mirror of
https://github.com/ascii-boxes/boxes.git
synced 2024-12-04 14:03:53 +01:00
Finalize rewritten 'remove' module
This commit is contained in:
parent
775cabfbf6
commit
6de060ceb6
@ -259,21 +259,21 @@ bxstr_t *bxs_substr(bxstr_t *pString, size_t start_idx, size_t end_idx)
|
||||
if (pString == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
if (start_idx > pString->num_chars_visible) {
|
||||
start_idx = pString->num_chars_visible;
|
||||
if (start_idx > pString->num_chars) {
|
||||
start_idx = pString->num_chars;
|
||||
}
|
||||
if (end_idx > pString->num_chars_visible) {
|
||||
end_idx = pString->num_chars_visible;
|
||||
if (end_idx > pString->num_chars) {
|
||||
end_idx = pString->num_chars;
|
||||
}
|
||||
if (end_idx < start_idx) {
|
||||
bx_fprintf(stderr, "%s: internal error: end_idx before start_idx in bxs_substr()\n", PROJECT);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ucs4_t save = pString->memory[pString->first_char[end_idx]];
|
||||
set_char_at(pString->memory, pString->first_char[end_idx], char_nul);
|
||||
bxstr_t *result = bxs_from_unicode(pString->memory + pString->first_char[start_idx]);
|
||||
set_char_at(pString->memory, pString->first_char[end_idx], save);
|
||||
ucs4_t save = pString->memory[end_idx];
|
||||
set_char_at(pString->memory, end_idx, char_nul);
|
||||
bxstr_t *result = bxs_from_unicode(pString->memory + start_idx);
|
||||
set_char_at(pString->memory, end_idx, save);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -115,9 +115,8 @@ bxstr_t *bxs_trimdup(bxstr_t *pString, size_t start_idx, size_t end_idx);
|
||||
/**
|
||||
* Take a substring from the given string.
|
||||
* @param pString the source string
|
||||
* @param start_idx the index of the first visible character of the substring, used as index into `first_char` and
|
||||
* `visible_char`
|
||||
* @param end_idx the index of the first visible character following the substring
|
||||
* @param start_idx the index of the first character (visible or invisible) of the substring, an index into `memory`
|
||||
* @param end_idx the index of the first character (visible or invisible) following the substring
|
||||
* @return the substring, in new memory
|
||||
*/
|
||||
bxstr_t *bxs_substr(bxstr_t *pString, size_t start_idx, size_t end_idx);
|
||||
|
423
src/remove.c
423
src/remove.c
@ -86,6 +86,12 @@ typedef struct _remove_ctx_t {
|
||||
* of the box is empty or missing, this value will be equal to `bottom_start_idx`. Lines below are blank. */
|
||||
size_t bottom_end_idx;
|
||||
|
||||
/** The current comparison type. This changes whenever another comparison type is tried. */
|
||||
comparison_t comp_type;
|
||||
|
||||
/** number of lines in `body` */
|
||||
size_t body_num_lines;
|
||||
|
||||
/** Information on the vertical east and west shapes in body lines, one entry for each line between `top_end_idx`
|
||||
* (inclusive) and `bottom_start_idx` (exclusive) */
|
||||
line_ctx_t *body;
|
||||
@ -93,6 +99,80 @@ typedef struct _remove_ctx_t {
|
||||
|
||||
|
||||
|
||||
static void debug_print_remove_ctx(remove_ctx_t *ctx, char *heading)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
fprintf(stderr, "Remove Context %s:\n", heading);
|
||||
fprintf(stderr, " - empty_side[BTOP] = %s\n", ctx->empty_side[BTOP] ? "true" : "false");
|
||||
fprintf(stderr, " - empty_side[BRIG] = %s\n", ctx->empty_side[BRIG] ? "true" : "false");
|
||||
fprintf(stderr, " - empty_side[BBOT] = %s\n", ctx->empty_side[BBOT] ? "true" : "false");
|
||||
fprintf(stderr, " - empty_side[BLEF] = %s\n", ctx->empty_side[BLEF] ? "true" : "false");
|
||||
fprintf(stderr, " - design_is_mono = %s\n", ctx->design_is_mono ? "true" : "false");
|
||||
fprintf(stderr, " - input_is_mono = %s\n", ctx->input_is_mono ? "true" : "false");
|
||||
fprintf(stderr, " - top_start_idx = %d\n", (int) ctx->top_start_idx);
|
||||
fprintf(stderr, " - top_end_idx = %d\n", (int) ctx->top_end_idx);
|
||||
fprintf(stderr, " - bottom_start_idx = %d\n", (int) ctx->bottom_start_idx);
|
||||
fprintf(stderr, " - bottom_end_idx = %d\n", (int) ctx->bottom_end_idx);
|
||||
fprintf(stderr, " - comp_type = %s\n", comparison_name[ctx->comp_type]);
|
||||
fprintf(stderr, " - body (%d lines):\n", (int) ctx->body_num_lines);
|
||||
for (size_t i = 0; i < ctx->body_num_lines; i++) {
|
||||
if (ctx->body[i].input_line_used != NULL) {
|
||||
char *out_input_line_used = u32_strconv_to_output(ctx->body[i].input_line_used);
|
||||
fprintf(stderr, " - lctx: \"%s\" (%d characters)\n", out_input_line_used,
|
||||
(int) u32_strlen(ctx->body[i].input_line_used));
|
||||
BFREE(out_input_line_used);
|
||||
}
|
||||
else {
|
||||
fprintf(stderr, " - lctx: (null)\n");
|
||||
}
|
||||
bxstr_t *orgline = input.lines[ctx->top_end_idx + i].text;
|
||||
if (orgline != NULL) {
|
||||
char *out_orgline = bxs_to_output(orgline);
|
||||
fprintf(stderr, " orgl: \"%s\" (%d characters, %d columns)\n", out_orgline,
|
||||
(int) orgline->num_chars, (int) orgline->num_columns);
|
||||
BFREE(out_orgline);
|
||||
}
|
||||
else {
|
||||
fprintf(stderr, " orgl: (null)\n");
|
||||
}
|
||||
fprintf(stderr, " west: %d-%d (quality: %d), east: %d-%d (quality: %d)\n",
|
||||
(int) ctx->body[i].west_start, (int) ctx->body[i].west_end, (int) ctx->body[i].west_quality,
|
||||
(int) ctx->body[i].east_start, (int) ctx->body[i].east_end, (int) ctx->body[i].east_quality);
|
||||
}
|
||||
#else
|
||||
UNUSED(ctx);
|
||||
UNUSED(heading);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void debug_print_shapes_relevant(shape_line_ctx_t *shapes_relevant)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
fprintf(stderr, " shapes_relevant = {");
|
||||
for (size_t ds = 0; ds < SHAPES_PER_SIDE; ds++) {
|
||||
if (shapes_relevant[ds].empty) {
|
||||
fprintf(stderr, "-");
|
||||
}
|
||||
else {
|
||||
char *out_shp_text = bxs_to_output(shapes_relevant[ds].text);
|
||||
fprintf(stderr, "\"%s\"(%d%s)", out_shp_text, (int) shapes_relevant[ds].text->num_chars,
|
||||
shapes_relevant[ds].elastic ? "E" : "");
|
||||
BFREE(out_shp_text);
|
||||
}
|
||||
if (ds < SHAPES_PER_SIDE - 1) {
|
||||
fprintf(stderr, ", ");
|
||||
}
|
||||
}
|
||||
fprintf(stderr, "}\n");
|
||||
#else
|
||||
UNUSED(shapes_relevant);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
||||
static size_t find_first_line()
|
||||
{
|
||||
size_t result = input.num_lines;
|
||||
@ -212,7 +292,8 @@ uint32_t *shorten(shape_line_ctx_t *shape_line_ctx, size_t *quality, int prefer_
|
||||
|
||||
|
||||
|
||||
static int hmm_shiftable(shape_line_ctx_t *shapes_relevant, uint32_t *cur_pos, size_t shape_idx, uint32_t *end_pos);
|
||||
static int hmm_shiftable(shape_line_ctx_t *shapes_relevant, uint32_t *cur_pos, size_t shape_idx, uint32_t *end_pos,
|
||||
int anchored_right);
|
||||
|
||||
|
||||
|
||||
@ -221,26 +302,30 @@ static int hmm_shiftable(shape_line_ctx_t *shapes_relevant, uint32_t *cur_pos, s
|
||||
* Recursive helper function for match_horiz_line(), uses backtracking.
|
||||
* @param shapes_relevant the prepared shape lines to be concatenated
|
||||
* @param cur_pos current position in the input line being matched
|
||||
* @param shiftable flag indicating that `cur_pos` is still shiftable (corner shape line was blank)
|
||||
* @param shape_idx index into `shapes_relevant` indicating which shape to try now
|
||||
* @param end_pos first character of the east corner
|
||||
* @param anchored_left flag indicating that `cur_pos` is already "anchored" or still "shiftable". "Anchored" means
|
||||
* that we have matched a non-blank shape line already (corner shape line was not blank). Else "shiftable".
|
||||
* @param anchored_right flag indicating that the east corner shape was not blank. If this is `false`, it means that
|
||||
* a shape may be shortened right if only blank shape lines follow.
|
||||
* @return `== 1`: success;
|
||||
* `== 0`: failed to match
|
||||
*/
|
||||
int hmm(shape_line_ctx_t *shapes_relevant, uint32_t *cur_pos, int shiftable, size_t shape_idx, uint32_t *end_pos)
|
||||
int hmm(shape_line_ctx_t *shapes_relevant, uint32_t *cur_pos, size_t shape_idx, uint32_t *end_pos, int anchored_left,
|
||||
int anchored_right)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
char *out_cur_pos = u32_strconv_to_output(cur_pos);
|
||||
char *out_end_pos = u32_strconv_to_output(end_pos);
|
||||
fprintf(stderr, "hmm(shapes_relevant, \"%s\", %s, %d, \"%s\") - enter\n", out_cur_pos,
|
||||
shiftable ? "true" : "false", (int) shape_idx, out_end_pos);
|
||||
fprintf(stderr, "hmm(shapes_relevant, \"%s\", %d, \"%s\", %s, %s) - enter\n", out_cur_pos,
|
||||
(int) shape_idx, out_end_pos, anchored_left ? "true" : "false", anchored_right ? "true" : "false");
|
||||
BFREE(out_cur_pos);
|
||||
BFREE(out_end_pos);
|
||||
#endif
|
||||
|
||||
int result = 0;
|
||||
if (shiftable) {
|
||||
result = hmm_shiftable(shapes_relevant, cur_pos, shape_idx, end_pos);
|
||||
if (!anchored_left) {
|
||||
result = hmm_shiftable(shapes_relevant, cur_pos, shape_idx, end_pos, anchored_right);
|
||||
}
|
||||
else if (cur_pos > end_pos) {
|
||||
/* invalid input */
|
||||
@ -257,23 +342,36 @@ int hmm(shape_line_ctx_t *shapes_relevant, uint32_t *cur_pos, int shiftable, siz
|
||||
}
|
||||
else if (shapes_relevant[shape_idx].empty) {
|
||||
/* the current shape line is empty, try the next one */
|
||||
result = hmm(shapes_relevant, cur_pos, 0, shape_idx + 1, end_pos);
|
||||
result = hmm(shapes_relevant, cur_pos, shape_idx + 1, end_pos, 1, anchored_right);
|
||||
}
|
||||
else if (u32_strncmp(cur_pos, shapes_relevant[shape_idx].text->memory, shapes_relevant[shape_idx].text->num_chars) == 0) {
|
||||
cur_pos = cur_pos + shapes_relevant[shape_idx].text->num_chars;
|
||||
if (cur_pos == end_pos && !non_empty_shapes_after(shapes_relevant, shape_idx)) {
|
||||
result = 1; /* success */
|
||||
}
|
||||
else {
|
||||
int rc = 0;
|
||||
if (shapes_relevant[shape_idx].elastic) {
|
||||
rc = hmm(shapes_relevant, cur_pos, 0, shape_idx, end_pos);
|
||||
else {
|
||||
uint32_t *shape_line = u32_strdup(shapes_relevant[shape_idx].text->memory);
|
||||
size_t quality = shapes_relevant[shape_idx].text->num_chars;
|
||||
while (shape_line != NULL) {
|
||||
if (u32_strncmp(cur_pos, shape_line, quality) == 0) {
|
||||
BFREE(shape_line);
|
||||
cur_pos = cur_pos + quality;
|
||||
if (cur_pos == end_pos && !non_empty_shapes_after(shapes_relevant, shape_idx)) {
|
||||
result = 1; /* success */
|
||||
}
|
||||
else {
|
||||
int rc = 0;
|
||||
if (shapes_relevant[shape_idx].elastic) {
|
||||
rc = hmm(shapes_relevant, cur_pos, shape_idx, end_pos, 1, anchored_right);
|
||||
}
|
||||
if (rc == 0) {
|
||||
result = hmm(shapes_relevant, cur_pos, shape_idx + 1, end_pos, 1, anchored_right);
|
||||
}
|
||||
else {
|
||||
result = rc;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (rc == 0) {
|
||||
result = hmm(shapes_relevant, cur_pos, 0, shape_idx + 1, end_pos);
|
||||
else if (!anchored_right) {
|
||||
shape_line = shorten(shapes_relevant + shape_idx, &quality, 0, 0, 1);
|
||||
}
|
||||
else {
|
||||
result = rc;
|
||||
BFREE(shape_line);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -286,7 +384,8 @@ int hmm(shape_line_ctx_t *shapes_relevant, uint32_t *cur_pos, int shiftable, siz
|
||||
|
||||
|
||||
|
||||
static int hmm_shiftable(shape_line_ctx_t *shapes_relevant, uint32_t *cur_pos, size_t shape_idx, uint32_t *end_pos)
|
||||
static int hmm_shiftable(shape_line_ctx_t *shapes_relevant, uint32_t *cur_pos, size_t shape_idx, uint32_t *end_pos,
|
||||
int anchored_right)
|
||||
{
|
||||
int result = 0;
|
||||
int shapes_are_empty = 1;
|
||||
@ -299,8 +398,8 @@ static int hmm_shiftable(shape_line_ctx_t *shapes_relevant, uint32_t *cur_pos, s
|
||||
while (shape_line != NULL) {
|
||||
uint32_t *p = u32_strstr(cur_pos, shape_line);
|
||||
if (p != NULL && p < end_pos && is_blank_between(cur_pos, p)) {
|
||||
result = hmm(shapes_relevant, p + quality, 0, i + (shapes_relevant[i].elastic ? 0 : 1),
|
||||
end_pos);
|
||||
result = hmm(shapes_relevant, p + quality, i + (shapes_relevant[i].elastic ? 0 : 1),
|
||||
end_pos, 1, anchored_right);
|
||||
break;
|
||||
}
|
||||
if (can_shorten_right == -1) {
|
||||
@ -409,33 +508,6 @@ match_result_t *match_outer_shape(int vside, bxstr_t *input_line, bxstr_t *shape
|
||||
|
||||
|
||||
|
||||
static void debug_print_shapes_relevant(shape_line_ctx_t *shapes_relevant)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
fprintf(stderr, " shapes_relevant = {");
|
||||
for (size_t ds = 0; ds < SHAPES_PER_SIDE; ds++) {
|
||||
if (shapes_relevant[ds].empty) {
|
||||
fprintf(stderr, "-");
|
||||
}
|
||||
else {
|
||||
char *out_shp_text = bxs_to_output(shapes_relevant[ds].text);
|
||||
fprintf(stderr, "\"%s\"(%d%s)", out_shp_text, (int) shapes_relevant[ds].text->num_chars,
|
||||
shapes_relevant[ds].elastic ? "E" : "");
|
||||
BFREE(out_shp_text);
|
||||
}
|
||||
if (ds < SHAPES_PER_SIDE - 1) {
|
||||
fprintf(stderr, ", ");
|
||||
}
|
||||
}
|
||||
fprintf(stderr, "}\n");
|
||||
#else
|
||||
UNUSED(shapes_relevant);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
||||
// TODO gdb --args out/boxes -n UTF-8 -d diamonds -ac -m test/117_unicode_ansi_mending.input.tmp
|
||||
static int match_horiz_line(remove_ctx_t *ctx, int hside, size_t input_line_idx, size_t shape_line_idx)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
@ -448,6 +520,7 @@ static int match_horiz_line(remove_ctx_t *ctx, int hside, size_t input_line_idx,
|
||||
if (!comp_type_is_viable(comp_type, ctx->input_is_mono, ctx->design_is_mono)) {
|
||||
continue;
|
||||
}
|
||||
ctx->comp_type = comp_type;
|
||||
|
||||
shape_line_ctx_t *shapes_relevant = prepare_comp_shapes_horiz(hside, comp_type, shape_line_idx);
|
||||
debug_print_shapes_relevant(shapes_relevant);
|
||||
@ -462,7 +535,7 @@ static int match_horiz_line(remove_ctx_t *ctx, int hside, size_t input_line_idx,
|
||||
BFREE(out_input_prepped);
|
||||
#endif
|
||||
match_result_t *mrl = match_outer_shape(BLEF, input_prepped, shapes_relevant[0].text);
|
||||
if (mrl != NULL) { // TODO if mrl->shiftable, might need to match the next shape line
|
||||
if (mrl != NULL) {
|
||||
cur_pos = mrl->p + mrl->len;
|
||||
}
|
||||
|
||||
@ -481,7 +554,7 @@ static int match_horiz_line(remove_ctx_t *ctx, int hside, size_t input_line_idx,
|
||||
#endif
|
||||
|
||||
if (cur_pos && end_pos) {
|
||||
result = hmm(shapes_relevant, cur_pos, mrl->shiftable, 1, end_pos);
|
||||
result = hmm(shapes_relevant, cur_pos, 1, end_pos, mrl->shiftable ? 0 : 1, mrr->shiftable ? 0 : 1);
|
||||
}
|
||||
|
||||
BFREE(mrl);
|
||||
@ -573,7 +646,7 @@ static size_t count_shape_lines(shape_t side_shapes[])
|
||||
|
||||
static shape_line_ctx_t **prepare_comp_shapes_vert(int vside, comparison_t comp_type)
|
||||
{
|
||||
shape_t west_side_shapes[SHAPES_PER_SIDE - CORNERS_PER_SIDE] = {WSW, W, WNW};
|
||||
shape_t west_side_shapes[SHAPES_PER_SIDE - CORNERS_PER_SIDE] = {WNW, W, WSW};
|
||||
shape_t east_side_shapes[SHAPES_PER_SIDE - CORNERS_PER_SIDE] = {ENE, E, ESE};
|
||||
shape_t side_shapes[SHAPES_PER_SIDE - CORNERS_PER_SIDE];
|
||||
if (vside == BLEF) {
|
||||
@ -585,13 +658,14 @@ static shape_line_ctx_t **prepare_comp_shapes_vert(int vside, comparison_t comp_
|
||||
|
||||
size_t num_shape_lines = count_shape_lines(side_shapes);
|
||||
|
||||
/* allocate a NUL-terminated array: */
|
||||
shape_line_ctx_t **shape_lines = (shape_line_ctx_t **) calloc(num_shape_lines + 1, sizeof(shape_line_ctx_t *));
|
||||
for (size_t i = 0; i < num_shape_lines; i++) {
|
||||
shape_lines[i] = (shape_line_ctx_t *) calloc(1, sizeof(shape_line_ctx_t));
|
||||
}
|
||||
|
||||
for (size_t shape_idx = 0, i = 0; shape_idx < SHAPES_PER_SIDE - CORNERS_PER_SIDE; shape_idx++) {
|
||||
if (!isempty(opt.design->shape + side_shapes[shape_idx])) {
|
||||
int deep_empty = isdeepempty(opt.design->shape + side_shapes[shape_idx]);
|
||||
shape_lines[i] = (shape_line_ctx_t *) calloc(1, sizeof(shape_line_ctx_t));
|
||||
for (size_t slno = 0; slno < opt.design->shape[side_shapes[shape_idx]].height; slno++, i++) {
|
||||
uint32_t *s = prepare_comp_shape(opt.design, side_shapes[shape_idx], slno, comp_type, 0, 0);
|
||||
shape_lines[i]->text = bxs_from_unicode(s);
|
||||
@ -656,6 +730,7 @@ static void match_vertical_side(remove_ctx_t *ctx, int vside, shape_line_ctx_t *
|
||||
line_ctx->west_quality = quality;
|
||||
BFREE(line_ctx->input_line_used);
|
||||
line_ctx->input_line_used = u32_strdup(input_line);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (vside == BRIG && ((size_t) (p - input_line) >= input_length - input_trailing - quality)) {
|
||||
@ -665,6 +740,7 @@ static void match_vertical_side(remove_ctx_t *ctx, int vside, shape_line_ctx_t *
|
||||
line_ctx->east_quality = quality;
|
||||
BFREE(line_ctx->input_line_used);
|
||||
line_ctx->input_line_used = u32_strdup(input_line);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -695,6 +771,18 @@ static int sufficient_body_quality(remove_ctx_t *ctx)
|
||||
|
||||
|
||||
|
||||
static void reset_body(remove_ctx_t *ctx)
|
||||
{
|
||||
if (ctx->body != NULL) {
|
||||
for (size_t i = 0; i < ctx->body_num_lines; i++) {
|
||||
BFREE(ctx->body[i].input_line_used);
|
||||
}
|
||||
memset(ctx->body, 0, ctx->body_num_lines * sizeof(line_ctx_t));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void find_vertical_shapes(remove_ctx_t *ctx)
|
||||
{
|
||||
int west_empty = ctx->empty_side[BLEF];
|
||||
@ -707,6 +795,7 @@ static void find_vertical_shapes(remove_ctx_t *ctx)
|
||||
if (!comp_type_is_viable(comp_type, ctx->input_is_mono, ctx->design_is_mono)) {
|
||||
continue;
|
||||
}
|
||||
ctx->comp_type = comp_type;
|
||||
|
||||
shape_line_ctx_t **shape_lines_west = NULL;
|
||||
if (!west_empty) {
|
||||
@ -739,7 +828,7 @@ static void find_vertical_shapes(remove_ctx_t *ctx)
|
||||
if (sufficient_body_quality(ctx)) {
|
||||
break;
|
||||
}
|
||||
memset(ctx->body, 0, (ctx->bottom_start_idx - ctx->top_end_idx) * sizeof(line_ctx_t));
|
||||
reset_body(ctx);
|
||||
}
|
||||
}
|
||||
|
||||
@ -773,96 +862,6 @@ static void detect_design_if_needed()
|
||||
|
||||
|
||||
|
||||
static void toblank_plain(uint32_t **s, size_t from, size_t n)
|
||||
{
|
||||
if (n > 0) {
|
||||
size_t cols = (size_t) u32_width((*s) + from, n, encoding);
|
||||
if (cols > n) {
|
||||
u32_insert_space_at(s, from, cols - n);
|
||||
}
|
||||
u32_set((*s) + from, char_space, cols);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void toblank_mixed(bxstr_t *org_line, uint32_t **s, size_t from, size_t to, size_t n)
|
||||
{
|
||||
if (n > 0) {
|
||||
for (int i = (int) to - 1; i >= (int) from; i--) {
|
||||
if (bxs_is_visible_char(org_line, (size_t) i)) {
|
||||
size_t cols = (size_t) uc_width((*s)[i], encoding);
|
||||
if (cols > 1) {
|
||||
u32_insert_space_at(s, (size_t) i + 1, cols - 1);
|
||||
}
|
||||
u32_set((*s) + i, char_space, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
static size_t count_leading_spaces_plain(uint32_t *s, size_t max)
|
||||
{
|
||||
size_t result = 0;
|
||||
for (size_t i = 0; i < max && is_char_at(s, i, char_space); i++, result++)
|
||||
;
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void clip_spaces_carefully_plain(remove_ctx_t *ctx, uint32_t *s)
|
||||
{
|
||||
if (!ctx->empty_side[BLEF]) {
|
||||
size_t num_remove = count_leading_spaces_plain(s, opt.design->shape[NW].width);
|
||||
u32_move(s, s + num_remove, u32_strlen(s) - num_remove + 1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
static uint32_t *clip_spaces_carefully_mixed(remove_ctx_t *ctx, uint32_t *s)
|
||||
{
|
||||
if (!ctx->empty_side[BLEF]) {
|
||||
bxstr_t *bs = bxs_from_unicode(s);
|
||||
return bxs_ltrim(bs, opt.design->shape[NW].width);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void remove_vertical_shapes(remove_ctx_t *ctx)
|
||||
{
|
||||
size_t num_body_lines = ctx->bottom_start_idx - ctx->top_end_idx;
|
||||
line_ctx_t *body = ctx->body;
|
||||
for (size_t body_line_idx = 0; body_line_idx < num_body_lines; body_line_idx++) {
|
||||
line_ctx_t line_ctx = body[body_line_idx];
|
||||
bxstr_t *org_line = input.lines[ctx->top_end_idx + body_line_idx].text;
|
||||
|
||||
if (org_line->num_chars_invisible > 0) {
|
||||
toblank_mixed(
|
||||
org_line, &line_ctx.input_line_used, line_ctx.east_start, line_ctx.east_end, line_ctx.east_quality);
|
||||
toblank_mixed(
|
||||
org_line, &line_ctx.input_line_used, line_ctx.west_start, line_ctx.west_end, line_ctx.west_quality);
|
||||
uint32_t *clipped = clip_spaces_carefully_mixed(ctx, line_ctx.input_line_used);
|
||||
if (clipped) {
|
||||
BFREE(line_ctx.input_line_used);
|
||||
line_ctx.input_line_used = clipped;
|
||||
}
|
||||
}
|
||||
else {
|
||||
toblank_plain(&line_ctx.input_line_used, line_ctx.east_start, line_ctx.east_quality); /* east first */
|
||||
toblank_plain(&line_ctx.input_line_used, line_ctx.west_start, line_ctx.west_quality);
|
||||
clip_spaces_carefully_plain(ctx, line_ctx.input_line_used);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void free_line_text(line_t *line)
|
||||
{
|
||||
BFREE(line->cache_visible);
|
||||
@ -899,18 +898,41 @@ static void killblank(remove_ctx_t *ctx)
|
||||
|
||||
|
||||
|
||||
static void apply_results_to_input(remove_ctx_t *ctx)
|
||||
static int org_is_not_blank(bxstr_t *org_line, comparison_t comp_type, size_t idx)
|
||||
{
|
||||
for (size_t j = ctx->top_end_idx; j < ctx->bottom_start_idx; ++j) {
|
||||
free_line_text(input.lines + j);
|
||||
input.lines[j].text = bxs_from_unicode(ctx->body[j - ctx->top_end_idx].input_line_used);
|
||||
// FIXME now we might have lost the input colors
|
||||
if (comp_type == literal || comp_type == ignore_invisible_shape) {
|
||||
return !is_blank(org_line->memory[idx]);
|
||||
}
|
||||
return !is_blank(org_line->memory[org_line->visible_char[idx]]);
|
||||
}
|
||||
|
||||
if (opt.killblank) {
|
||||
killblank(ctx);
|
||||
|
||||
|
||||
static size_t max_chars_line(bxstr_t *org_line, comparison_t comp_type)
|
||||
{
|
||||
return (comp_type == literal || comp_type == ignore_invisible_shape)
|
||||
? org_line->num_chars : org_line->num_chars_visible;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static size_t confirmed_padding(bxstr_t *org_line, comparison_t comp_type, size_t start_idx, size_t num_padding)
|
||||
{
|
||||
size_t result = 0;
|
||||
size_t max_chars = max_chars_line(org_line, comp_type);
|
||||
for (size_t i = start_idx; i < BMIN(max_chars, start_idx + num_padding); i++) {
|
||||
if (org_is_not_blank(org_line, comp_type, i)) {
|
||||
break;
|
||||
}
|
||||
result++;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void remove_top_from_input(remove_ctx_t *ctx)
|
||||
{
|
||||
if (ctx->top_end_idx > ctx->top_start_idx) {
|
||||
for (size_t j = ctx->top_start_idx; j < ctx->top_end_idx; ++j) {
|
||||
free_line(input.lines + j);
|
||||
@ -922,7 +944,69 @@ static void apply_results_to_input(remove_ctx_t *ctx)
|
||||
ctx->bottom_start_idx -= num_lines_removed;
|
||||
ctx->bottom_end_idx -= num_lines_removed;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
static size_t calculate_start_idx(remove_ctx_t *ctx, size_t body_line_idx)
|
||||
{
|
||||
size_t input_line_idx = ctx->top_end_idx + body_line_idx;
|
||||
line_ctx_t *lctx = ctx->body + body_line_idx;
|
||||
bxstr_t *org_line = input.lines[input_line_idx].text;
|
||||
|
||||
size_t s_idx = 0;
|
||||
if (lctx->west_quality > 0) {
|
||||
s_idx = lctx->west_end + confirmed_padding(org_line, ctx->comp_type, lctx->west_end, opt.design->padding[BLEF]);
|
||||
}
|
||||
if (ctx->comp_type == ignore_invisible_input || ctx->comp_type == ignore_invisible_all) {
|
||||
/* our line context worked with visible characters only, convert back to org_line */
|
||||
s_idx = org_line->first_char[s_idx];
|
||||
}
|
||||
return s_idx;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static size_t calculate_end_idx(remove_ctx_t *ctx, size_t body_line_idx)
|
||||
{
|
||||
size_t input_line_idx = ctx->top_end_idx + body_line_idx;
|
||||
line_ctx_t *lctx = ctx->body + body_line_idx;
|
||||
bxstr_t *org_line = input.lines[input_line_idx].text;
|
||||
|
||||
size_t e_idx = lctx->east_quality > 0 ? lctx->east_start : max_chars_line(org_line, ctx->comp_type);
|
||||
if (ctx->comp_type == ignore_invisible_input || ctx->comp_type == ignore_invisible_all) {
|
||||
e_idx = org_line->first_char[e_idx];
|
||||
}
|
||||
return e_idx;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void remove_vertical_from_input(remove_ctx_t *ctx)
|
||||
{
|
||||
for (size_t body_line_idx = 0; body_line_idx < ctx->body_num_lines; body_line_idx++) {
|
||||
size_t input_line_idx = ctx->top_end_idx + body_line_idx;
|
||||
bxstr_t *org_line = input.lines[input_line_idx].text;
|
||||
size_t s_idx = calculate_start_idx(ctx, body_line_idx);
|
||||
size_t e_idx = calculate_end_idx(ctx, body_line_idx);
|
||||
#ifdef DEBUG
|
||||
fprintf(stderr, "remove_vertical_from_input(): body_line_idx=%d, input_line_idx=%d, s_idx=%d, e_idx=%d, "
|
||||
"input.indent=%d\n", (int) body_line_idx, (int) input_line_idx, (int) s_idx, (int) e_idx,
|
||||
(int) input.indent);
|
||||
#endif
|
||||
|
||||
bxstr_t *temp2 = bxs_substr(org_line, s_idx, e_idx);
|
||||
bxstr_t *temp = bxs_prepend_spaces(temp2, input.indent);
|
||||
free_line_text(input.lines + input_line_idx);
|
||||
input.lines[input_line_idx].text = temp;
|
||||
bxs_free(temp2);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void remove_bottom_from_input(remove_ctx_t *ctx)
|
||||
{
|
||||
if (ctx->bottom_end_idx > ctx->bottom_start_idx) {
|
||||
for (size_t j = ctx->bottom_start_idx; j < ctx->bottom_end_idx; ++j) {
|
||||
free_line(input.lines + j);
|
||||
@ -933,6 +1017,19 @@ static void apply_results_to_input(remove_ctx_t *ctx)
|
||||
}
|
||||
input.num_lines -= ctx->bottom_end_idx - ctx->bottom_start_idx;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void apply_results_to_input(remove_ctx_t *ctx)
|
||||
{
|
||||
remove_vertical_from_input(ctx);
|
||||
|
||||
if (opt.killblank) {
|
||||
killblank(ctx);
|
||||
}
|
||||
remove_bottom_from_input(ctx);
|
||||
remove_top_from_input(ctx);
|
||||
|
||||
input.maxline = 0;
|
||||
input.indent = SIZE_MAX;
|
||||
@ -992,23 +1089,27 @@ int remove_box()
|
||||
}
|
||||
else {
|
||||
ctx->bottom_start_idx = find_bottom_side(ctx);
|
||||
if (ctx->bottom_start_idx > ctx->top_end_idx) {
|
||||
ctx->body_num_lines = ctx->bottom_start_idx - ctx->top_end_idx;
|
||||
}
|
||||
}
|
||||
|
||||
ctx->body = (line_ctx_t *) calloc(ctx->bottom_start_idx - ctx->top_end_idx, sizeof(line_ctx_t));
|
||||
if (ctx->bottom_start_idx > ctx->top_end_idx) {
|
||||
if (ctx->body_num_lines > 0) {
|
||||
ctx->body = (line_ctx_t *) calloc(ctx->body_num_lines, sizeof(line_ctx_t));
|
||||
find_vertical_shapes(ctx);
|
||||
}
|
||||
|
||||
remove_vertical_shapes(ctx);
|
||||
|
||||
debug_print_remove_ctx(ctx, "before apply_results_to_input()");
|
||||
apply_results_to_input(ctx);
|
||||
|
||||
for (size_t i = 0; i < ctx->bottom_start_idx - ctx->top_end_idx; i++) {
|
||||
BFREE(ctx->body[i].input_line_used);
|
||||
if (ctx->body != NULL) {
|
||||
for (size_t i = 0; i < ctx->body_num_lines; i++) {
|
||||
BFREE(ctx->body[i].input_line_used);
|
||||
}
|
||||
BFREE(ctx->body);
|
||||
}
|
||||
BFREE(ctx->body);
|
||||
BFREE(ctx);
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
:DESC
|
||||
Remove a box which has been piped through lolcat, including the box itself and the indentation, all of which was
|
||||
colored.
|
||||
colored. This will lead to the indent losing its coloring, which is normally an invisible change.
|
||||
|
||||
:ARGS
|
||||
-r
|
||||
@ -11,6 +11,6 @@ colored.
|
||||
[38;5;44m [0m[38;5;44m [0m[38;5;49m [0m[38;5;49m/[0m[38;5;48m*[0m[38;5;48m*[0m[38;5;83m*[0m[38;5;83m*[0m[38;5;83m*[0m[38;5;118m*[0m[38;5;118m*[0m[38;5;154m*[0m[38;5;148m*[0m[38;5;184m*[0m[38;5;184m/[0m
|
||||
:OUTPUT-FILTER
|
||||
:EXPECTED
|
||||
[0m[38;5;39m [0m[38;5;39m [0m[38;5;44mf[0m[38;5;44mo[0m[38;5;49mo[0m[38;5;49mb[0m[38;5;48ma[0m[38;5;48mr[0m
|
||||
[0m[38;5;44m [0m[38;5;49m [0m[38;5;49mb[0m[38;5;48mo[0m[38;5;48mo[0m[38;5;83mf[0m[38;5;83ma[0m[38;5;83mr[0m
|
||||
[38;5;44mf[0m[38;5;44mo[0m[38;5;49mo[0m[38;5;49mb[0m[38;5;48ma[0m[38;5;48mr[0m
|
||||
[38;5;49mb[0m[38;5;48mo[0m[38;5;48mo[0m[38;5;83mf[0m[38;5;83ma[0m[38;5;83mr[0m
|
||||
:EOF
|
||||
|
@ -554,7 +554,7 @@ void test_hmm_sunny_day(void **state)
|
||||
uint32_t *cur_pos = input_line + 9; /* '-' after WCORNER */
|
||||
uint32_t *end_pos = input_line + 25; /* 'E' of ECORNER */
|
||||
|
||||
int actual = hmm(shapes_relevant, cur_pos, 0, 1, end_pos);
|
||||
int actual = hmm(shapes_relevant, cur_pos, 1, end_pos, 1, 1);
|
||||
|
||||
assert_int_equal(1, actual);
|
||||
|
||||
@ -574,7 +574,7 @@ void test_hmm_sunny_day_short(void **state)
|
||||
uint32_t *cur_pos = input_line + 7; /* '-' after WCORNER */
|
||||
uint32_t *end_pos = input_line + 15; /* NUL */
|
||||
|
||||
int actual = hmm(shapes_relevant, cur_pos, 0, 1, end_pos);
|
||||
int actual = hmm(shapes_relevant, cur_pos, 1, end_pos, 1, 0);
|
||||
|
||||
assert_int_equal(1, actual);
|
||||
|
||||
@ -594,7 +594,7 @@ void test_hmm_missing_elastic_nne(void **state)
|
||||
uint32_t *cur_pos = input_line + 7; /* '-' after WCORNER */
|
||||
uint32_t *end_pos = input_line + 16; /* 'E' of ECORNER */
|
||||
|
||||
int actual = hmm(shapes_relevant, cur_pos, 0, 1, end_pos);
|
||||
int actual = hmm(shapes_relevant, cur_pos, 1, end_pos, 1, 1);
|
||||
|
||||
assert_int_equal(0, actual); /* should fail because NNE shape is not present */
|
||||
|
||||
@ -614,7 +614,7 @@ void test_hmm_invalid_input(void **state)
|
||||
uint32_t *cur_pos = input_line + 5;
|
||||
uint32_t *end_pos = input_line + 2; /* before cur_pos, which is an error */
|
||||
|
||||
int actual = hmm(shapes_relevant, cur_pos, 0, 1, end_pos);
|
||||
int actual = hmm(shapes_relevant, cur_pos, 1, end_pos, 1, 1);
|
||||
|
||||
assert_int_equal(0, actual); /* should fail because cur_pos > end_pos */
|
||||
|
||||
@ -657,7 +657,7 @@ void test_hmm_empty_shapes_success(void **state)
|
||||
uint32_t *cur_pos = input_line + 8; /* first 'x' */
|
||||
uint32_t *end_pos = input_line + 16; /* NUL */
|
||||
|
||||
int actual = hmm(shapes_relevant, cur_pos, 0, 1, end_pos);
|
||||
int actual = hmm(shapes_relevant, cur_pos, 1, end_pos, 1, 1);
|
||||
|
||||
assert_int_equal(1, actual); /* matches */
|
||||
|
||||
@ -700,7 +700,7 @@ void test_hmm_backtracking(void **state)
|
||||
uint32_t *cur_pos = input_line + 8; /* first '-' */
|
||||
uint32_t *end_pos = input_line + 24; /* NUL */
|
||||
|
||||
int actual = hmm(shapes_relevant, cur_pos, 0, 1, end_pos);
|
||||
int actual = hmm(shapes_relevant, cur_pos, 1, end_pos, 1, 1);
|
||||
|
||||
assert_int_equal(1, actual); /* matches, elastic NNW doesn't eat part of N shape */
|
||||
|
||||
@ -743,7 +743,7 @@ void test_hmm_shiftable(void **state)
|
||||
uint32_t *cur_pos = input_line; /* first character */
|
||||
uint32_t *end_pos = input_line + 17; /* NUL */
|
||||
|
||||
int actual = hmm(shapes_relevant, cur_pos, 1, 1, end_pos);
|
||||
int actual = hmm(shapes_relevant, cur_pos, 1, end_pos, 0, 0);
|
||||
|
||||
assert_int_equal(1, actual); /* matches */
|
||||
|
||||
@ -763,7 +763,7 @@ void test_hmm_shortened(void **state)
|
||||
uint32_t *cur_pos = input_line; /* first character */
|
||||
uint32_t *end_pos = input_line + 5; /* NUL */
|
||||
|
||||
int actual = hmm(shapes_relevant, cur_pos, 1, 1, end_pos);
|
||||
int actual = hmm(shapes_relevant, cur_pos, 1, end_pos, 0, 0);
|
||||
|
||||
assert_int_equal(1, actual); /* matches, because " NORTH " can be shortened because shiftable */
|
||||
|
||||
@ -806,7 +806,7 @@ void test_hmm_shortened_right_fail(void **state)
|
||||
uint32_t *cur_pos = input_line; /* first character */
|
||||
uint32_t *end_pos = input_line + 6; /* 'E' of "ECORNER" */
|
||||
|
||||
int actual = hmm(shapes_relevant, cur_pos, 1, 1, end_pos);
|
||||
int actual = hmm(shapes_relevant, cur_pos, 1, end_pos, 0, 1);
|
||||
|
||||
assert_int_equal(0, actual); /* does not match */
|
||||
/* because " NORTH " cannot be shortened right b/c right not shiftable */
|
||||
@ -827,7 +827,7 @@ void test_hmm_shortened_right(void **state)
|
||||
uint32_t *cur_pos = input_line; /* first character */
|
||||
uint32_t *end_pos = input_line + 7; /* 'E' of "ECORNER" */
|
||||
|
||||
int actual = hmm(shapes_relevant, cur_pos, 1, 1, end_pos);
|
||||
int actual = hmm(shapes_relevant, cur_pos, 1, end_pos, 0, 1);
|
||||
|
||||
assert_int_equal(1, actual); /* matches */
|
||||
/* because " NORTH " can be shortened left, and ECORNER can be shortened right */
|
||||
@ -871,7 +871,7 @@ void test_hmm_blank_shiftable(void **state)
|
||||
uint32_t *cur_pos = input_line; /* first character */
|
||||
uint32_t *end_pos = input_line + 6; /* NUL */
|
||||
|
||||
int actual = hmm(shapes_relevant, cur_pos, 1, 1, end_pos);
|
||||
int actual = hmm(shapes_relevant, cur_pos, 1, end_pos, 0, 0);
|
||||
|
||||
assert_int_equal(1, actual); /* matches */
|
||||
|
||||
@ -891,7 +891,7 @@ void test_hmm_blank(void **state)
|
||||
uint32_t *cur_pos = input_line + 7; /* first blank */
|
||||
uint32_t *end_pos = input_line + 14; /* 'E' of "ECORNER" */
|
||||
|
||||
int actual = hmm(shapes_relevant, cur_pos, 0, 1, end_pos);
|
||||
int actual = hmm(shapes_relevant, cur_pos, 1, end_pos, 1, 1);
|
||||
|
||||
assert_int_equal(1, actual); /* matches */
|
||||
|
||||
|
@ -24,10 +24,12 @@
|
||||
#include "remove.h"
|
||||
|
||||
|
||||
/* defined here and not in remove.h because it's only visible for testing */
|
||||
/* defined here and not in remove.h because these functions are only visible for testing */
|
||||
match_result_t *match_outer_shape(int vside, bxstr_t *input_line, bxstr_t *shape_line);
|
||||
int hmm(shape_line_ctx_t *shapes_relevant, uint32_t *cur_pos, int shiftable, size_t shape_idx, uint32_t *end_pos);
|
||||
int hmm(shape_line_ctx_t *shapes_relevant, uint32_t *cur_pos, size_t shape_idx, uint32_t *end_pos, int anchored_left,
|
||||
int anchored_right);
|
||||
uint32_t *shorten(shape_line_ctx_t *shape_line_ctx, size_t *quality, int prefer_left, int allow_left, int allow_right);
|
||||
void toblank_mixed(bxstr_t *org_line, uint32_t **s, size_t from, size_t to);
|
||||
|
||||
|
||||
void test_match_outer_shape_null(void **state);
|
||||
|
Loading…
Reference in New Issue
Block a user