]> gitweb.factorcode.org Git - factor.git/blob - misc/fuel/factor-mode.el
Conflict resolution
[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 ;;; Syntax table:
64
65 (defun factor-mode--syntax-setup ()
66   (set-syntax-table fuel-syntax--syntax-table)
67   (set (make-local-variable 'beginning-of-defun-function)
68        'fuel-syntax--beginning-of-defun)
69   (set (make-local-variable 'end-of-defun-function) 'fuel-syntax--end-of-defun)
70   (set (make-local-variable 'open-paren-in-column-0-is-defun-start) nil))
71
72 \f
73 ;;; Indentation:
74
75 (make-variable-buffer-local
76  (defvar factor-mode-indent-width factor-mode-default-indent-width
77    "Indentation width in factor buffers. A local variable."))
78
79 (defun factor-mode--guess-indent-width ()
80   "Chooses an indentation value from existing code."
81   (let ((word-cont "^ +[^ ]")
82         (iw))
83     (save-excursion
84       (beginning-of-buffer)
85       (while (not iw)
86         (if (not (re-search-forward fuel-syntax--definition-start-regex nil t))
87             (setq iw factor-mode-default-indent-width)
88           (forward-line)
89           (when (looking-at word-cont)
90             (setq iw (current-indentation))))))
91     iw))
92
93 (defun factor-mode--indent-in-brackets ()
94   (save-excursion
95     (beginning-of-line)
96     (when (> (fuel-syntax--brackets-depth) 0)
97       (let* ((op (fuel-syntax--brackets-start))
98              (cl (fuel-syntax--brackets-end))
99              (ln (line-number-at-pos))
100              (iop (fuel-syntax--indentation-at op)))
101         (when (> ln (line-number-at-pos op))
102           (if (and (> cl 0)
103                    (= (- cl (point)) (current-indentation))
104                    (= ln (line-number-at-pos cl)))
105               iop
106             (fuel-syntax--increased-indentation iop)))))))
107
108 (defun factor-mode--indent-definition ()
109   (save-excursion
110     (beginning-of-line)
111     (when (fuel-syntax--at-begin-of-def) 0)))
112
113 (defun factor-mode--indent-setter-line ()
114   (when (fuel-syntax--at-setter-line)
115     (save-excursion
116       (let ((indent (and (fuel-syntax--at-constructor-line) (current-indentation))))
117         (while (not (or indent
118                         (bobp)
119                         (fuel-syntax--at-begin-of-def)
120                         (fuel-syntax--at-end-of-def)))
121           (if (fuel-syntax--at-constructor-line)
122               (setq indent (fuel-syntax--increased-indentation))
123             (forward-line -1)))
124         indent))))
125
126 (defun factor-mode--indent-continuation ()
127   (save-excursion
128     (forward-line -1)
129     (while (and (not (bobp))
130                 (fuel-syntax--looking-at-emptiness))
131       (forward-line -1))
132     (cond ((or (fuel-syntax--at-end-of-def)
133                (fuel-syntax--at-setter-line))
134            (fuel-syntax--decreased-indentation))
135           ((and (fuel-syntax--at-begin-of-def)
136                 (not (fuel-syntax--at-using)))
137            (fuel-syntax--increased-indentation))
138           (t (current-indentation)))))
139
140 (defun factor-mode--calculate-indentation ()
141   "Calculate Factor indentation for line at point."
142   (or (and (bobp) 0)
143       (factor-mode--indent-definition)
144       (factor-mode--indent-in-brackets)
145       (factor-mode--indent-setter-line)
146       (factor-mode--indent-continuation)
147       0))
148
149 (defun factor-mode--indent-line ()
150   "Indent current line as Factor code"
151   (let ((target (factor-mode--calculate-indentation))
152         (pos (- (point-max) (point))))
153     (if (= target (current-indentation))
154         (if (< (current-column) (current-indentation))
155             (back-to-indentation))
156       (beginning-of-line)
157       (delete-horizontal-space)
158       (indent-to target)
159       (if (> (- (point-max) pos) (point))
160           (goto-char (- (point-max) pos))))))
161
162 (defun factor-mode--indentation-setup ()
163   (set (make-local-variable 'indent-line-function) 'factor-mode--indent-line)
164   (setq factor-indent-width (factor-mode--guess-indent-width))
165   (setq indent-tabs-mode nil))
166
167 \f
168 ;;; Buffer cycling:
169
170 (defconst factor-mode--cycle-endings
171   '(".factor" "-tests.factor" "-docs.factor"))
172
173 (defconst factor-mode--regex-cycle-endings
174   (format "\\(.*?\\)\\(%s\\)$"
175           (regexp-opt factor-mode--cycle-endings)))
176
177 (defconst factor-mode--cycle-endings-ring
178   (let ((ring (make-ring (length factor-mode--cycle-endings))))
179     (dolist (e factor-mode--cycle-endings ring)
180       (ring-insert ring e))))
181
182 (defun factor-mode--cycle-next (file)
183   (let* ((match (string-match factor-mode--regex-cycle-endings file))
184          (base (and match (match-string-no-properties 1 file)))
185          (ending (and match (match-string-no-properties 2 file)))
186          (idx (and ending (ring-member factor-mode--cycle-endings-ring ending)))
187          (gfl (lambda (i) (concat base (ring-ref factor-mode--cycle-endings-ring i)))))
188     (if (not idx) file
189       (let ((l (length factor-mode--cycle-endings)) (i 1) next)
190         (while (and (not next) (< i l))
191           (when (file-exists-p (funcall gfl (+ idx i)))
192             (setq next (+ idx i)))
193           (setq i (1+ i)))
194         (funcall gfl (or next idx))))))
195
196 (defun factor-mode-visit-other-file (&optional file)
197   "Cycle between code, tests and docs factor files."
198   (interactive)
199   (find-file (factor-mode--cycle-next (or file (buffer-file-name)))))
200
201 \f
202 ;;; Keymap:
203
204 (defun factor-mode-insert-and-indent (n)
205   (interactive "p")
206   (self-insert-command n)
207   (indent-for-tab-command))
208
209 (defvar factor-mode-map
210   (let ((map (make-sparse-keymap)))
211     (define-key map [?\]] 'factor-mode-insert-and-indent)
212     (define-key map [?}] 'factor-mode-insert-and-indent)
213     (define-key map "\C-m" 'newline-and-indent)
214     (define-key map "\C-co" 'factor-mode-visit-other-file)
215     (define-key map "\C-c\C-o" 'factor-mode-visit-other-file)
216     map))
217
218 (defun factor-mode--keymap-setup ()
219   (use-local-map factor-mode-map))
220
221 \f
222 ;;; Factor mode:
223
224 ;;;###autoload
225 (defun factor-mode ()
226   "A mode for editing programs written in the Factor programming language.
227 \\{factor-mode-map}"
228   (interactive)
229   (kill-all-local-variables)
230   (setq major-mode 'factor-mode)
231   (setq mode-name "Factor")
232   (fuel-font-lock--font-lock-setup)
233   (factor-mode--keymap-setup)
234   (factor-mode--indentation-setup)
235   (factor-mode--syntax-setup)
236   (when factor-mode-use-fuel (require 'fuel-mode) (fuel-mode))
237   (run-hooks 'factor-mode-hook))
238
239 \f
240 (provide 'factor-mode)
241 ;;; factor-mode.el ends here