diff options
author | Robin Haberkorn <robin.haberkorn@googlemail.com> | 2012-12-04 17:29:01 +0100 |
---|---|---|
committer | Robin Haberkorn <robin.haberkorn@googlemail.com> | 2012-12-04 22:07:08 +0100 |
commit | d8a316514c03d85b771a9dce4a8a51b875d955b3 (patch) | |
tree | 8966c29db767a155848f6d90f76771ce5b9de32e /src/cmdline.cpp | |
parent | b120616b6da52e951097f69ad267de06081d218a (diff) | |
download | sciteco-d8a316514c03d85b771a9dce4a8a51b875d955b3.tar.gz |
autoconf preparation: move everything into src/ subdir
Diffstat (limited to 'src/cmdline.cpp')
-rw-r--r-- | src/cmdline.cpp | 371 |
1 files changed, 371 insertions, 0 deletions
diff --git a/src/cmdline.cpp b/src/cmdline.cpp new file mode 100644 index 0000000..212722a --- /dev/null +++ b/src/cmdline.cpp @@ -0,0 +1,371 @@ +#include <string.h> +#include <stdlib.h> + +#include <glib.h> +#include <glib/gprintf.h> +#include <glib/gstdio.h> + +#include "sciteco.h" +#include "interface.h" +#include "expressions.h" +#include "parser.h" +#include "qregisters.h" +#include "ring.h" +#include "goto.h" +#include "undo.h" +#include "symbols.h" + +static inline const gchar *process_edit_cmd(gchar key); +static gchar *macro_echo(const gchar *macro); + +static gchar *filename_complete(const gchar *filename, gchar completed = ' '); +static gchar *symbol_complete(SymbolList &list, const gchar *symbol, + gchar completed = ' '); + +static const gchar *last_occurrence(const gchar *str, + const gchar *chars = " \t\v\r\n\f<>,;@"); +static inline gboolean filename_is_dir(const gchar *filename); + +gchar *cmdline = NULL; + +bool quit_requested = false; + +void +cmdline_keypress(gchar key) +{ + gchar *cmdline_p; + const gchar *insert; + gchar *echo; + + /* + * Cleanup messages, popups, etc... + */ + interface.popup_clear(); + interface.msg_clear(); + + /* + * Process immediate editing commands + */ + insert = process_edit_cmd(key); + + /* + * Parse/execute characters + */ + if (cmdline) { + gint len = strlen(cmdline); + cmdline = (gchar *)g_realloc(cmdline, len + strlen(insert) + 1); + cmdline_p = cmdline + len; + } else { + cmdline = (gchar *)g_malloc(strlen(insert) + 1); + *cmdline = '\0'; + cmdline_p = cmdline; + } + + /* + * Execute one insertion character, extending cmdline, at a time so + * undo tokens get emitted for the corresponding characters. + */ + for (const gchar *p = insert; *p; p++) { + *cmdline_p++ = *p; + *cmdline_p = '\0'; + + try { + Execute::step(cmdline); + } catch (...) { + /* + * Undo tokens may have been emitted (or had to be) + * before the exception is thrown. They must be + * executed so as if the character had never been + * inserted. + */ + undo.pop(cmdline_p - cmdline); + cmdline_p[-1] = '\0'; + break; + } + } + + /* + * Echo command line + */ + echo = macro_echo(cmdline); + interface.cmdline_update(echo); + g_free(echo); +} + +static inline const gchar * +process_edit_cmd(gchar key) +{ + static gchar insert[255]; + gint cmdline_len = cmdline ? strlen(cmdline) : 0; + + insert[0] = '\0'; + + switch (key) { + case '\b': + if (cmdline_len) { + undo.pop(cmdline_len); + cmdline[cmdline_len - 1] = '\0'; + macro_pc--; + } + break; + + case CTL_KEY('T'): { + const gchar *filename = cmdline ? last_occurrence(cmdline) + 1 + : NULL; + gchar *new_chars = filename_complete(filename); + if (new_chars) + g_stpcpy(insert, new_chars); + g_free(new_chars); + break; + } + + case '\t': + if (States::current == &States::editfile || + States::current == &States::savefile || + States::current == &States::loadqreg) { + gchar complete = escape_char == '{' ? ' ' : escape_char; + gchar *new_chars = filename_complete(strings[0], + complete); + if (new_chars) + g_stpcpy(insert, new_chars); + g_free(new_chars); + } else if (States::current == &States::scintilla_symbols) { + const gchar *symbol = NULL; + SymbolList &list = Symbols::scintilla; + gchar *new_chars; + + if (strings[0]) { + symbol = last_occurrence(strings[0], ","); + if (*symbol == ',') { + symbol++; + list = Symbols::scilexer; + } + } + + new_chars = symbol_complete(list, symbol, ','); + if (new_chars) + g_stpcpy(insert, new_chars); + g_free(new_chars); + } else { + insert[0] = key; + insert[1] = '\0'; + } + break; + + case '\x1B': + if (States::current == &States::start && + cmdline && cmdline[cmdline_len - 1] == '\x1B') { + if (Goto::skip_label) { + interface.msg(Interface::MSG_ERROR, + "Label \"%s\" not found", + Goto::skip_label); + break; + } + + if (quit_requested) { + /* FIXME */ + exit(EXIT_SUCCESS); + } + + undo.clear(); + interface.ssm(SCI_EMPTYUNDOBUFFER); + Goto::table->clear(); + expressions.clear(); + + *cmdline = '\0'; + macro_pc = 0; + break; + } + /* fall through */ + default: + insert[0] = key; + insert[1] = '\0'; + } + + return insert; +} + +static gchar * +macro_echo(const gchar *macro) +{ + gchar *result, *rp; + + if (!macro) + return g_strdup(""); + + rp = result = (gchar *)g_malloc(strlen(macro)*5 + 1); + + for (const gchar *p = macro; *p; p++) { + switch (*p) { + case '\x1B': + *rp++ = '$'; + break; + case '\r': + rp = g_stpcpy(rp, "<CR>"); + break; + case '\n': + rp = g_stpcpy(rp, "<LF>"); + break; + case '\t': + rp = g_stpcpy(rp, "<TAB>"); + break; + default: + if (IS_CTL(*p)) { + *rp++ = '^'; + *rp++ = CTL_ECHO(*p); + } else { + *rp++ = *p; + } + } + } + *rp = '\0'; + + return result; +} + +static gchar * +filename_complete(const gchar *filename, gchar completed) +{ + gchar *dirname, *basename; + GDir *dir; + GList *files = NULL, *matching; + GCompletion *completion; + gchar *new_prefix; + gchar *insert = NULL; + + if (!filename) + filename = ""; + + if (is_glob_pattern(filename)) + return NULL; + + dirname = g_path_get_dirname(filename); + dir = g_dir_open(dirname, 0, NULL); + if (!dir) { + g_free(dirname); + return NULL; + } + if (*dirname != *filename) + *dirname = '\0'; + + while ((basename = (gchar *)g_dir_read_name(dir))) { + gchar *filename = g_build_filename(dirname, basename, NULL); + + if (g_file_test(filename, G_FILE_TEST_IS_DIR)) { + gchar *new_filename; + new_filename = g_strconcat(filename, + G_DIR_SEPARATOR_S, NULL); + g_free(filename); + filename = new_filename; + } + + files = g_list_prepend(files, filename); + } + + g_free(dirname); + g_dir_close(dir); + + completion = g_completion_new(NULL); + g_completion_add_items(completion, files); + + matching = g_completion_complete(completion, filename, &new_prefix); + if (new_prefix && strlen(new_prefix) > strlen(filename)) + insert = g_strdup(new_prefix + strlen(filename)); + g_free(new_prefix); + + if (!insert && g_list_length(matching) > 1) { + matching = g_list_sort(matching, (GCompareFunc)g_strcmp0); + + for (GList *file = g_list_first(matching); + file != NULL; + file = g_list_next(file)) { + Interface::PopupEntryType type; + bool in_buffer = false; + + if (filename_is_dir((gchar *)file->data)) { + type = Interface::POPUP_DIRECTORY; + } else { + type = Interface::POPUP_FILE; + /* FIXME: inefficient */ + in_buffer = ring.find((gchar *)file->data); + } + + interface.popup_add(type, (gchar *)file->data, + in_buffer); + } + + interface.popup_show(); + } else if (g_list_length(matching) == 1 && + !filename_is_dir((gchar *)g_list_first(matching)->data)) { + String::append(insert, completed); + } + + g_completion_free(completion); + + for (GList *file = g_list_first(files); file; file = g_list_next(file)) + g_free(file->data); + g_list_free(files); + + return insert; +} + +static gchar * +symbol_complete(SymbolList &list, const gchar *symbol, gchar completed) +{ + GList *glist; + GList *matching; + GCompletion *completion; + gchar *new_prefix; + gchar *insert = NULL; + + if (!symbol) + symbol = ""; + + glist = list.get_glist(); + if (!glist) + return NULL; + + completion = g_completion_new(NULL); + g_completion_add_items(completion, glist); + + matching = g_completion_complete(completion, symbol, &new_prefix); + if (new_prefix && strlen(new_prefix) > strlen(symbol)) + insert = g_strdup(new_prefix + strlen(symbol)); + g_free(new_prefix); + + if (!insert && g_list_length(matching) > 1) { + for (GList *entry = g_list_first(matching); + entry != NULL; + entry = g_list_next(entry)) { + interface.popup_add(Interface::POPUP_PLAIN, + (gchar *)entry->data); + } + + interface.popup_show(); + } else if (g_list_length(matching) == 1) { + String::append(insert, completed); + } + + g_completion_free(completion); + + return insert; +} + +/* + * Auxiliary functions + */ + +static const gchar * +last_occurrence(const gchar *str, const gchar *chars) +{ + while (*chars) + str = strrchr(str, *chars++) ? : str; + + return str; +} + +static inline gboolean +filename_is_dir(const gchar *filename) +{ + return g_str_has_suffix(filename, G_DIR_SEPARATOR_S); +} |