aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/search.cpp
diff options
context:
space:
mode:
authorRobin Haberkorn <robin.haberkorn@googlemail.com>2021-05-30 02:38:43 +0200
committerRobin Haberkorn <robin.haberkorn@googlemail.com>2021-05-30 03:12:56 +0200
commit432ad24e382681f1c13b07e8486e91063dd96e2e (patch)
tree51838adac822767bd5884b9383cd4c72f29d3840 /src/search.cpp
parent524bc3960e6a6e5645ce904e20f72479e24e0a23 (diff)
downloadsciteco-432ad24e382681f1c13b07e8486e91063dd96e2e.tar.gz
THE GREAT CEEIFICATION EVENT
This is a total conversion of SciTECO to plain C (GNU C11). The chance was taken to improve a lot of internal datastructures, fix fundamental bugs and lay the foundations of future features. The GTK user interface is now in an useable state! All changes have been squashed together. The language itself has almost not changed at all, except for: * Detection of string terminators (usually Escape) now takes the string building characters into account. A string is only terminated outside of string building characters. In other words, you can now for instance write I^EQ[Hello$world]$ This removes one of the last bits of shellisms which is out of place in SciTECO where no tokenization/lexing is performed. Consequently, the current termination character can also be escaped using ^Q/^R. This is used by auto completions to make sure that strings are inserted verbatim and without unwanted sideeffects. * All strings can now safely contain null-characters (see also: 8-bit cleanliness). The null-character itself (^@) is not (yet) a valid SciTECO command, though. An incomplete list of changes: * We got rid of the BSD headers for RB trees and lists/queues. The problem with them was that they used a form of metaprogramming only to gain a bit of type safety. It also resulted in less readble code. This was a C++ desease. The new code avoids metaprogramming only to gain type safety. The BSD tree.h has been replaced by rb3ptr by Jens Stimpfle (https://github.com/jstimpfle/rb3ptr). This implementation is also more memory efficient than BSD's. The BSD list.h and queue.h has been replaced with a custom src/list.h. * Fixed crashes, performance issues and compatibility issues with the Gtk 3 User Interface. It is now more or less ready for general use. The GDK lock is no longer used to avoid using deprecated functions. On the downside, the new implementation (driving the Gtk event loop stepwise) is even slower than the old one. A few glitches remain (see TODO), but it is hoped that they will be resolved by the Scintilla update which will be performed soon. * A lot of program units have been split up, so they are shorter and easier to maintain: core-commands.c, qreg-commands.c, goto-commands.c, file-utils.h. * Parser states are simply structs of callbacks now. They still use a kind of polymorphy using a preprocessor trick. TECO_DEFINE_STATE() takes an initializer list that will be merged with the default list of field initializers. To "subclass" states, you can simply define new macros that add initializers to existing macros. * Parsers no longer have a "transitions" table but the input_cb() may use switch-case statements. There are also teco_machine_main_transition_t now which can be used to implement simple transitions. Additionally, you can specify functions to execute during transitions. This largely avoids long switch-case-statements. * Parsers are embeddable/reusable now, at least in parse-only mode. This does not currently bring any advantages but may later be used to write a Scintilla lexer for TECO syntax highlighting. Once parsers are fully embeddable, it will also be possible to run TECO macros in a kind of coroutine which would allow them to process string arguments in real time. * undo.[ch] still uses metaprogramming extensively but via the C preprocessor of course. On the downside, most undo token generators must be initiated explicitly (theoretically we could have used embedded functions / trampolines to instantiate automatically but this has turned out to be dangereous). There is a TECO_DEFINE_UNDO_CALL() to generate closures for arbitrary functions now (ie. to call an arbitrary function at undo-time). This simplified a lot of code and is much shorter than manually pushing undo tokens in many cases. * Instead of the ridiculous C++ Curiously Recurring Template Pattern to achieve static polymorphy for user interface implementations, we now simply declare all functions to implement in interface.h and link in the implementations. This is possible since we no longer hace to define interface subclasses (all state is static variables in the interface's *.c files). * Headers are now significantly shorter than in C++ since we can often hide more of our "class" implementations. * Memory counting is based on dlmalloc for most platforms now. Unfortunately, there is no malloc implementation that provides an efficient constant-time memory counter that is guaranteed to decrease when freeing memory. But since we use a defined malloc implementation now, malloc_usable_size() can be used safely for tracking memory use. malloc() replacement is very tricky on Windows, so we use a poll thread on Windows. This can also be enabled on other supported platforms using --disable-malloc-replacement. All in all, I'm still not pleased with the state of memory limiting. It is a mess. * Error handling uses GError now. This has the advantage that the GError codes can be reused once we support error catching in the SciTECO language. * Added a few more test suite cases. * Haiku is no longer supported as builds are instable and I did not manage to debug them - quite possibly Haiku bugs were responsible. * Glib v2.44 or later are now required. The GTK UI requires Gtk+ v3.12 or later now. The GtkFlowBox fallback and sciteco-wrapper workaround are no longer required. * We now extensively use the GCC/Clang-specific g_auto feature (automatic deallocations when leaving the current code block). * Updated copyright to 2021. SciTECO has been in continuous development, even though there have been no commits since 2018. * Since these changes are so significant, the target release has been set to v2.0. It is planned that beginning with v3.0, the language will be kept stable.
Diffstat (limited to 'src/search.cpp')
-rw-r--r--src/search.cpp946
1 files changed, 0 insertions, 946 deletions
diff --git a/src/search.cpp b/src/search.cpp
deleted file mode 100644
index 63c59e9..0000000
--- a/src/search.cpp
+++ /dev/null
@@ -1,946 +0,0 @@
-/*
- * Copyright (C) 2012-2017 Robin Haberkorn
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif
-
-#include <string.h>
-
-#include <glib.h>
-#include <glib/gprintf.h>
-
-#include "sciteco.h"
-#include "string-utils.h"
-#include "expressions.h"
-#include "undo.h"
-#include "qregisters.h"
-#include "ring.h"
-#include "parser.h"
-#include "search.h"
-#include "error.h"
-
-namespace SciTECO {
-
-namespace States {
- StateSearch search;
- StateSearchAll searchall;
- StateSearchKill searchkill;
- StateSearchDelete searchdelete;
-
- StateReplace replace;
- StateReplace_insert replace_insert;
- StateReplace_ignore replace_ignore;
-
- StateReplaceDefault replacedefault;
- StateReplaceDefault_insert replacedefault_insert;
- StateReplaceDefault_ignore replacedefault_ignore;
-}
-
-/*
- * Command states
- */
-
-/*$ S search pattern
- * S[pattern]$ -- Search for pattern
- * [n]S[pattern]$
- * -S[pattern]$
- * from,toS[pattern]$
- * :S[pattern]$ -> Success|Failure
- * [n]:S[pattern]$ -> Success|Failure
- * -:S[pattern]$ -> Success|Failure
- * from,to:S[pattern]$ -> Success|Failure
- *
- * Search for <pattern> in the current buffer/Q-Register.
- * Search order and range depends on the arguments given.
- * By default without any arguments, S will search forward
- * from dot till file end.
- * The optional single argument specifies the occurrence
- * to search (1 is the first occurrence, 2 the second, etc.).
- * Negative values for <n> perform backward searches.
- * If missing, the sign prefix is implied for <n>.
- * Therefore \(lq-S\(rq will search for the first occurrence
- * of <pattern> before dot.
- *
- * If two arguments are specified on the command,
- * search will be bounded in the character range <from> up to
- * <to>, and only the first occurrence will be searched.
- * <from> might be larger than <to> in which case a backward
- * search is performed in the selected range.
- *
- * After performing the search, the search <pattern> is saved
- * in the global search Q-Register \(lq_\(rq.
- * A success/failure condition boolean is saved in that
- * register's integer part.
- * <pattern> may be omitted in which case the pattern of
- * the last search or search and replace command will be
- * implied by using the contents of register \(lq_\(rq
- * (this could of course also be manually set).
- *
- * After a successful search, the pointer is positioned after
- * the found text in the buffer.
- * An unsuccessful search will display an error message but
- * not actually yield an error.
- * The message displaying is suppressed when executed from loops
- * and register \(lq_\(rq is the implied argument for break-commands
- * so that a search-break idiom can be implemented as follows:
- * .EX
- * <Sfoo$; ...>
- * .EE
- * Alternatively, S may be colon-modified in which case it returns
- * a condition boolean that may be directly evaluated by a
- * conditional or break-command.
- *
- * In interactive mode, searching will be performed immediately
- * (\(lqsearch as you type\(rq) highlighting matched text
- * on the fly.
- * Changing the <pattern> results in the search being reperformed
- * from the beginning.
- */
-void
-StateSearch::initial(void)
-{
- tecoInt v1, v2;
-
- undo.push_var(parameters);
-
- parameters.dot = interface.ssm(SCI_GETCURRENTPOS);
-
- v2 = expressions.pop_num_calc();
- if (expressions.args()) {
- /* TODO: optional count argument? */
- v1 = expressions.pop_num_calc();
- if (v1 <= v2) {
- parameters.count = 1;
- parameters.from = (gint)v1;
- parameters.to = (gint)v2;
- } else {
- parameters.count = -1;
- parameters.from = (gint)v2;
- parameters.to = (gint)v1;
- }
-
- if (!Validate::pos(parameters.from) ||
- !Validate::pos(parameters.to))
- throw RangeError("S");
- } else {
- parameters.count = (gint)v2;
- if (v2 >= 0) {
- parameters.from = parameters.dot;
- parameters.to = interface.ssm(SCI_GETLENGTH);
- } else {
- parameters.from = 0;
- parameters.to = parameters.dot;
- }
- }
-
- parameters.from_buffer = QRegisters::current ? NULL : ring.current;
- parameters.to_buffer = NULL;
-}
-
-static inline const gchar *
-regexp_escape_chr(gchar chr)
-{
- static gchar escaped[] = {'\\', '\0', '\0'};
-
- escaped[1] = chr;
- return g_ascii_isalnum(chr) ? escaped + 1 : escaped;
-}
-
-/**
- * Convert a SciTECO pattern character class to a regular
- * expression character class.
- * It will throw an error for wrong class specs (like invalid
- * Q-Registers) but not incomplete ones.
- *
- * @param state Initial pattern converter state.
- * May be modified on return, e.g. when ^E has been
- * scanned without a valid class.
- * @param pattern The character class definition to convert.
- * This may point into a longer pattern.
- * The pointer is modified and always left after
- * the last character used, so it may point to the
- * terminating null byte after the call.
- * @param escape_default Whether to treat single characters
- * as classes or not.
- * @return A regular expression string or NULL for incomplete
- * class specifications. When a string is returned, the
- * state has always been reset to STATE_START.
- * Must be freed with g_free().
- */
-gchar *
-StateSearch::class2regexp(MatchState &state, const gchar *&pattern,
- bool escape_default)
-{
- while (*pattern) {
- QRegister *reg;
- gchar *temp, *temp2;
-
- switch (state) {
- case STATE_START:
- switch (*pattern) {
- case CTL_KEY('S'):
- pattern++;
- return g_strdup("[:^alnum:]");
- case CTL_KEY('E'):
- state = STATE_CTL_E;
- break;
- default:
- /*
- * Either a single character "class" or
- * not a valid class at all.
- */
- return escape_default ? g_strdup(regexp_escape_chr(*pattern++))
- : NULL;
- }
- break;
-
- case STATE_CTL_E:
- switch (String::toupper(*pattern)) {
- case 'A':
- pattern++;
- state = STATE_START;
- return g_strdup("[:alpha:]");
- /* same as <CTRL/S> */
- case 'B':
- pattern++;
- state = STATE_START;
- return g_strdup("[:^alnum:]");
- case 'C':
- pattern++;
- state = STATE_START;
- return g_strdup("[:alnum:].$");
- case 'D':
- pattern++;
- state = STATE_START;
- return g_strdup("[:digit:]");
- case 'G':
- state = STATE_ANYQ;
- break;
- case 'L':
- pattern++;
- state = STATE_START;
- return g_strdup("\r\n\v\f");
- case 'R':
- pattern++;
- state = STATE_START;
- return g_strdup("[:alnum:]");
- case 'V':
- pattern++;
- state = STATE_START;
- return g_strdup("[:lower:]");
- case 'W':
- pattern++;
- state = STATE_START;
- return g_strdup("[:upper:]");
- default:
- /*
- * Not a valid ^E class, but could still
- * be a higher-level ^E pattern.
- */
- return NULL;
- }
- break;
-
- case STATE_ANYQ:
- /* will throw an error for invalid specs */
- if (!qreg_machine.input(*pattern, reg))
- /* incomplete, but consume byte */
- break;
- qreg_machine.reset();
-
- temp = reg->get_string();
- temp2 = g_regex_escape_string(temp, -1);
- g_free(temp);
-
- pattern++;
- state = STATE_START;
- return temp2;
-
- default:
- /*
- * Not a class, but could still be any other
- * high-level pattern.
- */
- return NULL;
- }
-
- pattern++;
- }
-
- /*
- * End of string. May happen for empty strings but also
- * incomplete ^E or ^EG classes.
- */
- return NULL;
-}
-
-/**
- * Convert SciTECO pattern to regular expression.
- * It will throw an error for definitely wrong pattern constructs
- * but not for incomplete patterns (a necessity of search-as-you-type).
- *
- * @bug Incomplete patterns after a pattern has been closed (i.e. its
- * string argument) are currently not reported as errors.
- *
- * @param pattern The pattern to scan through.
- * Modifies the pointer to point after the last
- * successfully scanned character, so it can be
- * called recursively. It may also point to the
- * terminating null byte after the call.
- * @param single_expr Whether to scan a single pattern
- * expression or an arbitrary sequence.
- * @return The regular expression string or NULL.
- * Must be freed with g_free().
- */
-gchar *
-StateSearch::pattern2regexp(const gchar *&pattern,
- bool single_expr)
-{
- MatchState state = STATE_START;
- gchar *re = NULL;
-
- do {
- gchar *temp;
-
- /*
- * First check whether it is a class.
- * This will not treat individual characters
- * as classes, so we do not convert them to regexp
- * classes unnecessarily.
- * NOTE: This might throw an error.
- */
- try {
- temp = class2regexp(state, pattern);
- } catch (...) {
- g_free(re);
- throw;
- }
- if (temp) {
- g_assert(state == STATE_START);
-
- String::append(re, "[");
- String::append(re, temp);
- String::append(re, "]");
- g_free(temp);
-
- /* class2regexp() already consumed all the bytes */
- continue;
- }
-
- if (!*pattern)
- /* end of pattern */
- break;
-
- switch (state) {
- case STATE_START:
- switch (*pattern) {
- case CTL_KEY('X'): String::append(re, "."); break;
- case CTL_KEY('N'): state = STATE_NOT; break;
- default:
- String::append(re, regexp_escape_chr(*pattern));
- }
- break;
-
- case STATE_NOT:
- state = STATE_START;
- try {
- temp = class2regexp(state, pattern, true);
- } catch (...) {
- g_free(re);
- throw;
- }
- if (!temp)
- /* a complete class is strictly required */
- goto incomplete;
- g_assert(state == STATE_START);
-
- String::append(re, "[^");
- String::append(re, temp);
- String::append(re, "]");
- g_free(temp);
-
- /* class2regexp() already consumed all the bytes */
- continue;
-
- case STATE_CTL_E:
- state = STATE_START;
- switch (String::toupper(*pattern)) {
- case 'M': state = STATE_MANY; break;
- case 'S': String::append(re, "\\s+"); break;
- /* same as <CTRL/X> */
- case 'X': String::append(re, "."); break;
- /* TODO: ASCII octal code!? */
- case '[':
- String::append(re, "(");
- state = STATE_ALT;
- break;
- default:
- g_free(re);
- throw SyntaxError(*pattern);
- }
- break;
-
- case STATE_MANY:
- /* consume exactly one pattern element */
- try {
- temp = pattern2regexp(pattern, true);
- } catch (...) {
- g_free(re);
- throw;
- }
- if (!temp)
- /* a complete expression is strictly required */
- goto incomplete;
-
- String::append(re, "(");
- String::append(re, temp);
- String::append(re, ")+");
- g_free(temp);
- state = STATE_START;
-
- /* pattern2regexp() already consumed all the bytes */
- continue;
-
- case STATE_ALT:
- switch (*pattern) {
- case ',':
- String::append(re, "|");
- break;
- case ']':
- String::append(re, ")");
- state = STATE_START;
- break;
- default:
- try {
- temp = pattern2regexp(pattern, true);
- } catch (...) {
- g_free(re);
- throw;
- }
- if (!temp)
- /* a complete expression is strictly required */
- goto incomplete;
-
- String::append(re, temp);
- g_free(temp);
-
- /* pattern2regexp() already consumed all the bytes */
- continue;
- }
- break;
-
- default:
- /* shouldn't happen */
- g_assert_not_reached();
- }
-
- pattern++;
- } while (!single_expr || state != STATE_START);
-
- /*
- * Complete open alternative.
- * This could be handled like an incomplete expression,
- * but closing it automatically improved search-as-you-type.
- */
- if (state == STATE_ALT)
- String::append(re, ")");
-
- return re;
-
-incomplete:
- g_free(re);
- return NULL;
-}
-
-void
-StateSearch::do_search(GRegex *re, gint from, gint to, gint &count)
-{
- GMatchInfo *info;
- const gchar *buffer;
-
- gint matched_from = -1, matched_to = -1;
-
- buffer = (const gchar *)interface.ssm(SCI_GETCHARACTERPOINTER);
- g_regex_match_full(re, buffer, (gssize)to, from,
- (GRegexMatchFlags)0, &info, NULL);
-
- if (count >= 0) {
- 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 {
- /* only keep the last `count' matches, in a circular stack */
- struct Range {
- gint from, to;
- };
- Range *matched = new Range[-count];
- gint matched_total = 0, 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 = ++matched_total % -count;
- }
-
- count = MIN(count + matched_total, 0);
- if (!count) {
- /* successful, i points to stack bottom */
- matched_from = matched[i].from;
- matched_to = matched[i].to;
- }
-
- delete[] matched;
- }
-
- g_match_info_free(info);
-
- if (matched_from >= 0 && matched_to >= 0)
- /* match success */
- interface.ssm(SCI_SETSEL, matched_from, matched_to);
-}
-
-void
-StateSearch::process(const gchar *str, gint new_chars)
-{
- static const gint flags = G_REGEX_CASELESS | G_REGEX_MULTILINE |
- G_REGEX_DOTALL | G_REGEX_RAW;
-
- QRegister *search_reg = QRegisters::globals["_"];
-
- gchar *re_pattern;
- GRegex *re;
-
- gint count = parameters.count;
-
- if (current_doc_must_undo())
- interface.undo_ssm(SCI_SETSEL,
- interface.ssm(SCI_GETANCHOR),
- interface.ssm(SCI_GETCURRENTPOS));
-
- search_reg->undo_set_integer();
- search_reg->set_integer(FAILURE);
-
- /*
- * NOTE: pattern2regexp() modifies str pointer and may throw
- */
- re_pattern = pattern2regexp(str);
- qreg_machine.reset();
-#ifdef DEBUG
- g_printf("REGEXP: %s\n", re_pattern);
-#endif
- if (!re_pattern)
- goto failure;
- re = g_regex_new(re_pattern, (GRegexCompileFlags)flags,
- (GRegexMatchFlags)0, NULL);
- g_free(re_pattern);
- if (!re)
- goto failure;
-
- if (!QRegisters::current &&
- ring.current != parameters.from_buffer) {
- ring.undo_edit();
- parameters.from_buffer->edit();
- }
-
- do_search(re, parameters.from, parameters.to, count);
-
- if (parameters.to_buffer && count) {
- Buffer *buffer = parameters.from_buffer;
-
- if (ring.current == buffer)
- ring.undo_edit();
-
- if (count > 0) {
- do {
- buffer = buffer->next() ? : ring.first();
- buffer->edit();
-
- if (buffer == parameters.to_buffer) {
- do_search(re, 0, parameters.dot, count);
- break;
- }
-
- do_search(re, 0, interface.ssm(SCI_GETLENGTH),
- count);
- } while (count);
- } else /* count < 0 */ {
- do {
- buffer = buffer->prev() ? : ring.last();
- buffer->edit();
-
- if (buffer == parameters.to_buffer) {
- do_search(re, parameters.dot,
- interface.ssm(SCI_GETLENGTH),
- count);
- break;
- }
-
- do_search(re, 0, interface.ssm(SCI_GETLENGTH),
- count);
- } while (count);
- }
-
- ring.current = buffer;
- }
-
- search_reg->set_integer(TECO_BOOL(!count));
-
- g_regex_unref(re);
-
- if (!count)
- return;
-
-failure:
- interface.ssm(SCI_GOTOPOS, parameters.dot);
-}
-
-State *
-StateSearch::done(const gchar *str)
-{
- BEGIN_EXEC(&States::start);
-
- QRegister *search_reg = QRegisters::globals["_"];
-
- if (*str) {
- /* workaround: preserve selection (also on rubout) */
- gint anchor = interface.ssm(SCI_GETANCHOR);
- if (current_doc_must_undo())
- interface.undo_ssm(SCI_SETANCHOR, anchor);
-
- search_reg->undo_set_string();
- search_reg->set_string(str);
-
- interface.ssm(SCI_SETANCHOR, anchor);
- } else {
- gchar *search_str = search_reg->get_string();
- process(search_str, 0 /* unused */);
- g_free(search_str);
- }
-
- if (eval_colon())
- expressions.push(search_reg->get_integer());
- else if (IS_FAILURE(search_reg->get_integer()) &&
- !loop_stack.items() /* not in loop */)
- interface.msg(InterfaceCurrent::MSG_ERROR, "Search string not found!");
-
- return &States::start;
-}
-
-/*$ N
- * [n]N[pattern]$ -- Search over buffer-boundaries
- * -N[pattern]$
- * from,toN[pattern]$
- * [n]:N[pattern]$ -> Success|Failure
- * -:N[pattern]$ -> Success|Failure
- * from,to:N[pattern]$ -> Success|Failure
- *
- * Search for <pattern> over buffer boundaries.
- * This command is similar to the regular search command
- * (S) but will continue to search for occurrences of
- * pattern when the end or beginning of the current buffer
- * is reached.
- * Occurrences of <pattern> spanning over buffer boundaries
- * will not be found.
- * When searching forward N will start in the current buffer
- * at dot, continue with the next buffer in the ring searching
- * the entire buffer until it reaches the end of the buffer
- * ring, continue with the first buffer in the ring until
- * reaching the current file again where it searched from the
- * beginning of the buffer up to its current dot.
- * Searching backwards does the reverse.
- *
- * N also differs from S in the interpretation of two arguments.
- * Using two arguments the search will be bounded between the
- * buffer with number <from>, up to the buffer with number
- * <to>.
- * When specifying buffer ranges, the entire buffers are searched
- * from beginning to end.
- * <from> may be greater than <to> in which case, searching starts
- * at the end of buffer <from> and continues backwards until the
- * beginning of buffer <to> has been reached.
- * Furthermore as with all buffer numbers, the buffer ring
- * is considered a circular structure and it is possible
- * to search over buffer ring boundaries by specifying
- * buffer numbers greater than the number of buffers in the
- * ring.
- */
-void
-StateSearchAll::initial(void)
-{
- tecoInt v1, v2;
-
- undo.push_var(parameters);
-
- parameters.dot = interface.ssm(SCI_GETCURRENTPOS);
-
- v2 = expressions.pop_num_calc();
- if (expressions.args()) {
- /* TODO: optional count argument? */
- v1 = expressions.pop_num_calc();
- if (v1 <= v2) {
- parameters.count = 1;
- parameters.from_buffer = ring.find(v1);
- parameters.to_buffer = ring.find(v2);
- } else {
- parameters.count = -1;
- parameters.from_buffer = ring.find(v2);
- parameters.to_buffer = ring.find(v1);
- }
-
- if (!parameters.from_buffer || !parameters.to_buffer)
- throw RangeError("N");
- } else {
- parameters.count = (gint)v2;
- /* NOTE: on Q-Registers, behave like "S" */
- if (QRegisters::current)
- parameters.from_buffer = parameters.to_buffer = NULL;
- else
- parameters.from_buffer = parameters.to_buffer = ring.current;
- }
-
- if (parameters.count >= 0) {
- parameters.from = parameters.dot;
- parameters.to = interface.ssm(SCI_GETLENGTH);
- } else {
- parameters.from = 0;
- parameters.to = parameters.dot;
- }
-}
-
-State *
-StateSearchAll::done(const gchar *str)
-{
- BEGIN_EXEC(&States::start);
-
- StateSearch::done(str);
- QRegisters::hook(QRegisters::HOOK_EDIT);
-
- return &States::start;
-}
-
-/*$ FK
- * FK[pattern]$ -- Delete up to occurrence of pattern
- * [n]FK[pattern]$
- * -FK[pattern]$
- * from,toFK[pattern]$
- * :FK[pattern]$ -> Success|Failure
- * [n]:FK[pattern]$ -> Success|Failure
- * -:FK[pattern]$ -> Success|Failure
- * from,to:FK[pattern]$ -> Success|Failure
- *
- * FK searches for <pattern> just like the regular search
- * command (S) but when found deletes all text from dot
- * up to but not including the found text instance.
- * When searching backwards the characters beginning after
- * the occurrence of <pattern> up to dot are deleted.
- *
- * In interactive mode, deletion is not performed
- * as-you-type but only on command termination.
- */
-State *
-StateSearchKill::done(const gchar *str)
-{
- gint dot;
-
- BEGIN_EXEC(&States::start);
-
- QRegister *search_reg = QRegisters::globals["_"];
-
- StateSearch::done(str);
-
- if (IS_FAILURE(search_reg->get_integer()))
- return &States::start;
-
- dot = interface.ssm(SCI_GETCURRENTPOS);
-
- interface.ssm(SCI_BEGINUNDOACTION);
- if (parameters.dot < dot) {
- /* kill forwards */
- gint anchor = interface.ssm(SCI_GETANCHOR);
-
- if (current_doc_must_undo())
- interface.undo_ssm(SCI_GOTOPOS, dot);
- interface.ssm(SCI_GOTOPOS, anchor);
-
- interface.ssm(SCI_DELETERANGE,
- parameters.dot, anchor - parameters.dot);
- } else {
- /* kill backwards */
- interface.ssm(SCI_DELETERANGE, dot, parameters.dot - dot);
- }
- interface.ssm(SCI_ENDUNDOACTION);
- ring.dirtify();
-
- if (current_doc_must_undo())
- interface.undo_ssm(SCI_UNDO);
-
- return &States::start;
-}
-
-/*$ FD
- * FD[pattern]$ -- Delete occurrence of pattern
- * [n]FD[pattern]$
- * -FD[pattern]$
- * from,toFD[pattern]$
- * :FD[pattern]$ -> Success|Failure
- * [n]:FD[pattern]$ -> Success|Failure
- * -:FD[pattern]$ -> Success|Failure
- * from,to:FD[pattern]$ -> Success|Failure
- *
- * Searches for <pattern> just like the regular search command
- * (S) but when found deletes the entire occurrence.
- */
-State *
-StateSearchDelete::done(const gchar *str)
-{
- BEGIN_EXEC(&States::start);
-
- QRegister *search_reg = QRegisters::globals["_"];
-
- StateSearch::done(str);
-
- if (IS_SUCCESS(search_reg->get_integer())) {
- interface.ssm(SCI_BEGINUNDOACTION);
- interface.ssm(SCI_REPLACESEL, 0, (sptr_t)"");
- interface.ssm(SCI_ENDUNDOACTION);
- ring.dirtify();
-
- if (current_doc_must_undo())
- interface.undo_ssm(SCI_UNDO);
- }
-
- return &States::start;
-}
-
-/*$ FS
- * FS[pattern]$[string]$ -- Search and replace
- * [n]FS[pattern]$[string]$
- * -FS[pattern]$[string]$
- * from,toFS[pattern]$[string]$
- * :FS[pattern]$[string]$ -> Success|Failure
- * [n]:FS[pattern]$[string]$ -> Success|Failure
- * -:FS[pattern]$[string]$ -> Success|Failure
- * from,to:FS[pattern]$[string]$ -> Success|Failure
- *
- * Search for <pattern> just like the regular search command
- * (S) does but replace it with <string> if found.
- * If <string> is empty, the occurrence will always be
- * deleted so \(lqFS[pattern]$$\(rq is similar to
- * \(lqFD[pattern]$\(rq.
- * The global replace register is \fBnot\fP touched
- * by the FS command.
- *
- * In interactive mode, the replacement will be performed
- * immediately and interactively.
- */
-State *
-StateReplace::done(const gchar *str)
-{
- BEGIN_EXEC(&States::replace_ignore);
-
- QRegister *search_reg = QRegisters::globals["_"];
-
- StateSearchDelete::done(str);
-
- return IS_SUCCESS(search_reg->get_integer())
- ? (State *)&States::replace_insert
- : (State *)&States::replace_ignore;
-}
-
-State *
-StateReplace_ignore::done(const gchar *str)
-{
- return &States::start;
-}
-
-/*$ FR
- * FR[pattern]$[string]$ -- Search and replace with default
- * [n]FR[pattern]$[string]$
- * -FR[pattern]$[string]$
- * from,toFR[pattern]$[string]$
- * :FR[pattern]$[string]$ -> Success|Failure
- * [n]:FR[pattern]$[string]$ -> Success|Failure
- * -:FR[pattern]$[string]$ -> Success|Failure
- * from,to:FR[pattern]$[string]$ -> Success|Failure
- *
- * The FR command is similar to the FS command.
- * It searches for <pattern> just like the regular search
- * command (S) and replaces the occurrence with <string>
- * similar to what FS does.
- * It differs from FS in the fact that the replacement
- * string is saved in the global replacement register
- * \(lq-\(rq.
- * If <string> is empty the string in the global replacement
- * register is implied instead.
- */
-State *
-StateReplaceDefault::done(const gchar *str)
-{
- BEGIN_EXEC(&States::replacedefault_ignore);
-
- QRegister *search_reg = QRegisters::globals["_"];
-
- StateSearchDelete::done(str);
-
- return IS_SUCCESS(search_reg->get_integer())
- ? (State *)&States::replacedefault_insert
- : (State *)&States::replacedefault_ignore;
-}
-
-State *
-StateReplaceDefault_insert::done(const gchar *str)
-{
- BEGIN_EXEC(&States::start);
-
- QRegister *replace_reg = QRegisters::globals["-"];
-
- if (*str) {
- replace_reg->undo_set_string();
- replace_reg->set_string(str);
- } else {
- gchar *replace_str = replace_reg->get_string();
- StateInsert::process(replace_str, strlen(replace_str));
- g_free(replace_str);
- }
-
- return &States::start;
-}
-
-State *
-StateReplaceDefault_ignore::done(const gchar *str)
-{
- BEGIN_EXEC(&States::start);
-
- if (*str) {
- QRegister *replace_reg = QRegisters::globals["-"];
-
- replace_reg->undo_set_string();
- replace_reg->set_string(str);
- }
-
- return &States::start;
-}
-
-} /* namespace SciTECO */