diff --git a/autoload/magit/state.vim b/autoload/magit/state.vim index 1163014d2d69bf0eb6deb45a9b326e6fda63ce93..170fd2d78bdc6e144dfcb3630dbd154258f5b857 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 c14c3be02f3ab50c7a97240a5415bb983ab46111..9a18e9133e83cde4aa6f6474c5c5952f6f2df3ce 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 8402ed53cb7493c06428d08ed3e4a3f1b02c2326..aa81dbb776b0c27fe821e49d26d1651e85d8457b 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 <SID>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 <SID>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=<SID>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