]> code.delx.au - gnu-emacs-elpa/blob - packages/dts-mode/dts-mode.el
Merge commit '0cda39255827f283e7578cd469ae42daad9556a2' from js2-mode
[gnu-emacs-elpa] / packages / dts-mode / dts-mode.el
1 ;;; dts-mode.el --- Major mode for Device Tree source files
2
3 ;; Copyright (C) 2014-2015 Free Software Foundation, Inc.
4
5 ;; Version: 0.1.0
6 ;; Author: Ben Gamari <ben@smart-cactus.org>
7 ;; Keywords: languages
8
9 ;; This program is free software; you can redistribute it and/or modify
10 ;; it under the terms of the GNU General Public License as published by
11 ;; the Free Software Foundation, either version 3 of the License, or
12 ;; (at your option) any later version.
13
14 ;; This program is distributed in the hope that it will be useful,
15 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
16 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 ;; GNU General Public License for more details.
18
19 ;; You should have received a copy of the GNU General Public License
20 ;; along with this program. If not, see <http://www.gnu.org/licenses/>.
21
22 ;;; Commentary:
23
24 ;;
25
26 ;;; Code:
27
28 (defconst dts-re-ident "\\([[:alpha:]_][[:alnum:]_,-]*\\)")
29
30 (defvar dts-mode-font-lock-keywords
31 `(
32 ;; Names like `name: hi {`
33 (,(concat dts-re-ident ":") 1 font-lock-variable-name-face)
34 ;; Nodes
35 (,(concat dts-re-ident "\\(@[[:xdigit:]]+\\)?[[:space:]]*{")
36 (1 font-lock-type-face))
37 ;; Assignments
38 (,(concat dts-re-ident "[[:space:]]*=") 1 font-lock-variable-name-face)
39 (,(concat dts-re-ident "[[:space:]]*;") 1 font-lock-variable-name-face)
40 ;; References
41 (,(concat "&" dts-re-ident) 1 font-lock-variable-name-face)
42 )
43 )
44
45 (defvar dts-mode-syntax-table
46 (let ((table (make-syntax-table)))
47 (modify-syntax-entry ?< "(>" table)
48 (modify-syntax-entry ?> ")<" table)
49
50 (modify-syntax-entry ?& "." table)
51 (modify-syntax-entry ?| "." table)
52 (modify-syntax-entry ?~ "." table)
53
54 ;; _ and , are both symbol constituents.
55 (modify-syntax-entry ?, "_" table)
56 (modify-syntax-entry ?_ "_" table)
57
58 ;; Strings
59 (modify-syntax-entry ?\" "\"" table)
60 (modify-syntax-entry ?\\ "\\" table)
61
62 ;; Comments
63 (modify-syntax-entry ?/ ". 124b" table)
64 (modify-syntax-entry ?* ". 23" table)
65 (modify-syntax-entry ?\n "> b" table)
66 (modify-syntax-entry ?\^m "> b" table)
67
68 table))
69
70 ;;;; Original manual indentation code.
71
72 (defun dts--calculate-indentation ()
73 (interactive)
74 (save-excursion
75 (let ((end (point-at-eol))
76 (cnt 0)
77 (initial-point (point)))
78 (goto-char 0)
79 (while (re-search-forward "\\([{}]\\)" end t)
80 (if (string= (match-string-no-properties 0) "{")
81 (setq cnt (1+ cnt))
82 (setq cnt (1- cnt))))
83 ;; subtract one if the current line has an opening brace since we
84 ;; shouldn't add the indentation level until the following line
85 (goto-char initial-point)
86 (beginning-of-line)
87 (when (re-search-forward "{" (point-at-eol) t)
88 (setq cnt (1- cnt)))
89 cnt)))
90
91 (defun dts-indent-line ()
92 (interactive)
93 (let ((indent (dts--calculate-indentation)))
94 (indent-line-to (* indent tab-width))))
95
96 ;;;; New SMIE-based indentation code.
97
98 ;; Compatibility macro.
99 (defmacro dts--using-macro (name exp)
100 (declare (indent 1) (debug (symbolp form)))
101 (if (fboundp name) ;If macro exists at compiler-time, just use it.
102 exp
103 `(when (fboundp ',name) ;Else, check if it exists at run-time.
104 (eval ',exp)))) ;If it does, then run the code.
105
106 (require 'smie nil t)
107
108 (defvar dts-use-smie (and (fboundp 'smie-prec2->grammar) (fboundp 'pcase)))
109
110 (defconst dts-grammar
111 ;; FIXME: The syntax-table gives symbol-constituent syntax to the comma,
112 ;; but the comma is also used as a separator!
113 (when (fboundp 'smie-prec2->grammar)
114 (smie-prec2->grammar
115 (smie-bnf->prec2
116 '((id) (val ("<" val ">"))
117 (exp ("{" exps "}")
118 ;; The "foo,bar = toto" can be handled either by considering
119 ;; "foo,bar" as a single token or as 3 tokens.
120 ;; Currently I consider it as 3 tokens, so the LHS of "=" can't be
121 ;; just `id' but has to be `vals'.
122 (vals "=" vals))
123 (exps (exp) (exps ";" exps))
124 (vals (val "," val)))
125 '((assoc ";")) '((assoc ","))))))
126
127 (defun dts-indent-rules (kind token)
128 (dts--using-macro pcase
129 (pcase (cons kind token)
130 (`(:elem . basic) tab-width)
131 ;; (`(:elem . args) 0)
132 (`(:list-intro . "") ;FIXME: Not sure why we get "" here!
133 ;; After < we either have a plain list of data, as in: "operating-points
134 ;; = <1008000 1400000 ...>" or we have sometimes "refs with args" as in
135 ;; "clocks = <&apb1_gates 6>;".
136 (and (eq (char-before) ?<) (not (looking-at "&"))))
137 (`(:before . "{") (smie-rule-parent))
138 (`(:before . "<") (if (smie-rule-hanging-p) (smie-rule-parent)))
139 (`(:after . "=") (dts-indent-rules :elem 'basic))
140 )))
141
142 ;;;; The major mode itself.
143
144 (defalias 'dts-parent-mode
145 (if (fboundp 'prog-mode) 'prog-mode 'fundamental-mode))
146
147 ;;;###autoload
148 (define-derived-mode dts-mode dts-parent-mode "Devicetree";DTS would be shorter!
149 "Major mode for editing Device Tree source files."
150
151 ;; Fonts
152 (set (make-local-variable 'font-lock-defaults)
153 '(dts-mode-font-lock-keywords nil nil nil nil))
154 (set (make-local-variable 'comment-start) "/* ")
155 (set (make-local-variable 'comment-end) " */")
156 (set (make-local-variable 'comment-multi-line) t)
157
158 ;; This is not specific to the DTS format, really, but DTS is mostly
159 ;; used in the context of the Linux kernel (and U-boot loader) where
160 ;; there's a strong preference to indent with TABs.
161 (set (make-local-variable 'indent-tabs-mode) t)
162
163 (dts--using-macro syntax-propertize-rules
164 (set (make-local-variable 'syntax-propertize-function)
165 (syntax-propertize-rules
166 ("#include[ \t]+\\(<\\).*\\(>\\)" (1 "|") (2 "|"))
167 ;; Treat things like /delete-property/ as a single identifier.
168 ("\\(/\\)[a-z]+\\(/\\)" (1 "_") (2 "_")))))
169 (if dts-use-smie
170 (smie-setup dts-grammar #'dts-indent-rules)
171 (set (make-local-variable 'indent-line-function) #'dts-indent-line)))
172
173 ;;;###autoload
174 (add-to-list 'auto-mode-alist '("\\.dtsi?\\'" . dts-mode))
175
176 (provide 'dts-mode)
177 ;;; dts-mode.el ends here