diff options
author | Robin Haberkorn <robin.haberkorn@googlemail.com> | 2016-11-19 23:59:52 +0100 |
---|---|---|
committer | Robin Haberkorn <robin.haberkorn@googlemail.com> | 2016-11-20 01:52:39 +0100 |
commit | 84ab1698e63170a94cfa1c5d99b7316cf3c2b584 (patch) | |
tree | cb87e28be35e710b960d6e643f4ea7cf5596ec96 /src | |
parent | f9e6f8630cdb0c6d056154b82e8f94289509f48a (diff) |
auto-completion of Q-Register names, goto labels and help topics
* Using a common implementation in RBTreeString::auto_complete().
This is very efficient even for very huge tables since only
an O(log(n)) lookup is required and then all entries with a matching
prefix are iterated. Worst-case complexity is still O(n), since all
entries may be legitimate completions.
If necessary, the number of matching entries could be restricted, though.
* Auto completes short and long Q-Reg names.
Short names are "case-insensitive" (since they are upper-cased).
Long specs are terminated with a closing bracket.
* Long spec completions may have problems with names containing
funny characters since they may be misinterpreted as string building
characters or contain braces. All the auto-completions suffered from
this problem already (see TODO).
* This greatly simplifies investigating the Q-Register name spaces
interactively and e.g. calling macros with long names, inserting
environment registers etc.
* Goto labels are terminated with commas since they may be part
of a computed goto.
* Help topics are matched case insensitive (just like the topic
lookup itself) and are terminated with the escape character.
This greatly simplifies navigating womanpages and looking up
topics with long names.
Diffstat (limited to 'src')
-rw-r--r-- | src/cmdline.cpp | 55 | ||||
-rw-r--r-- | src/goto.h | 6 | ||||
-rw-r--r-- | src/help.h | 6 | ||||
-rw-r--r-- | src/parser.h | 16 | ||||
-rw-r--r-- | src/qregisters.cpp | 38 | ||||
-rw-r--r-- | src/qregisters.h | 23 | ||||
-rw-r--r-- | src/rbtree.cpp | 58 | ||||
-rw-r--r-- | src/rbtree.h | 3 |
8 files changed, 194 insertions, 11 deletions
diff --git a/src/cmdline.cpp b/src/cmdline.cpp index 042cd6f..10e4a6b 100644 --- a/src/cmdline.cpp +++ b/src/cmdline.cpp @@ -39,6 +39,7 @@ #include "ring.h" #include "ioview.h" #include "goto.h" +#include "help.h" #include "undo.h" #include "symbols.h" #include "spawn.h" @@ -443,6 +444,34 @@ Cmdline::process_edit_cmd(gchar key) interface.popup_clear(); insert(key); } + } else if (States::is_qreg()) { + if (interface.popup_is_shown()) { + /* cycle through popup pages */ + interface.popup_show(); + break; + } + + QRegSpecMachine &machine = ((StateExpectQReg *)States::current)->machine; + gchar *new_chars = machine.auto_complete(); + + if (new_chars) + insert(new_chars); + g_free(new_chars); + } else if (States::is_string() && + ((StateExpectString *)States::current)->machine.qregspec_machine) { + if (interface.popup_is_shown()) { + /* cycle through popup pages */ + interface.popup_show(); + break; + } + + QRegSpecMachine *machine = + ((StateExpectString *)States::current)->machine.qregspec_machine; + gchar *new_chars = machine->auto_complete(); + + if (new_chars) + insert(new_chars); + g_free(new_chars); } else if (States::is_insertion() && !interface.ssm(SCI_GETUSETABS)) { interface.popup_clear(); @@ -514,6 +543,32 @@ Cmdline::process_edit_cmd(gchar key) if (new_chars) insert(new_chars); g_free(new_chars); + } else if (States::current == &States::gotocmd) { + if (interface.popup_is_shown()) { + /* cycle through popup pages */ + interface.popup_show(); + break; + } + + const gchar *label = last_occurrence(strings[0], ","); + gchar *new_chars = Goto::table->auto_complete(label); + + if (new_chars) + insert(new_chars); + g_free(new_chars); + } else if (States::current == &States::gethelp) { + if (interface.popup_is_shown()) { + /* cycle through popup pages */ + interface.popup_show(); + break; + } + + gchar complete = escape_char == '{' ? '\0' : escape_char; + gchar *new_chars = help_index.auto_complete(strings[0], complete); + + if (new_chars) + insert(new_chars); + g_free(new_chars); } else { interface.popup_clear(); insert(key); @@ -94,6 +94,12 @@ public: RBTreeString::clear(); } + inline gchar * + auto_complete(const gchar *name, gchar completed = ',') + { + return RBTreeString::auto_complete(name, completed); + } + #ifdef DEBUG void dump(void); #endif @@ -53,6 +53,12 @@ public: void set(const gchar *name, const gchar *filename, tecoInt pos = 0); + + inline gchar * + auto_complete(const gchar *name, gchar completed = '\0') + { + return RBTreeStringCase::auto_complete(name, completed); + } }; extern HelpIndex help_index; diff --git a/src/parser.h b/src/parser.h index b18e13c..f5dcba5 100644 --- a/src/parser.h +++ b/src/parser.h @@ -120,8 +120,6 @@ public: class QRegSpecMachine; class StringBuildingMachine : public MicroStateMachine<gchar *> { - QRegSpecMachine *qregspec_machine; - enum Mode { MODE_NORMAL, MODE_UPPER, @@ -131,9 +129,11 @@ class StringBuildingMachine : public MicroStateMachine<gchar *> { bool toctl; public: + QRegSpecMachine *qregspec_machine; + StringBuildingMachine() : MicroStateMachine<gchar *>(), - qregspec_machine(NULL), - mode(MODE_NORMAL), toctl(false) {} + mode(MODE_NORMAL), toctl(false), + qregspec_machine(NULL) {} ~StringBuildingMachine(); void reset(void); @@ -147,7 +147,6 @@ public: * string building commands and accumulation into a string */ class StateExpectString : public State { - StringBuildingMachine machine; gsize insert_len; gint nesting; @@ -156,6 +155,13 @@ class StateExpectString : public State { bool last; public: + /* + * FIXME: Only public as long as cmdline.cpp + * needs to access it. + * Can be avoided one process_edit_cmd() is in State. + */ + StringBuildingMachine machine; + StateExpectString(bool _building = true, bool _last = true) : insert_len(0), nesting(1), string_building(_building), last(_last) {} diff --git a/src/qregisters.cpp b/src/qregisters.cpp index 6ca7990..23d7fc4 100644 --- a/src/qregisters.cpp +++ b/src/qregisters.cpp @@ -990,9 +990,16 @@ QRegSpecMachine::input(gchar chr, QRegister *&result) MICROSTATE_START; switch (chr) { - case '.': undo.push_var(is_local) = true; break; - case '#': set(&&StateFirstChar); break; - case '[': set(&&StateString); break; + case '.': + undo.push_var(is_local) = true; + break; + case '#': + set(&&StateFirstChar); + break; + case '[': + set(&&StateString); + undo.push_var(nesting)++; + break; default: undo.push_str(name) = String::chrdup(String::toupper(chr)); goto done; @@ -1003,6 +1010,7 @@ MICROSTATE_START; StateFirstChar: undo.push_str(name) = (gchar *)g_malloc(3); name[0] = String::toupper(chr); + name[1] = '\0'; set(&&StateSecondChar); return false; @@ -1017,9 +1025,9 @@ StateString: undo.push_var(nesting)++; break; case ']': + undo.push_var(nesting)--; if (!nesting) goto done; - undo.push_var(nesting)--; break; } @@ -1071,6 +1079,28 @@ done: return true; } +gchar * +QRegSpecMachine::auto_complete(void) +{ + gsize restrict_len = 0; + + if (string_machine.qregspec_machine) + /* nested Q-Reg definition */ + return string_machine.qregspec_machine->auto_complete(); + + if (state == StateStart) + /* single-letter Q-Reg */ + restrict_len = 1; + else if (!nesting) + /* two-letter Q-Reg */ + restrict_len = 2; + + QRegisterTable &table = is_local ? *QRegisters::locals + : QRegisters::globals; + return table.auto_complete(name, nesting == 1 ? ']' : '\0', + restrict_len); +} + /* * Command states */ diff --git a/src/qregisters.h b/src/qregisters.h index 1ea47ee..1d0962c 100644 --- a/src/qregisters.h +++ b/src/qregisters.h @@ -374,6 +374,12 @@ public: void update_environ(void); void clear(void); + + inline gchar * + auto_complete(const gchar *name, gchar completed = '\0', gsize max_len = 0) + { + return RBTreeString::auto_complete(name, completed, max_len); + } }; class QRegisterStack { @@ -471,6 +477,8 @@ public: { throw InvalidQRegError(name, is_local); } + + gchar *auto_complete(void); }; /* @@ -482,14 +490,19 @@ public: */ class StateExpectQReg : public State { public: + /* + * FIXME: Only public, so we can access it from + * cmdline.cpp. Will not be necessary if process_edit_cmd() + * is a State method. + */ + QRegSpecMachine machine; + StateExpectQReg(QRegSpecType type = QREG_REQUIRED); private: State *custom(gchar chr); protected: - QRegSpecMachine machine; - virtual State *got_register(QRegister *reg) = 0; }; @@ -624,6 +637,12 @@ namespace States { extern StateMacro macro; extern StateMacroFile macro_file; extern StateCopyToQReg copytoqreg; + + static inline bool + is_qreg() + { + return dynamic_cast<StateExpectQReg *>(current); + } } namespace QRegisters { diff --git a/src/rbtree.cpp b/src/rbtree.cpp index f1d870d..5b1a4e1 100644 --- a/src/rbtree.cpp +++ b/src/rbtree.cpp @@ -19,13 +19,71 @@ #include "config.h" #endif +#include <string.h> + #include <glib.h> #include <glib/gprintf.h> +#include "sciteco.h" #include "rbtree.h" +#include "interface.h" +#include "string-utils.h" namespace SciTECO { +template <StringCmpFunc StringCmp, StringNCmpFunc StringNCmp> +gchar * +RBTreeStringT<StringCmp, StringNCmp>:: +auto_complete(const gchar *key, gchar completed, gsize restrict_len) +{ + gsize key_len; + RBEntryString *first = NULL; + gsize prefix_len = 0; + gint prefixed_entries = 0; + gchar *insert = NULL; + + if (!key) + key = ""; + key_len = strlen(key); + + for (RBEntryString *cur = nfind(key); + cur && !StringNCmp(cur->key, key, key_len); + cur = (RBEntryString *)cur->next()) { + if (restrict_len && strlen(cur->key) != restrict_len) + continue; + + if (!first) + first = cur; + + gsize len = String::diff(first->key + key_len, + cur->key + key_len); + if (!prefix_len || len < prefix_len) + prefix_len = len; + + prefixed_entries++; + } + if (prefix_len > 0) + insert = g_strndup(first->key + key_len, prefix_len); + + if (!insert && prefixed_entries > 1) { + for (RBEntryString *cur = first; + cur && !StringNCmp(cur->key, key, key_len); + cur = (RBEntryString *)cur->next()) { + if (restrict_len && strlen(cur->key) != restrict_len) + continue; + + interface.popup_add(InterfaceCurrent::POPUP_PLAIN, + cur->key); + } + + interface.popup_show(); + } else if (prefixed_entries == 1) { + String::append(insert, completed); + } + + return insert; +} + template class RBTreeStringT<strcmp, strncmp>; template class RBTreeStringT<g_ascii_strcasecmp, g_ascii_strncasecmp>; diff --git a/src/rbtree.h b/src/rbtree.h index 4a4bbdb..76b2141 100644 --- a/src/rbtree.h +++ b/src/rbtree.h @@ -181,6 +181,9 @@ public: RBEntryString entry((gchar *)str); return RBTree<RBEntryString>::nfind(&entry); } + + gchar *auto_complete(const gchar *key, gchar completed = '\0', + gsize restrict_len = 0); }; typedef RBTreeStringT<strcmp, strncmp> RBTreeString; |