Skip to content

Commit

Permalink
Refactor completion for supporting new features
Browse files Browse the repository at this point in the history
  • Loading branch information
krukas committed Jun 20, 2024
1 parent cb62b3c commit 7ff592b
Show file tree
Hide file tree
Showing 2 changed files with 76 additions and 75 deletions.
131 changes: 65 additions & 66 deletions djlsp/parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from functools import cached_property
from re import Match

from lsprotocol.types import CompletionItem
from pygls.workspace import TextDocument

from djlsp.constants import BUILTIN
Expand All @@ -12,25 +13,17 @@


class TemplateParser:
re_loaded = re.compile(r".*{% ?load ([\w ]*) ?%}$")
re_load = re.compile(r".*{% ?load ([\w ]*)$")
re_block = re.compile(r".*{% ?block ([\w]*)$")
re_url = re.compile(r""".*{% ?url ('|")([\w\-:]*)$""")
re_static = re.compile(r".*{% ?static ('|\")([\w\-\.\/]*)$")
re_tag = re.compile(r"^.*{% ?(\w*)$")
re_filter = re.compile(r"^.*({%|{{) ?[\w \.\|]*\|(\w*)$")
re_template = re.compile(r""".*{% ?(extends|include) ('|")([\w\-:]*)$""")
re_context = re.compile(r".*({{|{% \w+).* ([\w\d_\.]*)$")

def __init__(self, workspace_index: WorkspaceIndex, document: TextDocument):
self.workspace_index: WorkspaceIndex = workspace_index
self.document: TextDocument = document

@cached_property
def loaded_libraries(self):
re_loaded = re.compile(r".*{% ?load ([\w ]*) ?%}$")
loaded = {BUILTIN}
for line in self.document.lines:
if match := self.re_loaded.match(line):
if match := re_loaded.match(line):
loaded.update(
[
lib
Expand All @@ -43,38 +36,42 @@ def loaded_libraries(self):

def completions(self, line, character):
line_fragment = self.document.lines[line][:character]
try:
if match := self.re_load.match(line_fragment):
return self.get_load_completions(match)
if match := self.re_block.match(line_fragment):
return self.get_block_completions(match)
if match := self.re_url.match(line_fragment):
return self.get_url_completions(match)
elif match := self.re_static.match(line_fragment):
return self.get_static_completions(match)
elif match := self.re_template.match(line_fragment):
return self.get_template_completions(match)
elif match := self.re_tag.match(line_fragment):
return self.get_tag_completions(match)
elif match := self.re_filter.match(line_fragment):
return self.get_filter_completions(match)
elif match := self.re_context.match(line_fragment):
return self.get_context_completions(match)
except Exception as e:
logger.debug(e)

matchers = [
(re.compile(r".*{% ?load ([\w ]*)$"), self.get_load_completions),
(re.compile(r".*{% ?block ([\w]*)$"), self.get_block_completions),
(re.compile(r""".*{% ?url ('|")([\w\-:]*)$"""), self.get_url_completions),
(
re.compile(r".*{% ?static ('|\")([\w\-\.\/]*)$"),
self.get_static_completions,
),
(
re.compile(r""".*{% ?(extends|include) ('|")([\w\-:]*)$"""),
self.get_template_completions,
),
(re.compile(r"^.*{% ?(\w*)$"), self.get_tag_completions),
(
re.compile(r"^.*({%|{{) ?[\w \.\|]*\|(\w*)$"),
self.get_filter_completions,
),
(
re.compile(r".*({{|{% \w+).* ([\w\d_\.]*)$"),
self.get_context_completions,
),
]

for regex, completion in matchers:
if match := regex.match(line_fragment):
return completion(match)
return []

def get_load_completions(self, match: Match):
prefix = match.group(1).split(" ")[-1]
logger.debug(f"Find load matches for: {prefix}")
return sorted(
[
lib
for lib in self.workspace_index.libraries.keys()
if lib != BUILTIN and lib.startswith(prefix)
]
)
return [
CompletionItem(label=lib)
for lib in self.workspace_index.libraries.keys()
if lib != BUILTIN and lib.startswith(prefix)
]

def get_block_completions(self, match: Match):
prefix = match.group(1).strip()
Expand All @@ -91,13 +88,11 @@ def get_block_completions(self, match: Match):
if matches := re_block.findall(line):
used_block_names.extend(matches)

return sorted(
[
name
for name in block_names
if name not in used_block_names and name.startswith(prefix)
]
)
return [
CompletionItem(label=name)
for name in block_names
if name not in used_block_names and name.startswith(prefix)
]

def _recursive_block_names(self, template_name, looked_up_templates=None):
looked_up_templates = looked_up_templates if looked_up_templates else []
Expand All @@ -115,31 +110,29 @@ def _recursive_block_names(self, template_name, looked_up_templates=None):
def get_static_completions(self, match: Match):
prefix = match.group(2)
logger.debug(f"Find static matches for: {prefix}")
return sorted(
[
static_file
for static_file in self.workspace_index.static_files
if static_file.startswith(prefix)
]
)
return [
CompletionItem(label=static_file)
for static_file in self.workspace_index.static_files
if static_file.startswith(prefix)
]

def get_url_completions(self, match: Match):
prefix = match.group(2)
logger.debug(f"Find url matches for: {prefix}")
return sorted(
[url for url in self.workspace_index.urls if url.startswith(prefix)]
)
return [
CompletionItem(label=url)
for url in self.workspace_index.urls
if url.startswith(prefix)
]

def get_template_completions(self, match: Match):
prefix = match.group(3)
logger.debug(f"Find {match.group(1)} matches for: {prefix}")
return sorted(
[
template
for template in self.workspace_index.templates
if template.startswith(prefix)
]
)
return [
CompletionItem(label=template)
for template in self.workspace_index.templates
if template.startswith(prefix)
]

def get_tag_completions(self, match: Match):
prefix = match.group(1)
Expand All @@ -155,7 +148,7 @@ def get_tag_completions(self, match: Match):
if tag.closing_tag:
tags.append(tag.closing_tag)

return sorted([tag for tag in tags if tag.startswith(prefix)])
return [CompletionItem(label=tag) for tag in tags if tag.startswith(prefix)]

def get_filter_completions(self, match: Match):
prefix = match.group(2)
Expand All @@ -164,9 +157,11 @@ def get_filter_completions(self, match: Match):
for lib_name in self.loaded_libraries:
if lib := self.workspace_index.libraries.get(lib_name):
filters.extend(lib.filters)
return sorted(
[filter_name for filter_name in filters if filter_name.startswith(prefix)]
)
return [
CompletionItem(label=filter_name)
for filter_name in filters
if filter_name.startswith(prefix)
]

def get_context_completions(self, match: Match):
prefix = match.group(2)
Expand All @@ -181,7 +176,11 @@ def get_context_completions(self, match: Match):
prefix.strip().split("."), context
)

return [var for var in lookup_context if var.startswith(prefix)]
return [
CompletionItem(label=var)
for var in lookup_context
if var.startswith(prefix)
]

def _recursive_context_lookup(self, parts: [str], context: dict[str, str]):
if len(parts) == 1:
Expand Down
20 changes: 11 additions & 9 deletions djlsp/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
INITIALIZE,
TEXT_DOCUMENT_COMPLETION,
WORKSPACE_DID_CHANGE_WATCHED_FILES,
CompletionItem,
CompletionList,
CompletionOptions,
CompletionParams,
Expand Down Expand Up @@ -279,14 +278,17 @@ def initialized(ls: DjangoTemplateLanguageServer, params: InitializeParams):
def completions(ls: DjangoTemplateLanguageServer, params: CompletionParams):
logger.info(f"COMMAND: {TEXT_DOCUMENT_COMPLETION}")
logger.debug(f"PARAMS: {params}")
items = []
document = server.workspace.get_document(params.text_document.uri)
template = TemplateParser(ls.workspace_index, document)
for completion in template.completions(
params.position.line, params.position.character
):
items.append(CompletionItem(label=completion))
return CompletionList(is_incomplete=False, items=items)
try:
return CompletionList(
is_incomplete=False,
items=TemplateParser(
workspace_index=ls.workspace_index,
document=server.workspace.get_document(params.text_document.uri),
).completions(params.position.line, params.position.character),
)
except Exception as e:
logger.error(e)
return CompletionList(items=[])


@server.feature(WORKSPACE_DID_CHANGE_WATCHED_FILES)
Expand Down

0 comments on commit 7ff592b

Please sign in to comment.