Skip to content

Commit 6a86a64

Browse files
committed
Add css class name completions
1 parent 9e4e567 commit 6a86a64

File tree

4 files changed

+58
-1
lines changed

4 files changed

+58
-1
lines changed

djlsp/index.py

Lines changed: 2 additions & 0 deletions
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", [])

djlsp/parser.py

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -207,7 +207,8 @@ def completions(self, line, character):
207207
),
208208
)
209209
)
210-
return []
210+
# Check CSS
211+
return self.get_css_class_name_completions(line, character)
211212

212213
def get_load_completions(self, match: Match, **kwargs):
213214
prefix = match.group(1).split(" ")[-1]
@@ -418,6 +419,37 @@ def get_sort_text(comp):
418419
if var.startswith(prefix)
419420
]
420421

422+
def get_css_class_name_completions(self, line, character):
423+
# Flatten text to one line and remove Django template
424+
one_line_html = "".join(self.document.lines)
425+
one_line_html = re.sub(
426+
r"\{\%.*?\%\}", lambda m: " " * len(m.group(0)), one_line_html
427+
)
428+
one_line_html = re.sub(
429+
r"\{\{.*?\}\}", lambda m: " " * len(m.group(0)), one_line_html
430+
)
431+
432+
abs_position = sum(len(self.document.lines[i]) for i in range(line)) + character
433+
class_attr_pattern = re.compile(r'class=["\']([^"\']*)["\']', re.DOTALL)
434+
435+
for match in class_attr_pattern.finditer(one_line_html):
436+
start_idx, end_idx = match.span(1)
437+
438+
if start_idx <= abs_position <= end_idx:
439+
class_value = match.group(1)
440+
relative_pos = abs_position - start_idx
441+
442+
prefix_match = re.search(r"\b[\w-]*$", class_value[:relative_pos])
443+
prefix = prefix_match.group(0) if prefix_match else ""
444+
445+
return [
446+
CompletionItem(label=class_name)
447+
for class_name in self.workspace_index.css_class_names
448+
if class_name.startswith(prefix)
449+
]
450+
451+
return []
452+
421453
###################################################################################
422454
# Hover
423455
###################################################################################

djlsp/scripts/django-collector.py

Lines changed: 20 additions & 0 deletions
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
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
.blog .item {
2+
color: red;
3+
}

0 commit comments

Comments
 (0)