def request_completions(self, event: utils.EventWithData) -> None: if self._lsp_client.state != lsp.ClientState.NORMAL: self.log.warning( f"autocompletions requested but langserver state == {self._lsp_client.state!r}" ) return tab = event.widget assert isinstance(tab, tabs.FileTab) and tab.path is not None request = event.data_class(autocomplete.Request) lsp_id = self._lsp_client.completions( text_document_position=lsp.TextDocumentPosition( textDocument=lsp.TextDocumentIdentifier(uri=tab.path.as_uri()), position=_position_tk2lsp(request.cursor_pos), ), context=lsp.CompletionContext( # FIXME: this isn't always the case, porcupine can also trigger # it automagically triggerKind=lsp.CompletionTriggerKind.INVOKED ), ) assert lsp_id not in self._lsp_id_to_tab_and_request self._lsp_id_to_tab_and_request[lsp_id] = (tab, request)
def send_change_events(self, tab: tabs.FileTab, event: utils.EventWithData) -> None: if self._lsp_client.state != lsp.ClientState.NORMAL: # The langserver will receive the actual content of the file once # it starts. self.log.debug( "not sending change events because langserver state == %r", self._lsp_client.state) return assert tab.path is not None self._lsp_client.did_change( text_document=lsp.VersionedTextDocumentIdentifier( uri=tab.path.as_uri(), version=next(self._version_counter), ), content_changes=[ lsp.TextDocumentContentChangeEvent( range=lsp.Range( start=_position_tk2lsp(change.start), end=_position_tk2lsp(change.end), ), text=change.new_text, ) for change in event.data_class(textwidget.Changes).change_list ], )
def on_change(self, event: utils.EventWithData) -> None: change_list = event.data_class(textwidget.Changes).change_list if len(change_list) == 1: [change] = change_list if len(change.new_text) <= 1: # Optimization for typical key strokes (but not for reloading entire file): # only highlight the area that might have changed self.highlight_range(f"{change.start[0]}.0", f"{change.end[0]}.0 lineend") return self.highlight_visible()
def set_underlines(self, event: utils.EventWithData) -> None: underlines = event.data_class(Underlines) log.debug(f"Setting underlines: {underlines}") self.textwidget.tag_remove(f"underline:{underlines.id}", "1.0", "end") old_underlines_deleted = False for tag in list(self._tag2underline.keys()): literally_underline, tag_id, number = tag.split(":") if tag_id == underlines.id: self.textwidget.tag_delete(tag) del self._tag2underline[tag] old_underlines_deleted = True for index, underline in enumerate(underlines.underline_list): tag = f"underline:{underlines.id}:{index}" less_specific_tag = f"underline:{underlines.id}" self._tag2underline[tag] = underline if underline.color is None: self.textwidget.tag_config(tag, underline=True) else: self.textwidget.tag_config(tag, underline=True, underlinefg=underline.color) self.textwidget.tag_add(tag, underline.start, underline.end) self.textwidget.tag_add(less_specific_tag, underline.start, underline.end) # Updating underline_common tag is kind of brute-force because overlapping non-common # underline tags make it difficult. But let's not run it at every key press unless something # actually changed. if old_underlines_deleted or underlines.underline_list: self.textwidget.tag_remove("underline_common", "1.0", "end") for tag in self._tag2underline.keys(): ranges = self.textwidget.tag_ranges(tag) for start, end in zip(ranges[0::2], ranges[1::2]): self.textwidget.tag_add("underline_common", start, end) # update what's showing if any( tag.startswith("underline:") for tag in self.textwidget.tag_names("insert")): self._show_tag_at_index("insert") else: self._hide_message_label()