Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / tools / gn / misc / emacs / gn-mode.el
blob14bcd93bae12363f7ed3d72574c0c9e857730dad
1 ;;; gn-mode.el - A major mode for editing gn files.
3 ;; Copyright 2015 The Chromium Authors. All rights reserved.
4 ;; Use of this source code is governed by a BSD-style license that can be
5 ;; found in the LICENSE file.
7 ;; Author: Elliot Glaysher <erg@chromium.org>
8 ;; Created: April 03, 2015
9 ;; Keywords: tools, gn, ninja, chromium
11 ;; This file is not part of GNU Emacs.
13 ;;; Commentary:
15 ;; A major mode for editing GN files. GN stands for Generate Ninja. GN is the
16 ;; meta build system used in Chromium. For more information on GN, see the GN
17 ;; manual: <https://code.google.com/p/chromium/wiki/gn>
19 ;;; To Do:
21 ;; - We syntax highlight builtin actions, but don't highlight instantiations of
22 ;; templates. Should we?
26 (eval-when-compile (require 'cl)) ;For the `case' macro.
27 (require 'smie)
29 (defgroup gn nil
30 "Major mode for editing Generate Ninja files."
31 :prefix "gn-"
32 :group 'languages)
34 (defcustom gn-indent-basic 2
35 "The number of spaces to indent a new scope."
36 :group 'gn
37 :type 'integer)
39 (defgroup gn-faces nil
40 "Faces used in Generate Ninja mode."
41 :group 'gn
42 :group 'faces)
44 (defface gn-embedded-variable
45 '((t :inherit font-lock-variable-name-face))
46 "Font lock face used to highlight variable names in strings."
47 :group 'gn-faces)
49 (defface gn-embedded-variable-boundary
50 '((t :bold t
51 :inherit gn-embedded-variable))
52 "Font lock face used to highlight the '$' that starts a
53 variable name or the '{{' and '}}' which surround it."
54 :group 'gn-faces)
56 (defvar gn-font-lock-target-declaration-keywords
57 '("action" "action_foreach" "copy" "executable" "group"
58 "shared_library" "source_set" "static_library" "if" "else"))
60 (defvar gn-font-lock-buildfile-fun-keywords
61 '("assert" "config" "declare_args" "defined" "exec_script" "foreach"
62 "get_label_info" "get_path_info" "get_target_outputs" "getenv" "import"
63 "print" "process_file_template" "read_file" "rebase_path"
64 "set_default_toolchain" "set_defaults" "set_sources_assignment_filter"
65 "template" "tool" "toolchain" "toolchain_args" "write_file"))
67 (defvar gn-font-lock-predefined-var-keywords
68 '("current_cpu" "current_os" "current_toolchain" "default_toolchain"
69 "host_cpu" "host_os" "python_path" "root_build_dir" "root_gen_dir"
70 "root_out_dir" "target_cpu" "target_gen_dir" "target_os" "target_out_dir"))
72 (defvar gn-font-lock-var-keywords
73 '("all_dependent_configs" "allow_circular_includes_from" "args" "cflags"
74 "cflags_c" "cflags_cc" "cflags_objc" "cflags_objcc" "check_includes"
75 "complete_static_lib" "configs" "data" "data_deps" "defines" "depfile"
76 "deps" "forward_dependent_configs_from" "include_dirs" "inputs"
77 "ldflags" "lib_dirs" "libs" "output_extension" "output_name" "outputs"
78 "public" "public_configs" "public_deps" "script" "sources" "testonly"
79 "visibility"))
81 (defconst gn-font-lock-keywords
82 `((,(regexp-opt gn-font-lock-target-declaration-keywords 'words) .
83 font-lock-keyword-face)
84 (,(regexp-opt gn-font-lock-buildfile-fun-keywords 'words) .
85 font-lock-function-name-face)
86 (,(regexp-opt gn-font-lock-predefined-var-keywords 'words) .
87 font-lock-constant-face)
88 (,(regexp-opt gn-font-lock-var-keywords 'words) .
89 font-lock-variable-name-face)
90 ;; $variables_like_this
91 ("\\(\\$\\)\\([a-zA-Z0-9_]+\\)"
92 (1 'gn-embedded-variable-boundary t)
93 (2 'gn-embedded-variable t))
94 ;; ${variables_like_this}
95 ("\\(\\${\\)\\([^\n }]+\\)\\(}\\)"
96 (1 'gn-embedded-variable-boundary t)
97 (2 'gn-embedded-variable t)
98 (3 'gn-embedded-variable-boundary t))
99 ;; {{placeholders}} (see substitute_type.h)
100 ("\\({{\\)\\([^\n }]+\\)\\(}}\\)"
101 (1 'gn-embedded-variable-boundary t)
102 (2 'gn-embedded-variable t)
103 (3 'gn-embedded-variable-boundary t))))
105 (defun gn-smie-rules (kind token)
106 "These are slightly modified indentation rules from the SMIE
107 Indentation Example info page. This changes the :before rule
108 and adds a :list-intro to handle our x = [ ] syntax."
109 (pcase (cons kind token)
110 (`(:elem . basic) gn-indent-basic)
111 (`(,_ . ",") (smie-rule-separator kind))
112 (`(:list-intro . "") gn-indent-basic)
113 (`(:before . ,(or `"[" `"(" `"{"))
114 (if (smie-rule-hanging-p) (smie-rule-parent)))
115 (`(:before . "if")
116 (and (not (smie-rule-bolp)) (smie-rule-prev-p "else")
117 (smie-rule-parent)))))
119 (defun gn-fill-paragraph (&optional justify)
120 "We only fill inside of comments in GN mode."
121 (interactive "P")
122 (or (fill-comment-paragraph justify)
123 ;; Never return nil; `fill-paragraph' will perform its default behavior
124 ;; if we do.
127 ;;;###autoload
128 (define-derived-mode gn-mode prog-mode "GN"
129 "Major mode for editing gn (Generate Ninja)."
130 :group 'gn
132 (setq-local comment-use-syntax t)
133 (setq-local comment-start "#")
134 (setq-local comment-end "")
135 (setq-local indent-tabs-mode nil)
137 (setq-local fill-paragraph-function 'gn-fill-paragraph)
139 (setq-local font-lock-defaults '(gn-font-lock-keywords))
141 ;; For every 'rule("name") {', adds "name" to the imenu for quick navigation.
142 (setq-local imenu-generic-expression
143 '((nil "^\s*[a-zA-Z0-9_]+(\"\\([a-zA-Z0-9_]+\\)\")\s*{" 1)))
145 (smie-setup nil #'gn-smie-rules)
146 (setq-local smie-indent-basic gn-indent-basic)
148 ;; python style comment: “# …”
149 (modify-syntax-entry ?# "< b" gn-mode-syntax-table)
150 (modify-syntax-entry ?\n "> b" gn-mode-syntax-table)
151 (modify-syntax-entry ?_ "w" gn-mode-syntax-table))
153 ;;;###autoload
154 (add-to-list 'auto-mode-alist '("\\.gni?\\'" . gn-mode))
156 (provide 'gn-mode)