diff --git a/.code_doc_cli/parsers/base.py b/.code_doc_cli/parsers/base.py new file mode 100644 index 0000000..038c004 --- /dev/null +++ b/.code_doc_cli/parsers/base.py @@ -0,0 +1,110 @@ +"""Base parser interface and data structures.""" + +from abc import ABC, abstractmethod +from dataclasses import dataclass, field +from typing import Optional +from enum import Enum + + +class ElementType(Enum): + """Types of documentation elements.""" + + FUNCTION = "function" + CLASS = "class" + METHOD = "method" + INTERFACE = "interface" + STRUCT = "struct" + MODULE = "module" + CONSTANT = "constant" + VARIABLE = "variable" + + +@dataclass +class Parameter: + """Represents a function/method parameter.""" + + name: str + type_hint: Optional[str] = None + description: Optional[str] = None + default_value: Optional[str] = None + is_optional: bool = False + + def __str__(self) -> str: + parts = [self.name] + if self.type_hint: + parts.append(f": {self.type_hint}") + if self.default_value: + parts.append(f" = {self.default_value}") + return "".join(parts) + + +@dataclass +class DocElement: + """Represents a documentation element extracted from source code.""" + + name: str + element_type: ElementType + description: str = "" + full_docstring: str = "" + parameters: list[Parameter] = field(default_factory=list) + return_type: Optional[str] = None + return_description: Optional[str] = None + raises: list[tuple[str, str]] = field(default_factory=list) + examples: list[str] = field(default_factory=list) + source_file: str = "" + line_number: int = 0 + visibility: str = "public" + decorators: list[str] = field(default_factory=list) + attributes: list[tuple[str, Optional[str], Optional[str]]] = field( + default_factory=list + ) + imports: list[str] = field(default_factory=list) + + +class Parser(ABC): + """Abstract base class for code parsers.""" + + def __init__(self, file_path: str): + self.file_path = file_path + self.content: str = "" + self.elements: list[DocElement] = [] + + @abstractmethod + def parse(self) -> list[DocElement]: + """Parse the file and extract documentation elements.""" + pass + + @abstractmethod + def get_language_name(self) -> str: + """Return the name of the language this parser handles.""" + pass + + @classmethod + @abstractmethod + def supports_file(cls, file_path: str) -> bool: + """Check if this parser supports the given file.""" + pass + + def _read_content(self) -> str: + """Read file content safely.""" + with open(self.file_path, "r", encoding="utf-8") as f: + return f.read() + + def _strip_docstring_quotes(self, docstring: str) -> str: + """Remove leading/trailing quotes from docstring.""" + if not docstring: + return "" + lines = docstring.split("\n") + if len(lines) == 1: + return docstring.strip('"""').strip("'''").strip() + first_line = lines[0].strip('"""').strip("'''").strip() + last_line = lines[-1].strip('"""').strip("'''").strip() + if len(lines) > 1: + middle_lines = lines[1:-1] + cleaned = [] + for line in middle_lines: + stripped = line.strip() + if stripped: + cleaned.append(stripped) + return "\n".join([first_line] + cleaned + [last_line]) if cleaned else first_line + return first_line