Skip to content

Commit 7ef0e6e

Browse files
committed
fix: colors for new documents.
1 parent 16e248b commit 7ef0e6e

2 files changed

Lines changed: 77 additions & 23 deletions

File tree

  • src/routes/(console)/project-[region]-[project]/databases/database-[database]/collection-[collection]/(components)/editor

src/routes/(console)/project-[region]-[project]/databases/database-[database]/collection-[collection]/(components)/editor/extensions/highlighting.ts

Lines changed: 76 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,65 @@ import { Range, RangeSet, RangeSetBuilder, StateField, type Extension } from '@c
1111
import { forEachDiagnostic, setDiagnosticsEffect } from '@codemirror/lint';
1212
import { NESTED_KEY_REGEX } from '../helpers/constants';
1313

14+
const SYSTEM_FIELDS = ['$id', '$createdAt', '$updatedAt'] as const;
15+
16+
function escapeRegExp(source: string): string {
17+
return source.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
18+
}
19+
20+
// Match a system field key at the start of a line, either quoted ("$id") or unquoted ($id).
21+
const SYSTEM_FIELDS_SOURCE = SYSTEM_FIELDS.map(escapeRegExp).join('|');
22+
const SYSTEM_FIELD_KEY_LINE_PATTERN = new RegExp(
23+
`^\\s*("(?:${SYSTEM_FIELDS_SOURCE})"|(?:${SYSTEM_FIELDS_SOURCE}))\\s*:`
24+
);
25+
26+
function skipInlineWhitespace(text: string, from: number): number {
27+
let i = from;
28+
while (i < text.length) {
29+
const ch = text[i];
30+
// Don't cross lines; values for the system fields we style are expected to be on the same line.
31+
if (ch !== ' ' && ch !== '\t') break;
32+
i += 1;
33+
}
34+
return i;
35+
}
36+
37+
function findSingleLineValueEnd(text: string, from: number): number {
38+
if (from >= text.length) return from;
39+
40+
const quote = text[from];
41+
if (quote === '"' || quote === "'") {
42+
let escaped = false;
43+
for (let i = from + 1; i < text.length; i += 1) {
44+
const ch = text[i];
45+
if (escaped) {
46+
escaped = false;
47+
continue;
48+
}
49+
if (ch === '\\') {
50+
escaped = true;
51+
continue;
52+
}
53+
if (ch === quote) {
54+
return i + 1;
55+
}
56+
if (ch === '\n' || ch === '\r') {
57+
return i;
58+
}
59+
}
60+
return text.length;
61+
}
62+
63+
// Scalar token: read until comma or newline.
64+
for (let i = from; i < text.length; i += 1) {
65+
const ch = text[i];
66+
if (ch === ',' || ch === '\n' || ch === '\r') {
67+
return i;
68+
}
69+
}
70+
return text.length;
71+
}
72+
1473
// ViewPlugin to highlight nested keys (4+ spaces) only in visible ranges
1574
export function createNestedKeyPlugin(): Extension {
1675
return ViewPlugin.fromClass(
@@ -56,6 +115,8 @@ export function createNestedKeyPlugin(): Extension {
56115

57116
// ViewPlugin to apply muted styling to system fields ($id, $createdAt, $updatedAt)
58117
export function createSystemFieldStylePlugin(getShouldStyle: () => boolean): Extension {
118+
const mutedMark = Decoration.mark({ class: 'cm-system-field-muted' });
119+
59120
return ViewPlugin.fromClass(
60121
class {
61122
decorations: DecorationSet;
@@ -77,32 +138,24 @@ export function createSystemFieldStylePlugin(getShouldStyle: () => boolean): Ext
77138

78139
const doc = view.state.doc;
79140
const text = doc.toString();
80-
const systemFields = ['$id', '$createdAt', '$updatedAt'];
81141
const decos: Range<Decoration>[] = [];
82142

83-
// Find all occurrences of system field keys
84-
for (const field of systemFields) {
85-
// Match the key in format: "$id": or $id: (with or without quotes)
86-
const quotedPattern = new RegExp(`"${field.replace('$', '\\$')}"\\s*:`, 'g');
87-
const unquotedPattern = new RegExp(`${field.replace('$', '\\$')}\\s*:`, 'g');
88-
89-
let match: RegExpExecArray;
90-
// Check quoted format
91-
while ((match = quotedPattern.exec(text)) !== null) {
92-
const from = match.index;
93-
const to = from + field.length + 2; // +2 for quotes
94-
decos.push(
95-
Decoration.mark({ class: 'cm-system-field-muted' }).range(from, to)
96-
);
97-
}
143+
for (let ln = 1; ln <= doc.lines; ln += 1) {
144+
const line = doc.line(ln);
145+
const match = SYSTEM_FIELD_KEY_LINE_PATTERN.exec(line.text);
146+
if (!match) continue;
147+
148+
const keyToken = match[1]; // either "$id" or $id
149+
const keyOffset = match[0].indexOf(keyToken);
150+
const from = line.from + keyOffset;
151+
const to = line.from + match[0].length;
152+
153+
decos.push(mutedMark.range(from, to));
98154

99-
// Check unquoted format
100-
while ((match = unquotedPattern.exec(text)) !== null) {
101-
const from = match.index;
102-
const to = from + field.length;
103-
decos.push(
104-
Decoration.mark({ class: 'cm-system-field-muted' }).range(from, to)
105-
);
155+
const valueFrom = skipInlineWhitespace(text, to);
156+
const valueTo = findSingleLineValueEnd(text, valueFrom);
157+
if (valueTo > valueFrom) {
158+
decos.push(mutedMark.range(valueFrom, valueTo));
106159
}
107160
}
108161

src/routes/(console)/project-[region]-[project]/databases/database-[database]/collection-[collection]/(components)/editor/view.svelte

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1578,6 +1578,7 @@
15781578
// System fields muted styling (when suggestions are showing)
15791579
// Must come after .cm-propertyName to override
15801580
:global(.cm-system-field-muted),
1581+
:global(.cm-system-field-muted *),
15811582
:global(.cm-system-field-muted.cm-propertyName),
15821583
:global(.cm-system-field-muted .cm-propertyName) {
15831584
color: var(--fgcolor-neutral-tertiary, #97979b) !important;

0 commit comments

Comments
 (0)