@marimo-team/islands 0.23.16-dev9 → 0.23.17-dev0

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 (164) hide show
  1. package/dist/{chat-ui-JeekqoMH.js → chat-ui-DsZJj75A.js} +3104 -3107
  2. package/dist/{code-visibility-DveLKNtj.js → common-CotPLtrU.js} +1068 -1036
  3. package/dist/{formats-CO8cn5_K.js → formats-Dwq1THhr.js} +1 -1
  4. package/dist/{html-to-image-Bif4utQZ.js → html-to-image-CZ1kLKkq.js} +7336 -6342
  5. package/dist/main.js +211 -147
  6. package/dist/{node-sql-parser-DLl_zNw0.js → node-sql-parser-v1Vi-Fft.js} +16867 -16665
  7. package/dist/{process-output-TAzy-GUk.js → process-output-bVfbcx5_.js} +30 -26
  8. package/dist/{reveal-component-DxV4Ppa2.js → reveal-component-IoHipy82.js} +634 -626
  9. package/dist/style.css +1 -1
  10. package/dist/toDate-CqhBWjvO.js +713 -0
  11. package/dist/{vega-component-DphdxaJ_.js → vega-component-C7nO5zcf.js} +2 -2
  12. package/package.json +3 -4
  13. package/src/__mocks__/requests.ts +56 -4
  14. package/src/components/data-table/__tests__/__snapshots__/chart-spec-model.test.ts.snap +174 -21
  15. package/src/components/data-table/__tests__/chart-spec-model.test.ts +102 -1
  16. package/src/components/data-table/__tests__/columns.test.tsx +9 -1
  17. package/src/components/data-table/column-summary/chart-spec-model.tsx +163 -103
  18. package/src/components/data-table/column-summary/legacy-chart-spec.ts +15 -7
  19. package/src/components/data-table/column-summary/utils.ts +26 -7
  20. package/src/components/data-table/columns.tsx +11 -1
  21. package/src/components/data-table/types.ts +1 -1
  22. package/src/components/editor/__tests__/viewer-banner.test.tsx +14 -0
  23. package/src/components/editor/actions/__tests__/pair-with-agent-commands.test.ts +99 -18
  24. package/src/components/editor/actions/__tests__/pdf-export.test.ts +32 -13
  25. package/src/components/editor/actions/pair-with-agent-commands.ts +21 -10
  26. package/src/components/editor/actions/pair-with-agent-modal.tsx +2 -2
  27. package/src/components/editor/actions/pdf-export.ts +11 -14
  28. package/src/components/editor/actions/useNotebookActions.tsx +28 -19
  29. package/src/components/editor/cell/code/__tests__/cell-editor-activity-lifecycle.test.ts +250 -0
  30. package/src/components/editor/cell/code/cell-editor.tsx +13 -7
  31. package/src/components/editor/chrome/__tests__/state.test.ts +7 -1
  32. package/src/components/editor/chrome/panels/__tests__/logs-panel.test.tsx +25 -0
  33. package/src/components/editor/chrome/panels/__tests__/panel-accordion-state.test.ts +42 -0
  34. package/src/components/editor/chrome/panels/file-explorer-panel.tsx +6 -17
  35. package/src/components/editor/chrome/panels/logs-panel.tsx +12 -2
  36. package/src/components/editor/chrome/panels/panel-accordion-state.ts +51 -0
  37. package/src/components/editor/chrome/panels/session-panel.tsx +5 -16
  38. package/src/components/editor/chrome/panels/write-secret-modal.tsx +40 -12
  39. package/src/components/editor/chrome/state.ts +10 -0
  40. package/src/components/editor/connections/__tests__/json-credentials.test.ts +114 -0
  41. package/src/components/editor/connections/__tests__/paths.test.ts +39 -0
  42. package/src/components/editor/connections/__tests__/quick-add-data-sources.test.tsx +113 -0
  43. package/src/components/editor/connections/add-connection-dialog.tsx +2 -0
  44. package/src/components/editor/connections/components.tsx +6 -2
  45. package/src/components/editor/connections/database/__tests__/__snapshots__/as-code.test.ts.snap +27 -5
  46. package/src/components/editor/connections/database/__tests__/as-code.test.ts +33 -0
  47. package/src/components/editor/connections/database/as-code.ts +37 -13
  48. package/src/components/editor/connections/database/schemas.ts +6 -5
  49. package/src/components/editor/connections/form-renderers.tsx +99 -1
  50. package/src/components/editor/connections/json-credentials.ts +57 -0
  51. package/src/components/editor/connections/paths.ts +21 -0
  52. package/src/components/editor/connections/quick-add-data-sources.tsx +106 -0
  53. package/src/components/editor/connections/secret-combobox.tsx +41 -20
  54. package/src/components/editor/connections/storage/__tests__/__snapshots__/as-code.test.ts.snap +88 -2
  55. package/src/components/editor/connections/storage/__tests__/as-code.test.ts +128 -8
  56. package/src/components/editor/connections/storage/as-code.ts +79 -36
  57. package/src/components/editor/connections/storage/schemas.ts +85 -44
  58. package/src/components/editor/file-tree/__tests__/renderers.test.ts +31 -0
  59. package/src/components/editor/file-tree/file-viewer.tsx +11 -7
  60. package/src/components/editor/file-tree/renderers.tsx +21 -0
  61. package/src/components/editor/renderers/vertical-layout/vertical-layout.tsx +2 -2
  62. package/src/components/editor/viewer-banner.tsx +6 -1
  63. package/src/components/forms/form.tsx +59 -24
  64. package/src/components/forms/options.ts +2 -1
  65. package/src/components/slides/__tests__/cell-output-block.test.tsx +40 -0
  66. package/src/components/slides/__tests__/slide-cell-view.test.tsx +33 -0
  67. package/src/components/slides/reveal-component.tsx +22 -17
  68. package/src/components/slides/slide-cell-view.tsx +1 -1
  69. package/src/components/slides/slides.css +4 -2
  70. package/src/components/static-html/share-modal.tsx +1 -1
  71. package/src/core/ai/context/providers/file.ts +8 -29
  72. package/src/core/cells/__tests__/cells.test.ts +12 -2
  73. package/src/core/cells/__tests__/logs.test.ts +5 -5
  74. package/src/core/cells/cells.ts +17 -4
  75. package/src/core/cells/document-changes.ts +1 -0
  76. package/src/core/cells/logs.ts +14 -4
  77. package/src/core/codemirror/cm.ts +3 -0
  78. package/src/core/codemirror/code-lens/__tests__/actions.test.ts +68 -0
  79. package/src/core/codemirror/code-lens/__tests__/analyzer.test.ts +224 -0
  80. package/src/core/codemirror/code-lens/__tests__/entities.test.ts +175 -0
  81. package/src/core/codemirror/code-lens/__tests__/extension.test.ts +325 -0
  82. package/src/core/codemirror/code-lens/__tests__/perf.test.ts +121 -0
  83. package/src/core/codemirror/code-lens/__tests__/popover.test.ts +93 -0
  84. package/src/core/codemirror/code-lens/actions.ts +33 -0
  85. package/src/core/codemirror/code-lens/analyzer.ts +284 -0
  86. package/src/core/codemirror/code-lens/entities.ts +123 -0
  87. package/src/core/codemirror/code-lens/extension.ts +298 -0
  88. package/src/core/codemirror/code-lens/icons.ts +36 -0
  89. package/src/core/codemirror/code-lens/popover.tsx +240 -0
  90. package/src/core/codemirror/copilot/__tests__/language-server.test.ts +396 -100
  91. package/src/core/codemirror/copilot/__tests__/transport.test.ts +128 -540
  92. package/src/core/codemirror/copilot/language-server.ts +182 -70
  93. package/src/core/codemirror/copilot/transport.ts +15 -207
  94. package/src/core/codemirror/copilot/types.ts +9 -2
  95. package/src/core/codemirror/find-replace/__tests__/navigate.test.ts +42 -30
  96. package/src/core/codemirror/find-replace/navigate.ts +2 -0
  97. package/src/core/codemirror/go-to-definition/__tests__/commands.test.ts +24 -3
  98. package/src/core/codemirror/go-to-definition/commands.ts +93 -90
  99. package/src/core/codemirror/language/__tests__/sql.test.ts +627 -61
  100. package/src/core/codemirror/language/languages/python.ts +23 -11
  101. package/src/core/codemirror/language/languages/sql/completion-store.ts +5 -1
  102. package/src/core/codemirror/language/languages/sql/renderers.tsx +3 -3
  103. package/src/core/codemirror/language/languages/sql/sql.ts +251 -16
  104. package/src/core/codemirror/lsp/__tests__/federated-lsp.test.ts +161 -0
  105. package/src/core/codemirror/lsp/__tests__/log-messages.test.ts +53 -0
  106. package/src/core/codemirror/lsp/__tests__/normalize-markdown-math.test.ts +25 -0
  107. package/src/core/codemirror/lsp/__tests__/notebook-lsp.test.ts +598 -156
  108. package/src/core/codemirror/lsp/__tests__/transports.test.ts +112 -0
  109. package/src/core/codemirror/lsp/federated-lsp.ts +76 -23
  110. package/src/core/codemirror/lsp/log-messages.ts +37 -0
  111. package/src/core/codemirror/lsp/normalize-markdown-math.ts +8 -4
  112. package/src/core/codemirror/lsp/notebook-lsp.ts +220 -56
  113. package/src/core/codemirror/lsp/transports.ts +8 -1
  114. package/src/core/codemirror/lsp/types.ts +68 -8
  115. package/src/core/codemirror/lsp/utils.ts +14 -0
  116. package/src/core/codemirror/python-node-names.ts +90 -0
  117. package/src/core/codemirror/reactive-references/analyzer.ts +52 -57
  118. package/src/core/codemirror/utils.ts +28 -0
  119. package/src/core/config/feature-flag.tsx +3 -1
  120. package/src/core/datasets/data-source-discovery.ts +5 -0
  121. package/src/core/datasets/request-registry.ts +11 -0
  122. package/src/core/edit-app.tsx +1 -2
  123. package/src/core/export/__tests__/hooks.test.ts +2 -4
  124. package/src/core/export/hooks.ts +4 -3
  125. package/src/core/islands/__tests__/bridge.test.ts +107 -0
  126. package/src/core/islands/bootstrap.ts +1 -0
  127. package/src/core/islands/bridge.ts +18 -0
  128. package/src/core/kernel/messages.ts +2 -0
  129. package/src/core/lsp/__tests__/transport.test.ts +509 -337
  130. package/src/core/lsp/transport.ts +304 -132
  131. package/src/core/mode.ts +10 -0
  132. package/src/core/network/__tests__/api.test.ts +42 -0
  133. package/src/core/network/__tests__/connection.test.ts +45 -0
  134. package/src/core/network/__tests__/requests-lazy.test.ts +47 -1
  135. package/src/core/network/__tests__/requests-network.test.ts +48 -0
  136. package/src/core/network/api.ts +27 -0
  137. package/src/core/network/connection.ts +15 -0
  138. package/src/core/network/requests-lazy.ts +3 -0
  139. package/src/core/network/requests-network.ts +31 -8
  140. package/src/core/network/requests-static.ts +3 -0
  141. package/src/core/network/requests-toasting.tsx +3 -0
  142. package/src/core/network/types.ts +27 -6
  143. package/src/core/rtc/state.ts +9 -0
  144. package/src/core/static/download-html.ts +5 -14
  145. package/src/core/wasm/__tests__/bridge.test.ts +99 -5
  146. package/src/core/wasm/bridge.ts +42 -9
  147. package/src/core/wasm/worker/types.ts +10 -3
  148. package/src/core/wasm/worker/worker.ts +7 -8
  149. package/src/core/websocket/useMarimoKernelConnection.tsx +4 -0
  150. package/src/css/app/codemirror.css +16 -2
  151. package/src/hooks/__tests__/useDataSourceDiscovery.test.ts +80 -0
  152. package/src/hooks/useDataSourceDiscovery.ts +18 -0
  153. package/src/plugins/impl/DataTablePlugin.tsx +4 -1
  154. package/src/plugins/impl/__tests__/DataTablePlugin.test.tsx +158 -0
  155. package/src/plugins/impl/anywidget/__tests__/model.test.ts +55 -0
  156. package/src/plugins/impl/anywidget/model.ts +20 -5
  157. package/src/plugins/impl/data-frames/forms/__tests__/__snapshots__/form.test.tsx.snap +33 -21
  158. package/src/plugins/impl/data-frames/forms/renderers.tsx +11 -8
  159. package/src/stories/log-viewer.stories.tsx +12 -6
  160. package/src/utils/__tests__/blob.test.ts +27 -1
  161. package/src/utils/__tests__/download.test.tsx +44 -4
  162. package/src/utils/blob.ts +24 -1
  163. package/src/utils/download.ts +14 -15
  164. package/dist/toDate-CmR_xR3P.js +0 -692
@@ -20,6 +20,26 @@ vi.mock("@/core/cells/cells", () => ({
20
20
  getAllEditorViews: () => mockGetAllEditorViews(),
21
21
  }));
22
22
 
23
+ /**
24
+ * A view stub that only carries what navigate needs
25
+ */
26
+ function createMockView(state: EditorState): EditorView {
27
+ const cell = document.createElement("div");
28
+ cell.className = "marimo-cell";
29
+ // jsdom doesn't implement scrollIntoView.
30
+ cell.scrollIntoView = vi.fn();
31
+ const dom = document.createElement("div");
32
+ cell.append(dom);
33
+
34
+ return {
35
+ state,
36
+ dom,
37
+ // Don't actually apply changes; it's complex to mock properly and the
38
+ // assertions only care that dispatch was called.
39
+ dispatch: vi.fn(),
40
+ } as unknown as EditorView;
41
+ }
42
+
23
43
  describe("navigate", () => {
24
44
  let view1: EditorView;
25
45
  let view2: EditorView;
@@ -36,20 +56,8 @@ describe("navigate", () => {
36
56
  });
37
57
 
38
58
  // Create mock views that track dispatch calls
39
- view1 = {
40
- state: state1,
41
- dispatch: vi.fn(() => {
42
- // Mock dispatch - in real tests we just need to verify it was called
43
- // Don't actually apply changes as it's complex to mock properly
44
- }),
45
- } as unknown as EditorView;
46
-
47
- view2 = {
48
- state: state2,
49
- dispatch: vi.fn(() => {
50
- // Mock dispatch - in real tests we just need to verify it was called
51
- }),
52
- } as unknown as EditorView;
59
+ view1 = createMockView(state1);
60
+ view2 = createMockView(state2);
53
61
 
54
62
  mockViews = [view1, view2];
55
63
  mockGetAllEditorViews.mockReturnValue(mockViews);
@@ -234,6 +242,22 @@ describe("navigate", () => {
234
242
 
235
243
  expect(result).toBe(false);
236
244
  });
245
+
246
+ it("should scroll the cell owning the match into view", async () => {
247
+ const cell = view1.dom.closest(".marimo-cell");
248
+ invariant(cell, "mock view should be inside a cell");
249
+
250
+ expect(findNext()).toBeTruthy();
251
+
252
+ // The scroll is deferred a frame.
253
+ await new Promise((resolve) => requestAnimationFrame(resolve));
254
+
255
+ expect(cell.scrollIntoView).toHaveBeenCalledWith({
256
+ behavior: "instant",
257
+ block: "nearest",
258
+ inline: "nearest",
259
+ });
260
+ });
237
261
  });
238
262
 
239
263
  describe("findPrev", () => {
@@ -319,10 +343,7 @@ describe("navigate", () => {
319
343
  extensions: [EditorState.readOnly.of(true)],
320
344
  });
321
345
 
322
- const readOnlyView = {
323
- state: readOnlyState,
324
- dispatch: vi.fn(),
325
- } as unknown as EditorView;
346
+ const readOnlyView = createMockView(readOnlyState);
326
347
 
327
348
  // Replace view1 with read-only view temporarily
328
349
  mockGetAllEditorViews.mockReturnValueOnce([readOnlyView, view2]);
@@ -568,10 +589,7 @@ describe("navigate", () => {
568
589
  doc: Text.of(["hello world hello there hello"]),
569
590
  });
570
591
 
571
- const testView = {
572
- state: testDoc,
573
- dispatch: vi.fn(),
574
- } as unknown as EditorView;
592
+ const testView = createMockView(testDoc);
575
593
 
576
594
  mockGetAllEditorViews.mockReturnValue([testView]);
577
595
 
@@ -613,10 +631,7 @@ describe("navigate", () => {
613
631
  doc: Text.of([""]),
614
632
  });
615
633
 
616
- const emptyView = {
617
- state: emptyState,
618
- dispatch: vi.fn(),
619
- } as unknown as EditorView;
634
+ const emptyView = createMockView(emptyState);
620
635
 
621
636
  mockGetAllEditorViews.mockReturnValue([emptyView]);
622
637
 
@@ -629,10 +644,7 @@ describe("navigate", () => {
629
644
  doc: Text.of(["orphan content"]),
630
645
  });
631
646
 
632
- const orphanView = {
633
- state: orphanState,
634
- dispatch: vi.fn(),
635
- } as unknown as EditorView;
647
+ const orphanView = createMockView(orphanState);
636
648
 
637
649
  store.set(findReplaceAtom, {
638
650
  type: "setCurrentView",
@@ -6,6 +6,7 @@ import { EditorView } from "@codemirror/view";
6
6
  import { getAllEditorViews } from "@/core/cells/cells";
7
7
  import { replaceEditorContent } from "@/core/codemirror/replace-editor-content";
8
8
  import { store } from "@/core/state/jotai";
9
+ import { scrollOwnerCell } from "../utils";
9
10
  import { asQueryCreator, type QueryType } from "./query";
10
11
  import { findReplaceAtom } from "./state";
11
12
 
@@ -85,6 +86,7 @@ const findInDirection = (direction: "next" | "prev") =>
85
86
  effects: [EditorView.scrollIntoView(selection.main, { y: "center" })],
86
87
  userEvent: "select.search",
87
88
  });
89
+ scrollOwnerCell(view);
88
90
  store.set(findReplaceAtom, {
89
91
  type: "setCurrentView",
90
92
  view,
@@ -3,14 +3,14 @@
3
3
  import { python } from "@codemirror/lang-python";
4
4
  import { EditorState } from "@codemirror/state";
5
5
  import { EditorView } from "@codemirror/view";
6
- import { afterEach, describe, expect, test } from "vitest";
6
+ import { afterEach, describe, expect, test, vi } from "vitest";
7
7
  import { goToVariableDefinition } from "../commands";
8
8
 
9
9
  async function tick(): Promise<void> {
10
10
  await new Promise((resolve) => requestAnimationFrame(resolve));
11
11
  }
12
12
 
13
- function createEditor(content: string) {
13
+ function createEditor(content: string, parent: HTMLElement = document.body) {
14
14
  const state = EditorState.create({
15
15
  doc: content,
16
16
  extensions: [python()],
@@ -18,7 +18,7 @@ function createEditor(content: string) {
18
18
 
19
19
  const view = new EditorView({
20
20
  state,
21
- parent: document.body,
21
+ parent,
22
22
  });
23
23
 
24
24
  return view;
@@ -383,6 +383,27 @@ print('myVar')`);
383
383
  "
384
384
  `);
385
385
  });
386
+
387
+ test("scrolls the cell owning the definition into view", async () => {
388
+ const cell = document.createElement("div");
389
+ cell.className = "marimo-cell";
390
+ // jsdom doesn't implement scrollIntoView.
391
+ cell.scrollIntoView = vi.fn();
392
+ document.body.append(cell);
393
+
394
+ view = createEditor("myVar = 10\nprint(myVar)", cell);
395
+ expect(goToVariableDefinition(view, "myVar")).toBe(true);
396
+ // The jump and the scroll are both deferred to the next frame.
397
+ await tick();
398
+
399
+ expect(cell.scrollIntoView).toHaveBeenCalledWith({
400
+ behavior: "instant",
401
+ block: "nearest",
402
+ inline: "nearest",
403
+ });
404
+
405
+ cell.remove();
406
+ });
386
407
  });
387
408
 
388
409
  /**
@@ -4,19 +4,13 @@ import { syntaxTree } from "@codemirror/language";
4
4
  import type { EditorState } from "@codemirror/state";
5
5
  import { EditorView } from "@codemirror/view";
6
6
  import type { SyntaxNode, Tree, TreeCursor } from "@lezer/common";
7
+ import { PyKeyword, PyNode, SCOPE_CREATING_NODES } from "../python-node-names";
8
+ import { scrollOwnerCell } from "../utils";
7
9
 
8
- const SCOPE_CREATING_NODES = new Set([
9
- "FunctionDefinition",
10
- "LambdaExpression",
11
- "ArrayComprehensionExpression",
12
- "SetComprehensionExpression",
13
- "DictionaryComprehensionExpression",
14
- "ComprehensionExpression",
15
- "ClassDefinition",
10
+ const POSITION_SENSITIVE_SCOPES: ReadonlySet<string> = new Set([
11
+ PyNode.ClassDefinition,
16
12
  ]);
17
13
 
18
- const POSITION_SENSITIVE_SCOPES = new Set(["ClassDefinition"]);
19
-
20
14
  interface ScopeContext {
21
15
  id: number;
22
16
  type: string;
@@ -45,6 +39,8 @@ function goToPosition(view: EditorView, from: number): void {
45
39
  }),
46
40
  });
47
41
  });
42
+
43
+ scrollOwnerCell(view);
48
44
  }
49
45
 
50
46
  function findFirstMatchingVariable(
@@ -62,14 +58,14 @@ function findFirstMatchingVariable(
62
58
  }
63
59
 
64
60
  if (
65
- node.name === "VariableName" &&
61
+ node.name === PyNode.VariableName &&
66
62
  state.doc.sliceString(node.from, node.to) === variableName
67
63
  ) {
68
64
  from = node.from;
69
65
  return false;
70
66
  }
71
67
 
72
- if (node.name === "Comment" || node.name === "String") {
68
+ if (node.name === PyNode.Comment || node.name === PyNode.String) {
73
69
  return false;
74
70
  }
75
71
 
@@ -89,10 +85,12 @@ function getScopeChain(tree: Tree, usagePosition: number): ScopeContext[] {
89
85
  // Skip ClassDefinition if we've already seen a function/lambda.
90
86
  const inFunctionLikeScope = scopeChain.some(
91
87
  (scope) =>
92
- scope.type === "FunctionDefinition" ||
93
- scope.type === "LambdaExpression",
88
+ scope.type === PyNode.FunctionDefinition ||
89
+ scope.type === PyNode.LambdaExpression,
94
90
  );
95
- if (!(inFunctionLikeScope && currentNode.name === "ClassDefinition")) {
91
+ if (
92
+ !(inFunctionLikeScope && currentNode.name === PyNode.ClassDefinition)
93
+ ) {
96
94
  scopeChain.push({
97
95
  id: currentNode.from,
98
96
  type: currentNode.name,
@@ -127,30 +125,27 @@ function traverseChildren(
127
125
 
128
126
  function collectMatchingTargets(
129
127
  cursor: TreeCursor,
130
- state: EditorState,
131
- variableName: string,
132
- scopeId: number,
133
- declarations: VariableDeclaration[],
128
+ options: {
129
+ state: EditorState;
130
+ variableName: string;
131
+ scopeId: number;
132
+ declarations: VariableDeclaration[];
133
+ },
134
134
  ) {
135
+ const { state, variableName, scopeId, declarations } = options;
135
136
  switch (cursor.name) {
136
- case "VariableName":
137
+ case PyNode.VariableName:
137
138
  if (state.doc.sliceString(cursor.from, cursor.to) === variableName) {
138
139
  addDeclaration(declarations, scopeId, cursor.from);
139
140
  }
140
141
  break;
141
142
 
142
- case "TupleExpression":
143
- case "ArrayExpression": {
143
+ case PyNode.TupleExpression:
144
+ case PyNode.ArrayExpression: {
144
145
  const childCursor = cursor.node.cursor();
145
146
  childCursor.firstChild();
146
147
  do {
147
- collectMatchingTargets(
148
- childCursor,
149
- state,
150
- variableName,
151
- scopeId,
152
- declarations,
153
- );
148
+ collectMatchingTargets(childCursor, options);
154
149
  } while (childCursor.nextSibling());
155
150
  break;
156
151
  }
@@ -161,15 +156,18 @@ function collectMatchingTargets(
161
156
 
162
157
  function collectFunctionParameters(
163
158
  node: SyntaxNode | Tree,
164
- state: EditorState,
165
- variableName: string,
166
- scopeId: number,
167
- declarations: VariableDeclaration[],
159
+ options: {
160
+ state: EditorState;
161
+ variableName: string;
162
+ scopeId: number;
163
+ declarations: VariableDeclaration[];
164
+ },
168
165
  ) {
166
+ const { state, variableName, scopeId, declarations } = options;
169
167
  const cursor = node.cursor();
170
168
  cursor.firstChild();
171
169
  do {
172
- if (cursor.name !== "ParamList") {
170
+ if (cursor.name !== PyNode.ParamList) {
173
171
  continue;
174
172
  }
175
173
 
@@ -177,7 +175,7 @@ function collectFunctionParameters(
177
175
  paramCursor.firstChild();
178
176
  do {
179
177
  if (
180
- paramCursor.name === "VariableName" &&
178
+ paramCursor.name === PyNode.VariableName &&
181
179
  state.doc.sliceString(paramCursor.from, paramCursor.to) === variableName
182
180
  ) {
183
181
  addDeclaration(declarations, scopeId, paramCursor.from);
@@ -188,38 +186,37 @@ function collectFunctionParameters(
188
186
 
189
187
  function collectForTargets(
190
188
  node: SyntaxNode | Tree,
191
- state: EditorState,
192
- variableName: string,
193
- scopeId: number,
194
- declarations: VariableDeclaration[],
189
+ options: {
190
+ state: EditorState;
191
+ variableName: string;
192
+ scopeId: number;
193
+ declarations: VariableDeclaration[];
194
+ },
195
195
  ) {
196
196
  const cursor = node.cursor();
197
197
  cursor.firstChild();
198
198
  let foundFor = false;
199
199
  do {
200
- if (cursor.name === "for") {
200
+ if (cursor.name === PyKeyword.For) {
201
201
  foundFor = true;
202
- } else if (foundFor && cursor.name === "in") {
202
+ } else if (foundFor && cursor.name === PyKeyword.In) {
203
203
  break;
204
204
  } else if (foundFor) {
205
- collectMatchingTargets(
206
- cursor,
207
- state,
208
- variableName,
209
- scopeId,
210
- declarations,
211
- );
205
+ collectMatchingTargets(cursor, options);
212
206
  }
213
207
  } while (cursor.nextSibling());
214
208
  }
215
209
 
216
210
  function collectMatchingDeclarations(
217
211
  node: SyntaxNode | Tree,
218
- state: EditorState,
219
- variableName: string,
220
- scopeStack: number[],
221
- declarations: VariableDeclaration[],
212
+ options: {
213
+ state: EditorState;
214
+ variableName: string;
215
+ scopeStack: number[];
216
+ declarations: VariableDeclaration[];
217
+ },
222
218
  ) {
219
+ const { state, variableName, scopeStack, declarations } = options;
223
220
  const cursor = node.cursor();
224
221
  const nodeName = cursor.name;
225
222
  const nodeStart = cursor.from;
@@ -231,13 +228,13 @@ function collectMatchingDeclarations(
231
228
  const currentScope = currentScopeStack[currentScopeStack.length - 1] ?? -1;
232
229
 
233
230
  switch (nodeName) {
234
- case "FunctionDefinition":
235
- case "ClassDefinition": {
231
+ case PyNode.FunctionDefinition:
232
+ case PyNode.ClassDefinition: {
236
233
  const subCursor = node.cursor();
237
234
  subCursor.firstChild();
238
235
  do {
239
236
  if (
240
- subCursor.name === "VariableName" &&
237
+ subCursor.name === PyNode.VariableName &&
241
238
  state.doc.sliceString(subCursor.from, subCursor.to) === variableName
242
239
  ) {
243
240
  const parentScope = scopeStack[scopeStack.length - 1] ?? -1;
@@ -246,41 +243,44 @@ function collectMatchingDeclarations(
246
243
  }
247
244
  } while (subCursor.nextSibling());
248
245
 
249
- if (nodeName === "FunctionDefinition") {
250
- collectFunctionParameters(
251
- node,
246
+ if (nodeName === PyNode.FunctionDefinition) {
247
+ collectFunctionParameters(node, {
252
248
  state,
253
249
  variableName,
254
- nodeStart,
250
+ scopeId: nodeStart,
255
251
  declarations,
256
- );
252
+ });
257
253
  }
258
254
  break;
259
255
  }
260
- case "LambdaExpression":
261
- collectFunctionParameters(
262
- node,
256
+ case PyNode.LambdaExpression:
257
+ collectFunctionParameters(node, {
263
258
  state,
264
259
  variableName,
265
- nodeStart,
260
+ scopeId: nodeStart,
266
261
  declarations,
267
- );
262
+ });
268
263
  break;
269
264
 
270
- case "ArrayComprehensionExpression":
271
- case "DictionaryComprehensionExpression":
272
- case "SetComprehensionExpression":
273
- case "ComprehensionExpression":
274
- case "ForStatement":
275
- collectForTargets(node, state, variableName, currentScope, declarations);
265
+ case PyNode.ArrayComprehensionExpression:
266
+ case PyNode.DictionaryComprehensionExpression:
267
+ case PyNode.SetComprehensionExpression:
268
+ case PyNode.ComprehensionExpression:
269
+ case PyNode.ForStatement:
270
+ collectForTargets(node, {
271
+ state,
272
+ variableName,
273
+ scopeId: currentScope,
274
+ declarations,
275
+ });
276
276
  break;
277
277
 
278
- case "AssignStatement": {
278
+ case PyNode.AssignStatement: {
279
279
  const assignOpPositions: number[] = [];
280
280
  const subCursor = node.cursor();
281
281
  subCursor.firstChild();
282
282
  do {
283
- if (subCursor.name === "AssignOp") {
283
+ if (subCursor.name === PyNode.AssignOp) {
284
284
  assignOpPositions.push(subCursor.from);
285
285
  }
286
286
  } while (subCursor.nextSibling());
@@ -295,18 +295,17 @@ function collectMatchingDeclarations(
295
295
  targetCursor.firstChild();
296
296
  do {
297
297
  if (targetCursor.from < lastAssignOpPosition) {
298
- collectMatchingTargets(
299
- targetCursor,
298
+ collectMatchingTargets(targetCursor, {
300
299
  state,
301
300
  variableName,
302
- currentScope,
301
+ scopeId: currentScope,
303
302
  declarations,
304
- );
303
+ });
305
304
  }
306
305
  } while (targetCursor.nextSibling());
307
306
  break;
308
307
  }
309
- case "ImportStatement": {
308
+ case PyNode.ImportStatement: {
310
309
  // The grammar emits one ImportStatement for both `import x [as y]` and
311
310
  // `from m import x [as y], ...`. Direct children include the keywords
312
311
  // (`from`/`import`/`as`), commas, dots, and every VariableName from the
@@ -327,19 +326,19 @@ function collectMatchingDeclarations(
327
326
  pending = null;
328
327
  };
329
328
  do {
330
- if (subCursor.name === "import") {
329
+ if (subCursor.name === PyKeyword.Import) {
331
330
  pastImport = true;
332
331
  continue;
333
332
  }
334
333
  if (!pastImport) {
335
334
  continue;
336
335
  }
337
- if (subCursor.name === "as") {
336
+ if (subCursor.name === PyKeyword.As) {
338
337
  // Next VariableName is the alias and replaces `pending`.
339
338
  pending = null;
340
339
  continue;
341
340
  }
342
- if (subCursor.name === "VariableName") {
341
+ if (subCursor.name === PyNode.VariableName) {
343
342
  // Flush any previous pending name (no `as` followed it).
344
343
  commit();
345
344
  pending = {
@@ -355,17 +354,17 @@ function collectMatchingDeclarations(
355
354
  commit();
356
355
  break;
357
356
  }
358
- case "TryStatement":
359
- case "WithStatement": {
357
+ case PyNode.TryStatement:
358
+ case PyNode.WithStatement: {
360
359
  const subCursor = node.cursor();
361
360
  subCursor.firstChild();
362
361
  let foundAs = false;
363
362
  do {
364
- if (subCursor.name === "as") {
363
+ if (subCursor.name === PyKeyword.As) {
365
364
  foundAs = true;
366
365
  } else if (
367
366
  foundAs &&
368
- subCursor.name === "VariableName" &&
367
+ subCursor.name === PyNode.VariableName &&
369
368
  state.doc.sliceString(subCursor.from, subCursor.to) === variableName
370
369
  ) {
371
370
  addDeclaration(declarations, currentScope, subCursor.from);
@@ -379,13 +378,12 @@ function collectMatchingDeclarations(
379
378
  }
380
379
 
381
380
  traverseChildren(cursor, (childNode) => {
382
- collectMatchingDeclarations(
383
- childNode,
381
+ collectMatchingDeclarations(childNode, {
384
382
  state,
385
383
  variableName,
386
- currentScopeStack,
384
+ scopeStack: currentScopeStack,
387
385
  declarations,
388
- );
386
+ });
389
387
  });
390
388
  }
391
389
 
@@ -397,7 +395,12 @@ function findScopedDefinitionPosition(
397
395
  const tree = syntaxTree(state);
398
396
  const declarations: VariableDeclaration[] = [];
399
397
 
400
- collectMatchingDeclarations(tree, state, variableName, [], declarations);
398
+ collectMatchingDeclarations(tree, {
399
+ state,
400
+ variableName,
401
+ scopeStack: [],
402
+ declarations,
403
+ });
401
404
 
402
405
  const clampedUsagePosition = Math.max(
403
406
  0,