Skip to content

Commit

Permalink
Various find & replace improvements
Browse files Browse the repository at this point in the history
 - Can now enter escape sequences (\n,\t,\x..) into find and replace text.
 - Can now enter regex backreferences using two formats:
   \n or \{n} where n >= 0 e.g. this allows for replace text such as 1\{2}3
 - Can now escape regex backreferences e.g. \\2 results in \2
  • Loading branch information
rgburke committed Jan 6, 2016
1 parent 3171015 commit 8c70586
Show file tree
Hide file tree
Showing 11 changed files with 428 additions and 94 deletions.
7 changes: 2 additions & 5 deletions buffer.c
Original file line number Diff line number Diff line change
Expand Up @@ -1264,7 +1264,8 @@ Status bf_replace_string(Buffer *buffer, size_t replace_length,

if (STATUS_IS_SUCCESS(status)) {
buffer->is_dirty = 1;
status = bf_insert_string(buffer, string, string_length, 1);
status = bf_insert_string(buffer, string, string_length,
advance_cursor);
}

if (!grouped_changes_started) {
Expand All @@ -1275,10 +1276,6 @@ Status bf_replace_string(Buffer *buffer, size_t replace_length,
return status;
}

if (advance_cursor) {
bp_advance_to_offset(&buffer->pos, gb_get_point(buffer->data));
}

return status;
}

Expand Down
78 changes: 63 additions & 15 deletions command.c
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ static Status cm_buffer_save_as(const CommandArgs *);
static Status cm_save_file_prompt(Session *, char **file_path_ptr);
static void cm_generate_find_prompt(const BufferSearch *,
char prompt_text[MAX_CMD_PROMPT_LENGTH]);
static Status cm_prerpare_search(Session *, const BufferPos *start_pos);
static Status cm_prepare_search(Session *, const BufferPos *start_pos);
static Status cm_buffer_find(const CommandArgs *);
static Status cm_buffer_find_next(const CommandArgs *);
static Status cm_buffer_toggle_search_direction(const CommandArgs *);
Expand Down Expand Up @@ -707,7 +707,7 @@ static void cm_generate_find_prompt(const BufferSearch *search,
direction, case_sensitive);
}

static Status cm_prerpare_search(Session *sess, const BufferPos *start_pos)
static Status cm_prepare_search(Session *sess, const BufferPos *start_pos)
{
Buffer *buffer = sess->active_buffer;

Expand Down Expand Up @@ -737,14 +737,36 @@ static Status cm_prerpare_search(Session *sess, const BufferPos *start_pos)
return status;
}

return bs_reinit(&buffer->search, start_pos, pattern, strlen(pattern));
size_t pattern_len = strlen(pattern);

if (buffer->search.search_type == BST_TEXT) {
char *processed_pattern = su_process_string(
pattern, pattern_len,
buffer->file_format == FF_WINDOWS,
&pattern_len);

if (processed_pattern == NULL) {
return st_get_error(ERR_OUT_OF_MEMORY, "Out Of Memory - "
"Unable to process input");
} else {
pattern = processed_pattern;
}
}

status = bs_reinit(&buffer->search, start_pos, pattern, pattern_len);

if (buffer->search.search_type == BST_TEXT) {
free(pattern);
}

return status;
}

static Status cm_buffer_find(const CommandArgs *cmd_args)
{
Session *sess = cmd_args->sess;

RETURN_IF_FAIL(cm_prerpare_search(sess, NULL));
RETURN_IF_FAIL(cm_prepare_search(sess, NULL));

if (pr_prompt_cancelled(sess->prompt)) {
return STATUS_SUCCESS;
Expand Down Expand Up @@ -791,8 +813,15 @@ static Status cm_buffer_find_next(const CommandArgs *cmd_args)
status = bf_set_bp(buffer, &buffer->search.last_match_pos);
} else {
char msg[MAX_MSG_SIZE];
const char *pattern = buffer->search.opt.pattern;

if (list_size(sess->search_history) > 0) {
pattern = list_get(sess->search_history,
list_size(sess->search_history) - 1);
}

snprintf(msg, MAX_MSG_SIZE, "Unable to find pattern: \"%s\"",
buffer->search.opt.pattern);
pattern);
se_add_msg(sess, msg);
}
}
Expand Down Expand Up @@ -884,12 +913,7 @@ static Status cm_prepare_replace(Session *sess, char **rep_text_ptr,
*rep_length = strlen(rep_text);

Buffer *buffer = sess->active_buffer;
Status status = rp_replace_init(&buffer->search, rep_text, *rep_length);

if (!STATUS_IS_SUCCESS(status)) {
free(rep_text);
return status;
}
Status status = STATUS_SUCCESS;

if (*rep_text != '\0') {
status = se_add_replace_to_history(sess, rep_text);
Expand All @@ -900,6 +924,18 @@ static Status cm_prepare_replace(Session *sess, char **rep_text_ptr,
}
}

RETURN_IF_FAIL(rp_replace_init(&buffer->search, rep_text, *rep_length,
buffer->file_format == FF_WINDOWS));

rep_text = su_process_string(rep_text, *rep_length,
buffer->file_format == FF_WINDOWS,
rep_length);

if (rep_text == NULL) {
return st_get_error(ERR_OUT_OF_MEMORY, "Out Of Memory - "
"Unable to process input");
}

*rep_text_ptr = rep_text;

return status;
Expand All @@ -909,7 +945,7 @@ static Status cm_buffer_replace(const CommandArgs *cmd_args)
{
Session *sess = cmd_args->sess;
Buffer *buffer = sess->active_buffer;
RETURN_IF_FAIL(cm_prerpare_search(sess, NULL));
RETURN_IF_FAIL(cm_prepare_search(sess, NULL));

if (pr_prompt_cancelled(sess->prompt)) {
return STATUS_SUCCESS;
Expand Down Expand Up @@ -984,7 +1020,9 @@ static Status cm_buffer_replace(const CommandArgs *cmd_args)
} else if (response == QR_CANCEL) {
break;
} else if (response == QR_YES || response == QR_ALL) {
status = rp_replace_current_match(buffer, rep_text, rep_length);
size_t actual_rep_length;
status = rp_replace_current_match(buffer, rep_text, rep_length,
&actual_rep_length);

if (!STATUS_IS_SUCCESS(status)) {
break;
Expand All @@ -994,7 +1032,8 @@ static Status cm_buffer_replace(const CommandArgs *cmd_args)

if (search->opt.forward) {
size_t new_offset = buffer->search.last_match_pos.offset;
bp_advance_to_offset(&buffer->pos, new_offset + rep_length);
bp_advance_to_offset(&buffer->pos,
new_offset + actual_rep_length);
}
}
}
Expand All @@ -1007,15 +1046,24 @@ static Status cm_buffer_replace(const CommandArgs *cmd_args)
bc_end_grouped_changes(&buffer->changes);
}

free(rep_text);

if (!STATUS_IS_SUCCESS(status)) {
return status;
}

char msg[MAX_MSG_SIZE];

if (match_num == 0) {
const char *pattern = search->opt.pattern;

if (list_size(sess->search_history) > 0) {
pattern = list_get(sess->search_history,
list_size(sess->search_history) - 1);
}

snprintf(msg, MAX_MSG_SIZE, "Unable to find pattern \"%s\"",
search->opt.pattern);
pattern);
} else if (replace_num == 0) {
snprintf(msg, MAX_MSG_SIZE, "No occurrences replaced");
} else {
Expand Down
2 changes: 1 addition & 1 deletion config.mk
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ STATIC_SOURCES=wed.c display.c buffer.c util.c input.c session.c \
encoding.c config_parse_util.c gap_buffer.c buffer_pos.c \
text_search.c regex_search.c search.c replace.c undo.c \
file_type.c regex_util.c syntax.c theme.c prompt.c \
prompt_completer.c
prompt_completer.c search_util.c
GENERATED_SOURCES=config_parse.c config_scan.c
SOURCES=$(STATIC_SOURCES) $(GENERATED_SOURCES)

Expand Down
39 changes: 20 additions & 19 deletions regex_search.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
#include <pcre.h>
#include "status.h"
#include "buffer_pos.h"
#include "search_options.h"
#include "search_util.h"

/* Recommend reading: man pcreapi */

Expand All @@ -45,27 +45,28 @@
/* The max number of backreferences that can appear in replace text */
#define MAX_BACK_REF_OCCURRENCES 100

/* Structure to store each backreference occurence in replace text */
typedef struct {
size_t back_ref_num; /* Backreference number */
size_t rep_text_index; /* Starting index in replace string */
size_t rep_text_length; /* Backreference length in replace string */

/* e.g. If the user did a regex find and replace and entered \4 as
* the replace text then a BackReference struct would look like:
* { 4, 0, 2 }
*
* 1\{2}3 on the other hand would be:
* { 2, 1, 4 } */
} BackReference;

/* This structure contains backreference data processed from replace text
* entered by the user */
typedef struct {
/* Each backreference that occurs in replace text has an entry
* in the back_refs array below. Each entry has the following 3 properties
* stored in an array:
*
* 0: backreference number
* 1: starting index in replace string
* 2: backreference length
*
* e.g. If the user did a regex find and replace and entered \4 as
* the replace text then an entry in back_refs would look like:
*
* 0: 4
* 1: 0
* 2: 2
*
* This information is stored to allow the backreference to be replaced
* with the actual matched text once the search has been performed. */
size_t back_refs[MAX_BACK_REF_OCCURRENCES][3];
/* Each backreference that occurs in the replace text has an entry
* in the back_refs array. This information is stored to allow the
* backreference to be replaced with the actual matched text once
* a regex search has been performed. */
BackReference back_refs[MAX_BACK_REF_OCCURRENCES];

/* The number of backreference entries in the back_refs array */
size_t back_ref_occurrences;
Expand Down
Loading

0 comments on commit 8c70586

Please sign in to comment.