0

I have following in my .vimrc

set laststatus=2
set showcmd
set wildmenu

function! UpdateStatusline()
    let currentMode = mode()
    if currentMode ==# 'n'
        let currentMode = "Normal"
    elseif currentMode ==# 'i'
        let currentMode = "Insert"
    elseif currentMode ==# 'v'
        let currentMode = "Visual"
    else
        let currentMode = "Special"
    endif

    " Update the statusline
    set statusline=
    set statusline+=%<%f
    set statusline+=%h%m%r
    set statusline+=%=
    set statusline+=%-14.(%l,%c%V%)
    set statusline+=%P
    set statusline+=\ %{currentMode}

    " Redraw the statusline
    redrawstatus
endfunction

" Automatically call the function when mode changes
au VimEnter,VimLeave,InsertEnter,InsertLeave * call UpdateStatusline()

the intent of this is to display current mode on statusline. But I got error regarding undefined variable currentMode

If I do following then the variable is visible:

colorscheme desert
set laststatus=2
set showcmd
set wildmenu
" set statusline=%<%f\ %h%m%r%=%-14.(%l,%c%V%)\ %P%{toupper(mode())}



" Check the current mode and perform actions accordingly
let currentMode=mode()
if currentMode ==# 'n'
    " This is Normal mode
    let currentMode="Normal"
elseif currentMode ==# 'i'
    " This is Insert mode
    let currentMode="Insert"
elseif currentMode ==# 'v'
    " This is Visual mode
    let currentMode="Visual"
else
    " Unknown mode or other special mode
    let currentMode="Special"
endif


set statusline=
set statusline+=%<%f
set statusline+=%h%m%r
set statusline+=%=
set statusline+=%-14.(%l,%c%V%)
set statusline+=%P
set statusline+=\ %{currentMode}

but the obvious problem is that this does not update statusline after each mode chnage

Update

The closest I get is following code where everything seems work correctly but when going from insert to normal mode I need to press one more key after ESC (no matter which, direction, or ESC) to properly show Normal on statusline

set laststatus=2
set showcmd
set wildmenu

" Define a function to retrieve the current mode
function! GetCurrentMode()
    let modeStr = mode()
    if modeStr ==# 'n'
        return "Normal"
    elseif modeStr ==# 'i'
        return "Insert"
    elseif modeStr ==# 'v' || modeStr ==# "\<C-V>"
        return "Visual"
    else
        return "Special"
    endif
endfunction

" Define the statusline with a custom expression to dynamically retrieve the mode
set statusline=%<%f%h%m%r%=%-14.(%l,%c%V%)%P\ %{GetCurrentMode()}

" Automatically update the statusline when mode changes
augroup ModeUpdate
    autocmd!
    autocmd InsertEnter,InsertLeave,TextChangedI,TextChanged,VimEnter,VimLeave * redrawstatus
    autocmd InsertLeave * call UpdateStatusline()
augroup END

" Function to update the statusline
function! UpdateStatusline()
    redrawstatus
endfunction

vim version running on WSL

:version
VIM - Vi IMproved 8.1 (2018 May 18, compiled Aug 18 2023 03:41:54)
Included patches: 1-2269, 3612, 3625, 3669, 3741
Modified by [email protected]
Compiled by [email protected]
Huge version without GUI.  Features included (+) or not (-):
+acl               -clipboard         -dnd               +gettext           +localmap          +mouse_urxvt       +profile           +statusline        +timers            +windows
+arabic            +cmdline_compl     -ebcdic            -hangul_input      -lua               +mouse_xterm       -python            -sun_workshop      +title             +writebackup
+autocmd           +cmdline_hist      +emacs_tags        +iconv             +menu              +multi_byte        +python3           +syntax            -toolbar           -X11
+autochdir         +cmdline_info      +eval              +insert_expand     +mksession         +multi_lang        +quickfix          +tag_binary        +user_commands     -xfontset
-autoservername    +comments          +ex_extra          +job               +modify_fname      -mzscheme          +reltime           -tag_old_static    +vartabs           -xim
-balloon_eval      +conceal           +extra_search      +jumplist          +mouse             +netbeans_intg     +rightleft         -tag_any_white     +vertsplit         -xpm
+balloon_eval_term +cryptv            -farsi             +keymap            -mouseshape        +num64             -ruby              -tcl               +virtualedit       -xsmp
-browse            +cscope            +file_in_path      +lambda            +mouse_dec         +packages          +scrollbind        +termguicolors     +visual            -xterm_clipboard
++builtin_terms    +cursorbind        +find_in_path      +langmap           +mouse_gpm         +path_extra        +signs             +terminal          +visualextra       -xterm_save
+byte_offset       +cursorshape       +float             +libcall           -mouse_jsbterm     -perl              +smartindent       +terminfo          +viminfo
+channel           +dialog_con        +folding           +linebreak         +mouse_netterm     +persistent_undo   +sound             +termresponse      +vreplace
+cindent           +diff              -footer            +lispindent        +mouse_sgr         +postscript        +spell             +textobjects       +wildignore
-clientserver      +digraphs          +fork()            +listcmds          -mouse_sysmouse    +printer           +startuptime       +textprop          +wildmenu
   system vimrc file: "$VIM/vimrc"
     user vimrc file: "$HOME/.vimrc"
 2nd user vimrc file: "~/.vim/vimrc"
      user exrc file: "$HOME/.exrc"
       defaults file: "$VIMRUNTIME/defaults.vim"
  fall-back for $VIM: "/usr/share/vim"
Compilation: gcc -c -I. -Iproto -DHAVE_CONFIG_H   -Wdate-time  -g -O2 -fdebug-prefix-map=/build/vim-fY89Io/vim-8.1.2269=. -fstack-protector-strong -Wformat -Werror=format-security -D_REENTRANT -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=1
Linking: gcc   -Wl,-Bsymbolic-functions -Wl,-z,relro -Wl,-z,now -Wl,--as-needed -o vim        -lm -ltinfo -lnsl  -lselinux  -lcanberra -lacl -lattr -lgpm -ldl     -L/usr/lib/python3.8/config-3.8-x86_64-linux-gnu -lpython3.8 -lcrypt -lpthread -ldl -lutil -lm -lm
1
  • Do you still have something open in your question? How can we help you further. Otherwise maybe could you accept one of the answers using the v button next to the arrow voting buttons. It allow the question to rest :-) Commented Mar 13 at 13:48

2 Answers 2

2

The proposition of @LucHermitte is:

set laststatus=2

let g:mode2text = {
      \ 'n' : 'Normal',
      \ 'i' : 'Insert',
      \ 'v': 'Visual',
      \ 'V': 'V-Line',
      \ "\<C-v>" : 'V-Block',
      \ 'R': 'Replace',
      \ 's': 'Select',
      \ 'S': 'S-Line',
      \ "\<C-s>" : 'S-Block',
      \ 'c' : 'Command',
      \ 't' : 'Terminal'
  \}

set statusline=%{g:mode2text.get(mode()[0],'Special')}

Remark: This is a simplified version that should be extended to cover all the modes.

Remark: Like @LucHermitte is explaining the expression (in this case g:mode2text[mode()]) will be evaluated at each keystroke on the global scope.

Normally every key stroke should trigger the refresh of the statusline. If that is not enough you could force to refresh it every second with the following code:

function! RefreshBar(timer)
  redrawstatus!
endfunction

call timer_start(1000, 'RefreshBar', {'repeat': -1})

Remark: You can test it with the following statusline:

set statusline=%{strftime('%Y/%m/%d\ %H:%M:%S')}
10
  • This is a very nice and working solution, but it still has the same "bug" as I've explained: when going from insert to normal mode I need to press one more key after ESC (no matter which, direction, or ESC) Commented Mar 12 at 16:48
  • That is odd. On Vim 9.1 on Windows from both gVim and Vim it is working well. Can you tell us which version of Vim you are running (:version)? Commented Mar 12 at 17:15
  • 1
    I've tried to extend key-value map with no luck let g:mode2text = { 'n' : 'Normal', 'i' : 'Insert', 'V': 'Visual', 'v': 'Visual', '^v': 'Visual','^V': 'Visual', '\<C-V>': 'Visual', '<C-V>': 'Visual' } Commented Mar 12 at 20:20
  • 1
    The expression can be made resilient with get(g:mode2text, mode(), 'Unknown Mode') Commented Mar 13 at 9:59
  • 1
    I need to apologize and correct my statement. Seems that it works as expected, but when I go from Insert to Normal mode it takes a few milliseconds (I was impatient and pressed ESC again to get the desired result). From Normal to Insert it works immediately. Commented Mar 13 at 13:29
0

statusline option is an option that exists independently of the function where it's defined. Also, with %{} you say: "dynamically update the value of the status line with the content of that expression".

The expression needs to be available from anywhere. You cannot reference a local variable in that expression.

Either hard-code the current mode in the status-line value (you'll want to use :let-& or :exe), or have the status-line be defined with the result of a global CurrentMode() function (or use a global dictionary indexed with mode()...).

Note: If I'm not mistaken, no need to listen for events with the second approach.

EDIT:

The solutions would be:

Solution 1
set laststatus=2

let g:mode2text = {
      \ 'n' : 'Normal',
      \ 'i' : 'Insert',
      \ 'v': 'Visual',
      \ 'V': 'V-Line',
      \ "\<C-v>" : 'V-Block',
      \ 'R': 'Replace',
      \ 's': 'Select',
      \ 'S': 'S-Line',
      \ "\<C-s>" : 'S-Block',
      \ 'c' : 'Command',
      \ 't' : 'Terminal'
  \}

set statusline=%<%f%h%m%r%=%-14.(%l,%c%V%)%P\ %{get(g:mode2text,mode()[0],'UNKNOWN')}
Solution 2
set laststatus=2

let s:mode2text = {
      \ 'n' : 'Normal',
      \ 'i' : 'Insert',
      \ 'v': 'Visual',
      \ 'V': 'V-Line',
      \ "\<C-v>" : 'V-Block',
      \ 'R': 'Replace',
      \ 's': 'Select',
      \ 'S': 'S-Line',
      \ "\<C-s>" : 'S-Block',
      \ 'c' : 'Command',
      \ 't' : 'Terminal'
  \}

function s:UpdateStatusLine() abort
  let &statusline='%<%f%h%m%r%=%-14.(%l,%c%V%)%P ' .. get(s:mode2text,mode()[0],'UNKNOWN')
endfunction

augroup UpdateStatusLine
  au!
  au ModeChanged * call s:UpdateStatusLine()
augroup END

Honestly? I'll vote for solution 1

2
  • Sorry but I do not get the point of you answer Commented Mar 12 at 15:22
  • The point is that what's in %{} needs to belong to the global scope. Not local scope. Also, I don't see the point of refreshing the statusline from the moment we use %{} Commented Mar 13 at 10:04

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.