Overview
The Workspace interface provides methods for file operations and emits events when files change. Access it via this.app.workspace.
const files = await this.app.workspace.getAllFiles();
const content = await this.app.workspace.read(file);
Interface Definition
interface Workspace extends Events {
getRecentFiles(): string[];
addRecentFile(filePath: string): void;
getRoot(): string | null;
getAllFiles(): Promise<TFile[]>;
getMarkdownFiles(): Promise<TFile[]>;
activeEditor(): IEditor | null;
getAbstractFileByPath(path: string): TAbstractFile | null;
read(file: TFile): Promise<string>;
modify(file: TFile, content: string): Promise<void>;
create(path: string, content?: string): Promise<TFile>;
delete(file: TAbstractFile): Promise<void>;
rename(file: TAbstractFile, newPath: string): Promise<void>;
onFileCreate(callback: (file: TFile) => void): EventRef;
onFileModify(callback: (file: TFile) => void): EventRef;
onFileDelete(callback: (file: TAbstractFile) => void): EventRef;
onFileRename(callback: (file: TFile, oldPath: string) => void): EventRef;
}
File Types
TFile
Represents a file in the workspace.
interface TFile {
path: string; // Full path: "folder/note.md"
name: string; // File name: "note.md"
basename: string; // Name without extension: "note"
extension: string; // File extension: "md"
stat: {
size: number; // File size in bytes
mtime: number; // Modified time (timestamp)
ctime: number; // Created time (timestamp)
};
}
TFolder
Represents a folder in the workspace.
interface TFolder {
path: string;
name: string;
children: TAbstractFile[];
}
TAbstractFile
Union type for files and folders.
Query Methods
getRoot()
Get the workspace root path.
const root = this.app.workspace.getRoot();
if (root) {
console.log('Workspace root:', root);
}
getAllFiles()
Get all files in the workspace.
const allFiles = await this.app.workspace.getAllFiles();
console.log(`Total files: ${allFiles.length}`);
for (const file of allFiles) {
console.log(file.path);
}
getMarkdownFiles()
Get only markdown files (.md extension).
const mdFiles = await this.app.workspace.getMarkdownFiles();
console.log(`Markdown files: ${mdFiles.length}`);
getAbstractFileByPath()
getAbstractFileByPath
(path: string) => TAbstractFile | null
Get a file or folder by its path.
const file = this.app.workspace.getAbstractFileByPath('notes/todo.md');
if (file) {
console.log('Found:', file.name);
// Check type
if ('extension' in file) {
console.log('It\'s a file');
} else if ('children' in file) {
console.log('It\'s a folder');
}
}
getRecentFiles()
Get list of recently opened file paths.
const recent = this.app.workspace.getRecentFiles();
console.log('Recent files:', recent);
// Get TFile objects
const recentFiles = recent
.map(path => this.app.workspace.getAbstractFileByPath(path))
.filter(f => f !== null) as TFile[];
addRecentFile()
addRecentFile
(filePath: string) => void
Add a file to the recent files list.
this.app.workspace.addRecentFile('notes/new-note.md');
activeEditor()
Get the active editor instance.
const editor = this.app.workspace.activeEditor();
if (editor) {
const content = editor.getValue();
console.log('Current content length:', content.length);
}
File Operations
read()
read
(file: TFile) => Promise<string>
Read a file’s contents.
const file = this.app.workspace.getAbstractFileByPath('note.md') as TFile;
if (file) {
const content = await this.app.workspace.read(file);
console.log('File content:', content);
}
modify()
modify
(file: TFile, content: string) => Promise<void>
Modify a file’s contents.
const file = this.app.workspace.getAbstractFileByPath('note.md') as TFile;
if (file) {
const content = await this.app.workspace.read(file);
const updated = content + '\n\n## New Section';
await this.app.workspace.modify(file, updated);
this.showNotice('File updated');
}
Modifying a file triggers the onFileModify event and updates the metadata cache.
create()
create
(path: string, content?: string) => Promise<TFile>
Create a new file.
// Create empty file
const newFile = await this.app.workspace.create('notes/new.md');
// Create with content
const fileWithContent = await this.app.workspace.create(
'notes/todo.md',
'# Todo List\n\n- [ ] Task 1\n- [ ] Task 2'
);
console.log('Created:', fileWithContent.path);
delete()
delete
(file: TAbstractFile) => Promise<void>
Delete a file or folder.
const file = this.app.workspace.getAbstractFileByPath('old-note.md');
if (file) {
await this.app.workspace.delete(file);
this.showNotice('File deleted');
}
Deleting a folder recursively deletes all its contents.
rename()
rename
(file: TAbstractFile, newPath: string) => Promise<void>
Rename or move a file.
const file = this.app.workspace.getAbstractFileByPath('note.md');
if (file) {
// Rename in same directory
await this.app.workspace.rename(file, 'renamed.md');
// Move to different directory
await this.app.workspace.rename(file, 'archive/note.md');
}
Events
Listen to file system events using the workspace event emitters.
onFileCreate()
onFileCreate
(callback: (file: TFile) => void) => EventRef
Called when a file is created.
this.registerEvent(
this.app.workspace.onFileCreate((file) => {
console.log('File created:', file.path);
this.showNotice(`Created: ${file.basename}`);
})
);
onFileModify()
onFileModify
(callback: (file: TFile) => void) => EventRef
Called when a file is modified.
this.registerEvent(
this.app.workspace.onFileModify((file) => {
console.log('File modified:', file.path);
// Invalidate cache or update UI
this.updateFileStats(file);
})
);
onFileDelete()
onFileDelete
(callback: (file: TAbstractFile) => void) => EventRef
Called when a file or folder is deleted.
this.registerEvent(
this.app.workspace.onFileDelete((file) => {
console.log('Deleted:', file.path);
// Clean up any references
this.removeFromCache(file.path);
})
);
onFileRename()
onFileRename
(callback: (file: TFile, oldPath: string) => void) => EventRef
Called when a file is renamed or moved.
this.registerEvent(
this.app.workspace.onFileRename((file, oldPath) => {
console.log(`Renamed: ${oldPath} -> ${file.path}`);
// Update any stored paths
this.updateReferences(oldPath, file.path);
})
);
Common Patterns
Process All Markdown Files
async processAllMarkdownFiles() {
const files = await this.app.workspace.getMarkdownFiles();
for (const file of files) {
const content = await this.app.workspace.read(file);
// Process content
const processed = this.processContent(content);
// Save if changed
if (processed !== content) {
await this.app.workspace.modify(file, processed);
}
}
this.showNotice(`Processed ${files.length} files`);
}
Watch for File Changes
async onload() {
// Track file modifications
const modifiedFiles = new Set<string>();
this.registerEvent(
this.app.workspace.onFileModify((file) => {
modifiedFiles.add(file.path);
})
);
// Process modified files periodically
this.registerInterval(
window.setInterval(() => {
if (modifiedFiles.size > 0) {
console.log('Modified files:', Array.from(modifiedFiles));
modifiedFiles.clear();
}
}, 5000)
);
}
Create File with Frontmatter
async createNoteWithFrontmatter(title: string, tags: string[]) {
const frontmatter = [
'---',
`title: ${title}`,
`created: ${new Date().toISOString()}`,
`tags: [${tags.join(', ')}]`,
'---',
'',
`# ${title}`,
'',
].join('\n');
const fileName = `${title.toLowerCase().replace(/\s+/g, '-')}.md`;
const file = await this.app.workspace.create(
`notes/${fileName}`,
frontmatter
);
return file;
}
Backup File Before Modifying
async safeModify(file: TFile, newContent: string) {
// Read original content
const originalContent = await this.app.workspace.read(file);
// Create backup
const backupPath = file.path.replace('.md', '.backup.md');
await this.app.workspace.create(backupPath, originalContent);
try {
// Modify file
await this.app.workspace.modify(file, newContent);
this.showNotice('File modified successfully');
// Delete backup on success
const backup = this.app.workspace.getAbstractFileByPath(backupPath);
if (backup) {
await this.app.workspace.delete(backup);
}
} catch (error) {
this.showNotice('Failed to modify file');
console.error(error);
}
}
Find Files by Pattern
async findFilesByPattern(pattern: RegExp): Promise<TFile[]> {
const allFiles = await this.app.workspace.getAllFiles();
return allFiles.filter(file => pattern.test(file.path));
}
// Usage
const todoFiles = await this.findFilesByPattern(/todo/i);
const dailyNotes = await this.findFilesByPattern(/\d{4}-\d{2}-\d{2}\.md$/);
Best Practices
Always Check for Null
// ✅ Good
const file = this.app.workspace.getAbstractFileByPath('note.md');
if (file && 'extension' in file) {
const content = await this.app.workspace.read(file);
}
// ❌ Bad - may throw if file doesn't exist
const file = this.app.workspace.getAbstractFileByPath('note.md')!;
const content = await this.app.workspace.read(file);
Use Type Guards
function isFile(file: TAbstractFile): file is TFile {
return 'extension' in file;
}
function isFolder(file: TAbstractFile): file is TFolder {
return 'children' in file;
}
// Usage
const item = this.app.workspace.getAbstractFileByPath('path');
if (item && isFile(item)) {
const content = await this.app.workspace.read(item);
}
Handle Errors Gracefully
try {
await this.app.workspace.modify(file, newContent);
} catch (error) {
console.error('Failed to modify file:', error);
this.showNotice('Failed to save changes');
// Maybe restore from backup?
}
Batch Operations
// Process files in batches to avoid blocking
async processManyFiles(files: TFile[]) {
const batchSize = 10;
for (let i = 0; i < files.length; i += batchSize) {
const batch = files.slice(i, i + batchSize);
await Promise.all(
batch.map(file => this.processFile(file))
);
// Give UI time to update
await new Promise(resolve => setTimeout(resolve, 100));
}
}