Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

I wonder how long vim and emacs can stay vibrant. I've used emacs in the last 20 years, so I stick with it, but new generations who are trained on vscode and such are less likely to use such "old fashioned" tools.

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.



You can't really compare vim and emacs beyond a superficial level.

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.


Just for the other side of the picture, I live in vim and use it as my terminal multiplexer. Vim’s my shell. I have thousands of buffers in vim and practically never leave it. I’ve used terminal multiplexers for years before I switched to vim in that capacity and never looked back. The integration it’s allowed between all my buffers and commands and shells is difficult to match in my opinion.


As a 3rd anecdote, I used vim for 10 years as my primary editor and “shell”. Then 10 years ago I learned tmux and fell in love with its window multiplexing. Now I use vim strictly as an editor/splitter. But I use tmux to split my code from my repl window. And maintain multiple windows where I’m working on different projects.


I'm going to try this. I use tmux but the idea of being able to use vim for everything seems nice. If you have tips/suggestions, I'm interested.


For me, some of the things that's really important for a terminal multiplexer include:

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>")


vimrc for 4)

    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-@>b


Some other things I do:

Invoke 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>


When you get to using [count]g[something] and g[count][something] and <C-@>[count]g[something] etc a lot, you start getting a bit confused about where you need to put the count to make things work. So you can make putting the count anywhere work; now, don't worry and just start pressing g or <C-@>g for window actions, and put a count if you realise you need it:

    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


(equivalently, if you use screen; the key point is the \eP and the \e\\ between which the real xterm escape codes go, or else screen will eat your escape codes which will never see the light of day)

    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 end


thanks for the reply and tips, lots of useful information in there.


Lots of editors and IDEs came and went while Emacs/Vim persisted. Through my three decade career I recall the ascents and downfalls of tools like BRIEF, CodeWright, NEdit, JEdit, TextPad, Notepad++, Visual Studio, JBuilder, Eclipse, Sublime and a few others so the cemetary (or hospice in some of those cases) is large.


Sublime, nor Visual Studio are in the cemetery.


Visual Studio is very much dying. Visual Studio Code is its successor that's very much alive. Those are two different products


I'm sticking with emacs for now because it is the only editor I have encountered that actually works well in conjunction with a tiling window manager; by which I mean: it works well as a single process accessed through multiple windows (here I mean "windows" as in OS windows -- internally Emacs calls this "frames") although it has features for managing panes internally, it doesn't insist that you use them and each windows is very lightweight (no thick sidebars, embedded terminal, etc that are hard or impossible to remove). Vim offers the second feature but not the first (each window is a separate process), most other editors I've encountered do not offer the second feature.


Kakoune does this too, and it’s amazing with a tiling window manager. On a big monitor, I can get 4-5 terminal emulators across, and in any of them, at any time, I can attach a kakoune client, copy and paste between buffers in different windows, edit the same file in two places at the same time, close all the clients and reattach later, and so on. Emacs is the only other editor that does this, as far as I know.


Thanks, I might give that a try :)


To put it into perspective: vi was already 15 years old when Bram decided to write vim for the Amiga, which had a GUI - so vim already looked out of place on the Amiga too! - but it was still successful, of course (I think) mostly because of being ported to Linux pretty much at the same time as Linux got started.


Amiga by default had an Emacs clone on every install.


That's incredible. Why? And why a clone? ???


Probably because GNU Emacs was too bloated for the Amiga's 512KB of RAM (or whatever amount the original model had).


They also might have been conserving storage space. Though Emacs did run on other contemporary systems.


To me, vscode is unbearably slow. I think that alone is enough to keep vim alive.

Also, I don't really miss anything from more advanced ides when in vim. There are great packages for almost anything.


I use VIM bindings in VS Code. Always assumed many do but might be wrong


Ditto.

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)


At work I often switched to VSCode because I couldn't get pyright to work with our django project. The errors everywhere were just annoying to look at. So I looked around and found "ruff" and "jedi_language_server". This combination seems to do the trick. I don't have to configure anything. I source my venv and it "just works". I assume our python codebase is something around the 10k LOC, too. I am not mainly responsible for the python part, so I don't spent excessive amount of time in there, but for the time I do, it works nicely

- https://github.com/pappasam/jedi-language-server

- https://github.com/astral-sh/ruff


Similar. I use IntelliJ, Neovim, and some Zed all with vim keybindings. Modal editing just fits my mind. As an aside it’s also one of the reasons I dislike Notion. I feel like I’m always inadvertently changing the content.


Me too (there doesn't seem to be plugin that's completely free of issues unfortunately). Vi/Vim is basically the universal text input model which allows me to transcend text editors and platforms. Also I quite often find myself starting a vim instance inside the VSCode terminal for quick text edits.


I use the "VSCode Neovim" extension which lets me use a real Neovim instance inside VS Code, including my personalized vimrc and a lot of plugins. Not all plugins work but if they're just textual good chance they do.


Do the Vim bindings work in VSCodium as well?


This google trends graph is very illustrative: https://trends.google.com/trends/explore?date=all&q=emacs,vi...

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.


Google translate says vim = 'I came' in portuguese, so I guess that explains Brazil.


google trends can disambiguate the context

https://trends.google.com/trends/explore?date=all&q=%2Fm%2F0...

both decline, but emacs is steeper


I've never been able to use vim productively, at best I use it to write commits, do interactive rebases and some remote server configuration (cheap VPS for a website); I never really "grew up" with it and stuck with Notepad++ and Eclipse when I started out in software development nearly 20 years ago.

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.


You probably have a good reason for not doing it, but mastering your editor is a great power up. Especially when the task can be ruled based and repetitive. Like a loop of find-select-transform action.


Vim may die, but vim-mode will definitely be around.


Speaking for myself, I only used Neovim because of its modularity and the keybindings. Imho, everyone should give the basic Vim keybindings a chance at least once and see if they like it.

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.


Some time in the 80s (or maybe even 90s?) Bill Joy said he just uses ed, even though he's the original author of vi. That worked for him. I believe Linus still uses that 1980s Emacs-y clone that he kind of maintains for himself. jwz uses some flavour of Lucid Emacs/XEmacs from who-knows-when instead of "standard" GNU Emacs.

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.


Emacs and Vim have remained popular through several generations. What do developer surveys say? Vim was near the top a few years ago, iirc.


With nvim, there has been quite the resurgence of Vim. Good software tends to be resilient. I believe both emacs and vim will see many, many more years.


Neovim feels indeed the proper future-proof evolution of a standard. Its still a bit cumbersome to setup (fonts, lots of plugins to configure, opinionated and overly decorated UI etc.). The acid test of maturity is the dry functionality you get out of the box in a fresh linux. It should be "just right", introducing the new thinking and functionality of neovim without getting in the way.


I actually like `nvim --clean`, which is just the basics. Early on in the Neovim project, a lot of heirloom defaults were changed to be more modern, resulting in a better (IMHO) out-of-the-box experience. I use `nvim --clean` as my man-page viewer:

  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).


I wonder, will there be something to emacs, as nvim is to vim?


Emacs seems to be a local maximum that is difficult to overcome. An entire Lisp Machine environment would be better, but it would be a tremendous undertaking and the specialists, i.e. emacs devs, don't seem to be interested in such a thing.

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.


> 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.


There’s (arguably) an argument to be made that Emacs configuration distributions fit that niche - Doom Emacs, Spacemacs, and Prelude provide varying flavours for different kinds of Emacs users.

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.


Let's not forget that GNU Emacs also had his competitor, XEmacs which spurred GNU Emacs to improve. Similar with GCC and EGCS where the EGCS later became the new GCC.


This already happened, decades ago. Xemacs was widely deployed for a while. For that matter, vim is the most popular of several editors that did this to vi. (Elvis, stevie, nvi, others probably.) At any rate, I think Xemacs is still more or less maintained, but I haven’t seen anybody use it in decades.


sacrificing all existing elisp code makes a new editor worthless, maintaining compatibility makes it extraordinarily hard to get anywhere.


https://survey.stackoverflow.co/2024/technology#1-other-tool...

emacs is 4% vs VSCode 73%, so it's not popular, though vim still is


Thanks. In more detail:

    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.


StackOverflow isn't representative. It was always skewed towards .NET world (and thus VS). I assume this is the case because the founders were prominent .NET personalities.


There's no representative community though


What this doesn't show is people using vscode/vs with neovim driver or vim key bindings. The vim "backend" percentage is going to be significant.


I don't think you can add them, respondents could select multiple editors. I am guessing 34% is the upper limit if no Neovim users use Vim and vice versa, which is hard to believe.


This feels like it's skewed by the question: "Which development environments did you use". I know that vim is an IDE, but I still think of it as a text editor. I would probably answer "Zed" to that question (not even on the list!) and "vim" to a "Which text editor" question, although I use a third, graphical editor (CotEditor) for a lot of stuff too. I never use a full-blown 'ide' like visual studio.


I wonder why is Spacemacs listed as a separate entry?


You could probably lump PyCharm and IntelliJ together; different language modes of effectively the same editor.


I'm pleasantly surprised Notepad++ is so high up.


Isn't stackoverflow's most-accessed question to date "how do I exit vim?"


More like top-200+ by views, though even the wouldn’t contradict much else


One of the kids in my computer club found himself to be an avid Helix user. I guess once the dust settles around nvim's approach to LSP and stuff, give or take 5 years, it will build itself quite a following, including young people. I think the hackability is attractive.


I think Neovim helps with this, though last time I was using it (via... bootstrap? One of the prebuilt addon packs) at some point a Mason update broke my LSPs for multiple languages and i went back to VS Code on my Linux laptop because I didn't want to fight with it.


I ran screaming from VS when I noticed how much resources it used and what software it copies on remote servers in case you want to work remote. Did this improve at all in the last year+?




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: