Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Porperly indented folds #92

Open
DerWeh opened this issue Nov 8, 2017 · 14 comments
Open

Porperly indented folds #92

DerWeh opened this issue Nov 8, 2017 · 14 comments
Labels

Comments

@DerWeh
Copy link

DerWeh commented Nov 8, 2017

Python focuses on indents for readability, thus I think the fold-text should respect the indent for readability. The indent based on the fold-level as we get here is in my opinion intrusive.

I use a custom fold text putting the fold information at the end of the line and indenting the line as it would be without a fold. I do it with the following fold text

function! CustomFoldText()
  "get first non-blank line
  let l:fs = v:foldstart
  while getline(l:fs) =~ '^\s*$' | let l:fs = nextnonblank(l:fs + 1)
  endwhile
  if l:fs > v:foldend
    let l:line = getline(v:foldstart)
  else
    let l:line = substitute(getline(l:fs), '\t', repeat(' ', &tabstop), 'g')
  endif

  "strip foldmarkers
  let l:markexpr = escape(substitute(&foldmarker, ',', '|', 'g'),'{')
  " TODO: check comment sign for filetype, add first line if comment
  let l:whitespace = '(\w\s*)?["|#]\s*|\s*$'
  let l:strip_line = substitute(l:line, '\v'.l:markexpr.'|'.l:whitespace, '', 'g')
  let l:strip_line = substitute(l:strip_line, '\v'.l:whitespace, '', 'g')
  let l:strip_line = substitute(l:strip_line, '\v^(\s*)', '\1<', '').'>'
  let l:w = winwidth(0) - &foldcolumn - (&number ? 8 : 0)
  let l:foldSize = 1 + v:foldend - v:foldstart
  let l:foldSizeStr = ' ' . l:foldSize . ' lines '
  let l:foldLevelStr = repeat('+--', v:foldlevel)
  let l:lineCount = line('$')
  let l:foldPercentage = printf('[%.1f', (l:foldSize*1.0)/l:lineCount*100) . '%] '
  let l:expansionString = repeat('.', l:w - strwidth(l:foldSizeStr.strip_line.foldLevelStr.foldPercentage))
  return l:strip_line . l:expansionString . l:foldSizeStr . l:foldPercentage . l:foldLevelStr
endfunction
set foldtext=CustomFoldText()

Sadly I have no idea how to get the same thing with your awesome docstring preview. (I am still amazed that I somehow manage to get the proper folds, but I have no idea what I have done).

@nfnty nfnty added the question label Nov 22, 2017
@nfnty
Copy link
Collaborator

nfnty commented Nov 22, 2017

Not quite sure what you're trying to accomplish. This is how we do it: https://github.com/tmhedberg/SimpylFold/blob/master/autoload/SimpylFold.vim#L364-L389

@DerWeh
Copy link
Author

DerWeh commented Nov 30, 2017

A picture tells more than words:
foldcomparision

If you look at the left site, you see that the text in the folds is properly indented. Everything starts where it would start if it was not folded. On the right sight things start somewhere, depending on the number of folded lines. I find that really hard to read. Blocks of code should be recognizable by their indent in Python.

I would like to have your nice fold-text, showing the doc-strings, but properly indented like my patchwork fold-text. Sadly I don't know enough Vimscript to do it myself.

@ghost
Copy link

ghost commented Dec 1, 2017

@DerWeh : I used your fold function but I'd have to say that I like SimpyIFold more. Maybe because of colorscheme or something else, have to look on the right to get number of lines cause distracted.

@DerWeh
Copy link
Author

DerWeh commented Dec 1, 2017

@tuyenpm9 I agree that it is a matter of preference (and my fold-text surely isn't optimal, neither is my colorscheme). I myself am hardly ever interested in the number of folded lines. Either I am interested in the folded code and look into it, or a fold it away to get a better overview.

If I look to the class StyleGuide for example, I can immediately tell on the left side, which methods belong to it and when the new function starts. On the right side however it is not clear at all (at least to me). Which methods belong to the class? Is ignore_code defined in the method exclude or is it directly defined in StyleGuide? Of course, you can tell from the semantics that the first variant makes no sense. But It could also be an error, and you have to read to figure it out instead of looking.

I think the option to choose a fold text putting the numbers to the right and thus keeping the indent should be given. Python is indent-based and that should be respected.

@DerWeh
Copy link
Author

DerWeh commented Dec 1, 2017

There a probably some issues with my function. It was mostly try and error until I found the result to be acceptable.

@tuyenpm9 I am not exactly sure what you mean with incorrect, it is what I intended. The number of +-- represents the fold level:

let l:foldLevelStr = repeat('+--', v:foldlevel)

But I realized the code doesn't respect line numbers and the sign column, here issues might arise.

Likely there are more readable possibilities to do it, I simply didn't care.

The issue with <>..... looks like this got stripped that shouldn't have been stripped.

let l:markexpr = escape(substitute(&foldmarker, ',', '|', 'g'),'{')
" TODO: check comment sign for filetype, add first line if comment
let l:whitespace = '(\w\s*)?["|#]\s*|\s*$'
let l:strip_line = substitute(l:line, '\v'.l:markexpr.'|'.l:whitespace, '', 'g')
let l:strip_line = substitute(l:strip_line, '\v'.l:whitespace, '', 'g')
let l:strip_line = substitute(l:strip_line, '\v^(\s*)', '\1<', '').'>'

Does the stripping.

  • let l:markexpr = escape(substitute(&foldmarker, ',', '|', 'g'),'{')
    removes fold marker. As we don't use them it likely can be dropped together with let l:strip_line = substitute(l:line, '\v'.l:markexpr.'|'.l:whitespace, '', 'g')
  • let l:strip_line = substitute(l:strip_line, '\v^(\s*)', '\1<', '').'>' adds the < > around the text, it can also be dropped
  • let l:whitespace = '(\w\s*)?["|#]\s*|\s*$' likely is the culprit why you don't see anything. If you have an `empty' line just containing a comment marker # or the beginning of a multi-line string """ it indeed doesn't show the right thing.

@ghost
Copy link

ghost commented Dec 2, 2017

I'm considering use your fold method but without 12 lines [4.3%] +-- portion. If we can write a function that piece of information occurs when cursor on folded line at command bar is good :)
A big plus that your function strip """ in docstring.

@DerWeh
Copy link
Author

DerWeh commented Mar 28, 2018

I worked a bit on the fold text and came up with something rather satisfying:

scriptencoding utf-8

let s:docstring_re = '^\s*[bBfFrRuU]\{0,2}\\\@<!\(''''''\|"""\|[''"]\)'
let s:docstring_idedentifier_re = '[bBfFrRuU]\{0,2}\\\@<!\(''''''\|"""\|[''"]\)'


function! foldtext#python() abort
  let l:docstring = SimpylFold#FoldText()

  let l:lnum = v:foldstart

  " get first non-blank line
  while getline(l:lnum) =~# '^\s*$' | let l:lnum = nextnonblank(l:lnum + 1)
  endwhile
  if l:lnum > v:foldend
    let l:line = getline(v:foldstart)
  else
    let l:line = substitute(getline(l:lnum), '\t', repeat(' ', &tabstop), 'g')
  endif

  " if line is docstring than strip, is clear from that folding that it is docstring
  let l:is_docstring = !empty(matchlist(l:line, s:docstring_re))
  if l:is_docstring
    let l:line = substitute(l:line, s:docstring_idedentifier_re, '', '')
    " let l:docstring = substitute(l:docstring, '^\s', '', '')
    let l:docstring = l:docstring[1:]  " split leading whitespace
  endif

  let l:width = winwidth(0) - &foldcolumn - (&number ? 8 : 0)  " available space
  let l:foldSize = 1 + v:foldend - v:foldstart
  let l:foldSizeStr = ' ' . l:foldSize . ''
  let l:foldLevelStr = repeat('+--', v:foldlevel)
  let l:lineCount = line('$')
  let l:foldPercentage = printf('[%.1f', (l:foldSize*1.0)/l:lineCount*100) . '%] '
  " space remaining space to be filled
  let l:space = l:width - strwidth(l:foldSizeStr.l:line.l:foldLevelStr.l:foldPercentage.l:docstring)
  if l:space < 0
    " trim part of docstring that doesn't fit
    return l:line . l:docstring[:l:space-1] . '' . l:foldSizeStr . l:foldPercentage . l:foldLevelStr
  else
    " fill empty space
    let l:expansionString = repeat('', l:space)
    return l:line . l:docstring . l:expansionString . l:foldSizeStr . l:foldPercentage . l:foldLevelStr
  endif
endfunction

Again you can strip the parts you don't like, especially the Unicode chars. They are purely cosmetic.


Here is what it looks like:
folding

The indent is preserved, to long doc-strings will be cut to fit, and doc-string only lines start with a <. However I don't really know if it is a good idea to keep the <.

@ghost
Copy link

ghost commented Aug 1, 2018

@DerWeh : Look perfect to me :), will try.

EDIT: just tried, thanks a million :), extractly what I need for years. happy new year 2019 early. 💃

@ghost
Copy link

ghost commented Aug 1, 2018

if we set SimpylFold options (let g:SimpylFold_fold_docstring = 1 for e.g) , SimpylFold is used instead of our custome fold (custom fold in vimrc).

I'm looking for only fold python docstring by default when open a python file.

@ghost
Copy link

ghost commented Aug 1, 2018

Is there any way to only fold python docstring on python when open? but using this customfold.

EDIT: can achieve this by using modeline with foldenable and foldmethod=syntax

or may be a mapping will be better solution.

@ghost
Copy link

ghost commented Aug 1, 2018

I'm also curious about the ability of setting guifg and guibg color of spaces for e.g let l:expansionString = repeat(' ', l:space) so they have the same color as colorscheme's background - Here I want to only highlight words and all spaces has same color as colorscheme's background, so we can feel less distracted.

@ghost
Copy link

ghost commented Aug 1, 2018

I can't find L/N special char in your custom fold, how to have them?

@DerWeh
Copy link
Author

DerWeh commented Aug 2, 2018

@tuyenpm9 The L/N charter is a Unicode character: . I think FireFox's default font can't display it. But it works if you just copy the cryptic symbol into Vim, as long as you have a font able to display it. (I personally use nerdfonts, so I have all the symbols).

I use setlocal foldlevel=1 for python with SimpyFold, this does a decent job, as top-level functions are not folded.

I agree, setting the colors would increase readability. However, I think you have to modify how your colorsheme highlights folds to achieve this. I have no idea how to do this, but I am interested if you find a solution.
The only thing I can come up with would be setting :highlight Folded guibg=NONE ctermbg=NONE and then defining a new syntax element with a regular expression, maybe looking for your special  character, which should only appear in folds.

@ghost
Copy link

ghost commented Aug 2, 2018

@DerWeh : I do trick in python.vim and use foldmethod=syntax for python file.

It looks weird to me when set guibg to colorscheme's background color, I raised a question about this here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants