Skip to content

Commit 4358d47

Browse files
committed
Add css class name completions
1 parent b6ddddc commit 4358d47

File tree

4 files changed

+58
-1
lines changed

4 files changed

+58
-1
lines changed

Diff for: djlsp/index.py

+2
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ class WorkspaceIndex:
5050
libraries: dict[str, Library] = field(default_factory=dict)
5151
templates: dict[str, Template] = field(default_factory=dict)
5252
global_template_context: dict[str, str] = field(default_factory=dict)
53+
css_class_names: list = field(default_factory=list)
5354

5455
def update(self, django_data: dict):
5556
self.file_watcher_globs = django_data.get(
@@ -96,3 +97,4 @@ def update(self, django_data: dict):
9697
}
9798

9899
self.global_template_context = django_data.get("global_template_context", {})
100+
self.css_class_names = django_data.get("css_class_names", [])

Diff for: djlsp/parser.py

+33-1
Original file line numberDiff line numberDiff line change
@@ -193,7 +193,8 @@ def completions(self, line, character):
193193
),
194194
)
195195
)
196-
return []
196+
# Check CSS
197+
return self.get_css_class_name_completions(line, character)
197198

198199
def get_load_completions(self, match: Match, **kwargs):
199200
prefix = match.group(1).split(" ")[-1]
@@ -404,6 +405,37 @@ def get_sort_text(comp):
404405
if var.startswith(prefix)
405406
]
406407

408+
def get_css_class_name_completions(self, line, character):
409+
# Flatten text to one line and remove Django template
410+
one_line_html = "".join(self.document.lines)
411+
one_line_html = re.sub(
412+
r"\{\%.*?\%\}", lambda m: " " * len(m.group(0)), one_line_html
413+
)
414+
one_line_html = re.sub(
415+
r"\{\{.*?\}\}", lambda m: " " * len(m.group(0)), one_line_html
416+
)
417+
418+
abs_position = sum(len(self.document.lines[i]) for i in range(line)) + character
419+
class_attr_pattern = re.compile(r'class=["\']([^"\']*)["\']', re.DOTALL)
420+
421+
for match in class_attr_pattern.finditer(one_line_html):
422+
start_idx, end_idx = match.span(1)
423+
424+
if start_idx <= abs_position <= end_idx:
425+
class_value = match.group(1)
426+
relative_pos = abs_position - start_idx
427+
428+
prefix_match = re.search(r"\b[\w-]*$", class_value[:relative_pos])
429+
prefix = prefix_match.group(0) if prefix_match else ""
430+
431+
return [
432+
CompletionItem(label=class_name)
433+
for class_name in self.workspace_index.css_class_names
434+
if class_name.startswith(prefix)
435+
]
436+
437+
return []
438+
407439
###################################################################################
408440
# Hover
409441
###################################################################################

Diff for: djlsp/scripts/django-collector.py

+20
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,7 @@ def __init__(self, project_src_path):
196196
self.libraries = {}
197197
self.templates: dict[str, Template] = {}
198198
self.global_template_context = {}
199+
self.css_class_names = []
199200

200201
def collect(self):
201202
self.file_watcher_globs = self.get_file_watcher_globs()
@@ -204,6 +205,7 @@ def collect(self):
204205
self.urls = self.get_urls()
205206
self.libraries = self.get_libraries()
206207
self.global_template_context = self.get_global_template_context()
208+
self.css_class_names = self.get_css_class_names()
207209

208210
# Third party collectors
209211
self.collect_for_wagtail()
@@ -217,6 +219,7 @@ def to_json(self):
217219
"libraries": self.libraries,
218220
"templates": self.templates,
219221
"global_template_context": self.global_template_context,
222+
"css_class_names": self.css_class_names,
220223
},
221224
indent=4,
222225
)
@@ -573,6 +576,23 @@ def collect_for_wagtail(self):
573576
model.context_object_name
574577
] = (model.__module__ + "." + model.__name__)
575578

579+
# CSS class names
580+
# ---------------------------------------------------------------------------------
581+
def get_css_class_names(self):
582+
class_pattern = re.compile(r"\.(?!\d)([a-zA-Z0-9_-]+)")
583+
class_names = set()
584+
585+
for finder in get_finders():
586+
for path, file_storage in finder.list(None):
587+
if path.endswith(".css") and not path.startswith("admin"):
588+
try:
589+
with file_storage.open(path, "r") as f:
590+
content = f.read()
591+
class_names.update(class_pattern.findall(content))
592+
except Exception as e:
593+
logger.error(f"Could not parse CSS file: {e}")
594+
return list(class_names)
595+
576596

577597
#######################################################################################
578598
# CLI

Diff for: tests/django_test/test-static-folder/website.css

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
.blog .item {
2+
color: red;
3+
}

0 commit comments

Comments
 (0)