From a890d6b60b3d63c2b8d9accc5f355c2bd7c1f123 Mon Sep 17 00:00:00 2001 From: Jerome Reybert Date: Thu, 15 Oct 2015 13:40:00 +0200 Subject: [PATCH 01/46] plugin/magit.vim: s:mg_search_block now returns [startline, endline] instead of block of text --- plugin/magit.vim | 45 ++++++++++++++++++++++----------------------- 1 file changed, 22 insertions(+), 23 deletions(-) diff --git a/plugin/magit.vim b/plugin/magit.vim index e2d18e8..6e5d5db 100644 --- a/plugin/magit.vim +++ b/plugin/magit.vim @@ -560,8 +560,8 @@ function! s:mg_comment_char() endif endfunction -" s:mg_search_block: helper function, to get a block of text, giving a start -" and multiple end pattern +" s:mg_search_block: helper function, to get start and end line of a block, +" giving a start and multiple end pattern " a "pattern parameter" is a List: " @[0]: end pattern regex " @[1]: number of line to exclude above (negative), below (positive) or none (0) @@ -573,9 +573,7 @@ endfunction " (smallest region search) " param[in] upperlimit_pattern: regex of upper limit. If start_pattern line is " inferior to upper_limit line, block is discarded -" return: a list. -" @[0]: return status -" @[1]: List of selected block lines +" return: [startline, endline] function! s:mg_search_block(start_pattern, end_pattern, upper_limit_pattern) let l:winview = winsaveview() @@ -609,10 +607,9 @@ function! s:mg_search_block(start_pattern, end_pattern, upper_limit_pattern) throw "out_of_block" endif - let lines=getline(start, end) - call winrestview(l:winview) - return lines + + return [start,end] endfunction " s:mg_git_commit: commit staged stuff with message prepared in commit section @@ -631,9 +628,10 @@ function! s:mg_git_commit(mode) abort let commit_section_pat_start='^'.g:magit_sections['commit_start'].'$' let commit_section_pat_end='^'.g:magit_sections['commit_end'].'$' let commit_jump_line = 3 + mg_get_inline_help_line_nb('commit') - let commit_msg = mg_search_block( + let [start, end] = mg_search_block( \ [commit_section_pat_start, commit_jump_line], \ [ [commit_section_pat_end, -1] ], "") + let commit_msg = getline(start, end) let amend_flag="" if ( a:mode == 'CA' ) let amend_flag=" --amend " @@ -906,20 +904,21 @@ endfunction " param[in] block_type: can be 'file' or 'hunk' " param[in] discard: boolean, if true, discard instead of (un)stage " return: no -function! magit#stage_block(block_type, discard) abort - try - let selection = mg_select_closed_file() - catch 'out_of_block' - if ( a:block_type == 'hunk') - try - let selection = mg_select_hunk_block() - catch 'out_of_block' - let selection = mg_select_file_block() - endtry - else - let selection = mg_select_file_block() - endif - endtry +function! magit#stage_block(block_type, discard, ...) abort + try + let selection = mg_select_closed_file() + catch 'out_of_block' + if ( a:block_type == 'hunk') + try + let [start,end] = mg_select_hunk_block() + catch 'out_of_block' + let [start,end] = mg_select_file_block() + endtry + else + let [start, end] = mg_select_file_block() + endif + let selection = getline(start, end) + endtry let section=mg_get_section() let filename=mg_get_filename() From 1a31aa9dbe9b54507d3b7a8ddfcbac1a1d278415 Mon Sep 17 00:00:00 2001 From: Jerome Reybert Date: Fri, 16 Oct 2015 14:44:57 +0200 Subject: [PATCH 02/46] plugin/magit.vim: magit#stage_block now gets a selection as parameter, and looses block_type in the same time, magit#discard_hunk is disolved in magit#stage_hunk --- plugin/magit.vim | 64 +++++++++++++++++++++++------------------------- 1 file changed, 30 insertions(+), 34 deletions(-) diff --git a/plugin/magit.vim b/plugin/magit.vim index 6e5d5db..e6727ac 100644 --- a/plugin/magit.vim +++ b/plugin/magit.vim @@ -858,8 +858,8 @@ function! magit#show_magit(display) execute "file " . g:magit_unstaged_buffer_name execute "nnoremap " . g:magit_stage_file_mapping . " :call magit#stage_file()" - execute "nnoremap " . g:magit_stage_hunk_mapping . " :call magit#stage_hunk()" - execute "nnoremap " . g:magit_discard_hunk_mapping . " :call magit#discard_hunk()" + execute "nnoremap " . g:magit_stage_hunk_mapping . " :call magit#stage_hunk(0)" + execute "nnoremap " . g:magit_discard_hunk_mapping . " :call magit#stage_hunk(1)" execute "nnoremap " . g:magit_reload_mapping . " :call magit#update_buffer()" execute "cnoremap " . g:magit_commit_mapping_command." :call magit#commit_command('CC')" execute "nnoremap " . g:magit_commit_mapping . " :call magit#commit_command('CC')" @@ -898,28 +898,13 @@ function! s:mg_select_closed_file() throw "out_of_block" endfunction -" maagit#stage_block: this function (un)stage a block, according to parameter +" magit#stage_block: this function (un)stage a block, according to parameter " INFO: in unstaged section, it stages the hunk, and in staged section, it " unstages the hunk " param[in] block_type: can be 'file' or 'hunk' " param[in] discard: boolean, if true, discard instead of (un)stage " return: no -function! magit#stage_block(block_type, discard, ...) abort - try - let selection = mg_select_closed_file() - catch 'out_of_block' - if ( a:block_type == 'hunk') - try - let [start,end] = mg_select_hunk_block() - catch 'out_of_block' - let [start,end] = mg_select_file_block() - endtry - else - let [start, end] = mg_select_file_block() - endif - let selection = getline(start, end) - endtry - +function! magit#stage_block(selection, discard) abort let section=mg_get_section() let filename=mg_get_filename() let header = mg_diff_dict_get_header(section, filename) @@ -931,7 +916,7 @@ function! magit#stage_block(block_type, discard, ...) abort \ s:mg_diff_dict[section][filename]['binary'] == 1 ) call mg_system('git add ' . mg_add_quotes(filename)) else - call mg_git_apply(header, selection) + call mg_git_apply(header, a:selection) endif elseif ( section == 'staged' ) if ( s:mg_diff_dict[section][filename]['empty'] == 1 || @@ -939,7 +924,7 @@ function! magit#stage_block(block_type, discard, ...) abort \ s:mg_diff_dict[section][filename]['binary'] == 1 ) call mg_system('git reset ' . mg_add_quotes(filename)) else - call mg_git_unapply(header, selection, 'staged') + call mg_git_unapply(header, a:selection, 'staged') endif else echoerr "Must be in \"" . @@ -953,7 +938,7 @@ function! magit#stage_block(block_type, discard, ...) abort \ s:mg_diff_dict[section][filename]['binary'] == 1 ) call delete(filename) else - call mg_git_unapply(header, selection, 'unstaged') + call mg_git_unapply(header, a:selection, 'unstaged') endif else echoerr "Must be in \"" . @@ -970,24 +955,35 @@ endfunction " unstages the file " return: no function! magit#stage_file() - return magit#stage_block('file', 0) + try + let selection = mg_select_closed_file() + catch 'out_of_block' + let [start, end] = mg_select_file_block() + let selection = getline(start, end) + endtry + return magit#stage_block(selection, 0) endfunction " -" magit#stage_hunk: this function (un)stage a hunk, from the current +" magit#stage_hunk: this function (un)stage/discard a hunk, from the current " cursor position " INFO: in unstaged section, it stages the hunk, and in staged section, it " unstages the hunk +" param[in] discard: +" - when set to 0, (un)stage +" - when set to 1, discard " return: no -function! magit#stage_hunk() - return magit#stage_block('hunk', 0) -endfunction - -" magit#discard_hunk: this function discard a single hunk, from the current -" cursor position -" INFO: only works in unstaged section -" return: no -function! magit#discard_hunk() - return magit#stage_block('hunk', 1) +function! magit#stage_hunk(discard) + try + let selection = mg_select_closed_file() + catch 'out_of_block' + try + let [start,end] = mg_select_hunk_block() + catch 'out_of_block' + let [start,end] = mg_select_file_block() + endtry + let selection = getline(start, end) + endtry + return magit#stage_block(selection, a:discard) endfunction " magit#ignore_file: this function add the file under cursor to .gitignore From 2d5fc841d12eda38492007e1c5d561f73457072f Mon Sep 17 00:00:00 2001 From: Jerome Reybert Date: Fri, 16 Oct 2015 14:48:09 +0200 Subject: [PATCH 03/46] plugin/magit.vim: stage by visual selection \o/ (fixes #8) still need to add tests and doc it works as well as magit, witht he same limitation --- plugin/magit.vim | 67 +++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 63 insertions(+), 4 deletions(-) diff --git a/plugin/magit.vim b/plugin/magit.vim index e6727ac..8c02d5c 100644 --- a/plugin/magit.vim +++ b/plugin/magit.vim @@ -38,6 +38,7 @@ call s:set('g:magit_show_magit_mapping', 'M' ) " these mapping are applied locally, for magit buffer only call s:set('g:magit_stage_file_mapping', 'F' ) call s:set('g:magit_stage_hunk_mapping', 'S' ) +call s:set('g:magit_stage_line_mapping', 'L' ) call s:set('g:magit_discard_hunk_mapping', 'DDD' ) call s:set('g:magit_commit_mapping_command', 'w' ) call s:set('g:magit_commit_mapping', 'CC' ) @@ -691,11 +692,13 @@ function! s:mg_git_apply(header, selection) if ( selection[-1] !~ '^$' ) let selection += [ '' ] endif - silent let git_result=mg_system("git apply --no-index --cached -", selection) + let git_cmd="git apply --recount --index --cached -" + silent let git_result=mg_system(git_cmd, selection) if ( v:shell_error != 0 ) echoerr "Git error: " . git_result + echoerr "Git cmd: " . git_cmd echoerr "Tried to aply this" - echoerr string(a:selection) + echoerr string(selection) endif endfunction @@ -714,14 +717,54 @@ function! s:mg_git_unapply(header, selection, mode) if ( selection[-1] !~ '^$' ) let selection += [ '' ] endif - silent let git_result=mg_system("git apply --no-index " . cached_flag . " --reverse - ", selection) + silent let git_result=mg_system("git apply --recount --index " . cached_flag . " --reverse - ", selection) if ( v:shell_error != 0 ) echoerr "Git error: " . git_result echoerr "Tried to unaply this" - echoerr string(a:selection) + echoerr string(selection) endif endfunction +" s:mg_create_diff_from_select: craft the diff to apply from a selection +" in a chunk +" remarks: it works with full lines, and can not span over multiple chunks +" param[in] start_chunk_line,end_chunk_line: limits of the selection +" return: List containing the diff to apply, including the chunk header (must +" be applied with git apply --recount) +function! s:mg_create_diff_from_select(start_chunk_line, end_chunk_line) + let lines=getline(a:start_chunk_line, a:end_chunk_line) + let [starthunk,endhunk] = mg_select_hunk_block() + let section=mg_get_section() + let filename=mg_get_filename() + let hunks = mg_diff_dict_get_hunks(section, filename) + for hunk in hunks + if ( hunk[0] == getline(starthunk) ) + let current_hunk = hunk + break + endif + endfor + let selection = [] + let visual_selection = getline(a:start_chunk_line, a:end_chunk_line) + call add(selection, current_hunk[0]) + + let current_line = starthunk + 1 + for hunk_line in current_hunk[1:] + if ( current_line >= a:start_chunk_line && current_line <= a:end_chunk_line ) + call add(selection, visual_selection[current_line-a:start_chunk_line]) + elseif ( hunk_line =~ '^+.*' ) + " just ignore these lines + elseif ( hunk_line =~ '^-.*' ) + call add(selection, substitute(hunk_line, '^-\(.*\)$', ' \1', '')) + elseif ( hunk_line =~ '^ .*' ) + call add(selection, hunk_line) + else + throw 'visual selection error: ' . hunk_line + endif + let current_line += 1 + endfor + return selection +endfunction + " s:mg_get_section: helper function to get the current section, according to " cursor position " return: section id, empty string if no section found @@ -868,6 +911,10 @@ function! magit#show_magit(display) execute "nnoremap " . g:magit_ignore_mapping . " :call magit#ignore_file()" execute "nnoremap " . g:magit_close_mapping . " :close" execute "nnoremap " . g:magit_toggle_help_mapping . " :call magit#toggle_help()" + + execute "nnoremap " . g:magit_stage_line_mapping . " :call magit#stage_vselect()" + execute "xnoremap " . g:magit_stage_hunk_mapping . " :call magit#stage_vselect()" + for mapping in g:magit_folding_toggle_mapping " trick to pass '' in a mapping command without being interpreted let func_arg = ( mapping ==? "" ) ? '+' : mapping @@ -986,6 +1033,18 @@ function! magit#stage_hunk(discard) return magit#stage_block(selection, a:discard) endfunction +" magit#stage_vselect: this function (un)stage text being sectected in Visual +" mode +" remarks: it works with full lines, and can not span over multiple chunks +" INFO: in unstaged section, it stages the file, and in staged section, it +" unstages the file +" return: no +function! magit#stage_vselect() range + " func-range a:firstline a:lastline seems to work at least from vim 7.2 + let selection = mg_create_diff_from_select(a:firstline, a:lastline) + return magit#stage_block(selection, 0) +endfunction + " magit#ignore_file: this function add the file under cursor to .gitignore " FIXME: git diff adds some strange characters to end of line function! magit#ignore_file() abort From 66f2000a6f69acccb87ef605abf02ef3da3b1cbf Mon Sep 17 00:00:00 2001 From: Jerome Reybert Date: Fri, 16 Oct 2015 18:24:58 +0200 Subject: [PATCH 04/46] plugin/ doc/ test/: update magit#stage_hunk() --- doc/vimagit.txt | 4 ++-- test/addFile.vader | 8 ++++---- test/addHunk.vader | 12 ++++++------ test/ignoreFile.vader | 6 +++--- 4 files changed, 15 insertions(+), 15 deletions(-) diff --git a/doc/vimagit.txt b/doc/vimagit.txt index 2e0aaca..146024e 100644 --- a/doc/vimagit.txt +++ b/doc/vimagit.txt @@ -151,7 +151,7 @@ Following mappings are set locally, for magit buffer only, in normal mode. *vimagit-g:magit_folding_close_mapping* zc,zC Typing zc on a file will hide its diffs. - *vimagit-S* *magit#stage_hunk()* + *vimagit-S* *magit#stage_hunk(0)* *vimagit-g:magit_stage_hunk_mapping* S If cursor is in a hunk, stage/unstage hunk at cursor position. If cursor is in diff header, stage/unstage whole file at cursor @@ -170,7 +170,7 @@ Following mappings are set locally, for magit buffer only, in normal mode. will unstage file. - *vimagit-DDD* *magit#discard_hunk()* + *vimagit-DDD* *magit#stage_hunk(1)* *vimagit-g:magit_discard_hunk_mapping* DDD If cursor is in a hunk, discard hunk at cursor position. If cursor is in diff header, discard whole file at cursor diff --git a/test/addFile.vader b/test/addFile.vader index 759b2a4..d538201 100644 --- a/test/addFile.vader +++ b/test/addFile.vader @@ -74,7 +74,7 @@ Execute (Stage untracked file with magit#stage_hunk at closed file header): call Search_file('unstaged') call magit#open_close_folding(0) call Cursor_position() - call magit#stage_hunk() + call magit#stage_hunk(0) call Cd_test() let diff=Git_diff('staged', Get_filename()) call Expect_diff(g:test_script_dir . 'addFile/addFile_' . Get_safe_filename() . '_file_diff.expect', diff) @@ -85,7 +85,7 @@ Execute (Unstage file with magit#stage_hunk at closed file header): call Search_file('staged') call magit#open_close_folding(0) call Cursor_position() - call magit#stage_hunk() + call magit#stage_hunk(0) call Cd_test() let diff=Git_status(Get_filename()) call Expect_diff(g:test_script_dir . 'addFile/addFile_' . Get_safe_filename() . '_unstaged_status.expect', diff) @@ -96,7 +96,7 @@ Execute (Stage untracked file with magit#stage_hunk at file header): call Search_file('unstaged') call magit#open_close_folding(1) call Cursor_position() - call magit#stage_hunk() + call magit#stage_hunk(0) call Cd_test() let diff=Git_diff('staged', Get_filename()) call Expect_diff(g:test_script_dir . 'addFile/addFile_' . Get_safe_filename() . '_file_diff.expect', diff) @@ -107,7 +107,7 @@ Execute (Unstage file with magit#stage_hunk at file header): call Search_file('staged') call magit#open_close_folding(1) call Cursor_position() - call magit#stage_hunk() + call magit#stage_hunk(0) call Cd_test() let diff=Git_status(Get_filename()) call Expect_diff(g:test_script_dir . 'addFile/addFile_' . Get_safe_filename() . '_unstaged_status.expect', diff) diff --git a/test/addHunk.vader b/test/addHunk.vader index a966608..549df8d 100644 --- a/test/addHunk.vader +++ b/test/addHunk.vader @@ -7,7 +7,7 @@ Execute (Stage untracked file with magit#stage_hunk on start hunk): call magit#open_close_folding(1) /^@@ call Cursor_position() - call magit#stage_hunk() + call magit#stage_hunk(0) call Cd_test() let diff=Git_diff('staged', Get_filename()) call Expect_diff(g:test_script_dir . 'addHunk/addHunk_' . Get_safe_filename() . '_hunk_diff.expect', diff) @@ -19,7 +19,7 @@ Execute (Unstage untracked file with magit#stage_hunk on start hunk): call magit#open_close_folding(1) /^@@ call Cursor_position() - call magit#stage_hunk() + call magit#stage_hunk(0) call Cd_test() let diff=Git_status(Get_filename()) call Expect_diff(g:test_script_dir . 'addHunk/addHunk_' . Get_safe_filename() . '_unstaged_status.expect', diff) @@ -32,7 +32,7 @@ Execute (Stage untracked file with magit#stage_hunk on end hunk): /^@@ call Move_relative(+3) call Cursor_position() - call magit#stage_hunk() + call magit#stage_hunk(0) call Cd_test() let diff=Git_diff('staged', Get_filename()) call Expect_diff(g:test_script_dir . 'addHunk/addHunk_' . Get_safe_filename() . '_hunk_diff.expect', diff) @@ -45,7 +45,7 @@ Execute (Untage untracked file with magit#stage_hunk on end hunk): /^@@ call Move_relative(+3) call Cursor_position() - call magit#stage_hunk() + call magit#stage_hunk(0) call Cd_test() let diff=Git_status(Get_filename()) call Expect_diff(g:test_script_dir . 'addHunk/addHunk_' . Get_safe_filename() . '_unstaged_status.expect', diff) @@ -58,7 +58,7 @@ Execute (Stage untracked file with magit#stage_hunk modified hunk): /^+ call setline(line('.'), substitute(getline('.'), '^\(+.*\) .\{-\}$', '\1 vimatest', '')) call Cursor_position() - call magit#stage_hunk() + call magit#stage_hunk(0) call Cd_test() let diff=Git_diff('staged', Get_filename()) call Expect_diff(g:test_script_dir . 'addHunk/addHunk_' . Get_safe_filename() . '_modified_hunk_diff.expect', diff) @@ -70,7 +70,7 @@ Execute (Unstage untracked file with magit#stage_hunk): call magit#open_close_folding(1) /^@@ call Cursor_position() - call magit#stage_hunk() + call magit#stage_hunk(0) call Cd_test() let diff=Git_status(Get_filename()) call Expect_diff(g:test_script_dir . 'addHunk/addHunk_' . Get_safe_filename() . '_unstaged_status.expect', diff) diff --git a/test/ignoreFile.vader b/test/ignoreFile.vader index c0dc1f0..2475e91 100644 --- a/test/ignoreFile.vader +++ b/test/ignoreFile.vader @@ -15,7 +15,7 @@ Execute (Ignore untracked file at closed file header): call search('^modified: .gitignore$') call magit#open_close_folding(0) call Cursor_position() - call magit#discard_hunk() + call magit#stage_hunk(1) call magit#update_buffer() Assert (Search_file('unstaged') != 0), $VIMAGIT_TEST_FILENAME . 'should not be ignored anymore' @@ -34,7 +34,7 @@ Execute (Ignore untracked file at opened file header): call search('^modified: .gitignore$') call magit#open_close_folding(1) call Cursor_position() - call magit#discard_hunk() + call magit#stage_hunk(1) call magit#update_buffer() Assert (Search_file('unstaged') != 0), $VIMAGIT_TEST_FILENAME . 'should not be ignored anymore' @@ -55,7 +55,7 @@ Execute (Ignore untracked file at hunk position): call magit#open_close_folding(1) call Move_relative(+1) call Cursor_position() - call magit#discard_hunk() + call magit#stage_hunk(1) call magit#update_buffer() Assert (Search_file('unstaged') != 0), $VIMAGIT_TEST_FILENAME . 'should not be ignored anymore' From f9f918682eceea8c49b63e8db5253b78476dab97 Mon Sep 17 00:00:00 2001 From: Jerome Reybert Date: Fri, 16 Oct 2015 23:44:42 +0200 Subject: [PATCH 05/46] plugin/magit.vim: fix git_(un)apply --- plugin/magit.vim | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugin/magit.vim b/plugin/magit.vim index 8c02d5c..eaa6bd6 100644 --- a/plugin/magit.vim +++ b/plugin/magit.vim @@ -692,7 +692,7 @@ function! s:mg_git_apply(header, selection) if ( selection[-1] !~ '^$' ) let selection += [ '' ] endif - let git_cmd="git apply --recount --index --cached -" + let git_cmd="git apply --recount --no-index --cached -" silent let git_result=mg_system(git_cmd, selection) if ( v:shell_error != 0 ) echoerr "Git error: " . git_result @@ -717,7 +717,7 @@ function! s:mg_git_unapply(header, selection, mode) if ( selection[-1] !~ '^$' ) let selection += [ '' ] endif - silent let git_result=mg_system("git apply --recount --index " . cached_flag . " --reverse - ", selection) + silent let git_result=mg_system("git apply --recount --no-index " . cached_flag . " --reverse - ", selection) if ( v:shell_error != 0 ) echoerr "Git error: " . git_result echoerr "Tried to unaply this" From cb53d8498aed8b0762455e736434f0e4c19711b4 Mon Sep 17 00:00:00 2001 From: Jerome Reybert Date: Fri, 16 Oct 2015 23:01:37 +0200 Subject: [PATCH 06/46] autoload/magit/utils.vim: move general functions to an autoload dir --- autoload/magit/utils.vim | 164 ++++++++++++++++++++++++++++ plugin/magit.vim | 224 ++++++--------------------------------- 2 files changed, 199 insertions(+), 189 deletions(-) create mode 100644 autoload/magit/utils.vim diff --git a/autoload/magit/utils.vim b/autoload/magit/utils.vim new file mode 100644 index 0000000..6702944 --- /dev/null +++ b/autoload/magit/utils.vim @@ -0,0 +1,164 @@ +" s:magit_top_dir: top directory of git tree +" it is evaluated only once +" FIXME: it won't work when playing with multiple git directories wihtin one +" vim session +let s:magit_top_dir='' +" magit#utils#top_dir: return the absolute path of current git worktree +" return top directory +function! magit#utils#top_dir() + if ( s:magit_top_dir == '' ) + let s:magit_top_dir=magit#utils#strip( + \ system("git rev-parse --show-toplevel")) . "/" + if ( v:shell_error != 0 ) + echoerr "Git error: " . s:magit_top_dir + endif + endif + return s:magit_top_dir +endfunction + +" s:magit_git_dir: git directory +" it is evaluated only once +" FIXME: it won't work when playing with multiple git directories wihtin one +" vim session +let s:magit_git_dir='' +" magit#utils#git_dir: return the absolute path of current git worktree +" return git directory +function! magit#utils#git_dir() + if ( s:magit_git_dir == '' ) + let s:magit_git_dir=magit#utils#strip(system("git rev-parse --git-dir")) . "/" + if ( v:shell_error != 0 ) + echoerr "Git error: " . s:magit_git_dir + endif + endif + return s:magit_git_dir +endfunction + +" s:magit_cd_cmd: plugin variable to choose lcd/cd command, 'lcd' if exists, +" 'cd' otherwise +let s:magit_cd_cmd = exists('*haslocaldir') && haslocaldir() ? 'lcd ' : 'cd ' + +" magit#utils#system: wrapper for system, which only takes String as input in vim, +" although it can take String or List input in neovim. +" INFO: temporarly change pwd to git top directory, then restore to previous +" pwd at the end of function +" param[in] ...: command + optional args +" return: command output as a string +function! magit#utils#system(...) + let dir = getcwd() + try + execute s:magit_cd_cmd . magit#utils#top_dir() + " List as system() input is since v7.4.247, it is safe to check + " systemlist, which is sine v7.4.248 + if exists('*systemlist') + return call('system', a:000) + else + if ( a:0 == 2 ) + if ( type(a:2) == type([]) ) + " ouch, this one is tough: input is very very sensitive, join + " MUST BE done with "\n", not '\n' !! + let arg=join(a:2, "\n") + else + let arg=a:2 + endif + return system(a:1, arg) + else + return system(a:1) + endif + endif + finally + execute s:magit_cd_cmd . dir + endtry +endfunction + +" magit#utils#systemlist: wrapper for systemlist, which only exists in neovim for +" the moment. +" INFO: temporarly change pwd to git top directory, then restore to previous +" pwd at the end of function +" param[in] ...: command + optional args to execute, args can be List or String +" return: command output as a list +function! magit#utils#systemlist(...) + let dir = getcwd() + try + execute s:magit_cd_cmd . magit#utils#top_dir() + " systemlist since v7.4.248 + if exists('*systemlist') + return call('systemlist', a:000) + else + return split(call('magit#utils#system', a:000), '\n') + endif + finally + execute s:magit_cd_cmd . dir + endtry +endfunction + +" magit#utils#underline: helper function to underline a string +" param[in] title: string to underline +" return a string composed of strlen(title) '=' +function! magit#utils#underline(title) + return substitute(a:title, ".", "=", "g") +endfunction + +" magit#utils#strip: helper function to strip a string +" WARNING: it only works with monoline string +" param[in] string: string to strip +" return: stripped string +function! magit#utils#strip(string) + return substitute(a:string, '^\s*\(.\{-}\)\s*\n\=$', '\1', '') +endfunction + +" magit#utils#join_list: helper function to concatente a list of strings with newlines +" param[in] list: List to to concat +" return: concatenated list +function! magit#utils#join_list(list) + return join(a:list, "\n") . "\n" +endfunction + +" magit#utils#add_quotes: helper function to protect filename with quotes +" return quoted filename +function! magit#utils#add_quotes(filename) + return '"' . a:filename . '"' +endfunction + +" magit#utils#remove_quotes: helper function to remove quotes aroudn filename +" return unquoted filename +function! magit#utils#remove_quotes(filename) + let ret=matchlist(a:filename, '"\([^"]*\)"') + if ( empty(ret) ) + throw 'no quotes found: ' . a:filename + endif + return ret[1] +endfunction + +" magit#utils#fatten: flat a nested list. it return a one dimensional list with +" primary elements +" https://gist.github.com/dahu/3322468 +" param[in] list: a List, can be nested or not +" return: one dimensional list +function! magit#utils#flatten(list) + let val = [] + for elem in a:list + if type(elem) == type([]) + call extend(val, magit#utils#flatten(elem)) + else + call extend(val, [elem]) + endif + unlet elem + endfor + return val +endfunction + +" magit#utils#append_file: helper function to append to a file +" Version working with file *possibly* containing trailing newline +" param[in] file: filename to append +" param[in] lines: List of lines to append +function! magit#utils#append_file(file, lines) + let fcontents=[] + if ( filereadable(a:file) ) + let fcontents=readfile(a:file, 'b') + endif + if !empty(fcontents) && empty(fcontents[-1]) + call remove(fcontents, -1) + endif + call writefile(fcontents+a:lines, a:file, 'b') +endfunction + diff --git a/plugin/magit.vim b/plugin/magit.vim index eaa6bd6..0011725 100644 --- a/plugin/magit.vim +++ b/plugin/magit.vim @@ -62,169 +62,6 @@ execute "nnoremap " . g:magit_show_magit_mapping . " :call magit#show_m " {{{ Internal functions -" s:magit_top_dir: top directory of git tree -" it is evaluated only once -" FIXME: it won't work when playing with multiple git directories wihtin one -" vim session -let s:magit_top_dir='' -" s:mg_top_dir: return the absolute path of current git worktree -" return top directory -function! s:mg_top_dir() - if ( s:magit_top_dir == '' ) - let s:magit_top_dir=mg_strip(system("git rev-parse --show-toplevel")) . "/" - if ( v:shell_error != 0 ) - echoerr "Git error: " . s:magit_top_dir - endif - endif - return s:magit_top_dir -endfunction - -" s:magit_git_dir: git directory -" it is evaluated only once -" FIXME: it won't work when playing with multiple git directories wihtin one -" vim session -let s:magit_git_dir='' -" s:mg_git_dir: return the absolute path of current git worktree -" return git directory -function! s:mg_git_dir() - if ( s:magit_git_dir == '' ) - let s:magit_git_dir=mg_strip(system("git rev-parse --git-dir")) . "/" - if ( v:shell_error != 0 ) - echoerr "Git error: " . s:magit_git_dir - endif - endif - return s:magit_git_dir -endfunction - -" s:magit_cd_cmd: plugin variable to choose lcd/cd command, 'lcd' if exists, -" 'cd' otherwise -let s:magit_cd_cmd = exists('*haslocaldir') && haslocaldir() ? 'lcd ' : 'cd ' - -" s:mg_system: wrapper for system, which only takes String as input in vim, -" although it can take String or List input in neovim. -" INFO: temporarly change pwd to git top directory, then restore to previous -" pwd at the end of function -" param[in] ...: command + optional args -" return: command output as a string -function! s:mg_system(...) - let dir = getcwd() - try - execute s:magit_cd_cmd . mg_top_dir() - " List as system() input is since v7.4.247, it is safe to check - " systemlist, which is sine v7.4.248 - if exists('*systemlist') - return call('system', a:000) - else - if ( a:0 == 2 ) - if ( type(a:2) == type([]) ) - " ouch, this one is tough: input is very very sensitive, join - " MUST BE done with "\n", not '\n' !! - let arg=join(a:2, "\n") - else - let arg=a:2 - endif - return system(a:1, arg) - else - return system(a:1) - endif - endif - finally - execute s:magit_cd_cmd . dir - endtry -endfunction - -" s:mg_systemlist: wrapper for systemlist, which only exists in neovim for -" the moment. -" INFO: temporarly change pwd to git top directory, then restore to previous -" pwd at the end of function -" param[in] ...: command + optional args to execute, args can be List or String -" return: command output as a list -function! s:mg_systemlist(...) - let dir = getcwd() - try - execute s:magit_cd_cmd . mg_top_dir() - " systemlist since v7.4.248 - if exists('*systemlist') - return call('systemlist', a:000) - else - return split(call('mg_system', a:000), '\n') - endif - finally - execute s:magit_cd_cmd . dir - endtry -endfunction - -" s:mg_underline: helper function to underline a string -" param[in] title: string to underline -" return a string composed of strlen(title) '=' -function! s:mg_underline(title) - return substitute(a:title, ".", "=", "g") -endfunction - -" s:mg_strip: helper function to strip a string -" WARNING: it only works with monoline string -" param[in] string: string to strip -" return: stripped string -function! s:mg_strip(string) - return substitute(a:string, '^\s*\(.\{-}\)\s*\n\=$', '\1', '') -endfunction - -" s:mg_join_list: helper function to concatente a list of strings with newlines -" param[in] list: List to to concat -" return: concatenated list -function! s:mg_join_list(list) - return join(a:list, "\n") . "\n" -endfunction - -" s:mg_add_quotes: helper function to protect filename with quotes -" return quoted filename -function! s:mg_add_quotes(filename) - return '"' . a:filename . '"' -endfunction - -" s:mg_remove_quotes: helper function to remove quotes aroudn filename -" return unquoted filename -function! s:mg_remove_quotes(filename) - let ret=matchlist(a:filename, '"\([^"]*\)"') - if ( empty(ret) ) - throw 'no quotes found: ' . a:filename - endif - return ret[1] -endfunction - -" s:mg_fatten: flat a nested list. it return a one dimensional list with -" primary elements -" https://gist.github.com/dahu/3322468 -" param[in] list: a List, can be nested or not -" return: one dimensional list -function! s:mg_flatten(list) - let val = [] - for elem in a:list - if type(elem) == type([]) - call extend(val, mg_flatten(elem)) - else - call extend(val, [elem]) - endif - unlet elem - endfor - return val -endfunction - -" s:mg_append_file: helper function to append to a file -" Version working with file *possibly* containing trailing newline -" param[in] file: filename to append -" param[in] lines: List of lines to append -function! s:mg_append_file(file, lines) - let fcontents=[] - if ( filereadable(a:file) ) - let fcontents=readfile(a:file, 'b') - endif - if !empty(fcontents) && empty(fcontents[-1]) - call remove(fcontents, -1) - endif - call writefile(fcontents+a:lines, a:file, 'b') -endfunction - " s:mg_get_status_list: this function returns the git status output formated " into a List of Dict as " [ {staged', 'unstaged', 'filename'}, ... ] @@ -235,7 +72,7 @@ function! s:mg_get_status_list() " we can't use git status -z here, because system doesn't make the " difference between NUL and NL. -status z terminate entries with NUL, " instead of NF - let status_list=mg_systemlist("git status --porcelain") + let status_list=magit#utils#systemlist("git status --porcelain") for file_status_line in status_list let line_match = matchlist(file_status_line, '\(.\)\(.\) \%(.\{-\} -> \)\?"\?\(.\{-\}\)"\?$') let filename = line_match[3] @@ -303,10 +140,10 @@ endfunction function! s:mg_get_info() silent put ='' silent put =g:magit_sections['info'] - silent put =mg_underline(g:magit_sections['info']) + silent put =magit#utils#underline(g:magit_sections['info']) silent put ='' - let branch=mg_system("git rev-parse --abbrev-ref HEAD") - let commit=mg_system("git show -s --oneline") + let branch=magit#utils#system("git rev-parse --abbrev-ref HEAD") + let commit=magit#utils#system("git show -s --oneline") silent put ='Current branch: ' . branch silent put ='Last commit: ' . commit silent put ='' @@ -373,8 +210,8 @@ function! s:mg_diff_dict_add_file(mode, status, filename) let staged_flag = ( a:mode == 'staged' ) ? " --staged " : " " let diff_cmd="git diff --no-ext-diff " . staged_flag . \ "--no-color --patch -- " . dev_null . " " - \ . mg_add_quotes(a:filename) - let diff_list=s:mg_systemlist(diff_cmd) + \ . magit#utils#add_quotes(a:filename) + let diff_list=magit#utils#systemlist(diff_cmd) if ( empty(diff_list) ) echoerr "diff command \"" . diff_cmd . "\" returned nothing" endif @@ -393,7 +230,9 @@ function! s:mg_diff_dict_add_file(mode, status, filename) let diff_dict_file['empty'] = 1 call add(diff_dict_file['diff'], ['no header']) call add(diff_dict_file['diff'], ['New empty file']) - elseif ( match(system("file --mime " . mg_add_quotes(a:filename)), a:filename . ".*charset=binary") != -1 ) + elseif ( match(system("file --mime " . + \ magit#utils#add_quotes(a:filename)), + \ a:filename . ".*charset=binary") != -1 ) let diff_dict_file['binary'] = 1 call add(diff_dict_file['diff'], ['no header']) call add(diff_dict_file['diff'], ['Binary file']) @@ -458,7 +297,7 @@ function! s:mg_get_staged_section(mode) put ='' put =g:magit_sections[a:mode] call mg_section_help(a:mode) - put =s:mg_underline(g:magit_sections[a:mode]) + put =magit#utils#underline(g:magit_sections[a:mode]) put ='' for [ filename, file_props ] in items(s:mg_diff_dict[a:mode]) @@ -488,7 +327,7 @@ endfunction " WARNING: this function writes in file, it should only be called through " protected functions like magit#update_buffer function! s:mg_get_stashes() - silent! let stash_list=mg_systemlist("git stash list") + silent! let stash_list=magit#utils#systemlist("git stash list") if ( v:shell_error != 0 ) echoerr "Git error: " . stash_list endif @@ -496,7 +335,7 @@ function! s:mg_get_stashes() if (!empty(stash_list)) silent put ='' silent put =g:magit_sections['stash'] - silent put =mg_underline(g:magit_sections['stash']) + silent put =magit#utils#underline(g:magit_sections['stash']) silent put ='' for stash in stash_list @@ -533,19 +372,19 @@ function! s:mg_get_commit_section() silent put =g:magit_sections['commit_start'] silent put ='Commit mode: '.commit_mode_str call mg_section_help('commit') - silent put =mg_underline(g:magit_sections['commit_start']) + silent put =magit#utils#underline(g:magit_sections['commit_start']) silent put ='' - let git_dir=mg_git_dir() + let git_dir=magit#utils#git_dir() " refresh the COMMIT_EDITMSG file if ( s:magit_commit_mode == 'CC' ) - silent! call mg_system("GIT_EDITOR=/bin/false git commit -e 2> /dev/null") + silent! call magit#utils#system("GIT_EDITOR=/bin/false git commit -e 2> /dev/null") elseif ( s:magit_commit_mode == 'CA' ) - silent! call mg_system("GIT_EDITOR=/bin/false git commit --amend -e 2> /dev/null") + silent! call magit#utils#system("GIT_EDITOR=/bin/false git commit --amend -e 2> /dev/null") endif if ( filereadable(git_dir . 'COMMIT_EDITMSG') ) let comment_char=mg_comment_char() - let commit_msg=mg_join_list(filter(readfile(git_dir . 'COMMIT_EDITMSG'), 'v:val !~ "^' . comment_char . '"')) + let commit_msg=magit#utils#join_list(filter(readfile(git_dir . 'COMMIT_EDITMSG'), 'v:val !~ "^' . comment_char . '"')) put =commit_msg endif put =g:magit_sections['commit_end'] @@ -553,7 +392,8 @@ endfunction " s:mg_comment_char: this function gets the commentChar from git config function! s:mg_comment_char() - silent! let git_result=mg_strip(mg_system("git config --get core.commentChar")) + silent! let git_result=magit#utils#strip( + \ magit#utils#system("git config --get core.commentChar")) if ( v:shell_error != 0 ) return '#' else @@ -624,7 +464,7 @@ endfunction " return no function! s:mg_git_commit(mode) abort if ( a:mode == 'CF' ) - silent let git_result=mg_system("git commit --amend -C HEAD") + silent let git_result=magit#utils#system("git commit --amend -C HEAD") else let commit_section_pat_start='^'.g:magit_sections['commit_start'].'$' let commit_section_pat_end='^'.g:magit_sections['commit_end'].'$' @@ -637,7 +477,8 @@ function! s:mg_git_commit(mode) abort if ( a:mode == 'CA' ) let amend_flag=" --amend " endif - silent! let git_result=mg_system("git commit " . amend_flag . " --file - ", commit_msg) + silent! let git_result=magit#utils#system( + \ "git commit " . amend_flag . " --file - ", commit_msg) endif if ( v:shell_error != 0 ) echoerr "Git error: " . git_result @@ -688,12 +529,12 @@ endfunction " header plus one or more hunks " return: no function! s:mg_git_apply(header, selection) - let selection = mg_flatten(a:header + a:selection) + let selection = magit#utils#flatten(a:header + a:selection) if ( selection[-1] !~ '^$' ) let selection += [ '' ] endif let git_cmd="git apply --recount --no-index --cached -" - silent let git_result=mg_system(git_cmd, selection) + silent let git_result=magit#utils#system(git_cmd, selection) if ( v:shell_error != 0 ) echoerr "Git error: " . git_result echoerr "Git cmd: " . git_cmd @@ -713,11 +554,13 @@ function! s:mg_git_unapply(header, selection, mode) if ( a:mode == 'staged' ) let cached_flag=' --cached ' endif - let selection = mg_flatten(a:header + a:selection) + let selection = magit#utils#flatten(a:header + a:selection) if ( selection[-1] !~ '^$' ) let selection += [ '' ] endif - silent let git_result=mg_system("git apply --recount --no-index " . cached_flag . " --reverse - ", selection) + silent let git_result=magit#utils#system( + \ "git apply --recount --no-index " . cached_flag . " --reverse - ", + \ selection) if ( v:shell_error != 0 ) echoerr "Git error: " . git_result echoerr "Tried to unaply this" @@ -876,7 +719,7 @@ endfunction " 'h': horizontal split " 'c': current buffer (should be used when opening vim in vimagit mode function! magit#show_magit(display) - if ( mg_strip(system("git rev-parse --is-inside-work-tree")) != 'true' ) + if ( magit#utils#strip(system("git rev-parse --is-inside-work-tree")) != 'true' ) echoerr "Magit must be started from a git repository" return endif @@ -961,7 +804,8 @@ function! magit#stage_block(selection, discard) abort if ( s:mg_diff_dict[section][filename]['empty'] == 1 || \ s:mg_diff_dict[section][filename]['symlink'] != '' || \ s:mg_diff_dict[section][filename]['binary'] == 1 ) - call mg_system('git add ' . mg_add_quotes(filename)) + call magit#utils#system('git add ' . + \ magit#utils#add_quotes(filename)) else call mg_git_apply(header, a:selection) endif @@ -969,7 +813,8 @@ function! magit#stage_block(selection, discard) abort if ( s:mg_diff_dict[section][filename]['empty'] == 1 || \ s:mg_diff_dict[section][filename]['symlink'] != '' || \ s:mg_diff_dict[section][filename]['binary'] == 1 ) - call mg_system('git reset ' . mg_add_quotes(filename)) + call magit#utils#system('git reset ' . + \ magit#utils#add_quotes(filename)) else call mg_git_unapply(header, a:selection, 'staged') endif @@ -1049,7 +894,8 @@ endfunction " FIXME: git diff adds some strange characters to end of line function! magit#ignore_file() abort let ignore_file=mg_get_filename() - call mg_append_file(mg_top_dir() . ".gitignore", [ ignore_file ] ) + call magit#utils#append_file(magit#utils#top_dir() . ".gitignore", + \ [ ignore_file ] ) call magit#update_buffer() endfunction From 4151f23d0d5cbc1c597dd1e36653d19ecf7facd9 Mon Sep 17 00:00:00 2001 From: Jerome Reybert Date: Sat, 17 Oct 2015 00:19:03 +0200 Subject: [PATCH 07/46] autoload/magit/state.vim: move all state commands in autoload --- autoload/magit/git.vim | 19 ++++ autoload/magit/state.vim | 138 +++++++++++++++++++++++++++ plugin/magit.vim | 197 ++++----------------------------------- 3 files changed, 177 insertions(+), 177 deletions(-) create mode 100644 autoload/magit/git.vim create mode 100644 autoload/magit/state.vim diff --git a/autoload/magit/git.vim b/autoload/magit/git.vim new file mode 100644 index 0000000..0389b90 --- /dev/null +++ b/autoload/magit/git.vim @@ -0,0 +1,19 @@ +" magit#git#get_status: this function returns the git status output formated +" into a List of Dict as +" [ {staged', 'unstaged', 'filename'}, ... ] +function! magit#git#get_status() + let file_list = [] + + " systemlist v7.4.248 problem again + " we can't use git status -z here, because system doesn't make the + " difference between NUL and NL. -status z terminate entries with NUL, + " instead of NF + let status_list=magit#utils#systemlist("git status --porcelain") + for file_status_line in status_list + let line_match = matchlist(file_status_line, '\(.\)\(.\) \%(.\{-\} -> \)\?"\?\(.\{-\}\)"\?$') + let filename = line_match[3] + call add(file_list, { 'staged': line_match[1], 'unstaged': line_match[2], 'filename': filename }) + endfor + return file_list +endfunction + diff --git a/autoload/magit/state.vim b/autoload/magit/state.vim new file mode 100644 index 0000000..4252629 --- /dev/null +++ b/autoload/magit/state.vim @@ -0,0 +1,138 @@ + +" g:mg_diff_dict: big main global variable, containing all diffs +" It is formatted as follow +" { 'staged_or_unstaged': staged/unstaged +" [ +" { 'filename': +" { 'visible': bool, +" 'status' : g:magit_git_status_code, +" 'exists' : bool +" 'diff' : [ [header], [hunk0], [hunk1], ...] +" } +" }, +" ... +" ] +" } +let g:mg_diff_dict = { 'staged': {}, 'unstaged': {} } + +" magit#state#get_file: mg_diff_dict accessor for file +" param[in] mode: can be staged or unstaged +" param[in] filename: filename to access +" param[in] create: boolean. If 1, non existing file in Dict will be created. +" if 0, 'file_doesnt_exists' exception will be thrown +" return: Dict of file +function! magit#state#get_file(mode, filename, create) + let file_exists = has_key(g:mg_diff_dict[a:mode], a:filename) + if ( file_exists == 0 && a:create == 1 ) + let g:mg_diff_dict[a:mode][a:filename] = {} + let g:mg_diff_dict[a:mode][a:filename]['visible'] = 0 + elseif ( file_exists == 0 && a:create == 0 ) + throw 'file_doesnt_exists' + endif + return g:mg_diff_dict[a:mode][a:filename] +endfunction + +" magit#state#get_header: mg_diff_dict accessor for diff header +" param[in] mode: can be staged or unstaged +" param[in] filename: header of filename to access +" return: List of diff header lines +function! magit#state#get_header(mode, filename) + let diff_dict_file = magit#state#get_file(a:mode, a:filename, 0) + return diff_dict_file['diff'][0] +endfunction + +" magit#state#get_hunks: mg_diff_dict accessor for hunks +" param[in] mode: can be staged or unstaged +" param[in] filename: hunks of filename to access +" return: List of List of hunks lines +function! magit#state#get_hunks(mode, filename) + let diff_dict_file = magit#state#get_file(a:mode, a:filename, 0) + return diff_dict_file['diff'][1:-1] +endfunction + +" magit#state#add_file: mg_diff_dict method to add a file with all its +" properties (filename, exists, status, header and hunks) +" param[in] mode: can be staged or unstaged +" param[in] status: one character status code of the file (AMDRCU?) +" param[in] filename: filename +function! magit#state#add_file(mode, status, filename) + let dev_null = ( a:status == '?' ) ? " /dev/null " : " " + let staged_flag = ( a:mode == 'staged' ) ? " --staged " : " " + let diff_cmd="git diff --no-ext-diff " . staged_flag . + \ "--no-color --patch -- " . dev_null . " " + \ . magit#utils#add_quotes(a:filename) + let diff_list=magit#utils#systemlist(diff_cmd) + if ( empty(diff_list) ) + echoerr "diff command \"" . diff_cmd . "\" returned nothing" + endif + let diff_dict_file = magit#state#get_file(a:mode, a:filename, 1) + let diff_dict_file['diff'] = [] + let diff_dict_file['exists'] = 1 + let diff_dict_file['status'] = a:status + let diff_dict_file['empty'] = 0 + let diff_dict_file['binary'] = 0 + let diff_dict_file['symlink'] = '' + if ( a:status == '?' && getftype(a:filename) == 'link' ) + let diff_dict_file['symlink'] = resolve(a:filename) + call add(diff_dict_file['diff'], ['no header']) + call add(diff_dict_file['diff'], ['New symbolic link file']) + elseif ( a:status == '?' && getfsize(a:filename) == 0 ) + let diff_dict_file['empty'] = 1 + call add(diff_dict_file['diff'], ['no header']) + call add(diff_dict_file['diff'], ['New empty file']) + elseif ( match(system("file --mime " . + \ magit#utils#add_quotes(a:filename)), + \ a:filename . ".*charset=binary") != -1 ) + let diff_dict_file['binary'] = 1 + call add(diff_dict_file['diff'], ['no header']) + call add(diff_dict_file['diff'], ['Binary file']) + else + let index = 0 + call add(diff_dict_file['diff'], []) + for diff_line in diff_list + if ( diff_line =~ "^@.*" ) + let index+=1 + call add(diff_dict_file['diff'], []) + endif + call add(diff_dict_file['diff'][index], diff_line) + endfor + endif +endfunction + +" magit#state#update: update g:mg_diff_dict +" if a file does not exists anymore (because all its changes have been +" committed, deleted, discarded), it is removed from g:mg_diff_dict +" else, its diff is discarded and regenrated +" what is resilient is its 'visible' parameter +function! magit#state#update() + for diff_dict_mode in values(g:mg_diff_dict) + for file in values(diff_dict_mode) + let file['exists'] = 0 + " always discard previous diff + unlet file['diff'] + endfor + endfor + + for [mode, diff_dict_mode] in items(g:mg_diff_dict) + + let status_list = magit#git#get_status() + for file_status in status_list + let status=file_status[mode] + + " untracked code apperas in staged column, we skip it + if ( status == ' ' || ( ( mode == 'staged' ) && status == '?' ) ) + continue + endif + call magit#state#add_file(mode, status, file_status['filename']) + endfor + endfor + + " remove files that have changed their mode or been committed/deleted/discarded... + for diff_dict_mode in values(g:mg_diff_dict) + for [key, file] in items(diff_dict_mode) + if ( file['exists'] == 0 ) + unlet diff_dict_mode[key] + endif + endfor + endfor +endfunction diff --git a/plugin/magit.vim b/plugin/magit.vim index 0011725..a2ffcbf 100644 --- a/plugin/magit.vim +++ b/plugin/magit.vim @@ -62,25 +62,6 @@ execute "nnoremap " . g:magit_show_magit_mapping . " :call magit#show_m " {{{ Internal functions -" s:mg_get_status_list: this function returns the git status output formated -" into a List of Dict as -" [ {staged', 'unstaged', 'filename'}, ... ] -function! s:mg_get_status_list() - let file_list = [] - - " systemlist v7.4.248 problem again - " we can't use git status -z here, because system doesn't make the - " difference between NUL and NL. -status z terminate entries with NUL, - " instead of NF - let status_list=magit#utils#systemlist("git status --porcelain") - for file_status_line in status_list - let line_match = matchlist(file_status_line, '\(.\)\(.\) \%(.\{-\} -> \)\?"\?\(.\{-\}\)"\?$') - let filename = line_match[3] - call add(file_list, { 'staged': line_match[1], 'unstaged': line_match[2], 'filename': filename }) - endfor - return file_list -endfunction - " s:magit_inline_help: Dict containing inline help for each section let s:magit_inline_help = { \ 'staged': [ @@ -149,147 +130,9 @@ function! s:mg_get_info() silent put ='' endfunction -" s:mg_diff_dict: big main global variable, containing all diffs -" It is formatted as follow -" { 'staged_or_unstaged': staged/unstaged -" [ -" { 'filename': -" { 'visible': bool, -" 'status' : g:magit_git_status_code, -" 'exists' : bool -" 'diff' : [ [header], [hunk0], [hunk1], ...] -" } -" }, -" ... -" ] -" } -let s:mg_diff_dict = { 'staged': {}, 'unstaged': {} } - -" s:mg_diff_dict_get_file: mg_diff_dict accessor for file -" param[in] mode: can be staged or unstaged -" param[in] filename: filename to access -" param[in] create: boolean. If 1, non existing file in Dict will be created. -" if 0, 'file_doesnt_exists' exception will be thrown -" return: Dict of file -function! s:mg_diff_dict_get_file(mode, filename, create) - let file_exists = has_key(s:mg_diff_dict[a:mode], a:filename) - if ( file_exists == 0 && a:create == 1 ) - let s:mg_diff_dict[a:mode][a:filename] = {} - let s:mg_diff_dict[a:mode][a:filename]['visible'] = 0 - elseif ( file_exists == 0 && a:create == 0 ) - throw 'file_doesnt_exists' - endif - return s:mg_diff_dict[a:mode][a:filename] -endfunction - -" s:mg_diff_dict_get_header: mg_diff_dict accessor for diff header -" param[in] mode: can be staged or unstaged -" param[in] filename: header of filename to access -" return: List of diff header lines -function! s:mg_diff_dict_get_header(mode, filename) - let diff_dict_file = s:mg_diff_dict_get_file(a:mode, a:filename, 0) - return diff_dict_file['diff'][0] -endfunction - -" s:mg_diff_dict_get_hunks: mg_diff_dict accessor for hunks -" param[in] mode: can be staged or unstaged -" param[in] filename: hunks of filename to access -" return: List of List of hunks lines -function! s:mg_diff_dict_get_hunks(mode, filename) - let diff_dict_file = s:mg_diff_dict_get_file(a:mode, a:filename, 0) - return diff_dict_file['diff'][1:-1] -endfunction - -" s:mg_diff_dict_add_file: mg_diff_dict method to add a file with all its -" properties (filename, exists, status, header and hunks) -" param[in] mode: can be staged or unstaged -" param[in] status: one character status code of the file (AMDRCU?) -" param[in] filename: filename -function! s:mg_diff_dict_add_file(mode, status, filename) - let dev_null = ( a:status == '?' ) ? " /dev/null " : " " - let staged_flag = ( a:mode == 'staged' ) ? " --staged " : " " - let diff_cmd="git diff --no-ext-diff " . staged_flag . - \ "--no-color --patch -- " . dev_null . " " - \ . magit#utils#add_quotes(a:filename) - let diff_list=magit#utils#systemlist(diff_cmd) - if ( empty(diff_list) ) - echoerr "diff command \"" . diff_cmd . "\" returned nothing" - endif - let diff_dict_file = mg_diff_dict_get_file(a:mode, a:filename, 1) - let diff_dict_file['diff'] = [] - let diff_dict_file['exists'] = 1 - let diff_dict_file['status'] = a:status - let diff_dict_file['empty'] = 0 - let diff_dict_file['binary'] = 0 - let diff_dict_file['symlink'] = '' - if ( a:status == '?' && getftype(a:filename) == 'link' ) - let diff_dict_file['symlink'] = resolve(a:filename) - call add(diff_dict_file['diff'], ['no header']) - call add(diff_dict_file['diff'], ['New symbolic link file']) - elseif ( a:status == '?' && getfsize(a:filename) == 0 ) - let diff_dict_file['empty'] = 1 - call add(diff_dict_file['diff'], ['no header']) - call add(diff_dict_file['diff'], ['New empty file']) - elseif ( match(system("file --mime " . - \ magit#utils#add_quotes(a:filename)), - \ a:filename . ".*charset=binary") != -1 ) - let diff_dict_file['binary'] = 1 - call add(diff_dict_file['diff'], ['no header']) - call add(diff_dict_file['diff'], ['Binary file']) - else - let index = 0 - call add(diff_dict_file['diff'], []) - for diff_line in diff_list - if ( diff_line =~ "^@.*" ) - let index+=1 - call add(diff_dict_file['diff'], []) - endif - call add(diff_dict_file['diff'][index], diff_line) - endfor - endif -endfunction - -" s:mg_update_diff_dict: update s:mg_diff_dict -" if a file does not exists anymore (because all its changes have been -" committed, deleted, discarded), it is removed from s:mg_diff_dict -" else, its diff is discarded and regenrated -" what is resilient is its 'visible' parameter -function! s:mg_update_diff_dict() - for diff_dict_mode in values(s:mg_diff_dict) - for file in values(diff_dict_mode) - let file['exists'] = 0 - " always discard previous diff - unlet file['diff'] - endfor - endfor - - for [mode, diff_dict_mode] in items(s:mg_diff_dict) - - let status_list = s:mg_get_status_list() - for file_status in status_list - let status=file_status[mode] - - " untracked code apperas in staged column, we skip it - if ( status == ' ' || ( ( mode == 'staged' ) && status == '?' ) ) - continue - endif - call mg_diff_dict_add_file(mode, status, file_status['filename']) - endfor - endfor - - " remove files that have changed their mode or been committed/deleted/discarded... - for diff_dict_mode in values(s:mg_diff_dict) - for [key, file] in items(diff_dict_mode) - if ( file['exists'] == 0 ) - unlet diff_dict_mode[key] - endif - endfor - endfor -endfunction - " s:mg_get_staged_section: this function writes in current buffer all staged -" or unstaged files, using s:mg_diff_dict information +" or unstaged files, using g:mg_diff_dict information " WARNING: this function writes in file, it should only be called through " protected functions like magit#update_buffer " param[in] mode: 'staged' or 'unstaged' @@ -300,7 +143,7 @@ function! s:mg_get_staged_section(mode) put =magit#utils#underline(g:magit_sections[a:mode]) put ='' - for [ filename, file_props ] in items(s:mg_diff_dict[a:mode]) + for [ filename, file_props ] in items(g:mg_diff_dict[a:mode]) if ( file_props['empty'] == 1 ) put =g:magit_git_status_code['E'] . ': ' . filename elseif ( file_props['symlink'] != '' ) @@ -315,7 +158,7 @@ function! s:mg_get_staged_section(mode) if ( file_props['exists'] == 0 ) echoerr "Error, " . filename . " should not exists" endif - let hunks=mg_diff_dict_get_hunks(a:mode, filename) + let hunks=magit#state#get_hunks(a:mode, filename) for diff_line in hunks silent put =diff_line endfor @@ -579,7 +422,7 @@ function! s:mg_create_diff_from_select(start_chunk_line, end_chunk_line) let [starthunk,endhunk] = mg_select_hunk_block() let section=mg_get_section() let filename=mg_get_filename() - let hunks = mg_diff_dict_get_hunks(section, filename) + let hunks = magit#state#get_hunks(section, filename) for hunk in hunks if ( hunk[0] == getline(starthunk) ) let current_hunk = hunk @@ -658,9 +501,9 @@ function! magit#open_close_folding(...) let section=mg_get_section() " if first param is set, force visible to this value " else, toggle value - let s:mg_diff_dict[section][filename]['visible'] = + let g:mg_diff_dict[section][filename]['visible'] = \ ( a:0 == 1 ) ? a:1 : - \ ( s:mg_diff_dict[section][filename]['visible'] == 0 ) ? 1 : 0 + \ ( g:mg_diff_dict[section][filename]['visible'] == 0 ) ? 1 : 0 call magit#update_buffer() endfunction @@ -689,7 +532,7 @@ function! magit#update_buffer() if ( s:magit_commit_mode != '' ) call mg_get_commit_section() endif - call mg_update_diff_dict() + call magit#state#update() call mg_get_staged_section('staged') call mg_get_staged_section('unstaged') call mg_get_stashes() @@ -779,9 +622,9 @@ function! s:mg_select_closed_file() let list = matchlist(getline("."), g:magit_file_re) let filename = list[2] let section=mg_get_section() - if ( has_key(s:mg_diff_dict[section], filename) && - \ ( s:mg_diff_dict[section][filename]['visible'] == 0 ) ) - let selection = mg_diff_dict_get_hunks(section, filename) + if ( has_key(g:mg_diff_dict[section], filename) && + \ ( g:mg_diff_dict[section][filename]['visible'] == 0 ) ) + let selection = magit#state#get_hunks(section, filename) return selection endif endif @@ -797,22 +640,22 @@ endfunction function! magit#stage_block(selection, discard) abort let section=mg_get_section() let filename=mg_get_filename() - let header = mg_diff_dict_get_header(section, filename) + let header = magit#state#get_header(section, filename) if ( a:discard == 0 ) if ( section == 'unstaged' ) - if ( s:mg_diff_dict[section][filename]['empty'] == 1 || - \ s:mg_diff_dict[section][filename]['symlink'] != '' || - \ s:mg_diff_dict[section][filename]['binary'] == 1 ) + if ( g:mg_diff_dict[section][filename]['empty'] == 1 || + \ g:mg_diff_dict[section][filename]['symlink'] != '' || + \ g:mg_diff_dict[section][filename]['binary'] == 1 ) call magit#utils#system('git add ' . \ magit#utils#add_quotes(filename)) else call mg_git_apply(header, a:selection) endif elseif ( section == 'staged' ) - if ( s:mg_diff_dict[section][filename]['empty'] == 1 || - \ s:mg_diff_dict[section][filename]['symlink'] != '' || - \ s:mg_diff_dict[section][filename]['binary'] == 1 ) + if ( g:mg_diff_dict[section][filename]['empty'] == 1 || + \ g:mg_diff_dict[section][filename]['symlink'] != '' || + \ g:mg_diff_dict[section][filename]['binary'] == 1 ) call magit#utils#system('git reset ' . \ magit#utils#add_quotes(filename)) else @@ -825,9 +668,9 @@ function! magit#stage_block(selection, discard) abort endif else if ( section == 'unstaged' ) - if ( s:mg_diff_dict[section][filename]['empty'] == 1 || - \ s:mg_diff_dict[section][filename]['symlink'] != '' || - \ s:mg_diff_dict[section][filename]['binary'] == 1 ) + if ( g:mg_diff_dict[section][filename]['empty'] == 1 || + \ g:mg_diff_dict[section][filename]['symlink'] != '' || + \ g:mg_diff_dict[section][filename]['binary'] == 1 ) call delete(filename) else call mg_git_unapply(header, a:selection, 'unstaged') From dc0f9fd7d4af2d2f5991c71c85c27b9d12d3bb32 Mon Sep 17 00:00:00 2001 From: Jerome Reybert Date: Sat, 17 Oct 2015 10:03:19 +0200 Subject: [PATCH 08/46] autoload/magit/state.vim: state is now an object, accessible through accessors still need to fix some direct accesses to self.dict --- autoload/magit/state.vim | 81 ++++++++++++++++++++++------------------ plugin/magit.vim | 42 +++++++++++---------- 2 files changed, 67 insertions(+), 56 deletions(-) diff --git a/autoload/magit/state.vim b/autoload/magit/state.vim index 4252629..fcbf4c7 100644 --- a/autoload/magit/state.vim +++ b/autoload/magit/state.vim @@ -1,61 +1,46 @@ -" g:mg_diff_dict: big main global variable, containing all diffs -" It is formatted as follow -" { 'staged_or_unstaged': staged/unstaged -" [ -" { 'filename': -" { 'visible': bool, -" 'status' : g:magit_git_status_code, -" 'exists' : bool -" 'diff' : [ [header], [hunk0], [hunk1], ...] -" } -" }, -" ... -" ] -" } -let g:mg_diff_dict = { 'staged': {}, 'unstaged': {} } -" magit#state#get_file: mg_diff_dict accessor for file +" s:get_file: function accessor for file " param[in] mode: can be staged or unstaged " param[in] filename: filename to access " param[in] create: boolean. If 1, non existing file in Dict will be created. " if 0, 'file_doesnt_exists' exception will be thrown " return: Dict of file -function! magit#state#get_file(mode, filename, create) - let file_exists = has_key(g:mg_diff_dict[a:mode], a:filename) +function! s:get_file(mode, filename, create) dict + let file_exists = has_key(self.dict[a:mode], a:filename) if ( file_exists == 0 && a:create == 1 ) - let g:mg_diff_dict[a:mode][a:filename] = {} - let g:mg_diff_dict[a:mode][a:filename]['visible'] = 0 + let self.dict[a:mode][a:filename] = {} + let self.dict[a:mode][a:filename]['visible'] = 0 elseif ( file_exists == 0 && a:create == 0 ) throw 'file_doesnt_exists' endif - return g:mg_diff_dict[a:mode][a:filename] + return self.dict[a:mode][a:filename] endfunction -" magit#state#get_header: mg_diff_dict accessor for diff header +" s:get_header: function accessor for diff header " param[in] mode: can be staged or unstaged " param[in] filename: header of filename to access " return: List of diff header lines -function! magit#state#get_header(mode, filename) - let diff_dict_file = magit#state#get_file(a:mode, a:filename, 0) +function! s:get_header(mode, filename) dict + let diff_dict_file = self.get_file(a:mode, a:filename, 0) return diff_dict_file['diff'][0] endfunction -" magit#state#get_hunks: mg_diff_dict accessor for hunks +" s:get_hunks: function accessor for hunks " param[in] mode: can be staged or unstaged " param[in] filename: hunks of filename to access " return: List of List of hunks lines -function! magit#state#get_hunks(mode, filename) - let diff_dict_file = magit#state#get_file(a:mode, a:filename, 0) +function! s:get_hunks(mode, filename) dict + let diff_dict_file = self.get_file(a:mode, a:filename, 0) return diff_dict_file['diff'][1:-1] endfunction -" magit#state#add_file: mg_diff_dict method to add a file with all its +" s:add_file: method to add a file with all its " properties (filename, exists, status, header and hunks) " param[in] mode: can be staged or unstaged " param[in] status: one character status code of the file (AMDRCU?) " param[in] filename: filename -function! magit#state#add_file(mode, status, filename) +function! s:add_file(mode, status, filename) dict let dev_null = ( a:status == '?' ) ? " /dev/null " : " " let staged_flag = ( a:mode == 'staged' ) ? " --staged " : " " let diff_cmd="git diff --no-ext-diff " . staged_flag . @@ -65,7 +50,7 @@ function! magit#state#add_file(mode, status, filename) if ( empty(diff_list) ) echoerr "diff command \"" . diff_cmd . "\" returned nothing" endif - let diff_dict_file = magit#state#get_file(a:mode, a:filename, 1) + let diff_dict_file = self.get_file(a:mode, a:filename, 1) let diff_dict_file['diff'] = [] let diff_dict_file['exists'] = 1 let diff_dict_file['status'] = a:status @@ -99,13 +84,13 @@ function! magit#state#add_file(mode, status, filename) endif endfunction -" magit#state#update: update g:mg_diff_dict +" s:update: update self.dict " if a file does not exists anymore (because all its changes have been " committed, deleted, discarded), it is removed from g:mg_diff_dict " else, its diff is discarded and regenrated " what is resilient is its 'visible' parameter -function! magit#state#update() - for diff_dict_mode in values(g:mg_diff_dict) +function! s:update() dict + for diff_dict_mode in values(self.dict) for file in values(diff_dict_mode) let file['exists'] = 0 " always discard previous diff @@ -113,7 +98,7 @@ function! magit#state#update() endfor endfor - for [mode, diff_dict_mode] in items(g:mg_diff_dict) + for [mode, diff_dict_mode] in items(self.dict) let status_list = magit#git#get_status() for file_status in status_list @@ -123,12 +108,12 @@ function! magit#state#update() if ( status == ' ' || ( ( mode == 'staged' ) && status == '?' ) ) continue endif - call magit#state#add_file(mode, status, file_status['filename']) + call self.add_file(mode, status, file_status['filename']) endfor endfor " remove files that have changed their mode or been committed/deleted/discarded... - for diff_dict_mode in values(g:mg_diff_dict) + for diff_dict_mode in values(self.dict) for [key, file] in items(diff_dict_mode) if ( file['exists'] == 0 ) unlet diff_dict_mode[key] @@ -136,3 +121,27 @@ function! magit#state#update() endfor endfor endfunction + +" dict: structure containing all diffs +" It is formatted as follow +" { 'staged_or_unstaged': staged/unstaged +" [ +" { 'filename': +" { 'visible': bool, +" 'status' : g:magit_git_status_code, +" 'exists' : bool +" 'diff' : [ [header], [hunk0], [hunk1], ...] +" } +" }, +" ... +" ] +" } +let magit#state#state = { + \ 'get_file': function("s:get_file"), + \ 'get_header': function("s:get_header"), + \ 'get_hunks': function("s:get_hunks"), + \ 'add_file': function("s:add_file"), + \ 'update': function("s:update"), + \ 'dict': { 'staged': {}, 'unstaged': {}}, + \ } + diff --git a/plugin/magit.vim b/plugin/magit.vim index a2ffcbf..9d10a1f 100644 --- a/plugin/magit.vim +++ b/plugin/magit.vim @@ -18,6 +18,8 @@ execute 'source ' . resolve(expand(':p:h')) . '/../common/magit_common.vi " g:magit_unstaged_buffer_name: vim buffer name for vimagit let g:magit_unstaged_buffer_name = "magit-playground" +let s:state = copy(magit#state#state) + " s:set: helper function to set user definable variable " param[in] var: variable to set " param[in] default: default value if not already set by the user @@ -132,7 +134,7 @@ endfunction " s:mg_get_staged_section: this function writes in current buffer all staged -" or unstaged files, using g:mg_diff_dict information +" or unstaged files, using s:state.dict information " WARNING: this function writes in file, it should only be called through " protected functions like magit#update_buffer " param[in] mode: 'staged' or 'unstaged' @@ -143,7 +145,7 @@ function! s:mg_get_staged_section(mode) put =magit#utils#underline(g:magit_sections[a:mode]) put ='' - for [ filename, file_props ] in items(g:mg_diff_dict[a:mode]) + for [ filename, file_props ] in items(s:state.dict[a:mode]) if ( file_props['empty'] == 1 ) put =g:magit_git_status_code['E'] . ': ' . filename elseif ( file_props['symlink'] != '' ) @@ -158,7 +160,7 @@ function! s:mg_get_staged_section(mode) if ( file_props['exists'] == 0 ) echoerr "Error, " . filename . " should not exists" endif - let hunks=magit#state#get_hunks(a:mode, filename) + let hunks=s:state.get_hunks(a:mode, filename) for diff_line in hunks silent put =diff_line endfor @@ -422,7 +424,7 @@ function! s:mg_create_diff_from_select(start_chunk_line, end_chunk_line) let [starthunk,endhunk] = mg_select_hunk_block() let section=mg_get_section() let filename=mg_get_filename() - let hunks = magit#state#get_hunks(section, filename) + let hunks = s:state.get_hunks(section, filename) for hunk in hunks if ( hunk[0] == getline(starthunk) ) let current_hunk = hunk @@ -501,9 +503,9 @@ function! magit#open_close_folding(...) let section=mg_get_section() " if first param is set, force visible to this value " else, toggle value - let g:mg_diff_dict[section][filename]['visible'] = + let s:state.dict[section][filename]['visible'] = \ ( a:0 == 1 ) ? a:1 : - \ ( g:mg_diff_dict[section][filename]['visible'] == 0 ) ? 1 : 0 + \ ( s:state.dict[section][filename]['visible'] == 0 ) ? 1 : 0 call magit#update_buffer() endfunction @@ -532,7 +534,7 @@ function! magit#update_buffer() if ( s:magit_commit_mode != '' ) call mg_get_commit_section() endif - call magit#state#update() + call s:state.update() call mg_get_staged_section('staged') call mg_get_staged_section('unstaged') call mg_get_stashes() @@ -622,9 +624,9 @@ function! s:mg_select_closed_file() let list = matchlist(getline("."), g:magit_file_re) let filename = list[2] let section=mg_get_section() - if ( has_key(g:mg_diff_dict[section], filename) && - \ ( g:mg_diff_dict[section][filename]['visible'] == 0 ) ) - let selection = magit#state#get_hunks(section, filename) + if ( has_key(s:state.dict[section], filename) && + \ ( s:state.dict[section][filename]['visible'] == 0 ) ) + let selection = s:state.get_hunks(section, filename) return selection endif endif @@ -640,22 +642,22 @@ endfunction function! magit#stage_block(selection, discard) abort let section=mg_get_section() let filename=mg_get_filename() - let header = magit#state#get_header(section, filename) + let header = s:state.get_header(section, filename) if ( a:discard == 0 ) if ( section == 'unstaged' ) - if ( g:mg_diff_dict[section][filename]['empty'] == 1 || - \ g:mg_diff_dict[section][filename]['symlink'] != '' || - \ g:mg_diff_dict[section][filename]['binary'] == 1 ) + if ( s:state.dict[section][filename]['empty'] == 1 || + \ s:state.dict[section][filename]['symlink'] != '' || + \ s:state.dict[section][filename]['binary'] == 1 ) call magit#utils#system('git add ' . \ magit#utils#add_quotes(filename)) else call mg_git_apply(header, a:selection) endif elseif ( section == 'staged' ) - if ( g:mg_diff_dict[section][filename]['empty'] == 1 || - \ g:mg_diff_dict[section][filename]['symlink'] != '' || - \ g:mg_diff_dict[section][filename]['binary'] == 1 ) + if ( s:state.dict[section][filename]['empty'] == 1 || + \ s:state.dict[section][filename]['symlink'] != '' || + \ s:state.dict[section][filename]['binary'] == 1 ) call magit#utils#system('git reset ' . \ magit#utils#add_quotes(filename)) else @@ -668,9 +670,9 @@ function! magit#stage_block(selection, discard) abort endif else if ( section == 'unstaged' ) - if ( g:mg_diff_dict[section][filename]['empty'] == 1 || - \ g:mg_diff_dict[section][filename]['symlink'] != '' || - \ g:mg_diff_dict[section][filename]['binary'] == 1 ) + if ( s:state.dict[section][filename]['empty'] == 1 || + \ s:state.dict[section][filename]['symlink'] != '' || + \ s:state.dict[section][filename]['binary'] == 1 ) call delete(filename) else call mg_git_unapply(header, a:selection, 'unstaged') From 27d535ec98ab651f5533699553f7fb19600aae91 Mon Sep 17 00:00:00 2001 From: Jerome Reybert Date: Sat, 17 Oct 2015 10:21:35 +0200 Subject: [PATCH 09/46] autoload/magit/state.vim: remove last direct accesses to .dict outside of state.vim --- autoload/magit/state.vim | 9 +++++++++ plugin/magit.vim | 31 ++++++++++++++++--------------- 2 files changed, 25 insertions(+), 15 deletions(-) diff --git a/autoload/magit/state.vim b/autoload/magit/state.vim index fcbf4c7..7597d77 100644 --- a/autoload/magit/state.vim +++ b/autoload/magit/state.vim @@ -1,4 +1,11 @@ +function! s:is_file_visible(section, filename) dict + return ( has_key(self.dict[a:section], a:filename) && + \ ( self.dict[a:section][a:filename]['visible'] == 1 ) ) +endfunction +function! s:get_files(mode) dict + return self.dict[a:mode] +endfunction " s:get_file: function accessor for file " param[in] mode: can be staged or unstaged @@ -138,9 +145,11 @@ endfunction " } let magit#state#state = { \ 'get_file': function("s:get_file"), + \ 'get_files': function("s:get_files"), \ 'get_header': function("s:get_header"), \ 'get_hunks': function("s:get_hunks"), \ 'add_file': function("s:add_file"), + \ 'is_file_visible': function("s:is_file_visible"), \ 'update': function("s:update"), \ 'dict': { 'staged': {}, 'unstaged': {}}, \ } diff --git a/plugin/magit.vim b/plugin/magit.vim index 9d10a1f..04d8102 100644 --- a/plugin/magit.vim +++ b/plugin/magit.vim @@ -145,7 +145,7 @@ function! s:mg_get_staged_section(mode) put =magit#utils#underline(g:magit_sections[a:mode]) put ='' - for [ filename, file_props ] in items(s:state.dict[a:mode]) + for [ filename, file_props ] in items(s:state.get_files(a:mode)) if ( file_props['empty'] == 1 ) put =g:magit_git_status_code['E'] . ': ' . filename elseif ( file_props['symlink'] != '' ) @@ -503,9 +503,10 @@ function! magit#open_close_folding(...) let section=mg_get_section() " if first param is set, force visible to this value " else, toggle value - let s:state.dict[section][filename]['visible'] = + let file = s:state.get_file(section, filename, 0) + let file['visible'] = \ ( a:0 == 1 ) ? a:1 : - \ ( s:state.dict[section][filename]['visible'] == 0 ) ? 1 : 0 + \ ( file['visible'] == 0 ) ? 1 : 0 call magit#update_buffer() endfunction @@ -624,8 +625,7 @@ function! s:mg_select_closed_file() let list = matchlist(getline("."), g:magit_file_re) let filename = list[2] let section=mg_get_section() - if ( has_key(s:state.dict[section], filename) && - \ ( s:state.dict[section][filename]['visible'] == 0 ) ) + if ( s:state.is_file_visible(section, filename) == 0 ) let selection = s:state.get_hunks(section, filename) return selection endif @@ -643,21 +643,22 @@ function! magit#stage_block(selection, discard) abort let section=mg_get_section() let filename=mg_get_filename() let header = s:state.get_header(section, filename) - + + let file = s:state.get_file(section, filename, 0) if ( a:discard == 0 ) if ( section == 'unstaged' ) - if ( s:state.dict[section][filename]['empty'] == 1 || - \ s:state.dict[section][filename]['symlink'] != '' || - \ s:state.dict[section][filename]['binary'] == 1 ) + if ( file['empty'] == 1 || + \ file['symlink'] != '' || + \ file['binary'] == 1 ) call magit#utils#system('git add ' . \ magit#utils#add_quotes(filename)) else call mg_git_apply(header, a:selection) endif elseif ( section == 'staged' ) - if ( s:state.dict[section][filename]['empty'] == 1 || - \ s:state.dict[section][filename]['symlink'] != '' || - \ s:state.dict[section][filename]['binary'] == 1 ) + if ( file['empty'] == 1 || + \ file['symlink'] != '' || + \ file['binary'] == 1 ) call magit#utils#system('git reset ' . \ magit#utils#add_quotes(filename)) else @@ -670,9 +671,9 @@ function! magit#stage_block(selection, discard) abort endif else if ( section == 'unstaged' ) - if ( s:state.dict[section][filename]['empty'] == 1 || - \ s:state.dict[section][filename]['symlink'] != '' || - \ s:state.dict[section][filename]['binary'] == 1 ) + if ( file['empty'] == 1 || + \ file['symlink'] != '' || + \ file['binary'] == 1 ) call delete(filename) else call mg_git_unapply(header, a:selection, 'unstaged') From 8e01d05abbfbdd59ccede215629a0412483297d6 Mon Sep 17 00:00:00 2001 From: Jerome Reybert Date: Sat, 17 Oct 2015 14:53:58 +0200 Subject: [PATCH 10/46] autoload/magit/state.vim: local functions are not handled in dict with old vim version at least, it doesn't work with vim 7.2-445 --- autoload/magit/state.vim | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/autoload/magit/state.vim b/autoload/magit/state.vim index 7597d77..204eeae 100644 --- a/autoload/magit/state.vim +++ b/autoload/magit/state.vim @@ -1,19 +1,19 @@ -function! s:is_file_visible(section, filename) dict +function! magit#state#is_file_visible(section, filename) dict return ( has_key(self.dict[a:section], a:filename) && \ ( self.dict[a:section][a:filename]['visible'] == 1 ) ) endfunction -function! s:get_files(mode) dict +function! magit#state#get_files(mode) dict return self.dict[a:mode] endfunction -" s:get_file: function accessor for file +" magit#state#get_file: function accessor for file " param[in] mode: can be staged or unstaged " param[in] filename: filename to access " param[in] create: boolean. If 1, non existing file in Dict will be created. " if 0, 'file_doesnt_exists' exception will be thrown " return: Dict of file -function! s:get_file(mode, filename, create) dict +function! magit#state#get_file(mode, filename, create) dict let file_exists = has_key(self.dict[a:mode], a:filename) if ( file_exists == 0 && a:create == 1 ) let self.dict[a:mode][a:filename] = {} @@ -24,30 +24,30 @@ function! s:get_file(mode, filename, create) dict return self.dict[a:mode][a:filename] endfunction -" s:get_header: function accessor for diff header +" magit#state#get_header: function accessor for diff header " param[in] mode: can be staged or unstaged " param[in] filename: header of filename to access " return: List of diff header lines -function! s:get_header(mode, filename) dict +function! magit#state#get_header(mode, filename) dict let diff_dict_file = self.get_file(a:mode, a:filename, 0) return diff_dict_file['diff'][0] endfunction -" s:get_hunks: function accessor for hunks +" magit#state#get_hunks: function accessor for hunks " param[in] mode: can be staged or unstaged " param[in] filename: hunks of filename to access " return: List of List of hunks lines -function! s:get_hunks(mode, filename) dict +function! magit#state#get_hunks(mode, filename) dict let diff_dict_file = self.get_file(a:mode, a:filename, 0) return diff_dict_file['diff'][1:-1] endfunction -" s:add_file: method to add a file with all its +" magit#state#add_file: method to add a file with all its " properties (filename, exists, status, header and hunks) " param[in] mode: can be staged or unstaged " param[in] status: one character status code of the file (AMDRCU?) " param[in] filename: filename -function! s:add_file(mode, status, filename) dict +function! magit#state#add_file(mode, status, filename) dict let dev_null = ( a:status == '?' ) ? " /dev/null " : " " let staged_flag = ( a:mode == 'staged' ) ? " --staged " : " " let diff_cmd="git diff --no-ext-diff " . staged_flag . @@ -91,12 +91,12 @@ function! s:add_file(mode, status, filename) dict endif endfunction -" s:update: update self.dict +" magit#state#update: update self.dict " if a file does not exists anymore (because all its changes have been " committed, deleted, discarded), it is removed from g:mg_diff_dict " else, its diff is discarded and regenrated " what is resilient is its 'visible' parameter -function! s:update() dict +function! magit#state#update() dict for diff_dict_mode in values(self.dict) for file in values(diff_dict_mode) let file['exists'] = 0 @@ -144,13 +144,13 @@ endfunction " ] " } let magit#state#state = { - \ 'get_file': function("s:get_file"), - \ 'get_files': function("s:get_files"), - \ 'get_header': function("s:get_header"), - \ 'get_hunks': function("s:get_hunks"), - \ 'add_file': function("s:add_file"), - \ 'is_file_visible': function("s:is_file_visible"), - \ 'update': function("s:update"), + \ 'get_file': function("magit#state#get_file"), + \ 'get_files': function("magit#state#get_files"), + \ 'get_header': function("magit#state#get_header"), + \ 'get_hunks': function("magit#state#get_hunks"), + \ 'add_file': function("magit#state#add_file"), + \ 'is_file_visible': function("magit#state#is_file_visible"), + \ 'update': function("magit#state#update"), \ 'dict': { 'staged': {}, 'unstaged': {}}, \ } From f0d0c9dcdd3a25e6444e381c82f9f777c6032d97 Mon Sep 17 00:00:00 2001 From: Jerome Reybert Date: Sat, 17 Oct 2015 23:20:14 +0200 Subject: [PATCH 11/46] autoload/magit/state.vim: complexifying diff structure --- autoload/magit/state.vim | 37 ++++++++++++++++++++++--------------- 1 file changed, 22 insertions(+), 15 deletions(-) diff --git a/autoload/magit/state.vim b/autoload/magit/state.vim index 204eeae..445a3b5 100644 --- a/autoload/magit/state.vim +++ b/autoload/magit/state.vim @@ -30,7 +30,7 @@ endfunction " return: List of diff header lines function! magit#state#get_header(mode, filename) dict let diff_dict_file = self.get_file(a:mode, a:filename, 0) - return diff_dict_file['diff'][0] + return diff_dict_file['diff']['header'] endfunction " magit#state#get_hunks: function accessor for hunks @@ -39,7 +39,7 @@ endfunction " return: List of List of hunks lines function! magit#state#get_hunks(mode, filename) dict let diff_dict_file = self.get_file(a:mode, a:filename, 0) - return diff_dict_file['diff'][1:-1] + return diff_dict_file['diff']['hunks'] endfunction " magit#state#add_file: method to add a file with all its @@ -58,7 +58,10 @@ function! magit#state#add_file(mode, status, filename) dict echoerr "diff command \"" . diff_cmd . "\" returned nothing" endif let diff_dict_file = self.get_file(a:mode, a:filename, 1) - let diff_dict_file['diff'] = [] + let diff_dict_file['diff'] = { + \ 'header': [], + \ 'hunks': [], + \} let diff_dict_file['exists'] = 1 let diff_dict_file['status'] = a:status let diff_dict_file['empty'] = 0 @@ -66,27 +69,31 @@ function! magit#state#add_file(mode, status, filename) dict let diff_dict_file['symlink'] = '' if ( a:status == '?' && getftype(a:filename) == 'link' ) let diff_dict_file['symlink'] = resolve(a:filename) - call add(diff_dict_file['diff'], ['no header']) - call add(diff_dict_file['diff'], ['New symbolic link file']) + call add(diff_dict_file['diff']['header'], 'no header') + call add(diff_dict_file['diff']['hunks'], ['New symbolic link file']) elseif ( a:status == '?' && getfsize(a:filename) == 0 ) let diff_dict_file['empty'] = 1 - call add(diff_dict_file['diff'], ['no header']) - call add(diff_dict_file['diff'], ['New empty file']) + call add(diff_dict_file['diff']['header'], 'no header') + call add(diff_dict_file['diff']['hunks'], ['New empty file']) elseif ( match(system("file --mime " . \ magit#utils#add_quotes(a:filename)), \ a:filename . ".*charset=binary") != -1 ) let diff_dict_file['binary'] = 1 - call add(diff_dict_file['diff'], ['no header']) - call add(diff_dict_file['diff'], ['Binary file']) + call add(diff_dict_file['diff']['header'], 'no header') + call add(diff_dict_file['diff']['hunks'], ['Binary file']) else - let index = 0 - call add(diff_dict_file['diff'], []) - for diff_line in diff_list + let line = 0 + while ( line < len(diff_list) && diff_list[line] !~ "^@.*" ) + call add(diff_dict_file['diff']['header'], diff_list[line]) + let line += 1 + endwhile + let hunk_index = 0 + for diff_line in diff_list[line : -1] if ( diff_line =~ "^@.*" ) - let index+=1 - call add(diff_dict_file['diff'], []) + call add(diff_dict_file['diff']['hunks'], []) + let hunk_index = len(diff_dict_file['diff']['hunks']) - 1 endif - call add(diff_dict_file['diff'][index], diff_line) + call add(diff_dict_file['diff']['hunks'][hunk_index], diff_line) endfor endif endfunction From f07071350c634570bc0c09e83bd7187314f41abf Mon Sep 17 00:00:00 2001 From: Jerome Reybert Date: Sun, 18 Oct 2015 01:07:15 +0200 Subject: [PATCH 12/46] autoload/magit/state.vim: complexify a bit more hunk structures --- autoload/magit/state.vim | 98 ++++++++++++++++++++++++++++------------ plugin/magit.vim | 15 +++--- 2 files changed, 77 insertions(+), 36 deletions(-) diff --git a/autoload/magit/state.vim b/autoload/magit/state.vim index 445a3b5..e2cf932 100644 --- a/autoload/magit/state.vim +++ b/autoload/magit/state.vim @@ -7,6 +7,32 @@ function! magit#state#get_files(mode) dict return self.dict[a:mode] endfunction +" s:hunk_template: template for hunk object (nested in s:diff_template) +" WARNING: this variable must be deepcopy()'ied +let s:hunk_template = { +\ 'header': '', +\ 'lines': [], +\ 'marks': [], +\} + +" s:diff_template: template for diff object (nested in s:file_template) +" WARNING: this variable must be deepcopy()'ied +let s:diff_template = { +\ 'header': [], +\ 'hunks': [s:hunk_template], +\} + +" s:file_template: template for file object +" WARNING: this variable must be deepcopy()'ied +let s:file_template = { +\ 'exists': 0, +\ 'status': '', +\ 'empty': 0, +\ 'binary': 0, +\ 'symlink': '', +\ 'diff': s:diff_template, +\} + " magit#state#get_file: function accessor for file " param[in] mode: can be staged or unstaged " param[in] filename: filename to access @@ -16,7 +42,7 @@ endfunction function! magit#state#get_file(mode, filename, create) dict let file_exists = has_key(self.dict[a:mode], a:filename) if ( file_exists == 0 && a:create == 1 ) - let self.dict[a:mode][a:filename] = {} + let self.dict[a:mode][a:filename] = deepcopy(s:file_template) let self.dict[a:mode][a:filename]['visible'] = 0 elseif ( file_exists == 0 && a:create == 0 ) throw 'file_doesnt_exists' @@ -33,7 +59,7 @@ function! magit#state#get_header(mode, filename) dict return diff_dict_file['diff']['header'] endfunction -" magit#state#get_hunks: function accessor for hunks +" magit#state#get_hunks: function accessor for hunks objects " param[in] mode: can be staged or unstaged " param[in] filename: hunks of filename to access " return: List of List of hunks lines @@ -42,6 +68,20 @@ function! magit#state#get_hunks(mode, filename) dict return diff_dict_file['diff']['hunks'] endfunction +" magit#state#get_hunks: function accessor for hunks lines +" param[in] mode: can be staged or unstaged +" param[in] filename: hunks of filename to access +" return: all hunks lines of a file, including hunk headers +function! magit#state#get_flat_hunks(mode, filename) dict + let hunks = self.get_hunks(a:mode, a:filename) + let lines = [] + for hunk in hunks + call add(lines, hunk.header) + call add(lines, hunk.lines) + endfor + return lines +endfunction + " magit#state#add_file: method to add a file with all its " properties (filename, exists, status, header and hunks) " param[in] mode: can be staged or unstaged @@ -58,42 +98,41 @@ function! magit#state#add_file(mode, status, filename) dict echoerr "diff command \"" . diff_cmd . "\" returned nothing" endif let diff_dict_file = self.get_file(a:mode, a:filename, 1) - let diff_dict_file['diff'] = { - \ 'header': [], - \ 'hunks': [], - \} let diff_dict_file['exists'] = 1 let diff_dict_file['status'] = a:status - let diff_dict_file['empty'] = 0 - let diff_dict_file['binary'] = 0 - let diff_dict_file['symlink'] = '' if ( a:status == '?' && getftype(a:filename) == 'link' ) let diff_dict_file['symlink'] = resolve(a:filename) call add(diff_dict_file['diff']['header'], 'no header') - call add(diff_dict_file['diff']['hunks'], ['New symbolic link file']) + let diff_dict_file['diff']['hunks'][0]['header'] = 'New symbolic link file' elseif ( a:status == '?' && getfsize(a:filename) == 0 ) let diff_dict_file['empty'] = 1 call add(diff_dict_file['diff']['header'], 'no header') - call add(diff_dict_file['diff']['hunks'], ['New empty file']) + let diff_dict_file['diff']['hunks'][0]['header'] = 'New empty file' elseif ( match(system("file --mime " . \ magit#utils#add_quotes(a:filename)), \ a:filename . ".*charset=binary") != -1 ) let diff_dict_file['binary'] = 1 call add(diff_dict_file['diff']['header'], 'no header') - call add(diff_dict_file['diff']['hunks'], ['Binary file']) + let diff_dict_file['diff']['hunks'][0]['header'] = 'Binary file' else let line = 0 + " match( while ( line < len(diff_list) && diff_list[line] !~ "^@.*" ) call add(diff_dict_file['diff']['header'], diff_list[line]) let line += 1 endwhile - let hunk_index = 0 - for diff_line in diff_list[line : -1] + + let hunk = diff_dict_file['diff']['hunks'][0] + let hunk['header'] = diff_list[line] + + for diff_line in diff_list[line+1 : -1] if ( diff_line =~ "^@.*" ) - call add(diff_dict_file['diff']['hunks'], []) - let hunk_index = len(diff_dict_file['diff']['hunks']) - 1 + let hunk = deepcopy(s:hunk_template) + call add(diff_dict_file['diff']['hunks'], hunk) + let hunk['header'] = diff_line + continue endif - call add(diff_dict_file['diff']['hunks'][hunk_index], diff_line) + call add(hunk['lines'], diff_line) endfor endif endfunction @@ -108,7 +147,7 @@ function! magit#state#update() dict for file in values(diff_dict_mode) let file['exists'] = 0 " always discard previous diff - unlet file['diff'] + let file['diff'] = deepcopy(s:diff_template) endfor endfor @@ -138,23 +177,24 @@ endfunction " dict: structure containing all diffs " It is formatted as follow -" { 'staged_or_unstaged': staged/unstaged -" [ -" { 'filename': -" { 'visible': bool, -" 'status' : g:magit_git_status_code, -" 'exists' : bool -" 'diff' : [ [header], [hunk0], [hunk1], ...] -" } -" }, -" ... -" ] +" { +" 'staged': { +" 'filename': s:file_template, +" 'filename': s:file_template, +" ... +" }, +" 'unstaged': { +" 'filename': s:file_template, +" 'filename': s:file_template, +" ... +" }, " } let magit#state#state = { \ 'get_file': function("magit#state#get_file"), \ 'get_files': function("magit#state#get_files"), \ 'get_header': function("magit#state#get_header"), \ 'get_hunks': function("magit#state#get_hunks"), + \ 'get_flat_hunks': function("magit#state#get_flat_hunks"), \ 'add_file': function("magit#state#add_file"), \ 'is_file_visible': function("magit#state#is_file_visible"), \ 'update': function("magit#state#update"), diff --git a/plugin/magit.vim b/plugin/magit.vim index 04d8102..1e520d1 100644 --- a/plugin/magit.vim +++ b/plugin/magit.vim @@ -18,7 +18,7 @@ execute 'source ' . resolve(expand(':p:h')) . '/../common/magit_common.vi " g:magit_unstaged_buffer_name: vim buffer name for vimagit let g:magit_unstaged_buffer_name = "magit-playground" -let s:state = copy(magit#state#state) +let s:state = deepcopy(magit#state#state) " s:set: helper function to set user definable variable " param[in] var: variable to set @@ -161,8 +161,9 @@ function! s:mg_get_staged_section(mode) echoerr "Error, " . filename . " should not exists" endif let hunks=s:state.get_hunks(a:mode, filename) - for diff_line in hunks - silent put =diff_line + for hunk in hunks + silent put =hunk.header + silent put =hunk.lines endfor put ='' endfor @@ -426,17 +427,17 @@ function! s:mg_create_diff_from_select(start_chunk_line, end_chunk_line) let filename=mg_get_filename() let hunks = s:state.get_hunks(section, filename) for hunk in hunks - if ( hunk[0] == getline(starthunk) ) + if ( hunk.header == getline(starthunk) ) let current_hunk = hunk break endif endfor let selection = [] let visual_selection = getline(a:start_chunk_line, a:end_chunk_line) - call add(selection, current_hunk[0]) + call add(selection, current_hunk.header) let current_line = starthunk + 1 - for hunk_line in current_hunk[1:] + for hunk_line in current_hunk.lines if ( current_line >= a:start_chunk_line && current_line <= a:end_chunk_line ) call add(selection, visual_selection[current_line-a:start_chunk_line]) elseif ( hunk_line =~ '^+.*' ) @@ -626,7 +627,7 @@ function! s:mg_select_closed_file() let filename = list[2] let section=mg_get_section() if ( s:state.is_file_visible(section, filename) == 0 ) - let selection = s:state.get_hunks(section, filename) + let selection = s:state.get_flat_hunks(section, filename) return selection endif endif From 8f725d1fdb324e2b381fa9d9bcdd89349f93af46 Mon Sep 17 00:00:00 2001 From: Jerome Reybert Date: Sun, 18 Oct 2015 01:14:30 +0200 Subject: [PATCH 13/46] *.vim: use short dict access (.key VS ['key']) --- autoload/magit/state.vim | 50 ++++++++++++++++---------------- plugin/magit.vim | 62 ++++++++++++++++++++-------------------- 2 files changed, 56 insertions(+), 56 deletions(-) diff --git a/autoload/magit/state.vim b/autoload/magit/state.vim index e2cf932..9c95a29 100644 --- a/autoload/magit/state.vim +++ b/autoload/magit/state.vim @@ -1,6 +1,6 @@ function! magit#state#is_file_visible(section, filename) dict return ( has_key(self.dict[a:section], a:filename) && - \ ( self.dict[a:section][a:filename]['visible'] == 1 ) ) + \ ( self.dict[a:section][a:filename].visible == 1 ) ) endfunction function! magit#state#get_files(mode) dict @@ -43,7 +43,7 @@ function! magit#state#get_file(mode, filename, create) dict let file_exists = has_key(self.dict[a:mode], a:filename) if ( file_exists == 0 && a:create == 1 ) let self.dict[a:mode][a:filename] = deepcopy(s:file_template) - let self.dict[a:mode][a:filename]['visible'] = 0 + let self.dict[a:mode][a:filename].visible = 0 elseif ( file_exists == 0 && a:create == 0 ) throw 'file_doesnt_exists' endif @@ -56,7 +56,7 @@ endfunction " return: List of diff header lines function! magit#state#get_header(mode, filename) dict let diff_dict_file = self.get_file(a:mode, a:filename, 0) - return diff_dict_file['diff']['header'] + return diff_dict_file.diff.header endfunction " magit#state#get_hunks: function accessor for hunks objects @@ -65,7 +65,7 @@ endfunction " return: List of List of hunks lines function! magit#state#get_hunks(mode, filename) dict let diff_dict_file = self.get_file(a:mode, a:filename, 0) - return diff_dict_file['diff']['hunks'] + return diff_dict_file.diff.hunks endfunction " magit#state#get_hunks: function accessor for hunks lines @@ -98,41 +98,41 @@ function! magit#state#add_file(mode, status, filename) dict echoerr "diff command \"" . diff_cmd . "\" returned nothing" endif let diff_dict_file = self.get_file(a:mode, a:filename, 1) - let diff_dict_file['exists'] = 1 - let diff_dict_file['status'] = a:status + let diff_dict_file.exists = 1 + let diff_dict_file.status = a:status if ( a:status == '?' && getftype(a:filename) == 'link' ) - let diff_dict_file['symlink'] = resolve(a:filename) - call add(diff_dict_file['diff']['header'], 'no header') - let diff_dict_file['diff']['hunks'][0]['header'] = 'New symbolic link file' + let diff_dict_file.symlink = resolve(a:filename) + call add(diff_dict_file.diff.header, 'no header') + let diff_dict_file.diff.hunks[0].header = 'New symbolic link file' elseif ( a:status == '?' && getfsize(a:filename) == 0 ) - let diff_dict_file['empty'] = 1 - call add(diff_dict_file['diff']['header'], 'no header') - let diff_dict_file['diff']['hunks'][0]['header'] = 'New empty file' + let diff_dict_file.empty = 1 + call add(diff_dict_file.diff.header, 'no header') + let diff_dict_file.diff.hunks[0].header = 'New empty file' elseif ( match(system("file --mime " . \ magit#utils#add_quotes(a:filename)), \ a:filename . ".*charset=binary") != -1 ) - let diff_dict_file['binary'] = 1 - call add(diff_dict_file['diff']['header'], 'no header') - let diff_dict_file['diff']['hunks'][0]['header'] = 'Binary file' + let diff_dict_file.binary = 1 + call add(diff_dict_file.diff.header, 'no header') + let diff_dict_file.diff.hunks[0].header = 'Binary file' else let line = 0 " match( while ( line < len(diff_list) && diff_list[line] !~ "^@.*" ) - call add(diff_dict_file['diff']['header'], diff_list[line]) + call add(diff_dict_file.diff.header, diff_list[line]) let line += 1 endwhile - let hunk = diff_dict_file['diff']['hunks'][0] - let hunk['header'] = diff_list[line] + let hunk = diff_dict_file.diff.hunks[0] + let hunk.header = diff_list[line] for diff_line in diff_list[line+1 : -1] if ( diff_line =~ "^@.*" ) let hunk = deepcopy(s:hunk_template) - call add(diff_dict_file['diff']['hunks'], hunk) - let hunk['header'] = diff_line + call add(diff_dict_file.diff.hunks, hunk) + let hunk.header = diff_line continue endif - call add(hunk['lines'], diff_line) + call add(hunk.lines, diff_line) endfor endif endfunction @@ -145,9 +145,9 @@ endfunction function! magit#state#update() dict for diff_dict_mode in values(self.dict) for file in values(diff_dict_mode) - let file['exists'] = 0 + let file.exists = 0 " always discard previous diff - let file['diff'] = deepcopy(s:diff_template) + let file.diff = deepcopy(s:diff_template) endfor endfor @@ -161,14 +161,14 @@ function! magit#state#update() dict if ( status == ' ' || ( ( mode == 'staged' ) && status == '?' ) ) continue endif - call self.add_file(mode, status, file_status['filename']) + call self.add_file(mode, status, file_status.filename) endfor endfor " remove files that have changed their mode or been committed/deleted/discarded... for diff_dict_mode in values(self.dict) for [key, file] in items(diff_dict_mode) - if ( file['exists'] == 0 ) + if ( file.exists == 0 ) unlet diff_dict_mode[key] endif endfor diff --git a/plugin/magit.vim b/plugin/magit.vim index 1e520d1..6b5b32c 100644 --- a/plugin/magit.vim +++ b/plugin/magit.vim @@ -122,8 +122,8 @@ endfunction " protected functions like magit#update_buffer function! s:mg_get_info() silent put ='' - silent put =g:magit_sections['info'] - silent put =magit#utils#underline(g:magit_sections['info']) + silent put =g:magit_sections.info + silent put =magit#utils#underline(g:magit_sections.info) silent put ='' let branch=magit#utils#system("git rev-parse --abbrev-ref HEAD") let commit=magit#utils#system("git show -s --oneline") @@ -146,18 +146,18 @@ function! s:mg_get_staged_section(mode) put ='' for [ filename, file_props ] in items(s:state.get_files(a:mode)) - if ( file_props['empty'] == 1 ) - put =g:magit_git_status_code['E'] . ': ' . filename - elseif ( file_props['symlink'] != '' ) - put =g:magit_git_status_code['L'] . ': ' . filename . ' -> ' . file_props['symlink'] + if ( file_props.empty == 1 ) + put =g:magit_git_status_code.E . ': ' . filename + elseif ( file_props.symlink != '' ) + put =g:magit_git_status_code.L . ': ' . filename . ' -> ' . file_props.symlink else - put =g:magit_git_status_code[file_props['status']] . ': ' . filename + put =g:magit_git_status_code[file_props.status] . ': ' . filename endif - if ( file_props['visible'] == 0 ) + if ( file_props.visible == 0 ) put ='' continue endif - if ( file_props['exists'] == 0 ) + if ( file_props.exists == 0 ) echoerr "Error, " . filename . " should not exists" endif let hunks=s:state.get_hunks(a:mode, filename) @@ -180,8 +180,8 @@ function! s:mg_get_stashes() if (!empty(stash_list)) silent put ='' - silent put =g:magit_sections['stash'] - silent put =magit#utils#underline(g:magit_sections['stash']) + silent put =g:magit_sections.stash + silent put =magit#utils#underline(g:magit_sections.stash) silent put ='' for stash in stash_list @@ -215,10 +215,10 @@ function! s:mg_get_commit_section() let commit_mode_str="amend" endif silent put ='' - silent put =g:magit_sections['commit_start'] + silent put =g:magit_sections.commit_start silent put ='Commit mode: '.commit_mode_str call mg_section_help('commit') - silent put =magit#utils#underline(g:magit_sections['commit_start']) + silent put =magit#utils#underline(g:magit_sections.commit_start) silent put ='' let git_dir=magit#utils#git_dir() @@ -233,7 +233,7 @@ function! s:mg_get_commit_section() let commit_msg=magit#utils#join_list(filter(readfile(git_dir . 'COMMIT_EDITMSG'), 'v:val !~ "^' . comment_char . '"')) put =commit_msg endif - put =g:magit_sections['commit_end'] + put =g:magit_sections.commit_end endfunction " s:mg_comment_char: this function gets the commentChar from git config @@ -312,8 +312,8 @@ function! s:mg_git_commit(mode) abort if ( a:mode == 'CF' ) silent let git_result=magit#utils#system("git commit --amend -C HEAD") else - let commit_section_pat_start='^'.g:magit_sections['commit_start'].'$' - let commit_section_pat_end='^'.g:magit_sections['commit_end'].'$' + let commit_section_pat_start='^'.g:magit_sections.commit_start.'$' + let commit_section_pat_end='^'.g:magit_sections.commit_end.'$' let commit_jump_line = 3 + mg_get_inline_help_line_nb('commit') let [start, end] = mg_search_block( \ [commit_section_pat_start, commit_jump_line], @@ -505,9 +505,9 @@ function! magit#open_close_folding(...) " if first param is set, force visible to this value " else, toggle value let file = s:state.get_file(section, filename, 0) - let file['visible'] = + let file.visible = \ ( a:0 == 1 ) ? a:1 : - \ ( file['visible'] == 0 ) ? 1 : 0 + \ ( file.visible == 0 ) ? 1 : 0 call magit#update_buffer() endfunction @@ -544,7 +544,7 @@ function! magit#update_buffer() call winrestview(l:winview) if ( s:magit_commit_mode != '' ) - let commit_section_pat_start='^'.g:magit_sections['commit_start'].'$' + let commit_section_pat_start='^'.g:magit_sections.commit_start.'$' silent! let section_line=search(commit_section_pat_start, "w") silent! call cursor(section_line+3+mg_get_inline_help_line_nb('commit'), 0) endif @@ -648,18 +648,18 @@ function! magit#stage_block(selection, discard) abort let file = s:state.get_file(section, filename, 0) if ( a:discard == 0 ) if ( section == 'unstaged' ) - if ( file['empty'] == 1 || - \ file['symlink'] != '' || - \ file['binary'] == 1 ) + if ( file.empty == 1 || + \ file.symlink != '' || + \ file.binary == 1 ) call magit#utils#system('git add ' . \ magit#utils#add_quotes(filename)) else call mg_git_apply(header, a:selection) endif elseif ( section == 'staged' ) - if ( file['empty'] == 1 || - \ file['symlink'] != '' || - \ file['binary'] == 1 ) + if ( file.empty == 1 || + \ file.symlink != '' || + \ file.binary == 1 ) call magit#utils#system('git reset ' . \ magit#utils#add_quotes(filename)) else @@ -667,21 +667,21 @@ function! magit#stage_block(selection, discard) abort endif else echoerr "Must be in \"" . - \ g:magit_sections['staged'] . "\" or \"" . - \ g:magit_sections['unstaged'] . "\" section" + \ g:magit_sections.staged . "\" or \"" . + \ g:magit_sections.unstaged . "\" section" endif else if ( section == 'unstaged' ) - if ( file['empty'] == 1 || - \ file['symlink'] != '' || - \ file['binary'] == 1 ) + if ( file.empty == 1 || + \ file.symlink != '' || + \ file.binary == 1 ) call delete(filename) else call mg_git_unapply(header, a:selection, 'unstaged') endif else echoerr "Must be in \"" . - \ g:magit_sections['unstaged'] . "\" section" + \ g:magit_sections.unstaged . "\" section" endif endif From 5397c58abf90aca08b89e12279e7daba14ac82eb Mon Sep 17 00:00:00 2001 From: Jerome Reybert Date: Sun, 18 Oct 2015 22:45:47 +0200 Subject: [PATCH 14/46] plugin/magit.vim: rename variable containing magit buffer name --- plugin/magit.vim | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/plugin/magit.vim b/plugin/magit.vim index 6b5b32c..70acdad 100644 --- a/plugin/magit.vim +++ b/plugin/magit.vim @@ -15,8 +15,8 @@ let g:loaded_magit = 1 " syntax files execute 'source ' . resolve(expand(':p:h')) . '/../common/magit_common.vim' -" g:magit_unstaged_buffer_name: vim buffer name for vimagit -let g:magit_unstaged_buffer_name = "magit-playground" +" g:magit_buffer_name: vim buffer name for vimagit +let g:magit_buffer_name = "magit-playground" let s:state = deepcopy(magit#state#state) @@ -519,8 +519,8 @@ endfunction " 4. fills with unstage stuff " 5. restore window state function! magit#update_buffer() - if ( @% != g:magit_unstaged_buffer_name ) - echoerr "Not in magit buffer " . g:magit_unstaged_buffer_name . " but in " . @% + if ( @% != g:magit_buffer_name ) + echoerr "Not in magit buffer " . g:magit_buffer_name . " but in " . @% return endif " FIXME: find a way to save folding state. According to help, this won't @@ -587,8 +587,8 @@ function! magit#show_magit(display) setlocal filetype=magit "setlocal readonly - silent! execute "bdelete " . g:magit_unstaged_buffer_name - execute "file " . g:magit_unstaged_buffer_name + silent! execute "bdelete " . g:magit_buffer_name + execute "file " . g:magit_buffer_name execute "nnoremap " . g:magit_stage_file_mapping . " :call magit#stage_file()" execute "nnoremap " . g:magit_stage_hunk_mapping . " :call magit#stage_hunk(0)" From 37f32c0f78ed3187689fcb29c391d60c4141db4f Mon Sep 17 00:00:00 2001 From: Jerome Reybert Date: Sun, 18 Oct 2015 22:47:53 +0200 Subject: [PATCH 15/46] plugin/magit.vim: rework s:mg_create_diff_from_select function --- plugin/magit.vim | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/plugin/magit.vim b/plugin/magit.vim index 70acdad..78d99ba 100644 --- a/plugin/magit.vim +++ b/plugin/magit.vim @@ -417,12 +417,14 @@ endfunction " s:mg_create_diff_from_select: craft the diff to apply from a selection " in a chunk " remarks: it works with full lines, and can not span over multiple chunks -" param[in] start_chunk_line,end_chunk_line: limits of the selection +" param[in] start_select_line,end_select_line: limits of the selection " return: List containing the diff to apply, including the chunk header (must " be applied with git apply --recount) -function! s:mg_create_diff_from_select(start_chunk_line, end_chunk_line) - let lines=getline(a:start_chunk_line, a:end_chunk_line) +function! s:mg_create_diff_from_select(start_select_line, end_select_line) let [starthunk,endhunk] = mg_select_hunk_block() + if ( a:start_select_line < starthunk || a:end_select_line > endhunk ) + throw 'out of hunk selection' + endif let section=mg_get_section() let filename=mg_get_filename() let hunks = s:state.get_hunks(section, filename) @@ -433,13 +435,13 @@ function! s:mg_create_diff_from_select(start_chunk_line, end_chunk_line) endif endfor let selection = [] - let visual_selection = getline(a:start_chunk_line, a:end_chunk_line) + let visual_selection = getline(a:start_select_line, a:end_select_line) call add(selection, current_hunk.header) let current_line = starthunk + 1 for hunk_line in current_hunk.lines - if ( current_line >= a:start_chunk_line && current_line <= a:end_chunk_line ) - call add(selection, visual_selection[current_line-a:start_chunk_line]) + if ( current_line >= a:start_select_line && current_line <= a:end_select_line ) + call add(selection, visual_selection[current_line-a:start_select_line]) elseif ( hunk_line =~ '^+.*' ) " just ignore these lines elseif ( hunk_line =~ '^-.*' ) From b4d38eb648ca5407c477a09fe2af9d0c06c212c6 Mon Sep 17 00:00:00 2001 From: Jerome Reybert Date: Sun, 18 Oct 2015 23:09:24 +0200 Subject: [PATCH 16/46] autoload/magit/sign.vim: add mark line functionality (only visual for this commit) --- autoload/magit/sign.vim | 88 ++++++++++++++++++++++++++++++++++++++++ autoload/magit/utils.vim | 13 ++++++ plugin/magit.vim | 33 +++++++++++++++ 3 files changed, 134 insertions(+) create mode 100644 autoload/magit/sign.vim diff --git a/autoload/magit/sign.vim b/autoload/magit/sign.vim new file mode 100644 index 0000000..abf488d --- /dev/null +++ b/autoload/magit/sign.vim @@ -0,0 +1,88 @@ +" Got lot of stuf from vim-gitgutter +" https://github.com/airblade/vim-gitgutter + +" Vim doesn't namespace sign ids so every plugin shares the same +" namespace. Sign ids are simply integers so to avoid clashes with other +" signs we guess at a clear run. +" +" Note also we currently never reset s:next_sign_id. +let s:first_sign_id = 42000 +let s:next_sign_id = s:first_sign_id +let s:dummy_sign_id = s:first_sign_id - 1 +" Remove-all-signs optimisation requires Vim 7.3.596+. +let s:supports_star = v:version > 703 || (v:version == 703 && has("patch596")) + +let s:bufnr = bufnr(g:magit_buffer_name) + +" magit#sign#remove_signs: unplace a list of signs +" param[in] sign_ids: list of signs ids +function! magit#sign#remove_signs(sign_ids) + let bufnr = magit#utils#bufnr() + for id in a:sign_ids + execute "sign unplace" id + endfor +endfunction + +" s:get_next_sign_id: helper function to increment sign ids +function! s:get_next_sign_id() + let next_id = s:next_sign_id + let s:next_sign_id += 1 + return next_id +endfunction + +" magit#sign#find_signs: this function returns signs matching a pattern in a +" range of lines +" param[in] pattern: regex pattern to match +" param[in] startline,endline: range of lines +" FIXME: find since which version "sign place" is sorted +function! magit#sign#find_signs(pattern, startline, endline) + let bufnr = magit#utils#bufnr() + " : {'id': , 'name': } + let found_signs = {} + + redir => signs + silent execute "sign place buffer=" . bufnr + redir END + + for sign_line in filter(split(signs, '\n'), 'v:val =~# "="') + " Typical sign line: line=88 id=1234 name=GitGutterLineAdded + " We assume splitting is faster than a regexp. + let components = split(sign_line) + let name = split(components[2], '=')[1] + let line_number = str2nr(split(components[0], '=')[1]) + if ( name =~# a:pattern && + \ line_number >= a:startline && + \ line_number <= a:endline ) + let id = str2nr(split(components[1], '=')[1]) + let found_signs[line_number] = {'id': id, 'name': name} + endif + endfor + return found_signs +endfunction + +" s:magit_mark_sign: string of the sign for lines to be staged +let s:magit_mark_sign='MagitMark' + +" magit#sign#init: initializer function for signs +function! magit#sign#init() + execute "sign define " . s:magit_mark_sign . " text=S> linehl=Visual" +endfunction + +" magit#sign#toggle_signs: toggle marks for range of lines +" marked lines are unmarked, non marked are marked +" param[in] startline,endline: range of lines +function! magit#sign#toggle_signs(startline, endline) + let bufnr = magit#utils#bufnr() + let current_signs = magit#sign#find_signs(s:magit_mark_sign, a:startline, a:endline) + let line = a:startline + while ( line <= a:endline ) + if ( has_key(current_signs, line) == 0 ) + execute ":sign place " . get_next_sign_id() . + \ " line=" . line . " name=" . s:magit_mark_sign . + \ " buffer=" . bufnr + else + execute ":sign unplace " . current_signs[line].id + endif + let line += 1 + endwhile +endfunction diff --git a/autoload/magit/utils.vim b/autoload/magit/utils.vim index 6702944..6b8ca64 100644 --- a/autoload/magit/utils.vim +++ b/autoload/magit/utils.vim @@ -162,3 +162,16 @@ function! magit#utils#append_file(file, lines) call writefile(fcontents+a:lines, a:file, 'b') endfunction +" s:bufnr: local variable to store current magit buffer id +let s:bufnr = 0 +" magit#utils#setbufnr: function to set current magit buffer id +" param[in] bufnr: current magit buffer id +function! magit#utils#setbufnr(bufnr) + let s:bufnr = a:bufnr +endfunction + +" magit#utils#bufnr: function to get current magit buffer id +" return: current magit buffer id +function! magit#utils#bufnr() + return s:bufnr +endfunction diff --git a/plugin/magit.vim b/plugin/magit.vim index 78d99ba..99068ab 100644 --- a/plugin/magit.vim +++ b/plugin/magit.vim @@ -41,6 +41,7 @@ call s:set('g:magit_show_magit_mapping', 'M' ) call s:set('g:magit_stage_file_mapping', 'F' ) call s:set('g:magit_stage_hunk_mapping', 'S' ) call s:set('g:magit_stage_line_mapping', 'L' ) +call s:set('g:magit_mark_line_mapping', 'M' ) call s:set('g:magit_discard_hunk_mapping', 'DDD' ) call s:set('g:magit_commit_mapping_command', 'w' ) call s:set('g:magit_commit_mapping', 'CC' ) @@ -456,6 +457,19 @@ function! s:mg_create_diff_from_select(start_select_line, end_select_line) return selection endfunction +" s:mg_mark_lines_in_hunk: this function toggle marks for selected lines in a +" hunk. +" if a hunk contains marked lines, only these lines will be (un)staged on next +" (un)stage command +" param[in] start_select_line,end_select_line: limits of the selection +function! s:mg_mark_lines_in_hunk(start_select_line, end_select_line) + let [starthunk,endhunk] = mg_select_hunk_block() + if ( a:start_select_line < starthunk || a:end_select_line > endhunk ) + throw 'out of hunk selection' + endif + return magit#sign#toggle_signs(a:start_select_line, a:end_select_line) +endfunction + " s:mg_get_section: helper function to get the current section, according to " cursor position " return: section id, empty string if no section found @@ -476,6 +490,13 @@ function! s:mg_get_filename() return substitute(getline(search(g:magit_file_re, "cbnW")), g:magit_file_re, '\2', '') endfunction +" s:mg_get_hunkheader: helper function to get the current hunk header, +" according to cursor position +" return: hunk header +function! s:mg_get_hunkheader() + return getline(search(g:magit_hunk_re, "cbnW")) +endfunction + " }}} " {{{ User functions and commands @@ -592,6 +613,9 @@ function! magit#show_magit(display) silent! execute "bdelete " . g:magit_buffer_name execute "file " . g:magit_buffer_name + call magit#utils#setbufnr(bufnr(g:magit_buffer_name)) + call magit#sign#init() + execute "nnoremap " . g:magit_stage_file_mapping . " :call magit#stage_file()" execute "nnoremap " . g:magit_stage_hunk_mapping . " :call magit#stage_hunk(0)" execute "nnoremap " . g:magit_discard_hunk_mapping . " :call magit#stage_hunk(1)" @@ -607,6 +631,9 @@ function! magit#show_magit(display) execute "nnoremap " . g:magit_stage_line_mapping . " :call magit#stage_vselect()" execute "xnoremap " . g:magit_stage_hunk_mapping . " :call magit#stage_vselect()" + execute "nnoremap " . g:magit_mark_line_mapping . " :call magit#mark_vselect()" + execute "xnoremap " . g:magit_mark_line_mapping . " :call magit#mark_vselect()" + for mapping in g:magit_folding_toggle_mapping " trick to pass '' in a mapping command without being interpreted let func_arg = ( mapping ==? "" ) ? '+' : mapping @@ -739,6 +766,12 @@ function! magit#stage_vselect() range return magit#stage_block(selection, 0) endfunction +" magit#mark_vselect: wrapper function to mark selected lines (see +" mg_mark_lines_in_hunk) +function! magit#mark_vselect() range + return mg_mark_lines_in_hunk(a:firstline, a:lastline) +endfunction + " magit#ignore_file: this function add the file under cursor to .gitignore " FIXME: git diff adds some strange characters to end of line function! magit#ignore_file() abort From 046512137e766a263f624e6a288bb682da5cd144 Mon Sep 17 00:00:00 2001 From: Jerome Reybert Date: Sun, 18 Oct 2015 23:59:00 +0200 Subject: [PATCH 17/46] plugin/magit.vim: mark lines! when some lines are marked in a hunk, staging this hunk witll only stage marked lines --- autoload/magit/sign.vim | 6 ++++++ plugin/magit.vim | 29 +++++++++++++++++++++-------- 2 files changed, 27 insertions(+), 8 deletions(-) diff --git a/autoload/magit/sign.vim b/autoload/magit/sign.vim index abf488d..fd44abd 100644 --- a/autoload/magit/sign.vim +++ b/autoload/magit/sign.vim @@ -60,6 +60,12 @@ function! magit#sign#find_signs(pattern, startline, endline) return found_signs endfunction +" magit#sign#find_stage_signs: helper function to get marked lines for stage +" param[in] startline,endline: range of lines +function! magit#sign#find_stage_signs(startline, endline) + return magit#sign#find_signs(s:magit_mark_sign, a:startline, a:endline) +endfunction + " s:magit_mark_sign: string of the sign for lines to be staged let s:magit_mark_sign='MagitMark' diff --git a/plugin/magit.vim b/plugin/magit.vim index 99068ab..391c94c 100644 --- a/plugin/magit.vim +++ b/plugin/magit.vim @@ -418,12 +418,14 @@ endfunction " s:mg_create_diff_from_select: craft the diff to apply from a selection " in a chunk " remarks: it works with full lines, and can not span over multiple chunks -" param[in] start_select_line,end_select_line: limits of the selection +" param[in] select_lines: List containing all selected line numbers " return: List containing the diff to apply, including the chunk header (must " be applied with git apply --recount) -function! s:mg_create_diff_from_select(start_select_line, end_select_line) +function! s:mg_create_diff_from_select(select_lines) + let start_select_line = a:select_lines[0] + let end_select_line = a:select_lines[-1] let [starthunk,endhunk] = mg_select_hunk_block() - if ( a:start_select_line < starthunk || a:end_select_line > endhunk ) + if ( start_select_line < starthunk || end_select_line > endhunk ) throw 'out of hunk selection' endif let section=mg_get_section() @@ -436,13 +438,12 @@ function! s:mg_create_diff_from_select(start_select_line, end_select_line) endif endfor let selection = [] - let visual_selection = getline(a:start_select_line, a:end_select_line) call add(selection, current_hunk.header) let current_line = starthunk + 1 for hunk_line in current_hunk.lines - if ( current_line >= a:start_select_line && current_line <= a:end_select_line ) - call add(selection, visual_selection[current_line-a:start_select_line]) + if ( index(a:select_lines, current_line) != -1 ) + call add(selection, getline(current_line)) elseif ( hunk_line =~ '^+.*' ) " just ignore these lines elseif ( hunk_line =~ '^-.*' ) @@ -749,7 +750,13 @@ function! magit#stage_hunk(discard) catch 'out_of_block' let [start,end] = mg_select_file_block() endtry - let selection = getline(start, end) + let marked_lines = magit#sign#find_stage_signs(start, end) + if ( empty(marked_lines) ) + let selection = getline(start, end) + else + let selection = mg_create_diff_from_select( + \map(keys(marked_lines), 'str2nr(v:val)')) + endif endtry return magit#stage_block(selection, a:discard) endfunction @@ -762,7 +769,13 @@ endfunction " return: no function! magit#stage_vselect() range " func-range a:firstline a:lastline seems to work at least from vim 7.2 - let selection = mg_create_diff_from_select(a:firstline, a:lastline) + let lines = [] + let curline = a:firstline + while ( curline <= a:lastline ) + call add(lines, curline) + let curline += 1 + endwhile + let selection = mg_create_diff_from_select(lines) return magit#stage_block(selection, 0) endfunction From fb8c66a3dc4804044b41eb1a8b5c53dc2f73650e Mon Sep 17 00:00:00 2001 From: Jerome Reybert Date: Mon, 19 Oct 2015 00:32:24 +0200 Subject: [PATCH 18/46] autoload/magit/state.vim: cd top dir when looking for file state --- autoload/magit/state.vim | 100 +++++++++++++++++++++------------------ autoload/magit/utils.vim | 4 ++ 2 files changed, 57 insertions(+), 47 deletions(-) diff --git a/autoload/magit/state.vim b/autoload/magit/state.vim index 9c95a29..1163014 100644 --- a/autoload/magit/state.vim +++ b/autoload/magit/state.vim @@ -88,53 +88,59 @@ endfunction " param[in] status: one character status code of the file (AMDRCU?) " param[in] filename: filename function! magit#state#add_file(mode, status, filename) dict - let dev_null = ( a:status == '?' ) ? " /dev/null " : " " - let staged_flag = ( a:mode == 'staged' ) ? " --staged " : " " - let diff_cmd="git diff --no-ext-diff " . staged_flag . - \ "--no-color --patch -- " . dev_null . " " - \ . magit#utils#add_quotes(a:filename) - let diff_list=magit#utils#systemlist(diff_cmd) - if ( empty(diff_list) ) - echoerr "diff command \"" . diff_cmd . "\" returned nothing" - endif - let diff_dict_file = self.get_file(a:mode, a:filename, 1) - let diff_dict_file.exists = 1 - let diff_dict_file.status = a:status - if ( a:status == '?' && getftype(a:filename) == 'link' ) - let diff_dict_file.symlink = resolve(a:filename) - call add(diff_dict_file.diff.header, 'no header') - let diff_dict_file.diff.hunks[0].header = 'New symbolic link file' - elseif ( a:status == '?' && getfsize(a:filename) == 0 ) - let diff_dict_file.empty = 1 - call add(diff_dict_file.diff.header, 'no header') - let diff_dict_file.diff.hunks[0].header = 'New empty file' - elseif ( match(system("file --mime " . - \ magit#utils#add_quotes(a:filename)), - \ a:filename . ".*charset=binary") != -1 ) - let diff_dict_file.binary = 1 - call add(diff_dict_file.diff.header, 'no header') - let diff_dict_file.diff.hunks[0].header = 'Binary file' - else - let line = 0 - " match( - while ( line < len(diff_list) && diff_list[line] !~ "^@.*" ) - call add(diff_dict_file.diff.header, diff_list[line]) - let line += 1 - endwhile - - let hunk = diff_dict_file.diff.hunks[0] - let hunk.header = diff_list[line] - - for diff_line in diff_list[line+1 : -1] - if ( diff_line =~ "^@.*" ) - let hunk = deepcopy(s:hunk_template) - call add(diff_dict_file.diff.hunks, hunk) - let hunk.header = diff_line - continue - endif - call add(hunk.lines, diff_line) - endfor - endif + let dir = getcwd() + try + call magit#utils#lcd(magit#utils#top_dir()) + let dev_null = ( a:status == '?' ) ? " /dev/null " : " " + let staged_flag = ( a:mode == 'staged' ) ? " --staged " : " " + let diff_cmd="git diff --no-ext-diff " . staged_flag . + \ "--no-color --patch -- " . dev_null . " " + \ . magit#utils#add_quotes(a:filename) + let diff_list=magit#utils#systemlist(diff_cmd) + if ( empty(diff_list) ) + echoerr "diff command \"" . diff_cmd . "\" returned nothing" + endif + let diff_dict_file = self.get_file(a:mode, a:filename, 1) + let diff_dict_file.exists = 1 + let diff_dict_file.status = a:status + if ( a:status == '?' && getftype(a:filename) == 'link' ) + let diff_dict_file.symlink = resolve(a:filename) + call add(diff_dict_file.diff.header, 'no header') + let diff_dict_file.diff.hunks[0].header = 'New symbolic link file' + elseif ( a:status == '?' && getfsize(a:filename) == 0 ) + let diff_dict_file.empty = 1 + call add(diff_dict_file.diff.header, 'no header') + let diff_dict_file.diff.hunks[0].header = 'New empty file' + elseif ( match(system("file --mime " . + \ magit#utils#add_quotes(a:filename)), + \ a:filename . ".*charset=binary") != -1 ) + let diff_dict_file.binary = 1 + call add(diff_dict_file.diff.header, 'no header') + let diff_dict_file.diff.hunks[0].header = 'Binary file' + else + let line = 0 + " match( + while ( line < len(diff_list) && diff_list[line] !~ "^@.*" ) + call add(diff_dict_file.diff.header, diff_list[line]) + let line += 1 + endwhile + + let hunk = diff_dict_file.diff.hunks[0] + let hunk.header = diff_list[line] + + for diff_line in diff_list[line+1 : -1] + if ( diff_line =~ "^@.*" ) + let hunk = deepcopy(s:hunk_template) + call add(diff_dict_file.diff.hunks, hunk) + let hunk.header = diff_line + continue + endif + call add(hunk.lines, diff_line) + endfor + endif + finally + call magit#utils#lcd(dir) + endtry endfunction " magit#state#update: update self.dict diff --git a/autoload/magit/utils.vim b/autoload/magit/utils.vim index 6b8ca64..1cc472b 100644 --- a/autoload/magit/utils.vim +++ b/autoload/magit/utils.vim @@ -36,6 +36,10 @@ endfunction " s:magit_cd_cmd: plugin variable to choose lcd/cd command, 'lcd' if exists, " 'cd' otherwise let s:magit_cd_cmd = exists('*haslocaldir') && haslocaldir() ? 'lcd ' : 'cd ' +" magit#utils#lcd: helper function to lcd. use cd if lcd doesn't exists +function! magit#utils#lcd(dir) + execute s:magit_cd_cmd . a:dir +endfunction " magit#utils#system: wrapper for system, which only takes String as input in vim, " although it can take String or List input in neovim. From aa9c0649007b13c53315776ef60b872afadb34af Mon Sep 17 00:00:00 2001 From: Jerome Reybert Date: Mon, 19 Oct 2015 13:57:20 +0200 Subject: [PATCH 19/46] test/addSelect: add test for stage by select, line and marks --- test/addSelect.vader | 59 +++++++++++++++++++ ...dSelect_books_models_py_1_hunk_diff.expect | 13 ++++ ...lect_books_models_py_2_vselect_diff.expect | 24 ++++++++ ...Select_books_models_py_3_lines_diff.expect | 30 ++++++++++ ...Select_books_models_py_4_marks_diff.expect | 38 ++++++++++++ test/test.config | 1 + test/utils.vim | 7 ++- 7 files changed, 171 insertions(+), 1 deletion(-) create mode 100644 test/addSelect.vader create mode 100644 test/addSelect/addSelect_books_models_py_1_hunk_diff.expect create mode 100644 test/addSelect/addSelect_books_models_py_2_vselect_diff.expect create mode 100644 test/addSelect/addSelect_books_models_py_3_lines_diff.expect create mode 100644 test/addSelect/addSelect_books_models_py_4_marks_diff.expect diff --git a/test/addSelect.vader b/test/addSelect.vader new file mode 100644 index 0000000..4a2e9e4 --- /dev/null +++ b/test/addSelect.vader @@ -0,0 +1,59 @@ +Include: setup.inc + +Execute (Stage untracked file with magit#stage_hunk on start hunk (this hunk will stay staged all test)): + call Cd_test_sub() + Magit + call Search_file('unstaged') + call magit#open_close_folding(1) + call Search_pattern("^@@ ") + call Cursor_position() + call magit#stage_hunk(0) + call Cd_test() + let diff=Git_diff('staged', Get_filename()) + call Expect_diff(g:test_script_dir . 'addSelect/addSelect_' . Get_safe_filename() . '_1_hunk_diff.expect', diff) + +Execute (Stage selection): + call Cd_test_sub() + Magit + call Search_file('unstaged') + call magit#open_close_folding(1) + %foldopen! + call Search_pattern("^+\t\tif product.cover_url is not '':$") + execute "normal! v4j:call magit#stage_vselect()\" + call Cd_test() + let diff=Git_diff('staged', Get_filename()) + call Expect_diff(g:test_script_dir . 'addSelect/addSelect_' . Get_safe_filename() . '_2_vselect_diff.expect', diff) + +Execute (Stage lines): + call Cd_test_sub() + Magit + call Search_file('unstaged') + call magit#open_close_folding(1) + %foldopen! + call Search_pattern("^-\teisbn = models.CharField(max_length=13, blank=True)$") + call magit#stage_vselect() + %foldopen! + call Search_pattern("^+\teisbn = models.CharField(max_length=13, blank=True, null=True)$") + call magit#stage_vselect() + call Cd_test() + let diff=Git_diff('staged', Get_filename()) + call Expect_diff(g:test_script_dir . 'addSelect/addSelect_' . Get_safe_filename() . '_3_lines_diff.expect', diff) + +Execute (Stage marks): + call Cd_test_sub() + Magit + call Search_file('unstaged') + call magit#open_close_folding(1) + %foldopen! + call Search_pattern("^+def upload_path(book, filename):$") + execute "normal! v3j:call magit#mark_vselect()\" + call Search_pattern("^-\tedition = models.CharField(max_length=200, blank=True)$") + call magit#mark_vselect() + call Search_pattern("+\tedition = models.CharField(max_length=200, blank=True, null=True)$") + call magit#mark_vselect() + call magit#stage_hunk(0) + call Cd_test() + let diff=Git_diff('staged', Get_filename()) + call Expect_diff(g:test_script_dir . 'addSelect/addSelect_' . Get_safe_filename() . '_4_marks_diff.expect', diff) + +Include: cleanup.inc diff --git a/test/addSelect/addSelect_books_models_py_1_hunk_diff.expect b/test/addSelect/addSelect_books_models_py_1_hunk_diff.expect new file mode 100644 index 0000000..4d9138a --- /dev/null +++ b/test/addSelect/addSelect_books_models_py_1_hunk_diff.expect @@ -0,0 +1,13 @@ +diff --git books/models.py books/models.py +--- books/models.py ++++ books/models.py +@@ -1,5 +1,9 @@ + from django.db import models + ++import urllib2 as urllib ++from PIL import Image ++import io ++ + import isbn_search + + class User(models.Model): diff --git a/test/addSelect/addSelect_books_models_py_2_vselect_diff.expect b/test/addSelect/addSelect_books_models_py_2_vselect_diff.expect new file mode 100644 index 0000000..313e368 --- /dev/null +++ b/test/addSelect/addSelect_books_models_py_2_vselect_diff.expect @@ -0,0 +1,24 @@ +diff --git books/models.py books/models.py +--- books/models.py ++++ books/models.py +@@ -1,5 +1,9 @@ + from django.db import models + ++import urllib2 as urllib ++from PIL import Image ++import io ++ + import isbn_search + + class User(models.Model): +@@ -22,6 +26,10 @@ class BookManager(models.Manager): + title = product.title, + edition = product.edition + ) ++ if product.cover_url is not '': ++ cover_img_bin = urllib.urlopen(product.cover_url) ++ cover_img_file = Image.open(io.BytesIO(cover_img_bin.read())) ++ book.cover_img.save("cover.jpg", cover_img_file) + return book + + class Book(models.Model): diff --git a/test/addSelect/addSelect_books_models_py_3_lines_diff.expect b/test/addSelect/addSelect_books_models_py_3_lines_diff.expect new file mode 100644 index 0000000..0698fb6 --- /dev/null +++ b/test/addSelect/addSelect_books_models_py_3_lines_diff.expect @@ -0,0 +1,30 @@ +diff --git books/models.py books/models.py +--- books/models.py ++++ books/models.py +@@ -1,5 +1,9 @@ + from django.db import models + ++import urllib2 as urllib ++from PIL import Image ++import io ++ + import isbn_search + + class User(models.Model): +@@ -22,11 +26,15 @@ class BookManager(models.Manager): + title = product.title, + edition = product.edition + ) ++ if product.cover_url is not '': ++ cover_img_bin = urllib.urlopen(product.cover_url) ++ cover_img_file = Image.open(io.BytesIO(cover_img_bin.read())) ++ book.cover_img.save("cover.jpg", cover_img_file) + return book + + class Book(models.Model): + isbn = models.CharField(max_length=10, unique=True) +- eisbn = models.CharField(max_length=13, blank=True) ++ eisbn = models.CharField(max_length=13, blank=True, null=True) + title = models.CharField(max_length=200) + author = models.CharField(max_length=200) + edition = models.CharField(max_length=200, blank=True) diff --git a/test/addSelect/addSelect_books_models_py_4_marks_diff.expect b/test/addSelect/addSelect_books_models_py_4_marks_diff.expect new file mode 100644 index 0000000..e4dfb93 --- /dev/null +++ b/test/addSelect/addSelect_books_models_py_4_marks_diff.expect @@ -0,0 +1,38 @@ +diff --git books/models.py books/models.py +--- books/models.py ++++ books/models.py +@@ -1,5 +1,9 @@ + from django.db import models + ++import urllib2 as urllib ++from PIL import Image ++import io ++ + import isbn_search + + class User(models.Model): +@@ -22,15 +26,22 @@ class BookManager(models.Manager): + title = product.title, + edition = product.edition + ) ++ if product.cover_url is not '': ++ cover_img_bin = urllib.urlopen(product.cover_url) ++ cover_img_file = Image.open(io.BytesIO(cover_img_bin.read())) ++ book.cover_img.save("cover.jpg", cover_img_file) + return book + ++def upload_path(book, filename): ++ return 'covers/%s/%s' % (book.isbn, filename) ++ + class Book(models.Model): + isbn = models.CharField(max_length=10, unique=True) +- eisbn = models.CharField(max_length=13, blank=True) ++ eisbn = models.CharField(max_length=13, blank=True, null=True) + title = models.CharField(max_length=200) + author = models.CharField(max_length=200) +- edition = models.CharField(max_length=200, blank=True) + cover = models.ImageField(upload_to='covers', blank=True) ++ edition = models.CharField(max_length=200, blank=True, null=True) + def __unicode__(self): + return u'[%s] "%s" by %s' % (self.isbn, self.title, self.author,) + diff --git a/test/test.config b/test/test.config index 4178717..f99a8de 100644 --- a/test/test.config +++ b/test/test.config @@ -2,6 +2,7 @@ declare -a test_paths=(./ ./books/templates/) declare -A test_scripts=( [addFile.vader]='bootstrap;books/models.py;bootstrap.lnk;empty_file;bootstrap\ with\ spaces;bootstrap\ with\ spaces.lnk;empty_file\ with\ spaces' [addHunk.vader]='bootstrap;books/models.py' + [addSelect.vader]='books/models.py' [renameFile.vader]='manage.py|manage\ with\ spaces.py;djooks/settings\ with\ spaces.py|djooks/settings_without_spaces.py' [ignoreFile.vader]='bootstrap' ) diff --git a/test/utils.vim b/test/utils.vim index 975b4fa..4410147 100644 --- a/test/utils.vim +++ b/test/utils.vim @@ -150,10 +150,15 @@ function! Search_file(mode, ...) call Git_verbose_log('Search mode: "' . a:mode . '" => ' . getline('.')) let pattern='^.*: ' . call('Get_filename', a:000) . '\%( -> .*\)\?$' let ret = search(pattern) - call Git_verbose_log('Search: "' . pattern . '" => ' . getline('.')) + call Git_verbose_log('Search: "' . pattern . '" => ' . getline('.') . ' @line' . line('.')) return ret endfunction +function! Search_pattern(pattern) + let ret = search(a:pattern) + call Git_verbose_log('Search: "' . a:pattern . '" => ' . getline('.') . ' @line' . line('.')) +endfunction + " get a safe to use string of filename we curently test (for golden files) function! Get_safe_filename(...) return substitute(call('Get_filename', a:000), '[/. ]', '_', 'g') From 3ab04b28cf330b7579bd2b8e56c17f591581b235 Mon Sep 17 00:00:00 2001 From: Jerome Reybert Date: Mon, 19 Oct 2015 14:22:11 +0200 Subject: [PATCH 20/46] doc: update all docs with new commands --- README.md | 12 +++++++++++- doc/vimagit.txt | 15 +++++++++++++++ plugin/magit.vim | 21 ++++++++++++++------- 3 files changed, 40 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index e9f9063..ebe1aa2 100644 --- a/README.md +++ b/README.md @@ -27,6 +27,7 @@ Take a look at [TL;DR](#tldr) to start using it immediatly. * [x] See all your changes, staged changes, untracked/removed/renamed files in one unique buffer. * [x] Staged/unstaged/discard changes with one key press, moving the cursor around. Stage at hunk or file level. Line and partial line staging are ongoing. +* [x] Stage part of hunks, by visual select, lines or selecting bunch of lines with marks. * [x] Start to write the commit message in one key press, commit also in one key press. * [x] Modify in line the content just before staging it. * [x] Visualize stashes. Apply, pop, drop are on going. @@ -34,7 +35,6 @@ Take a look at [TL;DR](#tldr) to start using it immediatly. * [ ] Chase all corner cases. Please remember that vimagit is at an early development stage. If you try vimagit and nothing is working, please don't throw it, fill an issue on github :heart: ! More to come: -* Partial hunk staging (next release). * Vizualize and checkout branches. * Go through history, cherry-pick changes. * Something is missing? Open an [issue](https://github.com/jreybert/vimagit/issues/new)! @@ -123,6 +123,8 @@ Following mappings are set locally, for magit buffer only, in normal mode. **S** * If cursor is in a hunk, stage/unstage hunk at cursor position. * If cursor is in diff header, stage/unstage whole file at cursor position. + * If some lines in the hunk are selected (using **v**), stage only visual selected lines (only works for staging). + * If some lines in the hunk are marked (using **M**), stage only marked lines (only works for staging). * When cursor is in "Unstaged changes" section, it will stage the hunk/file. * On the other side, when cursor is in "Staged changes" section, it will unstage hunk/file. @@ -131,6 +133,14 @@ Following mappings are set locally, for magit buffer only, in normal mode. * When cursor is in "Unstaged changes" section, it will stage the file. * On the other side, when cursor is in "Staged changes" section, it will unstage file. +**L** + * Stage the line under the cursor. + +**M** + * Mark the line under the cursor "to be staged". + * If some lines in the hunk are selected (using **v**), mark selected lines "to be staged". + * To staged marked lines in a hunk, move cursor to this hunk and press **S**. + **DDD** * If cursor is in a hunk, discard hunk at cursor position. * If cursor is in diff header, discard whole file at cursor position. diff --git a/doc/vimagit.txt b/doc/vimagit.txt index 146024e..497c346 100644 --- a/doc/vimagit.txt +++ b/doc/vimagit.txt @@ -152,10 +152,15 @@ Following mappings are set locally, for magit buffer only, in normal mode. zc,zC Typing zc on a file will hide its diffs. *vimagit-S* *magit#stage_hunk(0)* + *magit#stage_vselect()* *vimagit-g:magit_stage_hunk_mapping* S If cursor is in a hunk, stage/unstage hunk at cursor position. If cursor is in diff header, stage/unstage whole file at cursor position. + If some lines in the hunk are selected (see |visual-use|), stage + only selected lines (only works for staging). + If some lines in the hunk are marked (see |vimagit-M|), stage only + these lines (only works for staging). When cursor is in "Unstaged changes" section, it will stage the hunk/file. On the other side, when cursor is in "Staged changes" section, it @@ -169,6 +174,16 @@ Following mappings are set locally, for magit buffer only, in normal mode. On the other side, when cursor is in "Staged changes" section, it will unstage file. + *vimagit-L* *magit#stage_vselect()* + L Stage the line under the cursor (only works for staging) + + *vimagit-M* *magit#mark_vselect()* + M + Mark the current line to be staged. + If some lines in the hunk are selected (see |visual-use|), mark + selected lines. + To staged marked lines, press S (see |vimagit-S|) in the current + hunk. *vimagit-DDD* *magit#stage_hunk(1)* *vimagit-g:magit_discard_hunk_mapping* diff --git a/plugin/magit.vim b/plugin/magit.vim index 391c94c..8402ed5 100644 --- a/plugin/magit.vim +++ b/plugin/magit.vim @@ -68,19 +68,26 @@ execute "nnoremap " . g:magit_show_magit_mapping . " :call magit#show_m " s:magit_inline_help: Dict containing inline help for each section let s:magit_inline_help = { \ 'staged': [ -\'S if cursor in diff header, unstage file', +\'S if cursor on filename header, unstage file', \' if cursor in hunk, unstage hunk', -\'F if cursor in diff header or hunk, unstage file', +\'F if cursor on filename header or hunk, unstage whole file', \], \ 'unstaged': [ -\'S if cursor in diff header, stage file', +\'S if cursor on filename header, stage file', \' if cursor in hunk, stage hunk', -\'F if cursor in diff header or hunk, stage file', +\' if visual selection in hunk (with v), stage selection', +\' if lines marked in hunk (with M), stage marked lines', +\'L stage the line under the cursor', +\'M if cursor in hunk, mark line under cursor "to be staged"', +\' if visual selection in hunk (with v), mark selected lines "to be' +\' staged"', +\'F if cursor on filename header or hunk, stage whole file', \'DDD discard file changes (warning, changes will be lost)', \'I add file in .gitgnore', \], \ 'global': [ -\'C CC set commit mode to normal, and show "Commit message" section', +\' if cursor on filename header line, unhide diffs for this file', +\'CC set commit mode to normal, and show "Commit message" section', \'CA set commit mode amend, and show "Commit message" section with previous', \' commit message', \'CF amend staged changes to previous commit without modifying the previous', @@ -93,8 +100,8 @@ let s:magit_inline_help = { \'You will still be able to toggle inline help with h', \], \ 'commit': [ -\'C CC commit all staged changes with commit mode previously set (normal or', -\':w amend) with message written in this section', +\'CC,:w commit all staged changes with commit mode previously set (normal or', +\' amend) with message written in this section', \], \} From e8b6928d3837dd7b0af816b748fdbae88f83ea9f Mon Sep 17 00:00:00 2001 From: Jerome Reybert Date: Mon, 19 Oct 2015 23:57:29 +0200 Subject: [PATCH 21/46] plugin/magit.vim: handle directories recursively (fix #10) --- autoload/magit/state.vim | 142 +++++++++++++++++++++------------------ common/magit_common.vim | 1 + plugin/magit.vim | 43 ++++++++---- 3 files changed, 108 insertions(+), 78 deletions(-) diff --git a/autoload/magit/state.vim b/autoload/magit/state.vim index 1163014..170fd2d 100644 --- a/autoload/magit/state.vim +++ b/autoload/magit/state.vim @@ -3,6 +3,11 @@ function! magit#state#is_file_visible(section, filename) dict \ ( self.dict[a:section][a:filename].visible == 1 ) ) endfunction +function! magit#state#is_dir(section, filename) dict + return ( has_key(self.dict[a:section], a:filename) && + \ ( self.dict[a:section][a:filename].dir != 0 ) ) +endfunction + function! magit#state#get_files(mode) dict return self.dict[a:mode] endfunction @@ -28,6 +33,7 @@ let s:file_template = { \ 'exists': 0, \ 'status': '', \ 'empty': 0, +\ 'dir': 0, \ 'binary': 0, \ 'symlink': '', \ 'diff': s:diff_template, @@ -87,60 +93,60 @@ endfunction " param[in] mode: can be staged or unstaged " param[in] status: one character status code of the file (AMDRCU?) " param[in] filename: filename -function! magit#state#add_file(mode, status, filename) dict - let dir = getcwd() - try - call magit#utils#lcd(magit#utils#top_dir()) - let dev_null = ( a:status == '?' ) ? " /dev/null " : " " - let staged_flag = ( a:mode == 'staged' ) ? " --staged " : " " - let diff_cmd="git diff --no-ext-diff " . staged_flag . - \ "--no-color --patch -- " . dev_null . " " - \ . magit#utils#add_quotes(a:filename) - let diff_list=magit#utils#systemlist(diff_cmd) - if ( empty(diff_list) ) - echoerr "diff command \"" . diff_cmd . "\" returned nothing" - endif - let diff_dict_file = self.get_file(a:mode, a:filename, 1) - let diff_dict_file.exists = 1 - let diff_dict_file.status = a:status - if ( a:status == '?' && getftype(a:filename) == 'link' ) - let diff_dict_file.symlink = resolve(a:filename) - call add(diff_dict_file.diff.header, 'no header') - let diff_dict_file.diff.hunks[0].header = 'New symbolic link file' - elseif ( a:status == '?' && getfsize(a:filename) == 0 ) - let diff_dict_file.empty = 1 - call add(diff_dict_file.diff.header, 'no header') - let diff_dict_file.diff.hunks[0].header = 'New empty file' - elseif ( match(system("file --mime " . - \ magit#utils#add_quotes(a:filename)), - \ a:filename . ".*charset=binary") != -1 ) - let diff_dict_file.binary = 1 - call add(diff_dict_file.diff.header, 'no header') - let diff_dict_file.diff.hunks[0].header = 'Binary file' - else - let line = 0 - " match( - while ( line < len(diff_list) && diff_list[line] !~ "^@.*" ) - call add(diff_dict_file.diff.header, diff_list[line]) - let line += 1 - endwhile - - let hunk = diff_dict_file.diff.hunks[0] - let hunk.header = diff_list[line] - - for diff_line in diff_list[line+1 : -1] - if ( diff_line =~ "^@.*" ) - let hunk = deepcopy(s:hunk_template) - call add(diff_dict_file.diff.hunks, hunk) - let hunk.header = diff_line - continue - endif - call add(hunk.lines, diff_line) - endfor - endif - finally - call magit#utils#lcd(dir) - endtry +function! magit#state#add_file(mode, status, filename, depth) dict + let dev_null = ( a:status == '?' ) ? " /dev/null " : " " + let staged_flag = ( a:mode == 'staged' ) ? " --staged " : " " + let diff_cmd="git diff --no-ext-diff " . staged_flag . + \ "--no-color --patch -- " . dev_null . " " + \ . magit#utils#add_quotes(a:filename) + let diff_list=magit#utils#systemlist(diff_cmd) + if ( empty(diff_list) ) + echoerr "diff command \"" . diff_cmd . "\" returned nothing" + endif + let diff_dict_file = self.get_file(a:mode, a:filename, 1) + let diff_dict_file.exists = 1 + let diff_dict_file.status = a:status + let diff_dict_file.depth = a:depth + if ( a:status == '?' && getftype(a:filename) == 'link' ) + let diff_dict_file.symlink = resolve(a:filename) + call add(diff_dict_file.diff.header, 'no header') + let diff_dict_file.diff.hunks[0].header = 'New symbolic link file' + elseif ( a:status == '?' && isdirectory(a:filename) == 1 ) + let diff_dict_file.dir = 1 + for subfile in split(globpath(a:filename, '\(.[^.]*\|*\)'), '\n') + call self.add_file(a:mode, a:status, subfile, a:depth + 1) + endfor + elseif ( a:status == '?' && getfsize(a:filename) == 0 ) + let diff_dict_file.empty = 1 + call add(diff_dict_file.diff.header, 'no header') + let diff_dict_file.diff.hunks[0].header = 'New empty file' + elseif ( match(system("file --mime " . + \ magit#utils#add_quotes(a:filename)), + \ a:filename . ".*charset=binary") != -1 ) + let diff_dict_file.binary = 1 + call add(diff_dict_file.diff.header, 'no header') + let diff_dict_file.diff.hunks[0].header = 'Binary file' + else + let line = 0 + " match( + while ( line < len(diff_list) && diff_list[line] !~ "^@.*" ) + call add(diff_dict_file.diff.header, diff_list[line]) + let line += 1 + endwhile + + let hunk = diff_dict_file.diff.hunks[0] + let hunk.header = diff_list[line] + + for diff_line in diff_list[line+1 : -1] + if ( diff_line =~ "^@.*" ) + let hunk = deepcopy(s:hunk_template) + call add(diff_dict_file.diff.hunks, hunk) + let hunk.header = diff_line + continue + endif + call add(hunk.lines, diff_line) + endfor + endif endfunction " magit#state#update: update self.dict @@ -157,19 +163,24 @@ function! magit#state#update() dict endfor endfor - for [mode, diff_dict_mode] in items(self.dict) - - let status_list = magit#git#get_status() - for file_status in status_list - let status=file_status[mode] + let dir = getcwd() + try + call magit#utils#lcd(magit#utils#top_dir()) + for [mode, diff_dict_mode] in items(self.dict) + let status_list = magit#git#get_status() + for file_status in status_list + let status=file_status[mode] - " untracked code apperas in staged column, we skip it - if ( status == ' ' || ( ( mode == 'staged' ) && status == '?' ) ) - continue - endif - call self.add_file(mode, status, file_status.filename) + " untracked code apperas in staged column, we skip it + if ( status == ' ' || ( ( mode == 'staged' ) && status == '?' ) ) + continue + endif + call self.add_file(mode, status, file_status.filename, 0) + endfor endfor - endfor + finally + call magit#utils#lcd(dir) + endtry " remove files that have changed their mode or been committed/deleted/discarded... for diff_dict_mode in values(self.dict) @@ -202,6 +213,7 @@ let magit#state#state = { \ 'get_hunks': function("magit#state#get_hunks"), \ 'get_flat_hunks': function("magit#state#get_flat_hunks"), \ 'add_file': function("magit#state#add_file"), + \ 'is_dir': function("magit#state#is_dir"), \ 'is_file_visible': function("magit#state#is_file_visible"), \ 'update': function("magit#state#update"), \ 'dict': { 'staged': {}, 'unstaged': {}}, diff --git a/common/magit_common.vim b/common/magit_common.vim index c14c3be..9a18e91 100644 --- a/common/magit_common.vim +++ b/common/magit_common.vim @@ -21,6 +21,7 @@ let g:magit_git_status_code = { \ '!': 'ignored', \ 'E': 'empty', \ 'L': 'symlink', + \ 'N': 'new dir', \ } " Regular expressions used to select blocks diff --git a/plugin/magit.vim b/plugin/magit.vim index 8402ed5..aa81dbb 100644 --- a/plugin/magit.vim +++ b/plugin/magit.vim @@ -140,24 +140,23 @@ function! s:mg_get_info() silent put ='' endfunction +function! s:mg_display_files(mode, curdir, depth) -" s:mg_get_staged_section: this function writes in current buffer all staged -" or unstaged files, using s:state.dict information -" WARNING: this function writes in file, it should only be called through -" protected functions like magit#update_buffer -" param[in] mode: 'staged' or 'unstaged' -function! s:mg_get_staged_section(mode) - put ='' - put =g:magit_sections[a:mode] - call mg_section_help(a:mode) - put =magit#utils#underline(g:magit_sections[a:mode]) - put ='' - + " FIXME: ouch, must store subdirs in more efficient way for [ filename, file_props ] in items(s:state.get_files(a:mode)) + if ( file_props.depth != a:depth || filename !~ a:curdir . '.*' ) + continue + endif if ( file_props.empty == 1 ) put =g:magit_git_status_code.E . ': ' . filename elseif ( file_props.symlink != '' ) put =g:magit_git_status_code.L . ': ' . filename . ' -> ' . file_props.symlink + elseif ( file_props.dir != 0 ) + put =g:magit_git_status_code.N . ': ' . filename + if ( file_props.visible == 1 ) + call s:mg_display_files(a:mode, filename, a:depth + 1) + continue + endif else put =g:magit_git_status_code[file_props.status] . ': ' . filename endif @@ -177,6 +176,20 @@ function! s:mg_get_staged_section(mode) endfor endfunction +" s:mg_get_staged_section: this function writes in current buffer all staged +" or unstaged files, using s:state.dict information +" WARNING: this function writes in file, it should only be called through +" protected functions like magit#update_buffer +" param[in] mode: 'staged' or 'unstaged' +function! s:mg_get_staged_section(mode) + put ='' + put =g:magit_sections[a:mode] + call mg_section_help(a:mode) + put =magit#utils#underline(g:magit_sections[a:mode]) + put ='' + call s:mg_display_files(a:mode, '', 0) +endfunction + " s:mg_get_stashes: this function write in current buffer all stashes " WARNING: this function writes in file, it should only be called through " protected functions like magit#update_buffer @@ -663,7 +676,8 @@ function! s:mg_select_closed_file() let list = matchlist(getline("."), g:magit_file_re) let filename = list[2] let section=mg_get_section() - if ( s:state.is_file_visible(section, filename) == 0 ) + if ( s:state.is_file_visible(section, filename) == 0 || + \ s:state.is_dir(section, filename) == 1 ) let selection = s:state.get_flat_hunks(section, filename) return selection endif @@ -687,6 +701,7 @@ function! magit#stage_block(selection, discard) abort if ( section == 'unstaged' ) if ( file.empty == 1 || \ file.symlink != '' || + \ file.dir != 0 || \ file.binary == 1 ) call magit#utils#system('git add ' . \ magit#utils#add_quotes(filename)) @@ -696,6 +711,7 @@ function! magit#stage_block(selection, discard) abort elseif ( section == 'staged' ) if ( file.empty == 1 || \ file.symlink != '' || + \ file.dir != 0 || \ file.binary == 1 ) call magit#utils#system('git reset ' . \ magit#utils#add_quotes(filename)) @@ -711,6 +727,7 @@ function! magit#stage_block(selection, discard) abort if ( section == 'unstaged' ) if ( file.empty == 1 || \ file.symlink != '' || + \ file.dir != 0 || \ file.binary == 1 ) call delete(filename) else From be178a5a9f2cd578d665cb6ee3868fe937f61edc Mon Sep 17 00:00:00 2001 From: Jerome Reybert Date: Tue, 20 Oct 2015 00:08:05 +0200 Subject: [PATCH 22/46] plugin/magit.vim: sort file display --- plugin/magit.vim | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/plugin/magit.vim b/plugin/magit.vim index aa81dbb..65a5d81 100644 --- a/plugin/magit.vim +++ b/plugin/magit.vim @@ -143,7 +143,8 @@ endfunction function! s:mg_display_files(mode, curdir, depth) " FIXME: ouch, must store subdirs in more efficient way - for [ filename, file_props ] in items(s:state.get_files(a:mode)) + for filename in sort(keys(s:state.get_files(a:mode))) + let file_props = s:state.get_file(a:mode, filename, 0) if ( file_props.depth != a:depth || filename !~ a:curdir . '.*' ) continue endif From c117cfc72ad233ca560336789c95c39dff17ce82 Mon Sep 17 00:00:00 2001 From: Jerome Reybert Date: Tue, 20 Oct 2015 13:14:36 +0200 Subject: [PATCH 23/46] plugin/magit.vim: remove signs when they are staged --- autoload/magit/sign.vim | 8 +++++--- plugin/magit.vim | 3 ++- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/autoload/magit/sign.vim b/autoload/magit/sign.vim index fd44abd..f979d8b 100644 --- a/autoload/magit/sign.vim +++ b/autoload/magit/sign.vim @@ -15,11 +15,12 @@ let s:supports_star = v:version > 703 || (v:version == 703 && has("patch596")) let s:bufnr = bufnr(g:magit_buffer_name) " magit#sign#remove_signs: unplace a list of signs -" param[in] sign_ids: list of signs ids +" param[in] sign_ids: list of signs dict function! magit#sign#remove_signs(sign_ids) let bufnr = magit#utils#bufnr() - for id in a:sign_ids - execute "sign unplace" id + for sign in values(a:sign_ids) + echom "sign unplace" sign.id + execute "sign unplace" sign.id endfor endfunction @@ -62,6 +63,7 @@ endfunction " magit#sign#find_stage_signs: helper function to get marked lines for stage " param[in] startline,endline: range of lines +" return Dict of marked lines function! magit#sign#find_stage_signs(startline, endline) return magit#sign#find_signs(s:magit_mark_sign, a:startline, a:endline) endfunction diff --git a/plugin/magit.vim b/plugin/magit.vim index 65a5d81..1d1e56e 100644 --- a/plugin/magit.vim +++ b/plugin/magit.vim @@ -780,7 +780,8 @@ function! magit#stage_hunk(discard) let selection = getline(start, end) else let selection = mg_create_diff_from_select( - \map(keys(marked_lines), 'str2nr(v:val)')) + \ map(keys(marked_lines), 'str2nr(v:val)')) + call magit#sign#remove_signs(marked_lines) endif endtry return magit#stage_block(selection, a:discard) From a3d362b089fa21fce914c8ec6964c8f34f31361e Mon Sep 17 00:00:00 2001 From: Jerome Reybert Date: Tue, 20 Oct 2015 13:40:13 +0200 Subject: [PATCH 24/46] autoload/magit/state.vim: rework OOP for files --- autoload/magit/state.vim | 40 +++++++++++++++++++++++++++++----------- plugin/magit.vim | 29 ++++++++++++----------------- 2 files changed, 41 insertions(+), 28 deletions(-) diff --git a/autoload/magit/state.vim b/autoload/magit/state.vim index 170fd2d..39f2c20 100644 --- a/autoload/magit/state.vim +++ b/autoload/magit/state.vim @@ -1,17 +1,31 @@ -function! magit#state#is_file_visible(section, filename) dict - return ( has_key(self.dict[a:section], a:filename) && - \ ( self.dict[a:section][a:filename].visible == 1 ) ) +function! magit#state#is_file_visible() dict + return self.visible endfunction -function! magit#state#is_dir(section, filename) dict - return ( has_key(self.dict[a:section], a:filename) && - \ ( self.dict[a:section][a:filename].dir != 0 ) ) +function! magit#state#set_file_visible(val) dict + let self.visible = a:val +endfunction + +function! magit#state#toggle_file_visible() dict + let self.visible = ( self.visible == 0 ) ? 1 : 0 +endfunction + +function! magit#state#is_file_dir() dict + return self.dir != 0 endfunction function! magit#state#get_files(mode) dict return self.dict[a:mode] endfunction +function! magit#state#must_be_added() dict + return ( self.empty == 1 || + \ self.symlink != '' || + \ self.dir != 0 || + \ self.binary == 1 ) +endfunction + + " s:hunk_template: template for hunk object (nested in s:diff_template) " WARNING: this variable must be deepcopy()'ied let s:hunk_template = { @@ -37,6 +51,11 @@ let s:file_template = { \ 'binary': 0, \ 'symlink': '', \ 'diff': s:diff_template, +\ 'is_dir': function("magit#state#is_file_dir"), +\ 'is_visible': function("magit#state#is_file_visible"), +\ 'set_visible': function("magit#state#set_file_visible"), +\ 'toggle_visible': function("magit#state#toggle_file_visible"), +\ 'must_be_added': function("magit#state#must_be_added"), \} " magit#state#get_file: function accessor for file @@ -45,12 +64,13 @@ let s:file_template = { " param[in] create: boolean. If 1, non existing file in Dict will be created. " if 0, 'file_doesnt_exists' exception will be thrown " return: Dict of file -function! magit#state#get_file(mode, filename, create) dict +function! magit#state#get_file(mode, filename, ...) dict let file_exists = has_key(self.dict[a:mode], a:filename) - if ( file_exists == 0 && a:create == 1 ) + let create = ( a:0 == 1 ) ? a:1 : 0 + if ( file_exists == 0 && create == 1 ) let self.dict[a:mode][a:filename] = deepcopy(s:file_template) let self.dict[a:mode][a:filename].visible = 0 - elseif ( file_exists == 0 && a:create == 0 ) + elseif ( file_exists == 0 && create == 0 ) throw 'file_doesnt_exists' endif return self.dict[a:mode][a:filename] @@ -213,8 +233,6 @@ let magit#state#state = { \ 'get_hunks': function("magit#state#get_hunks"), \ 'get_flat_hunks': function("magit#state#get_flat_hunks"), \ 'add_file': function("magit#state#add_file"), - \ 'is_dir': function("magit#state#is_dir"), - \ 'is_file_visible': function("magit#state#is_file_visible"), \ 'update': function("magit#state#update"), \ 'dict': { 'staged': {}, 'unstaged': {}}, \ } diff --git a/plugin/magit.vim b/plugin/magit.vim index 1d1e56e..3031449 100644 --- a/plugin/magit.vim +++ b/plugin/magit.vim @@ -550,9 +550,11 @@ function! magit#open_close_folding(...) " if first param is set, force visible to this value " else, toggle value let file = s:state.get_file(section, filename, 0) - let file.visible = - \ ( a:0 == 1 ) ? a:1 : - \ ( file.visible == 0 ) ? 1 : 0 + if ( a:0 == 1 ) + call file.set_visible(a:1) + else + call file.toggle_visible() + endif call magit#update_buffer() endfunction @@ -677,8 +679,10 @@ function! s:mg_select_closed_file() let list = matchlist(getline("."), g:magit_file_re) let filename = list[2] let section=mg_get_section() - if ( s:state.is_file_visible(section, filename) == 0 || - \ s:state.is_dir(section, filename) == 1 ) + + let file = s:state.get_file(section, filename) + if ( file.is_visible() == 0 || + \ file.is_dir() == 1 ) let selection = s:state.get_flat_hunks(section, filename) return selection endif @@ -700,20 +704,14 @@ function! magit#stage_block(selection, discard) abort let file = s:state.get_file(section, filename, 0) if ( a:discard == 0 ) if ( section == 'unstaged' ) - if ( file.empty == 1 || - \ file.symlink != '' || - \ file.dir != 0 || - \ file.binary == 1 ) + if ( file.must_be_added() ) call magit#utils#system('git add ' . \ magit#utils#add_quotes(filename)) else call mg_git_apply(header, a:selection) endif elseif ( section == 'staged' ) - if ( file.empty == 1 || - \ file.symlink != '' || - \ file.dir != 0 || - \ file.binary == 1 ) + if ( file.must_be_added() ) call magit#utils#system('git reset ' . \ magit#utils#add_quotes(filename)) else @@ -726,10 +724,7 @@ function! magit#stage_block(selection, discard) abort endif else if ( section == 'unstaged' ) - if ( file.empty == 1 || - \ file.symlink != '' || - \ file.dir != 0 || - \ file.binary == 1 ) + if ( file.must_be_added() ) call delete(filename) else call mg_git_unapply(header, a:selection, 'unstaged') From c2c3b87475d7526bbcf4b161983a0f91f00f27f0 Mon Sep 17 00:00:00 2001 From: Jerome Reybert Date: Tue, 20 Oct 2015 23:07:33 +0200 Subject: [PATCH 25/46] autoload/magit/sign.vim: rework some sign functions --- autoload/magit/sign.vim | 32 ++++++++++++++++++++++---------- plugin/magit.vim | 2 +- 2 files changed, 23 insertions(+), 11 deletions(-) diff --git a/autoload/magit/sign.vim b/autoload/magit/sign.vim index f979d8b..646347a 100644 --- a/autoload/magit/sign.vim +++ b/autoload/magit/sign.vim @@ -19,11 +19,22 @@ let s:bufnr = bufnr(g:magit_buffer_name) function! magit#sign#remove_signs(sign_ids) let bufnr = magit#utils#bufnr() for sign in values(a:sign_ids) - echom "sign unplace" sign.id execute "sign unplace" sign.id endfor endfunction +function! magit#sign#add_sign(line, type, bufnr) + let id = get_next_sign_id() + execute ":sign place " . id . + \ " line=" . a:line . " name=" . s:magit_mark_signs[a:type] . + \ " buffer=" . a:bufnr + return id +endfunction + +function! magit#sign#remove_sign(id) + execute ":sign unplace " . a:id +endfunction + " s:get_next_sign_id: helper function to increment sign ids function! s:get_next_sign_id() let next_id = s:next_sign_id @@ -65,31 +76,32 @@ endfunction " param[in] startline,endline: range of lines " return Dict of marked lines function! magit#sign#find_stage_signs(startline, endline) - return magit#sign#find_signs(s:magit_mark_sign, a:startline, a:endline) + return magit#sign#find_signs(s:magit_mark_signs.M, a:startline, a:endline) endfunction " s:magit_mark_sign: string of the sign for lines to be staged -let s:magit_mark_sign='MagitMark' +let s:magit_mark_signs = {'M': 'MagitTBS', 'S': 'MagitBS', 'E': 'MagitBE'} " magit#sign#init: initializer function for signs function! magit#sign#init() - execute "sign define " . s:magit_mark_sign . " text=S> linehl=Visual" + execute "sign define " . s:magit_mark_signs.M . " text=S> linehl=Visual" + execute "sign define " . s:magit_mark_signs.S + execute "sign define " . s:magit_mark_signs.E endfunction " magit#sign#toggle_signs: toggle marks for range of lines " marked lines are unmarked, non marked are marked +" param[in] type; type of sign to toggle (see s:magit_mark_signs) " param[in] startline,endline: range of lines -function! magit#sign#toggle_signs(startline, endline) +function! magit#sign#toggle_signs(type, startline, endline) let bufnr = magit#utils#bufnr() - let current_signs = magit#sign#find_signs(s:magit_mark_sign, a:startline, a:endline) + let current_signs = magit#sign#find_signs(s:magit_mark_signs[a:type], a:startline, a:endline) let line = a:startline while ( line <= a:endline ) if ( has_key(current_signs, line) == 0 ) - execute ":sign place " . get_next_sign_id() . - \ " line=" . line . " name=" . s:magit_mark_sign . - \ " buffer=" . bufnr + call magit#sign#add_sign(line, a:type, bufnr) else - execute ":sign unplace " . current_signs[line].id + call magit#sign#remove_sign(current_signs[line].id) endif let line += 1 endwhile diff --git a/plugin/magit.vim b/plugin/magit.vim index 3031449..192dc27 100644 --- a/plugin/magit.vim +++ b/plugin/magit.vim @@ -489,7 +489,7 @@ function! s:mg_mark_lines_in_hunk(start_select_line, end_select_line) if ( a:start_select_line < starthunk || a:end_select_line > endhunk ) throw 'out of hunk selection' endif - return magit#sign#toggle_signs(a:start_select_line, a:end_select_line) + return magit#sign#toggle_signs('M', a:start_select_line, a:end_select_line) endfunction " s:mg_get_section: helper function to get the current section, according to From a14782d7d86a0aa9bfb3c7c32e25d0891d2cf04b Mon Sep 17 00:00:00 2001 From: Jerome Reybert Date: Tue, 20 Oct 2015 23:13:01 +0200 Subject: [PATCH 26/46] autoload/state.vim: move get_hunks function to file object --- autoload/magit/state.vim | 44 +++++++++++++++++----------------------- plugin/magit.vim | 26 ++++++++++++------------ 2 files changed, 32 insertions(+), 38 deletions(-) diff --git a/autoload/magit/state.vim b/autoload/magit/state.vim index 39f2c20..c7b7d03 100644 --- a/autoload/magit/state.vim +++ b/autoload/magit/state.vim @@ -25,6 +25,23 @@ function! magit#state#must_be_added() dict \ self.binary == 1 ) endfunction +" magit#state#file_get_hunks: function accessor for hunks objects +" return: List of List of hunks lines +function! magit#state#file_get_hunks() dict + return self.diff.hunks +endfunction + +" magit#state#file_get_flat_hunks: function accessor for hunks lines +" return: all hunks lines of a file, including hunk headers +function! magit#state#file_get_flat_hunks() dict + let hunks = self.diff.hunks + let lines = [] + for hunk in hunks + call add(lines, hunk.header) + call add(lines, hunk.lines) + endfor + return lines +endfunction " s:hunk_template: template for hunk object (nested in s:diff_template) " WARNING: this variable must be deepcopy()'ied @@ -56,6 +73,8 @@ let s:file_template = { \ 'set_visible': function("magit#state#set_file_visible"), \ 'toggle_visible': function("magit#state#toggle_file_visible"), \ 'must_be_added': function("magit#state#must_be_added"), +\ 'get_hunks' : function("magit#state#file_get_hunks"), +\ 'get_flat_hunks' : function("magit#state#file_get_flat_hunks"), \} " magit#state#get_file: function accessor for file @@ -85,29 +104,6 @@ function! magit#state#get_header(mode, filename) dict return diff_dict_file.diff.header endfunction -" magit#state#get_hunks: function accessor for hunks objects -" param[in] mode: can be staged or unstaged -" param[in] filename: hunks of filename to access -" return: List of List of hunks lines -function! magit#state#get_hunks(mode, filename) dict - let diff_dict_file = self.get_file(a:mode, a:filename, 0) - return diff_dict_file.diff.hunks -endfunction - -" magit#state#get_hunks: function accessor for hunks lines -" param[in] mode: can be staged or unstaged -" param[in] filename: hunks of filename to access -" return: all hunks lines of a file, including hunk headers -function! magit#state#get_flat_hunks(mode, filename) dict - let hunks = self.get_hunks(a:mode, a:filename) - let lines = [] - for hunk in hunks - call add(lines, hunk.header) - call add(lines, hunk.lines) - endfor - return lines -endfunction - " magit#state#add_file: method to add a file with all its " properties (filename, exists, status, header and hunks) " param[in] mode: can be staged or unstaged @@ -230,8 +226,6 @@ let magit#state#state = { \ 'get_file': function("magit#state#get_file"), \ 'get_files': function("magit#state#get_files"), \ 'get_header': function("magit#state#get_header"), - \ 'get_hunks': function("magit#state#get_hunks"), - \ 'get_flat_hunks': function("magit#state#get_flat_hunks"), \ 'add_file': function("magit#state#add_file"), \ 'update': function("magit#state#update"), \ 'dict': { 'staged': {}, 'unstaged': {}}, diff --git a/plugin/magit.vim b/plugin/magit.vim index 192dc27..77daf24 100644 --- a/plugin/magit.vim +++ b/plugin/magit.vim @@ -144,31 +144,31 @@ function! s:mg_display_files(mode, curdir, depth) " FIXME: ouch, must store subdirs in more efficient way for filename in sort(keys(s:state.get_files(a:mode))) - let file_props = s:state.get_file(a:mode, filename, 0) - if ( file_props.depth != a:depth || filename !~ a:curdir . '.*' ) + let file = s:state.get_file(a:mode, filename, 0) + if ( file.depth != a:depth || filename !~ a:curdir . '.*' ) continue endif - if ( file_props.empty == 1 ) + if ( file.empty == 1 ) put =g:magit_git_status_code.E . ': ' . filename - elseif ( file_props.symlink != '' ) - put =g:magit_git_status_code.L . ': ' . filename . ' -> ' . file_props.symlink - elseif ( file_props.dir != 0 ) + elseif ( file.symlink != '' ) + put =g:magit_git_status_code.L . ': ' . filename . ' -> ' . file.symlink + elseif ( file.dir != 0 ) put =g:magit_git_status_code.N . ': ' . filename - if ( file_props.visible == 1 ) + if ( file.visible == 1 ) call s:mg_display_files(a:mode, filename, a:depth + 1) continue endif else - put =g:magit_git_status_code[file_props.status] . ': ' . filename + put =g:magit_git_status_code[file.status] . ': ' . filename endif - if ( file_props.visible == 0 ) + if ( file.visible == 0 ) put ='' continue endif - if ( file_props.exists == 0 ) + if ( file.exists == 0 ) echoerr "Error, " . filename . " should not exists" endif - let hunks=s:state.get_hunks(a:mode, filename) + let hunks = file.get_hunks() for hunk in hunks silent put =hunk.header silent put =hunk.lines @@ -451,7 +451,7 @@ function! s:mg_create_diff_from_select(select_lines) endif let section=mg_get_section() let filename=mg_get_filename() - let hunks = s:state.get_hunks(section, filename) + let hunks = s:state.get_file(section, filename).get_hunks() for hunk in hunks if ( hunk.header == getline(starthunk) ) let current_hunk = hunk @@ -683,7 +683,7 @@ function! s:mg_select_closed_file() let file = s:state.get_file(section, filename) if ( file.is_visible() == 0 || \ file.is_dir() == 1 ) - let selection = s:state.get_flat_hunks(section, filename) + let selection = s:state.get_file(section, filename).get_flat_hunks() return selection endif endif From e8970f24abe47bbe87626275ee87c67870d016c3 Mon Sep 17 00:00:00 2001 From: Jerome Reybert Date: Thu, 22 Oct 2015 15:09:54 +0200 Subject: [PATCH 27/46] plugin/magit.vim: full buffer cleanup when update --- autoload/magit/sign.vim | 10 ++++++++++ plugin/magit.vim | 7 ++++++- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/autoload/magit/sign.vim b/autoload/magit/sign.vim index 646347a..7a53390 100644 --- a/autoload/magit/sign.vim +++ b/autoload/magit/sign.vim @@ -14,6 +14,16 @@ let s:supports_star = v:version > 703 || (v:version == 703 && has("patch596")) let s:bufnr = bufnr(g:magit_buffer_name) +function! magit#sign#remove_all(...) + if ( a:0 == 1 ) + let pattern = a:1 + else + let pattern = '^Magit.*' + endif + let signs = magit#sign#find_signs(pattern, 1, line('$')) + call magit#sign#remove_signs(signs) +endfunction + " magit#sign#remove_signs: unplace a list of signs " param[in] sign_ids: list of signs dict function! magit#sign#remove_signs(sign_ids) diff --git a/plugin/magit.vim b/plugin/magit.vim index 77daf24..bf5eb4a 100644 --- a/plugin/magit.vim +++ b/plugin/magit.vim @@ -576,7 +576,12 @@ function! magit#update_buffer() " Playing with foldenable around does not help. " mkview does not help either. let l:winview = winsaveview() - silent! %d + + " remove all signs (needed as long as we wipe buffer) + call magit#sign#remove_all() + + " delete buffer + silent! execute "silent :%delete _" call mg_get_info() call mg_section_help('global') From c4afc428f46eb50130bd396e441cc385096ececf Mon Sep 17 00:00:00 2001 From: Jerome Reybert Date: Thu, 22 Oct 2015 23:46:00 +0200 Subject: [PATCH 28/46] plugin/magit.vim: add options to change behavior of magit display, show diffs and foldlevel (fixes #18) --- README.md | 47 +++++++++++++++++++++++++++++++++++----- autoload/magit/state.vim | 2 +- doc/vimagit.txt | 38 ++++++++++++++++++++++++++++---- plugin/magit.vim | 23 +++++++++++++++----- 4 files changed, 95 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index ebe1aa2..0d36951 100644 --- a/README.md +++ b/README.md @@ -93,7 +93,22 @@ There are 5 sections: ### Commands -**:Magit** +#### magit#show_magit() + +Function to open magit buffer. +It takes 3 parameters: + * orientation (mandatory): it can be + - 'v', curent window is split vertically, and magit is displayed in new + buffer + - 'h', curent window is split horizontally, and magit is displayed in + new buffer + - 'c', magit is displayed in current buffer + * show_all_files: define is file diffs are shown by default for this session + (see [g:magit_default_show_all_files](#g_magit_default_show_all_files)) + * foldlevel: set default magit buffer foldlevel for this session + (see [g:magit_default_fold_level](#g_magit_default_fold_level)) + +#### :Magit * open magit buffer. ### Mappings @@ -176,11 +191,33 @@ Following mappings are set locally, for magit buffer only, in normal mode. User can define in its prefered |vimrc| some options. -To disable vimagit plugin -> let g:magit_enabled=0 +#### g:magit_enabled + +To enable or disable vimagit plugin. +Default value is 1. +> let g:magit_enabled=[01] + +#### g:magit_show_help + +To disable chatty inline help in magit buffer (default 1) +> let g:magit_show_help=[01] + +#### g:magit_default_show_all_files + +When this variable is set to 0, all diff files are hidden by default. +When this variable is set to 1, all diff files are shown by default. +Default value is 0. +NB: for repository with large number of differences, display may be slow. +> let g:magit_default_show_all_files=[01] + +#### g:magit_default_fold_level -To disable chatty inline help in magit buffer -> let g:magit_show_help=0 +Default foldlevel for magit buffer. +When set to 0, both filenames and hunks are folded. +When set to 1, filenames are unfolded and hunks are folded. +When set to 2, filenames and hunks are unfolded. +Default value is 1. +> let g:magit_default_fold_level=[012] ## Installation diff --git a/autoload/magit/state.vim b/autoload/magit/state.vim index c7b7d03..160bd25 100644 --- a/autoload/magit/state.vim +++ b/autoload/magit/state.vim @@ -88,7 +88,7 @@ function! magit#state#get_file(mode, filename, ...) dict let create = ( a:0 == 1 ) ? a:1 : 0 if ( file_exists == 0 && create == 1 ) let self.dict[a:mode][a:filename] = deepcopy(s:file_template) - let self.dict[a:mode][a:filename].visible = 0 + let self.dict[a:mode][a:filename].visible = b:magit_default_show_all_files elseif ( file_exists == 0 && create == 0 ) throw 'file_doesnt_exists' endif diff --git a/doc/vimagit.txt b/doc/vimagit.txt index 497c346..78cbdfe 100644 --- a/doc/vimagit.txt +++ b/doc/vimagit.txt @@ -112,6 +112,20 @@ INLINE MODIFICATIONS *vimagit-inline-modification* COMMANDS *vimagit-commands* + *magit#show_magit()* +Function to open magit buffer. +It takes 3 parameters: + * orientation (mandatory): it can be + - 'v', curent window is split vertically, and magit is displayed in new + buffer + - 'h', curent window is split horizontally, and magit is displayed in + new buffer + - 'c', magit is displayed in current buffer + * show_all_files: define is file diffs are shown by default for this session + (see |vimagit-g:magit_default_show_all_files|) + * foldlevel: set default magit buffer foldlevel for this session + (see |vimagit-g:magit_default_fold_level|) + *:Magit* *magit#show_magit('v')* :Magit open magit buffer @@ -239,12 +253,28 @@ Following mappings are set locally, for magit buffer only, in normal mode. User can define in its prefered |vimrc| some options. *vimagit-g:magit_enabled* -To disable vimagit plugin -let g:magit_enabled=0 +To enable or disable vimagit plugin. +Default value is 1. +let g:magit_enabled=[01] *vimagit-g:magit_show_help* -To disable chatty inline help in magit buffer -let g:magit_show_help=0 +To disable chatty inline help in magit buffer (default 1) +let g:magit_show_help=[01] + + *vimagit-g:magit_default_show_all_files* +When this variable is set to 0, all diff files are hidden by default. +When this variable is set to 1, all diff files are shown by default. +Default value is 0. +NB: for repository with large number of differences, display may be slow. +let g:magit_default_show_all_files=[01] + + *vimagit-g:magit_default_fold_level* +Default foldlevel for magit buffer. +When set to 0, both filenames and hunks are folded. +When set to 1, filenames are unfolded and hunks are folded. +When set to 2, filenames and hunks are unfolded. +Default value is 1. +let g:magit_default_fold_level=[012] =============================================================================== 6. FAQ *vimagit-FAQ* diff --git a/plugin/magit.vim b/plugin/magit.vim index bf5eb4a..addd413 100644 --- a/plugin/magit.vim +++ b/plugin/magit.vim @@ -59,6 +59,8 @@ call s:set('g:magit_folding_close_mapping', [ 'zc', 'zC' ]) " user options call s:set('g:magit_enabled', 1) call s:set('g:magit_show_help', 1) +call s:set('g:magit_default_show_all_files', 0) +call s:set('g:magit_default_fold_level', 1) execute "nnoremap " . g:magit_show_magit_mapping . " :call magit#show_magit('v')" " }}} @@ -617,7 +619,7 @@ endfunction " 'v': vertical split " 'h': horizontal split " 'c': current buffer (should be used when opening vim in vimagit mode -function! magit#show_magit(display) +function! magit#show_magit(display, ...) if ( magit#utils#strip(system("git rev-parse --is-inside-work-tree")) != 'true' ) echoerr "Magit must be started from a git repository" return @@ -631,17 +633,28 @@ function! magit#show_magit(display) else throw 'parameter_error' endif + + let b:magit_default_show_all_files = g:magit_default_show_all_files + let b:magit_default_fold_level = g:magit_default_fold_level + + if ( a:0 > 0 ) + let b:magit_default_show_all_files = a:1 + endif + if ( a:0 > 1 ) + let b:magit_default_fold_level = a:2 + endif + + silent! execute "bdelete " . g:magit_buffer_name + execute "file " . g:magit_buffer_name + setlocal buftype=nofile setlocal bufhidden=delete setlocal noswapfile setlocal foldmethod=syntax - setlocal foldlevel=1 + let &l:foldlevel = b:magit_default_fold_level setlocal filetype=magit "setlocal readonly - silent! execute "bdelete " . g:magit_buffer_name - execute "file " . g:magit_buffer_name - call magit#utils#setbufnr(bufnr(g:magit_buffer_name)) call magit#sign#init() From bd4876986b9439406cc23dd7b6878a30fcf2f5e5 Mon Sep 17 00:00:00 2001 From: Jerome Reybert Date: Thu, 22 Oct 2015 23:48:03 +0200 Subject: [PATCH 29/46] autoload/magit/state.vim: move get_header to file object --- autoload/magit/state.vim | 9 ++++----- plugin/magit.vim | 2 +- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/autoload/magit/state.vim b/autoload/magit/state.vim index 160bd25..5e5c509 100644 --- a/autoload/magit/state.vim +++ b/autoload/magit/state.vim @@ -73,6 +73,7 @@ let s:file_template = { \ 'set_visible': function("magit#state#set_file_visible"), \ 'toggle_visible': function("magit#state#toggle_file_visible"), \ 'must_be_added': function("magit#state#must_be_added"), +\ 'get_header': function("magit#state#file_get_header"), \ 'get_hunks' : function("magit#state#file_get_hunks"), \ 'get_flat_hunks' : function("magit#state#file_get_flat_hunks"), \} @@ -95,13 +96,12 @@ function! magit#state#get_file(mode, filename, ...) dict return self.dict[a:mode][a:filename] endfunction -" magit#state#get_header: function accessor for diff header +" magit#state#file_get_header: function accessor for diff header " param[in] mode: can be staged or unstaged " param[in] filename: header of filename to access " return: List of diff header lines -function! magit#state#get_header(mode, filename) dict - let diff_dict_file = self.get_file(a:mode, a:filename, 0) - return diff_dict_file.diff.header +function! magit#state#file_get_header() dict + return self.diff.header endfunction " magit#state#add_file: method to add a file with all its @@ -225,7 +225,6 @@ endfunction let magit#state#state = { \ 'get_file': function("magit#state#get_file"), \ 'get_files': function("magit#state#get_files"), - \ 'get_header': function("magit#state#get_header"), \ 'add_file': function("magit#state#add_file"), \ 'update': function("magit#state#update"), \ 'dict': { 'staged': {}, 'unstaged': {}}, diff --git a/plugin/magit.vim b/plugin/magit.vim index addd413..ed66ec3 100644 --- a/plugin/magit.vim +++ b/plugin/magit.vim @@ -717,7 +717,7 @@ endfunction function! magit#stage_block(selection, discard) abort let section=mg_get_section() let filename=mg_get_filename() - let header = s:state.get_header(section, filename) + let header = s:state.get_file(section, filename).get_header() let file = s:state.get_file(section, filename, 0) if ( a:discard == 0 ) From b7cc91f30893d94898fc947ea6df9c6f0f4973e5 Mon Sep 17 00:00:00 2001 From: Jerome Reybert Date: Thu, 22 Oct 2015 23:48:55 +0200 Subject: [PATCH 30/46] autoload/magit/state.vim: rename function variable --- autoload/magit/state.vim | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/autoload/magit/state.vim b/autoload/magit/state.vim index 5e5c509..76d3c2b 100644 --- a/autoload/magit/state.vim +++ b/autoload/magit/state.vim @@ -119,44 +119,44 @@ function! magit#state#add_file(mode, status, filename, depth) dict if ( empty(diff_list) ) echoerr "diff command \"" . diff_cmd . "\" returned nothing" endif - let diff_dict_file = self.get_file(a:mode, a:filename, 1) - let diff_dict_file.exists = 1 - let diff_dict_file.status = a:status - let diff_dict_file.depth = a:depth + let file = self.get_file(a:mode, a:filename, 1) + let file.exists = 1 + let file.status = a:status + let file.depth = a:depth if ( a:status == '?' && getftype(a:filename) == 'link' ) - let diff_dict_file.symlink = resolve(a:filename) - call add(diff_dict_file.diff.header, 'no header') - let diff_dict_file.diff.hunks[0].header = 'New symbolic link file' + let file.symlink = resolve(a:filename) + call add(file.diff.header, 'no header') + let file.diff.hunks[0].header = 'New symbolic link file' elseif ( a:status == '?' && isdirectory(a:filename) == 1 ) - let diff_dict_file.dir = 1 + let file.dir = 1 for subfile in split(globpath(a:filename, '\(.[^.]*\|*\)'), '\n') call self.add_file(a:mode, a:status, subfile, a:depth + 1) endfor elseif ( a:status == '?' && getfsize(a:filename) == 0 ) - let diff_dict_file.empty = 1 - call add(diff_dict_file.diff.header, 'no header') - let diff_dict_file.diff.hunks[0].header = 'New empty file' + let file.empty = 1 + call add(file.diff.header, 'no header') + let file.diff.hunks[0].header = 'New empty file' elseif ( match(system("file --mime " . \ magit#utils#add_quotes(a:filename)), \ a:filename . ".*charset=binary") != -1 ) - let diff_dict_file.binary = 1 - call add(diff_dict_file.diff.header, 'no header') - let diff_dict_file.diff.hunks[0].header = 'Binary file' + let file.binary = 1 + call add(file.diff.header, 'no header') + let file.diff.hunks[0].header = 'Binary file' else let line = 0 " match( while ( line < len(diff_list) && diff_list[line] !~ "^@.*" ) - call add(diff_dict_file.diff.header, diff_list[line]) + call add(file.diff.header, diff_list[line]) let line += 1 endwhile - let hunk = diff_dict_file.diff.hunks[0] + let hunk = file.diff.hunks[0] let hunk.header = diff_list[line] for diff_line in diff_list[line+1 : -1] if ( diff_line =~ "^@.*" ) let hunk = deepcopy(s:hunk_template) - call add(diff_dict_file.diff.hunks, hunk) + call add(file.diff.hunks, hunk) let hunk.header = diff_line continue endif From 659e5049a02ff19638397edb35f45dc57a28db14 Mon Sep 17 00:00:00 2001 From: Jerome Reybert Date: Thu, 22 Oct 2015 23:52:40 +0200 Subject: [PATCH 31/46] autoload/magit/utils.vim: tabs VS spaces --- autoload/magit/utils.vim | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/autoload/magit/utils.vim b/autoload/magit/utils.vim index 1cc472b..091892c 100644 --- a/autoload/magit/utils.vim +++ b/autoload/magit/utils.vim @@ -139,16 +139,16 @@ endfunction " param[in] list: a List, can be nested or not " return: one dimensional list function! magit#utils#flatten(list) - let val = [] - for elem in a:list - if type(elem) == type([]) - call extend(val, magit#utils#flatten(elem)) - else - call extend(val, [elem]) - endif - unlet elem - endfor - return val + let val = [] + for elem in a:list + if type(elem) == type([]) + call extend(val, magit#utils#flatten(elem)) + else + call extend(val, [elem]) + endif + unlet elem + endfor + return val endfunction " magit#utils#append_file: helper function to append to a file From e407d5bb417f71735203c672e76e0fe1189018ab Mon Sep 17 00:00:00 2001 From: Jerome Reybert Date: Fri, 23 Oct 2015 00:45:15 +0200 Subject: [PATCH 32/46] plugin/magit.vim: ask user first if too much lines to display --- README.md | 10 ++++++++++ autoload/magit/state.vim | 14 ++++++++++++++ doc/vimagit.txt | 9 +++++++++ plugin/magit.vim | 14 ++++++++++++++ 4 files changed, 47 insertions(+) diff --git a/README.md b/README.md index 0d36951..e759ba6 100644 --- a/README.md +++ b/README.md @@ -219,6 +219,16 @@ When set to 2, filenames and hunks are unfolded. Default value is 1. > let g:magit_default_fold_level=[012] +#### g:magit_warning_max_lines + +This variable is the maximum number of diff lines that vimagit will display +without warning the user. If the number of diff lines to display is greater than +this variable, vimagit will ask a confirmation to the user before refreshing the +buffer. If user answer is 'yes', vimagit will display diff lines as expected. +If user answer is 'no', vimagit will close all file diffs before refreshing. +Default value is 10000. +> let g:magit_warning_max_lines=val + ## Installation The plugin hierarchy tree respects the vim plugin standard. It is compatible diff --git a/autoload/magit/state.vim b/autoload/magit/state.vim index 76d3c2b..74ed848 100644 --- a/autoload/magit/state.vim +++ b/autoload/magit/state.vim @@ -162,6 +162,9 @@ function! magit#state#add_file(mode, status, filename, depth) dict endif call add(hunk.lines, diff_line) endfor + if ( file.is_visible() ) + let self.nb_diff_lines += len(diff_list) + endif endif endfunction @@ -171,6 +174,7 @@ endfunction " else, its diff is discarded and regenrated " what is resilient is its 'visible' parameter function! magit#state#update() dict + let self.nb_diff_lines = 0 for diff_dict_mode in values(self.dict) for file in values(diff_dict_mode) let file.exists = 0 @@ -208,6 +212,14 @@ function! magit#state#update() dict endfor endfunction +function! magit#state#set_files_visible(is_visible) dict + for diff_dict_mode in values(self.dict) + for file in values(diff_dict_mode) + call file.set_visible(a:is_visible) + endfor + endfor +endfunction + " dict: structure containing all diffs " It is formatted as follow " { @@ -223,9 +235,11 @@ endfunction " }, " } let magit#state#state = { + \ 'nb_diff_lines': 0, \ 'get_file': function("magit#state#get_file"), \ 'get_files': function("magit#state#get_files"), \ 'add_file': function("magit#state#add_file"), + \ 'set_files_visible': function("magit#state#set_files_visible"), \ 'update': function("magit#state#update"), \ 'dict': { 'staged': {}, 'unstaged': {}}, \ } diff --git a/doc/vimagit.txt b/doc/vimagit.txt index 78cbdfe..98d1c3e 100644 --- a/doc/vimagit.txt +++ b/doc/vimagit.txt @@ -276,6 +276,15 @@ When set to 2, filenames and hunks are unfolded. Default value is 1. let g:magit_default_fold_level=[012] + *vimagit-g:magit_warning_max_lines* +This variable is the maximum number of diff lines that vimagit will display +without warning the user. If the number of diff lines to display is greater than +this variable, vimagit will ask a confirmation to the user before refreshing the +buffer. If user answer is 'yes', vimagit will display diff lines as expected. +If user answer is 'no', vimagit will close all file diffs before refreshing. +Defaulty value is 10000. +let g:magit_warning_max_lines=val + =============================================================================== 6. FAQ *vimagit-FAQ* diff --git a/plugin/magit.vim b/plugin/magit.vim index ed66ec3..b17c837 100644 --- a/plugin/magit.vim +++ b/plugin/magit.vim @@ -62,6 +62,8 @@ call s:set('g:magit_show_help', 1) call s:set('g:magit_default_show_all_files', 0) call s:set('g:magit_default_fold_level', 1) +call s:set('g:magit_warning_max_lines', 10000) + execute "nnoremap " . g:magit_show_magit_mapping . " :call magit#show_magit('v')" " }}} @@ -591,6 +593,17 @@ function! magit#update_buffer() call mg_get_commit_section() endif call s:state.update() + + if ( s:state.nb_diff_lines > g:magit_warning_max_lines && b:magit_warning_answered_yes == 0 ) + let ret = input("There are " . s:state.nb_diff_lines . " diff lines to display. Do you want to display all diffs? y(es) / N(o) : ", "") + if ( ret !~? '^y\%(e\%(s\)\?\)\?$' ) + let b:magit_default_show_all_files = 0 + call s:state.set_files_visible(0) + else + let b:magit_warning_answered_yes = 1 + endif + endif + call mg_get_staged_section('staged') call mg_get_staged_section('unstaged') call mg_get_stashes() @@ -636,6 +649,7 @@ function! magit#show_magit(display, ...) let b:magit_default_show_all_files = g:magit_default_show_all_files let b:magit_default_fold_level = g:magit_default_fold_level + let b:magit_warning_answered_yes = 0 if ( a:0 > 0 ) let b:magit_default_show_all_files = a:1 From be4da3abfdc2771ddea5fd34069605094a6b678c Mon Sep 17 00:00:00 2001 From: Jerome Reybert Date: Fri, 23 Oct 2015 11:16:34 +0200 Subject: [PATCH 33/46] README.md: fix links --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index e759ba6..b614d38 100644 --- a/README.md +++ b/README.md @@ -104,9 +104,9 @@ It takes 3 parameters: new buffer - 'c', magit is displayed in current buffer * show_all_files: define is file diffs are shown by default for this session - (see [g:magit_default_show_all_files](#g_magit_default_show_all_files)) + (see [g:magit_default_show_all_files](#gmagit_default_show_all_files)) * foldlevel: set default magit buffer foldlevel for this session - (see [g:magit_default_fold_level](#g_magit_default_fold_level)) + (see [g:magit_default_fold_level](#gmagit_default_fold_level)) #### :Magit * open magit buffer. From a76323f426fe687e6a95c5a6431167cdc81be687 Mon Sep 17 00:00:00 2001 From: Jerome Reybert Date: Fri, 23 Oct 2015 11:29:18 +0200 Subject: [PATCH 34/46] README.md: update readme --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index b614d38..27810b2 100644 --- a/README.md +++ b/README.md @@ -32,13 +32,15 @@ Take a look at [TL;DR](#tldr) to start using it immediatly. * [x] Modify in line the content just before staging it. * [x] Visualize stashes. Apply, pop, drop are on going. * [x] Add file to .gitignore file. -* [ ] Chase all corner cases. Please remember that vimagit is at an early development stage. If you try vimagit and nothing is working, please don't throw it, fill an issue on github :heart: ! +* [ ] Chase all corner cases. Please remember that vimagit is at an early development stage. If you try vimagit and nothing is working, please don't throw it, fill an [issue](https://github.com/jreybert/vimagit/issues/new) on github :heart: ! More to come: * Vizualize and checkout branches. * Go through history, cherry-pick changes. * Something is missing? Open an [issue](https://github.com/jreybert/vimagit/issues/new)! +The plugin is fully tested for various versions of vim on linux: vim 7.3.249, vim 7.4.273, neovim. It is also tested for macos X: vim, macvim and neovim. Anyway, if you feel that vimagit behaves oddly (slow refresh, weird display order...) please fill an [issue](https://github.com/jreybert/vimagit/issues/new). + For the most enthusiastic, you can try the branch [next](https://github.com/jreybert/vimagit/tree/next). It is quite stable, just check its travis status before fetching it. > Why should I use vimagit, there are already plethora git plugins for vim? From 868dab5b1934186852b66472a47ae892b5131ed0 Mon Sep 17 00:00:00 2001 From: Jerome Reybert Date: Fri, 23 Oct 2015 13:02:55 +0200 Subject: [PATCH 35/46] autoload/magit/utils.vim: new function magit#utils#is_binary to test file type --- autoload/magit/state.vim | 4 +--- autoload/magit/utils.vim | 7 +++++++ 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/autoload/magit/state.vim b/autoload/magit/state.vim index 74ed848..ba57009 100644 --- a/autoload/magit/state.vim +++ b/autoload/magit/state.vim @@ -136,9 +136,7 @@ function! magit#state#add_file(mode, status, filename, depth) dict let file.empty = 1 call add(file.diff.header, 'no header') let file.diff.hunks[0].header = 'New empty file' - elseif ( match(system("file --mime " . - \ magit#utils#add_quotes(a:filename)), - \ a:filename . ".*charset=binary") != -1 ) + elseif ( magit#utils#is_binary(magit#utils#add_quotes(a:filename))) let file.binary = 1 call add(file.diff.header, 'no header') let file.diff.hunks[0].header = 'Binary file' diff --git a/autoload/magit/utils.vim b/autoload/magit/utils.vim index 091892c..17c6511 100644 --- a/autoload/magit/utils.vim +++ b/autoload/magit/utils.vim @@ -33,6 +33,13 @@ function! magit#utils#git_dir() return s:magit_git_dir endfunction +" s:magit#utils#is_binary: check if file is a binary file +" param[in] filename: the file path. it must quoted if it contains spaces +function! magit#utils#is_binary(filename) + return ( match(system("file --mime " . a:filename ), + \ a:filename . ".*charset=binary") != -1 ) +endfunction + " s:magit_cd_cmd: plugin variable to choose lcd/cd command, 'lcd' if exists, " 'cd' otherwise let s:magit_cd_cmd = exists('*haslocaldir') && haslocaldir() ? 'lcd ' : 'cd ' From 0e7ddb6c27471b0cab0e7e7575e6704cbe0c710e Mon Sep 17 00:00:00 2001 From: Jerome Reybert Date: Fri, 23 Oct 2015 13:05:10 +0200 Subject: [PATCH 36/46] autoload/magit/state.vim: manage submodules (fix #12) --- autoload/magit/state.vim | 15 +++++++++++---- autoload/magit/utils.vim | 14 ++++++++++++++ common/magit_common.vim | 1 + plugin/magit.vim | 6 +++++- 4 files changed, 31 insertions(+), 5 deletions(-) diff --git a/autoload/magit/state.vim b/autoload/magit/state.vim index ba57009..e8e9787 100644 --- a/autoload/magit/state.vim +++ b/autoload/magit/state.vim @@ -22,7 +22,8 @@ function! magit#state#must_be_added() dict return ( self.empty == 1 || \ self.symlink != '' || \ self.dir != 0 || - \ self.binary == 1 ) + \ self.binary == 1 || + \ self.submodule == 1 ) endfunction " magit#state#file_get_hunks: function accessor for hunks objects @@ -66,6 +67,7 @@ let s:file_template = { \ 'empty': 0, \ 'dir': 0, \ 'binary': 0, +\ 'submodule': 0, \ 'symlink': '', \ 'diff': s:diff_template, \ 'is_dir': function("magit#state#is_file_dir"), @@ -125,8 +127,14 @@ function! magit#state#add_file(mode, status, filename, depth) dict let file.depth = a:depth if ( a:status == '?' && getftype(a:filename) == 'link' ) let file.symlink = resolve(a:filename) - call add(file.diff.header, 'no header') let file.diff.hunks[0].header = 'New symbolic link file' + elseif ( magit#utils#is_submodule(a:filename)) + let file.submodule = 1 + let file.diff.hunks[0].header = '' + let file.diff.hunks[0].lines = diff_list + if ( file.is_visible() ) + let self.nb_diff_lines += len(diff_list) + endif elseif ( a:status == '?' && isdirectory(a:filename) == 1 ) let file.dir = 1 for subfile in split(globpath(a:filename, '\(.[^.]*\|*\)'), '\n') @@ -134,11 +142,9 @@ function! magit#state#add_file(mode, status, filename, depth) dict endfor elseif ( a:status == '?' && getfsize(a:filename) == 0 ) let file.empty = 1 - call add(file.diff.header, 'no header') let file.diff.hunks[0].header = 'New empty file' elseif ( magit#utils#is_binary(magit#utils#add_quotes(a:filename))) let file.binary = 1 - call add(file.diff.header, 'no header') let file.diff.hunks[0].header = 'Binary file' else let line = 0 @@ -184,6 +190,7 @@ function! magit#state#update() dict let dir = getcwd() try call magit#utils#lcd(magit#utils#top_dir()) + call magit#utils#refresh_submodule_list() for [mode, diff_dict_mode] in items(self.dict) let status_list = magit#git#get_status() for file_status in status_list diff --git a/autoload/magit/utils.vim b/autoload/magit/utils.vim index 17c6511..b9e4cde 100644 --- a/autoload/magit/utils.vim +++ b/autoload/magit/utils.vim @@ -40,6 +40,20 @@ function! magit#utils#is_binary(filename) \ a:filename . ".*charset=binary") != -1 ) endfunction +let s:submodule_list = [] +" magit#utils#refresh_submodule_list: this function refresh the List s:submodule_list +" magit#utils#is_submodule() is using s:submodule_list +function! magit#utils#refresh_submodule_list() + let s:submodule_list = map(split(system("git submodule status"), "\n"), 'split(v:val)[1]') +endfunction + +" magit#utils#is_submodule search if dirname is in s:submodule_list +" param[in] dirname: must end with / +" INFO: must be called from top work tree +function! magit#utils#is_submodule(dirname) + return ( index(s:submodule_list, a:dirname) != -1 ) +endfunction + " s:magit_cd_cmd: plugin variable to choose lcd/cd command, 'lcd' if exists, " 'cd' otherwise let s:magit_cd_cmd = exists('*haslocaldir') && haslocaldir() ? 'lcd ' : 'cd ' diff --git a/common/magit_common.vim b/common/magit_common.vim index 9a18e91..47d488b 100644 --- a/common/magit_common.vim +++ b/common/magit_common.vim @@ -22,6 +22,7 @@ let g:magit_git_status_code = { \ 'E': 'empty', \ 'L': 'symlink', \ 'N': 'new dir', + \ 'S': 'submodule', \ } " Regular expressions used to select blocks diff --git a/plugin/magit.vim b/plugin/magit.vim index b17c837..94e2a3a 100644 --- a/plugin/magit.vim +++ b/plugin/magit.vim @@ -156,6 +156,8 @@ function! s:mg_display_files(mode, curdir, depth) put =g:magit_git_status_code.E . ': ' . filename elseif ( file.symlink != '' ) put =g:magit_git_status_code.L . ': ' . filename . ' -> ' . file.symlink + elseif ( file.submodule == 1 ) + put =g:magit_git_status_code.S . ': ' . filename elseif ( file.dir != 0 ) put =g:magit_git_status_code.N . ': ' . filename if ( file.visible == 1 ) @@ -174,7 +176,9 @@ function! s:mg_display_files(mode, curdir, depth) endif let hunks = file.get_hunks() for hunk in hunks - silent put =hunk.header + if ( hunk.header != '' ) + silent put =hunk.header + endif silent put =hunk.lines endfor put ='' From 632c241316643d42c6f3d0fba21a1fbbce729723 Mon Sep 17 00:00:00 2001 From: Jerome Reybert Date: Fri, 23 Oct 2015 14:19:45 +0200 Subject: [PATCH 37/46] autoload/magit/state.vim: add get_filename_header function --- autoload/magit/state.vim | 41 +++++++++++++++++++++++++++++----------- plugin/magit.vim | 18 +++++++----------- 2 files changed, 37 insertions(+), 22 deletions(-) diff --git a/autoload/magit/state.vim b/autoload/magit/state.vim index e8e9787..6254807 100644 --- a/autoload/magit/state.vim +++ b/autoload/magit/state.vim @@ -63,6 +63,7 @@ let s:diff_template = { " WARNING: this variable must be deepcopy()'ied let s:file_template = { \ 'exists': 0, +\ 'filename': '', \ 'status': '', \ 'empty': 0, \ 'dir': 0, @@ -78,6 +79,7 @@ let s:file_template = { \ 'get_header': function("magit#state#file_get_header"), \ 'get_hunks' : function("magit#state#file_get_hunks"), \ 'get_flat_hunks' : function("magit#state#file_get_flat_hunks"), +\ 'get_filename_header' : function("magit#state#file_get_filename_header"), \} " magit#state#get_file: function accessor for file @@ -92,6 +94,7 @@ function! magit#state#get_file(mode, filename, ...) dict if ( file_exists == 0 && create == 1 ) let self.dict[a:mode][a:filename] = deepcopy(s:file_template) let self.dict[a:mode][a:filename].visible = b:magit_default_show_all_files + let self.dict[a:mode][a:filename].filename = a:filename elseif ( file_exists == 0 && create == 0 ) throw 'file_doesnt_exists' endif @@ -106,6 +109,14 @@ function! magit#state#file_get_header() dict return self.diff.header endfunction +function! magit#state#file_get_filename_header() dict + if ( self.status == 'L' ) + return g:magit_git_status_code.L . ': ' . self.filename . ' -> ' . self.symlink + else + return g:magit_git_status_code[self.status] . ': ' . self.filename + endif +endfunction + " magit#state#add_file: method to add a file with all its " properties (filename, exists, status, header and hunks) " param[in] mode: can be staged or unstaged @@ -123,12 +134,16 @@ function! magit#state#add_file(mode, status, filename, depth) dict endif let file = self.get_file(a:mode, a:filename, 1) let file.exists = 1 + let file.status = a:status let file.depth = a:depth + if ( a:status == '?' && getftype(a:filename) == 'link' ) + let file.status = 'L' let file.symlink = resolve(a:filename) let file.diff.hunks[0].header = 'New symbolic link file' elseif ( magit#utils#is_submodule(a:filename)) + let file.status = 'S' let file.submodule = 1 let file.diff.hunks[0].header = '' let file.diff.hunks[0].lines = diff_list @@ -136,11 +151,13 @@ function! magit#state#add_file(mode, status, filename, depth) dict let self.nb_diff_lines += len(diff_list) endif elseif ( a:status == '?' && isdirectory(a:filename) == 1 ) + let file.status = 'N' let file.dir = 1 for subfile in split(globpath(a:filename, '\(.[^.]*\|*\)'), '\n') call self.add_file(a:mode, a:status, subfile, a:depth + 1) endfor elseif ( a:status == '?' && getfsize(a:filename) == 0 ) + let file.status = 'E' let file.empty = 1 let file.diff.hunks[0].header = 'New empty file' elseif ( magit#utils#is_binary(magit#utils#add_quotes(a:filename))) @@ -154,18 +171,20 @@ function! magit#state#add_file(mode, status, filename, depth) dict let line += 1 endwhile - let hunk = file.diff.hunks[0] - let hunk.header = diff_list[line] + if ( line < len(diff_list) ) + let hunk = file.diff.hunks[0] + let hunk.header = diff_list[line] - for diff_line in diff_list[line+1 : -1] - if ( diff_line =~ "^@.*" ) - let hunk = deepcopy(s:hunk_template) - call add(file.diff.hunks, hunk) - let hunk.header = diff_line - continue - endif - call add(hunk.lines, diff_line) - endfor + for diff_line in diff_list[line+1 : -1] + if ( diff_line =~ "^@.*" ) + let hunk = deepcopy(s:hunk_template) + call add(file.diff.hunks, hunk) + let hunk.header = diff_line + continue + endif + call add(hunk.lines, diff_line) + endfor + endif if ( file.is_visible() ) let self.nb_diff_lines += len(diff_list) endif diff --git a/plugin/magit.vim b/plugin/magit.vim index 94e2a3a..4b5ee38 100644 --- a/plugin/magit.vim +++ b/plugin/magit.vim @@ -152,21 +152,15 @@ function! s:mg_display_files(mode, curdir, depth) if ( file.depth != a:depth || filename !~ a:curdir . '.*' ) continue endif - if ( file.empty == 1 ) - put =g:magit_git_status_code.E . ': ' . filename - elseif ( file.symlink != '' ) - put =g:magit_git_status_code.L . ': ' . filename . ' -> ' . file.symlink - elseif ( file.submodule == 1 ) - put =g:magit_git_status_code.S . ': ' . filename - elseif ( file.dir != 0 ) - put =g:magit_git_status_code.N . ': ' . filename + put =file.get_filename_header() + + if ( file.dir != 0 ) if ( file.visible == 1 ) call s:mg_display_files(a:mode, filename, a:depth + 1) continue endif - else - put =g:magit_git_status_code[file.status] . ': ' . filename endif + if ( file.visible == 0 ) put ='' continue @@ -179,7 +173,9 @@ function! s:mg_display_files(mode, curdir, depth) if ( hunk.header != '' ) silent put =hunk.header endif - silent put =hunk.lines + if ( !empty(hunk.lines) ) + silent put =hunk.lines + endif endfor put ='' endfor From f74c95579e31227c4e8c07ef03b05c169c47ff11 Mon Sep 17 00:00:00 2001 From: Jerome Reybert Date: Fri, 23 Oct 2015 17:42:40 +0200 Subject: [PATCH 38/46] test/run.sh: new test HEAD SHA1 --- test/run.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/run.sh b/test/run.sh index 4054ace..751d8ca 100755 --- a/test/run.sh +++ b/test/run.sh @@ -26,7 +26,8 @@ fi pushd $TEST_PATH git config --local user.email 'tester@vimagit.org' git config --local user.name 'vimagit tester' -export TEST_HEAD_SHA1='8e589e4' +export TEST_HEAD_SHA1='dcacf08' +git submodule update git show $TEST_HEAD_SHA1 --stat git reset $TEST_HEAD_SHA1~1 && git status --porcelain && git reset --hard $TEST_HEAD_SHA1 popd From 8fb1725ab44bca819b26edb4fd6f3adf26252f60 Mon Sep 17 00:00:00 2001 From: Jerome Reybert Date: Fri, 23 Oct 2015 21:26:26 +0200 Subject: [PATCH 39/46] autoload/magit/state.vim: fix hidden file listing in new directories --- autoload/magit/state.vim | 2 +- autoload/magit/utils.vim | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/autoload/magit/state.vim b/autoload/magit/state.vim index 6254807..8781329 100644 --- a/autoload/magit/state.vim +++ b/autoload/magit/state.vim @@ -153,7 +153,7 @@ function! magit#state#add_file(mode, status, filename, depth) dict elseif ( a:status == '?' && isdirectory(a:filename) == 1 ) let file.status = 'N' let file.dir = 1 - for subfile in split(globpath(a:filename, '\(.[^.]*\|*\)'), '\n') + for subfile in magit#utils#ls_all(a:filename) call self.add_file(a:mode, a:status, subfile, a:depth + 1) endfor elseif ( a:status == '?' && getfsize(a:filename) == 0 ) diff --git a/autoload/magit/utils.vim b/autoload/magit/utils.vim index b9e4cde..a8ec6ef 100644 --- a/autoload/magit/utils.vim +++ b/autoload/magit/utils.vim @@ -40,6 +40,13 @@ function! magit#utils#is_binary(filename) \ a:filename . ".*charset=binary") != -1 ) endfunction +" magit#utils#ls_all: list all files (including hidden ones) in a given path +" return : list of filenames +function! magit#utils#ls_all(path) + return split(globpath(a:path, '.[^.]*', 1) . "\n" . + \ globpath(a:path, '*', 1), '\n') +endfunction + let s:submodule_list = [] " magit#utils#refresh_submodule_list: this function refresh the List s:submodule_list " magit#utils#is_submodule() is using s:submodule_list From 7722c2510711ed88061429fbc71dccf0e03514dd Mon Sep 17 00:00:00 2001 From: Jerome Reybert Date: Fri, 23 Oct 2015 22:17:23 +0200 Subject: [PATCH 40/46] test/addDir: add new tests for directories --- test/addDir.vader | 69 +++++++++++++++++++++++ test/addDir/addDir_all_diff.expect | 42 ++++++++++++++ test/addDir/addDir_hidden_diff.expect | 6 ++ test/addDir/addDir_subdir_diff.expect | 18 ++++++ test/addDir/addDir_subsubfile_diff.expect | 6 ++ test/run.sh | 2 +- test/test.config | 1 + test/utils.vim | 10 +++- 8 files changed, 151 insertions(+), 3 deletions(-) create mode 100644 test/addDir.vader create mode 100644 test/addDir/addDir_all_diff.expect create mode 100644 test/addDir/addDir_hidden_diff.expect create mode 100644 test/addDir/addDir_subdir_diff.expect create mode 100644 test/addDir/addDir_subsubfile_diff.expect diff --git a/test/addDir.vader b/test/addDir.vader new file mode 100644 index 0000000..75de58f --- /dev/null +++ b/test/addDir.vader @@ -0,0 +1,69 @@ +Include: setup.inc + +Execute (Stage untracked directory closed): + call Cd_test_sub() + Magit + call Search_file('unstaged') + call magit#open_close_folding(0) + call Cursor_position() + call magit#stage_file() + call Cd_test() + let diff=Git_diff('staged') + call Expect_diff(g:test_script_dir . 'addDir/addDir_all_diff.expect', diff) + call Git_cmd("git reset") + +Execute (Stage untracked directory opened): + call Cd_test_sub() + Magit + call Search_file('unstaged') + call magit#open_close_folding(1) + call Cursor_position() + call magit#stage_file() + call Cd_test() + let diff=Git_diff('staged') + call Expect_diff(g:test_script_dir . 'addDir/addDir_all_diff.expect', diff) + call Git_cmd("git reset") + +Execute (Stage untracked hidden file): + call Cd_test_sub() + Magit + call Search_file('unstaged') + call magit#open_close_folding(1) + call Search_pattern(Get_filename() . '.hidden') + call Cursor_position() + call magit#stage_file() + call Cd_test() + let diff=Git_diff('staged') + call Expect_diff(g:test_script_dir . 'addDir/addDir_hidden_diff.expect', diff) + call Git_cmd("git reset") + +Execute (Stage untracked subdir file): + call Cd_test_sub() + Magit + call Search_file('unstaged') + call magit#open_close_folding(1) + call Search_pattern(Get_filename() . 'newsubdir') + call Cursor_position() + call magit#stage_file() + call Cd_test() + let diff=Git_diff('staged') + call Expect_diff(g:test_script_dir . 'addDir/addDir_subdir_diff.expect', diff) + call Git_cmd("git reset") + +Execute (Stage untracked subsubfile): + call Cd_test_sub() + Magit + call Search_file('unstaged') + call magit#open_close_folding(1) + call Search_pattern(Get_filename() . 'newsubdir') + call magit#open_close_folding(1) + call Search_pattern(Get_filename() . 'newsubdir/e') + call Cursor_position() + call magit#stage_file() + call Cd_test() + let diff=Git_diff('staged') + call Expect_diff(g:test_script_dir . 'addDir/addDir_subsubfile_diff.expect', diff) + call Git_cmd("git reset") + +Include: cleanup.inc + diff --git a/test/addDir/addDir_all_diff.expect b/test/addDir/addDir_all_diff.expect new file mode 100644 index 0000000..1b81b04 --- /dev/null +++ b/test/addDir/addDir_all_diff.expect @@ -0,0 +1,42 @@ +diff --git newdir/.hidden newdir/.hidden +new file mode 100644 +--- /dev/null ++++ newdir/.hidden +@@ -0,0 +1 @@ ++this is an hidden file +diff --git newdir/a newdir/a +new file mode 100644 +--- /dev/null ++++ newdir/a +@@ -0,0 +1 @@ ++a +diff --git newdir/b newdir/b +new file mode 100644 +--- /dev/null ++++ newdir/b +@@ -0,0 +1 @@ ++b +diff --git newdir/c newdir/c +new file mode 100644 +--- /dev/null ++++ newdir/c +@@ -0,0 +1 @@ ++c +diff --git newdir/newsubdir/d newdir/newsubdir/d +new file mode 100644 +--- /dev/null ++++ newdir/newsubdir/d +@@ -0,0 +1 @@ ++d +diff --git newdir/newsubdir/e newdir/newsubdir/e +new file mode 100644 +--- /dev/null ++++ newdir/newsubdir/e +@@ -0,0 +1 @@ ++e +diff --git newdir/newsubdir/f newdir/newsubdir/f +new file mode 100644 +--- /dev/null ++++ newdir/newsubdir/f +@@ -0,0 +1 @@ ++f diff --git a/test/addDir/addDir_hidden_diff.expect b/test/addDir/addDir_hidden_diff.expect new file mode 100644 index 0000000..5168c5a --- /dev/null +++ b/test/addDir/addDir_hidden_diff.expect @@ -0,0 +1,6 @@ +diff --git newdir/.hidden newdir/.hidden +new file mode 100644 +--- /dev/null ++++ newdir/.hidden +@@ -0,0 +1 @@ ++this is an hidden file diff --git a/test/addDir/addDir_subdir_diff.expect b/test/addDir/addDir_subdir_diff.expect new file mode 100644 index 0000000..f44ecbd --- /dev/null +++ b/test/addDir/addDir_subdir_diff.expect @@ -0,0 +1,18 @@ +diff --git newdir/newsubdir/d newdir/newsubdir/d +new file mode 100644 +--- /dev/null ++++ newdir/newsubdir/d +@@ -0,0 +1 @@ ++d +diff --git newdir/newsubdir/e newdir/newsubdir/e +new file mode 100644 +--- /dev/null ++++ newdir/newsubdir/e +@@ -0,0 +1 @@ ++e +diff --git newdir/newsubdir/f newdir/newsubdir/f +new file mode 100644 +--- /dev/null ++++ newdir/newsubdir/f +@@ -0,0 +1 @@ ++f diff --git a/test/addDir/addDir_subsubfile_diff.expect b/test/addDir/addDir_subsubfile_diff.expect new file mode 100644 index 0000000..a62cdbd --- /dev/null +++ b/test/addDir/addDir_subsubfile_diff.expect @@ -0,0 +1,6 @@ +diff --git newdir/newsubdir/e newdir/newsubdir/e +new file mode 100644 +--- /dev/null ++++ newdir/newsubdir/e +@@ -0,0 +1 @@ ++e diff --git a/test/run.sh b/test/run.sh index 751d8ca..2648c20 100755 --- a/test/run.sh +++ b/test/run.sh @@ -26,7 +26,7 @@ fi pushd $TEST_PATH git config --local user.email 'tester@vimagit.org' git config --local user.name 'vimagit tester' -export TEST_HEAD_SHA1='dcacf08' +export TEST_HEAD_SHA1='bb86d5f' git submodule update git show $TEST_HEAD_SHA1 --stat git reset $TEST_HEAD_SHA1~1 && git status --porcelain && git reset --hard $TEST_HEAD_SHA1 diff --git a/test/test.config b/test/test.config index f99a8de..02f9a29 100644 --- a/test/test.config +++ b/test/test.config @@ -5,4 +5,5 @@ declare -A test_scripts=( [addSelect.vader]='books/models.py' [renameFile.vader]='manage.py|manage\ with\ spaces.py;djooks/settings\ with\ spaces.py|djooks/settings_without_spaces.py' [ignoreFile.vader]='bootstrap' + [addDir.vader]='newdir\/' ) diff --git a/test/utils.vim b/test/utils.vim index 4410147..9bca69a 100644 --- a/test/utils.vim +++ b/test/utils.vim @@ -77,10 +77,16 @@ function! Git_add_quotes(filename) endfunction " helper function to get the diff of a file, in staged or unstaged mode -function! Git_diff(state, file) +function! Git_diff(state, ...) let staged_flag = ( a:state == 'staged' ) ? ' --staged ' : '' + if ( a:0 == 1 ) + let file = " -- " . Git_add_quotes(a:file) + else + let file = "" + endif + let diff_cmd="git diff --no-color --no-ext-diff --src-prefix='' --dst-prefix='' " . - \ staged_flag . " -- " . Git_add_quotes(a:file) . + \ staged_flag . file . \ " | \\grep -v " . g:index_regex return Git_cmd(diff_cmd) endfunction From 46a502ee5ab6a022865fe876805dab38ff384450 Mon Sep 17 00:00:00 2001 From: Jerome Reybert Date: Fri, 23 Oct 2015 22:43:32 +0200 Subject: [PATCH 41/46] test/addSubmodule: add submodule test --- test/addSubmodule.vader | 28 ++++++++++++++++++++++ test/addSubmodule/addSubmodule_diff.expect | 9 +++++++ test/test.config | 1 + 3 files changed, 38 insertions(+) create mode 100644 test/addSubmodule.vader create mode 100644 test/addSubmodule/addSubmodule_diff.expect diff --git a/test/addSubmodule.vader b/test/addSubmodule.vader new file mode 100644 index 0000000..58450fc --- /dev/null +++ b/test/addSubmodule.vader @@ -0,0 +1,28 @@ +Include: setup.inc + +Execute (Stage untracked directory closed): + call Cd_test_sub() + Magit + call Search_file('unstaged') + call magit#open_close_folding(0) + call Cursor_position() + call magit#stage_file() + call Cd_test() + let diff=Git_diff('staged') + call Expect_diff(g:test_script_dir . 'addSubmodule/addSubmodule_diff.expect', diff) + call Git_cmd("git reset") + +Execute (Stage untracked directory opened): + call Cd_test_sub() + Magit + call Search_file('unstaged') + call magit#open_close_folding(1) + call Move_relative(+4) + call Cursor_position() + call magit#stage_file() + call Cd_test() + let diff=Git_diff('staged') + call Expect_diff(g:test_script_dir . 'addSubmodule/addSubmodule_diff.expect', diff) + call Git_cmd("git reset") + +Include: cleanup.inc diff --git a/test/addSubmodule/addSubmodule_diff.expect b/test/addSubmodule/addSubmodule_diff.expect new file mode 100644 index 0000000..1f38fa5 --- /dev/null +++ b/test/addSubmodule/addSubmodule_diff.expect @@ -0,0 +1,9 @@ +Submodule subdjooks a63bc77..6efcd49: + > books/models.py: first cover image support + > books/models.py: create Book entry with an isbn_search + > books/: add isbn_search module + > books/models.py: add BookManager + > migrations + > urls: add books urls in site urls + > views.py: fix typo + > urls: comment unsupported url diff --git a/test/test.config b/test/test.config index 02f9a29..ec70c72 100644 --- a/test/test.config +++ b/test/test.config @@ -6,4 +6,5 @@ declare -A test_scripts=( [renameFile.vader]='manage.py|manage\ with\ spaces.py;djooks/settings\ with\ spaces.py|djooks/settings_without_spaces.py' [ignoreFile.vader]='bootstrap' [addDir.vader]='newdir\/' + [addSubmodule.vader]='subdjooks' ) From 05b5970003ea2aa96088e76b5a8bc033ef71db67 Mon Sep 17 00:00:00 2001 From: Jerome Reybert Date: Fri, 23 Oct 2015 22:49:39 +0200 Subject: [PATCH 42/46] test/utils.vim: fix Git_diff --- test/utils.vim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/utils.vim b/test/utils.vim index 9bca69a..78cc63d 100644 --- a/test/utils.vim +++ b/test/utils.vim @@ -80,7 +80,7 @@ endfunction function! Git_diff(state, ...) let staged_flag = ( a:state == 'staged' ) ? ' --staged ' : '' if ( a:0 == 1 ) - let file = " -- " . Git_add_quotes(a:file) + let file = " -- " . Git_add_quotes(a:1) else let file = "" endif From 7efcc8b8dc9053af679697641e78a286bf4f3630 Mon Sep 17 00:00:00 2001 From: Jerome Reybert Date: Fri, 23 Oct 2015 23:42:09 +0200 Subject: [PATCH 43/46] test/run.sh: let vader to be less chatty --- .travis.yml | 2 +- test/run.sh | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 184293b..ea98bab 100644 --- a/.travis.yml +++ b/.travis.yml @@ -62,7 +62,7 @@ install: before_script: - git clone https://github.com/jreybert/djooks - - git clone https://github.com/junegunn/vader.vim + - git clone https://github.com/jreybert/vader.vim script: - ./test/run.sh . vader.vim djooks $VIM_VERSION diff --git a/test/run.sh b/test/run.sh index 2648c20..e6f4f54 100755 --- a/test/run.sh +++ b/test/run.sh @@ -75,6 +75,7 @@ for script in ${!test_scripts[@]}; do set rtp+=$VADER_PATH filetype plugin indent on syntax enable + let g:vader_show_version=0 EOF) -c "Vader! $VIMAGIT_PATH/test/$script" done From 33518fc661b1e092e4f77456a89032b35097b759 Mon Sep 17 00:00:00 2001 From: Jerome Reybert Date: Sat, 24 Oct 2015 09:54:19 +0200 Subject: [PATCH 44/46] test/addSubmodule: try to fix integration --- test/addSubmodule.vader | 8 ++++---- test/addSubmodule/addSubmodule_diff.expect | 9 --------- test/addSubmodule/addSubmodule_status.expect | 1 + test/setup.inc | 2 +- 4 files changed, 6 insertions(+), 14 deletions(-) delete mode 100644 test/addSubmodule/addSubmodule_diff.expect create mode 100644 test/addSubmodule/addSubmodule_status.expect diff --git a/test/addSubmodule.vader b/test/addSubmodule.vader index 58450fc..3b52b06 100644 --- a/test/addSubmodule.vader +++ b/test/addSubmodule.vader @@ -8,8 +8,8 @@ Execute (Stage untracked directory closed): call Cursor_position() call magit#stage_file() call Cd_test() - let diff=Git_diff('staged') - call Expect_diff(g:test_script_dir . 'addSubmodule/addSubmodule_diff.expect', diff) + let diff=Git_status(Get_filename()) + call Expect_diff(g:test_script_dir . 'addSubmodule/addSubmodule_status.expect', diff) call Git_cmd("git reset") Execute (Stage untracked directory opened): @@ -21,8 +21,8 @@ Execute (Stage untracked directory opened): call Cursor_position() call magit#stage_file() call Cd_test() - let diff=Git_diff('staged') - call Expect_diff(g:test_script_dir . 'addSubmodule/addSubmodule_diff.expect', diff) + let diff=Git_status(Get_filename()) + call Expect_diff(g:test_script_dir . 'addSubmodule/addSubmodule_status.expect', diff) call Git_cmd("git reset") Include: cleanup.inc diff --git a/test/addSubmodule/addSubmodule_diff.expect b/test/addSubmodule/addSubmodule_diff.expect deleted file mode 100644 index 1f38fa5..0000000 --- a/test/addSubmodule/addSubmodule_diff.expect +++ /dev/null @@ -1,9 +0,0 @@ -Submodule subdjooks a63bc77..6efcd49: - > books/models.py: first cover image support - > books/models.py: create Book entry with an isbn_search - > books/: add isbn_search module - > books/models.py: add BookManager - > migrations - > urls: add books urls in site urls - > views.py: fix typo - > urls: comment unsupported url diff --git a/test/addSubmodule/addSubmodule_status.expect b/test/addSubmodule/addSubmodule_status.expect new file mode 100644 index 0000000..99fc911 --- /dev/null +++ b/test/addSubmodule/addSubmodule_status.expect @@ -0,0 +1 @@ +M subdjooks diff --git a/test/setup.inc b/test/setup.inc index 3b1816e..bb56adc 100644 --- a/test/setup.inc +++ b/test/setup.inc @@ -1,6 +1,6 @@ Execute (setup): source $VIMAGIT_PATH/test/utils.vim call Cd_test() - call system("git reset --mixed " . $TEST_HEAD_SHA1 . "~1") + call system("git reset " . $TEST_HEAD_SHA1 . "~1") call Git_verbose_log(system("git status --porcelain")) call Cd_test_sub() From 65c2e6ff61be4b370919926899838c684df3d06f Mon Sep 17 00:00:00 2001 From: Jerome Reybert Date: Sat, 24 Oct 2015 10:04:40 +0200 Subject: [PATCH 45/46] test/test.config: temporarly disable submodule test --- test/test.config | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/test.config b/test/test.config index ec70c72..e137682 100644 --- a/test/test.config +++ b/test/test.config @@ -6,5 +6,5 @@ declare -A test_scripts=( [renameFile.vader]='manage.py|manage\ with\ spaces.py;djooks/settings\ with\ spaces.py|djooks/settings_without_spaces.py' [ignoreFile.vader]='bootstrap' [addDir.vader]='newdir\/' - [addSubmodule.vader]='subdjooks' +#[addSubmodule.vader]='subdjooks' ) From ee374116354cd2b018298327c0fad47c2fbde94a Mon Sep 17 00:00:00 2001 From: Jerome Reybert Date: Sat, 24 Oct 2015 10:30:30 +0200 Subject: [PATCH 46/46] test/run.sh: some updates for integration --- .travis.yml | 2 +- test/run.sh | 8 +++++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index ea98bab..184293b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -62,7 +62,7 @@ install: before_script: - git clone https://github.com/jreybert/djooks - - git clone https://github.com/jreybert/vader.vim + - git clone https://github.com/junegunn/vader.vim script: - ./test/run.sh . vader.vim djooks $VIM_VERSION diff --git a/test/run.sh b/test/run.sh index e6f4f54..ded972c 100755 --- a/test/run.sh +++ b/test/run.sh @@ -26,7 +26,7 @@ fi pushd $TEST_PATH git config --local user.email 'tester@vimagit.org' git config --local user.name 'vimagit tester' -export TEST_HEAD_SHA1='bb86d5f' +export TEST_HEAD_SHA1='origin/vimagit_test-1.4' git submodule update git show $TEST_HEAD_SHA1 --stat git reset $TEST_HEAD_SHA1~1 && git status --porcelain && git reset --hard $TEST_HEAD_SHA1 @@ -40,6 +40,9 @@ else VIM=vim fi +echo 'Git version' +git --version + echo 'Vim version' $VIM --version @@ -75,8 +78,7 @@ for script in ${!test_scripts[@]}; do set rtp+=$VADER_PATH filetype plugin indent on syntax enable - let g:vader_show_version=0 -EOF) -c "Vader! $VIMAGIT_PATH/test/$script" +EOF) -c "Vader! $VIMAGIT_PATH/test/$script 2> >(sed -n '/^Starting Vader/,$p')" done done