feat: improve treesitter highlighting
This commit is contained in:
parent
a8cb3c0c78
commit
21dd3e6045
|
@ -25,7 +25,7 @@ When looking for a way to extend remark I first looked for an existing plugin wh
|
||||||
|
|
||||||
I was able to write a really simple and short solution using a pair of Regexes working in conjunction to split strings and replace custom ruby shorthands with HTML, which then passes through to Rehype.
|
I was able to write a really simple and short solution using a pair of Regexes working in conjunction to split strings and replace custom ruby shorthands with HTML, which then passes through to Rehype.
|
||||||
|
|
||||||
```ts
|
```typescript
|
||||||
import { visit } from "unist-util-visit";
|
import { visit } from "unist-util-visit";
|
||||||
import type { Node } from "unist-util-visit/lib";
|
import type { Node } from "unist-util-visit/lib";
|
||||||
|
|
||||||
|
|
|
@ -226,7 +226,6 @@ An example of this is the `Functor` instance of `Either`:
|
||||||
instance Functor (Either a) where
|
instance Functor (Either a) where
|
||||||
fmap _ (Left x) = Left x
|
fmap _ (Left x) = Left x
|
||||||
fmap f (Right x) = Right (f x)
|
fmap f (Right x) = Right (f x)
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
In the instance declaration above, we specify that, for any type `a`, `Either a` is an instance of the `Functor` type class.
|
In the instance declaration above, we specify that, for any type `a`, `Either a` is an instance of the `Functor` type class.
|
||||||
|
|
|
@ -40,50 +40,57 @@
|
||||||
|
|
||||||
.kanagawa {
|
.kanagawa {
|
||||||
background-color: var(--kngw-sumiInk1);
|
background-color: var(--kngw-sumiInk1);
|
||||||
|
border: 0.25rem solid var(--kngw-sumiInk3);
|
||||||
color: var(--kngw-fujiWhite);
|
color: var(--kngw-fujiWhite);
|
||||||
|
|
||||||
.string {
|
&::before {
|
||||||
color: var(--kngw-springGreen);
|
background: var(--kngw-sumiInk3);
|
||||||
}
|
}
|
||||||
|
|
||||||
.type, .type-builtin {
|
// Identifiers
|
||||||
color: var(--kngw-waveAqua2);
|
.variable-builtin { color: var(--kngw-waveRed); }
|
||||||
}
|
.variable-parameter { color: var(--kngw-springViolet2); }
|
||||||
|
|
||||||
.keyword {
|
.constant { color: var(--kngw-surimiOrange); }
|
||||||
color: var(--kngw-oniViolet);
|
|
||||||
}
|
|
||||||
|
|
||||||
.constructor {
|
.label { color: var(--kngw-oniViolet); }
|
||||||
color: var(--kngw-springBlue);
|
|
||||||
}
|
|
||||||
|
|
||||||
.tag {
|
// Literals
|
||||||
color: var(--kngw-springBlue);
|
.string { color: var(--kngw-springGreen); }
|
||||||
}
|
.string-special { color: var(--kngw-boatYellow2); }
|
||||||
|
|
||||||
.variable {
|
.number { color: var(--kngw-sakuraPink); }
|
||||||
color: var(--kngw-fujiWhite);
|
.number-float { color: var(--kngw-sakuraPink); };
|
||||||
}
|
|
||||||
|
|
||||||
.operator, .string-special {
|
// Types
|
||||||
color: var(--kngw-boatYellow2);
|
.type { color: var(--kngw-waveAqua2); }
|
||||||
}
|
.type-builtin { color: var(--kngw-waveAqua2); }
|
||||||
|
|
||||||
.comment {
|
.attribute { color: var(--kngw-carpYellow); }
|
||||||
color: var(--kngw-fujiGray);
|
.property { color: var(--kngw-carpYellow); }
|
||||||
}
|
|
||||||
|
|
||||||
.include {
|
// Functions
|
||||||
color: var(--kngw-oniViolet);
|
.function { color: var(--kngw-crystalBlue); }
|
||||||
}
|
.function-macro { color: var(--kngw-waveRed); }
|
||||||
|
.function-method { color: var(--kngw-crystalBlue) }
|
||||||
|
|
||||||
.function {
|
.constructor { color: var(--kngw-springBlue); }
|
||||||
color: var(--kngw-crystalBlue);
|
.operator { color: var(--kngw-boatYellow2); }
|
||||||
}
|
|
||||||
|
|
||||||
.property, .attribute {
|
// Keywords
|
||||||
color: var(--kngw-carpYellow);
|
.keyword { color: var(--kngw-oniViolet); }
|
||||||
}
|
|
||||||
|
.keyword-return { color: var(--kngw-peachRed); }
|
||||||
|
.keyword-debug { color: var(--kngw-peachRed); }
|
||||||
|
.keyword-exception { color: var(--kngw-peachRed); }
|
||||||
|
|
||||||
|
// Punctuation
|
||||||
|
.punctuation-delimiter { color: var(--kngw-springViolet2); }
|
||||||
|
.punctuation-bracket { color: var(--kngw-springViolet2); }
|
||||||
|
|
||||||
|
// Comments
|
||||||
|
.comment { color: var(--kngw-fujiGray); }
|
||||||
|
|
||||||
|
// Markup
|
||||||
|
.tag { color: var(--kngw-springBlue); }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -104,18 +104,30 @@
|
||||||
border: 1px dashed lightgray;
|
border: 1px dashed lightgray;
|
||||||
}
|
}
|
||||||
|
|
||||||
> :is(pre:has(> code)),
|
.listing {
|
||||||
> pre.astro-code {
|
position: relative;
|
||||||
|
border-radius: 0.5rem;
|
||||||
margin: 1em 0.5em;
|
margin: 1em 0.5em;
|
||||||
padding: 0.5em;
|
|
||||||
max-width: 100%;
|
|
||||||
border-radius: 0.5em;
|
|
||||||
overflow-x: auto;
|
|
||||||
border: 1px dashed lightgray;
|
|
||||||
|
|
||||||
> code {
|
&::before {
|
||||||
|
content: attr(data-lang);
|
||||||
display: block;
|
display: block;
|
||||||
line-height: 1.5em;
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
position: absolute;
|
||||||
|
padding: 0.1em 0.2em 0 0.3em;
|
||||||
|
border-radius: 0 0 0 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
pre {
|
||||||
|
padding: 0.5em;
|
||||||
|
max-width: 100%;
|
||||||
|
overflow-x: auto;
|
||||||
|
|
||||||
|
> code {
|
||||||
|
display: block;
|
||||||
|
line-height: 1.5em;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -9,50 +9,54 @@ function text(value: string) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function span(classes: string[], value: string) {
|
function span(name: string) {
|
||||||
return {
|
return {
|
||||||
type: 'element',
|
type: 'element',
|
||||||
tagName: 'span',
|
tagName: 'span',
|
||||||
properties: {
|
properties: {
|
||||||
className: classes.map(c => c.replace('.', '-')).join(' '),
|
className: name.replace('.', '-'),
|
||||||
},
|
},
|
||||||
children: [
|
children: []
|
||||||
text(value),
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export default function rehypeTreesitter() {
|
export default function rehypeTreesitter() {
|
||||||
return function (tree: any) {
|
return function (tree: any) {
|
||||||
visit(tree, null, (node, _, parent) => {
|
visit(tree, null, (node, _, above) => {
|
||||||
if (node.tagName !== 'code' || parent.tagName !== 'pre') return;
|
if (node.tagName !== 'code' || above.tagName !== 'pre') return;
|
||||||
parent.properties.className = ['kanagawa'];
|
const code = node.children?.[0].value || '';
|
||||||
|
const lang = node.properties.className?.[0].replace('language-', '') || '';
|
||||||
|
const parent = { ...above };
|
||||||
|
|
||||||
const code = node.children?.[0].value;
|
above.tagName = 'figure';
|
||||||
const lang = node.properties.className?.[0].replace('language-', '');
|
above.children = [parent];
|
||||||
if (!lang || !code) return;
|
above.properties = {
|
||||||
|
className: 'listing kanagawa',
|
||||||
|
...!!lang && { "data-lang": lang },
|
||||||
|
};
|
||||||
|
|
||||||
const stack: string[] = [];
|
const root = { children: [] };
|
||||||
const children = (node.children = [] as any[] );
|
const ptrs: any[] = [root];
|
||||||
const events = treesitter.hl(lang, code);
|
|
||||||
|
|
||||||
for (const event of events) {
|
for (const event of treesitter.hl(lang, code)) {
|
||||||
switch (event.kind) {
|
switch (event.kind) {
|
||||||
case 'text': {
|
case 'text': {
|
||||||
const child = (stack.length)
|
const inserted = text(event.text);
|
||||||
? span(stack, event.text)
|
ptrs.at(-1).children.push(inserted);
|
||||||
: text(event.text);
|
|
||||||
children.push(child);
|
|
||||||
} break;
|
} break;
|
||||||
case 'open': {
|
case 'open': {
|
||||||
stack.push(event.name);
|
const inserted = span(event.name);
|
||||||
|
ptrs.at(-1).children.push(inserted);
|
||||||
|
ptrs.push(inserted);
|
||||||
} break;
|
} break;
|
||||||
case 'close': {
|
case 'close': {
|
||||||
stack.pop();
|
ptrs.pop();
|
||||||
} break;
|
} break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
node.children = root.children;
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,11 +17,13 @@ tree-sitter = "0.20.10"
|
||||||
tree-sitter-highlight = "0.20.1"
|
tree-sitter-highlight = "0.20.1"
|
||||||
|
|
||||||
# Languages
|
# Languages
|
||||||
|
tree-sitter-astro = { git = "https://github.com/virchau13/tree-sitter-astro.git", rev = "e924787e12e8a03194f36a113290ac11d6dc10f3" }
|
||||||
tree-sitter-css = "0.20.0"
|
tree-sitter-css = "0.20.0"
|
||||||
tree-sitter-haskell = { git = "https://github.com/tree-sitter/tree-sitter-haskell", rev = "cf98de23e4285b8e6bcb57b050ef2326e2cc284b" }
|
tree-sitter-haskell = { git = "https://github.com/tree-sitter/tree-sitter-haskell", rev = "1da347c88599faad7964e63facead5d163ac7dba" }
|
||||||
tree-sitter-html = "0.20.0"
|
tree-sitter-html = "0.20.0"
|
||||||
tree-sitter-javascript = "0.20.3"
|
tree-sitter-javascript = "0.20.3"
|
||||||
tree-sitter-md = "0.1.7"
|
tree-sitter-md = "0.1.7"
|
||||||
|
tree-sitter-regex = "0.20.0"
|
||||||
tree-sitter-rust = "0.20.4"
|
tree-sitter-rust = "0.20.4"
|
||||||
tree-sitter-typescript = "0.20.5"
|
tree-sitter-typescript = "0.20.5"
|
||||||
|
|
||||||
|
|
|
@ -1,3 +0,0 @@
|
||||||
# `xd-darwin-x64`
|
|
||||||
|
|
||||||
This is the **x86_64-apple-darwin** binary for `xd`
|
|
|
@ -1,18 +0,0 @@
|
||||||
{
|
|
||||||
"name": "xd-darwin-x64",
|
|
||||||
"version": "0.0.0",
|
|
||||||
"os": [
|
|
||||||
"darwin"
|
|
||||||
],
|
|
||||||
"cpu": [
|
|
||||||
"x64"
|
|
||||||
],
|
|
||||||
"main": "xd.darwin-x64.node",
|
|
||||||
"files": [
|
|
||||||
"xd.darwin-x64.node"
|
|
||||||
],
|
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 10"
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,3 +0,0 @@
|
||||||
# `xd-linux-x64-gnu`
|
|
||||||
|
|
||||||
This is the **x86_64-unknown-linux-gnu** binary for `xd`
|
|
|
@ -1,21 +0,0 @@
|
||||||
{
|
|
||||||
"name": "xd-linux-x64-gnu",
|
|
||||||
"version": "0.0.0",
|
|
||||||
"os": [
|
|
||||||
"linux"
|
|
||||||
],
|
|
||||||
"cpu": [
|
|
||||||
"x64"
|
|
||||||
],
|
|
||||||
"main": "xd.linux-x64-gnu.node",
|
|
||||||
"files": [
|
|
||||||
"xd.linux-x64-gnu.node"
|
|
||||||
],
|
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 10"
|
|
||||||
},
|
|
||||||
"libc": [
|
|
||||||
"glibc"
|
|
||||||
]
|
|
||||||
}
|
|
|
@ -1,3 +0,0 @@
|
||||||
# `xd-win32-x64-msvc`
|
|
||||||
|
|
||||||
This is the **x86_64-pc-windows-msvc** binary for `xd`
|
|
|
@ -1,18 +0,0 @@
|
||||||
{
|
|
||||||
"name": "xd-win32-x64-msvc",
|
|
||||||
"version": "0.0.0",
|
|
||||||
"os": [
|
|
||||||
"win32"
|
|
||||||
],
|
|
||||||
"cpu": [
|
|
||||||
"x64"
|
|
||||||
],
|
|
||||||
"main": "xd.win32-x64-msvc.node",
|
|
||||||
"files": [
|
|
||||||
"xd.win32-x64-msvc.node"
|
|
||||||
],
|
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 10"
|
|
||||||
}
|
|
||||||
}
|
|
25
tools/treesitter/queries/astro/highlights.scm
Normal file
25
tools/treesitter/queries/astro/highlights.scm
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
(tag_name) @tag
|
||||||
|
(erroneous_end_tag_name) @keyword
|
||||||
|
(doctype) @constant
|
||||||
|
(attribute_name) @property
|
||||||
|
(attribute_value) @string
|
||||||
|
(comment) @comment
|
||||||
|
|
||||||
|
[
|
||||||
|
(attribute_value)
|
||||||
|
(quoted_attribute_value)
|
||||||
|
] @string
|
||||||
|
|
||||||
|
"=" @operator
|
||||||
|
|
||||||
|
[
|
||||||
|
"{"
|
||||||
|
"}"
|
||||||
|
] @punctuation.bracket
|
||||||
|
|
||||||
|
[
|
||||||
|
"<"
|
||||||
|
">"
|
||||||
|
"</"
|
||||||
|
"/>"
|
||||||
|
] @tag.delimiter
|
15
tools/treesitter/queries/astro/injections.scm
Normal file
15
tools/treesitter/queries/astro/injections.scm
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
(frontmatter
|
||||||
|
(raw_text) @injection.content
|
||||||
|
(#set! "injection.language" "typescript"))
|
||||||
|
|
||||||
|
(interpolation
|
||||||
|
(raw_text) @injection.content
|
||||||
|
(#set! "injection.language" "tsx"))
|
||||||
|
|
||||||
|
(script_element
|
||||||
|
(raw_text) @injection.content
|
||||||
|
(#set! "injection.language" "typescript"))
|
||||||
|
|
||||||
|
(style_element
|
||||||
|
(raw_text) @injection.content
|
||||||
|
(#set! "injection.language" "css"))
|
78
tools/treesitter/queries/css/highlights.scm
Normal file
78
tools/treesitter/queries/css/highlights.scm
Normal file
|
@ -0,0 +1,78 @@
|
||||||
|
(comment) @comment
|
||||||
|
|
||||||
|
[
|
||||||
|
(tag_name)
|
||||||
|
(nesting_selector)
|
||||||
|
(universal_selector)
|
||||||
|
] @tag
|
||||||
|
|
||||||
|
[
|
||||||
|
"~"
|
||||||
|
">"
|
||||||
|
"+"
|
||||||
|
"-"
|
||||||
|
"*"
|
||||||
|
"/"
|
||||||
|
"="
|
||||||
|
"^="
|
||||||
|
"|="
|
||||||
|
"~="
|
||||||
|
"$="
|
||||||
|
"*="
|
||||||
|
"and"
|
||||||
|
"or"
|
||||||
|
"not"
|
||||||
|
"only"
|
||||||
|
] @operator
|
||||||
|
|
||||||
|
(attribute_selector (plain_value) @string)
|
||||||
|
|
||||||
|
(attribute_name) @attribute
|
||||||
|
(pseudo_element_selector (tag_name) @attribute)
|
||||||
|
(pseudo_class_selector (class_name) @attribute)
|
||||||
|
|
||||||
|
[
|
||||||
|
(class_name)
|
||||||
|
(id_name)
|
||||||
|
(namespace_name)
|
||||||
|
(property_name)
|
||||||
|
(feature_name)
|
||||||
|
] @property
|
||||||
|
|
||||||
|
(function_name) @function
|
||||||
|
|
||||||
|
(
|
||||||
|
[
|
||||||
|
(property_name)
|
||||||
|
(plain_value)
|
||||||
|
] @variable.special
|
||||||
|
(#match? @variable.special "^--")
|
||||||
|
)
|
||||||
|
|
||||||
|
[
|
||||||
|
"@media"
|
||||||
|
"@import"
|
||||||
|
"@charset"
|
||||||
|
"@namespace"
|
||||||
|
"@supports"
|
||||||
|
"@keyframes"
|
||||||
|
(at_keyword)
|
||||||
|
(to)
|
||||||
|
(from)
|
||||||
|
(important)
|
||||||
|
] @keyword
|
||||||
|
|
||||||
|
(string_value) @string
|
||||||
|
(color_value) @string.special
|
||||||
|
|
||||||
|
[
|
||||||
|
(integer_value)
|
||||||
|
(float_value)
|
||||||
|
] @number
|
||||||
|
|
||||||
|
(unit) @type
|
||||||
|
|
||||||
|
[
|
||||||
|
","
|
||||||
|
":"
|
||||||
|
] @punctuation.delimiter
|
47
tools/treesitter/queries/regex/highlights.scm
Normal file
47
tools/treesitter/queries/regex/highlights.scm
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
; Forked from tree-sitter-regex
|
||||||
|
; The MIT License (MIT) Copyright (c) 2014 Max Brunsfeld
|
||||||
|
[
|
||||||
|
"("
|
||||||
|
")"
|
||||||
|
"(?"
|
||||||
|
"(?:"
|
||||||
|
"(?<"
|
||||||
|
">"
|
||||||
|
"["
|
||||||
|
"]"
|
||||||
|
"{"
|
||||||
|
"}"
|
||||||
|
] @punctuation.bracket
|
||||||
|
|
||||||
|
(group_name) @property
|
||||||
|
|
||||||
|
; These are escaped special characters that lost their special meaning
|
||||||
|
; -> no special highlighting
|
||||||
|
(identity_escape) @string.regexp
|
||||||
|
|
||||||
|
(class_character) @constant
|
||||||
|
|
||||||
|
[
|
||||||
|
(control_letter_escape)
|
||||||
|
(character_class_escape)
|
||||||
|
(control_escape)
|
||||||
|
(boundary_assertion)
|
||||||
|
(non_boundary_assertion)
|
||||||
|
] @string.escape
|
||||||
|
|
||||||
|
[
|
||||||
|
"*"
|
||||||
|
"+"
|
||||||
|
"?"
|
||||||
|
"|"
|
||||||
|
"="
|
||||||
|
"!"
|
||||||
|
"-"
|
||||||
|
] @operator
|
||||||
|
|
||||||
|
[
|
||||||
|
(start_assertion)
|
||||||
|
(end_assertion)
|
||||||
|
] @punctuation.delimiter
|
||||||
|
|
||||||
|
(any_character) @variable.builtin
|
118
tools/treesitter/src/captures.rs
Normal file
118
tools/treesitter/src/captures.rs
Normal file
|
@ -0,0 +1,118 @@
|
||||||
|
/// Source: https://github.com/nvim-treesitter/nvim-treesitter/blob/master/CONTRIBUTING.md
|
||||||
|
pub const NAMES: &[&str] = &[
|
||||||
|
// Identifiers
|
||||||
|
"variable", // various variable names
|
||||||
|
"variable.builtin", // built-in variable names (e.g. `this`)
|
||||||
|
"variable.parameter", // parameters of a function
|
||||||
|
"variable.member", // object and struct fields
|
||||||
|
|
||||||
|
"constant", // constant identifiers
|
||||||
|
"constant.builtin", // built-in constant values
|
||||||
|
"constant.macro", // constants defined by the preprocessor
|
||||||
|
|
||||||
|
"module", // modules or namespaces
|
||||||
|
"module.builtin", // built-in modules or namespaces
|
||||||
|
"label", // GOTO and other labels (e.g. `label:` in C), including heredoc labels
|
||||||
|
|
||||||
|
// Literals
|
||||||
|
"string", // string literals
|
||||||
|
"string.documentation", // string documenting code (e.g. Python docstrings)
|
||||||
|
"string.regexp", // regular expressions
|
||||||
|
"string.escape", // escape sequences
|
||||||
|
"string.special", // other special strings (e.g. dates)
|
||||||
|
"string.special.symbol",// symbols or atoms
|
||||||
|
"string.special.url", // URIs (e.g. hyperlinks)
|
||||||
|
"string.special.path", // filenames
|
||||||
|
|
||||||
|
"character", // character literals
|
||||||
|
"character.special", // special characters (e.g. wildcards)
|
||||||
|
|
||||||
|
"boolean", // boolean literals
|
||||||
|
"number", // numeric literals
|
||||||
|
"number.float", // floating-point number literals
|
||||||
|
|
||||||
|
// Types
|
||||||
|
"type", // type or class definitions and annotations
|
||||||
|
"type.builtin", // built-in types
|
||||||
|
"type.definition", // identifiers in type definitions (e.g. `typedef <type> <identifier>` in C)
|
||||||
|
"type.qualifier", // type qualifiers (e.g. `const`)
|
||||||
|
|
||||||
|
"attribute", // attribute annotations (e.g. Python decorators)
|
||||||
|
"property", // the key in key/value pairs
|
||||||
|
|
||||||
|
// Functions
|
||||||
|
"function", // function definitions
|
||||||
|
"function.builtin", // built-in functions
|
||||||
|
"function.call", // function calls
|
||||||
|
"function.macro", // preprocessor macros
|
||||||
|
|
||||||
|
"function.method", // method definitions
|
||||||
|
"function.method.call", // method calls
|
||||||
|
|
||||||
|
"constructor", // constructor calls and definitions
|
||||||
|
"operator", // symbolic operators (e.g. `+` / `*`)
|
||||||
|
|
||||||
|
// Keywords
|
||||||
|
"keyword", // keywords not fitting into specific categories
|
||||||
|
"keyword.coroutine", // keywords related to coroutines (e.g. `go` in Go, `async/await` in Python)
|
||||||
|
"keyword.function", // keywords that define a function (e.g. `func` in Go, `def` in Python)
|
||||||
|
"keyword.operator", // operators that are English words (e.g. `and` / `or`)
|
||||||
|
"keyword.import", // keywords for including modules (e.g. `import` / `from` in Python)
|
||||||
|
"keyword.storage", // modifiers that affect storage in memory or life-time
|
||||||
|
"keyword.repeat", // keywords related to loops (e.g. `for` / `while`)
|
||||||
|
"keyword.return", // keywords like `return` and `yield`
|
||||||
|
"keyword.debug", // keywords related to debugging
|
||||||
|
"keyword.exception", // keywords related to exceptions (e.g. `throw` / `catch`)
|
||||||
|
|
||||||
|
"keyword.conditional", // keywords related to conditionals (e.g. `if` / `else`)
|
||||||
|
"keyword.conditional.ternary", // ternary operator (e.g. `?` / `:`)
|
||||||
|
|
||||||
|
"keyword.directive", // various preprocessor directives & shebangs
|
||||||
|
"keyword.directive.define", // preprocessor definition directives
|
||||||
|
|
||||||
|
// Punctuation
|
||||||
|
"punctuation.delimiter", // delimiters (e.g. `;` / `.` / `,`)
|
||||||
|
"punctuation.bracket", // brackets (e.g. `()` / `{}` / `[]`)
|
||||||
|
"punctuation.special", // special symbols (e.g. `{}` in string interpolation)
|
||||||
|
|
||||||
|
// Comments
|
||||||
|
"comment", // line and block comments
|
||||||
|
"comment.documentation", // comments documenting code
|
||||||
|
|
||||||
|
"comment.error", // error-type comments (e.g. `ERROR`, `FIXME`, `DEPRECATED:`)
|
||||||
|
"comment.warning", // warning-type comments (e.g. `WARNING:`, `FIX:`, `HACK:`)
|
||||||
|
"comment.todo", // todo-type comments (e.g. `TODO:`, `WIP:`, `FIXME:`)
|
||||||
|
"comment.note", // note-type comments (e.g. `NOTE:`, `INFO:`, `XXX`)
|
||||||
|
|
||||||
|
// Markup
|
||||||
|
"markup.strong", // bold text
|
||||||
|
"markup.italic", // italic text
|
||||||
|
"markup.strikethrough", // struck-through text
|
||||||
|
"markup.underline", // underlined text (only for literal underline markup!)
|
||||||
|
|
||||||
|
"markup.heading", // headings, titles (including markers)
|
||||||
|
|
||||||
|
"markup.quote", // block quotes
|
||||||
|
"markup.math", // math environments (e.g. `$ ... $` in LaTeX)
|
||||||
|
"markup.environment", // environments (e.g. in LaTeX)
|
||||||
|
|
||||||
|
"markup.link", // text references, footnotes, citations, etc.
|
||||||
|
"markup.link.label", // link, reference descriptions
|
||||||
|
"markup.link.url", // URL-style links
|
||||||
|
|
||||||
|
"markup.raw", // literal or verbatim text (e.g. inline code)
|
||||||
|
"markup.raw.block", // literal or verbatim text as a stand-alone block
|
||||||
|
// (use priority 90 for blocks with injections)
|
||||||
|
|
||||||
|
"markup.list", // list markers
|
||||||
|
"markup.list.checked", // checked todo-style list markers
|
||||||
|
"markup.list.unchecked", // unchecked todo-style list markers
|
||||||
|
|
||||||
|
"diff.plus", // added text (for diff files)
|
||||||
|
"diff.minus", // deleted text (for diff files)
|
||||||
|
"diff.delta", // changed text (for diff files)
|
||||||
|
|
||||||
|
"tag", // XML-style tag names (and similar)
|
||||||
|
"tag.attribute", // XML-style tag attributes
|
||||||
|
"tag.delimiter", // XML-style tag delimiters
|
||||||
|
];
|
|
@ -1,156 +1,137 @@
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::sync::Arc;
|
|
||||||
use once_cell::sync::Lazy;
|
use once_cell::sync::Lazy;
|
||||||
|
use tree_sitter::Language;
|
||||||
use tree_sitter_highlight::HighlightConfiguration;
|
use tree_sitter_highlight::HighlightConfiguration;
|
||||||
|
|
||||||
|
use crate::captures;
|
||||||
pub const NAMES: &[&str] = &[
|
|
||||||
"comment",
|
|
||||||
|
|
||||||
"attribute",
|
|
||||||
"carriage-return",
|
|
||||||
"comment",
|
|
||||||
"comment.documentation",
|
|
||||||
"constant",
|
|
||||||
"constant.builtin",
|
|
||||||
"constructor",
|
|
||||||
"constructor.builtin",
|
|
||||||
"embedded",
|
|
||||||
"error",
|
|
||||||
"escape",
|
|
||||||
"function",
|
|
||||||
"function.builtin",
|
|
||||||
"include",
|
|
||||||
"keyword",
|
|
||||||
"markup",
|
|
||||||
"markup.bold",
|
|
||||||
"markup.heading",
|
|
||||||
"markup.italic",
|
|
||||||
"markup.link",
|
|
||||||
"markup.link.url",
|
|
||||||
"markup.list",
|
|
||||||
"markup.list.checked",
|
|
||||||
"markup.list.numbered",
|
|
||||||
"markup.list.unchecked",
|
|
||||||
"markup.list.unnumbered",
|
|
||||||
"markup.quote",
|
|
||||||
"markup.raw",
|
|
||||||
"markup.raw.block",
|
|
||||||
"markup.raw.inline",
|
|
||||||
"markup.strikethrough",
|
|
||||||
"module",
|
|
||||||
"number",
|
|
||||||
"operator",
|
|
||||||
"property",
|
|
||||||
"property.builtin",
|
|
||||||
"punctuation",
|
|
||||||
"punctuation.bracket",
|
|
||||||
"punctuation.delimiter",
|
|
||||||
"punctuation.special",
|
|
||||||
"string",
|
|
||||||
"string.escape",
|
|
||||||
"string.regexp",
|
|
||||||
"string.special",
|
|
||||||
"string.special.symbol",
|
|
||||||
"tag",
|
|
||||||
"type",
|
|
||||||
"type.builtin",
|
|
||||||
"variable",
|
|
||||||
"variable.builtin",
|
|
||||||
"variable.member",
|
|
||||||
"variable.parameter",
|
|
||||||
];
|
|
||||||
|
|
||||||
|
|
||||||
pub static CONFIGS: Lazy<HashMap<&'static str, Arc<HighlightConfiguration>>> = Lazy::new(|| {
|
macro_rules! query {
|
||||||
[
|
($path:literal) => {
|
||||||
|
include_str!(concat!(
|
||||||
|
env!("CARGO_MANIFEST_DIR"),
|
||||||
|
"/queries/",
|
||||||
|
$path,
|
||||||
|
".scm"
|
||||||
|
))
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub static EXTENSIONS: Lazy<HashMap<&'static str, &'static str>> = Lazy::new(|| {
|
||||||
|
HashMap::from([
|
||||||
|
("hs", "haskell"),
|
||||||
|
("js", "javascript"),
|
||||||
|
("md", "markdown"),
|
||||||
|
("mdx", "markdown"),
|
||||||
|
("scss", "css"),
|
||||||
|
("ts", "typescript")
|
||||||
|
])
|
||||||
|
});
|
||||||
|
|
||||||
|
fn config_for(
|
||||||
|
lang: Language,
|
||||||
|
highlights: &str,
|
||||||
|
injections: &str,
|
||||||
|
locals: &str,
|
||||||
|
) -> HighlightConfiguration {
|
||||||
|
let mut config = HighlightConfiguration::new(lang, highlights, injections, locals).unwrap();
|
||||||
|
config.configure(captures::NAMES);
|
||||||
|
config
|
||||||
|
}
|
||||||
|
|
||||||
|
pub static CONFIGS: Lazy<HashMap<&'static str, HighlightConfiguration>> = Lazy::new(|| {
|
||||||
|
HashMap::from([
|
||||||
(
|
(
|
||||||
vec!["css", "scss"],
|
"astro",
|
||||||
HighlightConfiguration::new(
|
config_for(
|
||||||
tree_sitter_css::language(),
|
tree_sitter_astro::language(),
|
||||||
tree_sitter_css::HIGHLIGHTS_QUERY,
|
query!("astro/highlights"),
|
||||||
"",
|
query!("astro/injections"),
|
||||||
"",
|
""
|
||||||
).unwrap()
|
)
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
vec!["hs", "haskell"],
|
"css",
|
||||||
HighlightConfiguration::new(
|
config_for(
|
||||||
|
tree_sitter_css::language(),
|
||||||
|
query!("css/highlights"),
|
||||||
|
"",
|
||||||
|
"",
|
||||||
|
)
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"haskell",
|
||||||
|
config_for(
|
||||||
tree_sitter_haskell::language(),
|
tree_sitter_haskell::language(),
|
||||||
tree_sitter_haskell::HIGHLIGHTS_QUERY,
|
tree_sitter_haskell::HIGHLIGHTS_QUERY,
|
||||||
"",
|
"",
|
||||||
tree_sitter_haskell::LOCALS_QUERY,
|
tree_sitter_haskell::LOCALS_QUERY,
|
||||||
).unwrap()
|
)
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
vec!["html", "html"],
|
"html",
|
||||||
HighlightConfiguration::new(
|
config_for(
|
||||||
tree_sitter_html::language(),
|
tree_sitter_html::language(),
|
||||||
tree_sitter_html::HIGHLIGHTS_QUERY,
|
tree_sitter_html::HIGHLIGHTS_QUERY,
|
||||||
tree_sitter_html::INJECTIONS_QUERY,
|
tree_sitter_html::INJECTIONS_QUERY,
|
||||||
"",
|
"",
|
||||||
).unwrap()
|
)
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
vec!["md", "markdown"],
|
"javascript",
|
||||||
HighlightConfiguration::new(
|
config_for(
|
||||||
tree_sitter_md::language(),
|
|
||||||
// &format!("{}\n\n{}",
|
|
||||||
tree_sitter_md::HIGHLIGHT_QUERY_BLOCK,
|
|
||||||
// tree_sitter_md::HIGHLIGHT_QUERY_INLINE,
|
|
||||||
// ),
|
|
||||||
// &format!("{}\n\n{}",
|
|
||||||
tree_sitter_md::INJECTION_QUERY_BLOCK,
|
|
||||||
// tree_sitter_md::INJECTION_QUERY_INLINE,
|
|
||||||
// ),
|
|
||||||
""
|
|
||||||
).unwrap()
|
|
||||||
),
|
|
||||||
(
|
|
||||||
vec!["rs", "rust"],
|
|
||||||
HighlightConfiguration::new(
|
|
||||||
tree_sitter_rust::language(),
|
|
||||||
tree_sitter_rust::HIGHLIGHT_QUERY,
|
|
||||||
tree_sitter_rust::INJECTIONS_QUERY,
|
|
||||||
"",
|
|
||||||
).unwrap()
|
|
||||||
),
|
|
||||||
(
|
|
||||||
vec!["js", "javascript"],
|
|
||||||
HighlightConfiguration::new(
|
|
||||||
tree_sitter_javascript::language(),
|
tree_sitter_javascript::language(),
|
||||||
tree_sitter_javascript::HIGHLIGHT_QUERY,
|
tree_sitter_javascript::HIGHLIGHT_QUERY,
|
||||||
tree_sitter_javascript::INJECTION_QUERY,
|
tree_sitter_javascript::INJECTION_QUERY,
|
||||||
tree_sitter_javascript::LOCALS_QUERY,
|
tree_sitter_javascript::LOCALS_QUERY,
|
||||||
).unwrap()
|
)
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
vec!["jsx"],
|
"markdown",
|
||||||
HighlightConfiguration::new(
|
config_for(
|
||||||
tree_sitter_javascript::language(),
|
tree_sitter_md::language(),
|
||||||
tree_sitter_javascript::JSX_HIGHLIGHT_QUERY,
|
tree_sitter_md::HIGHLIGHT_QUERY_BLOCK,
|
||||||
tree_sitter_javascript::INJECTION_QUERY,
|
tree_sitter_md::INJECTION_QUERY_BLOCK,
|
||||||
tree_sitter_javascript::LOCALS_QUERY,
|
""
|
||||||
).unwrap()
|
)
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
vec!["ts", "typescript"],
|
"regex",
|
||||||
HighlightConfiguration::new(
|
config_for(
|
||||||
|
tree_sitter_regex::language(),
|
||||||
|
query!("regex/highlights"),
|
||||||
|
"",
|
||||||
|
""
|
||||||
|
)
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"rust",
|
||||||
|
config_for(
|
||||||
|
tree_sitter_rust::language(),
|
||||||
|
tree_sitter_rust::HIGHLIGHT_QUERY,
|
||||||
|
tree_sitter_rust::INJECTIONS_QUERY,
|
||||||
|
"",
|
||||||
|
)
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"typescript",
|
||||||
|
config_for(
|
||||||
tree_sitter_typescript::language_typescript(),
|
tree_sitter_typescript::language_typescript(),
|
||||||
&format!("{}\n\n{}",
|
&format!("{}\n{}",
|
||||||
tree_sitter_javascript::HIGHLIGHT_QUERY,
|
tree_sitter_javascript::HIGHLIGHT_QUERY,
|
||||||
tree_sitter_typescript::HIGHLIGHT_QUERY
|
tree_sitter_typescript::HIGHLIGHT_QUERY
|
||||||
),
|
),
|
||||||
tree_sitter_javascript::INJECTION_QUERY,
|
tree_sitter_javascript::INJECTION_QUERY,
|
||||||
tree_sitter_typescript::LOCALS_QUERY,
|
tree_sitter_typescript::LOCALS_QUERY,
|
||||||
).unwrap()
|
)
|
||||||
),
|
),
|
||||||
]
|
])
|
||||||
.into_iter()
|
|
||||||
.flat_map(|(keys, mut config)| {
|
|
||||||
config.configure(NAMES);
|
|
||||||
let config = Arc::new(config);
|
|
||||||
keys.into_iter().map(move |key| (key, config.clone()))
|
|
||||||
})
|
|
||||||
.collect()
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
pub fn get_config(name: &str) -> Option<&'static HighlightConfiguration> {
|
||||||
|
match EXTENSIONS.get(name) {
|
||||||
|
Some(name) => CONFIGS.get(name),
|
||||||
|
None => CONFIGS.get(name),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,19 +1,35 @@
|
||||||
#![deny(clippy::all)]
|
mod captures;
|
||||||
mod configs;
|
mod configs;
|
||||||
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use tree_sitter_highlight::Highlighter;
|
use tree_sitter_highlight::Highlighter;
|
||||||
use tree_sitter_highlight::HighlightEvent;
|
use tree_sitter_highlight::HighlightEvent;
|
||||||
use configs::{CONFIGS, NAMES};
|
|
||||||
|
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate napi_derive;
|
extern crate napi_derive;
|
||||||
|
|
||||||
|
|
||||||
|
fn map_event(event: HighlightEvent, src: &str) -> HashMap<String, String> {
|
||||||
|
match event {
|
||||||
|
HighlightEvent::Source {start, end} => HashMap::from([
|
||||||
|
("kind".into(), "text".into()),
|
||||||
|
("text".into(), src[start..end].into())
|
||||||
|
]),
|
||||||
|
HighlightEvent::HighlightStart(s) => HashMap::from([
|
||||||
|
("kind".into(), "open".into()),
|
||||||
|
("name".into(), captures::NAMES[s.0].into())
|
||||||
|
]),
|
||||||
|
HighlightEvent::HighlightEnd => HashMap::from([
|
||||||
|
("kind".into(), "close".into())
|
||||||
|
]),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
#[napi]
|
#[napi]
|
||||||
pub fn hl(lang: String, src: String) -> Vec<HashMap<String, String>> {
|
pub fn hl(lang: String, src: String) -> Vec<HashMap<String, String>> {
|
||||||
let config = match CONFIGS.get(&*lang) {
|
let config = match configs::get_config(&lang) {
|
||||||
Some(c) => c,
|
Some(c) => c,
|
||||||
None => return vec![
|
None => return vec![
|
||||||
HashMap::from([
|
HashMap::from([
|
||||||
|
@ -24,31 +40,20 @@ pub fn hl(lang: String, src: String) -> Vec<HashMap<String, String>> {
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
let mut highlighter = Highlighter::new();
|
let mut hl = Highlighter::new();
|
||||||
let highlights = highlighter.highlight(
|
let highlights = hl.highlight(
|
||||||
&config,
|
&config,
|
||||||
src.as_bytes(),
|
src.as_bytes(),
|
||||||
None,
|
None,
|
||||||
|key| CONFIGS.get(key).map(|arc| arc.as_ref())
|
|name| configs::get_config(name)
|
||||||
).unwrap();
|
).unwrap();
|
||||||
|
|
||||||
let mut out = vec![];
|
let mut out = vec![];
|
||||||
for event in highlights {
|
for event in highlights {
|
||||||
match event.unwrap() {
|
let event = event.unwrap();
|
||||||
HighlightEvent::Source {start, end} => out.push(HashMap::from([
|
let obj = map_event(event, &src);
|
||||||
("kind".into(), "text".into()),
|
out.push(obj);
|
||||||
("text".into(), src[start..end].into())
|
|
||||||
])),
|
|
||||||
HighlightEvent::HighlightStart(s) => out.push(HashMap::from([
|
|
||||||
("kind".into(), "open".into()),
|
|
||||||
("name".into(), NAMES[s.0].into())
|
|
||||||
])),
|
|
||||||
HighlightEvent::HighlightEnd => out.push(HashMap::from([
|
|
||||||
("kind".into(), "close".into())
|
|
||||||
]))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
out
|
out
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue