Repository navigation
How Editing Code Works
This chapter covers what happens inside a CodeMirror editor once a file is open: the Edit and Code menu commands and their keys, multiple cursors, Go to Line. How code looks (line spacing, render whitespace, the current line, indentation, zoom) is in How code appearance works. Tabs, loading and saving are in How the editor works; completion, folding, guides and the languages are in How Editor Features Work. The user side is in Editing Code.
People move between Nibtree and their IDE all day. If Cmd+/ comments a line there, it must comment a line here too, and the menu bar should list the commands so they can be found. The keys follow the conventions most code editors share, and the macOS ones where they differ.
EDITOR_SHORTCUTS in src/lib/editor/editorShortcuts.ts lists every Edit and Code menu command with its key in CodeMirror notation (Mod-Shift-d) and where it is bound:
-
default: CodeMirror'sdefaultKeymapalready binds it (Cmd+/, Shift+Cmd+K, Option+Up, Cmd+]). -
find: the find bar'sfindKeymap(see How find and replace works). -
code: our owncodeKeymapineditorCommands.ts, listed before the default keymap so Shift+Cmd+U is Toggle Case rather than CodeMirror's Redo Selection.
menuSpec.ts builds the Edit and Code menus from the same table with acceleratorFor, so a menu always shows the key the editor really binds.
flowchart TD
Key["Key press"] --> Page{"Focused editor binds it?"}
Page -->|"yes"| Run["Command runs in the editor"]
Page -->|"no"| Menu["macOS passes it to the menu"]
Click["Menu item clicked"] --> Action["runMenuAction"]
Menu --> Action
Action --> Editor["runEditorCommand: focusedEditor()"]
Editor -->|"an editor has focus"| Run
Editor -->|"none"| Nothing["Nothing happens"]
macOS gives a key to the web view first and only hands it to the menu when the page leaves it alone, so a key and its menu item never both run. A menu click goes through runMenuAction, which calls runEditorCommand: it finds the CodeMirror view that holds the focus (focusedEditor) and runs the entry from EDITOR_COMMANDS. Entries marked inText need the caret in the text; Find and friends also work from the find bar's fields.
editorFocus (menu/editorFocus.svelte.ts) follows focusin and focusout only, so typing costs nothing. menuState.ts uses it: the editing commands need a focused, writable editor; folding, Go to Line and Select Next Occurrence only a focused one. The menu routing in general is in How the menus work.
Most commands are CodeMirror's own (toggleComment, deleteLine, moveLineUp, foldCode...). The ones it lacks live in textCommands.ts as pure functions from an EditorState to a transaction spec, so they are tested without a view:
-
duplicateSelection: a selection is copied after itself, a caret copies its line below. -
joinLines: the break and the indentation around it become one space, none next to a blank line. -
toggleCase: upper case when the text has a lower case letter, otherwise lower case; a caret works on its word. -
sortLines: the selected lines, or the whole file, by character code so the order never depends on the system language. -
parseLineTargetandlineTargetPositionfor Go to Line, which asks throughdialogs.prompt("Line[:column]") and checks that the editor still exists when the dialog closes.
baseExtensions in setup.ts turns on allowMultipleSelections and makes Option+Shift+click add a cursor (clickAddsSelectionRange), so Cmd+D and Select All Occurrences can leave many carets.
| File | What it does |
|---|---|
src/lib/editor/editorShortcuts.ts |
The key of every Edit and Code command, acceleratorFor
|
src/lib/editor/editorCommands.ts |
EDITOR_COMMANDS, codeKeymap, focusedEditor, runEditorCommand, Go to Line |
src/lib/editor/textCommands.ts |
Duplicate, Join Lines, Toggle Case, Sort Lines, line targets |
src/lib/editor/setup.ts |
editorTheme and baseExtensions
|
src/lib/menu/menuSpec.ts, menuActions.ts, menuState.ts
|
The Edit and Code menus, their routing and enabling |
src/lib/menu/editorFocus.svelte.ts |
Which editor has the focus |
One table, two users. The editor keymaps and the menu accelerators come from EDITOR_SHORTCUTS, and a test checks that each key is bound to the menu item's own command. A menu can never show a key that does something else.
Editor keys where they clash. Cmd+D adds the next occurrence, so Duplicate takes Shift+Cmd+D. Cmd+L is Go to Line (CodeMirror's select line is Ctrl+L on macOS and stays). The fold keys use Option+Cmd+] and [ because Cmd+= and Cmd+- stay with zoom.
-
src/lib/editor/editorShortcuts.test.ts: accelerators, every key bound to its command, no key used twice. -
src/lib/editor/textCommands.test.ts: Duplicate, Join Lines, Toggle Case, Sort Lines and Go to Line input. -
src/lib/menu/menuSpec.test.tsandmenuState.test.ts: the menu items, keys and enabled states.
- A new editor command: add it to
EditorAction,EDITOR_SHORTCUTS,EDITOR_COMMANDSand the Code menu, then update Editing Code and Keyboard Shortcuts.
None on this page yet. The selection hidden by the current line highlight is in How code appearance works and How the editor works.
These pages are generated from docs/wiki. To correct something, open a pull request against the source file; edits made here are overwritten.
Using Nibtree
- Getting Started
- Welcome Screen
- Workspaces
- Files Panel
- File Operations
- File Icons
- Rounded Panels
- Editor and Tabs
- Pin Reorder and Wrap Tabs
- Single Tab Title
- Split Editor
- New File and Unsaved Changes
- Unload Hidden Tabs
- Editing Code
- Code Appearance
- Markdown Editor
- Image and PDF Preview
- Search Everywhere
- Recent Files
- Navigation Bar
- Find and Replace
- Changes and Commits
- Sign In Prompts
- Commit Box Layout
- Repository Actions
- Commit Options
- Ignoring Files
- Diffs
- Inline Diffs
- Staging View
- Revert a Change
- Resolving Conflicts
- Merge Tool
- Blame
- History and Log
- Merge Commits
- Branches and Tags
- Branches Popup
- Remotes
- Stashes
- Shelf
- Git Menu
- Git Dialogs
- Interactive Rebase
- Worktrees
- AI Agents
- AI Tasks
- AI Usage
- Keep Computer Awake
- Submodules
- Git LFS
- GitHub
- Git Console
- Terminal
- Terminal Features
- Scripts
- Navigation
- Menus
- Settings
- Settings Search
- Color Themes
- Settings Terminal and Automation
- Settings Files
- Updates
- Status Bar and Help
- Notifications
- Memory Use
- Clear Cache
- MCP and CLI
- Git Mergetool
- Keyboard Shortcuts
- Keyboard Shortcuts Editor
- Troubleshooting
- Troubleshooting Git
Developers
- Developer Guide
- Architecture
- Project Layout
- Backend
- How Folder Watching Works
- Folder Watching Bugs We Fixed
- How Workspace Files Work
- How the Preview Scheme Works
- Backend Services
- How Terminal Features Work
- Terminal Bugs We Fixed
- Frontend
- How the Rich Markdown Editor Works
- Rich Markdown Bugs We Fixed
- Git Menu Links
- Menu Keys and Routing
- Frontend Modules
- Blame Bugs We Fixed
- Project Color And Title Bar
- One Diff View
- Diff Bugs We Fixed
- Merge Tool Layouts
- Merge Tool Bugs We Fixed
- Color Theme Bugs We Fixed
- How the Connected Diff Works
- Rounded Panels Bugs We Fixed
- Update Bugs We Fixed
- Status Bar Bugs We Fixed
- MCP and CLI Bugs We Fixed
- Repository Actions Bugs We Fixed
- How the Changes Tab Works
- CodeMirror Patterns
- How the Cursor Works
- How Editor Features Work
- How the Path Bar Works
- Commands and Events
- Settings Reference
- Commands Workspace and Changes
- Commands Branches and Remotes
- Commands History and Extras
- Commands App and Tools
- Testing
- Test Suites
- Debugging
- Debugging with MCP
- Measuring Setting Memory
- How Terminal Memory Is Measured
- How Markdown Diagrams Work
- Releases and CI
- Publishing the Wiki
- Versioning and Changelog
- Browser Version Packages
- Browser Release Checks
- Docker Image
- Platforms and Signing
- Windows Support
- Windows Bugs We Fixed
- Windows MCP and CLI
- Docs and Screenshots
- UI Library
- UI Library MCP
- UI Library Bugs
- UI Library Migration
- Contributing
How features work
- Welcome screen
- Workspaces and folders
- Files panel
- File operations
- File icons
- Editor and tabs
- Pin, reorder and wrap tabs
- Single tab title
- Split editor
- New File and unsaved changes
- Unload hidden tabs
- Editing code
- Code appearance
- Markdown editor
- Image and PDF preview
- Search Everywhere
- Recent Files
- Navigation Bar
- Find and replace
- Changes and commits
- Sign-in prompts
- Commit box layout
- Repository actions
- Commit options
- Ignoring files
- Diffs
- Inline diffs
- Staging view
- Revert a change
- Resolving conflicts
- Merge tool
- Blame
- History and log
- Merge commits
- Commit tabs
- Branches and tags
- Branches popup
- Remotes
- Stashes
- Shelf
- Git menu
- Git dialogs
- Interactive rebase
- Worktrees
- AI agents
- AI usage
- Keep computer awake
- AI tasks
- Submodules
- Git LFS
- GitHub account
- Git Console
- Back and Forward
- Integrated terminal
- Scripts tool window
- Menu bar
- Settings
- Search in Settings
- Color themes
- Rounded panels
- Updates and release channels
- Status bar and help links
- Notifications
- Memory readout and memory log
- Clear Cache
- MCP server and command line tool
- git mergetool mode