Skip to content

Commit

Permalink
Merge pull request #1717 from posit-dev/dotnomad/config-watcher-mngr
Browse files Browse the repository at this point in the history
Create `ConfigWatcherManager` disposable
  • Loading branch information
dotNomad authored May 24, 2024
2 parents 0d1c0b0 + 55b9491 commit a6cd872
Show file tree
Hide file tree
Showing 4 changed files with 92 additions and 107 deletions.
3 changes: 3 additions & 0 deletions extensions/vscode/src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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";
5 changes: 3 additions & 2 deletions extensions/vscode/src/extension.ts
Original file line number Diff line number Diff line change
@@ -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";
Expand Down Expand Up @@ -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
Expand All @@ -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();
Expand Down
142 changes: 40 additions & 102 deletions extensions/vscode/src/views/homeView.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@ import {
CancellationToken,
Disposable,
ExtensionContext,
FileSystemWatcher,
RelativePattern,
ThemeIcon,
Uri,
Webview,
Expand Down Expand Up @@ -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";
Expand All @@ -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
Expand All @@ -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,
Expand All @@ -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,
);
});
}
/**
Expand Down Expand Up @@ -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) {
Expand Down
49 changes: 46 additions & 3 deletions extensions/vscode/src/watchers.ts
Original file line number Diff line number Diff line change
@@ -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.
*/
Expand All @@ -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;
}
Expand Down Expand Up @@ -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();
}
}

0 comments on commit a6cd872

Please sign in to comment.