emacs-elpa-diffs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[nongnu] elpa/typescript-mode 782e8dd32e 071/222: Add fontification supp


From: ELPA Syncer
Subject: [nongnu] elpa/typescript-mode 782e8dd32e 071/222: Add fontification support for jsdoc/typedoc tags.
Date: Sun, 6 Feb 2022 16:59:18 -0500 (EST)

branch: elpa/typescript-mode
commit 782e8dd32e7b30fbecd52ff49eaa65b78af0800f
Author: Louis-Dominique Dubeau <ldd@lddubeau.com>
Commit: Louis-Dominique Dubeau <ldd@lddubeau.com>

    Add fontification support for jsdoc/typedoc tags.
    
    This commit adds the machinery necessary for fontifying the jsdoc-like
    comments that appear in documentation comments (comments that start
    with "/**").
---
 typescript-mode-tests.el |  71 ++++++++++++++++
 typescript-mode.el       | 208 +++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 279 insertions(+)

diff --git a/typescript-mode-tests.el b/typescript-mode-tests.el
index ad69c1736e..cbc25dccdf 100644
--- a/typescript-mode-tests.el
+++ b/typescript-mode-tests.el
@@ -164,6 +164,77 @@ a severity set to WARNING, no rule name."
 (ert-deftest re-search-backwards-skips-multi-line-comments ()
   (test-re-search "token" "let token; /* token in \nmulti-line token comment" 
0))
 
+;; Adapted from jdee-mode's test suite.
+(defmacro test-with-temp-buffer (content &rest body)
+  "Fill a temporary buffer with `CONTENT' and eval `BODY' in it."
+  (declare (debug t)
+           (indent 1))
+  `(with-temp-buffer
+     (insert ,content)
+     (typescript-mode)
+     (font-lock-fontify-buffer)
+     (goto-char (point-min))
+     ,@body))
+
+(defun get-face-at (loc)
+  "Get the face at `LOC'. If it is not a number, then we `re-search-forward' 
with `LOC'
+as the search pattern."
+  (when (not (numberp loc))
+    (save-excursion
+      (re-search-forward loc)
+      (setq loc (match-beginning 0))))
+  (get-text-property loc 'face))
+
+(setq font-lock-contents
+ " * @param {Something} bar A parameter. References [[moo]] and [[foo]].
+ * @param second May hold ``x`` or ``y``.")
+
+(defun font-lock-test (contents expected)
+  "Perform a test on our template. `CONTENTS' is the string to
+put in the temporary buffer. `EXPECTED' is the expected
+results. It should be a list of (LOCATION . FACE) pairs."
+  (test-with-temp-buffer
+   contents
+   (dolist (spec expected)
+     (should (eq (get-face-at (car spec)) (cdr spec))))))
+
+(ert-deftest font-lock/documentation-in-documentation-comments ()
+  "Documentation in documentation comments should be fontified as
+documentation."
+  (font-lock-test
+   (concat "/**\n" font-lock-contents "\n*/")
+   '((1 . font-lock-comment-delimiter-face)
+     (5 . font-lock-comment-face)
+     ("@param" . typescript-jsdoc-tag)
+     ("{Something}" . typescript-jsdoc-type)
+     ("bar" . typescript-jsdoc-value)
+     ("\\[\\[moo\\]\\]" . typescript-jsdoc-value)
+     ("\\[\\[foo\\]\\]" . typescript-jsdoc-value)
+     ("``x``" . typescript-jsdoc-value)
+     ("``y``" . typescript-jsdoc-value))))
+
+(ert-deftest font-lock/no-documentation-in-non-documentation-comments ()
+  "Documentation tags that are not in documentation comments
+should not be fontified as documentation."
+  (test-with-temp-buffer
+   (concat "/*\n" font-lock-contents "\n*/\n")
+   (let ((loc 3))
+     ;; Make sure we start with the right face.
+     (should (eq (get-face-at loc) font-lock-comment-face))
+     (should (eq (text-property-not-all loc (point-max) 'face 
font-lock-comment-face)
+                 (1- (point-max)))))))
+
+(ert-deftest font-lock/no-documentation-in-strings ()
+  "Documentation tags that are not in strings should not be
+fontified as documentation."
+  (test-with-temp-buffer
+   (concat "const x = \"/**" font-lock-contents "*/\";")
+   (let ((loc (search-forward "\"")))
+     ;; Make sure we start with the right face.
+     (should (eq (get-face-at loc) font-lock-string-face))
+     ;; Make sure the face does not change later.
+     (should (eq (text-property-not-all loc (point-max) 'face 
font-lock-string-face)
+                 (1- (point-max)))))))
 
 (provide 'typescript-mode-tests)
 
diff --git a/typescript-mode.el b/typescript-mode.el
index 6eaca9a16c..7f5f4c4375 100644
--- a/typescript-mode.el
+++ b/typescript-mode.el
@@ -414,6 +414,147 @@ Match group 1 is the name of the macro.")
    :paren-depth most-negative-fixnum
    :type 'toplevel))
 
+;; 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.")
+
+;; This was taken from js2-mode.
+(defconst typescript-jsdoc-param-tag-regexp
+  (concat "\\(?:^\\s-*\\*+\\|/\\*\\*\\)\\s-*\\(@"
+          "\\(?:param\\|arg\\(?:ument\\)?\\|prop\\(?:erty\\)?\\)"
+          "\\)"
+          "\\s-*\\({[^}]+}\\)?"         ; optional type
+          "\\s-*\\[?\\([[:alnum:]_$\.]+\\)?\\]?"  ; name
+          "\\_>")
+  "Matches jsdoc tags with optional type and optional param name.")
+
+;; This was taken from js2-mode.
+(defconst typescript-jsdoc-typed-tag-regexp
+  (concat "^\\s-*\\*+\\s-*\\(@\\(?:"
+          (regexp-opt
+           '("enum"
+             "extends"
+             "field"
+             "id"
+             "implements"
+             "lends"
+             "mods"
+             "requires"
+             "return"
+             "returns"
+             "throw"
+             "throws"))
+          "\\)\\)\\s-*\\({[^}]+}\\)?")
+  "Matches jsdoc tags with optional type.")
+
+;; This was taken from js2-mode.
+(defconst typescript-jsdoc-arg-tag-regexp
+  (concat "^\\s-*\\*+\\s-*\\(@\\(?:"
+          (regexp-opt
+           '("alias"
+             "augments"
+             "borrows"
+             "callback"
+             "bug"
+             "base"
+             "config"
+             "default"
+             "define"
+             "emits"
+             "exception"
+             "fires"
+             "func"
+             "function"
+             "member"
+             "memberOf"
+             "method"
+             "name"
+             "namespace"
+             "since"
+             "suppress"
+             "this"
+             "throws"
+             "type"
+             "version"))
+          "\\)\\)\\s-+\\([^ \t]+\\)")
+  "Matches jsdoc tags with a single argument.")
+
+;; This was taken from js2-mode.
+(defconst typescript-jsdoc-empty-tag-regexp
+  (concat "^\\s-*\\*+\\s-*\\(@\\(?:"
+          (regexp-opt
+           '("addon"
+             "author"
+             "class"
+             "const"
+             "constant"
+             "constructor"
+             "constructs"
+             "copyright"
+             "deprecated"
+             "desc"
+             "description"
+             "event"
+             "example"
+             "exec"
+             "export"
+             "fileoverview"
+             "final"
+             "func"
+             "function"
+             "hidden"
+             "ignore"
+             "implicitCast"
+             "inheritDoc"
+             "inner"
+             "interface"
+             "license"
+             "method"
+             "noalias"
+             "noshadow"
+             "notypecheck"
+             "override"
+             "owner"
+             "preserve"
+             "preserveTry"
+             "private"
+             "protected"
+             "public"
+             "static"
+             "supported"
+             ))
+          "\\)\\)\\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)
+
 ;;; User Customization
 
 (defgroup typescript nil
@@ -1417,6 +1558,53 @@ point of view of font-lock.  It applies highlighting 
directly with
   ;; 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."
+  (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."
+  (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
   `(
     ;; This goes before keywords-2 so it gets used preferentially
@@ -1428,6 +1616,26 @@ point of view of font-lock.  It applies highlighting 
directly with
 
     ,@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))
 



reply via email to

[Prev in Thread] Current Thread [Next in Thread]