1 ;;; fuel-refactor.el -- code refactoring support
3 ;; Copyright (C) 2009 Jose Antonio Ortega Ruiz
4 ;; See http://factorcode.org/license.txt for BSD license.
6 ;; Author: Jose Antonio Ortega Ruiz <jao@gnu.org>
7 ;; Keywords: languages, fuel, factor
8 ;; Start date: Thu Jan 08, 2009 00:57
12 ;; Utilities performing refactoring on factor code.
16 (require 'fuel-scaffold
)
18 (require 'fuel-syntax
)
24 ;;; Word definitions in buffer
26 (defconst fuel-refactor--next-defun-regex
27 (format "^\\(:\\|MEMO:\\|MACRO:\\):? +\\(\\w+\\)\\(%s\\)\\([^;]+?\\) ;\\_>"
28 fuel-syntax--stack-effect-regex
))
30 (defun fuel-refactor--previous-defun ()
32 (while (and (not result
)
33 (setq pos
(fuel-syntax--beginning-of-defun)))
34 (setq result
(looking-at fuel-refactor--next-defun-regex
)))
35 (when (and result pos
)
36 (let ((name (match-string-no-properties 2))
37 (body (match-string-no-properties 4))
39 (list (split-string body nil t
) name pos end
)))))
41 (defun fuel-refactor--find (code to
)
42 (let ((candidate) (result))
43 (while (and (not result
)
44 (setq candidate
(fuel-refactor--previous-defun))
46 (when (equal (car candidate
) code
)
47 (setq result
(cdr candidate
))))
50 (defun fuel-refactor--reuse-p (word)
53 (move-overlay fuel-stack--overlay
(1+ (point)) (mark))
55 (and (y-or-n-p (format "Use existing word '%s'? " word
)) word
)
56 (delete-overlay fuel-stack--overlay
))))
58 (defun fuel-refactor--code-rx (code)
59 (let ((words (split-string code nil t
)))
60 (mapconcat 'regexp-quote words
"[ \n\f\r]+")))
65 (defun fuel-refactor--reuse-existing (code)
68 (let ((code (split-string (substring-no-properties code
) nil t
))
72 (while (and (not result
)
73 (setq found
(fuel-refactor--find code
(point-min))))
74 (when found
(setq result
(fuel-refactor--reuse-p (car found
)))))
75 (goto-char (point-max))
76 (while (and (not result
)
77 (setq found
(fuel-refactor--find code down
)))
78 (when found
(setq result
(fuel-refactor--reuse-p (car found
)))))
81 (defun fuel-refactor--insert-word (word stack-effect code
)
82 (let ((beg (save-excursion (fuel-syntax--beginning-of-defun) (point)))
84 (re-search-backward fuel-syntax--end-of-def-regex nil t
)
86 (skip-syntax-forward "-"))))
87 (let ((start (goto-char (max beg end
))))
89 (insert ": " word
" " stack-effect
"\n" code
" ;\n")
90 (indent-region start
(point))
91 (move-overlay fuel-stack--overlay start
(point)))))
93 (defun fuel-refactor--extract-other (start end code
)
95 (when (y-or-n-p "Apply refactoring to rest of buffer? ")
97 (let ((rx (fuel-refactor--code-rx code
))
99 (query-replace-regexp rx word t
(point-min) start
)
100 (query-replace-regexp rx word t end
(point-max)))))
101 (delete-overlay fuel-stack--overlay
)))
103 (defun fuel-refactor--extract (begin end
)
104 (unless (< begin end
) (error "No proper region to extract"))
105 (let* ((code (buffer-substring begin end
))
106 (existing (fuel-refactor--reuse-existing code
))
107 (code-str (or existing
(fuel--region-to-string begin end
)))
108 (word (or (car existing
) (read-string "New word name: ")))
109 (stack-effect (or existing
110 (fuel-stack--infer-effect code-str
)
111 (read-string "Stack effect: "))))
113 (delete-region begin end
)
115 (indent-region begin
(point))
117 (let ((start (or (cadr existing
) (point))))
119 (fuel-refactor--insert-word word stack-effect code
))
120 (fuel-refactor--extract-other start
121 (or (car (cddr existing
)) (point))
124 (defun fuel-refactor-extract-region (begin end
)
125 "Extracts current region as a separate word."
127 (let ((begin (save-excursion
129 (when (zerop (skip-syntax-backward "w"))
130 (skip-syntax-forward "-"))
134 (skip-syntax-forward "w")
136 (fuel-refactor--extract begin end
)))
138 (defun fuel-refactor-extract-sexp ()
139 "Extracts current innermost sexp (up to point) as a separate
142 (fuel-refactor-extract-region (1+ (fuel-syntax--beginning-of-sexp-pos))
143 (if (looking-at-p ";") (point)
144 (fuel-syntax--end-of-symbol-pos))))
149 (defun fuel-refactor--word-def (word)
150 (let ((def (fuel-eval--retort-result
151 (fuel-eval--send/wait
`(:fuel
* (,word fuel-word-def
) "fuel")))))
153 (substring (substring def
2) 0 -
2))))
155 (defun fuel-refactor-inline-word ()
156 "Inserts definition of word at point."
158 (let ((word (fuel-syntax-symbol-at-point)))
159 (unless word
(error "No word at point"))
160 (let ((code (fuel-refactor--word-def word
)))
161 (unless code
(error "Word's definition not found"))
162 (fuel-syntax--beginning-of-symbol)
164 (let ((start (point)))
166 (save-excursion (font-lock-fontify-region start
(point)))
167 (indent-region start
(point))))))
172 (defsubst fuel-refactor--rename-word
(from to file
)
173 (let ((files (fuel-xref--word-callers-files from
)))
174 (tags-query-replace from to t
`(cons ,file
',files
))
177 (defun fuel-refactor--def-word ()
179 (fuel-syntax--beginning-of-defun)
180 (or (and (looking-at fuel-syntax--method-definition-regex
)
181 (match-string-no-properties 2))
182 (and (looking-at fuel-syntax--word-definition-regex
)
183 (match-string-no-properties 2)))))
185 (defun fuel-refactor-rename-word (&optional arg
)
186 "Rename globally the word whose definition point is at.
187 With prefix argument, use word at point instead."
189 (let* ((from (if arg
(fuel-syntax-symbol-at-point) (fuel-refactor--def-word)))
190 (from (read-string "Rename word: " from
))
191 (to (read-string (format "Rename '%s' to: " from
)))
192 (buffer (current-buffer)))
193 (fuel-refactor--rename-word from to
(buffer-file-name))))
198 (defun fuel-refactor--insert-using (vocab)
200 (goto-char (point-min))
201 (let ((usings (sort (cons vocab
(fuel-syntax--usings)) 'string
<)))
202 (fuel-debug--replace-usings (buffer-file-name) usings
))))
204 (defun fuel-refactor--vocab-root (vocab)
205 (let ((cmd `(:fuel
* (,vocab fuel-scaffold-get-root
) "fuel")))
206 (fuel-eval--retort-result (fuel-eval--send/wait cmd
))))
208 (defun fuel-refactor--extract-vocab (begin end
)
210 (let* ((str (buffer-substring begin end
))
211 (buffer (current-buffer))
212 (vocab (fuel-syntax--current-vocab))
213 (vocab-hint (and vocab
(format "%s." vocab
)))
214 (root-hint (fuel-refactor--vocab-root vocab
))
215 (vocab (fuel-scaffold-vocab t vocab-hint root-hint
)))
216 (with-current-buffer buffer
217 (delete-region begin end
)
218 (fuel-refactor--insert-using vocab
))
223 (fuel-update-usings))))
225 (defun fuel-refactor-extract-vocab (begin end
)
226 "Creates a new vocab with the words in current region.
227 The region is extended to the closest definition boundaries."
229 (fuel-refactor--extract-vocab (save-excursion (goto-char begin
)
232 (save-excursion (goto-char end
)
236 (provide 'fuel-refactor
)
237 ;;; fuel-refactor.el ends here