PageRenderTime 50ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 0ms

/emacs.d/vendor/rcodetools.el

http://github.com/rmm5t/dotfiles
Emacs Lisp | 430 lines | 369 code | 36 blank | 25 comment | 5 complexity | 23caebe8c85d78d342fe4abca659c9fd MD5 | raw file
Possible License(s): CC0-1.0
  1. ;;; rcodetools.el -- annotation / accurate completion / browsing documentation
  2. ;;; Copyright (c) 2006-2008 rubikitch <rubikitch@ruby-lang.org>
  3. ;;;
  4. ;;; Use and distribution subject to the terms of the Ruby license.
  5. (defvar xmpfilter-command-name "ruby -S xmpfilter --dev --fork --detect-rbtest"
  6. "The xmpfilter command name.")
  7. (defvar rct-doc-command-name "ruby -S rct-doc --dev --fork --detect-rbtest"
  8. "The rct-doc command name.")
  9. (defvar rct-complete-command-name "ruby -S rct-complete --dev --fork --detect-rbtest"
  10. "The rct-complete command name.")
  11. (defvar ruby-toggle-file-command-name "ruby -S ruby-toggle-file"
  12. "The ruby-toggle-file command name.")
  13. (defvar rct-fork-command-name "ruby -S rct-fork")
  14. (defvar rct-option-history nil) ;internal
  15. (defvar rct-option-local nil) ;internal
  16. (make-variable-buffer-local 'rct-option-local)
  17. (defvar rct-debug nil
  18. "If non-nil, output debug message into *Messages*.")
  19. ;; (setq rct-debug t)
  20. (defadvice comment-dwim (around rct-hack activate)
  21. "If comment-dwim is successively called, add => mark."
  22. (if (and (eq major-mode 'ruby-mode)
  23. (eq last-command 'comment-dwim)
  24. ;; TODO =>check
  25. )
  26. (insert "=>")
  27. ad-do-it))
  28. ;; To remove this advice.
  29. ;; (progn (ad-disable-advice 'comment-dwim 'around 'rct-hack) (ad-update 'comment-dwim))
  30. (defun rct-current-line ()
  31. "Return the vertical position of point..."
  32. (+ (count-lines (point-min) (point))
  33. (if (= (current-column) 0) 1 0)))
  34. (defun rct-save-position (proc)
  35. "Evaluate proc with saving current-line/current-column/window-start."
  36. (let ((line (rct-current-line))
  37. (col (current-column))
  38. (wstart (window-start)))
  39. (funcall proc)
  40. (goto-char (point-min))
  41. (forward-line (1- line))
  42. (move-to-column col)
  43. (set-window-start (selected-window) wstart)))
  44. (defun rct-interactive ()
  45. "All the rcodetools-related commands with prefix args read rcodetools' common option. And store option into buffer-local variable."
  46. (list
  47. (let ((option (or rct-option-local "")))
  48. (if current-prefix-arg
  49. (setq rct-option-local
  50. (read-from-minibuffer "rcodetools option: " option nil nil 'rct-option-history))
  51. option))))
  52. (defun rct-shell-command (command &optional buffer)
  53. "Replacement for `(shell-command-on-region (point-min) (point-max) command buffer t' because of encoding problem."
  54. (let ((input-rb (concat (make-temp-name "xmptmp-in") ".rb"))
  55. (output-rb (concat (make-temp-name "xmptmp-out") ".rb"))
  56. (coding-system-for-read buffer-file-coding-system))
  57. (write-region (point-min) (point-max) input-rb nil 'nodisp)
  58. (shell-command
  59. (rct-debuglog (format "%s %s > %s" command input-rb output-rb))
  60. t " *rct-error*")
  61. (with-current-buffer (or buffer (current-buffer))
  62. (insert-file-contents output-rb nil nil nil t))
  63. (delete-file input-rb)
  64. (delete-file output-rb)))
  65. (defvar xmpfilter-command-function 'xmpfilter-command)
  66. (defun xmp (&optional option)
  67. "Run xmpfilter for annotation/test/spec on whole buffer.
  68. See also `rct-interactive'. "
  69. (interactive (rct-interactive))
  70. (rct-save-position
  71. (lambda ()
  72. (rct-shell-command (funcall xmpfilter-command-function option)))))
  73. (defun xmpfilter-command (&optional option)
  74. "The xmpfilter command line, DWIM."
  75. (setq option (or option ""))
  76. (flet ((in-block (beg-re)
  77. (save-excursion
  78. (goto-char (point-min))
  79. (when (re-search-forward beg-re nil t)
  80. (let ((s (point)) e)
  81. (when (re-search-forward "^end\n" nil t)
  82. (setq e (point))
  83. (goto-char s)
  84. (re-search-forward "# => *$" e t)))))))
  85. (cond ((in-block "^class.+< Test::Unit::TestCase$")
  86. (format "%s --unittest %s" xmpfilter-command-name option))
  87. ((in-block "^\\(describe\\|context\\).+do$")
  88. (format "%s --spec %s" xmpfilter-command-name option))
  89. (t
  90. (format "%s %s" xmpfilter-command-name option)))))
  91. ;;;; Completion
  92. (defvar rct-method-completion-table nil) ;internal
  93. (defvar rct-complete-symbol-function 'rct-complete-symbol--normal
  94. "Function to use rct-complete-symbol.")
  95. ;; (setq rct-complete-symbol-function 'rct-complete-symbol--icicles)
  96. (defvar rct-use-test-script t
  97. "Whether rct-complete/rct-doc use test scripts.")
  98. (defun rct-complete-symbol (&optional option)
  99. "Perform ruby method and class completion on the text around point.
  100. This command only calls a function according to `rct-complete-symbol-function'.
  101. See also `rct-interactive', `rct-complete-symbol--normal', and `rct-complete-symbol--icicles'."
  102. (interactive (rct-interactive))
  103. (call-interactively rct-complete-symbol-function))
  104. (defun rct-complete-symbol--normal (&optional option)
  105. "Perform ruby method and class completion on the text around point.
  106. See also `rct-interactive'."
  107. (interactive (rct-interactive))
  108. (let ((end (point)) beg
  109. pattern alist
  110. completion)
  111. (setq completion (rct-try-completion)) ; set also pattern / completion
  112. (save-excursion
  113. (search-backward pattern)
  114. (setq beg (point)))
  115. (cond ((eq completion t) ;sole completion
  116. (message "%s" "Sole completion"))
  117. ((null completion) ;no completions
  118. (message "Can't find completion for \"%s\"" pattern)
  119. (ding))
  120. ((not (string= pattern completion)) ;partial completion
  121. (delete-region beg end) ;delete word
  122. (insert completion)
  123. (message ""))
  124. (t
  125. (message "Making completion list...")
  126. (with-output-to-temp-buffer "*Completions*"
  127. (display-completion-list
  128. (all-completions pattern alist)))
  129. (message "Making completion list...%s" "done")))))
  130. ;; (define-key ruby-mode-map "\M-\C-i" 'rct-complete-symbol)
  131. (defun rct-debuglog (logmsg)
  132. "if `rct-debug' is non-nil, output LOGMSG into *Messages*. Returns LOGMSG."
  133. (if rct-debug
  134. (message "%s" logmsg))
  135. logmsg)
  136. (defun rct-exec-and-eval (command opt)
  137. "Execute rct-complete/rct-doc and evaluate the output."
  138. (let ((eval-buffer (get-buffer-create " *rct-eval*")))
  139. ;; copy to temporary buffer to do completion at non-EOL.
  140. (rct-shell-command
  141. (format "%s %s %s --line=%d --column=%d %s"
  142. command opt (or rct-option-local "")
  143. (rct-current-line)
  144. ;; specify column in BYTE
  145. (string-bytes
  146. (encode-coding-string
  147. (buffer-substring (point-at-bol) (point))
  148. buffer-file-coding-system))
  149. (if rct-use-test-script (rct-test-script-option-string) ""))
  150. eval-buffer)
  151. (message "")
  152. (eval (with-current-buffer eval-buffer
  153. (goto-char 1)
  154. (unwind-protect
  155. (read (current-buffer))
  156. (unless rct-debug (kill-buffer eval-buffer)))))))
  157. (defun rct-test-script-option-string ()
  158. (if (null buffer-file-name)
  159. ""
  160. (let ((test-buf (rct-find-test-script-buffer))
  161. (bfn buffer-file-name)
  162. bfn2 t-opt test-filename)
  163. (if (and test-buf
  164. (setq bfn2 (buffer-local-value 'buffer-file-name test-buf))
  165. (file-exists-p bfn2))
  166. ;; pass test script's filename and lineno
  167. (with-current-buffer test-buf
  168. (setq t-opt (format "%s@%s" buffer-file-name (rct-current-line)))
  169. (format "-t %s --filename=%s" t-opt bfn))
  170. ""))))
  171. (require 'cl)
  172. (defun rct-find-test-script-buffer (&optional buffer-list)
  173. "Find the latest used Ruby test script buffer."
  174. (setq buffer-list (or buffer-list (buffer-list)))
  175. (dolist (buf buffer-list)
  176. (with-current-buffer buf
  177. (if (and buffer-file-name (string-match "test.*\.rb$" buffer-file-name))
  178. (return buf)))))
  179. ;; (defun rct-find-test-method (buffer)
  180. ;; "Find test method on point on BUFFER."
  181. ;; (with-current-buffer buffer
  182. ;; (save-excursion
  183. ;; (forward-line 1)
  184. ;; (if (re-search-backward "^ *def *\\(test_[A-Za-z0-9?!_]+\\)" nil t)
  185. ;; (match-string 1)))))
  186. (defun rct-try-completion ()
  187. "Evaluate the output of rct-complete."
  188. (rct-exec-and-eval rct-complete-command-name "--completion-emacs"))
  189. ;;;; TAGS or Ri
  190. (autoload 'ri "ri-ruby" nil t)
  191. (defvar rct-find-tag-if-available t
  192. "If non-nil and the method location is in TAGS, go to the location instead of show documentation.")
  193. (defun rct-ri (&optional option)
  194. "Browse Ri document at the point.
  195. If `rct-find-tag-if-available' is non-nil, search the definition using TAGS.
  196. See also `rct-interactive'. "
  197. (interactive (rct-interactive))
  198. (rct-exec-and-eval
  199. rct-doc-command-name
  200. (concat "--ri-emacs --use-method-analyzer "
  201. (if (buffer-file-name)
  202. (concat "--filename=" (buffer-file-name))
  203. ""))))
  204. (defun rct-find-tag-or-ri (fullname)
  205. (if (not rct-find-tag-if-available)
  206. (ri fullname)
  207. (condition-case err
  208. (let ()
  209. (visit-tags-table-buffer)
  210. (find-tag-in-order (concat "::" fullname) 'search-forward '(tag-exact-match-p) nil "containing" t))
  211. (error
  212. (ri fullname)))))
  213. ;;;;
  214. (defun ruby-toggle-buffer ()
  215. "Open a related file to the current buffer. test<=>impl."
  216. (interactive)
  217. (find-file (shell-command-to-string
  218. (format "%s %s" ruby-toggle-file-command-name buffer-file-name))))
  219. ;;;; rct-fork support
  220. (defun rct-fork (options)
  221. "Run rct-fork.
  222. Rct-fork makes xmpfilter and completion MUCH FASTER because it pre-loads heavy libraries.
  223. When rct-fork is running, the mode-line indicates it to avoid unnecessary run.
  224. To kill rct-fork process, use \\[rct-fork-kill].
  225. "
  226. (interactive (list
  227. (read-string "rct-fork options (-e CODE -I LIBDIR -r LIB): "
  228. (rct-fork-default-options))))
  229. (rct-fork-kill)
  230. (rct-fork-minor-mode 1)
  231. (start-process-shell-command
  232. "rct-fork" "*rct-fork*" rct-fork-command-name options))
  233. (defun rct-fork-default-options ()
  234. "Default options for rct-fork by collecting requires."
  235. (mapconcat
  236. (lambda (lib) (format "-r %s" lib))
  237. (save-excursion
  238. (goto-char (point-min))
  239. (loop while (re-search-forward "\\<require\\> ['\"]\\([^'\"]+\\)['\"]" nil t)
  240. collect (match-string-no-properties 1)))
  241. " "))
  242. (defun rct-fork-kill ()
  243. "Kill rct-fork process invoked by \\[rct-fork]."
  244. (interactive)
  245. (when rct-fork-minor-mode
  246. (rct-fork-minor-mode -1)
  247. (interrupt-process "rct-fork")))
  248. (define-minor-mode rct-fork-minor-mode
  249. "This minor mode is turned on when rct-fork is run.
  250. It is nothing but an indicator."
  251. :lighter " <rct-fork>" :global t)
  252. ;;;; unit tests
  253. (when (and (fboundp 'expectations))
  254. (require 'ruby-mode)
  255. (require 'el-mock nil t)
  256. (expectations
  257. (desc "comment-dwim advice")
  258. (expect "# =>"
  259. (with-temp-buffer
  260. (ruby-mode)
  261. (setq last-command nil)
  262. (call-interactively 'comment-dwim)
  263. (setq last-command 'comment-dwim)
  264. (call-interactively 'comment-dwim)
  265. (buffer-string)))
  266. (expect (regexp "^1 +# =>")
  267. (with-temp-buffer
  268. (ruby-mode)
  269. (insert "1")
  270. (setq last-command nil)
  271. (call-interactively 'comment-dwim)
  272. (setq last-command 'comment-dwim)
  273. (call-interactively 'comment-dwim)
  274. (buffer-string)))
  275. (desc "rct-current-line")
  276. (expect 1
  277. (with-temp-buffer
  278. (rct-current-line)))
  279. (expect 1
  280. (with-temp-buffer
  281. (insert "1")
  282. (rct-current-line)))
  283. (expect 2
  284. (with-temp-buffer
  285. (insert "1\n")
  286. (rct-current-line)))
  287. (expect 2
  288. (with-temp-buffer
  289. (insert "1\n2")
  290. (rct-current-line)))
  291. (desc "rct-save-position")
  292. (expect (mock (set-window-start * 7) => nil)
  293. (stub window-start => 7)
  294. (with-temp-buffer
  295. (insert "abcdef\nghi")
  296. (rct-save-position #'ignore)))
  297. (expect 2
  298. (with-temp-buffer
  299. (stub window-start => 1)
  300. (stub set-window-start => nil)
  301. (insert "abcdef\nghi")
  302. (rct-save-position #'ignore)
  303. (rct-current-line)))
  304. (expect 3
  305. (with-temp-buffer
  306. (stub window-start => 1)
  307. (stub set-window-start => nil)
  308. (insert "abcdef\nghi")
  309. (rct-save-position #'ignore)
  310. (current-column)))
  311. (desc "rct-interactive")
  312. (expect '("read")
  313. (let ((current-prefix-arg t))
  314. (stub read-from-minibuffer => "read")
  315. (rct-interactive)))
  316. (expect '("-S ruby19")
  317. (let ((current-prefix-arg nil)
  318. (rct-option-local "-S ruby19"))
  319. (stub read-from-minibuffer => "read")
  320. (rct-interactive)))
  321. (expect '("")
  322. (let ((current-prefix-arg nil)
  323. (rct-option-local))
  324. (stub read-from-minibuffer => "read")
  325. (rct-interactive)))
  326. (desc "rct-shell-command")
  327. (expect "1+1 # => 2\n"
  328. (with-temp-buffer
  329. (insert "1+1 # =>\n")
  330. (rct-shell-command "xmpfilter")
  331. (buffer-string)))
  332. (desc "xmp")
  333. (desc "xmpfilter-command")
  334. (expect "xmpfilter --rails"
  335. (let ((xmpfilter-command-name "xmpfilter"))
  336. (with-temp-buffer
  337. (insert "class TestFoo < Test::Unit::TestCase\n")
  338. (xmpfilter-command "--rails"))))
  339. (expect "xmpfilter "
  340. (let ((xmpfilter-command-name "xmpfilter"))
  341. (with-temp-buffer
  342. (insert "context 'foo' do\n")
  343. (xmpfilter-command))))
  344. (expect "xmpfilter "
  345. (let ((xmpfilter-command-name "xmpfilter"))
  346. (with-temp-buffer
  347. (insert "describe Array do\n")
  348. (xmpfilter-command))))
  349. (expect "xmpfilter --unittest --rails"
  350. (let ((xmpfilter-command-name "xmpfilter"))
  351. (with-temp-buffer
  352. (insert "class TestFoo < Test::Unit::TestCase\n"
  353. " def test_0\n"
  354. " 1 + 1 # =>\n"
  355. " end\n"
  356. "end\n")
  357. (xmpfilter-command "--rails"))))
  358. (expect "xmpfilter --spec "
  359. (let ((xmpfilter-command-name "xmpfilter"))
  360. (with-temp-buffer
  361. (insert "context 'foo' do\n"
  362. " specify \"foo\" do\n"
  363. " 1 + 1 # =>\n"
  364. " end\n"
  365. "end\n")
  366. (xmpfilter-command))))
  367. (expect "xmpfilter --spec "
  368. (let ((xmpfilter-command-name "xmpfilter"))
  369. (with-temp-buffer
  370. (insert "describe Array do\n"
  371. " it \"foo\" do\n"
  372. " [1] + [1] # =>\n"
  373. " end\n"
  374. "end\n")
  375. (xmpfilter-command))))
  376. (expect "xmpfilter "
  377. (let ((xmpfilter-command-name "xmpfilter"))
  378. (with-temp-buffer
  379. (insert "1 + 2\n")
  380. (xmpfilter-command))))
  381. (desc "rct-fork")
  382. (expect t
  383. (stub start-process-shell-command => t)
  384. (stub interrupt-process => t)
  385. (rct-fork "-r activesupport")
  386. rct-fork-minor-mode)
  387. (expect nil
  388. (stub start-process-shell-command => t)
  389. (stub interrupt-process => t)
  390. (rct-fork "-r activesupport")
  391. (rct-fork-kill)
  392. rct-fork-minor-mode)
  393. ))
  394. (provide 'rcodetools)