def _detect_tabs(nvim: Nvim) -> None: buf = cur_buf(nvim) count = buf_line_count(nvim, buf=buf) rows = min(count, 100) lines = buf_get_lines(nvim, buf=buf, lo=0, hi=rows) _set_tabsize(nvim, buf=buf, lines=lines) _set_usetab(nvim, buf=buf, lines=lines)
def _word(nvim: Nvim, is_inside: bool) -> None: win = cur_win(nvim) buf = win_get_buf(nvim, win=win) row, col = win_get_cursor(nvim, win=win) line, *_ = buf_get_lines(nvim, buf=buf, lo=row, hi=row + 1) bline = encode(line) lhs, rhs = decode(bline[:col]), decode(bline[col:]) ctx = gen_split(lhs, rhs, unifying_chars=UNIFIYING_CHARS) if not (ctx.word_lhs + ctx.word_rhs): words_lhs, words_rhs = ctx.syms_lhs, ctx.syms_rhs else: words_lhs, words_rhs = ctx.word_lhs, ctx.word_rhs c_lhs = max(col - len(encode(words_lhs)), 0) c_rhs = max(col + len(encode(words_rhs)) - 1, 0) if is_inside: mark1 = (row, c_lhs) mark2 = (row, c_rhs) else: mark1 = (row, max(0, c_lhs - 1)) mark2 = (row, min(len(bline), c_rhs + 1)) set_visual_selection(nvim, win=win, mode="v", mark1=mark1, mark2=mark2)
def _word(nvim: Nvim, is_inside: bool) -> None: win = cur_win(nvim) buf = win_get_buf(nvim, win=win) row, c = win_get_cursor(nvim, win=win) lines = buf_get_lines(nvim, buf=buf, lo=row, hi=row + 1) line = next(iter(lines)) bline = line.encode() # position under cursor c = min(len(bline) - 1, c) col = len(bline[:c].decode()) + 1 lhs, rhs = line[:col], line[col:] # undo col + 1 offset = len(next(reversed(lhs), "").encode()) ctx = gen_split(lhs, rhs, unifying_chars=UNIFIYING_CHARS) if not (ctx.word_lhs + ctx.word_rhs): words_lhs, words_rhs = ctx.syms_lhs, ctx.syms_rhs else: words_lhs, words_rhs = ctx.word_lhs, ctx.word_rhs c_lhs = c + offset - len(words_lhs.encode()) c_rhs = c + offset + len(words_rhs.encode()) - 1 if is_inside: mark1 = (row, c_lhs) mark2 = (row, c_rhs) else: mark1 = (row, c_lhs - 1) mark2 = (row, c_rhs + 1) set_visual_selection(nvim, win=win, mode="v", mark1=mark1, mark2=mark2)
def _surround(nvim: Nvim) -> None: lhs: str = nvim.vvars["char"] rhs = _CHAR_PAIRS.get(lhs) if rhs: win = cur_win(nvim) buf = win_get_buf(nvim, win=win) row, col = win_get_cursor(nvim, win=win) lines = buf_get_lines(nvim, buf=buf, lo=row, hi=row + 1) line = next(iter(lines)) def cont() -> None: new_col = col + len(lhs.encode()) nvim.api.set_vvar("char", lhs + cast(str, rhs)) set_cur = lambda: win_set_cursor( nvim, win=win, row=row, col=new_col) go(async_call(nvim, set_cur)) if rhs == lhs: is_even = line.count(lhs) % 2 == 0 if is_even: cont() else: counts = Counter(line) if counts[lhs] >= counts[rhs]: cont()
def _go_replace(nvim: Nvim, visual: VisualTypes) -> None: buf = cur_buf(nvim) if not writable(nvim, buf=buf): return else: linefeed = buf_linefeed(nvim, buf=buf) (r1, c1), (r2, c2) = operator_marks(nvim, buf=buf, visual_type=visual) lines = buf_get_lines(nvim, buf=buf, lo=r1, hi=r2 + 1) if len(lines) > 1: h, *_, t = lines else: h, *_ = t, *_ = lines begin = (r1, min(c1, max(0, len(encode(h)) - 1))) end = (r2, min(len(encode(t)), c2 + 1)) text: str = nvim.funcs.getreg() new_lines = text.split(linefeed) if new_lines: n = new_lines.pop() if n: new_lines.append(n) nvim.options["undolevels"] = nvim.options["undolevels"] buf_set_text(nvim, buf=buf, begin=begin, end=end, text=new_lines)
def _restore(nvim: Nvim, win: Window, buf: Buffer, pos: NvimPos) -> Tuple[str, Optional[int]]: row, _ = pos ns = create_ns(nvim, ns=NS) marks = tuple(buf_get_extmarks(nvim, buf=buf, id=ns)) if len(marks) != 2: return "", 0 else: m1, m2 = marks after, *_ = buf_get_lines(nvim, buf=buf, lo=row, hi=row + 1) cur_row, cur_col = win_get_cursor(nvim, win=win) (_, lo), (_, hi) = m1.end, m2.begin binserted = encode(after)[lo:hi] inserted = decode(binserted) movement = cur_col - lo if cur_row == row and lo <= cur_col <= hi else None if inserted: buf_set_text(nvim, buf=buf, begin=m1.end, end=m2.begin, text=("", )) return inserted, movement
def _parse(nvim: Nvim, buf: Buffer, stack: Stack, state: State, comp: Completion) -> Tuple[Edit, Sequence[Mark]]: if isinstance(comp.primary_edit, SnippetEdit): comment_str = buf_commentstr(nvim, buf=buf) clipboard = nvim.funcs.getreg() info = ParseInfo(visual="", clipboard=clipboard, comment_str=comment_str) if isinstance(comp.primary_edit, SnippetRangeEdit): row, col = comp.primary_edit.begin line, *_ = buf_get_lines(nvim, buf=buf, lo=row, hi=row + 1) line_before = decode( encode(line, encoding=comp.primary_edit.encoding)[:col]) edit, marks = parse_range( context=state.context, snippet=comp.primary_edit, info=info, line_before=line_before, ) else: edit, marks = parse_norm( stack.settings.match.unifying_chars, smart=stack.settings.completion.smart, context=state.context, snippet=comp.primary_edit, info=info, ) else: edit, marks = comp.primary_edit, () return edit, marks
def _entire(nvim: Nvim) -> None: win = cur_win(nvim) buf = win_get_buf(nvim, win=win) count = buf_line_count(nvim, buf=buf) last_line, *_ = buf_get_lines(nvim, buf=buf, lo=-2, hi=-1) mark1 = (0, 0) mark2 = (count - 1, len(encode(last_line))) set_visual_selection(nvim, win=win, mode="V", mark1=mark1, mark2=mark2)
def current_ctx(nvim: Nvim) -> Tuple[str, BufContext]: cwd = get_cwd(nvim) buf = cur_buf(nvim) filename = buf_name(nvim, buf=buf) filetype = buf_filetype(nvim, buf=buf) tabsize: int = buf_get_option(nvim, buf=buf, key="tabstop") lines: Sequence[str] = buf_get_lines(nvim, buf=buf, lo=0, hi=-1) return cwd, BufContext( buf=buf, filename=filename, filetype=filetype, tabsize=tabsize, lines=lines )
def _sort_lines(nvim: Nvim, visual: VisualTypes) -> None: buf = cur_buf(nvim) if not writable(nvim, buf=buf): return else: (row1, _), (row2, _) = operator_marks(nvim, buf=buf, visual_type=visual) lines = buf_get_lines(nvim, buf=buf, lo=row1, hi=row2 + 1) new_lines = sorted(lines, key=strxfrm) buf_set_lines(nvim, buf=buf, lo=row1, hi=row2 + 1, lines=new_lines)
def preview_preview(nvim: Nvim, stack: Stack, *_: str) -> str: win = next(_ls(nvim), None) if win: buf = win_get_buf(nvim, win=win) syntax = buf_get_option(nvim, buf=buf, key="syntax") lines = buf_get_lines(nvim, buf=buf, lo=0, hi=-1) nvim.exec_lua(f"{NAMESPACE}.{_bigger_preview.name}(...)", (syntax, lines)) escaped: str = nvim.api.replace_termcodes("<c-e>", True, False, True) return escaped
def _comment_single(nvim: Nvim) -> None: win = cur_win(nvim) buf = win_get_buf(nvim, win=win) if not writable(nvim, buf=buf): return else: row, _ = win_get_cursor(nvim, win=win) lines = buf_get_lines(nvim, buf=buf, lo=row, hi=row + 1) lhs, rhs = buf_commentstr(nvim, buf=buf) new_lines = _toggle_comment(lhs, rhs, lines=lines) buf_set_lines(nvim, buf=buf, lo=row, hi=row + 1, lines=new_lines)
def _indent(nvim: Nvim) -> None: win = cur_win(nvim) buf = win_get_buf(nvim, win) row, _ = win_get_cursor(nvim, win) tabsize: int = buf_get_option(nvim, buf=buf, key="tabstop") lines = buf_get_lines(nvim, buf=buf, lo=0, hi=-1) before, curr, after = lines[:row], lines[row], lines[row + 1:] init_lv = p_indent(curr, tabsize=tabsize) top = row - _p_inside(init_lv, tabsize=tabsize, lines=reversed(before)) btm = row + _p_inside(init_lv, tabsize=tabsize, lines=after) lines = deque(buf_get_lines(nvim, buf=buf, lo=top, hi=btm + 1)) while lines: if line := lines.popleft(): lines.appendleft(line) break else: top += 1
def _comment(nvim: Nvim, visual: VisualTypes) -> None: buf = cur_buf(nvim) if not writable(nvim, buf=buf): return else: (row1, _), (row2, _) = operator_marks(nvim, buf=buf, visual_type=visual) lines = buf_get_lines(nvim, buf=buf, lo=row1, hi=row2 + 1) lhs, rhs = buf_commentstr(nvim, buf=buf) new_lines = _toggle_comment(lhs, rhs, lines=lines) buf_set_lines(nvim, buf=buf, lo=row1, hi=row2 + 1, lines=new_lines)
def _line(nvim: Nvim, is_inside: bool) -> None: win = cur_win(nvim) buf = win_get_buf(nvim, win=win) row, _ = win_get_cursor(nvim, win=win) lines = buf_get_lines(nvim, buf=buf, lo=row, hi=row + 1) line = next(iter(lines)) lhs, rhs = (_p_inside if is_inside else _p_around)(line) set_visual_selection(nvim, win=win, mode="v", mark1=(row, lhs), mark2=(row, rhs))
def _rename(nvim: Nvim) -> None: win = cur_win(nvim) buf = win_get_buf(nvim, win=win) row, col = win_get_cursor(nvim, win=win) line, *_ = buf_get_lines(nvim, buf=buf, lo=row, hi=row + 1) b_line = encode(line) lhs, rhs = decode(b_line[:col]), decode(b_line[col:]) split = gen_split(lhs=lhs, rhs=rhs, unifying_chars=UNIFIYING_CHARS) word = split.word_lhs + split.word_rhs ans = ask(nvim, question=LANG("rename: "), default=word) if ans: nvim.lua.vim.lsp.buf.rename(ans)
def _get_selected(nvim: Nvim, buf: Buffer, visual_type: VisualTypes) -> str: (row1, col1), (row2, col2) = operator_marks(nvim, buf=buf, visual_type=visual_type) lines = buf_get_lines(nvim, buf=buf, lo=row1, hi=row2 + 1) if len(lines) == 1: return lines[0].encode()[col1:col2 + 1].decode() else: head = lines[0].encode()[col1:].decode() body = lines[1:-1] tail = lines[-1].encode()[:col2 + 1].decode() return linesep.join(chain((head, ), body, (tail, )))
def eval_snips( nvim: Nvim, stack: Stack, visual: bool, maybe_grammar: str = REPL_GRAMMAR, ) -> None: try: grammar = SnippetGrammar[maybe_grammar] except KeyError: grammar = SnippetGrammar.lsp log.warn("%s", "bad snippet grammar -- reverting to LSP") win = cur_win(nvim) buf = win_get_buf(nvim, win=win) line_count = buf_line_count(nvim, buf=buf) path = PurePath(normcase(buf_name(nvim, buf=buf))) comment_str = buf_commentstr(nvim, buf=buf) clipboard = nvim.funcs.getreg() info = ParseInfo(visual="", clipboard=clipboard, comment_str=comment_str) if visual: (lo, _), (hi, _) = operator_marks(nvim, buf=buf, visual_type=None) hi = min(line_count, hi + 1) else: lo, hi = 0, line_count lines = buf_get_lines(nvim, buf=buf, lo=lo, hi=hi) try: compiled = compile_one( stack, grammar=grammar, path=path, info=info, lines=enumerate(lines, start=lo + 1), ) except (LoadError, ParseError) as e: preview = str(e).splitlines() with hold_win_pos(nvim, win=win): set_preview(nvim, syntax="", preview=preview) write(nvim, LANG("snip parse fail")) else: preview = _pprn(compiled).splitlines() with hold_win_pos(nvim, win=win): set_preview(nvim, syntax="yaml", preview=preview) if preview: write(nvim, LANG("snip parse succ")) else: write(nvim, LANG("no snippets found"))
def _toggle_case(nvim: Nvim) -> None: win = cur_win(nvim) row, col = win_get_cursor(nvim, win=win) buf = win_get_buf(nvim, win=win) if writable(nvim, buf=buf): line, *_ = buf_get_lines(nvim, buf=buf, lo=row, hi=row + 1) bline = encode(line) before, after = bline[:col], bline[col:] if after: cur, *post = after pt = decode(bytes((cur, ))) swapped = _swap_case(pt) new = decode(before) + swapped + decode(bytes(post)) pos = len(before) + len(encode(swapped)) buf_set_lines(nvim, buf=buf, lo=row, hi=row + 1, lines=(new, )) win_set_cursor(nvim, win=win, row=row, col=pos)
def _toggle_case(nvim: Nvim) -> None: win = cur_win(nvim) row, col = win_get_cursor(nvim, win=win) buf = win_get_buf(nvim, win=win) if not writable(nvim, buf=buf): return else: line, *_ = buf_get_lines(nvim, buf=buf, lo=row, hi=row + 1) bline = line.encode() before, after = bline[:col], bline[col:] cur, *post = after pt = bytes((cur,)).decode() swapped = _swap_case(pt) new = before.decode() + swapped + bytes(post).decode() pos = len(before) + len(swapped.encode()) buf_set_lines(nvim, buf=buf, lo=row, hi=row + 1, lines=(new,)) win_set_cursor(nvim, win=win, row=row, col=pos)
def _indent(nvim: Nvim) -> None: win = cur_win(nvim) buf = win_get_buf(nvim, win) row, _ = win_get_cursor(nvim, win) tabsize: int = buf_get_option(nvim, buf=buf, key="tabstop") lines = buf_get_lines(nvim, buf=buf, lo=0, hi=-1) before, curr, after = lines[:row], lines[row], lines[row + 1:] init_lv = p_indent(curr, tabsize=tabsize) top = row - _p_inside(init_lv, tabsize=tabsize, lines=reversed(before)) btm = row + _p_inside(init_lv, tabsize=tabsize, lines=after) set_visual_selection(nvim, win=win, mode="V", mark1=(top, 0), mark2=(btm, 0))
def _set_trimmed(nvim: Nvim, win: Window, buf: Buffer) -> None: row, col = win_get_cursor(nvim, win=win) lines = buf_get_lines(nvim, buf=buf, lo=0, hi=-1) new_lines = [ decode(encode(line)[:col]) + decode(encode(line)[col:]).rstrip() if r == row else line.rstrip() for r, line in enumerate(lines) ] while new_lines: line = new_lines.pop() if line or len(new_lines) <= row: new_lines.append(line) break if len(new_lines) < len(lines): new_lines.append("") if new_lines != lines: buf_set_lines(nvim, buf=buf, lo=0, hi=-1, lines=new_lines) win_set_cursor(nvim, win=win, row=row, col=col)
def edit( nvim: Nvim, stack: Stack, state: State, metric: Metric, synthetic: bool, ) -> Optional[NvimPos]: win = cur_win(nvim) buf = win_get_buf(nvim, win=win) if buf.number != state.context.buf_id: log.warn("%s", "stale buffer") return None else: nvim.options["undolevels"] = nvim.options["undolevels"] if synthetic: inserted, movement = "", None else: inserted, movement = _restore(nvim, win=win, buf=buf, pos=state.context.position) try: primary, marks = _parse(nvim, buf=buf, stack=stack, state=state, comp=metric.comp) except (NvimError, ParseError) as e: primary, marks = metric.comp.primary_edit, () write(nvim, LANG("failed to parse snippet")) log.info("%s", e) lo, hi = _rows_to_fetch( state.context, primary, *metric.comp.secondary_edits, ) if lo < 0 or hi > state.context.line_count: log.warn("%s", pformat(("OUT OF BOUNDS", (lo, hi), metric))) return None else: limited_lines = buf_get_lines(nvim, buf=buf, lo=lo, hi=hi) lines = [*chain(repeat("", times=lo), limited_lines)] view = _lines(lines) instructions = _consolidate(*_instructions( state.context, unifying_chars=stack.settings.match.unifying_chars, smart=stack.settings.completion.smart, lines=view, primary=primary, secondary=metric.comp.secondary_edits, )) n_row, n_col = _cursor( state.context.position, instructions=instructions, ) if not synthetic: stack.idb.inserted(metric.instance.bytes, sort_by=metric.comp.sort_by) m_shift = apply(nvim, buf=buf, instructions=instructions) if inserted: try: buf_set_text( nvim, buf=buf, begin=(n_row, n_col), end=(n_row, n_col), text=(inserted, ), ) except NvimError as e: log.warn("%s", e) if movement is not None: try: win_set_cursor(nvim, win=win, row=n_row, col=n_col + movement) except NvimError as e: log.warn("%s", e) if marks: new_marks = tuple(_shift_marks(m_shift, marks=marks)) mark(nvim, settings=stack.settings, buf=buf, marks=new_marks) if DEBUG: log.debug( "%s", pformat(( (metric.comp.primary_edit, *metric.comp.secondary_edits), instructions, )), ) return n_row, n_col
def detect_tabs(nvim: Nvim, buf: Buffer) -> None: count = buf_line_count(nvim, buf=buf) rows = min(count, 100) lines = buf_get_lines(nvim, buf=buf, lo=0, hi=rows) _set_tabsize(nvim, buf=buf, lines=lines) _set_usetab(nvim, buf=buf, lines=lines)
def context( nvim: Nvim, db: BDB, options: MatchOptions, state: State, manual: bool ) -> Context: with Atomic() as (atomic, ns): ns.scr_col = atomic.call_function("screencol", ()) ns.buf = atomic.get_current_buf() ns.name = atomic.buf_get_name(0) ns.line_count = atomic.buf_line_count(0) ns.filetype = atomic.buf_get_option(0, "filetype") ns.commentstring = atomic.buf_get_option(0, "commentstring") ns.fileformat = atomic.buf_get_option(0, "fileformat") ns.tabstop = atomic.buf_get_option(0, "tabstop") ns.expandtab = atomic.buf_get_option(0, "expandtab") ns.cursor = atomic.win_get_cursor(0) atomic.commit(nvim) scr_col = ns.scr_col buf = cast(Buffer, ns.buf) (r, col) = cast(Tuple[int, int], ns.cursor) row = r - 1 pos = (row, col) buf_line_count = ns.line_count filename = normcase(cast(str, ns.name)) filetype = cast(str, ns.filetype) comment_str = cast(str, ns.commentstring) tabstop = ns.tabstop expandtab = cast(bool, ns.expandtab) linefeed = cast(Literal["\n", "\r", "\r\n"], LFfmt[cast(str, ns.fileformat)].value) lo = max(0, row - options.proximate_lines) hi = min(buf_line_count, row + options.proximate_lines + 1) lines = buf_get_lines(nvim, buf=buf, lo=lo, hi=hi) if DEBUG: db_line_count, db_lit = db.lines(buf.number, lo=lo, hi=hi) db_lines = tuple(db_lit) assert db_line_count in { buf_line_count - 1, buf_line_count, buf_line_count + 1, }, (db_line_count, buf_line_count) assert tuple( "" if idx == row else line for idx, line in enumerate(db_lines, start=lo) ) == tuple( "" if idx == row else line for idx, line in enumerate(lines, start=lo) ), linesep.join( unified_diff(lines, db_lines) ) r = row - lo line = lines[r] lines_before, lines_after = lines[:r], lines[r + 1 :] lhs, _, rhs = comment_str.partition("%s") b_line = encode(line) before, after = decode(b_line[:col]), decode(b_line[col:]) split = gen_split(lhs=before, rhs=after, unifying_chars=options.unifying_chars) ctx = Context( manual=manual, change_id=state.change_id, commit_id=state.commit_id, cwd=state.cwd, buf_id=buf.number, filename=filename, filetype=filetype, line_count=buf_line_count, linefeed=linefeed, tabstop=tabstop, expandtab=expandtab, comment=(lhs, rhs), position=pos, scr_col=scr_col, line=split.lhs + split.rhs, line_before=split.lhs, line_after=split.rhs, lines=lines, lines_before=lines_before, lines_after=lines_after, words=split.word_lhs + split.word_rhs, words_before=split.word_lhs, words_after=split.word_rhs, syms=split.syms_lhs + split.syms_rhs, syms_before=split.syms_lhs, syms_after=split.syms_rhs, ws_before=split.ws_lhs, ws_after=split.ws_rhs, ) return ctx
def _comp_done(nvim: Nvim, stack: Stack, event: Mapping[str, Any]) -> None: data = event.get("user_data") if data: try: uid = _UDECODER(data) except DecodeError: pass else: s = state() if metric := stack.metrics.get(uid): row, col = s.context.position buf = cur_buf(nvim) ns = create_ns(nvim, ns=NS) clear_ns(nvim, buf=buf, id=ns) before, *_ = buf_get_lines(nvim, buf=buf, lo=row, hi=row + 1) e1 = ExtMark( idx=1, begin=(row, 0), end=(row, col), meta={}, ) e2 = ExtMark( idx=2, begin=(row, col), end=(row, len(encode(before))), meta={}, ) buf_set_extmarks(nvim, buf=buf, id=ns, marks=(e1, e2)) async def cont() -> None: if metric: new_metric = await _resolve(nvim, stack=stack, metric=metric) async def c2() -> None: if isinstance((extern := new_metric.comp.extern), ExternLSP): await cmd(nvim, extern=extern) def c1() -> None: if new_metric.comp.uid in stack.metrics: inserted_at = edit( nvim, stack=stack, state=s, metric=new_metric, synthetic=False, ) ins_pos = inserted_at or (-1, -1) state( inserted_pos=ins_pos, last_edit=new_metric, commit_id=uuid4(), ) go(nvim, aw=c2()) else: log.warn("%s", "delayed completion") await async_call(nvim, c1) go(nvim, aw=cont())