PageRenderTime 53ms CodeModel.GetById 25ms RepoModel.GetById 0ms app.codeStats 0ms

/misc/fuel/factor-mode.el

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