From 341f99ae557ed56de3c5fcbee6f521467a53d5b4 Mon Sep 17 00:00:00 2001 From: Alexander Sullivan Date: Mon, 15 Jun 2026 16:24:38 -0400 Subject: [PATCH] update docs --- .github/agents/extension-developer.md | 8 +- .github/agents/tester.md | 2 +- .github/copilot-instructions.md | 12 +- README.md | 12 +- SECURITY.MD | 2 +- SUPPORT.md | 5 +- docs/architecture/tree-data-provider.md | 10 +- docs/architecture/utilities.md | 6 +- docs/usage/publishing.md | 2 +- docs/usage/workflows.md | 2 +- example/index-files-test/index.md | 10 +- .../subdirectory-1/index.md | 6 +- package-lock.json | 182 ++++++++++-------- package.json | 2 +- src/controllers/previewController.ts | 11 +- src/scanner/workspaceScanner.ts | 3 + src/tree/buildTree.ts | 20 +- src/tree/treeProvider.ts | 42 ++++ src/utils/configUtils.ts | 25 +++ src/utils/fileUtils.ts | 25 +++ src/utils/textUtils.ts | 29 ++- 21 files changed, 282 insertions(+), 134 deletions(-) diff --git a/.github/agents/extension-developer.md b/.github/agents/extension-developer.md index 0607135..c154988 100644 --- a/.github/agents/extension-developer.md +++ b/.github/agents/extension-developer.md @@ -41,16 +41,16 @@ You are a specialized VS Code extension developer with deep expertise in the VS - **Purpose**: Documentation file tree view with preview and editing capabilities - **File Types**: `.md`, `.markdown`, `.txt` by default; configurable via settings -- **Tree Ordering**: README first, index.md as folder representative, alphabetical sorting +- **Tree Ordering**: README first, folders display their own name with `index.md` shown as a child, alphabetical sorting - **Settings Namespace**: All configuration under `workspaceWiki.*` - **File Filtering**: Support for hidden files (`showHiddenFiles`) and ignored files (`showIgnoredFiles`) -- **Performance**: File system caching and efficient tree updates +- **Performance**: Front matter is read in parallel during tree building; the tree re-scans on refresh (no persistent caching) ### Key Implementation Areas - **Scanner/Indexer**: Efficient file discovery with `workspace.findFiles()` -- **Tree Ordering Logic**: README prioritization and folder-as-index handling -- **File System Watching**: Responding to file changes for tree updates +- **Tree Ordering Logic**: README prioritization and folder name normalization +- **Refresh Triggers**: Manual `workspace-wiki.refresh` command and `onDidChangeConfiguration`; the active file is revealed on editor changes (no file system watcher) - **Preview Integration**: Opening files in preview vs editor modes - **Settings Integration**: Reading and reacting to configuration changes diff --git a/.github/agents/tester.md b/.github/agents/tester.md index d824938..0537f9d 100644 --- a/.github/agents/tester.md +++ b/.github/agents/tester.md @@ -98,7 +98,7 @@ describe('WikiTreeDataProvider', () => { - Extension activation/deactivation - Command registration and execution - Tree provider data flow -- File system watcher behavior +- Refresh on configuration changes and active-editor reveal behavior ### E2E Tests diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index 5e44385..2f3daba 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -49,8 +49,8 @@ This guide enables AI coding agents to be immediately productive in the Workspac - **Purpose:** Presents workspace documentation files in a sidebar tree view for fast preview and editing. - **Main Components:** - - **Scanner/Indexer:** Uses `workspace.findFiles` and file system watchers to discover docs. Caches metadata for performance. - - **TreeDataProvider:** Implements VS Code's tree view, applies ordering rules (README/index, alphabetical, folder-as-index). + - **Scanner/Indexer:** Uses `workspace.findFiles` to discover docs on each scan; no caching or file watching (the tree re-scans on refresh). + - **TreeDataProvider:** Implements VS Code's tree view, applies ordering rules (README first, folders display their own name with `index.md` as a child, alphabetical otherwise). - **Preview/Open Controller:** Handles user interactions, opens files in preview/editor modes. - **Settings Manager:** Reads extension config from `workspaceWiki` namespace. - **Sync Module:** Reveals active file in the tree. @@ -71,7 +71,7 @@ This guide enables AI coding agents to be immediately productive in the Workspac - Use `npm run test:jest` for Jest unit tests. - Use `npm run test:extension` for integration/e2e tests (via `vscode-test`). - Use `npm run watch-tests` for live test compilation. -- Test files must match `**.test.ts` (unit) and `**.e2e.test.ts` (e2e) and reside in `src/test/`. +- Test files must match `**.test.ts` (unit) and `**.e2e.test.ts` (e2e) and sit beside the file they test (e.g. `src/utils/textUtils.test.ts`). The `src/test/` directory holds only shared testing utilities (`mocks.ts`, `setupTests.ts`). **CRITICAL - Test Preservation:** @@ -117,7 +117,7 @@ This guide enables AI coding agents to be immediately productive in the Workspac ## Project-Specific Conventions -- **Ordering:** README at root is always top; index.md in folders replaces folder name; alphabetical sorting elsewhere. +- **Ordering:** README at root is always top; folders display their own normalized name with any `index.md` shown as a child; alphabetical sorting elsewhere. - **TypeScript Config:** Use only the root `tsconfig.json`. Do not create per-purpose `tsconfig` files. - **Supported File Types:** `.md`, `.markdown`, `.txt` by default; `.pdf`, `.html` opt-in via settings. - **Settings:** All config under `workspaceWiki` namespace (see design doc for full schema). @@ -148,9 +148,9 @@ This guide enables AI coding agents to be immediately productive in the Workspac - `src/extension.ts`: Main extension logic. - `esbuild.js`: Build pipeline. - `eslint.config.mjs`: Linting rules. -- `src/test/extension.test.ts`: Example test. +- `src/extension.test.ts`: Example test. - `.vscode/launch.json`, `.vscode/tasks.json`: Debug and build tasks. -- `docs/design-doc.md`: Architecture and implementation details. +- `docs/project/design-doc.md`: Architecture and implementation details. - `.github/agents/`: Specialized AI agent configurations for domain expertise. ## Examples diff --git a/README.md b/README.md index abaf36c..d68910e 100644 --- a/README.md +++ b/README.md @@ -4,8 +4,8 @@ [![Downloads](https://img.shields.io/visual-studio-marketplace/d/alexjsully.workspace-wiki)](https://marketplace.visualstudio.com/items?itemName=alexjsully.workspace-wiki) [![Open VSX Version](https://img.shields.io/open-vsx/v/alexjsully/workspace-wiki)](https://open-vsx.org/extension/alexjsully/workspace-wiki) [![Follow on Twitter](https://img.shields.io/twitter/follow/alexjsully?style=social)](https://twitter.com/alexjsully) -[![GitHub repo size](https://img.shields.io/github/repo-size/AlexJSully/alexjsully-portfolio)](https://github.com/AlexJSully/alexjsully-portfolio) -[![GitHub](https://img.shields.io/github/license/AlexJSully/alexjsully-portfolio)](https://github.com/AlexJSully/alexjsully-portfolio) +[![GitHub repo size](https://img.shields.io/github/repo-size/AlexJSully/workspace-wiki)](https://github.com/AlexJSully/workspace-wiki) +[![GitHub](https://img.shields.io/github/license/AlexJSully/workspace-wiki)](https://github.com/AlexJSully/workspace-wiki) [![Build Status](https://github.com/AlexJSully/workspace-wiki/actions/workflows/code-qa-js.yaml/badge.svg)](https://github.com/AlexJSully/workspace-wiki/actions) Workspace Wiki is a VS Code extension that scans your workspace for documentation files (Markdown and plain text by default) and presents them in a sidebar tree view for fast preview and editing. It emphasizes readability, predictable ordering (README/index handling, alphabetical directories), and fast access via preview or edit. All operations are local-first and privacy-friendly. @@ -22,7 +22,7 @@ Workspace Wiki is a VS Code extension that scans your workspace for documentatio - **Acronym Case Preservation:** Technical terms like HTML, CSS, API maintain proper casing in titles. - **Intelligent File Exclusion:** Respects .gitignore patterns and configurable exclude globs to hide unwanted files. - **Preview & Edit:** Single-click to preview, double-click to edit in full editor. -- **Live Updates:** Tree auto-refreshes when files change. +- **Refresh:** Update the tree on demand with the Refresh action; it also re-scans automatically when `workspaceWiki` settings change. - **Configurable:** Supported file types, excludes, open modes, and title formatting are configurable via settings. - **Multi-root Support:** Works with multi-root workspaces. - **Privacy:** No telemetry, no cloud sync, local-only by default. @@ -142,11 +142,7 @@ Array of acronyms to preserve proper casing in file titles. "REST", "SQL", "CSV", - "FHIR", - "BFF", - "MFE", - "PDF", - "VSC" + "FHIR" ] } ``` diff --git a/SECURITY.MD b/SECURITY.MD index 78f59d7..f298e22 100644 --- a/SECURITY.MD +++ b/SECURITY.MD @@ -6,4 +6,4 @@ We are actively maintaining and developing on the latest build on the master bra ## Reporting a Vulnerability -If you want to report a security vulnerability, you can do so [on the GitHub issues page](https://github.com/AlexJSully/alexjsully-portfolio/issues/new/choose). +If you want to report a security vulnerability, you can do so [on the GitHub issues page](https://github.com/AlexJSully/workspace-wiki/issues/new/choose). diff --git a/SUPPORT.md b/SUPPORT.md index a5bb8a3..c025fc3 100644 --- a/SUPPORT.md +++ b/SUPPORT.md @@ -21,7 +21,7 @@ For new features or enhancements, please [submit a feature request](https://gith ### 🔒 Security Issues -For security-related issues, please see our [Security Policy](SECURITY.md) for responsible disclosure guidelines. +For security-related issues, please see our [Security Policy](SECURITY.MD) for responsible disclosure guidelines. ### 💬 Discussions @@ -33,8 +33,7 @@ For general questions, usage help, or community discussions: ### 📚 Documentation - Read the [README](README.md) for setup and usage instructions -- Check the [Design Documentation](docs/design-doc.md) for technical details -- See the [Roadmap](docs/ROADMAP.md) for planned features +- Check the [Design Documentation](docs/project/design-doc.md) for technical details ### 🤝 Contributing diff --git a/docs/architecture/tree-data-provider.md b/docs/architecture/tree-data-provider.md index 3a3eeeb..e4fc566 100644 --- a/docs/architecture/tree-data-provider.md +++ b/docs/architecture/tree-data-provider.md @@ -12,7 +12,7 @@ The TreeDataProvider is implemented in [`src/tree/treeProvider.ts`](../../src/tr - Applies ordering rules: - `README.md` at root always first - Root-level docs next (alphabetical) - - Folders (with `index.md` as folder node) + - Folders (displayed by their own normalized name; an `index.md` appears as a child file, not as the folder node) - Files inside folders (alphabetical, with `README.md` at top) - Normalizes file names to human-friendly titles. - Handles cross-platform path compatibility by normalizing path separators @@ -87,7 +87,7 @@ See also: [Scanner/Indexer](./scanner.md) ```mermaid flowchart TD accTitle: Tree Node Ordering Logic - accDescr: Shows how tree nodes are sorted - README nodes always rank first, then root-level nodes are ordered by the directorySort setting (files-first, folders-first, or alphabetical), and finally alphabetically by title within each group. Folder nodes use index.md for their name when present. + accDescr: Shows how tree nodes are sorted - README nodes always rank first, then root-level nodes are ordered by the directorySort setting (files-first, folders-first, or alphabetical), and finally alphabetically by title within each group. Folder nodes always display their own normalized folder name; an index.md inside a folder is listed as a child file. A[All Nodes] --> B{Is README?} B -->|Yes| C[Rank First] B -->|No| D{Root Level?} @@ -101,10 +101,8 @@ flowchart TD I --> K J --> K K --> L[Final Sorted List] - F -->|index.md Exists| M[Folder Named by index] - F -->|No index.md| N[Use Folder Name] - M --> O[Children Sorted] - N --> O + F --> N[Use Folder Name] + N --> O[Children Sorted] O --> L ``` diff --git a/docs/architecture/utilities.md b/docs/architecture/utilities.md index 0c0fcc0..8a02e86 100644 --- a/docs/architecture/utilities.md +++ b/docs/architecture/utilities.md @@ -71,9 +71,9 @@ The `normalizeTitle()` function performs several transformations: ```typescript normalizeTitle('getting-started.md'); // → 'Getting Started' -normalizeTitle('api_reference.md'); // → 'API Reference' +normalizeTitle('api_reference.md', ['API']); // → 'API Reference' normalizeTitle('userGuide.md'); // → 'User Guide' -normalizeTitle('HTMLParser.md'); // → 'HTML Parser' +normalizeTitle('htmlParser.md', ['HTML']); // → 'HTML Parser' normalizeTitle('README.md'); // → 'README' ``` @@ -114,6 +114,8 @@ See also: [Settings Manager](./settings.md) ```mermaid flowchart TD + accTitle: Utilities Module Relationships + accDescr: Shows how the utility functions are consumed by other modules. The title normalizer feeds the TreeDataProvider, while the file type detector feeds both the Scanner/Indexer and the Preview/Open Controller. A[Title Normalizer] --> B[TreeDataProvider] C[File Type Detector] --> D[Scanner/Indexer] C --> E[Preview/Open Controller] diff --git a/docs/usage/publishing.md b/docs/usage/publishing.md index a16434e..3cad5a9 100644 --- a/docs/usage/publishing.md +++ b/docs/usage/publishing.md @@ -18,7 +18,7 @@ This guide explains how to package and publish the Workspace Wiki extension to t 1. Build the extension: ```sh - npm run build + npm run package ``` 2. Run tests and lint: diff --git a/docs/usage/workflows.md b/docs/usage/workflows.md index 425e12b..95dcfe0 100644 --- a/docs/usage/workflows.md +++ b/docs/usage/workflows.md @@ -90,7 +90,7 @@ npm run lint # ESLint validation npm run test:jest # Jest unit tests npm run test:extension # E2E tests npm run compile # TypeScript compilation -npm run build # Extension bundling +npm run package # Production extension bundle ``` ## Workflow Configuration diff --git a/example/index-files-test/index.md b/example/index-files-test/index.md index e00ba0e..29b3169 100644 --- a/example/index-files-test/index.md +++ b/example/index-files-test/index.md @@ -6,21 +6,21 @@ This directory tests the Workspace Wiki extension's handling of index files and This directory validates that the extension: -- Recognizes `index.md` files as folder landing pages -- Uses index file titles to replace folder names in the tree display +- Recognizes `index.md` files within a folder +- Lists `index.md` as a child file while the folder keeps its own name - Handles case variations in README files (README.md vs readme.md) - Correctly processes different index file extensions (index.html, index.txt) - Maintains proper ordering with index files present ## Expected Behavior -When this directory contains an `index.md` file, the tree should display the folder using the index file's title rather than the raw directory name "index-files-test". +When this directory contains an `index.md` file, the tree displays the folder using its own normalized name ("Index Files Test"); the `index.md` appears as a child file, flagged internally as an index file. -Files within this directory should be ordered with README files at the top, followed by other files alphabetically, while the index file itself serves as the folder representation. +Files within this directory are ordered with README files at the top, followed by other files alphabetically, including the `index.md` itself. ## Testing Scenarios -- **Index file as folder node**: This index.md should be used as the folder title +- **Index file as a child**: This index.md is listed as a child of the folder, not as the folder's title - **Case-insensitive README handling**: readme.md should be treated equivalently to README.md - **Multiple index types**: Different extensions for index files should be handled appropriately - **Ordering consistency**: Files should maintain alphabetical order regardless of index file presence diff --git a/example/nested-structure-test/subdirectory-1/index.md b/example/nested-structure-test/subdirectory-1/index.md index 7e3f787..f9428a1 100644 --- a/example/nested-structure-test/subdirectory-1/index.md +++ b/example/nested-structure-test/subdirectory-1/index.md @@ -7,7 +7,7 @@ This is the index file for subdirectory-1, demonstrating index file behavior in This directory tests: - **Nested index files** - How index.md works in subdirectories -- **Folder name replacement** - This title should replace "subdirectory-1" in the tree +- **Folder naming** - The folder displays its own name "Subdirectory 1"; this index.md is shown as a child - **Nested ordering** - Files within this subdirectory should follow standard ordering rules - **Tree expansion** - Proper collapsible/expandable behavior @@ -15,10 +15,10 @@ This directory tests: When this subdirectory is displayed in the Workspace Wiki tree: -1. It should show as "Level One Directory" instead of "subdirectory-1" +1. It shows as "Subdirectory 1" (its own normalized folder name) 2. It should be expandable/collapsible 3. Files within should be ordered alphabetically -4. The index.md file itself should not appear as a separate child (it IS the folder node) +4. The index.md file appears as a child of the folder (flagged internally as an index file) ## Content diff --git a/package-lock.json b/package-lock.json index 4b40f8a..2ab686e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1497,9 +1497,9 @@ } }, "node_modules/@eslint/plugin-kit": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.7.1.tgz", - "integrity": "sha512-rZAP3aVgB9ds9KOeUSL+zZ21hPmo8dh6fnIFwRQj5EAZl9gzR7wxYbYXYysAM8CTqGmUGyp2S4kUdV17MnGuWQ==", + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.7.2.tgz", + "integrity": "sha512-+CNAzxglkrpNf/kKywqQfk74QjtceuOE7Qm+AF8miRvPF/wmmK5+OJOgVh3AVTT3RP2mH3+FOaxlE5v72owk0A==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -2820,17 +2820,17 @@ "license": "MIT" }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.60.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.60.0.tgz", - "integrity": "sha512-QYb/sa74/s7OKMbACMjrYnGspj9Hs5YI5aaffSL65UfeBUzVzBJfVo3oWSpbzPurvm7yaCCo2Lk7lVj610HqKw==", + "version": "8.60.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.60.1.tgz", + "integrity": "sha512-JQ4S5GB0tfjO8BuJ4fcX+HodkzJjYBV+7OJ+wLygaX7OGQ7FudyHL4NSCA6ob+w3Yn+5MkKIozOwQhXeM7opVg==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.12.2", - "@typescript-eslint/scope-manager": "8.60.0", - "@typescript-eslint/type-utils": "8.60.0", - "@typescript-eslint/utils": "8.60.0", - "@typescript-eslint/visitor-keys": "8.60.0", + "@typescript-eslint/scope-manager": "8.60.1", + "@typescript-eslint/type-utils": "8.60.1", + "@typescript-eslint/utils": "8.60.1", + "@typescript-eslint/visitor-keys": "8.60.1", "ignore": "^7.0.5", "natural-compare": "^1.4.0", "ts-api-utils": "^2.5.0" @@ -2843,22 +2843,22 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^8.60.0", + "@typescript-eslint/parser": "^8.60.1", "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.1.0" } }, "node_modules/@typescript-eslint/parser": { - "version": "8.60.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.60.0.tgz", - "integrity": "sha512-fcqpj/MyK4sxDPcbe7STNPbpQL4RLZOPWuaTmwZYuc+hJKzRf58yRxfhqGpc6PIq9ZyfSBpfHgmUHmHs0KwHwg==", + "version": "8.60.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.60.1.tgz", + "integrity": "sha512-A0M6ua6H252bVjPvvtSgl2QA4+ET9S5Mtkb2GDyTxIhH/C4qDItT7RQNO5PhMC6NXGYXOR9dIalcDDgBKT7oFA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "8.60.0", - "@typescript-eslint/types": "8.60.0", - "@typescript-eslint/typescript-estree": "8.60.0", - "@typescript-eslint/visitor-keys": "8.60.0", + "@typescript-eslint/scope-manager": "8.60.1", + "@typescript-eslint/types": "8.60.1", + "@typescript-eslint/typescript-estree": "8.60.1", + "@typescript-eslint/visitor-keys": "8.60.1", "debug": "^4.4.3" }, "engines": { @@ -2874,14 +2874,14 @@ } }, "node_modules/@typescript-eslint/project-service": { - "version": "8.60.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.60.0.tgz", - "integrity": "sha512-aZu74NNKJeUWqCjDddzdiKaS82dgYgV/vmf+Ui3ZdZejmgfXR/q+pRumgobnQ2cCJTgGTWp4ypiwsuofFubavg==", + "version": "8.60.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.60.1.tgz", + "integrity": "sha512-eXkTH2bxmXlqD1RnOPmLZ9ZM9D3VwSx04JOwBnP9RQ+yUA5a2Mu7SfW8uaV2Aon53NJzZlZYuX7tn91Izf+xaw==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.60.0", - "@typescript-eslint/types": "^8.60.0", + "@typescript-eslint/tsconfig-utils": "^8.60.1", + "@typescript-eslint/types": "^8.60.1", "debug": "^4.4.3" }, "engines": { @@ -2896,14 +2896,14 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.60.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.60.0.tgz", - "integrity": "sha512-pFzqhllJMs+jghLQWzV00ds39xLzuyqPSev5pd8f4Ir0rtKR3ZLUB4/4dhjOFighWb9larvtfJvqL+4yKDI3Xw==", + "version": "8.60.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.60.1.tgz", + "integrity": "sha512-gvI5OQoptnxQnchOirukCuQ55svJSTuD/4k5+pC267xyBtYry748R9/c3tYUzb/iE6RZfllRz2lVulLCHkTm4w==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.60.0", - "@typescript-eslint/visitor-keys": "8.60.0" + "@typescript-eslint/types": "8.60.1", + "@typescript-eslint/visitor-keys": "8.60.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -2914,9 +2914,9 @@ } }, "node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.60.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.60.0.tgz", - "integrity": "sha512-BZPR3RGYlAXnly6ymAxfkVn5rCbZzQNou0rxv3GfWZ8cTQp+hhVd73khbGLAd8k1TlAPLISH337M+tAgAnaJDQ==", + "version": "8.60.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.60.1.tgz", + "integrity": "sha512-nh8w4qAteiKuZu3pSSzG/yGKpw0OlkrKnzFmbVRenKaD4qc+7i1GrmZaLVkr8rk4uipiPGMOW4YsM6WmKZ5CvA==", "dev": true, "license": "MIT", "engines": { @@ -2931,15 +2931,15 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.60.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.60.0.tgz", - "integrity": "sha512-SX46wEUtitCpq7AN38HkUU/+zvUpdKf7ephtWAFgckH8O7PQIyL5gvrhQgBLuEYgLfuKWOVvWVskMbuFHAz5xg==", + "version": "8.60.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.60.1.tgz", + "integrity": "sha512-sdwTrpjosW7ANQYJ39ZBF1ZyEMEGVB2UsikrserVM/30a/F1dTLnu9bGxEdosugyu5caigjLrR2qiD11asjI1A==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.60.0", - "@typescript-eslint/typescript-estree": "8.60.0", - "@typescript-eslint/utils": "8.60.0", + "@typescript-eslint/types": "8.60.1", + "@typescript-eslint/typescript-estree": "8.60.1", + "@typescript-eslint/utils": "8.60.1", "debug": "^4.4.3", "ts-api-utils": "^2.5.0" }, @@ -2956,9 +2956,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "8.60.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.60.0.tgz", - "integrity": "sha512-AsE7x2XaAK+CVbeih0Fvbn+r1qHxtpLDJ3XUuFcIinT318T90yHMJC+Zgv+jUuDjQQd06HKwxnDu6sz1IcTilA==", + "version": "8.60.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.60.1.tgz", + "integrity": "sha512-4h0tY8ppCkdCzcrl2YM5M3my0xsE1Tf8om3owEu5oPWmXwkKRmk0j0LGDzYBGUcAlesEbxBhazqu/K4cu3Ug7w==", "dev": true, "license": "MIT", "engines": { @@ -2970,16 +2970,16 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.60.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.60.0.tgz", - "integrity": "sha512-3AcZNBGMClm6CXDyo8kYvVGT/sx29sS0oBsIb9oZI2gunA4Vm2M3YHzRLPvsUBBsl+yB5FPtltq7gGH0iTlp9g==", + "version": "8.60.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.60.1.tgz", + "integrity": "sha512-alpRkfG8hlVE5kdJW2GkfgDgXxold3e8e4l6EnmhRmRLbekgAPCCGDVD++sABy9FcgPFroq+uFcCSM1vR57Cew==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/project-service": "8.60.0", - "@typescript-eslint/tsconfig-utils": "8.60.0", - "@typescript-eslint/types": "8.60.0", - "@typescript-eslint/visitor-keys": "8.60.0", + "@typescript-eslint/project-service": "8.60.1", + "@typescript-eslint/tsconfig-utils": "8.60.1", + "@typescript-eslint/types": "8.60.1", + "@typescript-eslint/visitor-keys": "8.60.1", "debug": "^4.4.3", "minimatch": "^10.2.2", "semver": "^7.7.3", @@ -3037,16 +3037,16 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.60.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.60.0.tgz", - "integrity": "sha512-HtXuPfrHTyBDkameWpl+vJb1Uevu2tznAyahM1Oc4AENidCLTPiZDWIo4GfcxNdC/RcfGcadzzkqbRG87dUrQA==", + "version": "8.60.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.60.1.tgz", + "integrity": "sha512-h2MPBLoNtjc3qZWfY3Tl51yPorQ2McHn8pJfcMNTcIvrrZrr90Ykffit0yjrPFWQcRcUxzH20+6OcVdW4yHtUg==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.9.1", - "@typescript-eslint/scope-manager": "8.60.0", - "@typescript-eslint/types": "8.60.0", - "@typescript-eslint/typescript-estree": "8.60.0" + "@typescript-eslint/scope-manager": "8.60.1", + "@typescript-eslint/types": "8.60.1", + "@typescript-eslint/typescript-estree": "8.60.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -3061,13 +3061,13 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.60.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.60.0.tgz", - "integrity": "sha512-9WI52t8ZGLVGrPMBet25yAftqY/n95+zmoUUtJBBQTKDSKUu7OsPTroT2op7U9JatkoRccL0YkWDNMFfC4Sjxg==", + "version": "8.60.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.60.1.tgz", + "integrity": "sha512-EbGRQg4FhrmwLodl+t3JNAnXHWVr9Vp+Zl1QBZVPY4ByfkzIT8cX3K6QWODHtkIZqqJVEWvhHSx3v5PDHsaQag==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.60.0", + "@typescript-eslint/types": "8.60.1", "eslint-visitor-keys": "^5.0.0" }, "engines": { @@ -4239,9 +4239,9 @@ "optional": true }, "node_modules/baseline-browser-mapping": { - "version": "2.10.32", - "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.32.tgz", - "integrity": "sha512-wbPvpyjJPC0zdfdKXxqEL3Ea+bOMD/87X4lftiJkkaBiuG6ALQy1SLmEd7BSmVCuwCQsBrCamgBoLyfFDD1EPg==", + "version": "2.10.33", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.33.tgz", + "integrity": "sha512-bA6+tcSLpz2tIEdDXZPpPTIuxBcC4+w6SieaYyfigIa4h8GlFxbA17v22Vx3JUtuZQj9SgOsnbK+aTBzyDyEuw==", "dev": true, "license": "Apache-2.0", "bin": { @@ -5759,9 +5759,9 @@ } }, "node_modules/eslint": { - "version": "10.4.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-10.4.0.tgz", - "integrity": "sha512-loXy6bWOoP3EP6JA7jo6p5jMpBJmHmsNZM5SFRHLdh1MGOPurMnNBj4ZlAbaqUAaQWbCr7jHV4P7gzAyryZWkQ==", + "version": "10.4.1", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-10.4.1.tgz", + "integrity": "sha512-AyIKhnOBuOAdueD7RB3xB+YeAWScb9jHsJBgH2Hcde8InP5JYhqrRR6iTMHyTEwgENK54Cp44e4v8BwNhsuHuw==", "dev": true, "license": "MIT", "dependencies": { @@ -5770,7 +5770,7 @@ "@eslint/config-array": "^0.23.5", "@eslint/config-helpers": "^0.6.0", "@eslint/core": "^1.2.1", - "@eslint/plugin-kit": "^0.7.1", + "@eslint/plugin-kit": "^0.7.2", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.2", @@ -8461,10 +8461,20 @@ "license": "MIT" }, "node_modules/js-yaml": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", - "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.2.0.tgz", + "integrity": "sha512-ePWsvanv0DWuDRsW8dnt+R4jQ31SCRCQ7hhNcPXZPsoBZiemuZNYGf7adZdqX2D86j6rvKp3RpCxVTSb8WQlOw==", "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/puzrin" + }, + { + "type": "github", + "url": "https://github.com/sponsors/nodeca" + } + ], "license": "MIT", "dependencies": { "argparse": "^2.0.1" @@ -9091,6 +9101,19 @@ "node": ">=20" } }, + "node_modules/markdownlint-cli/node_modules/js-yaml": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, "node_modules/markdownlint-cli/node_modules/markdown-it": { "version": "14.1.1", "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.1.1.tgz", @@ -11220,9 +11243,9 @@ }, "node_modules/react-is-19": { "name": "react-is", - "version": "19.2.6", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-19.2.6.tgz", - "integrity": "sha512-XjBR15BhXuylgWGuslhDKqlSayuqvqBX91BP8pauG8kd1zY8kotkNWbXksTCNRarse4kuGbe2kIY05ARtwNIvw==", + "version": "19.2.7", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-19.2.7.tgz", + "integrity": "sha512-kZFnouyVv7eP/Phmrlo9FK+zcAdriZJvzxXHF1Sl1P377WSGe2G/JxVolhTrB/jeV47lKImhNUsijjHAAbcl/A==", "dev": true, "license": "MIT" }, @@ -12794,9 +12817,9 @@ } }, "node_modules/tinyglobby": { - "version": "0.2.16", - "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.16.tgz", - "integrity": "sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg==", + "version": "0.2.17", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz", + "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==", "dev": true, "license": "MIT", "dependencies": { @@ -13215,9 +13238,9 @@ "license": "MIT" }, "node_modules/undici": { - "version": "7.26.0", - "resolved": "https://registry.npmjs.org/undici/-/undici-7.26.0.tgz", - "integrity": "sha512-3O9Tf67pGhgOv9jM35AbhkXAKi13f3oy3aE4CSgr+TckGeY+/iu97ZXN+J7DpHPzLbVApFd1IFhcnBjREYXYcg==", + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-7.27.0.tgz", + "integrity": "sha512-+t2Z/GwkZQDtu00813aP66ygViGtPHKhhoFZpQKpKrE+9jIgES+Zw+mFNaDWOVRKiuJjuqKHzD3B1sfGg8+ZOQ==", "dev": true, "license": "MIT", "engines": { @@ -13867,13 +13890,12 @@ } }, "node_modules/yauzl": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-3.3.1.tgz", - "integrity": "sha512-RNPCUkiE/ZgO4w8i9U5yDQVHaFDdnzaFANElRvpJteCspvmv2VqrRb9lvS6odVD+jqI/zDsxAHJVsafpcheVQQ==", + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-3.3.2.tgz", + "integrity": "sha512-Md9ankxxN23wncAN8s7+Tn3Co52zLUPMtnrLAbVCnfG5d2tKBFfmygYSgXlqFgXObtzIgqkx7aNgDBpso9+4qA==", "dev": true, "license": "MIT", "dependencies": { - "buffer-crc32": "~0.2.3", "pend": "~1.2.0" }, "engines": { diff --git a/package.json b/package.json index 12c72cc..9e0ce7b 100644 --- a/package.json +++ b/package.json @@ -214,7 +214,7 @@ "lint:markdown": "markdownlint \"**/*.md\"", "package": "node esbuild.js --production", "package:vsix": "npm run compile && vsce package", - "pretest": "npm run compile-tests && npm run compile && npm run lint", + "pretest": "npm run compile:tests && npm run compile && npm run lint", "prettier": "prettier --write ./", "prettier:check": "prettier --check ./", "regen-package-lock": "rm -rf node_modules package-lock.json && npm run audit:fix", diff --git a/src/controllers/previewController.ts b/src/controllers/previewController.ts index a18372a..e6a517d 100644 --- a/src/controllers/previewController.ts +++ b/src/controllers/previewController.ts @@ -31,7 +31,9 @@ function getFileExtension(uri: vscode.Uri): string | undefined { } /** - * Retrieves the 'openWith' configuration from settings + * Retrieves the 'openWith' configuration from settings, validating it is an object of string values. + * Falls back to `DEFAULT_OPEN_WITH` when the setting is missing or malformed. + * * @returns A mapping of file extensions to commands */ function getOpenWithConfig(): Record { @@ -51,7 +53,7 @@ function getOpenWithConfig(): Record { } /** - * Opens a file in preview mode + * Opens a file in preview mode using the command configured for its extension (falling back to `vscode.open`). * @param uri The URI of the file to open */ export function openInPreview(uri: vscode.Uri): void { @@ -60,7 +62,7 @@ export function openInPreview(uri: vscode.Uri): void { } /** - * Opens a file in editor mode + * Opens a file in editor mode (always via the `vscode.open` command). * @param uri The URI of the file to open */ export function openInEditor(uri: vscode.Uri): void { @@ -68,7 +70,8 @@ export function openInEditor(uri: vscode.Uri): void { } /** - * Handles file clicks with double-click detection + * Handles file clicks with double-click detection: a second click within `DOUBLE_CLICK_THRESHOLD` + * opens the file in the editor, otherwise the single-click `defaultCommand` runs. * @param uri The URI of the file that was clicked * @param defaultCommand The default command to execute on single click */ diff --git a/src/scanner/workspaceScanner.ts b/src/scanner/workspaceScanner.ts index ca3f1f1..4048119 100644 --- a/src/scanner/workspaceScanner.ts +++ b/src/scanner/workspaceScanner.ts @@ -4,6 +4,9 @@ import * as vscode from 'vscode'; /** * Scans the workspace for documentation files (.md, .markdown, .txt) * Returns a list of file URIs matching supported extensions, respecting excludes and settings. + * + * @param workspace The workspace abstraction providing `findFiles` and optional `getConfiguration` + * @returns Promise resolving to the matching file URIs (extension files plus extensionless README when Markdown is enabled) */ export async function scanWorkspaceDocs(workspace: WorkspaceLike): Promise { let supportedExtensions = ['md', 'markdown', 'txt']; diff --git a/src/tree/buildTree.ts b/src/tree/buildTree.ts index 3b90248..62a8971 100644 --- a/src/tree/buildTree.ts +++ b/src/tree/buildTree.ts @@ -2,7 +2,10 @@ import type { TreeNode } from '@types'; import { extractFrontMatter, normalizeTitle } from '@utils'; /** - * Sorts tree nodes based on directory sort setting + * Sorts tree nodes in place based on the directory sort setting (README nodes always rank first). + * + * @param nodes The sibling nodes to sort + * @param directorySort `'files-first'`, `'folders-first'`, or `'alphabetical'`; defaults to `'files-first'` */ export function sortNodes( nodes: TreeNode[], @@ -36,7 +39,10 @@ export function sortNodes( } /** - * Processes tree nodes recursively, applying sorting to children + * Recursively sorts a folder node's children (the folder keeps its own name; `index.md` stays a child). + * + * @param node The node to process + * @param directorySort The sort mode to apply to descendants; defaults to `'files-first'` */ export function processNode( node: TreeNode, @@ -53,7 +59,15 @@ export function processNode( } } -/** Build hierarchical tree structure from flat file list */ +/** + * Builds the hierarchical tree from a flat list of file URIs, reading YAML front matter for titles + * and applying ordering. Folders are named after their own path segment; `index.md` is a child file. + * + * @param uris The file URIs to arrange (each with an `fsPath`) + * @param directorySort The sort mode for each level; defaults to `'files-first'` + * @param acronyms Acronyms to preserve when normalizing titles; defaults to none + * @returns Promise resolving to the root-level tree nodes + */ export async function buildTree( uris: any[], directorySort: 'files-first' | 'folders-first' | 'alphabetical' = 'files-first', diff --git a/src/tree/treeProvider.ts b/src/tree/treeProvider.ts index eb77299..db79acd 100644 --- a/src/tree/treeProvider.ts +++ b/src/tree/treeProvider.ts @@ -31,6 +31,12 @@ export class WorkspaceWikiTreeProvider { this.onDidChangeTreeData = this._onDidChangeTreeData.event; } + /** + * Returns the tree items to display under an element, or the root tree when no element is given. + * + * @param element The parent tree item, or undefined for the root level + * @returns Promise resolving to the child tree items (rebuilds the tree and node map at the root) + */ async getChildren(element?: any): Promise { if (element && (element as any).treeNode) { // Return children of the specified element @@ -60,6 +66,12 @@ export class WorkspaceWikiTreeProvider { return this.treeData.map((node) => this.createTreeItem(node)); } + /** + * Populates `nodeMap` (absolute path to node) and records each node's parent for lookups and reveal. + * + * @param nodes The nodes to index + * @param parent The parent node to assign to each node, if any + */ private buildNodeMap(nodes: TreeNode[], parent?: TreeNode): void { for (const node of nodes) { // Use consistent absolute fsPath for both files and folders @@ -74,6 +86,12 @@ export class WorkspaceWikiTreeProvider { } } + /** + * Returns the parent tree item for an element, used by VS Code for reveal and sync. + * + * @param element The tree item whose parent is requested + * @returns The parent tree item, or undefined at the root + */ getParent(element: any): any | undefined { if (element && (element as any).treeNode) { const node = (element as any).treeNode as TreeNode; @@ -85,6 +103,13 @@ export class WorkspaceWikiTreeProvider { return undefined; } + /** + * Builds a VS Code TreeItem from a TreeNode, setting collapsible state, tooltip, context value, + * and (for files) the click command derived from `defaultOpenMode` and `openWith`. + * + * @param node The tree node to convert + * @returns The configured TreeItem, with the source node attached as `treeNode` + */ private createTreeItem(node: TreeNode): any { const collapsibleState = node.type === 'folder' && node.children && node.children.length > 0 @@ -150,6 +175,13 @@ export class WorkspaceWikiTreeProvider { return item; } + /** + * Returns the TreeItem for an element: the element itself when it is already a tree item, + * otherwise a new item built from a TreeNode. + * + * @param element A tree item or a TreeNode + * @returns The TreeItem to render + */ getTreeItem(element: any): any { // If element has treeNode, it means it's our custom tree item if (element && (element as any).treeNode) { @@ -164,6 +196,9 @@ export class WorkspaceWikiTreeProvider { return element; } + /** + * Clears the cached node map and fires the change event so VS Code rebuilds the tree. + */ refresh(): void { // Clear node map flag to force rebuild on next access this.nodeMapBuilt = false; @@ -171,6 +206,12 @@ export class WorkspaceWikiTreeProvider { this._onDidChangeTreeData.fire(undefined); } + /** + * Finds the file tree item matching a path, using direct then normalized (cross-platform) comparison. + * + * @param filePath The absolute file system path to locate + * @returns The matching file tree item, or undefined if not found + */ findNodeByPath(filePath: string): any | undefined { // Ensure nodeMap is built before lookup if (!this.nodeMapBuilt && this.treeData.length > 0) { @@ -198,6 +239,7 @@ export class WorkspaceWikiTreeProvider { return undefined; } + /** Disposes the tree-data change event emitter. */ dispose(): void { // Clean up any resources if needed if (this._onDidChangeTreeData && typeof this._onDidChangeTreeData.dispose === 'function') { diff --git a/src/utils/configUtils.ts b/src/utils/configUtils.ts index 8033ec6..b6c7690 100644 --- a/src/utils/configUtils.ts +++ b/src/utils/configUtils.ts @@ -5,6 +5,8 @@ import * as vscode from 'vscode'; /** * Gets workspace wiki configuration + * + * @returns The `workspaceWiki` workspace configuration */ export function getWorkspaceWikiConfig(): vscode.WorkspaceConfiguration { return vscode.workspace.getConfiguration('workspaceWiki'); @@ -12,6 +14,8 @@ export function getWorkspaceWikiConfig(): vscode.WorkspaceConfiguration { /** * Gets supported file extensions from configuration + * + * @returns The configured extensions, or `['md', 'markdown', 'txt']` by default */ export function getSupportedExtensions(): string[] { const config = getWorkspaceWikiConfig(); @@ -20,6 +24,8 @@ export function getSupportedExtensions(): string[] { /** * Gets exclude patterns from configuration + * + * @returns The configured `excludeGlobs`, or a built-in fallback list of common build and output directories */ export function getExcludePatterns(): string[] { const config = getWorkspaceWikiConfig(); @@ -59,6 +65,8 @@ export function getExcludePatterns(): string[] { /** * Gets directory sort setting from configuration + * + * @returns The sort mode, or `'files-first'` by default */ export function getDirectorySort(): 'files-first' | 'folders-first' | 'alphabetical' { const config = getWorkspaceWikiConfig(); @@ -67,6 +75,8 @@ export function getDirectorySort(): 'files-first' | 'folders-first' | 'alphabeti /** * Gets acronym casing settings from configuration + * + * @returns The configured acronyms, or an empty array by default */ export function getAcronymCasing(): string[] { const config = getWorkspaceWikiConfig(); @@ -75,6 +85,8 @@ export function getAcronymCasing(): string[] { /** * Gets auto-reveal settings from configuration + * + * @returns `enabled` (default `true`) and `delay` in milliseconds (default `500`) */ export function getAutoRevealSettings(): { enabled: boolean; delay: number } { const config = getWorkspaceWikiConfig(); @@ -86,6 +98,8 @@ export function getAutoRevealSettings(): { enabled: boolean; delay: number } { /** * Gets open with settings from configuration + * + * @returns A map of extension to command, or defaults for `md`/`markdown` (`markdown.showPreview`) and `txt` (`vscode.open`) */ export function getOpenWithSettings(): Record { const config = getWorkspaceWikiConfig(); @@ -100,6 +114,8 @@ export function getOpenWithSettings(): Record { /** * Gets default open mode from configuration + * + * @returns `'preview'` or `'editor'`, default `'preview'` */ export function getDefaultOpenMode(): 'preview' | 'editor' { const config = getWorkspaceWikiConfig(); @@ -108,6 +124,8 @@ export function getDefaultOpenMode(): 'preview' | 'editor' { /** * Gets max search depth from configuration + * + * @returns The maximum directory depth to scan, or `10` by default */ export function getMaxSearchDepth(): number { const config = getWorkspaceWikiConfig(); @@ -116,6 +134,8 @@ export function getMaxSearchDepth(): number { /** * Gets show hidden files setting from configuration + * + * @returns Whether hidden files and folders (dot-prefixed) are shown; default `false` */ export function getShowHiddenFiles(): boolean { const config = getWorkspaceWikiConfig(); @@ -124,6 +144,8 @@ export function getShowHiddenFiles(): boolean { /** * Gets show ignored files setting from configuration + * + * @returns Whether files matched by `.gitignore` and `excludeGlobs` are shown; default `false` */ export function getShowIgnoredFiles(): boolean { const config = getWorkspaceWikiConfig(); @@ -132,6 +154,9 @@ export function getShowIgnoredFiles(): boolean { /** * Syncs openWith extensions to supportedExtensions + * + * Adds any extension present in `openWith` but missing from `supportedExtensions`, then writes the + * result back to the workspace configuration when a change is needed (side effect; returns nothing). */ export function syncOpenWithToSupportedExtensions(): void { const config = getWorkspaceWikiConfig(); diff --git a/src/utils/fileUtils.ts b/src/utils/fileUtils.ts index fc01f74..60f8d9d 100644 --- a/src/utils/fileUtils.ts +++ b/src/utils/fileUtils.ts @@ -5,6 +5,9 @@ import * as vscode from 'vscode'; /** * Gets the relative path from workspace root + * + * @param uri The file URI + * @returns The path relative to its workspace folder, or the absolute `fsPath` when outside any workspace */ export function getRelativePath(uri: vscode.Uri): string { const workspaceFolder = vscode.workspace.getWorkspaceFolder(uri); @@ -16,6 +19,9 @@ export function getRelativePath(uri: vscode.Uri): string { /** * Checks if a path represents a hidden file or directory + * + * @param path The file path to inspect + * @returns `true` if any segment starts with a dot, is longer than one character, and does not end with a dot */ export function isHiddenPath(path: string): boolean { if (!path || typeof path !== 'string') { @@ -31,6 +37,9 @@ export function isHiddenPath(path: string): boolean { /** * Normalizes path separators to forward slashes + * + * @param path The path to normalize + * @returns The path with backslashes converted to forward slashes, or `''` for invalid input */ export function normalizePath(path: string): string { if (!path || typeof path !== 'string') { @@ -42,6 +51,9 @@ export function normalizePath(path: string): string { /** * Gets the directory name from a file path + * + * @param path The file path + * @returns The parent directory name (the second-to-last segment), or `''` if there is none */ export function getDirectoryName(path: string): string { if (!path || typeof path !== 'string') { @@ -55,6 +67,9 @@ export function getDirectoryName(path: string): string { /** * Gets the file name from a file path + * + * @param path The file path + * @returns The final path segment, or `''` if there is none */ export function getFileName(path: string): string { if (!path || typeof path !== 'string') { @@ -68,6 +83,13 @@ export function getFileName(path: string): string { /** * Checks if a file path matches any of the given glob patterns + * + * Patterns are converted to regular expressions (`**` matches any depth, `*` matches within a segment, + * `?` matches a single non-slash character); an invalid pattern falls back to a substring match. + * + * @param filePath The file path to test + * @param patterns The glob patterns to match against + * @returns `true` if the path matches any pattern */ export function matchesGlobPattern(filePath: string, patterns: string[]): boolean { if (!filePath || !Array.isArray(patterns)) { @@ -123,6 +145,9 @@ export function matchesGlobPattern(filePath: string, patterns: string[]): boolea /** * Calculates the depth of a file path (number of directory levels) + * + * @param path The file path + * @returns The number of non-empty path segments */ export function getPathDepth(path: string): number { if (!path || typeof path !== 'string') { diff --git a/src/utils/textUtils.ts b/src/utils/textUtils.ts index 2af7d0a..0c7f380 100644 --- a/src/utils/textUtils.ts +++ b/src/utils/textUtils.ts @@ -12,7 +12,7 @@ export interface FrontMatterData { * Extracts title and description from YAML front matter in a markdown file * * @param filePath - The path to the markdown file - * @returns Object containing title and description from front matter, or nulls if not found + * @returns Promise resolving to the `title` and `description` from front matter, or nulls when absent, unreadable, or not a `.md`/`.markdown` file */ export async function extractFrontMatter(filePath: string): Promise { if (!filePath || typeof filePath !== 'string') { @@ -68,7 +68,7 @@ export async function extractFrontMatter(filePath: string): Promise { const frontMatter = await extractFrontMatter(filePath); @@ -79,6 +79,10 @@ export async function extractFrontMatterTitle(filePath: string): Promise "Getting Started" * Applies acronym casing from settings for common technical terms + * + * @param fileName The file name to convert (extension is stripped) + * @param acronyms Acronyms to preserve in their given casing (e.g. `['API', 'HTML']`); defaults to none + * @returns The title-cased name (`'README'` for README files), or `''` for invalid input */ export function normalizeTitle(fileName: string, acronyms: string[] = []): string { if (!fileName || typeof fileName !== 'string') { @@ -132,7 +136,12 @@ export function normalizeTitle(fileName: string, acronyms: string[] = []): strin return result; } -/** Extracts file extension from a file name or path */ +/** + * Extracts file extension from a file name or path + * + * @param fileName The file name or path + * @returns The lowercase extension without the dot, or `''` when there is none + */ export function getFileExtension(fileName: string): string { if (!fileName || typeof fileName !== 'string') { return ''; @@ -142,7 +151,12 @@ export function getFileExtension(fileName: string): string { return match ? match[1].toLowerCase() : ''; } -/** Checks if a file name represents an index file */ +/** + * Checks if a file name represents an index file + * + * @param fileName The file name to check + * @returns `true` if the name starts with `index.` (case-insensitive) + */ export function isIndexFile(fileName: string): boolean { if (!fileName || typeof fileName !== 'string') { return false; @@ -151,7 +165,12 @@ export function isIndexFile(fileName: string): boolean { return fileName.toLowerCase().startsWith('index.'); } -/** Checks if a file name represents a README file */ +/** + * Checks if a file name represents a README file + * + * @param fileName The file name to check + * @returns `true` if the name starts with `readme.` (case-insensitive) + */ export function isReadmeFile(fileName: string): boolean { if (!fileName || typeof fileName !== 'string') { return false;