Skip to content

feat(cli): open a file at path and line from the command line#321

Open
jongio wants to merge 1 commit into
mainfrom
idea/cli-open-file
Open

feat(cli): open a file at path and line from the command line#321
jongio wants to merge 1 commit into
mainfrom
idea/cli-open-file

Conversation

@jongio

@jongio jongio commented Jul 12, 2026

Copy link
Copy Markdown
Owner

What

Lets grut accept a file path, optionally with a line number, as its positional argument. grut internal/git/client.go opens with that file selected and shown in the Preview panel; grut internal/git/client.go:120 also scrolls the preview to line 120. Passing a directory works exactly as before.

Closes #317

Why

grut [dir] only took a directory, so opening a specific file meant launching grut and walking the tree by hand. Accepting a file makes grut a usable jump target from shell pipelines and quick edits, for example grut $(git rev-parse --show-toplevel)/README.md:1.

How

  • cmd/startup_target.go adds a pure resolveStartupTarget(arg, statFn) helper that classifies the argument. An existing directory roots grut there; an existing file roots grut at the file's directory and selects the file; a file:line reference where the file exists and the line is a positive integer opens that file at the line. Anything else resolves to an empty target and grut opens in the current directory as before. The stat function is injected so the parsing is unit tested without the filesystem, and the last colon is used as the line separator so Windows drive letters like C:\path are not misread.
  • cmd/root.go sets Use: "grut [path]" and Args: cobra.MaximumNArgs(1) (required because the root command has subcommands, otherwise cobra rejects any positional arg), resolves the argument right after the --demo block, and os.Chdirs into the target directory before the git client and file tree pick up the working directory. Resolved paths are absolute so the chdir does not invalidate them.
  • internal/tui/app.go gains WithInitialFile(path, line). When a file is set, Init focuses the preview panel and emits a RevealFileMsg carrying the path and line.
  • internal/panels/messages.go adds a 1-based Line field to FileSelectedMsg and RevealFileMsg (0 means no line). internal/panels/filetree threads the line through when revealing the cursor file, and internal/panels/preview remembers a pending goto-line and applies it once the file content has loaded (the load is async, so the jump runs on fileLoadedMsg, not on selection).

Testing

  • go test ./cmd/... ./internal/panels/... ./internal/tui/... -count=1 passes, including new tests for the argument resolver (directory, file, file:line, nonexistent path, trailing colon, non-numeric and zero lines, and a Windows drive-colon case), the cobra Args validation, filetree line threading, preview scroll-on-load, and preview staying at the top when no line is given.
  • go build ./..., go vet, gofumpt -l, and golangci-lint run ./cmd/... ./internal/panels/... ./internal/tui/... are clean.

grut only accepted a directory as its positional argument, so jumping to a
specific file meant opening grut and then navigating the tree by hand. Now
the argument can also name a file, optionally with a ":line" suffix.

Passing a file roots grut at that file's directory, selects it in the File
Tree, focuses the Preview panel, and scrolls to the line when a ":line"
suffix is present. Passing a directory behaves exactly as before. A path
that does not exist, including a bogus "name:42", is ignored and grut opens
in the current directory, matching the prior behavior.

Argument parsing lives in a pure resolveStartupTarget helper with an
injected stat function so the file-vs-directory and ":line" cases are unit
tested without touching the filesystem. The last colon is used as the line
separator so Windows drive letters such as C:\path are not misread.

Closes #317

Co-authored-by: Copilot App <[email protected]>
@jongio jongio added the idea Feature idea from the idea pipeline label Jul 12, 2026
@jongio jongio self-assigned this Jul 12, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

idea Feature idea from the idea pipeline

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Open a file at path and line from the command line

1 participant