mirror of https://github.com/lukechilds/node.git
Ryan Dahl
13 years ago
24 changed files with 4065 additions and 159 deletions
File diff suppressed because it is too large
@ -0,0 +1,12 @@ |
|||||
|
How to install gyp-mode for emacs: |
||||
|
|
||||
|
Add the following to your ~/.emacs (replace ... with the path to your gyp |
||||
|
checkout). |
||||
|
|
||||
|
(setq load-path (cons ".../tools/emacs" load-path)) |
||||
|
(require 'gyp) |
||||
|
|
||||
|
Restart emacs (or eval-region the added lines) and you should be all set. |
||||
|
|
||||
|
Please note that ert is required for running the tests, which is included in |
||||
|
Emacs 24, or available separately from https://github.com/ohler/ert |
@ -0,0 +1,54 @@ |
|||||
|
;;; gyp-tests.el - unit tests for gyp-mode. |
||||
|
|
||||
|
;; 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. |
||||
|
|
||||
|
;; The recommended way to run these tests is to run them from the command-line, |
||||
|
;; with the run-unit-tests.sh script. |
||||
|
|
||||
|
(require 'cl) |
||||
|
(require 'ert) |
||||
|
(require 'gyp) |
||||
|
|
||||
|
(defconst samples (directory-files "testdata" t ".gyp$") |
||||
|
"List of golden samples to check") |
||||
|
|
||||
|
(defun fontify (filename) |
||||
|
(with-temp-buffer |
||||
|
(insert-file-contents-literally filename) |
||||
|
(gyp-mode) |
||||
|
(font-lock-fontify-buffer) |
||||
|
(buffer-string))) |
||||
|
|
||||
|
(defun read-golden-sample (filename) |
||||
|
(with-temp-buffer |
||||
|
(insert-file-contents-literally (concat filename ".fontified")) |
||||
|
(read (current-buffer)))) |
||||
|
|
||||
|
(defun text-face-properties (s) |
||||
|
"Extract the text properties from s" |
||||
|
(let ((result (list t))) |
||||
|
(dotimes (i (length s)) |
||||
|
(setq result (cons (get-text-property i 'face s) result))) |
||||
|
(nreverse result))) |
||||
|
|
||||
|
(ert-deftest test-golden-samples () |
||||
|
"Check that fontification produces the same results as the golden samples" |
||||
|
(dolist (sample samples) |
||||
|
(let ((golden (read-golden-sample sample)) |
||||
|
(fontified (fontify sample))) |
||||
|
(should (equal golden fontified)) |
||||
|
(should (equal (text-face-properties golden) |
||||
|
(text-face-properties fontified)))))) |
||||
|
|
||||
|
(defun create-golden-sample (filename) |
||||
|
"Create a golden sample by fontifying filename and writing out the printable |
||||
|
representation of the fontified buffer (with text properties) to the |
||||
|
FILENAME.fontified" |
||||
|
(with-temp-file (concat filename ".fontified") |
||||
|
(print (fontify filename) (current-buffer)))) |
||||
|
|
||||
|
(defun create-golden-samples () |
||||
|
"Recreate the golden samples" |
||||
|
(dolist (sample samples) (create-golden-sample sample))) |
@ -0,0 +1,251 @@ |
|||||
|
;;; 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-calculate-indentation (after ami-outdent-closing-parens |
||||
|
activate) |
||||
|
"De-indent closing parens, braces, and brackets in gyp-mode." |
||||
|
(if (and (eq major-mode 'gyp-mode) |
||||
|
(string-match "^ *[])}][],)}]* *$" |
||||
|
(buffer-substring-no-properties |
||||
|
(line-beginning-position) (line-end-position)))) |
||||
|
(setq ad-return-value (- ad-return-value 2)))) |
||||
|
|
||||
|
(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." |
||||
|
(setq python-continuation-offset 2 |
||||
|
python-indent 2 |
||||
|
python-guess-indent nil)) |
||||
|
|
||||
|
(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)) |
||||
|
|
||||
|
;;; 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)) |
||||
|
(forward-sexp 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" |
||||
|
"conditions" "configurations" "copies" "defines" |
||||
|
"dependencies" "destination" |
||||
|
"direct_dependent_settings" |
||||
|
"export_dependent_settings" "extension" "files" |
||||
|
"include_dirs" "includes" "inputs" "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) |
@ -0,0 +1,7 @@ |
|||||
|
#!/bin/sh |
||||
|
# 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. |
||||
|
emacs --no-site-file --no-init-file --batch \ |
||||
|
--load ert.el --load gyp.el --load gyp-tests.el \ |
||||
|
-f ert-run-tests-batch-and-exit |
File diff suppressed because it is too large
File diff suppressed because one or more lines are too long
Loading…
Reference in new issue