feat: find-in-files sidebar, command bar with :cd re-root, terminal tabs + diff viewer - #46
Open
jaivial wants to merge 8 commits into
Open
feat: find-in-files sidebar, command bar with :cd re-root, terminal tabs + diff viewer#46jaivial wants to merge 8 commits into
jaivial wants to merge 8 commits into
Conversation
Opens $SHELL in a real tab via "Open terminal in new tab" in the ≡ menu (or Esc-`), so tests and git are one keystroke away instead of a second tmux pane. The shell runs on a PTY (creack/pty) and is rendered by an embedded VT emulator (hinshun/vt10x) into the editor pane. We own the emulation rather than passing escape codes through to the host terminal — that's what lets the shell live in a sub-rectangle without fighting the editor for cursor position and scroll region, and it means no tmux passthrough config is needed. Both deps are pure Go, so the single static no-CGO binary is preserved. Notes on the less obvious decisions: - Adds Tab.IsTextual() and moves the existing !IsImage() guards onto it. Nearly every one of those guards meant "is this a normal text tab"; with only one alternate mode that was accidentally the same thing. Terminal tabs would otherwise have slipped through save, find, the git gutter, disk reconciliation, and the dirty-quit prompt. - The PTY reader goroutine never touches UI state. It parses into the emulator (internally locked) and posts termOutputEvent so the main loop redraws, matching the existing autoScroll/treeRefresh pattern. - Close() hangs up the child's process *group* with SIGHUP before escalating to SIGKILL. SIGKILLing the shell directly means bash never runs its exit path and every backgrounded job is orphaned. It also returns early if the child was already reaped, so a recycled PID can never be signalled. - The Esc-leader table stands down inside a terminal. A shell prompt is where users hit Esc by reflex, and swallowing the next rune to run an editor action is destructive: Esc then q would quit the editor and hang up every running shell. Double-Esc still opens the menu, so nothing becomes unreachable. - glyphStyle deliberately ignores the reverse-video bit: vt10x already swaps FG/BG into the stored cell while leaving the bit set, so honouring it double-swaps and cancels the highlight. Relatedly, termColor resolves the default-colour sentinels by meaning rather than by slot, or a reversed default cell collapses back to the normal pair. Terminal tabs are unix-only; the menu row is disabled on Windows, where creack/pty returns ErrUnsupported. Verified on linux/darwin/windows for amd64/arm64.
Add persistent Find in files surface in the sidebar, grouped into per-file accordions; matches jump to line on click. Independent state from the Esc-F modal so both search surfaces coexist. Also add far-right terminal tab button in the tab bar.
Esc-: (or the ≡ menu) opens a bottom command line. First built-in command: cd, which re-roots the editor — file tree, project finder index, git status and search surfaces all point at the new directory (nvim :cd behavior). Tab completes directories with common-prefix extension then cycles; Up/Down and click adopt a suggestion; invalid paths keep the bar open with a hint. Open tabs survive the re-root. Tests: commandbar_test.go (completion, cd validation, re-root wiring, leader routing, drawing); menu layout expectations updated for the new row. Full suite passes with -race.
commandSearchBase dropped the directory prefix for absolute tokens, so '/var/www/new' + Tab completed to bare 'newvillacarmen' instead of '/var/www/newvillacarmen'. The absolute branch now splices the dirname back in (single slash at filesystem root). Adds a regression test covering nested absolute completion, cycling, and root-level tokens.
Draws a + button right after the last tab (or after the menu button when no tabs are open) that opens and focuses a new unsaved scratch tab. Styled like the far-right terminal button.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
1. Summary
SpiceEdit had no way to search across a project from the sidebar, no way to change the working directory once the editor was running, and no shell access. Users opening
spiceeditin a directory had to quit and relaunch from another path to browse a different project, and grep-style cross-file search only existed as a centered modal that clobbered its state on every open.This PR adds four user-facing features built on the existing modal and event-loop patterns. First, a persistent Find-in-files panel in the sidebar with a Files/Find header tab strip — a second grep surface whose query and results survive independently of the Esc-F modal, grouped into per-file accordions with click-to-jump. Second, a command bar (Esc-: or the menu) whose first built-in command is
cd: it re-roots the file tree, the finder's project index, git status and every search surface at the target directory — nvim's:cdbehavior — with bash-style Tab completion (common-prefix extension, then cycling;~, absolute and relative paths; trailing-slash drill-down). Third, terminal tabs: a PTY-backed shell runs inside an editor tab, opened with Esc-`, the ≡ menu, or a "+" button pinned to the far right of the tab bar — keystrokes go straight to the child shell, and the tab closes with its usual × affordance. Fourth, a git-changes diff viewer, plus a fix so absolute-path completion splices the full directory prefix back into the token.All six commits were developed and merged incrementally via fork PRs #1–#3 with
go test -race ./...green at every step.2. Root Cause (bug fix df72919)
cd /var/www/newand pressing Tab completed to barenewvillacarmen, dropping/var/www/from the token.commandSearchBaseininternal/app/commandbar.goreturned an empty splice prefix for absolute tokens, so candidates were inserted without their directory part./var/www/newvillacarmen.TestCommandCompletionAbsolutepins nested absolute completion, Tab-cycling prefix survival, and root-level tokens.3. Flow
sequenceDiagram participant U as User participant CB as Command bar participant FS as Filesystem participant T as File tree participant F as Finder index U->>CB: Esc : cd /var/www/new + Tab CB->>FS: ReadDir(/var/www) FS-->>CB: matching directories CB-->>U: common prefix, then cycle U->>CB: Enter CB->>FS: Stat(target) FS-->>CB: valid dir CB->>T: rebuild at new root CB->>F: recreate + Rebuild (background) F-->>CB: finderRebuiltEvent CB-->>U: sidebar + finder point at new rootScreenshots
Command bar with directory completion — suggestion popup above the input, match count on the right:
Absolute-path completion fix —
/tmp/opencode/spicedemo/new+ Tab completes the full path, not just the last segment:After
cd+ Enter — the sidebar re-roots at the new directory (nvim:cdbehavior), open tabs survive, and the status bar flashes the new root:Sidebar Find-in-files panel — Files/Find tab strip, per-file accordions with match lines:
Terminal tab — a shell running inside an editor tab (Esc-` or the ≡ menu), file tree and terminal side by side:
New-terminal "+" button — pinned to the far right of the tab bar; clicking it opens a second terminal tab:
4. Changes
App (event loop / UI)
internal/app/app.gorerootsupport wiring, menu row for the command bar.internal/app/commandbar.go~/absolute/relative),cdvalidation,rerootof tree/finder/git status/search state.internal/app/sidebarsearch.gointernal/app/searchfiles.gointernal/app/diffviewer.gointernal/app/leader.go:command bar,Ffind-in-files.internal/app/modals.gocloseAllModals/anyModalOpencover the command bar and new modals.internal/app/find.gointernal/app/gitstatus.goEditor / Finder
internal/editor/terminal.gointernal/editor/terminal_unix.go,internal/editor/terminal_windows.gointernal/editor/tab.gointernal/editor/comment.gointernal/finder/grep.gointernal/icons/icons.goTests
internal/app/commandbar_test.gocdvalidation, re-root wiring, Esc-: routing, drawing.internal/app/sidebarsearch_test.gointernal/app/searchfiles_test.go,internal/app/diffviewer_test.go,internal/app/terminal_test.go,internal/editor/terminal_test.gointernal/finder/grep_test.gointernal/app/app_test.goConfig / Docs
README.mdgo.mod,go.suminternal/version/version.go5. Verification
go test -race ./...— all 12 packages pass on every commit (run before each fork PR merge).gofmt -l— no new unformatted files.go vet ./...— clean.cd /tmp/…/spicedemo/shows 4 directory candidates;…/new+ Tab completes the full absolute path; Enter re-roots the sidebar (see screenshots); clicking the Find tab and typingfuncreturns 4 per-file accordions; Esc-` opens a terminal tab, the far-right "+" button opens a second one, and both shells accept input in parallel with the editor.tcell.NewSimulationScreenfor draw assertions,t.TempDir()for filesystem state.