diff options
-rw-r--r-- | parser.cpp | 133 | ||||
-rw-r--r-- | parser.h | 14 | ||||
-rw-r--r-- | qbuffers.cpp | 3 | ||||
-rw-r--r-- | sciteco.h | 6 |
4 files changed, 153 insertions, 3 deletions
@@ -22,6 +22,7 @@ namespace States { StateCondCommand condcommand; StateECommand ecommand; StateInsert insert; + StateSearch search; State *current = &start; } @@ -332,6 +333,7 @@ StateStart::StateStart() : State() transitions['"'] = &States::condcommand; transitions['E'] = &States::ecommand; transitions['I'] = &States::insert; + transitions['S'] = &States::search; transitions['Q'] = &States::getqreginteger; transitions['U'] = &States::setqreginteger; transitions['%'] = &States::increaseqreg; @@ -507,13 +509,14 @@ StateStart::custom(gchar chr) break; case ';': { + gint64 v; BEGIN_EXEC(this); - /* TODO: search Q-reg */ - gint64 v = expressions.pop_num_calc(); + + v = expressions.pop_num_calc(1, qregisters["_"]->integer); if (eval_colon()) v = ~v; - if (v >= 0) { + if (IS_FAILURE(v)) { expressions.discard_args(); g_assert(expressions.pop_op() == Expressions::OP_LOOP); expressions.pop_num(); /* pc */ @@ -954,3 +957,127 @@ StateInsert::done(const gchar *str __attribute__((unused))) /* nothing to be done when done */ return &States::start; } + +void +StateSearch::initial(void) +{ + gint64 v; + + undo.push_var<Parameters>(parameters); + + parameters.dot = editor_msg(SCI_GETCURRENTPOS); + v = expressions.pop_num_calc(); + if (expressions.args()) { + /* TODO: optional count argument? */ + parameters.count = 1; + parameters.from = (gint)v; + parameters.to = (gint)expressions.pop_num_calc(); + } else { + parameters.count = (gint)v; + if (v >= 0) { + parameters.from = parameters.dot; + parameters.to = editor_msg(SCI_GETLENGTH); + } else { + parameters.from = 0; + parameters.to = parameters.dot; + } + } +} + +void +StateSearch::process(const gchar *str, gint new_chars __attribute__((unused))) +{ + static const gint flags = G_REGEX_CASELESS | G_REGEX_MULTILINE | + G_REGEX_DOTALL | G_REGEX_RAW; + + QRegister *search_reg = qregisters["_"]; + GRegex *re; + GMatchInfo *info; + const gchar *buffer; + + gint matched_from = -1, matched_to = -1; + + undo.push_var<gint64>(search_reg->integer); + + re = g_regex_new(str, (GRegexCompileFlags)flags, + (GRegexMatchFlags)0, NULL); + if (!re) { + search_reg->integer = FAILURE; + return; + } + + buffer = (const gchar *)editor_msg(SCI_GETCHARACTERPOINTER); + g_regex_match_full(re, buffer, (gssize)parameters.to, parameters.from, + (GRegexMatchFlags)0, &info, NULL); + + if (parameters.count >= 0) { + gint count = parameters.count; + + while (g_match_info_matches(info) && --count) + g_match_info_next(info, NULL); + + if (!count) + /* successful */ + g_match_info_fetch_pos(info, 0, + &matched_from, &matched_to); + } else { + /* + * FIXME: use queue, or self-expanding stack + */ + struct Range { + gint from, to; + }; + /* at most one match per character */ + Range matched[parameters.to - parameters.from]; + gint i = 0; + + while (g_match_info_matches(info)) { + g_match_info_fetch_pos(info, 0, + &matched[i].from, + &matched[i].to); + + g_match_info_next(info, NULL); + i++; + } + + if (i) { + /* successful */ + matched_from = matched[i + parameters.count].from; + matched_to = matched[i + parameters.count].to; + } + } + + g_match_info_free(info); + + if (matched_from >= 0 && matched_to >= 0) { + /* match success */ + search_reg->integer = SUCCESS; + + undo.push_msg(SCI_GOTOPOS, editor_msg(SCI_GETCURRENTPOS)); + editor_msg(SCI_SETSEL, matched_from, matched_to); + } else { + search_reg->integer = FAILURE; + } + + g_regex_unref(re); +} + +State * +StateSearch::done(const gchar *str) +{ + BEGIN_EXEC(&States::start); + + QRegister *search_reg = qregisters["_"]; + + if (*str) { + undo.push_var<gint>(search_reg->dot); + undo.push_msg(SCI_UNDO); + search_reg->set_string(str); + } else { + gchar *search_str = search_reg->get_string(); + process(search_str, 0); + g_free(search_str); + } + + return &States::start; +} @@ -151,6 +151,19 @@ private: State *done(const gchar *str); }; +class StateSearch : public StateExpectString { +private: + struct Parameters { + gint dot; + gint from, to; + gint count; + } parameters; + + void initial(void); + void process(const gchar *str, gint new_chars); + State *done(const gchar *str); +}; + extern gint macro_pc; namespace States { @@ -160,6 +173,7 @@ namespace States { extern StateCondCommand condcommand; extern StateECommand ecommand; extern StateInsert insert; + extern StateSearch search; extern State *current; } diff --git a/qbuffers.cpp b/qbuffers.cpp index c649fce..a2fed34 100644 --- a/qbuffers.cpp +++ b/qbuffers.cpp @@ -119,6 +119,9 @@ QRegisterTable::initialize(void) initialize_register((gchar []){q, '\0'}); for (gchar q = '0'; q <= '9'; q++) initialize_register((gchar []){q, '\0'}); + + /* search string and status register */ + initialize_register("_"); } void @@ -26,6 +26,12 @@ sptr_t editor_msg(unsigned int iMessage, uptr_t wParam = 0, sptr_t lParam = 0); #define CTL_ECHO(C) ((C) | 0x40) #define CTL_KEY(C) ((C) & ~0x40) +#define SUCCESS (-1) +#define FAILURE (0) + +#define IS_SUCCESS(X) ((X) < 0) +#define IS_FAILURE(X) (!IS_SUCCESS(X)) + /* TECO uses only lower 7 bits for commands */ #define MAX_TRANSITIONS 127 |