aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--src/cmdline.cpp349
-rw-r--r--src/cmdline.h72
-rw-r--r--src/error.cpp12
-rw-r--r--src/error.h9
-rw-r--r--src/interface-curses.cpp133
-rw-r--r--src/interface-curses.h18
-rw-r--r--src/interface-gtk.cpp88
-rw-r--r--src/interface-gtk.h8
-rw-r--r--src/interface.h16
-rw-r--r--src/parser.cpp16
-rw-r--r--src/parser.h5
-rw-r--r--src/qregisters.cpp13
-rw-r--r--src/qregisters.h21
-rw-r--r--src/undo.cpp20
-rw-r--r--src/undo.h8
15 files changed, 493 insertions, 295 deletions
diff --git a/src/cmdline.cpp b/src/cmdline.cpp
index 7c74db4..b1f3805 100644
--- a/src/cmdline.cpp
+++ b/src/cmdline.cpp
@@ -44,9 +44,6 @@
namespace SciTECO {
-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 = ' ');
@@ -56,9 +53,11 @@ static const gchar *last_occurrence(const gchar *str,
static inline gboolean filename_is_dir(const gchar *filename);
static inline gchar derive_dir_separator(const gchar *filename);
-gchar *cmdline = NULL;
-gint cmdline_pos = 0;
-static gchar *last_cmdline = NULL;
+/** Current command line. */
+Cmdline cmdline;
+
+/** Last terminated command line */
+static Cmdline last_cmdline;
bool quit_requested = false;
@@ -66,14 +65,59 @@ namespace States {
StateSaveCmdline save_cmdline;
}
+#if 0
+Cmdline *
+copy(void) const
+{
+ Cmdline *c = new Cmdline();
+
+ if (str)
+ c->str = g_memdup(str, len+rubout_len);
+ c->len = len;
+ c->rubout_len = rubout_len;
+
+ return c;
+}
+#endif
+
+/**
+ * Throws a command line based on the command line
+ * replacement register.
+ * It is catched by Cmdline::keypress() to actually
+ * perform the command line update.
+ */
+void
+Cmdline::replace(void)
+{
+ QRegister *cmdline_reg = QRegisters::globals["\x1B"];
+ /* use heap object to avoid copy constructors etc. */
+ Cmdline *new_cmdline = new Cmdline();
+
+ /* FIXME: does not handle null bytes */
+ new_cmdline->str = cmdline_reg->get_string();
+ new_cmdline->len = strlen(new_cmdline->str);
+ new_cmdline->rubout_len = 0;
+
+ /*
+ * Search for first differing character in old and
+ * new command line. This avoids unnecessary rubouts
+ * and insertions when the command line is updated.
+ */
+ for (new_cmdline->pc = 0;
+ new_cmdline->pc < len && new_cmdline->pc < new_cmdline->len &&
+ str[new_cmdline->pc] == new_cmdline->str[new_cmdline->pc];
+ new_cmdline->pc++);
+
+ throw new_cmdline;
+}
+
void
-cmdline_keypress(gchar key)
+Cmdline::keypress(gchar key)
{
- gchar *old_cmdline = NULL;
- gint repl_pos = 0;
+ Cmdline old_cmdline;
- const gchar *insert;
- gchar *echo;
+ gsize old_len = len;
+ guint repl_pc = 0;
/*
* Cleanup messages,etc...
@@ -81,44 +125,59 @@ cmdline_keypress(gchar key)
interface.msg_clear();
/*
- * Process immediate editing commands.
- * It may clear/hide the popup.
+ * Process immediate editing commands, inserting
+ * characters as necessary into the command line.
*/
- insert = process_edit_cmd(key);
+ if (process_edit_cmd(key))
+ interface.popup_clear();
/*
* Parse/execute characters, one at a time so
* undo tokens get emitted for the corresponding characters.
*/
- cmdline_pos = cmdline ? strlen(cmdline)+1 : 1;
- String::append(cmdline, insert);
+ macro_pc = pc = MIN(old_len, len);
- while (cmdline[cmdline_pos-1]) {
+ while (pc < len) {
try {
- Execute::step(cmdline, cmdline_pos);
- } catch (ReplaceCmdline &r) {
- undo.pop(r.pos);
-
- old_cmdline = cmdline;
- cmdline = r.new_cmdline;
- cmdline_pos = repl_pos = r.pos;
- macro_pc = r.pos-1;
+ Execute::step(str, pc+1);
+ } catch (Cmdline *new_cmdline) {
+ /*
+ * Result of command line replacement (}):
+ * Exchange command lines, avoiding
+ * deep copying
+ */
+ undo.pop(new_cmdline->pc);
+
+ old_cmdline = *this;
+ *this = *new_cmdline;
+ new_cmdline->str = NULL;
+ macro_pc = repl_pc = pc;
+
+ delete new_cmdline;
continue;
} catch (Error &error) {
error.add_frame(new Error::ToplevelFrame());
error.display_short();
- if (old_cmdline) {
- undo.pop(repl_pos);
-
- g_free(cmdline);
- cmdline = old_cmdline;
- cmdline[strlen(cmdline)-1] = '\0';
- old_cmdline = NULL;
- cmdline_pos = repl_pos;
- macro_pc = repl_pos-1;
+ if (old_cmdline.str) {
+ /*
+ * Error during command-line replacement.
+ * Replay previous command-line.
+ * This avoids deep copying.
+ */
+ undo.pop(repl_pc);
+
+ g_free(str);
+ *this = old_cmdline;
+ old_cmdline.str = NULL;
+ macro_pc = pc = repl_pc;
+
+ /* rubout cmdline replacement command */
+ len--;
+ rubout_len++;
continue;
}
+
/*
* Undo tokens may have been emitted
* (or had to be) before the exception
@@ -126,156 +185,151 @@ cmdline_keypress(gchar key)
* as if the character had never been
* inserted.
*/
- undo.pop(cmdline_pos);
- cmdline[cmdline_pos-1] = '\0';
+ undo.pop(pc);
+ rubout_len += len-pc;
+ len = pc;
/* program counter could be messed up */
- macro_pc = cmdline_pos - 1;
+ macro_pc = len;
break;
}
- cmdline_pos++;
+ pc++;
}
- g_free(old_cmdline);
-
/*
* Echo command line
*/
- echo = macro_echo(cmdline);
- interface.cmdline_update(echo);
- g_free(echo);
+ interface.cmdline_update(this);
}
-static inline const gchar *
-process_edit_cmd(gchar key)
+void
+Cmdline::insert(const gchar *src)
{
- static gchar insert[255];
- gint cmdline_len = cmdline ? strlen(cmdline) : 0;
- bool clear_popup = true;
-
- insert[0] = key;
- insert[1] = '\0';
+ size_t src_len = strlen(src);
+
+ if (src_len <= rubout_len && !strncmp(str+len, src, src_len)) {
+ len += src_len;
+ rubout_len -= src_len;
+ } else {
+ String::append(str, len, src);
+ len += src_len;
+ rubout_len = 0;
+ }
+}
+bool
+Cmdline::process_edit_cmd(gchar key)
+{
switch (key) {
- case '\b':
- if (cmdline_len) {
- undo.pop(cmdline_len);
- cmdline[cmdline_len - 1] = '\0';
- macro_pc--;
- }
- *insert = '\0';
+ case '\b': /* rubout character */
+ if (len)
+ rubout();
break;
- case CTL_KEY('W'):
+ case CTL_KEY('W'): /* rubout word */
if (States::is_string()) {
gchar wchars[interface.ssm(SCI_GETWORDCHARS)];
interface.ssm(SCI_GETWORDCHARS, 0, (sptr_t)wchars);
/* rubout non-word chars */
while (strings[0] && strlen(strings[0]) > 0 &&
- !strchr(wchars, cmdline[macro_pc-1]))
- undo.pop(macro_pc--);
+ !strchr(wchars, str[len-1]))
+ rubout();
/* rubout word chars */
while (strings[0] && strlen(strings[0]) > 0 &&
- strchr(wchars, cmdline[macro_pc-1]))
- undo.pop(macro_pc--);
- } else if (cmdline_len) {
+ strchr(wchars, str[len-1]))
+ rubout();
+ } else if (len) {
do
- undo.pop(macro_pc--);
+ rubout();
while (States::current != &States::start);
}
- cmdline[macro_pc] = '\0';
- *insert = '\0';
break;
- case CTL_KEY('U'):
+ case CTL_KEY('U'): /* rubout string */
if (States::is_string()) {
while (strings[0] && strlen(strings[0]) > 0)
- undo.pop(macro_pc--);
- cmdline[macro_pc] = '\0';
- *insert = '\0';
+ rubout();
+ } else {
+ insert(key);
}
break;
- case CTL_KEY('T'):
+ case CTL_KEY('T'): /* autocomplete file name */
+ /*
+ * TODO: In insertion commands, we can autocomplete
+ * the string at the buffer cursor.
+ */
if (States::is_string()) {
- *insert = '\0';
if (interface.popup_is_shown()) {
/* cycle through popup pages */
interface.popup_show();
- clear_popup = false;
- break;
+ return false;
}
const gchar *filename = last_occurrence(strings[0]);
gchar *new_chars = filename_complete(filename);
- clear_popup = !interface.popup_is_shown();
-
- if (new_chars)
- g_stpcpy(insert, new_chars);
+ insert(new_chars);
g_free(new_chars);
+
+ if (interface.popup_is_shown())
+ return false;
+ } else {
+ insert(key);
}
break;
- case '\t':
- if (States::is_insertion()) {
- if (!interface.ssm(SCI_GETUSETABS)) {
- gint len = interface.ssm(SCI_GETTABWIDTH);
+ case '\t': /* autocomplete symbol or file name */
+ if (States::is_insertion() && !interface.ssm(SCI_GETUSETABS)) {
+ gint spaces = interface.ssm(SCI_GETTABWIDTH);
- len -= interface.ssm(SCI_GETCOLUMN,
- interface.ssm(SCI_GETCURRENTPOS)) % len;
+ spaces -= interface.ssm(SCI_GETCOLUMN,
+ interface.ssm(SCI_GETCURRENTPOS)) % spaces;
- memset(insert, ' ', len);
- insert[len] = '\0';
- }
+ while (spaces--)
+ insert(' ');
} else if (States::is_file()) {
- *insert = '\0';
if (interface.popup_is_shown()) {
/* cycle through popup pages */
interface.popup_show();
- clear_popup = false;
- break;
+ return false;
}
gchar complete = escape_char == '{' ? ' ' : escape_char;
gchar *new_chars = filename_complete(strings[0], complete);
- clear_popup = !interface.popup_is_shown();
-
- if (new_chars)
- g_stpcpy(insert, new_chars);
+ insert(new_chars);
g_free(new_chars);
+
+ if (interface.popup_is_shown())
+ return false;
} 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
*/
- *insert = '\0';
if (interface.popup_is_shown()) {
/* cycle through popup pages */
interface.popup_show();
- clear_popup = false;
- break;
+ return false;
}
const gchar *filename = last_occurrence(strings[0]);
gchar *new_chars = filename_complete(filename);
- clear_popup = !interface.popup_is_shown();
-
- if (new_chars)
- g_stpcpy(insert, new_chars);
+ insert(new_chars);
g_free(new_chars);
+
+ if (interface.popup_is_shown())
+ return false;
} else if (States::current == &States::scintilla_symbols) {
- *insert = '\0';
if (interface.popup_is_shown()) {
/* cycle through popup pages */
interface.popup_show();
- clear_popup = false;
- break;
+ return false;
}
const gchar *symbol = last_occurrence(strings[0], ",");
@@ -284,20 +338,19 @@ process_edit_cmd(gchar key)
: Symbols::scilexer;
gchar *new_chars = symbol_complete(list, symbol, ',');
- clear_popup = !interface.popup_is_shown();
-
- if (new_chars)
- g_stpcpy(insert, new_chars);
+ insert(new_chars);
g_free(new_chars);
- }
+ if (interface.popup_is_shown())
+ return false;
+ } else {
+ insert(key);
+ }
break;
- case '\x1B':
+ case '\x1B': /* terminate command line */
if (States::current == &States::start &&
- cmdline && cmdline[cmdline_len - 1] == '\x1B') {
- *insert = '\0';
-
+ str && str[len-1] == '\x1B') {
if (Goto::skip_label) {
interface.msg(InterfaceCurrent::MSG_ERROR,
"Label \"%s\" not found",
@@ -316,10 +369,16 @@ process_edit_cmd(gchar key)
Goto::table->clear();
expressions.clear();
- g_free(last_cmdline);
- last_cmdline = cmdline;
- cmdline = NULL;
- macro_pc = 0;
+ last_cmdline = *this;
+ str = NULL;
+ len = rubout_len = 0;
+
+ /*
+ * FIXME: Perhaps to the malloc_trim() here
+ * instead of in UndoStack::clear()
+ */
+ } else {
+ insert(key);
}
break;
@@ -332,20 +391,20 @@ process_edit_cmd(gchar key)
* terminal (GTK+)
*/
raise(SIGTSTP);
- *insert = '\0';
break;
#endif
- }
- if (clear_popup)
- interface.popup_clear();
+ default:
+ insert(key);
+ }
- return insert;
+ return true;
}
void
-cmdline_fnmacro(const gchar *name)
+Cmdline::fnmacro(const gchar *name)
{
+ /* FIXME: check again if function keys are enabled */
gchar macro_name[1 + strlen(name) + 1];
QRegister *reg;
@@ -355,7 +414,7 @@ cmdline_fnmacro(const gchar *name)
reg = QRegisters::globals[macro_name];
if (reg) {
gchar *macro = reg->get_string();
- cmdline_keypress(macro);
+ keypress(macro);
g_free(macro);
}
}
@@ -375,44 +434,6 @@ get_eol(void)
}
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;
@@ -596,7 +617,7 @@ StateSaveCmdline::got_register(QRegister &reg)
BEGIN_EXEC(&States::start);
reg.undo_set_string();
- reg.set_string(last_cmdline);
+ reg.set_string(last_cmdline.str, last_cmdline.len);
return &States::start;
}
diff --git a/src/cmdline.h b/src/cmdline.h
index 1a254e6..6095cbb 100644
--- a/src/cmdline.h
+++ b/src/cmdline.h
@@ -20,25 +20,73 @@
#include <glib.h>
-#include "sciteco.h"
#include "parser.h"
#include "qregisters.h"
+#include "undo.h"
namespace SciTECO {
-extern gchar *cmdline;
-extern gint cmdline_pos;
-extern bool quit_requested;
+extern class Cmdline {
+public:
+ /**
+ * String containing the current command line.
+ * It is not null-terminated and contains the effective
+ * command-line up to cmdline_len followed by the recently rubbed-out
+ * command-line of length cmdline_rubout_len.
+ */
+ gchar *str;
+ /** Effective command line length */
+ gsize len;
+ /** Length of the rubbed out command line */
+ gsize rubout_len;
+ /** Program counter within the command-line macro */
+ guint pc;
-void cmdline_keypress(gchar key);
-static inline void
-cmdline_keypress(const gchar *keys)
-{
- while (*keys)
- cmdline_keypress(*keys++);
-}
+ Cmdline() : str(NULL), len(0), rubout_len(0), pc(0) {}
+ inline
+ ~Cmdline()
+ {
+ g_free(str);
+ }
+
+ inline gchar
+ operator [](guint i) const
+ {
+ return str[i];
+ }
+
+ void keypress(gchar key);
+ inline void
+ keypress(const gchar *keys)
+ {
+ while (*keys)
+ keypress(*keys++);
+ }
+
+ void fnmacro(const gchar *name);
-void cmdline_fnmacro(const gchar *name);
+ void replace(void) G_GNUC_NORETURN;
+
+private:
+ bool process_edit_cmd(gchar key);
+
+ inline void
+ rubout(void)
+ {
+ undo.pop(--len);
+ rubout_len++;
+ }
+
+ void insert(const gchar *src);
+ inline void
+ insert(gchar key)
+ {
+ gchar src[] = {key, '\0'};
+ insert(src);
+ }
+} cmdline;
+
+extern bool quit_requested;
const gchar *get_eol(void);
diff --git a/src/error.cpp b/src/error.cpp
index 301626b..34819e3 100644
--- a/src/error.cpp
+++ b/src/error.cpp
@@ -25,21 +25,11 @@
#include <glib/gprintf.h>
#include "sciteco.h"
-#include "qregisters.h"
#include "interface.h"
-#include "cmdline.h"
+#include "error.h"
namespace SciTECO {
-ReplaceCmdline::ReplaceCmdline()
-{
- QRegister *cmdline_reg = QRegisters::globals["\x1B"];
-
- new_cmdline = cmdline_reg->get_string();
- for (pos = 0; cmdline[pos] && cmdline[pos] == new_cmdline[pos]; pos++);
- pos++;
-}
-
Error::Frame *
Error::QRegFrame::copy() const
{
diff --git a/src/error.h b/src/error.h
index 7ee0ac6..c9cad00 100644
--- a/src/error.h
+++ b/src/error.h
@@ -29,15 +29,6 @@
namespace SciTECO {
-/* thrown as exception, executed at cmdline macro level */
-class ReplaceCmdline {
-public:
- gchar *new_cmdline;
- gint pos;
-
- ReplaceCmdline();
-};
-
/*
* Thrown as exception to signify that program
* should be terminated.
diff --git a/src/interface-curses.cpp b/src/interface-curses.cpp
index 6d2feb0..1f4506a 100644
--- a/src/interface-curses.cpp
+++ b/src/interface-curses.cpp
@@ -54,7 +54,7 @@ static void scintilla_notify(Scintilla *sci, int idFrom,
#define UNNAMED_FILE "(Unnamed)"
#define SCI_COLOR_ATTR(f, b) \
- ((chtype)COLOR_PAIR(SCI_COLOR_PAIR(f, b)))
+ ((attr_t)COLOR_PAIR(SCI_COLOR_PAIR(f, b)))
void
ViewCurses::initialize_impl(void)
@@ -95,7 +95,6 @@ InterfaceCurses::main_impl(int &argc, char **&argv)
msg_window = newwin(1, 0, LINES - 2, 0);
cmdline_window = newwin(0, 0, LINES - 1, 0);
- cmdline_current = NULL;
#ifdef EMSCRIPTEN
nodelay(cmdline_window, TRUE);
@@ -160,13 +159,13 @@ InterfaceCurses::resize_all_windows(void)
draw_info();
msg_clear(); /* FIXME: use saved message */
popup_clear();
- cmdline_update();
+ draw_cmdline();
}
void
InterfaceCurses::vmsg_impl(MessageType type, const gchar *fmt, va_list ap)
{
- static const chtype type2attr[] = {
+ static const attr_t type2attr[] = {
SCI_COLOR_ATTR(COLOR_BLACK, COLOR_WHITE), /* MSG_USER */
SCI_COLOR_ATTR(COLOR_BLACK, COLOR_GREEN), /* MSG_INFO */
SCI_COLOR_ATTR(COLOR_BLACK, COLOR_YELLOW), /* MSG_WARNING */
@@ -230,7 +229,7 @@ InterfaceCurses::draw_info(void)
}
void
-InterfaceCurses::info_update_impl(QRegister *reg)
+InterfaceCurses::info_update_impl(const QRegister *reg)
{
g_free(info_current);
info_current = g_strdup_printf("%s - <QRegister> %s", PACKAGE_NAME,
@@ -240,7 +239,7 @@ InterfaceCurses::info_update_impl(QRegister *reg)
}
void
-InterfaceCurses::info_update_impl(Buffer *buffer)
+InterfaceCurses::info_update_impl(const Buffer *buffer)
{
g_free(info_current);
info_current = g_strdup_printf("%s - <Buffer> %s%s", PACKAGE_NAME,
@@ -251,27 +250,103 @@ InterfaceCurses::info_update_impl(Buffer *buffer)
}
void
-InterfaceCurses::cmdline_update_impl(const gchar *cmdline)
+InterfaceCurses::format_chr(chtype *&target, gchar chr, attr_t attr)
{
- size_t len;
- int half_line = (getmaxx(stdscr) - 2) / 2;
- const gchar *line;
+ /*
+ * NOTE: This mapping is similar to
+ * View::set_representations()
+ */
+ switch (chr) {
+ case '\x1B': /* escape */
+ *target++ = '$' | attr | A_REVERSE;
+ break;
+ case '\r':
+ *target++ = 'C' | attr | A_REVERSE;
+ *target++ = 'R' | attr | A_REVERSE;
+ break;
+ case '\n':
+ *target++ = 'L' | attr | A_REVERSE;
+ *target++ = 'F' | attr | A_REVERSE;
+ break;
+ case '\t':
+ *target++ = 'T' | attr | A_REVERSE;
+ *target++ = 'A' | attr | A_REVERSE;
+ *target++ = 'B' | attr | A_REVERSE;
+ break;
+ default:
+ if (IS_CTL(chr)) {
+ *target++ = '^' | attr | A_REVERSE;
+ *target++ = CTL_ECHO(chr) | attr | A_REVERSE;
+ } else {
+ *target++ = chr | attr;
+ }
+ }
+}
- if (cmdline) {
- g_free(cmdline_current);
- cmdline_current = g_strdup(cmdline);
+void
+InterfaceCurses::cmdline_update_impl(const Cmdline *cmdline)
+{
+ gsize alloc_len = 1;
+ chtype *p;
+
+ /*
+ * Replace entire pre-formatted command-line.
+ * We don't know if it is similar to the last one,
+ * so realloc makes no sense.
+ * We approximate the size of the new formatted command-line,
+ * wasting a few bytes for control characters.
+ */
+ delete[] cmdline_current;
+ for (guint i = 0; i < cmdline->len+cmdline->rubout_len; i++)
+ alloc_len += IS_CTL((*cmdline)[i]) ? 3 : 1;
+ p = cmdline_current = new chtype[alloc_len];
+
+ /* format effective command line */
+ for (guint i = 0; i < cmdline->len; i++)
+ format_chr(p, (*cmdline)[i]);
+ cmdline_len = p - cmdline_current;
+
+ /*
+ * Format rubbed-out command line.
+ * AFAIK bold black should be rendered grey by any
+ * common terminal.
+ * If not, this problem will be gone once we support
+ * a Scintilla view command line.
+ */
+ for (guint i = cmdline->len; i < cmdline->len+cmdline->rubout_len; i++)
+ format_chr(p, (*cmdline)[i],
+ A_UNDERLINE | A_BOLD |
+ SCI_COLOR_ATTR(COLOR_BLACK, COLOR_BLACK));
+ cmdline_rubout_len = p - cmdline_current - cmdline_len;
+
+ /* highlight cursor after effective command line */
+ if (cmdline_rubout_len) {
+ cmdline_current[cmdline_len] &= A_CHARTEXT | A_UNDERLINE;
+ cmdline_current[cmdline_len] |= A_REVERSE;
} else {
- cmdline = cmdline_current;
+ cmdline_current[cmdline_len++] = ' ' | A_REVERSE;
}
- len = strlen(cmdline);
- /* FIXME: optimize */
- line = cmdline + len - MIN(len, half_line + len % half_line);
+ draw_cmdline();
+}
- mvwaddch(cmdline_window, 0, 0, '*');
- waddstr(cmdline_window, line);
- waddch(cmdline_window, ' ' | A_REVERSE);
- wclrtoeol(cmdline_window);
+void
+InterfaceCurses::draw_cmdline(void)
+{
+ /* total width available for command line */
+ guint total_width = getmaxx(stdscr) - 1;
+ /* beginning of command line to show */
+ guint disp_offset;
+ /* length of command line to show */
+ guint disp_len;
+
+ disp_offset = cmdline_len -
+ MIN(cmdline_len, total_width/2 + cmdline_len % (total_width/2));
+ disp_len = MIN(total_width, cmdline_len+cmdline_rubout_len - disp_offset);
+
+ werase(cmdline_window);
+ mvwaddch(cmdline_window, 0, 0, '*' | A_BOLD);
+ waddchnstr(cmdline_window, cmdline_current+disp_offset, disp_len);
}
void
@@ -430,18 +505,18 @@ event_loop_iter()
#endif
case 0x7F: /* DEL */
case KEY_BACKSPACE:
- cmdline_keypress('\b');
+ cmdline.keypress('\b');
break;
case KEY_ENTER:
case '\r':
case '\n':
- cmdline_keypress(get_eol());
+ cmdline.keypress(get_eol());
break;
/*
* Function key macros
*/
-#define FN(KEY) case KEY_##KEY: cmdline_fnmacro(#KEY); break
+#define FN(KEY) case KEY_##KEY: cmdline.fnmacro(#KEY); break
#define FNS(KEY) FN(KEY); FN(S##KEY)
FN(DOWN); FN(UP); FNS(LEFT); FNS(RIGHT);
FNS(HOME);
@@ -450,7 +525,7 @@ event_loop_iter()
g_snprintf(macro_name, sizeof(macro_name),
"F%d", key - KEY_F0);
- cmdline_fnmacro(macro_name);
+ cmdline.fnmacro(macro_name);
break;
}
FNS(DC);
@@ -468,7 +543,7 @@ event_loop_iter()
*/
default:
if (key <= 0xFF)
- cmdline_keypress((gchar)key);
+ cmdline.keypress((gchar)key);
}
sigint_occurred = FALSE;
@@ -486,6 +561,8 @@ event_loop_iter()
void
InterfaceCurses::event_loop_impl(void)
{
+ static const Cmdline empty_cmdline;
+
/* initial refresh */
/* FIXME: this does wrefresh() internally */
current_view->refresh();
@@ -493,7 +570,7 @@ InterfaceCurses::event_loop_impl(void)
wnoutrefresh(info_window);
msg_clear();
wnoutrefresh(msg_window);
- cmdline_update("");
+ cmdline_update(&empty_cmdline);
wnoutrefresh(cmdline_window);
doupdate();
@@ -540,7 +617,7 @@ InterfaceCurses::~InterfaceCurses()
g_free(info_current);
if (cmdline_window)
delwin(cmdline_window);
- g_free(cmdline_current);
+ delete[] cmdline_current;
if (msg_window)
delwin(msg_window);
diff --git a/src/interface-curses.h b/src/interface-curses.h
index cab2031..8ee8811 100644
--- a/src/interface-curses.h
+++ b/src/interface-curses.h
@@ -74,9 +74,12 @@ typedef class InterfaceCurses : public Interface<InterfaceCurses, ViewCurses> {
WINDOW *info_window;
gchar *info_current;
+
WINDOW *msg_window;
+
WINDOW *cmdline_window;
- gchar *cmdline_current;
+ chtype *cmdline_current;
+ gsize cmdline_len, cmdline_rubout_len;
struct Popup {
WINDOW *window;
@@ -101,7 +104,8 @@ public:
info_current(NULL),
msg_window(NULL),
cmdline_window(NULL),
- cmdline_current(NULL) {}
+ cmdline_current(NULL),
+ cmdline_len(0), cmdline_rubout_len(0) {}
~InterfaceCurses();
/* implementation of Interface::main() */
@@ -116,11 +120,11 @@ public:
void show_view_impl(ViewCurses *view);
/* implementation of Interface::info_update() */
- void info_update_impl(QRegister *reg);
- void info_update_impl(Buffer *buffer);
+ void info_update_impl(const QRegister *reg);
+ void info_update_impl(const Buffer *buffer);
/* implementation of Interface::cmdline_update() */
- void cmdline_update_impl(const gchar *cmdline = NULL);
+ void cmdline_update_impl(const Cmdline *cmdline);
/* implementation of Interface::popup_add() */
void popup_add_impl(PopupEntryType type,
@@ -144,6 +148,10 @@ private:
void resize_all_windows(void);
void draw_info(void);
+ void format_chr(chtype *&target, gchar chr,
+ attr_t attr = 0);
+ void draw_cmdline(void);
+
friend void event_loop_iter();
} InterfaceCurrent;
diff --git a/src/interface-gtk.cpp b/src/interface-gtk.cpp
index 7931957..e7f6927 100644
--- a/src/interface-gtk.cpp
+++ b/src/interface-gtk.cpp
@@ -20,6 +20,7 @@
#endif
#include <stdarg.h>
+#include <string.h>
#include <glib.h>
#include <glib/gprintf.h>
@@ -77,6 +78,7 @@ ViewGtk::initialize_impl(void)
void
InterfaceGtk::main_impl(int &argc, char **&argv)
{
+ static const Cmdline empty_cmdline;
GtkWidget *info_content;
gtk_init(&argc, &argv);
@@ -109,7 +111,7 @@ InterfaceGtk::main_impl(int &argc, char **&argv)
gtk_widget_grab_focus(cmdline_widget);
- cmdline_update("");
+ cmdline_update(&empty_cmdline);
}
void
@@ -164,7 +166,7 @@ InterfaceGtk::show_view_impl(ViewGtk *view)
}
void
-InterfaceGtk::info_update_impl(QRegister *reg)
+InterfaceGtk::info_update_impl(const QRegister *reg)
{
gchar buf[255];
@@ -174,7 +176,7 @@ InterfaceGtk::info_update_impl(QRegister *reg)
}
void
-InterfaceGtk::info_update_impl(Buffer *buffer)
+InterfaceGtk::info_update_impl(const Buffer *buffer)
{
gchar buf[255];
@@ -185,18 +187,66 @@ InterfaceGtk::info_update_impl(Buffer *buffer)
}
void
-InterfaceGtk::cmdline_update_impl(const gchar *cmdline)
+InterfaceGtk::cmdline_insert_chr(gint &pos, gchar chr)
{
- gint pos = 1;
+ gchar buffer[5+1];
- if (!cmdline)
- /* widget automatically redrawn */
- return;
+ /*
+ * NOTE: This mapping is similar to
+ * View::set_representations()
+ */
+ switch (chr) {
+ case '\x1B': /* escape */
+ strcpy(buffer, "$");
+ break;
+ case '\r':
+ strcpy(buffer, "<CR>");
+ break;
+ case '\n':
+ strcpy(buffer, "<LF>");
+ break;
+ case '\t':
+ strcpy(buffer, "<TAB>");
+ break;
+ default:
+ if (IS_CTL(chr)) {
+ buffer[0] = '^';
+ buffer[1] = CTL_ECHO(chr);
+ buffer[2] = '\0';
+ } else {
+ buffer[0] = chr;
+ buffer[1] = '\0';
+ }
+ }
- gtk_entry_set_text(GTK_ENTRY(cmdline_widget), "*");
gtk_editable_insert_text(GTK_EDITABLE(cmdline_widget),
- cmdline, -1, &pos);
- gtk_editable_set_position(GTK_EDITABLE(cmdline_widget), pos);
+ buffer, -1, &pos);
+}
+
+void
+InterfaceGtk::cmdline_update_impl(const Cmdline *cmdline)
+{
+ gint pos = 1;
+ gint cmdline_len;
+
+ /*
+ * We don't know if the new command line is similar to
+ * the old one, so we can just as well rebuild it.
+ */
+ gtk_entry_set_text(GTK_ENTRY(cmdline_widget), "*");
+
+ /* format effective command line */
+ for (guint i = 0; i < cmdline->len; i++)
+ cmdline_insert_chr(pos, (*cmdline)[i]);
+ /* save end of effective command line */
+ cmdline_len = pos;
+
+ /* format rubbed out command line */
+ for (guint i = cmdline->len; i < cmdline->len+cmdline->rubout_len; i++)
+ cmdline_insert_chr(pos, (*cmdline)[i]);
+
+ /* set cursor after effective command line */
+ gtk_editable_set_position(GTK_EDITABLE(cmdline_widget), cmdline_len);
}
void
@@ -258,25 +308,25 @@ handle_key_press(bool is_shift, bool is_ctl, guint keyval)
{
switch (keyval) {
case GDK_Escape:
- cmdline_keypress('\x1B');
+ cmdline.keypress('\x1B');
break;
case GDK_BackSpace:
- cmdline_keypress('\b');
+ cmdline.keypress('\b');
break;
case GDK_Tab:
- cmdline_keypress('\t');
+ cmdline.keypress('\t');
break;
case GDK_Return:
- cmdline_keypress(get_eol());
+ cmdline.keypress(get_eol());
break;
/*
* Function key macros
*/
#define FN(KEY, MACRO) \
- case GDK_##KEY: cmdline_fnmacro(#MACRO); break
+ case GDK_##KEY: cmdline.fnmacro(#MACRO); break
#define FNS(KEY, MACRO) \
- case GDK_##KEY: cmdline_fnmacro(is_shift ? "S" #MACRO : #MACRO); break
+ case GDK_##KEY: cmdline.fnmacro(is_shift ? "S" #MACRO : #MACRO); break
FN(Down, DOWN); FN(Up, UP);
FNS(Left, LEFT); FNS(Right, RIGHT);
FN(KP_Down, DOWN); FN(KP_Up, UP);
@@ -287,7 +337,7 @@ handle_key_press(bool is_shift, bool is_ctl, guint keyval)
g_snprintf(macro_name, sizeof(macro_name),
"F%d", keyval - GDK_F1 + 1);
- cmdline_fnmacro(macro_name);
+ cmdline.fnmacro(macro_name);
break;
}
FNS(Delete, DC);
@@ -317,7 +367,7 @@ handle_key_press(bool is_shift, bool is_ctl, guint keyval)
if (is_ctl)
key = CTL_KEY(g_ascii_toupper(key));
- cmdline_keypress(key);
+ cmdline.keypress(key);
}
}
}
diff --git a/src/interface-gtk.h b/src/interface-gtk.h
index a9b57a8..4afac2f 100644
--- a/src/interface-gtk.h
+++ b/src/interface-gtk.h
@@ -103,11 +103,11 @@ public:
void show_view_impl(ViewGtk *view);
/* implementation of Interface::info_update() */
- void info_update_impl(QRegister *reg);
- void info_update_impl(Buffer *buffer);
+ void info_update_impl(const QRegister *reg);
+ void info_update_impl(const Buffer *buffer);
/* implementation of Interface::cmdline_update() */
- void cmdline_update_impl(const gchar *cmdline = NULL);
+ void cmdline_update_impl(const Cmdline *cmdline);
/* implementation of Interface::popup_add() */
void popup_add_impl(PopupEntryType type,
@@ -138,6 +138,8 @@ public:
private:
static void widget_set_font(GtkWidget *widget, const gchar *font_name);
+
+ void cmdline_insert_chr(gint &pos, gchar chr);
} InterfaceCurrent;
} /* namespace SciTECO */
diff --git a/src/interface.h b/src/interface.h
index 8f37668..6c847f4 100644
--- a/src/interface.h
+++ b/src/interface.h
@@ -32,6 +32,7 @@ namespace SciTECO {
/* avoid include dependency conflict */
class QRegister;
class Buffer;
+class Cmdline;
extern sig_atomic_t sigint_occurred;
/**
@@ -170,10 +171,10 @@ class Interface {
template <class Type>
class UndoTokenInfoUpdate : public UndoTokenWithSize< UndoTokenInfoUpdate<Type> > {
- Type *obj;
+ const Type *obj;
public:
- UndoTokenInfoUpdate(Type *_obj)
+ UndoTokenInfoUpdate(const Type *_obj)
: obj(_obj) {}
void run(void);
@@ -257,30 +258,29 @@ public:
* by the deriving class.
*/
inline void
- info_update(QRegister *reg)
+ info_update(const QRegister *reg)
{
impl().info_update_impl(reg);
}
inline void
- info_update(Buffer *buffer)
+ info_update(const Buffer *buffer)
{
impl().info_update_impl(buffer);
}
inline void
- undo_info_update(QRegister *reg)
+ undo_info_update(const QRegister *reg)
{
undo.push(new UndoTokenInfoUpdate<QRegister>(reg));
}
inline void
- undo_info_update(Buffer *buffer)
+ undo_info_update(const Buffer *buffer)
{
undo.push(new UndoTokenInfoUpdate<Buffer>(buffer));
}
- /* NULL means to redraw the current cmdline if necessary */
inline void
- cmdline_update(const gchar *cmdline = NULL)
+ cmdline_update(const Cmdline *cmdline)
{
impl().cmdline_update_impl(cmdline);
}
diff --git a/src/parser.cpp b/src/parser.cpp
index e84edb5..0193e19 100644
--- a/src/parser.cpp
+++ b/src/parser.cpp
@@ -79,13 +79,14 @@ static gint nest_level = 0;
gchar *strings[2] = {NULL, NULL};
gchar escape_char = '\x1B';
-/*
- * handles all expected exceptions, converting them to
- * SciTECO::Error and preparing them for stack frame insertion
+/**
+ * Handles all expected exceptions, converting them to
+ * SciTECO::Error and preparing them for stack frame insertion.
+ * This method will only throw SciTECO::Error and
+ * SciTECO::Cmdline *.
*/
void
Execute::step(const gchar *macro, gint stop_pos)
- throw (Error, ReplaceCmdline)
{
while (macro_pc < stop_pos) {
#ifdef DEBUG
@@ -698,7 +699,7 @@ StateStart::custom(gchar chr)
break;
case '*':
- if (!g_strcmp0(cmdline, "*"))
+ if (cmdline.len == 1 && cmdline[0] == '*')
/* special save last commandline command */
return &States::save_cmdline;
@@ -1028,7 +1029,7 @@ StateStart::custom(gchar chr)
interface.ssm(SCI_BEGINUNDOACTION);
interface.ssm(SCI_CLEARALL);
- interface.ssm(SCI_ADDTEXT, cmdline_pos-1, (sptr_t)cmdline);
+ interface.ssm(SCI_ADDTEXT, cmdline.pc, (sptr_t)cmdline.str);
interface.ssm(SCI_SCROLLCARET);
interface.ssm(SCI_ENDUNDOACTION);
@@ -1046,7 +1047,8 @@ StateStart::custom(gchar chr)
"editing the replacement register");
/* replace cmdline in the outer macro environment */
- throw ReplaceCmdline();
+ cmdline.replace();
+ /* never reached */
/*
* modifiers
diff --git a/src/parser.h b/src/parser.h
index 4092435..a6a18bb 100644
--- a/src/parser.h
+++ b/src/parser.h
@@ -20,9 +20,9 @@
#include <glib.h>
+#include "sciteco.h"
#include "undo.h"
#include "error.h"
-#include "sciteco.h"
namespace SciTECO {
@@ -300,8 +300,7 @@ extern gchar *strings[2];
extern gchar escape_char;
namespace Execute {
- void step(const gchar *macro, gint stop_pos)
- throw (Error, ReplaceCmdline);
+ void step(const gchar *macro, gint stop_pos);
void macro(const gchar *macro, bool locals = true);
void file(const gchar *filename, bool locals = true);
}
diff --git a/src/qregisters.cpp b/src/qregisters.cpp
index 9cda9ca..615fc77 100644
--- a/src/qregisters.cpp
+++ b/src/qregisters.cpp
@@ -71,7 +71,7 @@ namespace QRegisters {
static QRegister *register_argument = NULL;
void
-QRegisterData::set_string(const gchar *str)
+QRegisterData::set_string(const gchar *str, gsize len)
{
if (QRegisters::current)
QRegisters::current->string.update(QRegisters::view);
@@ -80,8 +80,9 @@ QRegisterData::set_string(const gchar *str)
string.edit(QRegisters::view);
QRegisters::view.ssm(SCI_BEGINUNDOACTION);
- QRegisters::view.ssm(SCI_SETTEXT, 0,
- (sptr_t)(str ? : ""));
+ QRegisters::view.ssm(SCI_CLEARALL);
+ QRegisters::view.ssm(SCI_APPENDTEXT,
+ len, (sptr_t)(str ? : ""));
QRegisters::view.ssm(SCI_ENDUNDOACTION);
if (QRegisters::current)
@@ -110,7 +111,7 @@ QRegisterData::undo_set_string(void)
}
void
-QRegisterData::append_string(const gchar *str)
+QRegisterData::append_string(const gchar *str, gsize len)
{
/*
* NOTE: Will not create undo action
@@ -118,7 +119,7 @@ QRegisterData::append_string(const gchar *str)
* Also, appending preserves the string's
* parameters.
*/
- if (!str || !*str)
+ if (!len)
return;
if (QRegisters::current)
@@ -128,7 +129,7 @@ QRegisterData::append_string(const gchar *str)
QRegisters::view.ssm(SCI_BEGINUNDOACTION);
QRegisters::view.ssm(SCI_APPENDTEXT,
- strlen(str), (sptr_t)str);
+ len, (sptr_t)str);
QRegisters::view.ssm(SCI_ENDUNDOACTION);
if (QRegisters::current)
diff --git a/src/qregisters.h b/src/qregisters.h
index 8414439..ff4da87 100644
--- a/src/qregisters.h
+++ b/src/qregisters.h
@@ -18,6 +18,8 @@
#ifndef __QREGISTERS_H
#define __QREGISTERS_H
+#include <string.h>
+
#include <bsd/sys/queue.h>
#include <glib.h>
@@ -94,9 +96,20 @@ public:
return integer;
}
- virtual void set_string(const gchar *str);
+ virtual void set_string(const gchar *str, gsize len);
+ inline void
+ set_string(const gchar *str)
+ {
+ set_string(str, str ? strlen(str) : 0);
+ }
virtual void undo_set_string(void);
- virtual void append_string(const gchar *str);
+
+ virtual void append_string(const gchar *str, gsize len);
+ inline void
+ append_string(const gchar *str)
+ {
+ append_string(str, str ? strlen(str) : 0);
+ }
virtual inline void
undo_append_string(void)
{
@@ -155,9 +168,9 @@ public:
tecoInt get_integer(void);
- void set_string(const gchar *str) {}
+ void set_string(const gchar *str, gsize len) {}
void undo_set_string(void) {}
- void append_string(const gchar *str) {}
+ void append_string(const gchar *str, gsize len) {}
void undo_append_string(void) {}
gchar *get_string(void);
diff --git a/src/undo.cpp b/src/undo.cpp
index c1e842a..21fae61 100644
--- a/src/undo.cpp
+++ b/src/undo.cpp
@@ -33,18 +33,14 @@
#include "error.h"
#include "undo.h"
+#define SIZE_TO_MB(SIZE) ((gdouble)(SIZE))/(1024*1024)
+
namespace SciTECO {
//#define DEBUG
UndoStack undo;
-static inline gdouble
-size_to_mb(gsize s)
-{
- return ((gdouble)s)/(1024*1024);
-}
-
void
UndoStack::set_memory_limit(gsize new_limit)
{
@@ -61,8 +57,8 @@ UndoStack::set_memory_limit(gsize new_limit)
if (memory_usage > new_limit)
throw Error("Cannot set undo memory limit (%gmb): "
"Current stack too large (%gmb).",
- size_to_mb(new_limit),
- size_to_mb(memory_usage));
+ SIZE_TO_MB(new_limit),
+ SIZE_TO_MB(memory_usage));
}
push_var(memory_limit) = new_limit;
@@ -83,7 +79,7 @@ UndoStack::push(UndoToken *token)
delete token;
throw Error("Undo stack memory limit (%gmb) exceeded. "
"See <EJ> command.",
- size_to_mb(memory_limit));
+ SIZE_TO_MB(memory_limit));
}
memory_usage += token_size;
@@ -92,14 +88,14 @@ UndoStack::push(UndoToken *token)
#ifdef DEBUG
g_printf("UNDO PUSH %p\n", token);
#endif
- token->pos = cmdline_pos;
+ token->pc = cmdline.pc;
SLIST_INSERT_HEAD(&head, token, tokens);
}
void
-UndoStack::pop(gint pos)
+UndoStack::pop(gint pc)
{
- while (!SLIST_EMPTY(&head) && SLIST_FIRST(&head)->pos >= pos) {
+ while (!SLIST_EMPTY(&head) && SLIST_FIRST(&head)->pc >= pc) {
UndoToken *top = SLIST_FIRST(&head);
#ifdef DEBUG
g_printf("UNDO POP %p\n", top);
diff --git a/src/undo.h b/src/undo.h
index bd0ad74..028d73c 100644
--- a/src/undo.h
+++ b/src/undo.h
@@ -41,8 +41,8 @@ public:
SLIST_ENTRY(UndoToken) tokens;
/**
- * Command-line character position corresponding
- * to this token.
+ * Command-line character position (program counter)
+ * corresponding to this token.
*
* @todo This wastes memory in macro calls and loops
* because all undo tokens will have the same
@@ -50,7 +50,7 @@ public:
* stack data structure - as a list/array pointing
* to undo stacks per character.
*/
- gint pos;
+ gint pc;
virtual ~UndoToken() {}
@@ -240,7 +240,7 @@ public:
return push_obj<Type>(variable, variable);
}
- void pop(gint pos);
+ void pop(gint pc);
void clear(void);
} undo;