UltiSnips does not cope with auto wrapping

Bug #719998 reported by SirVer
6
This bug affects 1 person
Affects Status Importance Assigned to Milestone
UltiSnips
Fix Released
Medium
Unassigned

Bug Description

Reported by Gregor by Mail:

Aside from that I think I am experiencing a bug with ultisnips and
auto-wrapping enabled (i.e. in tex files). I think the options to
reproduce the problem are :set tw=xxx and :set fo=tcq

The problem occurs when I am editing inside a placeholder (like the
\ref{} snippet for tex files) that is being wrapped to the next line.
Then the snippet is corrupted and inserted multiple times.

You can try these steps to reproduce:
1. vim foo.tex -c 'set tw=10' -c 'call feedkeys("itest r\<tab>")'
2. Enter 'foo bar' without the qoutes

I am using ultisnips-1.3 and the latest vim-7.3.121 on linux.

Related branches

Revision history for this message
SirVer (sirver) wrote :

I can easily reproduce this, but i am not sure how to fix this.

The problem is that vim does not inform plugins about when or how it is modifying the buffer. Therefore ultisnips guesses the changes that happen from the movement of the cursor. Autoformatting can change the complete buffer (literally every line) and guessing is bound to fail in this case. I am therefore unsure what we can do to fix this problem and I am very open for suggestions.

Changed in ultisnips:
status: New → Confirmed
importance: Undecided → Medium
Revision history for this message
SirVer (sirver) wrote :

Another comment by Gregor:

I just wanted to give a hint for solving the problem: Before switching
to ultisnips I used snipmate for a long time and I don't know exactly
the details of implementation but the problem doesn't occur there
(AFAIK snipmate does not use any markers inside text either).

Revision history for this message
SirVer (sirver) wrote :

I just took a minute to investigate snipmates implementation of this problem: snipMate always escapes out of the snippet as soon as the cursor leaves the current line. For comparision: UltiSnips result with the sec snippet in tex is completely broken:

\section{lksdjfljsdfl kjsdflkjsdljkf sldkfj slkdjf lksdj flkjsd lkfj
lskdjf} % {{{ lksjd flksjd fl} % {{{
\label{sec:lksdjfljsdfl_kjsdflkjsdljkf_sldkfj_slkdjf_lksdj_flkjsd_lkfj_lskdjf_lksjd_flksjd_fl}

% section lksdjfljsdfl_kjsdflkjsdljkf_sldkfj_slkdjf_lksdj_flkjsd_lkfj_lskdjf_lksjd_flksjd_fl (end) }}}

snipmates result is less broken, but note that the mirror at the end is not correctly updated after the line break:

\section{lksdjf lksjdflksjdf lksdjfl ksjd flksjdf lksjdf lksjdflksjd flksjdf
lksjdflksjd flksjdf lksjd flksdjf lksdjf lksdjf} % (fold)
\label{sec:lksdjf lksjdflksjdf lksdjfl ksjd flksjdf lksjdf lksjdflksjd flksjdf lks}

% section lksdjf lksjdflksjdf lksdjfl ksjd flksjdf lksjdf lksjdflksjd flksjdf lks (end)

So snipmate is no real help here; it avoids the problem by not supporting multiline tabstops.

Revision history for this message
SirVer (sirver) wrote :

I went full circle and also took a look at xptemplate: it seems to deactivate automated line wrappings, which is a non elegant but working solution. We could adapt this, but it is likely not what the user expects from his snippet. Any opinions?

@Ryan: While I was on it, I also realized that Xptemplate supports nested snippets AND repeated snippets. I didn't see where this was useful for them though.

Revision history for this message
Gregor Uhlenheuer (kongo2002) wrote :

If you asked me, I would rather have auto-wrapping disabled when inside a snippet than having the snippet completely messed up.

To my mind it's much more time-consuming to correct a messed up snippet than to manually wrap the snippet into a new line.

Maybe you could make this behaviour configurable to not offend users expecting it the other way around (sth. like g:UltiSnipsDisableAutoWrap).

Cheers,
Gregor

Revision history for this message
SirVer (sirver) wrote :

I went the extra mile and implemented a proper fix which handles all cases that only contain one line break (It will not work when text is pasted using <C-R>, but that is another story entirely). Otherwise, UltiSnips behaves now well and even handles mirrors in the snippet fine. The fix is in r248.

Gregor, if you can update your ultisnips version to the current trunk and test this out, it would be much appreciated.

Changed in ultisnips:
status: Confirmed → Fix Committed
Revision history for this message
Gregor Uhlenheuer (kongo2002) wrote :

It looks like you definitely improved the situation but didn't solve it completely. It works if the static part of the snippet (i.e. "\ref{" for the \ref{} tex snippet) stays on it's initial line.
On the other hand it does not work right if this part of the snippet is wrapped to a new line as well.

Steps to reproduce:
- vim test.tex -c "set tw=10"

This works:
r<tab>foo bar

This does not work:
foo r<tab>bar

Revision history for this message
SirVer (sirver) wrote :

Thanks for testing. I can reproduce the problem. I will look into it some more, but I can't say when I will find the time.

Changed in ultisnips:
status: Fix Committed → Confirmed
Revision history for this message
SirVer (sirver) wrote :

I investigated this issue for a bit now. The problem is that we cannot detect when snippets or the start of a tabstop inside a snippet are moved by vims autolayouting. My earlier fixes only work when line breaks are inside the text of a tabstop; not if the line break happen in other text of the snippet.

I am a bit torn of what is the correct way of going forward.

Solution 1) Disable formatoptions while we expand and navigate inside a snippet. This is desirable for example for the box snippet where I do not want the lines to be broken, no matter what tw is set to. I think most snippets do not want to be broken in general, but obviously some do. This is also the behaviour of snipmate and xptemplate.

Solution 2) Keep the current implementation as is. This is desirable for snippets that contain tabstops that should take a paragraph of text instead of only one line. This will stay buggy if the start of the snippet or some static text inside the snippet is wrapped; I have no idea as of yet how to avoid this. For most cases I can think of, the current implementation will work fine though.

Solution 3) Leave the decision to the user by adding a snippet option that toggles solution 1 or 2. Obviously we would have to decide of a default if the option is not set (I'd prefer 1). And it might be a subtle issue to explain to a user because it is not immediately obvious why one way or the other is better.

Solution 4) Disable formatoptions but implement our own wrapping that emulates the formatoptions of vim. This is a lot of work and not trivial to implement. It will be also hard to match vim's behaviour in all corner cases. Frankly, I do not want to invest time into this.

I am leaning towards solution 3) or solution 1). I'd like to hear more opinions though.

Revision history for this message
Gregor Uhlenheuer (kongo2002) wrote :

Hm, I would vote for solution no. 3 and set the default to disabling formatoptions. This would pretty much catch all use cases I think.

For me it's simply much easier to reformat the line if formatoptions are disabled than to clean up a broken snippet.

Revision history for this message
SirVer (sirver) wrote :

I agree with your second paragraph.. I will not be available for 3 weeks now, so do not keep your breath till then.

Revision history for this message
Ryan Wooden (ryan.wooden) wrote :

I agree. I think this is a good solution for now at least.

SirVer (sirver)
Changed in ultisnips:
milestone: none → 1.4
Revision history for this message
SirVer (sirver) wrote :

I implemented this in r282.

Changed in ultisnips:
status: Confirmed → Fix Committed
Revision history for this message
SirVer (sirver) wrote :

Release in 1.4.

Changed in ultisnips:
status: Fix Committed → Fix Released
To post a comment you must log in.
This report contains Public information  
Everyone can see this information.

Other bug subscribers

Remote bug watches

Bug watches keep track of this bug in other bug trackers.