]> gitweb.factorcode.org Git - factor.git/blob - misc/fuel/factor-mode.el
Merge branch 'master' into experimental (untested!)
[factor.git] / misc / fuel / factor-mode.el
1 ;;; factor-mode.el -- mode for editing Factor source
2
3 ;; Copyright (C) 2008 Jose Antonio Ortega Ruiz
4 ;; See http://factorcode.org/license.txt for BSD license.
5
6 ;; Author: Jose Antonio Ortega Ruiz <jao@gnu.org>
7 ;; Keywords: languages, fuel, factor
8 ;; Start date: Tue Dec 02, 2008 21:32
9
10 ;;; Comentary:
11
12 ;; Definition of factor-mode, a major Emacs for editing Factor source
13 ;; code.
14
15 ;;; Code:
16
17 (require 'fuel-base)
18 (require 'fuel-syntax)
19 (require 'fuel-font-lock)
20
21 (require 'ring)
22
23 \f
24 ;;; Customization:
25
26 (defgroup factor-mode nil
27   "Major mode for Factor source code"
28   :group 'fuel)
29
30 (defcustom factor-mode-use-fuel t
31   "Whether to use the full FUEL facilities in factor mode.
32
33 Set this variable to nil if you just want to use Emacs as the
34 external editor of your Factor environment, e.g., by putting
35 these lines in your .emacs:
36
37   (add-to-list 'load-path \"/path/to/factor/misc/fuel\")
38   (setq factor-mode-use-fuel nil)
39   (require 'factor-mode)
40 "
41   :type 'boolean
42   :group 'factor-mode)
43
44 (defcustom factor-mode-default-indent-width 4
45   "Default indentation width for factor-mode.
46
47 This value will be used for the local variable
48 `factor-mode-indent-width' in new factor buffers. For existing
49 code, we first check if `factor-mode-indent-width' is set
50 explicitly in a local variable section or line (e.g.
51 '! -*- factor-mode-indent-witdth: 2 -*-'). If that's not the case,
52 `factor-mode' tries to infer its correct value from the existing
53 code in the buffer."
54   :type 'integer
55   :group 'fuel)
56
57 (defcustom factor-mode-hook nil
58   "Hook run when entering Factor mode."
59   :type 'hook
60   :group 'factor-mode)
61
62 \f
63 ;;; Faces:
64
65 (fuel-font-lock--define-faces
66  factor-font-lock font-lock factor-mode
67  ((comment comment "comments")
68   (constructor type  "constructors (<foo>)")
69   (declaration keyword "declaration words")
70   (parsing-word keyword  "parsing words")
71   (setter-word function-name "setter words (>>foo)")
72   (stack-effect comment "stack effect specifications")
73   (string string "strings")
74   (symbol variable-name "name of symbol being defined")
75   (type-name type "type names")
76   (vocabulary-name constant "vocabulary names")
77   (word function-name "word, generic or method being defined")))
78
79 \f
80 ;;; Syntax table:
81
82 (defun factor-mode--syntax-setup ()
83   (set-syntax-table fuel-syntax--syntax-table)
84   (set (make-local-variable 'beginning-of-defun-function)
85        'fuel-syntax--beginning-of-defun)
86   (set (make-local-variable 'end-of-defun-function) 'fuel-syntax--end-of-defun)
87   (set (make-local-variable 'open-paren-in-column-0-is-defun-start) nil)
88   (fuel-syntax--enable-usings))
89
90 \f
91 ;;; Indentation:
92
93 (make-variable-buffer-local
94  (defvar factor-mode-indent-width factor-mode-default-indent-width
95    "Indentation width in factor buffers. A local variable."))
96
97 (defun factor-mode--guess-indent-width ()
98   "Chooses an indentation value from existing code."
99   (let ((word-cont "^ +[^ ]")
100         (iw))
101     (save-excursion
102       (beginning-of-buffer)
103       (while (not iw)
104         (if (not (re-search-forward fuel-syntax--definition-start-regex nil t))
105             (setq iw factor-mode-default-indent-width)
106           (forward-line)
107           (when (looking-at word-cont)
108             (setq iw (current-indentation))))))
109     iw))
110
111 (defun factor-mode--indent-in-brackets ()
112   (save-excursion
113     (beginning-of-line)
114     (when (> (fuel-syntax--brackets-depth) 0)
115       (let ((op (fuel-syntax--brackets-start))
116             (cl (fuel-syntax--brackets-end))
117             (ln (line-number-at-pos)))
118         (when (> ln (line-number-at-pos op))
119           (if (and (> cl 0) (= ln (line-number-at-pos cl)))
120               (fuel-syntax--indentation-at op)
121             (fuel-syntax--increased-indentation (fuel-syntax--indentation-at op))))))))
122
123 (defun factor-mode--indent-definition ()
124   (save-excursion
125     (beginning-of-line)
126     (when (fuel-syntax--at-begin-of-def) 0)))
127
128 (defun factor-mode--indent-setter-line ()
129   (when (fuel-syntax--at-setter-line)
130     (save-excursion
131       (let ((indent (and (fuel-syntax--at-constructor-line) (current-indentation))))
132         (while (not (or indent
133                         (bobp)
134                         (fuel-syntax--at-begin-of-def)
135                         (fuel-syntax--at-end-of-def)))
136           (if (fuel-syntax--at-constructor-line)
137               (setq indent (fuel-syntax--increased-indentation))
138             (forward-line -1)))
139         indent))))
140
141 (defun factor-mode--indent-continuation ()
142   (save-excursion
143     (forward-line -1)
144     (while (and (not (bobp))
145                 (fuel-syntax--looking-at-emptiness))
146       (forward-line -1))
147     (cond ((or (fuel-syntax--at-end-of-def)
148                (fuel-syntax--at-setter-line))
149            (fuel-syntax--decreased-indentation))
150           ((and (fuel-syntax--at-begin-of-def)
151                 (not (fuel-syntax--at-using)))
152            (fuel-syntax--increased-indentation))
153           (t (current-indentation)))))
154
155 (defun factor-mode--calculate-indentation ()
156   "Calculate Factor indentation for line at point."
157   (or (and (bobp) 0)
158       (factor-mode--indent-definition)
159       (factor-mode--indent-in-brackets)
160       (factor-mode--indent-setter-line)
161       (factor-mode--indent-continuation)
162       0))
163
164 (defun factor-mode--indent-line ()
165   "Indent current line as Factor code"
166   (let ((target (factor-mode--calculate-indentation))
167         (pos (- (point-max) (point))))
168     (if (= target (current-indentation))
169         (if (< (current-column) (current-indentation))
170             (back-to-indentation))
171       (beginning-of-line)
172       (delete-horizontal-space)
173       (indent-to target)
174       (if (> (- (point-max) pos) (point))
175           (goto-char (- (point-max) pos))))))
176
177 (defun factor-mode--indentation-setup ()
178   (set (make-local-variable 'indent-line-function) 'factor-mode--indent-line)
179   (setq factor-indent-width (factor-mode--guess-indent-width))
180   (setq indent-tabs-mode nil))
181
182 \f
183 ;;; Buffer cycling:
184
185 (defconst factor-mode--cycle-endings
186   '(".factor" "-tests.factor" "-docs.factor"))
187
188 (defconst factor-mode--regex-cycle-endings
189   (format "\\(.*?\\)\\(%s\\)$"
190           (regexp-opt factor-mode--cycle-endings)))
191
192 (defconst factor-mode--cycle-endings-ring
193   (let ((ring (make-ring (length factor-mode--cycle-endings))))
194     (dolist (e factor-mode--cycle-endings ring)
195       (ring-insert ring e))))
196
197 (defun factor-mode--cycle-next (file)
198   (let* ((match (string-match factor-mode--regex-cycle-endings file))
199          (base (and match (match-string-no-properties 1 file)))
200          (ending (and match (match-string-no-properties 2 file)))
201          (idx (and ending (ring-member factor-mode--cycle-endings-ring ending)))
202          (gfl (lambda (i) (concat base (ring-ref factor-mode--cycle-endings-ring i)))))
203     (if (not idx) file
204       (let ((l (length factor-mode--cycle-endings)) (i 1) next)
205         (while (and (not next) (< i l))
206           (when (file-exists-p (funcall gfl (+ idx i)))
207             (setq next (+ idx i)))
208           (setq i (1+ i)))
209         (funcall gfl (or next idx))))))
210
211 (defun factor-mode-visit-other-file (&optional file)
212   "Cycle between code, tests and docs factor files."
213   (interactive)
214   (find-file (factor-mode--cycle-next (or file (buffer-file-name)))))
215
216 \f
217 ;;; Keymap:
218
219 (defun factor-mode-insert-and-indent (n)
220   (interactive "p")
221   (self-insert-command n)
222   (indent-for-tab-command))
223
224 (defvar factor-mode-map
225   (let ((map (make-sparse-keymap)))
226     (define-key map [?\]] 'factor-mode-insert-and-indent)
227     (define-key map [?}] 'factor-mode-insert-and-indent)
228     (define-key map "\C-m" 'newline-and-indent)
229     (define-key map "\C-co" 'factor-mode-visit-other-file)
230     (define-key map "\C-c\C-o" 'factor-mode-visit-other-file)
231     map))
232
233 (defun factor-mode--keymap-setup ()
234   (use-local-map factor-mode-map))
235
236 \f
237 ;;; Factor mode:
238
239 ;;;###autoload
240 (defun factor-mode ()
241   "A mode for editing programs written in the Factor programming language.
242 \\{factor-mode-map}"
243   (interactive)
244   (kill-all-local-variables)
245   (setq major-mode 'factor-mode)
246   (setq mode-name "Factor")
247   (fuel-font-lock--font-lock-setup)
248   (factor-mode--keymap-setup)
249   (factor-mode--indentation-setup)
250   (factor-mode--syntax-setup)
251   (when factor-mode-use-fuel (require 'fuel-mode) (fuel-mode))
252   (run-hooks 'factor-mode-hook))
253
254 \f
255 (provide 'factor-mode)
256 ;;; factor-mode.el ends here