Mercurial > octave
changeset 22171:0a4c5a90f286
Allow tab completion of arrays of structures.
* input.h, input.cc (find_indexed_expression): New function. Parse a
partial command line back, skipping () & {}, until a variable name
is found.
* variables.cc (generate_struct_completions): Call the above if prefix
starts with ".".
* input.cc (generate_completion): Handle manipulation of prefix by
generate_struct_completions.
author | Lachlan Andrew <lachlanbis@gmail.com> |
---|---|
date | Thu, 30 Jun 2016 18:19:37 +1000 |
parents | 20257791e358 |
children | ed8a0c39e14c |
files | libinterp/corefcn/input.cc libinterp/corefcn/input.h libinterp/corefcn/variables.cc libinterp/corefcn/variables.h liboctave/util/cmd-edit.cc liboctave/util/cmd-edit.h liboctave/util/oct-rl-edit.c liboctave/util/oct-rl-edit.h |
diffstat | 8 files changed, 130 insertions(+), 15 deletions(-) [+] |
line wrap: on
line diff
--- a/libinterp/corefcn/input.cc Sat Jul 23 14:49:54 2016 -0400 +++ b/libinterp/corefcn/input.cc Thu Jun 30 18:19:37 2016 +1000 @@ -348,13 +348,16 @@ static string_vector generate_possible_completions (const std::string& text, std::string& prefix, - std::string& hint) + std::string& hint, bool& deemed_struct) { string_vector names; prefix = ""; - if (looks_like_struct (text)) + char prev_char = octave::command_editor::get_prev_char (text.length ()); + deemed_struct = looks_like_struct (text, prev_char); + + if (deemed_struct) names = generate_struct_completions (text, prefix, hint); else names = make_name_list (); @@ -419,16 +422,26 @@ // No reason to display symbols while completing a // file/directory operation. + bool deemed_struct = false; + if (is_completing_dirfns ()) name_list = string_vector (); else - name_list = generate_possible_completions (text, prefix, hint); + name_list = generate_possible_completions (text, prefix, hint, + deemed_struct); name_list_len = name_list.numel (); - file_name_list = octave::command_editor::generate_filename_completions (text); + // If the line was something like "a{1}." then text = "." but + // we don't want to expand all the . files. + if (! deemed_struct) + { - name_list.append (file_name_list); + file_name_list = octave::command_editor::generate_filename_completions (text); + + name_list.append (file_name_list); + + } name_list_total_len = name_list.numel (); @@ -451,15 +464,16 @@ if (hint == name.substr (0, hint_len)) { + // Special case: array reference forces prefix="." + // in generate_struct_completions () if (list_index <= name_list_len && ! prefix.empty ()) - retval = prefix + "." + name; + retval = (prefix == "." ? "" : prefix) + "." + name; else retval = name; - // FIXME: looks_like_struct is broken for now, - // so it always returns false. - - if (matches == 1 && looks_like_struct (retval)) + char prev_char = octave::command_editor::get_prev_char + (text.length ()); + if (matches == 1 && looks_like_struct (retval, prev_char)) { // Don't append anything, since we don't know // whether it should be '(' or '.'. @@ -487,6 +501,53 @@ return (std::string ("'") + text); } +// Try to parse a partial command line in reverse, excluding trailing TEXT. +// If it appears a variable has been indexed by () or {}, +// return that expression, +// to allow autocomplete of field names of arrays of structures. +std::string +find_indexed_expression (const std::string& text) +{ + std::string line = octave::command_editor::get_line_buffer (); + + int pos = line.length () - text.length (); + int curly_count = 0; + int paren_count = 0; + + int last = --pos; + + while (pos >= 0 && (line[pos] == ')' || line[pos] == '}')) + { + if (line[pos] == ')') + paren_count++; + else if (line[pos] == '}') + curly_count++; + + while (curly_count + paren_count > 0 && --pos >= 0) + { + if (line[pos] == ')') + paren_count++; + else if (line[pos] == '(') + paren_count--; + else if (line[pos] == '}') + curly_count++; + else if (line[pos] == '{') + curly_count--; + } + + while (--pos >= 0 && line[pos] == ' ') + ; + } + + while (pos >= 0 && (isalnum (line[pos]) || line[pos] == '_')) + pos--; + + if (++pos >= 0) + return (line.substr (pos, last + 1 - pos)); + else + return std::string (); +} + void initialize_command_input (void) {
--- a/libinterp/corefcn/input.h Sat Jul 23 14:49:54 2016 -0400 +++ b/libinterp/corefcn/input.h Thu Jun 30 18:19:37 2016 +1000 @@ -53,6 +53,8 @@ // TRUE if we are not executing a command direct from debug> prompt. extern OCTINTERP_API bool Vtrack_line_num; +extern std::string find_indexed_expression (const std::string& text); + extern void initialize_command_input (void); extern bool octave_yes_or_no (const std::string& prompt);
--- a/libinterp/corefcn/variables.cc Sat Jul 23 14:49:54 2016 -0400 +++ b/libinterp/corefcn/variables.cc Thu Jun 30 18:19:37 2016 +1000 @@ -238,6 +238,7 @@ string_vector names; size_t pos = text.rfind ('.'); + bool array = false; if (pos != std::string::npos) { @@ -248,9 +249,15 @@ prefix = text.substr (0, pos); + if (prefix == "") + { + array = true; + prefix = find_indexed_expression (text); + } + std::string base_name = prefix; - pos = base_name.find_first_of ("{(."); + pos = base_name.find_first_of ("{(. "); if (pos != std::string::npos) base_name = base_name.substr (0, pos); @@ -284,16 +291,20 @@ } } + // Undo look-back that found the array expression, + // but insert an extra "." to distinguish from the non-struct case. + if (array) + prefix = "."; + return names; } // FIXME: this will have to be much smarter to work "correctly". - bool -looks_like_struct (const std::string& text) +looks_like_struct (const std::string& text, char prev_char) { bool retval = (! text.empty () - && text != "." + && (text != "." || prev_char == ')' || prev_char == '}') && text.find_first_of (octave::sys::file_ops::dir_sep_chars ()) == std::string::npos && text.find ("..") == std::string::npos && text.rfind ('.') != std::string::npos);
--- a/libinterp/corefcn/variables.h Sat Jul 23 14:49:54 2016 -0400 +++ b/libinterp/corefcn/variables.h Thu Jun 30 18:19:37 2016 +1000 @@ -68,7 +68,7 @@ std::string& hint); extern OCTINTERP_API bool -looks_like_struct (const std::string& text); +looks_like_struct (const std::string& text, char prev_char); extern OCTINTERP_API int symbol_exist (const std::string& name, const std::string& type = "any");
--- a/liboctave/util/cmd-edit.cc Sat Jul 23 14:49:54 2016 -0400 +++ b/liboctave/util/cmd-edit.cc Thu Jun 30 18:19:37 2016 +1000 @@ -153,6 +153,8 @@ std::string do_get_current_line (void) const; + char do_get_prev_char (int) const; + void do_replace_line (const std::string& text, bool clear_undo); void do_kill_full_line (void); @@ -647,6 +649,17 @@ return retval; } + // Return the character (offset+1) to the left of the cursor, + // or '\0' if the cursor is at the start of the line. + char + gnu_readline::do_get_prev_char (int offset) const + { + const char *buf = ::octave_rl_line_buffer (); + int p = ::octave_rl_point (); + + return p > offset ? buf[p - offset - 1] : '\0'; + } + void gnu_readline::do_replace_line (const std::string& text, bool clear_undo) { @@ -927,6 +940,8 @@ std::string do_get_current_line (void) const; + char do_get_prev_char (int) const; + void do_replace_line (const std::string& text, bool clear_undo); void do_kill_full_line (void); @@ -1003,6 +1018,12 @@ return ""; } + char + default_command_editor::do_get_prev_char (int) const + { + return '\0'; + } + void default_command_editor::do_replace_line (const std::string&, bool) { @@ -1411,6 +1432,14 @@ return (instance_ok ()) ? instance->do_get_current_line () : ""; } + // Return the character (offset+1) to the left of the cursor, + // or '\0' if the cursor is at the start of the line. + char + command_editor::get_prev_char (int offset) + { + return (instance_ok ()) ? instance->do_get_prev_char (offset) : '\0'; + } + void command_editor::replace_line (const std::string& text, bool clear_undo) {
--- a/liboctave/util/cmd-edit.h Sat Jul 23 14:49:54 2016 -0400 +++ b/liboctave/util/cmd-edit.h Thu Jun 30 18:19:37 2016 +1000 @@ -137,6 +137,8 @@ static std::string get_current_line (void); + static char get_prev_char (int); + static void replace_line (const std::string& text, bool clear_undo = true); static void kill_full_line (void); @@ -308,6 +310,8 @@ virtual std::string do_get_current_line (void) const = 0; + virtual char do_get_prev_char (int) const = 0; + virtual void do_replace_line (const std::string& text, bool clear_undo) = 0; virtual void do_kill_full_line (void) = 0;
--- a/liboctave/util/oct-rl-edit.c Sat Jul 23 14:49:54 2016 -0400 +++ b/liboctave/util/oct-rl-edit.c Thu Jun 30 18:19:37 2016 +1000 @@ -173,6 +173,12 @@ } int +octave_rl_point (void) +{ + return rl_point; +} + +int octave_rl_do_undo (void) { return rl_do_undo ();
--- a/liboctave/util/oct-rl-edit.h Sat Jul 23 14:49:54 2016 -0400 +++ b/liboctave/util/oct-rl-edit.h Thu Jun 30 18:19:37 2016 +1000 @@ -86,6 +86,8 @@ extern const char *octave_rl_line_buffer (void); +extern int octave_rl_point (void); + extern int octave_rl_do_undo (void); extern void octave_rl_clear_undo_list (void);