Skip to content

Commit 2ad48b7

Browse files
Development (#67)
* fix: codegen pragma comments (#66) * fix: Ignore `%#codegen` compilation directive * Exclude all pragmas instead of only %#codegen Co-authored-by: Mark Shui Hu <watermarkhu@gmail.com> --------- Co-authored-by: Frank Kusters <frank.kusters@sioux.eu> * feature: Show details (file, lineno...) on parse error (#65) * feature: Show details (file, lineno...) on parse error * Raise CollectionError so mkdocs doesn't exit with a stacktrace --------- Co-authored-by: Frank Kusters <frank.kusters@sioux.eu>
1 parent 53cdaeb commit 2ad48b7

File tree

2 files changed

+44
-19
lines changed

2 files changed

+44
-19
lines changed

src/mkdocstrings_handlers/matlab/handler.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -358,7 +358,15 @@ def collect(self, identifier: str, config: Mapping[str, Any]) -> CollectorItem:
358358
raise CollectionError("Empty identifier")
359359

360360
final_config = ChainMap(config, self.default_config) # type: ignore[arg-type]
361-
model = self.paths.resolve(identifier, config=final_config)
361+
try:
362+
model = self.paths.resolve(identifier, config=final_config)
363+
except SyntaxError as ex:
364+
msg = str(ex)
365+
if ex.text:
366+
msg += ':\n' + ex.text
367+
raise CollectionError(msg) from ex
368+
except Exception as ex:
369+
raise CollectionError(str(ex)) from ex
362370
if model is None:
363371
raise CollectionError(f"Identifier '{identifier}' not found")
364372
return model

src/mkdocstrings_handlers/matlab/treesitter.py

Lines changed: 35 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,7 @@ def __init__(self, filepath: Path):
198198
self.encoding: str = result.encoding if result else "utf-8"
199199
with open(filepath, "rb") as f:
200200
self._content: bytes = f.read()
201+
self._node: Node | None = None
201202

202203
@property
203204
def content(self):
@@ -226,26 +227,38 @@ def parse(self, **kwargs: Any) -> MatlabMixin:
226227
Raises:
227228
ValueError: If the file could not be parsed.
228229
"""
229-
tree = PARSER.parse(self._content)
230-
cursor = tree.walk()
231-
232-
if cursor.node is None:
233-
raise ValueError(f"The file {self.filepath} could not be parsed.")
234-
captures = FILE_QUERY.captures(cursor.node)
235-
236-
if "function" in captures:
237-
model = self._parse_function(captures["function"][0], **kwargs)
238-
elif "class" in captures:
239-
model = self._parse_class(captures["class"][0], **kwargs)
240-
else:
241-
model = Script(self.filepath.stem, filepath=self.filepath, **kwargs)
230+
try:
231+
tree = PARSER.parse(self._content)
232+
cursor = tree.walk()
233+
234+
if cursor.node is None:
235+
raise ValueError(f"The file {self.filepath} could not be parsed.")
236+
captures = FILE_QUERY.captures(cursor.node)
237+
if "function" in captures:
238+
model = self._parse_function(captures["function"][0], **kwargs)
239+
elif "class" in captures:
240+
model = self._parse_class(captures["class"][0], **kwargs)
241+
else:
242+
model = Script(self.filepath.stem, filepath=self.filepath, **kwargs)
242243

243-
if not model.docstring:
244-
model.docstring = self._comment_docstring(
245-
captures.get("header", None), parent=model
246-
)
244+
if not model.docstring:
245+
model.docstring = self._comment_docstring(
246+
captures.get("header", None), parent=model
247+
)
247248

248-
return model
249+
return model
250+
except Exception as ex:
251+
syntax_error = SyntaxError(f"Error parsing Matlab file")
252+
syntax_error.filename = str(self.filepath)
253+
if self._node is not None:
254+
if self._node.text is not None:
255+
indentation = ' ' * self._node.start_point.column
256+
syntax_error.text = indentation + self._node.text.decode(self.encoding)
257+
syntax_error.lineno = self._node.start_point.row + 1
258+
syntax_error.offset = self._node.start_point.column + 1
259+
syntax_error.end_lineno = self._node.end_point.row + 1
260+
syntax_error.end_offset = self._node.end_point.column + 1
261+
raise syntax_error from ex
249262

250263
def _parse_class(self, node: Node, **kwargs: Any) -> Class:
251264
"""
@@ -262,6 +275,7 @@ def _parse_class(self, node: Node, **kwargs: Any) -> Class:
262275
Returns:
263276
Class: The parsed Class or Classfolder model.
264277
"""
278+
self._node = node
265279
saved_kwargs = {key: value for key, value in kwargs.items()}
266280
captures = CLASS_QUERY.captures(node)
267281

@@ -401,6 +415,7 @@ def _parse_attribute(self, node: Node) -> tuple[str, Any]:
401415
The value is `True` if no value is specified,
402416
otherwise it is the parsed value which can be a boolean or a string.
403417
"""
418+
self._node = node
404419
captures = ATTRIBUTE_QUERY.captures(node)
405420

406421
key = self._first_from_capture(captures, "name")
@@ -429,6 +444,7 @@ def _parse_function(self, node: Node, method: bool = False, **kwargs: Any) -> Fu
429444
KeyError: If required captures are missing from the node.
430445
431446
"""
447+
self._node = node
432448
captures: dict = FUNCTION_QUERY.matches(node)[0][1]
433449

434450
input_names = self._decode_from_capture(captures, "input")
@@ -529,6 +545,7 @@ def _decode(self, node: Node) -> str:
529545
Returns:
530546
str: The decoded text of the node. If the node or its text is None, returns an empty string.
531547
"""
548+
self._node = node
532549
return (
533550
node.text.decode(self.encoding)
534551
if node is not None and node.text is not None

0 commit comments

Comments
 (0)