From 13e526e3c5fd262caf83fb8ca22a3121ea5c2006 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=ABl=20Galeran?= Date: Tue, 21 Dec 2021 12:52:54 +0100 Subject: [PATCH 1/3] feat: enable/disable File Watcher setting + commands Closes https://github.com/prisma/language-tools/issues/944 --- packages/language-server/src/server.ts | 8 +- packages/vscode/package.json | 22 +++- .../plugins/prisma-language-server/index.ts | 108 ++++++++++++++---- 3 files changed, 111 insertions(+), 27 deletions(-) diff --git a/packages/language-server/src/server.ts b/packages/language-server/src/server.ts index f1050f5f53..8c2b8079b6 100644 --- a/packages/language-server/src/server.ts +++ b/packages/language-server/src/server.ts @@ -113,7 +113,7 @@ export function startServer(options?: LSPOptions): void { // The global settings, used when the `workspace/configuration` request is not supported by the client or is not set by the user. // This does not apply to VSCode, as this client supports this setting. - // const defaultSettings: LSPSettings = { } + // const defaultSettings: LSPSettings = {} // let globalSettings: LSPSettings = defaultSettings // eslint-disable-line // Cache the settings of all open documents @@ -122,6 +122,7 @@ export function startServer(options?: LSPOptions): void { Thenable >() + // eslint-disable-line @typescript-eslint/no-unused-vars connection.onDidChangeConfiguration((change) => { connection.console.info('Configuration changed.') if (hasConfigurationCapability) { @@ -142,9 +143,12 @@ export function startServer(options?: LSPOptions): void { // function getDocumentSettings(resource: string): Thenable { // if (!hasConfigurationCapability) { - // connection.console.info(`Using default prisma-fmt binary path.`) + // connection.console.info( + // `hasConfigurationCapability === false. Defaults will be used.`, + // ) // return Promise.resolve(globalSettings) // } + // let result = documentSettings.get(resource) // if (!result) { // result = connection.workspace.getConfiguration({ diff --git a/packages/vscode/package.json b/packages/vscode/package.json index ccd8a254ce..a1f8382c4b 100644 --- a/packages/vscode/package.json +++ b/packages/vscode/package.json @@ -73,8 +73,14 @@ ], "configuration": { "type": "object", - "title": "Language server configuration", + "title": "Prisma", "properties": { + "prisma.fileWatcher": { + "scope": "window", + "type": "boolean", + "default": true, + "description": "Enable/disable the File Watcher functionnality for Prisma Client." + }, "prisma.trace.server": { "scope": "window", "type": "string", @@ -84,7 +90,7 @@ "verbose" ], "default": "off", - "description": "Traces the communication between VS Code and the language server." + "description": "Setting for logging between the VS Code extension and the language server." } } }, @@ -93,6 +99,16 @@ "command": "prisma.restartLanguageServer", "title": "Restart Language Server", "category": "Prisma" + }, + { + "command": "prisma.filewatcherEnable", + "title": "Enable the File Watcher functionnality for Prisma Client.", + "category": "Prisma" + }, + { + "command": "prisma.filewatcherDisable", + "title": "Disable the File Watcher functionnality for Prisma Client.", + "category": "Prisma" } ] }, @@ -111,4 +127,4 @@ "access": "public" }, "preview": true -} \ No newline at end of file +} diff --git a/packages/vscode/src/plugins/prisma-language-server/index.ts b/packages/vscode/src/plugins/prisma-language-server/index.ts index b363500d5e..f7d5b09fce 100644 --- a/packages/vscode/src/plugins/prisma-language-server/index.ts +++ b/packages/vscode/src/plugins/prisma-language-server/index.ts @@ -60,6 +60,32 @@ const activateClient = ( context.subscriptions.push(disposable) } +const startFileWatcher = (rootPath: string): chokidar.FSWatcher => { + console.debug('Starting File Watcher') + return chokidar.watch( + path.join(rootPath, '**/node_modules/.prisma/client/index.d.ts'), + { + // ignore dotfiles (except .prisma) adjusted from chokidar README example + ignored: /(^|[\/\\])\.(?!prisma)./, + // limits how many levels of subdirectories will be traversed. + // Note that `node_modules/.prisma/client/` counts for 3 already + // Example + // If vs code extension is open in root folder of a project and the path to index.d.ts is + // ./server/database/node_modules/.prisma/client/index.d.ts + // then the depth is equal to 2 + 3 = 5 + depth: 9, + // When false, only the symlinks themselves will be watched for changes + // instead of following the link references and bubbling events through the link's path. + followSymlinks: false, + }, + ) +} + +const onFileChange = (path: string) => { + console.log(`File ${path} has been changed. Restarting TS Server.`) + commands.executeCommand('typescript.restartTsServer') // eslint-disable-line +} + const plugin: PrismaVSCodePlugin = { name: 'prisma-language-server', enabled: () => true, @@ -68,23 +94,18 @@ const plugin: PrismaVSCodePlugin = { const rootPath = workspace.workspaceFolders?.[0].uri.path if (rootPath) { - watcher = chokidar.watch( - path.join(rootPath, '**/node_modules/.prisma/client/index.d.ts'), - { - // ignore dotfiles (except .prisma) adjusted from chokidar README example - ignored: /(^|[\/\\])\.(?!prisma)./, - // limits how many levels of subdirectories will be traversed. - // Note that `node_modules/.prisma/client/` counts for 3 already - // Example - // If vs code extension is open in root folder of a project and the path to index.d.ts is - // ./server/database/node_modules/.prisma/client/index.d.ts - // then the depth is equal to 2 + 3 = 5 - depth: 9, - // When false, only the symlinks themselves will be watched for changes - // instead of following the link references and bubbling events through the link's path. - followSymlinks: false, - }, - ) + const isFileWatcherEnabled = workspace + .getConfiguration('prisma') + .get('fileWatcher') + + if (isFileWatcherEnabled) { + watcher = startFileWatcher(rootPath) + console.debug('File Watcher is enabled and started.') + } else { + console.debug('File Watcher is disabled.') + } + } else { + console.debug('File Watcher was skipped, rootPath is falsy') } if (isDebugMode() || isE2ETestOnPullRequest()) { @@ -179,7 +200,38 @@ const plugin: PrismaVSCodePlugin = { // const config = workspace.getConfiguration('prisma') workspace.onDidChangeConfiguration( - async (e) => {}, // eslint-disable-line @typescript-eslint/no-unused-vars, @typescript-eslint/no-empty-function + async (event) => { + const fileWatcherConfigChanged = + event.affectsConfiguration('prisma.fileWatcher') + + if (fileWatcherConfigChanged) { + const isFileWatcherEnabled = workspace + .getConfiguration('prisma') + .get('fileWatcher') + + const rootPath = workspace.workspaceFolders?.[0].uri.path + if (isFileWatcherEnabled) { + // Let's start it + if (rootPath) { + watcher = startFileWatcher(rootPath) + watcher.on('change', onFileChange) + console.debug( + 'onDidChangeConfiguration: File Watcher is now enabled and started.', + ) + } else { + console.debug('onDidChangeConfiguration: rootPath is falsy') + } + } else { + // Let's stop it + if (watcher) { + await watcher.close() + console.log('onDidChangeConfiguration: File Watcher stopped.') + } else { + console.debug('onDidChangeConfiguration: No File Watcher found') + } + } + } + }, // eslint-disable-line @typescript-eslint/no-unused-vars, @typescript-eslint/no-empty-function null, context.subscriptions, ) @@ -194,11 +246,26 @@ const plugin: PrismaVSCodePlugin = { ) window.showInformationMessage('Prisma language server restarted.') // eslint-disable-line @typescript-eslint/no-floating-promises }), + /* This command is part of the workaround for https://github.com/prisma/language-tools/issues/311 */ commands.registerCommand( 'prisma.applySnippetWorkspaceEdit', applySnippetWorkspaceEdit(), ), + + commands.registerCommand('prisma.filewatcherEnable', async () => { + const prismaConfig = workspace.getConfiguration('prisma') + // First, is set to true value + // Second, is set it on Workspace level settings + await prismaConfig.update('fileWatcher', true, false) + }), + + commands.registerCommand('prisma.filewatcherDisable', async () => { + const prismaConfig = workspace.getConfiguration('prisma') + // First, is set to false value + // Second, is set it on Workspace level settings + await prismaConfig.update('fileWatcher', false, false) + }), ) activateClient(context, serverOptions, clientOptions) @@ -223,10 +290,7 @@ const plugin: PrismaVSCodePlugin = { checkForMinimalColorTheme() if (watcher) { - watcher.on('change', (path) => { - console.log(`File ${path} has been changed. Restarting TS Server.`) - commands.executeCommand('typescript.restartTsServer') // eslint-disable-line - }) + watcher.on('change', onFileChange) } }, deactivate: async () => { From 255c963e61ca5598c7233e0bbc7572118365f966 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=ABl=20Galeran?= Date: Tue, 21 Dec 2021 14:00:12 +0100 Subject: [PATCH 2/3] Apply suggestions from code review Co-authored-by: Jan Piotrowski --- packages/vscode/package.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/vscode/package.json b/packages/vscode/package.json index a1f8382c4b..467793522a 100644 --- a/packages/vscode/package.json +++ b/packages/vscode/package.json @@ -79,7 +79,7 @@ "scope": "window", "type": "boolean", "default": true, - "description": "Enable/disable the File Watcher functionnality for Prisma Client." + "description": "Enable/disable the File Watcher functionality for Prisma Client." }, "prisma.trace.server": { "scope": "window", @@ -102,12 +102,12 @@ }, { "command": "prisma.filewatcherEnable", - "title": "Enable the File Watcher functionnality for Prisma Client.", + "title": "Enable the File Watcher functionality for Prisma Client.", "category": "Prisma" }, { "command": "prisma.filewatcherDisable", - "title": "Disable the File Watcher functionnality for Prisma Client.", + "title": "Disable the File Watcher functionality for Prisma Client.", "category": "Prisma" } ] From cb980de1cef480857e67a021d96e413e642ebe7f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=ABl=20Galeran?= Date: Wed, 22 Dec 2021 15:54:27 +0100 Subject: [PATCH 3/3] catch case when watcher is already running --- .../plugins/prisma-language-server/index.ts | 26 ++++++++++++++----- 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/packages/vscode/src/plugins/prisma-language-server/index.ts b/packages/vscode/src/plugins/prisma-language-server/index.ts index f7d5b09fce..1c1f36adb7 100644 --- a/packages/vscode/src/plugins/prisma-language-server/index.ts +++ b/packages/vscode/src/plugins/prisma-language-server/index.ts @@ -94,6 +94,7 @@ const plugin: PrismaVSCodePlugin = { const rootPath = workspace.workspaceFolders?.[0].uri.path if (rootPath) { + // This setting defaults to true (see package.json of vscode extension) const isFileWatcherEnabled = workspace .getConfiguration('prisma') .get('fileWatcher') @@ -210,16 +211,29 @@ const plugin: PrismaVSCodePlugin = { .get('fileWatcher') const rootPath = workspace.workspaceFolders?.[0].uri.path + + // This setting defaults to true (see package.json of vscode extension) if (isFileWatcherEnabled) { - // Let's start it - if (rootPath) { - watcher = startFileWatcher(rootPath) - watcher.on('change', onFileChange) + // if watcher.closed === true, the watcher was closed previously and can be safely restarted + // if watcher.closed === false, it is already running + // but if the JSON settings are empty like {} and the user enables the file watcher + // we need to catch that case to avoid starting another extra file watcher + // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-member-access + if (watcher && (watcher as any).closed === false) { console.debug( - 'onDidChangeConfiguration: File Watcher is now enabled and started.', + "onDidChangeConfiguration: watcher.closed === false so it's already running. Do nothing.", ) } else { - console.debug('onDidChangeConfiguration: rootPath is falsy') + // Let's start it + if (rootPath) { + watcher = startFileWatcher(rootPath) + watcher.on('change', onFileChange) + console.debug( + 'onDidChangeConfiguration: File Watcher is now enabled and started.', + ) + } else { + console.debug('onDidChangeConfiguration: rootPath is falsy') + } } } else { // Let's stop it