tools/apiview/parsers/rust-api-parser/src/process-items/processModule.ts (141 lines of code) (raw):

import { ReviewLine, TokenKind } from "../models/apiview-models"; import { Item } from "../../rustdoc-types/output/rustdoc-types"; import { processItem } from "./processItem"; import { createDocsReviewLines } from "./utils/generateDocReviewLine"; import { isModuleItem, isUseItem } from "./utils/typeGuards"; import { getAPIJson, processedItems } from "../main"; import { getSortedChildIds } from "./utils/sorting"; import { processUse } from "./processUse"; import { AnnotatedReviewLines } from "./utils/models"; import { lineIdMap } from "../utils/lineIdUtils"; /** * Processes a module item and adds its documentation to the ReviewLine. * * @param {Item} item - The module item to process. * @param {Object} parentModule - Optional parent module information. * @returns {ReviewLine[]} Array of ReviewLine objects. */ export function processModule( item: Item, parentModule?: { prefix: string; id: number }, ): ReviewLine[] { if (!isModuleItem(item)) return []; const reviewLines: ReviewLine[] = item.docs ? createDocsReviewLines(item) : []; const apiJson = getAPIJson(); const isRootModule = item.id === apiJson.root; lineIdMap.set(item.id.toString(), item.name || "unknown_mod"); // Create the ReviewLine object const reviewLine: ReviewLine = { LineId: item.id.toString(), Tokens: [], Children: [], }; let fullName = ""; if (!isRootModule) { reviewLine.Tokens.push({ Kind: TokenKind.Keyword, Value: "pub mod", }); // parent module if (parentModule) { fullName += parentModule.prefix + "::"; reviewLine.Tokens.push({ Kind: TokenKind.TypeName, Value: parentModule.prefix, RenderClasses: ["namespace"], NavigateToId: parentModule.id.toString(), HasSuffixSpace: false, }); reviewLine.Tokens.push({ Kind: TokenKind.Punctuation, Value: `::`, HasSuffixSpace: false, }); } fullName += item.name; lineIdMap.set(item.id.toString(), fullName); // current module reviewLine.Tokens.push({ Kind: TokenKind.TypeName, Value: item.name || "unknown_mod", RenderClasses: ["namespace"], NavigateToId: item.id.toString(), NavigationDisplayName: fullName, }); reviewLine.Tokens.push({ Kind: TokenKind.Punctuation, Value: "{", HasSuffixSpace: false, }); } const allChildIds = item.inner.module.items; const regularChildIds = []; const useChildIds = []; const annotatedReviewLines: AnnotatedReviewLines = { siblingModule: {}, children: {} }; const passThroughParentInfo = isRootModule ? parentModule : { id: item.id, prefix: fullName, }; for (const childId of allChildIds) { if (isModuleItem(apiJson.index[childId])) { // 1. Process child modules first so that any references in the parent modules can be shown as links than the fully dereferenced version annotatedReviewLines.siblingModule[childId] = processModule( apiJson.index[childId], passThroughParentInfo, ); processedItems.add(childId); } else if (!isUseItem(apiJson.index[childId])) { // 2. Non-use children should be processed before the use items and after the child modules regularChildIds.push(childId); } else if (isUseItem(apiJson.index[childId])) { // 3. handle use items; with globs, non-globs, modules, items, etc; either as references or add as children to this module useChildIds.push(childId); } } if (regularChildIds.length > 0) { for (const childId of regularChildIds) { if (childId in apiJson.index) { annotatedReviewLines.children[childId] = processItem(apiJson.index[childId]) || []; processedItems.add(childId); } } } // Process the use items if (useChildIds.length > 0) { for (const childId of useChildIds) { const useResult = processUse(apiJson.index[childId], passThroughParentInfo); processedItems.add(childId); for (const key in useResult.siblingModule) { annotatedReviewLines.siblingModule[key] = useResult.siblingModule[key]; } for (const key in useResult.children) { annotatedReviewLines.children[key] = useResult.children[key]; } } } // Take the keys from the children object, sort using getSortedChildren // and then push to reviewLine.Children in that order if (Object.keys(annotatedReviewLines.children).length > 0) { const sortedChildIds = getSortedChildIds( Object.keys(annotatedReviewLines.children).map((key) => parseInt(key)), ); for (const childId of sortedChildIds.nonModule) { if (annotatedReviewLines.children[childId]) { if (!isRootModule) { // If it is not rootModule, add items as children to the reviewLine reviewLine.Children.push(...annotatedReviewLines.children[childId]); } else { // If it is rootModule, add items to the top-level reviewLines.push(...annotatedReviewLines.children[childId]); } } } } if (!isRootModule) { if (reviewLine.Children.length > 0) { // Add the closing brace for the module reviewLines.push(reviewLine); reviewLines.push({ Tokens: [{ Kind: TokenKind.Punctuation, Value: "}" }], IsContextEndLine: true, RelatedToLine: reviewLine.LineId, }); } else { // Empty module case (no direct items or relevant re-exports) reviewLine.Tokens.push({ Kind: TokenKind.Punctuation, Value: "}", }); reviewLines.push(reviewLine); } } // there will be sibling modules from the re-exported Use modules // add them to the allChildIds.module, sort them again const sortedSiblingModuleIds = getSortedChildIds( Object.keys(annotatedReviewLines.siblingModule).map((key) => parseInt(key)), ); for (const childId of sortedSiblingModuleIds.module) { reviewLines.push(...annotatedReviewLines.siblingModule[childId]); } return reviewLines; }