add using
[factor/jcg.git] / misc / fuel / factor-mode.el
blobba9be2edd3e727e8e0c3b876f420188fc973c49a
1 ;;; factor-mode.el -- mode for editing Factor source
3 ;; Copyright (C) 2008, 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: Tue Dec 02, 2008 21:32
10 ;;; Comentary:
12 ;; Definition of factor-mode, a major Emacs for editing Factor source
13 ;; code.
15 ;;; Code:
17 (require 'fuel-base)
18 (require 'fuel-syntax)
19 (require 'fuel-font-lock)
21 (require 'ring)
24 ;;; Customization:
26 (defgroup factor-mode nil
27 "Major mode for Factor source code."
28 :group 'fuel
29 :group 'languages)
31 (defcustom factor-mode-cycle-always-ask-p t
32 "Whether to always ask for file creation when cycling to a
33 source/docs/tests file.
35 When set to false, you'll be asked only once."
36 :type 'boolean
37 :group 'factor-mode)
39 (defcustom factor-mode-use-fuel t
40 "Whether to use the full FUEL facilities in factor mode.
42 Set this variable to nil if you just want to use Emacs as the
43 external editor of your Factor environment, e.g., by putting
44 these lines in your .emacs:
46 (add-to-list 'load-path \"/path/to/factor/misc/fuel\")
47 (setq factor-mode-use-fuel nil)
48 (require 'factor-mode)
50 :type 'boolean
51 :group 'factor-mode)
53 (defcustom factor-mode-default-indent-width 4
54 "Default indentation width for factor-mode.
56 This value will be used for the local variable
57 `factor-mode-indent-width' in new factor buffers. For existing
58 code, we first check if `factor-mode-indent-width' is set
59 explicitly in a local variable section or line (e.g.
60 '! -*- factor-mode-indent-witdth: 2 -*-'). If that's not the case,
61 `factor-mode' tries to infer its correct value from the existing
62 code in the buffer."
63 :type 'integer
64 :group 'fuel)
66 (defcustom factor-mode-hook nil
67 "Hook run when entering Factor mode."
68 :type 'hook
69 :group 'factor-mode)
72 ;;; Syntax table:
74 (defun factor-mode--syntax-setup ()
75 (set-syntax-table fuel-syntax--syntax-table)
76 (set (make-local-variable 'beginning-of-defun-function)
77 'fuel-syntax--beginning-of-defun)
78 (set (make-local-variable 'end-of-defun-function) 'fuel-syntax--end-of-defun)
79 (set (make-local-variable 'open-paren-in-column-0-is-defun-start) nil))
82 ;;; Indentation:
84 (make-variable-buffer-local
85 (defvar factor-mode-indent-width factor-mode-default-indent-width
86 "Indentation width in factor buffers. A local variable."))
88 (defun factor-mode--guess-indent-width ()
89 "Chooses an indentation value from existing code."
90 (let ((word-cont "^ +[^ ]")
91 (iw))
92 (save-excursion
93 (beginning-of-buffer)
94 (while (not iw)
95 (if (not (re-search-forward fuel-syntax--definition-start-regex nil t))
96 (setq iw factor-mode-default-indent-width)
97 (forward-line)
98 (when (looking-at word-cont)
99 (setq iw (current-indentation))))))
100 iw))
102 (defun factor-mode--indent-in-brackets ()
103 (save-excursion
104 (beginning-of-line)
105 (when (> (fuel-syntax--brackets-depth) 0)
106 (let* ((bs (fuel-syntax--brackets-start))
107 (be (fuel-syntax--brackets-end))
108 (ln (line-number-at-pos)))
109 (when (> ln (line-number-at-pos bs))
110 (cond ((and (> be 0)
111 (= (- be (point)) (current-indentation))
112 (= ln (line-number-at-pos be)))
113 (fuel-syntax--indentation-at bs))
114 ((or (fuel-syntax--is-last-char bs)
115 (not (eq ?\ (char-after (1+ bs)))))
116 (fuel-syntax--increased-indentation
117 (fuel-syntax--indentation-at bs)))
118 (t (+ 2 (fuel-syntax--line-offset bs)))))))))
120 (defun factor-mode--indent-definition ()
121 (save-excursion
122 (beginning-of-line)
123 (when (fuel-syntax--at-begin-of-def) 0)))
125 (defun factor-mode--indent-setter-line ()
126 (when (fuel-syntax--at-setter-line)
127 (save-excursion
128 (let ((indent (and (fuel-syntax--at-constructor-line) (current-indentation))))
129 (while (not (or indent
130 (bobp)
131 (fuel-syntax--at-begin-of-def)
132 (fuel-syntax--at-end-of-def)))
133 (if (fuel-syntax--at-constructor-line)
134 (setq indent (fuel-syntax--increased-indentation))
135 (forward-line -1)))
136 indent))))
138 (defun factor-mode--indent-continuation ()
139 (save-excursion
140 (forward-line -1)
141 (while (and (not (bobp))
142 (fuel-syntax--looking-at-emptiness))
143 (forward-line -1))
144 (cond ((or (fuel-syntax--at-end-of-def)
145 (fuel-syntax--at-setter-line))
146 (fuel-syntax--decreased-indentation))
147 ((fuel-syntax--at-begin-of-indent-def)
148 (fuel-syntax--increased-indentation))
149 (t (current-indentation)))))
151 (defun factor-mode--calculate-indentation ()
152 "Calculate Factor indentation for line at point."
153 (or (and (bobp) 0)
154 (factor-mode--indent-definition)
155 (factor-mode--indent-in-brackets)
156 (factor-mode--indent-setter-line)
157 (factor-mode--indent-continuation)
160 (defun factor-mode--indent-line ()
161 "Indent current line as Factor code"
162 (let ((target (factor-mode--calculate-indentation))
163 (pos (- (point-max) (point))))
164 (if (= target (current-indentation))
165 (if (< (current-column) (current-indentation))
166 (back-to-indentation))
167 (beginning-of-line)
168 (delete-horizontal-space)
169 (indent-to target)
170 (if (> (- (point-max) pos) (point))
171 (goto-char (- (point-max) pos))))))
173 (defun factor-mode--indentation-setup ()
174 (set (make-local-variable 'indent-line-function) 'factor-mode--indent-line)
175 (setq factor-indent-width (factor-mode--guess-indent-width))
176 (setq indent-tabs-mode nil))
179 ;;; Buffer cycling:
181 (defconst factor-mode--cycle-endings
182 '(".factor" "-tests.factor" "-docs.factor"))
184 (make-local-variable
185 (defvar factor-mode--cycling-no-ask nil))
187 (defvar factor-mode--cycle-ring
188 (let ((ring (make-ring (length factor-mode--cycle-endings))))
189 (dolist (e factor-mode--cycle-endings ring)
190 (ring-insert ring e))
191 ring))
193 (defconst factor-mode--cycle-basename-regex
194 (format "\\(.+?\\)\\(%s\\)$" (regexp-opt factor-mode--cycle-endings)))
196 (defun factor-mode--cycle-split (basename)
197 (when (string-match factor-mode--cycle-basename-regex basename)
198 (cons (match-string 1 basename) (match-string 2 basename))))
200 (defun factor-mode--cycle-next (file)
201 (let* ((dir (file-name-directory file))
202 (basename (file-name-nondirectory file))
203 (p/s (factor-mode--cycle-split basename))
204 (prefix (car p/s))
205 (ring factor-mode--cycle-ring)
206 (idx (or (ring-member ring (cdr p/s)) 0))
207 (len (ring-size ring))
208 (i 1)
209 (result nil))
210 (while (and (< i len) (not result))
211 (let* ((suffix (ring-ref ring (+ i idx)))
212 (path (expand-file-name (concat prefix suffix) dir)))
213 (when (or (file-exists-p path)
214 (and (not (member suffix factor-mode--cycling-no-ask))
215 (y-or-n-p (format "Create %s? " path))))
216 (setq result path))
217 (when (and (not factor-mode-cycle-always-ask-p)
218 (not (member suffix factor-mode--cycling-no-ask)))
219 (setq factor-mode--cycling-no-ask
220 (cons name factor-mode--cycling-no-ask))))
221 (setq i (1+ i)))
222 result))
224 (defsubst factor-mode--cycling-setup ()
225 (setq factor-mode--cycling-no-ask nil))
227 (defun factor-mode-visit-other-file (&optional file)
228 "Cycle between code, tests and docs factor files."
229 (interactive)
230 (let ((file (factor-mode--cycle-next (or file (buffer-file-name)))))
231 (unless file (error "No other file found"))
232 (find-file file)
233 (unless (file-exists-p file)
234 (set-buffer-modified-p t)
235 (save-buffer))))
238 ;;; Keymap:
240 (defun factor-mode--insert-and-indent (n)
241 (interactive "*p")
242 (let ((start (point)))
243 (self-insert-command n)
244 (save-excursion (font-lock-fontify-region start (point))))
245 (indent-according-to-mode))
247 (defvar factor-mode-map
248 (let ((map (make-sparse-keymap)))
249 (define-key map [?\]] 'factor-mode--insert-and-indent)
250 (define-key map [?}] 'factor-mode--insert-and-indent)
251 (define-key map "\C-m" 'newline-and-indent)
252 (define-key map "\C-co" 'factor-mode-visit-other-file)
253 (define-key map "\C-c\C-o" 'factor-mode-visit-other-file)
254 map))
256 (defun factor-mode--keymap-setup ()
257 (use-local-map factor-mode-map))
260 ;;; Factor mode:
262 ;;;###autoload
263 (defun factor-mode ()
264 "A mode for editing programs written in the Factor programming language.
265 \\{factor-mode-map}"
266 (interactive)
267 (kill-all-local-variables)
268 (setq major-mode 'factor-mode)
269 (setq mode-name "Factor")
270 (fuel-font-lock--font-lock-setup)
271 (factor-mode--keymap-setup)
272 (factor-mode--indentation-setup)
273 (factor-mode--syntax-setup)
274 (factor-mode--cycling-setup)
275 (when factor-mode-use-fuel (require 'fuel-mode) (fuel-mode))
276 (run-hooks 'factor-mode-hook))
279 (provide 'factor-mode)
280 ;;; factor-mode.el ends here