Skip to content
Matěj Cepl edited this page Apr 16, 2023 · 18 revisions

Why don't you just use vi(m)?

The traditional vi is barely usable once you have become accustomed to the improvements introduced by vim.

However, we think vim can be improved further both in concepts and quality of implementation. Over the years it has accumulated a lot of cruft and support for now obsolete systems, many features have been added without a seemingly coherent design. By now it weights in at over 300K lines of C code of varying quality. We think this is unacceptable and off by at least an order of magnitude. It not only is a maintenance burden but also discourages experimentation with new ideas.

We decided this was beyond fixing and instead started from scratch:

  • incorporating the good ideas (e.g. modal editing, basic grammar: operators, motions + text objects)

  • combining them with multiple cursors/selections and a structural regular expression based command language

  • dropping the bad ones (e.g. custom regex syntax, vim script) and all the legacy cruft and feature creep

with a focus on a simple, clean, modular and efficient design as well as implementation. The result is quite different from vim.

Besides all that, it is fun (well most of the time anyway).

Multiple cursors/selections are unnecessary in vi(m)!?

Some people argue that vim has no need for multiple cursors/selections because the text objects gn and gN combined with the repeat command ., macros and visual block mode are more powerful.

While it is true that these features work for some cases (and are also supported up to an extent by vis), they are often inconvenient because they lack interactivity. More advanced operations like alignment (<S-Tab>) or rotations (+,-) where multiple selections depend on each other are simply not possible.

Something does not work as in vi(m)!?

For some things that is to be expected, for others it might be an oversight or simply a bug. In general the intention is not to be bug for bug compatible with vi(m), instead we aim to provide more powerful editing features based on an elegant design and clean implementation.

Doesn't this mean you have no plugin ecosystem?

Yes that is true. In general we are proponents of a workflow involving many editor agnostic tools rather than one all encompassing monolith.

Furthermore, having no legacy baggage gives us the opportunity to revisit how a plugin interface for a modal editor should look like.

Why not use Neovim?

While we applaud them for trying to modernize a horrible codebase, their endeavour seems ultimately futile. They have accumulated so much technical debt and are still dealing with a 247K LOC monster. In a way they are caught between two worlds. They chose to remain backward compatible with vim and thus actively merge upstream patches. That of course not only discourages experimentation with new ideas and breaking changes, but also conflicts or at the very least makes the badly needed code refactorings much harder.

The Neovim project also has some worthwhile goals e.g. first class async support (which we hope to address in the future) only for the parent project to implement it in an incompatible way.

What are Structural Regular Expressions?

A concept introduced by Rob Pike in 1987 for his text editor sam(1). The main insight is that text editing should not be artificially limited by the concept of a line. Most text editors treat a file as an array of lines. Sam (and vis) instead use a byte stream as underlying abstraction and let the user specify arbitrarily structured regions using regular expressions.

As an example suppose you have the following simple file opened in vim with [ and ] denoting the selection boundaries in visual mode:

foo bar [foo bar]

When you now try to enter an ex command, vim already adds :'<,'> to the command prompt. That is by default it will not only operate on the given selection, but on the whole lines covered by it. If you now perform a substitution like :'<,'>s/foo/---/ you will notice that the first (not second) occurrence of foo is replaced. In contrast :x/foo/c/---/ in vis extracts all foo from the given selection and changes them to ---.

Sam's command language applies these concepts to text editing and vis combines them with modal editing and multiple selections to provide a more interactive variation (check the vis(1) manual page for the supported syntax).

Why not use sam(1) or acme(1)?

We like the power of modal editing.

If the world were a better place we would all be using Plan 9 and 9P, but unfortunately it is not and we are stuck with Unix boxes connected through SSH. In such an environment an editor needs to work in the TTY world.

Moreover the hardware has evolved over time. Acme, Sam (to a lesser extent) and Oberon all make extensive use of the mouse for selection and action purposes. This works best with a 3-button mouse but is cumbersome on small form factor touchpads (e.g. netbooks). While we recognize that a suitable mouse is a powerful tool, it doesn't work in all environments and a lot of it comes down to personal preference. We cater to a keyboard driven workflow where the fingers best stay on the home row and avoid unnecessary context switches to the mouse.

Isn't this a case of NIH (Not invented here)?

No. We reuse existing work where appropriate. Consider syntax highlighting, instead of designing our own custom solution we build on the work of the Scintillua project.

As a result of our modular design we also try to make individual components available for others to use where appropriate. Examples include vis-menu(1), vis-digraph(1) and vis-clipboard(1).

What are the system requirements?

Check the build instructions in the main README.

Vis doesn't compile after updating from latest git!?

This is most likely due to a stale config.h file. If you have no local modifications you can simply remove it and restart the build which will regenerate it based on config.def.h:

rm config.h && ./configure && make

Otherwise you will have to manually merge the two C header files.

Why did you write it in C?

The original design relied on mmap(2) to provide efficient large file support. Using the operating system's virtual memory subsystem instead of an editor internal block/caching layer.

Furthermore, we consider an editor a core system component. It is probably the tool that (system) programmers spend most of their time with. As such it should be easy to bootstrap onto new systems and facilitate self-hosted development. For this reason heavy dependencies (e.g. C++, boost etc.) should be avoided. Using C ensures maximal portability to resource-constrained architectures and environments. The editor should be usable in scenarios where one would normally find busybox vi such as in rescue systems, routers, phones and embedded devices.

These days the general public seems to think that C applications are automatically riddled with memory bugs and security issues. We try to proactively prevent them by making extensive use of static analysis (Coverity Scan), runtime instrumentation (asan, msan, ubsan) dynamic analysis (valgrind), runtime interpretation (tis-interpreter) and fuzzing (american fuzzy lop). Additionally we enable hardening features such as _FORTIFY_SOURCE, stack protector, relro, PIE and BIND_NOW by default.

Besides that, the relatively small editor core written in C can be extended using Lua.

Why Lua as an in process extension language?

Lua was explicitly designed for this purpose. Like vis itself it has a reasonably clean design coupled with a portable, efficient implementation based on a small set of core primitives (tables) enabling powerful features (e.g. closures) and different usage paradigms (e.g. OOP).

It does not make sense to invent an editor specific extension language. Unlike for example Vimscript, Lua was designed as a general purpose programming language. As such it supports proper scoping rules and first class functions, it provides a rich set of existing libraries and easy integration through its C API. Unlike ELisp it does not need special hacks to make it performant.

In the distant future we might adopt a multi process architecture and expose a RPC API thereby enabling a language agnostic extension mechanism.

Why don't you use LuaJIT?

We might care about some architectures not supported by LuaJIT. More importantly, it would further expand our testing matrix with yet another build configuration. Plugin authors would have to be careful to only use the supported common subset of Lua 5.2 compatible with all implementations.

More generally we think that the tasks where Lua is used are not hugely performance critical (maybe with the exception of syntax highlighting). So far the standard Lua implementation was plenty fast enough. Should this ever become a serious bottleneck we will most likely have to re-evaluate our design decisions rather than simply switching to a faster implementation.

Having said that, there might be other reasons (e.g. the FFI) why one would like to reconsider LuaJIT.

How does configuration work?

We do not have a custom configuration file syntax, but instead use the regular Lua API for configuration purposes. Assuming your binary has been compiled with Lua support enabled (check the output of vis -v), you have the power of a full fledged programming language at your disposal.

During startup vis will attempt to source a visrc.lua file from a set of predefined locations.

Plugins are loaded using require. Subscribe to one of the exposed events and execute your commands as desired. The default visrc.lua should get you started.

If your configuration has no effect check the :help "Lua paths" section to see the exact paths being used by your binary. Alternatively use a tool like strace(1) to log all open(2) syscalls:

strace -o log -e open vis +q && grep visrc.lua log

How can I configure custom key bindings?

Try the :map command. Beware that all mappings are always evaluated recursively. Hence, the right hand side must not contain the left hand side (neither directly nor indirectly). As an example the following attempt to auto close braces will not work:

:map! insert ( ()<Left>

instead use

:map! insert ( <C-v>u0028)<Left>

See :help for a list of usable symbolic keys as well as editor pseudo keys. You might also be interested in keyboard layout specific mappings.

<M-${key}> is inserted whenever I press <Escape> and ${key} in short succession?

This is because terminal input handling is a mess. The representation of both <M-${key}> and <Escape>${key} are the same, the distinguishing property is only the delay between the two key presses. This threshold can be configured using the :set escdelay option, which has a default value of 50ms and is analogous to vim's ttimeoutlen setting. If the keys arrive within that time frame, they are interpreted as the former otherwise as the latter variant.

Terminal multiplexers will also affect this delay because they intercept the original input and then forward it. In tmux this is can be configured using the escape-time setting. For ncurses applications the $ESCDELAY environment variable applies. This is also the case for dvtm. Furthermore, the used delay can also be specified using with the -d command line argument.

Syntax highlighting does not work, how can I fix it?

Syntax highlighting needs both Lua support (check vis -v) and LPeg. The latter can either be statically compiled into the binary (in which case it will be reported as +lpeg in the above version query) or dynamically loaded at runtime using the standard Lua module system (based on dlopen(3)). The considered runtime paths ending with .so are also listed in the :help "Lua paths" section.

If you are using the dynamic LPeg module, then make sure it is compiled against the same Lua version as vis itself.

My terminal colors are messed up, what is going on?

We currently use terminal color palette changes to support 24bit True colors, if the terminal description indicates support for it. Otherwise we fall back to the closest color from the default 256 color cube which means some colors will not be displayed properly. If even 256 colors are not available, we fall back further to 16 colors according to this transition.

For 256-color fallback, we assume that the bottom 240 colors of the palette are set up like this. The fallback color is chosen as the one which has the smallest Euclidean distance from the target color, treating (R, G, B) triples as values in ℝ³. This does not strictly align with human perception (see CIEDE2000), but is close enough.

Some terminals (e.g. Apple's Terminal.app) incorrectly advertise the "can change color" ccc (seeterminfo(5)) capability, even though they are incapable of performing the requested change. As a result terminal colors end mixed up. Since we start replacing just after the initial 16 colors, this usually has the symptom of the editor appearing in dark blues and greens.

You have a couple of options to fix the issue:

  1. Use a base color scheme which does not rely on 24 bit colors (e.g. dark-16).
  2. Fix your terminal description to match its actual capabilities. This is usually accomplished by use of infocmp(1M) and tic(1M).
  3. Add :set change-256colors false to your visrc.lua configuration. Colors will be approximated and the option is an ugly workaround which might be removed in future versions.

Are there binary packages available?

There exist a number of distribution packages.

What about Windows?

We want to avoid platform dependent code as much as possible. As a consequence we do not support native Windows binaries, but instead rely on Cygwin (or in the future midipix) to provide a POSIX environment.

Windows based continuous integration using AppVeyor is set up and allows build artifacts to be downloaded.

Why do you care about large file support (and performance in general)?

Modern machines have lots of resources available (e.g. memory or at least virtual address space) and we should use it to handle arbitrary files. We want to use the same tool to inspect and/or edit:

  • large files (limited by the available virtual address space) including
    • Wikipedia/OpenStreetMap XML / SQL / CSV dumps / Logs
    • amalgamated source trees (e.g. SQLite)
  • single line ones (e.g. minified JavaScript)
  • binary ones (e.g. ELF files)

As a special case the editor should also be usable for regular editing tasks.

This is still work in progress.

How should I edit files in legacy encodings?

We believe all text should be stored in UTF-8 which is the only encoding supported by vis. Should you have a need to edit files in legacy encodings use iconv(1) to convert it to UTF-8 and back.

For binary files you can try the motions go (move to an absolute byte position) as well as gh and gl which move to relative byte offsets in either backward or forward direction, respectively. The % motion which moves to a percentage of the whole file content can be used to get a quick overview of a large file.

Are Windows style \r\n line endings supported?

Only \n is treated as a line break and Enter will always insert it. This is in line with POSIX and simplifies the implementation. If this is a problem, use e.g. dos2unix(1) / unix2dos(1) for external conversion - or you could change the line endings by typing :x/\r\n/c/\n/ in vis.