diff options
Diffstat (limited to 'src/cmdline.c')
| -rw-r--r-- | src/cmdline.c | 257 |
1 files changed, 181 insertions, 76 deletions
diff --git a/src/cmdline.c b/src/cmdline.c index 089bd7a..53436fe 100644 --- a/src/cmdline.c +++ b/src/cmdline.c @@ -38,6 +38,7 @@ #include "file-utils.h" #include "interface.h" #include "view.h" +#include "doc.h" #include "expressions.h" #include "parser.h" #include "core-commands.h" @@ -61,11 +62,86 @@ int malloc_trim(size_t pad); #define TECO_DEFAULT_BREAK_CHARS " \t\v\r\n\f<>,;@" +/** Style used for the asterisk at the beginning of the command line */ +#define STYLE_ASTERISK 64 + teco_cmdline_t teco_cmdline = {}; -/** Last terminated command line */ +/* + * FIXME: Should perhaps be in doc.h. + * But which view should it be called on? + */ +static inline void +teco_doc_scintilla_unref(teco_doc_scintilla_t *doc) +{ + if (doc) + teco_cmdline_ssm(SCI_RELEASEDOCUMENT, 0, (sptr_t)doc); +} + +G_DEFINE_AUTOPTR_CLEANUP_FUNC(teco_doc_scintilla_t, teco_doc_scintilla_unref); + +/** + * 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_HIDDEN, 0); + /* render tabs as "TAB" */ + teco_cmdline_ssm(SCI_SETTABDRAWMODE, SCTD_CONTROLCHAR, 0); + teco_cmdline_ssm(SCI_SETTABWIDTH, 1, 0); + teco_cmdline_ssm(SCI_SETTABMINIMUMWIDTH, + teco_cmdline_ssm(SCI_TEXTWIDTH, STYLE_DEFAULT, (sptr_t)"TAB"), 0); + + /* + * FIXME: Something resets the margin text, so we have to set it last. + */ + teco_cmdline_ssm(SCI_MARGINSETTEXT, 0, (sptr_t)"*"); +} + +static void +teco_cmdline_update(void) +{ + /* + * Update the command line indicators. + * 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)"*"); +} + /** * Insert string into command line and execute * it immediately. @@ -80,43 +156,52 @@ static gboolean teco_cmdline_insert(const gchar *data, gsize len, GError **error) { const teco_string_t src = {(gchar *)data, len}; - g_auto(teco_string_t) old_cmdline = {NULL, 0}; + g_autoptr(teco_doc_scintilla_t) old_cmdline = NULL; 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; - 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; + const gchar *macro = (const gchar *)teco_cmdline_ssm(SCI_GETCHARACTERPOINTER, 0, 0); + gsize macro_len = teco_cmdline_ssm(SCI_GETLENGTH, 0, 0); + + 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,14 +212,18 @@ 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); - g_assert(old_cmdline.len == 0); - old_cmdline = teco_cmdline.str; - teco_cmdline.str = new_cmdline; - teco_cmdline.effective_len = new_cmdline.len; + //g_assert(old_cmdline == NULL); + old_cmdline = (teco_doc_scintilla_t *)teco_cmdline_ssm(SCI_GETDOCPOINTER, 0, 0); + teco_cmdline_ssm(SCI_ADDREFDOCUMENT, 0, (sptr_t)old_cmdline); + /* create new document in the command line view */ + teco_cmdline_ssm(SCI_SETDOCPOINTER, 0, 0); + teco_cmdline_ssm(SCI_ADDTEXT, new_cmdline.len, (sptr_t)new_cmdline.data); + 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; @@ -144,7 +233,7 @@ teco_cmdline_insert(const gchar *data, gsize len, GError **error) teco_error_add_frame_toplevel(); teco_error_display_short(tmp_error); - if (old_cmdline.len > 0) { + if (old_cmdline) { /* * Error during command-line replacement. * Replay previous command-line. @@ -152,13 +241,16 @@ teco_cmdline_insert(const gchar *data, gsize len, GError **error) */ teco_undo_pop(repl_pc); - teco_string_clear(&teco_cmdline.str); - teco_cmdline.str = old_cmdline; - memset(&old_cmdline, 0, sizeof(old_cmdline)); + teco_cmdline_ssm(SCI_SETDOCPOINTER, 0, (sptr_t)old_cmdline); + old_cmdline = NULL; teco_cmdline.machine.macro_pc = teco_cmdline.pc = repl_pc; /* rubout cmdline replacement command */ - teco_cmdline.effective_len--; + 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 +269,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 +315,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 +342,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 +393,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 +417,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 +475,19 @@ 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 +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 +545,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 +606,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 +618,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 +627,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 +640,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 +655,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 +670,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 +690,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 +712,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 +728,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 +751,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 +929,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 +952,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; |
