Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

rcodetools-0.8.7.0

  • Loading branch information...
commit 24654f8ef1d2ee7a420b1bd2416a2a59dad22af9 0 parents
Takahiro Noda authored
Showing with 10,470 additions and 0 deletions.
  1. +2 −0  .gitignore
  2. +70 −0 CHANGES
  3. +98 −0 README
  4. +158 −0 README.TDC
  5. +75 −0 README.emacs
  6. +84 −0 README.ja
  7. +13 −0 README.method_analysis
  8. +111 −0 README.vim
  9. +287 −0 README.xmpfilter
  10. +121 −0 Rakefile
  11. +30 −0 Rakefile.method_analysis
  12. +11 −0 THANKS
  13. +151 −0 anything-rcodetools.el
  14. +266 −0 bin/rbtest
  15. +45 −0 bin/rct-complete
  16. +52 −0 bin/rct-doc
  17. +6 −0 bin/rct-fork
  18. +5 −0 bin/rct-fork-client
  19. +442 −0 bin/rct-meth-args
  20. +26 −0 bin/ruby-toggle-file
  21. +85 −0 bin/xmpfilter
  22. +35 −0 icicles-rcodetools.el
  23. +107 −0 lib/method_analyzer.rb
  24. +14 −0 lib/rcodetools/compat.rb
  25. +406 −0 lib/rcodetools/completion.rb
  26. +168 −0 lib/rcodetools/doc.rb
  27. +210 −0 lib/rcodetools/fork.rb
  28. +26 −0 lib/rcodetools/fork_config.rb
  29. +136 −0 lib/rcodetools/options.rb
  30. +379 −0 lib/rcodetools/xmpfilter.rb
  31. +314 −0 lib/rcodetools/xmptestunitfilter.rb
  32. +99 −0 lib/ruby_toggle_file.rb
  33. +430 −0 rcodetools.el
  34. BIN  rcodetools.elc
  35. BIN  rcodetools.gif
  36. BIN  rcodetools.sxmp
  37. +173 −0 rcodetools.vim
  38. +1,585 −0 setup.rb
  39. +45 −0 test/attic/test_run.rb
  40. +2 −0  test/data/attic/add_markers-input.rb
  41. +2 −0  test/data/attic/add_markers-output.rb
  42. +26 −0 test/data/attic/bindings-input.rb
  43. +31 −0 test/data/attic/bindings-output.rb
  44. +1 −0  test/data/attic/completion-input.rb
  45. +2 −0  test/data/attic/completion-output.rb
  46. +1 −0  test/data/attic/completion_class_info-input.rb
  47. +10 −0 test/data/attic/completion_class_info-output.rb
  48. +1 −0  test/data/attic/completion_class_info_no_candidates-input.rb
  49. +1 −0  test/data/attic/completion_class_info_no_candidates-output.rb
  50. +7 −0 test/data/attic/completion_detect_rbtest-input.rb
  51. +2 −0  test/data/attic/completion_detect_rbtest-output.rb
  52. +1 −0  test/data/attic/completion_detect_rbtest2-input.rb
  53. +2 −0  test/data/attic/completion_detect_rbtest2-output.rb
  54. +1 −0  test/data/attic/completion_emacs-input.rb
  55. +6 −0 test/data/attic/completion_emacs-output.rb
  56. +1 −0  test/data/attic/completion_emacs_icicles-input.rb
  57. +6 −0 test/data/attic/completion_emacs_icicles-output.rb
  58. +3 −0  test/data/attic/completion_in_method-input.rb
  59. +1 −0  test/data/attic/completion_in_method-output.rb
  60. +6 −0 test/data/attic/completion_in_method-test.rb
  61. +7 −0 test/data/attic/completion_rbtest-input.rb
  62. +2 −0  test/data/attic/completion_rbtest-output.rb
  63. +1 −0  test/data/attic/doc-input.rb
  64. +1 −0  test/data/attic/doc-output.rb
  65. +1 −0  test/data/attic/doc_detect_rbtest-input.rb
  66. +1 −0  test/data/attic/doc_detect_rbtest-output.rb
  67. +7 −0 test/data/attic/doc_detect_rbtest2-input.rb
  68. +1 −0  test/data/attic/doc_detect_rbtest2-output.rb
  69. +7 −0 test/data/attic/doc_rbtest-input.rb
  70. +1 −0  test/data/attic/doc_rbtest-output.rb
  71. +3 −0  test/data/attic/no_warnings-input.rb
  72. +4 −0 test/data/attic/no_warnings-output.rb
  73. +1 −0  test/data/attic/refe-input.rb
  74. +1 −0  test/data/attic/refe-output.rb
  75. +1 −0  test/data/attic/ri-input.rb
  76. +1 −0  test/data/attic/ri-output.rb
  77. +1 −0  test/data/attic/ri_emacs-input.rb
  78. +1 −0  test/data/attic/ri_emacs-output.rb
  79. +1 −0  test/data/attic/ri_vim-input.rb
  80. +1 −0  test/data/attic/ri_vim-output.rb
  81. +48 −0 test/data/attic/rspec-input.rb
  82. +52 −0 test/data/attic/rspec-output.rb
  83. +48 −0 test/data/attic/rspec_poetry-input.rb
  84. +52 −0 test/data/attic/rspec_poetry-output.rb
  85. +8 −0 test/data/attic/simple_annotation-input.rb
  86. +8 −0 test/data/attic/simple_annotation-output.rb
  87. +50 −0 test/data/attic/unit_test-input.rb
  88. +52 −0 test/data/attic/unit_test-output.rb
  89. +50 −0 test/data/attic/unit_test_detect_rbtest-input.rb
  90. +52 −0 test/data/attic/unit_test_detect_rbtest-output.rb
  91. +6 −0 test/data/attic/unit_test_detect_rbtest2-input.rb
  92. +6 −0 test/data/attic/unit_test_detect_rbtest2-output.rb
  93. +50 −0 test/data/attic/unit_test_poetry-input.rb
  94. +52 −0 test/data/attic/unit_test_poetry-output.rb
  95. +6 −0 test/data/attic/unit_test_rbtest-input.rb
  96. +6 −0 test/data/attic/unit_test_rbtest-output.rb
  97. +33 −0 test/data/method_analyzer-data.rb
  98. +106 −0 test/data/method_args.data.rb
  99. +17 −0 test/data/rct-complete-TDC/completion_in_method__testmethod.taf
  100. +17 −0 test/data/rct-complete-TDC/completion_in_method__testscript.taf
  101. +17 −0 test/data/rct-complete-TDC/completion_in_method__wrong_testmethod.taf
  102. +9 −0 test/data/rct-complete/completion.taf
  103. +9 −0 test/data/rct-complete/completion_class_info.taf
  104. +8 −0 test/data/rct-complete/completion_class_info_no_candidates.taf
  105. +15 −0 test/data/rct-complete/completion_detect_rbtest.taf
  106. +9 −0 test/data/rct-complete/completion_detect_rbtest2.taf
  107. +13 −0 test/data/rct-complete/completion_emacs.taf
  108. +13 −0 test/data/rct-complete/completion_emacs_icicles.taf
  109. +15 −0 test/data/rct-complete/completion_rbtest.taf
  110. +8 −0 test/data/rct-doc/doc.taf
  111. +8 −0 test/data/rct-doc/doc_detect_rbtest.taf
  112. +14 −0 test/data/rct-doc/doc_detect_rbtest2.taf
  113. +14 −0 test/data/rct-doc/doc_rbtest.taf
  114. +8 −0 test/data/rct-doc/refe.taf
  115. +8 −0 test/data/rct-doc/ri.taf
  116. +8 −0 test/data/rct-doc/ri_emacs.taf
  117. +8 −0 test/data/rct-doc/ri_vim.taf
  118. +9 −0 test/data/sample_test_script.rb
  119. +10 −0 test/data/xmpfilter/add_markers.taf
  120. +63 −0 test/data/xmpfilter/bindings.taf
  121. +22 −0 test/data/xmpfilter/comment_out.taf
  122. +14 −0 test/data/xmpfilter/exception.taf
  123. +54 −0 test/data/xmpfilter/expectations.taf
  124. +18 −0 test/data/xmpfilter/last_match.taf
  125. +9 −0 test/data/xmpfilter/mult.rb
  126. +22 −0 test/data/xmpfilter/multi_line_annotation_1.taf
  127. +24 −0 test/data/xmpfilter/multi_line_annotation_2.taf
  128. +20 −0 test/data/xmpfilter/multi_line_annotation_3.taf
  129. +22 −0 test/data/xmpfilter/multi_line_annotation_4.taf
  130. +34 −0 test/data/xmpfilter/multi_line_annotation_5.taf
  131. +12 −0 test/data/xmpfilter/multi_line_annotation_6.taf
  132. +23 −0 test/data/xmpfilter/multi_line_annotation_7.taf
  133. +13 −0 test/data/xmpfilter/no_warnings.taf
  134. +16 −0 test/data/xmpfilter/nospace.taf
  135. +106 −0 test/data/xmpfilter/rspec.taf
  136. +106 −0 test/data/xmpfilter/rspec_poetry.taf
  137. +22 −0 test/data/xmpfilter/simple_annotation.taf
  138. +108 −0 test/data/xmpfilter/unit_test.taf
  139. +108 −0 test/data/xmpfilter/unit_test_detect_rbtest.taf
  140. +18 −0 test/data/xmpfilter/unit_test_detect_rbtest2.taf
  141. +108 −0 test/data/xmpfilter/unit_test_poetry.taf
  142. +18 −0 test/data/xmpfilter/unit_test_rbtest.taf
  143. +16 −0 test/data/xmpfilter/width.taf
  144. +674 −0 test/test_completion.rb
  145. +446 −0 test/test_doc.rb
  146. +47 −0 test/test_functional.rb
  147. +107 −0 test/test_method_analyzer.rb
  148. +134 −0 test/test_method_args.rb
  149. +33 −0 test/test_options.rb
  150. +174 −0 test/test_ruby_toggle_file.rb
  151. +221 −0 test/test_xmpfilter.rb
  152. +86 −0 test/test_xmptestunitfilter.rb
  153. +162 −0 test/tmp_functional.rb
  154. +66 −0 test/tmp_run.rb
2  .gitignore
@@ -0,0 +1,2 @@
+pkg
+
70 CHANGES
@@ -0,0 +1,70 @@
+rcodetools history
+==================
+User-visible changes since 0.8.5
+--------------------------------
+* Fix DATA and __END__ handling in xmpfilter --tempfile (windows)
+
+User-visible changes since 0.8.4
+--------------------------------
+* OOPS, added missing files.
+
+User-visible changes since 0.8.0
+--------------------------------
+* xmpfilter: fixed multi-line annotation bugs
+
+User-visible changes since 0.7.0
+--------------------------------
+* Support Ruby 1.9!
+* xmpfilter: multi-line annotation
+* xmpfilter --expectations generates expectations by Jay Fields
+* anything-rcodetools.el: new elisp
+* --tmpfile, --tempfile: use temporary file instead of open3 on un*x
+* rcodetools.el: smarter xmpfilter-command
+* rcodetools.el: rct-fork interface
+* rct-fork: require 'rubygems' initially
+* rct-fork: more stable
+
+User-visible changes since 0.5.0
+--------------------------------
+* "test-driven completion" (TDC) support for Emacs and vim (see README.TDC)
+* --test (-t), --filename options for rct-complete and rct-doc, allowing to
+ specify the test to be run for 100% accurate completion/documentation in the
+ corresponding implementation
+* ruby-toggle-file: finds the test file corresponding to a given
+ implementation and vice versa
+* rct-fork, rct-fork-client: allow to eliminate the overhead due to library
+ loading (esp. useful for Rails)
+* rbtest: executes unit tests in a single Ruby script
+* --fork, --rbtest, --detect-rbtest supported by several commands
+* xmpfilter's --spec now autodetects the RSpec version and generates
+ specifications with the appropriate syntax
+
+User-visible changes since 0.4.1
+--------------------------------
+* --dev: adds project directories to $:
+* --completion-class-info: list completion candidates and class info
+* display completion candidates with description, both in emacs
+ and vim (using the menu+preview window).
+
+User-visible changes since 0.4.0
+--------------------------------
+* rct-meth-args: implemented -I
+* many bug fixes
+
+xmpfilter was integrated into rcodetools as of 0.4.0.
+
+xmpfilter history
+=================
+User-visible changes since 0.3.1 (2006-10-17)
+* implemented --debug
+* --[no]-warnings
+* --cd working_dir
+* --rails
+* --no-poetry
+* more intelligent assertions: try to find which local variables hold the
+ values compared against in assertions/expectations
+* editor-independent completion (-C, --completion-emacs, --completion-vim)
+* quick method/class reference with -D (--refe, --ri*)
+
+User-visible changes since 0.3.0 (2006-10-16)
+* xmpfilter.rb --spec works on win32 too
98 README
@@ -0,0 +1,98 @@
+
+ rcodetools http://eigenclass.org/hiki.rb?rcodetools
+ Copyright (c) 2005-2007 Mauricio Fernandez <mfp@acm.org> http://eigenclass.org
+ Copyright (c) 2006-2008 rubikitch <rubikitch@ruby-lang.org> http://www.rubyist.net/~rubikitch/
+Use and distribution subject to the terms of the Ruby license.
+
+= Overview
+rcodetools is a collection of Ruby code manipulation tools.
+It includes xmpfilter and editor-independent Ruby development helper tools,
+as well as emacs and vim interfaces.
+
+Currently, rcodetools comprises:
+* xmpfilter: Automagic Test::Unit assertions/RSpec expectations and code annotations
+* rct-complete: Accurate method/class/constant etc. completions
+* rct-doc: Document browsing and code navigator
+* rct-meth-args: Precise method info (meta-prog. aware) and TAGS generation
+* rct-fork: Pre-load heavy library(Rails etc) and speed up rct-complete/rct-doc (server)
+* rct-fork-client: Run Ruby programs from state the rct-fork server has
+* ruby-toggle-file: Toggle implementation file and test file
+* rbtest: Embedded Test::Unit for small scripts
+
+See also README.xmpfilter.
+
+Originally rct-complete and rct-doc were subcommands of xmpfilter.
+Actually they use xmpfilter's code heavily.
+But the relationship between xmpfilter (annotation) and completion/doc is not
+intuitive, so I (rubikitch) split it into separate executables.
+
+= Usage
+xmpfilter, rct-complete and rct-doc take its input from stdin and write to
+stdout. They can run in several modes; see
+ xmpfilter -h
+ rct-complete -h
+ rct-doc -h
+ rct-meth-args -h
+ rct-fork -h
+ rct-fork-client -h
+ ruby-toggle-file -h
+ rbtest -h
+README.emacs and README.vim describe how to use rcodetools from your editor.
+
+= Accurate Completion Internal and Caveat
+rct-complete and rct-doc use xmpfilter engine, ie they get runtime information by executing code.
+In Ruby (dynamic languages), type of any expressions except literals cannot be known without actually executing code.
+Moreover Ruby has open classes and singleton methods.
+Rcodetools asks `ruby' run-time informations, so we can get very accurate informations.
+Completion and document browsing are essentially identical operations,
+they both need the object value in question.
+Therefore we discuss completion.
+
+rct-complete does:
+(1) replaces target line with completion magic
+ (it calculates methods the target object has).
+(2) executes modified script.
+(3) once the control reaches completion magic, modified script exits.
+(4) outputs in specified format. (list candidates, EmacsLisp...)
+
+But this methodology has two big drawbacks, side-effects and inability to get any informations of uncovered code!
+
+An extreme side-effect example:
+ File.unlink a_file
+ File. <-
+
+If you call rct-complete, it removes a_file (sends a mail, accesses DB ...).
+So you must be careful to use, especially at TOPLEVEL.
+I (rubikitch) often experiment at TOPLEVEL with rcodetools, I NEVER use irb(sh) since rcodetools!
+
+An uncovered code example:
+ def foo
+ 1. <-
+ end
+
+If the code does not call foo, we cannot do any completions.
+
+Useless eh? But we already have a way to elude the drawbacks, test scripts (unit tests)!
+Test scripts are self-enclosed and expected to be executed, so side-effects are not problem.
+Moreover tests call methods we write.
+Because Ruby's Test::Unit has an ability to test only one test method, we can do lightning-fast completion.
+Let's call it Test-Driven Completion (TDC).
+
+To support TDC, rct-complete has -t option.
+With -t, it concatenate modified script and test/unit code.
+If the control does not reach target line, test/unit code calls the line.
+
+How do we select test script and test method?
+The editor selects recently selected buffer of test script as test script of TDC,
+because the test-infected tend to go and return between test script and implementation script.
+It considers files matching /test.*\.rb/ as test script.
+It selects test method at the cursor position.
+
+TDC adds roles of test scripts.
+Enjoy TDC magic!
+
+See also README.TDC.
+
+
+= License
+rcodetools is licensed under the same terms as Ruby.
158 README.TDC
@@ -0,0 +1,158 @@
+
+= Overview
+
+Ruby is very dynamic language, therefore it is impossible to do
+accurate completion without executing script. While executing script
+from start to cursor point is often dangerous, executing unit test
+script covering current point is SAFE. I call this methodology
+`Test-Driven Completion' (TDC).
+
+As I have already stated in README, browsing documentation of method
+(rct-doc) is almost identical operation to completion. This
+discussion is applicable to rct-doc.
+
+= Why TDD Is Needed
+
+In the following code snippet:
+
+ File.unlink a_file
+ File. <-
+
+If you complete after `File.', rct-complete actually deletes a_file.
+Normally it is unpleasant.
+In real-life development, side-effect is inevitable.
+
+In the foo method which are not called:
+
+ def foo
+ 1. <-
+ end
+
+If the code does not call foo, rct-complete cannot do any completions.
+Before TDC, if you want to do completion in methods, you have to write
+method call and remove it after completion. Too useless!!
+
+= Messianic Unit Test Script
+
+Recently Test-Driven Development (TDD) is widespread. Many developers
+write unit tests. Fortunately Ruby's unit tester, Test::Unit, is
+sophisticated enough to test one test method. Unit tests are
+self-enclosed: they must tear down resources, so executing unit tests
+are SAFE. TDC uses unit test to do completion.
+
+= TDC Methodology
+
+(1) Switch to unit test script.
+(2) Write a test for target method.
+(3) Switch to implementation script.
+(4) You can write target method WITH COMPLETION!
+(5) Back to (1)
+
+TDC methodology is almost identical to TDD. TDC is very easy for TDDers.
+
+= TDC With Example
+
+For simplicity, suppose that you are unfamiliar with Time class and
+you want to write a method to format date string.
+
+The directory structure and file contents is following:
+
+ /tmp/mylib0/
+ /tmp/mylib0/lib/
+ mylib0.rb
+ /tmp/mylib0/test/
+ test_mylib0.rb
+
+ List: mylib0.rb
+ # contrived example of long-runtime method
+ def mysleep(x)
+ sleep x
+ end
+
+ def mytime(tm)
+
+ end
+
+
+ List: test_mylib0.rb
+ require 'test/unit'
+ require 'mylib0'
+ class TestMylib0 < Test::Unit::TestCase
+ def test_0_mysleep
+ s = Time.now
+ mysleep 3.0
+ e = Time.now
+ assert_in_delta 3.0, e-s, 0.01
+ end
+
+ def test_1_mytime
+
+ end
+ end
+
+These sample files are in demo/ directory.
+
+
+== Switch to unit test script.
+
+TDC starts with writing unit test as TDD does.
+Open test_mylib0.rb.
+
+== Write a test for target method.
+
+Suppose that you want to write mytime method and test_1_mytime test
+method, and that you want to experiment Time class first (before
+forming an assertion).
+
+In TDC, you do not have to write an assertion first: just write only a
+method call. If you are familiar with Time class, you are free to
+write an assertion, of course.
+
+ def test_1_mytime
+ mytime(Time.now)
+ end
+
+At this time, the cursor position is in test_1_mytime test method.
+
+== Switch to implementation script.
+
+Open mylib0.rb with the `ruby-toggle-file' script. For example, in Emacs use
+the `ruby-toggle-buffer' command, and in vim the <localleader>t (by default
+\t) binding. Since in TDD/TDC you often switch between the test and the
+implementation, it is much handier than typing the filename manually.
+
+The rct-complete uses latest-selected test script as TDC test script
+and test method at cursor position as TDC test method. In this case,
+test_mylib0.rb is TDC test script and test_1_mytime is TDC test
+method. If the cursor position of test_mylib0.rb is at the top,
+rct-complete executes whole test methods in test_mylib0.rb. Therefore
+latency of completion is longer.
+
+== You can write target method WITH COMPLETION!
+
+Fill mytime method.
+
+ def mytime(tm)
+ tm.
+ end
+
+Do completion after `tm.'. Here! Your editor is listing methods `tm'
+accepts!! If your editor has help-on-candidate mechanism (eg. Emacs +
+Icicles), you would see documentation of each listed method.
+
+Then you find `Time#strftime' method. Type `str' and do completion.
+
+ def mytime(tm)
+ tm.strftime
+ end
+
+Usage is... use `rct-doc' (in Emacs, `rct-ri') after `strftime'.
+
+After you are familiar with Time class, switch to test script and write assertions.
+
+= When Modifying Another Method
+
+If you want to modify already-written method, setting cursor position
+of corresponding test script to corresponding test method is better.
+It tells rct-complete new test script and test method, so you can do
+completion in the new method.
75 README.emacs
@@ -0,0 +1,75 @@
+
+rcodetools.el allows you to run rcodetools on a buffer.
+
+To eval the sexp, type C-e C-x C-e; `end-of-line' and `eval-last-sexp'.
+
+installation
+============
+
+If you use RI document feature, you must install ri-emacs first.
+ http://rubyforge.org/projects/ri-emacs/
+
+If you feel RI and ri-emacs.rb startup is SLOW, you want to install FastRI.
+FastRI offers ri-emacs compatible layer, so you can use it with ri-ruby.el.
+ http://eigenclass.org/hiki.rb?fastri
+
+Copy <tt>rcodetools.el</tt> to the appropriate directory, which is in load-path.
+Then require it.
+ (require 'rcodetools)
+
+If you use icicles copy <tt>icicles-rcodetools.el</tt> too.
+Then require it.
+ (require 'icicles-rcodetools)
+It provides wonderful `help on candidate' feature, RI document on each candidate during completion.
+
+If you use anything.el copy <tt>anything-rcodetools.el</tt> too.
+Then require it.
+ (require 'anything-rcodetools)
+RI document on each candidate during completion.
+
+anything-show-completion.el shows selection (mehod) in buffer for completion.
+It is available in:
+ http://www.emacswiki.org/cgi-bin/wiki/download/anything-show-completion.el
+
+I think anything-rcodetools is more convenient than icicles-rcodetools.
+I'm addicted to anything!
+ http://www.emacswiki.org/cgi-bin/wiki/Anything
+
+xmpfilter on buffer
+===================
+
+# [EVAL IT] (describe-function 'xmp)
+
+If you want to add => marks, call comment-dwim twice.
+
+# [EVAL IT] (describe-function 'comment-dwim)
+
+method/class/constant completion
+================================
+
+# [EVAL IT] (describe-function 'rct-complete-symbol)
+
+If you use icicles-rcodetools or anything-rcodetools, you can browse RI document
+for selected candidate by typing C-M-RET (icicles) or C-z (anything.
+It is wonderful icicles and anything feature!!
+
+show RI document / jump to the definition
+=========================================
+
+# [EVAL IT] (describe-function 'rct-ri)
+
+By default rct-ri asks for a TAGS file, which is generated by tag generator like rtags.
+If there is a TAGS file, this command jumps to the definition of current method.
+If use do not use this feature, evaluate:
+ (setq rct-find-tag-if-available nil)
+
+# [EVAL IT] (describe-variable 'rct-find-tag-if-available)
+
+speed-up xmpfilter and completion
+=================================
+
+# [EVAL IT] (describe-function 'rct-fork)
+# [EVAL IT] (describe-function 'rct-fork-kill)
+
+M-x rct-fork pre-loads heavy libraries (like rails).
+You need not every time wait for loading them anymore!
84 README.ja
@@ -0,0 +1,84 @@
+
+ rcodetools http://eigenclass.org/hiki.rb?rcodetools
+ Copyright (c) 2005-2007 Mauricio Fernandez <mfp@acm.org> http://eigenclass.org
+ Copyright (c) 2006-2008 rubikitch <rubikitch@ruby-lang.org> http://www.rubyist.net/~rubikitch/
+Use and distribution subject to the terms of the Ruby license.
+
+= 概要
+rcodetools は Ruby のコードを扱うツール群です。
+rcodetools には xmpfilter とエディタに依存しない開発支援ツールが含まれています。
+Emacs と Vim のインターフェースも用意しています。
+
+* xmpfilter: Test::Unit assert* / RSpec should* 自動生成、コード注釈
+* rct-complete: 高精度メソッド名・クラス名・定数名等補完
+* rct-doc: ドキュメント参照・コードナビゲーター
+* rct-meth-args: 高精度メソッド情報リスト、TAGS ファイル作成
+* rct-fork: Rails 等重いライブラリを予めロードし、補完を高速化する(サーバ)
+* rct-fork-client: rct-fork サーバが保持する状態から Ruby スクリプトを実行する
+* ruby-toggle-file: テストスクリプトと実装スクリプトを切り換える
+* rbtest: 小規模スクリプトのための埋め込み Test::Unit
+
+
+= 使い方
+
+== コード注釈
+式の値を表示したい行に # => を加えます。
+
+ a, b = "foo", "baz"
+ a + b # =>
+ a.size # =>
+
+xmpfilter に通すと下のように式の値を表示してくれます。
+
+ a, b = "foo", "baz"
+ a + b # => "foobaz"
+ a.size # => 3
+
+
+== Test::Unit assert 文生成
+
+すでにできあがっているプログラムのテストスクリプトを書くのは面倒ですね。
+
+ def test_insertion
+ @o.insert "bar"
+ @o.insert "baz"
+ @o.size # =>
+ @o.last # =>
+ @o.first # =>
+ @o.complex_computation # =>
+ @o.last(2) # =>
+ end
+
+xmpfilter(-u オプション)が手間を軽減してくれます。
+
+ def test_insertion
+ @o.insert "bar"
+ @o.insert "baz"
+ assert_equal(2, @o.size)
+ assert_equal("baz", @o.last)
+ assert_equal("bar", @o.first)
+ assert_in_delta(3.14159265358979, @o.complex_computation, 0.0001)
+ assert_equal(["baz", "bar"], @o.last(2))
+ end
+
+RSpec についても同様のことができます。(-s オプション)
+
+== 補完・ドキュメント参照
+
+動画によるスクリーンショットを見てください。
+
+http://eigenclass.org/hiki.rb?rcodetools-screenshots
+
+== 詳しい使い方
+-h オプションをつけると使用可能なオプションが表示されます。
+
+ xmpfilter -h
+ rct-complete -h
+ rct-doc -h
+ rct-meth-args -h
+ rct-fork -h
+ rct-fork-client -h
+ ruby-toggle-file -h
+ rbtest -h
+
+README.emacs と README.vim にエディタ上での使い方が詳しく書いてあります。
13 README.method_analysis
@@ -0,0 +1,13 @@
+
+method_analyzer.rb can be used to gather precise information about the exact
+methods called in your code, allowing you to explore it better with rct-doc
+(see README.emacs and README.vim for more information). This requires high
+code coverage, since it can only record such data when the code is executed.
+
+rct-meth-args can be used to generate fairly complete TAGS files. It operates
+by loading the specified files and tracking method definitions; therefore, it
+is meta-programming aware, unlike other implementations.
+
+You can use them conveniently by adding the code shown in
+Rakefile.method_analysis to your Rakefile.
+
111 README.vim
@@ -0,0 +1,111 @@
+
+Copy rcodetools.vim to your plugin directory (typically $HOME/.vim/plugin) in
+order to enable accurate code completion, quick RI execution and exact tag
+jumping.
+
+Switching between implementation and test files
+===============================================
+The <localleader>t binding (by default \t) will call ruby-toggle-file to
+switch from test to implementation and vice versa. Moreover, when you switch
+from the test to the implementation, rcodetools will remember which test
+you were editing (based on the cursor position), and call it as needed for
+advanced code completion or precise RI documentation, as documented below.
+
+The actual binding can be changed in your .vimrc as follows:
+ let g:RCT_toggle_binding="<C-X><C-t>" " use ^X^T to go test <=> implementation
+
+Code completion
+===============
+rcodetools.vim redefines user-defined completion for Ruby programs, so you can
+use the intelligent, 100%-accurate completion with <C-X><C-U> in insert mode.
+Note that this runs the code to obtain the exact candidate list.
+
+If you've set completeopt to menu,preview then rcodetools.vim can display
+information about the completion candidates. The menu will show the synopsis
+as given in the RI documentation, and the preview window will contain the full
+RI documentation.
+
+This functionality relies on fri for quick lookups. It can be enabled by setting
+
+ let g:rct_completion_use_fri = 1 " 0 by default (disabled)
+
+in your .vimrc (don't forget to run fastri-server too).
+Obtaining the documentation for many candidates can be slow, so you can set
+the threshold above which additional documentation will not be shown with
+
+ " 20 by default, about a couple secs max wait on a normal machine
+ let g:rct_completion_info_max_len = 20
+
+Quick RI documentation and exact tag jumping
+============================================
+When you're editing a Ruby file, <C-]> will jump to the definition of the
+chosen element if found in the TAGS file; otherwise, it will call RI and show
+the documentation in a new window.
+You can specify the RI executable to use by adding something like
+ let g:RCT_ri_cmd = "ri -T -f plain "
+to your .vimrc. (rcodetools.vim also honors b:RCT_RI_cmd and w:RCT_RI_cmd if set).
+By default, "fri -f plain " will be used. fri (FastRI) is an improved RI
+documentation browser, which features more intelligent search modes, gem
+integration, vastly better performance... You can find it at
+http://eigenclass.org/hiki.rb?fastri and it's also available in gem format
+gem install fastri
+
+If you want to call RI for the word the cursor is on (instead of jumping to
+the definition if found), you can use this binding:
+ <LocalLeader>r (\r by default if you haven't changed your localleader)
+You can specify another binding in your .vimrc as follows:
+ let g:RCT_ri_binding="<C-X><C-R>" " use ^X^R to call vim on current word
+
+Using xmpfilter
+===============
+xmpfilter takes code from stdin and outputs to stdout so you can filter
+your code with ! as usual.
+
+If you use xmpfilter often, you might want to use mappings like the
+following, which allow you to:
+* add annotations
+* expand assertions
+* insert/remove # => markers
+
+
+
+" plain annotations
+map <silent> <F10> !xmpfilter -a<cr>
+nmap <silent> <F10> V<F10>
+imap <silent> <F10> <ESC><F10>a
+
+" Test::Unit assertions; use -s to generate RSpec expectations instead
+map <silent> <S-F10> !xmpfilter -u<cr>
+nmap <silent> <S-F10> V<S-F10>
+imap <silent> <S-F10> <ESC><S-F10>a
+
+" Annotate the full buffer
+" I actually prefer ggVG to %; it's a sort of poor man's visual bell
+nmap <silent> <F11> mzggVG!xmpfilter -a<cr>'z
+imap <silent> <F11> <ESC><F11>
+
+" assertions
+nmap <silent> <S-F11> mzggVG!xmpfilter -u<cr>'z
+imap <silent> <S-F11> <ESC><S-F11>a
+
+" Add # => markers
+vmap <silent> <F12> !xmpfilter -m<cr>
+nmap <silent> <F12> V<F12>
+imap <silent> <F12> <ESC><F12>a
+
+" Remove # => markers
+vmap <silent> <S-F12> ms:call RemoveRubyEval()<CR>
+nmap <silent> <S-F12> V<S-F12>
+imap <silent> <S-F12> <ESC><S-F12>a
+
+
+function! RemoveRubyEval() range
+ let begv = a:firstline
+ let endv = a:lastline
+ normal Hmt
+ set lz
+ execute ":" . begv . "," . endv . 's/\s*# \(=>\|!!\).*$//e'
+ normal 'tzt`s
+ set nolz
+ redraw
+endfunction
287 README.xmpfilter
@@ -0,0 +1,287 @@
+
+xmpfilter http://eigenclass.org/hiki.rb?xmpfilter
+Copyright (c) 2005-2008 Mauricio Fernandez <mfp@acm.org> http://eigenclass.org
+ rubikitch <rubikitch@ruby-lang.org>
+Use and distribution subject to the terms of the Ruby license.
+
+Overview
+========
+xmpfilter is a small tool that can be used to
+* generate Test::Unit assertions, RSpec expectations and
+ expectations blocks semi-automatically
+* annotate source code with intermediate results (a bit like irb
+ --simple-prompt but only for the lines explicitly marked with # =>)
+ Very useful for example code (such as postings to ruby-talk).
+
+Usage
+=====
+xmpfilter takes its input from stdin and writes to stdout. It can run in
+several modes (annotation, Test::Unit assertion expansion, RSpec expectation
+generation, expectations expectations generation, marker insertion); see
+ xmpfilter -h
+README.emacs and README.vim describe how to use xmpfilter from your editor.
+
+Example: code annotation
+========================
+Just add "# =>" markers to the lines whose values you want to be shown:
+
+ a, b = "foo", "baz"
+ a + b # =>
+ a.size # =>
+
+will be expanded to (in one keypress in a decent editor, see README.emacs and
+README.vim)
+
+ a, b = "foo", "baz"
+ a + b # => "foobaz"
+ a.size # => 3
+
+This saves much cut&pasting when you're posting to ruby-list/ruby-talk/ruby-core
+(We use it all the time).
+
+
+Example: multi-line code annotation
+===================================
+Just add "# =>" markers to the next lines whose values you want to be shown with pp:
+
+a = ["1111111111111111111111111111111111111111111111111111", 123334324234242342,
+ 1332333333,6,8 ]
+1 # =>
+a
+# =>
+
+will be expanded to (in one keypress in a decent editor, see README.emacs and
+README.vim)
+
+a = ["1111111111111111111111111111111111111111111111111111", 123334324234242342,
+ 1332333333,6,8 ]
+1 # => 1
+a
+# => ["1111111111111111111111111111111111111111111111111111",
+# 123334324234242342,
+# 1332333333,
+# 6,
+# 8]
+
+
+Example: assertion generation
+=============================
+
+xmpfilter can generate assertions based on the current behavior of the code
+to be tested (iow. the current behavior is assumed to be correct and is used
+to generate assertions which won't be modified by further runs of
+xmpfilter), making it quite useful for regression testing.
+
+Imagine you have a ComplexClass you want to test. You might start with
+
+ class TestComplexClass < Test::Unit::TestCase
+ def setup; @o = ComplexClass.new("foo", false) end
+ end
+
+and then want to add some tests:
+
+ def test_insertion
+ @o.insert "bar"
+ @o.insert "baz"
+ # ... assertions here
+ end
+
+At this point, you want to add several assertions to verify that the values
+returned by @o.size, @o.last, @o.first, @o.complex_computation and @o.last(2)
+are correct. You can just write the following and feed the file to
+xmpfilter in -u mode (the # => markers can also be inserted by
+xmpfilter, see README.vim for more information:
+
+ def test_insertion
+ @o.insert "bar"
+ @o.insert "baz"
+ @o.size # =>
+ @o.last # =>
+ @o.first # =>
+ @o.complex_computation # =>
+ @o.last(2) # =>
+ end
+
+xmpfilter will run the test and remember what happened in each marked line,
+and then rewrite the code so that it looks for instance like
+
+ def test_insertion
+ @o.insert "bar"
+ @o.insert "baz"
+ assert_equal(2, @o.size)
+ assert_equal("baz", @o.last)
+ assert_equal("bar", @o.first)
+ assert_in_delta(3.14159265358979, @o.complex_computation, 0.0001)
+ assert_equal(["baz", "bar"], @o.last(2))
+ end
+
+As you can see, it can save some typing.
+
+You can edit the generated assertions as you want: xmpfilter will not
+modify lines without the "# =>" marker. xmpfilter can be used repeatedly as
+you add more assertions. Imagine you want to verify that @o.last(3) raises an
+ArgumentError. You can simply add one line marked with # => :
+
+ ...
+ assert_in_delta(3.14159265358979, @o.complex_computation, 0.0001)
+ assert_equal(["baz", "bar"], @o.last(2))
+ @o.last(3) # =>
+ end
+
+and have it expanded by xmpfilter:
+
+ ...
+ assert_in_delta(3.14159265358979, @o.complex_computation, 0.0001)
+ assert_equal(["baz", "bar"], @o.last(2))
+ assert_raise(ArgumentError){ @o.last(3) }
+ end
+
+
+Example: RSpec expectations
+===========================
+Here's some code before and after filtering it with xmpfilter:
+
+ class X
+ Y = Struct.new(:a)
+ def foo(b); b ? Y.new(2) : 2 end
+ def bar; raise "No good" end
+ def baz; nil end
+ def fubar(x); x ** 2.0 + 1 end
+ def babar; [1,2] end
+ A = 1
+ A = 1
+ end
+
+ context "Testing xmpfilter's expectation expansion" do
+ setup do
+ @o = X.new
+ end
+
+ specify "Should expand should_equal expectations" do
+ @o.foo(true) # =>
+ @o.foo(true).a # =>
+ @o.foo(false) # =>
+ end
+
+ specify "Should expand should_raise expectations" do
+ @o.bar # =>
+ end
+
+ specify "Should expand should_be_nil expectations" do
+ @o.baz # =>
+ end
+
+ specify "Should expand correct expectations for complex values" do
+ @o.babar # =>
+ end
+
+ specify "Should expand should_be_close expectations" do
+ @o.fubar(10) # =>
+ end
+ end
+
+
+after piping it to xmpfilter -s:
+
+ class X
+ Y = Struct.new(:a)
+ def foo(b); b ? Y.new(2) : 2 end
+ def bar; raise "No good" end
+ def baz; nil end
+ def fubar(x); x ** 2.0 + 1 end
+ def babar; [1,2] end
+ A = 1
+ A = 1 # !> already initialized constant A
+ end
+
+ context "Testing xmpfilter's expectation expansion" do
+ setup do
+ @o = X.new
+ end
+
+ specify "Should expand should_equal expectations" do
+ (@o.foo(true)).should_be_a_kind_of X::Y
+ (@o.foo(true).inspect).should_equal "#<struct X::Y a=2>"
+ (@o.foo(true).a).should_equal 2
+ (@o.foo(false)).should_equal 2
+ end
+
+ specify "Should expand should_raise expectations" do
+ lambda{(@o.bar)}.should_raise RuntimeError
+ end
+
+ specify "Should expand should_be_nil expectations" do
+ (@o.baz).should_be_nil
+ end
+
+ specify "Should expand correct expectations for complex values" do
+ (@o.babar).should_equal [1, 2]
+ end
+
+ specify "Should expand should_be_close expectations" do
+ (@o.fubar(10)).should_be_close(101.0, 0.0001)
+ end
+ end
+
+
+Example: expectations expectations
+==================================
+Expectations is a light-weight unit testing framework by Jay Fields.
+(http://expectations.rubyforge.org)
+
+Here's some code before and after filtering it with xmpfilter:
+
+ require 'rubygems'
+ require 'expectations'
+
+ S = Struct.new :a
+ Expectations do
+ 1 + 1 # =>
+ "a".length # =>
+ [][1] # =>
+ 1.hoge # =>
+ 1.1 + 1.0 # =>
+ S.new(1) # =>
+ end
+
+after piping it to xmpfilter --expectations:
+
+ require 'rubygems'
+ require 'expectations'
+
+ S = Struct.new :a
+ Expectations do
+ expect 2 do
+ 1 + 1
+ end
+
+ expect 1 do
+ "a".length
+ end
+
+ expect nil do
+ [][1]
+ end
+
+ expect NoMethodError do
+ 1.hoge
+ end
+
+ expect 2.0999..2.1001 do
+ 1.1 + 1.0
+ end
+
+ expect S do
+ S.new(1)
+ end
+
+ expect "#<struct S a=1>" do
+ S.new(1).inspect
+ end
+
+ end
+
+
+License
+=======
+xmpfilter is licensed under the same terms as Ruby.
121 Rakefile
@@ -0,0 +1,121 @@
+
+PKG_REVISION = ".0"
+
+$:.unshift "lib" if File.directory? "lib"
+require 'rcodetools/xmpfilter'
+require 'rake/testtask'
+include Rcodetools
+RCT_VERSION = XMPFilter::VERSION
+
+desc "Run the unit tests in pure-Ruby mode ."
+Rake::TestTask.new(:test) do |t|
+ t.test_files = FileList['test/test*.rb']
+ t.verbose = true
+end
+
+begin
+ require 'rcov/rcovtask'
+ desc "Run rcov."
+ Rcov::RcovTask.new do |t|
+ t.rcov_opts << "--xrefs" # comment to disable cross-references
+ t.test_files = FileList['test/test_*.rb'].to_a - ["test/test_functional.rb"]
+ t.verbose = true
+ end
+
+ desc "Save current coverage state for later comparisons."
+ Rcov::RcovTask.new(:rcovsave) do |t|
+ t.rcov_opts << "--save"
+ t.test_files = FileList['test/test_*.rb'].to_a - ["test/test_functional.rb"]
+ t.verbose = true
+ end
+rescue LoadError
+ # rcov is not installed
+end
+task :default => :test
+
+
+#{{{ Package tasks
+PKG_FILES = FileList[
+ "bin/xmpfilter", "bin/rct-*", "bin/ruby-toggle-file", "bin/rbtest",
+"lib/**/*.rb",
+"CHANGES", "rcodetools.*", "icicles-rcodetools.el", "anything-rcodetools.el",
+"README", "README.*", "THANKS",
+"Rakefile", "Rakefile.method_analysis",
+"setup.rb",
+"test/**/*.rb","test/**/*.taf"
+]
+
+begin
+ require 'rake/gempackagetask'
+ Spec = Gem::Specification.new do |s|
+ s.name = "rcodetools"
+ s.version = RCT_VERSION + PKG_REVISION
+ s.summary = "rcodetools is a collection of Ruby code manipulation tools"
+ s.description = <<EOF
+rcodetools is a collection of Ruby code manipulation tools.
+It includes xmpfilter and editor-independent Ruby development helper tools,
+as well as emacs and vim interfaces.
+
+Currently, rcodetools comprises:
+* xmpfilter: Automagic Test::Unit assertions/RSpec expectations and code annotations
+* rct-complete: Accurate method/class/constant etc. completions
+* rct-doc: Document browsing and code navigator
+* rct-meth-args: Precise method info (meta-prog. aware) and TAGS generation
+EOF
+ s.files = PKG_FILES.to_a
+ s.require_path = 'lib'
+ s.author = "rubikitch and Mauricio Fernandez"
+ s.email = %{"rubikitch" <rubikitch@ruby-lang.org>, "Mauricio Fernandez" <mfp@acm.org>}
+ s.homepage = "http://eigenclass.org/hiki.rb?rcodetools"
+ s.bindir = "bin"
+ s.executables = %w[rct-complete rct-doc xmpfilter rct-meth-args]
+ s.has_rdoc = true
+ s.extra_rdoc_files = %w[README]
+ s.rdoc_options << "--main" << "README" << "--title" << 'rcodetools'
+ s.test_files = Dir["test/test_*.rb"]
+ s.post_install_message = <<EOF
+
+==============================================================================
+
+rcodetools will work better if you use it along with FastRI, an alternative to
+the standard 'ri' documentation browser which features intelligent searching,
+better RubyGems integration, vastly improved performance, remote queries via
+DRb... You can find it at http://eigenclass.org/hiki.rb?fastri and it is also
+available in RubyGems format:
+
+ gem install fastri
+
+Read README.emacs and README.vim for information on how to integrate
+rcodetools in your editor.
+
+==============================================================================
+
+EOF
+
+ end
+
+ task :gem => [:test]
+ Rake::GemPackageTask.new(Spec) do |p|
+ p.need_tar_gz = true
+ end
+
+rescue LoadError
+ # RubyGems not installed
+end
+
+desc "install by setup.rb"
+task :install do
+ sh "sudo ruby setup.rb install"
+end
+
+desc "release in rubyforge (package is created)"
+task :release_only do
+ sh "rubyforge login"
+ sh "rubyforge add_release rcodetools rcodetools #{RCT_VERSION} pkg/rcodetools-#{RCT_VERSION}.0.tar.gz "
+ sh "rubyforge add_file rcodetools rcodetools #{RCT_VERSION} pkg/rcodetools-#{RCT_VERSION}.0.gem "
+end
+
+desc "release in rubyforge"
+task :release => [:package, :release_only]
+
+# vim: set sw=2 ft=ruby:
30 Rakefile.method_analysis
@@ -0,0 +1,30 @@
+
+# Rake tasks to generate TAGS and gather method analysis information.
+# See README.method_analysis for more information.
+
+## my standard Rakefile
+task :tags => "TAGS"
+
+desc "Generate method_analysis by ruby -rmethod_analyzer."
+task :analyze => [:_prepare_method_analyze, :tags] do
+ at_exit { sh "ls -l method_analysis" }
+end
+
+task :_prepare_method_analyze do
+ ENV['METHOD_ANALYZER_FORMAT']="marshal"
+ sh "rm -f method_analysis"
+ puts "generating method_analysis"
+end
+
+## application-specific Rakefile (RTtool)
+task :analyze do
+ sh "ruby -Ilib -rmethod_analyzer test/test.rb"
+ sh "ruby -Ilib -rmethod_analyzer test/test-rt2html-lib.rb"
+ sh "ruby -Ilib -rmethod_analyzer test/test-rtparser.rb"
+end
+
+file "TAGS" => FileList["lib/rt/*.rb"] do
+ sh "rct-meth-args -t lib/rt/*.rb > TAGS"
+end
+
+
11 THANKS
@@ -0,0 +1,11 @@
+Some names forgotten, tell me if you care :) -- mfp
+
+rubikitch
+* expanded xmp3.rb (a previous version of xmpfilter.rb) to support RSpec expectations
+* wrote the elisp magic to use xmpfilter.rb with emacs
+* made the 100% accurate, editor-independent completion system
+[rubikitch took xmpfilter and turned it into the much more powerful
+rcodetools, so there are way too many things to list them here :)]
+
+Adagios
+* found & fixed problem with rcodetools.vim plugin on win32
151 anything-rcodetools.el
@@ -0,0 +1,151 @@
+;;; anything-rcodetools.el --- accurate Ruby method completion with anything
+;; $Id: anything-rcodetools.el,v 1.13 2009/04/20 16:25:37 rubikitch Exp $
+
+;;; Copyright (c) 2007 rubikitch
+
+;; Author: rubikitch <rubikitch@ruby-lang.org>
+;; URL: http://www.emacswiki.org/cgi-bin/wiki/download/anything-rcodetools.el
+
+;;; Use and distribution subject to the terms of the Ruby license.
+
+;;; Commentary:
+
+;; (0) You need rcodetools, anything.el and FastRI. Note that you do not have to
+;; configure anything.el if you use anything.el for this package.
+;; (1) You need to add to .emacs:
+;; (require 'anything)
+;; (require 'anything-rcodetools)
+;; ;; Command to get all RI entries.
+;; (setq rct-get-all-methods-command "PAGER=cat fri -l")
+;; ;; See docs
+;; (define-key anything-map "\C-z" 'anything-execute-persistent-action)
+
+;;; Commands:
+;;
+;; Below are complete command list:
+;;
+;;
+;;; Customizable Options:
+;;
+;; Below are customizable option list:
+;;
+
+;;; History:
+
+;; $Log: anything-rcodetools.el,v $
+;; Revision 1.13 2009/04/20 16:25:37 rubikitch
+;; Set anything-samewindow to nil
+;;
+;; Revision 1.12 2009/04/18 10:12:02 rubikitch
+;; Adjust to change of `use-anything-show-completion'
+;;
+;; Revision 1.11 2009/04/17 20:21:47 rubikitch
+;; * require anything
+;; * require anything-show-completion.el if available
+;;
+;; Revision 1.10 2009/04/17 20:11:03 rubikitch
+;; removed old code
+;;
+;; Revision 1.9 2009/04/17 20:07:52 rubikitch
+;; * use --completion-emacs-anything option
+;; * New implementation of `anything-c-source-complete-ruby-all'
+;;
+;; Revision 1.8 2009/04/15 10:25:25 rubikitch
+;; Set `anything-execute-action-at-once-if-one' t
+;;
+;; Revision 1.7 2009/04/15 10:24:23 rubikitch
+;; regexp bug fix
+;;
+;; Revision 1.6 2008/01/14 17:59:34 rubikitch
+;; * uniform format (anything-c-source-complete-ruby, anything-c-source-complete-ruby-all)
+;; * rename command: anything-c-ri -> anything-rct-ri
+;;
+;; Revision 1.5 2008/01/13 17:54:04 rubikitch
+;; anything-current-buffer advice.
+;;
+;; Revision 1.4 2008/01/08 14:47:34 rubikitch
+;; Added (require 'rcodetools).
+;; Revised commentary.
+;;
+;; Revision 1.3 2008/01/04 09:32:29 rubikitch
+;; *** empty log message ***
+;;
+;; Revision 1.2 2008/01/04 09:21:23 rubikitch
+;; fixed typo
+;;
+;; Revision 1.1 2008/01/04 09:21:05 rubikitch
+;; Initial revision
+;;
+
+;;; Code:
+
+(require 'anything)
+(require 'rcodetools)
+(when (require 'anything-show-completion nil t)
+ (use-anything-show-completion 'rct-complete-symbol--anything
+ '(length pattern)))
+
+(defun anything-rct-ri (meth)
+ (ri (get-text-property 0 'desc meth)))
+
+(defun anything-rct-complete (meth)
+ (save-excursion
+ (set-buffer anything-current-buffer)
+ (search-backward pattern)
+ (delete-char (length pattern)))
+ (insert meth))
+
+(setq rct-complete-symbol-function 'rct-complete-symbol--anything)
+(defvar anything-c-source-complete-ruby
+ '((name . "Ruby Method Completion")
+ (candidates . rct-method-completion-table)
+ (init
+ . (lambda ()
+ (condition-case x
+ (rct-exec-and-eval rct-complete-command-name "--completion-emacs-anything")
+ ((error) (setq rct-method-completion-table nil)))))
+ (action
+ ("Completion" . anything-rct-complete)
+ ("RI" . anything-rct-ri))
+ (volatile)
+ (persistent-action . anything-rct-ri)))
+
+(defvar rct-get-all-methods-command "PAGER=cat fri -l")
+(defvar anything-c-source-complete-ruby-all
+ '((name . "Ruby Method Completion (ALL)")
+ (init
+ . (lambda ()
+ (unless (anything-candidate-buffer)
+ (with-current-buffer (anything-candidate-buffer 'global)
+ (call-process-shell-command rct-get-all-methods-command nil t)
+ (goto-char 1)
+ (while (re-search-forward "^.+[:#.]\\([^:#.]+\\)$" nil t)
+ (replace-match "\\1\t[\\&]"))))))
+ (candidates-in-buffer
+ . (lambda ()
+ (let ((anything-pattern (format "^%s.*%s" (regexp-quote pattern) anything-pattern)))
+ (anything-candidates-in-buffer))))
+ (display-to-real
+ . (lambda (line)
+ (if (string-match "\t\\[\\(.+\\)\\]$" line)
+ (propertize (substring line 0 (match-beginning 0))
+ 'desc (match-string 1 line))
+ line)))
+ (action
+ ("Completion" . anything-rct-complete)
+ ("RI" . anything-rct-ri))
+ (persistent-action . anything-rct-ri)))
+
+
+(defun rct-complete-symbol--anything ()
+ (interactive)
+ (let ((anything-execute-action-at-once-if-one t)
+ anything-samewindow)
+ (anything '(anything-c-source-complete-ruby
+ anything-c-source-complete-ruby-all))))
+
+(provide 'anything-rcodetools)
+
+;; How to save (DO NOT REMOVE!!)
+;; (emacswiki-post "anything-rcodetools.el")
+;;; install-elisp.el ends here
266 bin/rbtest
@@ -0,0 +1,266 @@
+#! /usr/local/bin/ruby18
+# Copyright (c) 2006-2007 rubikitch <rubikitch@ruby-lang.org>
+#
+# Use and distribution subject to the terms of the Ruby license.
+
+USAGE = <<'XXX'
+Usage: rbtest SCRIPT [-S RUBY_INTERPRETER] [Test::Unit OPTIONS]
+Usage: rbtest [-h] [--help] [--example]
+
+I am rbtest, embedded Test::Unit executor for one-file scripts.
+Splitting a small script into many files (executables, libraries and tests) is cumbersome.
+And it is handy to put unit tests near implementations like D language, which has
+built-in unittest keyword.
+
+Embedded Test::Unit is simpler than vanilla Test::Unit.
+You do not have to define a Test::Unit::TestCase subclass,
+it is automagically defined and executed by me.
+Embedded Test::Unit uses =begin/=end comment blocks.
+
+"=begin TEST_METHOD_NAME" blocks define test methods, eg. "=begin test_foo".
+"=begin rbtest" blocks define utility methods and setup/teardown methods.
+
+Of course, you MUST use "if __FILE__ ==$0" idiom to split executable and class/method/function.
+
+I am also an real-life example of rbtest usage.
+Issue:
+ rbtest --example
+to show me.
+
+
+options:
+ -h, --help Print usage.
+ -S RUBY_INTERPRETER Use Ruby interpreter RUBY_INTERPRETER.
+ --example Print this file.
+ --output Print internally-generated test script (for debug).
+XXX
+
+
+def first_test(script_filename)
+ "require 'test/unit';" +
+ "load '#{script_filename}';" +
+ "class TestByRbtest < Test::Unit::TestCase;"
+end
+
+=begin rbtest
+def setup
+end
+
+def unindent(s)
+ s.lines.map{|x| x[1..-1]}.join
+end
+=end
+
+=begin test_script_to_test_script
+# indent is needed to avoid syntax error.
+script = <<XXX
+ #!/usr/bin/env ruby
+ =begin test0
+ assert_equal(10, f(1))
+ =end
+ def f(x) x*10 end
+ =begin
+ other block is skipped
+ =end
+ =begin test1
+ assert_equal(100, f(10))
+ =end
+ puts f(99) if __FILE__==$0
+XXX
+
+test_script = <<'XXX'
+ #!/usr/bin/env ruby
+ require 'test/unit';load 'f.rb';class TestByRbtest < Test::Unit::TestCase;def test0
+ assert_equal(10, f(1))
+ end
+
+
+
+
+ def test1
+ assert_equal(100, f(10))
+ end
+
+ end
+XXX
+assert_equal unindent(test_script), script_to_test_script(unindent(script), "f.rb")
+
+script = <<XXX
+ =begin rbtest
+ def setup() end
+ =end
+ def f(x) x*10 end
+ =begin test1
+ assert_equal(100, f(10))
+ =end
+ puts f(99) if __FILE__==$0
+XXX
+
+test_script = <<XXX
+ require 'test/unit';load 'f.rb';class TestByRbtest < Test::Unit::TestCase;
+ def setup() end
+
+
+ def test1
+ assert_equal(100, f(10))
+ end
+
+ end
+XXX
+
+assert_equal unindent(test_script), script_to_test_script(unindent(script), "f.rb")
+=end
+
+def script_to_test_script(ruby_source, script_filename)
+ _SKIP = "\n"
+ state = :start
+ ruby_source.lines.map do |line|
+ case line
+ when /^#!/
+ line
+ when "=begin rbtest\n"
+ preamble = (state == :start) ? first_test(script_filename) : ""
+ state = :rbtest_block
+ preamble + "\n"
+ when /^=begin (test\S*)$/ # test method block
+ preamble = (state == :start) ? first_test(script_filename) : ""
+ state = :test_method
+ preamble + "def #{$1}\n"
+ when /^=begin/ # other block
+ state = :begin
+ _SKIP
+ when /^=end$/
+ begin
+ if state == :test_method
+ "end\n" # /def test_xxx
+ else
+ _SKIP
+ end
+ ensure
+ state = :script
+ end
+ else
+ if state == :test_method or state == :rbtest_block
+ line
+ else
+ _SKIP
+ end
+ end
+ end.join + "end\n" # /class TestByRbtest
+end
+
+=begin test_execute_test_script_command
+require 'tempfile'
+temp = Tempfile.new "rbtest"
+test_script_filename = temp.path
+
+temp.puts "#!/usr/bin/ruby18"
+temp.flush
+assert_equal "/usr/bin/ruby18 #{test_script_filename} -v", execute_test_script_command(test_script_filename, parse_argv!(["a0.rb", "-v"]))
+
+temp.rewind
+temp.puts "class XX"
+temp.flush
+assert_equal "ruby #{test_script_filename} ", execute_test_script_command(test_script_filename, parse_argv!(["a0.rb"]))
+assert_equal "ruby18 #{test_script_filename} ", execute_test_script_command(test_script_filename, parse_argv!(["-S", "ruby18", "a0.rb"]))
+
+=end
+
+def shebang(filename)
+ open(filename) {|f| line = f.gets.chomp; line[0,2] == "#!" and line[2..-1] }
+end
+
+def execute_test_script_command(test_script_filename, args)
+ ruby = args.interpreter || shebang(test_script_filename) || "ruby"
+ "#{ruby} #{test_script_filename} #{args.opts.join ' '}"
+end
+
+def execute_test_script(test_script_filename, args)
+ output = `#{execute_test_script_command test_script_filename, args}`
+ print output.gsub(Regexp.union(test_script_filename), args.script_filename)
+end
+
+def generate_test_script(script_filename, test_script_filename)
+ open(test_script_filename, "w") do |f|
+ f.write(script_to_test_script(File.read(script_filename), script_filename))
+ f.chmod 0755 # executable
+ end
+end
+
+################
+def do_test(args)
+ begin
+ test_script_filename = "#{args.script_filename}__test__.rb"
+ generate_test_script(args.script_filename, test_script_filename)
+ execute_test_script(test_script_filename, args)
+ ensure
+ File.unlink test_script_filename
+ end
+end
+
+def output_test_script(args)
+ print(script_to_test_script(File.read(args.script_filename), args.script_filename))
+end
+
+def example(args)
+ print File.read($0)
+end
+
+def help(args)
+ print USAGE
+end
+
+=begin test_parse_argv_bang
+args = parse_argv!(["a.rb", "-S", "ruby19"])
+assert_equal "a.rb", args.script_filename
+assert_equal "ruby19", args.interpreter
+assert_equal [], args.opts
+assert_equal :do_test, args.action
+
+args = parse_argv!(["-S", "ruby19", "a.rb", "-v"])
+assert_equal "a.rb", args.script_filename
+assert_equal "ruby19", args.interpreter
+assert_equal ["-v"], args.opts
+assert_equal :do_test, args.action
+
+args = parse_argv!(["a.rb", "-n", "test_0"])
+assert_equal "a.rb", args.script_filename
+assert_equal nil, args.interpreter
+assert_equal ["-n", "test_0"], args.opts
+assert_equal :do_test, args.action
+
+assert_equal :help , parse_argv!([]).action
+assert_equal :help , parse_argv!(["--help"]).action
+assert_equal :help , parse_argv!(["-h"]).action
+
+args = parse_argv!(["--output", "a.rb"])
+assert_equal "a.rb", args.script_filename
+assert_equal :output_test_script, args.action
+
+assert_equal :example, parse_argv!(["--example"]).action
+
+=end
+
+Args = Struct.new :script_filename, :interpreter, :opts, :action
+def parse_argv!(argv)
+ args = Args.new
+ args.action = :do_test
+ argv.delete '--output' and args.action = :output_test_script
+ argv.delete '--example' and args.action = :example and return args
+ (argv.delete '-h' or argv.delete '--help') and args.action = :help
+ i = argv.index "-S"
+ if i and args.interpreter=argv[i+1]
+ argv.delete_at i+1
+ argv.delete_at i
+ end
+ args.script_filename = argv.shift
+ args.opts = argv
+ args.script_filename or args.action = :help
+ args
+end
+
+if __FILE__ ==$0
+ args = parse_argv! ARGV
+ __send__(args.action, args)
+end
+
45 bin/rct-complete
@@ -0,0 +1,45 @@
+#! /usr/local/bin/ruby18
+require 'rcodetools/xmpfilter'
+require 'rcodetools/completion'
+require 'rcodetools/options'
+
+include Rcodetools
+options = DEFAULT_OPTIONS
+klass = XMPCompletionFilter
+
+opts = OptionParser.new do |opts|
+ opts.extend OptionHandler
+ opts.set_banner
+
+ opts.separator ""
+ opts.separator "Modes:"
+ opts.on("-C", "--completion", "List completion candidates.(default)") do
+ klass = XMPCompletionFilter
+ end
+ opts.on("--completion-emacs", "Generate completion code for Emacs.") do
+ klass = XMPCompletionEmacsFilter
+ end
+ opts.on("--completion-emacs-icicles", "Generate completion code for Emacs/Icicles.") do
+ klass = XMPCompletionEmacsIciclesFilter
+ end
+ opts.on("--completion-emacs-anything", "Generate completion code for Emacs/Anything.") do
+ klass = XMPCompletionEmacsAnythingFilter
+ end
+ opts.on("--completion-class-info", "List completion candidates with class.") do
+ klass = XMPCompletionClassInfoFilter
+ end
+
+ opts.handle_position options
+ opts.handle_interpreter options
+ opts.handle_misc options
+end
+
+set_extra_opts options
+opts.parse!(ARGV)
+check_opts options
+
+targetcode = ARGF.read
+Dir.chdir options[:wd] if options[:wd]
+XMPFilter.detect_rbtest(targetcode, options)
+# Do the job. dispatched by klass.
+puts klass.run(targetcode, options)
52 bin/rct-doc
@@ -0,0 +1,52 @@
+#! /usr/local/bin/ruby18
+require 'rcodetools/xmpfilter'
+require 'rcodetools/doc'
+require 'rcodetools/options'
+
+include Rcodetools
+options = DEFAULT_OPTIONS
+klass = XMPDocFilter
+
+opts = OptionParser.new do |opts|
+ opts.extend OptionHandler
+ opts.set_banner
+
+ opts.separator ""
+ opts.separator "Modes:"
+ opts.on("-D", "--doc", "Print callee method with class.(default)") do
+ klass = XMPDocFilter
+ end
+ opts.on("--refe", "Refe callee method.") do
+ klass = XMPReFeFilter
+ end
+ opts.on("--ri", "Ri callee method.") do
+ klass = XMPRiFilter
+ end
+ opts.on("--ri-emacs", "Generate ri code for emacs.") do
+ klass = XMPRiEmacsFilter
+ end
+ opts.on("--ri-vim", "Generate ri code for vim.") do
+ klass = XMPRiVimFilter
+ end
+ opts.separator ""
+ opts.on("--use-method-analyzer", "") do |n|
+ options[:use_method_analyzer] = true
+ end
+ opts.on("--filename=FILENAME") do |n|
+ options[:filename] = File.expand_path n
+ end
+
+ opts.handle_position options
+ opts.handle_interpreter options
+ opts.handle_misc options
+end
+
+set_extra_opts options
+opts.parse!(ARGV)
+check_opts options
+
+targetcode = ARGF.read
+Dir.chdir options[:wd] if options[:wd]
+XMPFilter.detect_rbtest(targetcode, options)
+# Do the job. dispatched by klass.
+puts klass.run(targetcode, options)
6 bin/rct-fork
@@ -0,0 +1,6 @@
+#! /usr/local/bin/ruby18
+
+require 'rcodetools/fork'
+
+Rcodetools::Fork.start_server ARGV
+
5 bin/rct-fork-client
@@ -0,0 +1,5 @@
+#! /usr/local/bin/ruby18
+
+require 'rcodetools/fork'
+
+Rcodetools::Fork.start_client ARGV
442 bin/rct-meth-args
@@ -0,0 +1,442 @@
+#! /usr/local/bin/ruby18
+# Copyright (c)
+# 2006-2007 Mauricio Fernandez <mfp@acm.org> http://eigenclass.org
+# 2006-2007 rubikitch <rubikitch@ruby-lang.org> http://www.rubyist.net/~rubikitch/
+#
+# Use and distribution subject to the same conditions as Ruby.
+
+require 'rcodetools/options'
+include Rcodetools
+$VERBOSE = nil
+$__method_args_off = true
+
+if ARGV.empty? or ARGV.include? '-h' or ARGV.include? '--help'
+ puts <<EOF
+rct-meth-args [-Idirectory] [-i] [-m] [-c] [-n] <file> [<file> ...]
+
+-Idirectory specify $LOAD_PATH directory (same as ruby)
+--dev Add this project's bin/ and lib/ to $LOAD_PATH.
+ A directory that has Rakefile is considered as project base direcotry.
+-i omit instance methods defined in classes
+-m omit instance methods defined in modules
+-c omit class methods
+
+-n print the filename and line number with output lines
+-t generate TAGS output
+--summarize summary output sorted by method name
+
+The given files will be #require()d in order.
+Examples:
+ rct-meth-args complex
+ rct-meth-args thread
+ rct-meth-args -c rubygems
+EOF
+ exit # '
+end
+
+def debugprint(*args)
+ $stderr.puts(*args) if $DEBUG
+end
+
+module MethodArgs
+ MAX_ARGS = 20
+
+ private
+ def needed_args(arity)
+ arity >= 0 ? arity : arity.abs - 1
+ end
+
+ def with_tracer
+ begin
+ set_trace_func @__trace_func
+ yield
+ ensure
+ set_trace_func nil
+ end
+ end
+
+ module LineContents
+ # usage: Contents[filename][lineno]
+ Contents = Hash.new{ |h,k|
+ sum = 0
+ # Unshift is needed because lineno starts with 1.
+ h[k] = File.readlines(k).map{ |line| [line.chomp, sum+=line.length] }.unshift nil
+ }
+ def content
+ Contents[filename][lineno.to_i][0] rescue ""
+ end
+
+ def byte
+ Contents[filename][lineno.to_i-1][1] rescue 0
+ end
+
+ def arg_desc
+ # TODO
+ desc = content[ /\((.+)\)/]
+ desc.gsub!(/(\S),(\S)/, '\1, \2')
+ desc.gsub!(/(\S)=(\S)/, '\1 = \2')
+ desc
+ end
+
+ FileNameCache = Hash.new{ |h,k| h[k] = File.expand_path(k) }
+ def full_filename
+ FileNameCache[filename]
+ end
+ end
+
+ class Location < Struct.new(:filename, :lineno)
+ include LineContents
+ end
+
+ class Printer
+ def method_info(io, x)
+ prefix = x[:location] ? "#{x[:location].filename}:#{x[:location].lineno}:" : ""
+ io.puts "#{prefix}#{x[:fullname]}#{x[:arg_desc]}" unless x[:klass].to_s == "<Struct>"
+ end
+
+ def included_location(io, x)
+ x[:callsite] and io.print "#{x[:callsite].filename}:#{x[:callsite].lineno}:"
+ io.puts([ x[:comment], x[:mod], x[:flag], x[:self] ].join(" "))
+ end
+ end
+
+ class SummarizePrinter
+ def initialize
+ @hash = Hash.new
+ at_exit { print_result }
+ end
+
+ def method_info(io, x)
+ @io = io
+ prefix = x[:location] ? "#{x[:location].filename}:#{x[:location].lineno}:" : ""
+ (@hash[x[:meth]]||=[]) << "#{prefix}#{x[:fullname]}#{x[:arg_desc]}" unless x[:klass].to_s == "<Struct>"
+ end
+
+ def included_location(io, x)
+ # not implemented
+ end
+
+ def print_result
+ @hash.keys.sort.each do |meth|
+ @io.puts meth
+ @hash[meth].each do |line|
+ @io.puts line
+ end
+ @io.puts
+ end
+ end
+ end
+
+ class TagsPrinter
+ def initialize
+ @previous_filename = ""
+ end
+
+ def output_filename(filename)
+ if @previous_filename != filename
+ @previous_filename = filename
+ puts "\cl"
+ puts "#{filename},0"
+ end
+ end
+
+ def method_info(io, x)
+ return unless x[:location]
+ output_filename x[:location].full_filename
+ io.puts "#{x[:location].content}::#{x[:fullname]}#{x[:location].lineno},#{x[:location].byte}"
+ end
+
+ def included_location(io, x)
+ return unless x[:callsite]
+ output_filename x[:callsite].full_filename
+ io.puts "#{x[:callsite].content}::#{x[:mod]}#{x[:callsite].lineno},#{x[:callsite].byte}"
+ end
+ end
+
+ def _print_method_info(klass, is_singleton, meth, arg_desc=nil, location=nil, with_location=$__with_location, printer=$__printer)
+ arg_desc ||= "(...)"
+ arg_desc = " " + arg_desc unless arg_desc.empty?
+ flag = is_singleton ? "." : "#"
+ x = { :arg_desc => arg_desc, :klass => klass,
+ :meth => meth.to_s,
+ :fullname => [klass, flag, meth].join,
+ :location => with_location && location,
+ }
+ printer.method_info $>, x
+ end
+
+ def output_attr_info(klass, object, meth, is_singleton, location)
+ arg_desc = meth.to_s =~ /=$/ ? "(value)" : ""
+ _print_method_info klass, is_singleton, meth, arg_desc, location
+ end
+
+ def output_method_info(klass, object, meth, is_singleton = false, by_attr = nil, by_define_method = nil)
+ return if $__method_args_off
+ file = line = params = values = nil
+ location = nil
+ begin
+ unless %w[initialize].include?(meth.to_s)
+ if is_singleton
+ return if class << klass; private_instance_methods(true) end.include?(meth.to_s)
+ else
+ return if class << object; private_instance_methods(true) end.include?(meth.to_s)
+ end
+ end
+ rescue TypeError # no virtual class
+ end
+
+ arity = is_singleton ? object.method(meth).arity : klass.instance_method(meth).arity
+ @__trace_func = lambda{|event, file, line, id, binding, classname|
+ begin
+ debugprint "!EVENT: #{event} #{classname}##{id}, #{file} #{line}"
+ debugprint "(#{self} #{meth})"
+ if event[/call/] && classname == self && id == meth
+ location = Location.new(file, line) if event == 'call'
+ debugprint "EVENT: #{event} #{classname}##{id}"
+ throw :done
+ end
+ rescue Exception
+ nil # rcov workaround
+ end
+ }
+ if by_define_method
+ _print_method_info klass, is_singleton, meth, nil, by_define_method
+ return
+ end
+
+ if by_attr
+ output_attr_info klass, object, meth, is_singleton, by_attr
+ return
+ end
+
+ catch(:done) do
+ begin
+ with_tracer { object.send(meth, *(0...needed_args(arity))) }
+ rescue Exception
+ nil # rcov workaround
+ end
+ end
+
+ arg_desc = location.arg_desc
+
+ _print_method_info klass, is_singleton, meth, arg_desc, location
+ rescue Exception
+ debugprint "GOT EXCEPTION while processing #{klass} #{meth}"
+ debugprint $!.class
+ debugprint $!.message
+ debugprint $!.backtrace
+ _print_method_info klass, is_singleton, meth, nil, location
+ ensure
+ set_trace_func(nil)
+ end
+end
+
+class Object
+ include MethodArgs
+end
+
+class Module
+ class CallSite # compatible with Location
+ include MethodArgs::LineContents
+ def initialize(caller_string)
+ @filename, @lineno, @in = caller_string.split(/:/)
+ end
+ attr_reader :filename, :lineno
+ end
+
+ def _method_defined_with(ivar, caller, &block)
+ begin
+ instance_variable_set(ivar, caller)
+ yield
+ ensure
+ instance_variable_set(ivar, false)
+ end
+ end
+
+ def with_attr(caller, &block)
+ _method_defined_with :@by_attr, caller, &block
+ end
+
+ def with_define_method(caller, &block)
+ _method_defined_with :@by_define_method, caller, &block
+ end
+
+ [ :attr, :attr_reader, :attr_writer, :attr_accessor ].each do |meth|
+ attr = instance_method(meth)
+ define_method(meth) do |*args|
+ with_attr(CallSite.new(caller(1)[0])){ attr.bind(self).call(*args) }
+ end
+ end
+
+ alias :__define_method_orig :define_method
+ def define_method(*args, &body)
+ with_define_method(CallSite.new(caller(1)[0])){ __define_method_orig(*args, &body) }
+ end
+
+end
+
+new_args = []
+omissions = {}
+$__with_location = false
+$__printer = Printer.new
+i_opt_p = false
+
+ARGV.each do |arg|
+ case arg
+ when "-n"; $__with_location = true
+ when "-t"; $__printer = TagsPrinter.new; $__with_location = true
+ when "--summarize"; $__printer = SummarizePrinter.new
+ when /-I(.+)$/; $:.unshift $1
+ when "-I"; i_opt_p = true
+ when "--dev"; OptionHandler.auto_include_paths($:, Dir.pwd)
+ when /^-.$/; omissions[$&] = true
+ else
+ if i_opt_p
+ $:.unshift arg
+ i_opt_p = false
+ else
+ new_args << arg
+ end
+ end
+end
+
+ARGV.replace new_args
+
+class Object
+ ALLOCATOR = Hash.new{|h,k| h[k] = k.allocate }.merge(Fixnum=>1,
+ Bignum=>10000000000000000,
+ Float=>1.1,
+ Symbol=>:method_args_tmp,
+ Binding=>binding,
+ UnboundMethod=>instance_method(:object_id),
+ Method=>method(:object_id),
+ Proc=>lambda{},
+ Continuation=>callcc{|c|c},
+ Thread=>Thread.new{},
+ FalseClass=>false,
+ TrueClass=>true,
+ NilClass=>nil,
+ Struct=>Struct.new(:a).new(1))
+
+# ABSTRACT_CLASSES = %w[Digest::Base ]
+ def self.method_added(meth)
+# if ABSTRACT_CLASSES.include? self.to_s
+# _print_method_info self, false, meth
+# return
+# end
+ begin
+ o = ALLOCATOR[self]
+ rescue Exception # for abstract classes
+ nil
+ end
+ output_method_info(self, o, meth, false, @by_attr, @by_define_method)
+ end
+end unless omissions["-i"]
+
+class Module
+ method_added = instance_method(:method_added)
+ define_method(:method_added) do |meth|
+ begin
+ if instance_of? Module
+ o = Object.new
+ o.extend(self)
+ output_method_info(self, o, meth, false, @by_attr, @by_define_method)
+ end
+ method_added.bind(self).call(meth)
+ rescue Exception
+ _print_method_info self, false, meth
+ end
+ end
+end unless omissions["-m"]
+
+
+class Class
+ def singleton_method_added(meth)
+ by_attr = class << self; @by_attr; end
+ by_define_method = class << self; @by_define_method; end
+ output_method_info(self, self, meth, true, by_attr, by_define_method)
+ rescue Exception
+ _print_method_info self, true, meth
+ end
+end unless omissions["-c"]
+
+#### include / extend
+class Module
+ def _print_included_location(mod, caller_string, comment, flag, with_location=$__with_location, printer=$__printer)
+ if caller_string # nil when the class is defined by C
+ x = { :comment => comment, :mod => mod, :flag => flag, :self => self,
+ :callsite => with_location && CallSite.new(caller_string)
+ }
+ printer.included_location $>, x
+ end
+ end
+
+ undef_method :included
+ def included(mod)
+ _print_included_location mod, caller(0)[1], "include", "<="
+ end
+
+ undef_method :extended
+ def extended(mod)
+ _print_included_location mod, caller(0)[1], "extend", "<-" if Module === mod
+ end
+end
+
+#### inheritance
+class Class
+ undef :inherited
+ def inherited(klass, caller_level=0)
+ _print_included_location klass, caller(caller_level)[1], "class", "<" unless self == Object
+ end
+end
+
+#### Struct inspection
+class << Struct
+ def to_s
+ orig = super
+ if orig =~ /<Class/
+ mems = instance_methods(false).select{ |m| m.to_s !~ /=$/}.sort.join(",")
+ mems.empty? ? "<Struct>" : "<Struct: #{mems}>"
+ else
+ orig
+ end
+ end
+
+ def inherited(klass)
+ if self == Struct
+ @inherited_delay = lambda{ super(klass, 3) }
+ else
+ super(klass, 1)
+ end
+ end
+
+ @@struct_initializing = false
+ def method_added(meth)
+ super unless @@struct_initializing
+ end
+
+ orig_new = instance_method(:new)
+ define_method(:new) do |*args|
+ begin
+ @@struct_initializing = true
+ orig_new.bind(self).call(*args)
+ ensure
+ @@struct_initializing = false
+ @inherited_delay[]
+ end
+ end
+
+end
+
+$__method_args_off = false
+
+ARGV.each{|x|
+ begin
+ require x
+ rescue LoadError
+ load x
+ end
+}
+
+$__method_args_off = true
+# END { puts "zzzz OK" }
26 bin/ruby-toggle-file
@@ -0,0 +1,26 @@
+#! /usr/local/bin/ruby18
+require 'ruby_toggle_file'
+require 'optparse'
+
+USAGE = <<XXX
+Usage: ruby-toggle-file RUBY_SCRIPT
+
+I (ruby-toggle-file) accept one Ruby script filename and prints
+corresponding file. If RUBY_SCRIPT is implementation script
+(library), then returns test script and vice versa. It is recommended
+that implementation scripts are located in lib/ directory and test
+scripts are located in test/ directory. This command supports Ruby on
+Rails file layout.
+
+I infer corresponding file if it does not exist.
+
+If you are curious about behavior, see test/test_ruby_toggle_file.rb.
+
+I am meant to be used in text editors or IDEs.
+XXX
+
+ARGV.options {|o|
+ o.banner = USAGE
+ o.parse!
+}
+print RubyToggleFile.new.ruby_toggle_file(ARGV.first)
85 bin/xmpfilter
@@ -0,0 +1,85 @@
+#! /usr/local/bin/ruby18
+require 'rcodetools/xmpfilter'
+require 'rcodetools/options'
+
+include Rcodetools
+options = DEFAULT_OPTIONS
+rails_settings = false
+klass = XMPFilter
+
+opts = OptionParser.new do |opts|
+ opts.extend OptionHandler
+ opts.set_banner
+
+ opts.separator ""
+ opts.separator "Modes:"
+ opts.on("-a", "--annotations", "Annotate code (default)") do
+ klass = XMPFilter
+ end
+ opts.on("-u", "--unittest", "Complete Test::Unit assertions.") do
+ require 'rcodetools/xmptestunitfilter'
+ klass = XMPTestUnitFilter
+ end
+ opts.on("-s", "--spec", "Complete RSpec expectations.") do
+ require 'rcodetools/xmptestunitfilter'
+ klass = XMPRSpecFilter
+ options[:interpreter] = "spec"
+ end
+ opts.on("--expectations", "Complete expectations expectations.") do
+ require 'rcodetools/xmptestunitfilter'
+ klass = XMPExpectationsFilter
+ end
+ opts.on("-m", "--markers", "Add # => markers.") do
+ klass = XMPAddMarkers
+ end
+
+ opts.handle_interpreter options
+
+ opts.separator ""
+ opts.separator "Specific options:"
+ opts.on("-l N", "--min-line-length N", Integer, "Align markers to N spaces.") do |min_codeline_size|
+ options[:min_codeline_size] = min_codeline_size
+ end
+ opts.on("--rails", "Setting appropriate for Rails.",
+ "(no warnings, find working directory,",
+ " Test::Unit assertions)") do
+ require 'rcodetools/xmptestunitfilter'
+ options[:warnings] = false
+ klass = XMPTestUnitFilter
+ rails_settings = true
+ end
+ opts.on("--[no-]poetry", "Whether to use extra parentheses.",
+ "(default: use them)") do |poetry_p|
+ options[:use_parentheses] = !poetry_p
+ end
+ opts.on("--[no-]warnings", "Whether to add warnings (# !>).",
+ "(default: enabled)") {|warnings_p| options[:warnings] = warnings_p }
+ opts.on("-q", "--quiet", "Supress standard output.") do
+ options[:output_stdout] = false
+ end
+
+ opts.handle_misc options
+end
+
+set_extra_opts options
+opts.parse!(ARGV)
+
+if rails_settings && !options[:wd]
+ if File.exist? ARGF.path
+ options[:wd] = File.dirname(ARGF.path)
+ elsif File.exist? "test/unit"
+ options[:wd] = "test/unit"
+ elsif File.exist? "unit"
+ options[:wd] = "unit"
+ end
+end
+targetcode = ARGF.read
+Dir.chdir options[:wd] if options[:wd]
+
+if XMPFilter.detect_rbtest(targetcode, options)
+ require 'rcodetools/xmptestunitfilter'
+ klass = XMPTestUnitFilter
+end
+
+# Do the job. dispatched by klass.
+puts klass.run(targetcode, options)
35 icicles-rcodetools.el
@@ -0,0 +1,35 @@
+;;; icicles-rcodetools.el -- accurate completion with icicles
+
+;;; Copyright (c) 2006-2007 rubikitch <rubikitch@ruby-lang.org>
+;;;
+;;; Use and distribution subject to the terms of the Ruby license.
+
+(require 'icicles)
+(require 'rcodetools)
+
+(setq rct-complete-symbol-function 'rct-complete-symbol--icicles)
+(icicle-define-command rct-complete-symbol--icicles
+ "Perform ruby method and class completion on the text around point with icicles.
+C-M-RET shows RI documentation on each candidate.
+See also `rct-interactive'."
+
+ (lambda (result)
+ (save-excursion
+ (search-backward pattern)
+ (setq beg (point)))
+ (delete-region beg end)
+ (insert result)) ;/function
+ "rct-complete: " ;prompt
+ rct-method-completion-table
+ nil nil pattern nil nil nil
+ ((end (point)) beg
+ (icicle-list-join-string "\t")
+ (icicle-list-use-nth-parts '(1))
+ (icicle-point-position-in-candidate 'input-end)
+ pattern klass alist
+ (icicle-candidate-help-fn
+ (lambda (result)
+ (ri (cdr (assoc result alist)))))) ;bindings
+ (rct-exec-and-eval rct-complete-command-name "--completion-emacs-icicles"))
+
+(provide 'icicles-rcodetools)
107 lib/method_analyzer.rb
@@ -0,0 +1,107 @@
+class Module
+ remove_method :attr_reader
+ def attr_reader(*names)
+ names.each do |name|
+ module_eval "def #{name}() @#{name} end"
+ end
+ end
+ remove_method :attr_writer
+ def attr_writer(*names)
+ names.each do |name|
+ module_eval "def #{name}=(x) @#{name}=x end"
+ end
+ end
+ remove_method :attr_accessor
+ def attr_accessor(*names)
+ attr_reader(*names)
+ attr_writer(*names)
+ end
+ remove_method :attr
+ def attr(name, writer=false)
+ attr_reader name
+ attr_writer name if writer
+ end
+end
+
+
+module MethodAnalyzer
+ @@methods = Hash.new{ |h,k| h[k] = Hash.new{ |h,k| h[k] = []} }
+ @@whereis = []
+ @@expand_path = Hash.new{ |h,k| h[k] = File.expand_path(k)}
+
+ def self.trace_func(event, file, line, id, binding, klass, *rest)
+ return if file == __FILE__
+ return if (event != 'call' and event != 'c-call')
+ return if klass == Class and id == :inherited
+ return if klass == Module and id == :method_added
+ return if klass == Kernel and id == :singleton_method_added
+ saved_crit = Thread.critical
+ Thread.critical = true
+
+ the_self = eval("self",binding)
+ flag = Class === the_self ? "." : "#"
+ #klass = klass == Kernel ? Object : klass
+ fullname = "#{klass}#{flag}#{id}"
+ file.replace @@expand_path[file]
+ if event == 'call'
+ @@whereis << [file, line, fullname] if file !~ /\(eval\)$/
+ file, line, rest = caller(4)[0].split(/:/)
+ file.replace @@expand_path[file] # DRY
+ p caller(0) if $DEBUG
+ line = line.to_i
+ end
+ @@methods[file][line] << fullname if event =~ /call/
+
+ Thread.critical = saved_crit
+ end
+
+ def self.at_exit__output_marshal
+ at_exit do
+ set_trace_func nil
+ dbfile = "method_analysis"
+ old = Marshal.load(File.read(dbfile)) rescue {}
+ open(dbfile, "wb") do |io|
+ # Because Marshal.dump cannot handle hashes with default_proc
+ @@methods.default = nil
+ @@methods.each_value{ |v| v.default=nil; v.each_value{ |vv| vv.uniq! } }
+ Marshal.dump(@@methods.merge(old), io)
+ end
+ end
+ end
+
+
+ def self.at_exit__output_text
+ at_exit do
+ set_trace_func nil
+ puts "method fullnames"
+ @@methods.sort.each do |file, lines|
+ lines.sort.each do |line, methods|
+ printf "%s:%s:%s\n", file, line, methods.uniq.join(" ")
+ end
+ end
+
+ puts
+ puts "method definitions"
+ @@whereis.sort.uniq.each do |file, line, fullname |
+ printf "%s:%s:%s\n", file, line, fullname
+ end
+
+ end
+ end
+
+ def self.set_at_exit
+ case ENV['METHOD_ANALYZER_FORMAT']
+ when 'marshal'
+ at_exit__output_marshal
+ else
+ at_exit__output_text
+ end
+ end
+
+ set_at_exit
+ set_trace_func method(:trace_func).to_proc
+end
+
+if __FILE__ == $0
+ load "./test/data/method_analyzer-data.rb"
+end
14 lib/rcodetools/compat.rb
@@ -0,0 +1,14 @@
+if RUBY_VERSION >= "1.9"
+ class String
+ alias :each :each_line
+ include Enumerable
+ end
+
+ module Enumerable
+ alias :enum_with_index :each_with_index
+ end
+
+ class Array
+ alias :to_s :join
+ end
+end
406 lib/rcodetools/completion.rb
@@ -0,0 +1,406 @@
+# Nearly 100% accurate completion for any editors!!
+# by rubikitch <rubikitch@ruby-lang.org>
+
+require 'rcodetools/xmpfilter'
+require 'enumerator'
+
+module Rcodetools
+
+# Common routines for XMPCompletionFilter/XMPDocFilter
+module ProcessParticularLine
+ def fill_literal!(expr)
+ [ "\"", "'", "`" ].each do |q|
+ expr.gsub!(/#{q}(.+)#{q}/){ '"' + "x"*$1.length + '"' }
+ end
+ expr.gsub!(/(%([wWqQxrs])?(\W))(.+?)\3/){
+ percent = $2 == 'x' ? '%'+$3 : $1 # avoid executing shell command
+ percent + "x"*$4.length + $3
+ }
+ [ %w[( )], %w[{ }], %w![ ]!, %w[< >] ].each do |b,e|
+ rb, re = [b,e].map{ |x| Regexp.quote(x)}
+ expr.gsub!(/(%([wWqQxrs])?(#{rb}))(.+)#{re}/){
+ percent = $2 == 'x' ? '%'+$3 : $1 # avoid executing shell command
+ percent + "x"*$4.length + e
+ }
+ end
+ end
+
+ module ExpressionExtension
+ attr_accessor :eval_string
+ attr_accessor :meth
+ end
+ OPERATOR_CHARS = '\|^&<>=~\+\-\*\/%\['
+ def set_expr_and_postfix!(expr, column, &regexp)
+ expr.extend ExpressionExtension
+
+ @postfix = ""
+ expr_orig = expr.clone
+ column ||= expr.length
+ last_char = expr[column-1]
+ expr.replace expr[ regexp[column] ]
+ debugprint "expr_orig=#{expr_orig}", "expr(sliced)=#{expr}"
+ right_stripped = Regexp.last_match.post_match
+ _handle_do_end right_stripped
+ aref_or_aset = aref_or_aset? right_stripped, last_char
+ debugprint "aref_or_aset=#{aref_or_aset.inspect}"
+ set_last_word! expr, aref_or_aset
+ fill_literal! expr_orig
+ _handle_brackets expr_orig, expr
+ expr << aref_or_aset if aref_or_aset
+ _handle_keywords expr_orig, column
+ debugprint "expr(processed)=#{expr}"
+ expr
+ end
+
+ def _handle_do_end(right_stripped)
+ right_stripped << "\n"
+ n_do = right_stripped.scan(/[\s\)]do\s/).length
+ n_end = right_stripped.scan(/\bend\b/).length
+ @postfix = ";begin" * (n_do - n_end)
+ end
+
+ def _handle_brackets(expr_orig, expr)
+ [ %w[{ }], %w[( )], %w![ ]! ].each do |left, right|
+ n_left = expr_orig.count(left) - expr.count(left)
+ n_right = expr_orig.count(right) - expr.count(right)
+ n = n_left - n_right
+ @postfix << ";#{left}" * n if n >= 0
+ end
+ end
+
+ def _handle_keywords(expr_orig, column)
+ %w[if unless while until for].each do |keyw|
+ pos = expr_orig.index(/\b#{keyw}\b/)
+ @postfix << ";begin" if pos and pos < column # if * xxx
+
+ pos = expr_orig.index(/;\s*#{keyw}\b/)
+ @postfix << ";begin" if pos and column < pos # * ; if xxx
+ end
+ end
+
+ def aref_or_aset?(right_stripped, last_char)
+ if last_char == ?[
+ case right_stripped
+ when /\]\s*=/ then "[]="
+ when /\]/ then "[]"
+ end
+ end
+ end
+
+ def set_last_word!(expr, aref_or_aset=nil)
+ debugprint "expr(before set_last_word)=#{expr}"
+ if aref_or_aset
+ opchars = ""
+ else
+ opchars = expr.slice!(/\s*[#{OPERATOR_CHARS}]+$/)
+ debugprint "expr(strip opchars)=#{expr}"
+ end
+
+ expr.replace(if expr =~ /[\"\'\`]$/ # String operations
+ "''"
+ else
+ fill_literal! expr
+ phrase = current_phrase(expr)
+ if aref_or_aset
+ expr.eval_string = expr[0..-2]
+ expr.meth = aref_or_aset
+ elsif phrase.match( /^(.+)\.(.*)$/ )
+ expr.eval_string, expr.meth = $1, $2
+ elsif opchars != ''
+ expr
+ end
+ debugprint "expr.eval_string=#{expr.eval_string}", "expr.meth=#{expr.meth}"
+ phrase
+ end << (opchars || '')) # ` font-lock hack
+ debugprint "expr(after set_last_word)=#{expr}"
+ end
+
+ def current_phrase(expr)
+ paren_level = 0
+ start = 0
+ (expr.length-1).downto(0) do |i|
+ c = expr[i,1]
+ if c =~ /[\)\}\]]/
+ paren_level += 1
+ next
+ end
+ if paren_level > 0
+ next if c =~ /[, ]/
+ else
+ break (start = i+1) if c =~ /[ ,\(\{\[]/
+ end
+ if c =~ /[\(\{\[]/
+ paren_level -= 1
+ break (start = i+1) if paren_level < 0
+ end
+ end
+ expr[start..-1]
+ end
+
+ def add_BEGIN
+ <<XXX
+BEGIN {
+class Object
+ def method_missing(meth, *args, &block)
+ # ignore NoMethodError
+ end
+end
+}
+XXX
+ end
+
+ class RuntimeDataError < RuntimeError; end
+ class NewCodeError < Exception; end
+ def runtime_data_with_class(code, lineno, column=nil)
+ newcode = code.to_a.enum_with_index.map{|line, i|
+ i+1==lineno ? prepare_line(line.chomp, column) : line
+ }.join
+ newcode << add_BEGIN if @ignore_NoMethodError
+ debugprint "newcode", newcode.gsub(/;/, "\n"), "-"*80
+ stdout, stderr = execute(newcode)
+ output = stderr.readlines
+ debugprint "stderr", output, "-"*80
+ output = output.reject{|x| /^-:[0-9]+: warning/.match(x)}
+ runtime_data = extract_data(output)
+ if exception = /^-:[0-9]+:.*/m.match(output.join)