Example #1
0
static Filerange text_object_bracket(Text *txt, size_t pos, char type) {
	char c, open, close;
	int opened = 1, closed = 1;
	Filerange r = text_range_empty();

	switch (type) {
	case '(':  case ')': open = '(';  close = ')';  break;
	case '{':  case '}': open = '{';  close = '}';  break;
	case '[':  case ']': open = '[';  close = ']';  break;
	case '<':  case '>': open = '<';  close = '>';  break;
	case '"':            open = '"';  close = '"';  break;
	case '`':            open = '`';  close = '`';  break;
	case '\'':           open = '\''; close = '\''; break;
	default: return r;
	}

	Iterator it = text_iterator_get(txt, pos);

	if (open == close && text_iterator_byte_get(&it, &c) && (c == '"' || c == '`' || c == '\'')) {
		size_t match = text_bracket_match(txt, pos);
		r.start = MIN(pos, match) + 1;
		r.end = MAX(pos, match);
		return r;
	}

	while (text_iterator_byte_get(&it, &c)) {
		if (c == open && --opened == 0) {
			r.start = it.pos + 1;
			break;
		} else if (c == close && it.pos != pos) {
			opened++;
		}
		text_iterator_byte_prev(&it, NULL);
	}

	it = text_iterator_get(txt, pos);
	while (text_iterator_byte_get(&it, &c)) {
		if (c == close && --closed == 0) {
			r.end = it.pos;
			break;
		} else if (c == open && it.pos != pos) {
			closed++;
		}
		text_iterator_byte_next(&it, NULL);
	}

	if (!text_range_valid(&r))
		return text_range_empty();
	return r;
}
Example #2
0
File: sam.c Project: ewqasd200g/vis
enum SamError sam_cmd(Vis *vis, const char *s) {
	enum SamError err = SAM_ERR_OK;
	if (!s)
		return err;

	Command *cmd = sam_parse(vis, s, &err);
	if (!cmd) {
		if (err == SAM_ERR_OK)
			err = SAM_ERR_MEMORY;
		return err;
	}

	Filerange range = text_range_empty();
	sam_execute(vis, vis->win, cmd, NULL, &range);

	if (vis->win) {
		bool completed = true;
		for (Cursor *c = view_cursors(vis->win->view); c; c = view_cursors_next(c)) {
			Filerange sel = view_cursors_selection_get(c);
			if (text_range_valid(&sel)) {
				completed = false;
				break;
			}
		}
		vis_mode_switch(vis, completed ? VIS_MODE_NORMAL : VIS_MODE_VISUAL);
	}
	command_free(cmd);
	return err;
}
Example #3
0
Filerange text_object_number(Text *txt, size_t pos) {
    char *buf, *err = NULL;
    Filerange r = text_object_range(txt, pos, is_number);
    if (!text_range_valid(&r))
        return r;
    if (!(buf = text_bytes_alloc0(txt, r.start, text_range_size(&r))))
        return text_range_empty();
    errno = 0;
    strtoll(buf, &err, 0);
    if (errno || err == buf)
        r = text_range_empty();
    else
        r.end = r.start + (err - buf);
    free(buf);
    return r;
}
Example #4
0
Filerange text_object_longword_outer(Text *txt, size_t pos) {
	Filerange r;
	char c, prev = '0', next = '0';
	Iterator it = text_iterator_get(txt, pos);
	if (!text_iterator_byte_get(&it, &c))
		return text_range_empty();
	if (text_iterator_byte_prev(&it, &prev))
		text_iterator_byte_next(&it, NULL);
	text_iterator_byte_next(&it, &next);
	if (isspace(c)) {
		/* middle of two words, include leading white space */
		r.start = text_char_next(txt, text_longword_end_prev(txt, pos));
		r.end = text_char_next(txt, text_longword_end_next(txt, pos));
	} else if (isspace(prev) && isspace(next)) {
		/* on a single character */
		r.start = pos;
		r.end = text_longword_start_next(txt, pos);
	} else if (isspace(prev)) {
		/* at start of a word */
		r.start = pos;
		r.end = text_longword_start_next(txt, text_longword_end_next(txt, pos));
	} else if (isspace(next)) {
		/* at end of a word */
		r.start = text_longword_start_prev(txt, pos);
		r.end = text_longword_start_next(txt, pos);
	} else {
		/* in the middle of a word */
		r.start = text_longword_start_prev(txt, pos);
		r.end = text_longword_start_next(txt, text_longword_end_next(txt, pos));
	}
	return r;
}
Example #5
0
Filerange text_object_function(Text *txt, size_t pos) {
	size_t a = text_function_start_prev(txt, pos);
	size_t b = text_function_end_next(txt, pos);
	if (text_function_end_next(txt, a) == b) {
		Filerange r = text_range_new(a, b+1);
		return text_range_linewise(txt, &r);
	}
	return text_range_empty();
}
Example #6
0
Filerange text_object_search_forward(Text *txt, size_t pos, Regex *regex) {
    size_t start = pos;
    size_t end = text_size(txt);
    RegexMatch match[1];
    bool found = start < end && !text_search_range_forward(txt, start, end - start, regex, 1, match, 0);
    if (found)
        return text_range_new(match[0].start, match[0].end);
    return text_range_empty();
}
Example #7
0
Filerange text_object_search_backward(Text *txt, size_t pos, Regex *regex) {
    size_t start = 0;
    size_t end = pos;
    RegexMatch match[1];
    bool found = !text_search_range_backward(txt, start, end, regex, 1, match, 0);
    if (found)
        return text_range_new(match[0].start, match[0].end);
    return text_range_empty();
}
Example #8
0
Filerange text_object_indentation(Text *txt, size_t pos) {
    char c;
    size_t bol = text_line_begin(txt, pos);
    size_t sol = text_line_start(txt, bol);
    size_t start = bol;
    size_t end = text_line_next(txt, bol);
    size_t line_indent = sol - bol;
    bool line_empty = text_byte_get(txt, bol, &c) && (c == '\r' || c == '\n');

    char *buf = text_bytes_alloc0(txt, bol, line_indent);
    char *tmp = malloc(line_indent);

    if (!buf || !tmp) {
        free(buf);
        free(tmp);
        return text_range_empty();
    }

    while ((bol = text_line_begin(txt, text_line_prev(txt, start))) != start) {
        sol = text_line_start(txt, bol);
        size_t indent = sol - bol;
        if (indent < line_indent)
            break;
        bool empty = text_byte_get(txt, bol, &c) && (c == '\r' || c == '\n');
        if (line_empty && !empty)
            break;
        if (line_indent == 0 && empty)
            break;
        text_bytes_get(txt, bol, line_indent, tmp);
        if (memcmp(buf, tmp, line_indent))
            break;
        start = bol;
    }

    do {
        bol = end;
        sol = text_line_start(txt, bol);
        size_t indent = sol - bol;
        if (indent < line_indent)
            break;
        bool empty = text_byte_get(txt, bol, &c) && (c == '\r' || c == '\n');
        if (line_empty && !empty)
            break;
        if (line_indent == 0 && empty)
            break;
        text_bytes_get(txt, bol, line_indent, tmp);
        if (memcmp(buf, tmp, line_indent))
            break;
        end = text_line_next(txt, bol);
    } while (bol != end);

    free(buf);
    free(tmp);
    return text_range_new(start, end);
}
Example #9
0
File: view.c Project: mulianov/vis
Filerange view_selection_get(View *view) {
	Filerange sel = view->sel;
	if (sel.start > sel.end) {
		size_t tmp = sel.start;
		sel.start = sel.end;
		sel.end = tmp;
	}
	if (!text_range_valid(&sel))
		return text_range_empty();
	sel.end = text_char_next(view->text, sel.end);
	return sel;
}
Example #10
0
Filerange text_object_range(Text *txt, size_t pos, int (*isboundary)(int)) {
    char c;
    size_t start;
    Iterator it = text_iterator_get(txt, pos), rit = it;
    if (!text_iterator_byte_get(&rit, &c) || boundary(c))
        return text_range_empty();
    char tmp = c;
    do start = rit.pos;
    while (text_iterator_char_prev(&rit, &c) && !boundary(c));
    for (c = tmp; !boundary(c) && text_iterator_byte_next(&it, &c););
    return text_range_new(start, it.pos);
}
Example #11
0
File: sam.c Project: ewqasd200g/vis
static bool cmd_select(Vis *vis, Win *win, Command *cmd, const char *argv[], Cursor *cur, Filerange *range) {
	Filerange sel = text_range_empty();
	if (!win)
		return sam_execute(vis, NULL, cmd->cmd, NULL, &sel);
	bool ret = true;
	View *view = win->view;
	Text *txt = win->file->text;
	bool multiple_cursors = view_cursors_multiple(view);
	Cursor *primary = view_cursors_primary_get(view);

	for (Cursor *c = view_cursors(view), *next; c && ret; c = next) {
		next = view_cursors_next(c);
		size_t pos = view_cursors_pos(c);
		if (vis->mode->visual) {
			sel = view_cursors_selection_get(c);
		} else if (cmd->cmd->address) {
			/* convert a single line range to a goto line motion */
			if (!multiple_cursors && cmd->cmd->cmddef->func == cmd_print) {
				Address *addr = cmd->cmd->address;
				switch (addr->type) {
				case '+':
				case '-':
					addr = addr->right;
					/* fall through */
				case 'l':
					if (addr && addr->type == 'l' && !addr->right)
						addr->type = 'g';
					break;
				}
			}
			sel = text_range_new(pos, pos);
		} else if (cmd->cmd->cmddef->flags & CMD_ADDRESS_POS) {
			sel = text_range_new(pos, pos);
		} else if (cmd->cmd->cmddef->flags & CMD_ADDRESS_LINE) {
			sel = text_object_line(txt, pos);
		} else if (cmd->cmd->cmddef->flags & CMD_ADDRESS_AFTER) {
			size_t next_line = text_line_next(txt, pos);
			sel = text_range_new(next_line, next_line);
		} else if (multiple_cursors) {
			sel = text_object_line(txt, pos);
		} else {
			sel = text_range_new(0, text_size(txt));
		}
		if (text_range_valid(&sel))
			ret &= sam_execute(vis, win, cmd->cmd, c, &sel);
		if (cmd->cmd->cmddef->flags & CMD_ONCE)
			break;
	}

	if (vis->win && vis->win->view == view && primary != view_cursors_primary_get(view))
		view_cursors_primary_set(view_cursors(view));
	return ret;
}
Example #12
0
Filerange text_object_word_find_prev(Text *txt, size_t pos, const char *word) {
	size_t len = strlen(word);
	for (;;) {
		size_t match_pos = text_find_prev(txt, pos, word);
		if (match_pos != pos) {
			Filerange match_word = text_object_word(txt, match_pos);
			if (text_range_size(&match_word) == len)
				return match_word;
			pos = match_pos;
		} else {
			return text_range_empty();
		}
	}
}
Example #13
0
static Filerange object_function(Text *txt, size_t pos) {
    size_t start_prev = text_function_start_prev(txt, pos);
    size_t end_next = text_function_end_next(txt, pos);
    size_t start = text_function_start_next(txt, start_prev);
    size_t end = text_function_end_prev(txt, end_next);
    if (start == pos)
        start_prev = pos;
    if (end == pos)
        end_next = pos;
    if (text_function_end_next(txt, start_prev) == end_next &&
            text_function_start_prev(txt, end_next) == start_prev) {
        return text_range_new(start_prev, end_next);
    }
    return text_range_empty();
}
Example #14
0
static Filerange text_object_customword(Text *txt, size_t pos, int (*isboundary)(int)) {
    Filerange r;
    char c, prev = '0', next = '0';
    Iterator it = text_iterator_get(txt, pos);
    if (!text_iterator_byte_get(&it, &c))
        return text_range_empty();
    if (text_iterator_byte_prev(&it, &prev))
        text_iterator_byte_next(&it, NULL);
    text_iterator_byte_next(&it, &next);
    if (space(c)) {
        r.start = text_char_next(txt, text_customword_end_prev(txt, pos, isboundary));
        r.end = text_customword_start_next(txt, pos, isboundary);
    } else if (boundary(prev) && boundary(next)) {
        if (boundary(c)) {
            r.start = text_char_next(txt, text_customword_end_prev(txt, pos, isboundary));
            r.end = text_char_next(txt, text_customword_end_next(txt, pos, isboundary));
        } else {
            /* on a single character */
            r.start = pos;
            r.end = text_char_next(txt, pos);
        }
    } else if (boundary(prev)) {
        /* at start of a word */
        r.start = pos;
        r.end = text_char_next(txt, text_customword_end_next(txt, pos, isboundary));
    } else if (boundary(next)) {
        /* at end of a word */
        r.start = text_customword_start_prev(txt, pos, isboundary);
        r.end = text_char_next(txt, pos);
    } else {
        /* in the middle of a word */
        r.start = text_customword_start_prev(txt, pos, isboundary);
        r.end = text_char_next(txt, text_customword_end_next(txt, pos, isboundary));
    }

    return r;
}
Example #15
0
File: view.c Project: mulianov/vis
void view_selection_clear(View *view) {
	view->sel = text_range_empty();
	view_draw(view);
	view_cursor_update(view);
}
Example #16
0
File: sam.c Project: ewqasd200g/vis
static bool cmd_extract(Vis *vis, Win *win, Command *cmd, const char *argv[], Cursor *cur, Filerange *range) {
	if (!win)
		return false;
	bool ret = true;
	Text *txt = win->file->text;

	if (cmd->regex) {
		size_t start = range->start, end = range->end, last_start = EPOS;
		RegexMatch match[1];
		while (start < end) {
			bool found = text_search_range_forward(txt, start,
				end - start, cmd->regex, 1, match,
				start > range->start ? REG_NOTBOL : 0) == 0;
			Filerange r = text_range_empty();
			if (found) {
				if (argv[0][0] == 'x')
					r = text_range_new(match[0].start, match[0].end);
				else
					r = text_range_new(start, match[0].start);
				if (match[0].start == match[0].end) {
					if (last_start == match[0].start) {
						start++;
						continue;
					}
					/* in Plan 9's regexp library ^ matches the beginning
					 * of a line, however in POSIX with REG_NEWLINE ^
					 * matches the zero-length string immediately after a
					 * newline. Try filtering out the last such match at EOF.
					 */
					if (end == match[0].start && start > range->start)
						break;
				}
				start = match[0].end;
			} else {
				if (argv[0][0] == 'y')
					r = text_range_new(start, end);
				start = end;
			}

			if (text_range_valid(&r)) {
				Mark mark_start = text_mark_set(txt, start);
				Mark mark_end = text_mark_set(txt, end);
				ret &= sam_execute(vis, win, cmd->cmd, NULL, &r);
				last_start = start = text_mark_get(txt, mark_start);
				if (start == EPOS && last_start != r.end)
					last_start = start = r.end;
				end = text_mark_get(txt, mark_end);
				if (start == EPOS || end == EPOS) {
					ret = false;
					break;
				}
			}
		}
	} else {
		size_t start = range->start, end = range->end;
		while (start < end) {
			size_t next = text_line_next(txt, start);
			if (next > end)
				next = end;
			Filerange r = text_range_new(start, next);
			if (start == next || !text_range_valid(&r))
				break;
			start = next;
			Mark mark_start = text_mark_set(txt, start);
			Mark mark_end = text_mark_set(txt, end);
			ret &= sam_execute(vis, win, cmd->cmd, NULL, &r);
			start = text_mark_get(txt, mark_start);
			if (start == EPOS)
				start = r.end;
			end = text_mark_get(txt, mark_end);
			if (end == EPOS) {
				ret = false;
				break;
			}
		}
	}

	view_cursors_dispose(cur);
	return ret;
}
Example #17
0
File: vis.c Project: ewqasd200g/vis
void action_do(Vis *vis, Action *a) {
	Win *win = vis->win;
	Text *txt = win->file->text;
	View *view = win->view;

	if (a->op == &vis_operators[VIS_OP_FILTER] && !vis->mode->visual)
		vis_mode_switch(vis, VIS_MODE_VISUAL_LINE);

	int count = MAX(a->count, 1);
	bool repeatable = a->op && !vis->macro_operator;
	bool multiple_cursors = view_cursors_multiple(view);
	bool linewise = !(a->type & CHARWISE) && (
		a->type & LINEWISE || (a->movement && a->movement->type & LINEWISE) ||
		vis->mode == &vis_modes[VIS_MODE_VISUAL_LINE]);

	for (Cursor *cursor = view_cursors(view), *next; cursor; cursor = next) {

		next = view_cursors_next(cursor);
		size_t pos = view_cursors_pos(cursor);
		Register *reg = multiple_cursors ? view_cursors_register(cursor) : a->reg;
		if (!reg)
			reg = &vis->registers[win->file->internal ? VIS_REG_PROMPT : VIS_REG_DEFAULT];

		OperatorContext c = {
			.count = count,
			.pos = pos,
			.newpos = EPOS,
			.range = text_range_empty(),
			.reg = reg,
			.linewise = linewise,
			.arg = &a->arg,
		};

		if (a->movement) {
			size_t start = pos;
			for (int i = 0; i < count; i++) {
				size_t pos_prev = pos;
				if (a->movement->txt)
					pos = a->movement->txt(txt, pos);
				else if (a->movement->cur)
					pos = a->movement->cur(cursor);
				else if (a->movement->file)
					pos = a->movement->file(vis, vis->win->file, pos);
				else if (a->movement->vis)
					pos = a->movement->vis(vis, txt, pos);
				else if (a->movement->view)
					pos = a->movement->view(vis, view);
				else if (a->movement->win)
					pos = a->movement->win(vis, win, pos);
				else if (a->movement->user)
					pos = a->movement->user(vis, win, a->movement->data, pos);
				if (pos == EPOS || a->movement->type & IDEMPOTENT || pos == pos_prev)
					break;
			}

			if (pos == EPOS) {
				c.range.start = start;
				c.range.end = start;
				pos = start;
			} else {
				c.range = text_range_new(start, pos);
				c.newpos = pos;
			}

			if (!a->op) {
				if (a->movement->type & CHARWISE)
					view_cursors_scroll_to(cursor, pos);
				else
					view_cursors_to(cursor, pos);
				if (vis->mode->visual)
					c.range = view_cursors_selection_get(cursor);
				if (a->movement->type & JUMP)
					window_jumplist_add(win, pos);
				else
					window_jumplist_invalidate(win);
			} else if (a->movement->type & INCLUSIVE ||
			          (linewise && a->movement->type & LINEWISE_INCLUSIVE)) {
				c.range.end = text_char_next(txt, c.range.end);
			}
		} else if (a->textobj) {
			if (vis->mode->visual)
				c.range = view_cursors_selection_get(cursor);
			else
				c.range.start = c.range.end = pos;
			for (int i = 0; i < count; i++) {
				Filerange r = text_range_empty();
				if (a->textobj->txt)
					r = a->textobj->txt(txt, pos);
				else if (a->textobj->vis)
					r = a->textobj->vis(vis, txt, pos);
				else if (a->textobj->user)
					r = a->textobj->user(vis, win, a->textobj->data, pos);
				if (!text_range_valid(&r))
					break;
				if (a->textobj->type & OUTER) {
					r.start--;
					r.end++;
				}

				if (a->textobj->type & SPLIT)
					c.range = r;
				else
					c.range = text_range_union(&c.range, &r);

				if (i < count - 1)
					pos = c.range.end + 1;
			}
		} else if (vis->mode->visual) {
			c.range = view_cursors_selection_get(cursor);
			if (!text_range_valid(&c.range))
				c.range.start = c.range.end = pos;
		}

		if (linewise && vis->mode != &vis_modes[VIS_MODE_VISUAL])
			c.range = text_range_linewise(txt, &c.range);
		if (vis->mode->visual) {
			view_cursors_selection_set(cursor, &c.range);
			if (vis->mode == &vis_modes[VIS_MODE_VISUAL] || a->textobj)
				view_cursors_selection_sync(cursor);
		}

		if (a->op) {
			size_t pos = a->op->func(vis, txt, &c);
			if (pos == EPOS) {
				view_cursors_dispose(cursor);
			} else if (pos <= text_size(txt)) {
				/* moving the cursor will affect the selection.
				 * because we want to be able to later restore
				 * the old selection we update it again before
				 * leaving visual mode.
				 */
				Filerange sel = view_cursors_selection_get(cursor);
				view_cursors_to(cursor, pos);
				if (vis->mode->visual) {
					if (sel.start == EPOS && sel.end == EPOS)
						sel = c.range;
					else if (sel.start == EPOS)
						sel = text_range_new(c.range.start, sel.end);
					else if (sel.end == EPOS)
						sel = text_range_new(c.range.start, sel.start);
					if (vis->mode == &vis_modes[VIS_MODE_VISUAL_LINE])
						sel = text_range_linewise(txt, &sel);
					view_cursors_selection_set(cursor, &sel);
				}
			}
		}
	}

	if (a->op) {
		/* we do not support visual repeat, still do something resonable */
		if (vis->mode->visual && !a->movement && !a->textobj)
			a->movement = &vis_motions[VIS_MOVE_NOP];

		/* operator implementations must not change the mode,
		 * they might get called multiple times (once for every cursor)
		 */
		if (a->op == &vis_operators[VIS_OP_INSERT] || a->op == &vis_operators[VIS_OP_CHANGE]) {
			vis_mode_switch(vis, VIS_MODE_INSERT);
		} else if (a->op == &vis_operators[VIS_OP_REPLACE]) {
			vis_mode_switch(vis, VIS_MODE_REPLACE);
		} else if (a->op == &vis_operators[VIS_OP_FILTER]) {
			if (a->arg.s)
				vis_cmd(vis, a->arg.s);
			else
				vis_prompt_show(vis, ":|");
		} else if (vis->mode == &vis_modes[VIS_MODE_OPERATOR_PENDING]) {
			mode_set(vis, vis->mode_prev);
		} else if (vis->mode->visual) {
			vis_mode_switch(vis, VIS_MODE_NORMAL);
		}
		text_snapshot(txt);
		vis_draw(vis);
	}

	if (a != &vis->action_prev) {
		if (repeatable) {
			if (!a->macro)
				a->macro = vis->macro_operator;
			vis->action_prev = *a;
		}
		action_reset(a);
	}
}

void action_reset(Action *a) {
	memset(a, 0, sizeof(*a));
	a->count = VIS_COUNT_UNKNOWN;
}
Example #18
0
File: sam.c Project: ewqasd200g/vis
static Filerange address_evaluate(Address *addr, File *file, Filerange *range, int sign) {
	Filerange ret = text_range_empty();

	do {
		switch (addr->type) {
		case '#':
			if (sign > 0)
				ret.start = ret.end = range->end + addr->number;
			else if (sign < 0)
				ret.start = ret.end = range->start - addr->number;
			else
				ret = text_range_new(addr->number, addr->number);
			break;
		case 'l':
		case 'g':
			ret = address_line_evaluate(addr, file, range, sign);
			break;
		case '?':
			sign = sign == 0 ? -1 : -sign;
			/* fall through */
		case '/':
			if (sign >= 0)
				ret = text_object_search_forward(file->text, range->end, addr->regex);
			else
				ret = text_object_search_backward(file->text, range->start, addr->regex);
			break;
		case '$':
		{
			size_t size = text_size(file->text);
			ret = text_range_new(size, size);
			break;
		}
		case '.':
			ret = *range;
			break;
		case '+':
		case '-':
			sign = addr->type == '+' ? +1 : -1;
			if (!addr->right || addr->right->type == '+' || addr->right->type == '-')
				ret = address_line_evaluate(addr, file, range, sign);
			break;
		case ',':
		case ';':
		{
			Filerange left, right;
			if (addr->left)
				left = address_evaluate(addr->left, file, range, 0);
			else
				left = text_range_new(0, 0);

			if (addr->type == ';')
				range = &left;

			if (addr->right) {
				right = address_evaluate(addr->right, file, range, 0);
			} else {
				size_t size = text_size(file->text);
				right = text_range_new(size, size);
			}
			/* TODO: enforce strict ordering? */
			return text_range_union(&left, &right);
		}
		case '%':
			return text_range_new(0, text_size(file->text));
		}
		if (text_range_valid(&ret))
			range = &ret;
	} while ((addr = addr->right));

	return ret;
}