Skip to main content

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

TFile
object
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

TFolder
object
Represents a folder in the workspace.
interface TFolder {
  path: string;
  name: string;
  children: TAbstractFile[];
}

TAbstractFile

TAbstractFile
TFile | TFolder
Union type for files and folders.

Query Methods

getRoot()

getRoot
() => string | null
Get the workspace root path.
const root = this.app.workspace.getRoot();
if (root) {
  console.log('Workspace root:', root);
}

getAllFiles()

getAllFiles
() => Promise<TFile[]>
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()

getMarkdownFiles
() => Promise<TFile[]>
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()

getRecentFiles
() => string[]
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()

activeEditor
() => IEditor | null
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));
  }
}