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

added support for multiple diffs (up to 8) #27

Merged
merged 14 commits into from
Mar 28, 2017

Conversation

hcgraf
Copy link
Contributor

@hcgraf hcgraf commented Feb 8, 2017

OK, that was quick :)
I just couldn't resist to hack with this a bit, so here is my patch for my improvement proposal #26

But it was done in a few hours, with only very basic vimscript knowledge, and without in-depth testing.
So maybe this PR is not really a PR but rather a TR (Try-Request).

I tried to be minimally-invasive, to change as little as possible.
Notably, the original UI was kept, so :Linediff will automatically start the diffing after 2 blocks have been selected.

Here is the rest of my commit message, it explains how to use the modification, and some other details:

  • use LinediffAdd for each Block, LinediffLast for the last one
    OR use LinediffShow AFTER the last block
  • max number of diff blocks = 8 is hardcoded, because it's the max number
    of diff buffers in the newest vim, version 8
  • older versions support only 4 diff buffers, no check is done for that
    they just throw an error if you try to diff more than 4 blocks
  • LinediffMerge should work, not tested yet at all!!
  • in general not very well tested yet, use with caution!

I would be very happy to receive feedback from anybody!

@AndrewRadev
Copy link
Owner

At a first glance, it looks really good! I still have to test it out carefully and maybe think how the documentation will change, but we'll see. If you can keep using this branch and make modifications as you find issues, this would be a good exercise for it. In the meantime, I'll see what I can do about adding some automated tests in master.

@hcgraf
Copy link
Contributor Author

hcgraf commented Feb 15, 2017

Thank you for your feedback. I checked LinediffMerge & LinediffPick now, and made them work in the last commit.
I'm also planning to add support for git's diff3 conflictmarker-style, but I won't have time for that in the next couple of days.

In general, I'm using my branch without issues so far, but I have to admit that I'm not using it too extensively at the moment. A few automated testcases would definitely be nice!

@AndrewRadev
Copy link
Owner

AndrewRadev commented Feb 17, 2017

I've added some automated tests. If you could merge master in this branch, Travis should let us know if your branch continues to work for the existing use cases (provided I've written enough tests to cover them, of course, but even if not, at least it'll cover some of the basics).

- use LinediffAdd for each Block, LinediffLast for the last one
  OR use LinediffShow AFTER the last block
- max number of diff blocks = 8 is hardcoded, because it's the max number
  of diff buffers in the  newest vim, version 8
- older versions support only 4 diff buffers, no check is done for that
  they just throw an error if you try to diff more than 4 blocks
- LinediffMerge *should* work, not tested yet at all!!
- in general not very well tested yet, use with caution!
@hcgraf
Copy link
Contributor Author

hcgraf commented Feb 23, 2017

OK it fails :)
However, the "no registered server" error doesn't really seem related to my changes.
Can you comment on how to debug this?

@AndrewRadev
Copy link
Owner

Hm, that's very strange. Locally, I checked out this branch, and the build seems to run, not sure what's happening on the build server. Might be an issue with the Vim version. I'll try to debug it tomorrow, or over the weekend. I compile Vim from source, so I can check out that particular version and see what's happening.

@hcgraf
Copy link
Contributor Author

hcgraf commented Feb 24, 2017

This is now a nice new feature, LinediffMerge produces 3 diff-splits (only) when the common ancestor marker ||||||| is present in the git merge conflict.
Set "merge.conflictStyle" configuration variable to "diff3" to test it.

Editing and LinediffPick will of course work in all of the three splits.

The tests are still failing, though :(

@AndrewRadev
Copy link
Owner

I think I figured it out, sort of. This is a problem I used to have a long time ago, when I was trying to implement auto-LinediffReset upon closing any one of the special buffers. Turns out, Vim isn't happy about trying to close an already-closed buffer. That's why I had this code:

" The closing logic is a bit roundabout, since changing a buffer in a
" BufUnload autocommand doesn't seem to work.
"
" The process is: if a window is entered after the other differ was destroyed,
" destroy this one as well and close the window.

  " .. .snip ...

  call s:differ_one.CreateDiffBuffer(g:linediff_first_buffer_command)
  autocmd BufUnload <buffer> silent call s:differ_one.Reset()
  autocmd WinEnter <buffer> if s:differ_two.IsBlank() | silent call s:differ_one.CloseAndReset(0) | endif

  call s:differ_two.CreateDiffBuffer(g:linediff_second_buffer_command)
  autocmd BufUnload <buffer> silent call s:differ_two.Reset()
  autocmd WinEnter <buffer> if s:differ_one.IsBlank() | silent call s:differ_two.CloseAndReset(0) | endif

So, if one buffer is unloaded, its data structure is reset, but the other ones get reset when the cursor ends up in them, so it happens after the autocommand is done. To reproduce, it should be enough to :tabclose on any one of the proxy buffers, it gets pretty confused and says it can't close the last window. There's also a situation where it segfaults, but I can't seem to figure out how to reproduce it (this is how the tests fail)

This doesn't seem to be a problem in a recent enough Vim version, but Travis uses some sensible Debian, so I'd rather not break the plugin for it -- it's likely that a lot of people use that version.

One solution I can think of is to extract some sort of diff controller, or something -- a piece of global state that can be consulted. Something like: when a buffer is closed, that controller gets a signal to propagate the effect (a flag is set, basically). Every diff buffer checks on WinEnter whether it should be closing itself. So, basically, the same logic, just generalized for a list of buffers rather than specifically for two. (Note that b:differ no longer seems to exist in the BufUnload event, so that might also be a problem that needs a workaround).

I can't really say how this would work without trying it out. I've been poking around trying to figure out a workaround for a while, but nothing simple seems to work. Reimplementing the old hack with some controller object should hopefully fix the issue, but there might be more complications. Since I can compile 7.4.52 locally, I'll see if I can implement it and push it to your branch.

@hcgraf
Copy link
Contributor Author

hcgraf commented Feb 26, 2017

Ok, thanks for your effort!
It's really strange, because it is checked if the buffer exists, before it is deleted.
I tried to avoid autocmd-loops by removing them here.

After reading a bit documentation, I saw that BufUnload is called before unloading the buffer, so the problem might rather be that the same buffer is destroyed again in that autocmd. That might be easily catchable here.
I'm not able to try that right now because of time and not having a vim version at hand which let's me reproduce the issue…

@AndrewRadev
Copy link
Owner

Yeah, I really think it's a bug in Vim, and I think it's been fixed already (this doesn't happen in my current Vim version, or in yours). I think you've done a good job of setting up the logic, but, in the end, it's just a matter of squeezing in a hack to work around a system bug :).

I'll try to do it in the following days, but no guarantees when I'll manage, I'm afraid.

If we :tabnew, and then :tabonly, all buffers get unloaded without a
WinEnter in any of them. That's why this case exists, if
`StartDestroying` has been called on a blank set of differs, we can just
stop it.

This should really be carefully documented somewhere.
Was used for debugging purposes.
@AndrewRadev
Copy link
Owner

Okay, so, I've pushed a few commits to your branch which should fix the tests. I've basically reimplemented the old WinEnter logic, and boy, was it annoying. It's also super hacky and it doesn't work for all scenarios, but I've got another workaround for it.

For now, what I still need to do is:

  • Write some documentation on the closing hacks, because I really don't want to figure the scenarios out again next time.
  • Write some tests on the new commands, and the new merge behaviour with the threeway merge.

If you can test this on your own Vim version and let me know if it doesn't segfault or something, that would be great. I'll switch to Vim 8 soon as well, but I'd like to write the tests first, so I know the new ones continue to work.

@hcgraf
Copy link
Contributor Author

hcgraf commented Mar 15, 2017

cool, looks like you thought about a lot of corner cases!
I will most probably get some opportunities tomorrow to test it in a real workflow. I'll report back!

Btw, I was using/testing my own version quite a bit over the last weeks, and it worked fine but I got some error messages when closing modified diff-tabs.
For me, that's OK as long as I know what's going on behind the scenes (I just ignored the errors), but it's of course not ready for a release. I've never seen a segfault though.

@hcgraf
Copy link
Contributor Author

hcgraf commented Mar 17, 2017

I didn't have too many real-world testcases these two days, but some. No problems so far!

I will definitely continue to use this branch, so I'll report any possible problems here.

Thank you very much for your effort!

Copy link
Owner

@AndrewRadev AndrewRadev left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that the PR is ready to merge now. I've added more test cases for the new features, and documentation for them. I've also tried to explain the closing logic in the "internals" section of the docs. I may have squeezed in some code that isn't directly connected to the PR while I was at it, but it was easier than trying to do a separate thing.

@hcgraf Could you go over the PR and see if you approve of its contents right now? Is there anything in the code you find unclear or confusing (I'm guessing the closing logic would be pretty weird, for instance, but I've tried to explain it in the "internals" section)? Is the naming of commands and settings to your liking?

I'm okay with waiting for a while to merge it, mind you, so feel free to keep using the branch for experimentation and check if there are edge cases that we've missed. Of course, we could also merge it sooner rather than later and fix issues as they pop up, I'm fine either way.

@@ -1,23 +1,42 @@
let s:differ_one = linediff#differ#New('linediff_one', 1)
let s:differ_two = linediff#differ#New('linediff_two', 2)
let s:differ = []
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would prefer we call this variable s:differs or s:differ_list. The singular name "differ" doesn't indicate it's a collection of items.

endfunction

function! linediff#LinediffAdd(from, to, options)
for dfr in s:differ
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd prefer we use the full word "differ", I'm not a big fan of skipping vowels :). With the list variable called differs or differ_list, there would be no ambiguity.

@@ -12,8 +12,10 @@ function! linediff#differ#New(sign_name, sign_number)
\ 'sign_number': a:sign_number,
\ 'sign_text': a:sign_number.'-',
\ 'is_blank': 1,
\ 'other_differ': {},
\ 'other_differs': {},
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this should be a list.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Very well. I did all this basically without vimscript knowledge, only general programming knowledge. So it was mostly a mix'n'match approach ;)

@hcgraf
Copy link
Contributor Author

hcgraf commented Mar 28, 2017

Wow, thanks for the continued effort!
I glanced over most of your changes when you pushed them, and everything seems fine! But I didn't have enough time to really analyze it & do some tests.

I'm currently using it at work on a regular basis, but usually in a similar flow - so it's not likely to cover strange corner cases. Anyway, no issues so far! (Btw this is GVim in Windows and regular Vim in Linux - so at least some cases covered).

I think I understand the logic of the closing-logic, the documentation seems as clear as it can get for this one. Although I recently had one use-case where I had wished for another behavior:

  • I was diffing three regions
  • I wanted to close one of them and execute :diffupdate to only compare two of them
  • That was of course not possible, as closing one closes all
  • But that's a different story (maybe a next feature-request), and shouldn't be related to this PR

As for merging: I think it's highly unlikely that this PR will destroy anything for the regular user (used to diffing two areas and continuing to use it like this), so it might be helpful to get some more field-tests for the N-way diffing…
So I would probably vote for merging.

@AndrewRadev
Copy link
Owner

Yes, I can agree that closing one buffer to leave two makes sense. Maybe it's time to reconsider the logic there. Originally, the idea was that there's no point in leaving just one diff buffer. Now that it's possible to add a bunch of them, it doesn't hold up.

If you happen to have some time, I would love to get a PR for this logic, not sure how hard it would be. Something like it only resets once there's only one differ left. If not, an issue with a feature request to remind me would be fine, and I'd get to working on it in my own time.

Either way, time to merge this and see what's next :). Thank you very much for your work!

@AndrewRadev AndrewRadev merged commit 94c1b1a into AndrewRadev:master Mar 28, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants