@marimo-team/islands 0.22.6-dev8 → 0.22.6-dev9
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 +1 -1
- package/package.json +1 -1
- package/src/components/app-config/user-config-form.tsx +25 -0
- package/src/components/editor/cell/code/cell-editor.tsx +4 -0
- package/src/core/codemirror/cm.ts +3 -1
- package/src/core/codemirror/completion/__tests__/keymap.test.ts +40 -1
- package/src/core/codemirror/completion/accept-on-enter-atom.ts +10 -0
- package/src/core/codemirror/completion/keymap.ts +16 -9
package/dist/main.js
CHANGED
|
@@ -65645,7 +65645,7 @@ ${c}
|
|
|
65645
65645
|
return Logger.warn("Failed to get version from mount config"), null;
|
|
65646
65646
|
}
|
|
65647
65647
|
}
|
|
65648
|
-
const marimoVersionAtom = atom(getVersionFromMountConfig() || "0.22.6-
|
|
65648
|
+
const marimoVersionAtom = atom(getVersionFromMountConfig() || "0.22.6-dev9"), showCodeInRunModeAtom = atom(true);
|
|
65649
65649
|
atom(null);
|
|
65650
65650
|
var VIRTUAL_FILE_REGEX = /\/@file\/([^\s"&'/]+)\.([\dA-Za-z]+)/g, VirtualFileTracker = class e {
|
|
65651
65651
|
constructor() {
|
package/package.json
CHANGED
|
@@ -19,6 +19,7 @@ import { useForm } from "react-hook-form";
|
|
|
19
19
|
import type z from "zod";
|
|
20
20
|
import { Button } from "@/components/ui/button";
|
|
21
21
|
import { Checkbox } from "@/components/ui/checkbox";
|
|
22
|
+
import { acceptCompletionOnEnterAtom } from "@/core/codemirror/completion/accept-on-enter-atom";
|
|
22
23
|
import {
|
|
23
24
|
Form,
|
|
24
25
|
FormControl,
|
|
@@ -118,6 +119,9 @@ const LOCALE_SYSTEM_VALUE = "__system__";
|
|
|
118
119
|
|
|
119
120
|
export const UserConfigForm: React.FC = () => {
|
|
120
121
|
const [config, setConfig] = useUserConfig();
|
|
122
|
+
const [acceptOnEnter, setAcceptOnEnter] = useAtom(
|
|
123
|
+
acceptCompletionOnEnterAtom,
|
|
124
|
+
);
|
|
121
125
|
const formElement = useRef<HTMLFormElement>(null);
|
|
122
126
|
const setKeyboardShortcutsOpen = useSetAtom(keyboardShortcutsAtom);
|
|
123
127
|
const [activeCategory, setActiveCategory] = useAtom(
|
|
@@ -446,6 +450,27 @@ export const UserConfigForm: React.FC = () => {
|
|
|
446
450
|
</div>
|
|
447
451
|
)}
|
|
448
452
|
/>
|
|
453
|
+
<div className="flex flex-col space-y-1">
|
|
454
|
+
<FormItem className={formItemClasses}>
|
|
455
|
+
<FormLabel className="font-normal">
|
|
456
|
+
Accept suggestion on Enter
|
|
457
|
+
</FormLabel>
|
|
458
|
+
<FormControl>
|
|
459
|
+
<Checkbox
|
|
460
|
+
data-testid="accept-completion-on-enter-checkbox"
|
|
461
|
+
checked={acceptOnEnter}
|
|
462
|
+
onCheckedChange={(checked) =>
|
|
463
|
+
setAcceptOnEnter(Boolean(checked))
|
|
464
|
+
}
|
|
465
|
+
/>
|
|
466
|
+
</FormControl>
|
|
467
|
+
</FormItem>
|
|
468
|
+
<FormDescription>
|
|
469
|
+
When unchecked, pressing Enter inserts a new line instead of
|
|
470
|
+
accepting an autocomplete suggestion. Use Tab to accept
|
|
471
|
+
suggestions.
|
|
472
|
+
</FormDescription>
|
|
473
|
+
</div>
|
|
449
474
|
<FormField
|
|
450
475
|
control={form.control}
|
|
451
476
|
name="completion.signature_hint_on_typing"
|
|
@@ -13,6 +13,7 @@ import { useCellActions } from "@/core/cells/cells";
|
|
|
13
13
|
import { usePendingDeleteService } from "@/core/cells/pending-delete-service";
|
|
14
14
|
import type { CellData, CellRuntimeState } from "@/core/cells/types";
|
|
15
15
|
import { setupCodeMirror } from "@/core/codemirror/cm";
|
|
16
|
+
import { acceptCompletionOnEnterAtom } from "@/core/codemirror/completion/accept-on-enter-atom";
|
|
16
17
|
import {
|
|
17
18
|
getInitialLanguageAdapter,
|
|
18
19
|
languageAdapterState,
|
|
@@ -146,6 +147,7 @@ const CellEditorInternal = ({
|
|
|
146
147
|
});
|
|
147
148
|
|
|
148
149
|
const autoInstantiate = useAtomValue(autoInstantiateAtom);
|
|
150
|
+
const acceptCompletionOnEnter = useAtomValue(acceptCompletionOnEnterAtom);
|
|
149
151
|
const afterToggleMarkdown = useEvent(() => {
|
|
150
152
|
maybeAddMarimoImport({
|
|
151
153
|
autoInstantiate,
|
|
@@ -212,6 +214,7 @@ const CellEditorInternal = ({
|
|
|
212
214
|
},
|
|
213
215
|
},
|
|
214
216
|
completionConfig: userConfig.completion,
|
|
217
|
+
acceptCompletionOnEnter,
|
|
215
218
|
keymapConfig: userConfig.keymap,
|
|
216
219
|
lspConfig: userConfig.language_servers,
|
|
217
220
|
theme,
|
|
@@ -261,6 +264,7 @@ const CellEditorInternal = ({
|
|
|
261
264
|
return extensions;
|
|
262
265
|
}, [
|
|
263
266
|
cellId,
|
|
267
|
+
acceptCompletionOnEnter,
|
|
264
268
|
userConfig.keymap,
|
|
265
269
|
userConfig.completion,
|
|
266
270
|
userConfig.language_servers,
|
|
@@ -73,6 +73,7 @@ export interface CodeMirrorSetupOpts {
|
|
|
73
73
|
cellId: CellId;
|
|
74
74
|
showPlaceholder: boolean;
|
|
75
75
|
enableAI: boolean;
|
|
76
|
+
acceptCompletionOnEnter?: boolean;
|
|
76
77
|
cellActions: CodemirrorCellActions;
|
|
77
78
|
completionConfig: CompletionConfig;
|
|
78
79
|
keymapConfig: KeymapConfig;
|
|
@@ -175,6 +176,7 @@ export const basicBundle = (opts: CodeMirrorSetupOpts): Extension[] => {
|
|
|
175
176
|
theme,
|
|
176
177
|
hotkeys,
|
|
177
178
|
completionConfig,
|
|
179
|
+
acceptCompletionOnEnter,
|
|
178
180
|
cellId,
|
|
179
181
|
lspConfig,
|
|
180
182
|
diagnosticsConfig,
|
|
@@ -207,7 +209,7 @@ export const basicBundle = (opts: CodeMirrorSetupOpts): Extension[] => {
|
|
|
207
209
|
foldGutter(),
|
|
208
210
|
stringsAutoCloseBraces(),
|
|
209
211
|
closeBrackets(),
|
|
210
|
-
completionKeymap(),
|
|
212
|
+
completionKeymap(acceptCompletionOnEnter),
|
|
211
213
|
// to avoid clash with charDeleteBackward keymap
|
|
212
214
|
Prec.high(keymap.of(closeBracketsKeymap)),
|
|
213
215
|
bracketMatching(),
|
|
@@ -1,8 +1,21 @@
|
|
|
1
1
|
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
2
|
|
|
3
3
|
import { completionKeymap as defaultCompletionKeymap } from "@codemirror/autocomplete";
|
|
4
|
+
import { EditorState } from "@codemirror/state";
|
|
5
|
+
import { keymap } from "@codemirror/view";
|
|
4
6
|
import { describe, expect, it } from "vitest";
|
|
5
|
-
import { filterCompletionBindings } from "../keymap";
|
|
7
|
+
import { completionKeymap, filterCompletionBindings } from "../keymap";
|
|
8
|
+
|
|
9
|
+
function hasEnterBinding(acceptOnEnter: boolean): boolean {
|
|
10
|
+
const state = EditorState.create({
|
|
11
|
+
extensions: [completionKeymap(acceptOnEnter)],
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
return state
|
|
15
|
+
.facet(keymap)
|
|
16
|
+
.flat()
|
|
17
|
+
.some((binding) => binding.key === "Enter");
|
|
18
|
+
}
|
|
6
19
|
|
|
7
20
|
describe("completionKeymap", () => {
|
|
8
21
|
it("upstream includes the macOS-only completion bindings we care about", () => {
|
|
@@ -21,4 +34,30 @@ describe("completionKeymap", () => {
|
|
|
21
34
|
expect(filtered.some((binding) => binding.key === "Escape")).toBe(false);
|
|
22
35
|
expect(filtered.some((binding) => binding.mac === "Alt-i")).toBe(true);
|
|
23
36
|
});
|
|
37
|
+
|
|
38
|
+
it("includes Enter by default", () => {
|
|
39
|
+
const filtered = filterCompletionBindings(defaultCompletionKeymap);
|
|
40
|
+
expect(filtered.some((binding) => binding.key === "Enter")).toBe(true);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it("removes Enter when passed a keysToRemove set containing Enter", () => {
|
|
44
|
+
const keysToRemove = new Set<string | undefined>([
|
|
45
|
+
"Escape",
|
|
46
|
+
"Alt-`",
|
|
47
|
+
"Enter",
|
|
48
|
+
]);
|
|
49
|
+
const filtered = filterCompletionBindings(
|
|
50
|
+
defaultCompletionKeymap,
|
|
51
|
+
keysToRemove,
|
|
52
|
+
);
|
|
53
|
+
expect(filtered.some((binding) => binding.key === "Enter")).toBe(false);
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
it("completionKeymap includes Enter when acceptOnEnter is true", () => {
|
|
57
|
+
expect(hasEnterBinding(true)).toBe(true);
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
it("completionKeymap removes Enter when acceptOnEnter is false", () => {
|
|
61
|
+
expect(hasEnterBinding(false)).toBe(false);
|
|
62
|
+
});
|
|
24
63
|
});
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
|
+
import { atomWithStorage } from "jotai/utils";
|
|
3
|
+
import { jotaiJsonStorage } from "@/utils/storage/jotai";
|
|
4
|
+
// Default: true (Enter accepts suggestion, matching VS Code default)
|
|
5
|
+
export const acceptCompletionOnEnterAtom = atomWithStorage<boolean>(
|
|
6
|
+
"marimo:accept-completion-on-enter",
|
|
7
|
+
true,
|
|
8
|
+
jotaiJsonStorage,
|
|
9
|
+
{ getOnInit: true },
|
|
10
|
+
);
|
|
@@ -22,20 +22,27 @@ const KEYS_TO_REMOVE = new Set<string | undefined>([
|
|
|
22
22
|
"Alt-`",
|
|
23
23
|
]);
|
|
24
24
|
|
|
25
|
-
function hasRemovedKeybinding(binding: KeyBinding): boolean {
|
|
26
|
-
return [binding.key, binding.mac, binding.linux, binding.win].some((key) =>
|
|
27
|
-
KEYS_TO_REMOVE.has(key),
|
|
28
|
-
);
|
|
29
|
-
}
|
|
30
|
-
|
|
31
25
|
export function filterCompletionBindings(
|
|
32
26
|
bindings: readonly KeyBinding[],
|
|
27
|
+
keysToRemove: Set<string | undefined> = KEYS_TO_REMOVE,
|
|
33
28
|
): readonly KeyBinding[] {
|
|
34
|
-
return bindings.filter(
|
|
29
|
+
return bindings.filter(
|
|
30
|
+
(binding) =>
|
|
31
|
+
![binding.key, binding.mac, binding.linux, binding.win].some((key) =>
|
|
32
|
+
keysToRemove.has(key),
|
|
33
|
+
),
|
|
34
|
+
);
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
-
export function completionKeymap(): Extension {
|
|
38
|
-
const
|
|
37
|
+
export function completionKeymap(acceptOnEnter = true): Extension {
|
|
38
|
+
const keysToRemove = new Set(KEYS_TO_REMOVE);
|
|
39
|
+
if (!acceptOnEnter) {
|
|
40
|
+
keysToRemove.add("Enter");
|
|
41
|
+
}
|
|
42
|
+
const withoutKeysToRemove = filterCompletionBindings(
|
|
43
|
+
defaultCompletionKeymap,
|
|
44
|
+
keysToRemove,
|
|
45
|
+
);
|
|
39
46
|
|
|
40
47
|
return Prec.highest(
|
|
41
48
|
keymap.of([
|