def _click(nvim: Nvim, state: State, settings: Settings, is_visual: bool, click_type: ClickType) -> Optional[Stage]: node = next(indices(nvim, state=state, is_visual=is_visual), None) if not node: return None else: if Mode.orphan_link in node.mode: name = node.name write(nvim, LANG("dead_link", name=name), error=True) return None else: if is_dir(node): if node.path == state.root.path: return None elif state.filter_pattern: write(nvim, LANG("filter_click")) return None else: paths = {node.path} index = state.index ^ paths new_state = forward(state, settings=settings, index=index, paths=paths) return Stage(new_state) else: nxt = open_file( nvim, state=state, settings=settings, path=node.path, click_type=click_type, ) return nxt
def cont() -> bool: assert isinstance(nvim.loop, AbstractEventLoop) nvim.loop.set_default_executor(self._pool) atomic, specs = rpc.drain(nvim.channel_id) self._handlers.update(specs) try: self._settings = initial_settings(nvim, specs) except DecodeError as e: tpl = """ Some options may hanve changed. See help doc on Github under [docs/CONFIGURATION.md] ${e} """ ms = Template(dedent(tpl)).substitute(e=e) write(nvim, ms, error=True) return False else: hl = highlight(*self._settings.view.hl_context.groups) (atomic + autocmd.drain() + hl).commit(nvim) self._state = initial_state( nvim, pool=self._pool, settings=self._settings ) init_locale(self._settings.lang) return True
def c2() -> None: def it() -> Iterator[Tuple[Window, Tuple[int, int]]]: wins = list_wins(nvim) for win in wins: buf = win_get_buf(nvim, win=win) if buf == ctx.buf: row, col = win_get_cursor(nvim, win) yield win, (row, col) saved = {win: pos for win, pos in it()} lines = temp.read_text().split(ctx.linefeed) if lines: l = lines.pop() if l: lines.append(l) buf_set_lines(nvim, buf=ctx.buf, lo=0, hi=-1, lines=lines) for win, (row, col) in saved.items(): new_row = min(row, len(lines) - 1) with suppress(NvimError): win_set_cursor(nvim, win=win, row=new_row, col=col) detect_tabs(nvim, buf=ctx.buf) prettiers = LANG("step join sep").join(attr.bin for attr in attrs) nice = LANG("prettier succeeded", prettiers=prettiers) write(nvim, nice)
def _help(nvim: Nvim, state: State, settings: Settings, args: Sequence[str]) -> None: """ Open help doc """ try: topic, use_web = _parse_args(args) except ArgparseError as e: write(nvim, e, error=True) else: md, uri = _directory(topic) web_d = open_w(uri) if use_web else False if not web_d: for win in list_floatwins(nvim): win_close(nvim, win=win) lines = md.read_text("UTF-8").splitlines() buf = create_buf(nvim, listed=False, scratch=True, wipe=True, nofile=True, noswap=True) buf_set_lines(nvim, buf=buf, lo=0, hi=-1, lines=lines) buf_set_option(nvim, buf=buf, key="modifiable", val=False) buf_set_option(nvim, buf=buf, key="syntax", val="markdown") open_float_win(nvim, margin=0, relsize=0.95, buf=buf, border="rounded")
def cont() -> bool: rpc_atomic, specs = rpc.drain(nvim.channel_id) self._handlers.update(specs) try: self._stack = stack(self._pool, nvim=nvim) except (DecodeError, ValidationError) as e: tpl = """ Some options may have changed. See help doc on Github under [docs/CONFIGURATION.md] ⚠️ ${e} """ msg = Template(dedent(tpl)).substitute(e=e) write(nvim, msg, error=True) return False else: (rpc_atomic + autocmd.drain() + atomic).commit(nvim) set_options( nvim, mapping=self._stack.settings.keymap, fast_close=self._stack.settings.display.pum.fast_close, ) return True
def _single_mark( nvim: Nvim, mark: ExtMark, marks: Sequence[ExtMark], ns: int, win: Window, buf: Buffer, ) -> None: row, col = mark.begin nvim.options["undolevels"] = nvim.options["undolevels"] try: apply(nvim, buf=buf, instructions=_trans("", marks=(mark, ))) win_set_cursor(nvim, win=win, row=row, col=col) nvim.command("startinsert") except NvimError as e: msg = f""" bad mark location {mark} {e} """ log.warn("%s", dedent(msg)) else: nvim.command("startinsert") state(inserted_pos=(row, col)) msg = LANG("applied mark", marks_left=len(marks)) write(nvim, msg) finally: _del_marks(nvim, buf=buf, id=ns, marks=(mark, ))
def _remove( nvim: Nvim, state: State, settings: Settings, is_visual: bool, yeet: Callable[[Executor, Iterable[PurePath]], None], ) -> Optional[Stage]: cwd, root = get_cwd(nvim), state.root.path nono = {cwd, root} | ancestors(cwd) | ancestors(root) selection = state.selection or { node.path for node in indices(nvim, state=state, is_visual=is_visual) } unified = unify_ancestors(selection) if not unified: return None elif not unified.isdisjoint(nono): write(nvim, LANG("operation not permitted on root"), error=True) return None else: display_paths = linesep.join( sorted((display_path(path, state=state) for path in unified), key=strxfrm)) question = LANG("ask_trash", display_paths=display_paths) ans = ask_mc( nvim, question=question, answers=LANG("ask_yesno"), answer_key={ 1: True, 2: False }, ) if not ans: return None else: try: yeet(state.pool, unified) except Exception as e: write(nvim, e, error=True) return refresh(nvim, state=state, settings=settings) else: paths = {path.parent for path in unified} new_state = forward(state, settings=settings, selection=set(), paths=paths) kill_buffers(nvim, last_used=new_state.window_order, paths=selection, reopen={}) lsp_removed(nvim, paths=unified) return Stage(new_state)
def open_term(nvim: Nvim, *args: AnyPath, opts: TermOpts = {}) -> None: argv = args or (environ["SHELL"],) prog, *_ = argv if not which(prog): write(nvim, LANG("invaild command: ", cmd=normcase(prog)), error=True) else: for win in list_floatwins(nvim): win_close(nvim, win=win) _term_open(nvim, *argv, opts=opts)
def _toggle_follow( nvim: Nvim, state: State, settings: Settings, is_visual: bool ) -> Stage: """ Toggle follow """ new_state = forward(state, settings=settings, follow=not state.follow) write(nvim, LANG("follow_mode_indi", follow=str(new_state.follow))) return Stage(new_state)
def _toggle_version_control( nvim: Nvim, state: State, settings: Settings, is_visual: bool ) -> Stage: """ Toggle version control """ enable_vc = not state.enable_vc vc: Union[VoidType, VCStatus] = Void if enable_vc else VCStatus() new_state = forward(state, settings=settings, enable_vc=enable_vc, vc=vc) write(nvim, LANG("version_control_indi", enable_vc=str(new_state.enable_vc))) return Stage(new_state)
def _new(nvim: Nvim, state: State, settings: Settings, is_visual: bool) -> Optional[Stage]: """ new file / folder """ node = next(indices(nvim, state=state, is_visual=is_visual), None) if not node: return None else: parent = node.path if is_dir(node) else dirname(node.path) child: Optional[str] = nvim.funcs.input(LANG("pencil")) if not child: return None else: path = abspath(join(parent, child)) if child.endswith(sep): path = path + sep if exists(path): write(nvim, LANG("already_exists", name=path), error=True) return None else: try: new((path, )) except Exception as e: write(nvim, e, error=True) return refresh(nvim, state=state, settings=settings) else: new_parent = dirname(path) if new_parent in state.root.ancestors: new_state = new_root(nvim, state=state, settings=settings, new_cwd=new_parent) else: paths = ancestors(path) index = state.index | paths new_state = forward(state, settings=settings, index=index, paths=paths) nxt = open_file( nvim, state=new_state, settings=settings, path=path, click_type=ClickType.secondary, ) return nxt
def _profile(nvim: Nvim, t1: float) -> None: t2 = monotonic() info = uname() msg = f""" First msg {int((t2 - t1) * 1000)}ms Arch {info.machine} Processor {info.processor} Cores {cpu_count()} System {info.system} Version {info.version} Python {Path(executable).resolve(strict=True)} """ write(nvim, dedent(msg))
def _open( nvim: Nvim, state: State, settings: Settings, args: Sequence[str] ) -> Optional[Stage]: """ Toggle sidebar """ try: opts = _parse_args(args) except ArgparseError as e: write(nvim, e, error=True) return None else: if opts.version_ctl: if which("git"): try: cwd = version_ctl_toplv(state.root.path) new_state = new_root( nvim, state=state, settings=settings, new_cwd=cwd, indices=set() ) except CalledProcessError: write(nvim, LANG("cannot find version ctl root"), error=True) return None else: write(nvim, LANG("cannot find version ctl root"), error=True) return None else: new_state = state raw_path = opts.path if raw_path: path = realpath( raw_path if isabs(raw_path) else join(get_cwd(nvim), raw_path) ) if not exists(path): write(nvim, LANG("path not exist", path=path)) return None else: next_state = ( maybe_path_above( nvim, state=new_state, settings=settings, path=path ) or new_state ) _open_fm_window( nvim, settings=settings, opts=opts, width=next_state.width ) return Stage(next_state, focus=path) else: curr = find_current_buffer_name(nvim) stage = new_current_file( nvim, state=new_state, settings=settings, current=curr ) _open_fm_window(nvim, settings=settings, opts=opts, width=new_state.width) return ( Stage(stage.state, focus=curr) if stage else Stage(new_state, focus=curr) )
def now(nvim: Nvim, stack: Stack, args: Sequence[str]) -> None: try: ns = _parse_args(args) except ArgparseError as e: write(nvim, e, error=True) else: if not ns.shut_up: lo, hi = _HELO.chars chars = choice(range(lo, hi)) star = (choice(_HELO.stars),) birds = " ".join(chain(star, sample(_HELO.cocks, k=chars), star)) helo = choice(_HELO.helo) msg = f"{birds} {helo}{linesep}" encoded = encode(msg) stdout.buffer.write(encoded) stdout.buffer.flush()
def _change_dir(nvim: Nvim, state: State, settings: Settings, is_visual: bool) -> Optional[Stage]: """ Change root directory """ node = next(indices(nvim, state=state, is_visual=is_visual), None) if not node: return None else: cwd = node.path if is_dir(node) else dirname(node.path) new_state = new_root(nvim, state=state, settings=settings, new_cwd=cwd) focus = new_state.root.path nvim.command(f"chdir {focus}") write(nvim, LANG("new cwd", cwd=focus)) return Stage(new_state, focus=focus)
def _rename( nvim: Nvim, state: State, settings: Settings, is_visual: bool ) -> Optional[Stage]: """ rename file / folder """ node = next(indices(nvim, state=state, is_visual=is_visual), None) if not node: return None else: prev_name = node.path parent = state.root.path rel_path = relpath(prev_name, start=parent) child: Optional[str] = nvim.funcs.input(LANG("pencil"), rel_path) if not child: return None else: new_name = abspath(join(parent, child)) new_parent = dirname(new_name) if exists(new_name): write(nvim, LANG("already_exists", name=new_name), error=True) return None else: try: rename({prev_name: new_name}) except Exception as e: write(nvim, e, error=True) return refresh(nvim, state=state, settings=settings) else: if new_parent in state.root.ancestors: new_state = new_root( nvim, state=state, settings=settings, new_cwd=new_parent ) return Stage(new_state, focus=new_name) else: paths = { *chain((parent,), (new_parent,), ancestors(new_parent)) } index = state.index | paths new_state = forward( state, settings=settings, index=index, paths=paths ) kill_buffers(nvim, paths={prev_name}) return Stage(new_state, focus=new_name)
def _remove( nvim: Nvim, state: State, settings: Settings, is_visual: bool, yeet: Callable[[Iterable[str]], None], ) -> Optional[Stage]: cwd, root = get_cwd(nvim), state.root.path nono = {cwd, root} | ancestors(cwd) | ancestors(root) selection = state.selection or { node.path for node in indices(nvim, state=state, is_visual=is_visual) } unified = unify_ancestors(selection) if not unified: return None elif not unified.isdisjoint(nono): write(nvim, LANG("operation not permitted on root"), error=True) return None else: display_paths = linesep.join( sorted((display_path(path, state=state) for path in unified), key=strxfrm)) question = LANG("ask_trash", display_paths=display_paths) resp: int = nvim.funcs.confirm(question, LANG("ask_yesno"), 2) ans = resp == 1 if not ans: return None else: try: yeet(unified) except Exception as e: write(nvim, e, error=True) return refresh(nvim, state=state, settings=settings) else: paths = {dirname(path) for path in unified} new_state = forward(state, settings=settings, selection=set(), paths=paths) kill_buffers(nvim, paths=selection) return Stage(new_state)
def _rename(nvim: Nvim, state: State, settings: Settings, is_visual: bool) -> Optional[Stage]: """ rename file / folder """ node = next(indices(nvim, state=state, is_visual=is_visual), None) if not node: return None else: child = ask(nvim, question=LANG("pencil"), default=str(node.path.name)) if not child: return None else: new_path = PurePath(abspath(node.path.parent / child)) operations = {node.path: new_path} if exists(new_path, follow=False): write(nvim, LANG("already_exists", name=str(new_path)), error=True) return None else: try: rename(state.pool, operations=operations) except Exception as e: write(nvim, e, error=True) return refresh(nvim, state=state, settings=settings) else: new_state = (maybe_path_above( nvim, state=state, settings=settings, path=new_path) or state) paths = ancestors(new_path) index = state.index | paths next_state = forward(new_state, settings=settings, index=index, paths=paths) kill_buffers( nvim, last_used=new_state.window_order, paths={node.path}, reopen={node.path: new_path}, ) lsp_moved(nvim, paths=operations) return Stage(next_state, focus=new_path)
def _cn(nvim: Nvim, state: State, is_visual: bool, al_qaeda: bool) -> None: def gen_paths() -> Iterator[str]: selection = state.selection if not selection: nodes = indices(nvim, state=state, is_visual=is_visual) for node in nodes: yield basename(node.path) if al_qaeda else node.path else: for path in selection: yield basename(path) if al_qaeda else path paths = sorted(gen_paths(), key=strxfrm) clip = linesep.join(paths) copied_paths = ", ".join(paths) nvim.funcs.setreg("+", clip) nvim.funcs.setreg("*", clip) write(nvim, LANG("copy_paths", copied_paths=copied_paths))
def _change_dir(nvim: Nvim, state: State, settings: Settings, is_visual: bool) -> Optional[Stage]: """ Change root directory """ node = next(indices(nvim, state=state, is_visual=is_visual), None) if not node: return None else: cwd = node.path if is_dir(node) else node.path.parent new_state = new_root(nvim, state=state, settings=settings, new_cwd=cwd, indices=set()) chdir(nvim, path=new_state.root.path) write(nvim, LANG("new cwd", cwd=normcase(new_state.root.path))) return Stage(new_state, focus=new_state.root.path)
def _new(nvim: Nvim, state: State, settings: Settings, is_visual: bool) -> Optional[Stage]: """ new file / folder """ node = next(indices(nvim, state=state, is_visual=is_visual), None) if not node: return None else: parent = node.path if is_dir(node) else node.path.parent child = ask(nvim, question=LANG("pencil"), default="") if not child: return None else: path = PurePath(abspath(parent / child)) if exists(path, follow=False): write(nvim, LANG("already_exists", name=str(path)), error=True) return None else: try: if child.endswith(sep): mkdir(state.pool, paths=(path, )) else: new(state.pool, paths=(path, )) except Exception as e: write(nvim, e, error=True) return refresh(nvim, state=state, settings=settings) else: new_state = (maybe_path_above( nvim, state=state, settings=settings, path=path) or state) paths = ancestors(path) index = state.index | paths next_state = forward(new_state, settings=settings, index=index, paths=paths) lsp_created(nvim, paths=(path, )) return Stage(next_state, focus=path)
def _new( nvim: Nvim, state: State, settings: Settings, is_visual: bool ) -> Optional[Stage]: """ new file / folder """ node = next(indices(nvim, state=state, is_visual=is_visual), None) if not node: return None else: parent = node.path if is_dir(node) else dirname(node.path) child = ask(nvim, question=LANG("pencil"), default="") if not child: return None else: path = abspath(join(parent, child)) if exists(path): write(nvim, LANG("already_exists", name=path), error=True) return None else: try: dest = path + sep if child.endswith(sep) else path new((dest,)) except Exception as e: write(nvim, e, error=True) return refresh(nvim, state=state, settings=settings) else: new_state = ( maybe_path_above( nvim, state=state, settings=settings, path=path ) or state ) paths = ancestors(path) index = state.index | paths next_state = forward( new_state, settings=settings, index=index, paths=paths ) return Stage(next_state, focus=path)
def mark(nvim: Nvim, settings: Settings, buf: Buffer, marks: Sequence[Mark]) -> None: emarks = tuple( ExtMark( idx=mark.idx + 1, begin=mark.begin, end=mark.end, meta={"hl_group": settings.display.mark_highlight_group}, ) for mark in marks) ns = create_ns(nvim, ns=NS) clear_ns(nvim, buf=buf, id=ns) try: buf_set_extmarks(nvim, buf=buf, id=ns, marks=emarks) except NvimError: log.warn("%s", f"bad mark locations {marks}") else: msg = LANG("added marks", regions=" ".join(f"[{mark.text}]" for mark in marks)) write(nvim, msg)
def _rename(nvim: Nvim, state: State, settings: Settings, is_visual: bool) -> Optional[Stage]: """ rename file / folder """ node = next(indices(nvim, state=state, is_visual=is_visual), None) if not node: return None else: prev_name = node.path parent = state.root.path rel_path = relpath(prev_name, start=parent) child = ask(nvim, question=LANG("pencil"), default=rel_path) if not child: return None else: new_name = abspath(join(parent, child)) if exists(new_name): write(nvim, LANG("already_exists", name=new_name), error=True) return None else: try: rename({prev_name: new_name}) except Exception as e: write(nvim, e, error=True) return refresh(nvim, state=state, settings=settings) else: new_state = (maybe_path_above( nvim, state=state, settings=settings, path=new_name) or state) paths = ancestors(new_name) index = state.index | paths next_state = forward(new_state, settings=settings, index=index, paths=paths) kill_buffers(nvim, paths={prev_name}) return Stage(next_state, focus=new_name)
def _open( nvim: Nvim, state: State, settings: Settings, args: Sequence[str] ) -> Optional[Stage]: """ Toggle sidebar """ try: opts = _parse_args(args) except ArgparseError as e: write(nvim, e, error=True) return None else: curr = find_current_buffer_name(nvim) _toggle_fm_window(nvim, state=state, settings=settings, opts=opts) stage = new_current_file(nvim, state=state, settings=settings, current=curr) if stage: return stage else: return Stage(state)
def _copy_name(nvim: Nvim, state: State, settings: Settings, is_visual: bool) -> None: """ Copy dirname / filename """ def gen_paths() -> Iterator[str]: selection = state.selection if not selection: nodes = indices(nvim, state=state, is_visual=is_visual) for node in nodes: yield node.path else: yield from selection paths = sorted(gen_paths(), key=strxfrm) clip = linesep.join(paths) copied_paths = ", ".join(paths) nvim.funcs.setreg("+", clip) nvim.funcs.setreg("*", clip) write(nvim, LANG("copy_paths", copied_paths=copied_paths))
def _stat(nvim: Nvim, state: State, settings: Settings, is_visual: bool) -> None: """ Print file stat to cmdline """ node = next(indices(nvim, state=state, is_visual=is_visual), None) if not node: return None else: try: stat = fs_stat(node.path) except Exception as e: write(nvim, e, error=True) else: permissions = stat.permissions size = si_prefixed(stat.size, precision=2) user = stat.user group = stat.group mtime = stat.date_mod.strftime(settings.view.time_fmt) name = display_path(node.path, state=state) full_name = f"{name} -> {stat.link}" if stat.link else name mode_line = f"{permissions} {size}b {user} {group} {mtime} {full_name}" write(nvim, mode_line)
def cont() -> None: nonlocal has_drawn stage = cast(AnyFun[Optional[Stage]], handler)(nvim, self._state, settings, *args) if stage: self._state = stage.state mgr = suppress( NvimError) if stage.silent else nullcontext() with mgr: redraw(nvim, state=self._state, focus=stage.focus) if settings.profiling and not has_drawn: has_drawn = True t2 = monotonic() info = uname() msg = f""" First msg {int((t2 - t1) * 1000)}ms Arch {info.machine} Processor {info.processor} Cores {cpu_count()} System {info.system} Version {info.version} Python {Path(executable).resolve()} """ write(nvim, dedent(msg))
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 with_manual(nvim: Nvim) -> Iterator[None]: write(nvim, LANG("hourglass")) yield None write(nvim, LANG("ok_sym"))