Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/master'
Browse files Browse the repository at this point in the history
  • Loading branch information
lilydjwg committed Sep 27, 2012
2 parents dfbe5c9 + 393f68b commit a9efa3c
Show file tree
Hide file tree
Showing 5 changed files with 354 additions and 0 deletions.
169 changes: 169 additions & 0 deletions autoload/linediff/differ.vim
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
" Constructs a Differ object that is still unbound. To initialize the object
" with data, `Init(from, to)` needs to be invoked on that object.
function! linediff#differ#New(sign_name, sign_number)
let differ = {
\ 'original_buffer': -1,
\ 'diff_buffer': -1,
\ 'filetype': '',
\ 'from': -1,
\ 'to': -1,
\ 'sign_name': a:sign_name,
\ 'sign_number': a:sign_number,
\ 'sign_text': a:sign_number.'-',
\ 'is_blank': 1,
\ 'other_differ': {},
\
\ 'Init': function('linediff#differ#Init'),
\ 'IsBlank': function('linediff#differ#IsBlank'),
\ 'Reset': function('linediff#differ#Reset'),
\ 'Lines': function('linediff#differ#Lines'),
\ 'CreateDiffBuffer': function('linediff#differ#CreateDiffBuffer'),
\ 'SetupDiffBuffer': function('linediff#differ#SetupDiffBuffer'),
\ 'CloseDiffBuffer': function('linediff#differ#CloseDiffBuffer'),
\ 'UpdateOriginalBuffer': function('linediff#differ#UpdateOriginalBuffer'),
\ 'PossiblyUpdateOtherDiffer': function('linediff#differ#PossiblyUpdateOtherDiffer'),
\ 'SetupSigns': function('linediff#differ#SetupSigns')
\ }

exe "sign define ".differ.sign_name." text=".differ.sign_text." texthl=Search"

return differ
endfunction

" Sets up the Differ with data from the argument list and from the current
" file.
function! linediff#differ#Init(from, to) dict
let self.original_buffer = bufnr('%')
let self.filetype = &filetype
let self.from = a:from
let self.to = a:to

call self.SetupSigns()

let self.is_blank = 0
endfunction

" Returns true if the differ is blank, which means not initialized with data.
function! linediff#differ#IsBlank() dict
return self.is_blank
endfunction

" Resets the differ to the blank state. Invoke `Init(from, to)` on it later to
" make it usable again.
function! linediff#differ#Reset() dict
call self.CloseDiffBuffer()

let self.original_buffer = -1
let self.diff_buffer = -1
let self.filetype = ''
let self.from = -1
let self.to = -1
let self.other_differ = {}

exe "sign unplace ".self.sign_number."1"
exe "sign unplace ".self.sign_number."2"

let self.is_blank = 1
endfunction

" Extracts the relevant lines from the original buffer and returns them as a
" list.
function! linediff#differ#Lines() dict
return getbufline(self.original_buffer, self.from, self.to)
endfunction

" Creates the buffer used for the diffing and connects it to this differ
" object.
function! linediff#differ#CreateDiffBuffer(edit_command) dict
let lines = self.Lines()
let temp_file = tempname()

exe a:edit_command . " " . temp_file
call append(0, lines)
normal! Gdd
set nomodified

let self.diff_buffer = bufnr('%')
call self.SetupDiffBuffer()

diffthis
endfunction

" Sets up the temporary buffer's filetype and statusline.
"
" Attempts to leave the current statusline as it is, and simply add the
" relevant information in the place of the current filename. If that fails,
" replaces the whole statusline.
function! linediff#differ#SetupDiffBuffer() dict
let b:differ = self

let statusline = printf('[%s:%%{b:differ.from}-%%{b:differ.to}]', bufname(self.original_buffer))
if &statusline =~ '%[fF]'
let statusline = substitute(&statusline, '%[fF]', statusline, '')
endif
exe "setlocal statusline=" . escape(statusline, ' |')
exe "set filetype=" . self.filetype
setlocal bufhidden=hide

autocmd BufWrite <buffer> silent call b:differ.UpdateOriginalBuffer()
endfunction

function! linediff#differ#CloseDiffBuffer() dict
if bufexists(self.diff_buffer)
exe "bdelete ".self.diff_buffer
endif
endfunction

function! linediff#differ#SetupSigns() dict
exe "sign unplace ".self.sign_number."1"
exe "sign unplace ".self.sign_number."2"

exe printf("sign place %d1 name=%s line=%d buffer=%d", self.sign_number, self.sign_name, self.from, self.original_buffer)
exe printf("sign place %d2 name=%s line=%d buffer=%d", self.sign_number, self.sign_name, self.to, self.original_buffer)
endfunction

" Updates the original buffer after saving the temporary one. It might also
" update the other differ's data, provided a few conditions are met. See
" linediff#differ#PossiblyUpdateOtherDiffer() for details.
function! linediff#differ#UpdateOriginalBuffer() dict
let new_lines = getbufline('%', 0, '$')

" Switch to the original buffer, delete the relevant lines, add the new
" ones, switch back to the diff buffer.
call linediff#util#SwitchBuffer(self.original_buffer)
let saved_cursor = getpos('.')
call cursor(self.from, 1)
exe "normal! ".(self.to - self.from + 1)."dd"
call append(self.from - 1, new_lines)
call setpos('.', saved_cursor)
call linediff#util#SwitchBuffer(self.diff_buffer)

" Keep the difference in lines to know how to update the other differ if
" necessary.
let line_count = self.to - self.from + 1
let new_line_count = len(new_lines)

let self.to = self.from + len(new_lines) - 1
call self.SetupDiffBuffer()
call self.SetupSigns()

call self.PossiblyUpdateOtherDiffer(new_line_count - line_count)
endfunction

" If the other differ originates from the same buffer and it's located below
" this one, we need to update its starting and ending lines, since any change
" would result in a line shift.
"
" a:delta is the change in the number of lines.
function! linediff#differ#PossiblyUpdateOtherDiffer(delta) dict
let other = self.other_differ

if self.original_buffer == other.original_buffer
\ && self.to <= other.from
\ && a:delta != 0
let other.from = other.from + a:delta
let other.to = other.to + a:delta

call other.SetupSigns()
endif
endfunction
4 changes: 4 additions & 0 deletions autoload/linediff/util.vim
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
" Helper method to change to a certain buffer.
function! linediff#util#SwitchBuffer(bufno)
exe "buffer ".a:bufno
endfunction
124 changes: 124 additions & 0 deletions doc/linediff.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
==============================================================================
CONTENTS *linediff* *linediff-contents*

Installation...........................: |linediff-installation|
Usage..................................: |linediff-usage|
Commands...............................: |linediff-commands|
Internals..............................: |linediff-internals|
Issues.................................: |linediff-issues|


==============================================================================
INSTALLATION *linediff-installation*

There are several ways to install the plugin. The recommended one is by using
Tim Pope's pathogen (http://www.vim.org/scripts/script.php?script_id=2332). In
that case, you can clone the plugin's git repository like so:
>
git clone git://github.com/AndrewRadev/linediff.vim.git ~/.vim/bundle/linediff
<
If your vim configuration is under git version control, you could also set up
the repository as a submodule, which would allow you to update more easily.
The command is (provided you're in ~/.vim):
>
git submodule add git://github.com/AndrewRadev/linediff.vim.git bundle/linediff
<

Another way is to simply copy all the essential directories inside the ~.vim/
directory: plugin, autoload, doc.

==============================================================================
USAGE *linediff-usage*

The plugin provides a simple command, |:Linediff|, which is used to diff two
separate blocks of text.

A simple example:

def one
two
end

def two
three
end

If we mark the first three lines, starting from "def one", in visual mode, and
execute the |:Linediff| command, the signs "1-" will be placed at the start
and at the end of the visual mode's range. Doing the same thing on the bottom
half of the code, starting from "def two", will result in the signs "2-"
placed there. After that, a new tab will be opened with the two blocks of code
in vertical splits, diffed against each other.

The two buffers are temporary, but when any one of them is saved, its original
buffer is updated. Note that this doesn't save the original buffer, just
performs the change. Saving is something you should do later.

Executing the command |:LinediffReset| will delete the temporary buffers and
remove the signs.

Executing a new |:Linediff| will do the same as |:LinediffReset|, but will
also initiate a new diff process.

The statuslines of the two temporary buffers will be changed to contain:
- The original buffer
- The starting line of the selected segment
- The ending line of the selected segment

If you're using a custom statusline and it contains "%f" (the current file's
name), that token will simply be substituted by the above data. Otherwise, the
entire statusline will be set to a custom one.

==============================================================================
COMMANDS *linediff-commands*

*:Linediff*
:Linediff The main interface of the plugin. Needs to be executed on a
range of lines, which will be marked with a sign. On the
selection of the second such range, the command will open a
tab with the two ranges in vertically split windows and
perform a diff on them. Saving one of the two buffers will
automatically update the original buffer the text was taken
from.

When executed for a third time, a new line diff is
initiated, and the current process is reset, much like the
effect of |LinediffReset| would be.


*:LinediffReset*
:LinediffReset Removes the signs denoting the diffed regions and deletes
the temporary buffers, used for the diff. The original
buffers are untouched by this, which means that any updates
to them, performed by the diff process will remain.

==============================================================================
INTERNALS *linediff-internals*

When a block of text is diffed with the plugin, a "Differ" object is
initialized with its relevant data. The differ contains information about the
buffer number, filetype, start and end lines of the text, and a few other
things. Almost all functions the plugin uses are scoped to this object in
order to keep the interface simple. They're located under
"autoload/linediff/differ.vim" and should be fairly understandable.

Functions that are general-purpose utilities are placed in
"autoload/linediff/util.vim".

The two differ objects that are required for the two diff buffers are linked
to each other out of necessity. If they originate from a single buffer,
updating one would move the lines of the other, so that one would have to be
updated as well. Apart from that, they have no interaction.

==============================================================================
ISSUES *linediff-issues*

You shouldn't linediff two pieces of text that overlap. Not that anything
horribly bad will happen, it just won't work as you'd hope to. I don't feel
like it's a very important use case, but if someone requests sensible
behaviour in that case, I should be able to get it working.

To report any issues or offer suggestions, use the bugtracker of the github
project at http://github.com/AndrewRadev/linediff.vim/issues

vim:tw=78:sw=4:ft=help:norl:
9 changes: 9 additions & 0 deletions doc/tags
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,8 @@
:IndentGuidesDisable indent_guides.txt /*:IndentGuidesDisable*
:IndentGuidesEnable indent_guides.txt /*:IndentGuidesEnable*
:IndentGuidesToggle indent_guides.txt /*:IndentGuidesToggle*
:Linediff linediff.txt /*:Linediff*
:LinediffReset linediff.txt /*:LinediffReset*
:Mark mark.txt /*:Mark*
:MarkClear mark.txt /*:MarkClear*
:MarkLoad mark.txt /*:MarkLoad*
Expand Down Expand Up @@ -889,6 +891,13 @@ javacomplete-todo javacomplete.txt /*javacomplete-todo*
javacomplete-usage javacomplete.txt /*javacomplete-usage*
javacomplete.txt javacomplete.txt /*javacomplete.txt*
keyword_complete neocomplcache.txt /*keyword_complete*
linediff linediff.txt /*linediff*
linediff-commands linediff.txt /*linediff-commands*
linediff-contents linediff.txt /*linediff-contents*
linediff-installation linediff.txt /*linediff-installation*
linediff-internals linediff.txt /*linediff-internals*
linediff-issues linediff.txt /*linediff-issues*
linediff-usage linediff.txt /*linediff-usage*
list-snippets snipMate.txt /*list-snippets*
local_markfilelist pi_netrw.txt /*local_markfilelist*
manpageview manpageview.txt /*manpageview*
Expand Down
48 changes: 48 additions & 0 deletions plugin/linediff.vim
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
if exists("g:loaded_linediff") || &cp
finish
endif

let g:loaded_linediff = '0.1.1' " version number
let s:keepcpo = &cpo
set cpo&vim

" Initialized lazily to avoid executing the autoload file before it's really
" needed.
function! s:Init()
if !exists('s:differ_one')
let s:differ_one = linediff#differ#New('linediff_one', 1)
let s:differ_two = linediff#differ#New('linediff_two', 2)
endif
endfunction

command! -range Linediff call s:Linediff(<line1>, <line2>)
function! s:Linediff(from, to)
call s:Init()

if s:differ_one.IsBlank()
call s:differ_one.Init(a:from, a:to)
elseif s:differ_two.IsBlank()
call s:differ_two.Init(a:from, a:to)

call s:PerformDiff(s:differ_one, s:differ_two)
else
call s:differ_one.Reset()
call s:differ_two.Reset()

call s:Linediff(a:from, a:to)
endif
endfunction

command! LinediffReset call s:LinediffReset()
function! s:LinediffReset()
call s:differ_one.Reset()
call s:differ_two.Reset()
endfunction

function! s:PerformDiff(one, two)
call a:one.CreateDiffBuffer("tabedit")
call a:two.CreateDiffBuffer("rightbelow vsplit")

let a:one.other_differ = a:two
let a:two.other_differ = a:one
endfunction

0 comments on commit a9efa3c

Please sign in to comment.