Skip to content

spec: bindable shortcut to copy editor file path (#10290)#10497

Open
lonexreb wants to merge 4 commits intowarpdotdev:masterfrom
lonexreb:spec/10290-copy-relative-path-shortcut
Open

spec: bindable shortcut to copy editor file path (#10290)#10497
lonexreb wants to merge 4 commits intowarpdotdev:masterfrom
lonexreb:spec/10290-copy-relative-path-shortcut

Conversation

@lonexreb
Copy link
Copy Markdown
Contributor

@lonexreb lonexreb commented May 8, 2026

Spec for #10290.

Summary

Add three new bindable Command Palette actions for the Warp editor — editor.copy_absolute_path, editor.copy_relative_path, and editor.copy_path_with_line — each rebindable in Settings → Keyboard Shortcuts and discoverable in the Command Palette without a binding. Defaults to unbound, matching Warp's pattern for optional new actions. Mirrors VS Code's editor.action.copyPath / copyRelativeFilePath pattern users already know.

Scope

  • In: three new actions, OS-correct path separators, project-root resolution via git toplevel (fallback to workspace folder), no-file no-op + toast, outside-project fallback to absolute + toast, Command Palette discoverability, brief confirmation toasts.
  • Out: GitHub-style URL paths, multi-cursor or multi-file copy, picker dialog between formats, terminal-output copy changes.

Notes

  • Spec-only PR. No code changes.
  • Pattern matches VS Code's editor.action.copyPath and copyRelativeFilePath.
  • Tag: ready-to-spec.

Test plan

  • Confirm action IDs (editor.copy_absolute_path / copy_relative_path / copy_path_with_line) align with existing editor.* namespace
  • Confirm project root = git toplevel with workspace fallback is the right anchor
  • Confirm default-unbound is the desired default vs. proposing a recommended chord
  • Confirm outside-project fallback (absolute + toast) is acceptable
  • Confirm telemetry reuse of command_palette.action_invoked is correct

@cla-bot cla-bot Bot added the cla-signed label May 8, 2026
@oz-for-oss
Copy link
Copy Markdown
Contributor

oz-for-oss Bot commented May 8, 2026

@lonexreb

I'm starting a first review of this spec-only pull request.

You can view the conversation on Warp.

I completed the review and no human review was requested for this pull request.

Comment /oz-review on this pull request to retrigger a review (up to 3 times on the same pull request).

Powered by Oz

@github-actions github-actions Bot added the external-contributor Indicates that a PR has been opened by someone outside the Warp team. label May 8, 2026
Copy link
Copy Markdown
Contributor

@oz-for-oss oz-for-oss Bot left a comment

Choose a reason for hiding this comment

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

Overview

This spec adds unbound editor copy-path actions and covers discoverability, project-root resolution, fallbacks, and validation coverage.

Concerns

  • The copy_path_with_line contract does not define line/column numbering or when a column is included, so implementations could produce different clipboard strings for the same cursor state.
  • The outside-project fallback does not say whether copy_path_with_line preserves the line/column suffix when switching from relative to absolute paths.
  • Command Palette invocation behavior conflicts with the editor-focus requirement because the palette itself moves focus away from the editor; the spec needs to define which editor, if any, is targeted.

Verdict

Found: 0 critical, 3 important, 0 suggestions

Request changes

Comment /oz-review on this pull request to retrigger a review (up to 3 times on the same pull request).

Powered by Oz

Comment thread specs/GH10290/SPEC.md Outdated

- `editor.copy_absolute_path` — copies the absolute path of the active editor file to the clipboard.
- `editor.copy_relative_path` — copies the path relative to the project root.
- `editor.copy_path_with_line` — copies the relative path with `:line[:column]` suffix where `column` is appended only when the cursor has an explicit column position (e.g., `src/main.rs:42:7`; `src/main.rs:42` if no column).
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ [IMPORTANT] Define line/column numbering and when column is considered explicit; editor cursors normally have a column, so implementers could emit either :line or :line:column for the same cursor state.

Comment thread specs/GH10290/SPEC.md Outdated

### B4. File outside project root

If the file resolves but is OUTSIDE the project root (e.g., a file opened from `/tmp` while a project is active), `editor.copy_relative_path` and `editor.copy_path_with_line` fall back to the absolute path and surface a toast: "File is outside project root — copied absolute path instead". `editor.copy_absolute_path` is unaffected.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ [IMPORTANT] Specify whether editor.copy_path_with_line preserves the :line[:column] suffix when falling back to an absolute path; as written, both /tmp/foo.rs and /tmp/foo.rs:42 satisfy this sentence.

Comment thread specs/GH10290/SPEC.md Outdated

### B8. Command Palette discoverability

All three actions are visible in the Command Palette unconditionally and searchable by "copy path". When invoked from the Command Palette while the editor is not focused, B3's no-op-with-toast behavior applies.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ [IMPORTANT] Clarify Command Palette invocation targeting: the palette moves focus away from the editor, so this conflicts with the active-editor contract unless it explicitly targets the last active editor pane or defines a distinct no-focused-editor case.

@lonexreb
Copy link
Copy Markdown
Contributor Author

lonexreb commented May 8, 2026

Round-1 fixes for oz-for-oss review: (1) copy_path_with_line numbering pinned — 1-based lines, 1-based codepoint columns, column emitted only when cursor has explicit position (start/end of line emit <path>:N without column); selection uses anchor; tests added for line-only, line+col, selection cases; (2) outside-project fallback for copy_path_with_line preserves the line/column suffix on absolute path with appropriate toast; T_outside_project_with_line added; (3) Command Palette targets the last-focused editor (tracked at invocation time); no-op + toast when no editor was previously focused; actions visible in palette unconditionally; acceptance items added.

@lonexreb
Copy link
Copy Markdown
Contributor Author

lonexreb commented May 8, 2026

/oz-review

@oz-for-oss
Copy link
Copy Markdown
Contributor

oz-for-oss Bot commented May 8, 2026

@lonexreb

I'm re-reviewing this spec-only pull request in response to a review request.

You can view the conversation on Warp.

I completed the review and no human review was requested for this pull request.

Comment /oz-review on this pull request to retrigger a review (up to 3 times on the same pull request).

Powered by Oz

Copy link
Copy Markdown
Contributor

@oz-for-oss oz-for-oss Bot left a comment

Choose a reason for hiding this comment

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

Overview

This spec defines three bindable editor actions for copying absolute, relative, and line-qualified paths. The overall shape is clear, but several requirements need tightening before implementation because they conflict or rely on state the editor may not preserve.

Concerns

  • Project root resolution is defined from the editor file's parent directory, which conflicts with the outside-active-project fallback behavior.
  • The column-inclusion contract depends on cursor placement provenance and has edge cases that can produce contradictory outputs.
  • Unicode column numbering says codepoints while also treating combining marks as part of the base character, which needs one model.

Verdict

Found: 0 critical, 3 important, 0 suggestions

Request changes

Comment /oz-review on this pull request to retrigger a review (up to 3 times on the same pull request).

Powered by Oz

Comment thread specs/GH10290/SPEC.md Outdated
Comment on lines +38 to +42
"Project root" is the same root used by the file tree:

1. The git toplevel (`git rev-parse --show-toplevel`) of the editor file's parent directory.
2. If not in a git repo, the workspace folder containing the file.
3. If neither resolves, the action falls back per B4.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ [IMPORTANT] Resolving the root from the editor file's parent conflicts with B4: a /tmp file inside its own git repo would get that repo as root instead of being treated as outside the active project. Specify whether relative paths anchor to the active workspace/file-tree root or the opened file's containing repo, then align B4/A3/A8.

Comment thread specs/GH10290/SPEC.md Outdated
`editor.copy_path_with_line` produces a deterministic clipboard string for a given cursor/selection state. The contract:

- **Line numbering**: 1-based — the first line is line `1`. This matches universal editor convention (vim, VS Code, IntelliJ).
- **Column numbering**: 1-based — the first column is column `1`. Columns count CHARACTERS (Unicode codepoints), NOT bytes. `<path>:N:K` always points to the K-th codepoint of line N (so a multi-byte UTF-8 character counts as one column; combining marks count as one column at their base codepoint).
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ [IMPORTANT] The column model is internally inconsistent: Unicode codepoints make combining marks separate positions, while 'combining marks count as one column at their base codepoint' describes grapheme-cluster behavior. Pick codepoints or grapheme clusters and update the examples/tests accordingly.

Comment thread specs/GH10290/SPEC.md Outdated
Comment on lines +86 to +94
- **When column is included**: the column suffix is emitted ONLY when the cursor has an EXPLICIT column position. Specifically, include `:column` ONLY when the cursor was positioned via:
- mouse click,
- arrow-key navigation that establishes a column,
- or programmatic positioning that supplied a column.

EXCLUDE the column suffix (emit `<path>:N` only) when:
- the cursor is at the start of a line (col 1),
- the cursor is at the end of a line,
- the cursor was placed via a "go to line N" command without a column.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ [IMPORTANT] These rules require preserving how the cursor reached its position and can contradict themselves when mouse/arrow placement lands at col 1 or end-of-line. Replace the provenance-based rule with a deterministic state-based contract, such as always omit at col 1/EOL and include otherwise, or always include the column.

@lonexreb
Copy link
Copy Markdown
Contributor Author

lonexreb commented May 8, 2026

Addressed all 3 important concerns from the latest oz-for-oss review:

  1. Project root resolution (B2): Re-anchored on the active workspace folder, not the editor file's parent dir. Order: active workspace folder → git toplevel of that folder → no project root (single-file session). "Outside project root" is now well-defined as "editor file's canonicalized path is not a descendant of the canonicalized workspace root". Added explicit symlink canonicalization.
  2. Column-inclusion provenance (B9, A6, T-tests): Replaced provenance-tracking with a deterministic per-cursor boolean HasExplicitColumn whose transitions (set on click/horizontal-arrow/Home/End/programmatic-with-column/edit; unset on goto-line/file-open/vertical-only-arrows) are explicit. Removed the contradictory "col 1" / "end of line" position-based rules. Added new test cases for the previously contradictory scenarios (T_line_col_after_click_at_col_1, T_line_only_after_goto, T_line_col_after_vertical_only).
  3. Unicode column model (B9, T_unicode_grapheme_columns): Picked one model — UAX Core: Can't close command pallete with Esc after creating new tab while command pallete is open #29 extended grapheme clusters — and superseded the codepoint wording. Combining marks + ZWJ emoji + base-char now consistently yield one column at the base grapheme. Test was updated and renamed.

Commit: f613f77

@lonexreb
Copy link
Copy Markdown
Contributor Author

lonexreb commented May 8, 2026

/oz-review

@oz-for-oss
Copy link
Copy Markdown
Contributor

oz-for-oss Bot commented May 8, 2026

@lonexreb

I'm re-reviewing this spec-only pull request in response to a review request.

You can view the conversation on Warp.

I completed the review and no human review was requested for this pull request.

Comment /oz-review on this pull request to retrigger a review (up to 3 times on the same pull request).

Powered by Oz

Copy link
Copy Markdown
Contributor

@oz-for-oss oz-for-oss Bot left a comment

Choose a reason for hiding this comment

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

Overview

This spec defines three bindable editor actions for copying absolute, relative, and line-qualified file paths, with command palette discoverability, fallback behavior, and detailed cursor/column semantics.

Concerns

  • The project-root resolution section is internally ambiguous about whether the workspace folder or git toplevel is the authoritative root. This affects the core relative-path output and should be clarified before implementation.
  • The tests do not cover several root-resolution branches that the behavior contract requires, including workspace fallback, no-workspace fallback, and symlink canonicalization.

Verdict

Found: 0 critical, 2 important, 0 suggestions

Request changes

Comment /oz-review on this pull request to retrigger a review (up to 3 times on the same pull request).

Powered by Oz

Comment thread specs/GH10290/SPEC.md Outdated
Comment on lines +45 to +54
Resolution order (the same root the file tree uses):

1. **Active workspace folder** — the folder backing the currently active
Warp workspace (the same folder shown at the top of the file tree).
This is the canonical "project root".
2. **Git toplevel of the workspace folder** — when the workspace folder is
itself inside a git working tree, run `git rev-parse --show-toplevel`
on the **workspace folder** (not the editor file's parent directory)
and use the toplevel as the project root. This widens the root for
monorepos where the workspace was opened on a sub-directory.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ [IMPORTANT] This resolution order contradicts the stated git-toplevel-first behavior: step 1 calls the workspace folder canonical, then step 2 overrides it with the git toplevel. Specify the exact algorithm so implementers know whether relative paths anchor to the workspace folder or the git root.

Suggested change
Resolution order (the same root the file tree uses):
1. **Active workspace folder** — the folder backing the currently active
Warp workspace (the same folder shown at the top of the file tree).
This is the canonical "project root".
2. **Git toplevel of the workspace folder** — when the workspace folder is
itself inside a git working tree, run `git rev-parse --show-toplevel`
on the **workspace folder** (not the editor file's parent directory)
and use the toplevel as the project root. This widens the root for
monorepos where the workspace was opened on a sub-directory.
Resolution algorithm (the same root the file tree uses):
1. Start with the **active workspace folder** — the folder backing the currently active
Warp workspace (the same folder shown at the top of the file tree).
2. If that workspace folder is itself inside a git working tree, run
`git rev-parse --show-toplevel` on the **workspace folder** (not the editor
file's parent directory) and use the toplevel as the project root. This widens
the root for monorepos where the workspace was opened on a sub-directory.
3. If git toplevel resolution fails, fall back to the active workspace folder.

Comment thread specs/GH10290/SPEC.md
## Tests

- T1. `copy_absolute_path` produces the absolute path for the active editor file.
- T2. `copy_relative_path` produces the path relative to the git toplevel.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ [IMPORTANT] B2 specifies workspace-folder fallback, no-workspace fallback, and symlink canonicalization, but the tests only cover the git-toplevel branch. Add tests for those required branches so implementation cannot satisfy the spec while skipping root-resolution edge cases.

Two important concerns:

1. Project-root resolution authority. Promoted git toplevel to step 1
   and made workspace folder the explicit fallback at step 2. Added
   an authoritative-rule paragraph stating: 'project root = git
   toplevel containing the active workspace folder when one exists,
   otherwise the active workspace folder itself'. Removed earlier
   'canonical' wording about the workspace folder. Documented why
   git toplevel wins (monorepo sub-package case).

2. Test coverage for root-resolution branches. Added six new tests:
   - T_root_git_toplevel_in_monorepo (B2.1 precedence)
   - T_root_workspace_fallback_no_git (B2.2 no-repo fallback)
   - T_root_workspace_fallback_git_failure (B2.2 git error fallback)
   - T_root_no_workspace_fallback (B2.3 no-workspace path)
   - T_root_symlink_canonicalization_inside / _outside / _failure
     (canonicalization edge cases)
@lonexreb
Copy link
Copy Markdown
Contributor Author

lonexreb commented May 8, 2026

Addressed both round-N review concerns.

1. Project-root resolution authority (B2). Rewrote B2 to make git toplevel the authoritative project root and the workspace folder the explicit fallback. Added a single authoritative-rule paragraph: 'project root = git toplevel containing the active workspace folder when one exists, otherwise the active workspace folder itself'. Removed earlier 'canonical' wording about the workspace folder. Documented the rationale (monorepo sub-package case where users open a sub-package as workspace but expect repo-rooted relative paths).

2. Test coverage for root-resolution branches. Added six new tests covering every B2 branch:

  • T_root_git_toplevel_in_monorepo — B2.1 precedence over B2.2
  • T_root_workspace_fallback_no_git — B2.2 fires when no git repo
  • T_root_workspace_fallback_git_failure — B2.2 fires when git rev-parse fails (corrupt repo)
  • T_root_no_workspace_fallback — B2.3 single-file editor session
  • T_root_symlink_canonicalization_inside — symlink resolves into project
  • T_root_symlink_canonicalization_outside — symlink resolves outside
  • T_root_symlink_canonicalization_failure — dangling symlink graceful fallback

@lonexreb
Copy link
Copy Markdown
Contributor Author

lonexreb commented May 8, 2026

/oz-review

@oz-for-oss
Copy link
Copy Markdown
Contributor

oz-for-oss Bot commented May 8, 2026

@lonexreb

I'm re-reviewing this spec-only pull request in response to a review request.

You can view the conversation on Warp.

I completed the review and no human review was requested for this pull request.

Comment /oz-review on this pull request to retrigger a review (up to 3 times on the same pull request).

Powered by Oz

Copy link
Copy Markdown
Contributor

@oz-for-oss oz-for-oss Bot left a comment

Choose a reason for hiding this comment

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

Overview

This spec defines three new bindable editor copy-path actions, including relative path resolution, Command Palette behavior, line/column suffix behavior, acceptance criteria, and tests.

Concerns

  • The project-root cache can become stale when git metadata changes without a workspace switch.
  • The line/column contract describes columns as grapheme clusters, but cursor columns are insertion points and end-of-line behavior is undefined.
  • HasExplicitColumn is described inconsistently as per-document and per-cursor state, leaving multi-cursor and selection behavior ambiguous.

Verdict

Found: 0 critical, 3 important, 0 suggestions

Request changes

Comment /oz-review on this pull request to retrigger a review (up to 3 times on the same pull request).

Powered by Oz

Comment thread specs/GH10290/SPEC.md
Comment on lines +78 to +79
The resolved project root is cached per workspace and invalidated when the
active workspace changes or the workspace folder moves.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ [IMPORTANT] The cache invalidation contract omits git metadata changes. If a workspace is opened before/after git init, .git is deleted or repaired, or a worktree is retargeted, the cached root can produce stale relative paths until the workspace changes; specify watching git/worktree state, a TTL, or resolving on each action.

Comment thread specs/GH10290/SPEC.md
`editor.copy_path_with_line` produces a deterministic clipboard string for a given cursor/selection state. The contract:

- **Line numbering**: 1-based — the first line is line `1`. This matches universal editor convention (vim, VS Code, IntelliJ).
- **Column numbering**: 1-based — the first column is column `1`. Columns count **Unicode extended grapheme clusters** (per UAX #29 GraphemeBreakProperty), NOT bytes and NOT raw codepoints. `<path>:N:K` always points to the K-th grapheme cluster of line N. Concretely:
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ [IMPORTANT] This defines K as the K-th grapheme cluster, but cursor positions are insertion points; the tests expect cursor-after-first-grapheme to emit column 2, and end-of-line would be len + 1 with no K-th cluster. Define columns as insertion-point indices between grapheme clusters and explicitly cover end-of-line.

Comment thread specs/GH10290/SPEC.md
- An emoji ZWJ sequence (e.g., 👨‍👩‍👧) is one column.
- Tabs (`\t`) count as one column. Width-aware tab expansion is NOT performed.
- This single grapheme-cluster model supersedes any earlier wording that said "codepoints" — the prior phrasing is replaced by this one.
- **When column is included** — driven by an explicit per-document boolean **`HasExplicitColumn`** that the editor maintains for each cursor position. The clipboard suffix is emitted iff `HasExplicitColumn == true`. The flag transitions are deterministic and source-of-input agnostic; provenance is not tracked at copy time:
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ [IMPORTANT] HasExplicitColumn is described as a per-document boolean while also being maintained for each cursor position, and acceptance later says per-cursor. Specify whether the flag is per cursor/anchor/selection and how it transfers when cursors are added, merged, restored, or selected.

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

Labels

cla-signed external-contributor Indicates that a PR has been opened by someone outside the Warp team.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant