Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add functionality to exclude directories from graph and update tsconf… #29

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,6 @@ data.json
/package-lock.json
/.tgitconfig
/src/version_info.txt

# exclude compiled .js in tests
/tests/pathExclusion.js
28 changes: 27 additions & 1 deletion src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@ interface Settings {
dark: number;
light: number;
}
excludedDirectories: string[],
openInCurrentTab: boolean;
enableTagsReaction: boolean;
enableAutoFocus: boolean;
Expand Down Expand Up @@ -198,7 +199,8 @@ const DEFAULT_SETTINGS: Settings = {
structuredClone(DEFAULT_DISPLAY_SETTINGS_LIGHT),
structuredClone(DEFAULT_DISPLAY_SETTINGS_LIGHT),
structuredClone(DEFAULT_DISPLAY_SETTINGS_LIGHT),
]
],
excludedDirectories: []
}
// plugin 主体
export default class TagsRoutes extends Plugin {
Expand Down Expand Up @@ -365,6 +367,9 @@ export default class TagsRoutes extends Plugin {
DebugMsg(DebugLevel.INFO,`New installation: Using default settings.`)
}
}
if (!this.settings.excludedDirectories) {
this.settings.excludedDirectories = [];
}
this.settings.customSlot = this.settings[this.settings.currentTheme];
this.settings.currentSlotNum = this.settings.themeSlotNum[this.settings.currentTheme];
this.settings.customSlot[0] = structuredClone(
Expand Down Expand Up @@ -704,5 +709,26 @@ class TagsroutesSettingsTab extends PluginSettingTab {
this.colors.push(new colorPickerGroup(this.plugin, colorSettingsGroup, "Normal", "linkParticleColor"));
this.colors.push(new colorPickerGroup(this.plugin, colorSettingsGroup, "Highlight", "linkParticleHighlightColor"));
this.plugin.skipSave = false;

containerEl.createEl('h2', { text: 'Directory Exclusions' });

new Setting(containerEl)
.setName('Excluded Directories')
.setDesc('Enter directory paths to exclude from the graph, separated by commas (e.g., "Private Notes, Archive")')
.addText(text => text
.setPlaceholder('directory1, directory2')
.setValue(this.plugin.settings.excludedDirectories.join(', '))
.onChange(async (value) => {
this.plugin.settings.excludedDirectories = value
.split(',')
.map(dir => dir.trim())
.filter(dir => dir.length > 0);
await this.plugin.saveSettings();

// Refresh the graph if it's open
if (this.plugin.view) {
this.plugin.view.onResetGraph();
}
}));
}
}
19 changes: 19 additions & 0 deletions src/util/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,25 @@ export function createFolderIfNotExists(folderPath: string) {
// DebugMsg(DebugLevel.DEBUG,`Folder already exists: ${folderPath}`);
}
}

// Allow for users to exclude directories within Vault from graph indexing
export function isPathExcluded(path: string, excludedDirs: string[]): boolean {
return excludedDirs.some(dir => {
// Normalize paths to handle different path separators | Remove trailing slashes
const normalizedDir = dir.replace(/\\/g, '/').toLowerCase().replace(/\/+$/, '');
const normalizedPath = path.replace(/\\/g, '/').toLowerCase();

// Handle wildcard matching (e.g., MyNotes/*)
if (normalizedDir.endsWith('/*')) {
// Remove /* to get the base directory
const baseDir = normalizedDir.slice(0, -2);
return normalizedPath === baseDir || normalizedPath.startsWith(baseDir + '/');
}

// Check for exact matches | exclude subdirectories of excluded directory
return normalizedPath === normalizedDir || normalizedPath.startsWith(normalizedDir + '/');
});
};
// 函数:获取所有标签
export const getTags = (cache: CachedMetadata | null): TagCache[] => {
if (!cache || !cache.tags) return [];
Expand Down
31 changes: 25 additions & 6 deletions src/views/TagsRoutes.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { moment, MarkdownView, Notice, CachedMetadata, ValueComponent, Platform, View } from 'obsidian';
import { ItemView, WorkspaceLeaf, TFile } from "obsidian";
import * as THREE from 'three';
import { getFileType, getTags, parseTagHierarchy, filterStrings, shouldRemove, setViewType, showFile, DebugMsg, DebugLevel, createFolderIfNotExists } from "../util/util"
import { getFileType, getTags, parseTagHierarchy, filterStrings, shouldRemove, setViewType, showFile, DebugMsg, DebugLevel, createFolderIfNotExists, isPathExcluded } from "../util/util"
import ForceGraph3D, { ForceGraph3DInstance } from "3d-force-graph";
import { UnrealBloomPass } from 'three/examples/jsm/postprocessing/UnrealBloomPass.js';
import * as d3 from 'd3-force-3d';
Expand Down Expand Up @@ -1634,17 +1634,37 @@ export class TagRoutesView extends ItemView {
const tagCount: Map<string, number> = new Map(); // 初始化标签计数对象
const frontmatterTagCount: Map<string, number> = new Map(); // 初始化标签计数对象
// 添加resolved links来创建文件间的关系,和文件节点
// Set to track unique node ids
const nodeSet: Set<string> = new Set();
const nodeList: Array<{ id: string, type: string }> = [];
const linkList: Array<{ source: string, target: string, sourceId: string, targetId: string }> = [];

// Iterate over each source path in resolvedLink object
for (const sourcePath in resolvedLinks) {
if (!nodes.some(node => node.id == sourcePath)) {
// check source paths against excludedDirectories
if (isPathExcluded(sourcePath, this.plugin.settings.excludedDirectories)) {
// -> skip iteration if found
continue;
}
// Add sourcePath to nodes if not present
if (!nodeSet.has(sourcePath)) {
nodes.push({ id: sourcePath, type: getFileType(sourcePath) });
nodeSet.add(sourcePath);
}
const targetPaths = resolvedLinks[sourcePath];
// Iterate over each target path to process from sourcePath
for (const targetPath in targetPaths) {
// 确保目标文件也在图中
if (!nodes.some(node => node.id == targetPath)) {
// Check against already indexed resolvedLinks
if (isPathExcluded(targetPath, this.plugin.settings.excludedDirectories)) {
continue;
}
// Add targetPath to nodes if not present
if (!nodeSet.has(targetPath)) {
nodes.push({ id: targetPath, type: getFileType(targetPath) });
nodeSet.add(targetPath);
}
// 创建链接

// link sourcePath and targetPath
links.push({ source: sourcePath, target: targetPath, sourceId: sourcePath, targetId: targetPath });
}
}
Expand Down Expand Up @@ -2015,7 +2035,6 @@ export class TagRoutesView extends ItemView {
.add({
arg: (new settingGroup(this.plugin, "button-box", "button-box", "normal-box")
.addButton("Apply Theme Color", "graph-button", () => { this.applyThemeColor() })
)
})
.add({
arg: (new settingGroup(this.plugin, "button-box", "button-box", "flex-box")
Expand Down
36 changes: 36 additions & 0 deletions tests/pathExclusion.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
export function isPathExcluded(path: string, excludedDirs: string[]): boolean {
return excludedDirs.some(dir => {
const normalizedDir = dir.replace(/\\/g, '/').toLowerCase().replace(/\/+$/, '');
const normalizedPath = path.replace(/\\/g, '/').toLowerCase();

// Handle wildcard matching (e.g., MyNotes/*)
if (normalizedDir.endsWith('/*')) {
const baseDir = normalizedDir.slice(0, -2);
return normalizedPath === baseDir || normalizedPath.startsWith(baseDir + '/');
}

// Check for exact matches or if the path is within the excluded directory
return normalizedPath === normalizedDir || normalizedPath.startsWith(normalizedDir + '/');
});
}


const testCases = [
{ path: 'MyNotes/Section1/Notes', excludedDirs: ['MyNotes/*'], expected: true },
{ path: 'Logs', excludedDirs: ['Logs'], expected: true },
{ path: 'Logs/Archive', excludedDirs: ['Logs'], expected: true },
{ path: 'NotesArchive', excludedDirs: ['Notes'], expected: false },
{ path: 'Private/Notes', excludedDirs: ['Private'], expected: true },
{ path: 'Projects/Important', excludedDirs: ['Projects/*'], expected: true },
{ path: 'Projects', excludedDirs: ['Projects/*'], expected: true },
{ path: 'Reports/2024', excludedDirs: ['Reports/*'], expected: true },
{ path: 'Reports', excludedDirs: ['Archives'], expected: false }
];

testCases.forEach(({ path, excludedDirs, expected }, index) => {
const result = isPathExcluded(path, excludedDirs);
console.log(`Test ${index + 1}: Path "${path}" | ExcludedDirs: [${excludedDirs.join(', ')}] | Expected: ${expected} | Result: ${result}`);
console.assert(result === expected, `Test ${index + 1} failed`);
});

console.log('All tests completed.');
10 changes: 4 additions & 6 deletions tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,19 @@
"inlineSourceMap": true,
"inlineSources": true,
"module": "ESNext",
"target": "ES6",
"target": "ES2015", // Ensures support for modern string methods
"allowJs": true,
"noImplicitAny": true,
"moduleResolution": "node",
"importHelpers": true,
"isolatedModules": true,
"strictNullChecks": true,
"strictNullChecks": true,
"lib": [
"DOM",
"ES5",
"ES6",
"ES7"
"ES2015" // Includes ES6 and above features
]
},
"include": [
"**/*.ts"
]
}
}