diff options
Diffstat (limited to 'third_party/python/gyp/tools/emacs/gyp.el')
-rw-r--r-- | third_party/python/gyp/tools/emacs/gyp.el | 275 |
1 files changed, 275 insertions, 0 deletions
diff --git a/third_party/python/gyp/tools/emacs/gyp.el b/third_party/python/gyp/tools/emacs/gyp.el new file mode 100644 index 0000000000..b98b155ced --- /dev/null +++ b/third_party/python/gyp/tools/emacs/gyp.el @@ -0,0 +1,275 @@ +;;; gyp.el - font-lock-mode support for gyp files. + +;; Copyright (c) 2012 Google Inc. All rights reserved. +;; Use of this source code is governed by a BSD-style license that can be +;; found in the LICENSE file. + +;; Put this somewhere in your load-path and +;; (require 'gyp) + +(require 'python) +(require 'cl) + +(when (string-match "python-mode.el" (symbol-file 'python-mode 'defun)) + (error (concat "python-mode must be loaded from python.el (bundled with " + "recent emacsen), not from the older and less maintained " + "python-mode.el"))) + +(defadvice python-indent-calculate-levels (after gyp-outdent-closing-parens + activate) + "De-indent closing parens, braces, and brackets in gyp-mode." + (when (and (eq major-mode 'gyp-mode) + (string-match "^ *[])}][],)}]* *$" + (buffer-substring-no-properties + (line-beginning-position) (line-end-position)))) + (setf (first python-indent-levels) + (- (first python-indent-levels) python-continuation-offset)))) + +(defadvice python-indent-guess-indent-offset (around + gyp-indent-guess-indent-offset + activate) + "Guess correct indent offset in gyp-mode." + (or (and (not (eq major-mode 'gyp-mode)) + ad-do-it) + (save-excursion + (save-restriction + (widen) + (goto-char (point-min)) + ;; Find first line ending with an opening brace that is not a comment. + (or (and (re-search-forward "\\(^[[{]$\\|^.*[^#].*[[{]$\\)") + (forward-line) + (/= (current-indentation) 0) + (set (make-local-variable 'python-indent-offset) + (current-indentation)) + (set (make-local-variable 'python-continuation-offset) + (current-indentation))) + (message "Can't guess gyp indent offset, using default: %s" + python-continuation-offset)))))) + +(define-derived-mode gyp-mode python-mode "Gyp" + "Major mode for editing .gyp files. See http://code.google.com/p/gyp/" + ;; gyp-parse-history is a stack of (POSITION . PARSE-STATE) tuples, + ;; with greater positions at the top of the stack. PARSE-STATE + ;; is a list of section symbols (see gyp-section-name and gyp-parse-to) + ;; with most nested section symbol at the front of the list. + (set (make-local-variable 'gyp-parse-history) '((1 . (list)))) + (gyp-add-font-lock-keywords)) + +(defun gyp-set-indentation () + "Hook function to configure python indentation to suit gyp mode." + (set (make-local-variable 'python-indent-offset) 2) + (set (make-local-variable 'python-continuation-offset) 2) + (set (make-local-variable 'python-indent-guess-indent-offset) t) + (python-indent-guess-indent-offset)) + +(add-hook 'gyp-mode-hook 'gyp-set-indentation) + +(add-to-list 'auto-mode-alist '("\\.gyp\\'" . gyp-mode)) +(add-to-list 'auto-mode-alist '("\\.gypi\\'" . gyp-mode)) +(add-to-list 'auto-mode-alist '("/\\.gclient\\'" . gyp-mode)) + +;;; Font-lock support + +(defconst gyp-dependencies-regexp + (regexp-opt (list "dependencies" "export_dependent_settings")) + "Regular expression to introduce 'dependencies' section") + +(defconst gyp-sources-regexp + (regexp-opt (list "action" "files" "include_dirs" "includes" "inputs" + "libraries" "outputs" "sources")) + "Regular expression to introduce 'sources' sections") + +(defconst gyp-conditions-regexp + (regexp-opt (list "conditions" "target_conditions")) + "Regular expression to introduce conditions sections") + +(defconst gyp-variables-regexp + "^variables" + "Regular expression to introduce variables sections") + +(defconst gyp-defines-regexp + "^defines" + "Regular expression to introduce 'defines' sections") + +(defconst gyp-targets-regexp + "^targets" + "Regular expression to introduce 'targets' sections") + +(defun gyp-section-name (section) + "Map the sections we are interested in from SECTION to symbol. + + SECTION is a string from the buffer that introduces a section. The result is + a symbol representing the kind of section. + + This allows us to treat (for the purposes of font-lock) several different + section names as the same kind of section. For example, a 'sources section + can be introduced by the 'sources', 'inputs', 'outputs' keyword. + + 'other is the default section kind when a more specific match is not made." + (cond ((string-match-p gyp-dependencies-regexp section) 'dependencies) + ((string-match-p gyp-sources-regexp section) 'sources) + ((string-match-p gyp-variables-regexp section) 'variables) + ((string-match-p gyp-conditions-regexp section) 'conditions) + ((string-match-p gyp-targets-regexp section) 'targets) + ((string-match-p gyp-defines-regexp section) 'defines) + (t 'other))) + +(defun gyp-invalidate-parse-states-after (target-point) + "Erase any parse information after target-point." + (while (> (caar gyp-parse-history) target-point) + (setq gyp-parse-history (cdr gyp-parse-history)))) + +(defun gyp-parse-point () + "The point of the last parse state added by gyp-parse-to." + (caar gyp-parse-history)) + +(defun gyp-parse-sections () + "A list of section symbols holding at the last parse state point." + (cdar gyp-parse-history)) + +(defun gyp-inside-dictionary-p () + "Predicate returning true if the parser is inside a dictionary." + (not (eq (cadar gyp-parse-history) 'list))) + +(defun gyp-add-parse-history (point sections) + "Add parse state SECTIONS to the parse history at POINT so that parsing can be + resumed instantly." + (while (>= (caar gyp-parse-history) point) + (setq gyp-parse-history (cdr gyp-parse-history))) + (setq gyp-parse-history (cons (cons point sections) gyp-parse-history))) + +(defun gyp-parse-to (target-point) + "Parses from (point) to TARGET-POINT adding the parse state information to + gyp-parse-state-history. Parsing stops if TARGET-POINT is reached or if a + string literal has been parsed. Returns nil if no further parsing can be + done, otherwise returns the position of the start of a parsed string, leaving + the point at the end of the string." + (let ((parsing t) + string-start) + (while parsing + (setq string-start nil) + ;; Parse up to a character that starts a sexp, or if the nesting + ;; level decreases. + (let ((state (parse-partial-sexp (gyp-parse-point) + target-point + -1 + t)) + (sections (gyp-parse-sections))) + (if (= (nth 0 state) -1) + (setq sections (cdr sections)) ; pop out a level + (cond ((looking-at-p "['\"]") ; a string + (setq string-start (point)) + (goto-char (scan-sexps (point) 1)) + (if (gyp-inside-dictionary-p) + ;; Look for sections inside a dictionary + (let ((section (gyp-section-name + (buffer-substring-no-properties + (+ 1 string-start) + (- (point) 1))))) + (setq sections (cons section (cdr sections))))) + ;; Stop after the string so it can be fontified. + (setq target-point (point))) + ((looking-at-p "{") + ;; Inside a dictionary. Increase nesting. + (forward-char 1) + (setq sections (cons 'unknown sections))) + ((looking-at-p "\\[") + ;; Inside a list. Increase nesting + (forward-char 1) + (setq sections (cons 'list sections))) + ((not (eobp)) + ;; other + (forward-char 1)))) + (gyp-add-parse-history (point) sections) + (setq parsing (< (point) target-point)))) + string-start)) + +(defun gyp-section-at-point () + "Transform the last parse state, which is a list of nested sections and return + the section symbol that should be used to determine font-lock information for + the string. Can return nil indicating the string should not have any attached + section." + (let ((sections (gyp-parse-sections))) + (cond + ((eq (car sections) 'conditions) + ;; conditions can occur in a variables section, but we still want to + ;; highlight it as a keyword. + nil) + ((and (eq (car sections) 'list) + (eq (cadr sections) 'list)) + ;; conditions and sources can have items in [[ ]] + (caddr sections)) + (t (cadr sections))))) + +(defun gyp-section-match (limit) + "Parse from (point) to LIMIT returning by means of match data what was + matched. The group of the match indicates what style font-lock should apply. + See also `gyp-add-font-lock-keywords'." + (gyp-invalidate-parse-states-after (point)) + (let ((group nil) + (string-start t)) + (while (and (< (point) limit) + (not group) + string-start) + (setq string-start (gyp-parse-to limit)) + (if string-start + (setq group (case (gyp-section-at-point) + ('dependencies 1) + ('variables 2) + ('conditions 2) + ('sources 3) + ('defines 4) + (nil nil))))) + (if group + (progn + ;; Set the match data to indicate to the font-lock mechanism the + ;; highlighting to be performed. + (set-match-data (append (list string-start (point)) + (make-list (* (1- group) 2) nil) + (list (1+ string-start) (1- (point))))) + t)))) + +;;; Please see http://code.google.com/p/gyp/wiki/GypLanguageSpecification for +;;; canonical list of keywords. +(defun gyp-add-font-lock-keywords () + "Add gyp-mode keywords to font-lock mechanism." + ;; TODO(jknotten): Move all the keyword highlighting into gyp-section-match + ;; so that we can do the font-locking in a single font-lock pass. + (font-lock-add-keywords + nil + (list + ;; Top-level keywords + (list (concat "['\"]\\(" + (regexp-opt (list "action" "action_name" "actions" "cflags" + "cflags_cc" "conditions" "configurations" + "copies" "defines" "dependencies" "destination" + "direct_dependent_settings" + "export_dependent_settings" "extension" "files" + "include_dirs" "includes" "inputs" "ldflags" "libraries" + "link_settings" "mac_bundle" "message" + "msvs_external_rule" "outputs" "product_name" + "process_outputs_as_sources" "rules" "rule_name" + "sources" "suppress_wildcard" + "target_conditions" "target_defaults" + "target_defines" "target_name" "toolsets" + "targets" "type" "variables" "xcode_settings")) + "[!/+=]?\\)") 1 'font-lock-keyword-face t) + ;; Type of target + (list (concat "['\"]\\(" + (regexp-opt (list "loadable_module" "static_library" + "shared_library" "executable" "none")) + "\\)") 1 'font-lock-type-face t) + (list "\\(?:target\\|action\\)_name['\"]\\s-*:\\s-*['\"]\\([^ '\"]*\\)" 1 + 'font-lock-function-name-face t) + (list 'gyp-section-match + (list 1 'font-lock-function-name-face t t) ; dependencies + (list 2 'font-lock-variable-name-face t t) ; variables, conditions + (list 3 'font-lock-constant-face t t) ; sources + (list 4 'font-lock-preprocessor-face t t)) ; preprocessor + ;; Variable expansion + (list "<@?(\\([^\n )]+\\))" 1 'font-lock-variable-name-face t) + ;; Command expansion + (list "<!@?(\\([^\n )]+\\))" 1 'font-lock-variable-name-face t) + ))) + +(provide 'gyp) |