@wallarm-org/design-system 1.30.0 → 1.31.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/CodeEditor/CodeEditorContent.d.ts +17 -0
- package/dist/components/CodeEditor/CodeEditorContent.js +197 -0
- package/dist/components/CodeEditor/CodeEditorContext.d.ts +17 -0
- package/dist/components/CodeEditor/CodeEditorContext.js +8 -0
- package/dist/components/CodeEditor/CodeEditorRoot.d.ts +77 -0
- package/dist/components/CodeEditor/CodeEditorRoot.js +203 -0
- package/dist/components/CodeEditor/classes.d.ts +21 -0
- package/dist/components/CodeEditor/classes.js +41 -0
- package/dist/components/CodeEditor/engine/SearchPanel.d.ts +13 -0
- package/dist/components/CodeEditor/engine/SearchPanel.js +201 -0
- package/dist/components/CodeEditor/engine/adapterPainter.d.ts +23 -0
- package/dist/components/CodeEditor/engine/adapterPainter.js +110 -0
- package/dist/components/CodeEditor/engine/babelSyntax.d.ts +15 -0
- package/dist/components/CodeEditor/engine/babelSyntax.js +54 -0
- package/dist/components/CodeEditor/engine/completion.d.ts +17 -0
- package/dist/components/CodeEditor/engine/completion.js +129 -0
- package/dist/components/CodeEditor/engine/contentAttributes.d.ts +12 -0
- package/dist/components/CodeEditor/engine/contentAttributes.js +24 -0
- package/dist/components/CodeEditor/engine/diagnostics.d.ts +73 -0
- package/dist/components/CodeEditor/engine/diagnostics.js +327 -0
- package/dist/components/CodeEditor/engine/diff.d.ts +37 -0
- package/dist/components/CodeEditor/engine/diff.js +145 -0
- package/dist/components/CodeEditor/engine/folds.d.ts +25 -0
- package/dist/components/CodeEditor/engine/folds.js +458 -0
- package/dist/components/CodeEditor/engine/gutters.d.ts +22 -0
- package/dist/components/CodeEditor/engine/gutters.js +160 -0
- package/dist/components/CodeEditor/engine/index.d.ts +12 -0
- package/dist/components/CodeEditor/engine/index.js +387 -0
- package/dist/components/CodeEditor/engine/languages/http/index.d.ts +20 -0
- package/dist/components/CodeEditor/engine/languages/http/index.js +69 -0
- package/dist/components/CodeEditor/engine/languages/http/parser.d.ts +2 -0
- package/dist/components/CodeEditor/engine/languages/http/parser.js +31 -0
- package/dist/components/CodeEditor/engine/languages/http/parser.terms.d.ts +1 -0
- package/dist/components/CodeEditor/engine/languages/http/parser.terms.js +2 -0
- package/dist/components/CodeEditor/engine/languages/index.d.ts +8 -0
- package/dist/components/CodeEditor/engine/languages/index.js +41 -0
- package/dist/components/CodeEditor/engine/languages/json.d.ts +8 -0
- package/dist/components/CodeEditor/engine/languages/json.js +11 -0
- package/dist/components/CodeEditor/engine/languages/jsonPointers.d.ts +47 -0
- package/dist/components/CodeEditor/engine/languages/jsonPointers.js +125 -0
- package/dist/components/CodeEditor/engine/languages/yaml.d.ts +4 -0
- package/dist/components/CodeEditor/engine/languages/yaml.js +11 -0
- package/dist/components/CodeEditor/engine/lines.d.ts +22 -0
- package/dist/components/CodeEditor/engine/lines.js +131 -0
- package/dist/components/CodeEditor/engine/luaSyntax.d.ts +14 -0
- package/dist/components/CodeEditor/engine/luaSyntax.js +36 -0
- package/dist/components/CodeEditor/engine/positions.d.ts +19 -0
- package/dist/components/CodeEditor/engine/positions.js +22 -0
- package/dist/components/CodeEditor/engine/schema/complete.d.ts +12 -0
- package/dist/components/CodeEditor/engine/schema/complete.js +211 -0
- package/dist/components/CodeEditor/engine/schema/hover.d.ts +12 -0
- package/dist/components/CodeEditor/engine/schema/hover.js +73 -0
- package/dist/components/CodeEditor/engine/schema/loadSchema.d.ts +19 -0
- package/dist/components/CodeEditor/engine/schema/loadSchema.js +32 -0
- package/dist/components/CodeEditor/engine/schema/validate.d.ts +9 -0
- package/dist/components/CodeEditor/engine/schema/validate.js +67 -0
- package/dist/components/CodeEditor/engine/search.d.ts +13 -0
- package/dist/components/CodeEditor/engine/search.js +84 -0
- package/dist/components/CodeEditor/engine/searchMatches.d.ts +8 -0
- package/dist/components/CodeEditor/engine/searchMatches.js +15 -0
- package/dist/components/CodeEditor/engine/theme.d.ts +8 -0
- package/dist/components/CodeEditor/engine/theme.js +89 -0
- package/dist/components/CodeEditor/engine/types.d.ts +44 -0
- package/dist/components/CodeEditor/engine/types.js +0 -0
- package/dist/components/CodeEditor/hooks/useCodeEditor.d.ts +6 -0
- package/dist/components/CodeEditor/hooks/useCodeEditor.js +3 -0
- package/dist/components/CodeEditor/index.d.ts +6 -0
- package/dist/components/CodeEditor/index.js +5 -0
- package/dist/components/CodeEditor/lib/PortalOutlet.d.ts +11 -0
- package/dist/components/CodeEditor/lib/PortalOutlet.js +11 -0
- package/dist/components/CodeEditor/lib/httpCompletions.d.ts +7 -0
- package/dist/components/CodeEditor/lib/httpCompletions.js +102 -0
- package/dist/components/CodeEditor/lib/httpCompletionsData.d.ts +29 -0
- package/dist/components/CodeEditor/lib/httpCompletionsData.js +650 -0
- package/dist/components/CodeEditor/lib/keyboardHint.d.ts +5 -0
- package/dist/components/CodeEditor/lib/keyboardHint.js +2 -0
- package/dist/components/CodeEditor/lib/loadEngine.d.ts +7 -0
- package/dist/components/CodeEditor/lib/loadEngine.js +9 -0
- package/dist/components/CodeEditor/lib/portalRegistry.d.ts +20 -0
- package/dist/components/CodeEditor/lib/portalRegistry.js +47 -0
- package/dist/components/CodeEditor/lib/splitContentProps.d.ts +12 -0
- package/dist/components/CodeEditor/lib/splitContentProps.js +28 -0
- package/dist/components/CodeEditor/types.d.ts +72 -0
- package/dist/components/CodeEditor/types.js +0 -0
- package/dist/components/CodeSnippet/CodeSnippetCode.js +6 -23
- package/dist/components/CodeSnippet/CodeSnippetCopyButton.js +4 -3
- package/dist/components/CodeSnippet/CodeSnippetFrameContext.d.ts +25 -0
- package/dist/components/CodeSnippet/CodeSnippetFrameContext.js +3 -0
- package/dist/components/CodeSnippet/CodeSnippetFullscreenButton.js +2 -2
- package/dist/components/CodeSnippet/CodeSnippetRoot.d.ts +3 -5
- package/dist/components/CodeSnippet/CodeSnippetRoot.js +56 -50
- package/dist/components/CodeSnippet/CodeSnippetShowMoreButton.js +7 -5
- package/dist/components/CodeSnippet/CodeSnippetWrapButton.js +2 -2
- package/dist/components/CodeSnippet/adapters/highlightjs.js +1 -0
- package/dist/components/CodeSnippet/adapters/prism.js +3 -1
- package/dist/components/CodeSnippet/adapters/shiki.js +1 -0
- package/dist/components/CodeSnippet/adapters/types.d.ts +3 -3
- package/dist/components/CodeSnippet/classes.d.ts +3 -0
- package/dist/components/CodeSnippet/classes.js +14 -0
- package/dist/components/CodeSnippet/hooks/index.d.ts +1 -0
- package/dist/components/CodeSnippet/hooks/index.js +1 -0
- package/dist/components/CodeSnippet/hooks/useCodeSnippetFrame.d.ts +2 -0
- package/dist/components/CodeSnippet/hooks/useCodeSnippetFrame.js +8 -0
- package/dist/components/CodeSnippet/index.d.ts +2 -1
- package/dist/components/CodeSnippet/index.js +1 -1
- package/dist/components/CodeSnippet/internal/CodeSnippetFrame.d.ts +22 -0
- package/dist/components/CodeSnippet/internal/CodeSnippetFrame.js +81 -0
- package/dist/components/CodeSnippet/internal/CodeToken.d.ts +2 -1
- package/dist/components/CodeSnippet/internal/CodeToken.js +1 -1
- package/dist/components/CodeSnippet/internal/FoldSummary.d.ts +11 -0
- package/dist/components/CodeSnippet/internal/FoldSummary.js +27 -0
- package/dist/components/CodeSnippet/internal/FoldToggle.d.ts +2 -0
- package/dist/components/CodeSnippet/internal/FoldToggle.js +2 -1
- package/dist/components/CodeSnippet/internal/index.d.ts +1 -0
- package/dist/components/CodeSnippet/internal/index.js +1 -0
- package/dist/components/CodeSnippet/lib/foldUtils.d.ts +9 -2
- package/dist/components/CodeSnippet/lib/foldUtils.js +2 -2
- package/dist/components/CodeSnippet/lib/showMore.d.ts +7 -0
- package/dist/components/CodeSnippet/lib/showMore.js +7 -0
- package/dist/components/Copyable/Copyable.d.ts +5 -2
- package/dist/components/Copyable/Copyable.js +1 -1
- package/dist/metadata/components.json +1235 -5
- package/package.json +24 -1
|
@@ -0,0 +1,327 @@
|
|
|
1
|
+
import { ensureSyntaxTree, syntaxTree } from "@codemirror/language";
|
|
2
|
+
import { linter, setDiagnosticsEffect } from "@codemirror/lint";
|
|
3
|
+
import { EditorView } from "@codemirror/view";
|
|
4
|
+
import { babelSyntaxDiagnostics } from "./babelSyntax.js";
|
|
5
|
+
import { findJsonBodyRange } from "./languages/index.js";
|
|
6
|
+
import { luaSyntaxDiagnostics } from "./luaSyntax.js";
|
|
7
|
+
import { offsetToPosition, positionToOffset } from "./positions.js";
|
|
8
|
+
const LINT_DELAY = 300;
|
|
9
|
+
const SYNTAX_SOURCE = 'syntax';
|
|
10
|
+
const SCHEMA_SOURCE = 'schema';
|
|
11
|
+
const ENSURE_TREE_TIMEOUT = 200;
|
|
12
|
+
const jsonRegion = (state, language)=>{
|
|
13
|
+
if ('json' === language) return {
|
|
14
|
+
from: 0,
|
|
15
|
+
to: state.doc.length
|
|
16
|
+
};
|
|
17
|
+
if ('http' === language) return findJsonBodyRange(state);
|
|
18
|
+
return null;
|
|
19
|
+
};
|
|
20
|
+
const POSITION = /at position (\d+)/;
|
|
21
|
+
const LINE_COLUMN = /line (\d+) column (\d+)/;
|
|
22
|
+
const END_OF_INPUT = /end of (?:JSON )?(?:input|data)/i;
|
|
23
|
+
const offsetOfLineColumn = (text, line, column)=>{
|
|
24
|
+
let offset = 0;
|
|
25
|
+
for(let current = 1; current < line; current++){
|
|
26
|
+
const newline = text.indexOf('\n', offset);
|
|
27
|
+
if (newline < 0) return text.length;
|
|
28
|
+
offset = newline + 1;
|
|
29
|
+
}
|
|
30
|
+
return Math.min(offset + column - 1, text.length);
|
|
31
|
+
};
|
|
32
|
+
const cleanMessage = (message)=>{
|
|
33
|
+
const cleaned = message.replace(/^JSON\.parse: /, '').replace(/^JSON Parse error: /, '').replace(/ (in|after) JSON at position \d+(?: \(line \d+ column \d+\))?$/, (_, word)=>'after' === word ? ' after JSON' : '').replace(/ at line \d+ column \d+ of the JSON data$/, '').replace(/, (?:\.\.\.)?".*"(?:\.\.\.)? is not valid JSON$/s, '').trim();
|
|
34
|
+
if ('' === cleaned) return 'Invalid JSON';
|
|
35
|
+
return cleaned.charAt(0).toUpperCase() + cleaned.slice(1);
|
|
36
|
+
};
|
|
37
|
+
const locateJsonSyntaxError = (message, text)=>{
|
|
38
|
+
const clean = cleanMessage(message);
|
|
39
|
+
const position = POSITION.exec(message);
|
|
40
|
+
if (position) return {
|
|
41
|
+
offset: Number(position[1]),
|
|
42
|
+
message: clean
|
|
43
|
+
};
|
|
44
|
+
const lineColumn = LINE_COLUMN.exec(message);
|
|
45
|
+
if (lineColumn) return {
|
|
46
|
+
offset: offsetOfLineColumn(text, Number(lineColumn[1]), Number(lineColumn[2])),
|
|
47
|
+
message: clean
|
|
48
|
+
};
|
|
49
|
+
if (END_OF_INPUT.test(message)) return {
|
|
50
|
+
offset: text.trimEnd().length,
|
|
51
|
+
message: clean
|
|
52
|
+
};
|
|
53
|
+
return {
|
|
54
|
+
offset: null,
|
|
55
|
+
message: clean
|
|
56
|
+
};
|
|
57
|
+
};
|
|
58
|
+
const rangeAt = (text, relative, base)=>{
|
|
59
|
+
const contentEnd = text.trimEnd().length;
|
|
60
|
+
if (relative >= contentEnd) return {
|
|
61
|
+
from: base + contentEnd - 1,
|
|
62
|
+
to: base + contentEnd
|
|
63
|
+
};
|
|
64
|
+
const from = Math.max(relative, 0);
|
|
65
|
+
return '\n' === text.charAt(from) ? {
|
|
66
|
+
from: base + from,
|
|
67
|
+
to: base + from
|
|
68
|
+
} : {
|
|
69
|
+
from: base + from,
|
|
70
|
+
to: base + from + 1
|
|
71
|
+
};
|
|
72
|
+
};
|
|
73
|
+
const parsedTree = (state, upto, timeout)=>{
|
|
74
|
+
const ensured = ensureSyntaxTree(state, upto, timeout);
|
|
75
|
+
if (ensured) return {
|
|
76
|
+
tree: ensured,
|
|
77
|
+
parsedTo: 1 / 0
|
|
78
|
+
};
|
|
79
|
+
const tree = syntaxTree(state);
|
|
80
|
+
return {
|
|
81
|
+
tree,
|
|
82
|
+
parsedTo: tree.length
|
|
83
|
+
};
|
|
84
|
+
};
|
|
85
|
+
const firstErrorNode = (state, region, timeout)=>{
|
|
86
|
+
const { tree, parsedTo } = parsedTree(state, region.to, timeout);
|
|
87
|
+
let found = null;
|
|
88
|
+
tree.iterate({
|
|
89
|
+
from: region.from,
|
|
90
|
+
to: region.to,
|
|
91
|
+
enter: (node)=>{
|
|
92
|
+
if (found) return false;
|
|
93
|
+
if (!node.type.isError || node.from < region.from) return true;
|
|
94
|
+
if (node.from >= parsedTo) return false;
|
|
95
|
+
found = {
|
|
96
|
+
from: node.from,
|
|
97
|
+
to: node.to
|
|
98
|
+
};
|
|
99
|
+
return false;
|
|
100
|
+
}
|
|
101
|
+
});
|
|
102
|
+
return found;
|
|
103
|
+
};
|
|
104
|
+
const syntaxErrorDiagnostics = (state, region, timeout = ENSURE_TREE_TIMEOUT)=>{
|
|
105
|
+
const length = state.doc.length;
|
|
106
|
+
const { tree, parsedTo } = parsedTree(state, length, timeout);
|
|
107
|
+
const out = [];
|
|
108
|
+
tree.iterate({
|
|
109
|
+
from: region?.from ?? 0,
|
|
110
|
+
to: region?.to ?? length,
|
|
111
|
+
enter: (node)=>{
|
|
112
|
+
if (!node.type.isError) return true;
|
|
113
|
+
if (region && (node.from < region.from || node.from > region.to)) return false;
|
|
114
|
+
if (node.from >= parsedTo) return false;
|
|
115
|
+
const from = node.from;
|
|
116
|
+
const to = Math.min(Math.max(node.to, from + 1), length);
|
|
117
|
+
const last = out.at(-1);
|
|
118
|
+
if (last && from <= last.to) {
|
|
119
|
+
last.to = Math.max(last.to, to);
|
|
120
|
+
return false;
|
|
121
|
+
}
|
|
122
|
+
out.push({
|
|
123
|
+
from,
|
|
124
|
+
to,
|
|
125
|
+
severity: 'error',
|
|
126
|
+
source: SYNTAX_SOURCE,
|
|
127
|
+
message: from < length ? `Unexpected "${state.doc.sliceString(from, from + 1)}"` : 'Unexpected end of input'
|
|
128
|
+
});
|
|
129
|
+
return false;
|
|
130
|
+
}
|
|
131
|
+
});
|
|
132
|
+
return out;
|
|
133
|
+
};
|
|
134
|
+
const ERROR_NODE_LANGUAGES = new Set([
|
|
135
|
+
'yaml',
|
|
136
|
+
'python'
|
|
137
|
+
]);
|
|
138
|
+
const isBabelLanguage = (language)=>"javascript" === language || "typescript" === language;
|
|
139
|
+
const jsonSyntaxDiagnostics = (state, region, timeout = ENSURE_TREE_TIMEOUT)=>{
|
|
140
|
+
const text = state.sliceDoc(region.from, region.to);
|
|
141
|
+
if ('' === text.trim()) return [];
|
|
142
|
+
try {
|
|
143
|
+
JSON.parse(text);
|
|
144
|
+
return [];
|
|
145
|
+
} catch (error) {
|
|
146
|
+
const raw = error instanceof Error ? error.message : String(error);
|
|
147
|
+
const located = locateJsonSyntaxError(raw, text);
|
|
148
|
+
let range;
|
|
149
|
+
if (null !== located.offset) range = rangeAt(text, located.offset, region.from);
|
|
150
|
+
else {
|
|
151
|
+
const node = firstErrorNode(state, region, timeout);
|
|
152
|
+
range = node && node.to > node.from ? node : node ? rangeAt(text, node.from - region.from, region.from) : rangeAt(text, text.length - text.trimStart().length, region.from);
|
|
153
|
+
}
|
|
154
|
+
return [
|
|
155
|
+
{
|
|
156
|
+
from: range.from,
|
|
157
|
+
to: range.to,
|
|
158
|
+
severity: 'error',
|
|
159
|
+
message: located.message,
|
|
160
|
+
source: SYNTAX_SOURCE
|
|
161
|
+
}
|
|
162
|
+
];
|
|
163
|
+
}
|
|
164
|
+
};
|
|
165
|
+
const defaultEnd = (doc, from)=>from < doc.lineAt(from).to ? from + 1 : from;
|
|
166
|
+
const toCmDiagnostic = (doc, diagnostic, startingLineNumber)=>{
|
|
167
|
+
const from = positionToOffset(doc, diagnostic.from, startingLineNumber);
|
|
168
|
+
if (null === from) return null;
|
|
169
|
+
const to = diagnostic.to ? Math.max(from, positionToOffset(doc, diagnostic.to, startingLineNumber) ?? doc.length) : defaultEnd(doc, from);
|
|
170
|
+
return {
|
|
171
|
+
from,
|
|
172
|
+
to,
|
|
173
|
+
severity: diagnostic.severity,
|
|
174
|
+
message: diagnostic.message,
|
|
175
|
+
...void 0 === diagnostic.source ? {} : {
|
|
176
|
+
source: diagnostic.source
|
|
177
|
+
}
|
|
178
|
+
};
|
|
179
|
+
};
|
|
180
|
+
const toPublicDiagnostic = (doc, diagnostic, startingLineNumber)=>({
|
|
181
|
+
from: offsetToPosition(doc, diagnostic.from, startingLineNumber),
|
|
182
|
+
to: offsetToPosition(doc, diagnostic.to, startingLineNumber),
|
|
183
|
+
severity: 'hint' === diagnostic.severity ? 'info' : diagnostic.severity,
|
|
184
|
+
message: diagnostic.message,
|
|
185
|
+
...void 0 === diagnostic.source ? {} : {
|
|
186
|
+
source: diagnostic.source
|
|
187
|
+
}
|
|
188
|
+
});
|
|
189
|
+
const externalDiagnostics = (doc, external, startingLineNumber)=>external.flatMap((diagnostic)=>{
|
|
190
|
+
const converted = toCmDiagnostic(doc, diagnostic, startingLineNumber);
|
|
191
|
+
return converted ? [
|
|
192
|
+
converted
|
|
193
|
+
] : [];
|
|
194
|
+
});
|
|
195
|
+
const latestLintState = new WeakMap();
|
|
196
|
+
const superseded = ()=>new Promise(()=>void 0);
|
|
197
|
+
const lintSource = (config)=>(view)=>{
|
|
198
|
+
const { state } = view;
|
|
199
|
+
latestLintState.set(view, state);
|
|
200
|
+
const isCurrent = ()=>latestLintState.get(view) === state;
|
|
201
|
+
const region = jsonRegion(state, config.language);
|
|
202
|
+
const external = externalDiagnostics(state.doc, config.external, config.startingLineNumber);
|
|
203
|
+
if (isBabelLanguage(config.language)) return babelSyntaxDiagnostics(state.doc.toString(), config.language).then((syntax)=>isCurrent() ? [
|
|
204
|
+
...syntax,
|
|
205
|
+
...external
|
|
206
|
+
] : superseded(), (error)=>{
|
|
207
|
+
if (!isCurrent()) return superseded();
|
|
208
|
+
console.error('[CodeEditor] failed to load the JavaScript syntax checker', error);
|
|
209
|
+
return external;
|
|
210
|
+
});
|
|
211
|
+
if ('lua' === config.language) return luaSyntaxDiagnostics(state.doc.toString()).then((syntax)=>isCurrent() ? [
|
|
212
|
+
...syntax,
|
|
213
|
+
...external
|
|
214
|
+
] : superseded(), (error)=>{
|
|
215
|
+
if (!isCurrent()) return superseded();
|
|
216
|
+
console.error('[CodeEditor] failed to load the Lua syntax checker', error);
|
|
217
|
+
return external;
|
|
218
|
+
});
|
|
219
|
+
const syntax = region ? jsonSyntaxDiagnostics(state, region) : ERROR_NODE_LANGUAGES.has(config.language) ? syntaxErrorDiagnostics(state) : [];
|
|
220
|
+
const { schemaSource, schema } = config;
|
|
221
|
+
if (!region || syntax.length > 0 || void 0 === schema || !schemaSource || '' === state.sliceDoc(region.from, region.to).trim()) return [
|
|
222
|
+
...syntax,
|
|
223
|
+
...external
|
|
224
|
+
];
|
|
225
|
+
return schemaSource(state, region).then((schemaDiagnostics)=>isCurrent() ? [
|
|
226
|
+
...schemaDiagnostics.map((d)=>({
|
|
227
|
+
...d,
|
|
228
|
+
source: d.source ?? SCHEMA_SOURCE
|
|
229
|
+
})),
|
|
230
|
+
...external
|
|
231
|
+
] : superseded(), (error)=>{
|
|
232
|
+
if (!isCurrent()) return superseded();
|
|
233
|
+
console.error('[CodeEditor] JSON Schema validation failed', error);
|
|
234
|
+
return external;
|
|
235
|
+
});
|
|
236
|
+
};
|
|
237
|
+
const reported = new WeakMap();
|
|
238
|
+
const EMPTY_LIST = '[]';
|
|
239
|
+
const reportChanges = (config)=>EditorView.updateListener.of((update)=>{
|
|
240
|
+
for (const tr of update.transactions)for (const effect of tr.effects){
|
|
241
|
+
if (!effect.is(setDiagnosticsEffect)) continue;
|
|
242
|
+
const list = effect.value.map((d)=>toPublicDiagnostic(tr.state.doc, d, config.startingLineNumber));
|
|
243
|
+
const key = JSON.stringify(list);
|
|
244
|
+
if ((reported.get(update.view) ?? EMPTY_LIST) !== key) {
|
|
245
|
+
reported.set(update.view, key);
|
|
246
|
+
config.onChange(list);
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
});
|
|
250
|
+
const wavyUnderline = (token)=>({
|
|
251
|
+
backgroundImage: 'none',
|
|
252
|
+
textDecorationLine: 'underline',
|
|
253
|
+
textDecorationStyle: 'wavy',
|
|
254
|
+
textDecorationColor: `var(${token})`,
|
|
255
|
+
textDecorationSkipInk: 'none',
|
|
256
|
+
textUnderlineOffset: '3px'
|
|
257
|
+
});
|
|
258
|
+
const ERROR_INDICATOR = '--color-syntax-highlight-error-indicator';
|
|
259
|
+
const WARNING_INDICATOR = '--color-syntax-highlight-warning-indicator';
|
|
260
|
+
const INFO_INDICATOR = '--color-syntax-highlight-info-indicator';
|
|
261
|
+
const diagnosticsTheme = EditorView.theme({
|
|
262
|
+
'.cm-lintRange': {
|
|
263
|
+
paddingBottom: '0'
|
|
264
|
+
},
|
|
265
|
+
'.cm-lintRange-error': wavyUnderline(ERROR_INDICATOR),
|
|
266
|
+
'.cm-lintRange-warning': wavyUnderline(WARNING_INDICATOR),
|
|
267
|
+
'.cm-lintRange-info': wavyUnderline(INFO_INDICATOR),
|
|
268
|
+
'.cm-lintRange-hint': wavyUnderline(INFO_INDICATOR),
|
|
269
|
+
'.cm-lintRange-active': {
|
|
270
|
+
backgroundColor: 'transparent'
|
|
271
|
+
},
|
|
272
|
+
'.cm-lintPoint:after': {
|
|
273
|
+
borderBottomColor: `var(${ERROR_INDICATOR})`
|
|
274
|
+
},
|
|
275
|
+
'.cm-lintPoint-warning:after': {
|
|
276
|
+
borderBottomColor: `var(${WARNING_INDICATOR})`
|
|
277
|
+
},
|
|
278
|
+
'.cm-lintPoint-info:after': {
|
|
279
|
+
borderBottomColor: `var(${INFO_INDICATOR})`
|
|
280
|
+
},
|
|
281
|
+
'.cm-lintPoint-hint:after': {
|
|
282
|
+
borderBottomColor: `var(${INFO_INDICATOR})`
|
|
283
|
+
},
|
|
284
|
+
'.cm-tooltip.cm-tooltip-hover': {
|
|
285
|
+
border: 'none',
|
|
286
|
+
borderRadius: 'var(--radius-8)',
|
|
287
|
+
backgroundColor: 'var(--color-component-tooltip-bg)',
|
|
288
|
+
color: 'var(--color-text-primary-alt)',
|
|
289
|
+
fontFamily: 'var(--font-sans)',
|
|
290
|
+
fontSize: 'var(--text-xs)',
|
|
291
|
+
lineHeight: 'var(--text-xs--line-height)',
|
|
292
|
+
fontWeight: 'var(--font-weight-medium)',
|
|
293
|
+
overflow: 'hidden',
|
|
294
|
+
zIndex: 'var(--tooltip-z-index)'
|
|
295
|
+
},
|
|
296
|
+
'.cm-tooltip-lint': {
|
|
297
|
+
padding: '4px 0'
|
|
298
|
+
},
|
|
299
|
+
'.cm-diagnostic': {
|
|
300
|
+
padding: '0 8px 0 6px',
|
|
301
|
+
marginLeft: '0',
|
|
302
|
+
borderLeft: `2px solid var(${ERROR_INDICATOR})`
|
|
303
|
+
},
|
|
304
|
+
'.cm-diagnostic-warning': {
|
|
305
|
+
borderLeft: `2px solid var(${WARNING_INDICATOR})`
|
|
306
|
+
},
|
|
307
|
+
'.cm-diagnostic-info': {
|
|
308
|
+
borderLeft: `2px solid var(${INFO_INDICATOR})`
|
|
309
|
+
},
|
|
310
|
+
'.cm-diagnostic-hint': {
|
|
311
|
+
borderLeft: `2px solid var(${INFO_INDICATOR})`
|
|
312
|
+
},
|
|
313
|
+
'.cm-diagnosticSource': {
|
|
314
|
+
fontSize: 'var(--text-xs)',
|
|
315
|
+
opacity: '0.7'
|
|
316
|
+
}
|
|
317
|
+
});
|
|
318
|
+
const diagnosticsExtension = (config)=>[
|
|
319
|
+
linter(lintSource(config), {
|
|
320
|
+
delay: LINT_DELAY,
|
|
321
|
+
autoPanel: false,
|
|
322
|
+
needsRefresh: (update)=>update.transactions.some((tr)=>tr.reconfigured)
|
|
323
|
+
}),
|
|
324
|
+
reportChanges(config),
|
|
325
|
+
diagnosticsTheme
|
|
326
|
+
];
|
|
327
|
+
export { LINT_DELAY, SCHEMA_SOURCE, SYNTAX_SOURCE, diagnosticsExtension, diagnosticsTheme, jsonRegion, jsonSyntaxDiagnostics, locateJsonSyntaxError, syntaxErrorDiagnostics, toCmDiagnostic, toPublicDiagnostic };
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { type EditorState, type Extension } from '@codemirror/state';
|
|
2
|
+
import { type BlockInfo, type ViewUpdate } from '@codemirror/view';
|
|
3
|
+
import type { PortalRegistry } from '../lib/portalRegistry';
|
|
4
|
+
export interface DiffExtensionConfig {
|
|
5
|
+
original: string;
|
|
6
|
+
/** Reserved for ReactNode content in diff rows; the `+` / `-` markers are plain text. */
|
|
7
|
+
portals: PortalRegistry;
|
|
8
|
+
}
|
|
9
|
+
/** Semantic hooks for the theme below (never used as test selectors in RTL tests). */
|
|
10
|
+
export declare const DIFF_INSERTED_CLASS = "cm-ds-diff-inserted";
|
|
11
|
+
export declare const DIFF_CHANGED_CLASS = "cm-ds-diff-changed";
|
|
12
|
+
export declare const DIFF_DELETED_CLASS = "cm-ds-diff-deleted";
|
|
13
|
+
/** `true` when `lineFrom` is the start of an inserted or changed line of the current document. */
|
|
14
|
+
export declare const isDiffInsertedLine: (state: EditorState, lineFrom: number) => boolean;
|
|
15
|
+
/**
|
|
16
|
+
* Number of original lines shown by the merge view's deleted-chunk widget for this gutter
|
|
17
|
+
* block, or 0 when the block is not a (non-empty) deleted chunk. The merge view places
|
|
18
|
+
* that widget as a block widget *before* `chunk.fromB`.
|
|
19
|
+
*/
|
|
20
|
+
export declare const deletedRowCount: (state: EditorState, block: BlockInfo) => number;
|
|
21
|
+
/**
|
|
22
|
+
* Rows the merge view's deleted-chunk widgets add in diff mode (one per deleted original line),
|
|
23
|
+
* skipping chunks whose widget sits strictly inside a `hidden` (collapsed fold) range — the fold's
|
|
24
|
+
* replace decoration hides those widgets. 0 outside diff mode.
|
|
25
|
+
*/
|
|
26
|
+
export declare const diffDeletedRowCount: (state: EditorState, hidden: readonly {
|
|
27
|
+
from: number;
|
|
28
|
+
to: number;
|
|
29
|
+
}[]) => number;
|
|
30
|
+
/** For gutter `lineMarkerChange`: the diff changed although the document may not have. */
|
|
31
|
+
export declare const diffChunksChanged: (update: ViewUpdate) => boolean;
|
|
32
|
+
/**
|
|
33
|
+
* Diff mode (spec §7.15, D7): CodeMirror's unified merge view against `original`, restyled as
|
|
34
|
+
* CodeSnippet `success` / `danger` lines. The `+` / `-` prefixes, colour sticks and blank line
|
|
35
|
+
* numbers of deleted rows are drawn by `guttersExtension({ diff: true })`.
|
|
36
|
+
*/
|
|
37
|
+
export declare const diffExtension: (config: DiffExtensionConfig) => Extension;
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import { getChunks, getOriginalDoc, unifiedMergeView } from "@codemirror/merge";
|
|
2
|
+
import { Prec, RangeSet, StateField } from "@codemirror/state";
|
|
3
|
+
import { BlockType, Decoration, EditorView, GutterMarker, gutterLineClass, gutterWidgetClass, lineNumberMarkers } from "@codemirror/view";
|
|
4
|
+
import { cn } from "../../../utils/cn.js";
|
|
5
|
+
import { LINE_COLOR_STYLES } from "../../CodeSnippet/lib/lineStyles.js";
|
|
6
|
+
const DIFF_INSERTED_CLASS = 'cm-ds-diff-inserted';
|
|
7
|
+
const DIFF_CHANGED_CLASS = 'cm-ds-diff-changed';
|
|
8
|
+
const DIFF_DELETED_CLASS = 'cm-ds-diff-deleted';
|
|
9
|
+
const success = LINE_COLOR_STYLES.success;
|
|
10
|
+
class DiffClassMarker extends GutterMarker {
|
|
11
|
+
elementClass;
|
|
12
|
+
constructor(elementClass){
|
|
13
|
+
super(), this.elementClass = elementClass;
|
|
14
|
+
}
|
|
15
|
+
eq(other) {
|
|
16
|
+
return other instanceof DiffClassMarker && other.elementClass === this.elementClass;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
const insertedGutterBackground = new DiffClassMarker(cn(DIFF_INSERTED_CLASS, success.bg));
|
|
20
|
+
const insertedNumberText = new DiffClassMarker(cn(DIFF_INSERTED_CLASS, success.text));
|
|
21
|
+
const deletedGutterBackground = new DiffClassMarker(cn(DIFF_DELETED_CLASS, LINE_COLOR_STYLES.danger.bg));
|
|
22
|
+
const EMPTY = {
|
|
23
|
+
chunks: null,
|
|
24
|
+
insertedLines: new Set(),
|
|
25
|
+
decorations: Decoration.none,
|
|
26
|
+
gutterBackground: RangeSet.empty,
|
|
27
|
+
numberText: RangeSet.empty
|
|
28
|
+
};
|
|
29
|
+
const buildDiffDecorations = (state)=>{
|
|
30
|
+
const chunks = getChunks(state)?.chunks ?? null;
|
|
31
|
+
if (!chunks) return EMPTY;
|
|
32
|
+
const insertedLines = new Set();
|
|
33
|
+
const decorations = [];
|
|
34
|
+
const gutterBackground = [];
|
|
35
|
+
const numberText = [];
|
|
36
|
+
for (const chunk of chunks){
|
|
37
|
+
if (chunk.fromB >= chunk.toB) continue;
|
|
38
|
+
const changed = chunk.fromA < chunk.toA;
|
|
39
|
+
const lineDecoration = Decoration.line({
|
|
40
|
+
class: cn(DIFF_INSERTED_CLASS, changed && DIFF_CHANGED_CLASS, success.bg)
|
|
41
|
+
});
|
|
42
|
+
const textMark = Decoration.mark({
|
|
43
|
+
class: success.text
|
|
44
|
+
});
|
|
45
|
+
const first = state.doc.lineAt(chunk.fromB).number;
|
|
46
|
+
const last = state.doc.lineAt(chunk.endB).number;
|
|
47
|
+
for(let number = first; number <= last; number++){
|
|
48
|
+
const line = state.doc.line(number);
|
|
49
|
+
insertedLines.add(line.from);
|
|
50
|
+
decorations.push(lineDecoration.range(line.from));
|
|
51
|
+
if (line.length > 0) decorations.push(textMark.range(line.from, line.to));
|
|
52
|
+
gutterBackground.push(insertedGutterBackground.range(line.from));
|
|
53
|
+
numberText.push(insertedNumberText.range(line.from));
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return {
|
|
57
|
+
chunks,
|
|
58
|
+
insertedLines,
|
|
59
|
+
decorations: Decoration.set(decorations, true),
|
|
60
|
+
gutterBackground: RangeSet.of(gutterBackground, true),
|
|
61
|
+
numberText: RangeSet.of(numberText, true)
|
|
62
|
+
};
|
|
63
|
+
};
|
|
64
|
+
const diffDecorationsField = StateField.define({
|
|
65
|
+
create: buildDiffDecorations,
|
|
66
|
+
update: (value, tr)=>getChunks(tr.state)?.chunks === value.chunks ? value : buildDiffDecorations(tr.state),
|
|
67
|
+
provide: (field)=>[
|
|
68
|
+
EditorView.decorations.from(field, (value)=>value.decorations),
|
|
69
|
+
gutterLineClass.from(field, (value)=>value.gutterBackground),
|
|
70
|
+
lineNumberMarkers.from(field, (value)=>value.numberText)
|
|
71
|
+
]
|
|
72
|
+
});
|
|
73
|
+
const isDiffInsertedLine = (state, lineFrom)=>state.field(diffDecorationsField, false)?.insertedLines.has(lineFrom) ?? false;
|
|
74
|
+
const deletedRowCount = (state, block)=>{
|
|
75
|
+
if (block.type !== BlockType.WidgetBefore) return 0;
|
|
76
|
+
const chunks = getChunks(state)?.chunks;
|
|
77
|
+
if (!chunks) return 0;
|
|
78
|
+
const chunk = chunks.find((candidate)=>candidate.fromB === block.from);
|
|
79
|
+
if (!chunk || chunk.fromA >= chunk.toA) return 0;
|
|
80
|
+
const original = getOriginalDoc(state);
|
|
81
|
+
return original.lineAt(chunk.endA).number - original.lineAt(chunk.fromA).number + 1;
|
|
82
|
+
};
|
|
83
|
+
const diffDeletedRowCount = (state, hidden)=>{
|
|
84
|
+
const chunks = getChunks(state)?.chunks;
|
|
85
|
+
if (!chunks) return 0;
|
|
86
|
+
const original = getOriginalDoc(state);
|
|
87
|
+
let rows = 0;
|
|
88
|
+
for (const chunk of chunks)if (!(chunk.fromA >= chunk.toA)) {
|
|
89
|
+
if (!hidden.some(({ from, to })=>from < chunk.fromB && chunk.fromB <= to)) rows += original.lineAt(chunk.endA).number - original.lineAt(chunk.fromA).number + 1;
|
|
90
|
+
}
|
|
91
|
+
return rows;
|
|
92
|
+
};
|
|
93
|
+
const diffChunksChanged = (update)=>getChunks(update.startState)?.chunks !== getChunks(update.state)?.chunks;
|
|
94
|
+
const deletedGutterClass = gutterWidgetClass.of((view, _widget, block)=>deletedRowCount(view.state, block) > 0 ? deletedGutterBackground : null);
|
|
95
|
+
const diffTheme = EditorView.theme({
|
|
96
|
+
[`.cm-line.${DIFF_INSERTED_CLASS}`]: {
|
|
97
|
+
backgroundColor: 'var(--color-syntax-highlight-success-highlight)'
|
|
98
|
+
},
|
|
99
|
+
[`.cm-gutterElement.${DIFF_INSERTED_CLASS}`]: {
|
|
100
|
+
backgroundColor: 'var(--color-syntax-highlight-success-highlight)'
|
|
101
|
+
},
|
|
102
|
+
[`.cm-lineNumbers .cm-gutterElement.${DIFF_INSERTED_CLASS}`]: {
|
|
103
|
+
color: 'var(--color-syntax-highlight-success-code)'
|
|
104
|
+
},
|
|
105
|
+
[`.cm-gutterElement.${DIFF_DELETED_CLASS}`]: {
|
|
106
|
+
backgroundColor: 'var(--color-syntax-highlight-error-highlight)'
|
|
107
|
+
},
|
|
108
|
+
'& .cm-deletedChunk': {
|
|
109
|
+
backgroundColor: 'var(--color-syntax-highlight-error-highlight)',
|
|
110
|
+
color: 'var(--color-syntax-highlight-error-code)',
|
|
111
|
+
fontWeight: 'var(--font-weight-medium, 450)',
|
|
112
|
+
paddingLeft: '0'
|
|
113
|
+
},
|
|
114
|
+
'& .cm-deletedChunk .cm-deletedLine': {
|
|
115
|
+
padding: '0 12px'
|
|
116
|
+
},
|
|
117
|
+
'&:has(.cm-gutters) .cm-deletedChunk .cm-deletedLine': {
|
|
118
|
+
paddingLeft: '8px'
|
|
119
|
+
},
|
|
120
|
+
'&.cm-merge-b .cm-changedText, & .cm-deletedChunk .cm-deletedText, &.cm-merge-b .cm-deletedText': {
|
|
121
|
+
background: 'none'
|
|
122
|
+
},
|
|
123
|
+
[`.${DIFF_CHANGED_CLASS} .cm-changedText, .${DIFF_CHANGED_CLASS} .cm-changedText *`]: {
|
|
124
|
+
fontWeight: 'var(--font-weight-bold, 700)',
|
|
125
|
+
color: 'var(--color-syntax-highlight-success-code)'
|
|
126
|
+
},
|
|
127
|
+
[`.cm-deletedChunk:has(+ .${DIFF_CHANGED_CLASS}) .cm-deletedText`]: {
|
|
128
|
+
fontWeight: 'var(--font-weight-bold, 700)',
|
|
129
|
+
color: 'var(--color-syntax-highlight-error-code)'
|
|
130
|
+
}
|
|
131
|
+
});
|
|
132
|
+
const diffExtension = (config)=>[
|
|
133
|
+
unifiedMergeView({
|
|
134
|
+
original: config.original,
|
|
135
|
+
highlightChanges: true,
|
|
136
|
+
gutter: false,
|
|
137
|
+
mergeControls: false,
|
|
138
|
+
syntaxHighlightDeletions: false,
|
|
139
|
+
allowInlineDiffs: false
|
|
140
|
+
}),
|
|
141
|
+
Prec.highest(diffDecorationsField.init(buildDiffDecorations)),
|
|
142
|
+
deletedGutterClass,
|
|
143
|
+
diffTheme
|
|
144
|
+
];
|
|
145
|
+
export { DIFF_CHANGED_CLASS, DIFF_DELETED_CLASS, DIFF_INSERTED_CLASS, deletedRowCount, diffChunksChanged, diffDeletedRowCount, diffExtension, isDiffInsertedLine };
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { EditorState, Extension } from '@codemirror/state';
|
|
2
|
+
import { EditorView } from '@codemirror/view';
|
|
3
|
+
import type { PortalRegistry } from '../lib/portalRegistry';
|
|
4
|
+
import type { CodeEditorFolds } from '../types';
|
|
5
|
+
/** Debounce for re-running function-form `folds` after edits (spec §4). */
|
|
6
|
+
export declare const FOLDS_DEBOUNCE_MS = 150;
|
|
7
|
+
export declare const getCollapsedFoldIds: (state: EditorState) => ReadonlySet<string>;
|
|
8
|
+
export declare const getVisibleRowCount: (state: EditorState) => number;
|
|
9
|
+
/** Document ranges of the collapsed regions (what the fold replace decorations hide). */
|
|
10
|
+
export declare const getCollapsedRanges: (state: EditorState) => {
|
|
11
|
+
from: number;
|
|
12
|
+
to: number;
|
|
13
|
+
}[];
|
|
14
|
+
export declare const toggleFoldRegion: (view: EditorView, id: string) => boolean;
|
|
15
|
+
export declare const foldAllRegions: (view: EditorView) => boolean;
|
|
16
|
+
export declare const unfoldAllRegions: (view: EditorView) => boolean;
|
|
17
|
+
export declare const foldsExtension: (config: {
|
|
18
|
+
folds: CodeEditorFolds | undefined;
|
|
19
|
+
startingLineNumber: number;
|
|
20
|
+
portals: PortalRegistry;
|
|
21
|
+
testId: string | undefined;
|
|
22
|
+
}) => {
|
|
23
|
+
extension: Extension;
|
|
24
|
+
gutter: Extension | null;
|
|
25
|
+
};
|