Przeglądaj źródła

update vim fold text WIP

Made cleaner, diff fold now handled differently, fold descriptions still work in
progress. Taking stuff from lokaltog's foldtext as is useful (though a fair
amount of it is also a bit disorganised).
Weiyi Lou 12 lat temu
rodzic
commit
36609b1e73
1 zmienionych plików z 170 dodań i 49 usunięć
  1. 170 49
      vim/settings/folding.vim

+ 170 - 49
vim/settings/folding.vim

@@ -1,11 +1,11 @@
 " Folding - Modeline and Notes {{{
-" vim: set sw=2 ts=2 sts=2 et tw=78 foldmarker={{{,}}} foldlevel=0 foldmethod=marker spell:
+" vim: set sw=2 ts=2 sts=2 et tw=78 foldmarker={{{,}}} foldlevel=0 foldmethod=marker:
 "
 "   cinaeco/dotfiles Folding Settings
 "
 "   These are settings to make folding easier to use and look at:
 "    - Indented Folds to match their first line.
-"    - Stat box to right displays line count and fold level.
+"    - Statbox to right displays line count and fold level.
 "    - Coloured distinctly red!
 "        (sounds harsh, but actually works well with solarized-dark!)
 "    - Fillchar is forced to '.' rather than '-'.
@@ -13,7 +13,7 @@
 "    - SpaceBar toggles folds, if any.
 "        (much more convenient than the 'z' commands)
 "    - SPF13-VIM provides quick foldlevel setting map: <Leader>f[0-9]
-"        (useful with stat box lvl)
+"        (useful with statbox lvl)
 "
 "   Note that custom foldtext will only work for vim 7.3+. For earlier
 "   versions of vim, only the colouring and spacebar mapping will take effect.
@@ -46,79 +46,200 @@ if has('folding')
   " }}}
 
   " Fold Highlighting {{{
-  " Red fold text! (Pretty good with solarized-dark)
-  highlight Folded term=none cterm=none ctermfg=darkred ctermbg=none guifg=darkred guibg=none
+  highlight Folded term=none cterm=none ctermfg=darkgrey ctermbg=none guifg=darkgrey guibg=none
   " }}}
 
   " Fold Text {{{
   set foldtext=CustomFoldText()
 
-  function! CustomFoldText()
+  function! CustomFoldText(...)
 
-    " At least vim 7.3. {{{
-    "  - Requirement for strdisplaywidth.
+    " At least vim 7.3 {{{
+    "  - Requirement for strdisplaywidth().
+    "  - strdisplaywidth() seems to work. If multi-byte characters start to give
+    "  trouble, consider checking the more primitive solution in strlen() help.
     if v:version < 703
       return foldtext()
     endif
     " }}}
 
-    " Force dot fillchar. {{{
-    "  - The complicated line is to ensure we replace the fold fillchar only.
+    " Common variables for all foldmethods {{{
+    let lineCount = v:foldend - v:foldstart + 1
+    let displayWidth = winwidth(0) - &foldcolumn
+    if (&number || &relativenumber)
+      let displayWidth -= &numberwidth
+    endif
+    let foldChar = '┄'
+    " }}}
+
+    " Set fold fillchar {{{
+    "  - This complicated line is to ensure we replace the fold fillchar only.
     let &l:fillchars = substitute(&l:fillchars,',\?fold:.','','gi')
-    setlocal fillchars+=fold:.
+    exec 'setlocal fillchars+=fold:' . foldChar
     " }}}
 
-    " Prepare script variables. {{{
-    let foldChar = matchstr(&fillchars, 'fold:\zs.')
-    let currWinWidth = winwidth(0)
+    " Handle diff foldmethod {{{
+    "  - Display a centre-aligned statbox with the number of lines.
+    if &foldmethod == 'diff'
+
+      " Prepare the statbox {{{
+      let statBox = printf('[ %s matching lines ]', lineCount)
+      " }}}
+
+      " Prepare filler lines {{{
+      let filler = repeat(foldChar, (displayWidth - strchars(statBox)) / 2)
+      " }}}
+
+      " Output the combined fold text {{{
+      return filler.statBox
+      " }}}
+
+    endif
     " }}}
 
-    " Prepare fold indent. {{{
-    "  - Indent taken from first line in fold.
-    let indentLevel = indent(v:foldstart)
-    let indent = repeat(' ', indentLevel)
+    " Handle all other foldmethods {{{
+
+      " Prepare fold indent and indicator {{{
+      "  - If indent allows, build the indicator into it.
+      let foldIndicator = '▸ '
+      let indLen = strdisplaywidth(foldIndicator)
+      if indent(v:foldstart) >= indLen
+        let indent = repeat(' ', indent(v:foldstart) - indLen) . foldIndicator
+      else
+        let indent = repeat(' ', indent(v:foldstart))
+      endif
+      " }}}
+
+      " Prepare the statbox {{{
+      "  - Fixed statbox width at 18 characters.
+      "  - Count width by display cells instead of bytes if at least vim 7.4
+      if v:version >= 704
+        let countType = 'S'
+      else
+        let countType = 's'
+      endif
+      let statBox = '[ ' . printf('%14'.countType, lineCount.' lns, lv '.v:foldlevel) . ' ]'
+      " }}}
+
+      " Prepare fold description {{{
+      "  - Remove fold markers and comment markers.
+      "  - Truncate to 1/3 of the current window width.
+
+        " Use function argument as line text if provided {{{
+        let line = a:0 > 0 ? a:1 : getline(v:foldstart)
+        " }}}
+
+        " Remove fold markers {{{
+        let foldmarkers = split(&foldmarker, ',')
+        let line = substitute(line, '\V' . foldmarkers[0] . '\%(\d\+\)\?\s\*', '', '')
+        " }}}
+
+        " Remove surrounding whitespace {{{
+        let line = substitute(line, '^\s*\(.\{-}\)\s*$', '\1', '')
+        " }}}
+
+        " Add an extra space at the end {{{
+        let foldDesc = line.' '
+        " }}}
+
+      " }}}
+
+      " Prepare filler lines {{{
+      "  - midFiller is the fill between the description and statbox.
+      "  - midFiller compensates for column widths generated by foldcolumn, number
+      "  and relativenumber.
+      let endFiller = repeat(foldChar, 1)
+      let midFillerLength = displayWidth - strdisplaywidth(indent.foldDesc.statBox.endFiller)
+      let midFiller = repeat(foldChar, midFillerLength)
+      " }}}
+
+      " Output the combined fold text {{{
+      return indent.foldDesc.midFiller.statBox.endFiller
+      " }}}
+
     " }}}
 
-    " Prepare fold description. {{{
-    "  - Truncated to 1/3 of the current window width.
-    let allFoldMarkers = split(&foldmarker, ',')
-    let frontFoldMarker = allFoldMarkers[0]
-    let lineRaw = substitute(getline(v:foldstart), '^\s*"\?\s*\|\s*"\?\s*'.frontFoldMarker.'\d*\s*', '', 'g')
-    let line = '+ '.lineRaw.' '
-    let foldDesc = strpart(line, 0, currWinWidth / 3)
+  endfunction
+  " }}}
+
+  " Lokaltog's Fold Text for learning more stuff about fold description preparation {{{
+  function! FoldText(...)
+    " This function uses code from doy's vim-foldtext: https://github.com/doy/vim-foldtext
+    " Prepare fold variables {{{
+    " Use function argument as line text if provided
+    let l:line = a:0 > 0 ? a:1 : getline(v:foldstart)
+
+    let l:line_count = v:foldend - v:foldstart + 1
+    let l:indent = repeat(' ', indent(v:foldstart))
+
+    let l:w_win = winwidth(0)
+    let l:w_num = getwinvar(0, '&number') * getwinvar(0, '&numberwidth')
+    let l:w_fold = getwinvar(0, '&foldcolumn')
     " }}}
+    " Handle diff foldmethod {{{
+    if &fdm == 'diff'
+      let l:text = printf('┤ %s matching lines ├', l:line_count)
 
-    " Prepare the stat box. {{{
-    "  - Fixed stat box width at 18 characters.
-    "  - Count width by display cells instead of bytes if at least vim 7.4
-    let lineCount = v:foldend - v:foldstart + 1
-    if v:version >= 704
-      let formatForm = 'S'
-    else
-      let formatForm = 's'
+      " Center-align the foldtext
+      return repeat('┄', (l:w_win - strchars(l:text) - l:w_num - l:w_fold) / 2) . l:text
     endif
-    let statBox = '| ' . printf('%18'.formatForm, lineCount.' lines, lvl '.v:foldlevel) . ' |'
     " }}}
+    " Handle other foldmethods {{{
+    let l:text = l:line
+    " Remove foldmarkers {{{
+    let l:foldmarkers = split(&foldmarker, ',')
+    let l:text = substitute(l:text, '\V' . l:foldmarkers[0] . '\%(\d\+\)\?\s\*', '', '')
+    " }}}
+    " Remove comments {{{
+    let l:comment = split(&commentstring, '%s')
 
-    " Prepare filler lines. {{{
-    "  - endFiller is the fill after the stat box. Fixed at 5 characters.
-    "  - midFiller is the fill between the description and stat box.
-    "  - midFiller compensates for column widths generated by foldcolumn, number
-    "  and relativenumber.
-    "  - strdisplaywidth() seems to work. If multi-byte characters start to give
-    "  trouble, consider checking the more primitive solution in strlen() help.
-    let endFiller = repeat(foldChar, 5)
-    let midFillerLength = winwidth(0) - strdisplaywidth(indent.foldDesc.statBox.endFiller) - &foldcolumn
-    if (&number || &relativenumber)
-      let midFillerLength -= &numberwidth
+    if l:comment[0] != ''
+      let l:comment_begin = l:comment[0]
+      let l:comment_end = ''
+
+      if len(l:comment) > 1
+        let l:comment_end = l:comment[1]
+      endif
+
+      let l:pattern = '\V' . l:comment_begin . '\s\*' . l:comment_end . '\s\*\$'
+
+      if l:text =~ l:pattern
+        let l:text = substitute(l:text, l:pattern, ' ', '')
+      else
+        let l:text = substitute(l:text, '.*\V' . l:comment_begin, ' ', '')
+
+        if l:comment_end != ''
+          let l:text = substitute(l:text, '\V' . l:comment_end, ' ', '')
+        endif
+      endif
     endif
-    let midFiller = repeat(foldChar, midFillerLength)
     " }}}
-
-    " Output the combined fold text. {{{
-    return indent.foldDesc.midFiller.statBox.endFiller
+    " Remove preceding non-word characters {{{
+    let l:text = substitute(l:text, '^\W*', '', '')
+    " }}}
+    " Remove surrounding whitespace {{{
+    let l:text = substitute(l:text, '^\s*\(.\{-}\)\s*$', '\1', '')
+    " }}}
+    " Make unmatched block delimiters prettier {{{
+    let l:text = substitute(l:text, '([^)]*$',   '⟯ ⋯ ⟮', '')
+    let l:text = substitute(l:text, '{[^}]*$',   '⟯ ⋯ ⟮', '')
+    let l:text = substitute(l:text, '\[[^\]]*$', '⟯ ⋯ ⟮', '')
     " }}}
+    " Add arrows when indent level > 2 spaces {{{
+    if indent(v:foldstart) > 2
+      let l:cline = substitute(l:line, '^\s*\(.\{-}\)\s*$', '\1', '')
+      let l:clen = strlen(matchstr(l:cline, '^\W*'))
 
+      let l:indent = repeat(' ', indent(v:foldstart) - 2)
+      let l:text = '▸ ' . l:text
+    endif
+    " }}}
+    " Prepare fold text {{{
+    let l:fnum = printf('┤ %s ⭡ ', printf('%4s', l:line_count))
+    let l:ftext = printf('%s%s ', l:indent, l:text)
+    " }}}
+    return l:ftext . repeat('┄', l:w_win - strchars(l:fnum) - strchars(l:ftext) - l:w_num - l:w_fold) . l:fnum
+    " }}}
   endfunction
   " }}}