From 670e33ab27244a48c90bf2b1abcc215edfe9c0bb Mon Sep 17 00:00:00 2001 From: ridiculousfish Date: Mon, 20 Aug 2012 13:09:21 -0700 Subject: [PATCH] Properly handle empty completions (e.g. tab-complete 'foo' with extant files 'foo' and 'foobar' should offer both) Fixes issue described in https://github.com/fish-shell/fish-shell/issues/290 --- reader.cpp | 53 ++++++++++++++++++++-------------------------------- wildcard.cpp | 11 +++-------- 2 files changed, 23 insertions(+), 41 deletions(-) diff --git a/reader.cpp b/reader.cpp index 777502934..eb8cb3193 100644 --- a/reader.cpp +++ b/reader.cpp @@ -975,18 +975,15 @@ static void run_pager( const wcstring &prefix, int is_quoted, const std::vector< long base_len=-1; const completion_t &el = comp.at( i ); - wchar_t *foo=0; - wchar_t *baz=0; + wcstring completion_text; + wcstring description_text; if( has_case_sensitive && (el.flags & COMPLETE_NO_CASE )) { continue; } - - if( el.completion.empty() ){ - continue; - } - + + // Note that an empty completion is perfectly sensible here, e.g. tab-completing 'foo' with a file called 'foo' and another called 'foobar' if( el.flags & COMPLETE_NO_CASE ) { if( base_len == -1 ) @@ -997,40 +994,30 @@ static void run_pager( const wcstring &prefix, int is_quoted, const std::vector< base_len = data->buff_pos - (begin-buff); } - wcstring foo_wstr = escape_string( el.completion.c_str() + base_len, ESCAPE_ALL | ESCAPE_NO_QUOTED ); - foo = wcsdup(foo_wstr.c_str()); + completion_text = escape_string( el.completion.c_str() + base_len, ESCAPE_ALL | ESCAPE_NO_QUOTED ); } else { - wcstring foo_wstr = escape_string( el.completion, ESCAPE_ALL | ESCAPE_NO_QUOTED ); - foo = wcsdup(foo_wstr.c_str()); + completion_text = escape_string( el.completion, ESCAPE_ALL | ESCAPE_NO_QUOTED ); } - if( !el.description.empty() ) + if( ! el.description.empty() ) { - wcstring baz_wstr = escape_string( el.description, 1 ); - baz = wcsdup(baz_wstr.c_str()); - } - - if( !foo ) - { - debug( 0, L"Run pager called with bad argument." ); - bugreport(); - show_stackframe(); - } - else if( baz ) - { - append_format(msg, L"%ls%ls%ls\n", foo, escaped_separator, baz); - } - else - { - append_format(msg, L"%ls\n", foo); + description_text = escape_string( el.description, true ); } - free( foo ); - free( baz ); - } + /* It's possible (even common) to have an empty completion with no description. An example would be completing 'foo' with extant files 'foo' and 'foobar'. But fish_pager ignores blank lines. So if our completion text is empty, always include a description, even if it's empty. + */ + msg.reserve(msg.size() + completion_text.size() + description_text.size() + 2); + msg.append(completion_text); + if (! description_text.empty() || completion_text.empty()) + { + msg.append(escaped_separator); + msg.append(description_text); + } + msg.push_back(L'\n'); + } free( escaped_separator ); @@ -1490,7 +1477,7 @@ static bool handle_completions( const std::vector &comp ) is_quoted = (quote != L'\0'); write_loop(1, "\n", 1 ); - + run_pager( prefix, is_quoted, comp ); } s_reset( &data->screen, true); diff --git a/wildcard.cpp b/wildcard.cpp index 5a87eeb27..db42fadc8 100644 --- a/wildcard.cpp +++ b/wildcard.cpp @@ -250,13 +250,8 @@ static bool wildcard_complete_internal(const wcstring &orig, } - if (! out_completion.empty()) - { - append_completion( out, - out_completion, - out_desc, - flags ); - } + /* Note: out_completion may be empty if the completion really is empty, e.g. tab-completing 'foo' when a file 'foo' exists. */ + append_completion(out, out_completion, out_desc, flags); return true; } @@ -1073,7 +1068,7 @@ int wildcard_expand( const wchar_t *wc, std::set visited_files; int res = wildcard_expand_internal( wc, base_dir, flags, out, completion_set, visited_files ); - + if( flags & ACCEPT_INCOMPLETE ) { wcstring wc_base;