Skip to content

Commit

Permalink
Merge pull request #729 from pulsar-edit/clojure-grammar-enhancements
Browse files Browse the repository at this point in the history
Another batch of Clojure enhancements
  • Loading branch information
mauricioszabo authored Jun 11, 2024
2 parents 8c6bef4 + 65c445d commit 7e38c83
Show file tree
Hide file tree
Showing 18 changed files with 367 additions and 89 deletions.
12 changes: 12 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,18 @@
- Project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html)

## [Unreleased]
- Added a new modern tree sitter "test" for highlight query - `ancestorTypeNearerThan`
that matches if it finds the _first_ type as an ancestor, but _not matches_ if
any "other" ancestors are found before
- Syntax quoting and unquoting in Clojure now highlights correctly, and also
highlights full qualified keywords differently than generated ones
- `content` field of addInjectionPoint for modern-tree-sitter now supports a second
`buffer` argument, for better customization if one wants to
- EDN is back to be detected as Clojure (for compatibility) but highlights as EDN
- Fixed syntax quoting on Clojure grammar (newer tree-sitter), fixed some
injection points on Clojure. Added support for highligting metadata, and added
better support for "def" elements (example - don't syntax `default` or
`definition` as a `def`, but highlights `p/defresolver`)

## 1.117.0

Expand Down
2 changes: 2 additions & 0 deletions packages/language-clojure/grammars/clojure.cson
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
'fileTypes': [
'boot'
'clj'
'clje'
'cljr'
'clj.hl'
'cljc'
'cljs'
Expand Down
11 changes: 10 additions & 1 deletion packages/language-clojure/grammars/tree-sitter-clojure.cson
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,19 @@ parser: 'tree-sitter-clojure'
'fileTypes': [
'boot'
'clj'
'clje'
'cljr'
'clj.hl'
'cljc'
'cljs'
'cljs.hl'
'cljx'
'clojure'
'edn'
'bb'
'joke'
'joker'
'jank'
]
'firstLineMatch': '''(?x)
# Hashbang
Expand All @@ -37,6 +41,11 @@ parser: 'tree-sitter-clojure'
)
'''
treeSitter:
parserSource: 'github:mauricioszabo/tree-sitter-clojure#inner-strings'
grammar: 'ts/grammar.wasm'
highlightsQuery: 'ts/highlights.scm'
highlightsQuery: [
'ts/highlights.scm'
'ts/edn-highlights.scm'
]
foldsQuery: 'ts/folds.scm'
tagsQuery: 'ts/tags.scm'
9 changes: 6 additions & 3 deletions packages/language-clojure/grammars/tree-sitter-edn.cson
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
name: 'Clojure EDN'
scopeName: 'source.edn'
type: 'modern-tree-sitter'
injectionRegExp: '^source-edn$'
injectionRegExp: '^source-clojure-edn$'
parser: 'tree-sitter-clojure'

fileTypes: [
'edn'
]
treeSitter:
parserSource: 'github:mauricioszabo/tree-sitter-clojure#inner-strings'
grammar: 'ts/grammar.wasm'
highlightsQuery: 'ts/edn-highlights.scm'
highlightsQuery: [
'ts/edn-only-highlights.scm'
'ts/edn-highlights.scm'
]
foldsQuery: 'ts/folds.scm'
9 changes: 0 additions & 9 deletions packages/language-clojure/grammars/tree-sitter-quoted.cson

This file was deleted.

15 changes: 10 additions & 5 deletions packages/language-clojure/grammars/ts/edn-highlights.scm
Original file line number Diff line number Diff line change
@@ -1,9 +1,4 @@
;; Collections
(list_lit
"(" @punctuation.section.list.begin (#is-not? test.descendantOfNodeWithData "clojure.dismissTag")
")" @punctuation.section.list.end)
@meta.list

(vec_lit
"[" @punctuation.section.vector.begin (#is-not? test.descendantOfNodeWithData "clojure.dismissTag")
"]" @punctuation.section.vector.end)
Expand All @@ -19,9 +14,19 @@
"}" @punctuation.section.set.end)
@meta.set

(meta_lit) @meta.metadata.clojure

((regex_lit) @string.regexp (#is-not? test.descendantOfNodeWithData "clojure.dismissTag"))
((sym_lit) @meta.symbol (#is-not? test.descendantOfNodeWithData "clojure.dismissTag"))
((kwd_lit) @constant.keyword (#is-not? test.descendantOfNodeWithData "clojure.dismissTag"))
(str_lit
"\"" @punctuation.definition.string.begin.clojure
(#is-not? test.descendantOfNodeWithData "clojure.dismissTag")
(#is? test.first))
(str_lit
"\"" @punctuation.definition.string.end.clojure
(#is-not? test.descendantOfNodeWithData "clojure.dismissTag")
(#is? test.last))
((str_lit) @string.quoted.double (#is-not? test.descendantOfNodeWithData "clojure.dismissTag"))
((num_lit) @constant.numeric (#is-not? test.descendantOfNodeWithData "clojure.dismissTag"))
((nil_lit) @constant.language (#is-not? test.descendantOfNodeWithData "clojure.dismissTag"))
Expand Down
4 changes: 4 additions & 0 deletions packages/language-clojure/grammars/ts/edn-only-highlights.scm
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
(list_lit
"(" @punctuation.section.list.begin (#is-not? test.descendantOfNodeWithData "clojure.dismissTag")
")" @punctuation.section.list.end)
@meta.list
Binary file modified packages/language-clojure/grammars/ts/grammar.wasm
Binary file not shown.
90 changes: 51 additions & 39 deletions packages/language-clojure/grammars/ts/highlights.scm
Original file line number Diff line number Diff line change
@@ -1,4 +1,44 @@
;; "Special" things
(list_lit
"(" @punctuation.section.expression.begin (#is-not? test.descendantOfNodeWithData clojure.dismissTag)
.
(sym_lit) @storage.control (#eq? @storage.control "do"))

(list_lit
"(" @punctuation.section.expression.begin (#is-not? test.descendantOfNodeWithData clojure.dismissTag)
.
(sym_lit) @keyword.control.conditional.if (#eq? @keyword.control.conditional.if "if"))

(list_lit
"(" @punctuation.section.expression.begin (#is-not? test.descendantOfNodeWithData clojure.dismissTag)
.
(sym_lit) @keyword.control.conditional.when (#eq? @keyword.control.conditional.when "when"))

(list_lit
"(" @punctuation.section.expression.begin (#is-not? test.descendantOfNodeWithData clojure.dismissTag)
.
(sym_lit) @keyword.control.js.clojure (#eq? @keyword.control.js.clojure "js*"))

;; Syntax quoting
((syn_quoting_lit)
@meta.syntax-quoted
(#is? test.ancestorTypeNearerThan "syn_quoting_lit unquoting_lit"))

((sym_lit) @meta.symbol.syntax-quoted
(#is? test.ancestorTypeNearerThan "syn_quoting_lit unquoting_lit")
(#match? @meta.symbol.syntax-quoted "[^#]$"))

((sym_lit) @meta.symbol.generated
(#is? test.ancestorTypeNearerThan "syn_quoting_lit unquoting_lit")
(#match? @meta.symbol.generated "#$"))

;; Function calls
(list_lit
"(" @punctuation.section.expression.begin (#is-not? test.descendantOfNodeWithData clojure.dismissTag)
.
(sym_lit) @keyword.control.conditional.cond (#match? @keyword.control.conditional.cond "^cond(|.|-{1,2}>)$"))

;; Other function calls
(anon_fn_lit
"(" @punctuation.section.expression.begin (#is-not? test.descendantOfNodeWithData "clojure.dismissTag")
.
Expand Down Expand Up @@ -48,14 +88,22 @@
"("
.
(kwd_lit) @invalid.deprecated (#eq? @invalid.deprecated ":use")
(#is? test.descendantOfNodeWithData isNamespace)
(#is? test.config language-clojure.markDeprecations))
(#is? test.config language-clojure.markDeprecations)
(#is? test.descendantOfNodeWithData isNamespace))

;; Definition
(list_lit
"(" @punctuation.section.expression.begin (#is-not? test.descendantOfNodeWithData "clojure.dismissTag")
.
(sym_lit) @keyword.control (#match? @keyword.control "^def")
(sym_lit) @keyword.control (#match? @keyword.control "^def(on[^\s]*|test|macro|n|n-|protocol|record|struct|)$")
.
(sym_lit) @meta.definition.global @entity.global
")" @punctuation.section.expression.end)

(list_lit
"(" @punctuation.section.expression.begin (#is-not? test.descendantOfNodeWithData "clojure.dismissTag")
.
(sym_lit) @keyword.control (#match? @keyword.control "/def")
.
(sym_lit) @meta.definition.global @entity.global
")" @punctuation.section.expression.end)
Expand All @@ -76,39 +124,3 @@
(sym_lit) @keyword.control (#eq? @keyword.control "comment")
(#is-not? test.config language-clojure.commentTag)
")" @punctuation.section.expression.end)

;;; COPY-PASTED from edn-highlights.
;; IF you need to add something here, add to edn-highlights
;; and then paste here, but DON'T PASTE the first `list_lit`

;; Collections
(vec_lit
"[" @punctuation.section.vector.begin (#is-not? test.descendantOfNodeWithData "clojure.dismissTag")
"]" @punctuation.section.vector.end)
@meta.vector

(map_lit
"{" @punctuation.section.map.begin (#is-not? test.descendantOfNodeWithData "clojure.dismissTag")
"}" @punctuation.section.map.end)
@meta.map

(set_lit
("#" "{") @punctuation.section.set.begin (#is-not? test.descendantOfNodeWithData "clojure.dismissTag")
"}" @punctuation.section.set.end)
@meta.set

((regex_lit) @string.regexp (#is-not? test.descendantOfNodeWithData "clojure.dismissTag"))
((sym_lit) @meta.symbol (#is-not? test.descendantOfNodeWithData "clojure.dismissTag"))
((kwd_lit) @constant.keyword (#is-not? test.descendantOfNodeWithData "clojure.dismissTag"))
((str_lit) @string.quoted.double (#is-not? test.descendantOfNodeWithData "clojure.dismissTag"))
((num_lit) @constant.numeric (#is-not? test.descendantOfNodeWithData "clojure.dismissTag"))
((nil_lit) @constant.language (#is-not? test.descendantOfNodeWithData "clojure.dismissTag"))
((bool_lit) @constant.language (#is-not? test.descendantOfNodeWithData clojure.dismissTag))
(comment) @comment.line.semicolon
((dis_expr)
@comment.block.clojure
(#is? test.config language-clojure.dismissTag)
(#set! clojure.dismissTag true)
(#set! capture.final true))

("ERROR" @invalid.illegal)
47 changes: 47 additions & 0 deletions packages/language-clojure/grammars/ts/tags.scm
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
(list_lit
"(" (#is-not? test.descendantOfNodeWithData "clojure.dismissTag")
.
(sym_lit) @keyword.control (#match? @keyword.control "defmacro")
.
(sym_lit) @name
")") @definition.constructor

(list_lit
"(" (#is-not? test.descendantOfNodeWithData "clojure.dismissTag")
.
(sym_lit) @keyword.control (#match? @keyword.control "def(protocol|struct|record)")
.
(sym_lit) @name
")") @definition.struct

(list_lit
"(" (#is-not? test.descendantOfNodeWithData "clojure.dismissTag")
.
(sym_lit) @keyword.control (#match? @keyword.control "^def(on[^\s]*|test|macro|n|n-)$")
.
(sym_lit) @name
")") @definition.function

(list_lit
"(" (#is-not? test.descendantOfNodeWithData "clojure.dismissTag")
.
(sym_lit) @keyword.control (#match? @keyword.control "^def(on[^\s]*|test|macro|n|n-)$")
.
(sym_lit) @name
")") @definition.variable

(list_lit
"(" @punctuation.section.expression.begin (#is-not? test.descendantOfNodeWithData "clojure.dismissTag")
.
(sym_lit) @keyword.control (#match? @keyword.control "/def.")
.
(sym_lit) @name
")") @definition.function

(list_lit
"(" @punctuation.section.expression.begin (#is-not? test.descendantOfNodeWithData "clojure.dismissTag")
.
(sym_lit) @keyword.control (#match? @keyword.control "/def")
.
(sym_lit) @name
")") @definition.variable
59 changes: 35 additions & 24 deletions packages/language-clojure/lib/main.js
Original file line number Diff line number Diff line change
@@ -1,39 +1,50 @@
const path = require('path');

exports.activate = function() {
if (!atom.grammars.addInjectionPoint) return;

const notDisChild = (node) => {
let parent = node.parent
while(parent) {
if(parent.type === 'dis_expr') return null
parent = parent.parent
}
return node
}

atom.grammars.addInjectionPoint('source.clojure', {
type: 'quoting_lit',
language: () => 'source-edn',
content: (node) => {
let parent = node.parent
while(parent) {
if(parent.type === 'dis_expr') return null
parent = parent.parent
}
return node
},
language: () => 'source-clojure-edn',
content: notDisChild,
includeChildren: true,
languageScope: 'source.edn',
languageScope: 'source.clojure',
coverShallowerScopes: true
});

atom.grammars.addInjectionPoint('source.clojure', {
type: 'syn_quoting_lit',
language: () => 'source-quoted-clojure',
content: (node) => node,
type: 'source',
language: () => 'source-clojure-edn',
content: (node, buffer) => {
if (path.extname(buffer.getPath() ?? '') === '.edn') {
return node
}
},
includeChildren: true,
languageScope: 'source.quoted.clojure',
languageScope: 'source.clojure',
coverShallowerScopes: true
});

['unquoting_lit', 'unquote_splicing_lit'].forEach(scope => {
atom.grammars.addInjectionPoint('source.quoted.clojure', {
type: scope,
language: () => 'source-clojure',
content: (node) => node,
includeChildren: true,
languageScope: 'source.clojure',
coverShallowerScopes: true
});
})
atom.grammars.addInjectionPoint('source.clojure', {
type: 'list_lit',
language(node) {
if(node.children[1].text === 'js*') {
return 'javascript'
}
},
content(node) {
if(node.children[2].type === 'str_lit') {
return node.children[2].children[1];
}
},
});
}
1 change: 1 addition & 0 deletions packages/language-clojure/settings/language-clojure.cson
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
'.source.clojure':
'editor':
'commentStart': '; '
'nonWordCharacters': '/\\()"\':,;~^[]{}`'
'commentDelimiters':
'line': ';'
'autocomplete':
Expand Down
Loading

1 comment on commit 7e38c83

@ianffcs
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add ".cljd" for clojuredart also :D

Please sign in to comment.