|
|
@@ -43,11 +43,66 @@ export function getChildByField(node: SyntaxNode, fieldName: string): SyntaxNode
|
|
|
return node.childForFieldName(fieldName);
|
|
|
}
|
|
|
|
|
|
+/**
|
|
|
+ * Node types that *wrap* a declaration so a leading comment is a sibling of the
|
|
|
+ * wrapper, not of the emitted (inner) declaration node. CodeGraph emits the
|
|
|
+ * inner node, so before looking for its preceding comment we climb out through
|
|
|
+ * these. Examples: `export class X {}` (export_statement), `@dec\ndef f()`
|
|
|
+ * (decorated_definition), `const f = () => {}` (lexical_declaration →
|
|
|
+ * variable_declarator). Each wraps exactly one declaration, so climbing can't
|
|
|
+ * mis-attribute a comment to a sibling. (#780)
|
|
|
+ */
|
|
|
+const DOCSTRING_WRAPPER_TYPES = new Set([
|
|
|
+ 'export_statement', // JS/TS: export class/function/const ...
|
|
|
+ 'decorated_definition', // Python: @decorator over def/class
|
|
|
+ 'lexical_declaration', // JS/TS: const/let x = () => {}
|
|
|
+ 'variable_declaration', // JS/TS: var x = ...
|
|
|
+ 'variable_declarator', // JS/TS: the `x = () => {}` inside the declaration
|
|
|
+ 'ambient_declaration', // TS: declare ...
|
|
|
+]);
|
|
|
+
|
|
|
+/**
|
|
|
+ * Strip comment-syntax markers from a raw comment so the stored docstring is
|
|
|
+ * just the prose. Covers the marker styles across every supported language:
|
|
|
+ * C-family line and block comments and their doc variants, Rust/Swift/Kotlin
|
|
|
+ * triple-slash and bang doc lines, hash lines (Python/Ruby/shell), Lua/Luau
|
|
|
+ * line and long-bracket comments, and Pascal brace and paren-star comments.
|
|
|
+ * (#780)
|
|
|
+ *
|
|
|
+ * Paired block delimiters are stripped only when the comment OPENS with one,
|
|
|
+ * so a line comment that merely happens to END with a closing delimiter is
|
|
|
+ * never truncated. The per-line markers are anchored at line start, so
|
|
|
+ * they're safe to apply to any comment.
|
|
|
+ */
|
|
|
+function cleanCommentMarkers(comment: string): string {
|
|
|
+ let c = comment.trim();
|
|
|
+ if (c.startsWith('/*')) c = c.replace(/^\/\*+!?/, '').replace(/\*+\/$/, '');
|
|
|
+ else if (c.startsWith('--[')) c = c.replace(/^--\[=*\[/, '').replace(/\]=*\]$/, '');
|
|
|
+ else if (c.startsWith('(*')) c = c.replace(/^\(\*/, '').replace(/\*\)$/, '');
|
|
|
+ else if (c.startsWith('{')) c = c.replace(/^\{/, '').replace(/\}$/, '');
|
|
|
+ return c
|
|
|
+ .replace(/^\/\/[/!]?\s?/gm, '') // // , and Rust/Swift doc lines /// //!
|
|
|
+ .replace(/^--\s?/gm, '') // Lua/Luau line comments
|
|
|
+ .replace(/^#\s?/gm, '') // Python/Ruby/shell line comments
|
|
|
+ .replace(/^\s*\*\s?/gm, '') // block-comment continuation (* foo)
|
|
|
+ .trim();
|
|
|
+}
|
|
|
+
|
|
|
/**
|
|
|
* Get the docstring/comment preceding a node
|
|
|
*/
|
|
|
export function getPrecedingDocstring(node: SyntaxNode, source: string): string | undefined {
|
|
|
- let sibling = node.previousNamedSibling;
|
|
|
+ // Climb out of any wrapper(s) so a comment preceding the WHOLE construct
|
|
|
+ // (export-, decorator-, or const-arrow-wrapped) is reachable as a sibling.
|
|
|
+ // The emitted node's own `previousNamedSibling` is empty (export/const) or a
|
|
|
+ // decorator (Python) in those cases, so without this the docstring was
|
|
|
+ // dropped. (#780)
|
|
|
+ let anchor = node;
|
|
|
+ while (anchor.parent && DOCSTRING_WRAPPER_TYPES.has(anchor.parent.type)) {
|
|
|
+ anchor = anchor.parent;
|
|
|
+ }
|
|
|
+
|
|
|
+ let sibling = anchor.previousNamedSibling;
|
|
|
const comments: string[] = [];
|
|
|
|
|
|
while (sibling) {
|
|
|
@@ -66,15 +121,6 @@ export function getPrecedingDocstring(node: SyntaxNode, source: string): string
|
|
|
|
|
|
if (comments.length === 0) return undefined;
|
|
|
|
|
|
- // Clean up comment markers
|
|
|
- return comments
|
|
|
- .map((c) =>
|
|
|
- c
|
|
|
- .replace(/^\/\*\*?|\*\/$/g, '')
|
|
|
- .replace(/^\/\/\s?/gm, '')
|
|
|
- .replace(/^\s*\*\s?/gm, '')
|
|
|
- .trim()
|
|
|
- )
|
|
|
- .join('\n')
|
|
|
- .trim();
|
|
|
+ // Strip each comment's syntax markers (language-aware), then join.
|
|
|
+ return comments.map(cleanCommentMarkers).join('\n').trim();
|
|
|
}
|