The State of Vim
lwn.net
lwn.net
(also software projects don't need to be democracies, e.g. people won't starve or sent to the gulag if things go sideways)
> Braam really took Neovim personally and got better at getting stuff into vim that he wouldn't merge before once neovim was arround as a competitor. I really lost track of vim in the last years because neovim is just a solid platform with an active community.
I think that's the answer - the specific makeup of any given team isn't as relevant as competition which spurs competitors to either keep up or the more successful upstarts to take over.
We saw some complaints about a repo like this I forget for what project some weeks back here on HN and it came down to, people dropping a PR and then the maintainer left holding the bag if something goes wrong, having to maintain someone else's code, which can become a problem if its a completely new feature they didn't implement or want, but users wanted. The other case is, they fix a bug, then disappear, so if the maintainer has feedback, now they have to take time to check out the person's code, update it, out of their current planned work.
I wonder if more open source projects would benefit from adding plugin architecture so people can do those one-off features as plugins without "tainting" the core project.
Honestly in open source I'd argue it's not. If an OSS project has significant usage, if BDFL struggles/etc - community forks can put the project back on pace. NeoVim is the classic (successful) example that gave us a great alternative while also nudging the BDFL into the modern age.
Communist assets?
https://en.wikipedia.org/wiki/Arrow%27s_impossibility_theore...
Especially opens source ones, where anyone can spawn their own kingdom via a simple "git clone".
I think in the case of Linus, you REALLY have to be strict. I mean, its arguably one of if not the most used pieces of software deployed in enterprise and globally for all manner of use cases.
As quote goes, “I thought we were an autonomous collective.” Humans have some sort of tribal or pack instinct; if we want to do a project, we’ll often naturally form up around the person who’s doing it, willingly and without compulsion. The leader is the leader because everyone agrees they are doing good enough.
Open source project is sort of an ideal form of this. Unlike physical projects, the ability to fork for free means that the leader doesn’t even have the implicit moat that a potential alternative leader needs to take the current work-in-progress away from them.
BDFL is just a joke title.
IMO no governance model can defend against supply chain attacks.
IMO you shouldn’t try to fix things with governance model which it doesn’t fix. Use a governance model that leads to good governance. In technical and creative work single highest point of authority is best.
Not because they have perfect knowledge or are the best but because dictator led models lead to best alignment - which is something you simply need to execute when performing a complex task with many stakeholders.
Dictator led models aren’t best for everything. But for complex tasks with specific fixed scope with multiple concurrent contributors it seems to be the only model that works (if there are case studies as counter example I would love read on them! I.e. complex task, more than 10 contributors, non-dictator governance).
[1] https://en.wikipedia.org/wiki/Benevolent_dictator_for_life
The single project owner decides what goes and doesn't go into the project by merging or rejecting PRs. People are still free to maintain their own forks with contributions that are not accepted into the main version.
In a way, the entire Github workflow is built around the idea of BDFL managed projects.
It’s an accurate observation that single contributor projects work well since then there is no need to spend effort in coordination and communication.
...IMHO it's the other way around, somebody needs to do the difficult and unpopular decisions exactly for those hopefully rare situations where the self-coordination within a team fails (and doing that in a way that doesn't piss off people in the team). In the end, contributors to an open source project are also just a very loosely coupled team.
'Unpopular' decisions are much easier to do in projects that have a universally accepted and respected BDFL (ideally the project founder) than in most 'commercial' teams led by random 'management-caste' peeps.
Isn’t the manager himself the BDFL - or a part of a BDFL led chain of command - for the team he is empowering in this case?
He does not give out the organizational governance-authority just by empowering the team.
Having a BDFL does not imply micromanagement or not empowering teams. It’a a different granularity role than implementing day-to-day issues.
There are people who are really talented leaders. The problem are bad leaders - not the governance model IMO. And this is not a “no true scotsman” argument since effective leadership is a documented and observable phenomenon.
Lack of authority is probably better than poor leadership but not better than good leadership (e.g. legendary John Kelly of Skunkworks fame for an fairly well documented example).
And given the article describes big fails at basically every aspect of project management (from github account to money and website), not sure where you see the benefits of the supposed "focus"
I don't know it seems to me that committees are famously bad at it, so that we use "design by committee" for something that refuses to take a single direction and either makes bad compromises or says "yes" to all options.
Those that are paralyzed by fear of change and say no to everything, and those that are afraid of offending people and say yes to everything. Both are counterproductive.
There are counter examples of course but the power dynamic of committees is not conductive to results that have properties desirable in software.
I'm not a particular fan of XML, even if it's functional enough to get the job done.
Of course you have to find a dictator that is ready to invest all the time and energy to care for a project over a prolonged time and is actually capable of doing so while avoiding to alienate the user base. That's a pretty tall order.
XML by itself is okay-ish. The true design by comittee disasters are the specs surrounding it. XMLSignature, SOAP, etc
It depends on your motivation. Probably a lot (many, most?) committees can just sit pretty and try to do as little as possible. But a lot of software projects exist by getting attention, and they tend to do that by adding features. A lot of FOSS maintainers find it super hard to say no. For some anecdata: I have a small/medium project and I've found that just the energy requirements of really thinking about everything everybody proposes are pretty high. I could just say "yes", but then I'm on the hook. I could also just say "no", but then I'm discouraging people and not really giving them any information about how to contribute productively--this would be something like, "sure we could add a flag to turn video upside down, but I'm concerned that putting this kind of functionality in flags means we'll have a UX of 1000 flags that no one can remember or use; should we start considering building another place to put this kind of functionality?
> just the energy requirements of really thinking about everything everybody proposes are pretty high
Indeed, that's why it most often results in a no (your ending quote is just a polite way of saying no), and you're right about the discouragement part, and that's one of the reasons forks like neovim appear. (and unfortunately often you can't "motivate" your way into creating enough time for all that extra work either, so with the best intentions... no it is )
On the contrary, saying No to an otherwise great contribution that doesn't fit into the longterm vision of a project for one or another reason is the hardest thing in the world.
No thanks. I dont care how many golden elephants are in the trunk if I cannot drive it.
If neovim thought that something was broken, it was a very valid reason for a quick change. That’s how changes are made and I think most Vimers respect that. It was a proper community fork, almost a textbook example despite the initial buzz. Everyone got what they needed.
But if you refuse to update, it’s a clear signal for ideological mismatch. The usual issue with picking pace, as I see it through years, is that there’s often no clear finish line where you switch to walking again. The pace just stays like that forever and people start to grow tired of changes they were happy about.
Will that kill Neovim? I don’t think so. People who migrated to it (opposed to newcomers) were built for a change too and will probably “survive” all that. Is your method of dealing with it valid? In principle I agree, nothing wrong with that either.
I've moved away from maintaining my own vimrc, and towards vim distributions, and they all seem to be neovim targeted. First it was LunarVim, and more recently after Lunar stopped being maintained to AstroVim. They have been quite good at batteries included vim, and I'll never go back to vim without LSP.
I really love the Helix editor, and haven't had these issues yet, but it's not intended as a 1-1 vim replacement.
Sure the configuration file is retro compatible, but some of the plugins are better suited for neovim and vice versa. I use a dozen of them and if I switch permanently to neovim I'd like to start fresh using more "modern" alternatives that make use of the newer features.
I ended up moving to Kakoune. The community is small but the tool is so much better designed and integrates well with unix. That means that i can usually glue together whatever I need myself with 1-3 lines of config and don't need an entire plugin when I want something that isn't built-in.
I also have seen that the very first time I tried neovim. Some people may like it but I consider a terminal to be as simple as possible. Furthermore, I use often the CLI on non-GUI terminals where such non-ASCII characters can have various side effects (e.g. unicode bars, braille like progress bar and so on, those destroy your serial terminal line).
I don't think I've seen plugins with animations (nor would I want to). Agreed that emoji in the terminal, modulo useful glyphs like language logos next to files in a directory tree [0], are distracting.
What I don't understand are people who add a million plugins, and then wonder why the startup performance is terrible. I have a fair amount, including some I honestly rarely or never need, and startup time is still 75 msec, which is fast enough for me not to be bothered.
I wanted to like neovim for its possibly better lsp integration, but if all I need is an IDE with partial vi emulation, I might as well use VSCode.
i.e if you do :!bash in vim you enter bash in neovim you won't
I also think that "stability" and the "community" are two other things that made me switch back to vim.
When I tried it, nvim-qt was hard to associate with file extensions on windows (required something like “-- “filename”” in different shapes and hours of regedit debugging, can’t remember now), then nvim itself had issues with refreshing on manual window resize and with autoresizing the window on setting ‘lines’. And a few more os integration related issues. It was basically unusable gui-wise so I bailed out due to no good reason to stay.
I was probably talking about that astro-thing that works like a christmas tree and was the main selling point at the time.
What does that even mean? Just sounds like a lazy and false argument.
Things may have settled since then, maybe that’s why you think that this phrase looks odd.
I've been using Neovim since it forked and it has always offered a Vim-like experience, it's just that Neovim has given you more options.
Maybe that's why some think it's too "blinky" as it's possible to turn it into a blinky IDE if you really want to because it's more capable (and some do).
It's just in recent years I've migrated to Lua and the large amount of Neovim specific plugins (many that heavily depend on async for non-blocking operation of for instance picking or completion).
https://news.ycombinator.com/item?id=42811182
Vim 21.6%
Neovim 12.5%
I wonder what it looked like in prior years.But honestly at work, I think I am the only one using either a vim or emacs (I kind of use neovim and emacs but primarily neovim). In my childhood there was a TV series called "The last of his class" and it really showed old people (retirement age) doing jobs that will be gone once their retire. While some jobs truly vanished, others just transformed so drastically that they cannot be likened anymore to the job those folks did. Anyway, I feel we are watching changes in developer tooling that will be seen as the end of an era.
* https://de.wikipedia.org/wiki/Der_Letzte_seines_Standes%3F
Why not make an application you love and leave everyone else alone? The community's behavior - what I see - is a deterrent to using Neovim.
That’s a baseless accusation. I’ve been using Vim for about 25 years, and over the years have contributed small changes to some of the default plugins maintained by third parties (mostly language syntax and `filetype` configuration files). I have yet to even try Neovim but I too noticed that the rate of minor version releases and new features in Vim had increased after the Neovim project got off the ground.
I am happy with bram’s work on vim and with the neovim devs and their work on neovim.
I hope my development projects fail like Moolenaar's! Vim has been extraordinarily successful for decades, possibly the top 5 or 10 FOSS projects ever (?). Citing Vim as an example of development process failure is really incredible.
However, it is also very understandable why vim was forked by the neovim devs, in my view it has been a great success. That doesn't diminish Bram Molenaars achievements and contributions to the world.
The treesitter highlighting in neovim is also better/supports more files out of the box than vim. In vim I was far too used to syntax highlighting being broke halfway through a file for one reason or another (lines too long, syntax new or broken, etc).
I dislike setting up neovim, but I also dislike vim's heavy use of language specific plugins. In a perfect world I'd have lsp+treesitter in vim out of the box and no need to install any plugins.
One way is to inform users and prospective plugin writers that
1) Vim9 script is vastly superior to the old Vimscript, to the point where it is not unpleasant to use, and
2) it is much more conductive to writing text editor code than the general purpose Lua.
Of course this still does not mean that people will want to learn yet another scripting language to write Vim plugins in particular when they already know Lua, but it is very important to be adequately informed about the two above points.
Lua is very much not a general-purpose Language. It can be used like one, but it's a specialized language thought to live inside a "host" application, which it then controls. Which does seem to fit the usecase here.
Would you be able to substantiate your claim that it is more conductive to text editor code?
> it is not unpleasant to use
I'm afraid that is a very low bar. Lua is not unpleasant to use either.
For instance, when I compare Lua to vim9 script and say the former is general purpose, I am obviously in the context of comparing the one scripting language to the latter. And you know that. And yet, even though you understand the context perfectly, you still choose to write "Lua is very much not a general-purpose Language..." and proceed to patronize me on semantics.
So, will I be able to substantiate? Yes. Will I bother to do so to you? No.
Vim is not my cup of tea actually. I live in eMacs.
I understand this sentiment and that there are certainly psychological blockers in having to learn too many languages, but vim9 is very simple to learn. It is much closer to a "familiar" language than Lua. Plus, you are going to have to be familiar with Vim's standard library anyway. I believe they are adding more and more helper functions but Lua plugins are full of `vim.cmd` and `vim.fn`. I don't dislike Lua as a language at all, but I much prefer "scripting Vim" in some sort of "VimScript" :) But to each their own.
Personally I think that was already the case with "classic" VimScript, although I also appreciate it's a bit idiosyncratic and that many people don't have the time or interest to learn it.
I suppose that's also the problem with VimScript9. I agree is a real and meaningful improvement over VimScript, but I suppose that for many it's just "not important enough" to learn, even though it's not an especially difficult language to learn. I don't think that's unreasonable – there's tons of not especially difficult things I never bothered to learn in depth either, I just happened to choose Vim stuff at some point.
Surely, there will still be emacs and vim users 50 years from now, but the user numbers and the community power will diminish as the graybeards gradually leave this plane.
Emacs is fundamentally an interactive shell, like Bash. It has a text editor, also like Bash. It is of course generally more powerful and featureful than Bash.
Hence, people sometimes live in Emacs, because it's a shell like Bash or Gnome or KDE.
I use Emacs and VSCode. VSCode for some code repos, and Emacs for general computer usage.
Meanwhile, Vim is a text editor. It is neither a shell nor an IDE, although it can be adapted somewhat into an IDE.
I also use vi (alongside Emacs and VSCode). vi is for editing some text if I am not in Emacs for some reason or if I temporarily borked my Emacs config.
(I also use ed, for when I'm in a dumb terminal or I don't want to lose screen context.)
Vim or Emacs "dying" is not really an issue, although Vim or Emacs losing enough mindshare to keep them up to date as competitive IDE options, maybe that might happen.
1) Keybindings having no conflicts with other terminal applications. For that, vim's default window-management key of <C-w> is unfortunate, as it is very important for delete words in bash and readline. I remap that to <C-@> (CTRL-Space) instead, which is basically used by no terminal program that I am aware of. The other key binding that trips me up are the default vim keybindings to kill the terminal (<C-w><C-c>). If I am midway attempting a window action, but realise something is taking too long and want to stop the job, the keybinding kills my whole shell. If I really want to kill my terminal, I can always issue ZQ or :q! in normal mode.
2) Change my cursor shapes based on which mode I'm in (normal, insert, or terminal/command), which helps me easily tell the mode.
3) Improve the default keybinding for going from terminal back to terminal normal mode. I find the default keybindings <C-\><C-n> and <C-w>N too difficult to type because I need to use this binding very much.
4) Be able to move between windows with the same, consistent set of keybindings no matter which mode I'm in (e.g. not having to go back to normal mode first, from insert or terminal mode). In the same vein, when I land on a buffer I always expect to be in normal mode, so I can start moving around immediately and yank stuff without remembering whether I'm in a terminal or a non-terminal buffer. When I need to manipulate the terminal program I can always `i` or `a`.
I have quite a few other customisations to my vim terminal, but I think these are the most essential ones.
vimrc for 1)
set termwinkey=<C-@>
noremap <C-@> <C-w>
noremap <C-@><CR> <C-w><CR>
noremap! <C-@> <Esc><C-w>
noremap <C-@>c <Nop>
noremap <C-@><C-c> <Nop>
noremap <C-@><C-q> <Nop>
noremap! <C-@>c <Nop>
noremap! <C-@><C-c> <Nop>
noremap! <C-@><C-q> <Nop>
tnoremap <C-@>c <Nop>
tnoremap <C-@><C-c> <Nop>
tnoremap <C-@><C-q> <Nop>
noremap <C-@>q <Nop>
noremap! <C-@>q <Nop>
tnoremap <C-@>q <Nop>
vimrc for 2) and 3) augroup term_normal
au!
autocmd WinNew * let w:winnew = 1
autocmd CmdlineEnter * let w:outcmd = 0
autocmd CmdlineLeave * let w:outcmd = 1
autocmd WinEnter * if exists ('w:winnew') | unlet w:winnew | if (mode() == 't') | call Termcursor () | endif | elseif get(w:, 'outcmd', 1) && (mode() == 't') | call feedkeys("\<C-@>N", "") | endif
autocmd WinLeave * if (mode() == 't') && (! exists('w:tu')) | call feedkeys("\<C-@>N", 'x') | call Normalcursor () | elseif (mode(1) == 'nt') && (exists('w:tu')) | call feedkeys("i", 'ix') | endif
tmap <expr> <C-@><C-@> ((mode() == 't') && get(w:, 'outcmd', 1)) ? '<C-@>N' : '<C-@><C-@>'
augroup end
let &t_SI = "\e[6 q\e]12;#FF00DF\<C-g>"
let &t_EI = "\e[2 q\e]12;#FF00DF\<C-g>"
let &t_ti = "\e[4 q\e]12;#FF00DF\<C-g>"
let &t_te = "\e[2 q\e]12;#FF00DF\<C-g>"
let &t_Us = "\e[4:2m"
let &t_ds = "\e[4:4m"
let &t_Ds = "\e[58:5:4m\e[4:5m"
function! Normalcursor ()
let &t_ve ="\e[2 q\e]12;#FF00DF\<C-g>"
let &t_vi ="\e[2 q\e]12;#FF00DF\<C-g>"
endfunction
function! Echonormalcursor ()
call echoraw("\e[2 q\e]12;#FF00DF\<C-g>")
endfunction
function! Termcursor ()
let &t_ve ="\e[4 q\e]12;#FF00DF\<C-g>"
let &t_vi ="\e[4 q\e]12;#FF00DF\<C-g>"
endfunction
function! Echotermcursor ()
call echoraw("\e[4 q\e]12;#FF00DF\<C-g>")
endfunction
augroup termcursor
au!
autocmd ModeChanged *:n* let &t_ve ="\e[2 q\e]12;#FF00DF\<C-g>"
autocmd ModeChanged *:n* let &t_vi ="\e[2 q\e]12;#FF00DF\<C-g>"
autocmd ModeChanged i:* if &t_ve == "" | let &t_ve ="\e[2 q\e]12;#FF00DF\<C-g>" | endif
autocmd ModeChanged i:* if &t_vi == "" | let &t_vi ="\e[2 q\e]12;#FF00DF\<C-g>" | endif
autocmd ModeChanged *:i* let &t_ve =""
autocmd ModeChanged *:i* let &t_vi =""
autocmd ModeChanged *:t call echoraw("\e[4 q\e]12;#FF00DF\<C-g>")
autocmd ModeChanged *:t let &t_ve ="\e[4 q\e]12;#FF00DF\<C-g>"
autocmd ModeChanged *:t let &t_vi ="\e[4 q\e]12;#FF00DF\<C-g>"
autocmd ModeChanged t:* call echoraw("\e[2 q\e]12;#FF00DF\<C-g>")
autocmd ModeChanged t:* if (mode(1) != 'ct') | let &t_ve ="\e[2 q\e]12;#FF00DF\<C-g>" | endif
autocmd ModeChanged t:* if (mode(1) != 'ct') | let &t_vi ="\e[2 q\e]12;#FF00DF\<C-g>" | endif
let g:cmdlinedepth = 0
autocmd CmdlineEnter * let g:cmdlinedepth = g:cmdlinedepth + 1 | let &t_ve = "\e[4 q\e]12;#FF00DF\<C-g>" | let &t_vi = "\e[4 q\e]12;#FF00DF\<C-g>"
autocmd CmdlineLeave * let g:cmdlinedepth = g:cmdlinedepth - 1 | if g:cmdlinedepth == 0 | if (mode(1) == 'ct') | call Termcursor () | else | call Normalcursor () | endif | endif
autocmd CmdwinEnter * let &t_ve ="\e[2 q\e]12;#FF00DF\<C-g>" | let &t_vi ="\e[2 q\e]12;#FF00DF\<C-g>"
autocmd CmdwinLeave * let &t_ve = "\e[4 q\e]12;#FF00DF\<C-g>" | let &t_vi = "\e[4 q\e]12;#FF00DF\<C-g>"
" autocmd WinEnter * call echoraw("\e[2 q\e]12;#FF00DF\<C-g>") vnoremap <C-@>s <C-w>sgv
vnoremap <C-@>v <C-w>vgv
nnoremap <expr> <C-@>w '@_' .. ((v:count1 + (winnr() - 1)) % (winnr('$')) + 1) .. '<C-w>w'
nnoremap <expr> <C-@>W '@_' .. (((- v:count1 + (winnr() - 1)) % (winnr('$')) + winnr('$')) % (winnr('$')) + 1) .. '<C-w>w'
nnoremap <expr> <C-@>t '@_' .. ((v:count1 - 1) % (winnr('$')) + 1) .. '<C-w>w'
nnoremap <expr> <C-@>b '@_' .. (((- v:count1) % (winnr('$')) + winnr('$')) % (winnr('$')) + 1) .. '<C-w>w'
inoremap <C-@><C-@> <C-@>
map! <C-@>w <Esc><C-@>w
map! <C-@>W <Esc><C-@>W
map! <C-@>t <Esc><C-@>t
map! <C-@>b <Esc><C-@>b
tmap <C-@>w <C-@>N<C-@>w
tmap <C-@>W <C-@>N<C-@>W
tmap <C-@>t <C-@>N<C-@>t
tmap <C-@>b <C-@>N<C-@>bInvoke terminals on directories (instead of netrw). So, I can e.g. sp. to split to a new terminal.
function! Isdir(dir) abort
return !empty(a:dir) && (isdirectory(a:dir) ||
\ (!empty($SYSTEMDRIVE) && isdirectory('/'..tolower($SYSTEMDRIVE[0])..a:dir)))
endfunction
augroup terminal-explorer
au!
au VimEnter * sil! au! FileExplorer *
au VimEnter * sil! au! Network *
au VimEnter * sil! au! AuNetrwEvent *
" au FileType netrw cd % | bw | exe 'terminal ++curwin'
au BufEnter * if &filetype != 'netrw' && Isdir(expand('%')) | lcd % | exe bufnr("%") ..'bufdo terminal ++curwin' | endif
augroup end
Make <C-@>m close windows instead: noremap <C-@>m <C-w>c
noremap! <C-@>m <Esc><C-w>c
tnoremap <C-@>m <C-@>c
Make <C-@>p find the next window if there is no previous window: noremap <expr> <silent> <C-@>p ':<C-u>wincmd p <Bar> if win_getid () == ' .. win_getid () .. ' <Bar> wincmd w <Bar> endif <CR>'
noremap! <expr> <silent> <C-@>p '<Esc>:<C-u>wincmd p <Bar> if win_getid () == ' .. win_getid () .. ' <Bar> wincmd w <Bar> endif <CR>'
tnoremap <expr> <silent> <C-@>p '<C-@>:<C-u>wincmd p <Bar> if win_getid () == ' .. win_getid () .. ' <Bar> wincmd w <Bar> endif <CR>'
Use <C-@>~. to detach the GNU screen that my vim is running in (I put my vim in a screen with `unbindall` i.e. I use screen merely for keeping my vim alive) (<C-@>~. is in analogy to the SSH quit keybinding <CR>~.). noremap <expr> <silent> <C-@>~. ':<C-u>silent !screen -X detach<CR>'
noremap! <expr> <silent> <C-@>~. '<C-@>:<C-u>silent !screen -X detach<CR>'
tnoremap <expr> <silent> <C-@>~. '<C-@>:<C-u>silent !screen -X detach<CR>'
With gf and gF, opening files just from a terminal grep is practically more convenient than vim's :grep. For gf in a new tab: map <silent> <C-@><C-f> :<C-u>tab split<CR>gf
With terminal multiplexing in vim, you start getting a lot of buffers. You can consider getting a custom tabline. Other tab management features become more important too. You might expect [count]gt to behave just like gt for [count] times: nnoremap <expr> gt '@_' .. ((v:count1 + (tabpagenr () - 1)) % (tabpagenr('$') ) + 1) .. 'gt'
For ease of moving to the last tab: nnoremap <expr> g<C-t> '@_' .. (((v:count == 0) ? tabpagenr ('$') : v:count)) .. 'gt'
To consistently access the gt family of bindings regardless of mode: map <C-@>gt gt
map <C-@>gT gT
map <C-@>g<C-t> g<C-t>
map! <C-@>gt <Esc>gt
map! <C-@>gT <Esc>gT
map! <C-@>g<C-t> <Esc>g<C-t>
tmap <C-@>gt <C-@>Ngt
tmap <C-@>gT <C-@>NgT
tmap <C-@>g<C-t> <C-@>Ng<C-t>
When you use <C-w>T a lot to move your windows in to new tabs, you might sometimes want to move them to the previous tab instead of the next one: nnoremap <silent> <C-@><C-t> <C-w>T
nnoremap <silent> <C-@>T <C-w>T:<C-u>tabm -1<CR>
To access these two in any mode consistently: map! <C-@><C-t> <Esc><C-@><C-t>
map! <C-@>T <Esc><C-@>T
tmap <C-@><C-t> <C-@>N<C-@><C-t>
tmap <C-@>T <C-@>N<C-@>T
Analogously to gt and gT, sometimes you may want to move your tabs around, or split them: function! SSStabmadjust(tabmnum)
if a:tabmnum < tabpagenr ()
return a:tabmnum - 1
else
return a:tabmnum
endif
endfunction
noremap <silent> <C-@><C-g>s :<C-u>tab split<CR>
noremap <expr> <silent> <C-@><C-g>t '@_:<C-u>' .. SSStabmadjust ((v:count1 + (tabpagenr () - 1 )) % (tabpagenr('$') ) + 1) .. 'tabm<CR>'
noremap <expr> <silent> <C-@><C-g>T '@_:<C-u>' .. SSStabmadjust (((- v:count1 + (tabpagenr () - 1 )) % (tabpagenr('$') ) + tabpagenr('$') ) % (tabpagenr('$') ) + 1) .. 'tabm<CR>'
noremap <expr> <silent> <C-@><C-g><C-t> ':<C-u>' .. ((v:count == 0) ? "" : SSStabmadjust ((v:count % (tabpagenr ('$') )) )) .. 'tabm<CR>'
map! <C-@><C-g>s <Esc><C-@><C-g>s
map! <C-@><C-g>t <Esc><C-@><C-g>t
map! <C-@><C-g>T <Esc><C-@><C-g>T
map! <C-@><C-g><C-t> <Esc><C-@><C-g><C-t>
tmap <C-@><C-g>s <C-@>N<C-@><C-g>s
tmap <C-@><C-g>t <C-@>N<C-@><C-g>t
tmap <C-@><C-g>T <C-@>N<C-@><C-g>T
tmap <C-@><C-g><C-t> <C-@>N<C-@><C-g><C-t> map <expr> <C-@>1 Termcountmap(1, "")
map <expr> <C-@>2 Termcountmap(2, "")
map <expr> <C-@>3 Termcountmap(3, "")
map <expr> <C-@>4 Termcountmap(4, "")
map <expr> <C-@>5 Termcountmap(5, "")
map <expr> <C-@>6 Termcountmap(6, "")
map <expr> <C-@>7 Termcountmap(7, "")
map <expr> <C-@>8 Termcountmap(8, "")
map <expr> <C-@>9 Termcountmap(9, "")
map! <expr> <C-@>1 Termcountmap(1, '<Esc>')
map! <expr> <C-@>2 Termcountmap(2, '<Esc>')
map! <expr> <C-@>3 Termcountmap(3, '<Esc>')
map! <expr> <C-@>4 Termcountmap(4, '<Esc>')
map! <expr> <C-@>5 Termcountmap(5, '<Esc>')
map! <expr> <C-@>6 Termcountmap(6, '<Esc>')
map! <expr> <C-@>7 Termcountmap(7, '<Esc>')
map! <expr> <C-@>8 Termcountmap(8, '<Esc>')
map! <expr> <C-@>9 Termcountmap(9, '<Esc>')
tmap <expr> <C-@>1 Termcountmap(1, '<C-@>N')
tmap <expr> <C-@>2 Termcountmap(2, '<C-@>N')
tmap <expr> <C-@>3 Termcountmap(3, '<C-@>N')
tmap <expr> <C-@>4 Termcountmap(4, '<C-@>N')
tmap <expr> <C-@>5 Termcountmap(5, '<C-@>N')
tmap <expr> <C-@>6 Termcountmap(6, '<C-@>N')
tmap <expr> <C-@>7 Termcountmap(7, '<C-@>N')
tmap <expr> <C-@>8 Termcountmap(8, '<C-@>N')
tmap <expr> <C-@>9 Termcountmap(9, '<C-@>N')
function! Termcountmap(initcount, normalkeys)
let termcount = a:initcount
while 1
try
let char = getchar()
catch /^Vim:Interrupt$/
return ""
endtry
if type(char) == 0
let char = nr2char(char)
endif
if char == '0'
let termcount = termcount * 10
elseif char == '1'
let termcount = termcount * 10 + 1
elseif char == '2'
let termcount = termcount * 10 + 2
elseif char == '3'
let termcount = termcount * 10 + 3
elseif char == '4'
let termcount = termcount * 10 + 4
elseif char == '5'
let termcount = termcount * 10 + 5
elseif char == '6'
let termcount = termcount * 10 + 6
elseif char == '7'
let termcount = termcount * 10 + 7
elseif char == '8'
let termcount = termcount * 10 + 8
elseif char == '9'
let termcount = termcount * 10 + 9
elseif char == 'g'
return a:normalkeys .. termcount .. 'g'
elseif char == "\<C-g>"
return a:normalkeys .. termcount .. "\<C-@>\<C-g>"
elseif char == 'w'
return a:normalkeys .. termcount .. "\<C-@>w"
elseif char == 'W'
return a:normalkeys .. termcount .. "\<C-@>W"
elseif char == 't'
return a:normalkeys .. termcount .. "\<C-@>t"
elseif char == 'b'
return a:normalkeys .. termcount .. "\<C-@>b"
else
return a:normalkeys .. ":\<C-u>" .. termcount .. ' wincmd ' .. char .. "\<CR>\<C-l>"
endif
endwhile
endfunction
map <expr> g<C-@> <C-@>g
map <expr> g1 Prefixcountmap (v:count * 10 + 1, "", 'g')
map <expr> g2 Prefixcountmap (v:count * 10 + 2, "", 'g')
map <expr> g3 Prefixcountmap (v:count * 10 + 3, "", 'g')
map <expr> g4 Prefixcountmap (v:count * 10 + 4, "", 'g')
map <expr> g5 Prefixcountmap (v:count * 10 + 5, "", 'g')
map <expr> g6 Prefixcountmap (v:count * 10 + 6, "", 'g')
map <expr> g7 Prefixcountmap (v:count * 10 + 7, "", 'g')
map <expr> g8 Prefixcountmap (v:count * 10 + 8, "", 'g')
map <expr> g9 Prefixcountmap (v:count * 10 + 9, "", 'g')
tmap <expr> <c-@>g1 Prefixcountmap (v:count * 10 + 1, '<c-@>n', 'g')
tmap <expr> <c-@>g2 Prefixcountmap (v:count * 10 + 2, '<c-@>n', 'g')
tmap <expr> <c-@>g3 Prefixcountmap (v:count * 10 + 3, '<c-@>n', 'g')
tmap <expr> <c-@>g4 Prefixcountmap (v:count * 10 + 4, '<c-@>n', 'g')
tmap <expr> <c-@>g5 Prefixcountmap (v:count * 10 + 5, '<c-@>n', 'g')
tmap <expr> <c-@>g6 Prefixcountmap (v:count * 10 + 6, '<c-@>n', 'g')
tmap <expr> <c-@>g7 Prefixcountmap (v:count * 10 + 7, '<c-@>n', 'g')
tmap <expr> <c-@>g8 Prefixcountmap (v:count * 10 + 8, '<c-@>n', 'g')
tmap <expr> <c-@>g9 Prefixcountmap (v:count * 10 + 9, '<c-@>n', 'g')
map <expr> <C-@><C-g>1 Prefixcountmap (v:count * 10 + 1, "", '<C-@><C-g>')
map <expr> <C-@><C-g>2 Prefixcountmap (v:count * 10 + 2, "", '<C-@><C-g>')
map <expr> <C-@><C-g>3 Prefixcountmap (v:count * 10 + 3, "", '<C-@><C-g>')
map <expr> <C-@><C-g>4 Prefixcountmap (v:count * 10 + 4, "", '<C-@><C-g>')
map <expr> <C-@><C-g>5 Prefixcountmap (v:count * 10 + 5, "", '<C-@><C-g>')
map <expr> <C-@><C-g>6 Prefixcountmap (v:count * 10 + 6, "", '<C-@><C-g>')
map <expr> <C-@><C-g>7 Prefixcountmap (v:count * 10 + 7, "", '<C-@><C-g>')
map <expr> <C-@><C-g>8 Prefixcountmap (v:count * 10 + 8, "", '<C-@><C-g>')
map <expr> <C-@><C-g>9 Prefixcountmap (v:count * 10 + 9, "", '<C-@><C-g>')
map! <expr> <C-@><C-g>1 Prefixcountmap (v:count * 10 + 1, '<Esc>', '<C-@><C-g>')
map! <expr> <C-@><C-g>2 Prefixcountmap (v:count * 10 + 2, '<Esc>', '<C-@><C-g>')
map! <expr> <C-@><C-g>3 Prefixcountmap (v:count * 10 + 3, '<Esc>', '<C-@><C-g>')
map! <expr> <C-@><C-g>4 Prefixcountmap (v:count * 10 + 4, '<Esc>', '<C-@><C-g>')
map! <expr> <C-@><C-g>5 Prefixcountmap (v:count * 10 + 5, '<Esc>', '<C-@><C-g>')
map! <expr> <C-@><C-g>6 Prefixcountmap (v:count * 10 + 6, '<Esc>', '<C-@><C-g>')
map! <expr> <C-@><C-g>7 Prefixcountmap (v:count * 10 + 7, '<Esc>', '<C-@><C-g>')
map! <expr> <C-@><C-g>8 Prefixcountmap (v:count * 10 + 8, '<Esc>', '<C-@><C-g>')
map! <expr> <C-@><C-g>9 Prefixcountmap (v:count * 10 + 9, '<Esc>', '<C-@><C-g>')
tmap <expr> <C-@><C-g>1 Prefixcountmap (v:count * 10 + 1, '<C-@>N', '<C-@><C-g>')
tmap <expr> <C-@><C-g>2 Prefixcountmap (v:count * 10 + 2, '<C-@>N', '<C-@><C-g>')
tmap <expr> <C-@><C-g>3 Prefixcountmap (v:count * 10 + 3, '<C-@>N', '<C-@><C-g>')
tmap <expr> <C-@><C-g>4 Prefixcountmap (v:count * 10 + 4, '<C-@>N', '<C-@><C-g>')
tmap <expr> <C-@><C-g>5 Prefixcountmap (v:count * 10 + 5, '<C-@>N', '<C-@><C-g>')
tmap <expr> <C-@><C-g>6 Prefixcountmap (v:count * 10 + 6, '<C-@>N', '<C-@><C-g>')
tmap <expr> <C-@><C-g>7 Prefixcountmap (v:count * 10 + 7, '<C-@>N', '<C-@><C-g>')
tmap <expr> <C-@><C-g>8 Prefixcountmap (v:count * 10 + 8, '<C-@>N', '<C-@><C-g>')
tmap <expr> <C-@><C-g>9 Prefixcountmap (v:count * 10 + 9, '<C-@>N', '<C-@><C-g>')
function! Prefixcountmap(initcount, prefix, countprefix)
let termcount = a:initcount
while 1
try
let char = getchar()
catch /^Vim:Interrupt$/
return ""
endtry
if type(char) == 0
let char = nr2char(char)
endif
if char == '0'
let termcount = termcount * 10
elseif char == '1'
let termcount = termcount * 10 + 1
elseif char == '2'
let termcount = termcount * 10 + 2
elseif char == '3'
let termcount = termcount * 10 + 3
elseif char == '4'
let termcount = termcount * 10 + 4
elseif char == '5'
let termcount = termcount * 10 + 5
elseif char == '6'
let termcount = termcount * 10 + 6
elseif char == '7'
let termcount = termcount * 10 + 7
elseif char == '8'
let termcount = termcount * 10 + 8
elseif char == '9'
let termcount = termcount * 10 + 9
else
return a:prefix .. termcount .. a:countprefix .. char
endif
endwhile
endfunction augroup term_normal
au!
autocmd WinNew * let w:winnew = 1
autocmd CmdlineEnter * let w:outcmd = 0
autocmd CmdlineLeave * let w:outcmd = 1
autocmd WinEnter * if exists ('w:winnew') | unlet w:winnew | if (mode() == 't') | call Termcursor () | endif | elseif get(w:, 'outcmd', 1) && (mode() == 't') | call feedkeys("\<C-@>N", "") | endif
autocmd WinLeave * if (mode() == 't') && (! exists('w:tu')) | call feedkeys("\<C-@>N", 'x') | call Normalcursor () | elseif (mode(1) == 'nt') && (exists('w:tu')) | call feedkeys("i", 'ix') | endif
tmap <expr> <C-@><C-@> ((mode() == 't') && get(w:, 'outcmd', 1)) ? '<C-@>N' : '<C-@><C-@>'
augroup end
let &t_SI = "\eP\e[6 q\e]12;#FF00DF\<C-g>\e\\"
let &t_EI = "\eP\e[2 q\e]12;#FF00DF\<C-g>\e\\"
let &t_ti = "\eP\e[4 q\e]12;#FF00DF\<C-g>\e\\"
let &t_te = "\eP\e[2 q\e]12;#FF00DF\<C-g>\e\\"
let &t_Us = "\eP\e[4:2m\e\\"
let &t_ds = "\eP\e[4:4m\e\\"
let &t_Ds = "\eP\e[58:5:3m\e[4:5m\e\\"
function! Normalcursor ()
let &t_ve ="\eP\e[2 q\e]12;#FF00DF\<C-g>\e\\"
let &t_vi ="\eP\e[2 q\e]12;#FF00DF\<C-g>\e\\"
endfunction
function! Echonormalcursor ()
call echoraw("\eP\e[2 q\e]12;#FF00DF\<C-g>\e\\")
endfunction
function! Termcursor ()
let &t_ve ="\eP\e[4 q\e]12;#FF00DF\<C-g>\e\\"
let &t_vi ="\eP\e[4 q\e]12;#FF00DF\<C-g>\e\\"
endfunction
function! Echotermcursor ()
call echoraw("\eP\e[2 q\e]12;#FF00DF\<C-g>\e\\")
endfunction
augroup termcursor
au!
autocmd ModeChanged *:n* let &t_ve ="\eP\e[2 q\e]12;#FF00DF\<C-g>\e\\"
autocmd ModeChanged *:n* let &t_vi ="\eP\e[2 q\e]12;#FF00DF\<C-g>\e\\"
autocmd ModeChanged i:* if &t_ve == "" | let &t_ve ="\eP\e[2 q\e]12;#FF00DF\<C-g>\e\\" | endif
autocmd ModeChanged i:* if &t_vi == "" | let &t_vi ="\eP\e[2 q\e]12;#FF00DF\<C-g>\e\\" | endif
autocmd ModeChanged *:i* let &t_ve =""
autocmd ModeChanged *:i* let &t_vi =""
autocmd ModeChanged *:t call echoraw("\eP\e[4 q\e]12;#FF00DF\<C-g>\e\\")
autocmd ModeChanged *:t let &t_ve ="\eP\e[4 q\e]12;#FF00DF\<C-g>\e\\"
autocmd ModeChanged *:t let &t_vi ="\eP\e[4 q\e]12;#FF00DF\<C-g>\e\\"
autocmd ModeChanged t:* call echoraw("\eP\e[2 q\e]12;#FF00DF\<C-g>\e\\")
autocmd ModeChanged t:* if (mode(1) != 'ct') | let &t_ve ="\eP\e[2 q\e]12;#FF00DF\<C-g>\e\\" | endif
autocmd ModeChanged t:* if (mode(1) != 'ct') | let &t_vi ="\eP\e[2 q\e]12;#FF00DF\<C-g>\e\\" | endif
" TODO -- doesnt restore from redraw?
let g:cmdlinedepth = 0
autocmd CmdlineEnter * let g:cmdlinedepth = g:cmdlinedepth + 1 | let &t_ve = "\eP\e[4 q\e]12;#FF00DF\<C-g>\e\\" | let &t_vi = "\eP\e[4 q\e]12;#FF00DF\<C-g>\e\\"
autocmd CmdlineLeave * let g:cmdlinedepth = g:cmdlinedepth - 1 | if g:cmdlinedepth == 0 | if (mode(1) == 'ct') | call Termcursor () | else | call Normalcursor () | endif | endif
autocmd CmdwinEnter * let &t_ve ="\eP\e[2 q\e]12;#FF00DF\<C-g>\e\\" | let &t_vi ="\eP\e[2 q\e]12;#FF00DF\<C-g>\e\\"
autocmd CmdwinLeave * let &t_ve = "\eP\e[4 q\e]12;#FF00DF\<C-g>\e\\" | let &t_vi = "\eP\e[4 q\e]12;#FF00DF\<C-g>\e\\"
" autocmd WinEnter * call echoraw("\eP\e[2 q\e]12;#FF00DF\<C-g>\e\\")
augroup endAlso, I don't really miss anything from more advanced ides when in vim. There are great packages for almost anything.
I went from vim to neovim, but the LSPs for python/go(lang) for large files (not that large, maybe 10k loc) seem to really bog it down (back then, no idea if it's better now), whereas with VS Code it was still performant enough. So I ended up using VS Code with Vim bindings.
And yes, with ad hoc work, I still end up using system vim when just doing simple edits (e.g. adding a line to README.md or somesuch)
emacs is surely on a decline, but it’s not obvious that Vim is on the same trend.
This matches “theory” and anecdotal evidence: the people who chose emacs probably didn’t like modal editing, and when “better” IDEs came along they just switched. But there’s nothing like Vim (except editors specifically inspired by Vim), and those haven’t gotten any traction if only because every one of us Vim users have hjkl muscle memory burned into our brains.
https://trends.google.com/trends/explore?date=all&q=%2Fm%2F0...
both decline, but emacs is steeper
I will concede that VS Code is the default for many, but I just can't get productive in it anymore. I mainly use intellij, which has its own issues. But I can't say I've ever mastered any editor, the closest was sublime text, and that mastery mainly came from being able to use cmd+p and global search effectively.
At this point, however, I do not really use Neovim anymore. I switched to Zed, the Vim emulation is pretty good and customizable and most functionality I want is already there along with incoming support for Jupyter Notebooks. VSCode also has these features.
It is fun to use Vim/Neovim but unless I need to use it, I doubt I will return to it.
In the end, it doesn't really matter what other people are using. If Vim (or Emacs) works for you, then you can basically keep using it until the end of time since code are just text files, and these things don't really change all that much (outside of encodings, which is the biggest problem with older editors – but I don't see UTF-8 replaced any time soon, if ever).
I don't really know what the kids these days are up to, but I don't think it really matters. I guess there's still few Bill Joys around using ed, but the existence of vi, Vim, VSCode, or anything else doesn't really take away anything for them.
MANPAGER=nvim --clean -c "colo sorbet" +Man!
Startup speed is blistering.My current config is pretty stable, and not that large. But if it were causing issues, I'd seriously consider only doing LSP setup, which is not that onerous with the latest APIs (it was already fairly easy with `vim.lsp.start`, but `vim.lsp.config` and `vim.lsp.enable` make it easier still: https://neovim.io/doc/user/lsp.html).
A multithreaded version of emacs would also be an interesting addition; I read some arguments against moving emacs to a multithreaded model, but I don't really remember them.
Everyone including the maintainers would like this to happen. The arguments against it are technical hurdles. Emacs is a large ball of global state and the lisp evaluator hooks into everything, including the display engine, so it's not clear to anyone how to disentangle things to the point where the interpreter lock can be released.
Apart from that, I don’t really know what an application would be to Emacs as nvim is to Vim. It’s more like nvim is to Vim what Emacs is to nano, except Emacs came first.
emacs is 4% vs VSCode 73%, so it's not popular, though vim still is
Visual Studio Code 73.6%
Visual Studio 29.3%
IntelliJ IDEA 26.8%
Notepad++ 23.9%
Vim 21.6%
Android Studio 16.1%
PyCharm 15.1%
Jupyter Notebook/JupyterLab 12.8%
Neovim 12.5%
...
Emacs 4.2%
...
Spacemacs 0.4%
So Vim + Neovim = 34.1%, essentially second to Visual Studio, the overwhelming leader.EDIT: Ok, maybe the reason distributions were created is because the integration of some lsp/treesitter stuff enabled it/made it easier ? So if not technically superior, at least more capable
I have Neovim, it still haven't replaced vim yet. But I see the reasoning, IF I want to use a editor to do heavy development, Neovim seems to have more detailed syntax highlighting, and yes LSP integration good.
I use intelliJ with ideaVim for my work, and I don't think these editors can fill the capability that JetBrains offers. Even though vim has a special place in my heart
I keep wanting them to make a Neovim headless plugin for their IDEs.
In the docs it shows the minimal configuration to get AstroNvim running which is <10 lines in your ~/.config/init.lua file and then anything else you can just drop in that same file if you want. (https://github.com/AstroNvim/AstroNvim?tab=readme-ov-file#mi...)
Here is a user on GitHub that has a single file AstroNvim configuration: https://github.com/20k-ultra/dotfiles/blob/master/nvim/init....
Though I might one day switch to Zed or Helix -- I want an editor that has more bells and whistles built-in and that I could just switch them off if I don't want them. Which should be a much smaller configuration footprint compared to what we have today with Neovim.
It feels like every single user-facing open source project needs to have its own XDG drama at some point.
What's the big deal ?
But I do get that for those of us who’ve been using Unix for decades it can be a bit weird when stuff which used to be under $HOME/.$WHATEVER ends up under $HOME/.config/$WHATEVER.
vim.org was created (probably around 1998) and has been owned by Sven Guckes for most of its existence. In the beginning Sven also managed the content but I think at some point Bram took over. Unfortunately Sven passed away not long before Bram.
LSP as a replacement for original vi's cscope integration is the main reason I eventually switched from nvi to vim.
As a new user to NeoVim, I was okay with investing some time, but man it feels like each update to NeoVim itself, or even the popular plugins, breaks something that I then need to go hunt down and fix. Every answer online isn't any better, pointing to 5 different doc pages. I like my IDEs to "just work" and continue to do so after I have them configured.