nved → an introduction
nved: an introduction
The ved tutorial taught you to drive a line editor: address a line, name a command, watch it replay against the buffer. nved keeps the addressing and changes the verb. Where ved replays commands against text, nved prints the lines you addressed and lets you climb into that block and move a real cursor through it, changing the characters directly. It is the same ed heritage with a cursor bolted on — and no full-screen takeover to pay for it.
That is the one genuinely new idea, and the rest of this page turns on it. There is no alternate screen, no viewport that owns your terminal. Output scrolls normally and your scrollback stays intact, the way cat leaves it. You print a range, climb in, edit, and step back down to the prompt — the editing happens inside ordinary scrollback, just above a bare >. If you have used ved, you already know the addresses; what is new is the climb.
Starting a session
Launch nved with a file name and it loads the file into its buffer. With no argument it starts on an empty, unnamed buffer. Either way you land at a > prompt, waiting for a command.
$ nved notes.txt
>
You can also open straight to a range. A +spec argument runs one command on startup, so the block you want is already on screen before you type anything — spec is any of the addressing commands below.
$ nved +42 notes.txt # open with line 42 printed
$ nved +10.30 notes.txt # ... lines 10 through 30
$ nved +tail notes.txt # ... the last screenful, ready to climb into
Type h (or H, or ?) at any time for the full command and key reference. That is the fastest way to remind yourself of anything on this page once you are working.
Printing a range
nved addresses lines by number, and a bare address simply prints. There is no roaming current line and no "press Enter to advance" — you say which lines you want and they print as a block, ready to climb into.
| Command | Prints |
|---|---|
42 | Line 42 |
10.30 | Lines 10 through 30 |
10. | From line 10 to the end |
.30 | From the start to line 30 |
. | The whole file |
$ | The last line |
$-9.$ | The last ten lines |
head / head N | The first screenful, or the first N lines |
tail / tail N | The last screenful, or the last N lines |
The separator between two numbers is a period, not a comma — it sits under your right hand on the numeric keypad, where there is no comma. A , works in its place anywhere, so 10,30 and 10.30 are the same range. Print a few lines and they appear above the prompt with a faint line-number gutter:
> 1.3
1 The quick brown fox
2 jumps over the lazy dog
3 and trots back home again
>
The $-N form counts back from the end, so $-9.$ is "nine before the last, through the last" — the last ten lines. tail is the everyday shorthand for exactly that: bare tail brings the last screenful on screen ready to edit, and head is its mirror at the top. Out-of-range numbers clamp to the nearest real line rather than erroring. A range taller than the terminal prints one screenful from the top; Page Up and Page Down reprint the screenful above or below, so paging is a fresh print, not a scroll — the page stays cat-shaped.
Climbing into the block
This is the move the whole editor is built around. The block you just printed sits directly above the prompt. To edit it, you climb from the prompt up into the block with an arrow key, and a real cursor appears in the text.
| From the prompt | Lands the cursor |
|---|---|
| Up | On the bottom line of the block |
| Left | At the end of the last line |
| Ctrl+Home | On the first line of the block |
Once you are in, it edits the way any cursor editor does. Arrows move; Ctrl+Left and Ctrl+Right skip by words; Home / End and Ctrl+Home / Ctrl+End jump to the edges. Typing inserts, Enter splits a line in two, and Backspace or Delete at a line edge joins lines back together. Long lines are word-wrapped by nved itself, with the continuation indented to the gutter, and the cursor moves by logical line so wrapping never trips you up.
Leaving the block is the mirror of entering it: step off the bottom with Down, or off the end of the last line with Right, and you drop back to the > prompt. Esc or Ctrl+C leaves immediately from anywhere. The point of the climb is that the cursor and the prompt share one screen: you dial in a range by number, fix it by hand, and step back down — your scrollback, and the rest of the file's history, never scroll away.
Whole rows: insert and kill
Splitting and joining lines from inside the cursor handles most edits, but adding or removing whole lines is a structural change, so it is typed at the prompt rather than done by hand. Two commands cover it, each with a short form.
| Command | Short | Does |
|---|---|---|
insert row [N] | ir | Open a blank line after line N — bare appends at the end, 0 prepends |
kill row N | kr | Delete line N, or an N.M range |
rows | Report the line count (it changes nothing) |
Deleting a range asks for confirmation first; deleting a single line just does it. Both an insert and a kill are a single Ctrl+U away from being undone. That bare rows with no argument follows a convention that runs through nved: a command with no argument reports its current state instead of guessing at a default.
> ir 2
inserted a blank line after line 2
> 1.4
1 The quick brown fox
2 jumps over the lazy dog
3
4 and trots back home again
>
The new line 3 is blank and ready — climb in with Up and type.
Find and replace
You rarely know a line's number cold. find — short f — searches the whole buffer for a Go regular expression, jumps to the first match, and highlights it. find next (or fn) steps to the next match and wraps back to the top at the end.
> find lazy
2 jumps over the lazy dog
>
Replacement is its careful twin. replace /old/new/ — short r — highlights the first match unchanged so you see what is about to go; each replace next (rn) swaps the highlighted match and advances to the next, so you confirm every change as you make it. When you would rather not step through them, replace all /old/new/ (ra) swaps every match in one undoable pass — all is a plain word here, in nved's verbose idiom, not a /g flag.
The delimiter is any non-letter character, so replace ,/usr/local,/opt, sidesteps escaping the slashes in a path. Capture groups expand in the replacement as $1, $2:
> replace /(\w+) (\w+) fox/\2 \1 fox/
1 The brown quick fox
>
Two chords tie searching to the cursor. Ctrl+F and Ctrl+R seed the prompt with find and replace so you can just type the pattern. And after a match, the prompt arms the next step: Enter repeats it, Esc clears it, and climbing in with Up or Left drops the cursor straight onto the highlighted match. That is the loop that replaces line numbers in practice — find a line by what it says, then climb in and edit it in place.
Undo, save, and leaving
nved never writes without being told, and never throws away an edit you can't take back. Ctrl+U undoes the last change. The history lives with the buffer, not with the cursor, so it works at the > prompt as well as mid-edit, and it survives climbing in and out — if the edit you are undoing has scrolled off, nved reprints it so you can see it come back.
Saving is deliberate and explicit. s (or save) writes the buffer back to its file; an unnamed buffer prompts you for a name, or you give one with save report.txt. Ctrl+S does the same without leaving — your cursor stays exactly where it was, mid-edit — so a quick save is one chord and you keep working.
| Command | Does |
|---|---|
s / save [name] / Ctrl+S | Write in place; a name is required only when the buffer is unnamed |
x / q / quit / Ctrl+X | Exit; warns once if there are unsaved changes |
| Ctrl+U | Undo the last edit, from the prompt or mid-edit |
Exit with x, quit, or Ctrl+X. If the buffer is dirty, nved warns once and waits — repeat the command to leave anyway. It will not auto-save behind your back and it will not lose your work silently; the only way text reaches the disk is a save you asked for.
Reading a CSV
A .csv is a text file, so nved opens it as plain text — nothing happens until you ask. Type csv and the printed block re-renders as aligned columns with the header pinned in place:
> csv
> .
name , city , role
1 Alice , Austin , lead
2 Bob , Dallas , build
3 Carla , Austin , ops
>
The display stays raw: quoted cells keep their quotes, and the delimiter is drawn faint in the gap between columns, so you always see exactly what separates the fields rather than guessing. csv is a preset over independent switches you can also set on their own — dsv , sets the delimiter, quotes on|off controls splitting on quoted fields, headers on|off pins line one as the header. tsv is the same for tabs and asv for ASCII-separated files, whose unit and record separators never appear in ordinary text and so need no quoting at all. Each switch invoked bare reports its current state. And because the view is reached through ordinary commands, nved +csv data.csv opens straight into it.
Climb into an aligned block and you edit it cell by cell. Tab and Shift+Tab (or Ctrl+Left / Ctrl+Right) move field to field, and the grid re-aligns as you type — widen one cell and the column shifts to match. Editing changes the field value only: the delimiter key is swallowed and line splits and joins are suppressed, so you cannot accidentally restructure the file from inside a cell. The exception is a quoted cell, where the delimiter is data — inside "a,b" a comma types fine.
Whole columns and rows are edited by command. Here the two axes read differently on purpose, and it is worth pausing on: rows are numbered, columns are lettered. A bare 3 is unmistakably a line; column C is unmistakably a column. columns (or c) prints a faint letter ruler above the grid, naming the columns A, B, C from the left the way a spreadsheet does.
| Command | Short | Does |
|---|---|---|
columns | c | Show the faint A B C… letter ruler over the grid |
insert column [L] | ic | Add an empty column before column L — bare appends, ic A prepends |
kill column L | kc | Remove column L from every row, after a confirmation |
Insert lands the new column before the one you name — there is no letter before A, so ic A is how you prepend, a bare insert column appends on the right, and ic C slips one in ahead of C. Kill names the column it is about to remove when headers are on, then asks before doing it. Row insert and kill from the previous section work in this view too, the insert carrying one empty field per column so the new row lines up. Each column or row edit is a single undo, and dsv off drops back to plain text whenever you want free-form control of the bytes.
What nved is for
nved is one window over one buffer, and it leans into that. It is at its best when you know roughly where you are going — print line 80, or tail, or find a phrase — climb in, make the change, and save. It is not a pager and not an IDE; for roaming a large file or reading code you have better tools, and nved does not pretend otherwise.
What it gives you that a line editor never could is the cursor, and what it gives you that a full-screen editor never does is your scrollback. The session stays a readable transcript: every range you printed, every edit you made, sitting in ordinary terminal history above the prompt. If you came from ved, you already had the addresses; now you can reach into what they print.
Ready to try it?
nved is open source under the MIT license, written in pure Go. The nved page has the install steps, and the full source and design notes live at github.com/excelano/nved.