Skip to content

[codex] Fix reactive go-to-definition lookup#9747

Open
nkgotcode wants to merge 3 commits into
marimo-team:mainfrom
nkgotcode:codex/fix-go-to-definition-reactive-variable
Open

[codex] Fix reactive go-to-definition lookup#9747
nkgotcode wants to merge 3 commits into
marimo-team:mainfrom
nkgotcode:codex/fix-go-to-definition-reactive-variable

Conversation

@nkgotcode
Copy link
Copy Markdown

@nkgotcode nkgotcode commented Jun 1, 2026

This pull request was authored by a coding agent.

Summary

  • prevent current-cell lookups from falling back to the first matching local variable when resolving reactive variables
  • keep local scope resolution for same-cell definitions and then fall through to notebook reactive variable lookup
  • add coverage for jumping from a reactive variable use in one cell to its defining cell

Root cause

goToDefinition checked same-cell definitions first, but goToVariableDefinition always fell back to the first same-cell name match. For a reactive variable used in another cell, that fallback treated the usage token as a local match and stopped before the notebook variable lookup could jump to the defining cell.

Validation

  • pnpm --filter @marimo-team/frontend test src/core/codemirror/go-to-definition/__tests__/utils.test.ts --run
  • Result: 1 test file passed, 3 tests passed.

@vercel
Copy link
Copy Markdown

vercel Bot commented Jun 1, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
marimo-docs Ready Ready Preview, Comment Jun 3, 2026 12:27am

Request Review

@github-actions
Copy link
Copy Markdown

github-actions Bot commented Jun 1, 2026

All contributors have signed the CLA ✍️ ✅
Posted by the CLA Assistant Lite bot.

@nkgotcode
Copy link
Copy Markdown
Author

I have read the CLA Document and I hereby sign the CLA

@nkgotcode
Copy link
Copy Markdown
Author

recheck

@dmadisetti dmadisetti added the bug Something isn't working label Jun 2, 2026
@akshayka akshayka marked this pull request as ready for review June 2, 2026 19:27
@akshayka akshayka requested review from Copilot and kirangadhave June 2, 2026 19:27
@akshayka
Copy link
Copy Markdown
Contributor

akshayka commented Jun 2, 2026

@kirangadhave this might be right, can you confirm?

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes go-to-definition behavior in the frontend CodeMirror integration so that reactive variable references in one cell can correctly jump to the variable’s defining cell, instead of incorrectly “resolving” to a same-cell name match.

Changes:

  • Adds a fallbackToFirstMatch toggle to goToVariableDefinition to prevent same-cell lookups from incorrectly short-circuiting reactive resolution.
  • Updates goToDefinition to disable first-match fallback when a usage position is available, allowing cross-cell reactive lookup to proceed.
  • Adds a test covering go-to-definition from a reactive variable usage in one cell to its defining cell.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.

File Description
frontend/src/core/codemirror/go-to-definition/utils.ts Adjusts local vs reactive resolution flow to avoid false-positive local matches.
frontend/src/core/codemirror/go-to-definition/commands.ts Extends variable-definition navigation API with an opt-out for first-match fallback.
frontend/src/core/codemirror/go-to-definition/tests/utils.test.ts Adds regression coverage for cross-cell reactive go-to-definition.
Comments suppressed due to low confidence (1)

frontend/src/core/codemirror/go-to-definition/utils.ts:90

  • When goToDefinition is invoked on a private variable and no same-cell definition is found, the current logic proceeds to getEditorForVariable() which returns the current editor for private vars, then calls goToVariableDefinition(editorWithVariable, variableName) without a usagePosition. Because goToVariableDefinition can fall back to the first VariableName match, this can end up "navigating" to the usage token itself even though no definition exists. Since private variables are intended to be same-cell only, it’s better to return false once the scoped lookup fails.
  if (usagePosition !== undefined) {
    const foundLocally = goToVariableDefinition(
      view,
      variableName,
      usagePosition,
      false,
    );
    if (foundLocally) {
      return true;
    }
  }

Comment on lines 412 to +416
export function goToVariableDefinition(
view: EditorView,
variableName: string,
usagePosition?: number,
fallbackToFirstMatch = true,
@kirangadhave
Copy link
Copy Markdown
Member

@cubic-dev-ai

@cubic-dev-ai
Copy link
Copy Markdown
Contributor

cubic-dev-ai Bot commented Jun 2, 2026

@cubic-dev-ai

@kirangadhave I have started the AI code review. It will take a few minutes to complete.

Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No issues found across 3 files

Architecture diagram
sequenceDiagram
    participant User as "User (Cursor)"
    participant View as "EditorView (CodeMirror)"
    participant Store as "Jotai Store"
    participant Defs as "goToDefinition()"
    participant VarDef as "goToVariableDefinition()"
    participant Scoped as "findScopedDefinitionPosition()"
    participant First as "findFirstMatchingVariable()"
    participant Cells as "cellHandles (Notebook)"

    Note over User,Cells: Reactive Variable Go-To-Definition Flow

    User->>View: Cursor at variable "a" in usage cell
    View->>Defs: goToDefinition(usageView, variableName, usagePosition)
    
    alt Same-cell variable (existing)
        Note over Defs: fallbackToFirstMatch = false
        Defs->>VarDef: goToVariableDefinition(view, variableName, usagePosition, false)
        VarDef->>Scoped: findScopedDefinitionPosition(state, variableName, usagePosition)
        alt Scoped definition found
            Scoped-->>VarDef: Position {from, to} (same cell)
            VarDef->>View: Set cursor to definition
            VarDef-->>Defs: true (foundLocally)
            Defs-->>User: Jumped to same-cell definition
        else No scoped definition
            Scoped-->>VarDef: null
            VarDef->>First: fallbackToFirstMatch = false (skip call)
            Note over VarDef: NEW: fallbackToFirstMatch flag prevents fallback
            VarDef-->>Defs: false (not found locally)
            Defs->>Cells: Lookup reactive variable in notebook.variablesAtom
            Cells-->>Defs: Variable data (declaredBy / usedBy)
            Defs->>Cells: Navigate to defining cell for "a"
            Cells-->>User: Jumped to defining cell
        end
    else Same-cell variable (old behavior before PR)
        Note over Defs: Old: always fallbackToFirstMatch = true
        Defs->>VarDef: goToVariableDefinition(view, variableName, usagePosition, true)
        VarDef->>Scoped: findScopedDefinitionPosition(state, variableName, usagePosition)
        alt Scoped definition found
            Scoped-->>VarDef: Position (same cell)
            VarDef-->>Defs: true
        else No scoped definition
            Scoped-->>VarDef: null
            VarDef->>First: findFirstMatchingVariable(state, variableName)
            Note over First: NEW: This fallback is now skipped for cross-cell lookups
            First-->>VarDef: First same-cell match (incorrectly treats reactive var as local)
            VarDef-->>Defs: true (incorrect local match)
            Defs-->>User: Jumps to wrong first variable in same cell
        end
    end

    Note over User,Cells: Key architectural boundary: local scope vs notebook reactive variables
    Note over Defs,VarDef: CHANGED: fallbackToFirstMatch parameter controls boundary
Loading

Re-trigger cubic

Copy link
Copy Markdown
Member

@kirangadhave kirangadhave left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

two small followups

view: EditorView,
variableName: string,
usagePosition?: number,
fallbackToFirstMatch = true,
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

jsdoc doesn't reflect the new param, default value and what it does

: null) ??
(fallbackToFirstMatch
? findFirstMatchingVariable(state, variableName)
: null);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ternaries with null coalescing operator is difficult to parse visually. let's simplify:

let from: number | null = null;
if (usagePosition !== undefined) {
  from = findScopedDefinitionPosition(state, variableName, usagePosition);
}
if (from === null && fallbackToFirstMatch) {
  from = findFirstMatchingVariable(state, variableName);
}

@nkgotcode
Copy link
Copy Markdown
Author

Addressed the remaining JSDoc feedback in 39b195b74: the comment now describes scoped-definition lookup, the fallbackToFirstMatch behavior, and its default.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants