From a90ccdf44e9fc6dffc162b89b6203d49249f9ac2 Mon Sep 17 00:00:00 2001 From: linonetwo Date: Sat, 6 Dec 2025 02:10:54 +0800 Subject: [PATCH] refactor: remove outdated method signature --- src/services/wiki/index.ts | 11 +- src/services/wiki/interface.ts | 11 +- .../FileSystemAdaptor.ts | 83 +--------- .../watchFileSystemAdaptor/changelog.tid | 67 ++++---- .../routingUtilities.ts | 148 ++++++++++++++++++ src/windows/AddWorkspace/useCloneWiki.ts | 2 - src/windows/AddWorkspace/useExistedWiki.ts | 9 +- src/windows/AddWorkspace/useNewWiki.ts | 2 +- 8 files changed, 188 insertions(+), 145 deletions(-) create mode 100644 src/services/wiki/plugin/watchFileSystemAdaptor/routingUtilities.ts diff --git a/src/services/wiki/index.ts b/src/services/wiki/index.ts index 1baca24d..4ab8be64 100644 --- a/src/services/wiki/index.ts +++ b/src/services/wiki/index.ts @@ -482,7 +482,7 @@ export class Wiki implements IWikiService { this.logProgress(i18n.t('AddWorkspace.WikiTemplateCopyCompleted') + newWikiPath); } - public async createSubWiki(parentFolderLocation: string, folderName: string, _subWikiFolderName: string, _mainWikiPath: string, _tagName = '', onlyLink = false): Promise { + public async createSubWiki(parentFolderLocation: string, folderName: string, onlyLink = false): Promise { this.logProgress(i18n.t('AddWorkspace.StartCreatingSubWiki')); const newWikiPath = path.join(parentFolderLocation, folderName); if (!(await pathExists(parentFolderLocation))) { @@ -625,14 +625,7 @@ export class Wiki implements IWikiService { await gitService.clone(gitRepoUrl, path.join(parentFolderLocation, wikiFolderName), gitUserInfo); } - public async cloneSubWiki( - parentFolderLocation: string, - wikiFolderName: string, - _mainWikiPath: string, - gitRepoUrl: string, - gitUserInfo: IGitUserInfos, - _tagName = '', - ): Promise { + public async cloneSubWiki(parentFolderLocation: string, wikiFolderName: string, gitRepoUrl: string, gitUserInfo: IGitUserInfos): Promise { this.logProgress(i18n.t('AddWorkspace.StartCloningSubWiki')); const newWikiPath = path.join(parentFolderLocation, wikiFolderName); if (!(await pathExists(parentFolderLocation))) { diff --git a/src/services/wiki/interface.ts b/src/services/wiki/interface.ts index ba8463a2..a536c498 100644 --- a/src/services/wiki/interface.ts +++ b/src/services/wiki/interface.ts @@ -26,14 +26,7 @@ export interface IWikiService { /** return true if wiki does existed and folder is a valid tiddlywiki folder, return error message (a string) if there is an error checking wiki existence */ checkWikiExist(workspace: IWorkspace, options?: { shouldBeMainWiki?: boolean; showDialog?: boolean }): Promise; checkWikiStartLock(wikiFolderLocation: string): boolean; - cloneSubWiki( - parentFolderLocation: string, - wikiFolderName: string, - mainWikiPath: string, - gitRepoUrl: string, - gitUserInfo: IGitUserInfos, - tagName?: string, - ): Promise; + cloneSubWiki(parentFolderLocation: string, wikiFolderName: string, gitRepoUrl: string, gitUserInfo: IGitUserInfos): Promise; cloneWiki(parentFolderLocation: string, wikiFolderName: string, gitRepoUrl: string, gitUserInfo: IGitUserInfos): Promise; copyWikiTemplate(newFolderPath: string, folderName: string): Promise; /** @@ -43,7 +36,7 @@ export interface IWikiService { * @param mainWikiToLink * @param onlyLink not creating new subwiki folder, just link existed subwiki folder to main wiki folder */ - createSubWiki(parentFolderLocation: string, folderName: string, subWikiFolderName: string, mainWikiPath: string, tagName?: string, onlyLink?: boolean): Promise; + createSubWiki(parentFolderLocation: string, folderName: string, onlyLink?: boolean): Promise; ensureWikiExist(wikiPath: string, shouldBeMainWiki: boolean): Promise; extractWikiHTML(htmlWikiPath: string, saveWikiFolderPath: string): Promise; /** diff --git a/src/services/wiki/plugin/watchFileSystemAdaptor/FileSystemAdaptor.ts b/src/services/wiki/plugin/watchFileSystemAdaptor/FileSystemAdaptor.ts index a9ca3169..7bd7a761 100644 --- a/src/services/wiki/plugin/watchFileSystemAdaptor/FileSystemAdaptor.ts +++ b/src/services/wiki/plugin/watchFileSystemAdaptor/FileSystemAdaptor.ts @@ -7,6 +7,7 @@ import fs from 'fs'; import path from 'path'; import type { IFileInfo } from 'tiddlywiki'; import type { Tiddler, Wiki } from 'tiddlywiki'; +import { isWikiWorkspaceWithRouting, matchTiddlerToWorkspace } from './routingUtilities'; import { isFileLockError } from './utilities'; /** @@ -83,32 +84,10 @@ export class FileSystemAdaptor { const allWorkspaces = await workspace.getWorkspacesAsList(); - // Include both main workspace and sub-wikis for tag-based routing or filter-based routing - const isWikiWorkspaceWithRouting = (workspaceItem: IWorkspace): workspaceItem is IWikiWorkspace => { - if (!('wikiFolderLocation' in workspaceItem) || !workspaceItem.wikiFolderLocation) { - return false; - } - - // Check if workspace has routing config (either tagNames or fileSystemPathFilter) - const hasRoutingConfig = ('tagNames' in workspaceItem && workspaceItem.tagNames.length > 0) || - ('fileSystemPathFilterEnable' in workspaceItem && workspaceItem.fileSystemPathFilterEnable && 'fileSystemPathFilter' in workspaceItem && - workspaceItem.fileSystemPathFilter); - - if (!hasRoutingConfig) { - return false; - } - - // Include if it's the main workspace - const isMain = workspaceItem.id === currentWorkspace.id; - - // Include if it's a sub-wiki of the current main workspace - const isSubWiki = 'isSubWiki' in workspaceItem && - workspaceItem.isSubWiki && - workspaceItem.mainWikiID === currentWorkspace.id; - - return isMain || isSubWiki; - }; - const workspacesWithRouting = allWorkspaces.filter(isWikiWorkspaceWithRouting).sort(workspaceSorter); + // Filter to wiki workspaces with routing config (main or sub-wikis) + const workspacesWithRouting = allWorkspaces + .filter((w: IWorkspace): w is IWikiWorkspace => isWikiWorkspaceWithRouting(w, currentWorkspace.id)) + .sort(workspaceSorter); this.wikisWithRouting = workspacesWithRouting; } catch (error) { @@ -163,7 +142,7 @@ export class FileSystemAdaptor { } // Find matching workspace using the routing logic - const matchingWiki = this.matchTitleToWiki(title, tags); + const matchingWiki = matchTiddlerToWorkspace(title, tags, this.wikisWithRouting, $tw.wiki, $tw.rootWidget); // Determine the target directory based on routing // Sub-wikis store tiddlers directly in their root folder (not in /tiddlers subfolder) @@ -209,56 +188,6 @@ export class FileSystemAdaptor { } } - /** - * Match a tiddler to a workspace based on routing rules. - * Checks workspaces in order (priority) and returns the first match. - * - * For each workspace, checks in order (any match wins): - * 1. Direct tag match (including if tiddler's title IS one of the tagNames - it's a "tag tiddler") - * 2. If includeTagTree is enabled, use in-tagtree-of filter for recursive tag matching - * 3. If fileSystemPathFilterEnable is enabled, use custom filter expressions (one per line, any match wins) - */ - protected matchTitleToWiki(title: string, tags: string[]): IWikiWorkspace | undefined { - for (const wiki of this.wikisWithRouting) { - // Direct tag match - check if any of the tiddler's tags match any of the wiki's tagNames - // Also check if the tiddler's title IS one of the tagNames (it's a "tag tiddler" that defines that tag) - if (wiki.tagNames.length > 0) { - const hasMatchingTag = wiki.tagNames.some(tagName => tags.includes(tagName)); - const isTitleATagName = wiki.tagNames.includes(title); - if (hasMatchingTag || isTitleATagName) { - return wiki; - } - } - - // Tag tree match if enabled - check all tagNames - if (wiki.includeTagTree && wiki.tagNames.length > 0) { - for (const tagName of wiki.tagNames) { - const result = $tw.wiki.filterTiddlers( - `[in-tagtree-of:inclusive]`, - $tw.rootWidget.makeFakeWidgetWithVariables({ tagName }), - $tw.wiki.makeTiddlerIterator([title]), - ); - if (result.length > 0) { - return wiki; - } - } - } - - // Custom filter match if enabled - if (wiki.fileSystemPathFilterEnable && wiki.fileSystemPathFilter) { - // Split by newlines and try each filter - const filters = wiki.fileSystemPathFilter.split('\n').map(f => f.trim()).filter(f => f.length > 0); - for (const filter of filters) { - const result = $tw.wiki.filterTiddlers(filter, undefined, $tw.wiki.makeTiddlerIterator([title])); - if (result.length > 0) { - return wiki; - } - } - } - } - return undefined; - } - /** * Generate file info for sub-wiki directory * Handles symlinks correctly across platforms (Windows junctions and Linux symlinks) diff --git a/src/services/wiki/plugin/watchFileSystemAdaptor/changelog.tid b/src/services/wiki/plugin/watchFileSystemAdaptor/changelog.tid index 5f1f065d..75c4e880 100644 --- a/src/services/wiki/plugin/watchFileSystemAdaptor/changelog.tid +++ b/src/services/wiki/plugin/watchFileSystemAdaptor/changelog.tid @@ -12,20 +12,19 @@ Version: TiddlyWiki v5.3.x (as of 2025-10-24) !!! Key Modifications -!!!! 1. Dynamic Workspace Information via IPC +!!!! 1. Dynamic Workspace Information via Worker Services * ''Original'': Uses static `$:/config/FileSystemPaths` tiddler for routing -* ''Modified'': Queries workspace information from main process via worker threads IPC +* ''Modified'': Queries workspace information from main process via worker thread services * ''Reason'': Eliminates need for complex string manipulation of `FileSystemPaths` configuration ```typescript -// Added: Worker service caller integration -import { callMainProcessService } from '@services/wiki/wikiWorker/workerServiceCaller'; -import type { IWorkspace } from '@services/workspaces/interface'; +// Added: Worker service integration +import { workspace } from '@services/wiki/wikiWorker/services'; -// Added: Methods to query workspace dynamically -private async getCurrentWorkspace(): Promise -private async getSubWikis(currentWorkspace: IWorkspace): Promise +// Queries workspace data dynamically +const currentWorkspace = await workspace.get(this.workspaceID); +const allWorkspaces = await workspace.getWorkspacesAsList(); ``` !!!! 2. Tag-Based and Filter-Based Sub-Wiki Routing @@ -40,52 +39,42 @@ private async getSubWikis(currentWorkspace: IWorkspace): Promise * ''Important'': Always recalculates path on save to handle tag changes - old `fileInfo` only used for cleanup * ''Implementation'': ** Checks tiddler tags/filters against sub-workspace routing rules (in priority order) -** Routes matching tiddlers to sub-wiki's `tiddlers` folder +** Routes matching tiddlers directly to sub-wiki's root folder (not `/tiddlers` subfolder) ** Falls back to TiddlyWiki's `$:/config/FileSystemPaths` logic for non-matching tiddlers ** Loads sub-wikis cache on initialization ** Currently loads sub-wikis once, future enhancements can watch for workspace changes ```typescript -// Modified: getTiddlerFileInfo is now async (safe since callers only use callback) -async getTiddlerFileInfo(tiddler: Tiddler, callback: IFileSystemAdaptorCallback): Promise { - // Direct async/await instead of nested void IIFE - const currentWorkspace = await this.getCurrentWorkspace(); - const subWikis = this.getSubWikis(); // Uses cache instead of IPC - const matchingSubWiki = subWikis.find(...); - - if (matchingSubWiki) { - this.routeToSubWorkspace(...); - } else { - this.useDefaultFileSystemLogic(...); - } -} +// Uses pure functions from routingUtils.ts for matching logic +import { matchTiddlerToWorkspace, isWikiWorkspaceWithRouting } from './routingUtils'; -// Added: Caching mechanism -private subWikis: IWorkspace[] = []; +// Match tiddler to workspace +const matchingWiki = matchTiddlerToWorkspace(title, tags, this.wikisWithRouting, $tw.wiki, $tw.rootWidget); -private async initializeSubWikisCache(): Promise { - await this.updateSubWikisCache(); -} - -private async updateSubWikisCache(): Promise { - // Load sub-wikis once and cache them - const allWorkspaces = await callMainProcessService(...); - this.subWikis = allWorkspaces.filter(...); +// Generate file info based on routing result +if (matchingWiki) { + return this.generateSubWikiFileInfo(tiddler, matchingWiki); +} else { + return this.generateDefaultFileInfo(tiddler); } ``` -!!!! 3. Separated Routing Logic +!!!! 3. Separated Routing Logic into Pure Functions -* ''Added'': `routeToSubWorkspace()` method for sub-wiki routing -* ''Added'': `useDefaultFileSystemLogic()` method for standard routing -* ''Reason'': Better code organization and maintainability +* ''Added'': `routingUtils.ts` with pure functions for routing logic: +** `isWikiWorkspaceWithRouting()` - checks if workspace has routing config +** `matchTiddlerToWorkspace()` - matches tiddler to workspace based on routing rules +** `matchesDirectTag()` - checks direct tag match +** `matchesTagTree()` - checks tag tree match using in-tagtree-of filter +** `matchesCustomFilter()` - checks custom filter match +* ''Reason'': Better code organization, testability, and maintainability !!! Future Compatibility Notes When updating from upstream TiddlyWiki filesystem adaptor: # Review changes to core methods: `saveTiddler`, `deleteTiddler`, `getTiddlerInfo` -# Preserve our IPC-based workspace querying logic +# Preserve our worker-service-based workspace querying logic # Preserve tag-based routing in `getTiddlerFileInfo` # Update type definitions if TiddlyWiki's FileInfo interface changes # Test sub-wiki routing functionality after merge @@ -96,7 +85,7 @@ When validating this adaptor: * [ ] Tiddlers with matching tags route to correct sub-wiki * [ ] Tiddlers without matching tags use default FileSystemPaths -* [ ] IPC communication works correctly in worker thread +* [ ] Worker service communication works correctly * [ ] Error handling falls back gracefully * [ ] File operations (save/delete) work in both main and sub-wikis -* [ ] Workspace ID caching reduces IPC overhead +* [ ] Workspace caching reduces service call overhead diff --git a/src/services/wiki/plugin/watchFileSystemAdaptor/routingUtilities.ts b/src/services/wiki/plugin/watchFileSystemAdaptor/routingUtilities.ts new file mode 100644 index 00000000..2fe4868e --- /dev/null +++ b/src/services/wiki/plugin/watchFileSystemAdaptor/routingUtilities.ts @@ -0,0 +1,148 @@ +import type { IWikiWorkspace, IWorkspace } from '@services/workspaces/interface'; + +/** + * Check if a workspace has routing configuration (tagNames or fileSystemPathFilter). + */ +export function hasRoutingConfig(workspaceItem: IWorkspace): boolean { + const hasTagNames = 'tagNames' in workspaceItem && Array.isArray(workspaceItem.tagNames) && workspaceItem.tagNames.length > 0; + const hasFilter = 'fileSystemPathFilterEnable' in workspaceItem && + workspaceItem.fileSystemPathFilterEnable && + 'fileSystemPathFilter' in workspaceItem && + Boolean(workspaceItem.fileSystemPathFilter); + return hasTagNames || hasFilter; +} + +/** + * Check if a workspace is a wiki workspace with routing configuration. + * This filters to wiki workspaces that are either the main workspace or sub-wikis of it. + */ +export function isWikiWorkspaceWithRouting( + workspaceItem: IWorkspace, + mainWorkspaceId: string, +): workspaceItem is IWikiWorkspace { + // Must have wiki folder location + if (!('wikiFolderLocation' in workspaceItem) || !workspaceItem.wikiFolderLocation) { + return false; + } + + // Must have routing config + if (!hasRoutingConfig(workspaceItem)) { + return false; + } + + // Include if it's the main workspace + const isMain = workspaceItem.id === mainWorkspaceId; + + // Include if it's a sub-wiki of the current main workspace + const isSubWiki = 'isSubWiki' in workspaceItem && + workspaceItem.isSubWiki && + 'mainWikiID' in workspaceItem && + workspaceItem.mainWikiID === mainWorkspaceId; + + return isMain || isSubWiki; +} + +/** + * Check if a tiddler matches a workspace's direct tag routing. + * Returns true if: + * - Any of the tiddler's tags match any of the workspace's tagNames + * - The tiddler's title IS one of the tagNames (it's a "tag tiddler") + */ +export function matchesDirectTag( + tiddlerTitle: string, + tiddlerTags: string[], + workspaceTagNames: string[], +): boolean { + if (workspaceTagNames.length === 0) { + return false; + } + + const hasMatchingTag = workspaceTagNames.some(tagName => tiddlerTags.includes(tagName)); + const isTitleATagName = workspaceTagNames.includes(tiddlerTitle); + + return hasMatchingTag || isTitleATagName; +} + +/** + * Check if a tiddler matches a workspace's tag tree routing. + * Uses TiddlyWiki's in-tagtree-of filter for recursive tag hierarchy matching. + */ +export function matchesTagTree( + tiddlerTitle: string, + workspaceTagNames: string[], + wiki: typeof $tw.wiki, + rootWidget: typeof $tw.rootWidget, +): boolean { + for (const tagName of workspaceTagNames) { + const result = wiki.filterTiddlers( + `[in-tagtree-of:inclusive]`, + rootWidget.makeFakeWidgetWithVariables({ tagName }), + wiki.makeTiddlerIterator([tiddlerTitle]), + ); + if (result.length > 0) { + return true; + } + } + return false; +} + +/** + * Check if a tiddler matches a workspace's custom filter routing. + * Filters are separated by newlines; any match wins. + */ +export function matchesCustomFilter( + tiddlerTitle: string, + filterExpression: string, + wiki: typeof $tw.wiki, +): boolean { + const filters = filterExpression.split('\n').map(f => f.trim()).filter(f => f.length > 0); + + for (const filter of filters) { + const result = wiki.filterTiddlers(filter, undefined, wiki.makeTiddlerIterator([tiddlerTitle])); + if (result.length > 0) { + return true; + } + } + + return false; +} + +/** + * Match a tiddler to a workspace based on routing rules. + * Checks workspaces in order (priority) and returns the first match. + * + * For each workspace, checks in order (any match wins): + * 1. Direct tag match (including if tiddler's title IS one of the tagNames) + * 2. If includeTagTree is enabled, use in-tagtree-of filter for recursive tag matching + * 3. If fileSystemPathFilterEnable is enabled, use custom filter expressions + */ +export function matchTiddlerToWorkspace( + tiddlerTitle: string, + tiddlerTags: string[], + workspacesWithRouting: IWikiWorkspace[], + wiki: typeof $tw.wiki, + rootWidget: typeof $tw.rootWidget, +): IWikiWorkspace | undefined { + for (const workspace of workspacesWithRouting) { + // 1. Direct tag match + if (matchesDirectTag(tiddlerTitle, tiddlerTags, workspace.tagNames)) { + return workspace; + } + + // 2. Tag tree match (if enabled) + if (workspace.includeTagTree && workspace.tagNames.length > 0) { + if (matchesTagTree(tiddlerTitle, workspace.tagNames, wiki, rootWidget)) { + return workspace; + } + } + + // 3. Custom filter match (if enabled) + if (workspace.fileSystemPathFilterEnable && workspace.fileSystemPathFilter) { + if (matchesCustomFilter(tiddlerTitle, workspace.fileSystemPathFilter, wiki)) { + return workspace; + } + } + } + + return undefined; +} diff --git a/src/windows/AddWorkspace/useCloneWiki.ts b/src/windows/AddWorkspace/useCloneWiki.ts index 78309bde..e382954e 100644 --- a/src/windows/AddWorkspace/useCloneWiki.ts +++ b/src/windows/AddWorkspace/useCloneWiki.ts @@ -73,10 +73,8 @@ export function useCloneWiki( await window.service.wiki.cloneSubWiki( form.parentFolderLocation, form.wikiFolderName, - form.mainWikiToLink.wikiFolderLocation, form.gitRepoUrl, form.gitUserInfo!, - form.tagNames[0] ?? '', ); } await callWikiInitialization(newWorkspaceConfig, wikiCreationMessageSetter, t, form.gitUserInfo, { from: WikiCreationMethod.Clone }); diff --git a/src/windows/AddWorkspace/useExistedWiki.ts b/src/windows/AddWorkspace/useExistedWiki.ts index a4d0bc8e..15dd2c54 100644 --- a/src/windows/AddWorkspace/useExistedWiki.ts +++ b/src/windows/AddWorkspace/useExistedWiki.ts @@ -81,14 +81,7 @@ export function useExistedWiki( ); } await window.service.wiki.ensureWikiExist(form.wikiFolderLocation, false); - await window.service.wiki.createSubWiki( - parentFolderLocationForExistedFolder, - wikiFolderNameForExistedFolder, - 'subwiki', - form.mainWikiToLink.wikiFolderLocation, - form.tagNames[0] ?? '', - true, - ); + await window.service.wiki.createSubWiki(parentFolderLocationForExistedFolder, wikiFolderNameForExistedFolder, true); } await callWikiInitialization(newWorkspaceConfig, wikiCreationMessageSetter, t, form.gitUserInfo, { from: WikiCreationMethod.LoadExisting }); } catch (error) { diff --git a/src/windows/AddWorkspace/useNewWiki.ts b/src/windows/AddWorkspace/useNewWiki.ts index 5724f4da..7ef21188 100644 --- a/src/windows/AddWorkspace/useNewWiki.ts +++ b/src/windows/AddWorkspace/useNewWiki.ts @@ -80,7 +80,7 @@ export function useNewWiki( await window.service.wiki.copyWikiTemplate(form.parentFolderLocation, form.wikiFolderName); } } else { - await window.service.wiki.createSubWiki(form.parentFolderLocation, form.wikiFolderName, 'subwiki', form.mainWikiToLink.wikiFolderLocation, form.tagNames[0] ?? ''); + await window.service.wiki.createSubWiki(form.parentFolderLocation, form.wikiFolderName); } await callWikiInitialization(newWorkspaceConfig, wikiCreationMessageSetter, t, form.gitUserInfo, { notClose: options?.notClose, from: WikiCreationMethod.Create }); } catch (error) {