Skip to content

Keyword and Symbol prefix matching #27

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Jan 7, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
127 changes: 111 additions & 16 deletions clj/test/vim/syntax_test.clj
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,54 @@
;; Joel Holdbrooks <[email protected]>

(ns vim.syntax-test
(:require [vim.test :refer [defpredicates defsyntaxtest]]))
(:require [vim.test :refer [defpredicates defsyntaxtest def-eq-predicates]]))

;; defpredicates also register not-equal vars, this is just for clj-kondo
(declare !number !regexp-escape !regexp-posix-char-class !regexp-quantifier)

(defpredicates number :clojureNumber)
(defpredicates kw :clojureKeyword)
(def-eq-predicates kw [:clojureKeywordNsColon :clojureKeyword])
(def-eq-predicates kwCurrentNs [:clojureKeywordNsColon :clojureKeywordNsColon :clojureKeyword])
(def-eq-predicates kwWithNs [:clojureKeywordNsColon :clojureKeywordNs :clojureKeywordNsSeparator :clojureKeyword])
(def-eq-predicates kwWithNs3_4 [:clojureKeywordNsColon
:clojureKeywordNs
:clojureKeywordNs
:clojureKeywordNs
:clojureKeywordNsSeparator
:clojureKeyword
:clojureKeyword
:clojureKeyword
:clojureKeyword])
(def-eq-predicates sym [:clojureSymbol])
(def-eq-predicates symWithNs [:clojureSymbol])
(def-eq-predicates symWithNs_tripleBody [:clojureKeywordNsColon
:clojureKeywordNs :clojureKeywordNsSeparator
:clojureKeywordNs :clojureKeywordNsSeparator
:clojureKeywordNs :clojureKeywordNsSeparator
:clojureKeyword])
(def-eq-predicates kwWithNamedNs [:clojureKeywordNsColon :clojureKeywordNsColon
:clojureKeywordNs :clojureKeywordNsSeparator :clojureKeyword])
(def-eq-predicates dispatchWithSymbolInside [:clojureDispatch
:clojureSymbol
:clojureSymbol
:clojureSymbol
:clojureSymbol
:clojureSymbol
:clojureSymbol
:clojureSymbol
:clojureSymbol
:clojureParen
:clojureSymbolNs
:clojureSymbolNs
:clojureSymbolNs
:clojureSymbolNs
:clojureSymbolNsSeparator
:clojureSymbol
:clojureSymbol
:clojureSymbol
:clojureSymbol
:clojureSymbol
:clojureParen])
(defpredicates character :clojureCharacter)
(defpredicates regexp :clojureRegexp)
(defpredicates regexp-delimiter :clojureRegexpDelimiter)
Expand Down Expand Up @@ -108,23 +149,78 @@

(comment (test #'test-character-literals))

(def emptyKeyword (keyword ""))

(defsyntaxtest keywords-test
["%s"
[":1" kw
":A" kw
":a" kw
":αβγ" kw
"::a" kw
":a/b" kw
":a:b" kw
":a:b/:c:b" kw
":a/b/c/d" kw
"::a/b" kw
"::" !kw
":a:" !kw
":a/" !kw
; ":/" !kw ; This is legal, but for simplicity we do not match it
":" !kw]])
":αβγ" (partial = [:clojureKeywordNsColon :clojureKeyword :clojureKeyword :clojureKeyword])
"::a" kwCurrentNs
":a/b" kwWithNs
":a:b/:c:b" kwWithNs3_4
":a/b/c/d" symWithNs_tripleBody
"::a/b" kwWithNamedNs
"::" (partial = [emptyKeyword emptyKeyword])
":a:" (partial = [emptyKeyword :clojureSymbol emptyKeyword])
":a/" (partial = [:clojureKeywordNsColon :clojureKeywordNs :clojureKeywordNsSeparator])
":/" (partial = [:clojureKeywordNsColon :clojureKeywordNsSeparator])
":" (partial = [emptyKeyword])
"a[:b/c]" (partial = [:clojureSymbol
:clojureParen
:clojureKeywordNsColon
:clojureKeywordNs
:clojureKeywordNsSeparator
:clojureKeyword
:clojureParen])
":a[:b/c]" (partial = [:clojureKeywordNsColon
:clojureKeyword
:clojureParen
:clojureKeywordNsColon
:clojureKeywordNs
:clojureKeywordNsSeparator
:clojureKeyword
:clojureParen])]])

(defsyntaxtest symbols-test
["%s"
["1" !sym
"1" !symWithNs
"A" sym
"a" sym
"αβγ" (partial = [:clojureSymbol :clojureSymbol :clojureSymbol])
"a/b" (partial = [:clojureSymbolNs :clojureSymbolNsSeparator :clojureSymbol])
"a:b" (partial = [:clojureSymbol :clojureSymbol :clojureSymbol])
"a:b/:c:b" (partial = [:clojureSymbolNs
:clojureSymbolNs
:clojureSymbolNs
:clojureSymbolNsSeparator
:clojureSymbol
:clojureSymbol
:clojureSymbol
:clojureSymbol])
"a/b/c/d" (partial = [:clojureSymbolNs :clojureSymbolNsSeparator
:clojureSymbolNs :clojureSymbolNsSeparator
:clojureSymbolNs :clojureSymbolNsSeparator
:clojureSymbol])
"a:" !sym
"a:" !symWithNs
"a/" !sym
"a/" !symWithNs
"/" !sym
"#function[test/hello]" dispatchWithSymbolInside
"a[b/c]" (partial = [:clojureSymbol
:clojureParen
:clojureSymbolNs
:clojureSymbolNsSeparator
:clojureSymbol
:clojureParen])
"#'a/b" (partial = [:clojureDispatch
:clojureDispatch
:clojureSymbolNs
:clojureSymbolNsSeparator
:clojureSymbol])]])

(comment (test #'keywords-test))

Expand Down Expand Up @@ -403,8 +499,7 @@
;; (?>X) X, as an independent, non-capturing group
"(?>X)" regexp-mod

"(?X)" !regexp-mod
]]
"(?X)" !regexp-mod]]
["#%s"
[;; Backslashes with character classes
"\"[\\\\]\"" (partial = [:clojureRegexpDelimiter :clojureRegexpCharClass :clojureRegexpCharClass :clojureRegexpCharClass :clojureRegexpCharClass :clojureRegexpDelimiter])
Expand Down
26 changes: 20 additions & 6 deletions clj/test/vim/test.clj
Original file line number Diff line number Diff line change
Expand Up @@ -90,21 +90,35 @@
ss λs)))
contexts)))))

(defmacro defpredicates
(defmacro defpredicates-general
"Create two complementary predicate vars, `sym` and `!sym`, which test if
all members of a passed collection are equal to `kw`"
[sym kw]
[pred-eq pred-neq sym kw]
`(do
(defn ~sym
~(str "Returns true if all elements of coll equal " kw)
{:arglists '~'[coll]}
{:arglists (list '~'[coll])}
[coll#]
(every? (partial = ~kw) coll#))
(~pred-eq ~kw coll#))
(defn ~(symbol (str \! sym))
~(str "Returns true if any elements of coll do not equal " kw)
{:arglists '~'[coll]}
{:arglists (list '~'[coll])}
[coll#]
(boolean (some (partial not= ~kw) coll#)))))
(~pred-neq ~kw coll#))))

(defmacro defpredicates
"Create two complementary predicate vars, `sym` and `!sym`, which test if
all members of a passed collection are equal to `kw`"
[sym kw]
(let [pred-eq (fn [expected results] (every? (partial = expected) results))
pred-neq (fn [expected results] (boolean (some (partial not= expected) results)))]
`(defpredicates-general ~pred-eq ~pred-neq ~sym ~kw)))

(defmacro def-eq-predicates
"Create two complementary predicate vars, `sym` and `!sym`, which test if
input and result are equal"
[sym kw]
`(defpredicates-general = not= ~sym ~kw))

(defn benchmark [n file buf & exprs]
(vim-exec file buf (format "Benchmark(%d, %s)"
Expand Down
16 changes: 13 additions & 3 deletions syntax/clojure.vim
Original file line number Diff line number Diff line change
Expand Up @@ -62,21 +62,26 @@ endif
unlet! s:key
delfunction s:syntax_keyword

syntax match clojureKeywordNs contained "\v[^ \n\r\t()\[\]{}";@^`~\\\/'#]+\ze\/"
syntax match clojureKeywordNsSeparator contained "/"
syntax match clojureKeywordNsColon contained "\v<:{1,2}"
" Keywords are symbols:
" static Pattern symbolPat = Pattern.compile("[:]?([\\D&&[^/]].*/)?([\\D&&[^/]][^/]*)");
" But they:
" * Must not end in a : or /
" * Must not have two adjacent colons except at the beginning
" * Must not contain any reader metacharacters except for ' and #
syntax match clojureKeyword "\v<:{1,2}([^ \n\r\t()\[\]{}";@^`~\\/]+/)*[^ \n\r\t()\[\]{}";@^`~\\/]+:@1<!>"
syntax match clojureKeyword "\v<:{1,2}([^ \n\r\t()\[\]{}";@^`~\\/]*/)*[^ \n\r\t()\[\]{}";@^`~\\/]*:@1<!>" contains=clojureKeywordNs,clojureKeywordNsSeparator,clojureKeywordNsColon

syntax match clojureStringEscape "\v\\%([\\btnfr"]|u\x{4}|[0-3]\o{2}|\o{1,2})" contained

syntax region clojureString matchgroup=clojureStringDelimiter start=/"/ skip=/\\\\\|\\"/ end=/"/ contains=clojureStringEscape,@Spell

syntax match clojureCharacter "\v\\%(o%([0-3]\o{2}|\o{1,2})|u\x{4}|newline|tab|space|return|backspace|formfeed|.)"

syntax match clojureSymbol "\v%([a-zA-Z!$&*_+=|<.>?-]|[^\x00-\x7F])+%(:?%([a-zA-Z0-9!#$%&*_+=|'<.>/?-]|[^\x00-\x7F]))*[#:]@1<!"
syntax match clojureSymbolNs contained "\v[^ \n\r\t()\[\]{}";@^`~\\\/'#]+\ze\/"
syntax match clojureSymbolNsSeparator contained "/"
syntax match clojureSymbol "\v%([a-zA-Z!$&*_+=|<.>?-]|[^\x00-\x7F])+%(:?%([a-zA-Z0-9!#$%&*_+=|'<.>/?-]|[^\x00-\x7F]))*[#:]@1<!" contains=clojureSymbolNs,clojureSymbolNsSeparator

" NB. Correct matching of radix literals was removed for better performance.
syntax match clojureNumber "\v<[-+]?%(%([2-9]|[12]\d|3[0-6])[rR][[:alnum:]]+|%(0\o*|0x\x+|[1-9]\d*)N?|%(0|[1-9]\d*|%(0|[1-9]\d*)\.\d*)%(M|[eE][-+]?\d+)?|%(0|[1-9]\d*)/%(0|[1-9]\d*))>"
Expand Down Expand Up @@ -162,12 +167,17 @@ syntax sync fromstart
highlight default link clojureConstant Constant
highlight default link clojureBoolean Boolean
highlight default link clojureCharacter Character
highlight default link clojureKeyword Keyword
highlight default link clojureNumber Number
highlight default link clojureString String
highlight default link clojureStringDelimiter String
highlight default link clojureStringEscape Character

highlight default link clojureKeyword Keyword
highlight default link clojureKeywordNsColon clojureKeyword
highlight default link clojureKeywordNs clojureKeyword

highlight default link clojureSymbolNs clojureSymbol

highlight default link clojureRegexp Constant
highlight default link clojureRegexpDelimiter Constant
highlight default link clojureRegexpEscape Character
Expand Down