def parse( extern_type: Union[Type[ExternLSP], Type[ExternLUA]], client: Optional[str], short_name: str, weight_adjust: float, resp: CompletionResponse, ) -> LSPcomp: if _falsy(resp): return LSPcomp(client=client, local_cache=True, items=iter(())) elif isinstance(resp, Mapping): is_complete = _falsy(resp.get("isIncomplete")) if not isinstance((items := resp.get("items")), MutableSequence): log.warn("%s", f"Unknown LSP resp -- {type(resp)}") return LSPcomp(client=client, local_cache=is_complete, items=iter(())) else: shuffle(items) comps = (co1 for item in items if (co1 := parse_item( extern_type, client=client, short_name=short_name, weight_adjust=weight_adjust, item=item, )))
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, ))
async def c0(s: State, manual: bool) -> None: with with_suppress(), timeit("**OVERALL**"): if lock.locked(): log.warn("%s", "SHOULD NOT BE LOCKED <><> OODA") async with lock: ctx = await async_call( nvim, lambda: context( nvim, db=stack.bdb, options=stack.settings.match, state=s, manual=manual, ), ) should = _should_cont(s, prev=s.context, cur=ctx) if ctx else False _, col = ctx.position if should: state(context=ctx) await stack.supervisor.interrupt() metrics, _ = await gather( stack.supervisor.collect(ctx), async_call( nvim, lambda: complete( nvim, stack=stack, col=col, comps=()), ) if stack.settings.display.pum.fast_close else sleep(0), ) s = state() if s.change_id == ctx.change_id: vim_comps = tuple( trans( stack, pum_width=s.pum_width, context=ctx, metrics=metrics, )) await async_call( nvim, lambda: complete(nvim, stack=stack, col=col, comps=vim_comps), ) else: await async_call( nvim, lambda: complete( nvim, stack=stack, col=col, comps=())) state(inserted_pos=(-1, -1))
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"))
async def ensure_updated(vars_dir: Path, retries: int, timeout: float) -> Optional[PurePath]: bin = t9_bin(vars_dir) for _ in range(retries): try: cont = await to_thread(_update, vars_dir=vars_dir, timeout=timeout) except (URLError, TimeoutE) as e: log.warn("%s", e) await sleep(timeout) else: if not cont: return None elif access(bin, X_OK): return bin else: return None
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")
def apply(nvim: Nvim, buf: Buffer, instructions: Iterable[EditInstruction]) -> _MarkShift: insts, m_shift = _shift(instructions) for inst in insts: try: buf_set_text(nvim, buf=buf, begin=inst.begin, end=inst.end, text=inst.new_lines) except NvimError as e: tpl = """ ${e} ${inst} """ msg = Template(dedent(tpl)).substitute(e=e, inst=inst) log.warn(f"%s", msg) return m_shift
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)
async def work(self, context: Context) -> AsyncIterator[Completion]: if self._cwd != context.cwd: await self._clean() if self._bin: req = _encode( self._supervisor.match, context=context, limit=self._supervisor.match.max_results, ) json = dumps(req, check_circular=False, ensure_ascii=False) reply = await self._comm(context.cwd, json=json) if reply: try: resp = loads(reply) except JSONDecodeError as e: log.warn("%s", e) else: for comp in _decode(self._options, reply=resp): yield comp
def parse_item( extern_type: Union[Type[ExternLSP], Type[ExternLUA]], client: Optional[str], short_name: str, weight_adjust: float, item: Any, ) -> Optional[Completion]: if not item: return None else: go, parsed = _item_parser(item) if not go: log.warn("%s", parsed) return None else: assert isinstance(parsed, CompletionItem) p_edit = _primary(parsed) r_edits = tuple( _range_edit("", edit=edit) for edit in (parsed.additionalTextEdits or ())) sort_by = parsed.filterText or (parsed.label if isinstance( p_edit, SnippetEdit) else p_edit.new_text) kind = PROTOCOL.CompletionItemKind.get(item.get("kind"), "") doc = _doc(parsed) extern = extern_type(client=client, item=item, command=parsed.command) comp = Completion( source=short_name, weight_adjust=weight_adjust, label=parsed.label, primary_edit=p_edit, secondary_edits=r_edits, sort_by=sort_by, preselect=parsed.preselect or False, kind=kind, doc=doc, icon_match=kind, extern=extern, ) return comp
def _consolidate( text: str, regions: Mapping[int, Sequence[Region]]) -> Iterator[Tuple[int, Region]]: new_regions = (( r.end - r.begin, idx == 0, idx, Region(begin=r.begin, end=r.end, text=text[r.begin:r.end]), ) for idx, rs in regions.items() for r in rs) ordered = sorted(new_regions, key=lambda t: t[:-1]) acc: MutableMapping[int, MutableSequence[Region]] = {} for _, _, idx, region in ordered: if overlapped := tuple((region, reg) for reg in chain.from_iterable(acc.values()) if _overlap(region, reg)): log.warn("%s", f"snippet region overlapped -- {overlapped}") else: a = acc.setdefault(idx, []) a.append(region)
def cdraw() -> None: nonlocal has_drawn if stage := handler(nvim, self._state, settings, *args): self._state = stage.state for _ in range(RENDER_RETRIES - 1): try: redraw(nvim, state=self._state, focus=stage.focus) except NvimError: pass else: break else: try: redraw(nvim, state=self._state, focus=stage.focus) except NvimError as e: log.warn("%s", e) if settings.profiling and not has_drawn: has_drawn = True _profile(nvim, t1=t1)
def load_direct( ignore_error: bool, lsp: Iterable[Path], neosnippet: Iterable[Path], ultisnip: Iterable[Path], lsp_grammar: SnippetGrammar = SnippetGrammar.lsp, neosnippet_grammar: SnippetGrammar = SnippetGrammar.snu, ultisnip_grammar: SnippetGrammar = SnippetGrammar.snu, ) -> LoadedSnips: specs = { load_lsp: (lsp_grammar, lsp), load_neosnippet: (neosnippet_grammar, neosnippet), load_ultisnip: (ultisnip_grammar, ultisnip), } extensions: MutableMapping[str, MutableSet[str]] = {} snippets: MutableMapping[UUID, ParsedSnippet] = {} for parser, (grammar, paths) in specs.items(): for path in paths: with path.open(encoding="UTF-8") as fd: try: filetype, exts, snips = parser( grammar, path=path, lines=enumerate(fd, start=1) ) except LoadError as e: if ignore_error: log.warn("%s", e) else: raise else: ext_acc = extensions.setdefault(filetype, set()) for ext in exts: ext_acc.add(ext) for snip in snips: uid = _key(snip) snippets[uid] = snip loaded = LoadedSnips(exts=extensions, snippets=snippets) return loaded
async def cont() -> Sequence[Metric]: nonlocal done with with_suppress(), timeit("COLLECTED -- ALL"): if self._lock.locked(): log.warn("%s", "SHOULD NOT BE LOCKED <><> supervisor") async with self._lock: await self._reviewer.begin(context) self._tasks = tasks = tuple( loop.create_task(supervise(worker, assoc=assoc)) for worker, assoc in self._workers.items()) try: if not tasks: return () else: _, pending = await wait(tasks, timeout=timeout) if not acc: for fut in as_completed(pending): await fut if acc: break return acc finally: done = True
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
return LSPcomp(client=client, local_cache=is_complete, items=iter(())) else: shuffle(items) comps = (co1 for item in items if (co1 := parse_item( extern_type, client=client, short_name=short_name, weight_adjust=weight_adjust, item=item, ))) return LSPcomp(client=client, local_cache=is_complete, items=comps) elif isinstance(resp, MutableSequence): shuffle(resp) comps = (co2 for item in resp if (co2 := parse_item( extern_type, client=client, short_name=short_name, weight_adjust=weight_adjust, item=item, ))) return LSPcomp(client=client, local_cache=True, items=comps) else: log.warn("%s", f"Unknown LSP resp -- {type(resp)}") return LSPcomp(client=client, local_cache=False, items=iter(()))
l1 = ReqL1(Autocomplete=l2) req = Request(request=l1, version=_VERSION) return _ENCODER(req) def _decode(client: BaseClient, reply: Response) -> Iterator[Completion]: if (not isinstance(reply, Mapping) or not isinstance( (old_prefix := reply.get("old_prefix")), str) or not isinstance( (results := reply.get("results")), Sequence)): log.warn("%s", reply) else: for result in results: try: resp = _DECODER(result) except DecodeError as e: log.warn("%s", e) else: edit = ContextualEdit( old_prefix=old_prefix, new_prefix=resp.new_prefix, old_suffix=resp.old_suffix, new_text=resp.new_prefix + resp.new_suffix, ) label_pre, *_ = resp.new_prefix.splitlines() or ("", ) *_, label_post = resp.new_suffix.splitlines() or ("", ) label = label_pre + label_post kind = PROTOCOL.CompletionItemKind.get(resp.kind) cmp = Completion( source=client.short_name, weight_adjust=client.weight_adjust, label=label,
async def _slurp(nvim: Nvim, stack: Stack, warn: AbstractSet[SnippetWarnings]) -> None: with timeit("LOAD SNIPS"): ( cwd, bundled, (user_compiled, user_compiled_mtimes), (_, user_snips_mtimes), mtimes, ) = await gather( async_call(nvim, get_cwd, nvim), _bundled_mtimes(nvim), _load_user_compiled(stack.supervisor.vars_dir), user_mtimes(nvim, user_path=stack.settings.clients.snippets.user_path), stack.sdb.mtimes(), ) stale = mtimes.keys() - (bundled.keys() | user_compiled.keys()) compiled = { path: mtime for path, mtime in chain(bundled.items(), user_compiled.items()) if mtime > mtimes.get(path, -inf) } new_user_snips = { fmt_path(cwd, path=path, is_dir=False): ( datetime.fromtimestamp(mtime).strftime( stack.settings.display.time_fmt), datetime.fromtimestamp(prev).strftime( stack.settings.display.time_fmt) if (prev := user_compiled_mtimes.get(path)) else "??", ) for path, mtime in user_snips_mtimes.items() if mtime > user_compiled_mtimes.get(path, -inf) } await stack.sdb.clean(stale) if SnippetWarnings.missing in warn and not (bundled or user_compiled): await sleep(0) await awrite(nvim, LANG("fs snip load empty")) for fut in as_completed( tuple( _load_compiled(path, mtime) for path, mtime in compiled.items())): try: path, mtime, loaded = await fut except (OSError, JSONDecodeError, DecodeError) as e: tpl = """ Failed to load compiled snips ${e} """.rstrip() log.warn("%s", Template(dedent(tpl)).substitute(e=type(e))) else: await stack.sdb.populate(path, mtime=mtime, loaded=loaded) await awrite( nvim, LANG( "fs snip load succ", path=fmt_path(cwd, path=path, is_dir=False), ), ) if SnippetWarnings.outdated in warn and new_user_snips: paths = linesep.join(f"{path} -- {prev} -> {cur}" for path, (cur, prev) in new_user_snips.items()) await awrite(nvim, LANG("fs snip needs compile", paths=paths)) @rpc(blocking=True) def _load_snips(nvim: Nvim, stack: Stack) -> None: go(nvim, aw=_slurp(nvim, stack=stack, warn=stack.settings.clients.snippets.warn)) atomic.exec_lua(f"{NAMESPACE}.{_load_snips.name}()", ()) def compile_one( stack: Stack, grammar: SnippetGrammar, path: PurePath, info: ParseInfo, lines: Iterable[Tuple[int, str]], ) -> Compiled: filetype, exts, snips = load_neosnippet(grammar, path=path, lines=lines) parsed = tuple( _trans( stack.settings.match.unifying_chars, smart=stack.settings.completion.smart, info=info, snips=snips, )) compiled = Compiled( path=path, filetype=filetype, exts=exts, parsed=parsed, ) return compiled async def compile_user_snippets(nvim: Nvim, stack: Stack) -> None: with timeit("COMPILE SNIPS"): info = ParseInfo(visual="", clipboard="", comment_str=("", "")) _, mtimes = await user_mtimes( nvim, user_path=stack.settings.clients.snippets.user_path) loaded = await to_thread(lambda: load_direct( False, lsp=(), neosnippet=mtimes, ultisnip=(), neosnippet_grammar=SnippetGrammar.lsp, )) _ = tuple( _trans( stack.settings.match.unifying_chars, smart=stack.settings.completion.smart, info=info, snips=loaded.snippets.values(), )) try: await _dump_compiled(stack.supervisor.vars_dir, mtimes=mtimes, loaded=loaded) except OSError as e: await awrite(nvim, e) else: await _slurp(nvim, stack=stack, warn={SnippetWarnings.missing})
def _decode(client: BaseClient, reply: Response) -> Iterator[Completion]: if (not isinstance(reply, Mapping) or not isinstance( (old_prefix := reply.get("old_prefix")), str) or not isinstance( (results := reply.get("results")), Sequence)): log.warn("%s", reply)