diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/core-commands.c | 2 | ||||
-rw-r--r-- | src/interface-curses/interface.c | 25 | ||||
-rw-r--r-- | src/interface-gtk/interface.c | 24 | ||||
-rw-r--r-- | src/interface.c | 49 | ||||
-rw-r--r-- | src/interface.h | 15 | ||||
-rw-r--r-- | src/parser.c | 4 | ||||
-rw-r--r-- | src/qreg.h | 2 | ||||
-rw-r--r-- | src/stdio-commands.c | 52 | ||||
-rw-r--r-- | src/stdio-commands.h | 1 |
9 files changed, 108 insertions, 66 deletions
diff --git a/src/core-commands.c b/src/core-commands.c index 04cdf94..3aaf816 100644 --- a/src/core-commands.c +++ b/src/core-commands.c @@ -1546,6 +1546,8 @@ teco_state_control_input(teco_machine_main_t *ctx, gunichar chr, GError **error) ['^'] = {&teco_state_ascii}, ['['] = {&teco_state_escape}, ['C'] = {&teco_state_ctlc}, + ['A'] = {&teco_state_print_string, + .modifier_at = TRUE}, /* * Additional numeric operations diff --git a/src/interface-curses/interface.c b/src/interface-curses/interface.c index 1d7f4ed..e461b3c 100644 --- a/src/interface-curses/interface.c +++ b/src/interface-curses/interface.c @@ -22,7 +22,6 @@ #include <string.h> #include <stdio.h> #include <stdlib.h> -#include <stdarg.h> #include <unistd.h> #include <errno.h> @@ -860,10 +859,10 @@ teco_interface_resize_all_windows(void) } void -teco_interface_vmsg(teco_msg_t type, const gchar *fmt, va_list ap) +teco_interface_msg_literal(teco_msg_t type, const gchar *str, gsize len) { if (!teco_interface.cmdline_window) { /* batch mode */ - teco_interface_stdio_vmsg(type, fmt, ap); + teco_interface_stdio_msg(type, str, len); return; } @@ -872,10 +871,7 @@ teco_interface_vmsg(teco_msg_t type, const gchar *fmt, va_list ap) * even in interactive mode. */ #if defined(PDCURSES_GUI) || defined(CURSES_TTY) || defined(NCURSES_WIN32) - va_list aq; - va_copy(aq, ap); - teco_interface_stdio_vmsg(type, fmt, aq); - va_end(aq); + teco_interface_stdio_msg(type, str, len); #endif short fg, bg; @@ -899,14 +895,10 @@ teco_interface_vmsg(teco_msg_t type, const gchar *fmt, va_list ap) break; } - /* - * NOTE: This is safe since we don't have to cancel out any A_REVERSE, - * that could be set in the background attributes. - */ wmove(teco_interface.msg_window, 0, 0); - wbkgdset(teco_interface.msg_window, teco_color_attr(fg, bg)); - vw_printw(teco_interface.msg_window, fmt, ap); - wclrtoeol(teco_interface.msg_window); + wattrset(teco_interface.msg_window, teco_color_attr(fg, bg)); + teco_curses_format_str(teco_interface.msg_window, str, len, -1); + teco_curses_clrtobot(teco_interface.msg_window); } void @@ -918,8 +910,9 @@ teco_interface_msg_clear(void) short fg = teco_rgb2curses(teco_interface_ssm(SCI_STYLEGETBACK, STYLE_DEFAULT, 0)); short bg = teco_rgb2curses(teco_interface_ssm(SCI_STYLEGETFORE, STYLE_DEFAULT, 0)); - wbkgdset(teco_interface.msg_window, teco_color_attr(fg, bg)); - werase(teco_interface.msg_window); + wmove(teco_interface.msg_window, 0, 0); + wattrset(teco_interface.msg_window, teco_color_attr(fg, bg)); + teco_curses_clrtobot(teco_interface.msg_window); } void diff --git a/src/interface-gtk/interface.c b/src/interface-gtk/interface.c index 973225e..544ff22 100644 --- a/src/interface-gtk/interface.c +++ b/src/interface-gtk/interface.c @@ -19,7 +19,6 @@ #include "config.h" #endif -#include <stdarg.h> #include <string.h> #include <signal.h> @@ -193,7 +192,7 @@ teco_interface_init(void) */ teco_interface.info_bar_widget = gtk_header_bar_new(); gtk_widget_set_name(teco_interface.info_bar_widget, "sciteco-info-bar"); - teco_interface.info_name_widget = teco_gtk_label_new(NULL, 0); + teco_interface.info_name_widget = teco_gtk_label_new("", 0); gtk_widget_set_valign(teco_interface.info_name_widget, GTK_ALIGN_CENTER); /* eases writing portable fallback.css that avoids CSS element names */ gtk_style_context_add_class(gtk_widget_get_style_context(teco_interface.info_name_widget), @@ -279,8 +278,7 @@ teco_interface_init(void) gtk_widget_set_name(teco_interface.message_bar_widget, "sciteco-message-bar"); GtkWidget *message_bar_content = gtk_info_bar_get_content_area(GTK_INFO_BAR(teco_interface.message_bar_widget)); - /* NOTE: Messages are always pre-canonicalized */ - teco_interface.message_widget = gtk_label_new(NULL); + teco_interface.message_widget = teco_gtk_label_new(NULL, 0); /* eases writing portable fallback.css that avoids CSS element names */ gtk_style_context_add_class(gtk_widget_get_style_context(teco_interface.message_widget), "label"); @@ -391,7 +389,7 @@ teco_interface_get_options(void) void teco_interface_init_color(guint color, guint32 rgb) {} void -teco_interface_vmsg(teco_msg_t type, const gchar *fmt, va_list ap) +teco_interface_msg_literal(teco_msg_t type, const gchar *str, gsize len) { /* * The message types are chosen such that there is a CSS class @@ -407,21 +405,11 @@ teco_interface_vmsg(teco_msg_t type, const gchar *fmt, va_list ap) g_assert(type < G_N_ELEMENTS(type2gtk)); - gchar buf[256]; - - /* - * stdio_vmsg() leaves `ap` undefined and we are expected - * to do the same and behave like vprintf(). - */ - va_list aq; - va_copy(aq, ap); - teco_interface_stdio_vmsg(type, fmt, ap); - g_vsnprintf(buf, sizeof(buf), fmt, aq); - va_end(aq); + teco_interface_stdio_msg(type, str, len); gtk_info_bar_set_message_type(GTK_INFO_BAR(teco_interface.message_bar_widget), type2gtk[type]); - gtk_label_set_text(GTK_LABEL(teco_interface.message_widget), buf); + teco_gtk_label_set_text(TECO_GTK_LABEL(teco_interface.message_widget), str, len); if (type == TECO_MSG_ERROR) gtk_widget_error_bell(teco_interface.window); @@ -432,7 +420,7 @@ teco_interface_msg_clear(void) { gtk_info_bar_set_message_type(GTK_INFO_BAR(teco_interface.message_bar_widget), GTK_MESSAGE_QUESTION); - gtk_label_set_text(GTK_LABEL(teco_interface.message_widget), ""); + teco_gtk_label_set_text(TECO_GTK_LABEL(teco_interface.message_widget), "", 0); } void diff --git a/src/interface.c b/src/interface.c index 9ec1bed..cf8f1ca 100644 --- a/src/interface.c +++ b/src/interface.c @@ -81,35 +81,52 @@ teco_interface_undo_set_clipboard(const gchar *name, gchar *str, gsize len) } } +void +teco_interface_msg(teco_msg_t type, const gchar *fmt, ...) +{ + gchar buf[512]; + va_list ap; + + va_start(ap, fmt); + /* + * If the buffer could ever be exceeded, perhaps + * use g_strdup_vprintf() instead. + */ + gint len = g_vsnprintf(buf, sizeof(buf), fmt, ap); + g_assert(0 <= len && len < sizeof(buf)); + va_end(ap); + + teco_interface_msg_literal(type, buf, len); +} + /** - * Print a message to the appropriate stdio streams. + * Print a raw message to the appropriate stdio streams. * - * This method has similar semantics to `vprintf`, i.e. - * it leaves `ap` undefined. Therefore to pass the format - * string and arguments to another `vprintf`-like function, - * you have to copy the arguments via `va_copy`. + * This deliberately does not echo (i.e. escape non-printable characters) + * the string. Either they are supposed to be written verbatim + * (TECO_MSG_USER) or are already echoed. + * Everything higher than TECO_MSG_USER is also terminated by LF. + * + * @fixme TECO_MSG_USER could always be flushed. + * This however makes the message disappear, though. + * We might also want to put flushing under control of the language instead. */ void -teco_interface_stdio_vmsg(teco_msg_t type, const gchar *fmt, va_list ap) +teco_interface_stdio_msg(teco_msg_t type, const gchar *str, gsize len) { - FILE *stream = stdout; - switch (type) { case TECO_MSG_USER: + fwrite(str, 1, len, stdout); + //fflush(stdout); break; case TECO_MSG_INFO: - fputs("Info: ", stream); + g_fprintf(stdout, "Info: %.*s\n", (gint)len, str); break; case TECO_MSG_WARNING: - stream = stderr; - fputs("Warning: ", stream); + g_fprintf(stderr, "Warning: %.*s\n", (gint)len, str); break; case TECO_MSG_ERROR: - stream = stderr; - fputs("Error: ", stream); + g_fprintf(stderr, "Error: %.*s\n", (gint)len, str); break; } - - g_vfprintf(stream, fmt, ap); - fputc('\n', stream); } diff --git a/src/interface.h b/src/interface.h index b1ad2b8..0e03a98 100644 --- a/src/interface.h +++ b/src/interface.h @@ -16,7 +16,6 @@ */ #pragma once -#include <stdarg.h> #include <signal.h> #include <glib.h> @@ -62,17 +61,9 @@ typedef enum { } teco_msg_t; /** @pure */ -void teco_interface_vmsg(teco_msg_t type, const gchar *fmt, va_list ap); +void teco_interface_msg_literal(teco_msg_t type, const gchar *str, gsize len); -static inline void G_GNUC_PRINTF(2, 3) -teco_interface_msg(teco_msg_t type, const gchar *fmt, ...) -{ - va_list ap; - - va_start(ap, fmt); - teco_interface_vmsg(type, fmt, ap); - va_end(ap); -} +void teco_interface_msg(teco_msg_t type, const gchar *fmt, ...) G_GNUC_PRINTF(2, 3); /** @pure */ void teco_interface_msg_clear(void); @@ -180,7 +171,7 @@ gboolean teco_interface_event_loop(GError **error); * Interfacing to the external SciTECO world */ /** @protected */ -void teco_interface_stdio_vmsg(teco_msg_t type, const gchar *fmt, va_list ap); +void teco_interface_stdio_msg(teco_msg_t type, const gchar *str, gsize len); /** @pure */ void teco_interface_cleanup(void); diff --git a/src/parser.c b/src/parser.c index 33d2e7f..347c1a6 100644 --- a/src/parser.c +++ b/src/parser.c @@ -1047,8 +1047,12 @@ teco_state_expectstring_input(teco_machine_main_t *ctx, gunichar chr, GError **e * to the ANSI range (teco_ascii_toupper())? * This would be faster than case folding each and every character * of a string argument to check against the escape char. + * + * FIXME: This has undesired effects if you try to use one of + * of these characters with multiple string arguments. */ switch (ctx->expectstring.machine.escape_char) { + case TECO_CTL_KEY('A'): case '\e': case '{': if (ctx->parent.must_undo) @@ -45,6 +45,8 @@ extern teco_view_t *teco_qreg_view; * teco_qreg_set_integer_t set_integer; * ... * teco_qreg_set_integer(qreg, 23, error); + * + * But this probably won't work. Perhaps use the X-macro pattern. */ typedef const struct { gboolean (*set_integer)(teco_qreg_t *qreg, teco_int_t value, GError **error); diff --git a/src/stdio-commands.c b/src/stdio-commands.c index 263425c..09f5e2a 100644 --- a/src/stdio-commands.c +++ b/src/stdio-commands.c @@ -23,6 +23,7 @@ #include "sciteco.h" #include "parser.h" #include "error.h" +#include "undo.h" #include "expressions.h" #include "interface.h" #include "cmdline.h" @@ -49,16 +50,16 @@ teco_print(guint radix, GError **error) * also to allow output without trailing LF. * Perhaps repurpose teco_expressions_format(). */ - const gchar *fmt = "%" TECO_INT_MODIFIER "d"; + const gchar *fmt = "%" TECO_INT_MODIFIER "d\n"; switch (radix) { - case 8: fmt = "%" TECO_INT_MODIFIER "o"; break; - case 16: fmt = "%" TECO_INT_MODIFIER "X"; break; + case 8: fmt = "%" TECO_INT_MODIFIER "o\n"; break; + case 16: fmt = "%" TECO_INT_MODIFIER "X\n"; break; } teco_interface_msg(TECO_MSG_USER, fmt, teco_expressions_peek_num(0)); return TRUE; } -/*$ "=" "==" "===" print +/*$ "=" "==" "===" "print number" * <n>= -- Print integer as message * <n>== * <n>=== @@ -195,3 +196,46 @@ TECO_DEFINE_STATE_START(teco_state_print_octal, .end_of_macro_cb = (teco_state_end_of_macro_cb_t) teco_state_print_octal_end_of_macro ); + +static gboolean +teco_state_print_string_initial(teco_machine_main_t *ctx, GError **error) +{ + /* + * ^A differs from all other string-taking commands in having + * a default ^A escape char. + */ + if (ctx->parent.must_undo) + teco_undo_gunichar(ctx->expectstring.machine.escape_char); + ctx->expectstring.machine.escape_char = TECO_CTL_KEY('A'); + + /* chain to the default initial_cb */ + return teco_state_expectstring_initial(ctx, error); +} + +static teco_state_t * +teco_state_print_string_done(teco_machine_main_t *ctx, const teco_string_t *str, GError **error) +{ + teco_interface_msg_literal(TECO_MSG_USER, str->data, str->len); + return &teco_state_start; +} + +/*$ "^A" print + * ^A<string>^A -- Print string as message + * @^A/string/ + * + * Print <string> as a message, i.e. in the message line + * in interactive mode and if possible on the terminal (stdout) as well. + * + * \fB^A\fP differs from all other commands in the way <string> + * is terminated. + * It is terminated by ^A (CTRL+A, ASCII 1) by default. + * While the initial \fB^A\fP can be written with upcarets, + * the terminating ^A must always be ASCII 1. + * You can however overwrite the <string> terminator as usual + * by \fB@\fP-modifying the command. + * + * String-building characters are enabled for this command. + */ +TECO_DEFINE_STATE_EXPECTSTRING(teco_state_print_string, + .initial_cb = (teco_state_initial_cb_t)teco_state_print_string_initial +); diff --git a/src/stdio-commands.h b/src/stdio-commands.h index b795c08..33b2594 100644 --- a/src/stdio-commands.h +++ b/src/stdio-commands.h @@ -19,3 +19,4 @@ #include "parser.h" TECO_DECLARE_STATE(teco_state_print_decimal); +TECO_DECLARE_STATE(teco_state_print_string); |