aboutsummaryrefslogtreecommitdiffhomepage
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/core-commands.c150
-rw-r--r--src/interface-curses/interface.c417
-rw-r--r--src/interface-gtk/interface.c3
-rw-r--r--src/interface.h10
-rw-r--r--src/main.c14
-rw-r--r--src/search.c291
-rw-r--r--src/search.h2
-rw-r--r--src/stdio-commands.c37
-rw-r--r--src/view.c16
9 files changed, 573 insertions, 367 deletions
diff --git a/src/core-commands.c b/src/core-commands.c
index 2dc1da7..5ca508c 100644
--- a/src/core-commands.c
+++ b/src/core-commands.c
@@ -2084,7 +2084,7 @@ teco_state_ecommand_flags(teco_machine_main_t *ctx, GError **error)
/*$ EJ properties
* [key]EJ -> value -- Get and set system properties
* value,keyEJ
- * rgb,color,3EJ
+ * [fore,]back,7EJ
* -EJ -> event
* -2EJ -> y, x
*
@@ -2110,6 +2110,7 @@ teco_state_ecommand_flags(teco_machine_main_t *ctx, GError **error)
* of buffers in the ring.
* (\fBread-only\fP)
* .IP 2:
+ * .SCITECO_TOPIC "memory limit"
* The current memory limit in bytes.
* This limit helps to prevent dangerous out-of-memory
* conditions (e.g. resulting from infinite loops) by
@@ -2134,49 +2135,40 @@ teco_state_ecommand_flags(teco_machine_main_t *ctx, GError **error)
* requirements are too large for the new limit \(em if
* this happens you may have to clear your command-line
* first.
- * Memory limiting is enabled by default.
+ * Memory limiting is enabled by default (default: 500MB).
* .IP 3:
- * This \fBwrite-only\fP property allows redefining the
- * first 16 entries of the terminal color palette \(em a
- * feature required by some
- * color schemes when using the Curses user interface.
- * When setting this property, you are making a request
- * to define the terminal \fIcolor\fP as the Scintilla-compatible
- * RGB color value given in the \fIrgb\fP parameter.
- * \fIcolor\fP must be a value between 0 and 15
- * corresponding to black, red, green, yellow, blue, magenta,
- * cyan, white, bright black, bright red, etc. in that order.
- * The \fIrgb\fP value has the format 0xBBGGRR, i.e. the red
- * component is the least-significant byte and all other bytes
- * are ignored.
- * Note that on curses, RGB color values sent to Scintilla
- * are actually mapped to these 16 colors by the Scinterm port
- * and may represent colors with no resemblance to the \(lqRGB\(rq
- * value used (depending on the current palette) \(em they should
- * instead be viewed as placeholders for 16 standard terminal
- * color codes.
- * Please refer to the Scinterm manual for details on the allowed
- * \(lqRGB\(rq values and how they map to terminal colors.
- * This command provides a crude way to request exact RGB colors
- * for the first 16 terminal colors.
- * The color definition may be queued or be completely ignored
- * on other user interfaces and no feedback is given
- * if it fails. In fact feedback cannot be given reliably anyway.
- * Note that on 8 color terminals, only the first 8 colors
- * can be redefined (if you are lucky).
- * Note that due to restrictions of most terminal emulators
- * and some curses implementations, this command simply will not
- * restore the original palette entry or request
- * when rubbed out and should generally only be used in
- * \fIbatch-mode\fP \(em typically when loading a color scheme.
- * For the same reasons \(em even though \*(ST tries hard to
- * restore the original palette on exit \(em palette changes may
- * persist after \*(ST terminates on most terminal emulators on Unix.
- * The only emulator which will restore their default palette
- * on exit the author is aware of is \fBxterm\fP(1) and
- * the Linux console driver.
- * You have been warned. Good luck.
+ * .SCITECO_TOPIC palette
+ * This \fBwrite-only\fP property allows disabling use of
+ * the 16 color palette of default colors when using
+ * the Curses user interface.
+ * By default, when working with RGB values in Scintilla
+ * messages (and \*(ST color schemes for that matter)
+ * the 16 \(lqdefault\(rq colors are represented by
+ * special RGB placeholders (e.g. 0x000080 for \fICOLOR_RED\fP)
+ * as documented in the Scinterm manual.
+ * Any other RGB values will be automatically allocated
+ * in the terminal emulator if supported, giving the illusion
+ * of true color support, even though the actual number of
+ * concurrent colors on screen may be limited by the terminal
+ * emulator (see \fImax_colors\fP and \fImax_pairs\fP
+ * .B terminfo (1)
+ * capabilities) and other factors.
+ * \*(ST tries to avoid the first 16 color ids so as not to
+ * interfer with other terminal-based applications.
+ * The default palette however may be overwritten when requesting
+ * new colors in terminals restricted to 16 colors.
+ * In this case the original palette might not be restored properly on
+ * program termination and affect other terminal applications.
+ * The 16 predefined colors do not map to exact RGB values,
+ * though, but point into a palette at the terminal emulator.
+ * \*(ST therefore allows you to disable the 16 RGB placeholders
+ * by calling \(lq0,3EJ\(rq so that all colors are freshly initialized
+ * and will represent their exact RGB values.
+ * It has no effect when using the GTK interface.
+ * You are recommended to add \(lq0,3EJ\(rq to all color
+ * schemes that rely on exact RGB values.
* .IP 4:
+ * .SCITECO_TOPIC caretx
* 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.
@@ -2199,6 +2191,41 @@ teco_state_ecommand_flags(teco_machine_main_t *ctx, GError **error)
* After changing the interval, the new value may become
* active only after the previous interval expires.
* Recovery files are not dumped in batch mode.
+ * .IP 7:
+ * .SCITECO_TOPIC "default colors"
+ * This \fBwrite-only\fP property allows configuring the default
+ * foreground and background colors, given as two
+ * Scintilla-encoded RGB (0xBBGGRR) numbers.
+ * When asked to draw these colors, the terminal's native
+ * default colors \(em which may be some kind of white on black or
+ * black on white \(em are used instead.
+ * Therefore this command does not change any colors on screen
+ * \(em it just defines placeholder values for the terminal's
+ * default colors.
+ * This may be used e.g. to get a true black background where
+ * the terminal emulator's palette uses a different hue for
+ * Curses color COLOR_BLACK without having to resort to
+ * color redefinitions.
+ * With caution this may even be used to build color schemes that
+ * work both on white on black and black on white terminals.
+ * Use -1 to disable default foreground or background colors.
+ * It has no effect when using the GTK interface.
+ * .IP 8:
+ * .SCITECO_TOPIC "block size"
+ * Block size during backwards searches (e.g. \(lq-S\(rq).
+ * When searching backwards \*(ST will perform forward searches
+ * in within blocks before the search range's end.
+ * If not enough matches could be produced, it moves back
+ * another block.
+ * The smaller the block size the more likely that a match
+ * will not be the leftmost longest, but the less matching overhead.
+ * A negative or 0 value (\(lq0,8EJ\(rq) is the maximum block size and
+ * ensures forward searches over the entire search range.
+ * Only this setting guarantees leftmost longest matches that
+ * are entirely symmetric to forward searches, but can be
+ * unpractically slow on huge files.
+ * The default is 0.
+ * \# FIXME: Feature is currently broken!
* .
* .IP -1:
* Type of the last mouse event (\fBread-only\fP).
@@ -2250,10 +2277,12 @@ teco_state_ecommand_properties(teco_machine_main_t *ctx, GError **error)
EJ_USER_INTERFACE = 0,
EJ_BUFFERS,
EJ_MEMORY_LIMIT,
- EJ_INIT_COLOR,
+ EJ_PALETTE,
EJ_CARETX,
EJ_CMDLINE_HEIGHT,
- EJ_RECOVERY_INTERVAL
+ EJ_RECOVERY_INTERVAL,
+ EJ_DEFAULT_COLORS,
+ EJ_SEARCH_BLOCK_SIZE
};
static teco_int_t caret_x = 0;
@@ -2274,19 +2303,13 @@ teco_state_ecommand_properties(teco_machine_main_t *ctx, GError **error)
return;
break;
- case EJ_INIT_COLOR:
- if (value < 0 || value >= 16) {
- g_set_error(error, TECO_ERROR, TECO_ERROR_FAILED,
- "Invalid color code %" TECO_INT_FORMAT " "
- "specified for <EJ>", value);
- return;
- }
- if (!teco_expressions_args()) {
- teco_error_argexpected_set(error, "EJ");
+ case EJ_PALETTE:
+ if (teco_is_success(value)) {
+ g_set_error_literal(error, TECO_ERROR, TECO_ERROR_FAILED,
+ "Default color palette can only be disabled");
return;
}
- teco_interface_init_color((guint)value,
- (guint32)teco_expressions_pop_num(0));
+ teco_interface_disable_palette();
break;
case EJ_CARETX:
@@ -2315,6 +2338,19 @@ teco_state_ecommand_properties(teco_machine_main_t *ctx, GError **error)
/* FIXME: Perhaps signal the interface to reprogram timers */
break;
+ case EJ_DEFAULT_COLORS: {
+ teco_int_t fore;
+
+ if (!teco_expressions_pop_num_calc(&fore, -1, error))
+ return;
+ teco_interface_set_default_colors(fore, value);
+ break;
+ }
+
+ case EJ_SEARCH_BLOCK_SIZE:
+ teco_undo_gsize(teco_search_block_size) = MAX(0, value);
+ break;
+
default:
g_set_error(error, TECO_ERROR, TECO_ERROR_FAILED,
"Cannot set property %" TECO_INT_FORMAT " "
@@ -2378,6 +2414,10 @@ teco_state_ecommand_properties(teco_machine_main_t *ctx, GError **error)
teco_expressions_push(teco_ring_recovery_interval);
break;
+ case EJ_SEARCH_BLOCK_SIZE:
+ teco_expressions_push(teco_search_block_size);
+ break;
+
default:
g_set_error(error, TECO_ERROR, TECO_ERROR_FAILED,
"Invalid property %" TECO_INT_FORMAT " "
diff --git a/src/interface-curses/interface.c b/src/interface-curses/interface.c
index 569b13a..ff6a0f2 100644
--- a/src/interface-curses/interface.c
+++ b/src/interface-curses/interface.c
@@ -140,6 +140,16 @@ static gint teco_xterm_version(void) G_GNUC_UNUSED;
static gint teco_interface_blocking_getch(void);
static inline gboolean teco_interface_check_key(gint key);
+#define MAX_COLORS (MIN(COLORS, G_MAXSHORT)/2)
+/**
+ * Maximum effective number of color pairs.
+ * Scinterm uses shorts for color pairs, so G_MAXSHORT is the maximum.
+ * Some Curses implementations (NetBSD, PDCurses) may support less,
+ * but set COLOR_PAIRS accordingly.
+ * Half of the range is reserved to Scinterm.
+ */
+#define MAX_PAIRS (MIN(COLOR_PAIRS, G_MAXSHORT)/2)
+
/**
* Get bright variant of one of the 8 standard
* curses colors.
@@ -156,6 +166,12 @@ static inline gboolean teco_interface_check_key(gint key);
* The 8 bright colors (if terminal supports at
* least 16 colors), else they are identical to
* the non-bright colors (default curses colors).
+ *
+ * As a consequence a light black (grey) foreground
+ * on black background might be invisible on 8 color
+ * terminals.
+ * If you add in A_BOLD, there is a good chance that
+ * the text will still become grey.
*/
#define COLOR_LBLACK COLOR_LIGHT(COLOR_BLACK)
#define COLOR_LRED COLOR_LIGHT(COLOR_RED)
@@ -168,30 +184,28 @@ static inline gboolean teco_interface_check_key(gint key);
static struct {
/**
+ * Mapping of RGB codes (encoded into a pointer)
+ * to a curses color number.
+ */
+ GHashTable *color_table;
+ /**
* 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).
+ * Whether to map the 16 standard palettized colors
+ * in color_table.
+ * Should be disabled when using arbitrary RGB values,
+ * so every color is initialized to exact RGB values.
*/
- gint32 color_table[16];
-
+ gboolean use_palette;
/**
- * 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.
+ * Scintilla RGB values which should be rendered
+ * as the terminal's default foreground/background colors.
+ * This is -1 if colors haven't been configured.
*/
- struct {
- gshort r, g, b;
- } orig_color_table[16];
+ gint32 default_fg, default_bg;
int stdin_orig, stdout_orig, stderr_orig;
SCREEN *screen;
@@ -229,6 +243,76 @@ static struct {
} teco_interface;
/**
+ * Translate a Scintilla-compatible RGB color value
+ * (0xBBGGRR) to a Curses color triple (0 to 1000
+ * for each component).
+ */
+static inline void
+teco_rgb2curses_triple(guint32 rgb, gshort *r, gshort *g, gshort *b)
+{
+ /* NOTE: We could also use 200/51 */
+ *r = ((rgb & 0x0000FF) >> 0)*1000/0xFF;
+ *g = ((rgb & 0x00FF00) >> 8)*1000/0xFF;
+ *b = ((rgb & 0xFF0000) >> 16)*1000/0xFF;
+}
+
+static inline void
+teco_interface_insert_color(guint32 rgb, gshort color)
+{
+ /*
+ * Color ids are stored one-offset, so that COLOR_BLACK
+ * can be discerned from a missing entry.
+ */
+ g_hash_table_insert(teco_interface.color_table,
+ GUINT_TO_POINTER(rgb), GUINT_TO_POINTER(color)+1);
+}
+
+/**
+ * Convert a Scintilla-compatible RGB color value
+ * (0xBBGGRR) to a Curses color code (e.g. COLOR_BLACK).
+ *
+ * This will automatically initialize new curses colors
+ * when necessary.
+ * Unless the palette is disabled with
+ * teco_interface_disable_palette(), this will return
+ * the 16 "standard" colors for a predefined list of
+ * RGB values (just like defined by Scinterm).
+ */
+static gshort
+teco_rgb2curses(guint32 rgb)
+{
+ static guint colors = 0;
+
+ if (!has_colors())
+ return COLOR_WHITE;
+
+ gpointer value = g_hash_table_lookup(teco_interface.color_table,
+ GUINT_TO_POINTER(rgb));
+ if (G_LIKELY(value != NULL))
+ return GPOINTER_TO_UINT(value)-1;
+
+ /*
+ * Even when initializing new colors, we will __try__ not to
+ * touch the 16 palettized colors.
+ * First of all, we might still need them and secondly,
+ * this ensures we don't have to reset them on exit
+ * (which is not always possible anyway).
+ * There can still be 16 color terminals with
+ * can_change_color() == TRUE, though.
+ */
+ guint next_color = colors + (COLORS > 16 ? 16 : 0);
+
+ if (G_UNLIKELY(next_color >= MAX_COLORS) || !can_change_color())
+ return COLOR_WHITE;
+ gshort r, g, b;
+ teco_rgb2curses_triple(rgb, &r, &g, &b);
+ init_color(next_color, r, g, b);
+ teco_interface_insert_color(rgb, next_color);
+ colors++;
+ return next_color;
+}
+
+/**
* Returns the curses color pair for the given curses foreground and background colors.
* Initializes a new pair if necessary.
*
@@ -239,12 +323,12 @@ static struct {
* color pair space on demand, while the second half is reserved to Scinterm.
*
* @param attr attributes to modify (for supporting monochrome terminals)
- * @param fg curses foreground color
- * @param bg curses background color
+ * @param fg curses foreground color (0xBBGGRR)
+ * @param bg curses background color (0xBBGGRR)
* @return curses color pair number
*/
static gshort
-teco_color_pair(attr_t *attr, gshort fg, gshort bg)
+teco_color_pair(attr_t *attr, guint32 fg, guint32 bg)
{
static guint next_pair = 1;
@@ -256,78 +340,44 @@ teco_color_pair(attr_t *attr, gshort fg, gshort bg)
* color schemes.
*/
if (!has_colors()) {
- if (bg != COLOR_BLACK)
- *attr |= WA_REVERSE;
+ if (bg != 0x000000)
+ *attr ^= WA_REVERSE;
return 0;
}
+ gshort fg_id, bg_id;
+
+ /*
+ * Prefer to use reverse attribute since it will work with
+ * the terminals default colors.
+ */
+ if (fg == teco_interface.default_bg && bg == teco_interface.default_fg) {
+ fg_id = bg_id = -1;
+ *attr ^= WA_REVERSE;
+ } else {
+ fg_id = fg == teco_interface.default_fg ? -1 : teco_rgb2curses(fg);
+ bg_id = bg == teco_interface.default_bg ? -1 : teco_rgb2curses(bg);
+ }
+
G_STATIC_ASSERT(sizeof(gshort)*2 <= sizeof(guint));
- gpointer key = GUINT_TO_POINTER(((guint)fg << 8*sizeof(bg)) | bg);
+ gpointer key = GUINT_TO_POINTER(((guint)fg_id << 8*sizeof(bg_id)) | bg_id);
gpointer value = g_hash_table_lookup(teco_interface.pair_table, key);
if (G_LIKELY(value != NULL))
return GPOINTER_TO_UINT(value);
- if (G_UNLIKELY(next_pair >= COLOR_PAIRS || next_pair > G_MAXSHORT))
+ if (G_UNLIKELY(next_pair >= MAX_PAIRS))
return 0;
- init_pair(next_pair, fg, bg);
+ init_pair(next_pair, fg_id, bg_id);
g_hash_table_insert(teco_interface.pair_table, key, GUINT_TO_POINTER(next_pair));
return next_pair++;
}
static inline gint
-teco_wattr_set(WINDOW *win, attr_t attr, gshort fg, gshort bg)
+teco_wattr_set(WINDOW *win, attr_t attr, guint32 fg, guint32 bg)
{
gshort pair = teco_color_pair(&attr, fg, bg);
return wattr_set(win, attr, pair, NULL);
}
-/**
- * Translate a Scintilla-compatible RGB color value
- * (0xBBGGRR) to a Curses color triple (0 to 1000
- * for each component).
- */
-static inline void
-teco_rgb2curses_triple(guint32 rgb, gshort *r, gshort *g, gshort *b)
-{
- /* NOTE: We could also use 200/51 */
- *r = ((rgb & 0x0000FF) >> 0)*1000/0xFF;
- *g = ((rgb & 0x00FF00) >> 8)*1000/0xFF;
- *b = ((rgb & 0xFF0000) >> 16)*1000/0xFF;
-}
-
-/**
- * Convert a Scintilla-compatible RGB color value
- * (0xBBGGRR) to a Curses color code (e.g. COLOR_BLACK).
- * This does not work with arbitrary RGB values but
- * only the 16 RGB color values defined by Scinterm
- * corresponding to the 16 terminal colors.
- * It is equivalent to Scinterm's internal `term_color`
- * function.
- */
-static gshort
-teco_rgb2curses(guint32 rgb)
-{
- switch (rgb) {
- case 0x000000: return COLOR_BLACK;
- case 0x000080: return COLOR_RED;
- case 0x008000: return COLOR_GREEN;
- case 0x008080: return COLOR_YELLOW;
- case 0x800000: return COLOR_BLUE;
- case 0x800080: return COLOR_MAGENTA;
- case 0x808000: return COLOR_CYAN;
- case 0xC0C0C0: return COLOR_WHITE;
- case 0x404040: return COLOR_LBLACK;
- case 0x0000FF: return COLOR_LRED;
- case 0x00FF00: return COLOR_LGREEN;
- case 0x00FFFF: return COLOR_LYELLOW;
- case 0xFF0000: return COLOR_LBLUE;
- case 0xFF00FF: return COLOR_LMAGENTA;
- case 0xFFFF00: return COLOR_LCYAN;
- case 0xFFFFFF: return COLOR_LWHITE;
- }
-
- return COLOR_WHITE;
-}
-
static gint
teco_xterm_version(void)
{
@@ -416,9 +466,6 @@ teco_view_free(teco_view_t *ctx)
scintilla_delete(ctx);
}
-static void teco_interface_init_color_safe(guint color, guint32 rgb);
-static void teco_interface_restore_colors(void);
-
static void teco_interface_init_screen(void);
static gboolean teco_interface_init_interactive(GError **error);
static void teco_interface_restore_batch(void);
@@ -433,10 +480,9 @@ static void teco_interface_draw_info(void);
void
teco_interface_init(gint argc, gchar **argv)
{
- for (guint i = 0; i < G_N_ELEMENTS(teco_interface.color_table); i++)
- teco_interface.color_table[i] = -1;
- for (guint i = 0; i < G_N_ELEMENTS(teco_interface.orig_color_table); i++)
- teco_interface.orig_color_table[i].r = -1;
+ teco_interface.use_palette = TRUE;
+ teco_interface.default_fg = 0xC0C0C0; /* white */
+ teco_interface.default_bg = 0x000000; /* black */;
teco_interface.stdin_orig = teco_interface.stdout_orig = teco_interface.stderr_orig = -1;
@@ -478,133 +524,17 @@ teco_interface_get_options(void)
return NULL;
}
-static void
-teco_interface_init_color_safe(guint color, guint32 rgb)
-{
-#if defined(__PDCURSES__) && !defined(PDCURSES_GUI)
- if (teco_interface.orig_color_table[color].r < 0) {
- color_content((short)color,
- &teco_interface.orig_color_table[color].r,
- &teco_interface.orig_color_table[color].g,
- &teco_interface.orig_color_table[color].b);
- }
-#endif
-
- gshort r, g, b;
- teco_rgb2curses_triple(rgb, &r, &g, &b);
- init_color((short)color, r, g, b);
-}
-
-#if defined(__PDCURSES__) && !defined(PDCURSES_GUI)
-
-/*
- * On PDCurses/WinCon, color_content() will actually return
- * the real console color palette - or at least the default
- * palette when the console started.
- */
-static void
-teco_interface_restore_colors(void)
-{
- if (!can_change_color())
- return;
-
- for (guint i = 0; i < G_N_ELEMENTS(teco_interface.orig_color_table); i++) {
- if (teco_interface.orig_color_table[i].r < 0)
- continue;
-
- init_color((short)i,
- teco_interface.orig_color_table[i].r,
- teco_interface.orig_color_table[i].g,
- teco_interface.orig_color_table[i].b);
- }
-}
-
-#elif defined(CURSES_TTY)
-
-/*
- * FIXME: On UNIX/ncurses teco_interface_init_color_safe() __may__
- * change the terminal's palette permanently and there does not
- * appear to be any portable way of restoring the original one.
- * Curses has color_content(), but there is actually no terminal
- * that allows querying the current palette and so color_content()
- * will return bogus "default" values and only for the first 8 colors.
- * It would do more damage to restore the palette returned by
- * color_content() than it helps.
- * xterm has the escape sequence "\e]104\a" which restores
- * the palette from Xdefaults but not all terminal emulators
- * claiming to be "xterm" via $TERM support this escape sequence.
- * lxterminal for instance will print gibberish instead.
- * So we try to look whether $XTERM_VERSION is set.
- * There are hardly any other terminal emulators that support palette
- * resets.
- * The only emulator I'm aware of which can be identified reliably
- * by $TERM supporting a palette reset is the Linux console
- * (see console_codes(4)). The escape sequence "\e]R" is already
- * part of its terminfo description (orig_colors capability)
- * which is apparently sent by endwin(), so the palette is
- * already properly restored on endwin().
- * Welcome in Curses hell.
- */
-static void
-teco_interface_restore_colors(void)
-{
- if (teco_xterm_version() < 0)
- return;
-
- /*
- * Looks like a real XTerm
- */
- fputs("\e]104\a", teco_interface.screen_tty);
- fflush(teco_interface.screen_tty);
-}
-
-#else /* (!__PDCURSES__ || PDCURSES_GUI) && !CURSES_TTY */
-
-static void
-teco_interface_restore_colors(void)
+void
+teco_interface_disable_palette(void)
{
- /*
- * No way to restore the palette, or it's
- * unnecessary (e.g. XCurses)
- */
+ teco_interface.use_palette = FALSE;
}
-#endif
-
void
-teco_interface_init_color(guint color, guint32 rgb)
+teco_interface_set_default_colors(gint32 fg, gint32 bg)
{
- if (color >= G_N_ELEMENTS(teco_interface.color_table))
- return;
-
-#if defined(__PDCURSES__) && !defined(PDC_RGB)
- /*
- * PDCurses will usually number color codes differently
- * (least significant bit is the blue component) while
- * SciTECO macros will assume a standard terminal color
- * code numbering with red as the LSB.
- * Therefore we have to swap the bit order of the least
- * significant 3 bits here.
- */
- color = (color & ~0x5) |
- ((color & 0x1) << 2) | ((color & 0x4) >> 2);
-#endif
-
- if (teco_interface.input_pad) {
- /* interactive mode */
- if (!can_change_color())
- return;
-
- teco_interface_init_color_safe(color, rgb);
- } else {
- /*
- * batch mode: store colors,
- * they can only be initialized after start_color()
- * which is called by Scinterm when interactive
- * mode is initialized
- */
- teco_interface.color_table[color] = (gint32)rgb;
- }
+ teco_interface.default_fg = fg;
+ teco_interface.default_bg = bg;
}
#ifdef CURSES_TTY
@@ -746,14 +676,41 @@ teco_interface_init_interactive(GError **error)
teco_interface_init_screen();
+ teco_interface.color_table = g_hash_table_new(g_direct_hash, g_direct_equal);
teco_interface.pair_table = g_hash_table_new(g_direct_hash, g_direct_equal);
start_color();
- /*
- * Scinterm uses shorts for color pairs, so G_MAXSHORT is the maximum.
- * Some Curses implementations (NetBSD, PDCurses) may support less,
- * but set COLOR_PAIRS accordingly.
- */
- scintilla_set_color_offsets(0, MIN(COLOR_PAIRS, G_MAXSHORT)/2);
+ scintilla_set_color_offsets(MAX_COLORS, MAX_PAIRS);
+ if (!teco_interface.use_palette)
+ scintilla_disable_color_palette();
+#ifdef HAVE_USE_DEFAULT_COLORS
+ /* color -1 will be the terminal's default foreground or background color */
+ use_default_colors();
+#endif
+ scintilla_set_default_colors(teco_interface.default_fg,
+ teco_interface.default_bg);
+
+ if (teco_interface.use_palette && has_colors()) {
+ teco_interface_insert_color(0x000000, COLOR_BLACK);
+ teco_interface_insert_color(0x000080, COLOR_RED);
+ teco_interface_insert_color(0x008000, COLOR_GREEN);
+ teco_interface_insert_color(0x008080, COLOR_YELLOW);
+ teco_interface_insert_color(0x800000, COLOR_BLUE);
+ teco_interface_insert_color(0x800080, COLOR_MAGENTA);
+ teco_interface_insert_color(0x808000, COLOR_CYAN);
+ teco_interface_insert_color(0xC0C0C0, COLOR_WHITE);
+ /*
+ * On 8-color terminals, we still support the
+ * higher (light) colors, but map them to the lower codes as well.
+ */
+ teco_interface_insert_color(0x404040, COLOR_LBLACK);
+ teco_interface_insert_color(0x0000FF, COLOR_LRED);
+ teco_interface_insert_color(0x00FF00, COLOR_LGREEN);
+ teco_interface_insert_color(0x00FFFF, COLOR_LYELLOW);
+ teco_interface_insert_color(0xFF0000, COLOR_LBLUE);
+ teco_interface_insert_color(0xFF00FF, COLOR_LMAGENTA);
+ teco_interface_insert_color(0xFFFF00, COLOR_LCYAN);
+ teco_interface_insert_color(0xFFFFFF, COLOR_LWHITE);
+ }
/*
* On UNIX terminals, the escape key is usually
@@ -835,19 +792,6 @@ teco_interface_init_interactive(GError **error)
teco_interface_show_view(teco_interface_current_view);
/*
- * Only now it's safe to redefine the 16 default colors.
- */
- if (can_change_color()) {
- for (guint i = 0; i < G_N_ELEMENTS(teco_interface.color_table); i++) {
- /*
- * init_color() may still fail if COLORS < 16
- */
- if (teco_interface.color_table[i] >= 0)
- teco_interface_init_color_safe(i, (guint32)teco_interface.color_table[i]);
- }
- }
-
- /*
* Only now (in interactive mode), it's safe to initialize
* the clipboard Q-Registers on ncurses with a compatible terminal
* emulator since clipboard operations will no longer interfer
@@ -879,7 +823,6 @@ teco_interface_restore_batch(void)
* (i.e. return to batch mode)
*/
endwin();
- teco_interface_restore_colors();
/*
* Restore stdin, stdout and stderr, so output goes to
@@ -944,23 +887,23 @@ teco_interface_msg_literal(teco_msg_t type, const gchar *str, gsize len)
teco_interface_stdio_msg(type, str, len);
#endif
- gshort fg, bg;
+ guint32 fg, bg;
- fg = teco_rgb2curses(teco_interface_ssm(SCI_STYLEGETBACK, STYLE_DEFAULT, 0));
+ fg = teco_interface_ssm(SCI_STYLEGETBACK, STYLE_DEFAULT, 0);
switch (type) {
default:
case TECO_MSG_USER:
- bg = teco_rgb2curses(teco_interface_ssm(SCI_STYLEGETFORE, STYLE_DEFAULT, 0));
+ bg = teco_interface_ssm(SCI_STYLEGETFORE, STYLE_DEFAULT, 0);
break;
case TECO_MSG_INFO:
- bg = COLOR_GREEN;
+ bg = 0x008000; /* green */
break;
case TECO_MSG_WARNING:
- bg = COLOR_YELLOW;
+ bg = 0x008080; /* yellow */
break;
case TECO_MSG_ERROR:
- bg = COLOR_RED;
+ bg = 0x000080; /* red */
beep();
break;
}
@@ -977,8 +920,8 @@ teco_interface_msg_clear(void)
if (!teco_interface.input_pad) /* batch mode */
return;
- gshort fg = teco_rgb2curses(teco_interface_ssm(SCI_STYLEGETBACK, STYLE_DEFAULT, 0));
- gshort bg = teco_rgb2curses(teco_interface_ssm(SCI_STYLEGETFORE, STYLE_DEFAULT, 0));
+ guint32 fg = teco_interface_ssm(SCI_STYLEGETBACK, STYLE_DEFAULT, 0);
+ guint32 bg = teco_interface_ssm(SCI_STYLEGETFORE, STYLE_DEFAULT, 0);
wmove(teco_interface.msg_window, 0, 0);
teco_wattr_set(teco_interface.msg_window, 0, fg, bg);
@@ -1170,8 +1113,8 @@ teco_interface_draw_info(void)
* the current buffer's STYLE_DEFAULT.
* The same style is used for MSG_USER messages.
*/
- gshort fg = teco_rgb2curses(teco_interface_ssm(SCI_STYLEGETBACK, STYLE_DEFAULT, 0));
- gshort bg = teco_rgb2curses(teco_interface_ssm(SCI_STYLEGETFORE, STYLE_DEFAULT, 0));
+ guint32 fg = teco_interface_ssm(SCI_STYLEGETBACK, STYLE_DEFAULT, 0);
+ guint32 bg = teco_interface_ssm(SCI_STYLEGETFORE, STYLE_DEFAULT, 0);
wmove(teco_interface.info_window, 0, 0);
teco_wattr_set(teco_interface.info_window, 0, fg, bg);
@@ -1743,8 +1686,8 @@ teco_interface_popup_show(gsize prefix_len)
/* batch mode */
return;
- gshort fg = teco_rgb2curses(teco_interface_ssm(SCI_STYLEGETFORE, STYLE_CALLTIP, 0));
- gshort bg = teco_rgb2curses(teco_interface_ssm(SCI_STYLEGETBACK, STYLE_CALLTIP, 0));
+ guint32 fg = teco_interface_ssm(SCI_STYLEGETFORE, STYLE_CALLTIP, 0);
+ guint32 bg = teco_interface_ssm(SCI_STYLEGETBACK, STYLE_CALLTIP, 0);
teco_interface.popup_prefix_len = prefix_len;
attr_t attr = 0;
@@ -1949,8 +1892,8 @@ teco_interface_process_mevent(MEVENT *event, GError **error)
else if (event->bstate & BUTTON_NUM(5))
teco_curses_info_popup_scroll(&teco_interface.popup, +2);
- gshort fg = teco_rgb2curses(teco_interface_ssm(SCI_STYLEGETFORE, STYLE_CALLTIP, 0));
- gshort bg = teco_rgb2curses(teco_interface_ssm(SCI_STYLEGETBACK, STYLE_CALLTIP, 0));
+ guint32 fg = teco_interface_ssm(SCI_STYLEGETFORE, STYLE_CALLTIP, 0);
+ guint32 bg = teco_interface_ssm(SCI_STYLEGETBACK, STYLE_CALLTIP, 0);
attr_t attr = 0;
gshort pair = teco_color_pair(&attr, fg, bg);
teco_curses_info_popup_show(&teco_interface.popup, attr, pair);
@@ -2403,6 +2346,8 @@ teco_interface_cleanup(void)
if (teco_interface.stdout_orig >= 0)
close(teco_interface.stdout_orig);
+ if (teco_interface.color_table)
+ g_hash_table_destroy(teco_interface.color_table);
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 a31790f..1f6259f 100644
--- a/src/interface-gtk/interface.c
+++ b/src/interface-gtk/interface.c
@@ -404,7 +404,8 @@ teco_interface_get_options(void)
return group;
}
-void teco_interface_init_color(guint color, guint32 rgb) {}
+void teco_interface_disable_palette(void) {}
+void teco_interface_set_default_colors(gint32 fg, gint32 bg) {}
void
teco_interface_msg_literal(teco_msg_t type, const gchar *str, gsize len)
diff --git a/src/interface.h b/src/interface.h
index 54f807b..a7968d1 100644
--- a/src/interface.h
+++ b/src/interface.h
@@ -57,13 +57,17 @@ void teco_interface_init(gint argc, gchar **argv);
GOptionGroup *teco_interface_get_options(void);
/** @pure makes sense only on Curses */
-void teco_interface_init_color(guint color, guint32 rgb);
+void teco_interface_disable_palette(void);
+
+/** @pure makes sense only on Curses */
+void teco_interface_set_default_colors(gint32 fg, gint32 bg);
typedef enum {
- TECO_MSG_USER,
+ TECO_MSG_USER = 0,
TECO_MSG_INFO,
TECO_MSG_WARNING,
- TECO_MSG_ERROR
+ TECO_MSG_ERROR,
+ TECO_MSG_MAX = TECO_MSG_ERROR
} teco_msg_t;
extern teco_msg_t teco_interface_msg_level;
diff --git a/src/main.c b/src/main.c
index 33ab26f..29ef024 100644
--- a/src/main.c
+++ b/src/main.c
@@ -388,7 +388,21 @@ main(int argc, char **argv)
getchar();
#endif
+ /*
+ * FIXME: Gracefully handle SIGTERM.
+ * This however would require polling for the flag in Curses
+ * getch() loops.
+ * GTK already catches SIGTERM.
+ */
+#ifdef HAVE_SIGACTION
+ static const struct sigaction sigint = {
+ .sa_handler = teco_sigint_handler,
+ .sa_flags = 0 /* don't auto-restart syscalls */
+ };
+ sigaction(SIGINT, &sigint, NULL);
+#else
signal(SIGINT, teco_sigint_handler);
+#endif
/*
* Important for Unicode handling in curses and glib.
diff --git a/src/search.c b/src/search.c
index 856d079..92be012 100644
--- a/src/search.c
+++ b/src/search.c
@@ -20,6 +20,7 @@
#endif
#include <string.h>
+#include <stdlib.h>
#include <glib.h>
#include <glib/gprintf.h>
@@ -573,8 +574,46 @@ teco_get_ranges(const GMatchInfo *match_info, gsize offset, guint *count)
return ranges;
}
+/* only keep the last `count' matches, in a circular stack */
+typedef struct {
+ guint num_ranges;
+ teco_range_t *ranges;
+} teco_match_t;
+
+typedef struct {
+ guint count;
+ teco_match_t matches[];
+} teco_matches_t;
+
+static inline void
+teco_matches_free(teco_matches_t *p)
+{
+ for (int i = 0; i < p->count; i++)
+ g_free(p->matches[i].ranges);
+ g_free(p);
+}
+
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(teco_matches_t, teco_matches_free);
+
+/**
+ * Search forwards in the current buffer.
+ *
+ * If `*count == 0` after the call, the n-th match was found in
+ * the current document and teco_ranges/teco_ranges_count is
+ * set accordingly but must still be converted into glyph
+ * offsets.
+ *
+ * @param re Compiled regular expression.
+ * @param from Beginning of the range to search (in bytes).
+ * @param to End of the range to search (in bytes).
+ * @param count Pointer to an integer (n-th match to find).
+ * This is updated to reflect how many matches are still outstanding.
+ * A negative value indicates backwards searches.
+ * @param error A GError
+ * @return FALSE if an error occurred
+ */
static gboolean
-teco_do_search(GRegex *re, gsize from, gsize to, gint *count, GError **error)
+teco_do_search_forward(GRegex *re, gsize from, gsize to, gint *count, GError **error)
{
g_autoptr(GMatchInfo) info = NULL;
/* NOTE: can return NULL pointer for completely new and empty documents */
@@ -590,63 +629,175 @@ teco_do_search(GRegex *re, gsize from, gsize to, gint *count, GError **error)
return FALSE;
}
- guint num_ranges = 0;
- teco_range_t *matched_ranges = NULL;
+ g_assert(*count > 0);
+ while (g_match_info_matches(info) && --(*count)) {
+ /*
+ * NOTE: The return boolean does NOT signal whether an error was generated.
+ */
+ g_match_info_next(info, &tmp_error);
+ if (tmp_error) {
+ g_propagate_error(error, tmp_error);
+ return FALSE;
+ }
- if (*count >= 0) {
- while (g_match_info_matches(info) && --(*count)) {
- /*
- * NOTE: The return boolean does NOT signal whether an error was generated.
- */
- g_match_info_next(info, &tmp_error);
- if (tmp_error) {
- g_propagate_error(error, tmp_error);
- return FALSE;
- }
+ /*
+ * FIXME: A single pathological match could already be excessively slow.
+ */
+ if (G_UNLIKELY(teco_interface_is_interrupted())) {
+ teco_error_interrupted_set(error);
+ return FALSE;
}
+ }
- if (!*count)
- /* successful */
- matched_ranges = teco_get_ranges(info, from, &num_ranges);
- } else {
- /* only keep the last `count' matches, in a circular stack */
- typedef struct {
- guint num_ranges;
- teco_range_t *ranges;
- } teco_match_t;
+ if (!*count) {
+ /* successful */
+ teco_undo_guint(teco_ranges_count);
+ teco_undo_ranges_own(teco_ranges) = teco_get_ranges(info, from, &teco_ranges_count);
+ }
- guint matched_num = -*count;
- gsize matched_size = sizeof(teco_match_t[matched_num]);
+ return TRUE;
+}
+/**
+ * Block size for backwards scanning or 0
+ *
+ * @bug Block-wise matching is currently broken,
+ * so we disable this by default - see below.
+ */
+gsize teco_search_block_size = 0; //4*1024;
+
+/**
+ * Search backwards, in blocks of teco_search_block_size
+ * (within each block a forward scan is performed).
+ *
+ * This cannot always guarantee longest matches if a match is
+ * found at the beginning of a block.
+ * Only if teco_search_block_size is 0 the entire range
+ * if forward scanned, which ensures longest matches
+ * entirely symmetrical to forward searches.
+ * For large block sizes (or 0) even trivial backwards searches
+ * can be painstakingly slow on large files, though.
+ *
+ * @see teco_do_search_forward
+ */
+static gboolean
+teco_do_search_backwards(GRegex *re, gsize from, gsize to, gint *count, GError **error)
+{
+ /*
+ * NOTE: can return NULL pointer for completely new and empty documents.
+ *
+ * Theoretically we could also get range pointers on demand which would
+ * be more likely to preserve the buffer gap.
+ */
+ const gchar *buffer = (const gchar *)teco_interface_ssm(SCI_GETRANGEPOINTER, from, to-from) ? : "";
+
+ g_assert(*count < 0);
+ guint matched_num = -*count;
+ gsize total_size = sizeof(teco_matches_t) + sizeof(teco_match_t[matched_num]);
+
+ /*
+ * matched_size could overflow.
+ * NOTE: Glib 2.48 has g_size_checked_mul() which uses
+ * compiler intrinsics.
+ */
+ if ((gsize)matched_num > (G_MAXSIZE - sizeof(teco_matches_t))/sizeof(teco_match_t))
+ /* guaranteed to fail either teco_memory_check() or g_malloc() */
+ total_size = G_MAXSIZE;
+
+ /*
+ * NOTE: It's theoretically possible that the allocation of the `matched`
+ * array causes an OOM if (-count) is large enough and regular
+ * memory limiting in teco_machine_main_step() wouldn't help.
+ * That's why we exceptionally have to check before allocating.
+ */
+ if (!teco_memory_check(total_size, error))
+ return FALSE;
+
+ g_autoptr(teco_matches_t) matched = g_malloc0(total_size);
+ matched->count = matched_num;
+
+ gint matched_total = 0;
+ gint i = 0; /* ring buffer pointer into the `matched->matches` array */
+
+ gsize to_block = to-from;
+
+ while (to_block > 0) {
+ g_autoptr(GMatchInfo) info = NULL;
+
+ gsize from_block = teco_search_block_size > 0
+ ? MAX(0, to_block - teco_search_block_size) : 0;
/*
- * matched_size could overflow.
- * NOTE: Glib 2.48 has g_size_checked_mul() which uses
- * compiler intrinsics.
+ * FIXME: DEC TECO search semantics could actually demand
+ * allowing matches to extend beyond the [from,to] range.
*/
- if (matched_size / sizeof(teco_match_t) != matched_num)
- /* guaranteed to fail either teco_memory_check() or g_malloc() */
- matched_size = G_MAXSIZE;
+ GRegexMatchFlags flags = to_block != to-from ? G_REGEX_MATCH_PARTIAL_HARD : 0;
+
+ GError *tmp_error = NULL;
/*
- * NOTE: It's theoretically possible that the allocation of the `matched`
- * array causes an OOM if (-count) is large enough and regular
- * memory limiting in teco_machine_main_step() wouldn't help.
- * That's why we exceptionally have to check before allocating.
+ * NOTE: The return boolean does NOT signal whether an error was generated.
+ * FIXME: Why isn't it possible to specify a start_position != 0?
*/
- if (!teco_memory_check(matched_size, error))
+ g_regex_match_full(re, buffer+from_block, to_block-from_block, 0,
+ flags, &info, &tmp_error);
+ if (tmp_error) {
+ g_propagate_error(error, tmp_error);
return FALSE;
+ }
- /*
- * NOTE: This needs to be deep-freed, which does not currently
- * happen automatically.
- */
- g_autofree teco_match_t *matched = g_malloc0(matched_size);
+ for (;;) {
+ if (G_UNLIKELY(teco_interface_is_interrupted())) {
+ teco_error_interrupted_set(error);
+ return FALSE;
+ }
- gint matched_total = 0, i = 0;
+ if (g_match_info_is_partial_match(info)) {
+ /*
+ * Match may fall on the block boundary,
+ * so retry matching the rest of the document.
+ * This is the only case where we have to rescan
+ * the same memory more than once.
+ *
+ * FIXME FIXME FIXME: We cannot retrieve the position here
+ * since g_match_info_fetch_pos() treats partial matches as errors.
+ * This is a confirmed glib bug and fast backwards searches
+ * will continue to be broken until we switch to a custom regexp
+ * engine.
+ */
+ gint partial_start, partial_end;
+ G_GNUC_UNUSED gboolean rc;
+ rc = g_match_info_fetch_pos(info, 0, &partial_start, &partial_end);
+ //g_assert(rc == TRUE);
+ if (!rc)
+ abort();
+
+ if (partial_end == to_block-from_block) {
+ g_autoptr(GMatchInfo) partial_info = NULL;
+
+ g_regex_match_full(re, buffer+partial_start, to-from-partial_start, 0,
+ G_REGEX_MATCH_ANCHORED, &partial_info, &tmp_error);
+ if (tmp_error) {
+ g_propagate_error(error, tmp_error);
+ return FALSE;
+ }
+
+ if (g_match_info_matches(partial_info)) {
+ g_free(matched->matches[i].ranges);
+ matched->matches[i].ranges = teco_get_ranges(partial_info, from+partial_start,
+ &matched->matches[i].num_ranges);
+ i = ++matched_total % matched_num;
+ }
+ }
- while (g_match_info_matches(info)) {
- g_free(matched[i].ranges);
- matched[i].ranges = teco_get_ranges(info, from, &matched[i].num_ranges);
+ /* there might still be other matches within the current block */
+ } else if (g_match_info_matches(info)) {
+ g_free(matched->matches[i].ranges);
+ matched->matches[i].ranges = teco_get_ranges(info, from+from_block,
+ &matched->matches[i].num_ranges);
+ i = ++matched_total % matched_num;
+ } else {
+ break;
+ }
/*
* NOTE: The return boolean does NOT signal whether an error was generated.
@@ -654,30 +805,50 @@ teco_do_search(GRegex *re, gsize from, gsize to, gint *count, GError **error)
g_match_info_next(info, &tmp_error);
if (tmp_error) {
g_propagate_error(error, tmp_error);
- for (int i = 0; i < matched_num; i++)
- g_free(matched[i].ranges);
return FALSE;
}
-
- i = ++matched_total % -(*count);
}
- *count = MIN(*count + matched_total, 0);
- if (!*count) {
+ if (matched_total >= matched_num) {
/* successful -> i points to stack bottom */
- num_ranges = matched[i].num_ranges;
- matched_ranges = matched[i].ranges;
- matched[i].ranges = NULL;
+ teco_undo_guint(teco_ranges_count) = matched->matches[i].num_ranges;
+ teco_undo_ranges_own(teco_ranges) = matched->matches[i].ranges;
+ matched->matches[i].ranges = NULL;
+
+ *count = 0;
+ return TRUE;
}
- for (gint i = 0; i < matched_num; i++)
- g_free(matched[i].ranges);
+ /*
+ * We got less matches than the required number.
+ * We know that the result won't be any of the existing
+ * matches, so we can discard them and shrink our
+ * circular buffer.
+ * This avoids merging the result lists of two blocks.
+ * Elements are freed when being overwritten just like
+ * when the buffer overflows.
+ */
+ matched_num -= matched_total;
+ i = 0;
+
+ to_block = from_block;
}
- if (matched_ranges) {
+ *count += matched_total;
+ g_assert(*count < 0);
+ return TRUE;
+}
+
+static gboolean
+teco_do_search(GRegex *re, gsize from, gsize to, gint *count, GError **error)
+{
+ gboolean rc = *count >= 0 ? teco_do_search_forward(re, from, to, count, error)
+ : teco_do_search_backwards(re, from, to, count, error);
+ if (!rc)
+ return FALSE;
+
+ if (!*count) {
/* match success */
- teco_undo_ranges_own(teco_ranges) = matched_ranges;
- teco_undo_guint(teco_ranges_count) = num_ranges;
g_assert(teco_ranges_count > 0);
teco_interface_ssm(SCI_SETSEL, teco_ranges[0].from, teco_ranges[0].to);
@@ -685,6 +856,8 @@ teco_do_search(GRegex *re, gsize from, gsize to, gint *count, GError **error)
/*
* teco_get_ranges() returned byte positions,
* while everything else expects glyph offsets.
+ * They are fixed up only now to avoid unnecessary
+ * possibly costly teco_interface_bytes2glyphs() calls.
*/
for (guint i = 0; i < teco_ranges_count; i++) {
teco_ranges[i].from = teco_interface_bytes2glyphs(teco_ranges[i].from);
@@ -777,6 +950,7 @@ teco_state_search_process(teco_machine_main_t *ctx, teco_string_t str, gsize new
teco_ring_undo_edit();
if (count > 0) {
+ /* search forward */
do {
buffer = teco_buffer_next(buffer) ? : teco_ring_first();
teco_buffer_edit(buffer);
@@ -792,6 +966,7 @@ teco_state_search_process(teco_machine_main_t *ctx, teco_string_t str, gsize new
return FALSE;
} while (count);
} else /* count < 0 */ {
+ /* search backwards */
do {
buffer = teco_buffer_prev(buffer) ? : teco_ring_last();
teco_buffer_edit(buffer);
diff --git a/src/search.h b/src/search.h
index 9bd62f7..dc6413e 100644
--- a/src/search.h
+++ b/src/search.h
@@ -20,6 +20,8 @@
#include "parser.h"
+extern gsize teco_search_block_size;
+
void teco_state_control_search_mode(teco_machine_main_t *ctx, GError **error);
extern teco_state_t teco_state_search;
diff --git a/src/stdio-commands.c b/src/stdio-commands.c
index abb6566..c2a1c16 100644
--- a/src/stdio-commands.c
+++ b/src/stdio-commands.c
@@ -255,17 +255,42 @@ teco_state_print_string_initial(teco_machine_main_t *ctx, GError **error)
static teco_state_t *
teco_state_print_string_done(teco_machine_main_t *ctx, teco_string_t str, GError **error)
{
- teco_interface_msg_literal(TECO_MSG_USER, str.data, str.len);
+ teco_int_t type;
+
+ if (ctx->flags.mode > TECO_MODE_NORMAL)
+ return &teco_state_start;
+
+ if (!teco_expressions_pop_num_calc(&type, TECO_MSG_USER, error))
+ return NULL;
+ if (type < TECO_MSG_USER || type > TECO_MSG_MAX) {
+ g_set_error_literal(error, TECO_ERROR, TECO_ERROR_FAILED,
+ "Invalid message level specified for <^A>");
+ return NULL;
+ }
+
+ teco_interface_msg_literal(type, str.data, str.len);
return &teco_state_start;
}
/*$ "^A" ":^A" print "print string"
- * ^A<string>^A -- Print string as message
- * @^A/string/
- * :^A<string>^A
+ * [type]^A<string>^A -- Print string as message
+ * [type]@^A/string/
+ * [type]:^A<string>^A
*
- * Print <string> as a message, i.e. in the message line
- * in interactive mode and if possible on the terminal (stdout) as well.
+ * Print <string> as a message with severity <type>, i.e. in the
+ * message line in interactive mode and if possible on the terminal
+ * as well.
+ * The following message levels are supported:
+ * .IP 0: 3
+ * User-level message:
+ * They are written without any prefixes directly to stdout.
+ * This is the default if <type> is omitted.
+ * .IP 1:
+ * Info-level message
+ * .IP 2:
+ * Warnings
+ * .IP 3:
+ * Errors
*
* \fB^A\fP differs from all other commands in the way <string>
* is terminated.
diff --git a/src/view.c b/src/view.c
index a522d1c..8094186 100644
--- a/src/view.c
+++ b/src/view.c
@@ -109,12 +109,12 @@ teco_view_setup(teco_view_t *ctx)
teco_view_ssm(ctx, SCI_SETCARETSTYLE,
CARETSTYLE_BLOCK | CARETSTYLE_OVERSTRIKE_BLOCK | CARETSTYLE_BLOCK_AFTER, 0);
teco_view_ssm(ctx, SCI_SETCARETPERIOD, 0, 0);
- teco_view_ssm(ctx, SCI_SETCARETFORE, 0xFFFFFF, 0);
+ teco_view_ssm(ctx, SCI_SETCARETFORE, 0xC0C0C0, 0);
teco_view_ssm(ctx, SCI_SETSELFORE, TRUE, 0x000000);
- teco_view_ssm(ctx, SCI_SETSELBACK, TRUE, 0xFFFFFF);
+ teco_view_ssm(ctx, SCI_SETSELBACK, TRUE, 0xC0C0C0);
- teco_view_ssm(ctx, SCI_STYLESETFORE, STYLE_DEFAULT, 0xFFFFFF);
+ teco_view_ssm(ctx, SCI_STYLESETFORE, STYLE_DEFAULT, 0xC0C0C0);
teco_view_ssm(ctx, SCI_STYLESETBACK, STYLE_DEFAULT, 0x000000);
teco_view_ssm(ctx, SCI_STYLESETFONT, STYLE_DEFAULT, (sptr_t)"Monospace");
teco_view_ssm(ctx, SCI_STYLECLEARALL, 0, 0);
@@ -132,7 +132,7 @@ teco_view_setup(teco_view_t *ctx)
* default if no color-scheme is applied (and --no-profile).
*/
teco_view_ssm(ctx, SCI_STYLESETFORE, STYLE_CALLTIP, 0x000000);
- teco_view_ssm(ctx, SCI_STYLESETBACK, STYLE_CALLTIP, 0xFFFFFF);
+ teco_view_ssm(ctx, SCI_STYLESETBACK, STYLE_CALLTIP, 0xC0C0C0);
/*
* Since we have patched out Scintilla's original SetRepresentations(),
@@ -234,8 +234,8 @@ teco_view_load_from_channel(teco_view_t *ctx, GIOChannel *channel,
*/
guint cp = teco_view_get_codepage(ctx);
if (cp == SC_CP_UTF8)
- teco_interface_ssm(SCI_RELEASELINECHARACTERINDEX,
- SC_LINECHARACTERINDEX_UTF32, 0);
+ teco_view_ssm(ctx, SCI_RELEASELINECHARACTERINDEX,
+ SC_LINECHARACTERINDEX_UTF32, 0);
teco_view_ssm(ctx, SCI_BEGINUNDOACTION, 0, 0);
if (clear) {
@@ -314,8 +314,8 @@ cleanup:
teco_view_ssm(ctx, SCI_ENDUNDOACTION, 0, 0);
if (cp == SC_CP_UTF8)
- teco_interface_ssm(SCI_ALLOCATELINECHARACTERINDEX,
- SC_LINECHARACTERINDEX_UTF32, 0);
+ teco_view_ssm(ctx, SCI_ALLOCATELINECHARACTERINDEX,
+ SC_LINECHARACTERINDEX_UTF32, 0);
return ret;
}