README.md (12299B)
1 # nne: no-nonsense editor 2 3 ## Design goal 4 5 To create a tiny public domain text editor that's usable for coding on a daily basis, while keeping the codebase under 1000 SLOC of readable and well-commented ANSI C89 code in a single file. 6 7 ## Features 8 9 - unique semi-modal controls that can't conflict with any terminal emulator or multiplexer by design 10 - only depends on 8 POSIX-compatible header files 11 - can be compiled with any C89 compiler (static linking is encouraged) 12 - the code is well-commented and easy to understand 13 - fully automatic indentation (based on how the previous line was indented) 14 - matching bracket search: `()`, `[]`, `{}`, `<>` 15 - tabwidth (build-time configurable in `NNE_TABWIDTH` definition, 2 spaces by default) 16 - full UTF-8 support (except right-to-left text) 17 - external command runner (suitable for processing the currently opened file) 18 19 ## Limitations (by design) 20 21 - only a single file can be edited at a time 22 - only VT100/ANSI-compatible terminals are supported 23 - no horizontal scrolling support, lines are always wrapped 24 - no syntax highlighting (ever, see note) 25 - lines are only numbered in the status bar (see note) 26 - limited characters in command buffer line (configurable in `NNE_IOBUFSZ` definition) 27 - in interactive prompts, only backspace is supported, no arrow navigation 28 - tab key always inserts `NNE_TABWIDTH` spaces, use modstring to insert tabs 29 - no text replace functionality (use the shell command runner) 30 - search is strictly case-sensitive and without wildcards/regexps 31 - UTF-8 is the only supported text encoding (use iconv for others) 32 - all `\r` or `\r\n` line endings are converted to `\n` (use dos2unix etc) 33 34 _Note: syntax highlighing and (visual) line numbering are not implemented for two design reasons: simpler codebase and less distractions from the main text._ 35 36 ## Building 37 38 Just run the usual CLI compilation process (replace `cc` with the C compiler of your choice and adjust flags if required): 39 ``` 40 cc -std=c89 -Os -O2 -s nne.c -o nne [-DNNE_IOBUFSZ=n] [-DNNE_TABWIDTH=m] [-DNNE_NO_ALTBUF] 41 ``` 42 - `NNE_IOBUFSZ` defines the internal command buffers size (in characters, default 2000) 43 - `NNE_TABWIDTH` defines the amount of spaces that a tabulation key/char represents 44 - `NNE_NO_ALTBUF` disables the use of advanced terminal sequence like alternate buffer switching, cursor hiding and UTF-8 mode enforcement (this macro is useful on some older terminals/OSes that don't support these sequences and can't correctly process them) 45 46 The GCC and Clang build flags are the same as above. Below are tested examples for some other C compilers. 47 48 Example for `zig cc` from [Zig](https://ziglang.org/) project, targeting x86_64 musl: 49 ``` 50 zig cc -target x86_64-linux-musl -std=c89 -Os -O2 -s nne.c -o nne 51 ``` 52 Example for [cproc](https://git.sr.ht/~mcf/cproc), static linking with musl: 53 ``` 54 cproc -static -s nne.c /usr/lib/musl/lib/libc.a -o nne 55 ``` 56 Example for [chibicc](https://github.com/rui314/chibicc), static linking with musl: 57 ``` 58 /path/to/chibicc -static -s nne.c /usr/lib/musl/lib/libc.a -o nne 59 ``` 60 You get the idea. 61 62 ### Known build failures 63 64 Currently, nne can't be compiled with [TCC](https://bellard.org/tcc/) and [lacc](https://github.com/larmel/lacc) because they both fail with `undefined reference to '__dso_handle'` error. This symbol is used by the `atexit()` call that sets up the handler to clean up the terminal environment on any abnormal exit. For TCC, the error can be mitigated by directly linking with the `libc.a` from musl (as shown above) but the resulting binary still is dynamically linked with glibc and displays unstable behavior. 65 66 Also, [neatcc](https://github.com/aligrudi/neatcc) (+ [neatlibc](https://github.com/aligrudi/neatlibc)) can't compile nne because it gives `strlen undefined` error. 67 68 ## Usage 69 70 Invoking `nne` without arguments just creates a new buffer. You can specify a file name to open. If the file name doesn't exist, it will be created on the first save. 71 72 ### Status bar 73 74 The status bar in nne is the last line of the terminal. Some operations may change its contents, but normally it consists of the following elements: `[state] [row],[col] [file%] [W]x[H] | [filename]`, where: 75 76 - `[state]` can be `-` (normal/insertion) or `C` (modal command), 77 - `[filename]` refers to the file currently being edited, 78 - `[row],[col]` display the current in-document cursor position (1-based), 79 - `[file%]` displays the approximate percentage of the position relative to the entire file length, 80 - `[W]x[H]` show current terminal width and height (in characters). 81 82 ### Controls 83 84 Controls in nne are semi-modal and use a modifier `mod` which stands for double-pressing the Esc key. So, for instance, `mod w` in the table below actually means `Esc Esc w` sequence. When the editor is in the modal command state, the character in the lower-left screen corner will change from `-` to `C`. To abort the command, press `mod` (`Esc Esc`) one more time. 85 86 Action |Key sequence |Additional comments 87 -------------|-------------------------|------------------- 88 Save file |`mod s` or `mod w` | 89 Quit |`mod q` |Prompts on unsaved changes 90 Move up |`ArrowUp` | 91 Move down |`ArrowDown` | 92 Move left |`ArrowLeft` | 93 Move right |`ArrowRight` | 94 Tabulation |`Tab` |Insert `NNE_TABWIDTH` spaces 95 Literal tab |`mod Tab` |Insert literal tab character 96 Backspace |`Backspace` |Delete previous character 97 Delete |`Del` or `mod Backspace` |Delete current character 98 Page Up |`PgUp` or `mod ArrowUp` |Jump up half a screen 99 Page Down |`PgDn` or `mod ArrowDown`|Jump down half a screen 100 Home |`Home` or `mod 0` |Jump to the line start 101 End |`End` or `mod 4` |Jump to the line end 102 Next word |`mod ArrowRight` | 103 Previous word|`mod ArrowLeft` | 104 Jump to line |`mod l [number] Return` |Prompts for line number 105 Jump to start|`mod 8` |Jump to file start 106 Jump to end |`mod 9` |Jump to file end 107 Bracket match|`mod 5` |Jump to **matching** pair in `()`, `[]`, `{}` and `<>` 108 Find text |`mod / [text] Return` |If no text is entered, looks for the next occurrence of the same pattern 109 Copy line |`mod y` |Copy the current line into the clipboard 110 Copy lines |`mod Y [number] Return` |Copy N lines (starting from current row) into the clipboard 111 Cut line |`mod d` |Cut the current line into the clipboard 112 Cut lines |`mod D [number] Return` |Cut N lines (starting from current row) into the clipboard 113 Paste |`mod p` or `mod v` |Paste the line(s) from the clipboard into the current position 114 Undo (pseudo)|`mod u` |Discard all unsaved changes and reload the file contents 115 Shell command|`mod e [command] Return` |Save current file, run an external shell command and reopen the file 116 Help |`mod h` |Displays on-screen help (press Return to get back to normal mode) 117 118 ## FAQ 119 120 ### Why such design goals? What is the rationale behind nne? 121 122 Well, there are several reasons why nne was created: 123 124 1. **Minimum overhead**. A text editor is the most important tool on every system, and it's crucial that it does not itself get in the way in terms of resource consumption. Most well-known and established text editors, however, are already bloated beyond repair, up to the point that x86_64 static builds of Vim and Vis against musl libc are sized 3433600 and 644288 bytes respectively. And these are only two examples. On top of that, their codebase already is so large that it cannot be easily (or at all) maintained by a single person. On the contrary, nne weighs around 68k bytes when statically linked with musl, and the sub-1000 SLOC limit (that actually turned out to be sub-777) makes it easy to comprehend by anyone familiar with ANSI C whoever will be reading its source code. 125 2. **Maximum portability**. This editor is designed to be source-compatible with any POSIX environment and with any architecture a POSIX environment can run on. There is no OS-specific code and no external dependencies. You don't need to find or build any libtermkey, terminfo, ncurses and other nonsense for the target architecture you want to compile nne for. It also doesn't require a specific build system: just a simple command line to compile a single file. By the way, it also doesn't contain any compiler-specific quirks: any C89-compatible compiler can build nne binary in a POSIX environment (GCC, Clang, zig cc, tcc). 126 3. **Maximum freedom**. Public domain deserves a decent lightweight text editor, just like it deserves SQLite, oksh and pdpmake. Besides mg (whose portability is questionable as of now), vce (that can't into UTF-8) and ue (that is straight up unusable on modern terminals), there were no notable text editors released into public domain. 127 128 ### Why not the usual combos with Ctrl key (like in Nano/uEmacs/etc)? 129 130 Because it's not convenient in general to stretch fingers to use these combos, especially when having to jump between different keyboards where the Ctrl key is located in different places. If you got used to Ctrl-chords, then it won't take much time to get used to semi-modal sequences that start with double-Esc. Additionally, this approach ensures that no sequence will conflict with your terminal emulator or a terminal multiplexer (if you use one). 131 132 ### Why not full modality then, akin to vi-like editors? 133 134 The initial plan was to make nne compatible with a small subset of POSIX vi, but then a more obvious control scheme was devised that would neither involve uncomfortable key chording nor make the users think which mode they are in. Also, the minimum vi codebase is generally much larger than the nne's <1000 SLOC goal. The layout of some control keys was definitely borrowed from vi though, like `mod 5` (akin to vi's `%`), `mod y`, `mod d`, `mod p` and `mod w`. 135 136 ### Why no true undo functionality? 137 138 As nne's text modification actions don't always operate on single characters, this would complicate undo buffer management and the overall codebase well up to the point of >1000 SLOC. Instead, more frequent file saving is encouraged and a way to quickly discard all the unsaved changes is offered with `mod u`. It also prompts you for confirmation, so that no accidental deletion takes place. 139 140 ### Is it actually usable on a daily basis with such small code size? 141 142 Yes, the author had fully switched to it from Vim since Jul 29 2023. With that said, nne can't be considered ready for mission-critical systems, as there might be many edge cases that are not covered yet. Anyway, the best way to find hidden issues is to use the editor daily. 143 144 ### Is there going to be any new functionality implemented (within the required limits)? 145 146 No. All the focus is going to be on fixing bugs (if there are any found) and size/performance optimizations. As of Jul 28 2023 and further, nne is considered feature-complete. 147 148 ### Is nne easy to port onto non-POSIX systems like DOS or Windows? 149 150 It should not be hard. Most (if not all) changes are going to be related to terminal I/O. 151 152 ### Why does `mod e` (running external command) force saving the currently edited file? 153 154 Because it can't guarantee that the external command won't kill the nne process itself. Thus, saving is enforced to preserve your data. Also, this shell runner feature is introduced for easy offloading of some functionality to external tools designed to do it much better than any in-editor features would allow, e.g. running `sed` to perform a global substitution on the very file we're editing right now. It would be a shame if this global substitution had been performed on an old copy of the file and any subsequent save had obliterated all the changes made by the external command. 155 156 ### Why did you include a help screen in the editor itself? 157 158 Because nne is expected to be equally accessible when distributed in all forms: this 3-file repo, a single C source file or a single statically-linked binary. One should not rely on the fact that external documentation like this README is always present. 159 160 ## Credits 161 162 Created by Luxferre in 2023. Released into public domain. 163 164 Made in Ukraine.