diff --git a/CHANGES.md b/CHANGES.md index b43a548aee..da9423a0c6 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -5,7 +5,15 @@ Grammars: - fix(python) Fix recognition of numeric literals followed by keywords without whitespace (#2985) [Richard Gibson][] - enh(swift) add SE-0290 unavailability condition (#3382) [Bradley Mackey][] - enh(java) add `sealed` and `non-sealed` keywords (#3386) [Bradley Mackey][] -- fix(clojure) `comment` macro catches more than it should [Björn Ebbinghaus][] +- fix(clojure) Several issues with Clojure highlighting (#3397) [Björn Ebbinghaus][] + - fix(clojure) `comment` macro catches more than it should (#3395) + - fix(clojure) `$` in symbol breaks highlighting + - fix(clojure) Add complete regex for number detection + - enh(clojure) Add character mode for character literals + - fix(clojure) Inconsistent namespaced map highlighting + - enh(clojure) Add `regex` mode to regex literal + - fix(clojure) Remove inconsistent/broken highlighting for metadata + - enh(clojure) Add `punctuation` mode for commas. [Richard Gibson]: https://github.com/gibson042 [Bradley Mackey]: https://github.com/bradleymackey diff --git a/src/languages/clojure.js b/src/languages/clojure.js index 34c9ff18af..34a4dc7fe4 100644 --- a/src/languages/clojure.js +++ b/src/languages/clojure.js @@ -8,8 +8,8 @@ Category: lisp /** @type LanguageFn */ export default function(hljs) { - const SYMBOLSTART = 'a-zA-Z_\\-!.?+*=<>&#\''; - const SYMBOL_RE = '[' + SYMBOLSTART + '][' + SYMBOLSTART + '0-9/;:]*'; + const SYMBOLSTART = 'a-zA-Z_\\-!.?+*=<>&\''; + const SYMBOL_RE = '[#]?[' + SYMBOLSTART + '][' + SYMBOLSTART + '0-9/;:$#]*'; const globals = 'def defonce defprotocol defstruct defmulti defmethod defn- defn defmacro deftype defrecord'; const keywords = { $pattern: SYMBOL_RE, @@ -45,20 +45,44 @@ export default function(hljs) { 'lazy-seq spread list* str find-keyword keyword symbol gensym force rationalize' }; - const SIMPLE_NUMBER_RE = '[-+]?\\d+(\\.\\d+)?'; - const SYMBOL = { begin: SYMBOL_RE, relevance: 0 }; const NUMBER = { - className: 'number', - begin: SIMPLE_NUMBER_RE, - relevance: 0 + scope: 'number', + relevance: 0, + variants: [ + {match: /[-+]?0[xX][0-9a-fA-F]+N?/}, // hexadecimal // 0x2a + {match: /[-+]?0[0-7]+N?/}, // octal // 052 + {match: /[-+]?[1-9][0-9]?[rR][0-9a-zA-Z]+N?/}, // variable radix from 2 to 36 // 2r101010, 8r52, 36r16 + {match: /[-+]?[0-9]+\/[0-9]+N?/}, // ratio // 1/2 + {match: /[-+]?[0-9]+((\.[0-9]*([eE][+-]?[0-9]+)?M?)|([eE][+-]?[0-9]+M?|M))/}, // float // 0.42 4.2E-1M 42E1 42M + {match: /[-+]?([1-9][0-9]*|0)N?/}, // int (don't match leading 0) // 42 42N + ] }; + const CHARACTER = { + scope: 'character', + variants: [ + {match: /\\o[0-3]?[0-7]{1,2}/}, // Unicode Octal 0 - 377 + {match: /\\u[0-9a-fA-F]{4}/}, // Unicode Hex 0000 - FFFF + {match: /\\(newline|space|tab|formfeed|backspace|return)/}, // special characters + {match: /\\\S/, relevance: 0} // any non-whitespace char + ] + } + const REGEX = { + scope: 'regex', + begin: /#"/, + end: /"/ + } const STRING = hljs.inherit(hljs.QUOTE_STRING_MODE, { illegal: null }); + const COMMA = { + scope: 'punctuation', + match: /,/, + relevance: 0 + } const COMMENT = hljs.COMMENT( ';', '$', @@ -71,15 +95,10 @@ export default function(hljs) { begin: /\b(true|false|nil)\b/ }; const COLLECTION = { - begin: '[\\[\\{]', + begin: "\\[|(#::?" + SYMBOL_RE + ")?\\{", end: '[\\]\\}]', relevance: 0 }; - const HINT = { - className: 'comment', - begin: '\\^' + SYMBOL_RE - }; - const HINT_COL = hljs.COMMENT('\\^\\{', '\\}'); const KEY = { className: 'symbol', begin: '[:]{1,2}' + SYMBOL_RE @@ -100,10 +119,11 @@ export default function(hljs) { starts: BODY }; const DEFAULT_CONTAINS = [ + COMMA, LIST, + CHARACTER, + REGEX, STRING, - HINT, - HINT_COL, COMMENT, KEY, COLLECTION, @@ -138,17 +158,17 @@ export default function(hljs) { ]; BODY.contains = DEFAULT_CONTAINS; COLLECTION.contains = DEFAULT_CONTAINS; - HINT_COL.contains = [ COLLECTION ]; return { name: 'Clojure', aliases: [ 'clj', 'edn' ], illegal: /\S/, contains: [ + COMMA, LIST, + CHARACTER, + REGEX, STRING, - HINT, - HINT_COL, COMMENT, KEY, COLLECTION, diff --git a/test/markup/clojure/character.expect.txt b/test/markup/clojure/character.expect.txt new file mode 100644 index 0000000000..1b7cf05082 --- /dev/null +++ b/test/markup/clojure/character.expect.txt @@ -0,0 +1,5 @@ +\A +\a +\formfeed +\u00DF +\o303 \ No newline at end of file diff --git a/test/markup/clojure/character.txt b/test/markup/clojure/character.txt new file mode 100644 index 0000000000..7cf4531ab9 --- /dev/null +++ b/test/markup/clojure/character.txt @@ -0,0 +1,5 @@ +\A +\a +\formfeed +\u00DF +\o303 \ No newline at end of file diff --git a/test/markup/clojure/deps_edn.expect.txt b/test/markup/clojure/deps_edn.expect.txt index fdedd183bd..9d36af63ad 100644 --- a/test/markup/clojure/deps_edn.expect.txt +++ b/test/markup/clojure/deps_edn.expect.txt @@ -1,14 +1,14 @@ -{:aliases {:export {:exec-fn stelcodes.dev-blog.generator/export}, - :repl {:extra-deps {cider/cider-nrepl {:mvn/version "0.25.2"}, - nrepl/nrepl {:mvn/version "0.8.3"}}, - :extra-paths ["dev"], +{:aliases {:export {:exec-fn stelcodes.dev-blog.generator/export}, + :repl {:extra-deps {cider/cider-nrepl {:mvn/version "0.25.2"}, + nrepl/nrepl {:mvn/version "0.8.3"}}, + :extra-paths ["dev"], :main-opts ["-m" "nrepl.cmdline" "--middleware" "[cider.nrepl/cider-middleware]" - "--interactive"]}, - :webhook {:exec-fn stelcodes.dev-blog.webhook/listen}}, - :deps {http-kit/http-kit {:mvn/version "2.5.3"}, - org.clojure/clojure {:mvn/version "1.10.1"}, - stasis/stasis {:mvn/version "2.5.1"}}, + "--interactive"]}, + :webhook {:exec-fn stelcodes.dev-blog.webhook/listen}}, + :deps {http-kit/http-kit {:mvn/version "2.5.3"}, + org.clojure/clojure {:mvn/version "1.10.1"}, + stasis/stasis {:mvn/version "2.5.1"}}, :paths ["src" "resources"]} diff --git a/test/markup/clojure/globals_definition.expect.txt b/test/markup/clojure/globals_definition.expect.txt index e387a1b63c..93ce3c17a2 100644 --- a/test/markup/clojure/globals_definition.expect.txt +++ b/test/markup/clojure/globals_definition.expect.txt @@ -5,8 +5,8 @@ ; function (defn clojure-function [args] (let [string "multiline\nstring" - regexp #"regexp" - number 100,000 + regexp #"regexp" + number 100000 booleans [false true] keyword ::the-keyword] ;; this is comment @@ -14,12 +14,17 @@ (->> (list [vector] {:map map} #{'set}))))) +#:person{:first "Han" + :last "Solo" + :ship #:ship{:name "Millenium Falcon"}} +#::{:a 1, :b 2} + ; global (def some-var) ; another one (def alternative-var "132") ; defonce -(defonce ^:private another-var #"foo") +(defonce ^:private another-var #"foo") ; private function (defn- add [x y] (+ x y)) @@ -58,4 +63,4 @@ ;; create a couple shapes and get their area (def myCircle (Circle. 10)) -(def mySquare (Square. 5 11)) +(def mySquare (Square. 5 11)) \ No newline at end of file diff --git a/test/markup/clojure/globals_definition.txt b/test/markup/clojure/globals_definition.txt index 6faeee7942..fadd808156 100644 --- a/test/markup/clojure/globals_definition.txt +++ b/test/markup/clojure/globals_definition.txt @@ -6,7 +6,7 @@ (defn clojure-function [args] (let [string "multiline\nstring" regexp #"regexp" - number 100,000 + number 100000 booleans [false true] keyword ::the-keyword] ;; this is comment @@ -14,6 +14,11 @@ (->> (list [vector] {:map map} #{'set}))))) +#:person{:first "Han" + :last "Solo" + :ship #:ship{:name "Millenium Falcon"}} +#::{:a 1, :b 2} + ; global (def some-var) ; another one diff --git a/test/markup/clojure/hint_col.expect.txt b/test/markup/clojure/hint_col.expect.txt deleted file mode 100644 index cb833d9027..0000000000 --- a/test/markup/clojure/hint_col.expect.txt +++ /dev/null @@ -1,34 +0,0 @@ -(import [java.lang.annotation Retention RetentionPolicy Target ElementType] - [javax.xml.ws WebServiceRef WebServiceRefs]) - -(definterface Foo (foo [])) - -;; annotation on type -(deftype ^{Deprecated true - Retention RetentionPolicy/RUNTIME - javax.annotation.processing.SupportedOptions ["foo" "bar" "baz"] - javax.xml.ws.soap.Addressing {:enabled false :required true} - WebServiceRefs [(WebServiceRef {:name "fred" :type String}) - (WebServiceRef {:name "ethel" :mappedName "lucy"})]} - Bar [^int a - ;; on field - ^{:tag int - Deprecated true - Retention RetentionPolicy/RUNTIME - javax.annotation.processing.SupportedOptions ["foo" "bar" "baz"] - javax.xml.ws.soap.Addressing {:enabled false :required true} - WebServiceRefs [(WebServiceRef {:name "fred" :type String}) - (WebServiceRef {:name "ethel" :mappedName "lucy"})]} - b] - ;; on method - Foo (^{Deprecated true - Retention RetentionPolicy/RUNTIME - javax.annotation.processing.SupportedOptions ["foo" "bar" "baz"] - javax.xml.ws.soap.Addressing {:enabled false :required true} - WebServiceRefs [(WebServiceRef {:name "fred" :type String}) - (WebServiceRef {:name "ethel" :mappedName "lucy"})]} - foo [this] 42)) - -(seq (.getAnnotations Bar)) -(seq (.getAnnotations (.getField Bar "b"))) -(seq (.getAnnotations (.getMethod Bar "foo" nil))) diff --git a/test/markup/clojure/hint_col.txt b/test/markup/clojure/hint_col.txt deleted file mode 100644 index 9584dec9d0..0000000000 --- a/test/markup/clojure/hint_col.txt +++ /dev/null @@ -1,34 +0,0 @@ -(import [java.lang.annotation Retention RetentionPolicy Target ElementType] - [javax.xml.ws WebServiceRef WebServiceRefs]) - -(definterface Foo (foo [])) - -;; annotation on type -(deftype ^{Deprecated true - Retention RetentionPolicy/RUNTIME - javax.annotation.processing.SupportedOptions ["foo" "bar" "baz"] - javax.xml.ws.soap.Addressing {:enabled false :required true} - WebServiceRefs [(WebServiceRef {:name "fred" :type String}) - (WebServiceRef {:name "ethel" :mappedName "lucy"})]} - Bar [^int a - ;; on field - ^{:tag int - Deprecated true - Retention RetentionPolicy/RUNTIME - javax.annotation.processing.SupportedOptions ["foo" "bar" "baz"] - javax.xml.ws.soap.Addressing {:enabled false :required true} - WebServiceRefs [(WebServiceRef {:name "fred" :type String}) - (WebServiceRef {:name "ethel" :mappedName "lucy"})]} - b] - ;; on method - Foo (^{Deprecated true - Retention RetentionPolicy/RUNTIME - javax.annotation.processing.SupportedOptions ["foo" "bar" "baz"] - javax.xml.ws.soap.Addressing {:enabled false :required true} - WebServiceRefs [(WebServiceRef {:name "fred" :type String}) - (WebServiceRef {:name "ethel" :mappedName "lucy"})]} - foo [this] 42)) - -(seq (.getAnnotations Bar)) -(seq (.getAnnotations (.getField Bar "b"))) -(seq (.getAnnotations (.getMethod Bar "foo" nil))) diff --git a/test/markup/clojure/number.expect.txt b/test/markup/clojure/number.expect.txt new file mode 100644 index 0000000000..25bb636d60 --- /dev/null +++ b/test/markup/clojure/number.expect.txt @@ -0,0 +1,69 @@ +; integer +00 +42 ++42 +-42 + +; BigInt +42N +0N ++42N +-42N + +; octal +052 +00N ++052 +-00N + +; hex +0x2a +0x0N ++0x2a +-0x0N + +; radix +2r101010 +8r52 +16r2a +36r16 +-2r101010 ++36r16 + +; radix BigInt +2r101010N +8r52N +16r2aN +36r16N ++8r52N +-16r2aN + +;; ratios +1/2 +-1/2 ++123/224 + +;; floats +42.0 +-42.0 ++42.0 +42. ++42. +-42. + +; BigDecimal +42.0M +-42M +42.M +42M + +; with Exponent +42.0E2 +-42.0E+9 +42E-0 ++42E-0 + +42.0E2M +42E+9M +-42E+9M ++42.0E2M \ No newline at end of file diff --git a/test/markup/clojure/number.txt b/test/markup/clojure/number.txt new file mode 100644 index 0000000000..01a0374c09 --- /dev/null +++ b/test/markup/clojure/number.txt @@ -0,0 +1,69 @@ +; integer +00 +42 ++42 +-42 + +; BigInt +42N +0N ++42N +-42N + +; octal +052 +00N ++052 +-00N + +; hex +0x2a +0x0N ++0x2a +-0x0N + +; radix +2r101010 +8r52 +16r2a +36r16 +-2r101010 ++36r16 + +; radix BigInt +2r101010N +8r52N +16r2aN +36r16N ++8r52N +-16r2aN + +;; ratios +1/2 +-1/2 ++123/224 + +;; floats +42.0 +-42.0 ++42.0 +42. ++42. +-42. + +; BigDecimal +42.0M +-42M +42.M +42M + +; with Exponent +42.0E2 +-42.0E+9 +42E-0 ++42E-0 + +42.0E2M +42E+9M +-42E+9M ++42.0E2M \ No newline at end of file diff --git a/test/markup/clojure/symbols-numbers.expect.txt b/test/markup/clojure/symbols-numbers.expect.txt index 87595d111b..7813d22876 100644 --- a/test/markup/clojure/symbols-numbers.expect.txt +++ b/test/markup/clojure/symbols-numbers.expect.txt @@ -1 +1,4 @@ (def +x [(a 1) +2 -3.0 y-5]) +(System/getProperty "java.vm.version") +(.getEnclosingClass java.util.Map$Entry) +(java.util.Map$Entry. .getEnclosingClass) diff --git a/test/markup/clojure/symbols-numbers.txt b/test/markup/clojure/symbols-numbers.txt index 01e839b555..97c92bb562 100644 --- a/test/markup/clojure/symbols-numbers.txt +++ b/test/markup/clojure/symbols-numbers.txt @@ -1 +1,4 @@ (def +x [(a 1) +2 -3.0 y-5]) +(System/getProperty "java.vm.version") +(.getEnclosingClass java.util.Map$Entry) +(java.util.Map$Entry. .getEnclosingClass) \ No newline at end of file