def _find_root(nvim: Nvim, _pattern: Any, filename: str, bufnr: int) -> Optional[str]: pattern: Optional[RootPattern] = _DECODER(_pattern) path = Path(filename) if not pattern: return str(get_cwd(nvim)) else: for parent in path.parents: for member in parent.iterdir(): name = member.name if name in pattern.exact: return str(parent) else: for glob in pattern.glob: if fnmatch(name, glob): return str(parent) else: if pattern.fallback is RPFallback.none: return None elif pattern.fallback is RPFallback.cwd: return str(get_cwd(nvim)) elif pattern.fallback is RPFallback.home: return str(Path.home()) elif pattern.fallback is RPFallback.parent: return str(path.parent) else: never(pattern)
def _sys_trash(nvim: Nvim) -> Callable[[Iterable[str]], None]: cwd = get_cwd(nvim) def cont(paths: Iterable[str]) -> None: def c1() -> None: cmd = "trash" if which(cmd): command = (cmd, "--", *paths) check_call(command, stdin=DEVNULL, stdout=PIPE, stderr=PIPE, cwd=cwd) else: raise LookupError(LANG("sys_trash_err")) def c2() -> None: try: c1() except (CalledProcessError, LookupError) as e: threadsafe_call(nvim, write, nvim, e, error=True) except Exception as e: log.exception("%s", e) else: enqueue_event(_refresh, True) pool.submit(c2) return cont
def it() -> Iterator[PurePath]: cwd = get_cwd(nvim) for q in nvim.funcs.getqflist(): bufnr = q["bufnr"] bufname = nvim.funcs.bufname(bufnr) if resolved := resolve_path(cwd, path=bufname): yield resolved
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 _new_cwd(nvim: Nvim, stack: Stack) -> None: cwd = get_cwd(nvim) async def cont() -> None: s = state(cwd=cwd) await stack.ctdb.swap(s.cwd) go(nvim, aw=cont())
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 _changedir(nvim: Nvim, state: State, settings: Settings) -> Stage: """ Follow cwd update """ cwd = get_cwd(nvim) chdir(cwd) new_state = new_root(nvim, state=state, settings=settings, new_cwd=cwd) return Stage(new_state)
def _refocus(nvim: Nvim, state: State, settings: Settings, is_visual: bool) -> Stage: """ Follow cwd update """ cwd = get_cwd(nvim) new_state = new_root(nvim, state=state, settings=settings, new_cwd=cwd) focus = new_state.root.path return Stage(new_state, focus=focus)
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 initial(nvim: Nvim, pool: Executor, settings: Settings) -> State: cwd = get_cwd(nvim) session_store = (Path(nvim.funcs.stdpath("cache")) / "chad_sessions" if settings.xdg else SESSION_DIR) session = (load_session(cwd, session_store=session_store) if settings.session else None) index = session.index if session and session.index is not None else {cwd} show_hidden = (session.show_hidden if session and session.show_hidden is not None else settings.show_hidden) enable_vc = (session.enable_vc if session and session.enable_vc is not None else settings.version_ctl.enable) selection: Selection = set() node = new(pool, root=cwd, index=index) marks = markers(nvim) vc = VCStatus() current = None filter_pattern = None derived = render( node, settings=settings, index=index, selection=selection, filter_pattern=filter_pattern, markers=marks, vc=vc, show_hidden=show_hidden, current=current, ) state = State( pool=pool, session_store=session_store, index=index, selection=selection, filter_pattern=filter_pattern, show_hidden=show_hidden, follow=settings.follow, enable_vc=enable_vc, width=settings.width, root=node, markers=marks, vc=vc, current=current, derived=derived, window_order={}, ) return state
def stack(pool: Executor, nvim: Nvim) -> Stack: settings = _settings(nvim) pum_width = nvim.options["pumwidth"] vars_dir = Path( nvim.funcs.stdpath("cache")) / "coq" if settings.xdg else VARS s = state(cwd=get_cwd(nvim), pum_width=pum_width) bdb, sdb, idb, tdb, ctdb, tmdb = ( BDB(pool), SDB(pool, vars_dir=vars_dir), IDB(pool), TDB(pool), CTDB(pool, vars_dir=vars_dir, cwd=s.cwd), TMDB(pool), ) reviewer = Reviewer( icons=settings.display.icons, options=settings.match, db=idb, ) supervisor = Supervisor( pool=pool, nvim=nvim, vars_dir=vars_dir, match=settings.match, comp=settings.completion, limits=settings.limits, reviewer=reviewer, ) workers = { *_from_each_according_to_their_ability( settings, bdb=bdb, sdb=sdb, tdb=tdb, ctdb=ctdb, tmdb=tmdb, supervisor=supervisor, ) } stack = Stack( settings=settings, lru=LRU(size=settings.match.max_results), metrics={}, bdb=bdb, sdb=sdb, idb=idb, tdb=tdb, ctdb=ctdb, tmdb=tmdb, supervisor=supervisor, workers=workers, ) return stack
def initial(nvim: Nvim, settings: Settings) -> State: cwd = get_cwd(nvim) session = load_session(cwd, use_xdg=settings.xdg) if settings.session else None index = session.index if session and session.index is not None else {cwd} show_hidden = ( session.show_hidden if session and session.show_hidden is not None else settings.show_hidden ) enable_vc = ( session.enable_vc if session and session.enable_vc is not None else settings.version_ctl.enable ) selection: Selection = set() node = new(cwd, index=index) qf = quickfix(nvim) vc = VCStatus() current = None filter_pattern = None derived = render( node, settings=settings, index=index, selection=selection, filter_pattern=filter_pattern, qf=qf, vc=vc, show_hidden=show_hidden, current=current, ) state = State( index=index, selection=selection, filter_pattern=filter_pattern, show_hidden=show_hidden, follow=settings.follow, enable_vc=enable_vc, width=settings.width, root=node, qf=qf, vc=vc, current=current, derived=derived, ) return state
def quickfix(nvim: Nvim) -> QuickFix: cwd = get_cwd(nvim) ql = nvim.funcs.getqflist() def it() -> Iterator[str]: for q in ql: bufnr = q["bufnr"] filename = nvim.funcs.bufname(bufnr) yield join(cwd, filename) filenames = tuple(it()) parents = (ancestor for fullname in filenames for ancestor in ancestors(fullname)) locations = Counter(chain(filenames, parents)) qf = QuickFix(locations=locations) return qf
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 _cut(nvim: Nvim, state: State, settings: Settings, is_visual: bool) -> Optional[Stage]: """ Cut selected """ cwd, root = get_cwd(nvim), state.root.path nono = {cwd, root} | ancestors(cwd) | ancestors(root) return _operation( nvim, state=state, settings=settings, is_visual=is_visual, nono=nono, op_name=LANG("cut"), action=cut, )
def _open_sys(nvim: Nvim, state: State, settings: Settings, is_visual: bool) -> None: """ Open using finder / dolphin, etc """ node = next(indices(nvim, state=state, is_visual=is_visual), None) if not node: return None else: cwd = get_cwd(nvim) def cont() -> None: try: _open_gui(cast(Node, node).path, cwd=cwd) except (CalledProcessError, LookupError) as e: threadsafe_call(nvim, write, nvim, e, error=True) except Exception as e: log.exception("%s", e) pool.submit(cont)
def vc_refresh(nvim: Nvim, state: State, settings: Settings) -> None: """ VC Refresh """ if state.enable_vc: cwd = get_cwd(nvim) def cont() -> None: if _lock.locked(): pass else: with _lock: try: vc = status(state.pool, cwd=cwd) except Exception as e: log.exception("%s", e) else: enqueue_event(_set_vc, vc) state.pool.submit(cont)
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 if opts.path: path = opts.path if opts.path.is_absolute( ) else get_cwd(nvim) / opts.path if not exists(path, follow=True): new(state.pool, paths=(path, )) next_state = (maybe_path_above( nvim, state=new_state, settings=settings, path=path) or new_state) _open_fm_window( nvim, settings=settings, opts=opts, window_order=new_state.window_order, width=next_state.width, ) open_file( nvim, state=state, settings=settings, path=path, click_type=ClickType.primary, ) return Stage(next_state, focus=path) else: curr = find_current_buffer_path(nvim) stage = (new_current_file( nvim, state=new_state, settings=settings, current=curr) if curr else None) _open_fm_window( nvim, settings=settings, opts=opts, window_order=new_state.window_order, width=new_state.width, ) return (Stage(stage.state, focus=curr) if stage else Stage(new_state, focus=curr))
def snips(nvim: Nvim, stack: Stack, args: Sequence[str]) -> None: buf = cur_buf(nvim) ft = buf_filetype(nvim, buf=buf) try: ns = _parse_args(args, filetype=ft or "*") except ArgparseError as e: write(nvim, e, error=True) else: if ns.action == "ls": cwd = get_cwd(nvim) async def c1() -> None: _, mtimes = await user_mtimes( nvim, user_path=stack.settings.clients.snippets.user_path ) preview = tuple( fmt_path(cwd, path=path, is_dir=False) for path in sorted(mtimes, key=pathsort_key) ) def cont() -> None: if mtimes: set_preview(nvim, syntax="", preview=preview) else: write(nvim, LANG("no snippets found")) await async_call(nvim, cont) go(nvim, aw=c1()) elif ns.action == "cd": async def c2() -> None: paths = await snippet_paths( nvim, user_path=stack.settings.clients.snippets.user_path ) if paths: path, *_ = paths path.mkdir(parents=True, exist_ok=True) await async_call(nvim, lambda: chdir(nvim, path)) else: assert False go(nvim, aw=c2()) elif ns.action == "compile": async def c3() -> None: await awrite(nvim, LANG("waiting...")) try: await compile_user_snippets(nvim, stack=stack) except (LoadError, ParseError) as e: preview = str(e).splitlines() def cont() -> None: set_preview(nvim, syntax="", preview=preview) write(nvim, LANG("snip parse fail")) await async_call(nvim, cont) else: await awrite(nvim, LANG("snip parse succ")) go(nvim, aw=c3()) elif ns.action == "edit": async def c4() -> None: paths, mtimes = await user_mtimes( nvim, user_path=stack.settings.clients.snippets.user_path ) path, *_ = paths exts = {path.stem: path for path in mtimes} snip_path = exts.get(ns.filetype, path / f"{ns.filetype}.snip") snip_path.parent.mkdir(parents=True, exist_ok=True) def cont() -> None: escaped = nvim.funcs.fnameescape(normcase(snip_path)) nvim.feedkeys(f":edit {escaped}", "n", False) await async_call(nvim, cont) go(nvim, aw=c4()) else: assert False