Skip to content

Search

Two ways to find things beyond the current file, sharing one rule: the scope is a visible, editable path. Both panels show exactly where they look — the current document's project by default — and widening the search is literally editing that path (up to ~ or / if you like). Search never silently looks somewhere you did not expect.

Being a path, the scope completes like one on macOS: typing forward offers the folders that finish the component under the caret, ~ included. A scope that is not there says so, and names the nearest folder that is — usually one typo away from the one that was meant. Blanks around a pasted path are ignored.

Both walks are gitignore-aware, skip hidden files, and cap file sizes, courtesy of ripgrep's own engine embedded in the core — not a subprocess.

Search Everywhere (⇧⌘A)

One box for files, symbols, commands and settings (Ctrl+Shift+A on Linux). What is typed is matched against all four at once, and each kind of answer comes in a group of its own, the group with the best match first: typing save puts the Save commands above a save.md, typing main puts main.rs above a command that mentions it. A group shows a few rows, enough to choose from with the next group still in view. Files are the project's, matched by name first; symbols are the language server's, and arrive when it answers; commands are the menus'; settings are the editor's on/off switches, each listed with whether it is on. With nothing typed it lists the files opened lately and the commands run lately. The selected file or symbol shows its code beside the list. ⏎ opens a file or a symbol, runs a command, and flips a setting — saved, and in effect at once, as if changed in Settings.

Type fragments of a file name — editwc finds EditorWindowController.swift — with fuzzy matching and ranking. ↑/↓ move the selection, ⏎ opens (fronting the window if the file is already open), ⎋ closes. An empty query browses the scope alphabetically. The same stacked filters as Find in Project apply here too — every filter kind refines the matched paths — and the status line says how many matches the filters pruned.

⏎ opens the selected row once the rows answer what was typed; while a search is still owed — the query changed a moment ago — it searches instead, so refining a query never opens a file by accident. ⌘⏎ (Ctrl+⏎ on Linux) opens regardless. ↑/↓ move the selection, ⎋ closes, and a double-click opens too. The status strip spells this out. The panel can be widened, and keeps its size; in a row the directory gives way first, losing its head, so the file name is always readable.

Under the list, the selected row's place is shown: a file's first lines in Open Quickly, a match's line among the lines around it in Find in Project, a symbol's declaration in Go to Symbol — read from the open document when the file is open, which may be ahead of the disk. The divider between the two can be dragged.

The scope is walked once when the panel opens, then matched in memory — typing stays instant on a real repository, and the file count in the status line tells you what is being searched. A scope with more files than the walk limit says so, so "narrow the scope" is advice rather than a guess.

Find in Project (⇧⌘F)

The query is the text to find, exactly as typed — what gets pasted into a search is code, and code is full of (, . and [. The .* toggle beside the query reads it as a regular expression instead, and stays as you left it until the editor quits. Results stream in as path:line: text. ⏎ jumps straight to the matching line. Results are capped (200) to stay instant; refine the pattern rather than scrolling.

Case follows the smart-case rule ripgrep made familiar: an all lowercase query matches any case, while a query containing an uppercase letter is matched exactly. So todo finds TODO, and TODO finds only TODO.

A line under the results says what the search did — "18 matches in 4 files · 812 searched", "No matches in 812 files searched", or the reason nothing could be searched at all (a scope that does not exist, one where everything is ignored, or a bad pattern, quoted). An empty result is never mute, so a mistyped pattern or a wrong scope announces itself instead of looking like an absence of matches.

Stacked filters

Under the Find in Project query, + Add Filter stacks refinements:

  • line contains / line excludes — the matching line's text;
  • file contains / file excludes — the hit's file path.

Filters are case-insensitive substrings and combine with and: lines with foo where bar also appears, but not in files with test in the name, is the query foo plus line contains bar plus file excludes test. File excludes prune whole files before they are even opened, so filtered searches stay as fast as plain ones.

The selected word

Selecting a whole word marks the other places it appears in the visible text — a grey tint, distinct from the selection and from the find bar's matches, so the three stay apart. Selecting part of a word, or a stretch spanning several, marks nothing: those selections were made for some other reason.

⎋ puts the marks away and leaves the selection, so the word is still there to act on. Editing or selecting elsewhere asks the question again. The search covers what is on screen, which is what a mark can be seen on, so a long file costs what a short one does.

Two settings in Settings ▸ Editor decide what counts: whether Item is item, and whether item inside items is one. Both are off by default — a name that differs in case is a different name, and the word was selected as a word.

The jump stack

Every jump — a search result, Jump to Definition, a reference, an outline entry, a chum open, Go to Line, the next finding — records where it left from. So do Find Next and Go to Block Start/End when where they go is out of sight; a step to a match already on screen is not a jump. Go Back (⌃⌘←) walks those origins; Go Forward (⌃⌘→) retraces. Each puts back what was there when the place was left: the caret, what was selected, and how far the view was scrolled. The history is rewritten from the current point: jumping somewhere new discards the forward trail. Positions survive edits as line/column, so going back after a change lands close, not lost.

Go to Last Edit (⇧⌘⌫, Ctrl+Shift+Backspace on Linux) goes to where something was last changed, in whichever open file that was; pressed again with the caret still there, it goes to the change before, and so back. A burst of typing on a few lines is one place, loading or reloading a file is none, and the places move with the text as it is edited around them. Each is a jump, so Go Back returns. The trail is kept for the session: a file closed takes its places with it.

Recent Files (⌘E, Ctrl+E on Linux) lists the files opened lately in the project in front, most recent first, without the one in front: ⌘E and Return goes back to the file before. Recent Places (⌥⌘E, Ctrl+Alt+Shift+E on Linux) lists where the caret has been — the places jumps left from and the places edited, in any file — most recent first, one entry per place, each with the code around it beside the list. Both filter as you type. Places, like the edit trail, last the session.

A jump to a line already on screen leaves the view where it is. One out of sight moves the view, in a short animation, so the line sits a third of the way down, with what comes before it in view; a far jump starts the motion a screen short of the target, so it shows which way it went without sweeping through the whole file. With reduced motion asked for in the system settings, the view moves at once.

Not there yet

  • Search Everywhere lists only the settings that are on or off; a tab width or a theme is chosen in Settings. Two taps of Shift do not open it.
  • Replace across files.
  • Explicit case/whole-word toggles in the panel (smart case covers the common case, and the pattern itself can express both).
  • Persisted search history.
  • Editing the scope in the GTK shell, whose panels search the current project only; path completion comes with it.