Skip to content

Commit af5e5ce

Browse files
committed
Add css class name completions
1 parent 6c44b6c commit af5e5ce

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
@@ -57,6 +57,7 @@ class WorkspaceIndex:
5757
libraries: dict[str, Library] = field(default_factory=dict)
5858
templates: dict[str, Template] = field(default_factory=dict)
5959
global_template_context: dict[str, Variable] = field(default_factory=dict)
60+
css_class_names: list = field(default_factory=list)
6061

6162
def update(self, django_data: dict):
6263
self.file_watcher_globs = django_data.get(
@@ -121,3 +122,4 @@ def update(self, django_data: dict):
121122
)
122123
for name, type_ in django_data.get("global_template_context", {}).items()
123124
}
125+
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
@@ -330,7 +330,8 @@ def completions(self, line, character):
330330
),
331331
)
332332
)
333-
return []
333+
# Check CSS
334+
return self.get_css_class_name_completions(line, character)
334335

335336
def get_load_completions(self, match: Match, **kwargs):
336337
prefix = match.group(1).split(" ")[-1]
@@ -558,6 +559,37 @@ def resolve_completion(item: CompletionItem):
558559

559560
return item
560561

562+
def get_css_class_name_completions(self, line, character):
563+
# Flatten text to one line and remove Django template
564+
one_line_html = "".join(self.document.lines)
565+
one_line_html = re.sub(
566+
r"\{\%.*?\%\}", lambda m: " " * len(m.group(0)), one_line_html
567+
)
568+
one_line_html = re.sub(
569+
r"\{\{.*?\}\}", lambda m: " " * len(m.group(0)), one_line_html
570+
)
571+
572+
abs_position = sum(len(self.document.lines[i]) for i in range(line)) + character
573+
class_attr_pattern = re.compile(r'class=["\']([^"\']*)["\']', re.DOTALL)
574+
575+
for match in class_attr_pattern.finditer(one_line_html):
576+
start_idx, end_idx = match.span(1)
577+
578+
if start_idx <= abs_position <= end_idx:
579+
class_value = match.group(1)
580+
relative_pos = abs_position - start_idx
581+
582+
prefix_match = re.search(r"\b[\w-]*$", class_value[:relative_pos])
583+
prefix = prefix_match.group(0) if prefix_match else ""
584+
585+
return [
586+
CompletionItem(label=class_name)
587+
for class_name in self.workspace_index.css_class_names
588+
if class_name.startswith(prefix)
589+
]
590+
591+
return []
592+
561593
###################################################################################
562594
# Hover
563595
###################################################################################

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)