[docs]classDirectiveArgumentProvider:"""Base class for directive argument providers."""def__init__(self,esbonio:server.EsbonioLanguageServer):self.converter=esbonio.converterself.logger=esbonio.logger.getChild(self.__class__.__name__)
[docs]defsuggest_arguments(self,context:server.CompletionContext,**kwargs)->(list[lsp.CompletionItem]|None|Coroutine[Any,Any,list[lsp.CompletionItem]|None]):"""Given a completion context, suggest directive arguments that may be used."""returnNone
[docs]classValuesProvider(DirectiveArgumentProvider):"""Simple completions provider that supports a static list of values."""
[docs]defsuggest_arguments(# type: ignore[override]self,context:server.CompletionContext,*,values:list[str|dict[str,Any]])->list[lsp.CompletionItem]:"""Given a completion context, suggest directive arguments that may be used."""result:list[lsp.CompletionItem]=[]forvalueinvalues:ifisinstance(value,str):result.append(lsp.CompletionItem(label=value))continuetry:result.append(self.converter.structure(value,lsp.CompletionItem))exceptException:self.logger.exception("Unable to create CompletionItem")returnresult
[docs]classFilepathProvider(DirectiveArgumentProvider):"""Argument provider for filepaths."""
[docs]defsuggest_arguments(# type: ignore[override]self,context:server.CompletionContext,*,root:str="/",pattern:str|None=None,)->list[lsp.CompletionItem]:"""Given a completion context, suggest files (or folders) that may be used. Parameters ---------- root If the user provides an absolute path, generate suggestions relative to this directory. pattern If set, limit suggestions only to matching files. Returns ------- list[lsp.CompletionItem] A list of completion items to suggest. """uri=server.Uri.parse(context.doc.uri)cwd=pathlib.Path(uri).parentif(partial:=context.match.group("argument"))andpartial.startswith("/"):candidate_dir=pathlib.Path(root)# Be sure to remove the leading '/', otherwise partial will wipe out the# root when concatenated.partial=partial[1:]else:candidate_dir=cwdcandidate_dir/=partialifpartialandnotpartial.endswith(("/",".")):candidate_dir=candidate_dir.parentself.logger.debug("Suggesting files relative to %r",candidate_dir)return[self._path_to_completion_item(context,p)forpincandidate_dir.glob("*")]
def_path_to_completion_item(self,context:server.CompletionContext,path:pathlib.Path)->lsp.CompletionItem:"""Create the ``CompletionItem`` for the given path. In the case where there are multiple filepath components, this function needs to provide an appropriate ``TextEdit`` so that the most recent entry in the path can be easily edited - without clobbering the existing path. Also bear in mind that this function must play nice with both role target and directive argument completions. """new_text=f"{path.name}"kind=(lsp.CompletionItemKind.Folderifpath.is_dir()elselsp.CompletionItemKind.File)if(start:=self._find_start_char(context))==-1:insert_text=new_textfilter_text=Nonetext_edit=Noneelse:start+=1_,end=context.match.span()prefix=context.match.group(0)[start:]insert_text=Nonefilter_text=f"{prefix}{new_text}"# Needed so VSCode will actually show the results.text_edit=lsp.TextEdit(range=lsp.Range(start=lsp.Position(line=context.position.line,character=start),end=lsp.Position(line=context.position.line,character=end),),new_text=new_text,)returnlsp.CompletionItem(label=new_text,kind=kind,insert_text=insert_text,filter_text=filter_text,text_edit=text_edit,)def_find_start_char(self,context:server.CompletionContext)->int:matched_text=context.match.group(0)idx=matched_text.find("/")whileTrue:next_idx=matched_text.find("/",idx+1)ifnext_idx==-1:breakidx=next_idxreturnidx