mirror of
https://github.com/klzgrad/naiveproxy.git
synced 2024-11-28 16:26:10 +03:00
192 lines
6.8 KiB
EmacsLisp
192 lines
6.8 KiB
EmacsLisp
;;; gn-mode.el - A major mode for editing gn files.
|
||
|
||
;; Copyright 2015 The Chromium Authors. All rights reserved.
|
||
;; Use of this source code is governed by a BSD-style license that can be
|
||
;; found in the LICENSE file.
|
||
|
||
;; Author: Elliot Glaysher <erg@chromium.org>
|
||
;; Created: April 03, 2015
|
||
;; Keywords: tools, gn, ninja, chromium
|
||
|
||
;; This file is not part of GNU Emacs.
|
||
|
||
;;; Commentary:
|
||
|
||
;; A major mode for editing GN files. GN stands for Generate Ninja. GN is the
|
||
;; meta build system used in Chromium. For more information on GN, see the GN
|
||
;; manual: <https://chromium.googlesource.com/chromium/src/+/master/tools/gn/README.md>
|
||
|
||
;;; To Do:
|
||
|
||
;; - We syntax highlight builtin actions, but don't highlight instantiations of
|
||
;; templates. Should we?
|
||
|
||
|
||
|
||
(require 'smie)
|
||
|
||
(defgroup gn nil
|
||
"Major mode for editing Generate Ninja files."
|
||
:prefix "gn-"
|
||
:group 'languages)
|
||
|
||
(defcustom gn-indent-basic 2
|
||
"The number of spaces to indent a new scope."
|
||
:group 'gn
|
||
:type 'integer)
|
||
|
||
(defcustom gn-format-command "gn format --stdin"
|
||
"The command to run to format gn files in place."
|
||
:group 'gn
|
||
:type 'string)
|
||
|
||
(defgroup gn-faces nil
|
||
"Faces used in Generate Ninja mode."
|
||
:group 'gn
|
||
:group 'faces)
|
||
|
||
(defface gn-embedded-variable
|
||
'((t :inherit font-lock-variable-name-face))
|
||
"Font lock face used to highlight variable names in strings."
|
||
:group 'gn-faces)
|
||
|
||
(defface gn-embedded-variable-boundary
|
||
'((t :bold t
|
||
:inherit gn-embedded-variable))
|
||
"Font lock face used to highlight the '$' that starts a
|
||
variable name or the '{{' and '}}' which surround it."
|
||
:group 'gn-faces)
|
||
|
||
(defvar gn-font-lock-reserved-keywords
|
||
'("true" "false" "if" "else"))
|
||
|
||
(defvar gn-font-lock-target-declaration-keywords
|
||
'("action" "action_foreach" "bundle_data" "copy" "create_bundle" "executable"
|
||
"group" "loadable_module" "shared_library" "source_set" "static_library"
|
||
"target"))
|
||
|
||
;; pool() is handled specially since it's also a variable name
|
||
(defvar gn-font-lock-buildfile-fun-keywords
|
||
'("assert" "config" "declare_args" "defined" "exec_script" "foreach"
|
||
"forward_variables_from" "get_label_info" "get_path_info"
|
||
"get_target_outputs" "getenv" "import" "not_needed" "print"
|
||
"process_file_template" "read_file" "rebase_path" "set_default_toolchain"
|
||
"set_defaults" "set_sources_assignment_filter" "split_list" "template"
|
||
"tool" "toolchain" "write_file"))
|
||
|
||
(defvar gn-font-lock-predefined-var-keywords
|
||
'("current_cpu" "current_os" "current_toolchain" "default_toolchain"
|
||
"host_cpu" "host_os" "invoker" "python_path" "root_build_dir" "root_gen_dir"
|
||
"root_out_dir" "target_cpu" "target_gen_dir" "target_name" "target_os"
|
||
"target_out_dir"))
|
||
|
||
(defvar gn-font-lock-var-keywords
|
||
'("all_dependent_configs" "allow_circular_includes_from" "arflags" "args"
|
||
"asmflags" "assert_no_deps" "bundle_deps_filter" "bundle_executable_dir"
|
||
"bundle_plugins_dir" "bundle_resources_dir" "bundle_root_dir" "cflags"
|
||
"cflags_c" "cflags_cc" "cflags_objc" "cflags_objcc" "check_includes"
|
||
"code_signing_args" "code_signing_outputs" "code_signing_script"
|
||
"code_signing_sources" "complete_static_lib" "configs" "data" "data_deps"
|
||
"defines" "depfile" "deps" "include_dirs" "inputs" "ldflags" "lib_dirs"
|
||
"libs" "output_dir" "output_extension" "output_name"
|
||
"output_prefix_override" "outputs" "pool" "precompiled_header"
|
||
"precompiled_header_type" "precompiled_source" "product_type" "public"
|
||
"public_configs" "public_deps" "response_file_contents" "script" "sources"
|
||
"testonly" "visibility" "write_runtime_deps" "bundle_contents_dir"))
|
||
|
||
(defconst gn-font-lock-keywords
|
||
`((,(regexp-opt gn-font-lock-reserved-keywords 'words) .
|
||
font-lock-keyword-face)
|
||
(,(regexp-opt gn-font-lock-target-declaration-keywords 'words) .
|
||
font-lock-type-face)
|
||
(,(regexp-opt gn-font-lock-buildfile-fun-keywords 'words) .
|
||
font-lock-function-name-face)
|
||
;; pool() as a function
|
||
("\\<\\(pool\\)\\s-*("
|
||
(1 font-lock-function-name-face))
|
||
(,(regexp-opt gn-font-lock-predefined-var-keywords 'words) .
|
||
font-lock-constant-face)
|
||
(,(regexp-opt gn-font-lock-var-keywords 'words) .
|
||
font-lock-variable-name-face)
|
||
;; $variables_like_this
|
||
("\\(\\$\\)\\([a-zA-Z0-9_]+\\)"
|
||
(1 'gn-embedded-variable-boundary t)
|
||
(2 'gn-embedded-variable t))
|
||
;; ${variables_like_this}
|
||
("\\(\\${\\)\\([^\n }]+\\)\\(}\\)"
|
||
(1 'gn-embedded-variable-boundary t)
|
||
(2 'gn-embedded-variable t)
|
||
(3 'gn-embedded-variable-boundary t))
|
||
;; {{placeholders}} (see substitute_type.h)
|
||
("\\({{\\)\\([^\n }]+\\)\\(}}\\)"
|
||
(1 'gn-embedded-variable-boundary t)
|
||
(2 'gn-embedded-variable t)
|
||
(3 'gn-embedded-variable-boundary t))))
|
||
|
||
(defun gn-smie-rules (kind token)
|
||
"These are slightly modified indentation rules from the SMIE
|
||
Indentation Example info page. This changes the :before rule
|
||
and adds a :list-intro to handle our x = [ ] syntax."
|
||
(pcase (cons kind token)
|
||
(`(:elem . basic) gn-indent-basic)
|
||
(`(,_ . ",") (smie-rule-separator kind))
|
||
(`(:list-intro . "") gn-indent-basic)
|
||
(`(:before . ,(or `"[" `"(" `"{"))
|
||
(if (smie-rule-hanging-p) (smie-rule-parent)))
|
||
(`(:before . "if")
|
||
(and (not (smie-rule-bolp)) (smie-rule-prev-p "else")
|
||
(smie-rule-parent)))))
|
||
|
||
(defun gn-fill-paragraph (&optional justify)
|
||
"We only fill inside of comments in GN mode."
|
||
(interactive "P")
|
||
(or (fill-comment-paragraph justify)
|
||
;; Never return nil; `fill-paragraph' will perform its default behavior
|
||
;; if we do.
|
||
t))
|
||
|
||
(defun gn-run-format ()
|
||
"Run 'gn format' on the buffer in place."
|
||
(interactive)
|
||
;; We can't `save-excursion' here; that will put us at the beginning of the
|
||
;; shell output, aka the beginning of the document.
|
||
(let ((my-start-line (line-number-at-pos)))
|
||
(shell-command-on-region (point-min) (point-max) gn-format-command nil t)
|
||
(goto-line my-start-line)))
|
||
|
||
(defvar gn-mode-map
|
||
(let ((map (make-sparse-keymap)))
|
||
(define-key map "\C-c\C-f" 'gn-run-format)
|
||
map))
|
||
|
||
;;;###autoload
|
||
(define-derived-mode gn-mode prog-mode "GN"
|
||
"Major mode for editing gn (Generate Ninja)."
|
||
:group 'gn
|
||
|
||
(setq-local comment-use-syntax t)
|
||
(setq-local comment-start "#")
|
||
(setq-local comment-end "")
|
||
(setq-local indent-tabs-mode nil)
|
||
|
||
(setq-local fill-paragraph-function 'gn-fill-paragraph)
|
||
|
||
(setq-local font-lock-defaults '(gn-font-lock-keywords))
|
||
|
||
;; For every 'rule("name") {', adds "name" to the imenu for quick navigation.
|
||
(setq-local imenu-generic-expression
|
||
'((nil "^\s*[a-zA-Z0-9_]+(\"\\([a-zA-Z0-9_]+\\)\")\s*{" 1)))
|
||
|
||
(smie-setup nil #'gn-smie-rules)
|
||
(setq-local smie-indent-basic gn-indent-basic)
|
||
|
||
;; python style comment: “# …”
|
||
(modify-syntax-entry ?# "< b" gn-mode-syntax-table)
|
||
(modify-syntax-entry ?\n "> b" gn-mode-syntax-table)
|
||
(modify-syntax-entry ?_ "w" gn-mode-syntax-table))
|
||
|
||
;;;###autoload
|
||
(add-to-list 'auto-mode-alist '("\\.gni?\\'" . gn-mode))
|
||
|
||
(provide 'gn-mode)
|