aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--complete.cpp15
-rw-r--r--complete.h8
-rw-r--r--reader.cpp134
3 files changed, 129 insertions, 28 deletions
diff --git a/complete.cpp b/complete.cpp
index 11d689c6..4a9036ec 100644
--- a/complete.cpp
+++ b/complete.cpp
@@ -279,6 +279,21 @@ completion_t &completion_t::operator=(const completion_t &him)
return *this;
}
+bool completion_t::operator < (const completion_t& rhs) const
+{
+ return this->completion < rhs.completion;
+}
+
+bool completion_t::operator == (const completion_t& rhs) const
+{
+ return this->completion == rhs.completion;
+}
+
+bool completion_t::operator != (const completion_t& rhs) const
+{
+ return ! (*this == rhs);
+}
+
wcstring_list_t completions_to_wcstring_list( const std::vector<completion_t> &list )
{
wcstring_list_t strings;
diff --git a/complete.h b/complete.h
index 3b5bc619..d915a75b 100644
--- a/complete.h
+++ b/complete.h
@@ -134,15 +134,17 @@ public:
*/
int flags;
+ bool is_case_insensitive() const { return !! (flags & COMPLETE_NO_CASE); }
+
/* Construction. Note: defining these so that they are not inlined reduces the executable size. */
completion_t(const wcstring &comp, const wcstring &desc = L"", int flags_val = 0);
completion_t(const completion_t &);
completion_t &operator=(const completion_t &);
/* The following are needed for sorting and uniquing completions */
- bool operator < (const completion_t& rhs) const { return this->completion < rhs.completion; }
- bool operator == (const completion_t& rhs) const { return this->completion == rhs.completion; }
- bool operator != (const completion_t& rhs) const { return ! (*this == rhs); }
+ bool operator < (const completion_t& rhs) const;
+ bool operator == (const completion_t& rhs) const;
+ bool operator != (const completion_t& rhs) const;
};
enum complete_type_t {
diff --git a/reader.cpp b/reader.cpp
index eb8cb319..a245d1c3 100644
--- a/reader.cpp
+++ b/reader.cpp
@@ -1256,21 +1256,21 @@ static void reader_flash()
/**
Check if the specified string can be replaced by a case insensitive
- complition with the specified flags.
+ completion with the specified flags.
Advanced tokens like those containing {}-style expansion can not at
the moment be replaced, other than if the new token is already an
exact replacement, e.g. if the COMPLETE_DONT_ESCAPE flag is set.
*/
-static int reader_can_replace( const wcstring &in, int flags )
+static bool reader_can_replace( const wcstring &in, int flags )
{
const wchar_t * str = in.c_str();
if( flags & COMPLETE_DONT_ESCAPE )
{
- return 1;
+ return true;
}
/*
Test characters that have a special meaning in any character position
@@ -1278,11 +1278,64 @@ static int reader_can_replace( const wcstring &in, int flags )
while( *str )
{
if( wcschr( REPLACE_UNCLEAN, *str ) )
- return 0;
+ return false;
str++;
}
- return 1;
+ return true;
+}
+
+/* Compare two completions, except make the case insensitive comes larger than everyone (so they come last) */
+bool case_sensitive_completion_compare(const completion_t &a, const completion_t &b)
+{
+ if (a.is_case_insensitive() != b.is_case_insensitive())
+ {
+ /* Case insensitive ones come last. Exactly one of a, b is case insensitive. If it's a, return false, i.e. not less than, to make it appear at the end. */
+ return ! a.is_case_insensitive();
+ }
+ /* Compare using file comparison */
+ return wcsfilecmp(a.completion.c_str(), b.completion.c_str()) < 0;
+}
+
+/* Order completions such that case insensitive completions come first. */
+static void prioritize_completions(std::vector<completion_t> &comp)
+{
+ sort(comp.begin(), comp.end(), case_sensitive_completion_compare);
+}
+
+/* Given a list of completions, get the completion at an index past *inout_idx, and then increment it. inout_idx should be initialized to (size_t)(-1) for the first call. */
+static const completion_t *cycle_competions(const std::vector<completion_t> &comp, const wcstring &command_line, size_t *inout_idx)
+{
+ const size_t size = comp.size();
+ if (size == 0)
+ return NULL;
+
+ const size_t start_idx = *inout_idx;
+ size_t idx = start_idx;
+ const completion_t *result = NULL;
+ for (;;)
+ {
+ /* Bump the index */
+ idx = (idx + 1) % size;
+
+ /* Bail if we've looped */
+ if (idx == start_idx)
+ break;
+
+ /* Get the completion */
+ const completion_t &c = comp.at(idx);
+
+ /* Try this completion */
+ if (! c.is_case_insensitive() || reader_can_replace(command_line, c.flags))
+ {
+ /* Success */
+ result = &c;
+ break;
+ }
+ }
+
+ *inout_idx = idx;
+ return result;
}
/**
@@ -1298,14 +1351,16 @@ static int reader_can_replace( const wcstring &in, int flags )
a common prefix, call run_pager to display a list of completions. Depending on terminal size and the length of the list, run_pager may either show less than a screenfull and exit or use an interactive pager to allow the user to scroll through the completions.
\param comp the list of completion strings
+
+ Return true if we inserted text into the command line, false if we did not.
*/
-
static bool handle_completions( const std::vector<completion_t> &comp )
{
wchar_t *base = NULL;
size_t len = 0;
bool done = false;
+ bool success = false;
int count = 0;
int flags=0;
const wchar_t *begin, *end, *buff = data->command_line.c_str();
@@ -1325,6 +1380,7 @@ static bool handle_completions( const std::vector<completion_t> &comp )
{
reader_flash();
done = true;
+ success = false;
break;
}
@@ -1340,12 +1396,12 @@ static bool handle_completions( const std::vector<completion_t> &comp )
the token doesn't contain evil operators
like {}
*/
- if( !(c.flags & COMPLETE_NO_CASE) || reader_can_replace( tok, c.flags ) )
+ if( ! c.is_case_insensitive() || reader_can_replace( tok, c.flags ) )
{
completion_insert( c.completion.c_str(), c.flags );
}
done = true;
- len = 1; // I think this just means it's a true return
+ success = true;
break;
}
}
@@ -1353,13 +1409,13 @@ static bool handle_completions( const std::vector<completion_t> &comp )
if( !done )
{
- /* Try to find something to insert whith the correct case */
+ /* Try to find something to insert with the correct case */
for( size_t i=0; i< comp.size() ; i++ )
{
const completion_t &c = comp.at( i );
/* Ignore case insensitive completions for now */
- if( c.flags & COMPLETE_NO_CASE )
+ if( c.is_case_insensitive() )
continue;
count++;
@@ -1386,6 +1442,7 @@ static bool handle_completions( const std::vector<completion_t> &comp )
base[len]=L'\0';
completion_insert(base, flags);
done = true;
+ success = true;
}
}
@@ -1396,7 +1453,6 @@ static bool handle_completions( const std::vector<completion_t> &comp )
/* Try to find something to insert ignoring case */
if( begin )
{
-
size_t offset = tok.size();
count = 0;
@@ -1405,7 +1461,7 @@ static bool handle_completions( const std::vector<completion_t> &comp )
{
const completion_t &c = comp.at( i );
- if( !(c.flags & COMPLETE_NO_CASE) )
+ if( ! c.is_case_insensitive() )
continue;
if( !reader_can_replace( tok, c.flags ) )
@@ -1438,6 +1494,7 @@ static bool handle_completions( const std::vector<completion_t> &comp )
base[len]=L'\0';
completion_insert( base, flags );
done = 1;
+ success = true;
}
}
@@ -1482,9 +1539,9 @@ static bool handle_completions( const std::vector<completion_t> &comp )
}
s_reset( &data->screen, true);
reader_repaint();
-
+ success = false;
}
- return len > 0;
+ return success;
}
@@ -2499,10 +2556,17 @@ const wchar_t *reader_readline()
int last_char=0;
size_t yank_len=0;
const wchar_t *yank_str;
- std::vector<completion_t> comp;
bool comp_empty = true;
+ std::vector<completion_t> comp;
int finished=0;
struct termios old_modes;
+
+ /* The cycle index in our completion list */
+ size_t completion_cycle_idx = (size_t)(-1);
+
+ /* The command line before completion */
+ wcstring cycle_command_line;
+ size_t cycle_cursor_pos;
data->search_buff.clear();
data->search_mode = NO_SEARCH;
@@ -2577,13 +2641,7 @@ const wchar_t *reader_readline()
if( c != 0 )
break;
}
- /*
- if( (last_char == R_COMPLETE) && (c != R_COMPLETE) && (!comp_empty) )
- {
- halloc_destroy( comp );
- comp = 0;
- }
- */
+
if( last_char != R_YANK && last_char != R_YANK_POP )
yank_len=0;
const wchar_t *buff = data->command_line.c_str();
@@ -2662,13 +2720,31 @@ const wchar_t *reader_readline()
if( !data->complete_func )
break;
- if( comp_empty || last_char != R_COMPLETE)
+ if (! comp_empty && last_char == R_COMPLETE)
+ {
+ /* The user typed R_COMPLETE more than once in a row. Cycle through our available completions */
+ const completion_t *next_comp = cycle_competions(comp, cycle_command_line, &completion_cycle_idx);
+ if (next_comp != NULL)
+ {
+ size_t cursor_pos = cycle_cursor_pos;
+ const wcstring new_cmd_line = completion_apply_to_command_line(next_comp->completion, next_comp->flags, cycle_command_line, &cursor_pos);
+ reader_set_buffer(new_cmd_line, cursor_pos);
+
+ /* Since we just inserted a completion, don't immediately do a new autosuggestion */
+ data->suppress_autosuggestion = true;
+ }
+ }
+ else
{
+ /* Either the user hit tab only once, or we had no visible completion list. */
const wchar_t *begin, *end;
const wchar_t *token_begin, *token_end;
const wchar_t *buff = data->command_line.c_str();
long cursor_steps;
+ /* Clear the completion list */
+ comp.clear();
+
parse_util_cmdsubst_extent( buff, data->buff_pos, &begin, &end );
parse_util_token_extent( begin, data->buff_pos - (begin-buff), &token_begin, &token_end, 0, 0 );
@@ -2687,11 +2763,19 @@ const wchar_t *reader_readline()
data->complete_func( buffcpy, comp, COMPLETE_DEFAULT, NULL);
+ /* Munge our completions */
sort(comp.begin(), comp.end());
remove_duplicates( comp );
-
+ prioritize_completions(comp);
+
+ /* Record our cycle_command_line */
+ cycle_command_line = data->command_line;
+ cycle_cursor_pos = data->buff_pos;
+
comp_empty = handle_completions( comp );
- comp.clear();
+
+ /* Start the cycle at the beginning */
+ completion_cycle_idx = (size_t)(-1);
}
break;