commit f9b94021b062d68a7c3f7a6a5c9ebbb19d6ff2d5
parent af403165ac40091099fe717d58780df3be181942
Author: Alex Balgavy <alex@balgavy.eu>
Date:   Wed,  6 Oct 2021 19:39:09 +0200
vim: fix statusline mixed indent
Diffstat:
1 file changed, 85 insertions(+), 58 deletions(-)
diff --git a/vim/autoload/statusline.vim b/vim/autoload/statusline.vim
@@ -1,99 +1,126 @@
 " return '[&et]' if &et is set wrong
 " return '[mixed-indenting]' if spaces and tabs are used to indent
 " return an empty string if everything is fine
-function! statusline#StatuslineTabWarning()
-  if &readonly || &bt == "nofile"
-    return ""
-  endif
-
-  if !exists("b:statusline_tab_warning")
-    let tabs = search('^\t', 'nw') != 0
-    let spaces = search('^ \{' . &ts . ',}[^\t]', 'nw') != 0
+"
+" Indentation functions borrowed from airline, they do this pretty well.
+" Mixed indentation within a line. Returns the line number, or 0.
+function! s:check_mixed_line_indent()
+    " [<tab>]<space><tab>
+    " spaces before or between tabs are not allowed
+    let t_s_t = '(^\t* +\t\s*\S)'
+    " <tab>(<space> x count)
+    " count of spaces at the end of tabs should be less than tabstop value
+    let t_l_s = '(^\t+ {' . &ts . ',}' . '\S)'
+    return search('\v' . t_s_t . '|' . t_l_s, 'nw')
+endfunction
 
-    if tabs && spaces
-      let b:statusline_tab_warning =  '[mixed-indenting]'
-    elseif (spaces && !&et) || (tabs && &et)
-      let b:statusline_tab_warning = '[&et]'
+" Mixed indentation across the file. Returns line numbers
+" with_tabs:with_spaces, or empty string
+function! s:check_mixed_indent_file()
+    let c_like_langs = ['arduino', 'c', 'cpp', 'cuda', 'go', 'javascript', 'ld', 'php' ]
+    if index(c_like_langs, &ft) > -1
+        " for C-like languages: allow /** */ comment style with one space before the '*'
+        let head_spc = '\v(^ +\*@!)'
+    else
+        let head_spc = '\v(^ +)'
+    endif
+    let indent_tabs = search('\v(^\t+)', 'nw')
+    let indent_spc  = search(head_spc, 'nw')
+    if indent_tabs > 0 && indent_spc > 0
+        return printf("%d:%d", indent_tabs, indent_spc)
     else
-      let b:statusline_tab_warning = ''
+        return ''
+    endif
+endfunction
+
+function! statusline#StatuslineTabWarning()
+    if &readonly || &bt == "nofile"
+        return ""
+    endif
+
+    if !exists("b:statusline_tab_warning")
+        let mixed_on_line = s:check_mixed_line_indent()
+        let mixed_on_line_str = mixed_on_line ==# 0 ? '' : '[mixed-line '..mixed_on_line..']'
+        let mixed_in_file = s:check_mixed_indent_file()
+        let mixed_in_file_str = empty(mixed_in_file) ? '' : '[mixed-indenting '..mixed_in_file..']'
+        let b:statusline_tab_warning = mixed_on_line_str .. mixed_in_file_str
     endif
-  endif
-  return b:statusline_tab_warning
+    return b:statusline_tab_warning
 endfunction
 
 " return '[\s]' if trailing white space is detected
 " return '' otherwise
 function! statusline#StatuslineTrailingSpaceWarning()
-  if &readonly || &bt == "nofile"
-    return ""
-  endif
+    if &readonly || &bt == "nofile"
+        return ""
+    endif
 
-  if !exists("b:statusline_trailing_space_warning")
-    if search('\s\+$', 'nw') != 0
-      let b:statusline_trailing_space_warning = '[\s]'
-    else
-      let b:statusline_trailing_space_warning = ''
+    if !exists("b:statusline_trailing_space_warning")
+        if search('\s\+$', 'nw') != 0
+            let b:statusline_trailing_space_warning = '[\s]'
+        else
+            let b:statusline_trailing_space_warning = ''
+        endif
     endif
-  endif
-  return b:statusline_trailing_space_warning
+    return b:statusline_trailing_space_warning
 endfunction
 
 " build the current working directory string
 function! statusline#StatuslineBuildCwd()
-  let cwd = substitute(getcwd(),$HOME,'~','g')
-  return "(".cwd.")"
+    let cwd = substitute(getcwd(),$HOME,'~','g')
+    return "(".cwd.")"
 endfunction
 
 " get the current fold info
 function! statusline#StatuslineFoldmethod()
-  if &foldmethod == "indent"
-    return "z".&foldlevel.",c".foldlevel(line('.'))
-  elseif &foldmethod == "expr"
-    return "f:exp,l".&foldlevel
-  else
-    return "f:".strpart(&foldmethod, 0, 4)
-  endif
+    if &foldmethod == "indent"
+        return "z".&foldlevel.",c".foldlevel(line('.'))
+    elseif &foldmethod == "expr"
+        return "f:exp,l".&foldlevel
+    else
+        return "f:".strpart(&foldmethod, 0, 4)
+    endif
 endfunction
 
 " print the textwrap information (textwidth or wrapmargin)
 function! statusline#StatuslineWrapCol()
-  if &textwidth == 0 && &wrapmargin == 0 && &wrap
-    return "softwrap"
-  endif
-  if &textwidth == 0
-    return "⟺  ".(winwidth(0)-&wrapmargin)."(M".&wrapmargin.")"
-  else
-    return "⟺  ".&textwidth
-  endif
+    if &textwidth == 0 && &wrapmargin == 0 && &wrap
+        return "softwrap"
+    endif
+    if &textwidth == 0
+        return "⟺  ".(winwidth(0)-&wrapmargin)."(M".&wrapmargin.")"
+    else
+        return "⟺  ".&textwidth
+    endif
 endfunction
 
 function! statusline#StatuslineSpacesUsed()
-  if &expandtab
-    return (&shiftwidth ># 0 ? &shiftwidth : &tabstop)
-  else
-    if &shiftwidth ==# &tabstop
-      return &shiftwidth
+    if &expandtab
+        return (&shiftwidth ># 0 ? &shiftwidth : &tabstop)
     else
-      return &tabstop."s".&shiftwidth
+        if &shiftwidth ==# &tabstop
+            return &shiftwidth
+        else
+            return &tabstop."s".&shiftwidth
+        endif
     endif
-  endif
 endfunction
 
 function! statusline#StatuslineWordCount()
-  return wordcount().words . " words"
+    return wordcount().words . " words"
 endfunction
 
 function! statusline#StatuslineVimtexCompiler()
-  if exists('b:vimtex.compiler') && b:vimtex.compiler.is_running()
-    return '⚙︎ {'.(fnamemodify(b:vimtex.compiler.target_path, ":p:.")).'}'
-  endif
-  return ''
+    if exists('b:vimtex.compiler') && b:vimtex.compiler.is_running()
+        return '⚙︎ {'.(fnamemodify(b:vimtex.compiler.target_path, ":p:.")).'}'
+    endif
+    return ''
 endfunction
 
 function! statusline#StatuslineRemoteFile()
-  if exists('b:netrw_lastfile')
-    return ' ('.b:netrw_lastfile.')'
-  endif
-  return ''
+    if exists('b:netrw_lastfile')
+        return ' ('.b:netrw_lastfile.')'
+    endif
+    return ''
 endfunction