@deckbuilderkit/editor 0.0.0-stage → 0.1.1

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.
Files changed (56) hide show
  1. package/package.json +53 -4
  2. package/src/ArchiveView.tsx +111 -0
  3. package/src/CodeEditor.tsx +265 -0
  4. package/src/CodeView.tsx +67 -0
  5. package/src/DiffView.tsx +139 -0
  6. package/src/EnvView.tsx +42 -0
  7. package/src/FilePreview.tsx +108 -0
  8. package/src/FontView.tsx +48 -0
  9. package/src/HarView.tsx +70 -0
  10. package/src/HexView.tsx +28 -0
  11. package/src/HtmlPreview.tsx +23 -0
  12. package/src/MarkdownEditor.tsx +90 -0
  13. package/src/MarkdownToc.tsx +135 -0
  14. package/src/MarkdownView.tsx +84 -0
  15. package/src/MediaView.tsx +17 -0
  16. package/src/NotebookView.tsx +74 -0
  17. package/src/PdfView.tsx +289 -0
  18. package/src/RequestEditor.tsx +419 -0
  19. package/src/ResponseView.tsx +89 -0
  20. package/src/SqlGraph.tsx +322 -0
  21. package/src/SqlSteps.tsx +487 -0
  22. package/src/SqliteView.tsx +68 -0
  23. package/src/SvgView.tsx +231 -0
  24. package/src/TableView.tsx +85 -0
  25. package/src/VarSuggest.tsx +125 -0
  26. package/src/curl-file.ts +70 -0
  27. package/src/editorOptions.ts +83 -0
  28. package/src/editorOptionsContext.ts +4 -0
  29. package/src/fileKinds.ts +69 -0
  30. package/src/http-client.ts +9 -0
  31. package/src/http-import.ts +594 -0
  32. package/src/http.ts +251 -0
  33. package/src/import-warning.ts +8 -0
  34. package/src/index.ts +56 -0
  35. package/src/languages.ts +107 -0
  36. package/src/markdown.ts +123 -0
  37. package/src/markdownLive.ts +289 -0
  38. package/src/options.ts +3 -0
  39. package/src/pdf-url.d.ts +4 -0
  40. package/src/sql/format.ts +637 -0
  41. package/src/sql/formatOptions.ts +71 -0
  42. package/src/sql/lineage.ts +365 -0
  43. package/src/sql/lint.ts +951 -0
  44. package/src/sql/markerHighlight.ts +50 -0
  45. package/src/sql/markers.ts +138 -0
  46. package/src/sql/outline.ts +662 -0
  47. package/src/sql/statements.ts +83 -0
  48. package/src/sql/tokens.ts +156 -0
  49. package/src/sql/xmlQuery.ts +249 -0
  50. package/src/sqlProblems.ts +40 -0
  51. package/src/table.ts +89 -0
  52. package/src/text-save.ts +28 -0
  53. package/src/theme.ts +86 -0
  54. package/src/toc.ts +11 -0
  55. package/src/variables.ts +123 -0
  56. package/README.md +0 -3
@@ -0,0 +1,50 @@
1
+ import type { Extension } from '@codemirror/state';
2
+ import { Decoration, type DecorationSet, EditorView, ViewPlugin, type ViewUpdate } from '@codemirror/view';
3
+ import { sqlMarkers } from './markers';
4
+
5
+ const markerMark = Decoration.mark({ class: 'cm-sql-marker' });
6
+ const fragmentMark = Decoration.mark({ class: 'cm-sql-fragment' });
7
+ const paramMark = Decoration.mark({ class: 'cm-sql-param' });
8
+ const PARAM = /\{\d+\}/g;
9
+
10
+ /** Decoration ranges (sorted by `from`) for markers, guarded fragments and `{n}` placeholders. */
11
+ export function sqlMarkerRanges(text: string): Array<{ from: number; to: number; kind: 'marker' | 'fragment' | 'param' }> {
12
+ const out: Array<{ from: number; to: number; kind: 'marker' | 'fragment' | 'param' }> = [];
13
+ for (const m of sqlMarkers(text)) {
14
+ out.push({ from: m.from, to: m.to, kind: 'marker' });
15
+ if (m.fragmentTo > m.fragmentFrom) out.push({ from: m.fragmentFrom, to: m.fragmentTo, kind: 'fragment' });
16
+ }
17
+ for (const p of text.matchAll(PARAM)) out.push({ from: p.index!, to: p.index! + p[0].length, kind: 'param' });
18
+ const order = { marker: 0, fragment: 1, param: 2 };
19
+ return out.sort((a, b) => a.from - b.from || order[a.kind] - order[b.kind]);
20
+ }
21
+
22
+ function build(view: EditorView): DecorationSet {
23
+ const deco = { marker: markerMark, fragment: fragmentMark, param: paramMark };
24
+ return Decoration.set(
25
+ sqlMarkerRanges(view.state.doc.toString()).map((r) => deco[r.kind].range(r.from, r.to)),
26
+ true,
27
+ );
28
+ }
29
+
30
+ const plugin = ViewPlugin.fromClass(
31
+ class {
32
+ decorations: DecorationSet;
33
+ constructor(view: EditorView) {
34
+ this.decorations = build(view);
35
+ }
36
+ update(u: ViewUpdate) {
37
+ if (u.docChanged) this.decorations = build(u.view);
38
+ }
39
+ },
40
+ { decorations: (v) => v.decorations },
41
+ );
42
+
43
+ const theme = EditorView.baseTheme({
44
+ '.cm-sql-marker': { color: 'var(--info)', fontStyle: 'italic' },
45
+ '.cm-sql-param': { color: 'var(--accent)', fontWeight: '600' },
46
+ '.cm-sql-fragment': { backgroundColor: 'color-mix(in srgb, var(--accent) 9%, transparent)' },
47
+ });
48
+
49
+ /** Styles `/*%JS=...%*\/` markers, their guarded fragment and `{n}` placeholders. */
50
+ export const sqlMarkerHighlight: Extension = [plugin, theme];
@@ -0,0 +1,138 @@
1
+ export interface SqlMarker {
2
+ from: number;
3
+ to: number;
4
+ condition: string;
5
+ params: number[];
6
+ fragmentFrom: number;
7
+ fragmentTo: number;
8
+ fragmentParams: number[];
9
+ }
10
+
11
+ export interface SqlParam {
12
+ n: number;
13
+ count: number;
14
+ /** Uses inside a marker's guarded fragment. */
15
+ markers: number;
16
+ /** Uses outside any marker comment or fragment. */
17
+ unguarded: number;
18
+ firstFrom: number;
19
+ /** Every `{n}` occurrence in document order. */
20
+ uses: SqlParamUse[];
21
+ }
22
+
23
+ export interface SqlParamUse {
24
+ from: number;
25
+ to: number;
26
+ /** `condition` inside a marker comment, `guarded` in a marker's fragment, else `unguarded`. */
27
+ kind: 'condition' | 'guarded' | 'unguarded';
28
+ }
29
+
30
+ const MARKER = /\/\*%JS=([\s\S]*?)%\*\//g;
31
+ const PARAM = /\{(\d+)\}/g;
32
+
33
+ function paramsIn(s: string): number[] {
34
+ const out = new Set<number>();
35
+ for (const m of s.matchAll(PARAM)) out.add(Number(m[1]));
36
+ return [...out].sort((a, b) => a - b);
37
+ }
38
+
39
+ /** Finds `/*%JS=...%*\/` conditional markers and the rest-of-line fragment each guards. Never throws. */
40
+ export function sqlMarkers(text: string): SqlMarker[] {
41
+ const out: SqlMarker[] = [];
42
+ try {
43
+ for (const m of text.matchAll(MARKER)) {
44
+ const from = m.index!;
45
+ const to = from + m[0].length;
46
+ let end = text.indexOf('\n', to);
47
+ if (end < 0) end = text.length;
48
+ if (end > to && text[end - 1] === '\r') end--;
49
+ const condition = m[1]!.trim();
50
+ out.push({
51
+ from,
52
+ to,
53
+ condition,
54
+ params: paramsIn(condition),
55
+ fragmentFrom: to,
56
+ fragmentTo: end,
57
+ fragmentParams: paramsIn(text.slice(to, end)),
58
+ });
59
+ }
60
+ } catch {
61
+ return [];
62
+ }
63
+ return out;
64
+ }
65
+
66
+ /** Summarises every `{n}` placeholder: total, guarded and unguarded uses. Never throws. */
67
+ export function sqlParams(text: string): SqlParam[] {
68
+ const map = new Map<number, SqlParam>();
69
+ try {
70
+ const markers = sqlMarkers(text);
71
+ for (const m of text.matchAll(PARAM)) {
72
+ const at = m.index!;
73
+ const n = Number(m[1]);
74
+ let p = map.get(n);
75
+ if (!p) {
76
+ p = { n, count: 0, markers: 0, unguarded: 0, firstFrom: at, uses: [] };
77
+ map.set(n, p);
78
+ }
79
+ p.count++;
80
+ let kind: SqlParamUse['kind'] = 'unguarded';
81
+ if (markers.some((k) => at >= k.fragmentFrom && at < k.fragmentTo)) {
82
+ p.markers++;
83
+ kind = 'guarded';
84
+ } else if (markers.some((k) => at >= k.from && at < k.to)) kind = 'condition';
85
+ else p.unguarded++;
86
+ p.uses.push({ from: at, to: at + m[0].length, kind });
87
+ }
88
+ } catch {
89
+ return [];
90
+ }
91
+ return [...map.values()].sort((a, b) => a.n - b.n);
92
+ }
93
+
94
+ function escapeJs(v: string): string {
95
+ return v.replace(/\\/g, '\\\\').replace(/'/g, "\\'").replace(/"/g, '\\"').replace(/\r/g, '\\r').replace(/\n/g, '\\n');
96
+ }
97
+
98
+ const SIMPLE = /^result\s*=\s*\(?\s*'((?:[^'\\]|\\.)*)'\s*(===?|!==?)\s*'((?:[^'\\]|\\.)*)'\s*\)?\s*;?$/;
99
+ const unescapeJs = (s: string) => s.replace(/\\(.)/g, (_, c: string) => (c === 'n' ? '\n' : c === 'r' ? '\r' : c));
100
+
101
+ function evalCondition(condition: string, values: Record<number, string>): boolean {
102
+ const code = condition.replace(/(['"]?)\{(\d+)\}(['"]?)/g, (_, a: string, n: string, b: string) => {
103
+ const v = escapeJs(values[Number(n)] ?? '');
104
+ return a ? `${a}${v}${b}` : `'${v}'${b}`;
105
+ });
106
+ const simple = SIMPLE.exec(code.trim());
107
+ if (simple) {
108
+ const equal = unescapeJs(simple[1]!) === unescapeJs(simple[3]!);
109
+ return simple[2]!.startsWith('=') ? equal : !equal;
110
+ }
111
+ return !!new Function('result', `${code}\n;return result;`)(undefined);
112
+ }
113
+
114
+ /** Applies the `{n}` values: evaluates each marker, drops its comment (and its fragment when false), then substitutes the remaining `{n}` raw. Keeps the line count. */
115
+ export function resolveMarkers(text: string, values: Record<number, string>): { sql: string; error?: string } {
116
+ const markers = sqlMarkers(text);
117
+ let out = '';
118
+ let at = 0;
119
+ for (const m of markers) {
120
+ let keep: boolean;
121
+ try {
122
+ keep = evalCondition(m.condition, values);
123
+ } catch (e) {
124
+ return { sql: text, error: `${m.condition}: ${e instanceof Error ? e.message : String(e)}` };
125
+ }
126
+ const dropped = text.slice(m.from, m.to);
127
+ out += fill(text.slice(at, m.from), values) + dropped.replace(/[^\r\n]/g, '');
128
+ const fragment = text.slice(m.fragmentFrom, m.fragmentTo);
129
+ out += keep ? fill(fragment, values) : fragment.replace(/[^\r\n]/g, '');
130
+ at = m.fragmentTo;
131
+ }
132
+ out += fill(text.slice(at), values);
133
+ return { sql: out };
134
+ }
135
+
136
+ function fill(s: string, values: Record<number, string>): string {
137
+ return s.replace(PARAM, (_, n: string) => values[Number(n)] ?? '');
138
+ }