From 55b9491bff896ad825eb5962b7dc80311a852f70 Mon Sep 17 00:00:00 2001 From: Jordan Jensen Date: Thu, 23 May 2024 16:44:14 -0700 Subject: [PATCH] Create ConfigWatcherManager disposable --- extensions/vscode/src/constants.ts | 3 + extensions/vscode/src/extension.ts | 5 +- extensions/vscode/src/views/homeView.ts | 142 +++++++----------------- extensions/vscode/src/watchers.ts | 49 +++++++- 4 files changed, 92 insertions(+), 107 deletions(-) diff --git a/extensions/vscode/src/constants.ts b/extensions/vscode/src/constants.ts index 24553e5ef..60661f43c 100644 --- a/extensions/vscode/src/constants.ts +++ b/extensions/vscode/src/constants.ts @@ -4,3 +4,6 @@ export const PUBLISH_DEPLOYMENTS_FOLDER = ".posit/publish/deployments"; export const CONFIGURATIONS_PATTERN = ".posit/publish/*.toml"; export const DEPLOYMENTS_PATTERN = ".posit/publish/deployments/*.toml"; + +export const DEFAULT_PYTHON_PACKAGE_FILE = "requirements.txt"; +export const DEFAULT_R_PACKAGE_FILE = "renv.lock"; diff --git a/extensions/vscode/src/extension.ts b/extensions/vscode/src/extension.ts index 6296d9b04..91262a943 100644 --- a/extensions/vscode/src/extension.ts +++ b/extensions/vscode/src/extension.ts @@ -1,6 +1,6 @@ // Copyright (C) 2024 by Posit Software, PBC. -import { ExtensionContext, commands, workspace } from "vscode"; +import { ExtensionContext, commands } from "vscode"; import * as ports from "src/ports"; import { Service } from "src/services"; @@ -51,7 +51,7 @@ export async function activate(context: ExtensionContext) { service = new Service(context, port); - const watchers = new WatcherManager(workspace.workspaceFolders?.[0]); + const watchers = new WatcherManager(); context.subscriptions.push(watchers); // First the construction of the data providers @@ -75,6 +75,7 @@ export async function activate(context: ExtensionContext) { const logsTreeDataProvider = new LogsTreeDataProvider(context, stream); const homeViewProvider = new HomeViewProvider(context, stream); + context.subscriptions.push(homeViewProvider); // Then the registration of the data providers with the VSCode framework projectTreeDataProvider.register(); diff --git a/extensions/vscode/src/views/homeView.ts b/extensions/vscode/src/views/homeView.ts index 3530e5956..81db7d04e 100644 --- a/extensions/vscode/src/views/homeView.ts +++ b/extensions/vscode/src/views/homeView.ts @@ -4,8 +4,6 @@ import { CancellationToken, Disposable, ExtensionContext, - FileSystemWatcher, - RelativePattern, ThemeIcon, Uri, Webview, @@ -62,7 +60,7 @@ import { DestinationQuickPick } from "src/types/quickPicks"; import { normalizeURL } from "src/utils/url"; import { selectConfig } from "src/multiStepInputs/selectConfig"; import { RPackage, RVersionConfig } from "src/api/types/packages"; -import { WatcherManager } from "src/watchers"; +import { ConfigWatcherManager, WatcherManager } from "src/watchers"; const viewName = "posit.publisher.homeView"; const refreshCommand = viewName + ".refresh"; @@ -78,7 +76,7 @@ enum HomeViewInitialized { const lastSelectionState = viewName + ".lastSelectionState.v2"; -export class HomeViewProvider implements WebviewViewProvider { +export class HomeViewProvider implements WebviewViewProvider, Disposable { private _disposables: Disposable[] = []; private _deployments: ( | Deployment @@ -94,9 +92,7 @@ export class HomeViewProvider implements WebviewViewProvider { private _extensionUri: Uri; private _webviewConduit: WebviewConduit; - private activeConfigFileWatcher: FileSystemWatcher | undefined; - private activePythonPackageFileWatcher: FileSystemWatcher | undefined; - private activeRPackageFileWatcher: FileSystemWatcher | undefined; + private configWatchers: ConfigWatcherManager | undefined; constructor( private readonly _context: ExtensionContext, @@ -122,13 +118,44 @@ export class HomeViewProvider implements WebviewViewProvider { useBus().trigger("activeDeploymentChanged", this._getActiveDeployment()); }); - useBus().on("activeConfigChanged", (cfg: Configuration | undefined) => { + useBus().on("activeConfigChanged", (cfg) => { this.sendRefreshedFilesLists(); this._onRefreshPythonPackages(); this._onRefreshRPackages(); - this.createActiveConfigFileWatcher(cfg); - this.createActivePythonPackageFileWatcher(cfg); - this.createActiveRPackageFileWatcher(cfg); + + this.configWatchers?.dispose(); + this.configWatchers = new ConfigWatcherManager(cfg); + + this.configWatchers.configFile?.onDidChange( + this.sendRefreshedFilesLists, + this, + ); + + this.configWatchers.pythonPackageFile?.onDidCreate( + this._onRefreshPythonPackages, + this, + ); + this.configWatchers.pythonPackageFile?.onDidChange( + this._onRefreshPythonPackages, + this, + ); + this.configWatchers.pythonPackageFile?.onDidDelete( + this._onRefreshPythonPackages, + this, + ); + + this.configWatchers.rPackageFile?.onDidCreate( + this._onRefreshPythonPackages, + this, + ); + this.configWatchers.rPackageFile?.onDidChange( + this._onRefreshPythonPackages, + this, + ); + this.configWatchers.rPackageFile?.onDidDelete( + this._onRefreshPythonPackages, + this, + ); }); } /** @@ -1034,98 +1061,9 @@ export class HomeViewProvider implements WebviewViewProvider { * Cleans up and disposes of webview resources when view is disposed */ public dispose() { - // Dispose of all disposables (i.e. commands) for the current webview panel - while (this._disposables.length) { - const disposable = this._disposables.pop(); - if (disposable) { - disposable.dispose(); - } - } - } - - private createActiveConfigFileWatcher(cfg: Configuration | undefined) { - if (this.root === undefined || cfg === undefined) { - return; - } - - const watcher = workspace.createFileSystemWatcher( - new RelativePattern(this.root, cfg.configurationPath), - ); - watcher.onDidChange(this.sendRefreshedFilesLists); - - if (this.activeConfigFileWatcher) { - // Dispose the previous configuration file watcher - this.activeConfigFileWatcher.dispose(); - const index = this._context.subscriptions.indexOf( - this.activeConfigFileWatcher, - ); - if (index !== -1) { - this._context.subscriptions.splice(index, 1); - } - } - - this.activeConfigFileWatcher = watcher; - this._context.subscriptions.push(watcher); - } - - private createActivePythonPackageFileWatcher(cfg: Configuration | undefined) { - if (this.root === undefined || cfg === undefined) { - return; - } - - const watcher = workspace.createFileSystemWatcher( - new RelativePattern( - this.root, - cfg.configuration.python?.packageFile || "requirements.txt", - ), - ); - watcher.onDidCreate(this._onRefreshPythonPackages, this); - watcher.onDidChange(this._onRefreshPythonPackages, this); - watcher.onDidDelete(this._onRefreshPythonPackages, this); - - if (this.activePythonPackageFileWatcher) { - // Dispose the previous configuration file watcher - this.activePythonPackageFileWatcher.dispose(); - const index = this._context.subscriptions.indexOf( - this.activePythonPackageFileWatcher, - ); - if (index !== -1) { - this._context.subscriptions.splice(index, 1); - } - } - - this.activePythonPackageFileWatcher = watcher; - this._context.subscriptions.push(watcher); - } - - private createActiveRPackageFileWatcher(cfg: Configuration | undefined) { - if (this.root === undefined || cfg === undefined) { - return; - } - - const watcher = workspace.createFileSystemWatcher( - new RelativePattern( - this.root, - cfg.configuration.r?.packageFile || "renv.lock", - ), - ); - watcher.onDidCreate(this._onRefreshRPackages, this); - watcher.onDidChange(this._onRefreshRPackages, this); - watcher.onDidDelete(this._onRefreshRPackages, this); - - if (this.activeRPackageFileWatcher) { - // Dispose the previous configuration file watcher - this.activeRPackageFileWatcher.dispose(); - const index = this._context.subscriptions.indexOf( - this.activeRPackageFileWatcher, - ); - if (index !== -1) { - this._context.subscriptions.splice(index, 1); - } - } + Disposable.from(...this._disposables).dispose(); - this.activeRPackageFileWatcher = watcher; - this._context.subscriptions.push(watcher); + this.configWatchers?.dispose(); } public register(watchers: WatcherManager) { diff --git a/extensions/vscode/src/watchers.ts b/extensions/vscode/src/watchers.ts index 8c7664407..3a3617a27 100644 --- a/extensions/vscode/src/watchers.ts +++ b/extensions/vscode/src/watchers.ts @@ -1,21 +1,23 @@ import { Disposable, RelativePattern, - WorkspaceFolder, FileSystemWatcher, workspace, } from "vscode"; +import { Configuration } from "src/api"; import { PUBLISH_DEPLOYMENTS_FOLDER, POSIT_FOLDER, PUBLISH_FOLDER, CONFIGURATIONS_PATTERN, DEPLOYMENTS_PATTERN, + DEFAULT_PYTHON_PACKAGE_FILE, + DEFAULT_R_PACKAGE_FILE, } from "src/constants"; /** - * Manages all the file system watchers for the extension. + * Manages persistent file system watchers for the extension. * * The directory watchers only watch for onDidDelete events. */ @@ -27,7 +29,8 @@ export class WatcherManager implements Disposable { deployments: FileSystemWatcher | undefined; allFiles: FileSystemWatcher | undefined; - constructor(root?: WorkspaceFolder) { + constructor() { + const root = workspace.workspaceFolders?.[0]; if (root === undefined) { return; } @@ -75,3 +78,43 @@ export class WatcherManager implements Disposable { this.allFiles?.dispose(); } } + +/** + * Manages file watchers for a specific Configuration File + */ +export class ConfigWatcherManager implements Disposable { + configFile: FileSystemWatcher | undefined; + pythonPackageFile: FileSystemWatcher | undefined; + rPackageFile: FileSystemWatcher | undefined; + + constructor(cfg?: Configuration) { + const root = workspace.workspaceFolders?.[0]; + if (root === undefined || cfg === undefined) { + return; + } + + this.configFile = workspace.createFileSystemWatcher( + new RelativePattern(root, cfg.configurationPath), + ); + + this.pythonPackageFile = workspace.createFileSystemWatcher( + new RelativePattern( + root, + cfg.configuration.python?.packageFile || DEFAULT_PYTHON_PACKAGE_FILE, + ), + ); + + this.rPackageFile = workspace.createFileSystemWatcher( + new RelativePattern( + root, + cfg.configuration.r?.packageFile || DEFAULT_R_PACKAGE_FILE, + ), + ); + } + + dispose() { + this.configFile?.dispose(); + this.pythonPackageFile?.dispose(); + this.rPackageFile?.dispose(); + } +}