aboutsummaryrefslogtreecommitdiffhomepage
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/cmdline.c260
-rw-r--r--src/cmdline.h52
-rw-r--r--src/core-commands.c40
-rw-r--r--src/core-commands.h1
-rw-r--r--src/doc.c3
-rw-r--r--src/file-utils.c13
-rw-r--r--src/file-utils.h4
-rw-r--r--src/interface-curses/curses-info-popup.c32
-rw-r--r--src/interface-curses/interface.c344
-rw-r--r--src/interface-gtk/interface.c162
-rw-r--r--src/interface.h3
-rw-r--r--src/lexer.c40
-rw-r--r--src/lexer.h14
-rw-r--r--src/main.c1
-rw-r--r--src/sciteco.h3
-rw-r--r--src/stdio-commands.c2
-rw-r--r--src/symbols.c21
-rw-r--r--src/view.c28
18 files changed, 515 insertions, 508 deletions
diff --git a/src/cmdline.c b/src/cmdline.c
index 089bd7a..e1a4628 100644
--- a/src/cmdline.c
+++ b/src/cmdline.c
@@ -61,11 +61,49 @@ int malloc_trim(size_t pad);
#define TECO_DEFAULT_BREAK_CHARS " \t\v\r\n\f<>,;@"
-teco_cmdline_t teco_cmdline = {};
+/** Style used for the asterisk at the beginning of the command line */
+#define STYLE_ASTERISK 64
-/** Last terminated command line */
+teco_cmdline_t teco_cmdline = {
+ .height = 1
+};
+
+/**
+ * Last terminated command line.
+ * This is not a teco_doc_scintilla_t since we have to return it as a string
+ * at the end of the day.
+ */
static teco_string_t teco_last_cmdline = {NULL, 0};
+void
+teco_cmdline_init(void)
+{
+ teco_cmdline.view = teco_view_new();
+ teco_view_setup(teco_cmdline.view);
+
+ teco_cmdline_ssm(SCI_SETUNDOCOLLECTION, FALSE, 0);
+ teco_cmdline_ssm(SCI_SETVSCROLLBAR, FALSE, 0);
+ teco_cmdline_ssm(SCI_STYLESETBOLD, STYLE_ASTERISK, TRUE);
+ teco_cmdline_ssm(SCI_SETMARGINTYPEN, 1, SC_MARGIN_TEXT);
+ teco_cmdline_ssm(SCI_MARGINSETSTYLE, 0, STYLE_ASTERISK);
+ teco_cmdline_ssm(SCI_SETMARGINWIDTHN, 1,
+ teco_cmdline_ssm(SCI_TEXTWIDTH, STYLE_ASTERISK, (sptr_t)"*"));
+ /* NOTE: might not work on all UIs */
+ teco_cmdline_ssm(SCI_INDICSETSTYLE, INDICATOR_RUBBEDOUT, INDIC_STRIKE);
+ teco_cmdline_ssm(SCI_INDICSETFORE, INDICATOR_RUBBEDOUT,
+ teco_cmdline_ssm(SCI_STYLEGETFORE, STYLE_DEFAULT, 0));
+
+ /* single line mode - EOL characters won't break the line */
+ teco_cmdline_ssm(SCI_SETLINEENDTYPESALLOWED, SC_LINE_END_TYPE_NONE, 0);
+ /* render tabs as "TAB" without indentation */
+ teco_cmdline_ssm(SCI_SETTABDRAWMODE, SCTD_CONTROLCHAR, 0);
+
+ /*
+ * FIXME: Something resets the margin text, so we have to set it last.
+ */
+ teco_cmdline_ssm(SCI_MARGINSETTEXT, 0, (sptr_t)"*");
+}
+
/**
* Insert string into command line and execute
* it immediately.
@@ -83,40 +121,49 @@ teco_cmdline_insert(const gchar *data, gsize len, GError **error)
g_auto(teco_string_t) old_cmdline = {NULL, 0};
gsize repl_pc = 0;
- teco_cmdline.machine.macro_pc = teco_cmdline.pc = teco_cmdline.effective_len;
+ gsize effective_len = teco_cmdline_ssm(SCI_GETCURRENTPOS, 0, 0);
+ teco_cmdline.machine.macro_pc = teco_cmdline.pc = effective_len;
+
+ const gchar *macro = (const gchar *)teco_cmdline_ssm(SCI_GETCHARACTERPOINTER, 0, 0);
+ gsize macro_len = teco_cmdline_ssm(SCI_GETLENGTH, 0, 0);
- if (len <= teco_cmdline.str.len - teco_cmdline.effective_len &&
- !teco_string_cmp(&src, teco_cmdline.str.data + teco_cmdline.effective_len, len)) {
- teco_cmdline.effective_len += len;
+ if (len <= macro_len - effective_len &&
+ !teco_string_cmp(&src, macro + effective_len, len)) {
+ /* extend effective command line from rubbed out part */
+ teco_cmdline_ssm(SCI_GOTOPOS, effective_len+len, 0);
} else {
- if (teco_cmdline.effective_len < teco_cmdline.str.len)
+ /* discard rubbed out part of the command line */
+ if (effective_len < macro_len)
/*
* Automatically disable immediate editing modifier.
* FIXME: Should we show a message as when pressing ^G?
*/
teco_cmdline.modifier_enabled = FALSE;
- teco_cmdline.str.len = teco_cmdline.effective_len;
- teco_string_append(&teco_cmdline.str, data, len);
- teco_cmdline.effective_len = teco_cmdline.str.len;
+ teco_cmdline_ssm(SCI_DELETERANGE, effective_len, macro_len - effective_len);
+ teco_cmdline_ssm(SCI_ADDTEXT, len, (sptr_t)data);
+
+ /* the pointer shouldn't have changed... */
+ macro = (const gchar *)teco_cmdline_ssm(SCI_GETCHARACTERPOINTER, 0, 0);
+ macro_len = teco_cmdline_ssm(SCI_GETLENGTH, 0, 0);
}
+ effective_len += len;
/*
* Parse/execute characters, one at a time so
* undo tokens get emitted for the corresponding characters.
*/
- while (teco_cmdline.pc < teco_cmdline.effective_len) {
+ while (teco_cmdline.pc < effective_len) {
g_autoptr(GError) tmp_error = NULL;
- if (!teco_machine_main_step(&teco_cmdline.machine, teco_cmdline.str.data,
- teco_cmdline.pc+1, &tmp_error)) {
+ if (!teco_machine_main_step(&teco_cmdline.machine, macro, teco_cmdline.pc+1, &tmp_error)) {
if (g_error_matches(tmp_error, TECO_ERROR, TECO_ERROR_CMDLINE)) {
/*
* Result of command line replacement (}):
- * Exchange command lines, avoiding deep copying
+ * Exchange command lines
*/
teco_qreg_t *cmdline_reg = teco_qreg_table_find(&teco_qreg_table_globals, "\e", 1);
- teco_string_t new_cmdline;
+ g_auto(teco_string_t) new_cmdline = {NULL, 0};
if (!cmdline_reg->vtable->get_string(cmdline_reg, &new_cmdline.data, &new_cmdline.len,
NULL, error))
@@ -127,16 +174,26 @@ teco_cmdline_insert(const gchar *data, gsize len, GError **error)
* new command line. This avoids unnecessary rubouts
* and insertions when the command line is updated.
*/
- teco_cmdline.pc = teco_string_diff(&teco_cmdline.str, new_cmdline.data, new_cmdline.len);
+ teco_cmdline.pc = teco_string_diff(&new_cmdline, macro, effective_len);
teco_undo_pop(teco_cmdline.pc);
+ /*
+ * We don't replace the command line's document, since that would
+ * reset the line end type and other configurable settings.
+ * Also, we don't clear the document to avoid unnecessary restylings
+ * if syntax highlighting is enabled on the command line.
+ */
g_assert(old_cmdline.len == 0);
- old_cmdline = teco_cmdline.str;
- teco_cmdline.str = new_cmdline;
- teco_cmdline.effective_len = new_cmdline.len;
+ teco_string_init(&old_cmdline, macro, effective_len);
+ teco_cmdline_ssm(SCI_DELETERANGE, teco_cmdline.pc,
+ old_cmdline.len-teco_cmdline.pc);
+ teco_cmdline_ssm(SCI_ADDTEXT, new_cmdline.len-teco_cmdline.pc,
+ (sptr_t)new_cmdline.data+teco_cmdline.pc);
+
+ macro = (const gchar *)teco_cmdline_ssm(SCI_GETCHARACTERPOINTER, 0, 0);
+ macro_len = effective_len = new_cmdline.len;
teco_cmdline.machine.macro_pc = repl_pc = teco_cmdline.pc;
-
continue;
}
@@ -148,17 +205,26 @@ teco_cmdline_insert(const gchar *data, gsize len, GError **error)
/*
* Error during command-line replacement.
* Replay previous command-line.
- * This avoids deep copying.
+ * The commands leading up to the failed replacement
+ * will be left rubbed out.
*/
teco_undo_pop(repl_pc);
- teco_string_clear(&teco_cmdline.str);
- teco_cmdline.str = old_cmdline;
+ /*
+ * May cause restyling of the command lines,
+ * but that's probably okay - it's just a fallback.
+ */
+ teco_cmdline_ssm(SCI_CLEARALL, 0, 0);
+ teco_cmdline_ssm(SCI_ADDTEXT, old_cmdline.len, (sptr_t)old_cmdline.data);
+ teco_string_clear(&old_cmdline);
memset(&old_cmdline, 0, sizeof(old_cmdline));
teco_cmdline.machine.macro_pc = teco_cmdline.pc = repl_pc;
- /* rubout cmdline replacement command */
- teco_cmdline.effective_len--;
+ /* rub out cmdline replacement command */
+ teco_cmdline_ssm(SCI_GOTOPOS, --effective_len, 0);
+
+ macro = (const gchar *)teco_cmdline_ssm(SCI_GETCHARACTERPOINTER, 0, 0);
+ macro_len = teco_cmdline_ssm(SCI_GETLENGTH, 0, 0);
continue;
}
}
@@ -177,14 +243,18 @@ teco_cmdline_insert(const gchar *data, gsize len, GError **error)
static gboolean
teco_cmdline_rubin(GError **error)
{
- if (!teco_cmdline.str.len)
- return TRUE;
-
- const gchar *start, *end, *next;
- start = teco_cmdline.str.data+teco_cmdline.effective_len;
- end = teco_cmdline.str.data+teco_cmdline.str.len;
- next = g_utf8_find_next_char(start, end) ? : end;
- return teco_cmdline_insert(start, next-start, error);
+ gsize macro_len = teco_cmdline_ssm(SCI_GETLENGTH, 0, 0);
+ gsize pos = teco_cmdline_ssm(SCI_GETCURRENTPOS, 0, 0);
+ gchar buf[4+1];
+ struct Sci_TextRangeFull range = {
+ .chrg = {pos, MIN(macro_len, pos+sizeof(buf)-1)},
+ .lpstrText = buf
+ };
+ gsize len = teco_cmdline_ssm(SCI_GETTEXTRANGEFULL, 0, (sptr_t)&range);
+
+ const gchar *end = buf+len;
+ const gchar *next = g_utf8_find_next_char(buf, end) ? : end;
+ return teco_cmdline_insert(buf, next-buf, error);
}
/**
@@ -219,7 +289,7 @@ teco_cmdline_keypress(const gchar *data, gsize len, GError **error)
*/
teco_interface_msg_clear();
- gsize start_pc = teco_cmdline.effective_len;
+ gsize start_pc = teco_cmdline_ssm(SCI_GETCURRENTPOS, 0, 0);
for (guint i = 0; i < len; i = g_utf8_next_char(data+i) - data) {
gunichar chr = g_utf8_get_char(data+i);
@@ -246,9 +316,9 @@ teco_cmdline_keypress(const gchar *data, gsize len, GError **error)
* up until the insertion point.
*/
teco_undo_pop(start_pc);
- teco_cmdline.effective_len = start_pc;
+ teco_cmdline_ssm(SCI_GOTOPOS, start_pc, 0);
/* program counter could be messed up */
- teco_cmdline.machine.macro_pc = teco_cmdline.effective_len;
+ teco_cmdline.machine.macro_pc = start_pc;
#ifdef HAVE_MALLOC_TRIM
/*
@@ -297,12 +367,16 @@ teco_cmdline_keypress(const gchar *data, gsize len, GError **error)
g_array_remove_range(teco_loop_stack, 0, teco_loop_stack->len);
teco_string_clear(&teco_last_cmdline);
- teco_last_cmdline = teco_cmdline.str;
+ teco_last_cmdline.len = teco_cmdline_ssm(SCI_GETCURRENTPOS, 0, 0);
+ teco_last_cmdline.data = g_malloc(teco_last_cmdline.len + 1);
+ teco_cmdline_ssm(SCI_GETTEXT, teco_last_cmdline.len,
+ (sptr_t)teco_last_cmdline.data);
/*
* FIXME: Preserve the command line after the $$.
+ * This would be useful for command line editing macros.
+ * Perhaps just call teco_cmdline_insert().
*/
- memset(&teco_cmdline.str, 0, sizeof(teco_cmdline.str));
- teco_cmdline.effective_len = 0;
+ teco_cmdline_ssm(SCI_CLEARALL, 0, 0);
#ifdef HAVE_MALLOC_TRIM
/* see above */
@@ -317,10 +391,8 @@ teco_cmdline_keypress(const gchar *data, gsize len, GError **error)
start_pc = 0;
}
- /*
- * Echo command line
- */
- teco_interface_cmdline_update(&teco_cmdline);
+ teco_cmdline_update();
+
return TRUE;
}
@@ -377,20 +449,48 @@ teco_cmdline_keymacro(const gchar *name, gssize name_len, GError **error)
static void
teco_cmdline_rubout(void)
{
- const gchar *p;
- p = g_utf8_find_prev_char(teco_cmdline.str.data,
- teco_cmdline.str.data+teco_cmdline.effective_len);
- if (p) {
- teco_cmdline.effective_len = p - teco_cmdline.str.data;
- teco_undo_pop(teco_cmdline.effective_len);
+ gsize effective_len = teco_cmdline_ssm(SCI_GETCURRENTPOS, 0, 0);
+ gssize p = teco_view_glyphs2bytes_relative(teco_cmdline.view, effective_len, -1);
+ if (p >= 0) {
+ teco_cmdline_ssm(SCI_GOTOPOS, p, 0);
+ teco_undo_pop(p);
}
}
-static void TECO_DEBUG_CLEANUP
+/**
+ * Update the command line, i.e. prepare it for displaying.
+ *
+ * This updates the indicators and scrolls the caret, which isn't done every time
+ * we touch the command line itself.
+ */
+void
+teco_cmdline_update(void)
+{
+ /*
+ * FIXME: Perhaps this can be avoided completely by updating the
+ * indicators in teco_cmdline_insert().
+ */
+ gsize effective_len = teco_cmdline_ssm(SCI_GETCURRENTPOS, 0, 0);
+ gsize macro_len = teco_cmdline_ssm(SCI_GETLENGTH, 0, 0);
+ teco_cmdline_ssm(SCI_SETINDICATORCURRENT, INDICATOR_RUBBEDOUT, 0);
+ teco_cmdline_ssm(SCI_INDICATORCLEARRANGE, 0, macro_len);
+ teco_cmdline_ssm(SCI_INDICATORFILLRANGE, effective_len, macro_len - effective_len);
+
+ teco_cmdline_ssm(SCI_SCROLLCARET, 0, 0);
+
+ /*
+ * FIXME: This gets reset repeatedly.
+ * Setting it once per keypress however means you can no longer customize
+ * the margin text.
+ */
+ teco_cmdline_ssm(SCI_MARGINSETTEXT, 0, (sptr_t)"*");
+}
+
+void
teco_cmdline_cleanup(void)
{
teco_machine_main_clear(&teco_cmdline.machine);
- teco_string_clear(&teco_cmdline.str);
+ teco_view_free(teco_cmdline.view);
teco_string_clear(&teco_last_cmdline);
}
@@ -448,11 +548,12 @@ teco_state_process_edit_cmd(teco_machine_t *ctx, teco_machine_t *parent_ctx, gun
if (teco_cmdline.modifier_enabled) {
/* reinsert construct */
+ gsize macro_len = teco_cmdline_ssm(SCI_GETLENGTH, 0, 0);
do {
if (!teco_cmdline_rubin(error))
return FALSE;
} while (!ctx->current->is_start &&
- teco_cmdline.effective_len < teco_cmdline.str.len);
+ teco_cmdline_ssm(SCI_GETCURRENTPOS, 0, 0) < macro_len);
} else {
/* rubout construct */
do
@@ -508,6 +609,9 @@ teco_state_command_process_edit_cmd(teco_machine_main_t *ctx, teco_machine_t *pa
case TECO_CTL_KEY('W'): /* rubout/reinsert command */
teco_interface_popup_clear();
+ const gchar *macro = (const gchar *)teco_cmdline_ssm(SCI_GETCHARACTERPOINTER, 0, 0);
+ gsize macro_len = teco_cmdline_ssm(SCI_GETLENGTH, 0, 0);
+
/*
* This mimics the behavior of the `Y` command,
* so it also rubs out no-op commands.
@@ -517,9 +621,8 @@ teco_state_command_process_edit_cmd(teco_machine_main_t *ctx, teco_machine_t *pa
/* reinsert command */
/* @ and : are not separate states, but practically belong to the command */
while (ctx->parent.current->is_start &&
- teco_cmdline.effective_len < teco_cmdline.str.len &&
- (teco_cmdline.str.data[teco_cmdline.effective_len] == ':' ||
- teco_cmdline.str.data[teco_cmdline.effective_len] == '@'))
+ teco_cmdline_ssm(SCI_GETCURRENTPOS, 0, 0) < macro_len &&
+ strchr(":@", macro[teco_cmdline_ssm(SCI_GETCURRENTPOS, 0, 0)]) != NULL)
if (!teco_cmdline_rubin(error))
return FALSE;
@@ -527,11 +630,11 @@ teco_state_command_process_edit_cmd(teco_machine_main_t *ctx, teco_machine_t *pa
if (!teco_cmdline_rubin(error))
return FALSE;
} while (!ctx->parent.current->is_start &&
- teco_cmdline.effective_len < teco_cmdline.str.len);
+ teco_cmdline_ssm(SCI_GETCURRENTPOS, 0, 0) < macro_len);
while (ctx->parent.current->is_start &&
- teco_cmdline.effective_len < teco_cmdline.str.len &&
- teco_is_noop(teco_cmdline.str.data[teco_cmdline.effective_len]))
+ teco_cmdline_ssm(SCI_GETCURRENTPOS, 0, 0) < macro_len &&
+ teco_is_noop(macro[teco_cmdline_ssm(SCI_GETCURRENTPOS, 0, 0)]))
if (!teco_cmdline_rubin(error))
return FALSE;
@@ -540,8 +643,8 @@ teco_state_command_process_edit_cmd(teco_machine_main_t *ctx, teco_machine_t *pa
/* rubout command */
while (ctx->parent.current->is_start &&
- teco_cmdline.effective_len > 0 &&
- teco_is_noop(teco_cmdline.str.data[teco_cmdline.effective_len-1]))
+ teco_cmdline_ssm(SCI_GETCURRENTPOS, 0, 0) > 0 &&
+ teco_is_noop(macro[teco_cmdline_ssm(SCI_GETCURRENTPOS, 0, 0)-1]))
teco_cmdline_rubout();
do
@@ -555,7 +658,7 @@ teco_state_command_process_edit_cmd(teco_machine_main_t *ctx, teco_machine_t *pa
*/
while (ctx->parent.current->is_start &&
(ctx->flags.modifier_at || ctx->flags.modifier_colon) &&
- teco_cmdline.effective_len > 0)
+ teco_cmdline_ssm(SCI_GETCURRENTPOS, 0, 0) > 0)
teco_cmdline_rubout();
return TRUE;
@@ -570,6 +673,9 @@ teco_state_stringbuilding_start_process_edit_cmd(teco_machine_stringbuilding_t *
{
teco_state_t *current = ctx->parent.current;
+ const gchar *macro = (const gchar *)teco_cmdline_ssm(SCI_GETCHARACTERPOINTER, 0, 0);
+ gsize macro_len = teco_cmdline_ssm(SCI_GETLENGTH, 0, 0);
+
switch (key) {
case TECO_CTL_KEY('W'): { /* rubout/reinsert word */
teco_interface_popup_clear();
@@ -587,15 +693,15 @@ teco_state_stringbuilding_start_process_edit_cmd(teco_machine_stringbuilding_t *
if (teco_cmdline.modifier_enabled) {
/* reinsert word chars */
while (ctx->parent.current == current &&
- teco_cmdline.effective_len < teco_cmdline.str.len &&
- teco_string_contains(&wchars, teco_cmdline.str.data[teco_cmdline.effective_len]))
+ teco_cmdline_ssm(SCI_GETCURRENTPOS, 0, 0) < macro_len &&
+ teco_string_contains(&wchars, macro[teco_cmdline_ssm(SCI_GETCURRENTPOS, 0, 0)]))
if (!teco_cmdline_rubin(error))
return FALSE;
/* reinsert non-word chars */
while (ctx->parent.current == current &&
- teco_cmdline.effective_len < teco_cmdline.str.len &&
- !teco_string_contains(&wchars, teco_cmdline.str.data[teco_cmdline.effective_len]))
+ teco_cmdline_ssm(SCI_GETCURRENTPOS, 0, 0) < macro_len &&
+ !teco_string_contains(&wchars, macro[teco_cmdline_ssm(SCI_GETCURRENTPOS, 0, 0)]))
if (!teco_cmdline_rubin(error))
return FALSE;
@@ -609,7 +715,7 @@ teco_state_stringbuilding_start_process_edit_cmd(teco_machine_stringbuilding_t *
* a result string even in parse-only mode.
*/
if (ctx->result && ctx->result->len > 0) {
- gboolean is_wordchar = teco_string_contains(&wchars, teco_cmdline.str.data[teco_cmdline.effective_len-1]);
+ gboolean is_wordchar = teco_string_contains(&wchars, macro[teco_cmdline_ssm(SCI_GETCURRENTPOS, 0, 0)-1]);
teco_cmdline_rubout();
if (ctx->parent.current != current) {
/* rub out string building command */
@@ -625,13 +731,13 @@ teco_state_stringbuilding_start_process_edit_cmd(teco_machine_stringbuilding_t *
*/
if (!is_wordchar) {
while (ctx->result->len > 0 &&
- !teco_string_contains(&wchars, teco_cmdline.str.data[teco_cmdline.effective_len-1]))
+ !teco_string_contains(&wchars, macro[teco_cmdline_ssm(SCI_GETCURRENTPOS, 0, 0)-1]))
teco_cmdline_rubout();
}
/* rubout word chars */
while (ctx->result->len > 0 &&
- teco_string_contains(&wchars, teco_cmdline.str.data[teco_cmdline.effective_len-1]))
+ teco_string_contains(&wchars, macro[teco_cmdline_ssm(SCI_GETCURRENTPOS, 0, 0)-1]))
teco_cmdline_rubout();
return TRUE;
@@ -648,8 +754,7 @@ teco_state_stringbuilding_start_process_edit_cmd(teco_machine_stringbuilding_t *
if (teco_cmdline.modifier_enabled) {
/* reinsert string */
- while (ctx->parent.current == current &&
- teco_cmdline.effective_len < teco_cmdline.str.len)
+ while (ctx->parent.current == current && teco_cmdline_ssm(SCI_GETCURRENTPOS, 0, 0) < macro_len)
if (!teco_cmdline_rubin(error))
return FALSE;
@@ -827,18 +932,21 @@ teco_state_expectfile_process_edit_cmd(teco_machine_main_t *ctx, teco_machine_t
case TECO_CTL_KEY('W'): /* rubout/reinsert file names including directories */
teco_interface_popup_clear();
+ const gchar *macro = (const gchar *)teco_cmdline_ssm(SCI_GETCHARACTERPOINTER, 0, 0);
+ gsize macro_len = teco_cmdline_ssm(SCI_GETLENGTH, 0, 0);
+
if (teco_cmdline.modifier_enabled) {
/* reinsert one level of file name */
while (stringbuilding_ctx->parent.current == stringbuilding_current &&
- teco_cmdline.effective_len < teco_cmdline.str.len &&
- !G_IS_DIR_SEPARATOR(teco_cmdline.str.data[teco_cmdline.effective_len]))
+ teco_cmdline_ssm(SCI_GETCURRENTPOS, 0, 0) < macro_len &&
+ !G_IS_DIR_SEPARATOR(macro[teco_cmdline_ssm(SCI_GETCURRENTPOS, 0, 0)]))
if (!teco_cmdline_rubin(error))
return FALSE;
/* reinsert final directory separator */
if (stringbuilding_ctx->parent.current == stringbuilding_current &&
- teco_cmdline.effective_len < teco_cmdline.str.len &&
- G_IS_DIR_SEPARATOR(teco_cmdline.str.data[teco_cmdline.effective_len]) &&
+ teco_cmdline_ssm(SCI_GETCURRENTPOS, 0, 0) < macro_len &&
+ G_IS_DIR_SEPARATOR(macro[teco_cmdline_ssm(SCI_GETCURRENTPOS, 0, 0)]) &&
!teco_cmdline_rubin(error))
return FALSE;
@@ -847,12 +955,12 @@ teco_state_expectfile_process_edit_cmd(teco_machine_main_t *ctx, teco_machine_t
if (ctx->expectstring.string.len > 0) {
/* rubout directory separator */
- if (G_IS_DIR_SEPARATOR(teco_cmdline.str.data[teco_cmdline.effective_len-1]))
+ if (G_IS_DIR_SEPARATOR(macro[teco_cmdline_ssm(SCI_GETCURRENTPOS, 0, 0)-1]))
teco_cmdline_rubout();
/* rubout one level of file name */
while (ctx->expectstring.string.len > 0 &&
- !G_IS_DIR_SEPARATOR(teco_cmdline.str.data[teco_cmdline.effective_len-1]))
+ !G_IS_DIR_SEPARATOR(macro[teco_cmdline_ssm(SCI_GETCURRENTPOS, 0, 0)-1]))
teco_cmdline_rubout();
return TRUE;
diff --git a/src/cmdline.h b/src/cmdline.h
index abe9b53..9123358 100644
--- a/src/cmdline.h
+++ b/src/cmdline.h
@@ -19,10 +19,13 @@
#include <glib.h>
#include "sciteco.h"
-#include "string-utils.h"
#include "parser.h"
+#include "view.h"
#include "undo.h"
+/** Indicator number used for the rubbed out part of the command line */
+#define INDICATOR_RUBBEDOUT (INDICATOR_CONTAINER+0)
+
typedef struct {
/**
* State machine used for interactive mode (commandline macro).
@@ -34,16 +37,17 @@ typedef struct {
teco_machine_main_t machine;
/**
- * String containing the current command line
- * (both effective and rubbed out).
- */
- teco_string_t str;
- /**
- * Effective command line length.
- * The length of the rubbed out part of the command line
- * is (teco_cmdline.str.len - teco_cmdline.effective_len).
+ * Command-line Scintilla view.
+ * It's document contains the current command line macro.
+ * The current position (cursor) marks the end of the
+ * "effective" command line, while everything afterwards
+ * is the rubbed out part of the command line.
+ * The rubbed out part should be highlighted with an indicator.
*/
- gsize effective_len;
+ teco_view_t *view;
+
+ /** Height of the command line view in lines */
+ guint height;
/** Program counter within the command-line macro */
gsize pc;
@@ -60,6 +64,30 @@ typedef struct {
extern teco_cmdline_t teco_cmdline;
+void teco_cmdline_init(void);
+
+static inline sptr_t
+teco_cmdline_ssm(unsigned int iMessage, uptr_t wParam, sptr_t lParam)
+{
+ return teco_view_ssm(teco_cmdline.view, iMessage, wParam, lParam);
+}
+
+/**
+ * Update scroll beavior on command line after window resizes.
+ *
+ * This should ensure that the caret jumps to the middle of the command line.
+ *
+ * @param width Window (command line view) width in pixels or columns.
+ *
+ * @fixme
+ * On the other hand this limits how you can customize the scroll behavior.
+ */
+static inline void
+teco_cmdline_resized(guint width)
+{
+ teco_cmdline_ssm(SCI_SETXCARETPOLICY, CARET_SLOP | CARET_EVEN, width/2);
+}
+
gboolean teco_cmdline_keypress(const gchar *data, gsize len, GError **error);
typedef enum {
@@ -84,6 +112,10 @@ teco_cmdline_keymacro_c(gchar key, GError **error)
return TRUE;
}
+void teco_cmdline_update(void);
+
+void teco_cmdline_cleanup(void);
+
/*
* Command states
*/
diff --git a/src/core-commands.c b/src/core-commands.c
index 653a40f..e0b2f89 100644
--- a/src/core-commands.c
+++ b/src/core-commands.c
@@ -551,9 +551,11 @@ teco_state_start_cmdline_push(teco_machine_main_t *ctx, GError **error)
!teco_qreg_table_edit_name(&teco_qreg_table_globals, "\e", 1, error))
return;
+ const gchar *macro = (const gchar *)teco_cmdline_ssm(SCI_GETCHARACTERPOINTER, 0, 0);
+
teco_interface_ssm(SCI_BEGINUNDOACTION, 0, 0);
teco_interface_ssm(SCI_CLEARALL, 0, 0);
- teco_interface_ssm(SCI_ADDTEXT, teco_cmdline.pc, (sptr_t)teco_cmdline.str.data);
+ teco_interface_ssm(SCI_ADDTEXT, teco_cmdline.pc, (sptr_t)macro);
teco_interface_ssm(SCI_ENDUNDOACTION, 0, 0);
/*
@@ -771,8 +773,10 @@ teco_state_start_input(teco_machine_main_t *ctx, gunichar chr, GError **error)
* for beginnings of command-lines?
* It could also be used for a corresponding KEYMACRO mask.
*/
- if (teco_cmdline.effective_len == 1 && teco_cmdline.str.data[0] == '*')
+ if (teco_cmdline_ssm(SCI_GETCURRENTPOS, 0, 0) == 1 &&
+ teco_cmdline_ssm(SCI_GETCHARAT, 0, 0) == '*')
return &teco_state_save_cmdline;
+ /* treat as an operator */
break;
case '<':
@@ -2013,6 +2017,13 @@ TECO_DEFINE_STATE_COMMAND(teco_state_ctlc_control);
* by the \(lqNerd Fonts\(rq project.
* Changes to this flag in interactive mode may not become
* effective immediately.
+ * .IP 1024:
+ * If set the default clipboard register \(lq~\(rq will refer
+ * to the primary clipboard (\(lq~P\(rq) instead of the
+ * clipboard selection (\(lq~C\(rq).
+ * .IP 2048:
+ * Enable/Disable redirection of Scintilla messages (\fBES\fP)
+ * to the command line's Scintilla view.
*
* The features controlled thus are discribed in other sections
* of this manual.
@@ -2135,8 +2146,11 @@ teco_state_ecommand_flags(teco_machine_main_t *ctx, GError **error)
* The column after the last horizontal movement.
* This is only used by \fBfnkeys.tes\fP and is similar to the Scintilla-internal
* setting \fBSCI_CHOOSECARETX\fP.
- * Unless most other settings, this is on purpose not restored on rubout,
- * so it "survives" command line replacements.
+ * Unlike most other settings, this is on purpose not restored on rubout,
+ * so it \(lqsurvives\(rq command line replacements.
+ * .IP 5:
+ * Height of the command line view in lines.
+ * Must not be smaller than 1.
* .
* .IP -1:
* Type of the last mouse event (\fBread-only\fP).
@@ -2189,7 +2203,8 @@ teco_state_ecommand_properties(teco_machine_main_t *ctx, GError **error)
EJ_BUFFERS,
EJ_MEMORY_LIMIT,
EJ_INIT_COLOR,
- EJ_CARETX
+ EJ_CARETX,
+ EJ_CMDLINE_HEIGHT
};
static teco_int_t caret_x = 0;
@@ -2226,9 +2241,20 @@ teco_state_ecommand_properties(teco_machine_main_t *ctx, GError **error)
break;
case EJ_CARETX:
+ /* DON'T undo on rubout */
caret_x = value;
break;
+ case EJ_CMDLINE_HEIGHT:
+ if (value < 1 || value > G_MAXUINT) {
+ g_set_error(error, TECO_ERROR, TECO_ERROR_FAILED,
+ "Invalid command line height %" TECO_INT_FORMAT " "
+ "for <EJ>", value);
+ return;
+ }
+ teco_undo_guint(teco_cmdline.height) = value;
+ break;
+
default:
g_set_error(error, TECO_ERROR, TECO_ERROR_FAILED,
"Cannot set property %" TECO_INT_FORMAT " "
@@ -2284,6 +2310,10 @@ teco_state_ecommand_properties(teco_machine_main_t *ctx, GError **error)
teco_expressions_push(caret_x);
break;
+ case EJ_CMDLINE_HEIGHT:
+ teco_expressions_push(teco_cmdline.height);
+ break;
+
default:
g_set_error(error, TECO_ERROR, TECO_ERROR_FAILED,
"Invalid property %" TECO_INT_FORMAT " "
diff --git a/src/core-commands.h b/src/core-commands.h
index cb28dce..4cc8747 100644
--- a/src/core-commands.h
+++ b/src/core-commands.h
@@ -125,6 +125,7 @@ TECO_DECLARE_STATE(teco_state_insert_indent);
teco_state_t *teco_state_start_input(teco_machine_main_t *ctx, gunichar chr, GError **error);
TECO_DECLARE_STATE(teco_state_start);
+TECO_DECLARE_STATE(teco_state_control);
TECO_DECLARE_STATE(teco_state_escape);
TECO_DECLARE_STATE(teco_state_ctlc);
TECO_DECLARE_STATE(teco_state_ctlc_control);
diff --git a/src/doc.c b/src/doc.c
index 4ac96c8..1ec4d80 100644
--- a/src/doc.c
+++ b/src/doc.c
@@ -205,7 +205,8 @@ teco_doc_get_string(teco_doc_t *ctx, gchar **str, gsize *outlen, guint *codepage
gsize len = teco_view_ssm(teco_qreg_view, SCI_GETLENGTH, 0, 0);
if (str) {
*str = g_malloc(len + 1);
- teco_view_ssm(teco_qreg_view, SCI_GETTEXT, len + 1, (sptr_t)*str);
+ /* null-terminates the string */
+ teco_view_ssm(teco_qreg_view, SCI_GETTEXT, len, (sptr_t)*str);
}
if (outlen)
*outlen = len;
diff --git a/src/file-utils.c b/src/file-utils.c
index 75bcb48..db06ff3 100644
--- a/src/file-utils.c
+++ b/src/file-utils.c
@@ -109,6 +109,14 @@ teco_file_set_attributes(const gchar *filename, teco_file_attributes_t attrs)
#ifdef G_OS_UNIX
+/*
+ * NOTE: This version does not resolve symlinks to non-existing paths.
+ * It could be improved by repeating readlink() and g_canonicalize_filename(),
+ * but it would require glib v2.58.0.
+ * Alternatively we could also iteratively resolve all path components.
+ * Currently, we simply do not rely on successful canonicalization of
+ * yet non-existing paths.
+ */
gchar *
teco_file_get_absolute_path(const gchar *path)
{
@@ -137,13 +145,12 @@ teco_file_is_visible(const gchar *path)
#if GLIB_CHECK_VERSION(2,58,0)
/*
- * FIXME: This should perhaps be preferred on any platform.
- * But it will complicate preprocessing.
+ * NOTE: Does not resolve symlinks.
*/
gchar *
teco_file_get_absolute_path(const gchar *path)
{
- return g_canonicalize_filename(path, NULL);
+ return path ? g_canonicalize_filename(path, NULL) : NULL;
}
#else /* !GLIB_CHECK_VERSION(2,58,0) */
diff --git a/src/file-utils.h b/src/file-utils.h
index 12a9b83..9a2f8d6 100644
--- a/src/file-utils.h
+++ b/src/file-utils.h
@@ -32,9 +32,7 @@ void teco_file_set_attributes(const gchar *filename, teco_file_attributes_t attr
/**
* Get absolute/full version of a possibly relative path.
* The path is tried to be canonicalized so it does
- * not contain relative components.
- * Works with existing and non-existing paths (in the latter case,
- * heuristics may be applied).
+ * not contain relative components and symlinks.
* Depending on platform and existence of the path,
* canonicalization might fail, but the path returned is
* always absolute.
diff --git a/src/interface-curses/curses-info-popup.c b/src/interface-curses/curses-info-popup.c
index 332d434..c51a99b 100644
--- a/src/interface-curses/curses-info-popup.c
+++ b/src/interface-curses/curses-info-popup.c
@@ -26,6 +26,7 @@
#include "list.h"
#include "string-utils.h"
#include "interface.h"
+#include "cmdline.h"
#include "curses-utils.h"
#include "curses-info-popup.h"
#include "curses-icons.h"
@@ -71,7 +72,6 @@ teco_curses_info_popup_add(teco_curses_info_popup_t *ctx, teco_popup_entry_type_
static void
teco_curses_info_popup_init_pad(teco_curses_info_popup_t *ctx, attr_t attr)
{
- int cols = getmaxx(stdscr); /**! screen width */
int pad_lines; /**! pad height */
gint pad_cols; /**! entry columns */
gint pad_colwidth; /**! width per entry column */
@@ -82,10 +82,10 @@ teco_curses_info_popup_init_pad(teco_curses_info_popup_t *ctx, attr_t attr)
* Otherwise 2 characters after the entry.
*/
gint reserve = teco_ed & TECO_ED_ICONS ? 2+1 : 2;
- pad_colwidth = MIN(ctx->longest + reserve, cols - 2);
+ pad_colwidth = MIN(ctx->longest + reserve, COLS - 2);
/* pad_cols = floor((cols - 2) / pad_colwidth) */
- pad_cols = (cols - 2) / pad_colwidth;
+ pad_cols = (COLS - 2) / pad_colwidth;
/* pad_lines = ceil(length / pad_cols) */
pad_lines = (ctx->length+pad_cols-1) / pad_cols;
@@ -96,7 +96,7 @@ teco_curses_info_popup_init_pad(teco_curses_info_popup_t *ctx, attr_t attr)
* it will be drawn into the popup window which has left
* and right borders.
*/
- ctx->pad = newpad(pad_lines, cols - 2);
+ ctx->pad = newpad(pad_lines, COLS - 2);
/*
* NOTE: attr could contain A_REVERSE on monochrome terminals,
@@ -157,9 +157,6 @@ teco_curses_info_popup_show(teco_curses_info_popup_t *ctx, attr_t attr)
/* nothing to display */
return;
- int lines, cols; /* screen dimensions */
- getmaxyx(stdscr, lines, cols);
-
if (ctx->window)
delwin(ctx->window);
@@ -171,10 +168,10 @@ teco_curses_info_popup_show(teco_curses_info_popup_t *ctx, attr_t attr)
* Popup window can cover all but one screen row.
* Another row is reserved for the top border.
*/
- gint popup_lines = MIN(pad_lines + 1, lines - 1);
+ gint popup_lines = MIN(pad_lines + 1, LINES - teco_cmdline.height);
/* window covers message, scintilla and info windows */
- ctx->window = newwin(popup_lines, 0, lines - 1 - popup_lines, 0);
+ ctx->window = newwin(popup_lines, 0, LINES - teco_cmdline.height - popup_lines, 0);
wattrset(ctx->window, attr);
@@ -188,7 +185,7 @@ teco_curses_info_popup_show(teco_curses_info_popup_t *ctx, attr_t attr)
copywin(ctx->pad, ctx->window,
ctx->pad_first_line, 0,
- 1, 1, popup_lines - 1, cols - 2, FALSE);
+ 1, 1, popup_lines - 1, COLS - 2, FALSE);
if (pad_lines <= popup_lines - 1)
/* no need for scrollbar */
@@ -200,13 +197,13 @@ teco_curses_info_popup_show(teco_curses_info_popup_t *ctx, attr_t attr)
/* bar_y = floor(pad_first_line/pad_lines * (popup_lines-2)) + 1 */
gint bar_y = ctx->pad_first_line*(popup_lines-2) / pad_lines + 1;
- mvwvline(ctx->window, 1, cols-1, ACS_CKBOARD, popup_lines-2);
+ mvwvline(ctx->window, 1, COLS-1, ACS_CKBOARD, popup_lines-2);
/*
* We do not use ACS_BLOCK here since it will not
* always be drawn as a solid block (e.g. xterm).
* Instead, simply draw reverse blanks.
*/
- wmove(ctx->window, bar_y, cols-1);
+ wmove(ctx->window, bar_y, COLS-1);
wattrset(ctx->window, attr ^ A_REVERSE);
wvline(ctx->window, ' ', bar_height);
}
@@ -227,7 +224,6 @@ teco_curses_info_popup_show(teco_curses_info_popup_t *ctx, attr_t attr)
const teco_string_t *
teco_curses_info_popup_getentry(teco_curses_info_popup_t *ctx, gint y, gint x)
{
- int cols = getmaxx(stdscr); /**! screen width */
gint pad_cols; /**! entry columns */
gint pad_colwidth; /**! width per entry column */
@@ -240,10 +236,10 @@ teco_curses_info_popup_getentry(teco_curses_info_popup_t *ctx, gint y, gint x)
* Otherwise 2 characters after the entry.
*/
gint reserve = teco_ed & TECO_ED_ICONS ? 2+1 : 2;
- pad_colwidth = MIN(ctx->longest + reserve, cols - 2);
+ pad_colwidth = MIN(ctx->longest + reserve, COLS - 2);
/* pad_cols = floor((cols - 2) / pad_colwidth) */
- pad_cols = (cols - 2) / pad_colwidth;
+ pad_cols = (COLS - 2) / pad_colwidth;
gint cur_col = 0;
for (teco_stailq_entry_t *cur = ctx->list.first; cur != NULL; cur = cur->next) {
@@ -265,9 +261,8 @@ teco_curses_info_popup_getentry(teco_curses_info_popup_t *ctx, gint y, gint x)
void
teco_curses_info_popup_scroll_page(teco_curses_info_popup_t *ctx)
{
- gint lines = getmaxy(stdscr);
gint pad_lines = getmaxy(ctx->pad);
- gint popup_lines = MIN(pad_lines + 1, lines - 1);
+ gint popup_lines = MIN(pad_lines + 1, LINES - teco_cmdline.height);
/* progress scroll position */
ctx->pad_first_line += popup_lines - 1;
@@ -281,9 +276,8 @@ teco_curses_info_popup_scroll_page(teco_curses_info_popup_t *ctx)
void
teco_curses_info_popup_scroll(teco_curses_info_popup_t *ctx, gint delta)
{
- gint lines = getmaxy(stdscr);
gint pad_lines = getmaxy(ctx->pad);
- gint popup_lines = MIN(pad_lines + 1, lines - 1);
+ gint popup_lines = MIN(pad_lines + 1, LINES - teco_cmdline.height);
ctx->pad_first_line = MAX(ctx->pad_first_line+delta, 0);
if (pad_lines - ctx->pad_first_line < popup_lines - 1)
diff --git a/src/interface-curses/interface.c b/src/interface-curses/interface.c
index 6c8c812..8f41f2a 100644
--- a/src/interface-curses/interface.c
+++ b/src/interface-curses/interface.c
@@ -166,19 +166,91 @@ static gint teco_interface_blocking_getch(void);
#define COLOR_LCYAN COLOR_LIGHT(COLOR_CYAN)
#define COLOR_LWHITE COLOR_LIGHT(COLOR_WHITE)
+static struct {
+ /**
+ * Mapping of foreground and background curses color tuples
+ * (encoded into a pointer) to a color pair number.
+ */
+ GHashTable *pair_table;
+
+ /**
+ * Mapping of the first 16 curses color codes (that may or may not
+ * correspond with the standard terminal color codes) to
+ * Scintilla-compatible RGB values (red is LSB) to initialize after
+ * Curses startup.
+ * Negative values mean no color redefinition (keep the original
+ * palette entry).
+ */
+ gint32 color_table[16];
+
+ /**
+ * Mapping of the first 16 curses color codes to their
+ * original values for restoring them on shutdown.
+ * Unfortunately, this may not be supported on all
+ * curses ports, so this array may be unused.
+ */
+ struct {
+ gshort r, g, b;
+ } orig_color_table[16];
+
+ int stdin_orig, stdout_orig, stderr_orig;
+ SCREEN *screen;
+ FILE *screen_tty;
+
+ WINDOW *info_window;
+ enum {
+ TECO_INFO_TYPE_BUFFER = 0,
+ TECO_INFO_TYPE_QREG
+ } info_type;
+ teco_string_t info_current;
+ gboolean info_dirty;
+
+ WINDOW *msg_window;
+
+ /**
+ * Pad used exclusively for wgetch() as it will not
+ * result in unwanted wrefresh().
+ */
+ WINDOW *input_pad;
+ GQueue *input_queue;
+
+ teco_curses_info_popup_t popup;
+ gsize popup_prefix_len;
+
+ /**
+ * GError "thrown" by teco_interface_event_loop_iter().
+ * Having this in a variable avoids problems with EMScripten.
+ */
+ GError *event_loop_error;
+} teco_interface;
+
/**
- * Returns the curses `COLOR_PAIR` for the given curses foreground and background `COLOR`s.
- * This is used simply to enumerate every possible color combination.
- * Note: only 256 combinations are possible due to curses portability.
+ * Returns the curses color pair for the given curses foreground and background colors.
+ * Initializes a new pair if necessary.
+ *
+ * Scinterm no longer initializes all color pairs for all combinations of
+ * the builtin foreground and background colors.
+ * Since curses guarantees only 256 color pairs, we cannot do that either.
+ * Instead we allocate color pairs beginnig at 128 on demand
+ * (similar to what Scinterm does).
*
- * @param fg The curses foreground `COLOR`.
- * @param bg The curses background `COLOR`.
- * @return number for defining a curses `COLOR_PAIR`.
+ * @param fg curses foreground color
+ * @param bg curses background color
+ * @return curses color pair number
*/
-static inline gshort
+static gshort
teco_color_pair(gshort fg, gshort bg)
{
- return bg * (COLORS < 16 ? 8 : 16) + fg + 1;
+ static gshort last_pair = 127;
+
+ G_STATIC_ASSERT(sizeof(gshort)*2 <= sizeof(guint));
+ gpointer key = GUINT_TO_POINTER(((guint)fg << 16) | bg);
+ gpointer value = g_hash_table_lookup(teco_interface.pair_table, key);
+ if (G_LIKELY(value != NULL))
+ return GPOINTER_TO_UINT(value);
+ init_pair(++last_pair, fg, bg);
+ g_hash_table_insert(teco_interface.pair_table, key, GUINT_TO_POINTER(last_pair));
+ return last_pair;
}
/**
@@ -336,61 +408,6 @@ teco_view_free(teco_view_t *ctx)
scintilla_delete(ctx);
}
-static struct {
- /**
- * Mapping of the first 16 curses color codes (that may or may not
- * correspond with the standard terminal color codes) to
- * Scintilla-compatible RGB values (red is LSB) to initialize after
- * Curses startup.
- * Negative values mean no color redefinition (keep the original
- * palette entry).
- */
- gint32 color_table[16];
-
- /**
- * Mapping of the first 16 curses color codes to their
- * original values for restoring them on shutdown.
- * Unfortunately, this may not be supported on all
- * curses ports, so this array may be unused.
- */
- struct {
- gshort r, g, b;
- } orig_color_table[16];
-
- int stdin_orig, stdout_orig, stderr_orig;
- SCREEN *screen;
- FILE *screen_tty;
-
- WINDOW *info_window;
- enum {
- TECO_INFO_TYPE_BUFFER = 0,
- TECO_INFO_TYPE_QREG
- } info_type;
- teco_string_t info_current;
- gboolean info_dirty;
-
- WINDOW *msg_window;
-
- WINDOW *cmdline_window, *cmdline_pad;
- guint cmdline_len, cmdline_rubout_len;
-
- /**
- * Pad used exclusively for wgetch() as it will not
- * result in unwanted wrefresh().
- */
- WINDOW *input_pad;
- GQueue *input_queue;
-
- teco_curses_info_popup_t popup;
- gsize popup_prefix_len;
-
- /**
- * GError "thrown" by teco_interface_event_loop_iter().
- * Having this in a variable avoids problems with EMScripten.
- */
- GError *event_loop_error;
-} teco_interface;
-
static void teco_interface_init_color_safe(guint color, guint32 rgb);
static void teco_interface_restore_colors(void);
@@ -404,7 +421,6 @@ static void teco_interface_resize_all_windows(void);
static void teco_interface_set_window_title(const gchar *title);
static void teco_interface_draw_info(void);
-static void teco_interface_draw_cmdline(void);
void
teco_interface_init(void)
@@ -424,6 +440,15 @@ teco_interface_init(void)
*/
teco_string_init(&teco_interface.info_current, PACKAGE_NAME, strlen(PACKAGE_NAME));
+ teco_cmdline_init();
+ /*
+ * The default INDIC_STRIKE wouldn't be visible.
+ * Instead we use INDIC_STRAIGHTBOX, which will be rendered as underlined if
+ * the alpha is 0.
+ */
+ teco_cmdline_ssm(SCI_INDICSETSTYLE, INDICATOR_RUBBEDOUT, INDIC_STRAIGHTBOX);
+ teco_cmdline_ssm(SCI_INDICSETALPHA, INDICATOR_RUBBEDOUT, 0);
+
/*
* On all platforms except Curses/XTerm, it's
* safe to initialize the clipboards now.
@@ -561,7 +586,7 @@ teco_interface_init_color(guint color, guint32 rgb)
((color & 0x1) << 2) | ((color & 0x4) >> 2);
#endif
- if (teco_interface.cmdline_window) {
+ if (teco_interface.input_pad) {
/* interactive mode */
if (!can_change_color())
return;
@@ -717,6 +742,9 @@ teco_interface_init_interactive(GError **error)
teco_interface_init_screen();
+ teco_interface.pair_table = g_hash_table_new(g_direct_hash, g_direct_equal);
+ start_color();
+
/*
* On UNIX terminals, the escape key is usually
* delivered as the escape character even though function
@@ -768,8 +796,12 @@ teco_interface_init_interactive(GError **error)
leaveok(stdscr, TRUE);
teco_interface.info_window = newwin(1, 0, 0, 0);
- teco_interface.msg_window = newwin(1, 0, LINES - 2, 0);
- teco_interface.cmdline_window = newwin(0, 0, LINES - 1, 0);
+ teco_interface.msg_window = newwin(1, 0, LINES - teco_cmdline.height - 1, 0);
+
+ WINDOW *cmdline_win = teco_view_get_window(teco_cmdline.view);
+ wresize(cmdline_win, teco_cmdline.height, COLS);
+ mvwin(cmdline_win, LINES - teco_cmdline.height, 0);
+ teco_cmdline_resized(COLS);
teco_interface.input_pad = newpad(1, 1);
/*
@@ -861,39 +893,37 @@ teco_interface_restore_batch(void)
#endif
/*
- * cmdline_window determines whether we're in batch mode.
+ * input_pad determines whether we're in batch mode.
*/
- if (teco_interface.cmdline_window) {
- delwin(teco_interface.cmdline_window);
- teco_interface.cmdline_window = NULL;
+ if (teco_interface.input_pad) {
+ delwin(teco_interface.input_pad);
+ teco_interface.input_pad = NULL;
}
}
static void
teco_interface_resize_all_windows(void)
{
- int lines, cols; /* screen dimensions */
-
- getmaxyx(stdscr, lines, cols);
-
- wresize(teco_interface.info_window, 1, cols);
+ wresize(teco_interface.info_window, 1, COLS);
wresize(teco_view_get_window(teco_interface_current_view),
- lines - 3, cols);
- wresize(teco_interface.msg_window, 1, cols);
- mvwin(teco_interface.msg_window, lines - 2, 0);
- wresize(teco_interface.cmdline_window, 1, cols);
- mvwin(teco_interface.cmdline_window, lines - 1, 0);
+ LINES - 2 - teco_cmdline.height, COLS);
+ wresize(teco_interface.msg_window, 1, COLS);
+ mvwin(teco_interface.msg_window, LINES - 1 - teco_cmdline.height, 0);
+
+ WINDOW *cmdline_win = teco_view_get_window(teco_cmdline.view);
+ wresize(cmdline_win, teco_cmdline.height, COLS);
+ mvwin(cmdline_win, LINES - teco_cmdline.height, 0);
+ teco_cmdline_resized(COLS);
teco_interface_draw_info();
teco_interface_msg_clear(); /* FIXME: use saved message */
teco_interface_popup_clear();
- teco_interface_draw_cmdline();
}
void
teco_interface_msg_literal(teco_msg_t type, const gchar *str, gsize len)
{
- if (!teco_interface.cmdline_window) { /* batch mode */
+ if (!teco_interface.input_pad) { /* batch mode */
teco_interface_stdio_msg(type, str, len);
return;
}
@@ -936,7 +966,7 @@ teco_interface_msg_literal(teco_msg_t type, const gchar *str, gsize len)
void
teco_interface_msg_clear(void)
{
- if (!teco_interface.cmdline_window) /* batch mode */
+ if (!teco_interface.input_pad) /* batch mode */
return;
short fg = teco_rgb2curses(teco_interface_ssm(SCI_STYLEGETBACK, STYLE_DEFAULT, 0));
@@ -950,7 +980,7 @@ teco_interface_msg_clear(void)
teco_int_t
teco_interface_getch(gboolean widechar)
{
- if (!teco_interface.cmdline_window) /* batch mode */
+ if (!teco_interface.input_pad) /* batch mode */
return teco_interface_stdio_getch(widechar);
teco_interface_refresh(FALSE);
@@ -996,7 +1026,7 @@ teco_interface_show_view(teco_view_t *view)
{
teco_interface_current_view = view;
- if (!teco_interface.cmdline_window) /* batch mode */
+ if (!teco_interface.input_pad) /* batch mode */
return;
WINDOW *current_view_win = teco_view_get_window(teco_interface_current_view);
@@ -1005,9 +1035,7 @@ teco_interface_show_view(teco_view_t *view)
* screen size might have changed since
* this view's WINDOW was last active
*/
- int lines, cols; /* screen dimensions */
- getmaxyx(stdscr, lines, cols);
- wresize(current_view_win, lines - 3, cols);
+ wresize(current_view_win, LINES - 2 - teco_cmdline.height, COLS);
/* Set up window position: never changes */
mvwin(current_view_win, 1, 0);
}
@@ -1194,114 +1222,6 @@ teco_interface_info_update_buffer(const teco_buffer_t *buffer)
/* NOTE: drawn in teco_interface_event_loop_iter() */
}
-void
-teco_interface_cmdline_update(const teco_cmdline_t *cmdline)
-{
- /*
- * Especially important on PDCurses, which can crash
- * in newpad() when run with --fake-cmdline.
- */
- if (!teco_interface.cmdline_window) /* batch mode */
- return;
-
- /*
- * Replace entire pre-formatted command-line.
- * We don't know if it is similar to the last one,
- * so resizing makes no sense.
- * We approximate the size of the new formatted command-line,
- * wasting a few bytes for control characters and
- * multi-byte Unicode sequences.
- */
- if (teco_interface.cmdline_pad)
- delwin(teco_interface.cmdline_pad);
-
- int max_cols = 1;
- for (guint i = 0; i < cmdline->str.len; i++)
- max_cols += TECO_IS_CTL(cmdline->str.data[i]) ? 3 : 1;
- teco_interface.cmdline_pad = newpad(1, max_cols);
-
- short fg = teco_rgb2curses(teco_interface_ssm(SCI_STYLEGETFORE, STYLE_DEFAULT, 0));
- short bg = teco_rgb2curses(teco_interface_ssm(SCI_STYLEGETBACK, STYLE_DEFAULT, 0));
- wattrset(teco_interface.cmdline_pad, teco_color_attr(fg, bg));
-
- /* format effective command line */
- teco_interface.cmdline_len =
- teco_curses_format_str(teco_interface.cmdline_pad,
- cmdline->str.data, cmdline->effective_len, -1);
-
- /*
- * A_BOLD should result in either a bold font or a brighter
- * color both on 8 and 16 color terminals.
- * This is not quite color-scheme-agnostic, but works
- * with both the `terminal` and `solarized` themes.
- * This problem will be gone once we use a Scintilla view
- * as command line, since we can then define a style
- * for rubbed out parts of the command line which will
- * be user-configurable.
- * The attributes, supported by the terminal can theoretically
- * be queried with term_attrs().
- */
- wattron(teco_interface.cmdline_pad, A_UNDERLINE | A_BOLD);
-
- /*
- * Format rubbed-out command line.
- * NOTE: This formatting will never be truncated since we're
- * writing into the pad which is large enough.
- */
- teco_interface.cmdline_rubout_len =
- teco_curses_format_str(teco_interface.cmdline_pad, cmdline->str.data + cmdline->effective_len,
- cmdline->str.len - cmdline->effective_len, -1);
-
- /*
- * Highlight cursor after effective command line
- * FIXME: This should use SCI_GETCARETFORE().
- */
- attr_t attr = A_NORMAL;
- short pair = 0;
- if (teco_interface.cmdline_rubout_len) {
- wmove(teco_interface.cmdline_pad, 0, teco_interface.cmdline_len);
- wattr_get(teco_interface.cmdline_pad, &attr, &pair, NULL);
- wchgat(teco_interface.cmdline_pad, 1,
- (attr & (A_UNDERLINE | A_REVERSE)) ^ A_REVERSE, pair, NULL);
- } else {
- teco_interface.cmdline_len++;
- wattr_get(teco_interface.cmdline_pad, &attr, &pair, NULL);
- wattr_set(teco_interface.cmdline_pad, (attr & ~(A_UNDERLINE | A_BOLD)) ^ A_REVERSE, pair, NULL);
- waddch(teco_interface.cmdline_pad, ' ');
- }
-
- teco_interface_draw_cmdline();
-}
-
-static void
-teco_interface_draw_cmdline(void)
-{
- /* total width available for command line */
- guint total_width = getmaxx(teco_interface.cmdline_window) - 1;
-
- /* beginning of command line to show */
- guint disp_offset = teco_interface.cmdline_len -
- MIN(teco_interface.cmdline_len,
- total_width/2 + teco_interface.cmdline_len % MAX(total_width/2, 1));
- /*
- * length of command line to show
- *
- * NOTE: we do not use getmaxx(cmdline_pad) here since it may be
- * larger than the text the pad contains.
- */
- guint disp_len = MIN(total_width, teco_interface.cmdline_len +
- teco_interface.cmdline_rubout_len - disp_offset);
-
- short fg = teco_rgb2curses(teco_interface_ssm(SCI_STYLEGETFORE, STYLE_DEFAULT, 0));
- short bg = teco_rgb2curses(teco_interface_ssm(SCI_STYLEGETBACK, STYLE_DEFAULT, 0));
-
- wattrset(teco_interface.cmdline_window, teco_color_attr(fg, bg));
- mvwaddch(teco_interface.cmdline_window, 0, 0, '*' | A_BOLD);
- teco_curses_clrtobot(teco_interface.cmdline_window);
- copywin(teco_interface.cmdline_pad, teco_interface.cmdline_window,
- 0, disp_offset, 0, 1, 0, disp_len, FALSE);
-}
-
#if PDCURSES
/*
@@ -1771,7 +1691,7 @@ void
teco_interface_popup_add(teco_popup_entry_type_t type, const gchar *name, gsize name_len,
gboolean highlight)
{
- if (teco_interface.cmdline_window)
+ if (teco_interface.input_pad)
/* interactive mode */
teco_curses_info_popup_add(&teco_interface.popup, type, name, name_len, highlight);
}
@@ -1779,7 +1699,7 @@ teco_interface_popup_add(teco_popup_entry_type_t type, const gchar *name, gsize
void
teco_interface_popup_show(gsize prefix_len)
{
- if (!teco_interface.cmdline_window)
+ if (!teco_interface.input_pad)
/* batch mode */
return;
@@ -1793,7 +1713,7 @@ teco_interface_popup_show(gsize prefix_len)
void
teco_interface_popup_scroll(void)
{
- if (!teco_interface.cmdline_window)
+ if (!teco_interface.input_pad)
/* batch mode */
return;
@@ -1880,15 +1800,13 @@ teco_interface_is_interrupted(void)
void
teco_interface_refresh(gboolean force)
{
- if (!teco_interface.cmdline_window)
+ if (!teco_interface.input_pad)
/* batch mode */
return;
#ifdef NETBSD_CURSES
/* works around crashes in doupdate() */
- gint y, x;
- getmaxyx(stdscr, y, x);
- if (G_UNLIKELY(x <= 1 || y <= 1))
+ if (G_UNLIKELY(COLS <= 1 || LINES <= 1))
return;
#endif
@@ -1905,7 +1823,7 @@ teco_interface_refresh(gboolean force)
wnoutrefresh(teco_interface.info_window);
teco_view_noutrefresh(teco_interface_current_view);
wnoutrefresh(teco_interface.msg_window);
- wnoutrefresh(teco_interface.cmdline_window);
+ teco_view_noutrefresh(teco_cmdline.view);
teco_curses_info_popup_noutrefresh(&teco_interface.popup);
doupdate();
}
@@ -1952,7 +1870,7 @@ teco_interface_process_mevent(MEVENT *event, GError **error)
teco_interface_popup_clear();
teco_interface_msg_clear();
- teco_interface_cmdline_update(&teco_cmdline);
+ teco_cmdline_update();
}
return TRUE;
@@ -2136,6 +2054,7 @@ teco_interface_event_loop_iter(void)
const teco_view_t *last_view = teco_interface_current_view;
sptr_t last_vpos = teco_interface_ssm(SCI_GETFIRSTVISIBLELINE, 0, 0);
+ guint last_cmdline_height = teco_cmdline.height;
switch (key) {
case ERR:
@@ -2262,6 +2181,10 @@ teco_interface_event_loop_iter(void)
}
}
+ if (G_UNLIKELY(teco_cmdline.height != last_cmdline_height))
+ /* command line height was changed with h,5EJ */
+ teco_interface_resize_all_windows();
+
/*
* Scintilla has been patched to avoid any automatic scrolling since that
* has been benchmarked to be a very costly operation.
@@ -2286,8 +2209,6 @@ teco_interface_event_loop(GError **error)
if (!teco_interface_init_interactive(error))
return FALSE;
- static const teco_cmdline_t empty_cmdline; // FIXME
- teco_interface_cmdline_update(&empty_cmdline);
teco_interface_msg_clear();
teco_interface_ssm(SCI_SCROLLCARET, 0, 0);
/*
@@ -2342,10 +2263,6 @@ teco_interface_cleanup(void)
teco_string_clear(&teco_interface.info_current);
if (teco_interface.input_queue)
g_queue_free(teco_interface.input_queue);
- if (teco_interface.cmdline_window)
- delwin(teco_interface.cmdline_window);
- if (teco_interface.cmdline_pad)
- delwin(teco_interface.cmdline_pad);
if (teco_interface.msg_window)
delwin(teco_interface.msg_window);
if (teco_interface.input_pad)
@@ -2370,4 +2287,7 @@ teco_interface_cleanup(void)
close(teco_interface.stderr_orig);
if (teco_interface.stdout_orig >= 0)
close(teco_interface.stdout_orig);
+
+ if (teco_interface.pair_table)
+ g_hash_table_destroy(teco_interface.pair_table);
}
diff --git a/src/interface-gtk/interface.c b/src/interface-gtk/interface.c
index dcf3660..7aa9797 100644
--- a/src/interface-gtk/interface.c
+++ b/src/interface-gtk/interface.c
@@ -84,14 +84,6 @@ static gchar teco_interface_get_ansi_key(GdkEventKey *event);
/** printf() format for CSS RGB colors given as guint32 */
#define CSS_COLOR_FORMAT "#%06" G_GINT32_MODIFIER "X"
-/** Style used for the asterisk at the beginning of the command line */
-#define STYLE_ASTERISK 16
-
-/** Indicator number used for control characters in the command line */
-#define INDIC_CONTROLCHAR (INDIC_CONTAINER+0)
-/** Indicator number used for the rubbed out part of the command line */
-#define INDIC_RUBBEDOUT (INDIC_CONTAINER+1)
-
/** Convert Scintilla-style BGR color triple to RGB. */
static inline guint32
teco_bgr2rgb(guint32 bgr)
@@ -125,7 +117,6 @@ static struct {
GtkWidget *message_bar_widget;
GtkWidget *message_widget;
- teco_view_t *cmdline_view;
GtkIMContext *input_method;
GtkWidget *popup_widget;
@@ -227,7 +218,7 @@ teco_interface_init(void)
/*
* Overlay widget will allow overlaying the Scintilla view
* and message widgets with the info popup.
- * Therefore overlay_vbox (containing the view and popup)
+ * Therefore overlay_vbox (containing the view and message line)
* will be the main child of the overlay.
*/
GtkWidget *overlay_widget = gtk_overlay_new();
@@ -286,23 +277,9 @@ teco_interface_init(void)
gtk_container_add(GTK_CONTAINER(overlay_widget), overlay_vbox);
gtk_box_pack_start(GTK_BOX(vbox), overlay_widget, TRUE, TRUE, 0);
- teco_interface.cmdline_view = teco_view_new();
- teco_view_setup(teco_interface.cmdline_view);
- teco_view_ssm(teco_interface.cmdline_view, SCI_SETUNDOCOLLECTION, FALSE, 0);
- teco_view_ssm(teco_interface.cmdline_view, SCI_SETVSCROLLBAR, FALSE, 0);
- teco_view_ssm(teco_interface.cmdline_view, SCI_SETMARGINTYPEN, 1, SC_MARGIN_TEXT);
- teco_view_ssm(teco_interface.cmdline_view, SCI_MARGINSETSTYLE, 0, STYLE_ASTERISK);
- teco_view_ssm(teco_interface.cmdline_view, SCI_SETMARGINWIDTHN, 1,
- teco_view_ssm(teco_interface.cmdline_view, SCI_TEXTWIDTH, STYLE_ASTERISK, (sptr_t)"*"));
- teco_view_ssm(teco_interface.cmdline_view, SCI_MARGINSETTEXT, 0, (sptr_t)"*");
- /* only required as long as we avoid ordinary character representations */
- teco_view_ssm(teco_interface.cmdline_view, SCI_INDICSETSTYLE, INDIC_CONTROLCHAR, INDIC_ROUNDBOX);
- teco_view_ssm(teco_interface.cmdline_view, SCI_INDICSETALPHA, INDIC_CONTROLCHAR, 128);
- teco_view_ssm(teco_interface.cmdline_view, SCI_INDICSETSTYLE, INDIC_RUBBEDOUT, INDIC_STRIKE);
- /* we will forward key events, so the view should only react to text insertion */
- teco_view_ssm(teco_interface.cmdline_view, SCI_CLEARALLCMDKEYS, 0, 0);
-
- GtkWidget *cmdline_widget = GTK_WIDGET(teco_interface.cmdline_view);
+ teco_cmdline_init();
+
+ GtkWidget *cmdline_widget = GTK_WIDGET(teco_cmdline.view);
gtk_widget_set_name(cmdline_widget, "sciteco-cmdline");
g_signal_connect(cmdline_widget, "size-allocate",
G_CALLBACK(teco_interface_cmdline_size_allocate_cb), NULL);
@@ -337,10 +314,6 @@ teco_interface_init(void)
*/
gtk_widget_set_can_focus(teco_interface.message_widget, FALSE);
gtk_widget_set_can_focus(teco_interface.info_name_widget, FALSE);
-
- teco_cmdline_t empty_cmdline;
- memset(&empty_cmdline, 0, sizeof(empty_cmdline));
- teco_interface_cmdline_update(&empty_cmdline);
}
static void
@@ -604,81 +577,6 @@ teco_interface_info_update_buffer(const teco_buffer_t *buffer)
: TECO_INFO_TYPE_BUFFER;
}
-/**
- * Insert a single character into the command line.
- *
- * @fixme
- * Control characters should be inserted verbatim since the Scintilla
- * representations of them should be preferred.
- * However, Scintilla would break the line on every CR/LF and there is
- * currently no way to prevent this.
- * Scintilla needs to be patched.
- *
- * @see teco_view_set_representations()
- * @see teco_curses_format_str()
- */
-static void
-teco_interface_cmdline_insert_c(gchar chr)
-{
- gchar buffer[3+1] = "";
-
- /*
- * NOTE: This mapping is similar to teco_view_set_representations()
- */
- switch (chr) {
- case '\e': strcpy(buffer, "$"); break;
- case '\r': strcpy(buffer, "CR"); break;
- case '\n': strcpy(buffer, "LF"); break;
- case '\t': strcpy(buffer, "TAB"); break;
- default:
- if (TECO_IS_CTL(chr)) {
- buffer[0] = '^';
- buffer[1] = TECO_CTL_ECHO(chr);
- buffer[2] = '\0';
- }
- }
-
- if (*buffer) {
- gsize len = strlen(buffer);
- teco_view_ssm(teco_interface.cmdline_view, SCI_APPENDTEXT, len, (sptr_t)buffer);
- teco_view_ssm(teco_interface.cmdline_view, SCI_SETINDICATORCURRENT, INDIC_CONTROLCHAR, 0);
- teco_view_ssm(teco_interface.cmdline_view, SCI_INDICATORFILLRANGE,
- teco_view_ssm(teco_interface.cmdline_view, SCI_GETLENGTH, 0, 0) - len, len);
- } else {
- teco_view_ssm(teco_interface.cmdline_view, SCI_APPENDTEXT, 1, (sptr_t)&chr);
- }
-}
-
-void
-teco_interface_cmdline_update(const teco_cmdline_t *cmdline)
-{
- /*
- * We don't know if the new command line is similar to
- * the old one, so we can just as well rebuild it.
- *
- * NOTE: teco_view_ssm() already locks the GDK lock.
- */
- teco_view_ssm(teco_interface.cmdline_view, SCI_CLEARALL, 0, 0);
-
- /* format effective command line */
- for (guint i = 0; i < cmdline->effective_len; i++)
- teco_interface_cmdline_insert_c(cmdline->str.data[i]);
-
- /* cursor should be after effective command line */
- guint pos = teco_view_ssm(teco_interface.cmdline_view, SCI_GETLENGTH, 0, 0);
- teco_view_ssm(teco_interface.cmdline_view, SCI_GOTOPOS, pos, 0);
-
- /* format rubbed out command line */
- for (guint i = cmdline->effective_len; i < cmdline->str.len; i++)
- teco_interface_cmdline_insert_c(cmdline->str.data[i]);
-
- teco_view_ssm(teco_interface.cmdline_view, SCI_SETINDICATORCURRENT, INDIC_RUBBEDOUT, 0);
- teco_view_ssm(teco_interface.cmdline_view, SCI_INDICATORFILLRANGE, pos,
- teco_view_ssm(teco_interface.cmdline_view, SCI_GETLENGTH, 0, 0) - pos);
-
- teco_view_ssm(teco_interface.cmdline_view, SCI_SCROLLCARET, 0, 0);
-}
-
static GdkAtom
teco_interface_get_selection_by_name(const gchar *name)
{
@@ -885,40 +783,6 @@ teco_interface_set_css_variables(teco_view_t *view)
guint32 calltip_bg_color = teco_view_ssm(view, SCI_STYLEGETBACK, STYLE_CALLTIP, 0);
/*
- * FIXME: Font and colors of Scintilla views cannot be set via CSS.
- * But some day, there will be a way to send messages to the commandline view
- * from SciTECO code via ES.
- * Configuration will then be in the hands of color schemes.
- *
- * NOTE: We don't actually know apriori how large the font_size buffer should be,
- * but luckily SCI_STYLEGETFONT with a sptr==0 will return only the size.
- * This is undocumented in the Scintilla docs.
- */
- g_autofree gchar *font_name = g_malloc(teco_view_ssm(view, SCI_STYLEGETFONT, STYLE_DEFAULT, 0) + 1);
- teco_view_ssm(view, SCI_STYLEGETFONT, STYLE_DEFAULT, (sptr_t)font_name);
-
- teco_view_ssm(teco_interface.cmdline_view, SCI_STYLESETFORE, STYLE_DEFAULT, default_fg_color);
- teco_view_ssm(teco_interface.cmdline_view, SCI_STYLESETBACK, STYLE_DEFAULT, default_bg_color);
- teco_view_ssm(teco_interface.cmdline_view, SCI_STYLESETFONT, STYLE_DEFAULT, (sptr_t)font_name);
- teco_view_ssm(teco_interface.cmdline_view, SCI_STYLESETSIZE, STYLE_DEFAULT,
- teco_view_ssm(view, SCI_STYLEGETSIZE, STYLE_DEFAULT, 0));
- teco_view_ssm(teco_interface.cmdline_view, SCI_STYLECLEARALL, 0, 0);
- teco_view_ssm(teco_interface.cmdline_view, SCI_STYLESETFORE, STYLE_CALLTIP, calltip_fg_color);
- teco_view_ssm(teco_interface.cmdline_view, SCI_STYLESETBACK, STYLE_CALLTIP, calltip_bg_color);
- teco_view_ssm(teco_interface.cmdline_view, SCI_SETCARETFORE,
- teco_view_ssm(view, SCI_GETCARETFORE, 0, 0), 0);
- /* used for the asterisk at the beginning of the command line */
- teco_view_ssm(teco_interface.cmdline_view, SCI_STYLESETBOLD, STYLE_ASTERISK, TRUE);
- /* used for character representations */
- teco_view_ssm(teco_interface.cmdline_view, SCI_INDICSETFORE, INDIC_CONTROLCHAR, default_fg_color);
- /* used for the rubbed out command line */
- teco_view_ssm(teco_interface.cmdline_view, SCI_INDICSETFORE, INDIC_RUBBEDOUT, default_fg_color);
- /* this somehow gets reset */
- teco_view_ssm(teco_interface.cmdline_view, SCI_MARGINSETTEXT, 0, (sptr_t)"*");
-
- guint text_height = teco_view_ssm(teco_interface.cmdline_view, SCI_TEXTHEIGHT, 0, 0);
-
- /*
* Generates a CSS that sets some predefined color variables.
* This effectively "exports" Scintilla styles into the CSS
* world.
@@ -944,12 +808,13 @@ teco_interface_set_css_variables(teco_view_t *view)
gtk_css_provider_load_from_data(teco_interface.css_var_provider, css, -1, NULL);
/*
- * The font and size of the commandline view might have changed,
+ * The font and size and height of the command-line view might have changed,
* so we resize it.
* This cannot be done via CSS or Scintilla messages.
- * Currently, it is always exactly one line high in order to mimic the Curses UI.
*/
- gtk_widget_set_size_request(GTK_WIDGET(teco_interface.cmdline_view), -1, text_height);
+ g_assert(teco_cmdline.height > 0);
+ gtk_widget_set_size_request(GTK_WIDGET(teco_cmdline.view), -1,
+ teco_cmdline.height*teco_cmdline_ssm(SCI_TEXTHEIGHT, 0, 0));
}
static void
@@ -1420,17 +1285,12 @@ teco_interface_event_box_realized_cb(GtkWidget *widget, gpointer user_data)
teco_interface_set_cursor(widget, "text");
}
-/**
- * Called when the commandline widget is resized.
- * This should ensure that the caret jumps to the middle of the command line,
- * imitating the behaviour of the current Curses command line.
- */
+/** Called when the commandline widget is resized */
static void
teco_interface_cmdline_size_allocate_cb(GtkWidget *widget,
GdkRectangle *allocation, gpointer user_data)
{
- teco_view_ssm(teco_interface.cmdline_view, SCI_SETXCARETPOLICY,
- CARET_SLOP | CARET_EVEN, allocation->width/2);
+ teco_cmdline_resized(allocation->width);
}
static gboolean
@@ -1606,7 +1466,7 @@ teco_interface_popup_clicked_cb(GtkWidget *popup, gchar *str, gulong len, gpoint
!machine->current->insert_completion_cb(machine, &insert, NULL))
return;
teco_interface_popup_clear();
- teco_interface_cmdline_update(&teco_cmdline);
+ teco_cmdline_update();
teco_interface_update(teco_interface_current_view != last_view);
}
diff --git a/src/interface.h b/src/interface.h
index 9531d37..8ab66ef 100644
--- a/src/interface.h
+++ b/src/interface.h
@@ -118,9 +118,6 @@ void undo__teco_interface_info_update_qreg(const teco_qreg_t *);
void undo__teco_interface_info_update_buffer(const teco_buffer_t *);
/** @pure */
-void teco_interface_cmdline_update(const teco_cmdline_t *cmdline);
-
-/** @pure */
gboolean teco_interface_set_clipboard(const gchar *name, const gchar *str, gsize str_len,
GError **error);
void teco_interface_undo_set_clipboard(const gchar *name, gchar *str, gsize len);
diff --git a/src/lexer.c b/src/lexer.c
index 2f43b76..787fe89 100644
--- a/src/lexer.c
+++ b/src/lexer.c
@@ -26,6 +26,7 @@
#include "sciteco.h"
#include "view.h"
#include "parser.h"
+#include "core-commands.h"
#include "lexer.h"
static teco_style_t
@@ -37,16 +38,21 @@ teco_lexer_getstyle(teco_view_t *view, teco_machine_main_t *machine,
/*
* FIXME: At least this special workaround for numbers might be
* unnecessary once we get a special parser state for parsing numbers.
- *
- * FIXME: What about ^* and ^/?
- * They are currently highlighted as commands.
*/
if (machine->parent.current->keymacro_mask & TECO_KEYMACRO_MASK_START &&
chr <= 0xFF) {
if (g_ascii_isdigit(chr))
style = SCE_SCITECO_NUMBER;
- else if (strchr("+-*/#&", chr))
+ else if (strchr(",+-*/#&()", chr))
style = SCE_SCITECO_OPERATOR;
+ } else if (machine->parent.current == &teco_state_control) {
+ /*
+ * Two-character operators must always begin with caret
+ * They get a separate style, so we can extend it back to
+ * the caret in teco_lexter_step.
+ */
+ if (strchr("*/#", chr))
+ style = SCE_SCITECO_OPERATOR2;
}
/*
@@ -174,8 +180,9 @@ teco_lexer_step(teco_view_t *view, teco_machine_main_t *machine,
/*
* True comments begin with `!*` or `!!`, but only the second character gets
* the correct style by default, so we extend it backwards.
+ * The same is true for two-letter operators.
*/
- if (style == SCE_SCITECO_COMMENT)
+ if (style == SCE_SCITECO_COMMENT || style == SCE_SCITECO_OPERATOR2)
old_pc--;
teco_view_ssm(view, SCI_STARTSTYLING, start+old_pc, 0);
@@ -212,22 +219,35 @@ teco_lexer_style(teco_view_t *view, gsize end)
gsize start = teco_view_ssm(view, SCI_GETENDSTYLED, 0, 0);
guint start_line = teco_view_ssm(view, SCI_LINEFROMPOSITION, start, 0);
- gint start_col = 0;
/*
* The line state stores the laster character (column) in bytes,
* that starts from a fresh parser state.
* It's -1 if the line does not have a clean parser state.
- * Therefore we search for the first line before `start` that has a
- * known clean parser state.
+ * If the cached position on start_line does not fit our needs,
+ * we backtrack and search in previous lines
+ * for a known clean parser state.
+ *
+ * NOTE: It's crucial to consider the line state of the first possible
+ * line since we might be styling for a single-line command line view.
+ *
+ * FIXME: During rubout of regular commands we will frequently have the
+ * situation that the cached line state points after the last styled position
+ * forcing us to restyle the entire command line macro.
+ * If this turns out to be problematic, we might detect that
+ * view == teco_cmdline.view and inspect teco_cmdline.machine.
*/
- if (start_line > 0) {
+ gint start_col = teco_view_ssm(view, SCI_GETLINESTATE, start_line, 0);
+ if (start_col > start - teco_view_ssm(view, SCI_POSITIONFROMLINE, start_line, 0))
+ /* we are asked to style __before__ the last known start state */
+ start_col = -1;
+ if (start_col < 0 && start_line > 0) {
do
start_line--;
while ((start_col = teco_view_ssm(view, SCI_GETLINESTATE, start_line, 0)) < 0 &&
start_line > 0);
- start_col = MAX(start_col, 0);
}
+ start_col = MAX(start_col, 0);
start = teco_view_ssm(view, SCI_POSITIONFROMLINE, start_line, 0) + start_col;
g_assert(end > start);
diff --git a/src/lexer.h b/src/lexer.h
index 2b011be..e91cdd1 100644
--- a/src/lexer.h
+++ b/src/lexer.h
@@ -25,12 +25,14 @@ typedef enum {
SCE_SCITECO_DEFAULT = 0,
SCE_SCITECO_COMMAND = 1,
SCE_SCITECO_OPERATOR = 2,
- SCE_SCITECO_QREG = 3,
- SCE_SCITECO_STRING = 4,
- SCE_SCITECO_NUMBER = 5,
- SCE_SCITECO_LABEL = 6,
- SCE_SCITECO_COMMENT = 7,
- SCE_SCITECO_INVALID = 8
+ /** two-character operators */
+ SCE_SCITECO_OPERATOR2 = 3,
+ SCE_SCITECO_QREG = 4,
+ SCE_SCITECO_STRING = 5,
+ SCE_SCITECO_NUMBER = 6,
+ SCE_SCITECO_LABEL = 7,
+ SCE_SCITECO_COMMENT = 8,
+ SCE_SCITECO_INVALID = 9
} teco_style_t;
void teco_lexer_style(teco_view_t *view, gsize end);
diff --git a/src/main.c b/src/main.c
index b615e78..620455a 100644
--- a/src/main.c
+++ b/src/main.c
@@ -640,6 +640,7 @@ cleanup:
teco_qreg_table_clear(&teco_qreg_table_globals);
teco_qreg_stack_clear();
teco_view_free(teco_qreg_view);
+ teco_cmdline_cleanup();
#endif
teco_interface_cleanup();
diff --git a/src/sciteco.h b/src/sciteco.h
index cc43368..388d6c4 100644
--- a/src/sciteco.h
+++ b/src/sciteco.h
@@ -96,7 +96,8 @@ enum {
TECO_ED_SHELLEMU = (1 << 7),
TECO_ED_OSC52 = (1 << 8),
TECO_ED_ICONS = (1 << 9),
- TECO_ED_CLIP_PRIMARY = (1 << 10)
+ TECO_ED_CLIP_PRIMARY = (1 << 10),
+ TECO_ED_MINIBUF_SSM = (1 << 11)
};
/* in main.c */
diff --git a/src/stdio-commands.c b/src/stdio-commands.c
index 3a1b320..565f442 100644
--- a/src/stdio-commands.c
+++ b/src/stdio-commands.c
@@ -38,7 +38,7 @@ static inline gboolean
teco_cmdline_is_executing(teco_machine_main_t *ctx)
{
return ctx == &teco_cmdline.machine &&
- ctx->macro_pc == teco_cmdline.effective_len;
+ ctx->macro_pc == teco_cmdline_ssm(SCI_GETCURRENTPOS, 0, 0);
}
static gboolean is_executing = FALSE;
diff --git a/src/symbols.c b/src/symbols.c
index b5600e8..9cf1c35 100644
--- a/src/symbols.c
+++ b/src/symbols.c
@@ -38,6 +38,7 @@
#include "undo.h"
#include "expressions.h"
#include "interface.h"
+#include "cmdline.h"
#include "symbols.h"
teco_symbol_list_t teco_symbol_list_scintilla = {NULL, 0};
@@ -166,6 +167,13 @@ teco_symbol_list_auto_complete(teco_symbol_list_t *ctx, const gchar *symbol, tec
* Command states
*/
+static inline sptr_t
+teco_scintilla_ssm(unsigned int iMessage, uptr_t wParam, sptr_t lParam)
+{
+ return teco_view_ssm(teco_ed & TECO_ED_MINIBUF_SSM ? teco_cmdline.view : teco_interface_current_view,
+ iMessage, wParam, lParam);
+}
+
/*
* FIXME: This state could be static.
*/
@@ -316,6 +324,13 @@ gboolean teco_state_scintilla_symbols_insert_completion(teco_machine_main_t *ctx
* second string argument of \fBES\fP, i.e. it allows you
* to look up style ids by name.
*
+ * By default Scintilla messages are sent to the current buffer's
+ * view or the Q-register view \(em there is only one view for
+ * all Q-registers.
+ * If bit 11 is set in the \fBED\fP flags, the messages will be
+ * sent to the command-line view instead, which allows you to
+ * set up \*(ST syntax highlighting and other styles.
+ *
* .BR Warning :
* Almost all Scintilla messages may be dispatched using
* this command.
@@ -442,10 +457,10 @@ teco_state_scintilla_lparam_done(teco_machine_main_t *ctx, const teco_string_t *
/*
* FIXME: Should we cache the name to style id?
*/
- guint count = teco_interface_ssm(SCI_GETNAMEDSTYLES, 0, 0);
+ guint count = teco_scintilla_ssm(SCI_GETNAMEDSTYLES, 0, 0);
for (guint id = 0; id < count; id++) {
gchar style[128] = "";
- teco_interface_ssm(SCI_NAMEOFSTYLE, id, (sptr_t)style);
+ teco_scintilla_ssm(SCI_NAMEOFSTYLE, id, (sptr_t)style);
if (!teco_string_cmp(str, style, strlen(style))) {
teco_expressions_push(id);
return &teco_state_start;
@@ -491,7 +506,7 @@ teco_state_scintilla_lparam_done(teco_machine_main_t *ctx, const teco_string_t *
lParam = v;
}
- teco_expressions_push(teco_interface_ssm(ctx->scintilla.iMessage,
+ teco_expressions_push(teco_scintilla_ssm(ctx->scintilla.iMessage,
ctx->scintilla.wParam, lParam));
return &teco_state_start;
diff --git a/src/view.c b/src/view.c
index 790f832..53199bd 100644
--- a/src/view.c
+++ b/src/view.c
@@ -145,7 +145,14 @@ teco_view_setup(teco_view_t *ctx)
TECO_DEFINE_UNDO_CALL(teco_view_ssm, teco_view_t *, unsigned int, uptr_t, sptr_t);
-/** @memberof teco_view_t */
+/**
+ * Configure typical TECO representations for control characters.
+ *
+ * You may have to SCI_SETVIEWEOL(TRUE) to see the CR and LF characters.
+ * In order to see the TAB character use SCI_SETTABDRAWMODE(SCTD_CONTROLCHAR).
+ *
+ * @memberof teco_view_t
+ */
void
teco_view_set_representations(teco_view_t *ctx)
{
@@ -543,16 +550,16 @@ teco_view_save_to_file(teco_view_t *ctx, const gchar *filename, GError **error)
file_stat.st_gid = -1;
#endif
teco_file_attributes_t attributes = TECO_FILE_INVALID_ATTRIBUTES;
+ gboolean undo_remove_file = FALSE;
if (teco_undo_enabled) {
- if (g_file_test(filename, G_FILE_TEST_IS_REGULAR)) {
+ undo_remove_file = !g_file_test(filename, G_FILE_TEST_IS_REGULAR);
+ if (!undo_remove_file) {
#ifdef G_OS_UNIX
g_stat(filename, &file_stat);
#endif
attributes = teco_file_get_attributes(filename);
teco_make_savepoint(filename);
- } else {
- teco_undo_remove_file_push(filename);
}
}
@@ -561,6 +568,18 @@ teco_view_save_to_file(teco_view_t *ctx, const gchar *filename, GError **error)
if (!channel)
return FALSE;
+ if (undo_remove_file) {
+ /*
+ * The file is new, so has to be removed on undo.
+ * If `filename` is a symlink, it's crucial to resolve it now,
+ * since early canonicalization may have failed (for non-existent
+ * path segments).
+ * Now, `filename` is guaranteed to exist.
+ */
+ g_autofree gchar *filename_canon = teco_file_get_absolute_path(filename);
+ teco_undo_remove_file_push(filename_canon);
+ }
+
/*
* teco_view_save_to_channel() expects a buffered and blocking channel
*/
@@ -569,6 +588,7 @@ teco_view_save_to_file(teco_view_t *ctx, const gchar *filename, GError **error)
if (!teco_view_save_to_channel(ctx, channel, error)) {
g_prefix_error(error, "Error writing file \"%s\": ", filename);
+ /* file might also be removed (in interactive mode) */
return FALSE;
}