aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorRobin Haberkorn <robin.haberkorn@googlemail.com>2017-03-07 19:23:25 +0100
committerRobin Haberkorn <robin.haberkorn@googlemail.com>2017-03-07 19:23:25 +0100
commit892248991babb55366860488678632ab4ccdb0ba (patch)
treee2ed43f63c21dd7e70a62ff7cd6149cc150d54cc
parenta2e52ca49c6a5495f134648e91647008dca4a742 (diff)
downloadsciteco-892248991babb55366860488678632ab4ccdb0ba.tar.gz
refactored commandline key processing: rewritten Cmdline::process_edit_cmd() as State::process_edit_cmd() virtual methods
* Cmdline::process_edit_cmd() was much too long and deeply nested. It used RTTI excessively to implement the state-specific behaviour. It became apparent that the behaviour is largely state-specific and could be modelled much more elegantly as virtual methods of State. * Basically, a state can now implement a method to customize its commandline behaviour. In the case that the state does not define custom behaviour for the key pressed, it can "chain" to the parent class' process_edit_cmd(). This can be optimized to tail calls by the compiler. * The State::process_edit_cmd() implementations are still isolated in cmdline.cpp. This is not strictly necessary but allows us keep the already large compilations units like parser.cpp small. Also, the edit command processing has little to do with the rest of a state's functionality and is only used in interactive mode. * As a result, we have many small functions now which are much easier to maintain. This makes adding new and more complex context sensitive editing behaviour easier. * State-specific function key masking has been refactored by introducing State::get_fnmacro_mask(). * This allowed us to remove the States::is_*() functions which have always been a crutch to support context-sensitive key handling. * RTTI is almost completely eradicated, except for exception handling and StdError(). Both remaining cases can probably be avoided in the future, allowing us to compile smaller binaries.
-rw-r--r--TODO13
-rw-r--r--src/cmdline.cpp755
-rw-r--r--src/cmdline.h18
-rw-r--r--src/goto.h4
-rw-r--r--src/help.h4
-rw-r--r--src/parser.h106
-rw-r--r--src/qregisters.h16
-rw-r--r--src/spawn.h4
8 files changed, 528 insertions, 392 deletions
diff --git a/TODO b/TODO
index ff1568a..9748f6f 100644
--- a/TODO
+++ b/TODO
@@ -254,15 +254,6 @@ Optimizations:
using _msize() to measure the memory required by individual chunks.
This must be benchmarked.
* Add G_UNLIKELY to all error throws.
- * Instead of using RTTI to implement the immediate editing command
- behaviours in Cmdline::process_edit_cmd() depending on the current
- state, this could be modelled via virtual methods in State.
- This would almost eradicate Cmdline::process_edit_cmd() and the
- huge switch-case statement, would be more efficient (but who cares
- in this case?) and would allow us to -fno-rtti saving a few bytes.
- However, this would mean to make some more Cmdline methods public.
- The implementations of the States' commandline editing handlers
- could all be concentrated in cmdline.cpp.
* String::append() could be optimized by ORing a padding
into the realloc() size (e.g. 0xFF).
However, this has not proven effective on Linux/glibc
@@ -294,6 +285,10 @@ Optimizations:
exception types. By adding an error code to the error object
(which we will need anyway for supporting error handling in SciTECO
macros), we may even avoid RTTI.
+ Should also allow us to completely disable exceptions via -fno-exceptions.
+ * RTTI could be disabled (-fno-rtti). It's only still required
+ because of StdError() for handling arbitrary C++ exceptions.
+ This is probably not required.
* The position can be eliminated from UndoTokens by
rewriting the UndoStack into a stack of UndoToken lists.
Should be a significant memory reduction in interactive mode.
diff --git a/src/cmdline.cpp b/src/cmdline.cpp
index ffe44d6..55d63d7 100644
--- a/src/cmdline.cpp
+++ b/src/cmdline.cpp
@@ -231,7 +231,7 @@ Cmdline::keypress(gchar key)
* characters as necessary into the command line.
*/
try {
- process_edit_cmd(key);
+ States::current->process_edit_cmd(key);
} catch (Return) {
/*
* Return from top-level macro, results
@@ -295,318 +295,10 @@ Cmdline::keypress(gchar key)
}
void
-Cmdline::process_edit_cmd(gchar key)
-{
- switch (key) {
- case '\n': /* insert EOL sequence */
- interface.popup_clear();
-
- if (Flags::ed & Flags::ED_AUTOEOL)
- insert("\n");
- else
- insert(get_eol_seq(interface.ssm(SCI_GETEOLMODE)));
- break;
-
- case CTL_KEY('G'): /* toggle immediate editing modifier */
- interface.popup_clear();
-
- modifier_enabled = !modifier_enabled;
- interface.msg(InterfaceCurrent::MSG_INFO,
- "Immediate editing modifier is now %s.",
- modifier_enabled ? "enabled" : "disabled");
- break;
-
- case CTL_KEY('H'): /* rubout/reinsert character */
- interface.popup_clear();
-
- if (modifier_enabled)
- /* re-insert character */
- insert();
- else
- /* rubout character */
- rubout();
- break;
-
- case CTL_KEY('W'): /* rubout/reinsert word/command */
- interface.popup_clear();
-
- if (States::is_file()) {
- /* File names, including directories */
- if (modifier_enabled) {
- /* reinsert one level of file name */
- while (States::is_file() && rubout_len &&
- !G_IS_DIR_SEPARATOR(str[len]))
- insert();
-
- /* reinsert final directory separator */
- if (States::is_file() && rubout_len &&
- G_IS_DIR_SEPARATOR(str[len]))
- insert();
- } else if (strings[0] && *strings[0]) {
- /* rubout directory separator */
- if (strings[0] && *strings[0] &&
- G_IS_DIR_SEPARATOR(str[len-1]))
- rubout();
-
- /* rubout one level of file name */
- while (strings[0] && *strings[0] &&
- !G_IS_DIR_SEPARATOR(str[len-1]))
- rubout();
- } else {
- /*
- * Rub out entire command instead of
- * rubbing out nothing.
- */
- rubout_command();
- }
- } else if (States::is_string()) {
- gchar wchars[interface.ssm(SCI_GETWORDCHARS)];
- interface.ssm(SCI_GETWORDCHARS, 0, (sptr_t)wchars);
-
- if (modifier_enabled) {
- /* reinsert word chars */
- while (States::is_string() && rubout_len &&
- strchr(wchars, str[len]))
- insert();
-
- /* reinsert non-word chars */
- while (States::is_string() && rubout_len &&
- !strchr(wchars, str[len]))
- insert();
- } else if (strings[0] && *strings[0]) {
- /* rubout non-word chars */
- while (strings[0] && *strings[0] &&
- !strchr(wchars, str[len-1]))
- rubout();
-
- /* rubout word chars */
- while (strings[0] && *strings[0] &&
- strchr(wchars, str[len-1]))
- rubout();
- } else {
- /*
- * Rub out entire command instead of
- * rubbing out nothing.
- */
- rubout_command();
- }
- } else if (modifier_enabled) {
- /* reinsert command */
- do
- insert();
- while (!States::is_start() && rubout_len);
- } else {
- /* rubout command */
- rubout_command();
- }
- break;
-
- case CTL_KEY('U'): /* rubout/reinsert string */
- interface.popup_clear();
-
- if (States::is_string()) {
- if (modifier_enabled) {
- /* reinsert string */
- while (States::is_string() && rubout_len)
- insert();
- } else {
- /* rubout string */
- while (strings[0] && *strings[0])
- rubout();
- }
- } else {
- insert(key);
- }
- break;
-
- case '\t': /* autocomplete symbol or file name */
- if (modifier_enabled) {
- /*
- * TODO: In insertion commands, we can autocomplete
- * the string at the buffer cursor.
- */
- if (States::is_string()) {
- /* autocomplete filename using string argument */
- if (interface.popup_is_shown()) {
- /* cycle through popup pages */
- interface.popup_show();
- break;
- }
-
- const gchar *filename = last_occurrence(strings[0]);
- gchar *new_chars = filename_complete(filename);
-
- if (new_chars)
- insert(new_chars);
- g_free(new_chars);
-
- /* may be reset if there was a rubbed out command line */
- modifier_enabled = true;
- } else {
- 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();
-
- /* insert soft tabs */
- gint spaces = interface.ssm(SCI_GETTABWIDTH);
-
- spaces -= interface.ssm(SCI_GETCOLUMN,
- interface.ssm(SCI_GETCURRENTPOS)) % spaces;
-
- while (spaces--)
- insert(' ');
- } else if (States::is_dir()) {
- if (interface.popup_is_shown()) {
- /* cycle through popup pages */
- interface.popup_show();
- break;
- }
-
- gchar *new_chars = filename_complete(strings[0], '\0',
- G_FILE_TEST_IS_DIR);
-
- if (new_chars)
- insert(new_chars);
- g_free(new_chars);
- } else if (States::is_file()) {
- if (interface.popup_is_shown()) {
- /* cycle through popup pages */
- interface.popup_show();
- break;
- }
-
- gchar complete = escape_char == '{' ? '\0' : escape_char;
- gchar *new_chars = filename_complete(strings[0], complete);
-
- if (new_chars)
- insert(new_chars);
- g_free(new_chars);
- } else if (States::current == &States::executecommand) {
- /*
- * In the EC command, <TAB> completes files just like ^T
- * TODO: Implement shell-command completion by iterating
- * executables in $PATH
- */
- if (interface.popup_is_shown()) {
- /* cycle through popup pages */
- interface.popup_show();
- break;
- }
-
- const gchar *filename = last_occurrence(strings[0]);
- gchar *new_chars = filename_complete(filename);
-
- if (new_chars)
- insert(new_chars);
- g_free(new_chars);
- } else if (States::current == &States::scintilla_symbols) {
- if (interface.popup_is_shown()) {
- /* cycle through popup pages */
- interface.popup_show();
- break;
- }
-
- const gchar *symbol = last_occurrence(strings[0], ",");
- SymbolList &list = symbol == strings[0]
- ? Symbols::scintilla
- : Symbols::scilexer;
- gchar *new_chars = symbol_complete(list, symbol, ',');
-
- 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);
- }
- break;
-
-#ifdef SIGTSTP
- case CTL_KEY('Z'):
- /*
- * <CTL/Z> does not raise signal if handling of
- * special characters temporarily disabled in terminal
- * (Curses), or command-line is detached from
- * terminal (GTK+).
- * This does NOT change the state of the popup window.
- */
- raise(SIGTSTP);
- break;
-#endif
-
- default:
- interface.popup_clear();
- insert(key);
- }
-}
-
-void
Cmdline::fnmacro(const gchar *name)
{
- enum {
- FNMACRO_MASK_START = (1 << 0),
- FNMACRO_MASK_STRING = (1 << 1)
- };
-
gchar macro_name[1 + strlen(name) + 1];
QRegister *reg;
- tecoInt mask;
gchar *macro;
if (!(Flags::ed & Flags::ED_FNKEYS))
@@ -621,17 +313,8 @@ Cmdline::fnmacro(const gchar *name)
/* macro undefined */
goto default_action;
- mask = reg->get_integer();
- if (States::is_start()) {
- if (mask & FNMACRO_MASK_START)
- return;
- } else if (States::is_string()) {
- if (mask & FNMACRO_MASK_STRING)
- return;
- } else if (mask & ~(tecoInt)(FNMACRO_MASK_START | FNMACRO_MASK_STRING)) {
- /* all other bits refer to any other state */
+ if (reg->get_integer() & States::current->get_fnmacro_mask())
return;
- }
macro = reg->get_string();
try {
@@ -854,6 +537,440 @@ symbol_complete(SymbolList &list, const gchar *symbol, gchar completed)
}
/*
+ * Commandline key processing.
+ *
+ * These are all the implementations of State::process_edit_cmd().
+ * It makes sense to use virtual methods for key processing, as it is
+ * largely state-dependant; but it defines interactive-mode-only
+ * behaviour which can be kept isolated from the rest of the states'
+ * implementation.
+ */
+
+void
+State::process_edit_cmd(gchar key)
+{
+ switch (key) {
+ case '\n': /* insert EOL sequence */
+ interface.popup_clear();
+
+ if (Flags::ed & Flags::ED_AUTOEOL)
+ cmdline.insert("\n");
+ else
+ cmdline.insert(get_eol_seq(interface.ssm(SCI_GETEOLMODE)));
+ return;
+
+ case CTL_KEY('G'): /* toggle immediate editing modifier */
+ interface.popup_clear();
+
+ modifier_enabled = !modifier_enabled;
+ interface.msg(InterfaceCurrent::MSG_INFO,
+ "Immediate editing modifier is now %s.",
+ modifier_enabled ? "enabled" : "disabled");
+ return;
+
+ case CTL_KEY('H'): /* rubout/reinsert character */
+ interface.popup_clear();
+
+ if (modifier_enabled)
+ /* re-insert character */
+ cmdline.insert();
+ else
+ /* rubout character */
+ cmdline.rubout();
+ return;
+
+ case CTL_KEY('W'): /* rubout/reinsert command */
+ interface.popup_clear();
+
+ if (modifier_enabled) {
+ /* reinsert command */
+ do
+ cmdline.insert();
+ while (States::current != &States::start && cmdline.rubout_len);
+ } else {
+ /* rubout command */
+ do
+ cmdline.rubout();
+ while (States::current != &States::start);
+ }
+ return;
+
+#ifdef SIGTSTP
+ case CTL_KEY('Z'):
+ /*
+ * <CTL/Z> does not raise signal if handling of
+ * special characters temporarily disabled in terminal
+ * (Curses), or command-line is detached from
+ * terminal (GTK+).
+ * This does NOT change the state of the popup window.
+ */
+ raise(SIGTSTP);
+ return;
+#endif
+ }
+
+ interface.popup_clear();
+ cmdline.insert(key);
+}
+
+void
+StateExpectString::process_edit_cmd(gchar key)
+{
+ switch (key) {
+ case CTL_KEY('W'): { /* rubout/reinsert word */
+ interface.popup_clear();
+
+ gchar wchars[interface.ssm(SCI_GETWORDCHARS)];
+ interface.ssm(SCI_GETWORDCHARS, 0, (sptr_t)wchars);
+
+ if (modifier_enabled) {
+ /* reinsert word chars */
+ while (States::current == this && cmdline.rubout_len &&
+ strchr(wchars, cmdline.str[cmdline.len]))
+ cmdline.insert();
+
+ /* reinsert non-word chars */
+ while (States::current == this && cmdline.rubout_len &&
+ !strchr(wchars, cmdline.str[cmdline.len]))
+ cmdline.insert();
+ return;
+ }
+
+ if (strings[0] && *strings[0]) {
+ /* rubout non-word chars */
+ while (strings[0] && *strings[0] &&
+ !strchr(wchars, cmdline.str[cmdline.len-1]))
+ cmdline.rubout();
+
+ /* rubout word chars */
+ while (strings[0] && *strings[0] &&
+ strchr(wchars, cmdline.str[cmdline.len-1]))
+ cmdline.rubout();
+ return;
+ }
+
+ /*
+ * Otherwise, the entire command string will
+ * be rubbed out.
+ */
+ break;
+ }
+
+ case CTL_KEY('U'): /* rubout/reinsert string */
+ interface.popup_clear();
+
+ if (modifier_enabled) {
+ /* reinsert string */
+ while (States::current == this && cmdline.rubout_len)
+ cmdline.insert();
+ } else {
+ /* rubout string */
+ while (strings[0] && *strings[0])
+ cmdline.rubout();
+ }
+ return;
+
+ case '\t': /* autocomplete file name */
+ if (modifier_enabled) {
+ /*
+ * TODO: In insertion commands, we can autocomplete
+ * the string at the buffer cursor.
+ */
+ /* autocomplete filename using string argument */
+ if (interface.popup_is_shown()) {
+ /* cycle through popup pages */
+ interface.popup_show();
+ return;
+ }
+
+ const gchar *filename = last_occurrence(strings[0]);
+ gchar *new_chars = filename_complete(filename);
+
+ if (new_chars)
+ cmdline.insert(new_chars);
+ g_free(new_chars);
+
+ /* may be reset if there was a rubbed out command line */
+ modifier_enabled = true;
+ return;
+ }
+
+ if (machine.qregspec_machine) {
+ if (interface.popup_is_shown()) {
+ /* cycle through popup pages */
+ interface.popup_show();
+ return;
+ }
+
+ gchar *new_chars = machine.qregspec_machine->auto_complete();
+
+ if (new_chars)
+ cmdline.insert(new_chars);
+ g_free(new_chars);
+ return;
+ }
+ break;
+ }
+
+ State::process_edit_cmd(key);
+}
+
+void
+StateInsert::process_edit_cmd(gchar key)
+{
+ gint spaces;
+
+ switch (key) {
+ case '\t': /* insert <TAB> indention */
+ if (modifier_enabled || interface.ssm(SCI_GETUSETABS))
+ break;
+
+ interface.popup_clear();
+
+ /* insert soft tabs */
+ spaces = interface.ssm(SCI_GETTABWIDTH);
+ spaces -= interface.ssm(SCI_GETCOLUMN,
+ interface.ssm(SCI_GETCURRENTPOS)) % spaces;
+
+ while (spaces--)
+ cmdline.insert(' ');
+ return;
+ }
+
+ StateExpectString::process_edit_cmd(key);
+}
+
+void
+StateExpectFile::process_edit_cmd(gchar key)
+{
+ gchar *new_chars;
+
+ switch (key) {
+ case CTL_KEY('W'): /* rubout/reinsert file names including directories */
+ interface.popup_clear();
+
+ if (modifier_enabled) {
+ /* reinsert one level of file name */
+ while (States::current == this && cmdline.rubout_len &&
+ !G_IS_DIR_SEPARATOR(cmdline.str[cmdline.len]))
+ cmdline.insert();
+
+ /* reinsert final directory separator */
+ if (States::current == this && cmdline.rubout_len &&
+ G_IS_DIR_SEPARATOR(cmdline.str[cmdline.len]))
+ cmdline.insert();
+ return;
+ }
+
+ if (strings[0] && *strings[0]) {
+ /* rubout directory separator */
+ if (strings[0] && *strings[0] &&
+ G_IS_DIR_SEPARATOR(cmdline.str[cmdline.len-1]))
+ cmdline.rubout();
+
+ /* rubout one level of file name */
+ while (strings[0] && *strings[0] &&
+ !G_IS_DIR_SEPARATOR(cmdline.str[cmdline.len-1]))
+ cmdline.rubout();
+ return;
+ }
+
+ /*
+ * Rub out entire command instead of
+ * rubbing out nothing.
+ */
+ break;
+
+ case '\t': /* autocomplete file name */
+ if (modifier_enabled)
+ break;
+
+ if (interface.popup_is_shown()) {
+ /* cycle through popup pages */
+ interface.popup_show();
+ return;
+ }
+
+ new_chars = filename_complete(strings[0],
+ escape_char == '{' ? '\0' : escape_char);
+ if (new_chars)
+ cmdline.insert(new_chars);
+ g_free(new_chars);
+ return;
+ }
+
+ StateExpectString::process_edit_cmd(key);
+}
+
+void
+StateExpectDir::process_edit_cmd(gchar key)
+{
+ gchar *new_chars;
+
+ switch (key) {
+ case '\t': /* autocomplete directory */
+ if (modifier_enabled)
+ break;
+
+ if (interface.popup_is_shown()) {
+ /* cycle through popup pages */
+ interface.popup_show();
+ return;
+ }
+
+ new_chars = filename_complete(strings[0], '\0',
+ G_FILE_TEST_IS_DIR);
+ if (new_chars)
+ cmdline.insert(new_chars);
+ g_free(new_chars);
+ return;
+ }
+
+ StateExpectFile::process_edit_cmd(key);
+}
+
+void
+StateExpectQReg::process_edit_cmd(gchar key)
+{
+ gchar *new_chars;
+
+ switch (key) {
+ case '\t': /* autocomplete Q-Register name */
+ if (modifier_enabled)
+ break;
+
+ if (interface.popup_is_shown()) {
+ /* cycle through popup pages */
+ interface.popup_show();
+ return;
+ }
+
+ new_chars = machine.auto_complete();
+ if (new_chars)
+ cmdline.insert(new_chars);
+ g_free(new_chars);
+ return;
+ }
+
+ State::process_edit_cmd(key);
+}
+
+void
+StateExecuteCommand::process_edit_cmd(gchar key)
+{
+ gchar *new_chars;
+
+ switch (key) {
+ case '\t': /* autocomplete symbol or file name */
+ if (modifier_enabled)
+ break;
+
+ /*
+ * In the EC command, <TAB> completes files just like ^T
+ * TODO: Implement shell-command completion by iterating
+ * executables in $PATH
+ */
+ if (interface.popup_is_shown()) {
+ /* cycle through popup pages */
+ interface.popup_show();
+ return;
+ }
+
+ new_chars = filename_complete(last_occurrence(strings[0]));
+ if (new_chars)
+ cmdline.insert(new_chars);
+ g_free(new_chars);
+ return;
+ }
+
+ StateExpectString::process_edit_cmd(key);
+}
+
+void
+StateScintilla_symbols::process_edit_cmd(gchar key)
+{
+ switch (key) {
+ case '\t': { /* autocomplete Scintilla symbol */
+ if (modifier_enabled)
+ break;
+
+ if (interface.popup_is_shown()) {
+ /* cycle through popup pages */
+ interface.popup_show();
+ return;
+ }
+
+ const gchar *symbol = last_occurrence(strings[0], ",");
+ SymbolList &list = symbol == strings[0]
+ ? Symbols::scintilla
+ : Symbols::scilexer;
+ gchar *new_chars = symbol_complete(list, symbol, ',');
+
+ if (new_chars)
+ cmdline.insert(new_chars);
+ g_free(new_chars);
+ return;
+ }
+ }
+
+ StateExpectString::process_edit_cmd(key);
+}
+
+void
+StateGotoCmd::process_edit_cmd(gchar key)
+{
+ switch (key) {
+ case '\t': { /* autocomplete goto label */
+ if (modifier_enabled)
+ break;
+
+ if (interface.popup_is_shown()) {
+ /* cycle through popup pages */
+ interface.popup_show();
+ return;
+ }
+
+ const gchar *label = last_occurrence(strings[0], ",");
+ gchar *new_chars = Goto::table->auto_complete(label);
+
+ if (new_chars)
+ cmdline.insert(new_chars);
+ g_free(new_chars);
+ return;
+ }
+ }
+
+ StateExpectString::process_edit_cmd(key);
+}
+
+void
+StateGetHelp::process_edit_cmd(gchar key)
+{
+ switch (key) {
+ case '\t': { /* autocomplete help term */
+ if (modifier_enabled)
+ break;
+
+ if (interface.popup_is_shown()) {
+ /* cycle through popup pages */
+ interface.popup_show();
+ return;
+ }
+
+ gchar complete = escape_char == '{' ? '\0' : escape_char;
+ gchar *new_chars = help_index.auto_complete(strings[0], complete);
+
+ if (new_chars)
+ cmdline.insert(new_chars);
+ g_free(new_chars);
+ return;
+ }
+ }
+
+ StateExpectString::process_edit_cmd(key);
+}
+
+/*
* Command states
*/
diff --git a/src/cmdline.h b/src/cmdline.h
index 8b01df8..66e1829 100644
--- a/src/cmdline.h
+++ b/src/cmdline.h
@@ -27,6 +27,13 @@
namespace SciTECO {
+/*
+ * NOTE: Some of the members (esp. insert() and rubout())
+ * have to be public, so that State::process_edit_cmd()
+ * implementations can access it.
+ * Otherwise, we'd have to list all implementations as
+ * friend methods, which is inelegant.
+ */
extern class Cmdline : public Object {
public:
/**
@@ -68,9 +75,6 @@ public:
void replace(void) G_GNUC_NORETURN;
-private:
- void process_edit_cmd(gchar key);
-
inline void
rubout(void)
{
@@ -80,14 +84,6 @@ private:
}
}
- inline void
- rubout_command(void)
- {
- do
- rubout();
- while (States::current != &States::start);
- }
-
void insert(const gchar *src = NULL);
inline void
insert(gchar key)
diff --git a/src/goto.h b/src/goto.h
index 3365a79..78a9579 100644
--- a/src/goto.h
+++ b/src/goto.h
@@ -128,6 +128,10 @@ private:
class StateGotoCmd : public StateExpectString {
private:
State *done(const gchar *str);
+
+protected:
+ /* in cmdline.cpp */
+ void process_edit_cmd(gchar key);
};
namespace States {
diff --git a/src/help.h b/src/help.h
index 6695ae6..73eaa42 100644
--- a/src/help.h
+++ b/src/help.h
@@ -83,6 +83,10 @@ public:
private:
void initial(void);
State *done(const gchar *str);
+
+protected:
+ /* in cmdline.cpp */
+ void process_edit_cmd(gchar key);
};
namespace States {
diff --git a/src/parser.h b/src/parser.h
index 9aa13ad..700d25f 100644
--- a/src/parser.h
+++ b/src/parser.h
@@ -87,6 +87,41 @@ protected:
throw SyntaxError(chr);
return NULL;
}
+
+public:
+ /**
+ * Process editing command (or key press).
+ *
+ * This is part of command line handling in interactive
+ * mode and allows the definition of state-specific
+ * editing commands (behaviour on key press).
+ *
+ * By implementing this method, sub-states can either
+ * handle a key and return or chain to the
+ * parent's process_edit_cmd() implementation.
+ *
+ * All implementations of this method are defined in
+ * cmdline.cpp.
+ */
+ virtual void process_edit_cmd(gchar key);
+
+ enum fnmacroMask {
+ FNMACRO_MASK_START = (1 << 0),
+ FNMACRO_MASK_STRING = (1 << 1),
+ FNMACRO_MASK_DEFAULT = ~((1 << 2)-1)
+ };
+
+ /**
+ * Get the function key macro mask this
+ * state refers to.
+ *
+ * Could also be modelled as a State member.
+ */
+ virtual fnmacroMask
+ get_fnmacro_mask(void) const
+ {
+ return FNMACRO_MASK_DEFAULT;
+ }
};
template <typename Type>
@@ -166,14 +201,9 @@ class StateExpectString : public State {
bool string_building;
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;
+public:
StateExpectString(bool _building = true, bool _last = true)
: insert_len(0), nesting(1),
string_building(_building), last(_last) {}
@@ -182,10 +212,19 @@ private:
State *custom(gchar chr);
void refresh(void);
+ virtual fnmacroMask
+ get_fnmacro_mask(void) const
+ {
+ return FNMACRO_MASK_STRING;
+ }
+
protected:
virtual void initial(void) {}
virtual void process(const gchar *str, gint new_chars) {}
virtual State *done(const gchar *str) = 0;
+
+ /* in cmdline.cpp */
+ void process_edit_cmd(gchar key);
};
class StateExpectFile : public StateExpectString {
@@ -198,12 +237,19 @@ private:
protected:
virtual State *got_file(const gchar *filename) = 0;
+
+ /* in cmdline.cpp */
+ void process_edit_cmd(gchar key);
};
class StateExpectDir : public StateExpectFile {
public:
StateExpectDir(bool _building = true, bool _last = true)
: StateExpectFile(_building, _last) {}
+
+protected:
+ /* in cmdline.cpp */
+ void process_edit_cmd(gchar key);
};
class StateStart : public State {
@@ -222,6 +268,12 @@ private:
State *custom(gchar chr);
void end_of_macro(void) {}
+
+ fnmacroMask
+ get_fnmacro_mask(void) const
+ {
+ return FNMACRO_MASK_START;
+ }
};
class StateControl : public State {
@@ -304,6 +356,10 @@ public:
private:
State *done(const gchar *str);
+
+protected:
+ /* in cmdline.cpp */
+ void process_edit_cmd(gchar key);
};
class StateScintilla_lParam : public StateExpectString {
@@ -323,6 +379,9 @@ protected:
void initial(void);
void process(const gchar *str, gint new_chars);
State *done(const gchar *str);
+
+ /* in cmdline.cpp */
+ void process_edit_cmd(gchar key);
};
class StateInsertIndent : public StateInsert {
@@ -346,41 +405,6 @@ namespace States {
extern StateInsertIndent insert_indent;
extern State *current;
-
- static inline bool
- is_start()
- {
- /*
- * The "escape" state exists only as a hack,
- * to support $$. Otherwise it should behave
- * like the start state.
- */
- return current == &start || current == &escape;
- }
-
- static inline bool
- is_string()
- {
- return dynamic_cast<StateExpectString *>(current);
- }
-
- static inline bool
- is_insertion()
- {
- return dynamic_cast<StateInsert *>(current);
- }
-
- static inline bool
- is_file()
- {
- return dynamic_cast<StateExpectFile *>(current);
- }
-
- static inline bool
- is_dir()
- {
- return dynamic_cast<StateExpectDir *>(current);
- }
}
extern enum Mode {
diff --git a/src/qregisters.h b/src/qregisters.h
index f185f03..d57323a 100644
--- a/src/qregisters.h
+++ b/src/qregisters.h
@@ -489,14 +489,9 @@ public:
* Super class for states accepting Q-Register specifications
*/
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;
+public:
StateExpectQReg(QRegSpecType type = QREG_REQUIRED);
private:
@@ -504,6 +499,9 @@ private:
protected:
virtual State *got_register(QRegister *reg) = 0;
+
+ /* in cmdline.cpp */
+ void process_edit_cmd(gchar key);
};
class StatePushQReg : public StateExpectQReg {
@@ -637,12 +635,6 @@ 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/spawn.h b/src/spawn.h
index 5ddc9dd..4d7403e 100644
--- a/src/spawn.h
+++ b/src/spawn.h
@@ -57,6 +57,10 @@ private:
void initial(void);
State *done(const gchar *str);
+
+protected:
+ /* in cmdline.cpp */
+ void process_edit_cmd(gchar key);
};
class StateEGCommand : public StateExpectQReg {