aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/cmdline.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/cmdline.cpp')
-rw-r--r--src/cmdline.cpp755
1 files changed, 436 insertions, 319 deletions
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
*/