ui: fall back to xterm-256color if term initialization fails
[vis.git] / README.md
blob58a0a6fac1c1852e8ac6985dda343d68acfbd2ac
1 Why another text editor?
2 ========================
4 It all started when I was recently reading the excellent
5 [Project Oberon](http://www.inf.ethz.ch/personal/wirth/ProjectOberon/),
6 where in chapter 5 a data structure for managing text is introduced.
7 I found this rather appealing and wanted to see how it works in practice.
9 After some time I decided that besides just having fun hacking around I
10 might as well build something which could (at least in the long run)
11 replace my current editor of choice: vim.
13 This should be accomplished by a reasonable amount of clean (your mileage
14 may vary), modern and legacy free C code. Certainly not an old,
15 [500'000 lines long](https://www.openhub.net/p/vim) `#ifdef` cluttered
16 mess which tries to run on all broken systems ever envisioned by mankind.
18 Admittedly vim has a lot of functionally, most of which I don't use. I
19 therefore set out with the following main goals:
21  - Unicode aware
23  - handle arbitrary files including
24     - large ones e.g. >500M SQL dumps or CSV exports
25     - single line ones e.g. minified JavaScript
26     - binary ones e.g. ELF files
28  - unlimited undo/redo support, the possibility to revert to any earlier/later state
30  - regex search (and replace)
32  - multiple file/window support
34  - syntax highlighting
36 The goal could thus be summarized as "80% of vim's features (in other
37 words the useful ones) implemented in roughly 1% of the code".
39 Finally and most importantly it is fun! Writing a text editor presents
40 some interesting challenges and design decisions, some of which are
41 explained below.
43 ![vis demo](https://raw.githubusercontent.com/martanne/vis/gh-pages/screencast.gif)
45 Text management using a piece table/chain
46 =========================================
48 The core of this editor is a persistent data structure called a piece
49 table which supports all modifications in `O(m)`, where `m` is the number
50 of non-consecutive editing operations. This bound could be further
51 improved to `O(log m)` by use of a balanced search tree, however the
52 additional complexity doesn't seem to be worth it, for now.
54 The actual data is stored in buffers which are strictly append only.
55 There exist two types of buffers, one fixed-sized holding the original
56 file content and multiple append-only ones storing the modifications.
58 A text, i.e. a sequence of bytes, is represented as a double linked
59 list of pieces each with a pointer into a buffer and an associated
60 length. Pieces are never deleted but instead always kept around for
61 redo/undo support. A span is a range of pieces, consisting of a start
62 and end piece. Changes to the text are always performed by swapping
63 out an existing, possibly empty, span with a new one.
65 An empty document is represented by two special sentinel pieces which
66 always exist:
68     /-+ --> +-\
69     | |     | |
70     \-+ <-- +-/
71      #1     #2
73 Loading a file from disk is as simple as mmap(2)-ing it into a buffer,
74 creating a corresponding piece and adding it to the double linked list.
75 Hence loading a file is a constant time operation i.e. independent of
76 the actual file size (assuming the operating system uses demand paging).
78     /-+ --> +-----------------+ --> +-\
79     | |     | I am an editor! |     | |
80     \-+ <-- +-----------------+ <-- +-/
81      #1             #3              #2
83 Insert
84 ------
86 Inserting a junk of data amounts to appending the new content to a
87 modification buffer. Followed by the creation of new pieces. An insertion
88 in the middle of an existing piece requires the creation of 3 new pieces.
89 Two of them hold references to the text before respectively after the
90 insertion point. While the third one points to the newly added text.
92     /-+ --> +---------------+ --> +----------------+ --> +--+ --> +-\
93     | |     | I am an editor|     |which sucks less|     |! |     | |
94     \-+ <-- +---------------+ <-- +----------------+ <-- +--+ <-- +-/
95      #1            #4                   #5                #6      #2
97            modification buffer content: "which sucks less"
99 During this insertion operation the old span [3,3] has been replaced
100 by the new span [4,6]. Notice that the pieces in the old span were not
101 changed, therefore still point to their predecessors/successors, and can
102 thus be swapped back in.
104 If the insertion point happens to be at a piece boundary, the old span
105 is empty, and the new span only consists of the newly allocated piece.
107 Delete
108 ------
110 Similarly a delete operation splits the pieces at appropriate places.
112     /-+ --> +-----+ --> +--+ --> +-\
113     | |     | I am|     |! |     | |
114     \-+ <-- +-----+ <-- +--+ <-- +-/
115      #1       #7         #6      #2
117 Where the old span [4,5] got replaced by the new span [7,7]. The underlying
118 buffers remain unchanged.
120 Cache
121 -----
123 Notice that the common case of appending text to a given piece is fast
124 since, the new data is simply appended to the buffer and the piece length
125 is increased accordingly. In order to keep the number of pieces down,
126 the least recently edited piece is cached and changes to it are done
127 in place (this is the only time buffers are modified in a non-append
128 only way). As a consequence they can not be undone.
130 Undo/redo
131 ---------
133 Since the buffers are append only and the spans/pieces are never destroyed
134 undo/redo functionality is implemented by swapping the required spans/pieces
135 back in.
137 As illustrated above, each change to the text is recorded by an old and
138 a new span. An action consists of multiple changes which logically belong
139 to each other and should thus also be reverted together. For example
140 a search and replace operation is one action with possibly many changes
141 all over the text.
143 The text states can be marked by means of a snapshotting operation.
144 Snapshotting saves a new node to the history graph and creates a fresh
145 Action to which future changes will be appended until the next snapshot.
147 Actions make up the nodes of a connected digraph, each representing a state
148 of the file at some time during the current editing session. The edges of the
149 digraph represent state transitions that are supported by the editor. The edges
150 are implemented as four Action pointers (`prev`, `next`, `earlier`, and `later`).
152 The editor operations that execute the four aforementioned transitions
153 are `undo`, `redo`,`earlier`, and `later`, respectively. Undo and
154 redo behave in the traditional manner, changing the state one Action
155 at a time. Earlier and later, however, traverse the states in chronological
156 order, which may occasionally involve undoing and redoing many Actions at once.
158 Properties
159 ----------
161 The main advantage of the piece chain as described above is that all
162 operations are performed independent of the file size but instead linear
163 in the number of pieces i.e. editing operations. The original file buffer
164 never changes which means the `mmap(2)` can be performed read only which
165 makes optimal use of the operating system's virtual memory / paging system.
167 The maximum editable file size is limited by the amount of memory a process
168 is allowed to map into its virtual address space, this shouldn't be a problem
169 in practice. The whole process assumes that the file can be used as is.
170 In particular the editor assumes all input and the file itself is encoded
171 as UTF-8. Supporting other encodings would require conversion using `iconv(3)`
172 or similar upon loading and saving the document.
174 Similarly the editor has to cope with the fact that lines can be terminated
175 either by `\n` or `\r\n`. There is no conversion to a line based structure in
176 place. Instead the whole text is exposed as a sequence of bytes. All
177 addressing happens by means of zero based byte offsets from the start of
178 the file.
180 The main disadvantage of the piece chain data structure is that the text
181 is not stored contiguous in memory which makes seeking around somewhat
182 harder. This also implies that standard library calls like the `regex(3)`
183 functions can not be used as is. However this is the case for all but
184 the most simple data structures used in text editors.
186 Syntax Highlighting using Parsing Expression Grammars
187 =====================================================
189 [Parsing Expression Grammars](https://en.wikipedia.org/wiki/Parsing_expression_grammar)
190 (PEG) have the nice property that they are closed under composition.
191 In the context of an editor this is useful because lexers can be
192 embedded into each other, thus simplifying syntax highlighting
193 definitions.
195 Vis reuses the [Lua](http://www.lua.org/) [LPeg](http://www.inf.puc-rio.br/~roberto/lpeg/)
196 based lexers from the [Scintillua](http://foicica.com/scintillua/) project.
198 Future Plans / Ideas
199 ====================
201 This section contains some ideas for further architectural changes.
203 Client/Server Architecture / RPC interface
204 ------------------------------------------
206 In principle it would be nice to follow a similar client/server approach
207 as [sam/samterm](http://sam.cat-v.org/) i.e. having the main editor as a
208 server and each window as a separate client process with communication
209 over a unix domain socket.
211 That way window management would be taken care of by dwm or dvtm and the
212 different client processes would still share common cut/paste registers
213 etc.
215 Efficient Search and Replace
216 ----------------------------
218 Currently the editor copies the whole text to a contiguous memory block
219 and then uses the standard regex functions from libc. Clearly this is not
220 a satisfactory solution for large files.
222 The long term solution is to write our own regular expression engine or
223 modify an existing one to make use of the iterator API. This would allow
224 efficient search without having to double memory consumption.
226 The used regex engine should use a non-backtracking algorithm. Useful
227 resources include:
229  - [Russ Cox's regex page](http://swtch.com/~rsc/regexp/)
230  - [TRE](https://github.com/laurikari/tre) as
231    [used by musl](http://git.musl-libc.org/cgit/musl/tree/src/regex)
232    which uses a parallel [TNFA matcher](http://laurikari.net/ville/spire2000-tnfa.ps)
233  - [Plan9's regex library](http://plan9.bell-labs.com/sources/plan9/sys/src/libregexp/)
234    which has its root in Rob Pike's sam text editor
235  - [RE2](https://github.com/google/re2) C++ regex library
237 vis a vim-like frontend
238 =======================
240 The editor core is written in a library like fashion which should make
241 it possible to write multiple frontends with possibly different user
242 interfaces/paradigms.
244 The default, and currently only, interface is a vim clone called vis.
245 The following section gives a quick overview over various vim features
246 and their current support in vis.
248 ###  Operators
250     d   (delete)
251     c   (change)
252     y   (yank)
253     p   (put)
254     >   (shift-right)
255     <   (shift-left),
256     J   (join)
257     ~   (swap case)
258     gu  (make lowercase)
259     gU  (make uppercase)
261 Operators can be forced to work line wise by specifying `V`.
263 ### Movements
265     h        (char left)
266     l        (char right)
267     j        (line down)
268     k        (line up)
269     gj       (display line down)
270     gk       (display line up)
271     0        (start of line)
272     ^        (first non-blank of line)
273     g_       (last non-blank of line)
274     $        (end of line)
275     %        (match bracket)
276     b        (previous start of a word)
277     B        (previous start of a WORD)
278     w        (next start of a word)
279     W        (next start of a WORD)
280     e        (next end of a word)
281     E        (next end of a WORD)
282     ge       (previous end of a word)
283     gE       (previous end of a WORD)
284     {        (previous paragraph)
285     }        (next paragraph)
286     (        (previous sentence)
287     )        (next sentence)
288     [[       (previous start of C-like function)
289     []       (previous end of C-like function)
290     ][       (next start of C-like function)
291     ]]       (next end of C-like function)
292     gg       (begin of file)
293     g0       (begin of display line)
294     gm       (middle of display line)
295     g$       (end of display line)
296     G        (goto line or end of file)
297     |        (goto column)
298     n        (repeat last search forward)
299     N        (repeat last search backwards)
300     *        (search word under cursor forwards)
301     #        (search word under cursor backwards)
302     f{char}  (to next occurrence of char to the right)
303     t{char}  (till before next occurrence of char to the right)
304     F{char}  (to next occurrence of char to the left)
305     T{char}  (till before next occurrence of char to the left)
306     ;        (repeat last to/till movement)
307     ,        (repeat last to/till movement but in opposite direction)
308     /{text}  (to next match of text in forward direction)
309     ?{text}  (to next match of text in backward direction)
311   An empty line is currently neither a word nor a WORD.
313   The semantics of a paragraph and a sentence is also not always 100%
314   the same as in vim.
316   Some of these commands do not work as in vim when prefixed with a
317   digit i.e. a multiplier. As an example in vim `3$` moves to the end
318   of the 3rd line down. However vis treats it as a move to the end of
319   current line which is repeated 3 times where the last two have no
320   effect.
322 ### Text objects
324   All of the following text objects are implemented in an inner variant
325   (prefixed with `i`) and a normal variant (prefixed with `a`):
327     w  word
328     W  WORD
329     s  sentence
330     p  paragraph
331     [,], (,), {,}, <,>, ", ', `         block enclosed by these symbols
333   For sentence and paragraph there is no difference between the
334   inner and normal variants.
336   Additionally the following text objects, which are not part of stock vim
337   are also supported:
339     ae      entire file content
340     ie      entire file content except for leading and trailing empty lines
341     af      C-like function definition including immeadiately preceding comments
342     if      C-like function definition only function body
343     al      current line
344     il      current line without leading and trailing white spaces
346 ### Modes
348   At the moment there exists a more or less functional insert, replace
349   and visual mode (in both line and character wise variants).
350   
351   Visual block mode is not implemented and there exists no immediate
352   plan to do so. Instead vis has built in support for multiple cursors.
353   
354 ### Multiple Cursors / Selections
356   vis supports multiple cursors with immediate visual feedback (unlike
357   in the visual block mode of vim where for example inserts only become
358   visible upon exit). There always exists one primary cursor, additional
359   ones can be created as needed.
360   
361   To manipulate multiple cursors use in normal mode:
362   
363     CTRL-K   create a new cursor on the line above
364     CTRL-J   create a new cursor on the line below
365     CTRL-P   remove least recently added cursor
366     CTRL-N   select word the cursor is currently over, switch to visual mode
367     CTRL-A   try to align all cursor on the same column
368     ESC      if a selection is active, clear it.
369              Otherwise dispose all but the primary cursor.
371   Visual mode was enhanced to recognize:
372     
373     I        create a cursor at the start of every selected line
374     A        create a cursor at the end of every selected line
375     CTRL-N   create new cursor and select next word matching current selection
376     CTRL-X   clear (skip) current selection, but select next matching word
377     CTRL-P   remove least recently added cursor
379 ### Marks
381     [a-z] general purpose marks
382     <     start of the last selected visual area in current buffer
383     >     end of the last selected visual area in current buffer
385   No marks across files are supported. Marks are not preserved over
386   editing sessions.
388 ### Registers
390   Only the 26 lower case registers `[a-z]` and 1 additional default register
391   is supported.
393 ### Undo/Redo and Repeat
395   The text is currently snapshotted whenever an operator is completed as
396   well as when insert or replace mode is left. Additionally a snapshot
397   is also taken if in insert or replace mode a certain idle time elapses.
398   
399   Another idea is to snapshot based on the distance between two consecutive
400   editing operations (as they are likely unrelated and thus should be
401   individually reversible).
403   Besides the regular undo functionality, the key bindings `g+` and `g-`
404   traverse the history in chronological order. Further more the `:earlier`
405   and `:later` commands provide means to restore the text to an arbitrary
406   state.
408   The repeat command `.` works for all operators and is able to repeat
409   the last insertion or replacement.
411 ### Macros
413   `[a-z]` are recoginized macro names, `q` starts a recording, `@` plays it back.
414   `@@` refers to the least recently recorded macro.
416 ### Command line prompt
418   At the `:`-command prompt only the following commands are recognized, any
419   valid unique prefix can be used:
421     :nnn        go to line nnn
422     :bdelete    close all windows which display the same file as the current one
423     :edit       replace current file with a new one or reload it from disk
424     :open       open a new window
425     :qall       close all windows, exit editor
426     :quit       close currently focused window
427     :read       insert content of another file at current cursor position
428     :split      split window horizontally
429     :vsplit     split window vertically
430     :new        open an empty window, arrange horizontally
431     :vnew       open an empty window, arrange vertically
432     :wq         write changes then close window
433     :xit        like :wq but write only when changes have been made
434     :write      write current buffer content to file
435     :saveas     save file under another name
436     :substitute search and replace currently implemented in terms of `sed(1)`
437     :!          filter range through external command
438     :earlier    revert to older text state
439     :later      revert to newer text state 
440     :set        set the options below
442      tabwidth   [1-8]
444        set display width of a tab and number of spaces to use if
445        expandtab is enabled
447      expandtab  (yes|no)
449        whether typed in tabs should be expanded to tabwidth spaces
451      autoindent (yes|no)
453        replicate spaces and tabs at the beginning of the line when
454        starting a new line.
456      number         (yes|no)
457      relativenumber (yes|no)
459        whether absolute or relative line numbers are printed alongside
460        the file content
462      syntax      name
464        use syntax definition given (e.g. "c") or disable syntax
465        highlighting if no such definition exists (e.g :set syntax off)
467      show        newlines=[1|0] tabs=[1|0] spaces=[0|1]
469        show/hide special white space replacement symbols
471      cursorline (yes|no)
473        highlight the line on which the cursor currently resides
475      colorcolumn number
477        highlight the given column
479      theme      name
481        use the given theme / color scheme for syntax highlighting
483   Each command can be prefixed with a range made up of a start and
484   an end position as in start,end. Valid position specifiers are:
486     .          start of the current line
487     +n and -n  start of the line relative to the current line
488     'm         position of mark m
489     /pattern/  first match after current position
491   If only a start position without a command is given then the cursor
492   is moved to that position. Additionally the following ranges are
493   predefined:
495     %          the whole file, equivalent to 1,$
496     *          the current selection, equivalent to '<,'>
498   History support, tab completion and wildcard expansion are other
499   worthwhile features. However implementing them inside the editor
500   feels wrong.
502 ### Tab <-> Space conversion and Line endings \n vs \r\n
504   Tabs can optionally be expaned to a configurable number of spaces.
505   The first line ending in the file determines what will be inserted
506   upon a line break (defaults to \n).
508 ### Jump list and change list
510   A per window, file local jump list (navigate with `CTRL+O` and `CTRL+I`)
511   and change list (navigate with `g;` and `g,`) is supported. The jump
512   list is implemented as a fixed sized ring buffer.
514 ### Mouse support
516   The mouse is currently not used at all.
518 ### Future Plans / Ideas
520  Potentially interesting features:
522    + code completion: this should be done as an external process. I will
523      have to take a look at the tools from the llvm / clang project. Maybe
524      dvtm's terminal emulation support could be reused to display an
525      slmenu inside the editor at the cursor position?
527    + something similar to vim's quick fix functionality
529    + text folding
531    + runtime configurable key bindings
533 ### Non Goals
535   Some of the features of vim which will *not* be implemented:
537    - tabs / multiple workspaces / advanced window management
538    - file and directory browser
539    - support for file archives (tar, zip, ...)
540    - support for network protocols (ftp, http, ssh ...)
541    - encryption
542    - compression
543    - GUIs (neither x11, motif, gtk, win32 ...) although the codebase
544      should make it easy to add them
545    - VimL
546    - plugins (certainly not vimscript, if anything it should be lua based)
547    - right-to-left text
548    - ex mode (if you need a stream editor use `ssam(1)`
549    - diff mode
550    - vimgrep
551    - internal spell checker
552    - compile time configurable features / `#ifdef` mess
554 How to help?
555 ============
557 At this point it might be best to fetch the code, edit some scratch file,
558 notice an odd behavior or missing functionality, write and submit a patch
559 for it, then iterate.
561 Additional test cases either for the [low level text manipulation routines]
562 (https://github.com/martanne/vis/tree/test/test/text) or as [commands for the vis frontend]
563 (https://github.com/martanne/vis/tree/test/test/vis) would be highly appreciated.
565 WARNING: There are probably still some bugs left which could corrupt your
566          unsaved changes. Use at your own risk. At this point I suggest to
567          only edit non-critical files which are under version control and
568          thus easily recoverable!
570 A quick overview over the code structure to get you started:
572  File(s)             | Description
573  ------------------- | -----------------------------------------------------
574  `text.[ch]`         | low level text / marks / {un,re}do / piece table implementation
575  `text-motions.[ch]` | movement functions take a file position and return a new one
576  `text-objects.[ch]` | functions take a file position and return a file range
577  `text-regex.[ch]`   | text search functionality, designated place for regex engine
578  `text-util.[ch]`    | text related utility functions mostly dealing with file ranges
579  `view.[ch]`         | ui-independent viewport, shows part of a file, syntax highlighting, cursor placement, selection handling
580  `ui.h`              | abstract interface which has to be implemented by ui backends
581  `ui-curses.[ch]`    | a terminal / curses based user interface implementation
582  `buffer.[ch]`       | dynamically growing buffer used for registers and macros
583  `ring-buffer.[ch]`  | fixed size ring buffer used for the jump list
584  `map.[ch]`          | crit-bit tree based map supporting unique prefix lookups and ordered iteration. used to implement `:`-commands
585  `vis.h`             | vi(m) specific editor frontend library public API
586  `vis.c`             | vi(m) specific editor frontend implementation
587  `vis-core.h`        | internal header file, various structs for core editor primitives
588  `vis-cmds.c`        | vi(m) `:`-command implementation
589  `vis-modes.c`       | vi(m) mode switching, enter/leave event handling
590  `vis-motions.c`     | vi(m) cursor motion implementation
591  `vis-operators.c`   | vi(m) operator implementation
592  `main.c`            | key action definitions, program entry point
593  `config.def.h`      | definition of default key bindings (mapping of key actions)
594  `lexers/`           | Lua LPeg based lexers used for syntax highlighting
596 Hope this gets the interested people started.
598 Feel free to ask questions if something is unclear! There are still a lot
599 of bugs left to fix, but by now I'm fairly sure that the general concept
600 should work.
602 As always, comments and patches welcome!
604 Build dependencies
605 ==================
607 In order to build vis you will need a C99 compiler as well as:
609  * a C library, we recommend [musl](http://www.musl-libc.org/)
610  * [libcurses](http://www.gnu.org/software/ncurses/), preferably in the
611    wide-character version
612  * [libtermkey](http://www.leonerd.org.uk/code/libtermkey/)
613  * [lua](http://www.lua.org/) >= 5.1
614  * [LPeg](http://www.inf.puc-rio.br/~roberto/lpeg/) >= 0.12 (runtime
615    dependency required for syntax highlighting)
617 If you want a self contained statically linked binary you can try
618 to run `make standalone` which will attempt to download, compile
619 and install all of the above dependencies. `make local` will do
620 the same but only for libtermkey, lua and LPeg (i.e. the system
621 C and curses libraries are used).
623 To build a regular dynamically linked binary using the system
624 libraries, simply run `make` (possibly after adapting `config.mk`
625 to match your system).