Skip to content

Commit 0b70263

Browse files
committed
Add "match"/"begin"/"while"/"end" hovers
1 parent 24022be commit 0b70263

4 files changed

Lines changed: 104 additions & 44 deletions

File tree

README.md

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,12 +75,10 @@ Highlights scope names with their own themed colour in realtime:
7575
### Todo
7676
* Support unicode character insertions `\u00b0`
7777
* Finish ctrl+click definitions and references
78-
* Add hovers
7978
* Improve tree-sitter grammar
8079
* Improve TextMate [documentation](https://github.com/RedCMD/TmLanguage-Syntax-Highlighter/blob/main/documentation/index.md)
8180
* Finish CallStack viewer
8281
* Redo TextMate scopeNames [Naming conventions](https://github.com/atom/flight-manual.atom.io/pull/564)
83-
* `"match"`/`"begin"`/`"while"`/`"end"` hovers
8482
* Move `.vsix` file to releases
8583
* Refactor: Sort JSON keys
8684
* Move to LanguageServer

src/Providers/HoverProvider.ts

Lines changed: 99 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,49 +1,111 @@
11
import * as vscode from 'vscode';
2-
import { getTrees, toPoint, toRange } from "../TreeSitter";
3-
2+
import { getTrees, queryNode, toPoint, toRange, trees } from "../TreeSitter";
3+
import { Point } from 'web-tree-sitter';
44

55
export const HoverProvider: vscode.HoverProvider = {
66
provideHover(document: vscode.TextDocument, position: vscode.Position, token: vscode.CancellationToken): vscode.Hover {
77
// vscode.window.showInformationMessage(JSON.stringify("Hover"));
88
const trees = getTrees(document);
9-
const jsonTree = trees.jsonTree;
109
const point = toPoint(position);
11-
const node = jsonTree.rootNode.descendantForPosition(point);
12-
// const node = jsonTree.rootNode.namedDescendantForPosition(point);
13-
if (node == null) {
14-
return;
15-
}
1610

17-
if (node.type == 'regex') {
18-
const regexTrees = getTrees(document).regexTrees;
19-
const regexTree = regexTrees.get(node.id);
20-
const regexNode = regexTree.rootNode.descendantForPosition(point);
21-
const parentNode = regexNode.parent;
22-
const markdownString = new vscode.MarkdownString();
23-
markdownString.appendText(parentNode.type + ' => ' + regexNode.type);
24-
markdownString.appendCodeblock(parentNode.text, 'json-textmate-regex');
25-
markdownString.appendCodeblock(parentNode.toString(), 'scm');
26-
27-
const range = toRange(parentNode);
28-
const hover = new vscode.Hover(markdownString, range);
29-
// vscode.window.showInformationMessage(JSON.stringify(hover));
30-
return hover;
11+
// return debugTreeSitterHovers(trees, point);
12+
13+
const rootNode = trees.jsonTree.rootNode;
14+
const hoverQuery = `;scm
15+
(match (key) @match)
16+
(begin (key) @begin)
17+
(end (key) @end)
18+
(while (key) @while)
19+
`;
20+
const hoverCapture = queryNode(rootNode, hoverQuery, point);
21+
const hoverNode = hoverCapture.node;
22+
23+
const markdownString = new vscode.MarkdownString();
24+
switch (hoverCapture.name) {
25+
case 'match':
26+
markdownString.appendMarkdown('Regular Expression to match, (capture) and apply `scopeNames` to text \n');
27+
markdownString.appendMarkdown('TextMate uses the [Oniguruma](https://github.com/kkos/oniguruma/blob/master/doc/RE) regex dialect \n');
28+
markdownString.appendCodeblock('Example: \\\\b(true|false)\\\\b', 'json-textmate-regex');
29+
break;
30+
case 'begin':
31+
markdownString.appendMarkdown('Regular Expression just like `"match"` \n');
32+
markdownString.appendMarkdown('Starts a region that _can_ span multiple lines. When used in conjunction with a `"end"`/`"while"` key \n');
33+
markdownString.appendMarkdown('Rules can be nested inside the block with the `"patterns"` key \n');
34+
markdownString.appendMarkdown('An anchor is placed after the `"begin"` rule; that you can then match with `\\\\G` \n');
35+
markdownString.appendMarkdown('**\\*WARNING\\*** If `"begin"` matches the newline `\\n`. The `\\\\G` anchor is then placed at the beginning of the _next_ line \n');
36+
markdownString.appendCodeblock('Example: (\\\\s*)(#*)(\\")', 'json-textmate-regex');
37+
break;
38+
case 'end':
39+
markdownString.appendMarkdown('Regular Expression just like `"match"` \n');
40+
markdownString.appendMarkdown('Ends the block started by `"begin"` \n');
41+
markdownString.appendMarkdown('`"end"` has priority over rules inside the `"patterns"` array \n');
42+
markdownString.appendMarkdown('**\\*WARNING\\*** Rules inside `"patterns"` can \'overmatch\' the `"end"` rule. Effectively \'pushing\' it out \n');
43+
markdownString.appendMarkdown('(Captures) inside the `"begin"` key can be referenced here with regex back-references `\\\\1` \n');
44+
markdownString.appendCodeblock('Example: (\\")(\\\\2)', 'json-textmate-regex');
45+
break;
46+
case 'while':
47+
markdownString.appendMarkdown('Regular Expression just like `"match"` \n');
48+
markdownString.appendMarkdown('Continues the block started by `"begin"` \n');
49+
markdownString.appendMarkdown('**\\*WARNING\\*** [VSCode\'s TextMate Engine](https://github.com/microsoft/vscode-textmate) implements `"while"` slightly [differently](https://github.com/microsoft/vscode-textmate/issues/241) to how [Apple\'s TextMate 2.0 Application](https://macromates.com/) has \n');
50+
markdownString.appendMarkdown('Unlike `"end"`, `"while"` is line-based, not character-based. It is only checked once per line (starting on the line after the `"begin"`) \n');
51+
markdownString.appendMarkdown('If it matches then the _rest_ of the line is now part of the block (same also applies to the `"begin"` rule) \n');
52+
markdownString.appendMarkdown('Rules can be nested inside the block with the `"patterns"` key \n');
53+
markdownString.appendMarkdown('VSCode: `"while"` is always tested first, before any inner `"patterns"`. \n');
54+
markdownString.appendMarkdown('VSCode: When `"while"` doesn\'t match, all unfinished/unclosed patterns are terminated and the `"begin"`/`"while"` block is then finished/closed \n');
55+
markdownString.appendMarkdown('Apple: `"begin"`&`"end"` rules \'push\' the `"while"` rule to the next line \n');
56+
markdownString.appendMarkdown('An anchor is placed after the `"begin"` rule; that you can then match with `\\\\G` \n');
57+
markdownString.appendMarkdown('Apple: A `\\\G` anchor is also placed at the beginning of the _next_ line \n');
58+
markdownString.appendMarkdown('(Captures) inside the `"begin"` key can be referenced here with regex back-references `\\\\1` \n');
59+
markdownString.appendCodeblock('Example: ^\\\\1(?!\\\\s*\\")', 'json-textmate-regex');
60+
break;
3161
}
32-
else {
33-
const markdownString = new vscode.MarkdownString();
34-
markdownString.appendCodeblock(node.text, 'json-textmate');
35-
// const fieldName = node.walk().currentFieldName();
36-
// if (fieldName)
37-
// markdownString.appendText(fieldName + ':');
38-
if (node.parent)
39-
markdownString.appendText(node.parent.type + ' => ');
40-
markdownString.appendText(node.type);
41-
markdownString.appendCodeblock(node.toString(), 'scm');
42-
43-
const range = toRange(node);
44-
const hover = new vscode.Hover(markdownString, range);
45-
// vscode.window.showInformationMessage(JSON.stringify(hover));
46-
return hover;
62+
63+
const range = toRange(hoverNode);
64+
const hover = new vscode.Hover(markdownString, range);
65+
// vscode.window.showInformationMessage(`hover\n${JSON.stringify(hover)}`);
66+
return hover;
67+
}
68+
};
69+
70+
function debugTreeSitterHovers(trees: trees, point: Point): vscode.Hover {
71+
const node = trees.jsonTree.rootNode.descendantForPosition(point);
72+
// const node = jsonTree.rootNode.namedDescendantForPosition(point);
73+
74+
if (node == null) {
75+
return;
76+
}
77+
78+
if (node.type == 'regex') {
79+
const regexTrees = trees.regexTrees;
80+
const regexTree = regexTrees.get(node.id);
81+
const regexNode = regexTree.rootNode.descendantForPosition(point);
82+
const parentNode = regexNode.parent;
83+
const markdownString = new vscode.MarkdownString();
84+
markdownString.appendText(parentNode.type + ' => ' + regexNode.type);
85+
markdownString.appendCodeblock(parentNode.text, 'json-textmate-regex');
86+
markdownString.appendCodeblock(parentNode.toString(), 'scm');
87+
88+
const range = toRange(parentNode);
89+
const hover = new vscode.Hover(markdownString, range);
90+
// vscode.window.showInformationMessage(`debugRegexHover\n${JSON.stringify(hover)}`);
91+
return hover;
92+
}
93+
else {
94+
const markdownString = new vscode.MarkdownString();
95+
markdownString.appendCodeblock(node.text, 'json-textmate');
96+
// const fieldName = node.walk().currentFieldName();
97+
// if (fieldName) {
98+
// markdownString.appendText(`${fieldName}:`);
99+
// }
100+
if (node.parent) {
101+
markdownString.appendText(node.parent.type + ' => ');
47102
}
103+
markdownString.appendText(node.type);
104+
markdownString.appendCodeblock(node.toString(), 'scm');
105+
106+
const range = toRange(node);
107+
const hover = new vscode.Hover(markdownString, range);
108+
// vscode.window.showInformationMessage(`debugHover\n${JSON.stringify(hover)}`);
109+
return hover;
48110
}
49111
}

src/extension.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ export async function activate(context: vscode.ExtensionContext) {
4747
initThemeScopes(context);
4848

4949
context.subscriptions.push(
50-
// vscode.languages.registerHoverProvider(DocumentSelector, HoverProvider), // Mouse over Hovers
50+
vscode.languages.registerHoverProvider(DocumentSelector, HoverProvider), // Mouse over Hovers
5151
vscode.languages.registerRenameProvider(DocumentSelector, RenameProvider), // [F2] Rename
5252
vscode.languages.registerCodeLensProvider(DocumentSelector, CodeLensProvider), // Code Lens
5353
vscode.languages.registerReferenceProvider(DocumentSelector, ReferenceProvider), // Go to References

vscode.tmLanguage.schema.json

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -688,7 +688,7 @@
688688
}
689689
},
690690
"match": {
691-
"$comment": "Having hovers over regexes is annoying",
691+
"$comment": "Having hovers over regex's is annoying. https://github.com/RedCMD/TmLanguage-Syntax-Highlighter/blob/main/src/Providers/HoverProvider.ts",
692692
"type": "string",
693693
"defaultSnippets": [
694694
{
@@ -699,7 +699,7 @@
699699
]
700700
},
701701
"begin": {
702-
"$comment": "Having hovers over regexes is annoying",
702+
"$comment": "Having hovers over regex's is annoying. https://github.com/RedCMD/TmLanguage-Syntax-Highlighter/blob/main/src/Providers/HoverProvider.ts",
703703
"type": "string",
704704
"defaultSnippets": [
705705
{
@@ -710,7 +710,7 @@
710710
]
711711
},
712712
"end": {
713-
"$comment": "Having hovers over regexes is annoying",
713+
"$comment": "Having hovers over regex's is annoying. https://github.com/RedCMD/TmLanguage-Syntax-Highlighter/blob/main/src/Providers/HoverProvider.ts",
714714
"type": "string",
715715
"defaultSnippets": [
716716
{
@@ -721,7 +721,7 @@
721721
]
722722
},
723723
"while": {
724-
"$comment": "Having hovers over regexes is annoying",
724+
"$comment": "Having hovers over regex's is annoying. https://github.com/RedCMD/TmLanguage-Syntax-Highlighter/blob/main/src/Providers/HoverProvider.ts",
725725
"type": "string",
726726
"defaultSnippets": [
727727
{

0 commit comments

Comments
 (0)