@marimo-team/islands 0.19.3-dev10 → 0.19.3-dev11
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/main.js
CHANGED
|
@@ -101068,7 +101068,7 @@ Defaulting to \`null\`.`;
|
|
|
101068
101068
|
return Logger.warn("Failed to get version from mount config"), null;
|
|
101069
101069
|
}
|
|
101070
101070
|
}
|
|
101071
|
-
const marimoVersionAtom = atom(getVersionFromMountConfig() || "0.19.3-
|
|
101071
|
+
const marimoVersionAtom = atom(getVersionFromMountConfig() || "0.19.3-dev11"), showCodeInRunModeAtom = atom(true);
|
|
101072
101072
|
atom(null);
|
|
101073
101073
|
var VIRTUAL_FILE_REGEX = /\/@file\/([^\s"&'/]+)\.([\dA-Za-z]+)/g, VirtualFileTracker = class e {
|
|
101074
101074
|
constructor() {
|
package/package.json
CHANGED
|
@@ -64,6 +64,7 @@ import { getCurrentLanguageAdapter } from "./language/commands";
|
|
|
64
64
|
import { adaptiveLanguageConfiguration } from "./language/extension";
|
|
65
65
|
import { dndBundle } from "./misc/dnd";
|
|
66
66
|
import { pasteBundle } from "./misc/paste";
|
|
67
|
+
import { stringsAutoCloseBraces } from "./misc/string-braces";
|
|
67
68
|
import { reactiveReferencesBundle } from "./reactive-references/extension";
|
|
68
69
|
import { darkTheme } from "./theme/dark";
|
|
69
70
|
import { lightTheme } from "./theme/light";
|
|
@@ -203,6 +204,7 @@ export const basicBundle = (opts: CodeMirrorSetupOpts): Extension[] => {
|
|
|
203
204
|
hintTooltip(lspConfig),
|
|
204
205
|
copilotBundle(completionConfig),
|
|
205
206
|
foldGutter(),
|
|
207
|
+
stringsAutoCloseBraces(),
|
|
206
208
|
closeBrackets(),
|
|
207
209
|
completionKeymap(),
|
|
208
210
|
// to avoid clash with charDeleteBackward keymap
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
|
+
|
|
3
|
+
import { python } from "@codemirror/lang-python";
|
|
4
|
+
import { EditorState } from "@codemirror/state";
|
|
5
|
+
import { EditorView } from "@codemirror/view";
|
|
6
|
+
import { afterEach, describe, expect, it } from "vitest";
|
|
7
|
+
import { stringBraceInputHandler } from "../string-braces";
|
|
8
|
+
|
|
9
|
+
function createEditor(
|
|
10
|
+
initialContent: string,
|
|
11
|
+
cursorPosition: number,
|
|
12
|
+
): EditorView {
|
|
13
|
+
const state = EditorState.create({
|
|
14
|
+
doc: initialContent,
|
|
15
|
+
selection: { anchor: cursorPosition },
|
|
16
|
+
extensions: [python()],
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
const view = new EditorView({
|
|
20
|
+
state,
|
|
21
|
+
parent: document.body,
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
return view;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
describe("string brace auto-closing", () => {
|
|
28
|
+
let view: EditorView;
|
|
29
|
+
|
|
30
|
+
afterEach(() => {
|
|
31
|
+
if (view) {
|
|
32
|
+
view.destroy();
|
|
33
|
+
if (document.body.contains(view.dom)) {
|
|
34
|
+
view.dom.remove();
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it("should auto-close braces in f-strings", () => {
|
|
40
|
+
view = createEditor('f"hello ', 8);
|
|
41
|
+
const result = stringBraceInputHandler(view, 8, 8, "{");
|
|
42
|
+
|
|
43
|
+
expect(result).toBe(true);
|
|
44
|
+
expect(view.state.doc.toString()).toBe('f"hello {}');
|
|
45
|
+
expect(view.state.selection.main.head).toBe(9);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
it("should auto-close braces in regular double-quoted strings", () => {
|
|
49
|
+
view = createEditor('"hello ', 7);
|
|
50
|
+
const result = stringBraceInputHandler(view, 7, 7, "{");
|
|
51
|
+
|
|
52
|
+
expect(result).toBe(true);
|
|
53
|
+
expect(view.state.doc.toString()).toBe('"hello {}');
|
|
54
|
+
expect(view.state.selection.main.head).toBe(8);
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it("should auto-close braces in rf-strings", () => {
|
|
58
|
+
view = createEditor('rf"hello ', 9);
|
|
59
|
+
const result = stringBraceInputHandler(view, 9, 9, "{");
|
|
60
|
+
|
|
61
|
+
expect(result).toBe(true);
|
|
62
|
+
expect(view.state.doc.toString()).toBe('rf"hello {}');
|
|
63
|
+
expect(view.state.selection.main.head).toBe(10);
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
it("should auto-close braces in fr-strings", () => {
|
|
67
|
+
view = createEditor('fr"hello ', 9);
|
|
68
|
+
const result = stringBraceInputHandler(view, 9, 9, "{");
|
|
69
|
+
|
|
70
|
+
expect(result).toBe(true);
|
|
71
|
+
expect(view.state.doc.toString()).toBe('fr"hello {}');
|
|
72
|
+
expect(view.state.selection.main.head).toBe(10);
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
it("should auto-close braces in single-quoted strings", () => {
|
|
76
|
+
view = createEditor("'hello ", 7);
|
|
77
|
+
const result = stringBraceInputHandler(view, 7, 7, "{");
|
|
78
|
+
|
|
79
|
+
expect(result).toBe(true);
|
|
80
|
+
expect(view.state.doc.toString()).toBe("'hello {}");
|
|
81
|
+
expect(view.state.selection.main.head).toBe(8);
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
it("should auto-close braces in uppercase F-strings", () => {
|
|
85
|
+
view = createEditor('F"hello ', 8);
|
|
86
|
+
const result = stringBraceInputHandler(view, 8, 8, "{");
|
|
87
|
+
|
|
88
|
+
expect(result).toBe(true);
|
|
89
|
+
expect(view.state.doc.toString()).toBe('F"hello {}');
|
|
90
|
+
expect(view.state.selection.main.head).toBe(9);
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
it("should auto-close braces in raw strings without f/t", () => {
|
|
94
|
+
view = createEditor('r"hello ', 8);
|
|
95
|
+
const result = stringBraceInputHandler(view, 8, 8, "{");
|
|
96
|
+
|
|
97
|
+
expect(result).toBe(true);
|
|
98
|
+
expect(view.state.doc.toString()).toBe('r"hello {}');
|
|
99
|
+
expect(view.state.selection.main.head).toBe(9);
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
// Handled by other CodeMirror handler(s)
|
|
103
|
+
it("should NOT auto-close braces outside strings", () => {
|
|
104
|
+
view = createEditor("x = ", 4);
|
|
105
|
+
const result = stringBraceInputHandler(view, 4, 4, "{");
|
|
106
|
+
|
|
107
|
+
expect(result).toBe(false);
|
|
108
|
+
expect(view.state.doc.toString()).toBe("x = ");
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
it("should NOT auto-close braces when typing other characters", () => {
|
|
112
|
+
view = createEditor('f"hello ', 8);
|
|
113
|
+
const result = stringBraceInputHandler(view, 8, 8, "a");
|
|
114
|
+
|
|
115
|
+
expect(result).toBe(false);
|
|
116
|
+
expect(view.state.doc.toString()).toBe('f"hello ');
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
it("should handle braces at the start of string", () => {
|
|
120
|
+
view = createEditor('f"', 2);
|
|
121
|
+
const result = stringBraceInputHandler(view, 2, 2, "{");
|
|
122
|
+
|
|
123
|
+
expect(result).toBe(true);
|
|
124
|
+
expect(view.state.doc.toString()).toBe('f"{}');
|
|
125
|
+
expect(view.state.selection.main.head).toBe(3);
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
it("should handle braces in the middle of string content", () => {
|
|
129
|
+
view = createEditor('f"hello world ', 14);
|
|
130
|
+
const result = stringBraceInputHandler(view, 14, 14, "{");
|
|
131
|
+
|
|
132
|
+
expect(result).toBe(true);
|
|
133
|
+
expect(view.state.doc.toString()).toBe('f"hello world {}');
|
|
134
|
+
expect(view.state.selection.main.head).toBe(15);
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
it("should handle multiple braces in string", () => {
|
|
138
|
+
view = createEditor('f"hello {} world', 16);
|
|
139
|
+
const result = stringBraceInputHandler(view, 16, 16, "{");
|
|
140
|
+
|
|
141
|
+
expect(result).toBe(true);
|
|
142
|
+
expect(view.state.doc.toString()).toBe('f"hello {} world{}');
|
|
143
|
+
expect(view.state.selection.main.head).toBe(17);
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
it("should handle empty string", () => {
|
|
147
|
+
view = createEditor('f""', 2);
|
|
148
|
+
const result = stringBraceInputHandler(view, 2, 2, "{");
|
|
149
|
+
|
|
150
|
+
expect(result).toBe(true);
|
|
151
|
+
expect(view.state.doc.toString()).toBe('f"{}"');
|
|
152
|
+
expect(view.state.selection.main.head).toBe(3);
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
it("should auto-close braces in triple-quoted strings", () => {
|
|
156
|
+
view = createEditor('"""hello ', 9);
|
|
157
|
+
const result = stringBraceInputHandler(view, 9, 9, "{");
|
|
158
|
+
|
|
159
|
+
expect(result).toBe(true);
|
|
160
|
+
expect(view.state.doc.toString()).toBe('"""hello {}');
|
|
161
|
+
expect(view.state.selection.main.head).toBe(10);
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
it("should auto-close braces in triple-quoted f-strings", () => {
|
|
165
|
+
view = createEditor('f"""hello ', 10);
|
|
166
|
+
const result = stringBraceInputHandler(view, 10, 10, "{");
|
|
167
|
+
|
|
168
|
+
expect(result).toBe(true);
|
|
169
|
+
expect(view.state.doc.toString()).toBe('f"""hello {}');
|
|
170
|
+
expect(view.state.selection.main.head).toBe(11);
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
it("should auto-close braces in triple single-quoted strings", () => {
|
|
174
|
+
view = createEditor("'''hello ", 9);
|
|
175
|
+
const result = stringBraceInputHandler(view, 9, 9, "{");
|
|
176
|
+
|
|
177
|
+
expect(result).toBe(true);
|
|
178
|
+
expect(view.state.doc.toString()).toBe("'''hello {}");
|
|
179
|
+
expect(view.state.selection.main.head).toBe(10);
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
it("should auto-close braces in triple single-quoted f-strings", () => {
|
|
183
|
+
view = createEditor("f'''hello ", 10);
|
|
184
|
+
const result = stringBraceInputHandler(view, 10, 10, "{");
|
|
185
|
+
|
|
186
|
+
expect(result).toBe(true);
|
|
187
|
+
expect(view.state.doc.toString()).toBe("f'''hello {}");
|
|
188
|
+
expect(view.state.selection.main.head).toBe(11);
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
it("should NOT auto-close braces when text is selected", () => {
|
|
192
|
+
view = createEditor('f"hello world"', 8);
|
|
193
|
+
// User has selected "world" (from position 8 to 13)
|
|
194
|
+
const result = stringBraceInputHandler(view, 8, 13, "{");
|
|
195
|
+
|
|
196
|
+
expect(result).toBe(false);
|
|
197
|
+
// Document should remain unchanged since we return false
|
|
198
|
+
expect(view.state.doc.toString()).toBe('f"hello world"');
|
|
199
|
+
});
|
|
200
|
+
});
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
|
+
import { syntaxTree } from "@codemirror/language";
|
|
3
|
+
import type { Extension } from "@codemirror/state";
|
|
4
|
+
import { EditorView } from "@codemirror/view";
|
|
5
|
+
|
|
6
|
+
export function stringBraceInputHandler(
|
|
7
|
+
view: EditorView,
|
|
8
|
+
from: number,
|
|
9
|
+
to: number,
|
|
10
|
+
text: string,
|
|
11
|
+
): boolean {
|
|
12
|
+
if (text !== "{") {
|
|
13
|
+
return false;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
if (from !== to) {
|
|
17
|
+
return false;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const tree = syntaxTree(view.state);
|
|
21
|
+
const node = tree.resolveInner(from, -1);
|
|
22
|
+
|
|
23
|
+
if (!node?.type.name.includes("String")) {
|
|
24
|
+
return false;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
view.dispatch({
|
|
28
|
+
changes: { from, to, insert: "{}" },
|
|
29
|
+
selection: { anchor: from + 1 },
|
|
30
|
+
userEvent: "input.type",
|
|
31
|
+
});
|
|
32
|
+
return true;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function stringsAutoCloseBraces(): Extension {
|
|
36
|
+
return EditorView.inputHandler.of(stringBraceInputHandler);
|
|
37
|
+
}
|