Exemple #1
0
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,
            )))
Exemple #2
0
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, ))
Exemple #3
0
        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))
Exemple #4
0
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"))
Exemple #5
0
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
Exemple #6
0
 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")
Exemple #7
0
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
Exemple #8
0
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)
Exemple #9
0
    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
Exemple #10
0
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
Exemple #11
0
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)
Exemple #12
0
            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)
Exemple #13
0
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
Exemple #14
0
        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
Exemple #15
0
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
Exemple #16
0
            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(()))
Exemple #17
0
    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,
Exemple #18
0
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})
Exemple #19
0
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)