diff options
Diffstat (limited to 'elpa/typescript-mode-20220506.827/typescript-mode.el')
-rw-r--r-- | elpa/typescript-mode-20220506.827/typescript-mode.el | 2988 |
1 files changed, 0 insertions, 2988 deletions
diff --git a/elpa/typescript-mode-20220506.827/typescript-mode.el b/elpa/typescript-mode-20220506.827/typescript-mode.el deleted file mode 100644 index 220cf75..0000000 --- a/elpa/typescript-mode-20220506.827/typescript-mode.el +++ /dev/null @@ -1,2988 +0,0 @@ -;;; typescript-mode.el --- Major mode for editing typescript - -;; ----------------------------------------------------------------------------------- -;; TypeScript support for Emacs -;; Unmodified original sourve available at http://www.karllandstrom.se/downloads/emacs/javascript.el -;; Copyright (c) 2008 Free Software Foundation -;; Portions Copyright (C) Microsoft Open Technologies, Inc. All rights reserved. -;; -;; This program is free software: you can redistribute it and/or modify -;; it under the terms of the GNU General Public License as published by -;; the Free Software Foundation, either version 3 of the License, or -;; (at your option) any later version. -;; -;; This program is distributed in the hope that it will be useful, -;; but WITHOUT ANY WARRANTY; without even the implied warranty of -;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -;; GNU General Public License for more details. -;; -;; You should have received a copy of the GNU General Public License -;; along with this program. If not, see <http://www.gnu.org/licenses/>. -;; ------------------------------------------------------------------------------------------- - -;; URL: http://github.com/ananthakumaran/typescript.el -;; Version: 0.4 -;; Keywords: typescript languages -;; Package-Requires: ((emacs "24.3")) - -;; This file is not part of GNU Emacs. - -;;; Commentary: - -;; This is based on Karl Landstrom's barebones typescript-mode. This -;; is much more robust and works with cc-mode's comment filling -;; (mostly). -;; The modifications to the original javascript.el mode mainly consisted in -;; replacing "javascript" with "typescript" -;; -;; The main features of this typescript mode are syntactic -;; highlighting (enabled with `font-lock-mode' or -;; `global-font-lock-mode'), automatic indentation and filling of -;; comments. -;; -;; -;; General Remarks: -;; -;; XXX: This mode assumes that block comments are not nested inside block -;; XXX: comments -;; -;; Exported names start with "typescript-"; private names start with -;; "typescript--". - -;;; Code: - -(eval-and-compile - (require 'compile) - (require 'cc-mode) - (require 'font-lock) - (require 'rx) - (require 'newcomment)) - -(eval-when-compile - (require 'cl-lib)) - -;;; Constants - -(defconst typescript--type-name-re "\\(?:[A-Z][A-Za-z0-9]+\\.\\)\\{0,\\}\\(?:[A-Z][A-Za-z0-9]+\\)" - "Regexp matching a conventional TypeScript type-name. Must start with upper-case letter!") - -(defconst typescript--name-start-re "[a-zA-Z_$]" - "Regexp matching the start of a typescript identifier, without grouping.") - -(defconst typescript--name-re (concat typescript--name-start-re - "\\(?:\\s_\\|\\sw\\)*") - "Regexp matching a typescript identifier, without grouping.") - -(defconst typescript--objfield-re (concat typescript--name-re ":") - "Regexp matching the start of a typescript object field.") - -(defconst typescript--dotted-name-re - (concat typescript--name-re "\\(?:\\." typescript--name-re "\\)*") - "Regexp matching a dot-separated sequence of typescript names.") - -(defconst typescript--plain-method-re - (concat "^\\s-*?\\(" typescript--dotted-name-re "\\)\\.prototype" - "\\.\\(" typescript--name-re "\\)\\s-*?=\\s-*?\\(function\\)\\_>") - "Regexp matching an explicit typescript prototype \"method\" declaration. -Group 1 is a (possibly-dotted) class name, group 2 is a method name, -and group 3 is the 'function' keyword.") - -(defconst typescript--plain-class-re - (concat "^\\s-*\\(" typescript--dotted-name-re "\\)\\.prototype" - "\\s-*=\\s-*{") - "Regexp matching a typescript explicit prototype \"class\" declaration. -An example of this is \"Class.prototype = { method1: ...}\".") - -(defconst typescript--module-declaration-re - "^\\s-*\\(?:declare\\|\\(?:export\\(?:\\s-+default\\)?\\)\\)?" - "Regexp matching ambient declaration modifier or export declaration.") - -;; var NewClass = BaseClass.extend( -(defconst typescript--mp-class-decl-re - (concat "^\\s-*var\\s-+" - "\\(" typescript--name-re "\\)" - "\\s-*=\\s-*" - "\\(" typescript--dotted-name-re - "\\)\\.extend\\(?:Final\\)?\\s-*(\\s-*{?\\s-*$")) - -;; var NewClass = Class.create() -(defconst typescript--prototype-obsolete-class-decl-re - (concat "^\\s-*\\(?:var\\s-+\\)?" - "\\(" typescript--dotted-name-re "\\)" - "\\s-*=\\s-*Class\\.create()")) - -(defconst typescript--prototype-objextend-class-decl-re-1 - (concat "^\\s-*Object\\.extend\\s-*(" - "\\(" typescript--dotted-name-re "\\)" - "\\s-*,\\s-*{")) - -(defconst typescript--prototype-objextend-class-decl-re-2 - (concat "^\\s-*\\(?:var\\s-+\\)?" - "\\(" typescript--dotted-name-re "\\)" - "\\s-*=\\s-*Object\\.extend\\s-*\(")) - -;; var NewClass = Class.create({ -(defconst typescript--prototype-class-decl-re - (concat "^\\s-*\\(?:var\\s-+\\)?" - "\\(" typescript--name-re "\\)" - "\\s-*=\\s-*Class\\.create\\s-*(\\s-*" - "\\(?:\\(" typescript--dotted-name-re "\\)\\s-*,\\s-*\\)?{?")) - -;; Parent class name(s) (yes, multiple inheritance in typescript) are -;; matched with dedicated font-lock matchers -(defconst typescript--dojo-class-decl-re - (concat "^\\s-*dojo\\.declare\\s-*(\"\\(" typescript--dotted-name-re "\\)")) - -(defconst typescript--exttypescript-class-decl-re-1 - (concat "^\\s-*Ext\\.extend\\s-*(" - "\\s-*\\(" typescript--dotted-name-re "\\)" - "\\s-*,\\s-*\\(" typescript--dotted-name-re "\\)") - "Regexp matching an ExtTYPESCRIPT class declaration (style 1).") - -(defconst typescript--exttypescript-class-decl-re-2 - (concat "^\\s-*\\(?:var\\s-+\\)?" - "\\(" typescript--name-re "\\)" - "\\s-*=\\s-*Ext\\.extend\\s-*(\\s-*" - "\\(" typescript--dotted-name-re "\\)") - "Regexp matching an ExtTYPESCRIPT class declaration (style 2).") - -(defconst typescript--mochikit-class-re - (concat "^\\s-*MochiKit\\.Base\\.update\\s-*(\\s-*" - "\\(" typescript--dotted-name-re "\\)") - "Regexp matching a MochiKit class declaration.") - -(defconst typescript--dummy-class-style - '(:name "[Automatically Generated Class]")) - -(defconst typescript--class-styles - `((:name "Plain" - :class-decl ,typescript--plain-class-re - :prototype t - :contexts (toplevel) - :framework typescript) - - (:name "MochiKit" - :class-decl ,typescript--mochikit-class-re - :prototype t - :contexts (toplevel) - :framework mochikit) - - (:name "Prototype (Obsolete)" - :class-decl ,typescript--prototype-obsolete-class-decl-re - :contexts (toplevel) - :framework prototype) - - (:name "Prototype (Modern)" - :class-decl ,typescript--prototype-class-decl-re - :contexts (toplevel) - :framework prototype) - - (:name "Prototype (Object.extend)" - :class-decl ,typescript--prototype-objextend-class-decl-re-1 - :prototype t - :contexts (toplevel) - :framework prototype) - - (:name "Prototype (Object.extend) 2" - :class-decl ,typescript--prototype-objextend-class-decl-re-2 - :prototype t - :contexts (toplevel) - :framework prototype) - - (:name "Dojo" - :class-decl ,typescript--dojo-class-decl-re - :contexts (toplevel) - :framework dojo) - - (:name "ExtTYPESCRIPT (style 1)" - :class-decl ,typescript--exttypescript-class-decl-re-1 - :prototype t - :contexts (toplevel) - :framework exttypescript) - - (:name "ExtTYPESCRIPT (style 2)" - :class-decl ,typescript--exttypescript-class-decl-re-2 - :contexts (toplevel) - :framework exttypescript) - - (:name "Merrill Press" - :class-decl ,typescript--mp-class-decl-re - :contexts (toplevel) - :framework merrillpress)) - - "List of typescript class definition styles. - -A class definition style is a plist with the following keys: - -:name is a human-readable name of the class type - -:class-decl is a regular expression giving the start of the -class. Its first group must match the name of its class. If there -is a parent class, the second group should match, and it should be -the name of the class. - -If :prototype is present and non-nil, the parser will merge -declarations for this constructs with others at the same lexical -level that have the same name. Otherwise, multiple definitions -will create multiple top-level entries. Don't use :prototype -unnecessarily: it has an associated cost in performance. - -If :strip-prototype is present and non-nil, then if the class -name as matched contains") - -(defconst typescript--available-frameworks - (cl-loop with available-frameworks - for style in typescript--class-styles - for framework = (plist-get style :framework) - unless (memq framework available-frameworks) - collect framework into available-frameworks - finally return available-frameworks) - "List of available typescript frameworks symbols.") - -(defconst typescript--function-heading-1-re - (concat - typescript--module-declaration-re - "\\s-*function\\s-+\\(" typescript--name-re "\\)") - "Regexp matching the start of a typescript function header. -Match group 1 is the name of the function.") - -(defconst typescript--function-heading-2-re - (concat - "^\\s-*\\(" typescript--name-re "\\)\\s-*:\\s-*function\\_>") - "Regexp matching the start of a function entry in an associative array. -Match group 1 is the name of the function.") - -(defconst typescript--function-heading-3-re - (concat - "^\\s-*\\(?:var\\s-+\\)?\\(" typescript--dotted-name-re "\\)" - "\\s-*=\\s-*function\\_>") - "Regexp matching a line in the typescript form \"var MUMBLE = function\". -Match group 1 is MUMBLE.") - -(defun typescript--regexp-opt-symbol (list) - "Like `regexp-opt', but surround the result with `\\\\_<' and `\\\\_>'." - (concat "\\_<" (regexp-opt list t) "\\_>")) - -(defconst typescript--keyword-re - (typescript--regexp-opt-symbol - '("abstract" "any" "as" "async" "await" "boolean" "bigint" "break" "case" "catch" "class" "const" - "constructor" "continue" "debugger" "declare" "default" "delete" "do" "else" - "enum" "export" "extends" "extern" "false" "finally" "for" - "function" "from" "get" "goto" "if" "implements" "import" "in" "instanceof" - "interface" "keyof" "let" "module" "namespace" "never" "new" "null" "number" "object" "of" - "override" "private" "protected" "public" "readonly" "return" "set" "static" "string" - "super" "switch" "this" "throw" "true" - "try" "type" "typeof" "unknown" "var" "void" - "while")) ; yield is handled separately - "Regexp matching any typescript keyword.") - -(defconst typescript--basic-type-re - (typescript--regexp-opt-symbol - '("any" "bool" "boolean" "bigint" "never" "number" "string" "unknown" "void")) - "Regular expression matching any predefined type in typescript.") - -(defconst typescript--access-modifier-re - (typescript--regexp-opt-symbol - '("private" "protected" "public" "readonly" "static" "extends" "implements")) - "Regular expression matching access modifiers.") - -(defconst typescript--decorator-re - (concat "\\(@" typescript--name-re "\\)")) - -(defconst typescript--constant-re - (typescript--regexp-opt-symbol '("false" "null" "undefined" - "Infinity" "NaN" - "true" "arguments" "this")) - "Regular expression matching any future reserved words in typescript.") - -(defconst typescript--builtin-re - (typescript--regexp-opt-symbol - '("console")) - "Regular expression matching builtins.") - -(defconst typescript--function-call-re "\\(\\(?:\\w\\|\\s_\\)+\\)\\(<.+>\\)?\s*(" - "Regular expression matching function calls.") - -(defconst typescript--font-lock-keywords-1 - (list - "\\_<import\\_>" - (list typescript--function-heading-1-re 1 font-lock-function-name-face) - (list typescript--function-heading-2-re 1 font-lock-function-name-face)) - "Level one font lock keywords for `typescript-mode'.") - -(defconst typescript--font-lock-keywords-2 - (append typescript--font-lock-keywords-1 - (list (cons typescript--constant-re font-lock-constant-face) - (cons typescript--basic-type-re font-lock-type-face) - (list typescript--keyword-re 1 font-lock-keyword-face) - (list "\\_<for\\_>" - "\\s-+\\(each\\)\\_>" nil nil - (list 1 'font-lock-keyword-face)) - (cons "\\_<yield\\(\\*\\|\\_>\\)" 'font-lock-keyword-face))) - "Level two font lock keywords for `typescript-mode'.") - -;; typescript--pitem is the basic building block of the lexical -;; database. When one refers to a real part of the buffer, the region -;; of text to which it refers is split into a conceptual header and -;; body. Consider the (very short) block described by a hypothetical -;; typescript--pitem: -;; -;; function foo(a,b,c) { return 42; } -;; ^ ^ ^ -;; | | | -;; +- h-begin +- h-end +- b-end -;; -;; (Remember that these are buffer positions, and therefore point -;; between characters, not at them. An arrow drawn to a character -;; indicates the corresponding position is between that character and -;; the one immediately preceding it.) -;; -;; The header is the region of text [h-begin, h-end], and is -;; the text needed to unambiguously recognize the start of the -;; construct. If the entire header is not present, the construct is -;; not recognized at all. No other pitems may be nested inside the -;; header. -;; -;; The body is the region [h-end, b-end]. It may contain nested -;; typescript--pitem instances. The body of a pitem may be empty: in -;; that case, b-end is equal to header-end. -;; -;; The three points obey the following relationship: -;; -;; h-begin < h-end <= b-end -;; -;; We put a text property in the buffer on the character *before* -;; h-end, and if we see it, on the character *before* b-end. -;; -;; The text property for h-end, typescript--pstate, is actually a list -;; of all typescript--pitem instances open after the marked character. -;; -;; The text property for b-end, typescript--pend, is simply the -;; typescript--pitem that ends after the marked character. (Because -;; pitems always end when the paren-depth drops below a critical -;; value, and because we can only drop one level per character, only -;; one pitem may end at a given character.) -;; -;; In the structure below, we only store h-begin and (sometimes) -;; b-end. We can trivially and quickly find h-end by going to h-begin -;; and searching for an typescript--pstate text property. Since no other -;; typescript--pitem instances can be nested inside the header of a -;; pitem, the location after the character with this text property -;; must be h-end. -;; -;; typescript--pitem instances are never modified (with the exception -;; of the b-end field). Instead, modified copies are added at subseqnce parse points. -;; (The exception for b-end and its caveats is described below.) -;; - -(cl-defstruct (typescript--pitem (:type list)) - ;; IMPORTANT: Do not alter the position of fields within the list. - ;; Various bits of code depend on their positions, particularly - ;; anything that manipulates the list of children. - - ;; List of children inside this pitem's body - (children nil :read-only t) - - ;; When we reach this paren depth after h-end, the pitem ends - (paren-depth nil :read-only t) - - ;; Symbol or class-style plist if this is a class - (type nil :read-only t) - - ;; See above - (h-begin nil :read-only t) - - ;; List of strings giving the parts of the name of this pitem (e.g., - ;; '("MyClass" "myMethod"), or t if this pitem is anonymous - (name nil :read-only t) - - ;; THIS FIELD IS MUTATED, and its value is shared by all copies of - ;; this pitem: when we copy-and-modify pitem instances, we share - ;; their tail structures, so all the copies actually have the same - ;; terminating cons cell. We modify that shared cons cell directly. - ;; - ;; The field value is either a number (buffer location) or nil if - ;; unknown. - ;; - ;; If the field's value is greater than `typescript--cache-end', the - ;; value is stale and must be treated as if it were nil. Conversely, - ;; if this field is nil, it is guaranteed that this pitem is open up - ;; to at least `typescript--cache-end'. (This property is handy when - ;; computing whether we're inside a given pitem.) - ;; - (b-end nil)) - -;; The pitem we start parsing with. -(defconst typescript--initial-pitem - (make-typescript--pitem - :paren-depth most-negative-fixnum - :type 'toplevel)) - -;; When we say "jsdoc" here, we mean "jsdoc 3". There exist multiple dialects of -;; "jsdoc documentation". - -;; Note that all typedoc/jsdoc regexp by themselves would match occurrences that appear outside -;; documentation comments. The logic that uses these regexps must guard against it. -(defconst typescript-typedoc-link-tag-regexp - "\\[\\[.*?\\]\\]" - "Matches a typedoc link.") - -(defconst typescript-typedoc-literal-markup-regexp - "\\(`+\\).*?\\1" - "Matches a typedoc keyword markup.") - -(defconst typescript-jsdoc-before-tag-regexp - "\\(?:^\\s-*\\*+\\|/\\*\\*\\)\\s-*" - "Matches everything we allow before the @ of a jsdoc tag.") - -;; This was taken from js2-mode. -(defconst typescript-jsdoc-param-tag-regexp - (concat typescript-jsdoc-before-tag-regexp - "\\(@" - (regexp-opt - '("arg" - "argument" - "param" - "prop" - "property" - "typedef")) - "\\)" - "\\s-*\\({[^}]+}\\)?" ; optional type - "\\s-*\\[?\\([[:alnum:]_$\.]+\\)?\\]?" ; name - "\\_>") - "Matches jsdoc tags with optional type and optional param name.") - -;; This was taken from js2-mode. -;; and extended with tags in http://usejsdoc.org/ -(defconst typescript-jsdoc-typed-tag-regexp - (concat typescript-jsdoc-before-tag-regexp - "\\(@" - (regexp-opt - '("enum" - "extends" - "field" - "id" - "implements" - "lends" - "mods" - "requires" - "return" - "returns" - "throw" - "throws" - "type" - "yield" - "yields")) - "\\)\\s-*\\({[^}]+}\\)?") - "Matches jsdoc tags with optional type.") - -;; This was taken from js2-mode. -;; and extended with tags in http://usejsdoc.org/ -(defconst typescript-jsdoc-arg-tag-regexp - (concat typescript-jsdoc-before-tag-regexp - "\\(@" - (regexp-opt - '("access" - "alias" - "augments" - "base" - "borrows" - "bug" - "callback" - "config" - "default" - "define" - "emits" - "exception" - "extends" - "external" - "fires" - "func" - "function" - "host" - "kind" - "listens" - "member" - "memberof" - "method" - "mixes" - "module" - "name" - "namespace" - "requires" - "since" - "suppress" - "this" - "throws" - "var" - "variation" - "version")) - "\\)\\s-+\\([^ \t]+\\)") - "Matches jsdoc tags with a single argument.") - -;; This was taken from js2-mode -;; and extended with tags in http://usejsdoc.org/ -(defconst typescript-jsdoc-empty-tag-regexp - (concat typescript-jsdoc-before-tag-regexp - "\\(@" - (regexp-opt - '("abstract" - "addon" - "async" - "author" - "class" - "classdesc" - "const" - "constant" - "constructor" - "constructs" - "copyright" - "default" - "defaultvalue" - "deprecated" - "desc" - "description" - "event" - "example" - "exec" - "export" - "exports" - "file" - "fileoverview" - "final" - "func" - "function" - "generator" - "global" - "hidden" - "hideconstructor" - "ignore" - "implicitcast" - "inheritdoc" - "inner" - "instance" - "interface" - "license" - "method" - "mixin" - "noalias" - "noshadow" - "notypecheck" - "override" - "overview" - "owner" - "package" - "preserve" - "preservetry" - "private" - "protected" - "public" - "readonly" - "static" - "summary" - "supported" - "todo" - "tutorial" - "virtual")) - "\\)\\s-*") - "Matches empty jsdoc tags.") - -;; Note that this regexp by itself would match tslint flags that appear inside -;; strings. The logic using this regexp must guard against it. -(defconst typescript-tslint-flag-regexp - "\\(?://\\|/\\*\\)\\s-*\\(tslint:.*?\\)\\(?:\\*/\\|$\\)" - "Matches tslint flags.") - -;;; Faces - -(defface typescript-jsdoc-tag - '((t :foreground "SlateGray")) - "Face used to highlight @whatever tags in jsdoc comments." - :group 'typescript) - -(defface typescript-jsdoc-type - '((t :foreground "SteelBlue")) - "Face used to highlight {FooBar} types in jsdoc comments." - :group 'typescript) - -(defface typescript-jsdoc-value - '((t :foreground "gold4")) - "Face used to highlight tag values in jsdoc comments." - :group 'typescript) - -(defface typescript-access-modifier-face - '((t (:inherit font-lock-keyword-face))) - "Face used to highlight access modifiers." - :group 'typescript) - -(defface typescript-this-face - '((t (:inherit font-lock-keyword-face))) - "Face used to highlight 'this' keyword." - :group 'typescript) - -(defface typescript-primitive-face - '((t (:inherit font-lock-keyword-face))) - "Face used to highlight builtin types." - :group 'typescript) - -;;; User Customization - -(defgroup typescript nil - "Customization variables for typescript mode." - :tag "typescript" - :group 'languages) - -(defcustom typescript-indent-level 4 - "Number of spaces for each indentation step in `typescript-mode'." - :type 'integer - :safe 'integerp - :group 'typescript) -;;;###autoload(put 'typescript-indent-level 'safe-local-variable #'integerp) - -(defcustom typescript-expr-indent-offset 0 - "Number of additional spaces used for indentation of continued expressions. -The value must be no less than minus `typescript-indent-level'." - :type 'integer - :safe 'integerp - :group 'typescript) - -(defcustom typescript-indent-switch-clauses t - "Enable indenting of switch case and default clauses to -replicate tsserver behaviour. Indent level is taken to be -`typescript-indent-level'." - :type 'boolean - :group 'typescript) - -(defcustom typescript-indent-list-items t - "Enable indenting of list items, useful for certain code styles." - :type 'boolean - :group 'typescript) - -(defcustom typescript-auto-indent-flag t - "Whether to automatically indent when typing punctuation characters. -If non-nil, the characters {}();,: also indent the current line -in typescript mode." - :type 'boolean - :group 'typescript) - -(defcustom typescript-flat-functions nil - "Treat nested functions as top-level functions in `typescript-mode'. -This applies to function movement, marking, and so on." - :type 'boolean - :group 'typescript) - -(defcustom typescript-comment-lineup-func #'c-lineup-C-comments - "Lineup function for `cc-mode-style', for C comments in `typescript-mode'." - :type 'function - :group 'typescript) - -(defcustom typescript-enabled-frameworks typescript--available-frameworks - "Frameworks recognized by `typescript-mode'. -To improve performance, you may turn off some frameworks you -seldom use, either globally or on a per-buffer basis." - :type (cons 'set (mapcar (lambda (x) - (list 'const x)) - typescript--available-frameworks)) - :group 'typescript) - -(defcustom typescript-mode-hook nil - "*Hook called by `typescript-mode'." - :type 'hook - :group 'typescript) - -(defcustom typescript-autoconvert-to-template-flag nil - "Non-nil means automatically convert plain strings to templates. - -When the flag is non-nil the `typescript-autoconvert-to-template' -is called whenever a plain string delimiter is typed in the buffer." - :type 'boolean - :group 'typescript) - -;;; Public utilities - -(defun typescript-convert-to-template () - "Convert the string at point to a template string." - (interactive) - (save-restriction - (widen) - (save-excursion - (let* ((syntax (syntax-ppss)) - (str-terminator (nth 3 syntax)) - (string-start (or (and str-terminator (nth 8 syntax)) - ;; We have to consider the case that we're on the start delimiter of a string. - ;; We tentatively take (point) as string-start. If it turns out we're - ;; wrong, then typescript--move-to-end-of-plain-string will fail anway, - ;; and we won't use the bogus value. - (progn - (forward-char) - (point))))) - (when (typescript--move-to-end-of-plain-string) - (let ((end-start (or (nth 8 (syntax-ppss)) -1))) - (undo-boundary) - (when (= end-start string-start) - (delete-char 1) - (insert "`"))) - (goto-char string-start) - (delete-char 1) - (insert "`")))))) - -(defun typescript-autoconvert-to-template () - "Automatically convert a plain string to a teplate string, if needed. - -This function is meant to be automatically invoked when the user -enters plain string delimiters. It checks whether the character -before point is the end of a string. If it is, then it checks -whether the string contains ${...}. If it does, then it converts -the string from a plain string to a template." - (interactive) - (save-restriction - (widen) - (save-excursion - (backward-char) - (when (and (memq (char-after) '(?' ?\")) - (not (eq (char-before) ?\\))) - (let* ((string-start (nth 8 (syntax-ppss)))) - (when (and string-start - (save-excursion - (re-search-backward "\\${.*?}" string-start t))) - (typescript-convert-to-template))))))) - -;;; KeyMap - -(defvar typescript-mode-map - (let ((keymap (make-sparse-keymap))) - (define-key keymap (kbd "C-c '") #'typescript-convert-to-template) - keymap) - "Keymap for `typescript-mode'.") - -(defun typescript--post-self-insert-function () - (when (and (derived-mode-p 'typescript-mode) - typescript-autoconvert-to-template-flag - (or (eq last-command-event ?\') - (eq last-command-event ?\"))) - (typescript-autoconvert-to-template))) - -;;; Syntax table and parsing - -(defvar typescript-mode-syntax-table - (let ((table (make-syntax-table))) - (c-populate-syntax-table table) - (modify-syntax-entry ?$ "_" table) - (modify-syntax-entry ?` "\"" table) - table) - "Syntax table for `typescript-mode'.") - -(defvar typescript--quick-match-re nil - "Autogenerated regexp used by `typescript-mode' to match buffer constructs.") - -(defvar typescript--quick-match-re-func nil - "Autogenerated regexp used by `typescript-mode' to match constructs and functions.") - -(make-variable-buffer-local 'typescript--quick-match-re) -(make-variable-buffer-local 'typescript--quick-match-re-func) - -(defvar typescript--cache-end 1 - "Last valid buffer position for the `typescript-mode' function cache.") -(make-variable-buffer-local 'typescript--cache-end) - -(defvar typescript--last-parse-pos nil - "Latest parse position reached by `typescript--ensure-cache'.") -(make-variable-buffer-local 'typescript--last-parse-pos) - -(defvar typescript--state-at-last-parse-pos nil - "Parse state at `typescript--last-parse-pos'.") -(make-variable-buffer-local 'typescript--state-at-last-parse-pos) - -(defun typescript--flatten-list (list) - (cl-loop for item in list - nconc (cond ((consp item) - (typescript--flatten-list item)) - (item (list item))))) - -(defun typescript--maybe-join (prefix separator suffix &rest list) - "Helper function for `typescript--update-quick-match-re'. -If LIST contains any element that is not nil, return its non-nil -elements, separated by SEPARATOR, prefixed by PREFIX, and ended -with SUFFIX as with `concat'. Otherwise, if LIST is empty, return -nil. If any element in LIST is itself a list, flatten that -element." - (setq list (typescript--flatten-list list)) - (when list - (concat prefix (mapconcat #'identity list separator) suffix))) - -(defun typescript--update-quick-match-re () - "Internal function used by `typescript-mode' for caching buffer constructs. -This updates `typescript--quick-match-re', based on the current set of -enabled frameworks." - (setq typescript--quick-match-re - (typescript--maybe-join - "^[ \t]*\\(?:" "\\|" "\\)" - - ;; #define mumble - "#define[ \t]+[a-zA-Z_]" - - (when (memq 'exttypescript typescript-enabled-frameworks) - "Ext\\.extend") - - (when (memq 'prototype typescript-enabled-frameworks) - "Object\\.extend") - - ;; var mumble = THING ( - (typescript--maybe-join - "\\(?:var[ \t]+\\)?[a-zA-Z_$0-9.]+[ \t]*=[ \t]*\\(?:" - "\\|" - "\\)[ \t]*\(" - - (when (memq 'prototype typescript-enabled-frameworks) - "Class\\.create") - - (when (memq 'exttypescript typescript-enabled-frameworks) - "Ext\\.extend") - - (when (memq 'merrillpress typescript-enabled-frameworks) - "[a-zA-Z_$0-9]+\\.extend\\(?:Final\\)?")) - - (when (memq 'dojo typescript-enabled-frameworks) - "dojo\\.declare[ \t]*\(") - - (when (memq 'mochikit typescript-enabled-frameworks) - "MochiKit\\.Base\\.update[ \t]*\(") - - ;; mumble.prototypeTHING - (typescript--maybe-join - "[a-zA-Z_$0-9.]+\\.prototype\\(?:" "\\|" "\\)" - - (when (memq 'typescript typescript-enabled-frameworks) - '( ;; foo.prototype.bar = function( - "\\.[a-zA-Z_$0-9]+[ \t]*=[ \t]*function[ \t]*\(" - - ;; mumble.prototype = { - "[ \t]*=[ \t]*{"))))) - - (setq typescript--quick-match-re-func - (concat "function\\|" typescript--quick-match-re))) - -(defun typescript--forward-text-property (propname) - "Move over the next value of PROPNAME in the buffer. -If found, return that value and leave point after the character -having that value; otherwise, return nil and leave point at EOB." - (let ((next-value (get-text-property (point) propname))) - (if next-value - (forward-char) - - (goto-char (next-single-property-change - (point) propname nil (point-max))) - (unless (eobp) - (setq next-value (get-text-property (point) propname)) - (forward-char))) - - next-value)) - -(defun typescript--backward-text-property (propname) - "Move over the previous value of PROPNAME in the buffer. -If found, return that value and leave point just before the -character that has that value, otherwise return nil and leave -point at BOB." - (unless (bobp) - (let ((prev-value (get-text-property (1- (point)) propname))) - (if prev-value - (backward-char) - - (goto-char (previous-single-property-change - (point) propname nil (point-min))) - - (unless (bobp) - (backward-char) - (setq prev-value (get-text-property (point) propname)))) - - prev-value))) - -(defsubst typescript--forward-pstate () - (typescript--forward-text-property 'typescript--pstate)) - -(defsubst typescript--backward-pstate () - (typescript--backward-text-property 'typescript--pstate)) - -(defun typescript--pitem-goto-h-end (pitem) - (goto-char (typescript--pitem-h-begin pitem)) - (typescript--forward-pstate)) - -(defun typescript--re-search-forward-inner (regexp &optional bound count) - "Helper function for `typescript--re-search-forward'." - (let ((parse) - str-terminator) - (while (> count 0) - (re-search-forward regexp bound) - (setq parse (syntax-ppss)) - (cond ((setq str-terminator (nth 3 parse)) - (when (eq str-terminator t) - (setq str-terminator ?/)) - (re-search-forward - (concat "\\([^\\]\\|^\\)" (string str-terminator)) - (save-excursion (end-of-line) (point)) t)) - ((nth 7 parse) - (forward-line)) - ((or (nth 4 parse) - (and (eq (char-before) ?\/) (eq (char-after) ?\*))) - (re-search-forward "\\*/")) - (t - (setq count (1- count)))))) - (point)) - - -(defun typescript--re-search-forward (regexp &optional bound noerror count) - "Search forward, ignoring strings and comments. -This function invokes `re-search-forward', but treats the buffer -as if strings and comments have been removed." - (let ((saved-point (point)) - (search-expr - (cond ((null count) - '(typescript--re-search-forward-inner regexp bound 1)) - ((< count 0) - '(typescript--re-search-backward-inner regexp bound (- count))) - ((> count 0) - '(typescript--re-search-forward-inner regexp bound count))))) - (condition-case err - (eval search-expr) - (search-failed - (goto-char saved-point) - (unless noerror - (error (error-message-string err))))))) - - -(defun typescript--re-search-backward-inner (regexp &optional bound count) - "Auxiliary function for `typescript--re-search-backward'." - (let ((parse)) - (while (> count 0) - (re-search-backward regexp bound) - (when (and (> (point) (point-min)) - (save-excursion (backward-char) (looking-at "/[/*]"))) - (forward-char)) - (setq parse (syntax-ppss)) - (cond - ;; If we are in a comment or a string, jump back to the start - ;; of the comment or string. - ((nth 8 parse) - (goto-char (nth 8 parse))) - ((and (eq (char-before) ?/) (eq (char-after) ?*)) - (re-search-backward "/\\*")) - (t - (setq count (1- count)))))) - (point)) - - -(defun typescript--re-search-backward (regexp &optional bound noerror count) - "Search backward, ignoring strings, and comments. - -This function invokes `re-search-backward' but treats the buffer -as if strings and comments have been removed. - -IMPORTANT NOTE: searching for \"\\n\" with this function to find -line breaks will generally not work, because the final newline of -a one-line comment is considered to be part of the comment and -will be skipped. Take the following code: - - let a = 1; - let b = 2; // Foo - let c = 3; - -If the point is in the last line, searching back for \"\\n\" will -skip over the line with \"let b\". The newline found will be the -one at the end of the line with \"let a\"." - (let ((saved-point (point)) - (search-expr - (cond ((null count) - `(typescript--re-search-backward-inner ,regexp ,bound 1)) - ((< count 0) - `(typescript--re-search-forward-inner ,regexp ,bound (- ,count))) - ((> count 0) - `(typescript--re-search-backward-inner ,regexp ,bound ,count))))) - (condition-case err - (eval search-expr) - (search-failed - (goto-char saved-point) - (unless noerror - (error (error-message-string err))))))) - -(defun typescript--forward-expression () - "Move forward over a whole typescript expression. -This function doesn't move over expressions continued across -lines." - (cl-loop - do (progn - (forward-comment most-positive-fixnum) - (cl-loop until (or (eolp) - (progn - (forward-comment most-positive-fixnum) - (memq (char-after) '(?\, ?\; ?\] ?\) ?\})))) - do (forward-sexp))) - while (and (eq (char-after) ?\n) - (save-excursion - (forward-char) - (typescript--continued-expression-p))))) - -(defun typescript--forward-function-decl () - "Move forward over a typescript function declaration. -This puts point at the 'function' keyword. - -If this is a syntactically-correct non-expression function, -return the name of the function, or t if the name could not be -determined. Otherwise, return nil." - (cl-assert (looking-at "\\_<function\\_>")) - (let ((name t)) - (forward-word) - (forward-comment most-positive-fixnum) - (when (looking-at typescript--name-re) - (setq name (match-string-no-properties 0)) - (goto-char (match-end 0))) - (forward-comment most-positive-fixnum) - (and (eq (char-after) ?\( ) - (ignore-errors (forward-list) t) - (progn (forward-comment most-positive-fixnum) - (and (eq (char-after) ?{) - name))))) - -(defun typescript--function-prologue-beginning (&optional pos) - "Return the start of the typescript function prologue containing POS. -A function prologue is everything from start of the definition up -to and including the opening brace. POS defaults to point. -If POS is not in a function prologue, return nil." - (let (prologue-begin) - (save-excursion - (if pos - (goto-char pos) - (setq pos (point))) - - (when (save-excursion - (forward-line 0) - (or (looking-at typescript--function-heading-2-re) - (looking-at typescript--function-heading-3-re))) - - (setq prologue-begin (match-beginning 1)) - (when (<= prologue-begin pos) - (goto-char (match-end 0)))) - - (skip-syntax-backward "w_") - (and (or (looking-at "\\_<function\\_>") - (typescript--re-search-backward "\\_<function\\_>" nil t)) - - (save-match-data (goto-char (match-beginning 0)) - (typescript--forward-function-decl)) - - (<= pos (point)) - (or prologue-begin (match-beginning 0)))))) - -(defun typescript--beginning-of-defun-raw () - "Helper function for `typescript-beginning-of-defun'. -Go to previous defun-beginning and return the parse state for it, -or nil if we went all the way back to bob and don't find -anything." - (typescript--ensure-cache) - (let (pstate) - (while (and (setq pstate (typescript--backward-pstate)) - (not (eq 'function (typescript--pitem-type (car pstate)))))) - (and (not (bobp)) pstate))) - -(defun typescript--pstate-is-toplevel-defun (pstate) - "Helper function for `typescript--beginning-of-defun-nested'. -If PSTATE represents a non-empty top-level defun, return the -top-most pitem. Otherwise, return nil." - (cl-loop for pitem in pstate - with func-depth = 0 - with func-pitem - if (eq 'function (typescript--pitem-type pitem)) - do (cl-incf func-depth) - and do (setq func-pitem pitem) - finally return (if (eq func-depth 1) func-pitem))) - -(defun typescript--beginning-of-defun-nested () - "Helper function for `typescript--beginning-of-defun'. -Return the pitem of the function we went to the beginning of." - (or - ;; Look for the smallest function that encloses point... - (cl-loop for pitem in (typescript--parse-state-at-point) - if (and (eq 'function (typescript--pitem-type pitem)) - (typescript--inside-pitem-p pitem)) - do (goto-char (typescript--pitem-h-begin pitem)) - and return pitem) - - ;; ...and if that isn't found, look for the previous top-level - ;; defun - (cl-loop for pstate = (typescript--backward-pstate) - while pstate - if (typescript--pstate-is-toplevel-defun pstate) - do (goto-char (typescript--pitem-h-begin it)) - and return it))) - -(defun typescript--beginning-of-defun-flat () - "Helper function for `typescript-beginning-of-defun'." - (let ((pstate (typescript--beginning-of-defun-raw))) - (when pstate - (goto-char (typescript--pitem-h-begin (car pstate)))))) - -(defun typescript-beginning-of-defun (&optional arg) - "Value of `beginning-of-defun-function' for `typescript-mode'." - (setq arg (or arg 1)) - (while (and (not (eobp)) (< arg 0)) - (cl-incf arg) - (when (and (not typescript-flat-functions) - (or (eq (typescript-syntactic-context) 'function) - (typescript--function-prologue-beginning))) - (typescript-end-of-defun)) - - (if (typescript--re-search-forward - "\\_<function\\_>" nil t) - (goto-char (typescript--function-prologue-beginning)) - (goto-char (point-max)))) - - (while (> arg 0) - (cl-decf arg) - ;; If we're just past the end of a function, the user probably wants - ;; to go to the beginning of *that* function - (when (eq (char-before) ?}) - (backward-char)) - - (let ((prologue-begin (typescript--function-prologue-beginning))) - (cond ((and prologue-begin (< prologue-begin (point))) - (goto-char prologue-begin)) - - (typescript-flat-functions - (typescript--beginning-of-defun-flat)) - (t - (typescript--beginning-of-defun-nested)))))) - -(defun typescript--flush-caches (&optional beg ignored) - "Flush the `typescript-mode' syntax cache after position BEG. -BEG defaults to `point-min', meaning to flush the entire cache." - (interactive) - (setq beg (or beg (save-restriction (widen) (point-min)))) - (setq typescript--cache-end (min typescript--cache-end beg))) - -(defmacro typescript--debug (&rest arguments) - ;; `(message ,@arguments) - ) - -(defun typescript--ensure-cache--pop-if-ended (open-items paren-depth) - (let ((top-item (car open-items))) - (when (<= paren-depth (typescript--pitem-paren-depth top-item)) - (cl-assert (not (get-text-property (1- (point)) 'typescript-pend))) - (put-text-property (1- (point)) (point) 'typescript--pend top-item) - (setf (typescript--pitem-b-end top-item) (point)) - (setq open-items - ;; open-items must contain at least two items for this to - ;; work, but because we push a dummy item to start with, - ;; that assumption holds. - (cons (typescript--pitem-add-child (cl-second open-items) top-item) - (cddr open-items))))) - open-items) - -(defmacro typescript--ensure-cache--update-parse () - "Helper function for `typescript--ensure-cache'. -Update parsing information up to point, referring to parse, -prev-parse-point, goal-point, and open-items bound lexically in -the body of `typescript--ensure-cache'." - `(progn - (setq goal-point (point)) - (goto-char prev-parse-point) - (while (progn - (setq open-items (typescript--ensure-cache--pop-if-ended - open-items (car parse))) - ;; Make sure parse-partial-sexp doesn't stop because we *entered* - ;; the given depth -- i.e., make sure we're deeper than the target - ;; depth. - (cl-assert (> (nth 0 parse) - (typescript--pitem-paren-depth (car open-items)))) - (setq parse (parse-partial-sexp - prev-parse-point goal-point - (typescript--pitem-paren-depth (car open-items)) - nil parse)) - -;; (let ((overlay (make-overlay prev-parse-point (point)))) -;; (overlay-put overlay 'face '(:background "red")) -;; (unwind-protect -;; (progn -;; (typescript--debug "parsed: %S" parse) -;; (sit-for 1)) -;; (delete-overlay overlay))) - - (setq prev-parse-point (point)) - (< (point) goal-point))) - - (setq open-items (typescript--ensure-cache--pop-if-ended - open-items (car parse))))) - -(defun typescript--show-cache-at-point () - (interactive) - (require 'pp) - (let ((prop (get-text-property (point) 'typescript--pstate))) - (with-output-to-temp-buffer "*Help*" - (pp prop)))) - -(defun typescript--split-name (string) - "Split a typescript name into its dot-separated parts. -This also removes any prototype parts from the split name -\(unless the name is just \"prototype\" to start with)." - (let ((name (save-match-data - (split-string string "\\." t)))) - (unless (and (= (length name) 1) - (equal (car name) "prototype")) - - (setq name (remove "prototype" name))))) - -(defvar typescript--guess-function-name-start nil) - -(defun typescript--guess-function-name (position) - "Guess the name of the typescript function at POSITION. -POSITION should be just after the end of the word \"function\". -Return the name of the function, or nil if the name could not be -guessed. - -This function clobbers match data. If we find the preamble -begins earlier than expected while guessing the function name, -set `typescript--guess-function-name-start' to that position; otherwise, -set that variable to nil." - (setq typescript--guess-function-name-start nil) - (save-excursion - (goto-char position) - (forward-line 0) - (cond - ((looking-at typescript--function-heading-3-re) - (and (eq (match-end 0) position) - (setq typescript--guess-function-name-start (match-beginning 1)) - (match-string-no-properties 1))) - - ((looking-at typescript--function-heading-2-re) - (and (eq (match-end 0) position) - (setq typescript--guess-function-name-start (match-beginning 1)) - (match-string-no-properties 1)))))) - -(defun typescript--clear-stale-cache () - ;; Clear any endings that occur after point - (let (end-prop) - (save-excursion - (while (setq end-prop (typescript--forward-text-property - 'typescript--pend)) - (setf (typescript--pitem-b-end end-prop) nil)))) - - ;; Remove any cache properties after this point - (remove-text-properties (point) (point-max) - '(typescript--pstate t typescript--pend t))) - -(defun typescript--ensure-cache (&optional limit) - "Ensures brace cache is valid up to the character before LIMIT. -LIMIT defaults to point." - (setq limit (or limit (point))) - (when (< typescript--cache-end limit) - - (c-save-buffer-state - (open-items - orig-match-start - orig-match-end - orig-depth - parse - prev-parse-point - name - case-fold-search - filtered-class-styles - new-item - goal-point - end-prop) - - ;; Figure out which class styles we need to look for - (setq filtered-class-styles - (cl-loop for style in typescript--class-styles - if (memq (plist-get style :framework) - typescript-enabled-frameworks) - collect style)) - - (save-excursion - (save-restriction - (widen) - - ;; Find last known good position - (goto-char typescript--cache-end) - (unless (bobp) - (setq open-items (get-text-property - (1- (point)) 'typescript--pstate)) - - (unless open-items - (goto-char (previous-single-property-change - (point) 'typescript--pstate nil (point-min))) - - (unless (bobp) - (setq open-items (get-text-property (1- (point)) - 'typescript--pstate)) - (cl-assert open-items)))) - - (unless open-items - ;; Make a placeholder for the top-level definition - (setq open-items (list typescript--initial-pitem))) - - (setq parse (syntax-ppss)) - (setq prev-parse-point (point)) - - (typescript--clear-stale-cache) - - (narrow-to-region (point-min) limit) - - (cl-loop while (re-search-forward typescript--quick-match-re-func nil t) - for orig-match-start = (goto-char (match-beginning 0)) - for orig-match-end = (match-end 0) - do (typescript--ensure-cache--update-parse) - for orig-depth = (nth 0 parse) - - ;; Each of these conditions should return non-nil if - ;; we should add a new item and leave point at the end - ;; of the new item's header (h-end in the - ;; typescript--pitem diagram). This point is the one - ;; after the last character we need to unambiguously - ;; detect this construct. If one of these evaluates to - ;; nil, the location of the point is ignored. - if (cond - ;; In comment or string - ((nth 8 parse) nil) - - ;; Regular function declaration - ((and (looking-at "\\_<function\\_>") - (setq name (typescript--forward-function-decl))) - - (when (eq name t) - (setq name (typescript--guess-function-name orig-match-end)) - (if name - (when typescript--guess-function-name-start - (setq orig-match-start - typescript--guess-function-name-start)) - - (setq name t))) - - (cl-assert (eq (char-after) ?{)) - (forward-char) - (make-typescript--pitem - :paren-depth orig-depth - :h-begin orig-match-start - :type 'function - :name (if (eq name t) - name - (typescript--split-name name)))) - - ;; "Prototype function" declaration - ((looking-at typescript--plain-method-re) - (goto-char (match-beginning 3)) - (when (save-match-data - (typescript--forward-function-decl)) - (forward-char) - (make-typescript--pitem - :paren-depth orig-depth - :h-begin orig-match-start - :type 'function - :name (nconc (typescript--split-name - (match-string-no-properties 1)) - (list (match-string-no-properties 2)))))) - - ;; Class definition - ((cl-loop with syntactic-context = - (typescript--syntactic-context-from-pstate open-items) - for class-style in filtered-class-styles - if (and (memq syntactic-context - (plist-get class-style :contexts)) - (looking-at (plist-get class-style - :class-decl))) - do (goto-char (match-end 0)) - and return - (make-typescript--pitem - :paren-depth orig-depth - :h-begin orig-match-start - :type class-style - :name (typescript--split-name - (match-string-no-properties 1)))))) - - do (typescript--ensure-cache--update-parse) - and do (push it open-items) - and do (put-text-property - (1- (point)) (point) 'typescript--pstate open-items) - else do (goto-char orig-match-end)) - - (goto-char limit) - (typescript--ensure-cache--update-parse) - (setq typescript--cache-end limit) - (setq typescript--last-parse-pos limit) - (setq typescript--state-at-last-parse-pos open-items)))))) - -(defun typescript--end-of-defun-flat () - "Helper function for `typescript-end-of-defun'." - (cl-loop while (typescript--re-search-forward "}" nil t) - do (typescript--ensure-cache) - if (get-text-property (1- (point)) 'typescript--pend) - if (eq 'function (typescript--pitem-type it)) - return t - finally do (goto-char (point-max)))) - -(defun typescript--end-of-defun-nested () - "Helper function for `typescript-end-of-defun'." - (let* (pitem - (this-end (save-excursion - (and (setq pitem (typescript--beginning-of-defun-nested)) - (typescript--pitem-goto-h-end pitem) - (progn (backward-char) - (forward-list) - (point))))) - found) - - (if (and this-end (< (point) this-end)) - ;; We're already inside a function; just go to its end. - (goto-char this-end) - - ;; Otherwise, go to the end of the next function... - (while (and (typescript--re-search-forward "\\_<function\\_>" nil t) - (not (setq found (progn - (goto-char (match-beginning 0)) - (typescript--forward-function-decl)))))) - - (if found (forward-list) - ;; ... or eob. - (goto-char (point-max)))))) - -(defun typescript-end-of-defun (&optional arg) - "Value of `end-of-defun-function' for `typescript-mode'." - (setq arg (or arg 1)) - (while (and (not (bobp)) (< arg 0)) - (cl-incf arg) - (typescript-beginning-of-defun) - (typescript-beginning-of-defun) - (unless (bobp) - (typescript-end-of-defun))) - - (while (> arg 0) - (cl-decf arg) - ;; look for function backward. if we're inside it, go to that - ;; function's end. otherwise, search for the next function's end and - ;; go there - (if typescript-flat-functions - (typescript--end-of-defun-flat) - - ;; if we're doing nested functions, see whether we're in the - ;; prologue. If we are, go to the end of the function; otherwise, - ;; call typescript--end-of-defun-nested to do the real work - (let ((prologue-begin (typescript--function-prologue-beginning))) - (cond ((and prologue-begin (<= prologue-begin (point))) - (goto-char prologue-begin) - (re-search-forward "\\_<function") - (goto-char (match-beginning 0)) - (typescript--forward-function-decl) - (forward-list)) - - (t (typescript--end-of-defun-nested))))))) - -(defun typescript--backward-syntactic-ws (&optional lim) - "Simple implementation of `c-backward-syntactic-ws' for `typescript-mode'." - (save-restriction - (when lim (narrow-to-region lim (point-max))) - - (let ((pos (point))) - (while (progn (forward-comment most-negative-fixnum) - (/= (point) - (prog1 - pos - (setq pos (point))))))))) - -(defun typescript--forward-syntactic-ws (&optional lim) - "Simple implementation of `c-forward-syntactic-ws' for `typescript-mode'." - (save-restriction - (when lim (narrow-to-region (point-min) lim)) - (let ((pos (point))) - (while (progn - (forward-comment most-positive-fixnum) - (/= (point) - (prog1 - pos - (setq pos (point))))))))) - -;; Like (up-list -1), but only considers lists that end nearby" -(defun typescript--up-nearby-list () - (save-restriction - ;; Look at a very small region so our compuation time doesn't - ;; explode in pathological cases. - (narrow-to-region (max (point-min) (- (point) 500)) (point)) - (up-list -1))) - -(defun typescript--inside-param-list-p () - "Return non-nil iff point is in a function parameter list." - (ignore-errors - (save-excursion - (typescript--up-nearby-list) - (and (looking-at "(") - (progn (forward-symbol -1) - (or (looking-at "function") - (progn (forward-symbol -1) - (looking-at "function")))))))) - -(defun typescript--inside-dojo-class-list-p () - "Return non-nil iff point is in a Dojo multiple-inheritance class block." - (ignore-errors - (save-excursion - (typescript--up-nearby-list) - (let ((list-begin (point))) - (forward-line 0) - (and (looking-at typescript--dojo-class-decl-re) - (goto-char (match-end 0)) - (looking-at "\"\\s-*,\\s-*\\[") - (eq (match-end 0) (1+ list-begin))))))) - -(defun typescript--syntax-begin-function () - (when (< typescript--cache-end (point)) - (goto-char (max (point-min) typescript--cache-end))) - - (let ((pitem)) - (while (and (setq pitem (car (typescript--backward-pstate))) - (not (eq 0 (typescript--pitem-paren-depth pitem))))) - - (when pitem - (goto-char (typescript--pitem-h-begin pitem ))))) - -(defun typescript--move-to-end-of-plain-string () - "If the point is in a plain string, move to the end of it. - -Otherwise, don't move. A plain string is a string which is not a -template string. The point is considered to be \"in\" a string if -it is on the delimiters of the string, or any point inside. - -Returns point if the end of the string was found, or nil if the -end of the string was not found." - (let ((end-position - (save-excursion - (let* ((syntax (syntax-ppss)) - (str-terminator (nth 3 syntax)) - ;; The 8th element will also be set if we are in a comment. So we - ;; check str-terminator to protect against that. - (string-start (and str-terminator - (nth 8 syntax)))) - (if (and string-start - (not (eq str-terminator ?`))) - ;; We may already be at the end of the string. - (if (and (eq (char-after) str-terminator) - (not (eq (char-before) ?\\))) - (point) - ;; We just search forward and then check if the hit we get has a - ;; string-start equal to ours. - (cl-loop while (re-search-forward - (concat "\\(?:[^\\]\\|^\\)\\(" (string str-terminator) "\\)") - nil t) - if (eq string-start - (save-excursion (nth 8 (syntax-ppss (match-beginning 1))))) - return (match-beginning 1))) - ;; If we are on the start delimiter then the value of syntax-ppss will look - ;; like we're not in a string at all, but this function considers the - ;; start delimiter to be "in" the string. We take care of this here. - (when (memq (char-after) '(?' ?\")) - (forward-char) - (typescript--move-to-end-of-plain-string))))))) - (when end-position - (goto-char end-position)))) - -;;; Font Lock -(defun typescript--make-framework-matcher (framework &rest regexps) - "Helper function for building `typescript--font-lock-keywords'. -Create a byte-compiled function for matching a concatenation of -REGEXPS, but only if FRAMEWORK is in `typescript-enabled-frameworks'." - (setq regexps (apply #'concat regexps)) - (byte-compile - `(lambda (limit) - (when (memq (quote ,framework) typescript-enabled-frameworks) - (re-search-forward ,regexps limit t))))) - -(defvar typescript--tmp-location nil) -(make-variable-buffer-local 'typescript--tmp-location) - -(defun typescript--forward-destructuring-spec (&optional func) - "Move forward over a typescript destructuring spec. -If FUNC is supplied, call it with no arguments before every -variable name in the spec. Return true iff this was actually a -spec. FUNC must preserve the match data." - (cl-case (char-after) - (?\[ - (forward-char) - (while - (progn - (forward-comment most-positive-fixnum) - (cond ((memq (char-after) '(?\[ ?\{)) - (typescript--forward-destructuring-spec func)) - - ((eq (char-after) ?,) - (forward-char) - t) - - ((looking-at typescript--name-re) - (and func (funcall func)) - (goto-char (match-end 0)) - t)))) - (when (eq (char-after) ?\]) - (forward-char) - t)) - - (?\{ - (forward-char) - (forward-comment most-positive-fixnum) - (while - (when (looking-at typescript--objfield-re) - (goto-char (match-end 0)) - (forward-comment most-positive-fixnum) - (and (cond ((memq (char-after) '(?\[ ?\{)) - (typescript--forward-destructuring-spec func)) - ((looking-at typescript--name-re) - (and func (funcall func)) - (goto-char (match-end 0)) - t)) - (progn (forward-comment most-positive-fixnum) - (when (eq (char-after) ?\,) - (forward-char) - (forward-comment most-positive-fixnum) - t))))) - (when (eq (char-after) ?\}) - (forward-char) - t)))) - -(defun typescript--variable-decl-matcher (limit) - "Font-lock matcher for variable names in a variable declaration. -This is a cc-mode-style matcher that *always* fails, from the -point of view of font-lock. It applies highlighting directly with -`font-lock-apply-highlight'." - (condition-case nil - (save-restriction - (narrow-to-region (point-min) limit) - - (let ((first t)) - (forward-comment most-positive-fixnum) - (while - (and (or first - (when (eq (char-after) ?,) - (forward-char) - (forward-comment most-positive-fixnum) - t)) - (cond ((looking-at typescript--name-re) - (font-lock-apply-highlight - '(0 font-lock-variable-name-face)) - (goto-char (match-end 0))) - - ((save-excursion - (typescript--forward-destructuring-spec)) - - (typescript--forward-destructuring-spec - (lambda () - (font-lock-apply-highlight - '(0 font-lock-variable-name-face))))))) - - (forward-comment most-positive-fixnum) - (when (eq (char-after) ?=) - (forward-char) - (typescript--forward-expression) - (forward-comment most-positive-fixnum)) - - (setq first nil)))) - - ;; Conditions to handle - (scan-error nil) - (end-of-buffer nil)) - - ;; Matcher always "fails" - nil) - -(defun typescript--in-documentation-comment-p () - "Reports whether point is inside a documentation comment." - (let ((parse (syntax-ppss))) - (and - (nth 4 parse) ;; Inside a comment ... - (save-match-data - (save-excursion - (goto-char (nth 8 parse)) - (looking-at "/\\*\\*")))))) ;; ... which starts with /** - -(defun typescript--documentation-font-lock-helper (re limit) - "This is a helper macro that determines whether jsdoc highlighting is to be applied, -and searches for the next token to be highlighted." - (cl-loop while (re-search-forward re limit t) - if (typescript--in-documentation-comment-p) - return (point))) - -(defun typescript--jsdoc-param-matcher (limit) - "Font-lock mode matcher that finds jsdoc parameter tags in documentation." - (typescript--documentation-font-lock-helper typescript-jsdoc-param-tag-regexp limit)) - -(defun typescript--jsdoc-typed-tag-matcher (limit) - "Font-lock mode matcher that finds jsdoc typed tags in documentation." - (typescript--documentation-font-lock-helper typescript-jsdoc-typed-tag-regexp limit)) - -(defun typescript--jsdoc-arg-tag-matcher (limit) - "Font-lock mode matcher that finds jsdoc tags that take one argument in documentation." - (typescript--documentation-font-lock-helper typescript-jsdoc-arg-tag-regexp limit)) - -(defun typescript--jsdoc-empty-tag-matcher (limit) - "Font-lock mode matcher that finds jsdoc tags without argument in documentation." - (typescript--documentation-font-lock-helper typescript-jsdoc-empty-tag-regexp limit)) - -(defun typescript--typedoc-link-matcher (limit) - "Font-lock mode matcher that finds typedoc links in documentation." - (typescript--documentation-font-lock-helper typescript-typedoc-link-tag-regexp limit)) - -(defun typescript--typedoc-literal-markup-matcher (limit) - "Font-lock mode matcher that finds typedoc literal markup in documentation." - (typescript--documentation-font-lock-helper typescript-typedoc-literal-markup-regexp limit)) - -(defun typescript--tslint-flag-matcher (limit) - "Font-lock mode matcher that finds tslint flags in comments." - (cl-loop while (re-search-forward typescript-tslint-flag-regexp limit t) - if (nth 4 (syntax-ppss (match-beginning 1))) - return (point))) - -(defconst typescript--font-lock-keywords-3 - `( - ,@typescript--font-lock-keywords-2 - - (typescript--jsdoc-param-matcher (1 'typescript-jsdoc-tag t t) - (2 'typescript-jsdoc-type t t) - (3 'typescript-jsdoc-value t t)) - - (typescript--jsdoc-typed-tag-matcher (1 'typescript-jsdoc-tag t t) - (2 'typescript-jsdoc-type t t)) - - (typescript--jsdoc-arg-tag-matcher (1 'typescript-jsdoc-tag t t) - (2 'typescript-jsdoc-value t t)) - - (typescript--jsdoc-empty-tag-matcher (1 'typescript-jsdoc-tag t t)) - - (typescript--typedoc-link-matcher (0 'typescript-jsdoc-value t)) - - (typescript--typedoc-literal-markup-matcher - (0 'typescript-jsdoc-value t)) - - (typescript--tslint-flag-matcher - (1 font-lock-preprocessor-face t)) - - ("\\.\\(prototype\\)\\_>" - (1 font-lock-constant-face)) - - (,(rx symbol-start "class" (+ space) (group (+ (or (syntax word) (syntax symbol))))) - (1 font-lock-type-face)) - - (,(rx symbol-start "extends" (+ space) (group (+ (or (syntax word) (syntax symbol))))) - (1 font-lock-type-face)) - - (,(rx symbol-start "implements" (+ space)) - (,(rx symbol-start (+ (syntax word))) nil nil (0 font-lock-type-face))) - - (,(rx symbol-start "interface" (+ space) (group (+ (or (syntax word) (syntax symbol))))) - (1 font-lock-type-face)) - - (,(rx symbol-start "type" (+ space) (group (+ (or (syntax word) (syntax symbol))))) - (1 font-lock-type-face)) - - (,(rx symbol-start "enum" (+ space) (group (+ (or (syntax word) (syntax symbol))))) - (1 font-lock-type-face)) - - ;; Highlights class being declared, in parts - (typescript--class-decl-matcher - ,(concat "\\(" typescript--name-re "\\)\\(?:\\.\\|.*$\\)") - (goto-char (match-beginning 1)) - nil - (1 font-lock-type-face)) - - ;; Highlights parent class, in parts, if available - (typescript--class-decl-matcher - ,(concat "\\(" typescript--name-re "\\)\\(?:\\.\\|.*$\\)") - (if (match-beginning 2) - (progn - (setq typescript--tmp-location (match-end 2)) - (goto-char typescript--tmp-location) - (insert "=") - (goto-char (match-beginning 2))) - (setq typescript--tmp-location nil) - (goto-char (point-at-eol))) - (when typescript--tmp-location - (save-excursion - (goto-char typescript--tmp-location) - (delete-char 1))) - (1 font-lock-type-face)) - - ;; Highlights parent class - (typescript--class-decl-matcher - (2 font-lock-type-face nil t)) - - ;; Dojo needs its own matcher to override the string highlighting - (,(typescript--make-framework-matcher - 'dojo - "^\\s-*dojo\\.declare\\s-*(\"" - "\\(" typescript--dotted-name-re "\\)" - "\\(?:\"\\s-*,\\s-*\\(" typescript--dotted-name-re "\\)\\)?") - (1 font-lock-type-face t) - (2 font-lock-type-face nil t)) - - ;; Match Dojo base classes. Of course Mojo has to be different - ;; from everything else under the sun... - (,(typescript--make-framework-matcher - 'dojo - "^\\s-*dojo\\.declare\\s-*(\"" - "\\(" typescript--dotted-name-re "\\)\"\\s-*,\\s-*\\[") - ,(concat "[[,]\\s-*\\(" typescript--dotted-name-re "\\)\\s-*" - "\\(?:\\].*$\\)?") - (backward-char) - (end-of-line) - (1 font-lock-type-face)) - - ;; continued Dojo base-class list - (,(typescript--make-framework-matcher - 'dojo - "^\\s-*" typescript--dotted-name-re "\\s-*[],]") - ,(concat "\\(" typescript--dotted-name-re "\\)" - "\\s-*\\(?:\\].*$\\)?") - (if (save-excursion (backward-char) - (typescript--inside-dojo-class-list-p)) - (forward-symbol -1) - (end-of-line)) - (end-of-line) - (1 font-lock-type-face)) - - ;; variable declarations - ,(list - (concat "\\_<\\(const\\|var\\|let\\)\\_>\\|" typescript--basic-type-re) - (list #'typescript--variable-decl-matcher nil nil nil)) - - ;; class instantiation - ,(list - (concat "\\_<new\\_>\\s-+\\(" typescript--dotted-name-re "\\)") - (list 1 'font-lock-type-face)) - - ;; instanceof - ,(list - (concat "\\_<instanceof\\_>\\s-+\\(" typescript--dotted-name-re "\\)") - (list 1 'font-lock-type-face)) - - ;; formal parameters - ,(list - (concat - "\\_<function\\_>\\(\\s-+" typescript--name-re "\\)?\\s-*\\(<.*>\\)?\\s-*(\\s-*" - typescript--name-start-re) - (list (concat "\\(" typescript--name-re "\\)\\(\\s-*).*\\)?") - '(backward-char) - '(end-of-line) - '(1 font-lock-variable-name-face))) - - ;; continued formal parameter list - ,(list - (concat - "^\\s-*" typescript--name-re "\\s-*[,)]") - (list typescript--name-re - '(if (save-excursion (backward-char) - (typescript--inside-param-list-p)) - (forward-symbol -1) - (end-of-line)) - '(end-of-line) - '(0 font-lock-variable-name-face)))) - "Level three font lock for `typescript-mode'.") - -(defun typescript--flyspell-mode-predicate () - "A custom predicate to help `flyspell-prog-mode' determine whether a word should be checked." - ;; We depend on fontification for our results. font-lock-ensure is defined on - ;; Emacs 25 and over. Earlier versions use font-lock-fontify-buffer. - (if (fboundp 'font-lock-ensure) - (font-lock-ensure) - (font-lock-fontify-buffer)) - (and - ;; Check with the default method that flyspell provides. - (flyspell-generic-progmode-verify) - - ;; - ;; And eliminate cases specific to our mode we don't want to have - ;; spell-checked. - ;; - - ;; Don't check the module names in import statements. - (save-excursion - (not (let* ((parse (syntax-ppss (1- (point)))) - (string-start-pos (and (nth 3 parse) - (nth 8 parse)))) - (and string-start-pos - (save-match-data - ;; Move to back to the start of the string, then past any ws - ;; and then past any non-ws to see if we have "from" or "import". - (goto-char string-start-pos) - (typescript--backward-syntactic-ws) - (skip-syntax-backward "^-" (point-at-bol)) - (looking-at "from\\|import\\s-")))))))) - -(defun typescript--inside-pitem-p (pitem) - "Return whether point is inside the given pitem's header or body." - (typescript--ensure-cache) - (cl-assert (typescript--pitem-h-begin pitem)) - (cl-assert (typescript--pitem-paren-depth pitem)) - - (and (> (point) (typescript--pitem-h-begin pitem)) - (or (null (typescript--pitem-b-end pitem)) - (> (typescript--pitem-b-end pitem) (point))))) - -(defun typescript--parse-state-at-point () - "Parse the typescript program state at point. -Return a list of `typescript--pitem' instances that apply to point, most -specific first. In the worst case, the current toplevel instance -will be returned." - (save-excursion - (save-restriction - (widen) - (typescript--ensure-cache) - (let* ((bound (if (eobp) (point) (1+ (point)))) - (pstate (or (save-excursion - (typescript--backward-pstate)) - (list typescript--initial-pitem)))) - - ;; Loop until we either hit a pitem at BOB or pitem ends after - ;; point (or at point if we're at eob) - (cl-loop for pitem = (car pstate) - until (or (eq (typescript--pitem-type pitem) - 'toplevel) - (typescript--inside-pitem-p pitem)) - do (pop pstate)) - - pstate)))) - -(defun typescript--syntactic-context-from-pstate (pstate) - "Return the typescript syntactic context corresponding to PSTATE." - (let ((type (typescript--pitem-type (car pstate)))) - (cond ((memq type '(function macro)) - type) - ((consp type) - 'class) - (t 'toplevel)))) - -(defun typescript-syntactic-context () - "Return the typescript syntactic context at point. -When called interatively, also display a message with that -context." - (interactive) - (let* ((syntactic-context (typescript--syntactic-context-from-pstate - (typescript--parse-state-at-point)))) - - (when (called-interactively-p 'interactive) - (message "Syntactic context: %s" syntactic-context)) - - syntactic-context)) - -(defun typescript--class-decl-matcher (limit) - "Font lock function used by `typescript-mode'. -This performs fontification according to `typescript--class-styles'." - (cl-loop initially (typescript--ensure-cache limit) - while (re-search-forward typescript--quick-match-re limit t) - for orig-end = (match-end 0) - do (goto-char (match-beginning 0)) - if (cl-loop for style in typescript--class-styles - for decl-re = (plist-get style :class-decl) - if (and (memq (plist-get style :framework) - typescript-enabled-frameworks) - (memq (typescript-syntactic-context) - (plist-get style :contexts)) - decl-re - (looking-at decl-re)) - do (goto-char (match-end 0)) - and return t) - return t - else do (goto-char orig-end))) - -(defconst typescript--font-lock-keywords-4 - `( - ;; highlights that override previous levels - ;; - - ;; special highlight for `this' keyword - ("\\(this\\)\\." - (1 'typescript-this-face)) - - (,typescript--access-modifier-re (1 'typescript-access-modifier-face)) - (,typescript--basic-type-re (1 'typescript-primitive-face)) - - ;; generics support - ,(list - (concat typescript--name-re "\\s-*" "<\\s-*" typescript--name-start-re) - (list (concat "\\(" typescript--name-re "\\)\\(\\s-*>[^<]*\\)?") - '(backward-char) - '(end-of-line) - '(1 font-lock-type-face))) - - ;; type-highlighting in variable/parameter declarations - ;; supports a small variety of common declarations: - ;; - let a: SomeType; - ;; - private b: SomeType; - ;; - private someFunc(var: SomeType) { - ;; - private array: SomeType[] - ;; - private generic: SomeType<Foo> - ;; - private genericArray: SomeType<Foo>[] - ;; - function testFunc(): SomeType<> { - ;; TODO: namespaced classes! - ,(list - (concat ":\\s-\\(" typescript--type-name-re "\\)\\(<" typescript--type-name-re ">\\)?\\(\[\]\\)?\\([,;]\\)?\\s-*{?") - '(1 'font-lock-type-face)) - - ;; type-casts - ,(list - (concat "<\\(" typescript--type-name-re "\\)>") - '(1 'font-lock-type-face)) - - ;; highlights that append to previous levels - ;; - ,@typescript--font-lock-keywords-3 - - (,typescript--decorator-re (1 font-lock-function-name-face)) - (,typescript--function-call-re (1 font-lock-function-name-face)) - (,typescript--builtin-re (1 font-lock-type-face)) - - ;; arrow function - ("\\(=>\\)" - (1 font-lock-keyword-face))) - "Level four font lock for `typescript-mode'.") - -(defconst typescript--font-lock-keywords - '(typescript--font-lock-keywords-4 typescript--font-lock-keywords-1 - typescript--font-lock-keywords-2 - typescript--font-lock-keywords-3 - typescript--font-lock-keywords-4) - "Font lock keywords for `typescript-mode'. See `font-lock-keywords'.") - -;;; Propertize - -;; -;; The propertize code was adapted from: -;; https://github.com/emacs-mirror/emacs/blob/489d6466372f488adc53897435fff290394b62f7/lisp/progmodes/js.el -;; - -(defconst typescript--syntax-propertize-regexp-regexp - (rx - ;; Start of regexp. - "/" - (0+ (or - ;; Match characters outside of a character class. - (not (any ?\[ ?/ ?\\)) - ;; Match backslash quoted characters. - (and "\\" not-newline) - ;; Match character class. - (and - "[" - (0+ (or - (not (any ?\] ?\\)) - (and "\\" not-newline))) - "]"))) - (group (zero-or-one "/"))) - "Regular expression matching a JavaScript regexp literal.") - -(defun typescript-syntax-propertize-regexp (end) - (let ((ppss (syntax-ppss))) - (when (eq (nth 3 ppss) ?/) - ;; A /.../ regexp. - (goto-char (nth 8 ppss)) - (when (looking-at typescript--syntax-propertize-regexp-regexp) - ;; Don't touch text after END. - (when (> end (match-end 1)) - (setq end (match-end 1))) - (put-text-property (match-beginning 1) end - 'syntax-table (string-to-syntax "\"/")) - (goto-char end))))) - -(defun typescript-syntax-propertize (start end) - ;; JavaScript allows immediate regular expression objects, written /.../. - (funcall - (syntax-propertize-rules - ;; Distinguish /-division from /-regexp chars (and from /-comment-starter). - ;; FIXME: Allow regexps after infix ops like + ... - ;; https://developer.mozilla.org/en/JavaScript/Reference/Operators - ;; We can probably just add +, -, <, >, %, ^, ~, ?, : at which - ;; point I think only * and / would be missing which could also be added, - ;; but need care to avoid affecting the // and */ comment markers. - ("\\(?:^\\|[=([{,:;|&!]\\|\\_<return\\_>\\)\\(?:[ \t]\\)*\\(/\\)[^/*]" - (1 (ignore - (forward-char -1) - (when (or (not (memq (char-after (match-beginning 0)) '(?\s ?\t))) - ;; If the / is at the beginning of line, we have to check - ;; the end of the previous text. - (save-excursion - (goto-char (match-beginning 0)) - (forward-comment (- (point))) - (memq (char-before) - (eval-when-compile (append "=({[,:;" '(nil)))))) - (put-text-property (match-beginning 1) (match-end 1) - 'syntax-table (string-to-syntax "\"/")) - (typescript-syntax-propertize-regexp end))))) - ;; Hash-bang at beginning of buffer. - ("\\`\\(#\\)!" (1 "< b"))) - start end)) - -;;; Indentation - -(defconst typescript--possibly-braceless-keyword-re - (typescript--regexp-opt-symbol - '("catch" "do" "else" "finally" "for" "if" "try" "while" "with")) - "Regexp matching keywords optionally followed by an opening brace.") - -(defconst typescript--indent-keyword-re - (typescript--regexp-opt-symbol '("in" "instanceof")) - "Regexp matching keywords that affect indentation of continued expressions.") - -(defconst typescript--indent-operator-re - (concat "[-+*/%<>=&^|?:.]\\([^-+*/]\\|$\\)\\|" typescript--indent-keyword-re) - "Regexp matching operators that affect indentation of continued expressions.") - -;; -;; We purposely do not allow the plus symbol as a prefix here, as this -;; regex is used to check number literal in type annotations, and TS -;; does not allow to use a plus symbol to prefix numbers there: you -;; can use 1, but not +1 in a type annotation. -;; -;; This is meant to match NaN, floats, decimals, the two infinities -;; and numbers recorded in binary, octal and hex. -;; -;; This regular expression was derived from: -;; https://stackoverflow.com/a/30987109/ -;; -(defconst typescript--number-literal-re - "\\(?:NaN\\|-?\\(?:0[Bb][01]+\\|0[Oo][0-7]+\\|0[Xx][0-9a-fA-F]+\\|Infinity\\|\\(?:[[:digit:]]*\\.[[:digit:]]+\\|[[:digit:]]+\\)\\(?:[Ee][+-]?[[:digit:]]+\\)?\\)\\)" - "Regexp that matches number literals.") - -(defconst typescript--reserved-start-keywords - '("const" "export" "function" "let" "var") - "These keywords cannot be variable or type names and start a new sentence. -Note that the \"import\" keyword can be a type import since TS2.9, so it might -not start a sentence!") - -(defconst typescript--reserved-start-keywords-re - (typescript--regexp-opt-symbol '("const" "export" "function" "let" "var")) - "A regular expression matching `typescript--reserved-start-keywords'.") - -(defconst typescript--type-vs-ternary-re - (concat "[?]\\|" (typescript--regexp-opt-symbol - (append typescript--reserved-start-keywords - '("as" "class" "interface" "private" "public" "readonly")))) - "Keywords/Symbols that help tell apart colon for types vs ternary operators.") - -(defun typescript--search-backward-matching-angle-bracket-inner (depth) - "Auxiliary function for `typescript--search-backward-matching-angle-bracket'. -DEPTH indicates how nested we think we are: it increases when we cross closing -brackets, and decreases when we cross opening brackets." - ;; We look backwards for a "<" that would correspond to the ">" we started - ;; from. However, there is no guarantee that it exists, since our ">" could - ;; be a greater-than operation. Some symbols will make it clear that we are - ;; *not* in a type annotation, so we can return nil. Otherwise, we keep - ;; looking for the matching one. - (or (<= depth 0) - (and - ;; If we cross over a reserved start keyword, we abandon hope of finding - ;; a matching angle bracket. This prevents extreme recursion depths. - (typescript--re-search-backward (concat "[<>]\\|" typescript--reserved-start-keywords-re) nil t) - (cl-case (char-after) - (?< (typescript--search-backward-matching-angle-bracket-inner (- depth 1))) - (?> (typescript--search-backward-matching-angle-bracket-inner (+ depth 1))))))) - -(defun typescript--search-backward-matching-angle-bracket () - "Search for matching \"<\" preceding a starting \">\". -DEPTH indicates how nested we think we are. Assumes the starting position is -right before the closing \">\". Returns nil when a match was not found, -otherwise returns t and the current position is right before the matching -\"<\"." - (typescript--search-backward-matching-angle-bracket-inner 1)) - -(defun typescript--re-search-backward-ignoring-angle-brackets () - "Search backwards, jumping over text within angle brackets. -Searches specifically for any of \"=\", \"}\", and \"type\"." - (and - (typescript--re-search-backward "[>=}]\\|\\_<type\\_>" nil t) - (or (not (looking-at ">")) - (and - (typescript--search-backward-matching-angle-bracket) - (typescript--re-search-backward-ignoring-angle-brackets))))) - -(defun typescript--looking-at-operator-p () - "Return non-nil if point is on a typescript operator, other than a comma." - (save-match-data - (and (looking-at typescript--indent-operator-re) - (or (not (looking-at ":")) - (save-excursion - (backward-sexp) - (and - (typescript--re-search-backward "[?:{]\\|\\_<case\\_>" nil t) - (looking-at "?")))) - ;; Do not identify forward slashes appearing in a "list" as - ;; an operator. The lists are: arrays, or lists of - ;; arguments. In this context, they must be part of regular - ;; expressions, and not math operators. - (not (and (looking-at "/") - (save-excursion - (typescript--backward-syntactic-ws) - (memq (char-before) '(?, ?\[ ?\())))) - ;; Do not identify methods, or fields, that are named "in" or - ;; "instanceof" as being operator keywords. - (not (and - (looking-at typescript--indent-keyword-re) - (save-excursion - (typescript--backward-syntactic-ws) - (memq (char-before) '(?, ?{ ?} ?\;))))) - ;; Do not identify the symbol > if it is likely part of a type argument - ;; T<A>, but identify it if it is likely a greater-than symbol. This is - ;; a hard problem in the absence of semicolons, see: - ;; https://github.com/ananthakumaran/typescript.el/issues/81 - (not (and - (looking-at ">") - (save-excursion - (and - (typescript--search-backward-matching-angle-bracket) - ;; If we made it here, we found a candidate matching opening - ;; angle bracket. We still need to guess whether it actually - ;; is one, and not a spurious less-than operator! - - ;; Look backwards for the first of: - ;; - one of the symbols: = : - ;; - or a TypeScript keyword - ;; Depending on what comes first, we can make an educated - ;; guess on the nature of our ">" of interest. - (typescript--re-search-backward (concat "[=:]\\|" typescript--keyword-re) nil t) - (or - ;; If the previous keyword is "as", definitely a type. - (looking-at "\\_<as\\_>") - ;; Same goes for type imports. - (looking-at "\\_<import\\_>") - ;; A colon could be either a type symbol, or a ternary - ;; operator, try to guess which. - (and (looking-at ":") - (typescript--re-search-backward typescript--type-vs-ternary-re nil t) - (not (looking-at "?"))) - ;; This final check lets us distinguish between a - ;; 2-argument type "t < a , b > ..." and a use of the "," - ;; operator between two comparisons "t < a , b > ...". - ;; Looking back a little more lets us guess. - (and (looking-at "=") - (typescript--re-search-backward-ignoring-angle-brackets) - (looking-at "\\_<type\\_>"))))))) - (not (and - (looking-at "*") - ;; Generator method (possibly using computed property). - (looking-at (concat "\\* *\\(?:\\[\\|" typescript--name-re - " *(\\)")) - (save-excursion - (typescript--backward-syntactic-ws) - ;; We might misindent some expressions that would - ;; return NaN anyway. Shouldn't be a problem. - (memq (char-before) '(?, ?} ?{ ?\;)))))))) - - -(defun typescript--continued-expression-p () - "Return non-nil if the current line continues an expression." - (save-excursion - (back-to-indentation) - (let ((list-start (nth 1 (syntax-ppss)))) - (and - ;; This not clause is there to eliminate degenerate cases where we have - ;; something that looks like a continued expression but we are in fact at - ;; the beginning of the expression. Example: in `if (a) { .q(1)` when the - ;; point is on the dot, the expression that follows looks like a member - ;; expression but the object on which it is a member is missing. If we - ;; naively treat this as a continued expression, we run into trouble - ;; later. (An infinite loop.) - (not (and list-start - (save-excursion - (typescript--backward-syntactic-ws) - (backward-char) - (eq (point) list-start)))) - ;; Don't identify the spread syntax or rest operator as a "continuation". - (not (looking-at "\\.\\.\\.")) - (or (typescript--looking-at-operator-p) - (and (progn - (typescript--backward-syntactic-ws) - (or (bobp) (backward-char)) - (and (> (point) (point-min)) - (save-excursion (backward-char) (not (looking-at "[/*]/"))) - (typescript--looking-at-operator-p) - (and (progn (backward-char) - (not (looking-at "++\\|--\\|/[/*]")))))))))))) - -(cl-defun typescript--compute-member-expression-indent () - "Determine the indent of a member expression. - -This function must be called with point located at the dot that -starts the member expression. -" - ;; Find the line that has the object from which we are getting thismember. - ;; And set an indent relative to that. - (while (looking-at "\\.") - (typescript--backward-syntactic-ws) - (while (eq (char-before) ?\;) - (backward-char)) - (when (memq (char-before) '(?\? ?\!)) - (backward-char)) - (while (memq (char-before) '(?\] ?} ?\) ?>)) - (if (not (eq (char-before) ?>)) - (backward-list) - (backward-char) - (typescript--backward-over-generic-parameter-list)) - (typescript--backward-syntactic-ws)) - (if (looking-back typescript--dotted-name-re nil) - (back-to-indentation) - (typescript--forward-syntactic-ws))) - (+ (current-column) typescript-indent-level)) - -(defun typescript--end-of-do-while-loop-p () - "Return non-nil if point is on the \"while\" of a do-while statement. -Otherwise, return nil. A braceless do-while statement spanning -several lines requires that the start of the loop is indented to -the same column as the current line." - (interactive) - (save-excursion - (save-match-data - (when (looking-at "\\s-*\\_<while\\_>") - (if (save-excursion - (skip-chars-backward "[ \t\n]*}") - (looking-at "[ \t\n]*}")) - (save-excursion - (backward-list) (forward-symbol -1) (looking-at "\\_<do\\_>")) - (typescript--re-search-backward "\\_<do\\_>" (point-at-bol) t) - (or (looking-at "\\_<do\\_>") - (let ((saved-indent (current-indentation))) - (while (and (typescript--re-search-backward "^\\s-*\\_<" nil t) - (/= (current-indentation) saved-indent))) - (and (looking-at "\\s-*\\_<do\\_>") - (not (typescript--re-search-forward - "\\_<while\\_>" (point-at-eol) t)) - (= (current-indentation) saved-indent))))))))) - - -(defun typescript--ctrl-statement-indentation () - "Helper function for `typescript--proper-indentation'. -Return the proper indentation of the current line if it starts -the body of a control statement without braces; otherwise, return -nil." - (save-excursion - (back-to-indentation) - (when (save-excursion - (and (not (eq (point-at-bol) (point-min))) - (not (looking-at "[{]")) - (progn - (typescript--re-search-backward "[[:graph:]]" nil t) - (or (eobp) (forward-char)) - (when (= (char-before) ?\)) (backward-list)) - (skip-syntax-backward " ") - (skip-syntax-backward "w_") - (and - (looking-at typescript--possibly-braceless-keyword-re) - ;; If preceded by period, it's a method call. - (not (= (char-before) ?.)))) - (not (typescript--end-of-do-while-loop-p)))) - (save-excursion - (goto-char (match-beginning 0)) - (+ (current-indentation) typescript-indent-level))))) - -(defun typescript--get-c-offset (symbol anchor) - (let ((c-offsets-alist - (list (cons 'c typescript-comment-lineup-func)))) - (c-get-syntactic-indentation (list (cons symbol anchor))))) - -(defun typescript--backward-over-generic-parameter-list () - "Search backward for the start of a generic's parameter list and move to it. - -This is a utility function for -`typescript--backward-to-parameter-list'. - -This function must be called with the point placed on the final > -of the generic's parameter list. It will scan backwards to find -the start. If successful, it will move the point to the start of -the list. If not, it does not move the point. - -Returns nil on failure, or the position to which the point was -moved on success." - (when (eq (char-after) ?>) - (let ((depth 1)) - (cl-loop named search-loop - while (> depth 0) - do (progn - (unless (re-search-backward "[<>]" nil t) - (cl-return-from search-loop nil)) - (cond - ((looking-at ">") - (unless (eq (char-before) ?=) - (setq depth (1+ depth)))) - ((looking-at "<") (setq depth (1- depth))))) - finally return (point))))) - -(defun typescript--backward-to-parameter-list () - "Search backward for the end of a parameter list and move to it. - -This is a utility function for `typescript--proper-indentation'. - -This function must be called with the point placed before an -opening curly brace. It will try to skip over the type -annotation that would mark the return value of a function and -move to the end of the parameter list. If it is unsuccessful, it -does not move the point. \"Unsuccessful\" here also means that -the position at which we started did not in fact mark the -beginning of a function. The curly brace belonged to some other -syntactic construct than a function. - -Returns nil on failure, or the position to which the point was -moved on success." - (let ((location - (or - ;; This handles the case of a function with return type annotation. - (save-excursion - (cl-loop named search-loop - do - (typescript--backward-syntactic-ws) - ;; Check whether we are at "):". - (when (and (eq (char-before) ?\:) - (progn - (backward-char) - (skip-syntax-backward " ") - (eq (char-before) ?\)))) - ;; Success! This the end of the parameter list. - (cl-return-from search-loop (point))) - ;; If we recognize a structure that belongs in a return type annotation, - ;; skip back over it, or fail. - (cond - ;; Arrow of a function definition, or typeguard (eg. foo is SomeClass) - ((looking-back "=>\\|is" (- (point) 2)) - (backward-char 2)) - ;; End of the parameters list of a generic. - ((eq (char-before) ?>) - (backward-char) - (typescript--backward-over-generic-parameter-list)) - ;; Union of types, or a dot in a dotted name. - ((memq (char-before) '(?| ?.)) - (backward-char)) - ((or - ;; End-delimiter of a delimited construct, for constructs - ;; not handled above. - (memq (char-before) '(?\) ?} ?\" ?\])) - ;; This is also dealing with dotted names. This may come - ;; into play if a jump back moves over an entire dotted - ;; name at once. - ;; - ;; The earlier test for dotted names comes into play if the - ;; logic moves over one part of a dotted name at a time (which - ;; is what `backward-sexp` normally does). - (and (looking-back typescript--dotted-name-re nil) - ;; We don't want the loop to walk over constructs like switch (...) or for (...), etc. - (not (save-excursion - (backward-word) - (looking-at "\\_<\\(switch\\|if\\|while\\|until\\|for\\)\\_>\\(?:\\s-\\|\n\\)*("))))) - (condition-case nil - (backward-sexp) - (scan-error (cl-return-from search-loop nil)))) - ((looking-back typescript--number-literal-re - ;; We limit the search back to the previous space or end of line (if possible) - ;; to prevent the search from going over the whole buffer. - (save-excursion (re-search-backward "\\(?:\\s-\\|\n\\)" nil t)) t) - (goto-char (match-beginning 0))) - ;; Otherwise, we failed to find a location. - (t - (cl-return-from search-loop nil))))) - ;; This handles the case of a function without return type annotation. - (progn - (typescript--backward-syntactic-ws) - (when (eq (char-before) ?\)) - (point)))))) - (when location - (goto-char location)))) - -(defun typescript--proper-indentation (parse-status) - "Return the proper indentation for the current line." - (save-excursion - (back-to-indentation) - (let ((member-expr-p (looking-at "\\."))) - (cond ((nth 4 parse-status) ;; Inside a comment. - (typescript--get-c-offset 'c (nth 8 parse-status))) - ((nth 8 parse-status) 0) ;; Inside a string. - ((typescript--ctrl-statement-indentation)) ;; Control statements. - ((eq (char-after) ?#) 0) ;; Looking at a pragma. - ;; Inside a list of things. Note that in the TS contents, the curly braces - ;; marking code blocks are "list of things". - ((nth 1 parse-status) - (let ((indent-start (point)) - (same-indent-p (looking-at "[]})]")) - (switch-keyword-p (looking-at "\\_<default\\_>\\|\\_<case\\_>[^:]")) - (continued-expr-p (typescript--continued-expression-p)) - (list-start (nth 1 parse-status))) - (goto-char list-start) - (if (looking-at "[({[]\\s-*\\(/[/*]\\|$\\)") - (progn - (skip-syntax-backward " ") - (cond - ((or (typescript--backward-to-parameter-list) - (eq (char-before) ?\))) - ;; Take the curly brace as marking off the body of a function. - ;; In that case, we want the code that follows to see the indentation - ;; that was in effect at the beginning of the function declaration, and thus - ;; we want to move back over the list of function parameters. - (condition-case nil - (backward-list) - (error nil))) - ((looking-back "," nil) - ;; If we get here, we have a comma, spaces and an opening curly brace. (And - ;; (point) is just after the comma.) We don't want to move from the current position - ;; so that object literals in parameter lists are properly indented. - nil) - (t - ;; In all other cases, we don't want to move from the curly brace. - (goto-char list-start))) - (back-to-indentation) - (let* ((in-switch-p (unless same-indent-p - (looking-at "\\_<switch\\_>"))) - (same-indent-p (or same-indent-p - (and switch-keyword-p - in-switch-p))) - (indent - (cond (same-indent-p - (current-column)) - (continued-expr-p - (if (not member-expr-p) - (+ (current-column) (* 2 typescript-indent-level) - typescript-expr-indent-offset) - (goto-char indent-start) - (typescript--compute-member-expression-indent))) - (t - (+ (current-column) typescript-indent-level))))) - (if (and in-switch-p typescript-indent-switch-clauses) - (+ indent typescript-indent-level) - indent))) - (when (and (not same-indent-p) - typescript-indent-list-items) - (forward-char) - (skip-chars-forward " \t")) - (if continued-expr-p - (if (not member-expr-p) - (progn (back-to-indentation) - (+ (current-column) typescript-indent-level - typescript-expr-indent-offset)) - (goto-char indent-start) - (typescript--compute-member-expression-indent)) - (current-column))))) - - ((typescript--continued-expression-p) ;; Inside a continued expression. - (if member-expr-p - (typescript--compute-member-expression-indent) - (+ typescript-indent-level typescript-expr-indent-offset))) - (t 0))))) - -(defun typescript-indent-line () - "Indent the current line as typescript." - (interactive) - (save-restriction - (widen) - (let* ((parse-status - (save-excursion (syntax-ppss (point-at-bol)))) - (offset (- (current-column) (current-indentation)))) - (indent-line-to (typescript--proper-indentation parse-status)) - (when (> offset 0) (move-to-column (+ offset (current-indentation))))))) - -;;; Filling - -(defun typescript-c-fill-paragraph (&optional justify) - "Fill the paragraph with `c-fill-paragraph'." - (interactive "*P") - ;; Dynamically replace functions using the lexically scoped cl-letf. - ;; See below for more details: - ;; http://endlessparentheses.com/understanding-letf-and-how-it-replaces-flet.html - (cl-letf (((symbol-function 'c-forward-sws) - (lambda (&optional limit) - (typescript--forward-syntactic-ws limit))) - ((symbol-function 'c-backward-sws) - (lambda (&optional limit) - (typescript--backward-syntactic-ws limit)))) - (let ((fill-paragraph-function 'c-fill-paragraph)) - (c-fill-paragraph justify)))) - -;; We maintain a cache of semantic information, i.e., the classes and -;; functions we've encountered so far. In order to avoid having to -;; re-parse the buffer on every change, we cache the parse state at -;; each interesting point in the buffer. Each parse state is a -;; modified copy of the previous one, or in the case of the first -;; parse state, the empty state. -;; -;; The parse state itself is just a stack of typescript--pitem -;; instances. It starts off containing one element that is never -;; closed, that is initially typescript--initial-pitem. -;; - - -(defun typescript--pitem-format (pitem) - (let ((name (typescript--pitem-name pitem)) - (type (typescript--pitem-type pitem))) - - (format "name:%S type:%S" - name - (if (atom type) - type - (plist-get type :name))))) - -(defun typescript--make-merged-item (item child name-parts) - "Helper function for `typescript--splice-into-items'. -Return a new item that is the result of merging CHILD into -ITEM. NAME-PARTS is a list of parts of the name of CHILD -that we haven't consumed yet." - (typescript--debug "typescript--make-merged-item: {%s} into {%s}" - (typescript--pitem-format child) - (typescript--pitem-format item)) - - ;; If the item we're merging into isn't a class, make it into one - (unless (consp (typescript--pitem-type item)) - (typescript--debug "typescript--make-merged-item: changing dest into class") - (setq item (make-typescript--pitem - :children (list item) - - ;; Use the child's class-style if it's available - :type (if (atom (typescript--pitem-type child)) - typescript--dummy-class-style - (typescript--pitem-type child)) - - :name (typescript--pitem-strname item)))) - - ;; Now we can merge either a function or a class into a class - (cons (cond - ((cdr name-parts) - (typescript--debug "typescript--make-merged-item: recursing") - ;; if we have more name-parts to go before we get to the - ;; bottom of the class hierarchy, call the merger - ;; recursively - (typescript--splice-into-items (car item) child - (cdr name-parts))) - - ((atom (typescript--pitem-type child)) - (typescript--debug "typescript--make-merged-item: straight merge") - ;; Not merging a class, but something else, so just prepend - ;; it - (cons child (car item))) - - (t - ;; Otherwise, merge the new child's items into those - ;; of the new class - (typescript--debug "typescript--make-merged-item: merging class contents") - (append (car child) (car item)))) - (cdr item))) - -(defun typescript--pitem-strname (pitem) - "Last part of the name of PITEM, as a string or symbol." - (let ((name (typescript--pitem-name pitem))) - (if (consp name) - (car (last name)) - name))) - -(defun typescript--splice-into-items (items child name-parts) - "Splice CHILD into the `typescript--pitem' ITEMS at NAME-PARTS. -If a class doesn't exist in the tree, create it. Return -the new items list. NAME-PARTS is a list of strings given -the broken-down class name of the item to insert." - - (let ((top-name (car name-parts)) - (item-ptr items) - new-items last-new-item new-cons item) - - (typescript--debug "typescript--splice-into-items: name-parts: %S items:%S" - name-parts - (mapcar #'typescript--pitem-name items)) - - (cl-assert (stringp top-name)) - (cl-assert (> (length top-name) 0)) - - ;; If top-name isn't found in items, then we build a copy of items - ;; and throw it away. But that's okay, since most of the time, we - ;; *will* find an instance. - - (while (and item-ptr - (cond ((equal (typescript--pitem-strname (car item-ptr)) top-name) - ;; Okay, we found an entry with the right name. Splice - ;; the merged item into the list... - (setq new-cons (cons (typescript--make-merged-item - (car item-ptr) child - name-parts) - (cdr item-ptr))) - - (if last-new-item - (setcdr last-new-item new-cons) - (setq new-items new-cons)) - - ;; ...and terminate the loop - nil) - - (t - ;; Otherwise, copy the current cons and move onto the - ;; text. This is tricky; we keep track of the tail of - ;; the list that begins with new-items in - ;; last-new-item. - (setq new-cons (cons (car item-ptr) nil)) - (if last-new-item - (setcdr last-new-item new-cons) - (setq new-items new-cons)) - (setq last-new-item new-cons) - - ;; Go to the next cell in items - (setq item-ptr (cdr item-ptr)))))) - - (if item-ptr - ;; Yay! We stopped because we found something, not because - ;; we ran out of items to search. Just return the new - ;; list. - (progn - (typescript--debug "search succeeded: %S" name-parts) - new-items) - - ;; We didn't find anything. If the child is a class and we don't - ;; have any classes to drill down into, just push that class; - ;; otherwise, make a fake class and carry on. - (typescript--debug "search failed: %S" name-parts) - (cons (if (cdr name-parts) - ;; We have name-parts left to process. Make a fake - ;; class for this particular part... - (make-typescript--pitem - ;; ...and recursively digest the rest of the name - :children (typescript--splice-into-items - nil child (cdr name-parts)) - :type typescript--dummy-class-style - :name top-name) - - ;; Otherwise, this is the only name we have, so stick - ;; the item on the front of the list - child) - items)))) - -(defun typescript--pitem-add-child (pitem child) - "Copy `typescript--pitem' PITEM, and push CHILD onto its list of children." - (cl-assert (integerp (typescript--pitem-h-begin child))) - (cl-assert (if (consp (typescript--pitem-name child)) - (cl-loop for part in (typescript--pitem-name child) - always (stringp part)) - t)) - - ;; This trick works because we know (based on our cl-defstructs) that - ;; the child list is always the first element, and so the second - ;; element and beyond can be shared when we make our "copy". - (cons - - (let ((name (typescript--pitem-name child)) - (type (typescript--pitem-type child))) - - (cond ((cdr-safe name) ; true if a list of at least two elements - ;; Use slow path because we need class lookup - (typescript--splice-into-items (car pitem) child name)) - - ((and (consp type) - (plist-get type :prototype)) - - ;; Use slow path because we need class merging. We know - ;; name is a list here because down in - ;; `typescript--ensure-cache', we made sure to only add - ;; class entries with lists for :name - (cl-assert (consp name)) - (typescript--splice-into-items (car pitem) child name)) - - (t - ;; Fast path - (cons child (car pitem))))) - - (cdr pitem))) - -;;; compilation-mode support - -;; tsc supports formatting errors in two general ways: plain and -;; pretty. ("Plain" is our term for "not pretty".) In tsc versions -;; prior to 2.7, the plain and pretty formats both used the same -;; format for references into files. `typescript-tsc-error-regexp` -;; covers both plain and pretty for those versions. -;; -;; Version 2.7 changed the pretty format so as to format source code -;; references differently. This required the introduction of -;; `typescript-tsc-pretty-error-regexp`. The format of plain error -;; messages did not change. So from that version onwards, -;; `typescript-tsc-error-regexp` covers plain error messages and -;; `typescript-tsc-pretty-error-regexp` covers pretty error messages. - -;; handle plain compiler-errors like the following when doing M-x compile<ret>tsc<ret> -;; -;; greeter.ts(24,9): error TS2362: The left-hand side of an arithmetic operation must be of type 'any', 'number' or an enum type. -;; greeter.ts(30,12): error TS2339: Property 'indexOf' does not exist on type 'number'. -(defconst typescript-tsc-error-regexp - (concat - "^[[:blank:]]*" - "\\([^(\r\n)]+\\)(\\([0-9]+\\),\\([0-9]+\\)):[[:blank:]]+" - "error [[:alnum:]]+: [^\r\n]+$") - "Regexp to match errors generated by tsc.") - -;; handle pretty compiler-errors like the following when doing M-x compile<ret>tsc<ret> -;; test.ts:2:7 - error TS2322: Type '2' is not assignable to type 'string'. -(defconst typescript-tsc-pretty-error-regexp - (concat - "^[[:blank:]]*" - "\\([^(\r\n)]+\\):\\([0-9]+\\):\\([0-9]+\\) - [[:blank:]]*" - "error [[:alnum:]]+: [^\r\n]+$") - "Regexp to match errors generated by tsc.") - -;; -;; Should handle output like: -;; src/modules/authenticator.ts[1, 83]: ' should be " -;; (quotemarks) src/modules/authenticator.ts[2, 26]: ' should be " -;; ERROR: (quotemarks) src/modules/authenticator.ts[2, 26]: ' should be " -;; WARNING: src/modules/authenticator.ts[2, 26]: ' should be " -;; -;; "(quotemarks)" it the rule name. It is produced when using the -;; "verbose" formatter. The "verbose" formatter is identical to the -;; default ("prose") formatter, except for the additional rule name. -;; -;; "ERROR:" and "WARNING:" are the severity. This was added in tslint -;; 5.0. Prior versions have no notion of severity and simply omit this -;; part. -;; -(defconst typescript-tslint-report-regexp - (concat - "^[[:blank:]]*" - ;; severity ("type" in Emacs' parlance) - "\\(?:\\(?:ERROR\\|\\(WARNING\\)\\):[[:blank:]]+\\)?" - ;; rule name - "\\((.*)[[:blank:]]+\\)?" - ;; filename - "\\([^(\r\n)]+\\)" - "\\[" - ;; line - "\\([[:digit:]]+\\)" - ", " - ;; column - "\\([[:digit:]]+\\)" - "\\]: " - ;; message - ".*$") - "Regexp to match reports generated by tslint.") - -(defconst typescript-nglint-error-regexp - (concat - ;; severity ("type" in Emacs' parlance) - "ERROR:[[:blank:]]+" - - ;; filename - "\\([^(\r\n)]+\\)" - ":" - ;; line - "\\([[:digit:]]+\\)" - ":" - ;; column - "\\([[:digit:]]+\\)" - - " - " - ;; message - ".*$")) - -(defconst typescript-nglint-warning-regexp - (concat - ;; severity ("type" in Emacs' parlance) - "WARNING:[[:blank:]]+" - - ;; filename - "\\([^(\r\n)]+\\)" - ":" - ;; line - "\\([[:digit:]]+\\)" - ":" - ;; column - "\\([[:digit:]]+\\)" - - " - " - ;; message - ".*$")) - -(dolist - (regexp - `((typescript-tsc - ,typescript-tsc-error-regexp - 1 2 3 2) - - (typescript-tsc-pretty - ,typescript-tsc-pretty-error-regexp - 1 2 3 2) - - (typescript-tslint - ,typescript-tslint-report-regexp - 3 4 5 (1)) - - (typescript-nglint-error - ,typescript-nglint-error-regexp - 1 2 3 2) - - (typescript-nglint-warning - ,typescript-nglint-warning-regexp - 1 2 3 1))) - (add-to-list 'compilation-error-regexp-alist-alist regexp) - (add-to-list 'compilation-error-regexp-alist (car regexp))) - -;;; Main Function - -;;;###autoload -(define-derived-mode typescript-mode prog-mode "TypeScript" - "Major mode for editing typescript. - -Key bindings: - -\\{typescript-mode-map}" - - :group 'typescript - :syntax-table typescript-mode-syntax-table - - (setq-local indent-line-function 'typescript-indent-line) - (setq-local beginning-of-defun-function 'typescript-beginning-of-defun) - (setq-local end-of-defun-function 'typescript-end-of-defun) - (setq-local open-paren-in-column-0-is-defun-start nil) - (setq-local font-lock-defaults (list typescript--font-lock-keywords)) - (setq-local syntax-propertize-function #'typescript-syntax-propertize) - (setq-local parse-sexp-ignore-comments t) - (setq-local parse-sexp-lookup-properties t) - - ;; Comments - (setq-local comment-start "// ") - (setq-local comment-end "") - (setq-local fill-paragraph-function 'typescript-c-fill-paragraph) - - ;; Parse cache - (add-hook 'before-change-functions #'typescript--flush-caches t t) - - ;; Frameworks - (typescript--update-quick-match-re) - - ;; for filling, pretend we're cc-mode - (setq c-comment-prefix-regexp "//+\\|\\**" - c-paragraph-start "$" - c-paragraph-separate "$" - c-block-comment-prefix "* " - c-block-comment-ender-regexp "\\*/" - c-line-comment-starter "//" - c-comment-start-regexp "/[*/]\\|\\s!" - comment-start-skip "\\(//+\\|/\\*+\\)\\s *") - - (setq-local electric-indent-chars - (append "{}():;," electric-indent-chars)) - (setq-local electric-layout-rules - '((?\; . after) (?\{ . after) (?\} . before))) - - (let ((c-buffer-is-cc-mode t)) - ;; FIXME: These are normally set by `c-basic-common-init'. Should - ;; we call it instead? (Bug#6071) - (make-local-variable 'paragraph-start) - (make-local-variable 'paragraph-separate) - (make-local-variable 'paragraph-ignore-fill-prefix) - (make-local-variable 'adaptive-fill-mode) - (make-local-variable 'adaptive-fill-regexp) - (c-setup-paragraph-variables)) - - (add-hook 'post-self-insert-hook - #'typescript--post-self-insert-function) - - (setq-local syntax-begin-function #'typescript--syntax-begin-function)) - -;; Set our custom predicate for flyspell prog mode -(put 'typescript-mode 'flyspell-mode-predicate - 'typescript--flyspell-mode-predicate) - -;;;###autoload -(eval-after-load 'folding - '(when (fboundp 'folding-add-to-marks-list) - (folding-add-to-marks-list 'typescript-mode "// {{{" "// }}}" ))) - -;;;###autoload -(add-to-list 'auto-mode-alist '("\\.ts\\'" . typescript-mode)) - -(provide 'typescript-mode) - -;;; typescript-mode.el ends here |