Line data Source code
1 : ;;; prog-mode.el --- Generic major mode for programming -*- lexical-binding: t -*-
2 :
3 : ;; Copyright (C) 2013-2017 Free Software Foundation, Inc.
4 :
5 : ;; Maintainer: emacs-devel@gnu.org
6 : ;; Keywords: internal
7 : ;; Package: emacs
8 :
9 : ;; This file is part of GNU Emacs.
10 :
11 : ;; GNU Emacs is free software: you can redistribute it and/or modify
12 : ;; it under the terms of the GNU General Public License as published by
13 : ;; the Free Software Foundation, either version 3 of the License, or
14 : ;; (at your option) any later version.
15 :
16 : ;; GNU Emacs is distributed in the hope that it will be useful,
17 : ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
18 : ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 : ;; GNU General Public License for more details.
20 :
21 : ;; You should have received a copy of the GNU General Public License
22 : ;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
23 :
24 : ;;; Commentary:
25 :
26 : ;; This major mode is mostly intended as a parent of other programming
27 : ;; modes. All major modes for programming languages should derive from this
28 : ;; mode so that users can put generic customization on prog-mode-hook.
29 :
30 : ;;; Code:
31 :
32 : (eval-when-compile (require 'cl-lib)
33 : (require 'subr-x))
34 :
35 : (defgroup prog-mode nil
36 : "Generic programming mode, from which others derive."
37 : :group 'languages)
38 :
39 : (defcustom prog-mode-hook nil
40 : "Normal hook run when entering programming modes."
41 : :type 'hook
42 : :options '(flyspell-prog-mode abbrev-mode flymake-mode linum-mode
43 : prettify-symbols-mode)
44 : :group 'prog-mode)
45 :
46 : (defvar prog-mode-map
47 : (let ((map (make-sparse-keymap)))
48 : (define-key map [?\C-\M-q] 'prog-indent-sexp)
49 : map)
50 : "Keymap used for programming modes.")
51 :
52 : (defvar prog-indentation-context nil
53 : "When non-nil, provides context for indenting embedded code chunks.
54 :
55 : There are languages where part of the code is actually written in
56 : a sub language, e.g., a Yacc/Bison or ANTLR grammar also consists
57 : of plain C code. This variable enables the major mode of the
58 : main language to use the indentation engine of the sub-mode for
59 : lines in code chunks written in the sub-mode's language.
60 :
61 : When a major mode of such a main language decides to delegate the
62 : indentation of a line/region to the indentation engine of the sub
63 : mode, it should bind this variable to non-nil around the call.
64 :
65 : The non-nil value should be a list of the form:
66 :
67 : (FIRST-COLUMN (START . END) PREVIOUS-CHUNKS)
68 :
69 : FIRST-COLUMN is the column the indentation engine of the sub-mode
70 : should use for top-level language constructs inside the code
71 : chunk (instead of 0).
72 :
73 : START and END specify the region of the code chunk. END can be
74 : nil, which stands for the value of `point-max'. The function
75 : `prog-widen' uses this to restore restrictions imposed by the
76 : sub-mode's indentation engine.
77 :
78 : PREVIOUS-CHUNKS, if non-nil, provides the indentation engine of
79 : the sub-mode with the virtual context of the code chunk. Valid
80 : values are:
81 :
82 : - A string containing text which the indentation engine can
83 : consider as standing in front of the code chunk. To cache the
84 : string's calculated syntactic information for repeated calls
85 : with the same string, the sub-mode can add text-properties to
86 : the string.
87 :
88 : A typical use case is for grammars with code chunks which are
89 : to be indented like function bodies -- the string would contain
90 : the corresponding function preamble.
91 :
92 : - A function, to be called with the start position of the current
93 : chunk. It should return either the region of the previous chunk
94 : as (PREV-START . PREV-END), or nil if there is no previous chunk.
95 :
96 : A typical use case are literate programming sources -- the
97 : function would successively return the previous code chunks.")
98 :
99 : (defun prog-indent-sexp (&optional defun)
100 : "Indent the expression after point.
101 : When interactively called with prefix, indent the enclosing defun
102 : instead."
103 : (interactive "P")
104 0 : (save-excursion
105 0 : (when defun
106 0 : (end-of-line)
107 0 : (beginning-of-defun))
108 0 : (let ((start (point))
109 0 : (end (progn (forward-sexp 1) (point))))
110 0 : (indent-region start end nil))))
111 :
112 : (defun prog-first-column ()
113 : "Return the indentation column normally used for top-level constructs."
114 0 : (or (car prog-indentation-context) 0))
115 :
116 : (defun prog-widen ()
117 : "Remove restrictions (narrowing) from current code chunk or buffer.
118 : This function should be used instead of `widen' in any function used
119 : by the indentation engine to make it respect the value of
120 : `prog-indentation-context'.
121 :
122 : This function (like `widen') is useful inside a
123 : `save-restriction' to make the indentation correctly work when
124 : narrowing is in effect."
125 0 : (let ((chunk (cadr prog-indentation-context)))
126 0 : (if chunk
127 : ;; No call to `widen' is necessary here, as narrow-to-region
128 : ;; changes (not just narrows) the existing restrictions
129 0 : (narrow-to-region (car chunk) (or (cdr chunk) (point-max)))
130 0 : (widen))))
131 :
132 :
133 : (defvar-local prettify-symbols-alist nil
134 : "Alist of symbol prettifications.
135 : Each element looks like (SYMBOL . CHARACTER), where the symbol
136 : matching SYMBOL (a string, not a regexp) will be shown as
137 : CHARACTER instead.
138 :
139 : CHARACTER can be a character, or it can be a list or vector, in
140 : which case it will be used to compose the new symbol as per the
141 : third argument of `compose-region'.")
142 :
143 : (defun prettify-symbols-default-compose-p (start end _match)
144 : "Return true iff the symbol MATCH should be composed.
145 : The symbol starts at position START and ends at position END.
146 : This is the default for `prettify-symbols-compose-predicate'
147 : which is suitable for most programming languages such as C or Lisp."
148 : ;; Check that the chars should really be composed into a symbol.
149 0 : (let* ((syntaxes-beg (if (memq (char-syntax (char-after start)) '(?w ?_))
150 0 : '(?w ?_) '(?. ?\\)))
151 0 : (syntaxes-end (if (memq (char-syntax (char-before end)) '(?w ?_))
152 0 : '(?w ?_) '(?. ?\\))))
153 0 : (not (or (memq (char-syntax (or (char-before start) ?\s)) syntaxes-beg)
154 0 : (memq (char-syntax (or (char-after end) ?\s)) syntaxes-end)
155 0 : (nth 8 (syntax-ppss))))))
156 :
157 : (defvar-local prettify-symbols-compose-predicate
158 : #'prettify-symbols-default-compose-p
159 : "A predicate for deciding if the currently matched symbol is to be composed.
160 : The matched symbol is the car of one entry in `prettify-symbols-alist'.
161 : The predicate receives the match's start and end positions as well
162 : as the match-string as arguments.")
163 :
164 : (defun prettify-symbols--compose-symbol (alist)
165 : "Compose a sequence of characters into a symbol.
166 : Regexp match data 0 specifies the characters to be composed."
167 : ;; Check that the chars should really be composed into a symbol.
168 0 : (let ((start (match-beginning 0))
169 0 : (end (match-end 0))
170 0 : (match (match-string 0)))
171 0 : (if (and (not (equal prettify-symbols--current-symbol-bounds (list start end)))
172 0 : (funcall prettify-symbols-compose-predicate start end match))
173 : ;; That's a symbol alright, so add the composition.
174 0 : (with-silent-modifications
175 0 : (compose-region start end (cdr (assoc match alist)))
176 0 : (add-text-properties
177 0 : start end
178 0 : `(prettify-symbols-start ,start prettify-symbols-end ,end)))
179 : ;; No composition for you. Let's actually remove any
180 : ;; composition we may have added earlier and which is now
181 : ;; incorrect.
182 0 : (remove-text-properties start end '(composition
183 : prettify-symbols-start
184 0 : prettify-symbols-end))))
185 : ;; Return nil because we're not adding any face property.
186 : nil)
187 :
188 : (defun prettify-symbols--make-keywords ()
189 0 : (if prettify-symbols-alist
190 0 : `((,(regexp-opt (mapcar 'car prettify-symbols-alist) t)
191 0 : (0 (prettify-symbols--compose-symbol ',prettify-symbols-alist))))
192 0 : nil))
193 :
194 : (defvar-local prettify-symbols--keywords nil)
195 :
196 : (defvar-local prettify-symbols--current-symbol-bounds nil)
197 :
198 : (defcustom prettify-symbols-unprettify-at-point nil
199 : "If non-nil, show the non-prettified version of a symbol when point is on it.
200 : If set to the symbol `right-edge', also unprettify if point
201 : is immediately after the symbol. The prettification will be
202 : reapplied as soon as point moves away from the symbol. If
203 : set to nil, the prettification persists even when point is
204 : on the symbol."
205 : :version "25.1"
206 : :type '(choice (const :tag "Never unprettify" nil)
207 : (const :tag "Unprettify when point is inside" t)
208 : (const :tag "Unprettify when point is inside or at right edge" right-edge))
209 : :group 'prog-mode)
210 :
211 : (defun prettify-symbols--post-command-hook ()
212 0 : (cl-labels ((get-prop-as-list
213 : (prop)
214 0 : (remove nil
215 0 : (list (get-text-property (point) prop)
216 0 : (when (and (eq prettify-symbols-unprettify-at-point 'right-edge)
217 0 : (not (bobp)))
218 0 : (get-text-property (1- (point)) prop))))))
219 : ;; Re-apply prettification to the previous symbol.
220 0 : (when (and prettify-symbols--current-symbol-bounds
221 0 : (or (< (point) (car prettify-symbols--current-symbol-bounds))
222 0 : (> (point) (cadr prettify-symbols--current-symbol-bounds))
223 0 : (and (not (eq prettify-symbols-unprettify-at-point 'right-edge))
224 0 : (= (point) (cadr prettify-symbols--current-symbol-bounds)))))
225 0 : (apply #'font-lock-flush prettify-symbols--current-symbol-bounds)
226 0 : (setq prettify-symbols--current-symbol-bounds nil))
227 : ;; Unprettify the current symbol.
228 0 : (when-let ((c (get-prop-as-list 'composition))
229 0 : (s (get-prop-as-list 'prettify-symbols-start))
230 0 : (e (get-prop-as-list 'prettify-symbols-end))
231 0 : (s (apply #'min s))
232 0 : (e (apply #'max e)))
233 0 : (with-silent-modifications
234 0 : (setq prettify-symbols--current-symbol-bounds (list s e))
235 0 : (remove-text-properties s e '(composition))))))
236 :
237 : ;;;###autoload
238 : (define-minor-mode prettify-symbols-mode
239 : "Toggle Prettify Symbols mode.
240 : With a prefix argument ARG, enable Prettify Symbols mode if ARG is
241 : positive, and disable it otherwise. If called from Lisp, enable
242 : the mode if ARG is omitted or nil.
243 :
244 : When Prettify Symbols mode and font-locking are enabled, symbols are
245 : prettified (displayed as composed characters) according to the rules
246 : in `prettify-symbols-alist' (which see), which are locally defined
247 : by major modes supporting prettifying. To add further customizations
248 : for a given major mode, you can modify `prettify-symbols-alist' thus:
249 :
250 : (add-hook \\='emacs-lisp-mode-hook
251 : (lambda ()
252 : (push \\='(\"<=\" . ?≤) prettify-symbols-alist)))
253 :
254 : You can enable this mode locally in desired buffers, or use
255 : `global-prettify-symbols-mode' to enable it for all modes that
256 : support it."
257 : :init-value nil
258 0 : (if prettify-symbols-mode
259 : ;; Turn on
260 0 : (when (setq prettify-symbols--keywords (prettify-symbols--make-keywords))
261 0 : (font-lock-add-keywords nil prettify-symbols--keywords)
262 0 : (setq-local font-lock-extra-managed-props
263 0 : (append font-lock-extra-managed-props
264 : '(composition
265 : prettify-symbols-start
266 0 : prettify-symbols-end)))
267 0 : (when prettify-symbols-unprettify-at-point
268 0 : (add-hook 'post-command-hook
269 0 : #'prettify-symbols--post-command-hook nil t))
270 0 : (font-lock-flush))
271 : ;; Turn off
272 0 : (remove-hook 'post-command-hook #'prettify-symbols--post-command-hook t)
273 0 : (when prettify-symbols--keywords
274 0 : (font-lock-remove-keywords nil prettify-symbols--keywords)
275 0 : (setq prettify-symbols--keywords nil))
276 0 : (when (memq 'composition font-lock-extra-managed-props)
277 0 : (setq font-lock-extra-managed-props (delq 'composition
278 0 : font-lock-extra-managed-props))
279 0 : (with-silent-modifications
280 0 : (remove-text-properties (point-min) (point-max) '(composition nil))))))
281 :
282 : (defun turn-on-prettify-symbols-mode ()
283 0 : (when (and (not prettify-symbols-mode)
284 0 : (local-variable-p 'prettify-symbols-alist))
285 0 : (prettify-symbols-mode 1)))
286 :
287 : ;;;###autoload
288 : (define-globalized-minor-mode global-prettify-symbols-mode
289 : prettify-symbols-mode turn-on-prettify-symbols-mode)
290 :
291 : ;;;###autoload
292 : (define-derived-mode prog-mode fundamental-mode "Prog"
293 : "Major mode for editing programming language source code."
294 69 : (setq-local require-final-newline mode-require-final-newline)
295 69 : (setq-local parse-sexp-ignore-comments t)
296 : ;; Any programming language is always written left to right.
297 69 : (setq bidi-paragraph-direction 'left-to-right))
298 :
299 : (provide 'prog-mode)
300 :
301 : ;;; prog-mode.el ends here
|