git.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [ANNOUNCE] diffit - A git plugin for vim
@ 2010-05-15 10:50 Clemens Buchacher
  2010-05-15 14:40 ` Jakub Narebski
  2010-05-16  9:12 ` Wincent Colaiuta
  0 siblings, 2 replies; 4+ messages in thread
From: Clemens Buchacher @ 2010-05-15 10:50 UTC (permalink / raw)
  To: git

[-- Attachment #1: Type: text/plain, Size: 659 bytes --]

Hi,

I am currently working on a git plugin for vim. My aim is for it to imitate
much of git-gui's functionality. Right now, it is still closer to "add -p".

But I believe it's already useful. And since I'm new to vim scripting, it
can use some testing.

To install, copy diffit.vim (attached to this email) to
~/.vim/plugin/diffit.vim .

To toggle diffit mode, use <Leader>d, where <Leader> is '\' by default.

Control keys:

 <Leader>d      toggle diffit mode
 s              stage hunk
 d              skip file

I hope you like it. Have fun!

Clemens
---

You can clone diffit from here.

 git://repo.or.cz/diffit.git
 http://github.com/drizzd/diffit.git

[-- Attachment #2: diffit.vim --]
[-- Type: text/plain, Size: 5305 bytes --]

" ============================================================================
" File:        diffit.vim
" Description: Show diff for current buffer
" Maintainer:  Clemens Buchacher <drizzd@aon.at>
" License:     GPLv2
"
" ============================================================================
if exists('loaded_diffit')
    finish
end
let loaded_diffit = 1

let s:diffit_version = '0'

"for line continuation - i.e dont want C in &cpo
let s:old_cpo = &cpo
set cpo&vim

map <silent> <Leader>d :call <SID>Diffit()<CR>

function s:Error(msg)
	echohl ErrorMsg
	echon 'diffit: ' . a:msg
	echohl None
endfunction

function s:Info(msg)
	echon 'diffit: ' . a:msg
endfunction

function s:Die(msg)
	call s:Error('fatal: ' . a:msg)
	throw "diffit"
endfunction

function s:System(...)
	let out = system(join(a:000))
	if v:shell_error
		call s:Die(a:0 . ' failed: ' . out)
	end
	return out
endfunction

function s:Header()
	let header = []
	for n in range(1, line('$'))
			let l = getline(n)
			if l =~ '^diff --git ' ||
						\l =~ '^diff --cc ' ||
						\l =~ '^diff --combined ' ||
						\l =~ '^old mode ' ||
						\l =~ '^new mode ' ||
						\l =~ '^--- ' ||
						\l =~ '^+++ '
				call add(header, l)
				continue
			end
			if l !~ '^index '
				break
			end
	endfor
	return [header, n - 1]
endfunction

function s:Exit()
	let view = b:view
	bdelete
	call winrestview(view)
endfunction

function s:Diffit()
	if exists('b:diffit') && b:diffit == 1
		call s:Exit()
		return
	end
	try
		call s:Diffit_init()
	catch /^diffit$/
		if exists('b:diffit') && b:diffit == 1
			call s:Exit()
		end
	endtry
endfunction

function s:Read_diff(pathlist)
	let diff = tempname()
	let path = ''
	for path in a:pathlist
		let out = s:System('git diff', '--', path, '>', diff)
		if getfsize(diff) > 0
			break
		end
	endfor
	return [diff, path]
endfunction

function s:Write_diff(diff, orig)
	setlocal modifiable
	silent 1,$delete _
	silent exe 'read ' . a:diff
	silent 1delete _
	setlocal nomodifiable

	if a:orig
		let orig_pos = b:view['lnum']
		let new_pos = s:Diffpos(orig_pos)
		let view = copy(b:view)
		let view['lnum'] = abs(new_pos)
		if new_pos > 0
			let view['topline'] += new_pos - orig_pos
			let view['topline'] = max([1, view['topline']])
		else
			let view['topline'] = -new_pos - 4
		end
		let view['curswant'] += 1
		let view['col'] += 1
		call winrestview(view)
	else
		call cursor(abs(s:Diffpos(0)), 1)
	end
endfunction

function s:Diffit_init()
	update
	let out = s:System('git rev-parse',  '--is-inside-work-tree')
	if v:shell_error == 128 || split(out)[0] != 'true'
		call s:Error('not inside work tree')
		return
	elseif v:shell_error
		call s:Error('git rev-parse failed: ' . out)
		return
	end
	let out = s:System('git diff', '--name-only')
	let pathlist = split(out, '\n')
	if empty(pathlist)
		call s:Info('no changes')
		return
	end
	let orig_path = bufname('%')
	let k = index(pathlist, orig_path)
	if k > 0
		call remove(pathlist, k)
		call insert(pathlist, orig_path, 0)
	end
	let [diff, path] = s:Read_diff(pathlist)
	let orig = path == orig_path
	if getfsize(diff) == 0
		call s:Info('no changes')
		return
	end

	let view = winsaveview()
	silent! exe 'edit ' . tempname()
	let b:pathlist = pathlist
	let b:view = view
	let b:diffit = 1
	setf git-diff
	setlocal noswapfile
	setlocal buftype=nofile
	setlocal nowrap
	setlocal foldcolumn=0
	setlocal nobuflisted

	iabc <buffer>

	nnoremap <silent> <buffer> s :call <SID>Stage_hunk(line('.'))<CR>
	nnoremap <silent> <buffer> d :call <SID>Next_diff()<CR>

	call s:Write_diff(diff, orig)
	echon '"' . path . '"'
endfunction

function s:Next_diff()
	call remove(b:pathlist, 0)
	let [diff, path] = s:Read_diff(b:pathlist)
	if getfsize(diff) > 0
		call s:Write_diff(diff, 0)
		echon '"' . path . '"'
	else
		call s:Exit()
	end
endfunction

function s:Diffpos(orig_pos)
	let diffpos = -1
	let hunk_start = 1
	let hunk_end = 1
	call cursor(1, 1)
	while search('^@@', 'W') > 0
		let [start, length] = matchlist(getline('.'),
			\'^@@ -[0-9]*,[0-9]* +\%(\([0-9]*\),\)\?\([0-9]*\)')[1:2]
		if empty(start)
			let start = 1
		end
		if diffpos < 0
			let diffpos = -line('.')
		end
		if start > a:orig_pos
			break
		end
		let diffpos = line('.')
		let hunk_start = start
		let hunk_end = hunk_start + length - 1
	endwhile
	if diffpos < 0
		return diffpos
	end
	let pos = hunk_start - 1
	let target_pos = min([a:orig_pos, hunk_end])
	while diffpos < line('$')
		if getline(diffpos) =~ '^-'
			let diffpos += 1
			continue
		end
		if pos >= target_pos
			break
		end
		let diffpos += 1
		let pos += 1
	endwhile

	if getline(diffpos) =~ '^-'
		return -last
	else
		return diffpos
	end
endfunction

function s:Stage_hunk(pos)
	call cursor(a:pos, 1)
	let h_start = search('^@@', 'bcW')
	if h_start == 0
		return
	end
	call cursor(h_start, 1)
	let h_end = search('^@@', 'nW')-1
	if h_end < 0
		let h_end = line('$')
	end
	let h_range = h_start . ',' . h_end

	let [patch, header_end] = s:Header()
	call extend(patch, getline(h_start, h_end))
	let patchfile = tempname()
	call writefile(patch, patchfile)
	let out = s:System('git apply', '--cached', '--whitespace=nowarn', patchfile)

	setlocal modifiable
	silent exe h_range . 'delete _'
	setlocal nomodifiable
	if line('$') == header_end
		call s:Next_diff()
		return
	end
endfunction

let &cpo = s:old_cpo

^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: [ANNOUNCE] diffit - A git plugin for vim
  2010-05-15 10:50 [ANNOUNCE] diffit - A git plugin for vim Clemens Buchacher
@ 2010-05-15 14:40 ` Jakub Narebski
  2010-05-16  9:12 ` Wincent Colaiuta
  1 sibling, 0 replies; 4+ messages in thread
From: Jakub Narebski @ 2010-05-15 14:40 UTC (permalink / raw)
  To: Clemens Buchacher; +Cc: git

Clemens Buchacher <drizzd@aon.at> writes:

> I am currently working on a git plugin for vim. My aim is for it to imitate
> much of git-gui's functionality. Right now, it is still closer to "add -p".
> 
> But I believe it's already useful. And since I'm new to vim scripting, it
> can use some testing.
> 
> To install, copy diffit.vim (attached to this email) to
> ~/.vim/plugin/diffit.vim .
> 
> To toggle diffit mode, use <Leader>d, where <Leader> is '\' by default.
> 
> Control keys:
> 
>  <Leader>d      toggle diffit mode
>  s              stage hunk
>  d              skip file

> You can clone diffit from here.
> 
>  git://repo.or.cz/diffit.git
>  http://github.com/drizzd/diffit.git

Could you add information about 'diffit' to "Interfaces, Frontends And
Tools" page on Git Wiki:
  https://git.wiki.kernel.org/index.php/InterfacesFrontendsAndTools
in the 'Editors and IDE integration' section?

Thanks in advance.
-- 
Jakub Narebski
Poland
ShadeHawk on #git

^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: [ANNOUNCE] diffit - A git plugin for vim
  2010-05-15 10:50 [ANNOUNCE] diffit - A git plugin for vim Clemens Buchacher
  2010-05-15 14:40 ` Jakub Narebski
@ 2010-05-16  9:12 ` Wincent Colaiuta
  2010-05-16  9:41   ` Clemens Buchacher
  1 sibling, 1 reply; 4+ messages in thread
From: Wincent Colaiuta @ 2010-05-16  9:12 UTC (permalink / raw)
  To: Clemens Buchacher; +Cc: git

El 15/05/2010, a las 12:50, Clemens Buchacher escribió:

> Hi,
> 
> I am currently working on a git plugin for vim. My aim is for it to imitate
> much of git-gui's functionality. Right now, it is still closer to "add -p".
> 
> But I believe it's already useful. And since I'm new to vim scripting, it
> can use some testing.

Clemens, were you aware that there are already a couple of Git plug-ins for Vim? Of these, the most advanced one currently is almost certainly Fugitive:

http://github.com/tpope/vim-fugitive

Cheers,
Wincent

^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: [ANNOUNCE] diffit - A git plugin for vim
  2010-05-16  9:12 ` Wincent Colaiuta
@ 2010-05-16  9:41   ` Clemens Buchacher
  0 siblings, 0 replies; 4+ messages in thread
From: Clemens Buchacher @ 2010-05-16  9:41 UTC (permalink / raw)
  To: Wincent Colaiuta; +Cc: git

Hi Wincent,

On Sun, May 16, 2010 at 11:12:37AM +0200, Wincent Colaiuta wrote:

> El 15/05/2010, a las 12:50, Clemens Buchacher escribió:
> 
> > Hi,
> > 
> > I am currently working on a git plugin for vim. My aim is for it to imitate
> > much of git-gui's functionality. Right now, it is still closer to "add -p".
> > 
> > But I believe it's already useful. And since I'm new to vim scripting, it
> > can use some testing.
> 
> Clemens, were you aware that there are already a couple of Git plug-ins
> for Vim? Of these, the most advanced one currently is almost certainly
> Fugitive:

The git plugins I am aware of, including vim-fugitive, mostly implement git
commands to be executed from vim. I find that utterly useless, since I can
already do that from the command line.

What I can not do from the command line is staging hunks and lines
interactively, possibly interrupted by a quick fix to a change. This is what
I would use git-gui for, only it does not integrate very well with my vim +
cmdline centric workflow.

Here's my TODO list:

- handle file mode changes

- navigation (prev/next hunk/file)

- stage individual lines and (virtual) range

- unstage changes

- file browser (modified, staged, untracked)

- prepare commit message

- git add -e?

If existing plugins already implement these features, then I have missed it,
and I will be happy to use or improve them instead.

As for diffing, some of the existing plugins implement side-by-side, or even
inline diff, using the vimdiff feature. I prefer the native git-diff format,
however.

Cheers,
Clemens

^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2010-05-16 10:13 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2010-05-15 10:50 [ANNOUNCE] diffit - A git plugin for vim Clemens Buchacher
2010-05-15 14:40 ` Jakub Narebski
2010-05-16  9:12 ` Wincent Colaiuta
2010-05-16  9:41   ` Clemens Buchacher

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).