@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,125 @@
|
|
|
1
|
+
import { ensureSyntaxTree, syntaxTree } from "@codemirror/language";
|
|
2
|
+
const VALUE_NODE_NAMES = new Set([
|
|
3
|
+
'Object',
|
|
4
|
+
'Array',
|
|
5
|
+
'String',
|
|
6
|
+
'Number',
|
|
7
|
+
'True',
|
|
8
|
+
'False',
|
|
9
|
+
'Null'
|
|
10
|
+
]);
|
|
11
|
+
const ENSURE_TREE_TIMEOUT = 200;
|
|
12
|
+
const escapePointerSegment = (segment)=>segment.replaceAll('~', '~0').replaceAll('/', '~1');
|
|
13
|
+
const isValueNode = (node)=>VALUE_NODE_NAMES.has(node.name);
|
|
14
|
+
const readJsonKey = (state, node)=>{
|
|
15
|
+
const raw = state.sliceDoc(node.from, node.to);
|
|
16
|
+
try {
|
|
17
|
+
const parsed = JSON.parse(raw);
|
|
18
|
+
if ('string' == typeof parsed) return parsed;
|
|
19
|
+
} catch {}
|
|
20
|
+
return raw.slice(1, raw.endsWith('"') && raw.length > 1 ? -1 : void 0);
|
|
21
|
+
};
|
|
22
|
+
const findJsonText = (state, region)=>{
|
|
23
|
+
const tree = ensureSyntaxTree(state, region.to, ENSURE_TREE_TIMEOUT) ?? syntaxTree(state);
|
|
24
|
+
for(let node = tree.resolveInner(region.from, 1); node; node = node.parent)if ('JsonText' === node.name) return node;
|
|
25
|
+
return null;
|
|
26
|
+
};
|
|
27
|
+
const firstValueChild = (node)=>{
|
|
28
|
+
for(let child = node.firstChild; child; child = child.nextSibling)if (isValueNode(child)) return child;
|
|
29
|
+
return null;
|
|
30
|
+
};
|
|
31
|
+
const collect = (state, value, pointer, key, out)=>{
|
|
32
|
+
const entry = {
|
|
33
|
+
pointer,
|
|
34
|
+
valueFrom: value.from,
|
|
35
|
+
valueTo: value.to
|
|
36
|
+
};
|
|
37
|
+
if (key) {
|
|
38
|
+
entry.keyFrom = key.from;
|
|
39
|
+
entry.keyTo = key.to;
|
|
40
|
+
}
|
|
41
|
+
out.set(pointer, entry);
|
|
42
|
+
if ('Object' === value.name) {
|
|
43
|
+
for (const property of value.getChildren('Property')){
|
|
44
|
+
const name = property.getChild('PropertyName');
|
|
45
|
+
const member = firstValueChild(property);
|
|
46
|
+
if (!name || !member) continue;
|
|
47
|
+
const segment = escapePointerSegment(readJsonKey(state, name));
|
|
48
|
+
collect(state, member, `${pointer}/${segment}`, name, out);
|
|
49
|
+
}
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
if ('Array' === value.name) {
|
|
53
|
+
let index = 0;
|
|
54
|
+
for(let child = value.firstChild; child; child = child.nextSibling)if (isValueNode(child)) {
|
|
55
|
+
collect(state, child, `${pointer}/${index}`, null, out);
|
|
56
|
+
index++;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
const getJsonPointers = (state, region)=>{
|
|
61
|
+
const out = new Map();
|
|
62
|
+
const jsonText = findJsonText(state, region);
|
|
63
|
+
const root = jsonText ? firstValueChild(jsonText) : null;
|
|
64
|
+
if (root) collect(state, root, '', null, out);
|
|
65
|
+
return out;
|
|
66
|
+
};
|
|
67
|
+
const pointerAt = (state, pos, region)=>{
|
|
68
|
+
let best;
|
|
69
|
+
let bestSize = 1 / 0;
|
|
70
|
+
for (const entry of getJsonPointers(state, region).values()){
|
|
71
|
+
const ranges = [
|
|
72
|
+
[
|
|
73
|
+
entry.valueFrom,
|
|
74
|
+
entry.valueTo
|
|
75
|
+
]
|
|
76
|
+
];
|
|
77
|
+
if (void 0 !== entry.keyFrom && void 0 !== entry.keyTo) ranges.push([
|
|
78
|
+
entry.keyFrom,
|
|
79
|
+
entry.keyTo
|
|
80
|
+
]);
|
|
81
|
+
for (const [from, to] of ranges)if (!(pos < from) && !(pos > to) && !(to - from >= bestSize)) {
|
|
82
|
+
best = entry.pointer;
|
|
83
|
+
bestSize = to - from;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
return best;
|
|
87
|
+
};
|
|
88
|
+
const readPartialJson = (state, value)=>{
|
|
89
|
+
if ('Object' === value.name) {
|
|
90
|
+
const out = {};
|
|
91
|
+
for (const property of value.getChildren('Property')){
|
|
92
|
+
const name = property.getChild('PropertyName');
|
|
93
|
+
const member = firstValueChild(property);
|
|
94
|
+
if (!name || !member) continue;
|
|
95
|
+
const data = readPartialJson(state, member);
|
|
96
|
+
if (void 0 !== data) Object.defineProperty(out, readJsonKey(state, name), {
|
|
97
|
+
value: data,
|
|
98
|
+
enumerable: true,
|
|
99
|
+
writable: true,
|
|
100
|
+
configurable: true
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
return out;
|
|
104
|
+
}
|
|
105
|
+
if ('Array' === value.name) {
|
|
106
|
+
const items = [];
|
|
107
|
+
for(let child = value.firstChild; child; child = child.nextSibling)if (isValueNode(child)) items.push(readPartialJson(state, child));
|
|
108
|
+
return items;
|
|
109
|
+
}
|
|
110
|
+
try {
|
|
111
|
+
return JSON.parse(state.sliceDoc(value.from, value.to));
|
|
112
|
+
} catch {
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
};
|
|
116
|
+
const readRegionData = (state, region)=>{
|
|
117
|
+
try {
|
|
118
|
+
return JSON.parse(state.sliceDoc(region.from, region.to));
|
|
119
|
+
} catch {
|
|
120
|
+
const jsonText = findJsonText(state, region);
|
|
121
|
+
const root = jsonText ? firstValueChild(jsonText) : null;
|
|
122
|
+
return root ? readPartialJson(state, root) : void 0;
|
|
123
|
+
}
|
|
124
|
+
};
|
|
125
|
+
export { ENSURE_TREE_TIMEOUT, VALUE_NODE_NAMES, escapePointerSegment, findJsonText, getJsonPointers, isValueNode, pointerAt, readJsonKey, readPartialJson, readRegionData };
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { LanguageSupport, type LRLanguage } from '@codemirror/language';
|
|
2
|
+
/** lang-yaml's YAML language with its syntax folds switched off (folds come only from the `folds` prop). */
|
|
3
|
+
export declare const yamlEditorLanguage: LRLanguage;
|
|
4
|
+
export declare const yaml: () => LanguageSupport;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { yamlLanguage } from "@codemirror/lang-yaml";
|
|
2
|
+
import { LanguageSupport, foldNodeProp } from "@codemirror/language";
|
|
3
|
+
const yamlEditorLanguage = yamlLanguage.configure({
|
|
4
|
+
props: [
|
|
5
|
+
foldNodeProp.add({
|
|
6
|
+
'FlowMapping FlowSequence Item Pair BlockLiteral': ()=>null
|
|
7
|
+
})
|
|
8
|
+
]
|
|
9
|
+
});
|
|
10
|
+
const yaml = ()=>new LanguageSupport(yamlEditorLanguage);
|
|
11
|
+
export { yaml, yamlEditorLanguage };
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { CSSProperties } from 'react';
|
|
2
|
+
import { type EditorState, type Extension } from '@codemirror/state';
|
|
3
|
+
import { type DecorationSet } from '@codemirror/view';
|
|
4
|
+
import type { LineConfig } from '../../CodeSnippet/CodeSnippetContext';
|
|
5
|
+
export interface LinesConfig {
|
|
6
|
+
lines: Record<number, LineConfig>;
|
|
7
|
+
startingLineNumber: number;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Serialize React `CSSProperties` into an inline style string, following
|
|
11
|
+
* react-dom's rules: camelCase → kebab-case, custom properties kept as-is,
|
|
12
|
+
* numbers get `px` unless the property is unitless (or the value is 0),
|
|
13
|
+
* `null` / `undefined` / booleans / empty strings are skipped.
|
|
14
|
+
*/
|
|
15
|
+
export declare const cssPropertiesToString: (style: CSSProperties | undefined) => string;
|
|
16
|
+
/**
|
|
17
|
+
* `lines` prop → line decorations (background, text colour, text style, className, style)
|
|
18
|
+
* and range marks. Rebuilt on every document change, keyed by absolute line number.
|
|
19
|
+
* `Prec.high` makes these marks the innermost spans so they win over painter token colours.
|
|
20
|
+
*/
|
|
21
|
+
export declare const linesExtension: (config: LinesConfig) => Extension;
|
|
22
|
+
export declare const getLineDecorations: (state: EditorState) => DecorationSet;
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import { Facet, Prec, StateField } from "@codemirror/state";
|
|
2
|
+
import { Decoration, EditorView } from "@codemirror/view";
|
|
3
|
+
import { cn } from "../../../utils/cn.js";
|
|
4
|
+
import { LINE_COLOR_STYLES } from "../../CodeSnippet/lib/lineStyles.js";
|
|
5
|
+
import { getLineTextStyles } from "../../CodeSnippet/lib/lineUtils.js";
|
|
6
|
+
import { lineNumberToDocLine } from "./positions.js";
|
|
7
|
+
const DEFAULT_CONFIG = {
|
|
8
|
+
lines: {},
|
|
9
|
+
startingLineNumber: 1
|
|
10
|
+
};
|
|
11
|
+
const UNITLESS_PROPERTIES = new Set([
|
|
12
|
+
'animationIterationCount',
|
|
13
|
+
'aspectRatio',
|
|
14
|
+
'borderImageOutset',
|
|
15
|
+
'borderImageSlice',
|
|
16
|
+
'borderImageWidth',
|
|
17
|
+
'boxFlex',
|
|
18
|
+
'boxFlexGroup',
|
|
19
|
+
'boxOrdinalGroup',
|
|
20
|
+
'columnCount',
|
|
21
|
+
'columns',
|
|
22
|
+
'flex',
|
|
23
|
+
'flexGrow',
|
|
24
|
+
'flexPositive',
|
|
25
|
+
'flexShrink',
|
|
26
|
+
'flexNegative',
|
|
27
|
+
'flexOrder',
|
|
28
|
+
'gridArea',
|
|
29
|
+
'gridRow',
|
|
30
|
+
'gridRowEnd',
|
|
31
|
+
'gridRowSpan',
|
|
32
|
+
'gridRowStart',
|
|
33
|
+
'gridColumn',
|
|
34
|
+
'gridColumnEnd',
|
|
35
|
+
'gridColumnSpan',
|
|
36
|
+
'gridColumnStart',
|
|
37
|
+
'fontWeight',
|
|
38
|
+
'lineClamp',
|
|
39
|
+
'lineHeight',
|
|
40
|
+
'opacity',
|
|
41
|
+
'order',
|
|
42
|
+
'orphans',
|
|
43
|
+
'scale',
|
|
44
|
+
'tabSize',
|
|
45
|
+
'widows',
|
|
46
|
+
'zIndex',
|
|
47
|
+
'zoom',
|
|
48
|
+
'fillOpacity',
|
|
49
|
+
'floodOpacity',
|
|
50
|
+
'stopOpacity',
|
|
51
|
+
'strokeDasharray',
|
|
52
|
+
'strokeDashoffset',
|
|
53
|
+
'strokeMiterlimit',
|
|
54
|
+
'strokeOpacity',
|
|
55
|
+
'strokeWidth'
|
|
56
|
+
]);
|
|
57
|
+
const VENDOR_PREFIX = /^(Webkit|Moz|ms|O)(?=[A-Z])/;
|
|
58
|
+
const isUnitless = (property)=>{
|
|
59
|
+
const unprefixed = property.replace(VENDOR_PREFIX, '');
|
|
60
|
+
const normalized = unprefixed === property ? property : unprefixed.charAt(0).toLowerCase() + unprefixed.slice(1);
|
|
61
|
+
return UNITLESS_PROPERTIES.has(normalized);
|
|
62
|
+
};
|
|
63
|
+
const toKebabCase = (property)=>{
|
|
64
|
+
if (property.startsWith('--')) return property;
|
|
65
|
+
const kebab = property.replace(/[A-Z]/g, (char)=>`-${char.toLowerCase()}`);
|
|
66
|
+
return kebab.startsWith('ms-') ? `-${kebab}` : kebab;
|
|
67
|
+
};
|
|
68
|
+
const cssPropertiesToString = (style)=>{
|
|
69
|
+
if (!style) return '';
|
|
70
|
+
const declarations = [];
|
|
71
|
+
for (const [property, rawValue] of Object.entries(style)){
|
|
72
|
+
const value = rawValue;
|
|
73
|
+
if (null == value || 'boolean' == typeof value || '' === value) continue;
|
|
74
|
+
const isCustom = property.startsWith('--');
|
|
75
|
+
let serialized;
|
|
76
|
+
serialized = 'number' == typeof value ? 0 === value || isCustom || isUnitless(property) ? String(value) : `${value}px` : String(value).trim();
|
|
77
|
+
declarations.push(`${toKebabCase(property)}: ${serialized}`);
|
|
78
|
+
}
|
|
79
|
+
return declarations.join('; ');
|
|
80
|
+
};
|
|
81
|
+
const linesConfigFacet = Facet.define({
|
|
82
|
+
combine: (values)=>values[values.length - 1] ?? DEFAULT_CONFIG
|
|
83
|
+
});
|
|
84
|
+
const buildLineDecorations = (state)=>{
|
|
85
|
+
const { lines, startingLineNumber } = state.facet(linesConfigFacet);
|
|
86
|
+
const decorations = [];
|
|
87
|
+
for (const [key, config] of Object.entries(lines)){
|
|
88
|
+
const line = lineNumberToDocLine(state.doc, Number(key), startingLineNumber);
|
|
89
|
+
if (!line) continue;
|
|
90
|
+
const { colorClass, textStyleClass, className, style } = getLineTextStyles(config);
|
|
91
|
+
const lineClass = cn(config.color ? LINE_COLOR_STYLES[config.color].bg : void 0, colorClass, textStyleClass, className);
|
|
92
|
+
const styleString = cssPropertiesToString(style);
|
|
93
|
+
if (lineClass || styleString) decorations.push(Decoration.line({
|
|
94
|
+
...lineClass ? {
|
|
95
|
+
class: lineClass
|
|
96
|
+
} : {},
|
|
97
|
+
...styleString ? {
|
|
98
|
+
attributes: {
|
|
99
|
+
style: styleString
|
|
100
|
+
}
|
|
101
|
+
} : {}
|
|
102
|
+
}).range(line.from));
|
|
103
|
+
if (colorClass && line.length > 0) decorations.push(Decoration.mark({
|
|
104
|
+
class: colorClass
|
|
105
|
+
}).range(line.from, line.to));
|
|
106
|
+
for (const range of config.ranges ?? []){
|
|
107
|
+
const resolvedColor = range.color ?? config.color;
|
|
108
|
+
if (!resolvedColor) continue;
|
|
109
|
+
const start = Math.max(range.start, 0);
|
|
110
|
+
const end = Math.min(range.end, line.length);
|
|
111
|
+
if (!(start >= end)) decorations.push(Decoration.mark({
|
|
112
|
+
class: LINE_COLOR_STYLES[resolvedColor].text
|
|
113
|
+
}).range(line.from + start, line.from + end));
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
return Decoration.set(decorations, true);
|
|
117
|
+
};
|
|
118
|
+
const linesField = StateField.define({
|
|
119
|
+
create: buildLineDecorations,
|
|
120
|
+
update: (value, tr)=>{
|
|
121
|
+
if (tr.docChanged || tr.startState.facet(linesConfigFacet) !== tr.state.facet(linesConfigFacet)) return buildLineDecorations(tr.state);
|
|
122
|
+
return value;
|
|
123
|
+
},
|
|
124
|
+
provide: (field)=>EditorView.decorations.from(field)
|
|
125
|
+
});
|
|
126
|
+
const linesExtension = (config)=>[
|
|
127
|
+
linesConfigFacet.of(config),
|
|
128
|
+
Prec.high(linesField)
|
|
129
|
+
];
|
|
130
|
+
const getLineDecorations = (state)=>state.field(linesField, false) ?? Decoration.none;
|
|
131
|
+
export { cssPropertiesToString, getLineDecorations, linesExtension };
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { Diagnostic } from '@codemirror/lint';
|
|
2
|
+
type LuaParser = typeof import('luaparse');
|
|
3
|
+
/** Cached `import('luaparse')` — loaded only for lua documents. A failed load is retried next time. */
|
|
4
|
+
export declare const loadLuaParser: () => Promise<LuaParser>;
|
|
5
|
+
/**
|
|
6
|
+
* Syntax diagnostics for `text` from an already loaded luaparse: at most one (luaparse stops at the
|
|
7
|
+
* first error), at `error.index` covering one character (clamped to the text end). `base` shifts
|
|
8
|
+
* offsets into the document. `luaVersion: '5.3'` is the most permissive grammar (goto, `//`,
|
|
9
|
+
* bitwise operators), which also covers OpenResty/LuaJIT code.
|
|
10
|
+
*/
|
|
11
|
+
export declare const luaDiagnostics: (parser: LuaParser, text: string, base?: number) => Diagnostic[];
|
|
12
|
+
/** Loads luaparse (cached) and returns the syntax diagnostics of `text`. */
|
|
13
|
+
export declare const luaSyntaxDiagnostics: (text: string) => Promise<Diagnostic[]>;
|
|
14
|
+
export {};
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
let pending = null;
|
|
2
|
+
const loadLuaParser = ()=>{
|
|
3
|
+
if (!pending) pending = import("luaparse").catch((error)=>{
|
|
4
|
+
pending = null;
|
|
5
|
+
throw error;
|
|
6
|
+
});
|
|
7
|
+
return pending;
|
|
8
|
+
};
|
|
9
|
+
const isLuaParseError = (value)=>'object' == typeof value && null !== value && 'number' == typeof value.index && 'string' == typeof value.message;
|
|
10
|
+
const stripLocation = (message)=>message.replace(/^\[\d+:\d+\]\s*/, '');
|
|
11
|
+
const luaDiagnostics = (parser, text, base = 0)=>{
|
|
12
|
+
try {
|
|
13
|
+
parser.parse(text, {
|
|
14
|
+
luaVersion: '5.3',
|
|
15
|
+
comments: false,
|
|
16
|
+
locations: false,
|
|
17
|
+
ranges: false,
|
|
18
|
+
scope: false
|
|
19
|
+
});
|
|
20
|
+
return [];
|
|
21
|
+
} catch (error) {
|
|
22
|
+
if (!isLuaParseError(error)) return [];
|
|
23
|
+
const from = Math.min(Math.max(error.index, 0), text.length);
|
|
24
|
+
return [
|
|
25
|
+
{
|
|
26
|
+
from: base + from,
|
|
27
|
+
to: base + Math.min(from + 1, text.length),
|
|
28
|
+
severity: 'error',
|
|
29
|
+
source: 'syntax',
|
|
30
|
+
message: stripLocation(error.message)
|
|
31
|
+
}
|
|
32
|
+
];
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
const luaSyntaxDiagnostics = async (text)=>luaDiagnostics(await loadLuaParser(), text);
|
|
36
|
+
export { loadLuaParser, luaDiagnostics, luaSyntaxDiagnostics };
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { Line, Text } from '@codemirror/state';
|
|
2
|
+
import type { CodeEditorPosition } from '../types';
|
|
3
|
+
/**
|
|
4
|
+
* Converts a document offset into an absolute (startingLineNumber-based) line and 1-based column.
|
|
5
|
+
* Offsets outside the document are clamped to its bounds.
|
|
6
|
+
*/
|
|
7
|
+
export declare const offsetToPosition: (doc: Text, offset: number, startingLineNumber: number) => CodeEditorPosition;
|
|
8
|
+
/**
|
|
9
|
+
* Returns the document line for an absolute (startingLineNumber-based) line number,
|
|
10
|
+
* or `null` when it is outside the document.
|
|
11
|
+
*/
|
|
12
|
+
export declare const lineNumberToDocLine: (doc: Text, absoluteLine: number, startingLineNumber: number) => Line | null;
|
|
13
|
+
/**
|
|
14
|
+
* Converts an absolute line + 1-based column into a document offset.
|
|
15
|
+
* Returns `null` when the line is outside the document; the column is clamped to
|
|
16
|
+
* `[1, line.length + 1]` (column `line.length + 1` is the end of the line). A non-finite
|
|
17
|
+
* column (`NaN`, `±Infinity`) is treated as column 1 so a bad value can never yield `NaN`.
|
|
18
|
+
*/
|
|
19
|
+
export declare const positionToOffset: (doc: Text, position: CodeEditorPosition, startingLineNumber: number) => number | null;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
const clamp = (value, min, max)=>Math.min(Math.max(value, min), max);
|
|
2
|
+
const offsetToPosition = (doc, offset, startingLineNumber)=>{
|
|
3
|
+
const safeOffset = clamp(offset, 0, doc.length);
|
|
4
|
+
const line = doc.lineAt(safeOffset);
|
|
5
|
+
return {
|
|
6
|
+
line: line.number + startingLineNumber - 1,
|
|
7
|
+
column: safeOffset - line.from + 1
|
|
8
|
+
};
|
|
9
|
+
};
|
|
10
|
+
const lineNumberToDocLine = (doc, absoluteLine, startingLineNumber)=>{
|
|
11
|
+
const docLineNumber = absoluteLine - startingLineNumber + 1;
|
|
12
|
+
if (!Number.isInteger(docLineNumber) || docLineNumber < 1 || docLineNumber > doc.lines) return null;
|
|
13
|
+
return doc.line(docLineNumber);
|
|
14
|
+
};
|
|
15
|
+
const positionToOffset = (doc, position, startingLineNumber)=>{
|
|
16
|
+
const line = lineNumberToDocLine(doc, position.line, startingLineNumber);
|
|
17
|
+
if (!line) return null;
|
|
18
|
+
const raw = Number.isFinite(position.column) ? Math.floor(position.column) : 1;
|
|
19
|
+
const column = clamp(raw, 1, line.length + 1);
|
|
20
|
+
return line.from + column - 1;
|
|
21
|
+
};
|
|
22
|
+
export { lineNumberToDocLine, offsetToPosition, positionToOffset };
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { type CompletionSource } from '@codemirror/autocomplete';
|
|
2
|
+
import type { EditorState } from '@codemirror/state';
|
|
3
|
+
import type { JsonSchema } from '../../types';
|
|
4
|
+
/**
|
|
5
|
+
* JSON Schema completions for the JSON region (spec §7.13): missing property names in key
|
|
6
|
+
* position, `enum` / `const` / boolean values in value position. Registered by Task 14.
|
|
7
|
+
* Never rejects: a broken schema or a failed chunk load yields `null`.
|
|
8
|
+
*/
|
|
9
|
+
export declare const schemaCompletionSource: (getSchema: () => JsonSchema | undefined, getRegion: (state: EditorState) => {
|
|
10
|
+
from: number;
|
|
11
|
+
to: number;
|
|
12
|
+
} | null) => CompletionSource;
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
import { insertCompletionText, pickedCompletion } from "@codemirror/autocomplete";
|
|
2
|
+
import { ensureSyntaxTree, syntaxTree } from "@codemirror/language";
|
|
3
|
+
import { ENSURE_TREE_TIMEOUT, escapePointerSegment, getJsonPointers, isValueNode, readJsonKey, readPartialJson, readRegionData } from "../languages/jsonPointers.js";
|
|
4
|
+
import { getCompiledSchema } from "./loadSchema.js";
|
|
5
|
+
const WORD_BEFORE = /[\w$.+-]*$/;
|
|
6
|
+
const WORD_AFTER = /^[\w$.+-]*/;
|
|
7
|
+
const WHITESPACE = /\s/;
|
|
8
|
+
const tokenAt = (state, pos, region)=>{
|
|
9
|
+
const line = state.doc.lineAt(pos);
|
|
10
|
+
const start = Math.max(line.from, region.from);
|
|
11
|
+
const end = Math.min(line.to, region.to);
|
|
12
|
+
const before = state.sliceDoc(start, pos);
|
|
13
|
+
const after = state.sliceDoc(pos, end);
|
|
14
|
+
let stringStart = -1;
|
|
15
|
+
for(let i = 0; i < before.length; i++){
|
|
16
|
+
const ch = before[i];
|
|
17
|
+
if (stringStart < 0) {
|
|
18
|
+
if ('"' === ch) stringStart = i;
|
|
19
|
+
} else if ('\\' === ch) i++;
|
|
20
|
+
else if ('"' === ch) stringStart = -1;
|
|
21
|
+
}
|
|
22
|
+
if (stringStart >= 0) {
|
|
23
|
+
for(let i = 0; i < after.length; i++)if ('\\' === after[i]) i++;
|
|
24
|
+
else if ('"' === after[i]) return {
|
|
25
|
+
from: start + stringStart,
|
|
26
|
+
to: pos + i + 1,
|
|
27
|
+
kind: 'string',
|
|
28
|
+
closed: true
|
|
29
|
+
};
|
|
30
|
+
return {
|
|
31
|
+
from: start + stringStart,
|
|
32
|
+
to: pos,
|
|
33
|
+
kind: 'string',
|
|
34
|
+
closed: false
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
const wordBefore = WORD_BEFORE.exec(before)?.[0] ?? '';
|
|
38
|
+
const wordAfter = WORD_AFTER.exec(after)?.[0] ?? '';
|
|
39
|
+
return {
|
|
40
|
+
from: pos - wordBefore.length,
|
|
41
|
+
to: pos + wordAfter.length,
|
|
42
|
+
kind: 'word',
|
|
43
|
+
closed: false
|
|
44
|
+
};
|
|
45
|
+
};
|
|
46
|
+
const significantChar = (state, pos, region, direction)=>{
|
|
47
|
+
for(let p = direction < 0 ? pos - 1 : pos; p >= region.from && p < region.to; p += direction){
|
|
48
|
+
const ch = state.sliceDoc(p, p + 1);
|
|
49
|
+
if (!WHITESPACE.test(ch)) return {
|
|
50
|
+
ch,
|
|
51
|
+
pos: p
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
return null;
|
|
55
|
+
};
|
|
56
|
+
const ancestor = (node, names)=>{
|
|
57
|
+
for(let current = node; current; current = current.parent){
|
|
58
|
+
if (names.includes(current.name)) return current;
|
|
59
|
+
if ('JsonText' === current.name) break;
|
|
60
|
+
}
|
|
61
|
+
return null;
|
|
62
|
+
};
|
|
63
|
+
const targetAt = (state, token, region)=>{
|
|
64
|
+
const previous = significantChar(state, token.from, region, -1);
|
|
65
|
+
if (!previous) return null;
|
|
66
|
+
const tree = ensureSyntaxTree(state, token.to, ENSURE_TREE_TIMEOUT) ?? syntaxTree(state);
|
|
67
|
+
const punctuation = tree.resolveInner(previous.pos + 1, -1);
|
|
68
|
+
if (':' === previous.ch) {
|
|
69
|
+
const property = ancestor(punctuation, [
|
|
70
|
+
'Property'
|
|
71
|
+
]);
|
|
72
|
+
const name = property?.getChild('PropertyName');
|
|
73
|
+
const container = property?.parent;
|
|
74
|
+
if (!name || !container || 'Object' !== container.name) return null;
|
|
75
|
+
return {
|
|
76
|
+
role: 'value',
|
|
77
|
+
container,
|
|
78
|
+
key: readJsonKey(state, name)
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
if ('{' !== previous.ch && '[' !== previous.ch && ',' !== previous.ch) return null;
|
|
82
|
+
const container = ancestor(punctuation, [
|
|
83
|
+
'Object',
|
|
84
|
+
'Array'
|
|
85
|
+
]);
|
|
86
|
+
if (!container) return null;
|
|
87
|
+
if ('Object' === container.name) return '[' === previous.ch ? null : {
|
|
88
|
+
role: 'key',
|
|
89
|
+
container
|
|
90
|
+
};
|
|
91
|
+
if ('{' === previous.ch) return null;
|
|
92
|
+
let index = 0;
|
|
93
|
+
for(let child = container.firstChild; child; child = child.nextSibling)if (isValueNode(child) && child.to <= token.from) index++;
|
|
94
|
+
return {
|
|
95
|
+
role: 'value',
|
|
96
|
+
container,
|
|
97
|
+
key: index
|
|
98
|
+
};
|
|
99
|
+
};
|
|
100
|
+
const applyText = (text, tail)=>0 === tail ? text : (view, completion, from, to)=>{
|
|
101
|
+
view.dispatch({
|
|
102
|
+
...insertCompletionText(view.state, text, from, to + tail),
|
|
103
|
+
annotations: pickedCompletion.of(completion)
|
|
104
|
+
});
|
|
105
|
+
};
|
|
106
|
+
const typeLabel = (schema)=>{
|
|
107
|
+
const type = schema.type;
|
|
108
|
+
if ('string' == typeof type) return type;
|
|
109
|
+
if (Array.isArray(type)) return type.filter((item)=>'string' == typeof item).join(' | ');
|
|
110
|
+
if (Array.isArray(schema.enum)) return 'enum';
|
|
111
|
+
if ('const' in schema) return 'const';
|
|
112
|
+
return '';
|
|
113
|
+
};
|
|
114
|
+
const hasType = (schema, name)=>{
|
|
115
|
+
const type = schema.type;
|
|
116
|
+
return type === name || Array.isArray(type) && type.includes(name);
|
|
117
|
+
};
|
|
118
|
+
const stringOrUndefined = (value)=>'string' == typeof value && '' !== value ? value : void 0;
|
|
119
|
+
const presentKeys = (state, object, editing)=>{
|
|
120
|
+
const keys = new Set();
|
|
121
|
+
for (const property of object.getChildren('Property')){
|
|
122
|
+
const name = property.getChild('PropertyName');
|
|
123
|
+
if (name && name.from !== editing.from) keys.add(readJsonKey(state, name));
|
|
124
|
+
}
|
|
125
|
+
return keys;
|
|
126
|
+
};
|
|
127
|
+
const propertyOptions = (node, present, render)=>{
|
|
128
|
+
const required = new Set(node.required ?? []);
|
|
129
|
+
const options = [];
|
|
130
|
+
for (const [name, child] of Object.entries(node.properties ?? {})){
|
|
131
|
+
if (present.has(name)) continue;
|
|
132
|
+
const schema = (node.getNodeChild(name).node ?? child).schema;
|
|
133
|
+
const type = typeLabel(schema);
|
|
134
|
+
const isRequired = required.has(name);
|
|
135
|
+
options.push({
|
|
136
|
+
label: name,
|
|
137
|
+
apply: render(name),
|
|
138
|
+
type: 'property',
|
|
139
|
+
detail: isRequired ? type ? `${type} (required)` : 'required' : type || void 0,
|
|
140
|
+
info: stringOrUndefined(schema.description),
|
|
141
|
+
boost: isRequired ? 1 : 0
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
return options;
|
|
145
|
+
};
|
|
146
|
+
const valueOptions = (node, tail)=>{
|
|
147
|
+
const { schema } = node;
|
|
148
|
+
const values = [];
|
|
149
|
+
if (Array.isArray(schema.enum)) values.push(...schema.enum);
|
|
150
|
+
if ('const' in schema) values.push(schema.const);
|
|
151
|
+
if (hasType(schema, 'boolean')) values.push(true, false);
|
|
152
|
+
const seen = new Set();
|
|
153
|
+
const options = [];
|
|
154
|
+
for (const value of values){
|
|
155
|
+
const text = JSON.stringify(value);
|
|
156
|
+
if (!(void 0 === text || seen.has(text))) {
|
|
157
|
+
seen.add(text);
|
|
158
|
+
options.push({
|
|
159
|
+
label: text,
|
|
160
|
+
apply: applyText(text, tail),
|
|
161
|
+
type: 'value',
|
|
162
|
+
info: stringOrUndefined(schema.description)
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
return options;
|
|
167
|
+
};
|
|
168
|
+
const completeAt = async (context, getRegion, schema)=>{
|
|
169
|
+
const { state, pos } = context;
|
|
170
|
+
const region = getRegion(state);
|
|
171
|
+
if (!region || pos < region.from || pos > region.to) return null;
|
|
172
|
+
const token = tokenAt(state, pos, region);
|
|
173
|
+
if ('word' === token.kind && token.from === pos && token.to === pos && !context.explicit) return null;
|
|
174
|
+
const target = targetAt(state, token, region);
|
|
175
|
+
if (!target) return null;
|
|
176
|
+
const containerEntry = [
|
|
177
|
+
...getJsonPointers(state, region).values()
|
|
178
|
+
].find((entry)=>entry.valueFrom === target.container.from);
|
|
179
|
+
if (!containerEntry) return null;
|
|
180
|
+
const pointer = 'key' === target.role ? containerEntry.pointer : `${containerEntry.pointer}/${escapePointerSegment(String(target.key))}`;
|
|
181
|
+
const root = await getCompiledSchema(schema);
|
|
182
|
+
const { node } = root.getNode(pointer, readRegionData(state, region));
|
|
183
|
+
if (!node) return null;
|
|
184
|
+
const tail = 'string' !== token.kind || token.closed ? token.to - pos : 0;
|
|
185
|
+
if ('value' === target.role) {
|
|
186
|
+
const options = valueOptions(node, tail);
|
|
187
|
+
return options.length > 0 ? {
|
|
188
|
+
from: token.from,
|
|
189
|
+
options
|
|
190
|
+
} : null;
|
|
191
|
+
}
|
|
192
|
+
const reduced = node.reduceNode(readPartialJson(state, target.container)).node ?? node;
|
|
193
|
+
const next = significantChar(state, token.to, region, 1);
|
|
194
|
+
const suffix = next?.ch === ':' ? '' : ': ';
|
|
195
|
+
const inString = 'string' === token.kind;
|
|
196
|
+
const options = propertyOptions(reduced, presentKeys(state, target.container, token), (name)=>{
|
|
197
|
+
const quoted = JSON.stringify(name);
|
|
198
|
+
return applyText(inString ? `${quoted.slice(1)}${suffix}` : `${quoted}${suffix}`, tail);
|
|
199
|
+
});
|
|
200
|
+
if (0 === options.length) return null;
|
|
201
|
+
return {
|
|
202
|
+
from: inString ? token.from + 1 : token.from,
|
|
203
|
+
options
|
|
204
|
+
};
|
|
205
|
+
};
|
|
206
|
+
const schemaCompletionSource = (getSchema, getRegion)=>(context)=>{
|
|
207
|
+
const schema = getSchema();
|
|
208
|
+
if (void 0 === schema) return Promise.resolve(null);
|
|
209
|
+
return completeAt(context, getRegion, schema).catch(()=>null);
|
|
210
|
+
};
|
|
211
|
+
export { schemaCompletionSource };
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { EditorState, Extension } from '@codemirror/state';
|
|
2
|
+
import { type HoverTooltipSource } from '@codemirror/view';
|
|
3
|
+
import type { JsonSchema } from '../../types';
|
|
4
|
+
/** Hover source: `title` / `description` of the schema for the property under the pointer. */
|
|
5
|
+
export declare const schemaHoverSource: (getSchema: () => JsonSchema | undefined, getRegion: (state: EditorState) => {
|
|
6
|
+
from: number;
|
|
7
|
+
to: number;
|
|
8
|
+
} | null) => HoverTooltipSource;
|
|
9
|
+
export declare const schemaHover: (getSchema: () => JsonSchema | undefined, getRegion: (state: EditorState) => {
|
|
10
|
+
from: number;
|
|
11
|
+
to: number;
|
|
12
|
+
} | null) => Extension;
|