Skip to content

Commit 4d8b57c

Browse files
committed
Add DocComment mixed parse
Add keywords complete Add __arglist __makeref __reftype __refvalue Fix union param
1 parent 7e733e8 commit 4d8b57c

14 files changed

Lines changed: 1546 additions & 99 deletions

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
node_modules
2-
dist
2+
dist
3+
package-lock.json

dev/index.css

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,20 @@ body {
1313
overflow: hidden;
1414
}
1515

16+
@supports not (color-scheme: dark) {
17+
body {
18+
background: #121212;
19+
}
20+
}
21+
1622
div.split-view {
1723
width: 100%;
1824
height: 100%;
1925
display: flex;
20-
gap: 0.3rem;
26+
}
27+
28+
div.split-view>*:not(:last-child) {
29+
margin-right: 0.3rem;
2130
}
2231

2332
div.split-view #editor,
@@ -49,4 +58,18 @@ div.split-view #syntax pre {
4958
width: 100%;
5059
height: 50%;
5160
}
61+
62+
div.split-view>*:not(:last-child) {
63+
margin-bottom: 0.3rem;
64+
}
65+
}
66+
67+
.cm-completionInfo,
68+
.cm-tooltip-section {
69+
font-family: monospace;
70+
}
71+
72+
.cm-tooltip-section {
73+
padding: 3px 8px;
74+
white-space: pre-wrap;
5275
}

dev/vite.config.js

Lines changed: 0 additions & 11 deletions
This file was deleted.

dev/vite.config.mts

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import { defineConfig } from "vite";
2+
import simpleHtmlPlugin from "vite-plugin-simple-html";
3+
import legacy from "@vitejs/plugin-legacy";
4+
5+
export default defineConfig({
6+
base: "./",
7+
root: "dev",
8+
plugins: [
9+
simpleHtmlPlugin({
10+
minify: {
11+
minifyJs: true,
12+
sortSpaceSeparatedAttributeValues: true,
13+
sortAttributes: true,
14+
tagOmission: false
15+
}
16+
}),
17+
legacy({
18+
targets: "Edge >= 18",
19+
polyfills: true,
20+
renderModernChunks: false
21+
})
22+
],
23+
build: {
24+
outDir: "dist",
25+
sourcemap: true,
26+
rolldownOptions: {
27+
checks: {
28+
pluginTimings: false
29+
}
30+
},
31+
chunkSizeWarningLimit: 1024
32+
}
33+
});

package.json

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
},
2828
"peerDependencies": {
2929
"@codemirror/autocomplete": "^6.0.0",
30+
"@codemirror/lang-xml": "^6.0.0",
3031
"@codemirror/language": "^6.0.0",
3132
"@codemirror/state": "^6.0.0",
3233
"@codemirror/view": "^6.0.0",
@@ -35,21 +36,17 @@
3536
"@lezer/lr": "^1.0.0"
3637
},
3738
"devDependencies": {
38-
"@codemirror/autocomplete": "^6.0.0",
3939
"@codemirror/buildhelper": "^1.0.0",
40-
"@codemirror/language": "^6.0.0",
41-
"@codemirror/state": "^6.0.0",
40+
"@codemirror/lang-xml": "^6.0.0",
4241
"@codemirror/theme-one-dark": "^6.0.0",
43-
"@codemirror/view": "^6.0.0",
44-
"@lezer/common": "^1.0.0",
45-
"@lezer/generator": "^1.0.0",
46-
"@lezer/highlight": "^1.0.0",
47-
"@lezer/lr": "^1.0.0",
4842
"@types/node": "^25.6.0",
43+
"@vitejs/plugin-legacy": "^8.0.0",
4944
"codemirror": "^6.0.0",
5045
"lz-string": "^1.0.0",
5146
"npm-run-all": "^4.0.0",
52-
"vite": "^8.0.0"
47+
"terser": "^5.0.0",
48+
"vite": "^8.0.0",
49+
"vite-plugin-simple-html": "^1.0.0"
5350
},
5451
"overrides": {
5552
"serialize-javascript": "^7.0.0"

src/complete.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import { completeFromList, ifNotIn } from "@codemirror/autocomplete";
2+
import { atoms, keywords, types } from "./keywords";
3+
4+
export function csharpCompletion() {
5+
function map(type: string) {
6+
return (label: string) => { return { label, type }; }
7+
}
8+
return ifNotIn([
9+
';', '{', '}',
10+
"IntegerLiteral", "RealLiteral",
11+
"StringLiteral", "UTF8StringLiteral", "RawStringLiteral", "CharacterLiteral", "InterpolatedRegularString", "InterpolatedVerbatimString", "InterpolatedRawString",
12+
"LineComment", "BlockComment", "DocComment",
13+
"PP_Directive", "ArgumentName", "AttrsNamedArg",
14+
"ConstName", "MethodName", "ParamName", "VarName", "FieldName", "PropertyName"],
15+
completeFromList(keywords.map(map("keywords")).concat(types.map(map("type")), atoms.map(map("constant")))));
16+
}

src/csharp.ts

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
import type { LRParser } from "@lezer/lr";
2+
import { parser } from "./syntax.grammar";
3+
import {
4+
LRLanguage,
5+
LanguageSupport,
6+
indentNodeProp,
7+
foldNodeProp,
8+
foldInside,
9+
continuedIndent
10+
} from "@codemirror/language";
11+
import { parseMixed } from "@lezer/common";
12+
import { styleTags, tags } from "@lezer/highlight";
13+
import { xml } from "./xml";
14+
15+
function docCommentXmlOverlay(from: number, content: string) {
16+
const ranges: { from: number; to: number }[] = [];
17+
let i = 0;
18+
while (i < content.length) {
19+
// Each segment starts with ///
20+
if (content[i] === '/' && content[i + 1] === '/' && content[i + 2] === '/') {
21+
i += 3;
22+
if (content[i] === ' ') { i++; }
23+
const lineStart = i;
24+
while (i < content.length &&
25+
content[i] !== '\r' && content[i] !== '\n' &&
26+
content[i] !== '\u0085' && content[i] !== '\u2028' && content[i] !== '\u2029') {
27+
i++;
28+
}
29+
if (i > lineStart) { ranges.push({ from: from + lineStart, to: from + i }); }
30+
}
31+
// Advance past newline and leading whitespace
32+
while (i < content.length &&
33+
(content[i] === '\r' || content[i] === '\n' ||
34+
content[i] === '\u0085' || content[i] === '\u2028' || content[i] === '\u2029' ||
35+
content[i] === ' ' || content[i] === '\t')) {
36+
i++;
37+
}
38+
}
39+
return ranges.length > 0 ? ranges : null;
40+
}
41+
42+
export const csharpLanguage = LRLanguage.define({
43+
parser: parser.configure({
44+
props: [
45+
indentNodeProp.add({
46+
Delim: continuedIndent({ except: /^\s*(?:case\b|default:)/ })
47+
}),
48+
foldNodeProp.add({
49+
Delim: foldInside
50+
}),
51+
styleTags({
52+
"Keyword ContextualKeyword SimpleType": tags.keyword,
53+
BooleanLiteral: tags.bool,
54+
NullLiteral: tags.null,
55+
IntegerLiteral: tags.integer,
56+
RealLiteral: tags.float,
57+
'StringLiteral UTF8StringLiteral RawStringLiteral CharacterLiteral InterpolatedRegularString InterpolatedVerbatimString InterpolatedRawString $" @$" $@"':
58+
tags.string,
59+
LineComment: tags.lineComment,
60+
BlockComment: tags.blockComment,
61+
DocComment: tags.docComment,
62+
63+
". .. : Astrisk Slash % + - ++ -- Not ~ << & | ^ && || < > <= >= == NotEq = += -= *= SlashEq %= &= |= ^= ? ?? ??= =>":
64+
tags.operator,
65+
66+
PP_Directive: tags.keyword,
67+
68+
TypeIdentifier: tags.typeName,
69+
"ArgumentName AttrsNamedArg": tags.variableName,
70+
ConstName: tags.constant(tags.variableName),
71+
72+
//Ident: tags.name,
73+
MethodName: tags.function(tags.variableName),
74+
ParamName: [tags.emphasis, tags.variableName],
75+
VarName: tags.variableName,
76+
"FieldName PropertyName": tags.propertyName,
77+
78+
"( )": tags.paren,
79+
"{ }": tags.brace,
80+
"[ ]": tags.squareBracket
81+
})
82+
],
83+
wrap: parseMixed((node, input) => {
84+
if (node.name !== "DocComment") {
85+
return null;
86+
}
87+
const content = input.read(node.from, node.to);
88+
if (content.indexOf("<") === -1) {
89+
return null;
90+
}
91+
const overlay = docCommentXmlOverlay(node.from, content);
92+
if (!overlay) {
93+
return null;
94+
}
95+
return {
96+
parser: (xml.language.parser as LRParser).configure({
97+
props: [styleTags({ Text: tags.docComment })]
98+
}),
99+
overlay
100+
};
101+
})
102+
}),
103+
languageData: {
104+
commentTokens: { line: "//", block: { open: "/*", close: "*/" } },
105+
closeBrackets: { brackets: ["(", "[", "{", '"', "'", '"""'] },
106+
indentOnInput: /^\s*((\)|\]|\})$|(else|else\s+if|catch|finally|case)\b|default:)/
107+
}
108+
});
109+
110+
import { Prec } from "@codemirror/state";
111+
import { continueDocComment } from "./keymap";
112+
import { csharpCompletion } from "./complete";
113+
export function csharp() {
114+
return new LanguageSupport(csharpLanguage, [
115+
Prec.high(continueDocComment),
116+
csharpLanguage.data.of({
117+
autocomplete: csharpCompletion()
118+
}),
119+
xml.support
120+
]);
121+
}

src/index.ts

Lines changed: 4 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -1,63 +1,4 @@
1-
import { parser as csharpParser } from "./syntax.grammar";
2-
import {
3-
LRLanguage,
4-
LanguageSupport,
5-
indentNodeProp,
6-
foldNodeProp,
7-
foldInside,
8-
continuedIndent,
9-
} from "@codemirror/language";
10-
import { styleTags, tags as t } from "@lezer/highlight";
11-
12-
export const parser = csharpParser;
13-
14-
export const csharpLanguage = LRLanguage.define({
15-
parser: parser.configure({
16-
props: [
17-
indentNodeProp.add({
18-
Delim: continuedIndent({ except: /^\s*(?:case\b|default:)/ })
19-
}),
20-
foldNodeProp.add({
21-
Delim: foldInside
22-
}),
23-
styleTags({
24-
"Keyword ContextualKeyword SimpleType": t.keyword,
25-
BooleanLiteral: t.bool,
26-
NullLiteral: t.null,
27-
IntegerLiteral: t.integer,
28-
RealLiteral: t.float,
29-
'StringLiteral UTF8StringLiteral RawStringLiteral CharacterLiteral InterpolatedRegularString InterpolatedVerbatimString InterpolatedRawString $" @$" $@"':
30-
t.string,
31-
"LineComment BlockComment": t.comment,
32-
33-
". .. : Astrisk Slash % + - ++ -- Not ~ << & | ^ && || < > <= >= == NotEq = += -= *= SlashEq %= &= |= ^= ? ?? ??= =>":
34-
t.operator,
35-
36-
PP_Directive: t.keyword,
37-
38-
TypeIdentifier: t.typeName,
39-
"ArgumentName AttrsNamedArg": t.variableName,
40-
ConstName: t.constant(t.variableName),
41-
42-
//Ident: t.name,
43-
MethodName: t.function(t.variableName),
44-
ParamName: [t.emphasis, t.variableName],
45-
VarName: t.variableName,
46-
"FieldName PropertyName": t.propertyName,
47-
48-
"( )": t.paren,
49-
"{ }": t.brace,
50-
"[ ]": t.squareBracket
51-
})
52-
]
53-
}),
54-
languageData: {
55-
commentTokens: { line: "//", block: { open: "/*", close: "*/" } },
56-
closeBrackets: { brackets: ["(", "[", "{", '"', "'", '"""'] },
57-
indentOnInput: /^\s*((\)|\]|\})$|(else|else\s+if|catch|finally|case)\b|default:)/
58-
}
59-
});
60-
61-
export function csharp() {
62-
return new LanguageSupport(csharpLanguage);
63-
}
1+
export * from "./complete";
2+
export * from "./csharp";
3+
export * from "./keymap";
4+
export * from "./syntax.grammar";

src/keymap.ts

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import { syntaxTree } from "@codemirror/language";
2+
import { keymap } from "@codemirror/view";
3+
import { EditorSelection } from "@codemirror/state";
4+
5+
export const continueDocComment = keymap.of([{
6+
key: "Enter",
7+
run(view) {
8+
const state = view.state;
9+
const main = state.selection.main;
10+
if (!main.empty) { return false; }
11+
12+
const doc = state.doc;
13+
const line = doc.lineAt(main.head);
14+
const pos = Math.max(line.from, main.head);
15+
const node = syntaxTree(state).resolve(pos, -1);
16+
if (node.name !== "DocComment") { return false; }
17+
18+
const match = line.text.match(/^(\s*)\/\/\/ ?/);
19+
if (!match) { return false; }
20+
21+
const prefix = `\n${match[1]}/// `;
22+
let insert = prefix;
23+
24+
const nodeInner = syntaxTree(state).resolveInner(pos);
25+
let inTagNode = nodeInner.name === "Element"
26+
&& doc.sliceString(pos - 1, pos) === ">"
27+
&& doc.sliceString(pos, pos + 1) === "<";
28+
if (inTagNode) {
29+
insert += prefix;
30+
}
31+
const cursor = main.head + prefix.length;
32+
33+
view.dispatch(state.update({
34+
changes: { from: main.head, to: main.head, insert },
35+
selection: EditorSelection.cursor(cursor),
36+
scrollIntoView: true,
37+
userEvent: "input"
38+
}));
39+
return true;
40+
}
41+
}]);

0 commit comments

Comments
 (0)