All of lore.kernel.org
 help / color / mirror / Atom feed
From: Clemens Buchacher <drizzd@aon.at>
To: git@vger.kernel.org
Subject: [ANNOUNCE] diffit - A git plugin for vim
Date: Sat, 15 May 2010 12:50:09 +0200	[thread overview]
Message-ID: <20100515105009.GA10517@localhost> (raw)

[-- 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

             reply	other threads:[~2010-05-15 10:55 UTC|newest]

Thread overview: 4+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2010-05-15 10:50 Clemens Buchacher [this message]
2010-05-15 14:40 ` [ANNOUNCE] diffit - A git plugin for vim Jakub Narebski
2010-05-16  9:12 ` Wincent Colaiuta
2010-05-16  9:41   ` Clemens Buchacher

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20100515105009.GA10517@localhost \
    --to=drizzd@aon.at \
    --cc=git@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.