Skip to main content
The FileWatcher class monitors the workspace for changes to .NET source files and project files, automatically triggering updates when changes are detected.

Class Overview

Location: /workspace/source/src/fileWatcher.ts:20
export class FileWatcher implements vscode.Disposable {
    private watchers: vscode.FileSystemWatcher[];
    private projectManager: DotNetProjectManager;
    private debounceTimer: NodeJS.Timeout | undefined;

    constructor(projectManager: DotNetProjectManager);
    public dispose(): void;
}

Constructor

new FileWatcher(projectManager)

Creates a new file watcher instance and sets up file system monitoring.
constructor(projectManager: DotNetProjectManager)
projectManager
DotNetProjectManager
required
Instance of DotNetProjectManager to trigger updates
Initialization:
  • Reads watch and exclude patterns from configuration
  • Creates file system watchers for source files
  • Creates watcher for project and solution files
  • Registers event handlers for file changes
Example:
import { FileWatcher } from './fileWatcher';
import { DotNetProjectManager } from './dotnetProjectManager';

const projectManager = new DotNetProjectManager();
const fileWatcher = new FileWatcher(projectManager);

Public Methods

dispose

Cleans up all file watchers and timers.
public dispose(): void
Called When:
  • Extension is deactivated
  • File watcher needs to be recreated
  • Cleaning up resources
Example Usage:
fileWatcher.dispose();
This method is automatically called by VS Code when the extension deactivates if the watcher is registered in context.subscriptions.

Monitored File Patterns

Source Files

By default, the following patterns are watched:
watchPatterns
string[]
default:"[\"**/*.cs\", \"**/*.fs\", \"**/*.vb\"]"
File patterns to monitor for changes
Configurable via: dotnetBuildBuddy.watchPatterns Watched Events:
  • File created (onDidCreate)
  • File changed (onDidChange)
  • File deleted (onDidDelete)

Project Files

Automatically watches all project and solution files:
  • **/*.csproj
  • **/*.fsproj
  • **/*.vbproj
  • **/*.sln

Excluded Patterns

excludePatterns
string[]
Patterns to exclude from watching
Configurable via: dotnetBuildBuddy.excludePatterns Common Exclusions:
  • Build output directories (bin/, obj/)
  • Dependencies (node_modules/)
  • IDE directories (.vs/, .idea/)

Event Handling

Source File Changes

When a source file is created, modified, or deleted:
  1. Checks if auto-update is enabled (dotnetBuildBuddy.autoUpdate)
  2. Verifies the file is not in an excluded pattern
  3. Logs the change
  4. Triggers debounced update
Implementation:
private onFileChange(uri: vscode.Uri): void {
    const config = vscode.workspace.getConfiguration('dotnetBuildBuddy');
    const autoUpdate = config.get<boolean>('autoUpdate', true);
    
    if (!autoUpdate) {
        return;
    }

    if (this.shouldIgnoreFile(uri.fsPath)) {
        return;
    }

    this.debounceUpdate();
}

Project File Changes

When a project or solution file is modified:
  1. Logs the change
  2. Triggers debounced update
Implementation:
private onProjectFileChange(uri: vscode.Uri): void {
    console.log(`DotNET Build Buddy: Project file changed: ${uri.fsPath}`);
    this.debounceUpdate();
}

Debouncing

Why Debouncing?

File system events can fire rapidly (e.g., saving multiple files, IDE operations). Debouncing prevents excessive updates by waiting for activity to settle. Debounce Delay: 1000ms (1 second)

debounceUpdate

private debounceUpdate(): void {
    if (this.debounceTimer) {
        clearTimeout(this.debounceTimer);
    }

    this.debounceTimer = setTimeout(async () => {
        await this.projectManager.updateAllProjectFiles();
        await this.projectManager.generateSolutionFile();
    }, 1000);
}
Behavior:
  • Cancels any pending update
  • Starts a 1-second timer
  • Updates project files when timer expires
  • Regenerates solution file
  • Handles errors gracefully
Example Flow:
File saved at T+0ms   → Timer started (1000ms)
File saved at T+500ms → Timer reset (1000ms)
File saved at T+800ms → Timer reset (1000ms)
[No more changes]
T+1800ms → Update triggered

Configuration

Enable/Disable Auto-Update

dotnetBuildBuddy.autoUpdate
boolean
default:"true"
Automatically update project files when changes are detected
Effect: When false, the file watcher ignores all file changes.

Custom Watch Patterns

dotnetBuildBuddy.watchPatterns
string[]
default:"[\"**/*.cs\", \"**/*.fs\", \"**/*.vb\"]"
File patterns to watch for changes
Example:
{
  "dotnetBuildBuddy.watchPatterns": [
    "**/*.cs",
    "**/*.fs",
    "**/*.vb",
    "**/*.cshtml",
    "**/*.razor"
  ]
}

Custom Exclude Patterns

dotnetBuildBuddy.excludePatterns
string[]
Patterns to exclude from watching
Example:
{
  "dotnetBuildBuddy.excludePatterns": [
    "**/bin/**",
    "**/obj/**",
    "**/node_modules/**",
    "**/.vs/**",
    "**/TestResults/**"
  ]
}

Logging

The file watcher provides console logging for debugging: Initialization:
DotNET Build Buddy: Setting up file watchers for patterns: **/*.cs, **/*.fs, **/*.vb
DotNET Build Buddy: Excluding patterns: **/bin/**, **/obj/**, **/node_modules/**
DotNET Build Buddy: File watchers initialized successfully
File Changes:
DotNET Build Buddy: Detected change in .NET source file: /workspace/src/Program.cs
DotNET Build Buddy: Triggering project file update...
DotNET Build Buddy: Project files and solution updated successfully
Excluded Files:
DotNET Build Buddy: Ignoring excluded file /workspace/obj/Debug/Program.cs
Disabled Auto-Update:
DotNET Build Buddy: Auto-update disabled, ignoring change to /workspace/src/Program.cs

Integration Example

Extension Activation

import * as vscode from 'vscode';
import { DotNetProjectManager } from './dotnetProjectManager';
import { FileWatcher } from './fileWatcher';

let projectManager: DotNetProjectManager;
let fileWatcher: FileWatcher;

export function activate(context: vscode.ExtensionContext) {
    projectManager = new DotNetProjectManager();
    fileWatcher = new FileWatcher(projectManager);

    // Register for cleanup
    context.subscriptions.push(fileWatcher);

    vscode.window.showInformationMessage(
        'DotNET Build Buddy is watching for changes!'
    );
}

export function deactivate() {
    if (fileWatcher) {
        fileWatcher.dispose();
    }
}

Manual Control

// Temporarily disable watching
const config = vscode.workspace.getConfiguration('dotnetBuildBuddy');
await config.update('autoUpdate', false, vscode.ConfigurationTarget.Workspace);

// Perform operations...

// Re-enable watching
await config.update('autoUpdate', true, vscode.ConfigurationTarget.Workspace);

Error Handling

The file watcher includes error handling for update operations:
try {
    await this.projectManager.updateAllProjectFiles();
    await this.projectManager.generateSolutionFile();
    console.log('Project files and solution updated successfully');
} catch (error) {
    console.error('Error updating project files:', error);
    vscode.window.showErrorMessage(
        `DotNET Build Buddy: Failed to update project files: ${error}`
    );
}
Error Scenarios:
  • File system permission errors
  • Malformed project files
  • Network errors (NuGet API lookups)
  • Invalid XML in project files

Performance Considerations

Best Practices for Large Projects:
  • Add build directories to exclude patterns
  • Increase debounce delay if needed
  • Consider disabling auto-update during bulk operations
  • Use specific watch patterns instead of broad globs
Memory Usage:
  • Each file system watcher consumes minimal memory
  • Debounce timer is cleaned up properly
  • Watchers are disposed when no longer needed
CPU Usage:
  • File events are asynchronous and non-blocking
  • Updates only run after debounce period
  • NuGet checks can be disabled if not needed

Lifecycle

  1. Creation (constructor): Watchers are set up based on configuration
  2. Active Monitoring: File events trigger debounced updates
  3. Disposal (dispose): All watchers and timers are cleaned up
const lifecycle = {
    create: () => new FileWatcher(projectManager),
    monitor: () => {
        // Automatic - handled by VS Code
    },
    cleanup: (watcher) => watcher.dispose()
};
Always call dispose() when the file watcher is no longer needed to prevent memory leaks. Register the watcher in context.subscriptions for automatic cleanup.

Build docs developers (and LLMs) love