From 25bdcce311906a458d4dca7a8d7275b5ed4bc9c9 Mon Sep 17 00:00:00 2001 From: Achal Talati Date: Fri, 11 Oct 2024 14:02:23 +0530 Subject: [PATCH 01/17] Added configurations, lsp and logger modules --- vscode/src/configurations/configuration.ts | 52 + vscode/src/configurations/handlers.ts | 129 +++ vscode/src/configurations/listener.ts | 34 + vscode/src/constants.ts | 25 +- vscode/src/explorer.ts | 2 +- vscode/src/extension.ts | 926 ++++-------------- vscode/src/extensionInfo.ts | 25 + vscode/src/jdkDownloader/action.ts | 50 +- vscode/src/jdkDownloader/prompt.ts | 29 + vscode/src/jdkDownloader/view.ts | 28 +- vscode/src/launchConfigurations.ts | 8 +- vscode/src/localiser.ts | 4 +- vscode/src/logger.ts | 58 ++ vscode/src/lsp/clientPromise.ts | 80 ++ vscode/src/lsp/initializer.ts | 231 +++++ vscode/src/lsp/launchOptions.ts | 85 ++ vscode/src/lsp/nbLanguageClient.ts | 102 ++ vscode/src/lsp/nbProcessManager.ts | 106 ++ vscode/src/lsp/protocol.ts | 324 ++++++ vscode/src/lsp/types.ts | 22 + vscode/src/lsp/utils.ts | 69 ++ vscode/src/nbcode.ts | 167 ---- vscode/src/test/suite/extension.test.ts | 302 ++++++ .../src/test/suite/general/explorer.test.ts | 1 - vscode/src/test/suite/testutils.ts | 234 +++++ vscode/src/test/testutils.ts | 51 +- vscode/src/testAdapter.ts | 10 +- vscode/src/utils.ts | 5 +- 28 files changed, 2188 insertions(+), 971 deletions(-) create mode 100644 vscode/src/configurations/configuration.ts create mode 100644 vscode/src/configurations/handlers.ts create mode 100644 vscode/src/configurations/listener.ts create mode 100644 vscode/src/extensionInfo.ts create mode 100644 vscode/src/jdkDownloader/prompt.ts create mode 100644 vscode/src/logger.ts create mode 100644 vscode/src/lsp/clientPromise.ts create mode 100644 vscode/src/lsp/initializer.ts create mode 100644 vscode/src/lsp/launchOptions.ts create mode 100644 vscode/src/lsp/nbLanguageClient.ts create mode 100644 vscode/src/lsp/nbProcessManager.ts create mode 100644 vscode/src/lsp/protocol.ts create mode 100644 vscode/src/lsp/types.ts create mode 100644 vscode/src/lsp/utils.ts delete mode 100644 vscode/src/nbcode.ts create mode 100644 vscode/src/test/suite/extension.test.ts create mode 100644 vscode/src/test/suite/testutils.ts diff --git a/vscode/src/configurations/configuration.ts b/vscode/src/configurations/configuration.ts new file mode 100644 index 0000000..74f200d --- /dev/null +++ b/vscode/src/configurations/configuration.ts @@ -0,0 +1,52 @@ +/* + Copyright (c) 2023-2024, Oracle and/or its affiliates. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ +import { appendPrefixToCommand } from "../utils" + +export const configKeys = { + jdkHome: 'jdkhome', + projectJdkHome: 'project.jdkhome', + lspVmOptions: 'serverVmOptions', + disableNbJavac: 'advanced.disable.nbjavac', + disableProjSearchLimit: 'advanced.disable.projectSearchLimit', + formatPrefs: 'format', + hintPrefs: 'hints', + importPrefs: 'java.imports', + runConfigVmOptions: 'runConfig.vmOptions', + runConfigCwd: 'runConfig.cwd', + verbose: 'verbose', + userdir: 'userdir', + vscodeTheme: 'workbench.colorTheme' +}; + +export const userConfigsListened = [ + appendPrefixToCommand(configKeys.jdkHome), + appendPrefixToCommand(configKeys.userdir), + appendPrefixToCommand(configKeys.lspVmOptions), + appendPrefixToCommand(configKeys.disableNbJavac), + appendPrefixToCommand(configKeys.disableProjSearchLimit), + configKeys.vscodeTheme, +]; + + +export const userConfigsListenedByServer = [ + appendPrefixToCommand(configKeys.hintPrefs), + appendPrefixToCommand(configKeys.formatPrefs), + appendPrefixToCommand(configKeys.importPrefs), + appendPrefixToCommand(configKeys.projectJdkHome), + appendPrefixToCommand(configKeys.runConfigVmOptions), + appendPrefixToCommand(configKeys.runConfigCwd) +]; + diff --git a/vscode/src/configurations/handlers.ts b/vscode/src/configurations/handlers.ts new file mode 100644 index 0000000..b8c0ec0 --- /dev/null +++ b/vscode/src/configurations/handlers.ts @@ -0,0 +1,129 @@ +/* + Copyright (c) 2023-2024, Oracle and/or its affiliates. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ +import { extensions, workspace } from "vscode"; +import { configKeys } from "./configuration"; +import { extConstants, NODE_WINDOWS_LABEL } from "../constants"; +import * as os from 'os'; +import { globalVars, LOGGER } from "../extension"; +import { LogLevel } from "../logger"; +import * as path from 'path'; +import * as fs from 'fs'; + +export const getConfigurationValue = (key: string, defaultValue: T | undefined = undefined): T => { + const conf = workspace.getConfiguration(extConstants.COMMAND_PREFIX); + return defaultValue != undefined ? conf.get(key, defaultValue) : conf.get(key) as T; +} + +export const jdkHomeValueHandler = (): string | null => { + return getConfigurationValue(configKeys.jdkHome) || + process.env.JDK_HOME || + process.env.JAVA_HOME || + null; +} + +export const projectSearchRootsValueHandler = (): string => { + let projectSearchRoots: string = ''; + const isProjectFolderSearchLimited: boolean = !getConfigurationValue(configKeys.disableProjSearchLimit, false); + if (isProjectFolderSearchLimited) { + try { + projectSearchRoots = os.homedir() as string; + } catch (err: any) { + LOGGER.log(`Failed to obtain the user home directory due to: ${err}`, LogLevel.ERROR); + } + if (!projectSearchRoots) { + projectSearchRoots = os.type() === NODE_WINDOWS_LABEL ? '%USERPROFILE%' : '$HOME'; // The launcher script may perform the env variable substitution + LOGGER.log(`Using userHomeDir = "${projectSearchRoots}" as the launcher script may perform env var substitution to get its value.`); + } + const workspaces = workspace.workspaceFolders; + if (workspaces) { + workspaces.forEach(workspace => { + if (workspace.uri) { + try { + projectSearchRoots = projectSearchRoots + path.delimiter + path.normalize(workspace.uri.fsPath); + } catch (err: any) { + LOGGER.log(`Failed to get the workspace path: ${err}`); + } + } + }); + } + } + + return projectSearchRoots; +} + +export const lspServerVmOptionsHandler = (): string[] => { + let serverVmOptions: string[] = getConfigurationValue(configKeys.lspVmOptions, []); + + return serverVmOptions.map(el => `-J${el}`); +} + +export const isDarkColorThemeHandler = (): boolean => { + // const themeName = getConfigurationValue(configKeys.vscodeTheme); + const themeName = workspace.getConfiguration('workbench')?.get('colorTheme'); + if (!themeName) { + return false; + } + for (const ext of extensions.all) { + const themeList: object[] = ext.packageJSON?.contributes && ext.packageJSON?.contributes['themes']; + if (!themeList) { + continue; + } + let t: any; + for (t of themeList) { + if (t.id !== themeName) { + continue; + } + const uiTheme = t.uiTheme; + if (typeof (uiTheme) == 'string') { + if (uiTheme.includes('-dark') || uiTheme.includes('-black')) { + return true; + } + } + } + } + return false; +} + +export const userdirHandler = (): string => { + const userdirScope = process.env['nbcode_userdir'] || getConfigurationValue(configKeys.userdir, "local"); + const userdirParentDir = userdirScope === "local" + ? globalVars.extensionInfo.getWorkspaceStorage()?.fsPath + : globalVars.extensionInfo.getGlobalStorage().fsPath; + + if (!userdirParentDir) { + throw new Error(`Cannot create path for ${userdirScope} directory.`); + } + + const userdir = path.join(userdirParentDir, "userdir"); + + try { + if(!fs.existsSync(userdir)){ + fs.mkdirSync(userdir, { recursive: true }); + const stats = fs.statSync(userdir); + if (!stats.isDirectory()) { + throw new Error(`${userdir} is not a directory`); + } + } + + return userdir; + } catch (error) { + throw new Error(`Failed to create or access ${userdir}: ${(error as Error).message}`); + } +} + +export const isNbJavacDisabledHandler = (): boolean => { + return getConfigurationValue(configKeys.verbose, false); +} \ No newline at end of file diff --git a/vscode/src/configurations/listener.ts b/vscode/src/configurations/listener.ts new file mode 100644 index 0000000..ca78014 --- /dev/null +++ b/vscode/src/configurations/listener.ts @@ -0,0 +1,34 @@ +/* + Copyright (c) 2023-2024, Oracle and/or its affiliates. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ +import { ConfigurationChangeEvent, workspace } from "vscode"; +import { userConfigsListened } from "./configuration"; +import { globalVars } from "../extension"; + +let timeout: NodeJS.Timeout | undefined = undefined; +export const configChangeListener = (params: ConfigurationChangeEvent) => { + if (timeout) { + return; + } + timeout = setTimeout(() => { + timeout = undefined; + userConfigsListened.forEach((config: string) => { + const doesAffect = params.affectsConfiguration(config); + if(doesAffect){ + globalVars.clientPromise.restartExtension(globalVars.nbProcessManager, true); + } + }) + }, 0); +} \ No newline at end of file diff --git a/vscode/src/constants.ts b/vscode/src/constants.ts index cfdaf3f..7a6b493 100644 --- a/vscode/src/constants.ts +++ b/vscode/src/constants.ts @@ -15,15 +15,26 @@ */ -export const JDK_RELEASES_TRACK_URL = `https://www.java.com/releases/releases.json`; +export namespace extConstants { + export const API_VERSION: string = "1.0"; + export const SERVER_NAME: string = "Oracle Java SE Language Server"; + export const NB_LANGUAGE_CLIENT_ID: string = 'Oracle Java SE'; + export const NB_LANGUAGE_CLIENT_NAME: string = "java"; + export const LANGUAGE_ID: string = "java"; + export const ORACLE_VSCODE_EXTENSION_ID = 'oracle.oracle-java'; + export const COMMAND_PREFIX = 'jdk'; +} -export const ORACLE_JDK_BASE_DOWNLOAD_URL = `https://download.oracle.com/java`; +export namespace jdkDownloaderConstants { + export const JDK_RELEASES_TRACK_URL = `https://www.java.com/releases/releases.json`; -export const ORACLE_JDK_DOWNLOAD_VERSIONS = ['23','21']; + export const ORACLE_JDK_BASE_DOWNLOAD_URL = `https://download.oracle.com/java`; -export const OPEN_JDK_VERSION_DOWNLOAD_LINKS: { [key: string]: string } = { - "23": "https://download.java.net/java/GA/jdk23.0.1/c28985cbf10d4e648e4004050f8781aa/11/GPL/openjdk-23.0.1" -}; + export const ORACLE_JDK_DOWNLOAD_VERSIONS = ['23', '21']; + + export const OPEN_JDK_VERSION_DOWNLOAD_LINKS: { [key: string]: string } = { + "23": "https://download.java.net/java/GA/jdk23.0.1/c28985cbf10d4e648e4004050f8781aa/11/GPL/openjdk-23.0.1" + }; +} -export const ORACLE_VSCODE_EXTENSION_ID = 'oracle.oracle-java'; export const NODE_WINDOWS_LABEL = "Windows_NT"; diff --git a/vscode/src/explorer.ts b/vscode/src/explorer.ts index 9d6b613..7a4e996 100644 --- a/vscode/src/explorer.ts +++ b/vscode/src/explorer.ts @@ -19,7 +19,7 @@ import * as vscode from 'vscode'; import { ThemeIcon } from 'vscode'; import { LanguageClient } from 'vscode-languageclient/node'; -import { NbLanguageClient } from './extension'; +import { NbLanguageClient } from './lsp/nbLanguageClient'; import { NodeChangedParams, NodeInfoNotification, NodeInfoRequest, GetResourceParams, NodeChangeType, NodeChangesParams } from './protocol'; import { l10n } from './localiser'; const doLog : boolean = false; diff --git a/vscode/src/extension.ts b/vscode/src/extension.ts index ee8a8a0..3569045 100644 --- a/vscode/src/extension.ts +++ b/vscode/src/extension.ts @@ -27,23 +27,13 @@ import { commands, window, workspace, ExtensionContext, ProgressLocation, TextEd import { LanguageClient, - LanguageClientOptions, - ServerOptions, StreamInfo } from 'vscode-languageclient/node'; import { - CloseAction, - ErrorAction, - Message, MessageType, LogMessageNotification, - RevealOutputChannelOn, - DocumentSelector, - ErrorHandlerResult, - CloseHandlerResult, SymbolInformation, - TextDocumentFilter, TelemetryEventNotification } from 'vscode-languageclient'; @@ -51,10 +41,8 @@ import * as net from 'net'; import * as fs from 'fs'; import * as os from 'os'; import * as path from 'path'; -import { ChildProcess } from 'child_process'; import * as vscode from 'vscode'; import * as ls from 'vscode-languageserver-protocol'; -import * as launcher from './nbcode'; import { StreamDebugAdapter} from './streamDebugAdapter'; import { NbTestAdapter } from './testAdapter'; import { asRanges, StatusMessageRequest, ShowStatusMessageParams, QuickPickRequest, InputBoxRequest, MutliStepInputRequest, TestProgressNotification, DebugConnector, @@ -62,66 +50,32 @@ import { asRanges, StatusMessageRequest, ShowStatusMessageParams, QuickPickReque ExecInHtmlPageRequest, SetTextEditorDecorationParams, ProjectActionParams, UpdateConfigurationRequest, QuickPickStep, InputBoxStep, SaveDocumentsRequest, SaveDocumentRequestParams } from './protocol'; import * as launchConfigurations from './launchConfigurations'; -import { createTreeViewService, TreeViewService, Visualizer } from './explorer'; +import { TreeViewService, Visualizer } from './explorer'; import { initializeRunConfiguration, runConfigurationProvider, runConfigurationNodeProvider, configureRunSettings, runConfigurationUpdateAll } from './runConfiguration'; import { InputStep, MultiStepInput } from './utils'; import { PropertiesView } from './propertiesView/propertiesView'; import { l10n } from './localiser'; -import { ORACLE_VSCODE_EXTENSION_ID,NODE_WINDOWS_LABEL } from './constants'; +import { extConstants } from './constants'; import { JdkDownloaderView } from './jdkDownloader/view'; +import { ExtensionInfo } from './extensionInfo'; +import { ClientPromise } from './lsp/clientPromise'; +import { ExtensionLogger, LogLevel } from './logger'; +import { NbProcessManager } from './lsp/nbProcessManager'; +import { initializeServer } from './lsp/initializer'; +import { NbLanguageClient } from './lsp/nbLanguageClient'; +import { configChangeListener } from './configurations/listener'; +import { isNbJavacDisabledHandler } from './configurations/handlers'; -const API_VERSION : string = "1.0"; -const SERVER_NAME : string = "Oracle Java SE Language Server"; -const NB_LANGUAGE_CLIENT_ID: string = "java"; -const LANGUAGE_ID: string = "java"; - -export const COMMAND_PREFIX : string = "jdk"; const listeners = new Map(); -let client: Promise; -let testAdapter: NbTestAdapter | undefined; -let nbProcess : ChildProcess | null = null; -let debugPort: number = -1; -let debugHash: string | undefined; -let consoleLog: boolean = !!process.env['ENABLE_CONSOLE_LOG']; -let deactivated:boolean = true; -export class NbLanguageClient extends LanguageClient { - private _treeViewService: TreeViewService; - - constructor (id : string, name: string, s : ServerOptions, log : vscode.OutputChannel, c : LanguageClientOptions) { - super(id, name, s, c); - this._treeViewService = createTreeViewService(log, this); - } - - findTreeViewService(): TreeViewService { - return this._treeViewService; - } - - stop(): Promise { - // stop will be called even in case of external close & client restart, so OK. - const r: Promise = super.stop(); - this._treeViewService.dispose(); - return r; - } - -} - -export function handleLog(log: vscode.OutputChannel, msg: string): void { - log.appendLine(msg); - if (consoleLog) { - console.log(msg); - } -} - -function handleLogNoNL(log: vscode.OutputChannel, msg: string): void { - log.append(msg); - if (consoleLog) { - process.stdout.write(msg); - } -} - -export function enableConsoleLog() { - consoleLog = true; - console.log("enableConsoleLog"); +export let LOGGER: ExtensionLogger; +export namespace globalVars { + export let extensionInfo: ExtensionInfo; + export let clientPromise: ClientPromise; + export let debugPort: number = -1; + export let debugHash: string | undefined; + export let deactivated: boolean = true; + export let nbProcessManager: NbProcessManager | null; + export let testAdapter: NbTestAdapter | undefined; } export function findClusters(myPath : string): string[] { @@ -152,80 +106,24 @@ export function findClusters(myPath : string): string[] { // for tests only ! export function awaitClient() : Promise { - const c : Promise = client; - if (c && !(c instanceof InitialPromise)) { - return c; + const clientPromise = globalVars.clientPromise; + if (clientPromise.client && clientPromise.initialPromiseResolved) { + return clientPromise.client; } - let nbcode = vscode.extensions.getExtension(ORACLE_VSCODE_EXTENSION_ID); + let nbcode = vscode.extensions.getExtension(extConstants.ORACLE_VSCODE_EXTENSION_ID); if (!nbcode) { return Promise.reject(new Error(l10n.value("jdk.extension.notInstalled.label"))); } const t : Thenable = nbcode.activate().then(nc => { - if (client === undefined || client instanceof InitialPromise) { - throw new Error(l10n.value("jdk.extension.error_msg.clientNotAvailable")); + if (globalVars.clientPromise.client === undefined || !globalVars.clientPromise.initialPromiseResolved) { + throw new Error(l10n.value("jdk.extenstion.error_msg.clientNotAvailable")); } else { - return client; + return globalVars.clientPromise.client; } }); return Promise.resolve(t); } -function findJDK(onChange: (path : string | null) => void): void { - let nowDark : boolean = isDarkColorTheme(); - let nowNbJavacDisabled : boolean = isNbJavacDisabled(); - function find(): string | null { - let nbJdk = workspace.getConfiguration(COMMAND_PREFIX).get('jdkhome'); - if (nbJdk) { - return nbJdk as string; - } - let jdkHome: any = process.env.JDK_HOME; - if (jdkHome) { - return jdkHome as string; - } - let jHome: any = process.env.JAVA_HOME; - if (jHome) { - return jHome as string; - } - return null; - } - - let currentJdk = find(); - let projectJdk : string | undefined = getProjectJDKHome(); - let timeout: NodeJS.Timeout | undefined = undefined; - workspace.onDidChangeConfiguration(params => { - if (timeout) { - return; - } - let interested : boolean = false; - if (params.affectsConfiguration(COMMAND_PREFIX)) { - interested = true; - } else if (params.affectsConfiguration('workbench.colorTheme')) { - let d = isDarkColorTheme(); - if (d != nowDark) { - interested = true; - } - } - if (!interested) { - return; - } - timeout = setTimeout(() => { - timeout = undefined; - let newJdk = find(); - let newD = isDarkColorTheme(); - let newNbJavacDisabled = isNbJavacDisabled(); - let newProjectJdk : string | undefined = workspace.getConfiguration(COMMAND_PREFIX)?.get('project.jdkhome') as string; - if (newJdk !== currentJdk || newD != nowDark || newNbJavacDisabled != nowNbJavacDisabled || newProjectJdk != projectJdk) { - nowDark = newD; - currentJdk = newJdk; - nowNbJavacDisabled = newNbJavacDisabled; - projectJdk = newProjectJdk - onChange(currentJdk); - } - }, 0); - }); - onChange(currentJdk); -} - interface VSNetBeansAPI { version : string; apiVersion: string; @@ -274,13 +172,13 @@ function wrapProjectActionWithProgress(action : string, configuration : string | items.push(item); } } - return wrapCommandWithProgress(COMMAND_PREFIX + '.project.run.action', title, log, showOutput, actionParams, ...items); + return wrapCommandWithProgress(extConstants.COMMAND_PREFIX + '.project.run.action', title, log, showOutput, actionParams, ...items); } function wrapCommandWithProgress(lsCommand : string, title : string, log? : vscode.OutputChannel, showOutput? : boolean, ...args : any[]) : Thenable { return window.withProgress({ location: ProgressLocation.Window }, p => { return new Promise(async (resolve, reject) => { - let c : LanguageClient = await client; + let c : LanguageClient = await globalVars.clientPromise.client; const commands = await vscode.commands.getCommands(); if (commands.includes(lsCommand)) { p.report({ message: title }); @@ -288,12 +186,12 @@ function wrapCommandWithProgress(lsCommand : string, title : string, log? : vsco const start = new Date().getTime(); try { if (log) { - handleLog(log, `starting ${lsCommand}`); + LOGGER.log(`starting ${lsCommand}`); } const res = await vscode.commands.executeCommand(lsCommand, ...args) const elapsed = new Date().getTime() - start; if (log) { - handleLog(log, `finished ${lsCommand} in ${elapsed} ms with result ${res}`); + LOGGER.log(`finished ${lsCommand} in ${elapsed} ms with result ${res}`); } const humanVisibleDelay = elapsed < 1000 ? 1000 : 0; setTimeout(() => { // set a timeout so user would still see the message when build time is short @@ -301,14 +199,14 @@ function wrapCommandWithProgress(lsCommand : string, title : string, log? : vsco resolve(res); } else { if (log) { - handleLog(log, `Command ${lsCommand} takes too long to start`); + LOGGER.log(`Command ${lsCommand} takes too long to start`, LogLevel.ERROR); } reject(res); } }, humanVisibleDelay); } catch (err: any) { if (log) { - handleLog(log, `command ${lsCommand} executed with error: ${JSON.stringify(err)}`); + LOGGER.log(`command ${lsCommand} executed with error: ${JSON.stringify(err)}`, LogLevel.ERROR); } } } else { @@ -318,72 +216,59 @@ function wrapCommandWithProgress(lsCommand : string, title : string, log? : vsco }); } -/** - * Just a simple promise subclass, so I can test for the 'initial promise' value: - * unlike all other promises, that must be fullfilled in order to e.g. properly stop the server or otherwise communicate with it, - * the initial one needs to be largely ignored in the launching/mgmt code, BUT should be available to normal commands / features. - */ -class InitialPromise extends Promise { - constructor(f : (resolve: (value: NbLanguageClient | PromiseLike) => void, reject: (reason?: any) => void) => void) { - super(f); - } -} - export function activate(context: ExtensionContext): VSNetBeansAPI { + globalVars.deactivated = false; + globalVars.clientPromise = new ClientPromise(); + globalVars.extensionInfo = new ExtensionInfo(context); + LOGGER = new ExtensionLogger(extConstants.SERVER_NAME); - deactivated=false; - let log = vscode.window.createOutputChannel(SERVER_NAME); - var clientResolve : (x : NbLanguageClient) => void; - var clientReject : (err : any) => void; - - // establish a waitable Promise, export the callbacks so they can be called after activation. - client = new InitialPromise((resolve, reject) => { - clientResolve = resolve; - clientReject = reject; - }); + globalVars.clientPromise.clientPromiseInitialization(); + context.subscriptions.push(workspace.onDidChangeConfiguration(configChangeListener)); + doActivateWithJDK(); // find acceptable JDK and launch the Java part - findJDK((specifiedJDK) => { - let currentClusters = findClusters(context.extensionPath).sort(); - const dsSorter = (a: TextDocumentFilter, b: TextDocumentFilter) => { - return (a.language || '').localeCompare(b.language || '') - || (a.pattern || '').localeCompare(b.pattern || '') - || (a.scheme || '').localeCompare(b.scheme || ''); - }; - let currentDocumentSelectors = collectDocumentSelectors().sort(dsSorter); - context.subscriptions.push(vscode.extensions.onDidChange(() => { - const newClusters = findClusters(context.extensionPath).sort(); - const newDocumentSelectors = collectDocumentSelectors().sort(dsSorter); - if (newClusters.length !== currentClusters.length || newDocumentSelectors.length !== currentDocumentSelectors.length - || newClusters.find((value, index) => value !== currentClusters[index]) || newDocumentSelectors.find((value, index) => value !== currentDocumentSelectors[index])) { - currentClusters = newClusters; - currentDocumentSelectors = newDocumentSelectors; - activateWithJDK(specifiedJDK, context, log, true, clientResolve, clientReject); - } - })); - activateWithJDK(specifiedJDK, context, log, true, clientResolve, clientReject); - }); + // findJDK((specifiedJDK) => { + // let currentClusters = findClusters(context.extensionPath).sort(); + // const dsSorter = (a: TextDocumentFilter, b: TextDocumentFilter) => { + // return (a.language || '').localeCompare(b.language || '') + // || (a.pattern || '').localeCompare(b.pattern || '') + // || (a.scheme || '').localeCompare(b.scheme || ''); + // }; + // let currentDocumentSelectors = collectDocumentSelectors().sort(dsSorter); + // context.subscriptions.push(vscode.extensions.onDidChange(() => { + // const newClusters = findClusters(context.extensionPath).sort(); + // const newDocumentSelectors = collectDocumentSelectors().sort(dsSorter); + // if (newClusters.length !== currentClusters.length || newDocumentSelectors.length !== currentDocumentSelectors.length + // || newClusters.find((value, index) => value !== currentClusters[index]) || newDocumentSelectors.find((value, index) => value !== currentDocumentSelectors[index])) { + // currentClusters = newClusters; + // currentDocumentSelectors = newDocumentSelectors; + // activateWithJDK(specifiedJDK, context, log, true, clientResolve, clientReject); + // } + // })); + // activateWithJDK(specifiedJDK, context, log, true, clientResolve, clientReject); + // }); + //register debugger: let debugTrackerFactory =new NetBeansDebugAdapterTrackerFactory(); - context.subscriptions.push(vscode.debug.registerDebugAdapterTrackerFactory(COMMAND_PREFIX, debugTrackerFactory)); + context.subscriptions.push(vscode.debug.registerDebugAdapterTrackerFactory(extConstants.COMMAND_PREFIX, debugTrackerFactory)); let configInitialProvider = new NetBeansConfigurationInitialProvider(); - context.subscriptions.push(vscode.debug.registerDebugConfigurationProvider(COMMAND_PREFIX, configInitialProvider, vscode.DebugConfigurationProviderTriggerKind.Initial)); + context.subscriptions.push(vscode.debug.registerDebugConfigurationProvider(extConstants.COMMAND_PREFIX, configInitialProvider, vscode.DebugConfigurationProviderTriggerKind.Initial)); let configDynamicProvider = new NetBeansConfigurationDynamicProvider(context); - context.subscriptions.push(vscode.debug.registerDebugConfigurationProvider(COMMAND_PREFIX, configDynamicProvider, vscode.DebugConfigurationProviderTriggerKind.Dynamic)); + context.subscriptions.push(vscode.debug.registerDebugConfigurationProvider(extConstants.COMMAND_PREFIX, configDynamicProvider, vscode.DebugConfigurationProviderTriggerKind.Dynamic)); let configResolver = new NetBeansConfigurationResolver(); - context.subscriptions.push(vscode.debug.registerDebugConfigurationProvider(COMMAND_PREFIX, configResolver)); + context.subscriptions.push(vscode.debug.registerDebugConfigurationProvider(extConstants.COMMAND_PREFIX, configResolver)); context.subscriptions.push(vscode.debug.onDidTerminateDebugSession(((session) => onDidTerminateSession(session)))); let debugDescriptionFactory = new NetBeansDebugAdapterDescriptionFactory(); - context.subscriptions.push(vscode.debug.registerDebugAdapterDescriptorFactory(COMMAND_PREFIX, debugDescriptionFactory)); + context.subscriptions.push(vscode.debug.registerDebugAdapterDescriptorFactory(extConstants.COMMAND_PREFIX, debugDescriptionFactory)); // initialize Run Configuration initializeRunConfiguration().then(initialized => { if (initialized) { - context.subscriptions.push(vscode.debug.registerDebugConfigurationProvider(COMMAND_PREFIX, runConfigurationProvider)); + context.subscriptions.push(vscode.debug.registerDebugConfigurationProvider(extConstants.COMMAND_PREFIX, runConfigurationProvider)); context.subscriptions.push(vscode.window.registerTreeDataProvider('run-config', runConfigurationNodeProvider)); - context.subscriptions.push(vscode.commands.registerCommand(COMMAND_PREFIX + '.workspace.configureRunSettings', (...params: any[]) => { + context.subscriptions.push(vscode.commands.registerCommand(extConstants.COMMAND_PREFIX + '.workspace.configureRunSettings', (...params: any[]) => { configureRunSettings(context, params); })); vscode.commands.executeCommand('setContext', 'runConfigurationInitialized', true); @@ -391,10 +276,10 @@ export function activate(context: ExtensionContext): VSNetBeansAPI { }); // register commands - context.subscriptions.push(commands.registerCommand(COMMAND_PREFIX + '.workspace.new', async (ctx, template) => { - let c : LanguageClient = await client; + context.subscriptions.push(commands.registerCommand(extConstants.COMMAND_PREFIX + '.workspace.new', async (ctx, template) => { + let c : LanguageClient = await globalVars.clientPromise.client; const commands = await vscode.commands.getCommands(); - if (commands.includes(COMMAND_PREFIX + '.new.from.template')) { + if (commands.includes(extConstants.COMMAND_PREFIX + '.new.from.template')) { const workspaces=workspace.workspaceFolders; if(!workspaces) { @@ -409,7 +294,7 @@ export function activate(context: ExtensionContext): VSNetBeansAPI { await fs.promises.mkdir(folderPath); } const folderPathUri = vscode.Uri.file(folderPath); - await vscode.commands.executeCommand(COMMAND_PREFIX + '.new.from.template', folderPathUri.toString()); + await vscode.commands.executeCommand(extConstants.COMMAND_PREFIX + '.new.from.template', folderPathUri.toString()); await vscode.commands.executeCommand(`vscode.openFolder`, folderPathUri); return; @@ -421,7 +306,7 @@ export function activate(context: ExtensionContext): VSNetBeansAPI { params.push(template); } params.push(contextUri(ctx)?.toString(), vscode.window.activeTextEditor?.document?.uri?.toString()); - const res = await vscode.commands.executeCommand(COMMAND_PREFIX + '.new.from.template', ...params); + const res = await vscode.commands.executeCommand(extConstants.COMMAND_PREFIX + '.new.from.template', ...params); if (typeof res === 'string') { let newFile = vscode.Uri.parse(res as string); @@ -438,11 +323,11 @@ export function activate(context: ExtensionContext): VSNetBeansAPI { throw l10n.value("jdk.extension.error_msg.doesntSupportNewTeamplate",{client:c}); } })); - context.subscriptions.push(commands.registerCommand(COMMAND_PREFIX + '.workspace.newproject', async (ctx) => { - let c : LanguageClient = await client; + context.subscriptions.push(commands.registerCommand(extConstants.COMMAND_PREFIX + '.workspace.newproject', async (ctx) => { + let c : LanguageClient = await globalVars.clientPromise.client; const commands = await vscode.commands.getCommands(); - if (commands.includes(COMMAND_PREFIX + '.new.project')) { - const res = await vscode.commands.executeCommand(COMMAND_PREFIX + '.new.project', contextUri(ctx)?.toString()); + if (commands.includes(extConstants.COMMAND_PREFIX + '.new.project')) { + const res = await vscode.commands.executeCommand(extConstants.COMMAND_PREFIX + '.new.project', contextUri(ctx)?.toString()); if (typeof res === 'string') { let newProject = vscode.Uri.parse(res as string); @@ -457,16 +342,16 @@ export function activate(context: ExtensionContext): VSNetBeansAPI { } } } else { - throw l10n.value("jdk.extension.error_msg.doesntSupportNewProject",{client:c}); + throw l10n.value("jdk.extenstion.error_msg.doesntSupportNewProject",{client: globalVars.clientPromise.client,c}); } })); - context.subscriptions.push(commands.registerCommand(COMMAND_PREFIX + '.open.test', async (ctx) => { - let c: LanguageClient = await client; + context.subscriptions.push(commands.registerCommand(extConstants.COMMAND_PREFIX + '.open.test', async (ctx) => { + let c: LanguageClient = await globalVars.clientPromise.client; const commands = await vscode.commands.getCommands(); - if (commands.includes(COMMAND_PREFIX + '.go.to.test')) { + if (commands.includes(extConstants.COMMAND_PREFIX + '.go.to.test')) { try { - const res: any = await vscode.commands.executeCommand(COMMAND_PREFIX + '.go.to.test', contextUri(ctx)?.toString()); + const res: any = await vscode.commands.executeCommand(extConstants.COMMAND_PREFIX + '.go.to.test', contextUri(ctx)?.toString()); if("errorMessage" in res){ throw new Error(res.errorMessage); } @@ -516,7 +401,7 @@ export function activate(context: ExtensionContext): VSNetBeansAPI { } })); - context.subscriptions.push(vscode.commands.registerCommand(COMMAND_PREFIX + ".delete.cache", async () => { + context.subscriptions.push(vscode.commands.registerCommand(extConstants.COMMAND_PREFIX + ".delete.cache", async () => { const storagePath = context.storageUri?.fsPath; if (!storagePath) { vscode.window.showErrorMessage(l10n.value("jdk.extension.cache.error_msg.cannotFindWrkSpacePath")); @@ -532,9 +417,9 @@ export function activate(context: ExtensionContext): VSNetBeansAPI { if (confirmation === yes) { const reloadWindowActionLabel = l10n.value("jdk.extension.cache.label.reloadWindow"); try { - await stopClient(client); - deactivated = true; - await killNbProcess(false, log); + await globalVars.clientPromise.stopClient(); + globalVars.deactivated = true; + await globalVars.nbProcessManager?.killProcess(false); await fs.promises.rmdir(userDir, { recursive: true }); await vscode.window.showInformationMessage(l10n.value("jdk.extension.message.cacheDeleted"), reloadWindowActionLabel); } catch (err) { @@ -549,39 +434,39 @@ export function activate(context: ExtensionContext): VSNetBeansAPI { })); - context.subscriptions.push(vscode.commands.registerCommand(COMMAND_PREFIX + ".download.jdk", async () => { - const jdkDownloaderView = new JdkDownloaderView(log); + context.subscriptions.push(vscode.commands.registerCommand(extConstants.COMMAND_PREFIX + ".download.jdk", async () => { + const jdkDownloaderView = new JdkDownloaderView(); jdkDownloaderView.createView(); })); - context.subscriptions.push(commands.registerCommand(COMMAND_PREFIX + '.workspace.compile', () => - wrapCommandWithProgress(COMMAND_PREFIX + '.build.workspace', l10n.value('jdk.extension.command.progress.compilingWorkSpace'), log, true) + context.subscriptions.push(commands.registerCommand(extConstants.COMMAND_PREFIX + '.workspace.compile', () => + wrapCommandWithProgress(extConstants.COMMAND_PREFIX + '.build.workspace', l10n.value('jdk.extension.command.progress.compilingWorkSpace'), LOGGER.getOutputChannel(), true) )); - context.subscriptions.push(commands.registerCommand(COMMAND_PREFIX + '.workspace.clean', () => - wrapCommandWithProgress(COMMAND_PREFIX + '.clean.workspace',l10n.value('jdk.extension.command.progress.cleaningWorkSpace'), log, true) + context.subscriptions.push(commands.registerCommand(extConstants.COMMAND_PREFIX + '.workspace.clean', () => + wrapCommandWithProgress(extConstants.COMMAND_PREFIX + '.clean.workspace',l10n.value('jdk.extension.command.progress.cleaningWorkSpace'), LOGGER.getOutputChannel(), true) )); - context.subscriptions.push(commands.registerCommand(COMMAND_PREFIX + '.project.compile', (args) => { - wrapProjectActionWithProgress('build', undefined, l10n.value('jdk.extension.command.progress.compilingProject'), log, true, args); + context.subscriptions.push(commands.registerCommand(extConstants.COMMAND_PREFIX + '.project.compile', (args) => { + wrapProjectActionWithProgress('build', undefined, l10n.value('jdk.extension.command.progress.compilingProject'), LOGGER.getOutputChannel(), true, args); })); - context.subscriptions.push(commands.registerCommand(COMMAND_PREFIX + '.project.clean', (args) => { - wrapProjectActionWithProgress('clean', undefined, l10n.value('jdk.extension.command.progress.cleaningProject'), log, true, args); + context.subscriptions.push(commands.registerCommand(extConstants.COMMAND_PREFIX + '.project.clean', (args) => { + wrapProjectActionWithProgress('clean', undefined, l10n.value('jdk.extension.command.progress.cleaningProject'), LOGGER.getOutputChannel(), true, args); })); - context.subscriptions.push(commands.registerCommand(COMMAND_PREFIX + '.open.type', () => { - wrapCommandWithProgress(COMMAND_PREFIX + '.quick.open', l10n.value('jdk.extension.command.progress.quickOpen'), log, true).then(() => { + context.subscriptions.push(commands.registerCommand(extConstants.COMMAND_PREFIX + '.open.type', () => { + wrapCommandWithProgress(extConstants.COMMAND_PREFIX + '.quick.open', l10n.value('jdk.extension.command.progress.quickOpen'), LOGGER.getOutputChannel(), true).then(() => { commands.executeCommand('workbench.action.focusActiveEditorGroup'); }); })); - context.subscriptions.push(commands.registerCommand(COMMAND_PREFIX + '.java.goto.super.implementation', async () => { - if (window.activeTextEditor?.document.languageId !== LANGUAGE_ID) { + context.subscriptions.push(commands.registerCommand(extConstants.COMMAND_PREFIX + '.java.goto.super.implementation', async () => { + if (window.activeTextEditor?.document.languageId !== extConstants.LANGUAGE_ID) { return; } const uri = window.activeTextEditor.document.uri; const position = window.activeTextEditor.selection.active; - const locations: any[] = await vscode.commands.executeCommand(COMMAND_PREFIX + '.java.super.implementation', uri.toString(), position) || []; + const locations: any[] = await vscode.commands.executeCommand(extConstants.COMMAND_PREFIX + '.java.super.implementation', uri.toString(), position) || []; return vscode.commands.executeCommand('editor.action.goToLocations', window.activeTextEditor.document.uri, position, locations.map(location => new vscode.Location(vscode.Uri.parse(location.uri), new vscode.Range(location.range.start.line, location.range.start.character, location.range.end.line, location.range.end.character))), 'peek', l10n.value('jdk.extension.error_msg.noSuperImpl')); })); - context.subscriptions.push(commands.registerCommand(COMMAND_PREFIX + '.rename.element.at', async (offset) => { + context.subscriptions.push(commands.registerCommand(extConstants.COMMAND_PREFIX + '.rename.element.at', async (offset) => { const editor = window.activeTextEditor; if (editor) { await commands.executeCommand('editor.action.rename', [ @@ -590,21 +475,21 @@ export function activate(context: ExtensionContext): VSNetBeansAPI { ]); } })); - context.subscriptions.push(commands.registerCommand(COMMAND_PREFIX + '.surround.with', async (items) => { + context.subscriptions.push(commands.registerCommand(extConstants.COMMAND_PREFIX + '.surround.with', async (items) => { const selected: any = await window.showQuickPick(items, { placeHolder: l10n.value('jdk.extension.command.quickPick.placeholder.surroundWith') }); if (selected) { if (selected.userData.edit) { - const edit = await (await client).protocol2CodeConverter.asWorkspaceEdit(selected.userData.edit as ls.WorkspaceEdit); + const edit = await (await globalVars.clientPromise.client).protocol2CodeConverter.asWorkspaceEdit(selected.userData.edit as ls.WorkspaceEdit); await workspace.applyEdit(edit); await commands.executeCommand('workbench.action.focusActiveEditorGroup'); } await commands.executeCommand(selected.userData.command.command, ...(selected.userData.command.arguments || [])); } })); - context.subscriptions.push(commands.registerCommand(COMMAND_PREFIX + '.generate.code', async (command, data) => { + context.subscriptions.push(commands.registerCommand(extConstants.COMMAND_PREFIX + '.generate.code', async (command, data) => { const edit: any = await commands.executeCommand(command, data); if (edit) { - const wsEdit = await (await client).protocol2CodeConverter.asWorkspaceEdit(edit as ls.WorkspaceEdit); + const wsEdit = await (await globalVars.clientPromise.client).protocol2CodeConverter.asWorkspaceEdit(edit as ls.WorkspaceEdit); await workspace.applyEdit(wsEdit); await commands.executeCommand('workbench.action.focusActiveEditorGroup'); } @@ -617,7 +502,7 @@ export function activate(context: ExtensionContext): VSNetBeansAPI { if (!Array.isArray(c)) { return undefined; } - let f = c.filter((v) => v['type'] === COMMAND_PREFIX); + let f = c.filter((v) => v['type'] === extConstants.COMMAND_PREFIX); if (!f.length) { return undefined; } @@ -630,10 +515,10 @@ export function activate(context: ExtensionContext): VSNetBeansAPI { } } let provider = new P(); - let d = vscode.debug.registerDebugConfigurationProvider(COMMAND_PREFIX, provider); + let d = vscode.debug.registerDebugConfigurationProvider(extConstants.COMMAND_PREFIX, provider); // let vscode to select a debug config return await vscode.commands.executeCommand('workbench.action.debug.start', { config: { - type: COMMAND_PREFIX, + type: extConstants.COMMAND_PREFIX, mainClass: uri.toString() }, noDebug: true}).then((v) => { d.dispose(); @@ -649,7 +534,7 @@ export function activate(context: ExtensionContext): VSNetBeansAPI { if (docUri) { // attempt to find the active configuration in the vsode launch settings; undefined if no config is there. let debugConfig : vscode.DebugConfiguration = await findRunConfiguration(docUri) || { - type: COMMAND_PREFIX, + type: extConstants.COMMAND_PREFIX, name: "Java Single Debug", request: "launch" }; @@ -686,32 +571,32 @@ export function activate(context: ExtensionContext): VSNetBeansAPI { } }; - context.subscriptions.push(commands.registerCommand(COMMAND_PREFIX + '.run.test', async (uri, methodName?, launchConfiguration?) => { + context.subscriptions.push(commands.registerCommand(extConstants.COMMAND_PREFIX + '.run.test', async (uri, methodName?, launchConfiguration?) => { await runDebug(true, true, uri, methodName, launchConfiguration); })); - context.subscriptions.push(commands.registerCommand(COMMAND_PREFIX + '.debug.test', async (uri, methodName?, launchConfiguration?) => { + context.subscriptions.push(commands.registerCommand(extConstants.COMMAND_PREFIX + '.debug.test', async (uri, methodName?, launchConfiguration?) => { await runDebug(false, true, uri, methodName, launchConfiguration); })); - context.subscriptions.push(commands.registerCommand(COMMAND_PREFIX + '.run.single', async (uri, methodName?, launchConfiguration?) => { + context.subscriptions.push(commands.registerCommand(extConstants.COMMAND_PREFIX + '.run.single', async (uri, methodName?, launchConfiguration?) => { await runDebug(true, false, uri, methodName, launchConfiguration); })); - context.subscriptions.push(commands.registerCommand(COMMAND_PREFIX + '.debug.single', async (uri, methodName?, launchConfiguration?) => { + context.subscriptions.push(commands.registerCommand(extConstants.COMMAND_PREFIX + '.debug.single', async (uri, methodName?, launchConfiguration?) => { await runDebug(false, false, uri, methodName, launchConfiguration); })); - context.subscriptions.push(commands.registerCommand(COMMAND_PREFIX + '.project.run', async (node, launchConfiguration?) => { + context.subscriptions.push(commands.registerCommand(extConstants.COMMAND_PREFIX + '.project.run', async (node, launchConfiguration?) => { return runDebug(true, false, contextUri(node)?.toString() || '', undefined, launchConfiguration, true); })); - context.subscriptions.push(commands.registerCommand(COMMAND_PREFIX + '.project.debug', async (node, launchConfiguration?) => { + context.subscriptions.push(commands.registerCommand(extConstants.COMMAND_PREFIX + '.project.debug', async (node, launchConfiguration?) => { return runDebug(false, false, contextUri(node)?.toString() || '', undefined, launchConfiguration, true); })); - context.subscriptions.push(commands.registerCommand(COMMAND_PREFIX + '.project.test', async (node, launchConfiguration?) => { + context.subscriptions.push(commands.registerCommand(extConstants.COMMAND_PREFIX + '.project.test', async (node, launchConfiguration?) => { return runDebug(true, true, contextUri(node)?.toString() || '', undefined, launchConfiguration, true); })); - context.subscriptions.push(commands.registerCommand(COMMAND_PREFIX + '.package.test', async (uri, launchConfiguration?) => { + context.subscriptions.push(commands.registerCommand(extConstants.COMMAND_PREFIX + '.package.test', async (uri, launchConfiguration?) => { await runDebug(true, true, uri, undefined, launchConfiguration); })); - context.subscriptions.push(commands.registerCommand(COMMAND_PREFIX + '.open.stacktrace', async (uri, methodName, fileName, line) => { - const location: string | undefined = uri ? await commands.executeCommand(COMMAND_PREFIX + '.resolve.stacktrace.location', uri, methodName, fileName) : undefined; + context.subscriptions.push(commands.registerCommand(extConstants.COMMAND_PREFIX + '.open.stacktrace', async (uri, methodName, fileName, line) => { + const location: string | undefined = uri ? await commands.executeCommand(extConstants.COMMAND_PREFIX + '.resolve.stacktrace.location', uri, methodName, fileName) : undefined; if (location) { const lNum = line - 1; window.showTextDocument(vscode.Uri.parse(location), { selection: new vscode.Range(new vscode.Position(lNum, 0), new vscode.Position(lNum, 0)) }); @@ -722,21 +607,21 @@ export function activate(context: ExtensionContext): VSNetBeansAPI { } } })); - context.subscriptions.push(commands.registerCommand(COMMAND_PREFIX + '.workspace.symbols', async (query) => { - const c = await client; + context.subscriptions.push(commands.registerCommand(extConstants.COMMAND_PREFIX + '.workspace.symbols', async (query) => { + const c = await globalVars.clientPromise.client; return (await c.sendRequest("workspace/symbol", { "query": query })) ?? []; })); - context.subscriptions.push(commands.registerCommand(COMMAND_PREFIX + '.java.complete.abstract.methods', async () => { + context.subscriptions.push(commands.registerCommand(extConstants.COMMAND_PREFIX + '.java.complete.abstract.methods', async () => { const active = vscode.window.activeTextEditor; if (active) { const position = new vscode.Position(active.selection.start.line, active.selection.start.character); - await commands.executeCommand(COMMAND_PREFIX + '.java.implement.all.abstract.methods', active.document.uri.toString(), position); + await commands.executeCommand(extConstants.COMMAND_PREFIX + '.java.implement.all.abstract.methods', active.document.uri.toString(), position); } })); - context.subscriptions.push(commands.registerCommand(COMMAND_PREFIX + '.startup.condition', async () => { - return client; + context.subscriptions.push(commands.registerCommand(extConstants.COMMAND_PREFIX + '.startup.condition', async () => { + return globalVars.clientPromise.client; })); - context.subscriptions.push(commands.registerCommand(COMMAND_PREFIX + '.addEventListener', (eventName, listener) => { + context.subscriptions.push(commands.registerCommand(extConstants.COMMAND_PREFIX + '.addEventListener', (eventName, listener) => { let ls = listeners.get(eventName); if (!ls) { ls = []; @@ -744,12 +629,12 @@ export function activate(context: ExtensionContext): VSNetBeansAPI { } ls.push(listener); })); - context.subscriptions.push(commands.registerCommand(COMMAND_PREFIX + '.node.properties.edit', - async (node) => await PropertiesView.createOrShow(context, node, (await client).findTreeViewService()))); + context.subscriptions.push(commands.registerCommand(extConstants.COMMAND_PREFIX + '.node.properties.edit', + async (node) => await PropertiesView.createOrShow(context, node, (await globalVars.clientPromise.client).findTreeViewService()))); const archiveFileProvider = { provideTextDocumentContent: async (uri: vscode.Uri, token: vscode.CancellationToken): Promise => { - return await commands.executeCommand(COMMAND_PREFIX + '.get.archive.file.content', uri.toString()); + return await commands.executeCommand(extConstants.COMMAND_PREFIX + '.get.archive.file.content', uri.toString()); } }; context.subscriptions.push(workspace.registerTextDocumentContentProvider('jar', archiveFileProvider)); @@ -760,420 +645,41 @@ export function activate(context: ExtensionContext): VSNetBeansAPI { // register completions: launchConfigurations.registerCompletion(context); return Object.freeze({ - version : API_VERSION, - apiVersion : API_VERSION - }); -} - -/** - * Pending maintenance (install) task, activations should be chained after it. - */ -let maintenance : Promise | null; - -/** - * Pending activation flag. Will be cleared when the process produces some message or fails. - */ -let activationPending : boolean = false; - -function activateWithJDK(specifiedJDK: string | null, context: ExtensionContext, log : vscode.OutputChannel, notifyKill: boolean, - clientResolve? : (x : NbLanguageClient) => void, clientReject? : (x : any) => void): void { - if (activationPending) { - // do not activate more than once in parallel. - handleLog(log, "Server activation requested repeatedly, ignoring..."); - return; - } - let oldClient = client; - let setClient : [(c : NbLanguageClient) => void, (err : any) => void]; - client = new Promise((clientOK, clientErr) => { - setClient = [ - function (c : NbLanguageClient) { - clientOK(c); - if (clientResolve) { - clientResolve(c); - } - }, function (err) { - clientErr(err); - if (clientReject) { - clientReject(err); - } - } - ] - //setClient = [ clientOK, clientErr ]; + version : extConstants.API_VERSION, + apiVersion : extConstants.API_VERSION }); - const a : Promise | null = maintenance; - - commands.executeCommand('setContext', 'nbJdkReady', false); - activationPending = true; - // chain the restart after termination of the former process. - if (a != null) { - handleLog(log, "Server activation initiated while in maintenance mode, scheduling after maintenance"); - a.then(() => stopClient(oldClient)).then(() => killNbProcess(notifyKill, log)).then(() => { - doActivateWithJDK(specifiedJDK, context, log, notifyKill, setClient); - }); - } else { - handleLog(log, "Initiating server activation"); - stopClient(oldClient).then(() => killNbProcess(notifyKill, log)).then(() => { - doActivateWithJDK(specifiedJDK, context, log, notifyKill, setClient); - }); - } -} - - -function killNbProcess(notifyKill : boolean, log : vscode.OutputChannel, specProcess?: ChildProcess) : Promise { - const p = nbProcess; - handleLog(log, "Request to kill LSP server."); - if (p && (!specProcess || specProcess == p)) { - if (notifyKill) { - vscode.window.setStatusBarMessage(l10n.value("jdk.extension.command.statusBar.message.restartingServer",{SERVER_NAME:SERVER_NAME}), 2000); - } - return new Promise((resolve, reject) => { - nbProcess = null; - p.on('close', function(code: number) { - handleLog(log, "LSP server closed: " + p.pid) - resolve(); - }); - handleLog(log, "Killing LSP server " + p.pid); - if (!p.kill()) { - reject("Cannot kill"); - } - }); - } else { - let msg = "Cannot kill: "; - if (specProcess) { - msg += "Requested kill on " + specProcess.pid + ", "; - } - handleLog(log, msg + "current process is " + (p ? p.pid : "None")); - return new Promise((res, rej) => { res(); }); - } } -/** - * Attempts to determine if the Workbench is using dark-style color theme, so that NBLS - * starts with some dark L&F for icon resource selection. - */ -function isDarkColorTheme() : boolean { - const themeName = workspace.getConfiguration('workbench')?.get('colorTheme'); - if (!themeName) { - return false; - } - for (const ext of vscode.extensions.all) { - const themeList : object[] = ext.packageJSON?.contributes && ext.packageJSON?.contributes['themes']; - if (!themeList) { - continue; - } - let t : any; - for (t of themeList) { - if (t.id !== themeName) { - continue; - } - const uiTheme = t.uiTheme; - if (typeof(uiTheme) == 'string') { - if (uiTheme.includes('-dark') || uiTheme.includes('-black')) { - return true; - } - } - } - } - return false; -} - -function isNbJavacDisabled() : boolean { - return workspace.getConfiguration(COMMAND_PREFIX)?.get('advanced.disable.nbjavac') as boolean; -} - -function getProjectJDKHome() : string { - return workspace.getConfiguration(COMMAND_PREFIX)?.get('project.jdkhome') as string; -} - -function doActivateWithJDK(specifiedJDK: string | null, context: ExtensionContext, log : vscode.OutputChannel, notifyKill: boolean, - setClient : [(c : NbLanguageClient) => void, (err : any) => void] -): void { - maintenance = null; - let restartWithJDKLater : ((time: number, n: boolean) => void) = function restartLater(time: number, n : boolean) { - handleLog(log, `Restart of ${SERVER_NAME} requested in ${(time / 1000)} s.`); - setTimeout(() => { - activateWithJDK(specifiedJDK, context, log, n); - }, time); - }; - - const netbeansConfig = workspace.getConfiguration(COMMAND_PREFIX); - const beVerbose : boolean = netbeansConfig.get('verbose', false); - let userdir = process.env['nbcode_userdir'] || netbeansConfig.get('userdir', 'local'); - switch (userdir) { - case 'local': - if (context.storagePath) { - userdir = context.storagePath; - break; - } - // fallthru - case 'global': - userdir = context.globalStoragePath; - break; - default: - // assume storage is path on disk - } - - let disableModules : string[] = []; - let enableModules : string[] = []; - if (isNbJavacDisabled()) { - disableModules.push('org.netbeans.libs.nbjavacapi'); - } else { - enableModules.push('org.netbeans.libs.nbjavacapi'); - } - - let projectSearchRoots:string = ''; - const isProjectFolderSearchLimited : boolean = !netbeansConfig.get('advanced.disable.projectSearchLimit', false); - if (isProjectFolderSearchLimited) { - try { - projectSearchRoots = os.homedir() as string; - } catch (err:any) { - handleLog(log, `Failed to obtain the user home directory due to: ${err}`); - } - if (!projectSearchRoots) { - projectSearchRoots = os.type() === NODE_WINDOWS_LABEL ? '%USERPROFILE%' : '$HOME'; // The launcher script may perform the env variable substitution - handleLog(log, `Using userHomeDir = "${projectSearchRoots}" as the launcher script may perform env var substitution to get its value.`); - } - const workspaces = workspace.workspaceFolders; - if (workspaces) { - workspaces.forEach(workspace => { - if (workspace.uri) { - try { - projectSearchRoots = projectSearchRoots + path.delimiter + path.normalize(workspace.uri.fsPath); - } catch (err:any) { - handleLog(log, `Failed to get the workspace path: ${err}`); - } - } - }); - } - } - - let info = { - clusters : findClusters(context.extensionPath), - extensionPath: context.extensionPath, - storagePath : userdir, - jdkHome : specifiedJDK, - projectSearchRoots: projectSearchRoots, - verbose: beVerbose, - disableModules : disableModules, - enableModules : enableModules, - }; - - const requiredJdk = specifiedJDK ? specifiedJDK : 'default system JDK'; - let launchMsg = l10n.value("jdk.extension.lspServer.statusBar.message.launching",{ - SERVER_NAME:SERVER_NAME, - requiredJdk:requiredJdk, - userdir:userdir - }); - handleLog(log, launchMsg); - vscode.window.setStatusBarMessage(launchMsg, 2000); - - let ideRunning = new Promise((resolve, reject) => { - let stdOut : string | null = ''; - let stdErr : string | null = ''; - function logAndWaitForEnabled(text: string, isOut: boolean) { - if (p == nbProcess) { - activationPending = false; - } - handleLogNoNL(log, text); - if (stdOut == null) { - return; - } - if (isOut) { - stdOut += text; - } else { - stdErr += text; - } - if (stdOut.match(/org.netbeans.modules.java.lsp.server/)) { - resolve(text); - stdOut = null; - } - } - let extras : string[] = ["--modules", "--list", "-J-XX:PerfMaxStringConstLength=10240","--locale",l10n.nbLocaleCode()]; - if (isDarkColorTheme()) { - extras.push('--laf', 'com.formdev.flatlaf.FlatDarkLaf'); - } - let serverVmOptions: string[] = workspace.getConfiguration(COMMAND_PREFIX).get("serverVmOptions",[]); - extras.push(...serverVmOptions.map(el => `-J${el}`)); - let p = launcher.launch(info, ...extras); - handleLog(log, "LSP server launching: " + p.pid); - handleLog(log, "LSP server user directory: " + userdir); - p.stdout.on('data', function(d: any) { - logAndWaitForEnabled(d.toString(), true); - }); - p.stderr.on('data', function(d: any) { - logAndWaitForEnabled(d.toString(), false); - }); - nbProcess = p; - p.on('close', function(code: number) { - if (p == nbProcess) { - nbProcess = null; - } - if (p == nbProcess && code != 0 && code) { - vscode.window.showWarningMessage(l10n.value("jdk.extension.lspServer.warning_message.serverExited",{SERVER_NAME:SERVER_NAME,code:code})); - } - if (stdErr?.match(/Cannot find java/) || (os.type() === NODE_WINDOWS_LABEL && !deactivated) ) { - const downloadAndSetupActionLabel = l10n.value("jdk.extension.lspServer.label.downloadAndSetup") - vscode.window.showInformationMessage( - l10n.value("jdk.extension.lspServer.message.noJdkFound"), - downloadAndSetupActionLabel - ).then(selection => { - if (selection === downloadAndSetupActionLabel) { - commands.executeCommand(`${COMMAND_PREFIX}.download.jdk`); - } - }); - } - if (stdOut != null) { - let match = stdOut.match(/org.netbeans.modules.java.lsp.server[^\n]*/) - if (match?.length == 1) { - handleLog(log, match[0]); - } else { - handleLog(log, "Cannot find org.netbeans.modules.java.lsp.server in the log!"); - } - handleLog(log, `Please refer to troubleshooting section for more info: https://github.com/oracle/javavscode/blob/main/README.md#troubleshooting`); - log.show(false); - killNbProcess(false, log, p); - reject(l10n.value("jdk.extension.error_msg.notEnabled",{SERVER_NAME})); - } else { - handleLog(log, "LSP server " + p.pid + " terminated with " + code); - handleLog(log, "Exit code " + code); - } - }); - }); - - ideRunning.then(() => { - const connection = () => new Promise((resolve, reject) => { - const srv = launcher.launch(info, - `--start-java-language-server=listen-hash:0`, - `--start-java-debug-adapter-server=listen-hash:0` - ); - if (!srv) { - reject(); - } else { - if (!srv.stdout) { - reject(`No stdout to parse!`); - srv.disconnect(); - return; - } - debugPort = -1; - var lspServerStarted = false; - srv.stdout.on("data", (chunk) => { - if (debugPort < 0) { - const info = chunk.toString().match(/Debug Server Adapter listening at port (\d*) with hash (.*)\n/); - if (info) { - debugPort = info[1]; - debugHash = info[2]; - } - } - if (!lspServerStarted) { - const info = chunk.toString().match(/Java Language Server listening at port (\d*) with hash (.*)\n/); - if (info) { - const port : number = info[1]; - const server = net.connect(port, "127.0.0.1", () => { - server.write(info[2]); - resolve({ - reader: server, - writer: server - }); - }); - lspServerStarted = true; - } - } - }); - srv.once("error", (err) => { - reject(err); - }); - } - }); - const conf = workspace.getConfiguration(); - let documentSelectors : DocumentSelector = [ - { language: LANGUAGE_ID }, - { language: 'yaml', pattern: '**/{application,bootstrap}*.yml' }, - { language: 'properties', pattern: '**/{application,bootstrap}*.properties' }, - { language: 'jackpot-hint' }, - { language: 'xml', pattern: '**/pom.xml' }, - { pattern: '**/build.gradle'} - ]; - documentSelectors.push(...collectDocumentSelectors()); - // Options to control the language client - let clientOptions: LanguageClientOptions = { - // Register the server for java documents - documentSelector: documentSelectors, - synchronize: { - configurationSection: [ - COMMAND_PREFIX + '.hints', - COMMAND_PREFIX + '.format', - COMMAND_PREFIX + '.java.imports', - COMMAND_PREFIX + '.project.jdkhome', - COMMAND_PREFIX + '.runConfig.vmOptions', - COMMAND_PREFIX + '.runConfig.cwd' - ], - fileEvents: [ - workspace.createFileSystemWatcher('**/*.java') - ] - }, - outputChannel: log, - revealOutputChannelOn: RevealOutputChannelOn.Never, - progressOnInitialization: true, - initializationOptions : { - 'nbcodeCapabilities' : { - 'statusBarMessageSupport' : true, - 'testResultsSupport' : true, - 'showHtmlPageSupport' : true, - 'wantsJavaSupport' : true, - 'wantsGroovySupport' : false, - 'commandPrefix': COMMAND_PREFIX, - 'configurationPrefix': 'jdk.', - 'altConfigurationPrefix': 'jdk.' - } - }, - errorHandler: { - error : function(error: Error, _message: Message, count: number): ErrorHandlerResult { - return { action: ErrorAction.Continue, message: error.message }; - }, - closed : function(): CloseHandlerResult { - handleLog(log, `Connection to ${SERVER_NAME} closed.`); - if (!activationPending) { - restartWithJDKLater(10000, false); - } - return { action: CloseAction.DoNotRestart }; - } - } - } - - - let c = new NbLanguageClient( - NB_LANGUAGE_CLIENT_ID, - 'Oracle Java SE', - connection, - log, - clientOptions - ); - handleLog(log, 'Language Client: Starting'); +function doActivateWithJDK(): void { + const connection: () => Promise = initializeServer(); + const c = NbLanguageClient.build(connection, LOGGER); + + LOGGER.log('Language Client: Starting'); c.start().then(() => { - testAdapter = new NbTestAdapter(); + globalVars.testAdapter = new NbTestAdapter(); c.onNotification(StatusMessageRequest.type, showStatusBarMessage); c.onRequest(HtmlPageRequest.type, showHtmlPage); c.onRequest(ExecInHtmlPageRequest.type, execInHtmlPage); - c.onNotification(LogMessageNotification.type, (param) => handleLog(log, param.message)); + c.onNotification(LogMessageNotification.type, (param) => LOGGER.log(param.message)); c.onRequest(QuickPickRequest.type, async param => { const selected = await window.showQuickPick(param.items, { title: param.title, placeHolder: param.placeHolder, canPickMany: param.canPickMany, ignoreFocusOut: true }); return selected ? Array.isArray(selected) ? selected : [selected] : undefined; }); c.onRequest(UpdateConfigurationRequest.type, async (param) => { - handleLog(log, "Received config update: " + param.section + "." + param.key + "=" + param.value); + LOGGER.log("Received config update: " + param.section + "." + param.key + "=" + param.value); let wsFile: vscode.Uri | undefined = vscode.workspace.workspaceFile; let wsConfig: vscode.WorkspaceConfiguration = vscode.workspace.getConfiguration(param.section); if (wsConfig) { try { wsConfig.update(param.key, param.value, wsFile ? null : true) .then(() => { - handleLog(log, "Updated configuration: " + param.section + "." + param.key + "=" + param.value + "; in: " + (wsFile ? wsFile.toString() : "Global")); + LOGGER.log("Updated configuration: " + param.section + "." + param.key + "=" + param.value + "; in: " + (wsFile ? wsFile.toString() : "Global")); }) .then(() => { runConfigurationUpdateAll(); }); } catch (err) { - handleLog(log, "Failed to update configuration. Reason: " + (typeof err === "string" ? err : err instanceof Error ? err.message : "error")); + LOGGER.log("Failed to update configuration. Reason: " + (typeof err === "string" ? err : err instanceof Error ? err.message : "error"), LogLevel.ERROR); } } }); @@ -1234,8 +740,8 @@ function doActivateWithJDK(specifiedJDK: string | null, context: ExtensionContex return data; }); c.onNotification(TestProgressNotification.type, param => { - if (testAdapter) { - testAdapter.testProgress(param.suite); + if (globalVars.testAdapter) { + globalVars.testAdapter.testProgress(param.suite); } }); let decorations = new Map(); @@ -1268,7 +774,7 @@ function doActivateWithJDK(specifiedJDK: string | null, context: ExtensionContex } }); }); - context.subscriptions.push(disposableListener); + globalVars.extensionInfo.pushSubscription(disposableListener); c.onNotification(TextEditorDecorationDisposeNotification.type, param => { let decorationType = decorations.get(param); if (decorationType) { @@ -1289,21 +795,16 @@ function doActivateWithJDK(specifiedJDK: string | null, context: ExtensionContex } } }); - handleLog(log, 'Language Client: Ready'); - setClient[0](c); + LOGGER.log('Language Client: Ready'); + globalVars.clientPromise.setClient[0](c); commands.executeCommand('setContext', 'nbJdkReady', true); // create project explorer: //c.findTreeViewService().createView('foundProjects', 'Projects', { canSelectMany : false }); - createProjectView(context, c); - }).catch(setClient[1]); - }).catch((reason) => { - activationPending = false; - handleLog(log, reason); - window.showErrorMessage(l10n.value("jdk.extension.lspServer.error_message",{reason:reason})); - }); - - async function createProjectView(ctx : ExtensionContext, client : NbLanguageClient) { + createProjectView(c); + }).catch(globalVars.clientPromise.setClient[1]); +} + async function createProjectView(client : NbLanguageClient) { const ts : TreeViewService = client.findTreeViewService(); let tv : vscode.TreeView = await ts.createView('foundProjects', 'Projects', { canSelectMany : false }); @@ -1321,14 +822,13 @@ function doActivateWithJDK(specifiedJDK: string | null, context: ExtensionContex } tv.reveal(vis, { select : true, focus : false, expand : false }); } - - ctx.subscriptions.push(window.onDidChangeActiveTextEditor(ed => { - const netbeansConfig = workspace.getConfiguration(COMMAND_PREFIX); + const netbeansConfig = workspace.getConfiguration(extConstants.COMMAND_PREFIX); + globalVars.extensionInfo.pushSubscription(window.onDidChangeActiveTextEditor(ed => { if (netbeansConfig.get("revealActiveInProjects")) { revealActiveEditor(ed); } })); - ctx.subscriptions.push(vscode.commands.registerCommand(COMMAND_PREFIX + ".select.editor.projects", () => revealActiveEditor())); + globalVars.extensionInfo.pushSubscription(vscode.commands.registerCommand(extConstants.COMMAND_PREFIX + ".select.editor.projects", () => revealActiveEditor())); // attempt to reveal NOW: if (netbeansConfig.get("revealActiveInProjects")) { @@ -1343,11 +843,11 @@ function doActivateWithJDK(specifiedJDK: string | null, context: ExtensionContex let data = params.text; const match = /(.*)<\/title>/i.exec(data); const name = match && match.length > 1 ? match[1] : ''; - const resourceDir = vscode.Uri.joinPath(context.globalStorageUri, params.id); + const resourceDir = vscode.Uri.joinPath(globalVars.extensionInfo.getGlobalStorage(), params.id); workspace.fs.createDirectory(resourceDir); let view = vscode.window.createWebviewPanel('htmlView', name, vscode.ViewColumn.Beside, { enableScripts: true, - localResourceRoots: [resourceDir, vscode.Uri.joinPath(context.extensionUri, 'node_modules', '@vscode/codicons', 'dist')] + localResourceRoots: [resourceDir, vscode.Uri.joinPath(globalVars.extensionInfo.getExtensionStorageUri(), 'node_modules', '@vscode/codicons', 'dist')] }); webviews.set(params.id, view.webview); const resources = params.resources; @@ -1359,7 +859,7 @@ function doActivateWithJDK(specifiedJDK: string | null, context: ExtensionContex data = data.replace('href="' + resourceName + '"', 'href="' + view.webview.asWebviewUri(resourceUri) + '"'); } } - const codiconsUri = view.webview.asWebviewUri(vscode.Uri.joinPath(context.extensionUri, 'node_modules', '@vscode/codicons', 'dist', 'codicon.css')); + const codiconsUri = view.webview.asWebviewUri(vscode.Uri.joinPath(globalVars.extensionInfo.getExtensionStorageUri(), 'node_modules', '@vscode/codicons', 'dist', 'codicon.css')); view.webview.html = data.replace('href="codicon.css"', 'href="' + codiconsUri + '"'); view.webview.onDidReceiveMessage(message => { switch (message.command) { @@ -1368,7 +868,7 @@ function doActivateWithJDK(specifiedJDK: string | null, context: ExtensionContex view.dispose(); break; case 'command': - vscode.commands.executeCommand(COMMAND_PREFIX + '.htmlui.process.command', message.data); + vscode.commands.executeCommand(extConstants.COMMAND_PREFIX + '.htmlui.process.command', message.data); break; } }); @@ -1421,94 +921,40 @@ function doActivateWithJDK(specifiedJDK: string | null, context: ExtensionContex } } - function checkInstallNbJavac(msg : string) { - const NO_JAVA_SUPPORT = "Cannot initialize Java support"; - if (msg.startsWith(NO_JAVA_SUPPORT)) { - if (isNbJavacDisabled()) { - const message = l10n.value("jdk.extension.nbjavac.message.supportedVersionRequired"); - const enable = l10n.value("jdk.extension.nbjavac.label.enableNbjavac"); - const settings = l10n.value("jdk.extension.nbjavac.label.openSettings"); - window.showErrorMessage(message, enable, settings).then(reply => { - if (enable === reply) { - workspace.getConfiguration().update(COMMAND_PREFIX + '.advanced.disable.nbjavac', false); - } else if (settings === reply) { - vscode.commands.executeCommand('workbench.action.openSettings', COMMAND_PREFIX + '.jdkhome'); - } - }); - } else { - const yes = l10n.value("jdk.extension.javaSupport.label.installGpl"); - window.showErrorMessage(l10n.value("jdk.extension.javaSupport.message.needAdditionalSupport"), yes).then(reply => { - if (yes === reply) { - vscode.window.setStatusBarMessage(`Preparing ${SERVER_NAME} for additional installation`, 2000); - restartWithJDKLater = function() { - handleLog(log, `Ignoring request for restart of ${SERVER_NAME}`); - }; - maintenance = new Promise((resolve, reject) => { - const kill : Promise<void> = killNbProcess(false, log); - kill.then(() => { - let installProcess = launcher.launch(info, "-J-Dnetbeans.close=true", "--modules", "--install", ".*nbjavac.*"); - handleLog(log, "Launching installation process: " + installProcess.pid); - let logData = function(d: any) { - handleLogNoNL(log, d.toString()); - }; - installProcess.stdout.on('data', logData); - installProcess.stderr.on('data', logData); - installProcess.addListener("error", reject); - // MUST wait on 'close', since stdout is inherited by children. The installProcess dies but - // the inherited stream will be closed by the last child dying. - installProcess.on('close', function(code: number) { - handleLog(log, "Installation completed: " + installProcess.pid); - handleLog(log, "Additional Java Support installed with exit code " + code); - // will be actually run after maintenance is resolve()d. - activateWithJDK(specifiedJDK, context, log, notifyKill) - resolve(); - }); - return installProcess; - }); - }); - } - }); - } +function checkInstallNbJavac(msg: string) { + const NO_JAVA_SUPPORT = "Cannot initialize Java support"; + if (msg.startsWith(NO_JAVA_SUPPORT)) { + if (isNbJavacDisabledHandler()) { + const message = l10n.value("jdk.extension.nbjavac.message.supportedVersionRequired"); + const enable = l10n.value("jdk.extension.nbjavac.label.enableNbjavac"); + const settings = l10n.value("jdk.extension.nbjavac.label.openSettings"); + window.showErrorMessage(message, enable, settings).then(reply => { + if (enable === reply) { + workspace.getConfiguration().update(extConstants.COMMAND_PREFIX + '.advanced.disable.nbjavac', false); + } else if (settings === reply) { + vscode.commands.executeCommand('workbench.action.openSettings', extConstants.COMMAND_PREFIX + '.jdkhome'); + } + }); } } } -function stopClient(clientPromise: Promise<LanguageClient>): Thenable<void> { - if (testAdapter) { - testAdapter.dispose(); - testAdapter = undefined; - } - return clientPromise && !(clientPromise instanceof InitialPromise) ? clientPromise.then(c => c.stop()) : Promise.resolve(); -} export function deactivate(): Thenable<void> { - if (nbProcess != null) { - nbProcess.kill(); + if (globalVars.nbProcessManager?.getProcess() != null) { + globalVars.nbProcessManager?.getProcess()?.kill(); } - return stopClient(client); + return globalVars.clientPromise.stopClient(); } -function collectDocumentSelectors(): TextDocumentFilter[] { - const selectors = []; - for (const extension of vscode.extensions.all) { - const contributesSection = extension.packageJSON['contributes']; - if (contributesSection) { - const documentSelectors = contributesSection['netbeans.documentSelectors']; - if (Array.isArray(documentSelectors) && documentSelectors.length) { - selectors.push(...documentSelectors); - } - } - } - return selectors; -} class NetBeansDebugAdapterTrackerFactory implements vscode.DebugAdapterTrackerFactory { createDebugAdapterTracker(_session: vscode.DebugSession): vscode.ProviderResult<vscode.DebugAdapterTracker> { return { onDidSendMessage(message: any): void { - if (testAdapter && message.type === 'event' && message.event === 'output') { - testAdapter.testOutput(message.body.output); + if (globalVars.testAdapter && message.type === 'event' && message.event === 'output') { + globalVars.testAdapter.testOutput(message.body.output); } } } @@ -1521,7 +967,7 @@ class NetBeansDebugAdapterDescriptionFactory implements vscode.DebugAdapterDescr return new Promise<vscode.DebugAdapterDescriptor>((resolve, reject) => { let cnt = 10; const fnc = () => { - if (debugPort < 0) { + if (globalVars.debugPort < 0) { if (cnt-- > 0) { setTimeout(fnc, 1000); } else { @@ -1529,10 +975,10 @@ class NetBeansDebugAdapterDescriptionFactory implements vscode.DebugAdapterDescr } } else { // resolve(new vscode.DebugAdapterServer(debugPort)); - const socket = net.connect(debugPort, "127.0.0.1", () => {}); + const socket = net.connect(globalVars.debugPort, "127.0.0.1", () => {}); socket.on("connect", () => { const adapter = new StreamDebugAdapter(); - socket.write(debugHash ? debugHash : ""); + socket.write(globalVars.debugHash ? globalVars.debugHash : ""); adapter.connect(socket, socket); resolve(new vscode.DebugAdapterInlineImplementation(adapter)); }); @@ -1551,7 +997,7 @@ class NetBeansConfigurationInitialProvider implements vscode.DebugConfigurationP } async doProvideDebugConfigurations(folder: vscode.WorkspaceFolder | undefined, _token?: vscode.CancellationToken): Promise<vscode.DebugConfiguration[]> { - let c : LanguageClient = await client; + let c : LanguageClient = await globalVars.clientPromise.client; if (!folder) { return []; } @@ -1562,7 +1008,7 @@ class NetBeansConfigurationInitialProvider implements vscode.DebugConfigurationP u = vscode.window.activeTextEditor?.document?.uri } let result : vscode.DebugConfiguration[] = []; - const configNames : string[] | null | undefined = await vscode.commands.executeCommand(COMMAND_PREFIX + '.project.configurations', u?.toString()); + const configNames : string[] | null | undefined = await vscode.commands.executeCommand(extConstants.COMMAND_PREFIX + '.project.configurations', u?.toString()); if (configNames) { let first : boolean = true; for (let cn of configNames) { @@ -1577,7 +1023,7 @@ class NetBeansConfigurationInitialProvider implements vscode.DebugConfigurationP } const debugConfig : vscode.DebugConfiguration = { name: cname, - type: COMMAND_PREFIX, + type: extConstants.COMMAND_PREFIX, request: "launch", launchConfiguration: cn, }; @@ -1602,12 +1048,12 @@ class NetBeansConfigurationDynamicProvider implements vscode.DebugConfigurationP } async doProvideDebugConfigurations(folder: vscode.WorkspaceFolder | undefined, context: ExtensionContext, commandValues: Map<string, string>, _token?: vscode.CancellationToken): Promise<vscode.DebugConfiguration[]> { - let c : LanguageClient = await client; + let c : LanguageClient = await globalVars.clientPromise.client; if (!folder) { return []; } let result : vscode.DebugConfiguration[] = []; - const attachConnectors : DebugConnector[] | null | undefined = await vscode.commands.executeCommand(COMMAND_PREFIX + '.java.attachDebugger.configurations'); + const attachConnectors : DebugConnector[] | null | undefined = await vscode.commands.executeCommand(extConstants.COMMAND_PREFIX + '.java.attachDebugger.configurations'); if (attachConnectors) { for (let ac of attachConnectors) { const debugConfig : vscode.DebugConfiguration = { @@ -1619,7 +1065,7 @@ class NetBeansConfigurationDynamicProvider implements vscode.DebugConfigurationP let defaultValue: string = ac.defaultValues[i]; if (!defaultValue.startsWith("${command:")) { // Create a command that asks for the argument value: - let cmd: string = COMMAND_PREFIX + ".java.attachDebugger.connector." + ac.id + "." + ac.arguments[i]; + let cmd: string = extConstants.COMMAND_PREFIX + ".java.attachDebugger.connector." + ac.id + "." + ac.arguments[i]; debugConfig[ac.arguments[i]] = "${command:" + cmd + "}"; if (!commandValues.has(cmd)) { commandValues.set(cmd, ac.defaultValues[i]); @@ -1651,7 +1097,7 @@ class NetBeansConfigurationResolver implements vscode.DebugConfigurationProvider resolveDebugConfiguration(_folder: vscode.WorkspaceFolder | undefined, config: vscode.DebugConfiguration, _token?: vscode.CancellationToken): vscode.ProviderResult<vscode.DebugConfiguration> { if (!config.type) { - config.type = COMMAND_PREFIX; + config.type = extConstants.COMMAND_PREFIX; } if (!config.request) { config.request = 'launch'; diff --git a/vscode/src/extensionInfo.ts b/vscode/src/extensionInfo.ts new file mode 100644 index 0000000..39ba28d --- /dev/null +++ b/vscode/src/extensionInfo.ts @@ -0,0 +1,25 @@ +/* + Copyright (c) 2023-2024, Oracle and/or its affiliates. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ +import { Disposable, ExtensionContext } from "vscode"; + +export class ExtensionInfo { + constructor(private context: ExtensionContext){} + + getGlobalStorage = () => this.context.globalStorageUri; + getWorkspaceStorage = () => this.context.storageUri; + getExtensionStorageUri = () => this.context.extensionUri; + pushSubscription = (listener: Disposable) => this.context.subscriptions.push(listener); +} \ No newline at end of file diff --git a/vscode/src/jdkDownloader/action.ts b/vscode/src/jdkDownloader/action.ts index cfc1ff1..6f690f4 100644 --- a/vscode/src/jdkDownloader/action.ts +++ b/vscode/src/jdkDownloader/action.ts @@ -16,13 +16,15 @@ import { commands, OpenDialogOptions, OutputChannel, window, workspace } from "vscode"; import { JdkDownloaderView } from "./view"; -import { OPEN_JDK_VERSION_DOWNLOAD_LINKS, ORACLE_JDK_BASE_DOWNLOAD_URL } from "../constants"; +import { jdkDownloaderConstants } from "../constants"; import * as path from 'path'; import * as fs from 'fs'; import { calculateChecksum, downloadFileWithProgressBar, httpsGet } from "../utils"; import * as cp from 'child_process'; import { promisify } from "util"; import { l10n } from "../localiser"; +import { LOGGER } from "../extension"; +import { LogLevel } from "../logger"; export class JdkDownloaderAction { public static readonly MANUAL_INSTALLATION_TYPE = "manual"; @@ -38,12 +40,12 @@ export class JdkDownloaderAction { private downloadFilePath?: string; private downloadUrl?: string; - constructor(private readonly logger: OutputChannel, private readonly downloaderView: JdkDownloaderView) { } + constructor(private readonly downloaderView: JdkDownloaderView) { } public attachListener = async (message: any) => { const { command, id, jdkVersion, jdkOS, jdkArch, installType } = message; if (command === JdkDownloaderView.DOWNLOAD_CMD_LABEL) { - this.logger.appendLine(`Request received for downloading ${id} version ${jdkVersion}`); + LOGGER.log(`Request received for downloading ${id} version ${jdkVersion}`); this.jdkType = id; this.jdkVersion = jdkVersion; @@ -52,7 +54,7 @@ export class JdkDownloaderAction { this.installType = installType; this.installationPath = await this.getInstallationPathFromUser(); - this.logger.appendLine(`Parameters set in JDK Downloader: + LOGGER.log(`Parameters set in JDK Downloader: JDK Type: ${this.jdkType}, JDK Version: ${this.jdkVersion}, OS Type: ${this.osType}, @@ -92,14 +94,14 @@ export class JdkDownloaderAction { workspace.getConfiguration('jdk').update('jdkhome', this.installationPath, true); await this.installationCompletion(); - this.logger.appendLine(`manual JDK installation completed successfully`); + LOGGER.log(`manual JDK installation completed successfully`); return; } await this.jdkInstallationManager(); } catch (err: any) { window.showErrorMessage(l10n.value("jdk.downloader.error_message.installingJDK", { error: err })); - this.logger.appendLine(err?.message || "No Error message received"); + LOGGER.log(err?.message || "No Error message received", LogLevel.ERROR); } } @@ -110,7 +112,7 @@ export class JdkDownloaderAction { } else { dialogBoxMessage = l10n.value("jdk.downloader.message.completedInstallingJdk"); } - this.logger.appendLine(`JDK installation completed successfully`); + LOGGER.log(`JDK installation completed successfully`); const reloadNow: string = l10n.value("jdk.downloader.message.reload"); const selected = await window.showInformationMessage(dialogBoxMessage, reloadNow); @@ -140,8 +142,8 @@ export class JdkDownloaderAction { }); window.showInformationMessage(downloadSuccessLabel); - this.logger.appendLine(`JDK downloaded successfully`); - this.logger.appendLine(`JDK installation starting...`); + LOGGER.log(`JDK downloaded successfully`); + LOGGER.log(`JDK installation starting...`); await this.rmPreviousMatchingDownloads(); await this.extractJDK(); @@ -151,13 +153,13 @@ export class JdkDownloaderAction { let baseDownloadUrl: string = ''; if (this.jdkType === JdkDownloaderView.OPEN_JDK_LABEL) { - baseDownloadUrl = `${OPEN_JDK_VERSION_DOWNLOAD_LINKS[`${this.jdkVersion}`]}_${this.osType!.toLowerCase()}-${this.machineArch}_bin`; + baseDownloadUrl = `${jdkDownloaderConstants.OPEN_JDK_VERSION_DOWNLOAD_LINKS[`${this.jdkVersion}`]}_${this.osType!.toLowerCase()}-${this.machineArch}_bin`; } else if (this.jdkType === JdkDownloaderView.ORACLE_JDK_LABEL) { - baseDownloadUrl = `${ORACLE_JDK_BASE_DOWNLOAD_URL}/${this.jdkVersion}/latest/jdk-${this.jdkVersion}_${this.osType!.toLowerCase()}-${this.machineArch}_bin`; + baseDownloadUrl = `${jdkDownloaderConstants.ORACLE_JDK_BASE_DOWNLOAD_URL}/${this.jdkVersion}/latest/jdk-${this.jdkVersion}_${this.osType!.toLowerCase()}-${this.machineArch}_bin`; } const downloadUrl = this.osType === 'windows' ? `${baseDownloadUrl}.zip` : `${baseDownloadUrl}.tar.gz`; - this.logger.appendLine(`Downloading JDK from ${downloadUrl}`); + LOGGER.log(`Downloading JDK from ${downloadUrl}`); return downloadUrl; } @@ -170,7 +172,7 @@ export class JdkDownloaderAction { fs.mkdirSync(this.DOWNLOAD_DIR); } const downloadLocation = path.join(this.DOWNLOAD_DIR, newFileName); - this.logger.appendLine(`Downloading JDK at ${downloadLocation}`); + LOGGER.log(`Downloading JDK at ${downloadLocation}`); return downloadLocation; } @@ -181,7 +183,7 @@ export class JdkDownloaderAction { jdkVersion: this.jdkVersion }); await downloadFileWithProgressBar(this.downloadUrl!, this.downloadFilePath!, message); - this.logger.appendLine(`JDK downloaded successfully`); + LOGGER.log(`JDK downloaded successfully`); const doesMatch = await this.checksumMatch(); if (!doesMatch) { @@ -192,7 +194,7 @@ export class JdkDownloaderAction { }); throw new Error(checksumMatchFailedLabel); } - this.logger.appendLine(`Checksum match successful`); + LOGGER.log(`Checksum match successful`); } private checksumMatch = async (): Promise<boolean> => { @@ -209,25 +211,25 @@ export class JdkDownloaderAction { } private extractJDK = async (): Promise<void> => { - this.logger.appendLine(`Extracting JDK...`); + LOGGER.log(`Extracting JDK...`); const extractCommand = `tar -xzf "${this.downloadFilePath}" -C "${this.DOWNLOAD_DIR}"`; const exec = promisify(cp.exec); try { await exec(extractCommand); - this.logger.appendLine(`Extracting JDK successful`); + LOGGER.log(`Extracting JDK successful`); } catch (err) { - this.logger.appendLine(`Error while extracting JDK: ${(err as Error).message}`); + LOGGER.log(`Error while extracting JDK: ${(err as Error).message}`, LogLevel.ERROR); throw new Error(l10n.value("jdk.downloader.error_message.extractionError", { jdkType: this.jdkType, jdkVersion: this.jdkVersion })); } - this.logger.appendLine(`Copying JDK to installation path...`); + LOGGER.log(`Copying JDK to installation path...`); await this.copyJdkAndFinishInstallation(); - this.logger.appendLine(`Copying JDK to installation path successful`); + LOGGER.log(`Copying JDK to installation path successful`); } private copyJdkAndFinishInstallation = async () => { @@ -252,8 +254,8 @@ export class JdkDownloaderAction { // If user agrees for deleting the directory then delete it and move the temp directory to the user selected location await fs.promises.rename(tempDirectoryPath, newDirectoryPath); - this.logger.appendLine(`Copying extracted JDK at the installation path...`); - this.logger.appendLine(`Updating jdk.jdkhome settings...`); + LOGGER.log(`Copying extracted JDK at the installation path...`); + LOGGER.log(`Updating jdk.jdkhome settings...`); let binPath = newDirectoryPath; if (this.osType === 'macOS') { @@ -262,14 +264,14 @@ export class JdkDownloaderAction { workspace.getConfiguration('jdk').update('jdkhome', binPath, true); - this.logger.appendLine(`Finishing up installation...`); + LOGGER.log(`Finishing up installation...`); this.installationCleanup(tempDirectoryPath, newDirectoryPath); } private installationCleanup = (tempDirPath: string, newDirPath: string) => { fs.unlink(this.downloadFilePath!, async (err) => { if (err) { - this.logger.appendLine(`Error while installation cleanup: ${err.message}`); + LOGGER.log(`Error while installation cleanup: ${err.message}`, LogLevel.ERROR); window.showErrorMessage(l10n.value("jdk.downloader.error_message.installationCleanup")); } else { if (tempDirPath && fs.existsSync(tempDirPath)) { diff --git a/vscode/src/jdkDownloader/prompt.ts b/vscode/src/jdkDownloader/prompt.ts new file mode 100644 index 0000000..facc019 --- /dev/null +++ b/vscode/src/jdkDownloader/prompt.ts @@ -0,0 +1,29 @@ +/* + Copyright (c) 2023-2024, Oracle and/or its affiliates. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ +import { commands, window } from "vscode"; +import { l10n } from "../localiser"; + +export const jdkDownloaderPrompt = () => { + const downloadAndSetupActionLabel = l10n.value("jdk.extension.lspServer.label.downloadAndSetup") + window.showInformationMessage( + l10n.value("jdk.extension.lspServer.message.noJdkFound"), + downloadAndSetupActionLabel + ).then(selection => { + if (selection === downloadAndSetupActionLabel) { + commands.executeCommand("jdk.download.jdk"); + } + }); +} diff --git a/vscode/src/jdkDownloader/view.ts b/vscode/src/jdkDownloader/view.ts index dc6210a..4e254d0 100644 --- a/vscode/src/jdkDownloader/view.ts +++ b/vscode/src/jdkDownloader/view.ts @@ -14,12 +14,14 @@ limitations under the License. */ -import { OPEN_JDK_VERSION_DOWNLOAD_LINKS, ORACLE_JDK_DOWNLOAD_VERSIONS } from '../constants'; -import { OutputChannel, Uri, ViewColumn, WebviewPanel, window } from 'vscode'; +import { jdkDownloaderConstants } from '../constants'; +import { ViewColumn, WebviewPanel, window } from 'vscode'; import * as os from 'os'; import { JdkDownloaderAction } from './action'; import { downloaderCss } from './styles'; import { l10n } from '../localiser'; +import { LOGGER } from '../extension'; +import { LogLevel } from '../logger'; export class JdkDownloaderView { public static readonly OPEN_JDK_LABEL = "OpenJDK"; @@ -31,13 +33,9 @@ export class JdkDownloaderView { private osType?: string; private machineArch?: string; - constructor( - private readonly logger: OutputChannel - ) { } - public createView = () => { try { - this.logger.appendLine("Creating JDK downloader webview"); + LOGGER.log("Creating JDK downloader webview"); this.jdkDownloaderWebView = window.createWebviewPanel( 'jdkDownloader', this.jdkDownloaderTitle, @@ -49,13 +47,13 @@ export class JdkDownloaderView { this.setDropdownOptions(); this.jdkDownloaderWebView.webview.html = this.fetchJdkDownloadViewHtml(); this.jdkDownloaderWebView.webview.onDidReceiveMessage(message => { - const jdkDownloader = new JdkDownloaderAction(this.logger, this); + const jdkDownloader = new JdkDownloaderAction(this); jdkDownloader.attachListener(message); }); - this.logger.appendLine("JDK downloader webview created successfully"); + LOGGER.log("JDK downloader webview created successfully"); } catch (err: any) { - this.logger.appendLine("Error creating JDK downloader webview:") - this.logger.appendLine(err?.message || "No Error message received"); + LOGGER.log("Error creating JDK downloader webview:", LogLevel.ERROR) + LOGGER.log(err?.message || "No Error message received", LogLevel.ERROR); window.showErrorMessage(l10n.value("jdk.downloader.error_message.errorLoadingPage")); } } @@ -77,7 +75,7 @@ export class JdkDownloaderView { this.osType = "windows"; break; } - this.logger.appendLine(`OS identified: ${this.osType}`); + LOGGER.log(`OS identified: ${this.osType}`); const machineArchNode = os.arch(); if (machineArchNode === "arm64") { @@ -86,7 +84,7 @@ export class JdkDownloaderView { else { this.machineArch = "x64"; } - this.logger.appendLine(`Machine architecture identified: ${this.machineArch}`); + LOGGER.log(`Machine architecture identified: ${this.machineArch}`); } private fetchJdkDownloadViewHtml = (): string => { @@ -114,7 +112,7 @@ export class JdkDownloaderView { <br /> <div class="jdk-version-dropdown"> <select id="oracleJDKVersionDropdown"> - ${this.getJdkVersionsHtml(ORACLE_JDK_DOWNLOAD_VERSIONS)} + ${this.getJdkVersionsHtml(jdkDownloaderConstants.ORACLE_JDK_DOWNLOAD_VERSIONS)} </select> </div> </div> @@ -146,7 +144,7 @@ export class JdkDownloaderView { <br /> <div class="jdk-version-dropdown"> <select id="openJDKVersionDropdown"> - ${this.getJdkVersionsHtml(Object.keys(OPEN_JDK_VERSION_DOWNLOAD_LINKS))} + ${this.getJdkVersionsHtml(Object.keys(jdkDownloaderConstants.OPEN_JDK_VERSION_DOWNLOAD_LINKS))} </select> </div> </div> diff --git a/vscode/src/launchConfigurations.ts b/vscode/src/launchConfigurations.ts index 0afd9a0..13d5511 100644 --- a/vscode/src/launchConfigurations.ts +++ b/vscode/src/launchConfigurations.ts @@ -25,8 +25,8 @@ import { commands, CompletionItem, CompletionList, ExtensionContext, languages, import { InsertTextFormat } from 'vscode-languageclient'; import * as jsoncp from 'jsonc-parser'; import * as fs from 'fs'; -import { COMMAND_PREFIX } from "./extension"; import { l10n } from './localiser'; +import { extConstants } from './constants'; export function updateLaunchConfig() { workspace.findFiles('.vscode/launch.json').then(async files => { @@ -88,7 +88,7 @@ export function registerCompletion(context: ExtensionContext) { let completionItems: ProviderResult<CompletionList<CompletionItem>> | CompletionItem[]; if (path.length == 1) { // Get all configurations: - completionItems = commands.executeCommand(COMMAND_PREFIX + '.project.configuration.completion', uri); + completionItems = commands.executeCommand(extConstants.COMMAND_PREFIX + '.project.configuration.completion', uri); } else { let node: jsoncp.Node = currentNode; if (currentNode.type == 'property' && currentNode.parent) { @@ -99,11 +99,11 @@ export function registerCompletion(context: ExtensionContext) { node = currentNode.parent; let attributesMap = getAttributes(node); // Get possible values of property 'propName': - completionItems = commands.executeCommand(COMMAND_PREFIX + '.project.configuration.completion', uri, attributesMap, propName); + completionItems = commands.executeCommand(extConstants.COMMAND_PREFIX + '.project.configuration.completion', uri, attributesMap, propName); } else { let attributesMap = getAttributes(node); // Get additional possible attributes: - completionItems = commands.executeCommand(COMMAND_PREFIX + '.project.configuration.completion', uri, attributesMap); + completionItems = commands.executeCommand(extConstants.COMMAND_PREFIX + '.project.configuration.completion', uri, attributesMap); } } return (completionItems as Thenable<CompletionList<CompletionItem>>).then(itemsList => { diff --git a/vscode/src/localiser.ts b/vscode/src/localiser.ts index 266076f..4906909 100644 --- a/vscode/src/localiser.ts +++ b/vscode/src/localiser.ts @@ -19,7 +19,7 @@ import * as l10nLib from '@vscode/l10n' import * as vscode from 'vscode'; -import { ORACLE_VSCODE_EXTENSION_ID } from './constants'; +import { extConstants } from './constants'; const DEFAULT_LANGAUGE = "en"; const DEFAULT_BUNDLE_FILE = `l10n/bundle.l10n.${DEFAULT_LANGAUGE}.json`; @@ -61,4 +61,4 @@ class l10Wrapper implements l10n { } -export const l10n: l10n = new l10Wrapper(ORACLE_VSCODE_EXTENSION_ID, DEFAULT_BUNDLE_FILE); \ No newline at end of file +export const l10n: l10n = new l10Wrapper(extConstants.ORACLE_VSCODE_EXTENSION_ID, DEFAULT_BUNDLE_FILE); \ No newline at end of file diff --git a/vscode/src/logger.ts b/vscode/src/logger.ts new file mode 100644 index 0000000..7eca25f --- /dev/null +++ b/vscode/src/logger.ts @@ -0,0 +1,58 @@ +/* + Copyright (c) 2023-2024, Oracle and/or its affiliates. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ +import { OutputChannel, window } from "vscode"; + +export enum LogLevel { + INFO = 'INFO', + WARN = 'WARN', + ERROR = 'ERROR', +} + +export class ExtensionLogger { + private outChannel: OutputChannel; + + constructor(channelName: string) { + this.outChannel = window.createOutputChannel(channelName); + } + + public log(message: string, level: LogLevel = LogLevel.INFO): void { + this.outChannel.appendLine(`[${level}]: ${message}`); + } + + public warn(message: string): void { + this.log(message, LogLevel.WARN); + } + + public error(message: string): void { + this.log(message, LogLevel.ERROR); + } + + public logNoNL(message: string): void { + this.outChannel.append(message); + } + + public showOutputChannelUI(show: boolean): void { + this.outChannel.show(show); + } + + public getOutputChannel(): OutputChannel { + return this.outChannel; + } + + public dispose(): void { + this.outChannel.dispose(); + } +} \ No newline at end of file diff --git a/vscode/src/lsp/clientPromise.ts b/vscode/src/lsp/clientPromise.ts new file mode 100644 index 0000000..aba7f48 --- /dev/null +++ b/vscode/src/lsp/clientPromise.ts @@ -0,0 +1,80 @@ +/* + Copyright (c) 2023-2024, Oracle and/or its affiliates. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ +import { commands } from "vscode"; +import { globalVars, LOGGER } from "../extension"; +import { LogLevel } from "../logger"; +import { NbProcessManager } from "./nbProcessManager"; +import { clientInit } from "./initializer"; +import { NbLanguageClient } from "./nbLanguageClient"; + +export class ClientPromise { + setClient!: [(c: NbLanguageClient) => void, (err: any) => void]; + client!: Promise<NbLanguageClient>; + activationPending!: boolean; + initialPromiseResolved: boolean = false; + + public clientPromiseInitialization = (): void => { + this.client = new Promise<NbLanguageClient>((clientOK, clientErr) => { + this.setClient = [ + (c: NbLanguageClient) => { + this.initialPromiseResolved = true; + clientOK(c); + }, + (err: any) => { + clientErr(err); + } + ]; + }); + + this.activationPending = true; + commands.executeCommand('setContext', 'nbJdkReady', false); + } + + public stopClient = async (): Promise<void> => { + if(globalVars.testAdapter){ + globalVars.testAdapter.dispose(); + globalVars.testAdapter = undefined; + } + if (!this.client) { + return Promise.resolve(); + } + + return (await this.client).stop(); + } + + public restartExtension = async (nbProcessManager: NbProcessManager | null, notifyKill: boolean) => { + if (this.activationPending) { + LOGGER.log("Server activation requested repeatedly, ignoring...", LogLevel.WARN); + return; + } + if(!nbProcessManager){ + LOGGER.log("Nbcode Process is null", LogLevel.ERROR); + return; + } + try { + await this.stopClient(); + await nbProcessManager.killProcess(notifyKill); + this.clientPromiseInitialization(); + initializeServer(); + } catch (error) { + LOGGER.log(`Error during activation: ${error}`, LogLevel.ERROR); + throw error; + } finally { + this.activationPending = false; + } + } + +} \ No newline at end of file diff --git a/vscode/src/lsp/initializer.ts b/vscode/src/lsp/initializer.ts new file mode 100644 index 0000000..8a85a74 --- /dev/null +++ b/vscode/src/lsp/initializer.ts @@ -0,0 +1,231 @@ +/* + Copyright (c) 2023-2024, Oracle and/or its affiliates. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ +import { StreamInfo } from "vscode-languageclient/node"; +import { getUserConfigLaunchOptionsDefaults, prepareNbcodeLaunchOptions } from "./launchOptions"; +import { globalVars, LOGGER } from "../extension"; +import { configKeys } from "../configurations/configuration"; +import { NbProcessManager } from "./nbProcessManager"; +import { enableDisableModules, findNbcode } from "./utils"; +import * as net from 'net'; +import { extConstants, NODE_WINDOWS_LABEL } from "../constants"; +import { l10n } from "../localiser"; +import { window } from "vscode"; +import { ChildProcess } from "child_process"; +import { jdkDownloaderPrompt } from "../jdkDownloader/prompt"; +import * as os from 'os'; +import { LogLevel } from "../logger"; +import { isNbJavacDisabledHandler } from "../configurations/handlers"; + +const launchNbcode = (): void => { + const ideLaunchOptions = prepareNbcodeLaunchOptions(); + const userdir = getUserConfigLaunchOptionsDefaults()[configKeys.userdir].value; + const specifiedJDK = getUserConfigLaunchOptionsDefaults()[configKeys.jdkHome].value; + const extensionPath = globalVars.extensionInfo.getExtensionStorageUri().fsPath; + const nbcodePath = findNbcode(extensionPath); + + const requiredJdk = specifiedJDK ? specifiedJDK : 'default system JDK'; + let launchMsg = l10n.value("jdk.extension.lspServer.statusBar.message.launching", { + SERVER_NAME: extConstants.SERVER_NAME, + requiredJdk: requiredJdk, + userdir: userdir + }); + LOGGER.log(launchMsg); + window.setStatusBarMessage(launchMsg, 2000); + + globalVars.nbProcessManager = new NbProcessManager(userdir, nbcodePath, ideLaunchOptions); + globalVars.nbProcessManager.startProcess(); +} + +const establishConnection = () => new Promise<StreamInfo>((resolve, reject) => { + const nbProcess = globalVars.nbProcessManager?.getProcess(); + const nbProcessManager = globalVars.nbProcessManager; + + if (!nbProcessManager || !nbProcess) { + reject(); + return; + } + + LOGGER.log(`LSP server launching: ${nbProcessManager.getProcessId()}`); + LOGGER.log(`LSP server user directory: ${getUserConfigLaunchOptionsDefaults()[configKeys.userdir].value}`); + + let status = false; + nbProcess.stdout?.on('data', (d: any) => { + status = processOnDataHandler(nbProcessManager, d.toString(), true); + }); + nbProcess.stderr?.on('data', (d: any) => { + processOnDataHandler(nbProcessManager, d.toString(), false); + }); + nbProcess.on('close', (code: number) => { + const status = processOnCloseHandler(nbProcessManager, code) + if (status != null) { + reject(status); + } + }); + + try { + connectToServer(nbProcess).then(server => resolve({ + reader: server, + writer: server + })).catch(err => { throw err }); + } catch (err) { + reject(err); + globalVars.nbProcessManager?.disconnect(); + return; + } +}); + +const connectToServer = (nbProcess: ChildProcess): Promise<net.Socket> => { + return new Promise<net.Socket>((resolve, reject) => { + if (!nbProcess.stdout) { + reject('No stdout to parse!'); + return; + } + globalVars.debugPort = -1; + let lspServerStarted = false; + nbProcess.stdout.on("data", (chunk) => { + if (globalVars.debugPort < 0) { + const info = chunk.toString().match(/Debug Server Adapter listening at port (\d*) with hash (.*)\n/); + if (info) { + globalVars.debugPort = info[1]; + globalVars.debugHash = info[2]; + } + } + if (!lspServerStarted) { + const info = chunk.toString().match(/Java Language Server listening at port (\d*) with hash (.*)\n/); + if (info) { + const port: number = info[1]; + const server = net.connect(port, "127.0.0.1", () => { + server.write(info[2]); + resolve(server); + }); + lspServerStarted = true; + } + } + }); + nbProcess.once("error", (err) => { + reject(err); + }); + }); +} + +const processOnDataHandler = (nbProcessManager: NbProcessManager, text: string, isOut: boolean) => { + if (nbProcessManager) { + globalVars.clientPromise.activationPending = false; + } + LOGGER.logNoNL(text); + isOut ? nbProcessManager.appendStdOut(text) : nbProcessManager.appendStdErr(text); + + if (nbProcessManager.getStdOut()?.match(/org.netbeans.modules.java.lsp.server/)) { + return true; + } + return false; +} + + +const processOnCloseHandler = (nbProcessManager: NbProcessManager, code: number): string | null => { + const globalnbProcessManager = globalVars.nbProcessManager; + if (globalnbProcessManager == nbProcessManager) { + globalVars.nbProcessManager = null; + if (code != 0) { + window.showWarningMessage(l10n.value("jdk.extension.lspServer.warning_message.serverExited", { SERVER_NAME: extConstants.SERVER_NAME, code: code })); + } + } + if (nbProcessManager.getStdOut()?.match(/Cannot find java/) || (os.type() === NODE_WINDOWS_LABEL && !globalVars.deactivated)) { + jdkDownloaderPrompt(); + } + if (nbProcessManager.getStdOut() != null) { + let match = nbProcessManager.getStdOut()!.match(/org.netbeans.modules.java.lsp.server[^\n]*/) + if (match?.length == 1) { + LOGGER.log(match[0]); + } else { + LOGGER.log("Cannot find org.netbeans.modules.java.lsp.server in the log!", LogLevel.ERROR); + } + LOGGER.log(`Please refer to troubleshooting section for more info: https://github.com/oracle/javavscode/blob/main/README.md#troubleshooting`); + LOGGER.showOutputChannelUI(false); + + nbProcessManager.killProcess(false); + return l10n.value("jdk.extension.error_msg.notEnabled", { SERVER_NAME: extConstants.SERVER_NAME }); + } else { + LOGGER.log(`LSP server ${nbProcessManager.getProcessId()} terminated with ${code}`); + LOGGER.log(`Exit code ${code}`); + } + return null; +} + +const enableDisableNbjavacModule = () => { + const userdirPath = getUserConfigLaunchOptionsDefaults()[configKeys.userdir].value + const nbjavacValue = isNbJavacDisabledHandler(); + const extensionPath = globalVars.extensionInfo.getExtensionStorageUri().fsPath; + enableDisableModules(extensionPath, userdirPath, ['org.netbeans.libs.nbjavacapi'], nbjavacValue); +} + +const serverBuilder = () => { + enableDisableNbjavacModule(); + launchNbcode(); + return establishConnection; +} + +export const clientInit = () => { + const connection: () => Promise<StreamInfo> = serverBuilder(); + const client = NbLanguageClient.build(connection, LOGGER); + + LOGGER.log('Language Client: Starting'); + client.start().then(() => { + globalVars.testAdapter = new NbTestAdapter(); + + registerListenersAfterClientInit(); + registerNotificationListeners(client); + registerRequestListeners(client); + + LOGGER.log('Language Client: Ready'); + globalVars.clientPromise.initializedSuccessfully(client); + + createProjectView(client); + }).catch(globalVars.clientPromise.setClient[1]); +} + + +async function createProjectView(client : NbLanguageClient) { + const ts : TreeViewService = client.findTreeViewService(); + let tv : TreeView<Visualizer> = await ts.createView('foundProjects', 'Projects', { canSelectMany : false }); + + async function revealActiveEditor(ed? : TextEditor) { + const uri = window.activeTextEditor?.document?.uri; + if (!uri || uri.scheme.toLowerCase() !== 'file') { + return; + } + if (!tv.visible) { + return; + } + let vis : Visualizer | undefined = await ts.findPath(tv, uri.toString()); + if (!vis) { + return; + } + tv.reveal(vis, { select : true, focus : false, expand : false }); + } + const netbeansConfig = workspace.getConfiguration(extConstants.COMMAND_PREFIX); + globalVars.extensionInfo.pushSubscription(window.onDidChangeActiveTextEditor(ed => { + if (netbeansConfig.get("revealActiveInProjects")) { + revealActiveEditor(ed); + } + })); + globalVars.extensionInfo.pushSubscription(commands.registerCommand(extCommands.selectEditorProjs, () => revealActiveEditor())); + + // attempt to reveal NOW: + if (getConfigurationValue(configKeys.revealInActivteProj)) { + revealActiveEditor(); + } +} \ No newline at end of file diff --git a/vscode/src/lsp/launchOptions.ts b/vscode/src/lsp/launchOptions.ts new file mode 100644 index 0000000..778f5df --- /dev/null +++ b/vscode/src/lsp/launchOptions.ts @@ -0,0 +1,85 @@ +/* + Copyright (c) 2023-2024, Oracle and/or its affiliates. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ +import { configKeys } from "../configurations/configuration" +import { isDarkColorThemeHandler, isNbJavacDisabledHandler, jdkHomeValueHandler, lspServerVmOptionsHandler, projectSearchRootsValueHandler, userdirHandler } from "../configurations/handlers"; +import { l10n } from "../localiser"; +import { userDefinedLaunchOptionsType } from "./types" + +export const getUserConfigLaunchOptionsDefaults = (): userDefinedLaunchOptionsType => { + return { + [configKeys.jdkHome]: { + value: jdkHomeValueHandler(), + optionToPass: ['--jdkhome'] + }, + [configKeys.userdir]: { + value: userdirHandler(), + optionToPass: ['--userdir'] + }, + [configKeys.disableProjSearchLimit]: { + value: projectSearchRootsValueHandler(), + optionToPass: '-J-Dproject.limitScanRoot=', + },[configKeys.verbose]: { + value: isNbJavacDisabledHandler(), + optionToPass: '-J-Dnetbeans.logger.console=' + }, + [configKeys.vscodeTheme]: { + value: isDarkColorThemeHandler() ? 'com.formdev.flatlaf.FlatDarkLaf' : null, + optionToPass: ['--laf'] + }, + [configKeys.lspVmOptions]: { + value: lspServerVmOptionsHandler() + } + }; +}; + +const extraLaunchOptions = [ + "--modules", + "--list", + "-J-XX:PerfMaxStringConstLength=10240", + "--locale", l10n.nbLocaleCode(), + "--start-java-language-server=listen-hash:0", + "--start-java-debug-adapter-server=listen-hash:0" + ]; + +const prepareUserConfigLaunchOptions = (): string[] => { + const launchOptions: string[] = []; + const userConfigLaunchOptionsDefaults = getUserConfigLaunchOptionsDefaults(); + Object.values(userConfigLaunchOptionsDefaults).forEach(userConfig => { + const { value, optionToPass, encloseInvertedComma } = userConfig; + if (value) { + if (!optionToPass && Array.isArray(value)) { + launchOptions.push(...value); + } + else if (typeof (optionToPass) === "string") { + launchOptions.push(`${optionToPass}${value}`); + } else if (Array.isArray(optionToPass)) { + const arg: string[] = [...optionToPass, value]; + launchOptions.push(...arg); + } + } + }); + + return launchOptions; +} + +export const prepareNbcodeLaunchOptions = (): string[] => { + const nbcodeLaunchOptions = []; + + const userConfigLaunchOptions = prepareUserConfigLaunchOptions(); + nbcodeLaunchOptions.push(...userConfigLaunchOptions, ...extraLaunchOptions); + + return nbcodeLaunchOptions; +} \ No newline at end of file diff --git a/vscode/src/lsp/nbLanguageClient.ts b/vscode/src/lsp/nbLanguageClient.ts new file mode 100644 index 0000000..ee611a6 --- /dev/null +++ b/vscode/src/lsp/nbLanguageClient.ts @@ -0,0 +1,102 @@ +/* + Copyright (c) 2023-2024, Oracle and/or its affiliates. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ +import { LanguageClient, LanguageClientOptions, ServerOptions } from 'vscode-languageclient/node'; +import { CloseAction, CloseHandlerResult, DocumentSelector, ErrorAction, ErrorHandlerResult, Message, RevealOutputChannelOn } from "vscode-languageclient"; +import { createTreeViewService, TreeViewService } from "../explorer"; +import { OutputChannel, workspace } from "vscode"; +import { extConstants } from "../constants"; +import { userConfigsListenedByServer } from '../configurations/configuration'; +import { restartWithJDKLater } from './utils'; +import { ExtensionLogger, LogLevel } from '../logger'; +import { globalVars } from '../extension'; + + +export class NbLanguageClient extends LanguageClient { + private _treeViewService: TreeViewService; + constructor(id: string, name: string, s: ServerOptions, log: OutputChannel, c: LanguageClientOptions) { + super(id, name, s, c); + this._treeViewService = createTreeViewService(log, this); + } + + static build = (serverOptions: ServerOptions, logger: ExtensionLogger): NbLanguageClient => { + let documentSelectors: DocumentSelector = [ + { language: extConstants.LANGUAGE_ID }, + { language: 'properties', pattern: '**/*.properties' }, + { language: 'jackpot-hint' }, + { language: 'xml', pattern: '**/pom.xml' }, + { pattern: '*.gradle' }, + { pattern: '*.gradle.kts' } + ]; + + // Options to control the language client + let clientOptions: LanguageClientOptions = { + // Register the server for java documents + documentSelector: documentSelectors, + synchronize: { + configurationSection: userConfigsListenedByServer, + fileEvents: [ + workspace.createFileSystemWatcher('**/*.java') + ] + }, + outputChannel: logger.getOutputChannel(), + revealOutputChannelOn: RevealOutputChannelOn.Never, + progressOnInitialization: true, + initializationOptions: { + 'nbcodeCapabilities': { + 'statusBarMessageSupport': true, + 'testResultsSupport': true, + 'showHtmlPageSupport': true, + 'wantsJavaSupport': true, + 'wantsGroovySupport': false, + 'commandPrefix': extConstants.COMMAND_PREFIX, + 'configurationPrefix': 'jdk.', + 'altConfigurationPrefix': 'jdk.' + } + }, + errorHandler: { + error: function (error: Error, _message: Message, count: number): ErrorHandlerResult { + return { action: ErrorAction.Continue, message: error.message }; + }, + closed: function (): CloseHandlerResult { + logger.log(`Connection to ${extConstants.SERVER_NAME} closed.`, LogLevel.WARN); + if (!globalVars.clientPromise.activationPending) { + restartWithJDKLater(10000, false); + } + return { action: CloseAction.DoNotRestart }; + } + } + } + + return new NbLanguageClient( + extConstants.NB_LANGUAGE_CLIENT_ID, + extConstants.NB_LANGUAGE_CLIENT_NAME, + serverOptions, + logger.getOutputChannel(), + clientOptions + ) + } + + findTreeViewService(): TreeViewService { + return this._treeViewService; + } + + stop(): Promise<void> { + const r: Promise<void> = super.stop(); + this._treeViewService.dispose(); + return r; + } + +} diff --git a/vscode/src/lsp/nbProcessManager.ts b/vscode/src/lsp/nbProcessManager.ts new file mode 100644 index 0000000..3862e8d --- /dev/null +++ b/vscode/src/lsp/nbProcessManager.ts @@ -0,0 +1,106 @@ +/* + Copyright (c) 2023-2024, Oracle and/or its affiliates. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ +import { LOGGER } from "../extension"; +import { spawn, ChildProcessByStdio, ChildProcess } from 'child_process'; +import { Readable } from "stream"; +import { window } from "vscode"; +import { l10n } from "../localiser"; +import { extConstants } from "../constants"; +import { LogLevel } from "../logger"; + +export class NbProcessManager { + private process?: ChildProcess | null; + private nbcodePath: string; + private ideLaunchOptions: string[]; + private userdir: string; + private stdOutText: string | null = ""; + private stdErrText: string = ""; + + constructor(userdir: string, nbcodePath: string, ideLaunchOptions: string[]) { + this.nbcodePath = nbcodePath; + this.ideLaunchOptions = ideLaunchOptions; + this.userdir = userdir; + } + + startProcess = () => { + const spawnProcess: ChildProcessByStdio<any, Readable, Readable> = spawn(this.nbcodePath, + this.ideLaunchOptions, + { + cwd: this.userdir, + stdio: ["ignore", "pipe", "pipe"] + }); + this.process = spawnProcess; + } + + killProcess = (notifyKill: boolean): Promise<void> => { + LOGGER.log("Request to kill LSP server."); + + if (!this.process) { + LOGGER.log("Cannot kill: No current process", LogLevel.ERROR); + return Promise.resolve(); + } + const processToKill = this.process; + this.process = null; + if (notifyKill) { + window.setStatusBarMessage( + l10n.value("jdk.extension.command.statusBar.message.restartingServer", + { SERVER_NAME: extConstants.SERVER_NAME }), + 2000); + } + + return new Promise<void>((resolve, reject) => { + processToKill.on('close', (code: number) => { + LOGGER.log(`LSP server closed: ${processToKill.pid}`); + resolve(); + }); + + LOGGER.log(`Killing LSP server ${processToKill.pid}`); + if (!processToKill.kill()) { + reject(new Error("Cannot kill process")); + } + }); + } + + disconnect = () => { + return this.process?.disconnect(); + } + + getProcess = () => { + return this.process; + } + + getProcessId = () => { + return this.process?.pid; + } + + appendStdOut = (text: string) => { + if(this.stdOutText != null) { + this.stdOutText += text; + } + } + + appendStdErr = (text: string) => { + this.stdErrText += text; + } + + getStdOut = () => { + return this.stdOutText + } + + getStdErr = () => { + return this.stdErrText; + } +} diff --git a/vscode/src/lsp/protocol.ts b/vscode/src/lsp/protocol.ts new file mode 100644 index 0000000..5671986 --- /dev/null +++ b/vscode/src/lsp/protocol.ts @@ -0,0 +1,324 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +'use strict'; + +import * as vscode from 'vscode'; + +import { + ProtocolNotificationType, + ProtocolRequestType, + ShowMessageParams, + NotificationType +} from 'vscode-languageclient'; + +import { + Position, + Range +} from 'vscode-languageserver-protocol'; + +export interface HtmlPageParams { + id: string; + text: string; + pause: boolean; + resources?: { + [name: string]: string; + }; +} + +export namespace HtmlPageRequest { + export const type = new ProtocolRequestType<HtmlPageParams, void, never, void, void>('window/showHtmlPage'); +}; + +export namespace ExecInHtmlPageRequest { + export const type = new ProtocolRequestType<HtmlPageParams, boolean, never, void, void>('window/execInHtmlPage'); +}; + +export interface ShowStatusMessageParams extends ShowMessageParams { + /** + * The timeout + */ + timeout?: number; +} + +export interface UpdateConfigParams { + /** + * Information specifying configuration update. + */ + section: string; + key: string; + value: string; +} + +export namespace UpdateConfigurationRequest { + export const type = new ProtocolRequestType<UpdateConfigParams, void, never, void, void>('config/update'); +} + +export namespace StatusMessageRequest { + export const type = new ProtocolNotificationType<ShowStatusMessageParams, void>('window/showStatusBarMessage'); +}; + +export interface ShowQuickPickParams { + /** + * An optional title of the quick pick. + */ + title?: string; + /** + * A string to show as placeholder in the input box to guide the user what to pick on. + */ + placeHolder: string; + /** + * An optional flag to make the picker accept multiple selections. + */ + canPickMany?: boolean; + /** + * A list of items. + */ + items: vscode.QuickPickItem[]; +} + +export namespace QuickPickRequest { + export const type = new ProtocolRequestType<ShowQuickPickParams, vscode.QuickPickItem[], never, void, void>('window/showQuickPick'); +} + +export interface ShowInputBoxParams { + /** + * An optional title of the input box. + */ + title?: string; + /** + * The text to display underneath the input box. + */ + prompt: string; + /** + * The value to prefill in the input box. + */ + value: string; + /** + * Controls if a password input is shown. Password input hides the typed text. + */ + password?: boolean; +} + +export namespace InputBoxRequest { + export const type = new ProtocolRequestType<ShowInputBoxParams, string | undefined, never, void, void>('window/showInputBox'); +} + +export interface ShowMutliStepInputParams { + /** + * ID of the input. + */ + id: string; + /** + * An optional title. + */ + title?: string; +} + +export interface InputCallbackParams { + inputId : string; + step: number; + data: { [name: string]: readonly vscode.QuickPickItem[] | string }; +} + +export interface StepInfo { + totalSteps: number; + stepId: string; +} + +export type QuickPickStep = StepInfo & ShowQuickPickParams; + +export type InputBoxStep = StepInfo & ShowInputBoxParams; + +export namespace MutliStepInputRequest { + export const type = new ProtocolRequestType<ShowMutliStepInputParams, { [name: string]: readonly vscode.QuickPickItem[] | string }, never, void, void>('window/showMultiStepInput'); + export const step = new ProtocolRequestType<InputCallbackParams, QuickPickStep | InputBoxStep | undefined, never, void, void>('input/step'); + export const validate = new ProtocolRequestType<InputCallbackParams, string | undefined, never, void, void>('input/validate'); +} + +export interface TestProgressParams { + uri: string; + suite: TestSuite; +} + +export interface TestSuite { + name: string; + file?: string; + range?: Range; + state: 'loaded' | 'started' | 'passed' | 'failed' | 'skipped' | 'errored'; + tests?: TestCase[]; +} + +export interface TestCase { + id: string; + name: string; + file?: string; + range?: Range; + state: 'loaded' | 'started' | 'passed' | 'failed' | 'skipped' | 'errored'; + stackTrace?: string[]; +} + +export namespace TestProgressNotification { + export const type = new ProtocolNotificationType<TestProgressParams, void>('window/notifyTestProgress'); +}; + +export interface DebugConnector { + id: string; + name: string; + type: string; + arguments: string[]; + defaultValues: string[]; + descriptions: string[]; +} + +export interface SetTextEditorDecorationParams { + key: string; + uri: string; + ranges: Range[]; +}; + +export namespace TextEditorDecorationCreateRequest { + export const type = new ProtocolRequestType<vscode.DecorationRenderOptions, never, string, void, void>('window/createTextEditorDecoration'); +}; + +export namespace TextEditorDecorationSetNotification { + export const type = new ProtocolNotificationType<SetTextEditorDecorationParams, void>('window/setTextEditorDecoration'); +}; + +export namespace TextEditorDecorationDisposeNotification { + export const type = new ProtocolNotificationType<string, void>('window/disposeTextEditorDecoration'); +} +export interface SaveDocumentRequestParams { + documents: string[]; +} + +export namespace SaveDocumentsRequest { + export const type = new ProtocolRequestType<SaveDocumentRequestParams, boolean, never, void, void>('window/documentSave'); +} + +export interface NodeChangedParams { + rootId : number; + nodeId : number | null; + types? : NodeChangeType[]; + properties? : String[]; +} + +export interface CreateExplorerParams { + explorerId : string; +} + +export interface NodeOperationParams { + nodeId : number; +} + +export interface ProjectActionParams { + action : string; + configuration? : string; + fallback? : boolean; +} + +export interface GetResourceParams { + uri : vscode.Uri; + acceptEncoding? : string[]; + acceptContent? : string[]; +} + +export interface ResourceData { + contentType : string; + encoding : string; + content : string; + contentSize : number; +} + +export interface FindPathParams { + rootNodeId : number; + uri? : vscode.Uri; + selectData? : any; +} + +export enum NodeChangeType { + SELF = 0, + PROPERTY, + CHILDEN, + DESTROY +}; + +export interface NodeChangesParams { + rootId: number; + nodeId?: number; + deactivateListeners?: number[]; + types?: NodeChangeType[]; +} + +export namespace NodeInfoNotification { + export const type = new ProtocolNotificationType<NodeChangedParams, void>('nodes/nodeChanged'); +} + +export namespace NodeInfoRequest { + export const explorermanager = new ProtocolRequestType<CreateExplorerParams, never, Data, void, void>('nodes/explorermanager'); + export const info = new ProtocolRequestType<NodeOperationParams, Data, never,void, void>('nodes/info'); + export const children = new ProtocolRequestType<NodeOperationParams, number[], never, void, void>('nodes/children'); + export const destroy = new ProtocolRequestType<NodeOperationParams, boolean, never, void, void>('nodes/delete'); + export const collapsed = new ProtocolNotificationType<NodeOperationParams, void>('nodes/collapsed'); + export const getresource = new ProtocolRequestType<GetResourceParams, ResourceData, never, void, void>('nodes/getresource'); + export const findparams = new ProtocolRequestType<FindPathParams, number[], never, void, void>('nodes/findpath'); + export const changes = new ProtocolRequestType<NodeChangesParams, number, never, void, void>('nodes/changes'); + + export interface IconDescriptor { + baseUri : vscode.Uri; + } + export interface Data { + id : number; /* numeric ID of the node */ + name : string; /* Node.getName() */ + label : string; /* Node.getDisplayName() */ + tooltip? : string; + description : string; /* Node.getShortDescription() */ + resourceUri? : string; /* external URL to file: resource */ + collapsibleState : vscode.TreeItemCollapsibleState; + canDestroy : boolean; /* Node.canDestroy() */ + contextValue : string; /* Node.getCookies() */ + iconDescriptor? : IconDescriptor; + iconUri : string | null; + iconIndex : number; + command? : string; + } +}; + +export function asPosition(value: undefined | null): undefined; +export function asPosition(value: Position): vscode.Position; +export function asPosition(value: Position | undefined | null): vscode.Position | undefined; +export function asPosition(value: Position | undefined | null): vscode.Position | undefined { + if (!value) { + return undefined; + } + return new vscode.Position(value.line, value.character); +} + +export function asRange(value: undefined | null): undefined; +export function asRange(value: Range): vscode.Range; +export function asRange(value: Range | undefined | null): vscode.Range | undefined; +export function asRange(value: Range | undefined | null): vscode.Range | undefined { + if (!value) { + return undefined; + } + return new vscode.Range(asPosition(value.start), asPosition(value.end)); +} + +export function asRanges(value: Range[]): vscode.Range[] { + return value.map(value => asRange(value)); +} diff --git a/vscode/src/lsp/types.ts b/vscode/src/lsp/types.ts new file mode 100644 index 0000000..74cd9ee --- /dev/null +++ b/vscode/src/lsp/types.ts @@ -0,0 +1,22 @@ +/* + Copyright (c) 2023-2024, Oracle and/or its affiliates. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ +export type userDefinedLaunchOptionsType = { + [key: string]: { + value: any, + optionToPass?: string | string[], + encloseInvertedComma?: boolean + } +}; \ No newline at end of file diff --git a/vscode/src/lsp/utils.ts b/vscode/src/lsp/utils.ts new file mode 100644 index 0000000..b1305c2 --- /dev/null +++ b/vscode/src/lsp/utils.ts @@ -0,0 +1,69 @@ +/* + Copyright (c) 2023-2024, Oracle and/or its affiliates. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ +import * as fs from 'fs'; +import * as path from 'path'; +import * as os from 'os'; +import { globalVars, LOGGER } from '../extension'; +import { TextDocumentFilter } from 'vscode-languageclient'; +import { extensions } from 'vscode'; +import { extConstants } from '../constants'; + +export const enableDisableModules = ( + extensionPath: string, + userDir: string, + modules: string[], + enable: boolean) => { + for (var i = 0; i < modules.length; i++) { + const module = modules[i]; + const moduleXml: string = module.replace(/\./g, "-") + ".xml"; + var xmlContent: string = ""; + const clusters: string[] = fs.readdirSync(path.join(extensionPath, "nbcode")); + for (var c = 0; c < clusters.length; c++) { + const sourceXmlPath: string = path.join(extensionPath, "nbcode", clusters[c], "config", "Modules", moduleXml); + if (fs.existsSync(sourceXmlPath)) { + xmlContent = fs.readFileSync(sourceXmlPath).toString(); + } + } + xmlContent = xmlContent.replace(`<param name="enabled">${!enable}</param>`, `<param name="enabled">${enable}</param>`); + fs.mkdirSync(path.join(userDir, "config", "Modules"), { recursive: true }); + fs.writeFileSync(path.join(userDir, "config", "Modules", moduleXml), xmlContent); + } +} + +export const findNbcode = (extensionPath: string): string => { + let nbcode = os.platform() === 'win32' ? + os.arch() === 'x64' ? 'nbcode64.exe' : 'nbcode.exe' + : 'nbcode.sh'; + let nbcodePath = path.join(extensionPath, "nbcode", "bin", nbcode); + + let nbcodePerm = fs.statSync(nbcodePath); + if (!nbcodePerm.isFile()) { + throw `Cannot execute ${nbcodePath}`; + } + if (os.platform() !== 'win32') { + fs.chmodSync(path.join(extensionPath, "nbcode", "bin", nbcode), "744"); + fs.chmodSync(path.join(extensionPath, "nbcode", "platform", "lib", "nbexec.sh"), "744"); + fs.chmodSync(path.join(extensionPath, "nbcode", "java", "maven", "bin", "mvn.sh"), "744"); + } + return nbcodePath; +} + +export const restartWithJDKLater = (time: number, notifyKill: boolean): void => { + LOGGER.log(`Restart of ${extConstants.SERVER_NAME} requested in ${time / 1000} s.`); + const nbProcessManager = globalVars.nbProcessManager; + + setTimeout(() => globalVars.clientPromise.restartExtension(nbProcessManager, notifyKill), time); +}; \ No newline at end of file diff --git a/vscode/src/nbcode.ts b/vscode/src/nbcode.ts deleted file mode 100644 index b7dec2c..0000000 --- a/vscode/src/nbcode.ts +++ /dev/null @@ -1,167 +0,0 @@ -/* - * Copyright (c) 2023, Oracle and/or its affiliates. - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -'use strict'; - -import * as fs from 'fs'; -import * as os from 'os'; -import * as path from 'path'; -import { spawn, ChildProcessByStdio } from 'child_process'; -import { Readable } from 'stream'; -import { env } from 'process'; - -export interface LaunchInfo { - clusters: string[]; - extensionPath: string; - storagePath: string; - jdkHome: string | unknown; - projectSearchRoots? : string; - verbose? : boolean; - enableModules? : string[]; - disableModules? : string[]; -} - -function find(info: LaunchInfo): string { - let nbcode = os.platform() === 'win32' ? - os.arch() === 'x64' ? 'nbcode64.exe' : 'nbcode.exe' - : 'nbcode.sh'; - let nbcodePath = path.join(info.extensionPath, "nbcode", "bin", nbcode); - - let nbcodePerm = fs.statSync(nbcodePath); - if (!nbcodePerm.isFile()) { - throw `Cannot execute ${nbcodePath}`; - } - if (os.platform() !== 'win32') { - fs.chmodSync(path.join(info.extensionPath, "nbcode", "bin", nbcode), "744"); - fs.chmodSync(path.join(info.extensionPath, "nbcode", "platform", "lib", "nbexec.sh"), "744"); - fs.chmodSync(path.join(info.extensionPath, "nbcode", "java", "maven", "bin", "mvn.sh"), "744"); - } - return nbcodePath; -} - -function enableDisableModules( - info: LaunchInfo, - userDir : string, - modules : string[] | undefined, - enable : boolean) { - if (modules) { - for (var i = 0; i < modules.length; i++) { - const module = modules[i]; - const moduleXml : string = module.replace(/\./g, "-") + ".xml"; - var xmlContent : string = ""; - const clusters : string[] = fs.readdirSync(path.join(info.extensionPath, "nbcode")); - for (var c = 0; c < clusters.length; c++) { - const sourceXmlPath : string = path.join(info.extensionPath, "nbcode", clusters[c], "config", "Modules", moduleXml); - if (fs.existsSync(sourceXmlPath)) { - xmlContent = fs.readFileSync(sourceXmlPath).toString(); - } - } - xmlContent = xmlContent.replace(`<param name="enabled">${!enable}</param>`, `<param name="enabled">${enable}</param>`); - fs.mkdirSync(path.join(userDir, "config", "Modules"), {recursive: true}); - fs.writeFileSync(path.join(userDir, "config", "Modules", moduleXml), xmlContent); - } - } -} - -export function launch( - info: LaunchInfo, - ...extraArgs : string[] -): ChildProcessByStdio<null, Readable, Readable> { - let nbcodePath = find(info); - - const userDir = path.join(info.storagePath, "userdir"); - fs.mkdirSync(userDir, {recursive: true}); - let userDirPerm = fs.statSync(userDir); - if (!userDirPerm.isDirectory()) { - throw `Cannot create ${userDir}`; - } - - enableDisableModules(info, userDir, info.disableModules, false); - enableDisableModules(info, userDir, info.enableModules, true); - - let clusterPath = info.clusters.join(path.delimiter); - let ideArgs: string[] = [ - '--userdir', userDir - ]; - if (info.jdkHome) { - ideArgs.push('--jdkhome', info.jdkHome as string); - } - - if (info.projectSearchRoots) { - ideArgs.push(`-J-Dproject.limitScanRoot=${info.projectSearchRoots}`); - } - - if (info.verbose) { - ideArgs.push('-J-Dnetbeans.logger.console=true'); - } - ideArgs.push(`-J-Dnetbeans.extra.dirs=${clusterPath}`) - if (env['netbeans.extra.options']) { - ideArgs.push(...env['netbeans.extra.options'].split(' ')); - } - ideArgs.push(...extraArgs); - -// if (env['netbeans_debug'] && extraArgs && extraArgs.find(s => s.includes("--list"))) { -// ideArgs.push(...['-J-Xdebug', '-J-Dnetbeans.logger.console=true', '-J-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=8000']); -// } - - let process: ChildProcessByStdio<any, Readable, Readable> = spawn(nbcodePath, ideArgs, { - cwd : userDir, - stdio : ["ignore", "pipe", "pipe"], - }); - return process; -} - -if (typeof process === 'object' && process.argv0 === 'node') { - let extension = path.join(process.argv[1], '..', '..'); - let nbcode = path.join(extension, 'nbcode'); - if (!fs.existsSync(nbcode)) { - throw `Cannot find ${nbcode}. Try npm run compile first!`; - } - let clusters = fs.readdirSync(nbcode).filter(c => c !== 'bin' && c !== 'etc').map(c => path.join(nbcode, c)); - let args = process.argv.slice(2); - let json = JSON.parse("" + fs.readFileSync(path.join(extension, 'package.json'))); - let storage; - - if (!env.nbcode_userdir || env.nbcode_userdir == 'global') { - storage = path.join(os.platform() === 'darwin' ? - path.join(os.homedir(), 'Library', 'Application Support') : - path.join(os.homedir(), '.config'), - 'Code', 'User', 'globalStorage', json.publisher + '.' + json.name); - } else { - storage = env.nbcode_userdir; - } - console.log('Launching NBLS with user directory: ' + storage) - let info = { - clusters : clusters, - extensionPath: extension, - storagePath : storage, - jdkHome : null - }; - let p = launch(info, ...args); - p.stdout.on('data', function(data) { - console.log(data.toString()); - }); - p.stderr.on('data', function(data) { - console.log(data.toString()); - }); - p.on('close', (code) => { - console.log(`nbcode finished with status ${code}`); - }); -} diff --git a/vscode/src/test/suite/extension.test.ts b/vscode/src/test/suite/extension.test.ts new file mode 100644 index 0000000..c0d82c5 --- /dev/null +++ b/vscode/src/test/suite/extension.test.ts @@ -0,0 +1,302 @@ + +/* + * Copyright (c) 2023, Oracle and/or its affiliates. + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/* This file has been modified for Oracle Java SE extension */ + +import * as assert from 'assert'; +import * as fs from 'fs'; +import * as path from 'path'; +import * as vscode from 'vscode'; +import * as myExplorer from '../../explorer'; + +import { CodeAction, commands, extensions, Selection, Uri, window, workspace, TreeItem } from 'vscode'; +import { assertWorkspace, dumpJava, getFilePaths, openFile, prepareProject, replaceCode} from './testutils'; +import {SAMPLE_CODE_FORMAT_DOCUMENT, SAMPLE_CODE_SORT_IMPORTS, SAMPLE_CODE_UNUSED_IMPORTS } from './constants'; +import { extConstants } from '../../constants'; + +suite('Extension Test Suite', function () { + window.showInformationMessage('Start all tests.'); + + const filePaths = getFilePaths(); + + // Create project which used be used for testing + this.beforeAll(async () => { + await prepareProject(filePaths); + }).timeout(10000); + + // This test must be run first, in order to activate the extension and wait for the activation to complete + test("Extension loaded and activated", async () => { + const extension = extensions.getExtension('oracle.oracle-java'); + assert(extension, "No Java extension found!"); + + const api = await extension.activate(); + assert(extension?.isActive, "true"); + assert.ok(api.version, "Some version is specified"); + + let cannotReassignVersion = false; + try { + api.version = "different"; + } catch (e) { + cannotReassignVersion = true; + } + assert.ok(cannotReassignVersion, "Cannot reassign value of version"); + + }).timeout(10000); + + // Test if clusters are loaded or not + test('Find clusters', async () => { + const nbcode = extensions.getExtension('oracle.oracle-java'); + assert(nbcode); + + const extraCluster = path.join(nbcode.extensionPath, "nbcode", "extra"); + let clusters = findClusters('non-existent'). + // ignore 'extra' cluster in the extension path, since nbjavac is there during development: + filter(s => !s.startsWith(extraCluster)); + + let found: string[] = []; + function assertCluster(name: string) { + for (let c of clusters) { + if (c.endsWith('/' + name)) { + found.push(c); + return; + } + } + assert.fail(`Cannot find ${name} among ${clusters}`); + } + + assertCluster('extide'); + assertCluster('ide'); + assertCluster('java'); + assertCluster('nbcode'); + assertCluster('platform'); + assertCluster('webcommon'); + assertCluster('harness'); + + for (let c of found) { + assert.ok(c.startsWith(nbcode.extensionPath), `All extensions are below ${nbcode.extensionPath}, but: ${c}`); + } + }).timeout(10000); + + // Check if Jdk commands have been loaded + test("Jdk commands loaded", async () => { + let commandsList = await commands.getCommands(true); + + let containsJdkCommands: Boolean = false; + for (const command of commandsList) { + if (command.indexOf("jdk.") === 0) { + containsJdkCommands = true; + } + } + + assert.ok(containsJdkCommands, "No Jdk command has been loaded"); + }).timeout(10000); + + // Check if format document command is executed successfully + test("Format document", async () => { + const editor = await openFile(filePaths.formatDocument); + await commands.executeCommand('editor.action.formatDocument'); + + const formattedCode = editor.document.getText().split('\n').length; + const unformattedCode = SAMPLE_CODE_FORMAT_DOCUMENT.split('\n').length; + const isDocumentFormatted = formattedCode > unformattedCode; + assert.ok(isDocumentFormatted, "document is not formatted"); + }).timeout(10000); + + // Check if imports are getting sorted on saving document + test("Sort imports", async () => { + const editor = await openFile(filePaths.sortImports); + await replaceCode(editor, SAMPLE_CODE_SORT_IMPORTS); + + const isSaved = await editor.document.save(); + assert.ok(isSaved, "document cannot be saved"); + + const savedCode = editor.document.getText(); + const isImportsSorted = savedCode.indexOf('import java.util.Date;') > + savedCode.indexOf('import java.util.ArrayList;'); + assert.ok(isImportsSorted, "Imports are not sorted"); + + }).timeout(10000); + + // Check if unused imports are getting removed on saving document + test("Remove unused imports", async () => { + const editor = await openFile(filePaths.unusedImports); + await replaceCode(editor, SAMPLE_CODE_UNUSED_IMPORTS); + + const isSaved = await editor.document.save(); + assert.ok(isSaved, "document cannot be saved"); + + const savedCode = editor.document.getText(); + const areUnusedImportsRemoved = savedCode.indexOf('import java.lang.Float;') === -1 && + savedCode.indexOf('import java.lang.Integer;') === -1; + assert.ok(areUnusedImportsRemoved, "Unused imports are not removed"); + + }).timeout(10000); + + // Check if refactor actions are getting showing on UI and if they are working + test("Refactor actions executing", async () => { + const editor = await openFile(filePaths.refactorActions); + const doc = editor.document; + const sel = new Selection(doc.lineAt(12).range.start, doc.lineAt(12).range.end); + editor.selections = [sel]; + + const refactorActions = await commands.executeCommand<CodeAction[]>( + 'vscode.executeCodeActionProvider', + doc.uri, + sel + ); + + if (refactorActions && refactorActions.length > 0) { + for await (const action of refactorActions) { + if (action.command && action.command.arguments) { + if (action.command.command === extConstants.COMMAND_PREFIX + ".surround.with") { + //this action has a popup where the user needs to + //select a template that should be used for the surround: + continue; + } + await commands.executeCommand(action.command.command, ...action.command.arguments); + await commands.executeCommand('undo'); + } + } + } + }).timeout(10000); + + // Tests explorer is loading properly + test("Test Explorer tests", async () => { + let folder: string = assertWorkspace(); + + try { + console.log("Test: load workspace tests"); + const workspaceFolder = (workspace.workspaceFolders!)[0]; + let tests: any = await commands.executeCommand("jdk.load.workspace.tests", workspaceFolder.uri.toString()); + console.log(`Test: load workspace tests finished with ${tests}`); + assert.ok(tests, "No tests returned for workspace"); + assert.strictEqual(tests.length, 2, `Invalid number of test suites returned`); + assert.strictEqual(tests[0].name, 'pkg.MainTest', `Invalid test suite name returned`); + assert.strictEqual(tests[0].tests.length, 1, `Invalid number of tests in suite returned`); + assert.strictEqual(tests[0].tests[0].name, 'testGetName', `Invalid test name returned`); + assert.strictEqual(tests[1].name, 'pkg.MainTest$NestedTest', `Invalid test suite name returned`); + assert.strictEqual(tests[1].tests.length, 1, `Invalid number of tests in suite returned`); + assert.strictEqual(tests[1].tests[0].name, 'testTrue', `Invalid test name returned`); + + console.log("Test: run all workspace tests"); + await vscode.commands.executeCommand(extConstants.COMMAND_PREFIX + '.run.test', workspaceFolder.uri.toString()); + console.log(`Test: run all workspace tests finished`); + } catch (error) { + dumpJava(); + throw error; + } + }).timeout(10000); + + // Check if compile workspace command is excuted succesfully + test("Compile workspace", async () => { + let folder: string = assertWorkspace(); + const compile = await commands.executeCommand('jdk.workspace.compile'); + assert.ok(compile, " Compile workspace command not working"); + + + const mainClass = path.join(folder, 'target', 'classes', 'pkg', 'Main.class'); + assert.ok(fs.statSync(mainClass).isFile(), "Class created by compilation: " + mainClass); + + myExplorer.createViewProvider(await awaitClient(), "foundProjects").then(async (lvp) => { + const firstLevelChildren = await (lvp.getChildren() as Thenable<any[]>); + assert.strictEqual(firstLevelChildren.length, 1, "One child under the root"); + const item = await (lvp.getTreeItem(firstLevelChildren[0]) as Thenable<TreeItem>); + assert.strictEqual(item?.label, "basicapp", "Element is named as the Maven project"); + }); + }).timeout(10000); + + // Get Project info + test("Get project sources, classpath, and packages", async () => { + let folder: string = assertWorkspace(); + try { + console.log("Test: get project java source roots"); + let res: any = await commands.executeCommand("jdk.java.get.project.source.roots", Uri.file(folder).toString()); + console.log(`Test: get project java source roots finished with ${res}`); + assert.ok(res, "No java source root returned"); + assert.strictEqual(res.length, 2, `Invalid number of java roots returned`); + assert.strictEqual(res[0], path.join('file:', folder, 'src', 'main', 'java') + path.sep, `Invalid java main source root returned`); + assert.strictEqual(res[1], path.join('file:', folder, 'src', 'test', 'java') + path.sep, `Invalid java test source root returned`); + + console.log("Test: get project resource roots"); + res = await commands.executeCommand("jdk.java.get.project.source.roots", Uri.file(folder).toString(), 'resources'); + console.log(`Test: get project resource roots finished with ${res}`); + assert.ok(res, "No resource root returned"); + assert.strictEqual(res.length, 1, `Invalid number of resource roots returned`); + assert.strictEqual(res[0], path.join('file:', folder, 'src', 'main', 'resources') + path.sep, `Invalid resource root returned`); + + console.log("Test: get project compile classpath"); + res = await commands.executeCommand("jdk.java.get.project.classpath", Uri.file(folder).toString()); + console.log(`Test: get project compile classpath finished with ${res}`); + assert.ok(res, "No compile classpath returned"); + assert.strictEqual(res.length, 9, `Invalid number of compile classpath roots returned`); + assert.ok(res.find((item: any) => item === path.join('file:', folder, 'target', 'classes') + path.sep, `Invalid compile classpath root returned`)); + + console.log("Test: get project source classpath"); + res = await commands.executeCommand("jdk.java.get.project.classpath", Uri.file(folder).toString(), 'SOURCE'); + console.log(`Test: get project source classpath finished with ${res}`); + assert.ok(res, "No source classpath returned"); + assert.strictEqual(res.length, 3, `Invalid number of source classpath roots returned`); + assert.ok(res.find((item: any) => item === path.join('file:', folder, 'src', 'main', 'java') + path.sep, `Invalid source classpath root returned`)); + assert.ok(res.find((item: any) => item === path.join('file:', folder, 'src', 'main', 'resources') + path.sep, `Invalid source classpath root returned`)); + assert.ok(res.find((item: any) => item === path.join('file:', folder, 'src', 'test', 'java') + path.sep, `Invalid source classpath root returned`)); + + console.log("Test: get project boot classpath"); + res = await commands.executeCommand("jdk.java.get.project.classpath", Uri.file(folder).toString(), 'BOOT'); + console.log(`Test: get project boot classpath finished with ${res}`); + assert.ok(res, "No boot classpath returned"); + assert.ok(res.length > 0, `Invalid number of boot classpath roots returned`); + + console.log("Test: get project boot source classpath"); + res = await commands.executeCommand("jdk.java.get.project.classpath", Uri.file(folder).toString(), 'BOOT', true); + console.log(`Test: get project boot source classpath finished with ${res}`); + assert.ok(res, "No boot source classpath returned"); + assert.ok(res.length > 0, `Invalid number of boot source classpath roots returned`); + + console.log("Test: get all project packages"); + res = await commands.executeCommand("jdk.java.get.project.packages", Uri.file(folder).toString()); + console.log(`Test: get all project packages finished with ${res}`); + assert.ok(res, "No packages returned"); + assert.ok(res.length > 0, `Invalid number of packages returned`); + + console.log("Test: get project source packages"); + res = await commands.executeCommand("jdk.java.get.project.packages", Uri.file(folder).toString(), true); + console.log(`Test: get project source packages finished with ${res}`); + assert.ok(res, "No packages returned"); + assert.strictEqual(res.length, 1, `Invalid number of packages returned`); + assert.strictEqual(res[0], 'pkg', `Invalid package returned`); + } catch (error) { + dumpJava(); + throw error; + } + }).timeout(10000); + + // Check if clean workspace command is excuted succesfully + test("Clean workspace", async () => { + let folder: string = assertWorkspace(); + const clean = await commands.executeCommand('jdk.workspace.clean'); + assert.ok(clean, " Clean workspace command not working"); + + const mainClass = path.join(folder, 'target'); + assert.ok(!fs.existsSync(mainClass), "Class created by compilation: " + mainClass); + }).timeout(10000); + +}); \ No newline at end of file diff --git a/vscode/src/test/suite/general/explorer.test.ts b/vscode/src/test/suite/general/explorer.test.ts index 57c7f8e..e719d7c 100644 --- a/vscode/src/test/suite/general/explorer.test.ts +++ b/vscode/src/test/suite/general/explorer.test.ts @@ -30,7 +30,6 @@ import * as myExplorer from '../../../explorer'; suite('Explorer Test Suite', () => { vscode.window.showInformationMessage('Start explorer tests.'); - myExtension.enableConsoleLog(); test('Explorer can be created', async () => { const lvp = await myExplorer.createViewProvider(await myExtension.awaitClient(), 'foundProjects'); diff --git a/vscode/src/test/suite/testutils.ts b/vscode/src/test/suite/testutils.ts new file mode 100644 index 0000000..b5cd606 --- /dev/null +++ b/vscode/src/test/suite/testutils.ts @@ -0,0 +1,234 @@ + +/* + * Copyright (c) 2023, Oracle and/or its affiliates. + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/* This file has been modified for Oracle Java SE extension */ + +import * as assert from 'assert'; +import * as vscode from 'vscode'; +import * as fs from 'fs'; +import * as path from 'path'; +import { spawn, ChildProcessByStdio } from 'child_process'; +import { Readable } from 'stream'; +import { EXAMPLE_POM, MAIN_JAVA, MAIN_TEST_JAVA, SAMPLE_CODE_FORMAT_DOCUMENT, SAMPLE_CODE_REFACTOR, SAMPLE_CODE_SORT_IMPORTS, SAMPLE_CODE_UNUSED_IMPORTS } from './constants'; +import { extConstants } from '../../constants'; + +/** + * Folder path currently opened in VSCode workspace + * @returns String containing the folder path of the workspace + */ +export function assertWorkspace(): string { + assert.ok(vscode.workspace, "workspace is defined"); + const dirs = vscode.workspace.workspaceFolders; + assert.ok(dirs?.length, "There are some workspace folders: " + dirs); + assert.strictEqual(dirs.length, 1, "One folder provided"); + let folder: string = dirs[0].uri.fsPath; + + return folder; +} + +/** + * File paths of all the files and folders usd for testing + * @returns Object containing all the file and folder paths + */ +export function getFilePaths(): { [key: string]: string } { + let folder: string = assertWorkspace(); + + const filePaths: { [key: string]: string } = {}; + filePaths['pkg'] = path.join(folder, 'src', 'main', 'java', 'pkg'); + filePaths['testPkg'] = path.join(folder, 'src', 'test', 'java', 'pkg'); + filePaths['resources'] = path.join(folder, 'src', 'main', 'resources'); + + filePaths['mainJava'] = path.join(filePaths['pkg'], 'Main.java'); + filePaths['formatDocument'] = path.join(filePaths['pkg'], 'FormatDocument.java'); + filePaths['sortImports'] = path.join(filePaths['pkg'], 'SortImports.java'); + filePaths['unusedImports'] = path.join(filePaths['pkg'], 'UnusedImports.java'); + filePaths['refactorActions'] = path.join(filePaths['pkg'], 'RefactorActions.java'); + filePaths['mainTestJava'] = path.join(filePaths['testPkg'], 'MainTest.java'); + + filePaths['pom'] = path.join(folder, 'pom.xml'); + + return filePaths; +} + +/** + * Prepares the sample project for testing + * @param filePaths + * @returns promise that waits till all the files and folders are created + */ +export async function prepareProject(filePaths: { [key: string]: string }): Promise<void> { + await fs.promises.writeFile(filePaths['pom'], EXAMPLE_POM); + + await fs.promises.mkdir(filePaths['pkg'], { recursive: true }); + await fs.promises.mkdir(filePaths['resources'], { recursive: true }); + await fs.promises.mkdir(filePaths['testPkg'], { recursive: true }); + + await fs.promises.writeFile(filePaths['mainJava'], MAIN_JAVA); + + await fs.promises.writeFile(filePaths['mainTestJava'], MAIN_TEST_JAVA); + await vscode.workspace.saveAll(); + + await fs.promises.writeFile(filePaths['formatDocument'], SAMPLE_CODE_FORMAT_DOCUMENT); + await fs.promises.writeFile(filePaths['sortImports'], SAMPLE_CODE_SORT_IMPORTS); + await fs.promises.writeFile(filePaths['unusedImports'], SAMPLE_CODE_UNUSED_IMPORTS); + await fs.promises.writeFile(filePaths['refactorActions'], SAMPLE_CODE_REFACTOR); + + await waitProjectRecognized(filePaths.mainJava); +} + +/** + * Wait till all the commands of the extension are loaded + * @returns promise that timeouts till all the commands are loaded + */ +export async function waitCommandsReady(): Promise<void> { + return new Promise((resolve, reject) => { + function checkCommands(attempts: number, cb: () => void) { + try { + // this command is parameterless + vscode.commands.executeCommand("jdk.java.attachDebugger.configurations") + console.log("JDK commands ready."); + resolve(); + } catch (e) { + if (attempts > 0) { + console.log("Waiting for JDK commands to be registered, " + attempts + " attempts to go..."); + setTimeout(() => checkCommands(attempts - 1, cb), 100); + } else { + reject(new Error("Timeout waiting for JDK commands registration: " + e)); + } + } + } + awaitClient().then(() => checkCommands(5, () => { })); + }); +} + + +/** + * Ensures that the project that holds the parameter file was opened in JDK. + * @param someJavaFile + * @returns promise that will be fullfilled after the project opens in JDK. + */ +async function waitProjectRecognized(someJavaFile: string): Promise<void> { + return waitCommandsReady().then(() => { + const u: vscode.Uri = vscode.Uri.file(someJavaFile); + // clear out possible bad or negative caches. + return vscode.commands.executeCommand(extConstants.COMMAND_PREFIX + ".clear.project.caches").then( + // this should assure opening the root with the created project. + () => vscode.commands.executeCommand(extConstants.COMMAND_PREFIX + ".java.get.project.packages", u.toString()) + ); + }); +} + + +/** + * Replaces code in editor with the provided code + * @param editor + * @param code + * @returns promise that will have replaced code in the editor + */ +export async function replaceCode(editor: vscode.TextEditor | undefined, code: string): Promise<void> { + const doc = editor?.document; + assert(doc !== undefined, 'editor cannot be initialzed'); + + const range = new vscode.Range(doc.lineAt(0).range.start, doc.lineAt(doc.lineCount - 1).range.end) + + await editor?.edit(editBuilder => { + editBuilder.replace(range, code); + }); +} + +/** + * Opens a file in VScode workspace + * @param filePath + * @returns promise that contains instance of the editor opened + */ +export async function openFile(filePath: string): Promise<vscode.TextEditor> { + const document: vscode.TextDocument = await vscode.workspace.openTextDocument(vscode.Uri.file(filePath)); + await vscode.window.showTextDocument(document); + const editor = vscode.window.activeTextEditor; + assert(editor !== undefined, 'editor cannot be initialzed'); + + return editor; +} + +/** + * If some error is encountered in the tests then it dumps java process + * @returns promise that dumps the java process + */ +export async function dumpJava(): Promise<void> { + const cmd = 'jps'; + const args = ['-v']; + console.log(`Running: ${cmd} ${args.join(' ')}`); + let p: ChildProcessByStdio<null, Readable, Readable> = spawn(cmd, args, { + stdio: ["ignore", "pipe", "pipe"], + }); + let n = await new Promise<number>((r, e) => { + p.stdout.on('data', function (d: any) { + console.log(d.toString()); + }); + p.stderr.on('data', function (d: any) { + console.log(d.toString()); + }); + p.on('close', function (code: number) { + r(code); + }); + }); + console.log(`${cmd} ${args.join(' ')} finished with code ${n}`); +} + +export const awaitClient = async () : Promise<NbLanguageClient> => { + const extension = vscode.extensions.getExtension(extConstants.ORACLE_VSCODE_EXTENSION_ID); + if (!extension) { + return Promise.reject(new Error(l10n.value("jdk.extension.notInstalled.label"))); + } + if(extension.isActive){ + return globalVars.clientPromise.client; + } + const waitForExtenstionActivation : Thenable<NbLanguageClient> = extension.activate().then(async () => { + return await globalVars.clientPromise.client; + }); + return Promise.resolve(waitForExtenstionActivation); +} + +export function findClusters(myPath : string): string[] { + let clusters = []; + for (let e of vscode.extensions.all) { + if (e.extensionPath === myPath) { + continue; + } + const dir = path.join(e.extensionPath, 'nbcode'); + if (!fs.existsSync(dir)) { + continue; + } + const exists = fs.readdirSync(dir); + for (let clusterName of exists) { + let clusterPath = path.join(dir, clusterName); + let clusterModules = path.join(clusterPath, 'config', 'Modules'); + if (!fs.existsSync(clusterModules)) { + continue; + } + let perm = fs.statSync(clusterModules); + if (perm.isDirectory()) { + clusters.push(clusterPath); + } + } + } + return clusters; +} diff --git a/vscode/src/test/testutils.ts b/vscode/src/test/testutils.ts index 493d748..37efddf 100644 --- a/vscode/src/test/testutils.ts +++ b/vscode/src/test/testutils.ts @@ -24,7 +24,6 @@ import * as assert from "assert"; import * as fs from "fs"; import * as glob from 'glob'; -import * as myExtension from "../extension"; import * as Mocha from 'mocha'; import * as path from "path"; import { promisify } from "util"; @@ -32,6 +31,10 @@ import { Readable } from "stream"; import { spawn, ChildProcessByStdio, exec } from "child_process"; import * as vscode from "vscode"; import { EXAMPLE_POM, MAIN_JAVA, MAIN_TEST_JAVA, SAMPLE_APP_JAVA, SAMPLE_BUILD_GRADLE, SAMPLE_CODE_FORMAT_DOCUMENT, SAMPLE_CODE_REFACTOR, SAMPLE_CODE_SORT_IMPORTS, SAMPLE_CODE_UNUSED_IMPORTS, SAMPLE_SETTINGS_GRADLE } from "./constants"; +import { NbLanguageClient } from "../lsp/nbLanguageClient"; +import { extConstants } from "../constants"; +import { l10n } from "../localiser"; +import { globalVars } from "../extension"; /** * Folder path currently opened in VSCode workspace @@ -147,7 +150,7 @@ export async function waitCommandsReady(): Promise<void> { } } } - myExtension.awaitClient().then(() => checkCommands(5, () => { })); + awaitClient().then(() => checkCommands(5, () => { })); }); } @@ -161,12 +164,12 @@ export async function waitProjectRecognized(someJavaFile: string): Promise<void> const u: vscode.Uri = vscode.Uri.file(someJavaFile); // clear out possible bad or negative caches. return vscode.commands - .executeCommand(myExtension.COMMAND_PREFIX + ".clear.project.caches") + .executeCommand(extConstants.COMMAND_PREFIX + ".clear.project.caches") .then( // this should assure opening the root with the created project. () => vscode.commands.executeCommand( - myExtension.COMMAND_PREFIX + ".java.get.project.packages", + extConstants.COMMAND_PREFIX + ".java.get.project.packages", u.toString() ) ); @@ -328,3 +331,43 @@ export function runTestSuite(folder: string): Promise<void> { }); }); } + +export const awaitClient = async () : Promise<NbLanguageClient> => { + const extension = vscode.extensions.getExtension(extConstants.ORACLE_VSCODE_EXTENSION_ID); + if (!extension) { + return Promise.reject(new Error(l10n.value("jdk.extension.notInstalled.label"))); + } + if(extension.isActive){ + return globalVars.clientPromise.client; + } + const waitForExtenstionActivation : Thenable<NbLanguageClient> = extension.activate().then(async () => { + return await globalVars.clientPromise.client; + }); + return Promise.resolve(waitForExtenstionActivation); +} + +export function findClusters(myPath : string): string[] { + let clusters = []; + for (let e of vscode.extensions.all) { + if (e.extensionPath === myPath) { + continue; + } + const dir = path.join(e.extensionPath, 'nbcode'); + if (!fs.existsSync(dir)) { + continue; + } + const exists = fs.readdirSync(dir); + for (let clusterName of exists) { + let clusterPath = path.join(dir, clusterName); + let clusterModules = path.join(clusterPath, 'config', 'Modules'); + if (!fs.existsSync(clusterModules)) { + continue; + } + let perm = fs.statSync(clusterModules); + if (perm.isDirectory()) { + clusters.push(clusterPath); + } + } + } + return clusters; +} diff --git a/vscode/src/testAdapter.ts b/vscode/src/testAdapter.ts index b2ef284..1a54331 100644 --- a/vscode/src/testAdapter.ts +++ b/vscode/src/testAdapter.ts @@ -24,7 +24,7 @@ import { commands, debug, tests, workspace, CancellationToken, TestController, TestItem, TestRunProfileKind, TestRunRequest, Uri, TestRun, TestMessage, Location, Position, MarkdownString } from "vscode"; import * as path from 'path'; import { asRange, TestCase, TestSuite } from "./protocol"; -import { COMMAND_PREFIX } from "./extension"; +import { extConstants } from "./constants"; export class NbTestAdapter { @@ -45,7 +45,7 @@ export class NbTestAdapter { async load(): Promise<void> { for (let workspaceFolder of workspace.workspaceFolders || []) { - const loadedTests: any = await commands.executeCommand(COMMAND_PREFIX + '.load.workspace.tests', workspaceFolder.uri.toString()); + const loadedTests: any = await commands.executeCommand(extConstants.COMMAND_PREFIX + '.load.workspace.tests', workspaceFolder.uri.toString()); if (loadedTests) { loadedTests.forEach((suite: TestSuite) => { this.updateTests(suite); @@ -68,7 +68,7 @@ export class NbTestAdapter { this.set(item, 'enqueued'); const idx = item.id.indexOf(':'); if (!cancellation.isCancellationRequested) { - await commands.executeCommand(request.profile?.kind === TestRunProfileKind.Debug ? COMMAND_PREFIX + '.debug.single' : COMMAND_PREFIX + '.run.single', item.uri.toString(), idx < 0 ? undefined : item.id.slice(idx + 1)); + await commands.executeCommand(request.profile?.kind === TestRunProfileKind.Debug ? extConstants.COMMAND_PREFIX + '.debug.single' : extConstants.COMMAND_PREFIX + '.run.single', item.uri.toString(), idx < 0 ? undefined : item.id.slice(idx + 1)); } } } @@ -76,7 +76,7 @@ export class NbTestAdapter { this.testController.items.forEach(item => this.set(item, 'enqueued')); for (let workspaceFolder of workspace.workspaceFolders || []) { if (!cancellation.isCancellationRequested) { - await commands.executeCommand(request.profile?.kind === TestRunProfileKind.Debug ? COMMAND_PREFIX + '.debug.test': COMMAND_PREFIX + '.run.test', workspaceFolder.uri.toString()); + await commands.executeCommand(request.profile?.kind === TestRunProfileKind.Debug ? extConstants.COMMAND_PREFIX + '.debug.test': extConstants.COMMAND_PREFIX + '.run.test', workspaceFolder.uri.toString()); } } } @@ -312,7 +312,7 @@ export class NbTestAdapter { } const result = regExp.exec(line); if (result) { - message.appendText(result[1]).appendText('(').appendMarkdown(`[${result[3]}](command:${COMMAND_PREFIX}.open.stacktrace?${encodeURIComponent(JSON.stringify([currentTestUri, result[2], result[4], +result[5]]))})`).appendText(')'); + message.appendText(result[1]).appendText('(').appendMarkdown(`[${result[3]}](command:${extConstants.COMMAND_PREFIX}.open.stacktrace?${encodeURIComponent(JSON.stringify([currentTestUri, result[2], result[4], +result[5]]))})`).appendText(')'); } else { message.appendText(line); } diff --git a/vscode/src/utils.ts b/vscode/src/utils.ts index e578913..08adbc4 100644 --- a/vscode/src/utils.ts +++ b/vscode/src/utils.ts @@ -23,6 +23,7 @@ import * as fs from 'fs'; import { promisify } from "util"; import * as crypto from 'crypto'; import { l10n } from './localiser'; +import { extConstants } from './constants'; class InputFlowAction { static back = new InputFlowAction(); @@ -275,4 +276,6 @@ export const calculateChecksum = async (filePath: string, algorithm: string = 's const checksum = hash.digest('hex'); return checksum; -} \ No newline at end of file +} + +export const appendPrefixToCommand = (command: string) => `${extConstants.COMMAND_PREFIX}.${command}`; From af1466b3795ee7c9ca64914ae393a3e949f4554e Mon Sep 17 00:00:00 2001 From: Achal Talati <achal.talati@oracle.com> Date: Mon, 7 Oct 2024 23:16:28 +0530 Subject: [PATCH 02/17] Added commands module --- vscode/src/commands/buildOperations.ts | 52 ++++ vscode/src/commands/cache.ts | 60 ++++ vscode/src/commands/commands.ts | 80 +++++ vscode/src/commands/create.ts | 108 +++++++ vscode/src/commands/navigation.ts | 109 +++++++ vscode/src/commands/refactor.ts | 94 ++++++ vscode/src/commands/register.ts | 53 ++++ vscode/src/commands/types.ts | 19 ++ vscode/src/commands/utils.ts | 116 +++++++ vscode/src/commands/webViews.ts | 29 ++ vscode/src/explorer.ts | 2 +- vscode/src/extension.ts | 313 +------------------ vscode/src/propertiesView/propertiesView.ts | 2 +- vscode/src/protocol.ts | 324 -------------------- vscode/src/testAdapter.ts | 2 +- 15 files changed, 726 insertions(+), 637 deletions(-) create mode 100644 vscode/src/commands/buildOperations.ts create mode 100644 vscode/src/commands/cache.ts create mode 100644 vscode/src/commands/commands.ts create mode 100644 vscode/src/commands/create.ts create mode 100644 vscode/src/commands/navigation.ts create mode 100644 vscode/src/commands/refactor.ts create mode 100644 vscode/src/commands/register.ts create mode 100644 vscode/src/commands/types.ts create mode 100644 vscode/src/commands/utils.ts create mode 100644 vscode/src/commands/webViews.ts delete mode 100644 vscode/src/protocol.ts diff --git a/vscode/src/commands/buildOperations.ts b/vscode/src/commands/buildOperations.ts new file mode 100644 index 0000000..ca24805 --- /dev/null +++ b/vscode/src/commands/buildOperations.ts @@ -0,0 +1,52 @@ +/* + Copyright (c) 2023-2024, Oracle and/or its affiliates. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ +import { LOGGER } from "../extension"; +import { l10n } from "../localiser"; +import { extCommands, nbCommands } from "./commands"; +import { ICommand } from "./types"; +import { wrapCommandWithProgress, wrapProjectActionWithProgress } from "./utils"; + +const compileWorkspaceCHandler = () => { + wrapCommandWithProgress(nbCommands.buildWorkspace, l10n.value('jdk.extension.command.progress.compilingWorkSpace'), LOGGER.getOutputChannel(), true); +} +const cleanWorkspaceHandler = () => { + wrapCommandWithProgress(nbCommands.cleanWorkspace,l10n.value('jdk.extension.command.progress.cleaningWorkSpace'), LOGGER.getOutputChannel(), true) +} + +const compileProjectHandler = (args: any) => { + wrapProjectActionWithProgress('build', undefined, l10n.value('jdk.extension.command.progress.compilingProject'), LOGGER.getOutputChannel(), true, args); +} + +const cleanProjectHandler = (args: any) => { + wrapProjectActionWithProgress('clean', undefined, l10n.value('jdk.extension.command.progress.cleaningProject'), LOGGER.getOutputChannel(), true, args); +} + + +export const registerBuildOperationCommands: ICommand[] = [ + { + command: extCommands.compileWorkspace, + handler: compileWorkspaceCHandler + }, { + command: extCommands.cleanWorkspace, + handler: cleanWorkspaceHandler + },{ + command: extCommands.compileProject, + handler: compileProjectHandler + },{ + command: extCommands.cleanProject, + handler: cleanProjectHandler + } +]; \ No newline at end of file diff --git a/vscode/src/commands/cache.ts b/vscode/src/commands/cache.ts new file mode 100644 index 0000000..1b69ed6 --- /dev/null +++ b/vscode/src/commands/cache.ts @@ -0,0 +1,60 @@ +/* + Copyright (c) 2023-2024, Oracle and/or its affiliates. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ +import { commands, window } from "vscode"; +import { globalVars } from "../extension"; +import { builtInCommands, extCommands } from "./commands"; +import { ICommand } from "./types"; +import { l10n } from "../localiser"; +import * as fs from 'fs'; +import * as path from 'path'; + +const deleteCache = async () => { + // TODO: Change workspace path to userdir path + const storagePath = globalVars.extensionInfo.getWorkspaceStorage()?.fsPath; + if (!storagePath) { + window.showErrorMessage(l10n.value("jdk.extenstion.cache.error_msg.cannotFindWrkSpacePath")); + return; + } + + const userDir = path.join(storagePath, "userdir"); + if (userDir && fs.existsSync(userDir)) { + const yes = l10n.value("jdk.extension.cache.label.confirmation.yes") + const cancel = l10n.value("jdk.extension.cache.label.confirmation.cancel") + const confirmation = await window.showInformationMessage('Are you sure you want to delete cache for this workspace and reload the window ?', + yes, cancel); + if (confirmation === yes) { + const reloadWindowActionLabel = l10n.value("jdk.extension.cache.label.reloadWindow"); + try { + await globalVars.clientPromise.stopClient(); + globalVars.deactivated = true; + await globalVars.nbProcessManager?.killProcess(false); + await fs.promises.rmdir(userDir, { recursive: true }); + await window.showInformationMessage(l10n.value("jdk.extenstion.message.cacheDeleted"), reloadWindowActionLabel); + } catch (err) { + await window.showErrorMessage(l10n.value("jdk.extenstion.error_msg.cacheDeletionError"), reloadWindowActionLabel); + } finally { + commands.executeCommand(builtInCommands.reloadWindow); + } + } + } else { + window.showErrorMessage(l10n.value("jdk.extension.cache.message.noUserDir")); + } +} + +export const registerCacheCommands: ICommand[] = [{ + command: extCommands.deleteCache, + handler: deleteCache +}]; \ No newline at end of file diff --git a/vscode/src/commands/commands.ts b/vscode/src/commands/commands.ts new file mode 100644 index 0000000..b11234e --- /dev/null +++ b/vscode/src/commands/commands.ts @@ -0,0 +1,80 @@ +/* + Copyright (c) 2023-2024, Oracle and/or its affiliates. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ +import { appendPrefixToCommand } from "../utils"; + + +export const extCommands = { + configureRunSettings: appendPrefixToCommand('workspace.configureRunSettings'), + newFromTemplate : appendPrefixToCommand('workspace.new'), + newProject: appendPrefixToCommand('workspace.newproject'), + openTest: appendPrefixToCommand('open.test'), + deleteCache: appendPrefixToCommand('delete.cache'), + downloadJdk: appendPrefixToCommand('download.jdk'), + compileWorkspace: appendPrefixToCommand('workspace.compile'), + cleanWorkspace: appendPrefixToCommand('workspace.clean'), + compileProject: appendPrefixToCommand('project.compile'), + cleanProject: appendPrefixToCommand('project.clean'), + openType: appendPrefixToCommand('open.type'), + goToSuperImpl: appendPrefixToCommand('java.goto.super.implementation'), + renameElement: appendPrefixToCommand('rename.element.at'), + surroundWith: appendPrefixToCommand('surround.with'), + generateCode: appendPrefixToCommand('generate.code'), + runTest: appendPrefixToCommand('run.test'), + debugTest: appendPrefixToCommand('debug.test'), + runSingle: appendPrefixToCommand('run.single'), + debugSingle: appendPrefixToCommand('debug.single'), + projectRun: appendPrefixToCommand('project.run'), + projectDebug: appendPrefixToCommand('project.debug'), + projectTest: appendPrefixToCommand('project.test'), + packageTest: appendPrefixToCommand('package.test'), + openStackTrace: appendPrefixToCommand('open.stacktrace'), + workspaceSymbols: appendPrefixToCommand('workspace.symbols'), + abstractMethodsComplete: appendPrefixToCommand('java.complete.abstract.methods'), + startupCondition: appendPrefixToCommand('startup.condition'), + nbEventListener: appendPrefixToCommand('addEventListener'), + editNodeProps: appendPrefixToCommand('node.properties.edit'), + selectEditorProjs: appendPrefixToCommand('select.editor.projects'), + attachDebugger: appendPrefixToCommand("java.attachDebugger.connector"), + startDebug: 'workbench.action.debug.start', +} + +export const builtInCommands = { + setCustomContext: 'setContext', + openFolder: 'vscode.openFolder', + reloadWindow: 'workbench.action.reloadWindow', + focusActiveEditorGroup: 'workbench.action.focusActiveEditorGroup', + goToEditorLocations: 'editor.action.goToLocations', + renameSymbol: 'editor.action.rename', + quickAccess: 'workbench.action.quickOpen', + openSettings: 'workbench.action.openSettings' +} + +export const nbCommands = { + newFromTemplate: appendPrefixToCommand('new.from.template'), + newProject: appendPrefixToCommand('new.project'), + goToTest: appendPrefixToCommand('go.to.test'), + quickOpen: appendPrefixToCommand('quick.open'), + superImpl: appendPrefixToCommand('java.super.implementation'), + resolveStackLocation: appendPrefixToCommand('resolve.stacktrace.location'), + implementAbstractMethods: appendPrefixToCommand('java.implement.all.abstract.methods'), + archiveFileContent: appendPrefixToCommand('get.archive.file.content'), + htmlProcessCmd: appendPrefixToCommand('htmlui.process.command'), + projectConfigurations: appendPrefixToCommand('project.configurations'), + debuggerConfigurations: appendPrefixToCommand('java.attachDebugger.configurations'), + runProjectAction: appendPrefixToCommand('project.run.action'), + buildWorkspace: appendPrefixToCommand('build.workspace'), + cleanWorkspace: appendPrefixToCommand('clean.workspace') +} \ No newline at end of file diff --git a/vscode/src/commands/create.ts b/vscode/src/commands/create.ts new file mode 100644 index 0000000..27f05ef --- /dev/null +++ b/vscode/src/commands/create.ts @@ -0,0 +1,108 @@ +/* + Copyright (c) 2023-2024, Oracle and/or its affiliates. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ +import { workspace, commands, Uri, window } from "vscode"; +import { LanguageClient } from "vscode-languageclient/node"; +import { nbCommands, builtInCommands, extCommands } from "./commands"; +import { l10n } from "../localiser"; +import * as os from 'os'; +import * as fs from 'fs'; +import { ICommand } from "./types"; +import { globalVars } from "../extension"; +import { getContextUri, isNbCommandRegistered } from "./utils"; +import { isString } from "../typesUtil"; + +const newFromTemplate = async (ctx: any, template: any) => { + const client: LanguageClient = await globalVars.clientPromise.client; + if (await isNbCommandRegistered(nbCommands.newFromTemplate)) { + const workspaces = workspace.workspaceFolders; + + if (!workspaces) { + const userHomeDir = os.homedir(); + const folderPath = await window.showInputBox({ + prompt: l10n.value('jdk.workspace.new.prompt'), + value: `${userHomeDir}` + }); + if (!folderPath?.trim()) return; + + if (!fs.existsSync(folderPath)) { + await fs.promises.mkdir(folderPath); + } + const folderPathUri = Uri.file(folderPath); + await commands.executeCommand(nbCommands.newFromTemplate, folderPathUri.toString()); + await commands.executeCommand(builtInCommands.openFolder, folderPathUri); + + return; + } + + // first give the template (if present), then the context, and then the open-file hint in the case the context is not specific enough + const params = []; + if (isString(template)) { + params.push(template); + } + params.push(getContextUri(ctx)?.toString(), window.activeTextEditor?.document?.uri?.toString()); + const res = await commands.executeCommand(nbCommands.newFromTemplate, ...params); + + if (isString(res)) { + let newFile = Uri.parse(res as string); + await window.showTextDocument(newFile, { preview: false }); + } else if (Array.isArray(res)) { + for (let r of res) { + if (isString(r)) { + let newFile = Uri.parse(r as string); + await window.showTextDocument(newFile, { preview: false }); + } + } + } + } else { + throw l10n.value("jdk.extenstion.error_msg.doesntSupportNewTeamplate", { client }); + } +} + +const newProject = async (ctx: any) => { + const client: LanguageClient = await globalVars.clientPromise.client; + if (await isNbCommandRegistered(nbCommands.newProject)) { + const res = await commands.executeCommand(nbCommands.newProject, getContextUri(ctx)?.toString()); + if (isString(res)) { + let newProject = Uri.parse(res as string); + + const OPEN_IN_NEW_WINDOW = l10n.value("jdk.extension.label.openInNewWindow"); + const ADD_TO_CURRENT_WORKSPACE = l10n.value("jdk.extension.label.addToWorkSpace"); + + const value = await window.showInformationMessage(l10n.value("jdk.extension.message.newProjectCreated"), + OPEN_IN_NEW_WINDOW, + ADD_TO_CURRENT_WORKSPACE); + + if (value === OPEN_IN_NEW_WINDOW) { + await commands.executeCommand(builtInCommands.openFolder, newProject, true); + } else if (value === ADD_TO_CURRENT_WORKSPACE) { + workspace.updateWorkspaceFolders(workspace.workspaceFolders ? workspace.workspaceFolders.length : 0, undefined, { uri: newProject }); + } + } + } else { + throw l10n.value("jdk.extenstion.error_msg.doesntSupportNewProject", { client }); + } +}; + + +export const registerCreateCommands: ICommand[] = [ + { + command: extCommands.newFromTemplate, + handler: newFromTemplate + }, { + command: extCommands.newProject, + handler: newProject + } +]; \ No newline at end of file diff --git a/vscode/src/commands/navigation.ts b/vscode/src/commands/navigation.ts new file mode 100644 index 0000000..8a31711 --- /dev/null +++ b/vscode/src/commands/navigation.ts @@ -0,0 +1,109 @@ +/* + Copyright (c) 2023-2024, Oracle and/or its affiliates. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ +import { commands, Position, window, Selection, Range, Uri } from "vscode"; +import { builtInCommands, extCommands, nbCommands } from "./commands"; +import { l10n } from "../localiser"; +import * as path from 'path'; +import { ICommand } from "./types"; +import { LanguageClient } from "vscode-languageclient/node"; +import { globalVars, LOGGER } from "../extension"; +import { getContextUri, isNbCommandRegistered, wrapCommandWithProgress } from "./utils"; + +const goToTest = async (ctx: any) => { + let client: LanguageClient = await globalVars.clientPromise.client; + if (await isNbCommandRegistered(nbCommands.goToTest)) { + try { + const res: any = await commands.executeCommand(nbCommands.goToTest, getContextUri(ctx)?.toString()); + if ("errorMessage" in res) { + throw new Error(res.errorMessage); + } + res?.providerErrors?.map((error: any) => { + if (error?.message) { + window.showErrorMessage(error.message); + } + }); + if (res?.locations?.length) { + if (res.locations.length === 1) { + const { file, offset } = res.locations[0]; + const filePath = Uri.parse(file); + const editor = await window.showTextDocument(filePath, { preview: false }); + if (offset != -1) { + const pos: Position = editor.document.positionAt(offset); + editor.selections = [new Selection(pos, pos)]; + const range = new Range(pos, pos); + editor.revealRange(range); + } + + } else { + const namePathMapping: { [key: string]: string } = {} + res.locations.forEach((fp: any) => { + const fileName = path.basename(fp.file); + namePathMapping[fileName] = fp.file + }); + const selected = await window.showQuickPick(Object.keys(namePathMapping), { + title: l10n.value("jdk.extension.fileSelector.label.selectFiles"), + placeHolder: l10n.value("jdk.extension.fileSelector.label.testFilesOrSourceFiles"), + canPickMany: true + }); + if (selected) { + for await (const filePath of selected) { + let file = Uri.parse(filePath); + await window.showTextDocument(file, { preview: false }); + } + } else { + window.showInformationMessage(l10n.value("jdk.extension.fileSelector.label.noFileSelected")); + } + } + } + } catch (err: any) { + window.showInformationMessage(err?.message || l10n.value("jdk.extension.fileSelector.label.noTestFound")); + } + } else { + throw l10n.value("jdk.extenstion.error_msg.doesntSupportGoToTest", { client }); + } +} + +const openTypeHandler = () => { + wrapCommandWithProgress(nbCommands.quickOpen, l10n.value('jdk.extension.command.progress.quickOpen'), LOGGER.getOutputChannel(), true).then(() => { + commands.executeCommand(builtInCommands.focusActiveEditorGroup); + }); +} + +const openStackHandler = async (uri: any, methodName: any, fileName: any, line: any) => { + const location: string | undefined = uri ? await commands.executeCommand(nbCommands.resolveStackLocation, uri, methodName, fileName) : undefined; + if (location) { + const lNum = line - 1; + window.showTextDocument(Uri.parse(location), { selection: new Range(new Position(lNum, 0), new Position(lNum, 0)) }); + } else { + if (methodName) { + const fqn: string = methodName.substring(0, methodName.lastIndexOf('.')); + commands.executeCommand(builtInCommands.quickAccess, '#' + fqn.substring(fqn.lastIndexOf('.') + 1)); + } + } +} + +export const registerNavigationCommands: ICommand[] = [ + { + command: extCommands.openTest, + handler: goToTest + },{ + command: extCommands.openType, + handler: openTypeHandler + },{ + command: extCommands.openStackTrace, + handler: openStackHandler + } +]; \ No newline at end of file diff --git a/vscode/src/commands/refactor.ts b/vscode/src/commands/refactor.ts new file mode 100644 index 0000000..0a141f6 --- /dev/null +++ b/vscode/src/commands/refactor.ts @@ -0,0 +1,94 @@ +/* + Copyright (c) 2023-2024, Oracle and/or its affiliates. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ +import { commands, window, Uri, Range, Location, workspace, Position } from "vscode"; +import { ICommand } from "./types"; +import { extConstants } from "../constants"; +import { builtInCommands, extCommands, nbCommands } from "./commands"; +import { l10n } from "../localiser"; +import { globalVars } from "../extension"; +import { WorkspaceEdit } from 'vscode-languageserver-protocol'; + +const goToSuperImplementationHandler = async () => { + if (window.activeTextEditor?.document.languageId !== extConstants.LANGUAGE_ID) { + return; + } + const uri = window.activeTextEditor.document.uri; + const position = window.activeTextEditor.selection.active; + const locations: any[] = await commands.executeCommand(nbCommands.superImpl, uri.toString(), position) || []; + return commands.executeCommand(builtInCommands.goToEditorLocations, window.activeTextEditor.document.uri, position, + locations.map(location => new Location(Uri.parse(location.uri), new Range(location.range.start.line, location.range.start.character, location.range.end.line, location.range.end.character))), + 'peek', l10n.value('jdk.extenstion.error_msg.noSuperImpl')); +} + +const renameElementHandler = async (offset: any) => { + const editor = window.activeTextEditor; + if (editor) { + await commands.executeCommand(builtInCommands.renameSymbol, [ + editor.document.uri, + editor.document.positionAt(offset), + ]); + } +} + +const surroundWithHandler = async (items: any) => { + const selected: any = await window.showQuickPick(items, { placeHolder: l10n.value('jdk.extension.command.quickPick.placeholder.surroundWith') }); + if (selected) { + if (selected.userData.edit) { + const client = await globalVars.clientPromise.client; + const edit = await client.protocol2CodeConverter.asWorkspaceEdit(selected.userData.edit as WorkspaceEdit); + await workspace.applyEdit(edit); + await commands.executeCommand(builtInCommands.focusActiveEditorGroup); + } + await commands.executeCommand(selected.userData.command.command, ...(selected.userData.command.arguments || [])); + } +} + +const codeGenerateHandler = async (command: any, data: any) => { + const edit: any = await commands.executeCommand(command, data); + if (edit) { + const client = await globalVars.clientPromise.client; + const wsEdit = await client.protocol2CodeConverter.asWorkspaceEdit(edit as WorkspaceEdit); + await workspace.applyEdit(wsEdit); + await commands.executeCommand(builtInCommands.focusActiveEditorGroup); + } +} + +const completeAbstractMethodsHandler = async () => { + const active = window.activeTextEditor; + if (active) { + const position = new Position(active.selection.start.line, active.selection.start.character); + await commands.executeCommand(nbCommands.implementAbstractMethods, active.document.uri.toString(), position); + } +} + +export const registerRefactorCommands: ICommand[] = [ + { + command: extCommands.goToSuperImpl, + handler: goToSuperImplementationHandler + }, { + command: extCommands.renameElement, + handler: renameElementHandler + }, { + command: extCommands.surroundWith, + handler: surroundWithHandler + }, { + command: extCommands.generateCode, + handler: codeGenerateHandler + }, { + command: extCommands.abstractMethodsComplete, + handler: completeAbstractMethodsHandler + } +]; \ No newline at end of file diff --git a/vscode/src/commands/register.ts b/vscode/src/commands/register.ts new file mode 100644 index 0000000..27f2b85 --- /dev/null +++ b/vscode/src/commands/register.ts @@ -0,0 +1,53 @@ +/* + Copyright (c) 2023-2024, Oracle and/or its affiliates. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ +import { commands, Disposable, ExtensionContext } from "vscode"; +import { ICommand } from "./types"; +import { registerCreateCommands } from "./create"; +import { registerCacheCommands } from "./cache"; +import { registerNavigationCommands } from "./navigation"; +import { registerWebviewCommands } from "./webViews"; +import { registerBuildOperationCommands } from "./buildOperations"; +import { registerRefactorCommands } from "./refactor"; + +type ICommandModules = Record<string, ICommand[]>; + +const commandModules: ICommandModules = { + create: registerCreateCommands, + cache: registerCacheCommands, + navigation: registerNavigationCommands, + webview: registerWebviewCommands, + buildOperations: registerBuildOperationCommands, + refactor: registerRefactorCommands +} + +export const subscribeCommands = (context: ExtensionContext) => { + for (const cmds of Object.values(commandModules)) { + for (const command of cmds) { + const cmdRegistered = registerCommand(command); + if (cmdRegistered) { + context.subscriptions.push(cmdRegistered); + } + } + } +} + +const registerCommand = (commandInfo: ICommand): Disposable | null => { + const { command, handler } = commandInfo; + if (command.trim().length && handler) { + return commands.registerCommand(command, handler); + } + return null; +} diff --git a/vscode/src/commands/types.ts b/vscode/src/commands/types.ts new file mode 100644 index 0000000..a019ac3 --- /dev/null +++ b/vscode/src/commands/types.ts @@ -0,0 +1,19 @@ +/* + Copyright (c) 2023-2024, Oracle and/or its affiliates. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ +export type ICommand = { + command: string, + handler: any +} diff --git a/vscode/src/commands/utils.ts b/vscode/src/commands/utils.ts new file mode 100644 index 0000000..5bc4128 --- /dev/null +++ b/vscode/src/commands/utils.ts @@ -0,0 +1,116 @@ +/* + Copyright (c) 2023-2024, Oracle and/or its affiliates. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ +import { commands, OutputChannel, ProgressLocation, Uri, window } from "vscode"; +import { nbCommands } from "./commands"; +import { ProjectActionParams } from "../lsp/protocol"; +import { LanguageClient } from "vscode-languageclient/node"; +import { globalVars, LOGGER } from "../extension"; +import { l10n } from "../localiser"; +import { LogLevel } from "../logger"; + +export const getContextUri = (ctx: any): Uri | undefined => { + if (ctx?.fsPath) { + return ctx as Uri; + } + if (ctx?.resourceUri) { + return ctx.resourceUri as Uri; + } + if (typeof ctx == 'string') { + try { + return Uri.parse(ctx, true); + } catch (err) { + return Uri.file(ctx); + } + } + + return window.activeTextEditor?.document?.uri; +} + +export const isNbCommandRegistered = async (command: string) => { + const registeredCommands = await commands.getCommands(); + return registeredCommands.includes(command); +} + +/** + * Executes a project action. It is possible to provide an explicit configuration to use (or undefined), display output from the action etc. + * Arguments are attempted to parse as file or editor references or Nodes; otherwise they are attempted to be passed to the action as objects. + * + * @param action ID of the project action to run + * @param configuration configuration to use or undefined - use default/active one. + * @param title Title for the progress displayed in vscode + * @param log output channel that should be revealed + * @param showOutput if true, reveals the passed output channel + * @param args additional arguments + * @returns Promise for the command's result + */ +export const wrapProjectActionWithProgress = (action: string, configuration: string | undefined, title: string, log?: OutputChannel, showOutput?: boolean, ...args: any[]): Thenable<unknown> => { + let items = []; + let actionParams = { + action: action, + configuration: configuration, + } as ProjectActionParams; + for (let item of args) { + let u: Uri | undefined; + if (item?.fsPath) { + items.push((item.fsPath as Uri).toString()); + } else if (item?.resourceUri) { + items.push((item.resourceUri as Uri).toString()); + } else { + items.push(item); + } + } + return wrapCommandWithProgress(nbCommands.runProjectAction, title, log, showOutput, actionParams, ...items); +} + +export const wrapCommandWithProgress = (lsCommand: string, title: string, log?: OutputChannel, showOutput?: boolean, ...args: any[]): Thenable<unknown> => { + return window.withProgress({ location: ProgressLocation.Window }, p => { + return new Promise(async (resolve, reject) => { + let c: LanguageClient = await globalVars.clientPromise.client; + if (await isNbCommandRegistered(lsCommand)) { + p.report({ message: title }); + c.outputChannel.show(true); + const start = new Date().getTime(); + try { + if (log) { + LOGGER.log(`starting ${lsCommand}`); + } + const res = await commands.executeCommand(lsCommand, ...args) + const elapsed = new Date().getTime() - start; + if (log) { + LOGGER.log(`finished ${lsCommand} in ${elapsed} ms with result ${res}`); + } + const humanVisibleDelay = elapsed < 1000 ? 1000 : 0; + setTimeout(() => { // set a timeout so user would still see the message when build time is short + if (res) { + resolve(res); + } else { + if (log) { + LOGGER.log(`Command ${lsCommand} takes too long to start`, LogLevel.ERROR); + } + reject(res); + } + }, humanVisibleDelay); + } catch (err: any) { + if (log) { + LOGGER.log(`command ${lsCommand} executed with error: ${JSON.stringify(err)}`, LogLevel.ERROR); + } + } + } else { + reject(l10n.value("jdk.extenstion.progressBar.error_msg.cannotRun", { lsCommand: lsCommand, client: c })); + } + }); + }); +} \ No newline at end of file diff --git a/vscode/src/commands/webViews.ts b/vscode/src/commands/webViews.ts new file mode 100644 index 0000000..39ab54e --- /dev/null +++ b/vscode/src/commands/webViews.ts @@ -0,0 +1,29 @@ +/* + Copyright (c) 2023-2024, Oracle and/or its affiliates. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ +import { JdkDownloaderView } from "../jdkDownloader/view"; +import { extCommands } from "./commands"; +import { ICommand } from "./types"; + +const invokeDownloadJdkWebview = async () => { + const jdkDownloaderView = new JdkDownloaderView(); + jdkDownloaderView.createView(); +} + + +export const registerWebviewCommands: ICommand[] = [{ + command: extCommands.downloadJdk, + handler: invokeDownloadJdkWebview +}]; diff --git a/vscode/src/explorer.ts b/vscode/src/explorer.ts index 7a4e996..eacb7fb 100644 --- a/vscode/src/explorer.ts +++ b/vscode/src/explorer.ts @@ -20,7 +20,7 @@ import * as vscode from 'vscode'; import { ThemeIcon } from 'vscode'; import { LanguageClient } from 'vscode-languageclient/node'; import { NbLanguageClient } from './lsp/nbLanguageClient'; -import { NodeChangedParams, NodeInfoNotification, NodeInfoRequest, GetResourceParams, NodeChangeType, NodeChangesParams } from './protocol'; +import { NodeChangedParams, NodeInfoNotification, NodeInfoRequest, GetResourceParams, NodeChangeType, NodeChangesParams } from './lsp/protocol'; import { l10n } from './localiser'; const doLog : boolean = false; const EmptyIcon = "EMPTY_ICON"; diff --git a/vscode/src/extension.ts b/vscode/src/extension.ts index 3569045..d9df058 100644 --- a/vscode/src/extension.ts +++ b/vscode/src/extension.ts @@ -48,7 +48,7 @@ import { NbTestAdapter } from './testAdapter'; import { asRanges, StatusMessageRequest, ShowStatusMessageParams, QuickPickRequest, InputBoxRequest, MutliStepInputRequest, TestProgressNotification, DebugConnector, TextEditorDecorationCreateRequest, TextEditorDecorationSetNotification, TextEditorDecorationDisposeNotification, HtmlPageRequest, HtmlPageParams, ExecInHtmlPageRequest, SetTextEditorDecorationParams, ProjectActionParams, UpdateConfigurationRequest, QuickPickStep, InputBoxStep, SaveDocumentsRequest, SaveDocumentRequestParams -} from './protocol'; +} from './lsp/protocol'; import * as launchConfigurations from './launchConfigurations'; import { TreeViewService, Visualizer } from './explorer'; import { initializeRunConfiguration, runConfigurationProvider, runConfigurationNodeProvider, configureRunSettings, runConfigurationUpdateAll } from './runConfiguration'; @@ -65,6 +65,7 @@ import { initializeServer } from './lsp/initializer'; import { NbLanguageClient } from './lsp/nbLanguageClient'; import { configChangeListener } from './configurations/listener'; import { isNbJavacDisabledHandler } from './configurations/handlers'; +import { subscribeCommands } from './commands/register'; const listeners = new Map<string, string[]>(); export let LOGGER: ExtensionLogger; @@ -144,78 +145,6 @@ function contextUri(ctx : any) : vscode.Uri | undefined { return vscode.window.activeTextEditor?.document?.uri; } -/** - * Executes a project action. It is possible to provide an explicit configuration to use (or undefined), display output from the action etc. - * Arguments are attempted to parse as file or editor references or Nodes; otherwise they are attempted to be passed to the action as objects. - * - * @param action ID of the project action to run - * @param configuration configuration to use or undefined - use default/active one. - * @param title Title for the progress displayed in vscode - * @param log output channel that should be revealed - * @param showOutput if true, reveals the passed output channel - * @param args additional arguments - * @returns Promise for the command's result - */ -function wrapProjectActionWithProgress(action : string, configuration : string | undefined, title : string, log? : vscode.OutputChannel, showOutput? : boolean, ...args : any[]) : Thenable<unknown> { - let items = []; - let actionParams = { - action : action, - configuration : configuration, - } as ProjectActionParams; - for (let item of args) { - let u : vscode.Uri | undefined; - if (item?.fsPath) { - items.push((item.fsPath as vscode.Uri).toString()); - } else if (item?.resourceUri) { - items.push((item.resourceUri as vscode.Uri).toString()); - } else { - items.push(item); - } - } - return wrapCommandWithProgress(extConstants.COMMAND_PREFIX + '.project.run.action', title, log, showOutput, actionParams, ...items); -} - -function wrapCommandWithProgress(lsCommand : string, title : string, log? : vscode.OutputChannel, showOutput? : boolean, ...args : any[]) : Thenable<unknown> { - return window.withProgress({ location: ProgressLocation.Window }, p => { - return new Promise(async (resolve, reject) => { - let c : LanguageClient = await globalVars.clientPromise.client; - const commands = await vscode.commands.getCommands(); - if (commands.includes(lsCommand)) { - p.report({ message: title }); - c.outputChannel.show(true); - const start = new Date().getTime(); - try { - if (log) { - LOGGER.log(`starting ${lsCommand}`); - } - const res = await vscode.commands.executeCommand(lsCommand, ...args) - const elapsed = new Date().getTime() - start; - if (log) { - LOGGER.log(`finished ${lsCommand} in ${elapsed} ms with result ${res}`); - } - const humanVisibleDelay = elapsed < 1000 ? 1000 : 0; - setTimeout(() => { // set a timeout so user would still see the message when build time is short - if (res) { - resolve(res); - } else { - if (log) { - LOGGER.log(`Command ${lsCommand} takes too long to start`, LogLevel.ERROR); - } - reject(res); - } - }, humanVisibleDelay); - } catch (err: any) { - if (log) { - LOGGER.log(`command ${lsCommand} executed with error: ${JSON.stringify(err)}`, LogLevel.ERROR); - } - } - } else { - reject(l10n.value("jdk.extension.progressBar.error_msg.cannotRun",{lsCommand:lsCommand,client:c})); - } - }); - }); -} - export function activate(context: ExtensionContext): VSNetBeansAPI { globalVars.deactivated = false; globalVars.clientPromise = new ClientPromise(); @@ -276,224 +205,7 @@ export function activate(context: ExtensionContext): VSNetBeansAPI { }); // register commands - context.subscriptions.push(commands.registerCommand(extConstants.COMMAND_PREFIX + '.workspace.new', async (ctx, template) => { - let c : LanguageClient = await globalVars.clientPromise.client; - const commands = await vscode.commands.getCommands(); - if (commands.includes(extConstants.COMMAND_PREFIX + '.new.from.template')) { - const workspaces=workspace.workspaceFolders; - - if(!workspaces) { - const userHomeDir = os.homedir(); - const folderPath = await vscode.window.showInputBox({ - prompt: l10n.value('jdk.workspace.new.prompt'), - value: `${userHomeDir}` - }); - if(!folderPath?.trim()) return; - - if(!fs.existsSync(folderPath)) { - await fs.promises.mkdir(folderPath); - } - const folderPathUri = vscode.Uri.file(folderPath); - await vscode.commands.executeCommand(extConstants.COMMAND_PREFIX + '.new.from.template', folderPathUri.toString()); - await vscode.commands.executeCommand(`vscode.openFolder`, folderPathUri); - - return; - } - - // first give the template (if present), then the context, and then the open-file hint in the case the context is not specific enough - const params = []; - if (typeof template === 'string') { - params.push(template); - } - params.push(contextUri(ctx)?.toString(), vscode.window.activeTextEditor?.document?.uri?.toString()); - const res = await vscode.commands.executeCommand(extConstants.COMMAND_PREFIX + '.new.from.template', ...params); - - if (typeof res === 'string') { - let newFile = vscode.Uri.parse(res as string); - await vscode.window.showTextDocument(newFile, { preview: false }); - } else if (Array.isArray(res)) { - for(let r of res) { - if (typeof r === 'string') { - let newFile = vscode.Uri.parse(r as string); - await vscode.window.showTextDocument(newFile, { preview: false }); - } - } - } - } else { - throw l10n.value("jdk.extension.error_msg.doesntSupportNewTeamplate",{client:c}); - } - })); - context.subscriptions.push(commands.registerCommand(extConstants.COMMAND_PREFIX + '.workspace.newproject', async (ctx) => { - let c : LanguageClient = await globalVars.clientPromise.client; - const commands = await vscode.commands.getCommands(); - if (commands.includes(extConstants.COMMAND_PREFIX + '.new.project')) { - const res = await vscode.commands.executeCommand(extConstants.COMMAND_PREFIX + '.new.project', contextUri(ctx)?.toString()); - if (typeof res === 'string') { - let newProject = vscode.Uri.parse(res as string); - - const OPEN_IN_NEW_WINDOW = l10n.value("jdk.extension.label.openInNewWindow"); - const ADD_TO_CURRENT_WORKSPACE = l10n.value("jdk.extension.label.addToWorkSpace"); - - const value = await vscode.window.showInformationMessage(l10n.value("jdk.extension.message.newProjectCreated"), OPEN_IN_NEW_WINDOW, ADD_TO_CURRENT_WORKSPACE); - if (value === OPEN_IN_NEW_WINDOW) { - await vscode.commands.executeCommand('vscode.openFolder', newProject, true); - } else if (value === ADD_TO_CURRENT_WORKSPACE) { - vscode.workspace.updateWorkspaceFolders(vscode.workspace.workspaceFolders ? vscode.workspace.workspaceFolders.length : 0, undefined, { uri: newProject }); - } - } - } else { - throw l10n.value("jdk.extenstion.error_msg.doesntSupportNewProject",{client: globalVars.clientPromise.client,c}); - } - })); - - context.subscriptions.push(commands.registerCommand(extConstants.COMMAND_PREFIX + '.open.test', async (ctx) => { - let c: LanguageClient = await globalVars.clientPromise.client; - const commands = await vscode.commands.getCommands(); - if (commands.includes(extConstants.COMMAND_PREFIX + '.go.to.test')) { - try { - const res: any = await vscode.commands.executeCommand(extConstants.COMMAND_PREFIX + '.go.to.test', contextUri(ctx)?.toString()); - if("errorMessage" in res){ - throw new Error(res.errorMessage); - } - res?.providerErrors?.map((error: any) => { - if(error?.message){ - vscode.window.showErrorMessage(error.message); - } - }); - if (res?.locations?.length) { - if (res.locations.length === 1) { - const { file, offset } = res.locations[0]; - const filePath = vscode.Uri.parse(file); - const editor = await vscode.window.showTextDocument(filePath, { preview: false }); - if (offset != -1) { - const pos: vscode.Position = editor.document.positionAt(offset); - editor.selections = [new vscode.Selection(pos, pos)]; - const range = new vscode.Range(pos, pos); - editor.revealRange(range); - } - - } else { - const namePathMapping: { [key: string]: string } = {} - res.locations.forEach((fp:any) => { - const fileName = path.basename(fp.file); - namePathMapping[fileName] = fp.file - }); - const selected = await window.showQuickPick(Object.keys(namePathMapping), { - title: l10n.value("jdk.extension.fileSelector.label.selectFiles"), - placeHolder: l10n.value("jdk.extension.fileSelector.label.testFilesOrSourceFiles"), - canPickMany: true - }); - if (selected) { - for await (const filePath of selected) { - let file = vscode.Uri.parse(filePath); - await vscode.window.showTextDocument(file, { preview: false }); - } - } else { - vscode.window.showInformationMessage(l10n.value("jdk.extension.fileSelector.label.noFileSelected")); - } - } - } - } catch (err:any) { - vscode.window.showInformationMessage(err?.message || l10n.value("jdk.extension.fileSelector.label.noTestFound")); - } - } else { - throw l10n.value("jdk.extension.error_msg.doesntSupportGoToTest",{client:c}); - } - })); - - context.subscriptions.push(vscode.commands.registerCommand(extConstants.COMMAND_PREFIX + ".delete.cache", async () => { - const storagePath = context.storageUri?.fsPath; - if (!storagePath) { - vscode.window.showErrorMessage(l10n.value("jdk.extension.cache.error_msg.cannotFindWrkSpacePath")); - return; - } - - const userDir = path.join(storagePath, "userdir"); - if (userDir && fs.existsSync(userDir)) { - const yes = l10n.value("jdk.extension.cache.label.confirmation.yes") - const cancel = l10n.value("jdk.extension.cache.label.confirmation.cancel") - const confirmation = await vscode.window.showInformationMessage('Are you sure you want to delete cache for this workspace and reload the window ?', - yes, cancel); - if (confirmation === yes) { - const reloadWindowActionLabel = l10n.value("jdk.extension.cache.label.reloadWindow"); - try { - await globalVars.clientPromise.stopClient(); - globalVars.deactivated = true; - await globalVars.nbProcessManager?.killProcess(false); - await fs.promises.rmdir(userDir, { recursive: true }); - await vscode.window.showInformationMessage(l10n.value("jdk.extension.message.cacheDeleted"), reloadWindowActionLabel); - } catch (err) { - await vscode.window.showErrorMessage(l10n.value("jdk.extension.error_msg.cacheDeletionError"), reloadWindowActionLabel); - } finally { - vscode.commands.executeCommand("workbench.action.reloadWindow"); - } - } - } else { - vscode.window.showErrorMessage(l10n.value("jdk.extension.cache.message.noUserDir")); - } - })); - - - context.subscriptions.push(vscode.commands.registerCommand(extConstants.COMMAND_PREFIX + ".download.jdk", async () => { - const jdkDownloaderView = new JdkDownloaderView(); - jdkDownloaderView.createView(); - })); - context.subscriptions.push(commands.registerCommand(extConstants.COMMAND_PREFIX + '.workspace.compile', () => - wrapCommandWithProgress(extConstants.COMMAND_PREFIX + '.build.workspace', l10n.value('jdk.extension.command.progress.compilingWorkSpace'), LOGGER.getOutputChannel(), true) - )); - context.subscriptions.push(commands.registerCommand(extConstants.COMMAND_PREFIX + '.workspace.clean', () => - wrapCommandWithProgress(extConstants.COMMAND_PREFIX + '.clean.workspace',l10n.value('jdk.extension.command.progress.cleaningWorkSpace'), LOGGER.getOutputChannel(), true) - )); - context.subscriptions.push(commands.registerCommand(extConstants.COMMAND_PREFIX + '.project.compile', (args) => { - wrapProjectActionWithProgress('build', undefined, l10n.value('jdk.extension.command.progress.compilingProject'), LOGGER.getOutputChannel(), true, args); - })); - context.subscriptions.push(commands.registerCommand(extConstants.COMMAND_PREFIX + '.project.clean', (args) => { - wrapProjectActionWithProgress('clean', undefined, l10n.value('jdk.extension.command.progress.cleaningProject'), LOGGER.getOutputChannel(), true, args); - })); - context.subscriptions.push(commands.registerCommand(extConstants.COMMAND_PREFIX + '.open.type', () => { - wrapCommandWithProgress(extConstants.COMMAND_PREFIX + '.quick.open', l10n.value('jdk.extension.command.progress.quickOpen'), LOGGER.getOutputChannel(), true).then(() => { - commands.executeCommand('workbench.action.focusActiveEditorGroup'); - }); - })); - context.subscriptions.push(commands.registerCommand(extConstants.COMMAND_PREFIX + '.java.goto.super.implementation', async () => { - if (window.activeTextEditor?.document.languageId !== extConstants.LANGUAGE_ID) { - return; - } - const uri = window.activeTextEditor.document.uri; - const position = window.activeTextEditor.selection.active; - const locations: any[] = await vscode.commands.executeCommand(extConstants.COMMAND_PREFIX + '.java.super.implementation', uri.toString(), position) || []; - return vscode.commands.executeCommand('editor.action.goToLocations', window.activeTextEditor.document.uri, position, - locations.map(location => new vscode.Location(vscode.Uri.parse(location.uri), new vscode.Range(location.range.start.line, location.range.start.character, location.range.end.line, location.range.end.character))), - 'peek', l10n.value('jdk.extension.error_msg.noSuperImpl')); - })); - context.subscriptions.push(commands.registerCommand(extConstants.COMMAND_PREFIX + '.rename.element.at', async (offset) => { - const editor = window.activeTextEditor; - if (editor) { - await commands.executeCommand('editor.action.rename', [ - editor.document.uri, - editor.document.positionAt(offset), - ]); - } - })); - context.subscriptions.push(commands.registerCommand(extConstants.COMMAND_PREFIX + '.surround.with', async (items) => { - const selected: any = await window.showQuickPick(items, { placeHolder: l10n.value('jdk.extension.command.quickPick.placeholder.surroundWith') }); - if (selected) { - if (selected.userData.edit) { - const edit = await (await globalVars.clientPromise.client).protocol2CodeConverter.asWorkspaceEdit(selected.userData.edit as ls.WorkspaceEdit); - await workspace.applyEdit(edit); - await commands.executeCommand('workbench.action.focusActiveEditorGroup'); - } - await commands.executeCommand(selected.userData.command.command, ...(selected.userData.command.arguments || [])); - } - })); - context.subscriptions.push(commands.registerCommand(extConstants.COMMAND_PREFIX + '.generate.code', async (command, data) => { - const edit: any = await commands.executeCommand(command, data); - if (edit) { - const wsEdit = await (await globalVars.clientPromise.client).protocol2CodeConverter.asWorkspaceEdit(edit as ls.WorkspaceEdit); - await workspace.applyEdit(wsEdit); - await commands.executeCommand('workbench.action.focusActiveEditorGroup'); - } - })); + subscribeCommands(context); async function findRunConfiguration(uri : vscode.Uri) : Promise<vscode.DebugConfiguration|undefined> { // do not invoke debug start with no (jdk) configurations, as it would probably create an user prompt @@ -595,29 +307,10 @@ export function activate(context: ExtensionContext): VSNetBeansAPI { context.subscriptions.push(commands.registerCommand(extConstants.COMMAND_PREFIX + '.package.test', async (uri, launchConfiguration?) => { await runDebug(true, true, uri, undefined, launchConfiguration); })); - context.subscriptions.push(commands.registerCommand(extConstants.COMMAND_PREFIX + '.open.stacktrace', async (uri, methodName, fileName, line) => { - const location: string | undefined = uri ? await commands.executeCommand(extConstants.COMMAND_PREFIX + '.resolve.stacktrace.location', uri, methodName, fileName) : undefined; - if (location) { - const lNum = line - 1; - window.showTextDocument(vscode.Uri.parse(location), { selection: new vscode.Range(new vscode.Position(lNum, 0), new vscode.Position(lNum, 0)) }); - } else { - if (methodName) { - const fqn: string = methodName.substring(0, methodName.lastIndexOf('.')); - commands.executeCommand('workbench.action.quickOpen', '#' + fqn.substring(fqn.lastIndexOf('.') + 1)); - } - } - })); context.subscriptions.push(commands.registerCommand(extConstants.COMMAND_PREFIX + '.workspace.symbols', async (query) => { const c = await globalVars.clientPromise.client; return (await c.sendRequest<SymbolInformation[]>("workspace/symbol", { "query": query })) ?? []; })); - context.subscriptions.push(commands.registerCommand(extConstants.COMMAND_PREFIX + '.java.complete.abstract.methods', async () => { - const active = vscode.window.activeTextEditor; - if (active) { - const position = new vscode.Position(active.selection.start.line, active.selection.start.character); - await commands.executeCommand(extConstants.COMMAND_PREFIX + '.java.implement.all.abstract.methods', active.document.uri.toString(), position); - } - })); context.subscriptions.push(commands.registerCommand(extConstants.COMMAND_PREFIX + '.startup.condition', async () => { return globalVars.clientPromise.client; })); diff --git a/vscode/src/propertiesView/propertiesView.ts b/vscode/src/propertiesView/propertiesView.ts index d03d784..5ea0ca6 100644 --- a/vscode/src/propertiesView/propertiesView.ts +++ b/vscode/src/propertiesView/propertiesView.ts @@ -22,7 +22,7 @@ import { CommandKey, ID, Message, PropertyMessage, Properties, Property, Propert import { assertNever, isObject, isRecord, isString, IsType } from '../typesUtil'; import { makeHtmlForProperties } from './propertiesHtmlBuilder'; import { TreeViewService, TreeNodeListener, Visualizer } from '../explorer'; -import { NodeChangeType } from '../protocol'; +import { NodeChangeType } from '../lsp/protocol'; function isVisualizer(node : any) : node is Visualizer { return node?.id && node?.rootId; diff --git a/vscode/src/protocol.ts b/vscode/src/protocol.ts deleted file mode 100644 index 5671986..0000000 --- a/vscode/src/protocol.ts +++ /dev/null @@ -1,324 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -'use strict'; - -import * as vscode from 'vscode'; - -import { - ProtocolNotificationType, - ProtocolRequestType, - ShowMessageParams, - NotificationType -} from 'vscode-languageclient'; - -import { - Position, - Range -} from 'vscode-languageserver-protocol'; - -export interface HtmlPageParams { - id: string; - text: string; - pause: boolean; - resources?: { - [name: string]: string; - }; -} - -export namespace HtmlPageRequest { - export const type = new ProtocolRequestType<HtmlPageParams, void, never, void, void>('window/showHtmlPage'); -}; - -export namespace ExecInHtmlPageRequest { - export const type = new ProtocolRequestType<HtmlPageParams, boolean, never, void, void>('window/execInHtmlPage'); -}; - -export interface ShowStatusMessageParams extends ShowMessageParams { - /** - * The timeout - */ - timeout?: number; -} - -export interface UpdateConfigParams { - /** - * Information specifying configuration update. - */ - section: string; - key: string; - value: string; -} - -export namespace UpdateConfigurationRequest { - export const type = new ProtocolRequestType<UpdateConfigParams, void, never, void, void>('config/update'); -} - -export namespace StatusMessageRequest { - export const type = new ProtocolNotificationType<ShowStatusMessageParams, void>('window/showStatusBarMessage'); -}; - -export interface ShowQuickPickParams { - /** - * An optional title of the quick pick. - */ - title?: string; - /** - * A string to show as placeholder in the input box to guide the user what to pick on. - */ - placeHolder: string; - /** - * An optional flag to make the picker accept multiple selections. - */ - canPickMany?: boolean; - /** - * A list of items. - */ - items: vscode.QuickPickItem[]; -} - -export namespace QuickPickRequest { - export const type = new ProtocolRequestType<ShowQuickPickParams, vscode.QuickPickItem[], never, void, void>('window/showQuickPick'); -} - -export interface ShowInputBoxParams { - /** - * An optional title of the input box. - */ - title?: string; - /** - * The text to display underneath the input box. - */ - prompt: string; - /** - * The value to prefill in the input box. - */ - value: string; - /** - * Controls if a password input is shown. Password input hides the typed text. - */ - password?: boolean; -} - -export namespace InputBoxRequest { - export const type = new ProtocolRequestType<ShowInputBoxParams, string | undefined, never, void, void>('window/showInputBox'); -} - -export interface ShowMutliStepInputParams { - /** - * ID of the input. - */ - id: string; - /** - * An optional title. - */ - title?: string; -} - -export interface InputCallbackParams { - inputId : string; - step: number; - data: { [name: string]: readonly vscode.QuickPickItem[] | string }; -} - -export interface StepInfo { - totalSteps: number; - stepId: string; -} - -export type QuickPickStep = StepInfo & ShowQuickPickParams; - -export type InputBoxStep = StepInfo & ShowInputBoxParams; - -export namespace MutliStepInputRequest { - export const type = new ProtocolRequestType<ShowMutliStepInputParams, { [name: string]: readonly vscode.QuickPickItem[] | string }, never, void, void>('window/showMultiStepInput'); - export const step = new ProtocolRequestType<InputCallbackParams, QuickPickStep | InputBoxStep | undefined, never, void, void>('input/step'); - export const validate = new ProtocolRequestType<InputCallbackParams, string | undefined, never, void, void>('input/validate'); -} - -export interface TestProgressParams { - uri: string; - suite: TestSuite; -} - -export interface TestSuite { - name: string; - file?: string; - range?: Range; - state: 'loaded' | 'started' | 'passed' | 'failed' | 'skipped' | 'errored'; - tests?: TestCase[]; -} - -export interface TestCase { - id: string; - name: string; - file?: string; - range?: Range; - state: 'loaded' | 'started' | 'passed' | 'failed' | 'skipped' | 'errored'; - stackTrace?: string[]; -} - -export namespace TestProgressNotification { - export const type = new ProtocolNotificationType<TestProgressParams, void>('window/notifyTestProgress'); -}; - -export interface DebugConnector { - id: string; - name: string; - type: string; - arguments: string[]; - defaultValues: string[]; - descriptions: string[]; -} - -export interface SetTextEditorDecorationParams { - key: string; - uri: string; - ranges: Range[]; -}; - -export namespace TextEditorDecorationCreateRequest { - export const type = new ProtocolRequestType<vscode.DecorationRenderOptions, never, string, void, void>('window/createTextEditorDecoration'); -}; - -export namespace TextEditorDecorationSetNotification { - export const type = new ProtocolNotificationType<SetTextEditorDecorationParams, void>('window/setTextEditorDecoration'); -}; - -export namespace TextEditorDecorationDisposeNotification { - export const type = new ProtocolNotificationType<string, void>('window/disposeTextEditorDecoration'); -} -export interface SaveDocumentRequestParams { - documents: string[]; -} - -export namespace SaveDocumentsRequest { - export const type = new ProtocolRequestType<SaveDocumentRequestParams, boolean, never, void, void>('window/documentSave'); -} - -export interface NodeChangedParams { - rootId : number; - nodeId : number | null; - types? : NodeChangeType[]; - properties? : String[]; -} - -export interface CreateExplorerParams { - explorerId : string; -} - -export interface NodeOperationParams { - nodeId : number; -} - -export interface ProjectActionParams { - action : string; - configuration? : string; - fallback? : boolean; -} - -export interface GetResourceParams { - uri : vscode.Uri; - acceptEncoding? : string[]; - acceptContent? : string[]; -} - -export interface ResourceData { - contentType : string; - encoding : string; - content : string; - contentSize : number; -} - -export interface FindPathParams { - rootNodeId : number; - uri? : vscode.Uri; - selectData? : any; -} - -export enum NodeChangeType { - SELF = 0, - PROPERTY, - CHILDEN, - DESTROY -}; - -export interface NodeChangesParams { - rootId: number; - nodeId?: number; - deactivateListeners?: number[]; - types?: NodeChangeType[]; -} - -export namespace NodeInfoNotification { - export const type = new ProtocolNotificationType<NodeChangedParams, void>('nodes/nodeChanged'); -} - -export namespace NodeInfoRequest { - export const explorermanager = new ProtocolRequestType<CreateExplorerParams, never, Data, void, void>('nodes/explorermanager'); - export const info = new ProtocolRequestType<NodeOperationParams, Data, never,void, void>('nodes/info'); - export const children = new ProtocolRequestType<NodeOperationParams, number[], never, void, void>('nodes/children'); - export const destroy = new ProtocolRequestType<NodeOperationParams, boolean, never, void, void>('nodes/delete'); - export const collapsed = new ProtocolNotificationType<NodeOperationParams, void>('nodes/collapsed'); - export const getresource = new ProtocolRequestType<GetResourceParams, ResourceData, never, void, void>('nodes/getresource'); - export const findparams = new ProtocolRequestType<FindPathParams, number[], never, void, void>('nodes/findpath'); - export const changes = new ProtocolRequestType<NodeChangesParams, number, never, void, void>('nodes/changes'); - - export interface IconDescriptor { - baseUri : vscode.Uri; - } - export interface Data { - id : number; /* numeric ID of the node */ - name : string; /* Node.getName() */ - label : string; /* Node.getDisplayName() */ - tooltip? : string; - description : string; /* Node.getShortDescription() */ - resourceUri? : string; /* external URL to file: resource */ - collapsibleState : vscode.TreeItemCollapsibleState; - canDestroy : boolean; /* Node.canDestroy() */ - contextValue : string; /* Node.getCookies() */ - iconDescriptor? : IconDescriptor; - iconUri : string | null; - iconIndex : number; - command? : string; - } -}; - -export function asPosition(value: undefined | null): undefined; -export function asPosition(value: Position): vscode.Position; -export function asPosition(value: Position | undefined | null): vscode.Position | undefined; -export function asPosition(value: Position | undefined | null): vscode.Position | undefined { - if (!value) { - return undefined; - } - return new vscode.Position(value.line, value.character); -} - -export function asRange(value: undefined | null): undefined; -export function asRange(value: Range): vscode.Range; -export function asRange(value: Range | undefined | null): vscode.Range | undefined; -export function asRange(value: Range | undefined | null): vscode.Range | undefined { - if (!value) { - return undefined; - } - return new vscode.Range(asPosition(value.start), asPosition(value.end)); -} - -export function asRanges(value: Range[]): vscode.Range[] { - return value.map(value => asRange(value)); -} diff --git a/vscode/src/testAdapter.ts b/vscode/src/testAdapter.ts index 1a54331..fb93fc1 100644 --- a/vscode/src/testAdapter.ts +++ b/vscode/src/testAdapter.ts @@ -23,7 +23,7 @@ import { commands, debug, tests, workspace, CancellationToken, TestController, TestItem, TestRunProfileKind, TestRunRequest, Uri, TestRun, TestMessage, Location, Position, MarkdownString } from "vscode"; import * as path from 'path'; -import { asRange, TestCase, TestSuite } from "./protocol"; +import { asRange, TestCase, TestSuite } from "./lsp/protocol"; import { extConstants } from "./constants"; export class NbTestAdapter { From df8db18edafa5a5f613dd75bac80f712b250046f Mon Sep 17 00:00:00 2001 From: Achal Talati <achal.talati@oracle.com> Date: Tue, 8 Oct 2024 00:19:07 +0530 Subject: [PATCH 03/17] builtin config keys added --- vscode/src/configurations/configuration.ts | 9 ++++++--- vscode/src/configurations/handlers.ts | 19 +++++++++++++------ vscode/src/lsp/launchOptions.ts | 4 ++-- 3 files changed, 21 insertions(+), 11 deletions(-) diff --git a/vscode/src/configurations/configuration.ts b/vscode/src/configurations/configuration.ts index 74f200d..70005c3 100644 --- a/vscode/src/configurations/configuration.ts +++ b/vscode/src/configurations/configuration.ts @@ -28,16 +28,19 @@ export const configKeys = { runConfigCwd: 'runConfig.cwd', verbose: 'verbose', userdir: 'userdir', - vscodeTheme: 'workbench.colorTheme' }; -export const userConfigsListened = [ +export const builtInConfigKeys = { + vscodeTheme: 'workbench.colorTheme' +} + +export const userConfigsListened: string[] = [ appendPrefixToCommand(configKeys.jdkHome), appendPrefixToCommand(configKeys.userdir), appendPrefixToCommand(configKeys.lspVmOptions), appendPrefixToCommand(configKeys.disableNbJavac), appendPrefixToCommand(configKeys.disableProjSearchLimit), - configKeys.vscodeTheme, + builtInConfigKeys.vscodeTheme, ]; diff --git a/vscode/src/configurations/handlers.ts b/vscode/src/configurations/handlers.ts index b8c0ec0..e42eb5d 100644 --- a/vscode/src/configurations/handlers.ts +++ b/vscode/src/configurations/handlers.ts @@ -14,7 +14,7 @@ limitations under the License. */ import { extensions, workspace } from "vscode"; -import { configKeys } from "./configuration"; +import { builtInConfigKeys, configKeys } from "./configuration"; import { extConstants, NODE_WINDOWS_LABEL } from "../constants"; import * as os from 'os'; import { globalVars, LOGGER } from "../extension"; @@ -27,6 +27,14 @@ export const getConfigurationValue = <T>(key: string, defaultValue: T | undefine return defaultValue != undefined ? conf.get(key, defaultValue) : conf.get(key) as T; } +export const getBuiltinConfigurationValue = <T>(key: string, defaultValue: T | undefined = undefined): T => { + const splitKey = key.split('.'); + const selector = splitKey?.[0]; + const conf = workspace.getConfiguration(selector); + const confKey = splitKey?.slice(1)?.join('.'); + return defaultValue != undefined ? conf?.get(confKey, defaultValue) : conf?.get(confKey) as T; +} + export const jdkHomeValueHandler = (): string | null => { return getConfigurationValue(configKeys.jdkHome) || process.env.JDK_HOME || @@ -71,8 +79,7 @@ export const lspServerVmOptionsHandler = (): string[] => { } export const isDarkColorThemeHandler = (): boolean => { - // const themeName = getConfigurationValue(configKeys.vscodeTheme); - const themeName = workspace.getConfiguration('workbench')?.get('colorTheme'); + const themeName: string = getBuiltinConfigurationValue(builtInConfigKeys.vscodeTheme); if (!themeName) { return false; } @@ -110,12 +117,12 @@ export const userdirHandler = (): string => { const userdir = path.join(userdirParentDir, "userdir"); try { - if(!fs.existsSync(userdir)){ + if (!fs.existsSync(userdir)) { fs.mkdirSync(userdir, { recursive: true }); const stats = fs.statSync(userdir); if (!stats.isDirectory()) { throw new Error(`${userdir} is not a directory`); - } + } } return userdir; @@ -125,5 +132,5 @@ export const userdirHandler = (): string => { } export const isNbJavacDisabledHandler = (): boolean => { - return getConfigurationValue(configKeys.verbose, false); + return getConfigurationValue(configKeys.verbose, false); } \ No newline at end of file diff --git a/vscode/src/lsp/launchOptions.ts b/vscode/src/lsp/launchOptions.ts index 778f5df..fa00b18 100644 --- a/vscode/src/lsp/launchOptions.ts +++ b/vscode/src/lsp/launchOptions.ts @@ -13,7 +13,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -import { configKeys } from "../configurations/configuration" +import { builtInConfigKeys, configKeys } from "../configurations/configuration" import { isDarkColorThemeHandler, isNbJavacDisabledHandler, jdkHomeValueHandler, lspServerVmOptionsHandler, projectSearchRootsValueHandler, userdirHandler } from "../configurations/handlers"; import { l10n } from "../localiser"; import { userDefinedLaunchOptionsType } from "./types" @@ -35,7 +35,7 @@ export const getUserConfigLaunchOptionsDefaults = (): userDefinedLaunchOptionsTy value: isNbJavacDisabledHandler(), optionToPass: '-J-Dnetbeans.logger.console=' }, - [configKeys.vscodeTheme]: { + [builtInConfigKeys.vscodeTheme]: { value: isDarkColorThemeHandler() ? 'com.formdev.flatlaf.FlatDarkLaf' : null, optionToPass: ['--laf'] }, From 725efde437ce40c5dca97ccc5f220cbe7869dc28 Mon Sep 17 00:00:00 2001 From: Achal Talati <achal.talati@oracle.com> Date: Tue, 8 Oct 2024 00:25:04 +0530 Subject: [PATCH 04/17] lsp module linting fixed --- vscode/src/lsp/clientPromise.ts | 6 +-- vscode/src/lsp/launchOptions.ts | 2 +- vscode/src/lsp/nbProcessManager.ts | 8 +-- vscode/src/lsp/protocol.ts | 80 +++++++++++++++--------------- vscode/src/lsp/utils.ts | 4 +- 5 files changed, 50 insertions(+), 50 deletions(-) diff --git a/vscode/src/lsp/clientPromise.ts b/vscode/src/lsp/clientPromise.ts index aba7f48..cb41ab7 100644 --- a/vscode/src/lsp/clientPromise.ts +++ b/vscode/src/lsp/clientPromise.ts @@ -25,7 +25,7 @@ export class ClientPromise { client!: Promise<NbLanguageClient>; activationPending!: boolean; initialPromiseResolved: boolean = false; - + public clientPromiseInitialization = (): void => { this.client = new Promise<NbLanguageClient>((clientOK, clientErr) => { this.setClient = [ @@ -44,7 +44,7 @@ export class ClientPromise { } public stopClient = async (): Promise<void> => { - if(globalVars.testAdapter){ + if (globalVars.testAdapter) { globalVars.testAdapter.dispose(); globalVars.testAdapter = undefined; } @@ -60,7 +60,7 @@ export class ClientPromise { LOGGER.log("Server activation requested repeatedly, ignoring...", LogLevel.WARN); return; } - if(!nbProcessManager){ + if (!nbProcessManager) { LOGGER.log("Nbcode Process is null", LogLevel.ERROR); return; } diff --git a/vscode/src/lsp/launchOptions.ts b/vscode/src/lsp/launchOptions.ts index fa00b18..55d9675 100644 --- a/vscode/src/lsp/launchOptions.ts +++ b/vscode/src/lsp/launchOptions.ts @@ -52,7 +52,7 @@ const extraLaunchOptions = [ "--locale", l10n.nbLocaleCode(), "--start-java-language-server=listen-hash:0", "--start-java-debug-adapter-server=listen-hash:0" - ]; +]; const prepareUserConfigLaunchOptions = (): string[] => { const launchOptions: string[] = []; diff --git a/vscode/src/lsp/nbProcessManager.ts b/vscode/src/lsp/nbProcessManager.ts index 3862e8d..b6d174b 100644 --- a/vscode/src/lsp/nbProcessManager.ts +++ b/vscode/src/lsp/nbProcessManager.ts @@ -77,7 +77,7 @@ export class NbProcessManager { disconnect = () => { return this.process?.disconnect(); } - + getProcess = () => { return this.process; } @@ -85,9 +85,9 @@ export class NbProcessManager { getProcessId = () => { return this.process?.pid; } - + appendStdOut = (text: string) => { - if(this.stdOutText != null) { + if (this.stdOutText != null) { this.stdOutText += text; } } @@ -95,7 +95,7 @@ export class NbProcessManager { appendStdErr = (text: string) => { this.stdErrText += text; } - + getStdOut = () => { return this.stdOutText } diff --git a/vscode/src/lsp/protocol.ts b/vscode/src/lsp/protocol.ts index 5671986..2a83258 100644 --- a/vscode/src/lsp/protocol.ts +++ b/vscode/src/lsp/protocol.ts @@ -101,9 +101,9 @@ export interface ShowInputBoxParams { * An optional title of the input box. */ title?: string; - /** - * The text to display underneath the input box. - */ + /** + * The text to display underneath the input box. + */ prompt: string; /** * The value to prefill in the input box. @@ -131,13 +131,13 @@ export interface ShowMutliStepInputParams { } export interface InputCallbackParams { - inputId : string; + inputId: string; step: number; data: { [name: string]: readonly vscode.QuickPickItem[] | string }; } export interface StepInfo { - totalSteps: number; + totalSteps: number; stepId: string; } @@ -212,43 +212,43 @@ export namespace SaveDocumentsRequest { } export interface NodeChangedParams { - rootId : number; - nodeId : number | null; - types? : NodeChangeType[]; - properties? : String[]; + rootId: number; + nodeId: number | null; + types?: NodeChangeType[]; + properties?: String[]; } export interface CreateExplorerParams { - explorerId : string; + explorerId: string; } export interface NodeOperationParams { - nodeId : number; + nodeId: number; } export interface ProjectActionParams { - action : string; - configuration? : string; - fallback? : boolean; + action: string; + configuration?: string; + fallback?: boolean; } export interface GetResourceParams { - uri : vscode.Uri; - acceptEncoding? : string[]; - acceptContent? : string[]; + uri: vscode.Uri; + acceptEncoding?: string[]; + acceptContent?: string[]; } export interface ResourceData { - contentType : string; - encoding : string; - content : string; - contentSize : number; + contentType: string; + encoding: string; + content: string; + contentSize: number; } export interface FindPathParams { - rootNodeId : number; - uri? : vscode.Uri; - selectData? : any; + rootNodeId: number; + uri?: vscode.Uri; + selectData?: any; } export enum NodeChangeType { @@ -271,31 +271,31 @@ export namespace NodeInfoNotification { export namespace NodeInfoRequest { export const explorermanager = new ProtocolRequestType<CreateExplorerParams, never, Data, void, void>('nodes/explorermanager'); - export const info = new ProtocolRequestType<NodeOperationParams, Data, never,void, void>('nodes/info'); + export const info = new ProtocolRequestType<NodeOperationParams, Data, never, void, void>('nodes/info'); export const children = new ProtocolRequestType<NodeOperationParams, number[], never, void, void>('nodes/children'); export const destroy = new ProtocolRequestType<NodeOperationParams, boolean, never, void, void>('nodes/delete'); export const collapsed = new ProtocolNotificationType<NodeOperationParams, void>('nodes/collapsed'); export const getresource = new ProtocolRequestType<GetResourceParams, ResourceData, never, void, void>('nodes/getresource'); export const findparams = new ProtocolRequestType<FindPathParams, number[], never, void, void>('nodes/findpath'); export const changes = new ProtocolRequestType<NodeChangesParams, number, never, void, void>('nodes/changes'); - + export interface IconDescriptor { - baseUri : vscode.Uri; + baseUri: vscode.Uri; } export interface Data { - id : number; /* numeric ID of the node */ - name : string; /* Node.getName() */ - label : string; /* Node.getDisplayName() */ - tooltip? : string; - description : string; /* Node.getShortDescription() */ - resourceUri? : string; /* external URL to file: resource */ - collapsibleState : vscode.TreeItemCollapsibleState; - canDestroy : boolean; /* Node.canDestroy() */ - contextValue : string; /* Node.getCookies() */ - iconDescriptor? : IconDescriptor; - iconUri : string | null; - iconIndex : number; - command? : string; + id: number; /* numeric ID of the node */ + name: string; /* Node.getName() */ + label: string; /* Node.getDisplayName() */ + tooltip?: string; + description: string; /* Node.getShortDescription() */ + resourceUri?: string; /* external URL to file: resource */ + collapsibleState: vscode.TreeItemCollapsibleState; + canDestroy: boolean; /* Node.canDestroy() */ + contextValue: string; /* Node.getCookies() */ + iconDescriptor?: IconDescriptor; + iconUri: string | null; + iconIndex: number; + command?: string; } }; diff --git a/vscode/src/lsp/utils.ts b/vscode/src/lsp/utils.ts index b1305c2..c31b223 100644 --- a/vscode/src/lsp/utils.ts +++ b/vscode/src/lsp/utils.ts @@ -64,6 +64,6 @@ export const findNbcode = (extensionPath: string): string => { export const restartWithJDKLater = (time: number, notifyKill: boolean): void => { LOGGER.log(`Restart of ${extConstants.SERVER_NAME} requested in ${time / 1000} s.`); const nbProcessManager = globalVars.nbProcessManager; - - setTimeout(() => globalVars.clientPromise.restartExtension(nbProcessManager, notifyKill), time); + + setTimeout(() => globalVars.clientPromise.restartExtension(nbProcessManager, notifyKill), time); }; \ No newline at end of file From a49d15b42381e3e2ad85652fdda8e6ab72e9bc3d Mon Sep 17 00:00:00 2001 From: Achal Talati <achal.talati@oracle.com> Date: Tue, 8 Oct 2024 10:57:36 +0530 Subject: [PATCH 05/17] refactored some more commands Fixed tests fixed npm scripts --- vscode/package.json | 6 +- vscode/src/commands/buildOperations.ts | 12 +- vscode/src/commands/navigation.ts | 4 +- vscode/src/commands/refactor.ts | 10 + vscode/src/commands/register.ts | 4 +- vscode/src/commands/utilCommands.ts | 42 +++ vscode/src/commands/utils.ts | 6 +- vscode/src/extension.ts | 53 +-- vscode/src/lsp/clientPromise.ts | 8 +- vscode/src/lsp/types.ts | 7 +- vscode/src/test/launchNbcode.ts | 88 +++++ vscode/src/test/suite/extension.test.ts | 302 ------------------ .../src/test/suite/general/explorer.test.ts | 4 +- .../src/test/suite/general/extension.test.ts | 12 +- vscode/src/test/suite/testutils.ts | 234 -------------- 15 files changed, 182 insertions(+), 610 deletions(-) create mode 100644 vscode/src/commands/utilCommands.ts create mode 100644 vscode/src/test/launchNbcode.ts delete mode 100644 vscode/src/test/suite/extension.test.ts delete mode 100644 vscode/src/test/suite/testutils.ts diff --git a/vscode/package.json b/vscode/package.json index 4908703..47d167d 100644 --- a/vscode/package.json +++ b/vscode/package.json @@ -773,9 +773,9 @@ "compile": "tsc -p ./ && node ./esbuild.js", "watch": "tsc -watch -p ./ | node ./esbuild.js --watch", "test": "npm run compile && node ./out/test/runTest.js", - "nbcode": "node ./out/nbcode.js", - "nbjavac": "node ./out/nbcode.js -J-Dnetbeans.close=true --modules --install .*nbjavac.*", - "apisupport": "node ./out/nbcode.js -J-Dnetbeans.close=true --modules --install '(org.netbeans.libs.xerces|org.netbeans.modules.editor.structure|org.netbeans.modules.xml|org.netbeans.modules.xml.axi|org.netbeans.modules.xml.retriever|org.netbeans.modules.xml.schema.model|org.netbeans.modules.xml.tax|org.netbeans.modules.xml.text|org.netbeans.modules.ant.browsetask|.*apisupport.*|org.netbeans.modules.debugger.jpda.ant)' && node ./out/nbcode.js -J-Dnetbeans.close=true --modules --enable .*apisupport.ant", + "nbcode": "node ./out/test/launchNbcode.js", + "nbjavac": "node ./out/test/launchNbcode.js -J-Dnetbeans.close=true --modules --install .*nbjavac.*", + "apisupport": "node ./out/test/launchNbcode.js -J-Dnetbeans.close=true --modules --install '(org.netbeans.libs.xerces|org.netbeans.modules.editor.structure|org.netbeans.modules.xml|org.netbeans.modules.xml.axi|org.netbeans.modules.xml.retriever|org.netbeans.modules.xml.schema.model|org.netbeans.modules.xml.tax|org.netbeans.modules.xml.text|.*apisupport.*|org.netbeans.modules.debugger.jpda.ant)' && node ./out/test/launchNbcode.js -J-Dnetbeans.close=true --modules --enable .*apisupport.ant", "artifactory:check": "node ./esbuild.js --artifactory-check" }, "devDependencies": { diff --git a/vscode/src/commands/buildOperations.ts b/vscode/src/commands/buildOperations.ts index ca24805..7ca99ca 100644 --- a/vscode/src/commands/buildOperations.ts +++ b/vscode/src/commands/buildOperations.ts @@ -19,26 +19,26 @@ import { extCommands, nbCommands } from "./commands"; import { ICommand } from "./types"; import { wrapCommandWithProgress, wrapProjectActionWithProgress } from "./utils"; -const compileWorkspaceCHandler = () => { - wrapCommandWithProgress(nbCommands.buildWorkspace, l10n.value('jdk.extension.command.progress.compilingWorkSpace'), LOGGER.getOutputChannel(), true); +const compileWorkspaceHandler = () => { + return wrapCommandWithProgress(nbCommands.buildWorkspace, l10n.value('jdk.extension.command.progress.compilingWorkSpace'), LOGGER.getOutputChannel()); } const cleanWorkspaceHandler = () => { - wrapCommandWithProgress(nbCommands.cleanWorkspace,l10n.value('jdk.extension.command.progress.cleaningWorkSpace'), LOGGER.getOutputChannel(), true) + return wrapCommandWithProgress(nbCommands.cleanWorkspace,l10n.value('jdk.extension.command.progress.cleaningWorkSpace'), LOGGER.getOutputChannel()); } const compileProjectHandler = (args: any) => { - wrapProjectActionWithProgress('build', undefined, l10n.value('jdk.extension.command.progress.compilingProject'), LOGGER.getOutputChannel(), true, args); + return wrapProjectActionWithProgress('build', undefined, l10n.value('jdk.extension.command.progress.compilingProject'), LOGGER.getOutputChannel(), args); } const cleanProjectHandler = (args: any) => { - wrapProjectActionWithProgress('clean', undefined, l10n.value('jdk.extension.command.progress.cleaningProject'), LOGGER.getOutputChannel(), true, args); + return wrapProjectActionWithProgress('clean', undefined, l10n.value('jdk.extension.command.progress.cleaningProject'), LOGGER.getOutputChannel(), args); } export const registerBuildOperationCommands: ICommand[] = [ { command: extCommands.compileWorkspace, - handler: compileWorkspaceCHandler + handler: compileWorkspaceHandler }, { command: extCommands.cleanWorkspace, handler: cleanWorkspaceHandler diff --git a/vscode/src/commands/navigation.ts b/vscode/src/commands/navigation.ts index 8a31711..2a5548d 100644 --- a/vscode/src/commands/navigation.ts +++ b/vscode/src/commands/navigation.ts @@ -77,7 +77,7 @@ const goToTest = async (ctx: any) => { } const openTypeHandler = () => { - wrapCommandWithProgress(nbCommands.quickOpen, l10n.value('jdk.extension.command.progress.quickOpen'), LOGGER.getOutputChannel(), true).then(() => { + wrapCommandWithProgress(nbCommands.quickOpen, l10n.value('jdk.extension.command.progress.quickOpen'), LOGGER.getOutputChannel()).then(() => { commands.executeCommand(builtInCommands.focusActiveEditorGroup); }); } @@ -90,7 +90,7 @@ const openStackHandler = async (uri: any, methodName: any, fileName: any, line: } else { if (methodName) { const fqn: string = methodName.substring(0, methodName.lastIndexOf('.')); - commands.executeCommand(builtInCommands.quickAccess, '#' + fqn.substring(fqn.lastIndexOf('.') + 1)); + await commands.executeCommand(builtInCommands.quickAccess, '#' + fqn.substring(fqn.lastIndexOf('.') + 1)); } } } diff --git a/vscode/src/commands/refactor.ts b/vscode/src/commands/refactor.ts index 0a141f6..0a013fc 100644 --- a/vscode/src/commands/refactor.ts +++ b/vscode/src/commands/refactor.ts @@ -20,6 +20,7 @@ import { builtInCommands, extCommands, nbCommands } from "./commands"; import { l10n } from "../localiser"; import { globalVars } from "../extension"; import { WorkspaceEdit } from 'vscode-languageserver-protocol'; +import { SymbolInformation } from 'vscode-languageclient'; const goToSuperImplementationHandler = async () => { if (window.activeTextEditor?.document.languageId !== extConstants.LANGUAGE_ID) { @@ -74,6 +75,12 @@ const completeAbstractMethodsHandler = async () => { } } +const workspaceSymbolsHandler = async (query: any) => { + const client = await globalVars.clientPromise.client; + return (await client.sendRequest<SymbolInformation[]>("workspace/symbol", { "query": query })) ?? []; +} + + export const registerRefactorCommands: ICommand[] = [ { command: extCommands.goToSuperImpl, @@ -90,5 +97,8 @@ export const registerRefactorCommands: ICommand[] = [ }, { command: extCommands.abstractMethodsComplete, handler: completeAbstractMethodsHandler + }, { + command: extCommands.workspaceSymbols, + handler: workspaceSymbolsHandler } ]; \ No newline at end of file diff --git a/vscode/src/commands/register.ts b/vscode/src/commands/register.ts index 27f2b85..580c240 100644 --- a/vscode/src/commands/register.ts +++ b/vscode/src/commands/register.ts @@ -21,6 +21,7 @@ import { registerNavigationCommands } from "./navigation"; import { registerWebviewCommands } from "./webViews"; import { registerBuildOperationCommands } from "./buildOperations"; import { registerRefactorCommands } from "./refactor"; +import { registerUtilCommands } from "./utilCommands"; type ICommandModules = Record<string, ICommand[]>; @@ -30,7 +31,8 @@ const commandModules: ICommandModules = { navigation: registerNavigationCommands, webview: registerWebviewCommands, buildOperations: registerBuildOperationCommands, - refactor: registerRefactorCommands + refactor: registerRefactorCommands, + util: registerUtilCommands } export const subscribeCommands = (context: ExtensionContext) => { diff --git a/vscode/src/commands/utilCommands.ts b/vscode/src/commands/utilCommands.ts new file mode 100644 index 0000000..36576b2 --- /dev/null +++ b/vscode/src/commands/utilCommands.ts @@ -0,0 +1,42 @@ +/* + Copyright (c) 2023-2024, Oracle and/or its affiliates. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ +import { globalVars } from "../extension"; +import { extCommands } from "./commands"; +import { ICommand } from "./types"; + +const startupConditionHandler = () => { + return globalVars.clientPromise.client; +} + +const addEventListenerHandler = async (eventName: any, listener: any) => { + let ls = globalVars.listeners.get(eventName); + if (!ls) { + ls = []; + globalVars.listeners.set(eventName, ls); + } + ls.push(listener); +} + + +export const registerUtilCommands: ICommand[] = [ + { + command: extCommands.startupCondition, + handler: startupConditionHandler + }, { + command: extCommands.nbEventListener, + handler: addEventListenerHandler + } +]; \ No newline at end of file diff --git a/vscode/src/commands/utils.ts b/vscode/src/commands/utils.ts index 5bc4128..025edbb 100644 --- a/vscode/src/commands/utils.ts +++ b/vscode/src/commands/utils.ts @@ -56,7 +56,7 @@ export const isNbCommandRegistered = async (command: string) => { * @param args additional arguments * @returns Promise for the command's result */ -export const wrapProjectActionWithProgress = (action: string, configuration: string | undefined, title: string, log?: OutputChannel, showOutput?: boolean, ...args: any[]): Thenable<unknown> => { +export const wrapProjectActionWithProgress = (action: string, configuration: string | undefined, title: string, log?: OutputChannel, ...args: any[]): Thenable<unknown> => { let items = []; let actionParams = { action: action, @@ -72,10 +72,10 @@ export const wrapProjectActionWithProgress = (action: string, configuration: str items.push(item); } } - return wrapCommandWithProgress(nbCommands.runProjectAction, title, log, showOutput, actionParams, ...items); + return wrapCommandWithProgress(nbCommands.runProjectAction, title, log, actionParams, ...items); } -export const wrapCommandWithProgress = (lsCommand: string, title: string, log?: OutputChannel, showOutput?: boolean, ...args: any[]): Thenable<unknown> => { +export const wrapCommandWithProgress = (lsCommand: string, title: string, log?: OutputChannel, ...args: any[]): Thenable<unknown> => { return window.withProgress({ location: ProgressLocation.Window }, p => { return new Promise(async (resolve, reject) => { let c: LanguageClient = await globalVars.clientPromise.client; diff --git a/vscode/src/extension.ts b/vscode/src/extension.ts index d9df058..8d4d656 100644 --- a/vscode/src/extension.ts +++ b/vscode/src/extension.ts @@ -23,7 +23,7 @@ 'use strict'; -import { commands, window, workspace, ExtensionContext, ProgressLocation, TextEditorDecorationType } from 'vscode'; +import { commands, window, workspace, ExtensionContext, TextEditorDecorationType } from 'vscode'; import { LanguageClient, @@ -33,21 +33,18 @@ import { import { MessageType, LogMessageNotification, - SymbolInformation, TelemetryEventNotification } from 'vscode-languageclient'; import * as net from 'net'; import * as fs from 'fs'; -import * as os from 'os'; import * as path from 'path'; import * as vscode from 'vscode'; -import * as ls from 'vscode-languageserver-protocol'; import { StreamDebugAdapter} from './streamDebugAdapter'; import { NbTestAdapter } from './testAdapter'; import { asRanges, StatusMessageRequest, ShowStatusMessageParams, QuickPickRequest, InputBoxRequest, MutliStepInputRequest, TestProgressNotification, DebugConnector, TextEditorDecorationCreateRequest, TextEditorDecorationSetNotification, TextEditorDecorationDisposeNotification, HtmlPageRequest, HtmlPageParams, - ExecInHtmlPageRequest, SetTextEditorDecorationParams, ProjectActionParams, UpdateConfigurationRequest, QuickPickStep, InputBoxStep, SaveDocumentsRequest, SaveDocumentRequestParams + ExecInHtmlPageRequest, SetTextEditorDecorationParams, UpdateConfigurationRequest, QuickPickStep, InputBoxStep, SaveDocumentsRequest, SaveDocumentRequestParams } from './lsp/protocol'; import * as launchConfigurations from './launchConfigurations'; import { TreeViewService, Visualizer } from './explorer'; @@ -56,7 +53,6 @@ import { InputStep, MultiStepInput } from './utils'; import { PropertiesView } from './propertiesView/propertiesView'; import { l10n } from './localiser'; import { extConstants } from './constants'; -import { JdkDownloaderView } from './jdkDownloader/view'; import { ExtensionInfo } from './extensionInfo'; import { ClientPromise } from './lsp/clientPromise'; import { ExtensionLogger, LogLevel } from './logger'; @@ -66,10 +62,11 @@ import { NbLanguageClient } from './lsp/nbLanguageClient'; import { configChangeListener } from './configurations/listener'; import { isNbJavacDisabledHandler } from './configurations/handlers'; import { subscribeCommands } from './commands/register'; +import { VSNetBeansAPI } from './lsp/types'; -const listeners = new Map<string, string[]>(); export let LOGGER: ExtensionLogger; export namespace globalVars { + export const listeners = new Map<string, string[]>(); export let extensionInfo: ExtensionInfo; export let clientPromise: ClientPromise; export let debugPort: number = -1; @@ -105,31 +102,6 @@ export function findClusters(myPath : string): string[] { return clusters; } -// for tests only ! -export function awaitClient() : Promise<NbLanguageClient> { - const clientPromise = globalVars.clientPromise; - if (clientPromise.client && clientPromise.initialPromiseResolved) { - return clientPromise.client; - } - let nbcode = vscode.extensions.getExtension(extConstants.ORACLE_VSCODE_EXTENSION_ID); - if (!nbcode) { - return Promise.reject(new Error(l10n.value("jdk.extension.notInstalled.label"))); - } - const t : Thenable<NbLanguageClient> = nbcode.activate().then(nc => { - if (globalVars.clientPromise.client === undefined || !globalVars.clientPromise.initialPromiseResolved) { - throw new Error(l10n.value("jdk.extenstion.error_msg.clientNotAvailable")); - } else { - return globalVars.clientPromise.client; - } - }); - return Promise.resolve(t); -} - -interface VSNetBeansAPI { - version : string; - apiVersion: string; -} - function contextUri(ctx : any) : vscode.Uri | undefined { if (ctx?.fsPath) { return ctx as vscode.Uri; @@ -307,21 +279,6 @@ export function activate(context: ExtensionContext): VSNetBeansAPI { context.subscriptions.push(commands.registerCommand(extConstants.COMMAND_PREFIX + '.package.test', async (uri, launchConfiguration?) => { await runDebug(true, true, uri, undefined, launchConfiguration); })); - context.subscriptions.push(commands.registerCommand(extConstants.COMMAND_PREFIX + '.workspace.symbols', async (query) => { - const c = await globalVars.clientPromise.client; - return (await c.sendRequest<SymbolInformation[]>("workspace/symbol", { "query": query })) ?? []; - })); - context.subscriptions.push(commands.registerCommand(extConstants.COMMAND_PREFIX + '.startup.condition', async () => { - return globalVars.clientPromise.client; - })); - context.subscriptions.push(commands.registerCommand(extConstants.COMMAND_PREFIX + '.addEventListener', (eventName, listener) => { - let ls = listeners.get(eventName); - if (!ls) { - ls = []; - listeners.set(eventName, ls); - } - ls.push(listener); - })); context.subscriptions.push(commands.registerCommand(extConstants.COMMAND_PREFIX + '.node.properties.edit', async (node) => await PropertiesView.createOrShow(context, node, (await globalVars.clientPromise.client).findTreeViewService()))); @@ -481,7 +438,7 @@ function doActivateWithJDK(): void { } }); c.onNotification(TelemetryEventNotification.type, (param) => { - const ls = listeners.get(param); + const ls = globalVars.listeners.get(param); if (ls) { for (const listener of ls) { commands.executeCommand(listener); diff --git a/vscode/src/lsp/clientPromise.ts b/vscode/src/lsp/clientPromise.ts index cb41ab7..aab07f0 100644 --- a/vscode/src/lsp/clientPromise.ts +++ b/vscode/src/lsp/clientPromise.ts @@ -23,8 +23,7 @@ import { NbLanguageClient } from "./nbLanguageClient"; export class ClientPromise { setClient!: [(c: NbLanguageClient) => void, (err: any) => void]; client!: Promise<NbLanguageClient>; - activationPending!: boolean; - initialPromiseResolved: boolean = false; + activationPending: boolean = true; public clientPromiseInitialization = (): void => { this.client = new Promise<NbLanguageClient>((clientOK, clientErr) => { @@ -43,6 +42,11 @@ export class ClientPromise { commands.executeCommand('setContext', 'nbJdkReady', false); } + public initializedSuccessfully = (client: NbLanguageClient) => { + globalVars.clientPromise.setClient[0](client); + commands.executeCommand('setContext', 'nbJdkReady', true); + } + public stopClient = async (): Promise<void> => { if (globalVars.testAdapter) { globalVars.testAdapter.dispose(); diff --git a/vscode/src/lsp/types.ts b/vscode/src/lsp/types.ts index 74cd9ee..2f61bae 100644 --- a/vscode/src/lsp/types.ts +++ b/vscode/src/lsp/types.ts @@ -19,4 +19,9 @@ export type userDefinedLaunchOptionsType = { optionToPass?: string | string[], encloseInvertedComma?: boolean } -}; \ No newline at end of file +}; + +export interface VSNetBeansAPI { + version : string; + apiVersion: string; +} diff --git a/vscode/src/test/launchNbcode.ts b/vscode/src/test/launchNbcode.ts new file mode 100644 index 0000000..f5f1554 --- /dev/null +++ b/vscode/src/test/launchNbcode.ts @@ -0,0 +1,88 @@ +/* + Copyright (c) 2023-2024, Oracle and/or its affiliates. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ +import * as path from 'path'; +import * as fs from 'fs'; +import * as os from 'os'; +import { env } from 'process'; +import { ChildProcessByStdio, spawn } from 'child_process'; +import { Readable } from 'stream'; + +const findNbcode = (extensionPath: string): string => { + let nbcode = os.platform() === 'win32' ? + os.arch() === 'x64' ? 'nbcode64.exe' : 'nbcode.exe' + : 'nbcode.sh'; + let nbcodePath = path.join(extensionPath, "nbcode", "bin", nbcode); + + let nbcodePerm = fs.statSync(nbcodePath); + if (!nbcodePerm.isFile()) { + throw `Cannot execute ${nbcodePath}`; + } + if (os.platform() !== 'win32') { + fs.chmodSync(path.join(extensionPath, "nbcode", "bin", nbcode), "744"); + fs.chmodSync(path.join(extensionPath, "nbcode", "platform", "lib", "nbexec.sh"), "744"); + fs.chmodSync(path.join(extensionPath, "nbcode", "java", "maven", "bin", "mvn.sh"), "744"); + } + return nbcodePath; +} + +if (typeof process === 'object' && process.argv0 === 'node') { + let extension = path.join(process.argv[1], '..', '..', '..'); + let nbcode = path.join(extension, 'nbcode'); + if (!fs.existsSync(nbcode)) { + throw `Cannot find ${nbcode}. Try npm run compile first!`; + } + let clusters = fs.readdirSync(nbcode).filter(c => c !== 'bin' && c !== 'etc').map(c => path.join(nbcode, c)); + let args = process.argv.slice(2); + let json = JSON.parse("" + fs.readFileSync(path.join(extension, 'package.json'))); + let storage; + + if (!env.nbcode_userdir || env.nbcode_userdir == 'global') { + storage = path.join(os.platform() === 'darwin' ? + path.join(os.homedir(), 'Library', 'Application Support') : + path.join(os.homedir(), '.config'), + 'Code', 'User', 'globalStorage', json.publisher + '.' + json.name); + } else { + storage = env.nbcode_userdir; + } + const userdir = path.join(storage, "userdir"); + + if (!fs.existsSync(userdir)) { + fs.mkdirSync(userdir, { recursive: true }); + const stats = fs.statSync(userdir); + if (!stats.isDirectory()) { + throw `${userdir} is not a directory`; + } + } + + console.log('Launching NBLS with user directory: ' + userdir); + const ideArgs = []; + ideArgs.push(`-J-Dnetbeans.extra.dirs="${clusters.join(path.delimiter)}"`, ...args); + const nbcodeBinPath = findNbcode(extension); + const nbProcess: ChildProcessByStdio<any, Readable, Readable> = spawn(nbcodeBinPath, ideArgs, { + cwd: userdir, + stdio: ["ignore", "pipe", "pipe"], + }); + + nbProcess.stdout.on('data', function (data) { + console.log(data.toString()); + }); + nbProcess.stderr.on('data', function (data) { + console.log(data.toString()); + }); + nbProcess.on('close', (code) => { + console.log(`nbcode finished with status ${code}`); + }); +} diff --git a/vscode/src/test/suite/extension.test.ts b/vscode/src/test/suite/extension.test.ts deleted file mode 100644 index c0d82c5..0000000 --- a/vscode/src/test/suite/extension.test.ts +++ /dev/null @@ -1,302 +0,0 @@ - -/* - * Copyright (c) 2023, Oracle and/or its affiliates. - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -/* This file has been modified for Oracle Java SE extension */ - -import * as assert from 'assert'; -import * as fs from 'fs'; -import * as path from 'path'; -import * as vscode from 'vscode'; -import * as myExplorer from '../../explorer'; - -import { CodeAction, commands, extensions, Selection, Uri, window, workspace, TreeItem } from 'vscode'; -import { assertWorkspace, dumpJava, getFilePaths, openFile, prepareProject, replaceCode} from './testutils'; -import {SAMPLE_CODE_FORMAT_DOCUMENT, SAMPLE_CODE_SORT_IMPORTS, SAMPLE_CODE_UNUSED_IMPORTS } from './constants'; -import { extConstants } from '../../constants'; - -suite('Extension Test Suite', function () { - window.showInformationMessage('Start all tests.'); - - const filePaths = getFilePaths(); - - // Create project which used be used for testing - this.beforeAll(async () => { - await prepareProject(filePaths); - }).timeout(10000); - - // This test must be run first, in order to activate the extension and wait for the activation to complete - test("Extension loaded and activated", async () => { - const extension = extensions.getExtension('oracle.oracle-java'); - assert(extension, "No Java extension found!"); - - const api = await extension.activate(); - assert(extension?.isActive, "true"); - assert.ok(api.version, "Some version is specified"); - - let cannotReassignVersion = false; - try { - api.version = "different"; - } catch (e) { - cannotReassignVersion = true; - } - assert.ok(cannotReassignVersion, "Cannot reassign value of version"); - - }).timeout(10000); - - // Test if clusters are loaded or not - test('Find clusters', async () => { - const nbcode = extensions.getExtension('oracle.oracle-java'); - assert(nbcode); - - const extraCluster = path.join(nbcode.extensionPath, "nbcode", "extra"); - let clusters = findClusters('non-existent'). - // ignore 'extra' cluster in the extension path, since nbjavac is there during development: - filter(s => !s.startsWith(extraCluster)); - - let found: string[] = []; - function assertCluster(name: string) { - for (let c of clusters) { - if (c.endsWith('/' + name)) { - found.push(c); - return; - } - } - assert.fail(`Cannot find ${name} among ${clusters}`); - } - - assertCluster('extide'); - assertCluster('ide'); - assertCluster('java'); - assertCluster('nbcode'); - assertCluster('platform'); - assertCluster('webcommon'); - assertCluster('harness'); - - for (let c of found) { - assert.ok(c.startsWith(nbcode.extensionPath), `All extensions are below ${nbcode.extensionPath}, but: ${c}`); - } - }).timeout(10000); - - // Check if Jdk commands have been loaded - test("Jdk commands loaded", async () => { - let commandsList = await commands.getCommands(true); - - let containsJdkCommands: Boolean = false; - for (const command of commandsList) { - if (command.indexOf("jdk.") === 0) { - containsJdkCommands = true; - } - } - - assert.ok(containsJdkCommands, "No Jdk command has been loaded"); - }).timeout(10000); - - // Check if format document command is executed successfully - test("Format document", async () => { - const editor = await openFile(filePaths.formatDocument); - await commands.executeCommand('editor.action.formatDocument'); - - const formattedCode = editor.document.getText().split('\n').length; - const unformattedCode = SAMPLE_CODE_FORMAT_DOCUMENT.split('\n').length; - const isDocumentFormatted = formattedCode > unformattedCode; - assert.ok(isDocumentFormatted, "document is not formatted"); - }).timeout(10000); - - // Check if imports are getting sorted on saving document - test("Sort imports", async () => { - const editor = await openFile(filePaths.sortImports); - await replaceCode(editor, SAMPLE_CODE_SORT_IMPORTS); - - const isSaved = await editor.document.save(); - assert.ok(isSaved, "document cannot be saved"); - - const savedCode = editor.document.getText(); - const isImportsSorted = savedCode.indexOf('import java.util.Date;') > - savedCode.indexOf('import java.util.ArrayList;'); - assert.ok(isImportsSorted, "Imports are not sorted"); - - }).timeout(10000); - - // Check if unused imports are getting removed on saving document - test("Remove unused imports", async () => { - const editor = await openFile(filePaths.unusedImports); - await replaceCode(editor, SAMPLE_CODE_UNUSED_IMPORTS); - - const isSaved = await editor.document.save(); - assert.ok(isSaved, "document cannot be saved"); - - const savedCode = editor.document.getText(); - const areUnusedImportsRemoved = savedCode.indexOf('import java.lang.Float;') === -1 && - savedCode.indexOf('import java.lang.Integer;') === -1; - assert.ok(areUnusedImportsRemoved, "Unused imports are not removed"); - - }).timeout(10000); - - // Check if refactor actions are getting showing on UI and if they are working - test("Refactor actions executing", async () => { - const editor = await openFile(filePaths.refactorActions); - const doc = editor.document; - const sel = new Selection(doc.lineAt(12).range.start, doc.lineAt(12).range.end); - editor.selections = [sel]; - - const refactorActions = await commands.executeCommand<CodeAction[]>( - 'vscode.executeCodeActionProvider', - doc.uri, - sel - ); - - if (refactorActions && refactorActions.length > 0) { - for await (const action of refactorActions) { - if (action.command && action.command.arguments) { - if (action.command.command === extConstants.COMMAND_PREFIX + ".surround.with") { - //this action has a popup where the user needs to - //select a template that should be used for the surround: - continue; - } - await commands.executeCommand(action.command.command, ...action.command.arguments); - await commands.executeCommand('undo'); - } - } - } - }).timeout(10000); - - // Tests explorer is loading properly - test("Test Explorer tests", async () => { - let folder: string = assertWorkspace(); - - try { - console.log("Test: load workspace tests"); - const workspaceFolder = (workspace.workspaceFolders!)[0]; - let tests: any = await commands.executeCommand("jdk.load.workspace.tests", workspaceFolder.uri.toString()); - console.log(`Test: load workspace tests finished with ${tests}`); - assert.ok(tests, "No tests returned for workspace"); - assert.strictEqual(tests.length, 2, `Invalid number of test suites returned`); - assert.strictEqual(tests[0].name, 'pkg.MainTest', `Invalid test suite name returned`); - assert.strictEqual(tests[0].tests.length, 1, `Invalid number of tests in suite returned`); - assert.strictEqual(tests[0].tests[0].name, 'testGetName', `Invalid test name returned`); - assert.strictEqual(tests[1].name, 'pkg.MainTest$NestedTest', `Invalid test suite name returned`); - assert.strictEqual(tests[1].tests.length, 1, `Invalid number of tests in suite returned`); - assert.strictEqual(tests[1].tests[0].name, 'testTrue', `Invalid test name returned`); - - console.log("Test: run all workspace tests"); - await vscode.commands.executeCommand(extConstants.COMMAND_PREFIX + '.run.test', workspaceFolder.uri.toString()); - console.log(`Test: run all workspace tests finished`); - } catch (error) { - dumpJava(); - throw error; - } - }).timeout(10000); - - // Check if compile workspace command is excuted succesfully - test("Compile workspace", async () => { - let folder: string = assertWorkspace(); - const compile = await commands.executeCommand('jdk.workspace.compile'); - assert.ok(compile, " Compile workspace command not working"); - - - const mainClass = path.join(folder, 'target', 'classes', 'pkg', 'Main.class'); - assert.ok(fs.statSync(mainClass).isFile(), "Class created by compilation: " + mainClass); - - myExplorer.createViewProvider(await awaitClient(), "foundProjects").then(async (lvp) => { - const firstLevelChildren = await (lvp.getChildren() as Thenable<any[]>); - assert.strictEqual(firstLevelChildren.length, 1, "One child under the root"); - const item = await (lvp.getTreeItem(firstLevelChildren[0]) as Thenable<TreeItem>); - assert.strictEqual(item?.label, "basicapp", "Element is named as the Maven project"); - }); - }).timeout(10000); - - // Get Project info - test("Get project sources, classpath, and packages", async () => { - let folder: string = assertWorkspace(); - try { - console.log("Test: get project java source roots"); - let res: any = await commands.executeCommand("jdk.java.get.project.source.roots", Uri.file(folder).toString()); - console.log(`Test: get project java source roots finished with ${res}`); - assert.ok(res, "No java source root returned"); - assert.strictEqual(res.length, 2, `Invalid number of java roots returned`); - assert.strictEqual(res[0], path.join('file:', folder, 'src', 'main', 'java') + path.sep, `Invalid java main source root returned`); - assert.strictEqual(res[1], path.join('file:', folder, 'src', 'test', 'java') + path.sep, `Invalid java test source root returned`); - - console.log("Test: get project resource roots"); - res = await commands.executeCommand("jdk.java.get.project.source.roots", Uri.file(folder).toString(), 'resources'); - console.log(`Test: get project resource roots finished with ${res}`); - assert.ok(res, "No resource root returned"); - assert.strictEqual(res.length, 1, `Invalid number of resource roots returned`); - assert.strictEqual(res[0], path.join('file:', folder, 'src', 'main', 'resources') + path.sep, `Invalid resource root returned`); - - console.log("Test: get project compile classpath"); - res = await commands.executeCommand("jdk.java.get.project.classpath", Uri.file(folder).toString()); - console.log(`Test: get project compile classpath finished with ${res}`); - assert.ok(res, "No compile classpath returned"); - assert.strictEqual(res.length, 9, `Invalid number of compile classpath roots returned`); - assert.ok(res.find((item: any) => item === path.join('file:', folder, 'target', 'classes') + path.sep, `Invalid compile classpath root returned`)); - - console.log("Test: get project source classpath"); - res = await commands.executeCommand("jdk.java.get.project.classpath", Uri.file(folder).toString(), 'SOURCE'); - console.log(`Test: get project source classpath finished with ${res}`); - assert.ok(res, "No source classpath returned"); - assert.strictEqual(res.length, 3, `Invalid number of source classpath roots returned`); - assert.ok(res.find((item: any) => item === path.join('file:', folder, 'src', 'main', 'java') + path.sep, `Invalid source classpath root returned`)); - assert.ok(res.find((item: any) => item === path.join('file:', folder, 'src', 'main', 'resources') + path.sep, `Invalid source classpath root returned`)); - assert.ok(res.find((item: any) => item === path.join('file:', folder, 'src', 'test', 'java') + path.sep, `Invalid source classpath root returned`)); - - console.log("Test: get project boot classpath"); - res = await commands.executeCommand("jdk.java.get.project.classpath", Uri.file(folder).toString(), 'BOOT'); - console.log(`Test: get project boot classpath finished with ${res}`); - assert.ok(res, "No boot classpath returned"); - assert.ok(res.length > 0, `Invalid number of boot classpath roots returned`); - - console.log("Test: get project boot source classpath"); - res = await commands.executeCommand("jdk.java.get.project.classpath", Uri.file(folder).toString(), 'BOOT', true); - console.log(`Test: get project boot source classpath finished with ${res}`); - assert.ok(res, "No boot source classpath returned"); - assert.ok(res.length > 0, `Invalid number of boot source classpath roots returned`); - - console.log("Test: get all project packages"); - res = await commands.executeCommand("jdk.java.get.project.packages", Uri.file(folder).toString()); - console.log(`Test: get all project packages finished with ${res}`); - assert.ok(res, "No packages returned"); - assert.ok(res.length > 0, `Invalid number of packages returned`); - - console.log("Test: get project source packages"); - res = await commands.executeCommand("jdk.java.get.project.packages", Uri.file(folder).toString(), true); - console.log(`Test: get project source packages finished with ${res}`); - assert.ok(res, "No packages returned"); - assert.strictEqual(res.length, 1, `Invalid number of packages returned`); - assert.strictEqual(res[0], 'pkg', `Invalid package returned`); - } catch (error) { - dumpJava(); - throw error; - } - }).timeout(10000); - - // Check if clean workspace command is excuted succesfully - test("Clean workspace", async () => { - let folder: string = assertWorkspace(); - const clean = await commands.executeCommand('jdk.workspace.clean'); - assert.ok(clean, " Clean workspace command not working"); - - const mainClass = path.join(folder, 'target'); - assert.ok(!fs.existsSync(mainClass), "Class created by compilation: " + mainClass); - }).timeout(10000); - -}); \ No newline at end of file diff --git a/vscode/src/test/suite/general/explorer.test.ts b/vscode/src/test/suite/general/explorer.test.ts index e719d7c..1e844ac 100644 --- a/vscode/src/test/suite/general/explorer.test.ts +++ b/vscode/src/test/suite/general/explorer.test.ts @@ -25,14 +25,14 @@ import * as assert from 'assert'; // You can import and use all API from the 'vscode' module // as well as import your extension to test it import * as vscode from 'vscode'; -import * as myExtension from '../../../extension'; +import { awaitClient } from '../../testutils'; import * as myExplorer from '../../../explorer'; suite('Explorer Test Suite', () => { vscode.window.showInformationMessage('Start explorer tests.'); test('Explorer can be created', async () => { - const lvp = await myExplorer.createViewProvider(await myExtension.awaitClient(), 'foundProjects'); + const lvp = await myExplorer.createViewProvider(await awaitClient(), 'foundProjects'); const firstLevelChildren = await (lvp.getChildren() as Thenable<any[]>); assert.strictEqual(firstLevelChildren.length, 0, "No child under the root"); }).timeout(10000); diff --git a/vscode/src/test/suite/general/extension.test.ts b/vscode/src/test/suite/general/extension.test.ts index 49d3a44..03370a6 100644 --- a/vscode/src/test/suite/general/extension.test.ts +++ b/vscode/src/test/suite/general/extension.test.ts @@ -26,11 +26,11 @@ import * as assert from 'assert'; import * as fs from 'fs'; import * as path from 'path'; import * as vscode from 'vscode'; -import * as myExtension from '../../../extension'; import * as myExplorer from '../../../explorer'; import { CodeAction, commands, extensions, Selection, Uri, window, workspace, TreeItem } from 'vscode'; -import { assertWorkspace, dumpJava, getFilePaths, openFile, prepareProject, replaceCode } from '../../testutils'; +import { assertWorkspace, awaitClient, dumpJava, findClusters, getFilePaths, openFile, prepareProject, replaceCode } from '../../testutils'; import { FORMATTED_POM_XML, SAMPLE_CODE_FORMAT_DOCUMENT, SAMPLE_CODE_SORT_IMPORTS, SAMPLE_CODE_UNUSED_IMPORTS } from '../../constants'; +import { extConstants } from '../../../constants'; suite('Extension Test Suite', function () { window.showInformationMessage('Start all tests.'); @@ -67,7 +67,7 @@ suite('Extension Test Suite', function () { assert(nbcode); const extraCluster = path.join(nbcode.extensionPath, "nbcode", "extra"); - let clusters = myExtension.findClusters('non-existent'). + let clusters = findClusters('non-existent'). // ignore 'extra' cluster in the extension path, since nbjavac is there during development: filter(s => !s.startsWith(extraCluster)); @@ -166,7 +166,7 @@ suite('Extension Test Suite', function () { if (refactorActions && refactorActions.length > 0) { for await (const action of refactorActions) { if (action.command && action.command.arguments) { - if (action.command.command === myExtension.COMMAND_PREFIX + ".surround.with") { + if (action.command.command === extConstants.COMMAND_PREFIX + ".surround.with") { //this action has a popup where the user needs to //select a template that should be used for the surround: continue; @@ -197,7 +197,7 @@ suite('Extension Test Suite', function () { assert.strictEqual(tests[1].tests[0].name, 'testTrue', `Invalid test name returned`); console.log("Test: run all workspace tests"); - await vscode.commands.executeCommand(myExtension.COMMAND_PREFIX + '.run.test', workspaceFolder.uri.toString()); + await vscode.commands.executeCommand(extConstants.COMMAND_PREFIX + '.run.test', workspaceFolder.uri.toString()); console.log(`Test: run all workspace tests finished`); } catch (error) { dumpJava(); @@ -215,7 +215,7 @@ suite('Extension Test Suite', function () { const mainClass = path.join(folder, 'target', 'classes', 'pkg', 'Main.class'); assert.ok(fs.statSync(mainClass).isFile(), "Class created by compilation: " + mainClass); - myExplorer.createViewProvider(await myExtension.awaitClient(), "foundProjects").then(async (lvp) => { + myExplorer.createViewProvider(await awaitClient(), "foundProjects").then(async (lvp) => { const firstLevelChildren = await (lvp.getChildren() as Thenable<any[]>); assert.strictEqual(firstLevelChildren.length, 1, "One child under the root"); const item = await (lvp.getTreeItem(firstLevelChildren[0]) as Thenable<TreeItem>); diff --git a/vscode/src/test/suite/testutils.ts b/vscode/src/test/suite/testutils.ts deleted file mode 100644 index b5cd606..0000000 --- a/vscode/src/test/suite/testutils.ts +++ /dev/null @@ -1,234 +0,0 @@ - -/* - * Copyright (c) 2023, Oracle and/or its affiliates. - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -/* This file has been modified for Oracle Java SE extension */ - -import * as assert from 'assert'; -import * as vscode from 'vscode'; -import * as fs from 'fs'; -import * as path from 'path'; -import { spawn, ChildProcessByStdio } from 'child_process'; -import { Readable } from 'stream'; -import { EXAMPLE_POM, MAIN_JAVA, MAIN_TEST_JAVA, SAMPLE_CODE_FORMAT_DOCUMENT, SAMPLE_CODE_REFACTOR, SAMPLE_CODE_SORT_IMPORTS, SAMPLE_CODE_UNUSED_IMPORTS } from './constants'; -import { extConstants } from '../../constants'; - -/** - * Folder path currently opened in VSCode workspace - * @returns String containing the folder path of the workspace - */ -export function assertWorkspace(): string { - assert.ok(vscode.workspace, "workspace is defined"); - const dirs = vscode.workspace.workspaceFolders; - assert.ok(dirs?.length, "There are some workspace folders: " + dirs); - assert.strictEqual(dirs.length, 1, "One folder provided"); - let folder: string = dirs[0].uri.fsPath; - - return folder; -} - -/** - * File paths of all the files and folders usd for testing - * @returns Object containing all the file and folder paths - */ -export function getFilePaths(): { [key: string]: string } { - let folder: string = assertWorkspace(); - - const filePaths: { [key: string]: string } = {}; - filePaths['pkg'] = path.join(folder, 'src', 'main', 'java', 'pkg'); - filePaths['testPkg'] = path.join(folder, 'src', 'test', 'java', 'pkg'); - filePaths['resources'] = path.join(folder, 'src', 'main', 'resources'); - - filePaths['mainJava'] = path.join(filePaths['pkg'], 'Main.java'); - filePaths['formatDocument'] = path.join(filePaths['pkg'], 'FormatDocument.java'); - filePaths['sortImports'] = path.join(filePaths['pkg'], 'SortImports.java'); - filePaths['unusedImports'] = path.join(filePaths['pkg'], 'UnusedImports.java'); - filePaths['refactorActions'] = path.join(filePaths['pkg'], 'RefactorActions.java'); - filePaths['mainTestJava'] = path.join(filePaths['testPkg'], 'MainTest.java'); - - filePaths['pom'] = path.join(folder, 'pom.xml'); - - return filePaths; -} - -/** - * Prepares the sample project for testing - * @param filePaths - * @returns promise that waits till all the files and folders are created - */ -export async function prepareProject(filePaths: { [key: string]: string }): Promise<void> { - await fs.promises.writeFile(filePaths['pom'], EXAMPLE_POM); - - await fs.promises.mkdir(filePaths['pkg'], { recursive: true }); - await fs.promises.mkdir(filePaths['resources'], { recursive: true }); - await fs.promises.mkdir(filePaths['testPkg'], { recursive: true }); - - await fs.promises.writeFile(filePaths['mainJava'], MAIN_JAVA); - - await fs.promises.writeFile(filePaths['mainTestJava'], MAIN_TEST_JAVA); - await vscode.workspace.saveAll(); - - await fs.promises.writeFile(filePaths['formatDocument'], SAMPLE_CODE_FORMAT_DOCUMENT); - await fs.promises.writeFile(filePaths['sortImports'], SAMPLE_CODE_SORT_IMPORTS); - await fs.promises.writeFile(filePaths['unusedImports'], SAMPLE_CODE_UNUSED_IMPORTS); - await fs.promises.writeFile(filePaths['refactorActions'], SAMPLE_CODE_REFACTOR); - - await waitProjectRecognized(filePaths.mainJava); -} - -/** - * Wait till all the commands of the extension are loaded - * @returns promise that timeouts till all the commands are loaded - */ -export async function waitCommandsReady(): Promise<void> { - return new Promise((resolve, reject) => { - function checkCommands(attempts: number, cb: () => void) { - try { - // this command is parameterless - vscode.commands.executeCommand("jdk.java.attachDebugger.configurations") - console.log("JDK commands ready."); - resolve(); - } catch (e) { - if (attempts > 0) { - console.log("Waiting for JDK commands to be registered, " + attempts + " attempts to go..."); - setTimeout(() => checkCommands(attempts - 1, cb), 100); - } else { - reject(new Error("Timeout waiting for JDK commands registration: " + e)); - } - } - } - awaitClient().then(() => checkCommands(5, () => { })); - }); -} - - -/** - * Ensures that the project that holds the parameter file was opened in JDK. - * @param someJavaFile - * @returns promise that will be fullfilled after the project opens in JDK. - */ -async function waitProjectRecognized(someJavaFile: string): Promise<void> { - return waitCommandsReady().then(() => { - const u: vscode.Uri = vscode.Uri.file(someJavaFile); - // clear out possible bad or negative caches. - return vscode.commands.executeCommand(extConstants.COMMAND_PREFIX + ".clear.project.caches").then( - // this should assure opening the root with the created project. - () => vscode.commands.executeCommand(extConstants.COMMAND_PREFIX + ".java.get.project.packages", u.toString()) - ); - }); -} - - -/** - * Replaces code in editor with the provided code - * @param editor - * @param code - * @returns promise that will have replaced code in the editor - */ -export async function replaceCode(editor: vscode.TextEditor | undefined, code: string): Promise<void> { - const doc = editor?.document; - assert(doc !== undefined, 'editor cannot be initialzed'); - - const range = new vscode.Range(doc.lineAt(0).range.start, doc.lineAt(doc.lineCount - 1).range.end) - - await editor?.edit(editBuilder => { - editBuilder.replace(range, code); - }); -} - -/** - * Opens a file in VScode workspace - * @param filePath - * @returns promise that contains instance of the editor opened - */ -export async function openFile(filePath: string): Promise<vscode.TextEditor> { - const document: vscode.TextDocument = await vscode.workspace.openTextDocument(vscode.Uri.file(filePath)); - await vscode.window.showTextDocument(document); - const editor = vscode.window.activeTextEditor; - assert(editor !== undefined, 'editor cannot be initialzed'); - - return editor; -} - -/** - * If some error is encountered in the tests then it dumps java process - * @returns promise that dumps the java process - */ -export async function dumpJava(): Promise<void> { - const cmd = 'jps'; - const args = ['-v']; - console.log(`Running: ${cmd} ${args.join(' ')}`); - let p: ChildProcessByStdio<null, Readable, Readable> = spawn(cmd, args, { - stdio: ["ignore", "pipe", "pipe"], - }); - let n = await new Promise<number>((r, e) => { - p.stdout.on('data', function (d: any) { - console.log(d.toString()); - }); - p.stderr.on('data', function (d: any) { - console.log(d.toString()); - }); - p.on('close', function (code: number) { - r(code); - }); - }); - console.log(`${cmd} ${args.join(' ')} finished with code ${n}`); -} - -export const awaitClient = async () : Promise<NbLanguageClient> => { - const extension = vscode.extensions.getExtension(extConstants.ORACLE_VSCODE_EXTENSION_ID); - if (!extension) { - return Promise.reject(new Error(l10n.value("jdk.extension.notInstalled.label"))); - } - if(extension.isActive){ - return globalVars.clientPromise.client; - } - const waitForExtenstionActivation : Thenable<NbLanguageClient> = extension.activate().then(async () => { - return await globalVars.clientPromise.client; - }); - return Promise.resolve(waitForExtenstionActivation); -} - -export function findClusters(myPath : string): string[] { - let clusters = []; - for (let e of vscode.extensions.all) { - if (e.extensionPath === myPath) { - continue; - } - const dir = path.join(e.extensionPath, 'nbcode'); - if (!fs.existsSync(dir)) { - continue; - } - const exists = fs.readdirSync(dir); - for (let clusterName of exists) { - let clusterPath = path.join(dir, clusterName); - let clusterModules = path.join(clusterPath, 'config', 'Modules'); - if (!fs.existsSync(clusterModules)) { - continue; - } - let perm = fs.statSync(clusterModules); - if (perm.isDirectory()) { - clusters.push(clusterPath); - } - } - } - return clusters; -} From 0c664a4b2709c5ca13e5c5cbe79eaf35ac9a9612 Mon Sep 17 00:00:00 2001 From: Achal Talati <achal.talati@oracle.com> Date: Tue, 8 Oct 2024 18:57:10 +0530 Subject: [PATCH 06/17] refactored webviews, register and notification listeners --- vscode/src/commands/webViews.ts | 2 +- vscode/src/configurations/handlers.ts | 4 + vscode/src/configurations/listener.ts | 34 -- vscode/src/extension.ts | 333 +----------------- vscode/src/listener.ts | 59 ++++ vscode/src/lsp/clientPromise.ts | 5 +- vscode/src/lsp/initializer.ts | 2 +- vscode/src/lsp/notifications/handlers.ts | 135 +++++++ vscode/src/lsp/notifications/register.ts | 25 ++ vscode/src/lsp/requests/handlers.ts | 141 ++++++++ vscode/src/lsp/requests/register.ts | 25 ++ vscode/src/lsp/types.ts | 5 + .../{ => webviews}/jdkDownloader/action.ts | 10 +- .../{ => webviews}/jdkDownloader/prompt.ts | 2 +- .../{ => webviews}/jdkDownloader/styles.ts | 0 .../src/{ => webviews}/jdkDownloader/view.ts | 8 +- vscode/src/webviews/nbWebviewHandler.ts | 78 ++++ 17 files changed, 503 insertions(+), 365 deletions(-) delete mode 100644 vscode/src/configurations/listener.ts create mode 100644 vscode/src/listener.ts create mode 100644 vscode/src/lsp/notifications/handlers.ts create mode 100644 vscode/src/lsp/notifications/register.ts create mode 100644 vscode/src/lsp/requests/handlers.ts create mode 100644 vscode/src/lsp/requests/register.ts rename vscode/src/{ => webviews}/jdkDownloader/action.ts (98%) rename vscode/src/{ => webviews}/jdkDownloader/prompt.ts (96%) rename vscode/src/{ => webviews}/jdkDownloader/styles.ts (100%) rename vscode/src/{ => webviews}/jdkDownloader/view.ts (98%) create mode 100644 vscode/src/webviews/nbWebviewHandler.ts diff --git a/vscode/src/commands/webViews.ts b/vscode/src/commands/webViews.ts index 39ab54e..6a3efc7 100644 --- a/vscode/src/commands/webViews.ts +++ b/vscode/src/commands/webViews.ts @@ -13,7 +13,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -import { JdkDownloaderView } from "../jdkDownloader/view"; +import { JdkDownloaderView } from "../webviews/jdkDownloader/view"; import { extCommands } from "./commands"; import { ICommand } from "./types"; diff --git a/vscode/src/configurations/handlers.ts b/vscode/src/configurations/handlers.ts index e42eb5d..7cd3e97 100644 --- a/vscode/src/configurations/handlers.ts +++ b/vscode/src/configurations/handlers.ts @@ -27,6 +27,10 @@ export const getConfigurationValue = <T>(key: string, defaultValue: T | undefine return defaultValue != undefined ? conf.get(key, defaultValue) : conf.get(key) as T; } +export const updateConfigurationValue = <T>(key: string, newValue: T): void => { + workspace.getConfiguration(extConstants.COMMAND_PREFIX).update(key, newValue); +} + export const getBuiltinConfigurationValue = <T>(key: string, defaultValue: T | undefined = undefined): T => { const splitKey = key.split('.'); const selector = splitKey?.[0]; diff --git a/vscode/src/configurations/listener.ts b/vscode/src/configurations/listener.ts deleted file mode 100644 index ca78014..0000000 --- a/vscode/src/configurations/listener.ts +++ /dev/null @@ -1,34 +0,0 @@ -/* - Copyright (c) 2023-2024, Oracle and/or its affiliates. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - https://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ -import { ConfigurationChangeEvent, workspace } from "vscode"; -import { userConfigsListened } from "./configuration"; -import { globalVars } from "../extension"; - -let timeout: NodeJS.Timeout | undefined = undefined; -export const configChangeListener = (params: ConfigurationChangeEvent) => { - if (timeout) { - return; - } - timeout = setTimeout(() => { - timeout = undefined; - userConfigsListened.forEach((config: string) => { - const doesAffect = params.affectsConfiguration(config); - if(doesAffect){ - globalVars.clientPromise.restartExtension(globalVars.nbProcessManager, true); - } - }) - }, 0); -} \ No newline at end of file diff --git a/vscode/src/extension.ts b/vscode/src/extension.ts index 8d4d656..0907095 100644 --- a/vscode/src/extension.ts +++ b/vscode/src/extension.ts @@ -30,39 +30,28 @@ import { StreamInfo } from 'vscode-languageclient/node'; -import { - MessageType, - LogMessageNotification, - TelemetryEventNotification -} from 'vscode-languageclient'; - import * as net from 'net'; -import * as fs from 'fs'; -import * as path from 'path'; import * as vscode from 'vscode'; import { StreamDebugAdapter} from './streamDebugAdapter'; import { NbTestAdapter } from './testAdapter'; -import { asRanges, StatusMessageRequest, ShowStatusMessageParams, QuickPickRequest, InputBoxRequest, MutliStepInputRequest, TestProgressNotification, DebugConnector, - TextEditorDecorationCreateRequest, TextEditorDecorationSetNotification, TextEditorDecorationDisposeNotification, HtmlPageRequest, HtmlPageParams, - ExecInHtmlPageRequest, SetTextEditorDecorationParams, UpdateConfigurationRequest, QuickPickStep, InputBoxStep, SaveDocumentsRequest, SaveDocumentRequestParams -} from './lsp/protocol'; +import { DebugConnector, SetTextEditorDecorationParams} from './lsp/protocol'; import * as launchConfigurations from './launchConfigurations'; import { TreeViewService, Visualizer } from './explorer'; -import { initializeRunConfiguration, runConfigurationProvider, runConfigurationNodeProvider, configureRunSettings, runConfigurationUpdateAll } from './runConfiguration'; -import { InputStep, MultiStepInput } from './utils'; +import { initializeRunConfiguration, runConfigurationProvider, runConfigurationNodeProvider, configureRunSettings } from './runConfiguration'; import { PropertiesView } from './propertiesView/propertiesView'; import { l10n } from './localiser'; import { extConstants } from './constants'; import { ExtensionInfo } from './extensionInfo'; import { ClientPromise } from './lsp/clientPromise'; -import { ExtensionLogger, LogLevel } from './logger'; +import { ExtensionLogger } from './logger'; import { NbProcessManager } from './lsp/nbProcessManager'; import { initializeServer } from './lsp/initializer'; import { NbLanguageClient } from './lsp/nbLanguageClient'; -import { configChangeListener } from './configurations/listener'; -import { isNbJavacDisabledHandler } from './configurations/handlers'; import { subscribeCommands } from './commands/register'; import { VSNetBeansAPI } from './lsp/types'; +import { registerListenersAfterClientInit, registerListenersBeforeClientInit } from './listener'; +import { registerNotificationListeners } from './lsp/notifications/register'; +import { registerRequestListeners } from './lsp/requests/register'; export let LOGGER: ExtensionLogger; export namespace globalVars { @@ -74,32 +63,8 @@ export namespace globalVars { export let deactivated: boolean = true; export let nbProcessManager: NbProcessManager | null; export let testAdapter: NbTestAdapter | undefined; -} - -export function findClusters(myPath : string): string[] { - let clusters = []; - for (let e of vscode.extensions.all) { - if (e.extensionPath === myPath) { - continue; - } - const dir = path.join(e.extensionPath, 'nbcode'); - if (!fs.existsSync(dir)) { - continue; - } - const exists = fs.readdirSync(dir); - for (let clusterName of exists) { - let clusterPath = path.join(dir, clusterName); - let clusterModules = path.join(clusterPath, 'config', 'Modules'); - if (!fs.existsSync(clusterModules)) { - continue; - } - let perm = fs.statSync(clusterModules); - if (perm.isDirectory()) { - clusters.push(clusterPath); - } - } - } - return clusters; + export let decorations = new Map<string, TextEditorDecorationType>(); + export let decorationParamsByUri = new Map<vscode.Uri, SetTextEditorDecorationParams>(); } function contextUri(ctx : any) : vscode.Uri | undefined { @@ -123,32 +88,9 @@ export function activate(context: ExtensionContext): VSNetBeansAPI { globalVars.extensionInfo = new ExtensionInfo(context); LOGGER = new ExtensionLogger(extConstants.SERVER_NAME); - globalVars.clientPromise.clientPromiseInitialization(); - - context.subscriptions.push(workspace.onDidChangeConfiguration(configChangeListener)); + globalVars.clientPromise.initialize(); + registerListenersBeforeClientInit(); doActivateWithJDK(); - // find acceptable JDK and launch the Java part - // findJDK((specifiedJDK) => { - // let currentClusters = findClusters(context.extensionPath).sort(); - // const dsSorter = (a: TextDocumentFilter, b: TextDocumentFilter) => { - // return (a.language || '').localeCompare(b.language || '') - // || (a.pattern || '').localeCompare(b.pattern || '') - // || (a.scheme || '').localeCompare(b.scheme || ''); - // }; - // let currentDocumentSelectors = collectDocumentSelectors().sort(dsSorter); - // context.subscriptions.push(vscode.extensions.onDidChange(() => { - // const newClusters = findClusters(context.extensionPath).sort(); - // const newDocumentSelectors = collectDocumentSelectors().sort(dsSorter); - // if (newClusters.length !== currentClusters.length || newDocumentSelectors.length !== currentDocumentSelectors.length - // || newClusters.find((value, index) => value !== currentClusters[index]) || newDocumentSelectors.find((value, index) => value !== currentDocumentSelectors[index])) { - // currentClusters = newClusters; - // currentDocumentSelectors = newDocumentSelectors; - // activateWithJDK(specifiedJDK, context, log, true, clientResolve, clientReject); - // } - // })); - // activateWithJDK(specifiedJDK, context, log, true, clientResolve, clientReject); - // }); - //register debugger: let debugTrackerFactory =new NetBeansDebugAdapterTrackerFactory(); @@ -302,156 +244,18 @@ export function activate(context: ExtensionContext): VSNetBeansAPI { function doActivateWithJDK(): void { const connection: () => Promise<StreamInfo> = initializeServer(); - const c = NbLanguageClient.build(connection, LOGGER); + const client = NbLanguageClient.build(connection, LOGGER); LOGGER.log('Language Client: Starting'); - c.start().then(() => { + client.start().then(() => { globalVars.testAdapter = new NbTestAdapter(); - c.onNotification(StatusMessageRequest.type, showStatusBarMessage); - c.onRequest(HtmlPageRequest.type, showHtmlPage); - c.onRequest(ExecInHtmlPageRequest.type, execInHtmlPage); - c.onNotification(LogMessageNotification.type, (param) => LOGGER.log(param.message)); - c.onRequest(QuickPickRequest.type, async param => { - const selected = await window.showQuickPick(param.items, { title: param.title, placeHolder: param.placeHolder, canPickMany: param.canPickMany, ignoreFocusOut: true }); - return selected ? Array.isArray(selected) ? selected : [selected] : undefined; - }); - c.onRequest(UpdateConfigurationRequest.type, async (param) => { - LOGGER.log("Received config update: " + param.section + "." + param.key + "=" + param.value); - let wsFile: vscode.Uri | undefined = vscode.workspace.workspaceFile; - let wsConfig: vscode.WorkspaceConfiguration = vscode.workspace.getConfiguration(param.section); - if (wsConfig) { - try { - wsConfig.update(param.key, param.value, wsFile ? null : true) - .then(() => { - LOGGER.log("Updated configuration: " + param.section + "." + param.key + "=" + param.value + "; in: " + (wsFile ? wsFile.toString() : "Global")); - }) - .then(() => { - runConfigurationUpdateAll(); - }); - } catch (err) { - LOGGER.log("Failed to update configuration. Reason: " + (typeof err === "string" ? err : err instanceof Error ? err.message : "error"), LogLevel.ERROR); - } - } - }); - c.onRequest(SaveDocumentsRequest.type, async (request : SaveDocumentRequestParams) => { - const uriList = request.documents.map(s => { - let re = /^file:\/(?:\/\/)?([A-Za-z]):\/(.*)$/.exec(s); - if (!re) { - return s; - } - // don't ask why vscode mangles URIs this way; in addition, it uses lowercase drive letter ??? - return `file:///${re[1].toLowerCase()}%3A/${re[2]}`; - }); - for (let ed of workspace.textDocuments) { - if (uriList.includes(ed.uri.toString())) { - return ed.save(); - } - } - return false; - }); - c.onRequest(InputBoxRequest.type, async param => { - return await window.showInputBox({ title: param.title, prompt: param.prompt, value: param.value, password: param.password }); - }); - c.onRequest(MutliStepInputRequest.type, async param => { - const data: { [name: string]: readonly vscode.QuickPickItem[] | string } = {}; - async function nextStep(input: MultiStepInput, step: number, state: { [name: string]: readonly vscode.QuickPickItem[] | string }): Promise<InputStep | void> { - const inputStep = await c.sendRequest(MutliStepInputRequest.step, { inputId: param.id, step, data: state }); - if (inputStep && inputStep.hasOwnProperty('items')) { - const quickPickStep = inputStep as QuickPickStep; - state[inputStep.stepId] = await input.showQuickPick({ - title: param.title, - step, - totalSteps: quickPickStep.totalSteps, - placeholder: quickPickStep.placeHolder, - items: quickPickStep.items, - canSelectMany: quickPickStep.canPickMany, - selectedItems: quickPickStep.items.filter(item => item.picked) - }); - return (input: MultiStepInput) => nextStep(input, step + 1, state); - } else if (inputStep && inputStep.hasOwnProperty('value')) { - const inputBoxStep = inputStep as InputBoxStep; - state[inputStep.stepId] = await input.showInputBox({ - title: param.title, - step, - totalSteps: inputBoxStep.totalSteps, - value: state[inputStep.stepId] as string || inputBoxStep.value, - prompt: inputBoxStep.prompt, - password: inputBoxStep.password, - validate: (val) => { - const d = { ...state }; - d[inputStep.stepId] = val; - return c.sendRequest(MutliStepInputRequest.validate, { inputId: param.id, step, data: d }); - } - }); - return (input: MultiStepInput) => nextStep(input, step + 1, state); - } - } - await MultiStepInput.run(input => nextStep(input, 1, data)); - return data; - }); - c.onNotification(TestProgressNotification.type, param => { - if (globalVars.testAdapter) { - globalVars.testAdapter.testProgress(param.suite); - } - }); - let decorations = new Map<string, TextEditorDecorationType>(); - let decorationParamsByUri = new Map<vscode.Uri, SetTextEditorDecorationParams>(); - c.onRequest(TextEditorDecorationCreateRequest.type, param => { - let decorationType = vscode.window.createTextEditorDecorationType(param); - decorations.set(decorationType.key, decorationType); - return decorationType.key; - }); - c.onNotification(TextEditorDecorationSetNotification.type, param => { - let decorationType = decorations.get(param.key); - if (decorationType) { - let editorsWithUri = vscode.window.visibleTextEditors.filter( - editor => editor.document.uri.toString() == param.uri - ); - if (editorsWithUri.length > 0) { - editorsWithUri[0].setDecorations(decorationType, asRanges(param.ranges)); - decorationParamsByUri.set(editorsWithUri[0].document.uri, param); - } - } - }); - let disposableListener = vscode.window.onDidChangeVisibleTextEditors(editors => { - editors.forEach(editor => { - let decorationParams = decorationParamsByUri.get(editor.document.uri); - if (decorationParams) { - let decorationType = decorations.get(decorationParams.key); - if (decorationType) { - editor.setDecorations(decorationType, asRanges(decorationParams.ranges)); - } - } - }); - }); - globalVars.extensionInfo.pushSubscription(disposableListener); - c.onNotification(TextEditorDecorationDisposeNotification.type, param => { - let decorationType = decorations.get(param); - if (decorationType) { - decorations.delete(param); - decorationType.dispose(); - decorationParamsByUri.forEach((value, key, map) => { - if (value.key == param) { - map.delete(key); - } - }); - } - }); - c.onNotification(TelemetryEventNotification.type, (param) => { - const ls = globalVars.listeners.get(param); - if (ls) { - for (const listener of ls) { - commands.executeCommand(listener); - } - } - }); + registerListenersAfterClientInit(); + registerNotificationListeners(client); + registerRequestListeners(client); LOGGER.log('Language Client: Ready'); - globalVars.clientPromise.setClient[0](c); - commands.executeCommand('setContext', 'nbJdkReady', true); + globalVars.clientPromise.initializedSuccessfully(client); - // create project explorer: - //c.findTreeViewService().createView('foundProjects', 'Projects', { canSelectMany : false }); - createProjectView(c); + createProjectView(client); }).catch(globalVars.clientPromise.setClient[1]); } async function createProjectView(client : NbLanguageClient) { @@ -486,109 +290,6 @@ function doActivateWithJDK(): void { } } - const webviews = new Map<string, vscode.Webview>(); - - async function showHtmlPage(params : HtmlPageParams): Promise<void> { - return new Promise(resolve => { - let data = params.text; - const match = /<title>(.*)<\/title>/i.exec(data); - const name = match && match.length > 1 ? match[1] : ''; - const resourceDir = vscode.Uri.joinPath(globalVars.extensionInfo.getGlobalStorage(), params.id); - workspace.fs.createDirectory(resourceDir); - let view = vscode.window.createWebviewPanel('htmlView', name, vscode.ViewColumn.Beside, { - enableScripts: true, - localResourceRoots: [resourceDir, vscode.Uri.joinPath(globalVars.extensionInfo.getExtensionStorageUri(), 'node_modules', '@vscode/codicons', 'dist')] - }); - webviews.set(params.id, view.webview); - const resources = params.resources; - if (resources) { - for (const resourceName in resources) { - const resourceText = resources[resourceName]; - const resourceUri = vscode.Uri.joinPath(resourceDir, resourceName); - workspace.fs.writeFile(resourceUri, Buffer.from(resourceText, 'utf8')); - data = data.replace('href="' + resourceName + '"', 'href="' + view.webview.asWebviewUri(resourceUri) + '"'); - } - } - const codiconsUri = view.webview.asWebviewUri(vscode.Uri.joinPath(globalVars.extensionInfo.getExtensionStorageUri(), 'node_modules', '@vscode/codicons', 'dist', 'codicon.css')); - view.webview.html = data.replace('href="codicon.css"', 'href="' + codiconsUri + '"'); - view.webview.onDidReceiveMessage(message => { - switch (message.command) { - case 'dispose': - webviews.delete(params.id); - view.dispose(); - break; - case 'command': - vscode.commands.executeCommand(extConstants.COMMAND_PREFIX + '.htmlui.process.command', message.data); - break; - } - }); - view.onDidDispose(() => { - resolve(); - workspace.fs.delete(resourceDir, {recursive: true}); - }); - }); - } - - async function execInHtmlPage(params : HtmlPageParams): Promise<boolean> { - return new Promise(resolve => { - const webview = webviews.get(params.id); - if (webview) { - webview.postMessage({ - execScript: params.text, - pause: params.pause - }).then(ret => { - resolve(ret); - }); - } - resolve(false); - }); - } - - function showStatusBarMessage(params : ShowStatusMessageParams) { - let decorated : string = params.message; - let defTimeout; - - switch (params.type) { - case MessageType.Error: - decorated = '$(error) ' + params.message; - defTimeout = 0; - checkInstallNbJavac(params.message); - break; - case MessageType.Warning: - decorated = '$(warning) ' + params.message; - defTimeout = 0; - break; - default: - defTimeout = 10000; - break; - } - // params.timeout may be defined but 0 -> should be used - const timeout = params.timeout != undefined ? params.timeout : defTimeout; - if (timeout > 0) { - window.setStatusBarMessage(decorated, timeout); - } else { - window.setStatusBarMessage(decorated); - } - } - -function checkInstallNbJavac(msg: string) { - const NO_JAVA_SUPPORT = "Cannot initialize Java support"; - if (msg.startsWith(NO_JAVA_SUPPORT)) { - if (isNbJavacDisabledHandler()) { - const message = l10n.value("jdk.extension.nbjavac.message.supportedVersionRequired"); - const enable = l10n.value("jdk.extension.nbjavac.label.enableNbjavac"); - const settings = l10n.value("jdk.extension.nbjavac.label.openSettings"); - window.showErrorMessage(message, enable, settings).then(reply => { - if (enable === reply) { - workspace.getConfiguration().update(extConstants.COMMAND_PREFIX + '.advanced.disable.nbjavac', false); - } else if (settings === reply) { - vscode.commands.executeCommand('workbench.action.openSettings', extConstants.COMMAND_PREFIX + '.jdkhome'); - } - }); - } - } -} - export function deactivate(): Thenable<void> { if (globalVars.nbProcessManager?.getProcess() != null) { diff --git a/vscode/src/listener.ts b/vscode/src/listener.ts new file mode 100644 index 0000000..4713376 --- /dev/null +++ b/vscode/src/listener.ts @@ -0,0 +1,59 @@ +/* + Copyright (c) 2023-2024, Oracle and/or its affiliates. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ +import { ConfigurationChangeEvent, Disposable, TextEditor, window, workspace } from "vscode"; +import { userConfigsListened } from "./configurations/configuration"; +import { globalVars } from "./extension"; +import { asRanges } from "./lsp/protocol"; + +const configChangeHandler = (params: ConfigurationChangeEvent) => { + userConfigsListened.forEach((config: string) => { + const doesAffect = params.affectsConfiguration(config); + if (doesAffect) { + globalVars.clientPromise.restartExtension(globalVars.nbProcessManager, true); + } + }); +} + +const visibleTextEditorsChangeHandler = (editors: readonly TextEditor[]) => { + editors.forEach((editor: any) => { + let decorationParams = globalVars.decorationParamsByUri.get(editor.document.uri); + if (decorationParams) { + let decorationType = globalVars.decorations.get(decorationParams.key); + if (decorationType) { + editor.setDecorations(decorationType, asRanges(decorationParams.ranges)); + } + } + }); +} + +const configChangeListener = workspace.onDidChangeConfiguration(configChangeHandler); +const visibleTextEditorsChangeListener = window.onDidChangeVisibleTextEditors(visibleTextEditorsChangeHandler); + +const beforeInitlisteners: Disposable[] = [configChangeListener]; +const afterInitlisteners: Disposable[] = [visibleTextEditorsChangeListener]; + + +export const registerListenersBeforeClientInit = () => { + beforeInitlisteners.forEach(listener => { + globalVars.extensionInfo.pushSubscription(listener); + }); +} + +export const registerListenersAfterClientInit = () => { + afterInitlisteners.forEach(listener => { + globalVars.extensionInfo.pushSubscription(listener); + }); +} \ No newline at end of file diff --git a/vscode/src/lsp/clientPromise.ts b/vscode/src/lsp/clientPromise.ts index aab07f0..e75fc62 100644 --- a/vscode/src/lsp/clientPromise.ts +++ b/vscode/src/lsp/clientPromise.ts @@ -25,11 +25,10 @@ export class ClientPromise { client!: Promise<NbLanguageClient>; activationPending: boolean = true; - public clientPromiseInitialization = (): void => { + public initialize = (): void => { this.client = new Promise<NbLanguageClient>((clientOK, clientErr) => { this.setClient = [ (c: NbLanguageClient) => { - this.initialPromiseResolved = true; clientOK(c); }, (err: any) => { @@ -71,7 +70,7 @@ export class ClientPromise { try { await this.stopClient(); await nbProcessManager.killProcess(notifyKill); - this.clientPromiseInitialization(); + this.initialize(); initializeServer(); } catch (error) { LOGGER.log(`Error during activation: ${error}`, LogLevel.ERROR); diff --git a/vscode/src/lsp/initializer.ts b/vscode/src/lsp/initializer.ts index 8a85a74..ebbda6e 100644 --- a/vscode/src/lsp/initializer.ts +++ b/vscode/src/lsp/initializer.ts @@ -24,7 +24,7 @@ import { extConstants, NODE_WINDOWS_LABEL } from "../constants"; import { l10n } from "../localiser"; import { window } from "vscode"; import { ChildProcess } from "child_process"; -import { jdkDownloaderPrompt } from "../jdkDownloader/prompt"; +import { jdkDownloaderPrompt } from "../webviews/jdkDownloader/prompt"; import * as os from 'os'; import { LogLevel } from "../logger"; import { isNbJavacDisabledHandler } from "../configurations/handlers"; diff --git a/vscode/src/lsp/notifications/handlers.ts b/vscode/src/lsp/notifications/handlers.ts new file mode 100644 index 0000000..151db35 --- /dev/null +++ b/vscode/src/lsp/notifications/handlers.ts @@ -0,0 +1,135 @@ +/* + Copyright (c) 2023-2024, Oracle and/or its affiliates. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ +import { LogMessageNotification, MessageType, TelemetryEventNotification } from "vscode-languageclient"; +import { notificationOrRequestListenerType } from "../types"; +import { asRanges, ShowStatusMessageParams, StatusMessageRequest, TestProgressNotification, TextEditorDecorationDisposeNotification, TextEditorDecorationSetNotification } from "../protocol"; +import { commands, window, workspace } from "vscode"; +import { isNbJavacDisabledHandler, updateConfigurationValue } from "../../configurations/handlers"; +import { l10n } from "../../localiser"; +import { configKeys } from "../../configurations/configuration"; +import { builtInCommands } from "../../commands/commands"; +import { globalVars, LOGGER } from "../../extension"; + +const checkInstallNbJavac = (msg: string) => { + const NO_JAVA_SUPPORT = "Cannot initialize Java support"; + if (msg.startsWith(NO_JAVA_SUPPORT)) { + if (isNbJavacDisabledHandler()) { + const message = l10n.value("jdk.extension.nbjavac.message.supportedVersionRequired"); + const enable = l10n.value("jdk.extension.nbjavac.label.enableNbjavac"); + const settings = l10n.value("jdk.extension.nbjavac.label.openSettings"); + window.showErrorMessage(message, enable, settings).then(reply => { + if (enable === reply) { + updateConfigurationValue(configKeys.disableNbJavac, false); + } else if (settings === reply) { + commands.executeCommand(builtInCommands.openSettings, configKeys.jdkHome); + } + }); + } + } +} + +const showStatusBarMessageHandler = (params : ShowStatusMessageParams) => { + let decorated : string = params.message; + let defTimeout; + + switch (params.type) { + case MessageType.Error: + decorated = '$(error) ' + params.message; + defTimeout = 0; + checkInstallNbJavac(params.message); + break; + case MessageType.Warning: + decorated = '$(warning) ' + params.message; + defTimeout = 0; + break; + default: + defTimeout = 10000; + break; + } + // params.timeout may be defined but 0 -> should be used + const timeout = params.timeout != undefined ? params.timeout : defTimeout; + if (timeout > 0) { + window.setStatusBarMessage(decorated, timeout); + } else { + window.setStatusBarMessage(decorated); + } +} + +const logMessageHandler = (param: any) => { + LOGGER.log(param.message); +} + +const testProgressHandler = (param: any) => { + if (globalVars.testAdapter) { + globalVars.testAdapter.testProgress(param.suite); + } +} + +const textEditorSetDecorationHandler = (param: any) => { + let decorationType = globalVars.decorations.get(param.key); + if (decorationType) { + let editorsWithUri = window.visibleTextEditors.filter( + editor => editor.document.uri.toString() == param.uri + ); + if (editorsWithUri.length > 0) { + editorsWithUri[0].setDecorations(decorationType, asRanges(param.ranges)); + globalVars.decorationParamsByUri.set(editorsWithUri[0].document.uri, param); + } + } +} + +const textEditorDecorationDisposeHandler = (param: any) => { + let decorationType = globalVars.decorations.get(param); + if (decorationType) { + globalVars.decorations.delete(param); + decorationType.dispose(); + globalVars.decorationParamsByUri.forEach((value, key, map) => { + if (value.key == param) { + map.delete(key); + } + }); + } +} + + +const telemetryEventHandler = (param: any) => { + const ls = globalVars.listeners.get(param); + if (ls) { + for (const listener of ls) { + commands.executeCommand(listener); + } + } +} + +export const notificationListeners : notificationOrRequestListenerType[] = [{ + type: StatusMessageRequest.type, + handler: showStatusBarMessageHandler +}, { + type: LogMessageNotification.type, + handler: logMessageHandler +}, { + type: TestProgressNotification.type, + handler: testProgressHandler +},{ + type: TextEditorDecorationSetNotification.type, + handler: textEditorSetDecorationHandler +},{ + type: TextEditorDecorationDisposeNotification.type, + handler: textEditorDecorationDisposeHandler +},{ + type: TelemetryEventNotification.type, + handler: telemetryEventHandler +}]; \ No newline at end of file diff --git a/vscode/src/lsp/notifications/register.ts b/vscode/src/lsp/notifications/register.ts new file mode 100644 index 0000000..c5470a9 --- /dev/null +++ b/vscode/src/lsp/notifications/register.ts @@ -0,0 +1,25 @@ +/* + Copyright (c) 2023-2024, Oracle and/or its affiliates. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ +import { NbLanguageClient } from "../nbLanguageClient" +import { notificationOrRequestListenerType } from "../types" +import { notificationListeners } from "./handlers" + +export const registerNotificationListeners = (client: NbLanguageClient) => { + notificationListeners.forEach((listener: notificationOrRequestListenerType) => { + const { type, handler } = listener; + client.onNotification(type, handler); + }) +} \ No newline at end of file diff --git a/vscode/src/lsp/requests/handlers.ts b/vscode/src/lsp/requests/handlers.ts new file mode 100644 index 0000000..703bca8 --- /dev/null +++ b/vscode/src/lsp/requests/handlers.ts @@ -0,0 +1,141 @@ +/* + Copyright (c) 2023-2024, Oracle and/or its affiliates. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ +import { QuickPickItem, Uri, window, workspace, WorkspaceConfiguration } from "vscode"; +import { globalVars, LOGGER } from "../../extension"; +import { notificationOrRequestListenerType } from "../types"; +import { ExecInHtmlPageRequest, HtmlPageRequest, InputBoxRequest, InputBoxStep, MutliStepInputRequest, QuickPickRequest, QuickPickStep, SaveDocumentRequestParams, SaveDocumentsRequest, TextEditorDecorationCreateRequest, UpdateConfigurationRequest } from "../protocol"; +import { InputStep, MultiStepInput } from "../../utils"; +import { runConfigurationUpdateAll } from "../../runConfiguration"; +import { isError, isString } from "../../typesUtil"; +import { LogLevel } from "../../logger"; +import { execInHtmlPage, showHtmlPage } from "../../webviews/nbWebviewHandler"; + +const textEditorDecorationCreateRequestHandler = (param: any) => { + let decorationType = window.createTextEditorDecorationType(param); + globalVars.decorations.set(decorationType.key, decorationType); + return decorationType.key; +} + +const multiStepInputRequestHandler = async (param: any) => { + const client = await globalVars.clientPromise.client; + const data: { [name: string]: readonly QuickPickItem[] | string } = {}; + async function nextStep(input: MultiStepInput, step: number, state: { [name: string]: readonly QuickPickItem[] | string }): Promise<InputStep | void> { + const inputStep = await client.sendRequest(MutliStepInputRequest.step, { inputId: param.id, step, data: state }); + if (inputStep && inputStep.hasOwnProperty('items')) { + const quickPickStep = inputStep as QuickPickStep; + state[inputStep.stepId] = await input.showQuickPick({ + title: param.title, + step, + totalSteps: quickPickStep.totalSteps, + placeholder: quickPickStep.placeHolder, + items: quickPickStep.items, + canSelectMany: quickPickStep.canPickMany, + selectedItems: quickPickStep.items.filter(item => item.picked) + }); + return (input: MultiStepInput) => nextStep(input, step + 1, state); + } else if (inputStep && inputStep.hasOwnProperty('value')) { + const inputBoxStep = inputStep as InputBoxStep; + state[inputStep.stepId] = await input.showInputBox({ + title: param.title, + step, + totalSteps: inputBoxStep.totalSteps, + value: state[inputStep.stepId] as string || inputBoxStep.value, + prompt: inputBoxStep.prompt, + password: inputBoxStep.password, + validate: (val) => { + const d = { ...state }; + d[inputStep.stepId] = val; + return client.sendRequest(MutliStepInputRequest.validate, { inputId: param.id, step, data: d }); + } + }); + return (input: MultiStepInput) => nextStep(input, step + 1, state); + } + } + await MultiStepInput.run(input => nextStep(input, 1, data)); + return data; +} + +const inputBoxRequestHandler = async (param: any) =>{ + return await window.showInputBox({ title: param.title, prompt: param.prompt, value: param.value, password: param.password }); +} + +const saveDocumentRequestHandler = async (request : SaveDocumentRequestParams) => { + const uriList = request.documents.map(s => { + let re = /^file:\/(?:\/\/)?([A-Za-z]):\/(.*)$/.exec(s); + if (!re) { + return s; + } + // don't ask why vscode mangles URIs this way; in addition, it uses lowercase drive letter ??? + return `file:///${re[1].toLowerCase()}%3A/${re[2]}`; + }); + for (let ed of workspace.textDocuments) { + if (uriList.includes(ed.uri.toString())) { + return ed.save(); + } + } + return false; +} + +const updateConfigRequestHandler = async (param: any) => { + LOGGER.log(`Received config update: ${param.section}.${param.key}=${param.value}`); + let wsFile: Uri | undefined = workspace.workspaceFile; + let wsConfig: WorkspaceConfiguration = workspace.getConfiguration(param.section); + if (wsConfig) { + try { + wsConfig.update(param.key, param.value, wsFile ? null : true) + .then(() => { + LOGGER.log("Updated configuration: " + param.section + "." + param.key + "=" + param.value + "; in: " + (wsFile ? wsFile.toString() : "Global")); + }) + .then(() => { + runConfigurationUpdateAll(); + }); + } catch (err) { + LOGGER.log("Failed to update configuration. Reason: " + (isString(err) ? err : isError(err) ? err.message : "error"), LogLevel.ERROR); + } + } +} + +const quickPickRequestHandler = async (param: any) => { + const selected = await window.showQuickPick(param.items, { title: param.title, placeHolder: param.placeHolder, canPickMany: param.canPickMany, ignoreFocusOut: true }); + return selected ? Array.isArray(selected) ? selected : [selected] : undefined; +} + + +export const requestListeners : notificationOrRequestListenerType[] = [{ + type: TextEditorDecorationCreateRequest.type, + handler: textEditorDecorationCreateRequestHandler +},{ + type: MutliStepInputRequest.type, + handler: multiStepInputRequestHandler +}, { + type: InputBoxRequest.type, + handler: inputBoxRequestHandler +}, { + type: SaveDocumentsRequest.type, + handler: saveDocumentRequestHandler +}, { + type: UpdateConfigurationRequest.type, + handler: updateConfigRequestHandler +}, { + type: QuickPickRequest.type, + handler: quickPickRequestHandler +}, { + type: HtmlPageRequest.type, + handler: showHtmlPage +}, { + type: ExecInHtmlPageRequest.type, + handler: execInHtmlPage +}]; \ No newline at end of file diff --git a/vscode/src/lsp/requests/register.ts b/vscode/src/lsp/requests/register.ts new file mode 100644 index 0000000..f09e47f --- /dev/null +++ b/vscode/src/lsp/requests/register.ts @@ -0,0 +1,25 @@ +/* + Copyright (c) 2023-2024, Oracle and/or its affiliates. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ +import { NbLanguageClient } from "../nbLanguageClient" +import { notificationOrRequestListenerType } from "../types" +import { requestListeners } from "./handlers" + +export const registerRequestListeners = (client: NbLanguageClient) => { + requestListeners.forEach((listener: notificationOrRequestListenerType) => { + const { type, handler } = listener; + client.onRequest(type, handler); + }) +} \ No newline at end of file diff --git a/vscode/src/lsp/types.ts b/vscode/src/lsp/types.ts index 2f61bae..ffe58e9 100644 --- a/vscode/src/lsp/types.ts +++ b/vscode/src/lsp/types.ts @@ -25,3 +25,8 @@ export interface VSNetBeansAPI { version : string; apiVersion: string; } + +export type notificationOrRequestListenerType = { + type: any, + handler: any +} diff --git a/vscode/src/jdkDownloader/action.ts b/vscode/src/webviews/jdkDownloader/action.ts similarity index 98% rename from vscode/src/jdkDownloader/action.ts rename to vscode/src/webviews/jdkDownloader/action.ts index 6f690f4..0f9e68d 100644 --- a/vscode/src/jdkDownloader/action.ts +++ b/vscode/src/webviews/jdkDownloader/action.ts @@ -16,15 +16,15 @@ import { commands, OpenDialogOptions, OutputChannel, window, workspace } from "vscode"; import { JdkDownloaderView } from "./view"; -import { jdkDownloaderConstants } from "../constants"; +import { jdkDownloaderConstants } from "../../constants"; import * as path from 'path'; import * as fs from 'fs'; -import { calculateChecksum, downloadFileWithProgressBar, httpsGet } from "../utils"; +import { calculateChecksum, downloadFileWithProgressBar, httpsGet } from "../../utils"; import * as cp from 'child_process'; import { promisify } from "util"; -import { l10n } from "../localiser"; -import { LOGGER } from "../extension"; -import { LogLevel } from "../logger"; +import { l10n } from "../../localiser"; +import { LOGGER } from "../../extension"; +import { LogLevel } from "../../logger"; export class JdkDownloaderAction { public static readonly MANUAL_INSTALLATION_TYPE = "manual"; diff --git a/vscode/src/jdkDownloader/prompt.ts b/vscode/src/webviews/jdkDownloader/prompt.ts similarity index 96% rename from vscode/src/jdkDownloader/prompt.ts rename to vscode/src/webviews/jdkDownloader/prompt.ts index facc019..4de5a88 100644 --- a/vscode/src/jdkDownloader/prompt.ts +++ b/vscode/src/webviews/jdkDownloader/prompt.ts @@ -14,7 +14,7 @@ limitations under the License. */ import { commands, window } from "vscode"; -import { l10n } from "../localiser"; +import { l10n } from "../../localiser"; export const jdkDownloaderPrompt = () => { const downloadAndSetupActionLabel = l10n.value("jdk.extension.lspServer.label.downloadAndSetup") diff --git a/vscode/src/jdkDownloader/styles.ts b/vscode/src/webviews/jdkDownloader/styles.ts similarity index 100% rename from vscode/src/jdkDownloader/styles.ts rename to vscode/src/webviews/jdkDownloader/styles.ts diff --git a/vscode/src/jdkDownloader/view.ts b/vscode/src/webviews/jdkDownloader/view.ts similarity index 98% rename from vscode/src/jdkDownloader/view.ts rename to vscode/src/webviews/jdkDownloader/view.ts index 4e254d0..141d09c 100644 --- a/vscode/src/jdkDownloader/view.ts +++ b/vscode/src/webviews/jdkDownloader/view.ts @@ -14,14 +14,14 @@ limitations under the License. */ -import { jdkDownloaderConstants } from '../constants'; +import { jdkDownloaderConstants } from '../../constants'; import { ViewColumn, WebviewPanel, window } from 'vscode'; import * as os from 'os'; import { JdkDownloaderAction } from './action'; import { downloaderCss } from './styles'; -import { l10n } from '../localiser'; -import { LOGGER } from '../extension'; -import { LogLevel } from '../logger'; +import { l10n } from '../../localiser'; +import { LOGGER } from '../../extension'; +import { LogLevel } from '../../logger'; export class JdkDownloaderView { public static readonly OPEN_JDK_LABEL = "OpenJDK"; diff --git a/vscode/src/webviews/nbWebviewHandler.ts b/vscode/src/webviews/nbWebviewHandler.ts new file mode 100644 index 0000000..f7ff079 --- /dev/null +++ b/vscode/src/webviews/nbWebviewHandler.ts @@ -0,0 +1,78 @@ +/* + Copyright (c) 2023-2024, Oracle and/or its affiliates. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ +import { commands, Uri, ViewColumn, Webview, window, workspace } from "vscode"; +import { HtmlPageParams } from "../lsp/protocol"; +import { globalVars } from "../extension"; +import { nbCommands } from "../commands/commands"; + +const webviews = new Map<string, Webview>(); + +export const showHtmlPage = async (params: HtmlPageParams): Promise<void> => { + return new Promise(resolve => { + let data = params.text; + const match = /<title>(.*)<\/title>/i.exec(data); + const name = match && match.length > 1 ? match[1] : ''; + const resourceDir = Uri.joinPath(globalVars.extensionInfo.getGlobalStorage(), params.id); + const distPath = Uri.joinPath(globalVars.extensionInfo.getExtensionStorageUri(), 'node_modules', '@vscode/codicons', 'dist'); + workspace.fs.createDirectory(resourceDir); + let view = window.createWebviewPanel('htmlView', name, ViewColumn.Beside, { + enableScripts: true, + localResourceRoots: [resourceDir, distPath] + }); + webviews.set(params.id, view.webview); + const resources = params.resources; + if (resources) { + for (const resourceName in resources) { + const resourceText = resources[resourceName]; + const resourceUri = Uri.joinPath(resourceDir, resourceName); + workspace.fs.writeFile(resourceUri, Buffer.from(resourceText, 'utf8')); + data = data.replace(`href="${resourceName}"`, `href="${view.webview.asWebviewUri(resourceUri)}"`); + } + } + const codiconsUri = view.webview.asWebviewUri(Uri.joinPath(distPath, 'codicon.css')); + view.webview.html = data.replace('href="codicon.css"', `href="${codiconsUri}"`); + view.webview.onDidReceiveMessage(message => { + switch (message.command) { + case 'dispose': + webviews.delete(params.id); + view.dispose(); + break; + case 'command': + commands.executeCommand(nbCommands.htmlProcessCmd, message.data); + break; + } + }); + view.onDidDispose(() => { + resolve(); + workspace.fs.delete(resourceDir, { recursive: true }); + }); + }); +} + +export const execInHtmlPage = (params: HtmlPageParams): Promise<boolean> => { + return new Promise(resolve => { + const webview = webviews.get(params.id); + if (webview) { + webview.postMessage({ + execScript: params.text, + pause: params.pause + }).then(ret => { + resolve(ret); + }); + } + resolve(false); + }); +} \ No newline at end of file From ed7b018a6d4f5dc65abc47115b9d8f3d77933045 Mon Sep 17 00:00:00 2001 From: Achal Talati <achal.talati@oracle.com> Date: Wed, 9 Oct 2024 17:40:19 +0530 Subject: [PATCH 07/17] fixed disableNbJavac issue, refactored server startup code into multiple files --- vscode/src/configurations/configuration.ts | 1 + vscode/src/configurations/handlers.ts | 2 +- vscode/src/extension.ts | 51 +--------- vscode/src/lsp/clientPromise.ts | 2 +- vscode/src/lsp/initializer.ts | 106 +++----------------- vscode/src/lsp/launchOptions.ts | 3 +- vscode/src/lsp/nbProcessManager.ts | 4 + vscode/src/lsp/nbcode.ts | 110 +++++++++++++++++++++ 8 files changed, 137 insertions(+), 142 deletions(-) create mode 100644 vscode/src/lsp/nbcode.ts diff --git a/vscode/src/configurations/configuration.ts b/vscode/src/configurations/configuration.ts index 70005c3..709b030 100644 --- a/vscode/src/configurations/configuration.ts +++ b/vscode/src/configurations/configuration.ts @@ -28,6 +28,7 @@ export const configKeys = { runConfigCwd: 'runConfig.cwd', verbose: 'verbose', userdir: 'userdir', + revealInActivteProj: "revealActiveInProjects" }; export const builtInConfigKeys = { diff --git a/vscode/src/configurations/handlers.ts b/vscode/src/configurations/handlers.ts index 7cd3e97..7a50ee5 100644 --- a/vscode/src/configurations/handlers.ts +++ b/vscode/src/configurations/handlers.ts @@ -136,5 +136,5 @@ export const userdirHandler = (): string => { } export const isNbJavacDisabledHandler = (): boolean => { - return getConfigurationValue(configKeys.verbose, false); + return getConfigurationValue(configKeys.disableNbJavac, false); } \ No newline at end of file diff --git a/vscode/src/extension.ts b/vscode/src/extension.ts index 0907095..cd5cd33 100644 --- a/vscode/src/extension.ts +++ b/vscode/src/extension.ts @@ -45,7 +45,7 @@ import { ExtensionInfo } from './extensionInfo'; import { ClientPromise } from './lsp/clientPromise'; import { ExtensionLogger } from './logger'; import { NbProcessManager } from './lsp/nbProcessManager'; -import { initializeServer } from './lsp/initializer'; +import { clientInit, serverOptionsBuilder } from './lsp/initializer'; import { NbLanguageClient } from './lsp/nbLanguageClient'; import { subscribeCommands } from './commands/register'; import { VSNetBeansAPI } from './lsp/types'; @@ -90,7 +90,7 @@ export function activate(context: ExtensionContext): VSNetBeansAPI { globalVars.clientPromise.initialize(); registerListenersBeforeClientInit(); - doActivateWithJDK(); + clientInit(); //register debugger: let debugTrackerFactory =new NetBeansDebugAdapterTrackerFactory(); @@ -242,53 +242,6 @@ export function activate(context: ExtensionContext): VSNetBeansAPI { }); } -function doActivateWithJDK(): void { - const connection: () => Promise<StreamInfo> = initializeServer(); - const client = NbLanguageClient.build(connection, LOGGER); - - LOGGER.log('Language Client: Starting'); - client.start().then(() => { - globalVars.testAdapter = new NbTestAdapter(); - registerListenersAfterClientInit(); - registerNotificationListeners(client); - registerRequestListeners(client); - LOGGER.log('Language Client: Ready'); - globalVars.clientPromise.initializedSuccessfully(client); - - createProjectView(client); - }).catch(globalVars.clientPromise.setClient[1]); -} - async function createProjectView(client : NbLanguageClient) { - const ts : TreeViewService = client.findTreeViewService(); - let tv : vscode.TreeView<Visualizer> = await ts.createView('foundProjects', 'Projects', { canSelectMany : false }); - - async function revealActiveEditor(ed? : vscode.TextEditor) { - const uri = window.activeTextEditor?.document?.uri; - if (!uri || uri.scheme.toLowerCase() !== 'file') { - return; - } - if (!tv.visible) { - return; - } - let vis : Visualizer | undefined = await ts.findPath(tv, uri.toString()); - if (!vis) { - return; - } - tv.reveal(vis, { select : true, focus : false, expand : false }); - } - const netbeansConfig = workspace.getConfiguration(extConstants.COMMAND_PREFIX); - globalVars.extensionInfo.pushSubscription(window.onDidChangeActiveTextEditor(ed => { - if (netbeansConfig.get("revealActiveInProjects")) { - revealActiveEditor(ed); - } - })); - globalVars.extensionInfo.pushSubscription(vscode.commands.registerCommand(extConstants.COMMAND_PREFIX + ".select.editor.projects", () => revealActiveEditor())); - - // attempt to reveal NOW: - if (netbeansConfig.get("revealActiveInProjects")) { - revealActiveEditor(); - } - } export function deactivate(): Thenable<void> { diff --git a/vscode/src/lsp/clientPromise.ts b/vscode/src/lsp/clientPromise.ts index e75fc62..dd130d7 100644 --- a/vscode/src/lsp/clientPromise.ts +++ b/vscode/src/lsp/clientPromise.ts @@ -71,7 +71,7 @@ export class ClientPromise { await this.stopClient(); await nbProcessManager.killProcess(notifyKill); this.initialize(); - initializeServer(); + clientInit(); } catch (error) { LOGGER.log(`Error during activation: ${error}`, LogLevel.ERROR); throw error; diff --git a/vscode/src/lsp/initializer.ts b/vscode/src/lsp/initializer.ts index ebbda6e..1cda051 100644 --- a/vscode/src/lsp/initializer.ts +++ b/vscode/src/lsp/initializer.ts @@ -14,40 +14,23 @@ limitations under the License. */ import { StreamInfo } from "vscode-languageclient/node"; -import { getUserConfigLaunchOptionsDefaults, prepareNbcodeLaunchOptions } from "./launchOptions"; +import { getUserConfigLaunchOptionsDefaults } from "./launchOptions"; import { globalVars, LOGGER } from "../extension"; import { configKeys } from "../configurations/configuration"; -import { NbProcessManager } from "./nbProcessManager"; -import { enableDisableModules, findNbcode } from "./utils"; +import { enableDisableModules } from "./utils"; import * as net from 'net'; -import { extConstants, NODE_WINDOWS_LABEL } from "../constants"; -import { l10n } from "../localiser"; -import { window } from "vscode"; import { ChildProcess } from "child_process"; -import { jdkDownloaderPrompt } from "../webviews/jdkDownloader/prompt"; -import * as os from 'os'; -import { LogLevel } from "../logger"; -import { isNbJavacDisabledHandler } from "../configurations/handlers"; - -const launchNbcode = (): void => { - const ideLaunchOptions = prepareNbcodeLaunchOptions(); - const userdir = getUserConfigLaunchOptionsDefaults()[configKeys.userdir].value; - const specifiedJDK = getUserConfigLaunchOptionsDefaults()[configKeys.jdkHome].value; - const extensionPath = globalVars.extensionInfo.getExtensionStorageUri().fsPath; - const nbcodePath = findNbcode(extensionPath); - - const requiredJdk = specifiedJDK ? specifiedJDK : 'default system JDK'; - let launchMsg = l10n.value("jdk.extension.lspServer.statusBar.message.launching", { - SERVER_NAME: extConstants.SERVER_NAME, - requiredJdk: requiredJdk, - userdir: userdir - }); - LOGGER.log(launchMsg); - window.setStatusBarMessage(launchMsg, 2000); - - globalVars.nbProcessManager = new NbProcessManager(userdir, nbcodePath, ideLaunchOptions); - globalVars.nbProcessManager.startProcess(); -} +import { getConfigurationValue, isNbJavacDisabledHandler } from "../configurations/handlers"; +import { attachNbProcessListeners, launchNbcode } from "./nbcode"; +import { NbLanguageClient } from "./nbLanguageClient"; +import { NbTestAdapter } from "../testAdapter"; +import { registerListenersAfterClientInit } from "../listener"; +import { registerNotificationListeners } from "./notifications/register"; +import { registerRequestListeners } from "./requests/register"; +import { TreeViewService, Visualizer } from "../explorer"; +import { commands, TextEditor, TreeView, window, workspace } from "vscode"; +import { extConstants } from "../constants"; +import { extCommands } from "../commands/commands"; const establishConnection = () => new Promise<StreamInfo>((resolve, reject) => { const nbProcess = globalVars.nbProcessManager?.getProcess(); @@ -61,21 +44,8 @@ const establishConnection = () => new Promise<StreamInfo>((resolve, reject) => { LOGGER.log(`LSP server launching: ${nbProcessManager.getProcessId()}`); LOGGER.log(`LSP server user directory: ${getUserConfigLaunchOptionsDefaults()[configKeys.userdir].value}`); - let status = false; - nbProcess.stdout?.on('data', (d: any) => { - status = processOnDataHandler(nbProcessManager, d.toString(), true); - }); - nbProcess.stderr?.on('data', (d: any) => { - processOnDataHandler(nbProcessManager, d.toString(), false); - }); - nbProcess.on('close', (code: number) => { - const status = processOnCloseHandler(nbProcessManager, code) - if (status != null) { - reject(status); - } - }); - try { + attachNbProcessListeners(nbProcessManager); connectToServer(nbProcess).then(server => resolve({ reader: server, writer: server @@ -121,55 +91,11 @@ const connectToServer = (nbProcess: ChildProcess): Promise<net.Socket> => { }); } -const processOnDataHandler = (nbProcessManager: NbProcessManager, text: string, isOut: boolean) => { - if (nbProcessManager) { - globalVars.clientPromise.activationPending = false; - } - LOGGER.logNoNL(text); - isOut ? nbProcessManager.appendStdOut(text) : nbProcessManager.appendStdErr(text); - - if (nbProcessManager.getStdOut()?.match(/org.netbeans.modules.java.lsp.server/)) { - return true; - } - return false; -} - - -const processOnCloseHandler = (nbProcessManager: NbProcessManager, code: number): string | null => { - const globalnbProcessManager = globalVars.nbProcessManager; - if (globalnbProcessManager == nbProcessManager) { - globalVars.nbProcessManager = null; - if (code != 0) { - window.showWarningMessage(l10n.value("jdk.extension.lspServer.warning_message.serverExited", { SERVER_NAME: extConstants.SERVER_NAME, code: code })); - } - } - if (nbProcessManager.getStdOut()?.match(/Cannot find java/) || (os.type() === NODE_WINDOWS_LABEL && !globalVars.deactivated)) { - jdkDownloaderPrompt(); - } - if (nbProcessManager.getStdOut() != null) { - let match = nbProcessManager.getStdOut()!.match(/org.netbeans.modules.java.lsp.server[^\n]*/) - if (match?.length == 1) { - LOGGER.log(match[0]); - } else { - LOGGER.log("Cannot find org.netbeans.modules.java.lsp.server in the log!", LogLevel.ERROR); - } - LOGGER.log(`Please refer to troubleshooting section for more info: https://github.com/oracle/javavscode/blob/main/README.md#troubleshooting`); - LOGGER.showOutputChannelUI(false); - - nbProcessManager.killProcess(false); - return l10n.value("jdk.extension.error_msg.notEnabled", { SERVER_NAME: extConstants.SERVER_NAME }); - } else { - LOGGER.log(`LSP server ${nbProcessManager.getProcessId()} terminated with ${code}`); - LOGGER.log(`Exit code ${code}`); - } - return null; -} - const enableDisableNbjavacModule = () => { const userdirPath = getUserConfigLaunchOptionsDefaults()[configKeys.userdir].value const nbjavacValue = isNbJavacDisabledHandler(); const extensionPath = globalVars.extensionInfo.getExtensionStorageUri().fsPath; - enableDisableModules(extensionPath, userdirPath, ['org.netbeans.libs.nbjavacapi'], nbjavacValue); + enableDisableModules(extensionPath, userdirPath, ['org.netbeans.libs.nbjavacapi'], !nbjavacValue); } const serverBuilder = () => { @@ -179,7 +105,7 @@ const serverBuilder = () => { } export const clientInit = () => { - const connection: () => Promise<StreamInfo> = serverBuilder(); + const connection: () => Promise<StreamInfo> = serverOptionsBuilder(); const client = NbLanguageClient.build(connection, LOGGER); LOGGER.log('Language Client: Starting'); diff --git a/vscode/src/lsp/launchOptions.ts b/vscode/src/lsp/launchOptions.ts index 55d9675..7db45e5 100644 --- a/vscode/src/lsp/launchOptions.ts +++ b/vscode/src/lsp/launchOptions.ts @@ -16,6 +16,7 @@ import { builtInConfigKeys, configKeys } from "../configurations/configuration" import { isDarkColorThemeHandler, isNbJavacDisabledHandler, jdkHomeValueHandler, lspServerVmOptionsHandler, projectSearchRootsValueHandler, userdirHandler } from "../configurations/handlers"; import { l10n } from "../localiser"; +import { isString } from "../typesUtil"; import { userDefinedLaunchOptionsType } from "./types" export const getUserConfigLaunchOptionsDefaults = (): userDefinedLaunchOptionsType => { @@ -63,7 +64,7 @@ const prepareUserConfigLaunchOptions = (): string[] => { if (!optionToPass && Array.isArray(value)) { launchOptions.push(...value); } - else if (typeof (optionToPass) === "string") { + else if (isString(optionToPass)) { launchOptions.push(`${optionToPass}${value}`); } else if (Array.isArray(optionToPass)) { const arg: string[] = [...optionToPass, value]; diff --git a/vscode/src/lsp/nbProcessManager.ts b/vscode/src/lsp/nbProcessManager.ts index b6d174b..a0bef09 100644 --- a/vscode/src/lsp/nbProcessManager.ts +++ b/vscode/src/lsp/nbProcessManager.ts @@ -99,6 +99,10 @@ export class NbProcessManager { getStdOut = () => { return this.stdOutText } + + setStdOut = (stdOut: string | null) => { + this.stdOutText = stdOut; + } getStdErr = () => { return this.stdErrText; diff --git a/vscode/src/lsp/nbcode.ts b/vscode/src/lsp/nbcode.ts new file mode 100644 index 0000000..2cad5be --- /dev/null +++ b/vscode/src/lsp/nbcode.ts @@ -0,0 +1,110 @@ +/* + Copyright (c) 2023-2024, Oracle and/or its affiliates. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +import { window } from "vscode"; +import { configKeys } from "../configurations/configuration"; +import { extConstants, NODE_WINDOWS_LABEL } from "../constants"; +import { globalVars, LOGGER } from "../extension"; +import { prepareNbcodeLaunchOptions, getUserConfigLaunchOptionsDefaults } from "./launchOptions"; +import { NbProcessManager } from "./nbProcessManager"; +import { findNbcode } from "./utils"; +import { l10n } from "../localiser"; +import { jdkDownloaderPrompt } from "../webviews/jdkDownloader/prompt"; +import { LogLevel } from "../logger"; +import * as os from 'os'; + +export const launchNbcode = (): void => { + const ideLaunchOptions = prepareNbcodeLaunchOptions(); + const userdir = getUserConfigLaunchOptionsDefaults()[configKeys.userdir].value; + const specifiedJDK = getUserConfigLaunchOptionsDefaults()[configKeys.jdkHome].value; + const extensionPath = globalVars.extensionInfo.getExtensionStorageUri().fsPath; + const nbcodePath = findNbcode(extensionPath); + + const requiredJdk = specifiedJDK ? specifiedJDK : 'default system JDK'; + let launchMsg = l10n.value("jdk.extension.lspServer.statusBar.message.launching", { + SERVER_NAME: extConstants.SERVER_NAME, + requiredJdk: requiredJdk, + userdir: userdir + }); + LOGGER.log(launchMsg); + window.setStatusBarMessage(launchMsg, 2000); + + globalVars.nbProcessManager = new NbProcessManager(userdir, nbcodePath, ideLaunchOptions); + globalVars.nbProcessManager.startProcess(); +} + + +export const attachNbProcessListeners = (nbProcessManager: NbProcessManager): void => { + const nbProcess = nbProcessManager.getProcess(); + nbProcess?.stdout?.on('data', chunk => { + processOnDataHandler(nbProcessManager, chunk.toString(), true); + }); + nbProcess?.stderr?.on('data', chunk => { + processOnDataHandler(nbProcessManager, chunk.toString(), false); + }); + nbProcess?.on('close', (code: number) => { + const status = processOnCloseHandler(nbProcessManager, code) + if (status != null) { + throw status; + } + }); +} + +const processOnDataHandler = (nbProcessManager: NbProcessManager, text: string, isOut: boolean) => { + if (nbProcessManager) { + globalVars.clientPromise.activationPending = false; + } + if (nbProcessManager.getStdOut() == null) { + return; + } + LOGGER.logNoNL(text); + isOut ? nbProcessManager.appendStdOut(text) : nbProcessManager.appendStdErr(text); + + if (nbProcessManager.getStdOut()?.match(/org.netbeans.modules.java.lsp.server/)) { + nbProcessManager.setStdOut(null); + } +} + + +const processOnCloseHandler = (nbProcessManager: NbProcessManager, code: number): string | null => { + const globalnbProcessManager = globalVars.nbProcessManager; + if (globalnbProcessManager == nbProcessManager) { + globalVars.nbProcessManager = null; + if (code && code != 0) { + window.showWarningMessage(l10n.value("jdk.extension.lspServer.warning_message.serverExited", { SERVER_NAME: extConstants.SERVER_NAME, code })); + } + } + if (nbProcessManager.getStdOut()?.match(/Cannot find java/) || (os.type() === NODE_WINDOWS_LABEL && !globalVars.deactivated)) { + jdkDownloaderPrompt(); + } + if (nbProcessManager.getStdOut() != null) { + let match = nbProcessManager.getStdOut()!.match(/org.netbeans.modules.java.lsp.server[^\n]*/) + if (match?.length == 1) { + LOGGER.log(match[0]); + } else { + LOGGER.log("Cannot find org.netbeans.modules.java.lsp.server in the log!", LogLevel.ERROR); + } + LOGGER.log(`Please refer to troubleshooting section for more info: https://github.com/oracle/javavscode/blob/main/README.md#troubleshooting`); + LOGGER.showOutputChannelUI(false); + + nbProcessManager.killProcess(false); + return l10n.value("jdk.extension.error_msg.notEnabled", { SERVER_NAME: extConstants.SERVER_NAME }); + } else { + LOGGER.log(`LSP server ${nbProcessManager.getProcessId()} terminated with ${code}`); + LOGGER.log(`Exit code ${code}`); + } + return null; +} From a9936f1a53badbda0dd43994f6aaf5e6734df8f1 Mon Sep 17 00:00:00 2001 From: Achal Talati <achal.talati@oracle.com> Date: Wed, 9 Oct 2024 11:06:22 +0530 Subject: [PATCH 08/17] Debug refactor merge --- vscode/src/commands/commands.ts | 4 +- vscode/src/commands/debug.ts | 151 ++++++++ vscode/src/commands/register.ts | 4 +- vscode/src/debugger/debugger.ts | 218 ++++++++++++ .../src/{ => debugger}/streamDebugAdapter.ts | 2 +- vscode/src/extension.ts | 327 +----------------- vscode/src/lsp/requests/register.ts | 2 +- 7 files changed, 382 insertions(+), 326 deletions(-) create mode 100644 vscode/src/commands/debug.ts create mode 100644 vscode/src/debugger/debugger.ts rename vscode/src/{ => debugger}/streamDebugAdapter.ts (99%) diff --git a/vscode/src/commands/commands.ts b/vscode/src/commands/commands.ts index b11234e..8a73960 100644 --- a/vscode/src/commands/commands.ts +++ b/vscode/src/commands/commands.ts @@ -48,7 +48,6 @@ export const extCommands = { editNodeProps: appendPrefixToCommand('node.properties.edit'), selectEditorProjs: appendPrefixToCommand('select.editor.projects'), attachDebugger: appendPrefixToCommand("java.attachDebugger.connector"), - startDebug: 'workbench.action.debug.start', } export const builtInCommands = { @@ -59,7 +58,8 @@ export const builtInCommands = { goToEditorLocations: 'editor.action.goToLocations', renameSymbol: 'editor.action.rename', quickAccess: 'workbench.action.quickOpen', - openSettings: 'workbench.action.openSettings' + openSettings: 'workbench.action.openSettings', + startDebug: 'workbench.action.debug.start', } export const nbCommands = { diff --git a/vscode/src/commands/debug.ts b/vscode/src/commands/debug.ts new file mode 100644 index 0000000..6452226 --- /dev/null +++ b/vscode/src/commands/debug.ts @@ -0,0 +1,151 @@ +/* + Copyright (c) 2023-2024, Oracle and/or its affiliates. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +import * as vscode from 'vscode'; +import { builtInCommands, extCommands } from "./commands"; +import { ICommand } from "./types"; +import { extConstants } from '../constants'; +import { getContextUri } from './utils'; + +const runTest = async (uri: any, methodName? : string, launchConfiguration?: string) => { + await runDebug(true, true, uri, methodName, launchConfiguration); +} +const debugTest = async (uri: any, methodName? : string, launchConfiguration?: string) => { + await runDebug(false, true, uri, methodName, launchConfiguration); +} +const runSingle = async (uri: any, methodName? : string, launchConfiguration?: string) => { + await runDebug(true, false, uri, methodName, launchConfiguration); +} +const debugSingle = async (uri: any, methodName? : string, launchConfiguration?: string) => { + await runDebug(false, false, uri, methodName, launchConfiguration); +} +const projectRun = async (node: any, launchConfiguration? : string) => { + return runDebug(true, false, getContextUri(node)?.toString() || '', undefined, launchConfiguration, true); +} +const projectDebug = async (node: any, launchConfiguration? : string) => { + return runDebug(false, false, getContextUri(node)?.toString() || '', undefined, launchConfiguration, true); +} +const projectTest = async (node: any, launchConfiguration? : string) => { + return runDebug(true, true, getContextUri(node)?.toString() || '', undefined, launchConfiguration, true); +} +const packageTest = async (uri: any, launchConfiguration? : string) => { + await runDebug(true, true, uri, undefined, launchConfiguration); +} + +const runDebug = async (noDebug: boolean, testRun: boolean, uri: any, methodName?: string, launchConfiguration?: string, project : boolean = false, ) => { + const docUri = getContextUri(uri); + if (docUri) { + // attempt to find the active configuration in the vsode launch settings; undefined if no config is there. + let debugConfig : vscode.DebugConfiguration = await findRunConfiguration(docUri) || { + type: extConstants.COMMAND_PREFIX, + name: "Java Single Debug", + request: "launch" + }; + if (methodName) { + debugConfig['methodName'] = methodName; + } + if (launchConfiguration == '') { + if (debugConfig['launchConfiguration']) { + delete debugConfig['launchConfiguration']; + } + } else { + debugConfig['launchConfiguration'] = launchConfiguration; + } + debugConfig['testRun'] = testRun; + const workspaceFolder = vscode.workspace.getWorkspaceFolder(docUri); + if (project) { + debugConfig['projectFile'] = docUri.toString(); + debugConfig['project'] = true; + } else { + debugConfig['mainClass'] = docUri.toString(); + } + const debugOptions : vscode.DebugSessionOptions = { + noDebug: noDebug, + } + + + const ret = await vscode.debug.startDebugging(workspaceFolder, debugConfig, debugOptions); + return ret ? new Promise((resolve) => { + const listener = vscode.debug.onDidTerminateDebugSession(() => { + listener.dispose(); + resolve(true); + }); + }) : ret; + } +}; + + +async function findRunConfiguration(uri : vscode.Uri) : Promise<vscode.DebugConfiguration|undefined> { + // do not invoke debug start with no (jdk) configurations, as it would probably create an user prompt + let cfg = vscode.workspace.getConfiguration("launch"); + let c = cfg.get('configurations'); + if (!Array.isArray(c)) { + return undefined; + } + let f = c.filter((v) => v['type'] === extConstants.COMMAND_PREFIX); + if (!f.length) { + return undefined; + } + class P implements vscode.DebugConfigurationProvider { + config : vscode.DebugConfiguration | undefined; + + resolveDebugConfigurationWithSubstitutedVariables(folder: vscode.WorkspaceFolder | undefined, debugConfiguration: vscode.DebugConfiguration, token?: vscode.CancellationToken): vscode.ProviderResult<vscode.DebugConfiguration> { + this.config = debugConfiguration; + return undefined; + } + } + let provider = new P(); + let d = vscode.debug.registerDebugConfigurationProvider(extConstants.COMMAND_PREFIX, provider); + // let vscode to select a debug config + return await vscode.commands.executeCommand(builtInCommands.startDebug, { config: { + type: extConstants.COMMAND_PREFIX, + mainClass: uri.toString() + }, noDebug: true}).then((v) => { + d.dispose(); + return provider.config; + }, (err) => { + d.dispose(); + return undefined; + }); +} + +export const registerDebugCommands: ICommand[] = [ + { + command: extCommands.runTest, + handler: runTest + }, { + command: extCommands.debugTest, + handler: debugTest + }, { + command: extCommands.runSingle, + handler: runSingle + }, { + command: extCommands.debugSingle, + handler: debugSingle + }, { + command: extCommands.projectRun, + handler: projectRun + }, { + command: extCommands.projectDebug, + handler: projectDebug + }, { + command: extCommands.projectTest, + handler: projectTest + }, { + command: extCommands.packageTest, + handler: packageTest + } +]; diff --git a/vscode/src/commands/register.ts b/vscode/src/commands/register.ts index 580c240..22b4dd5 100644 --- a/vscode/src/commands/register.ts +++ b/vscode/src/commands/register.ts @@ -22,6 +22,7 @@ import { registerWebviewCommands } from "./webViews"; import { registerBuildOperationCommands } from "./buildOperations"; import { registerRefactorCommands } from "./refactor"; import { registerUtilCommands } from "./utilCommands"; +import { registerDebugCommands } from "./debug"; type ICommandModules = Record<string, ICommand[]>; @@ -32,7 +33,8 @@ const commandModules: ICommandModules = { webview: registerWebviewCommands, buildOperations: registerBuildOperationCommands, refactor: registerRefactorCommands, - util: registerUtilCommands + util: registerUtilCommands, + debug: registerDebugCommands } export const subscribeCommands = (context: ExtensionContext) => { diff --git a/vscode/src/debugger/debugger.ts b/vscode/src/debugger/debugger.ts new file mode 100644 index 0000000..a5a386b --- /dev/null +++ b/vscode/src/debugger/debugger.ts @@ -0,0 +1,218 @@ +/* + Copyright (c) 2023-2024, Oracle and/or its affiliates. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +import * as net from 'net'; +import * as vscode from 'vscode'; +import { commands, ExtensionContext } from 'vscode'; +import { LanguageClient } from 'vscode-languageclient/node'; +import { DebugConnector } from '../lsp/protocol'; +import { extConstants } from '../constants'; +import { l10n } from '../localiser'; +import { StreamDebugAdapter } from './streamDebugAdapter'; +import { globalVars } from '../extension'; +import { extCommands, nbCommands } from '../commands/commands'; + +export function registerDebugger(context: ExtensionContext): void { + let debugTrackerFactory =new NetBeansDebugAdapterTrackerFactory(); + context.subscriptions.push(vscode.debug.registerDebugAdapterTrackerFactory(extConstants.COMMAND_PREFIX, debugTrackerFactory)); + let configInitialProvider = new NetBeansConfigurationInitialProvider(); + context.subscriptions.push(vscode.debug.registerDebugConfigurationProvider(extConstants.COMMAND_PREFIX, configInitialProvider, vscode.DebugConfigurationProviderTriggerKind.Initial)); + let configDynamicProvider = new NetBeansConfigurationDynamicProvider(context); + context.subscriptions.push(vscode.debug.registerDebugConfigurationProvider(extConstants.COMMAND_PREFIX, configDynamicProvider, vscode.DebugConfigurationProviderTriggerKind.Dynamic)); + let configResolver = new NetBeansConfigurationResolver(); + context.subscriptions.push(vscode.debug.registerDebugConfigurationProvider(extConstants.COMMAND_PREFIX, configResolver)); + context.subscriptions.push(vscode.debug.onDidTerminateDebugSession(((session) => onDidTerminateSession(session)))); + let debugDescriptionFactory = new NetBeansDebugAdapterDescriptionFactory(); + context.subscriptions.push(vscode.debug.registerDebugAdapterDescriptorFactory(extConstants.COMMAND_PREFIX, debugDescriptionFactory)); +}; + +class NetBeansDebugAdapterTrackerFactory implements vscode.DebugAdapterTrackerFactory { + + createDebugAdapterTracker(_session: vscode.DebugSession): vscode.ProviderResult<vscode.DebugAdapterTracker> { + return { + onDidSendMessage(message: any): void { + if (globalVars.testAdapter && message.type === 'event' && message.event === 'output') { + globalVars.testAdapter.testOutput(message.body.output); + } + } + } + } +} + +class NetBeansDebugAdapterDescriptionFactory implements vscode.DebugAdapterDescriptorFactory { + + createDebugAdapterDescriptor(_session: vscode.DebugSession, _executable: vscode.DebugAdapterExecutable | undefined): vscode.ProviderResult<vscode.DebugAdapterDescriptor> { + return new Promise<vscode.DebugAdapterDescriptor>((resolve, reject) => { + let cnt = 10; + const fnc = () => { + if (globalVars.debugPort < 0) { + if (cnt-- > 0) { + setTimeout(fnc, 1000); + } else { + reject(new Error(l10n.value('jdk.extenstion.debugger.error_msg.debugAdapterNotInitialized'))); + } + } else { + // resolve(new vscode.DebugAdapterServer(debugPort)); + const socket = net.connect(globalVars.debugPort, "127.0.0.1", () => {}); + socket.on("connect", () => { + const adapter = new StreamDebugAdapter(); + socket.write(globalVars.debugHash ? globalVars.debugHash : ""); + adapter.connect(socket, socket); + resolve(new vscode.DebugAdapterInlineImplementation(adapter)); + }); + } + } + fnc(); + }); + } +} + + +class NetBeansConfigurationInitialProvider implements vscode.DebugConfigurationProvider { + + provideDebugConfigurations(folder: vscode.WorkspaceFolder | undefined, token?: vscode.CancellationToken): vscode.ProviderResult<vscode.DebugConfiguration[]> { + return this.doProvideDebugConfigurations(folder, token); + } + + async doProvideDebugConfigurations(folder: vscode.WorkspaceFolder | undefined, _token?: vscode.CancellationToken): Promise<vscode.DebugConfiguration[]> { + let c : LanguageClient = await globalVars.clientPromise.client; + if (!folder) { + return []; + } + var u : vscode.Uri | undefined; + if (folder && folder.uri) { + u = folder.uri; + } else { + u = vscode.window.activeTextEditor?.document?.uri + } + let result : vscode.DebugConfiguration[] = []; + const configNames : string[] | null | undefined = await vscode.commands.executeCommand(nbCommands.projectConfigurations, u?.toString()); + if (configNames) { + let first : boolean = true; + for (let cn of configNames) { + let cname : string; + + if (first) { + // ignore the default config, comes first. + first = false; + continue; + } else { + cname = "Launch Java: " + cn; + } + const debugConfig : vscode.DebugConfiguration = { + name: cname, + type: extConstants.COMMAND_PREFIX, + request: "launch", + launchConfiguration: cn, + }; + result.push(debugConfig); + } + } + return result; + } +} + +class NetBeansConfigurationDynamicProvider implements vscode.DebugConfigurationProvider { + + context: ExtensionContext; + commandValues = new Map<string, string>(); + + constructor(context: ExtensionContext) { + this.context = context; + } + + provideDebugConfigurations(folder: vscode.WorkspaceFolder | undefined, token?: vscode.CancellationToken): vscode.ProviderResult<vscode.DebugConfiguration[]> { + return this.doProvideDebugConfigurations(folder, this.context, this.commandValues, token); + } + + async doProvideDebugConfigurations(folder: vscode.WorkspaceFolder | undefined, context: ExtensionContext, commandValues: Map<string, string>, _token?: vscode.CancellationToken): Promise<vscode.DebugConfiguration[]> { + let c : LanguageClient = await globalVars.clientPromise.client; + if (!folder) { + return []; + } + let result : vscode.DebugConfiguration[] = []; + const attachConnectors : DebugConnector[] | null | undefined = await vscode.commands.executeCommand(extCommands.attachDebugger); + if (attachConnectors) { + for (let ac of attachConnectors) { + const debugConfig : vscode.DebugConfiguration = { + name: ac.name, + type: ac.type, + request: "attach", + }; + for (let i = 0; i < ac.arguments.length; i++) { + let defaultValue: string = ac.defaultValues[i]; + if (!defaultValue.startsWith("${command:")) { + // Create a command that asks for the argument value: + let cmd: string = `${extCommands.attachDebugger}.${ac.id}.${ac.arguments[i]}`; + debugConfig[ac.arguments[i]] = "${command:" + cmd + "}"; + if (!commandValues.has(cmd)) { + commandValues.set(cmd, ac.defaultValues[i]); + let description: string = ac.descriptions[i]; + context.subscriptions.push(commands.registerCommand(cmd, async (ctx) => { + return vscode.window.showInputBox({ + prompt: description, + value: commandValues.get(cmd), + }).then((value) => { + if (value) { + commandValues.set(cmd, value); + } + return value; + }); + })); + } + } else { + debugConfig[ac.arguments[i]] = defaultValue; + } + } + result.push(debugConfig); + } + } + return result; + } +} + +class NetBeansConfigurationResolver implements vscode.DebugConfigurationProvider { + + resolveDebugConfiguration(_folder: vscode.WorkspaceFolder | undefined, config: vscode.DebugConfiguration, _token?: vscode.CancellationToken): vscode.ProviderResult<vscode.DebugConfiguration> { + if (!config.type) { + config.type = extConstants.COMMAND_PREFIX; + } + if (!config.request) { + config.request = 'launch'; + } + if (vscode.window.activeTextEditor) { + config.file = '${file}'; + } + if (!config.classPaths) { + config.classPaths = ['any']; + } + if (!config.console) { + config.console = 'internalConsole'; + } + + return config; + } +} + +function onDidTerminateSession(session: vscode.DebugSession): any { + const config = session.configuration; + if (config.env) { + const file = config.env["MICRONAUT_CONFIG_FILES"]; + if (file) { + vscode.workspace.fs.delete(vscode.Uri.file(file)); + } + } +} diff --git a/vscode/src/streamDebugAdapter.ts b/vscode/src/debugger/streamDebugAdapter.ts similarity index 99% rename from vscode/src/streamDebugAdapter.ts rename to vscode/src/debugger/streamDebugAdapter.ts index aa92c7a..e67d16e 100644 --- a/vscode/src/streamDebugAdapter.ts +++ b/vscode/src/debugger/streamDebugAdapter.ts @@ -197,4 +197,4 @@ class Emitter<T> { this._listener = undefined; this._this = undefined; } -} +} \ No newline at end of file diff --git a/vscode/src/extension.ts b/vscode/src/extension.ts index cd5cd33..6ab0582 100644 --- a/vscode/src/extension.ts +++ b/vscode/src/extension.ts @@ -23,35 +23,25 @@ 'use strict'; -import { commands, window, workspace, ExtensionContext, TextEditorDecorationType } from 'vscode'; +import { commands, workspace, ExtensionContext, TextEditorDecorationType } from 'vscode'; -import { - LanguageClient, - StreamInfo -} from 'vscode-languageclient/node'; -import * as net from 'net'; import * as vscode from 'vscode'; -import { StreamDebugAdapter} from './streamDebugAdapter'; import { NbTestAdapter } from './testAdapter'; -import { DebugConnector, SetTextEditorDecorationParams} from './lsp/protocol'; +import { SetTextEditorDecorationParams} from './lsp/protocol'; import * as launchConfigurations from './launchConfigurations'; -import { TreeViewService, Visualizer } from './explorer'; import { initializeRunConfiguration, runConfigurationProvider, runConfigurationNodeProvider, configureRunSettings } from './runConfiguration'; import { PropertiesView } from './propertiesView/propertiesView'; -import { l10n } from './localiser'; import { extConstants } from './constants'; import { ExtensionInfo } from './extensionInfo'; import { ClientPromise } from './lsp/clientPromise'; import { ExtensionLogger } from './logger'; import { NbProcessManager } from './lsp/nbProcessManager'; -import { clientInit, serverOptionsBuilder } from './lsp/initializer'; -import { NbLanguageClient } from './lsp/nbLanguageClient'; +import { clientInit } from './lsp/initializer'; import { subscribeCommands } from './commands/register'; import { VSNetBeansAPI } from './lsp/types'; -import { registerListenersAfterClientInit, registerListenersBeforeClientInit } from './listener'; -import { registerNotificationListeners } from './lsp/notifications/register'; -import { registerRequestListeners } from './lsp/requests/register'; +import { registerListenersBeforeClientInit } from './listener'; +import { registerDebugger } from './debugger/debugger'; export let LOGGER: ExtensionLogger; export namespace globalVars { @@ -67,20 +57,6 @@ export namespace globalVars { export let decorationParamsByUri = new Map<vscode.Uri, SetTextEditorDecorationParams>(); } -function contextUri(ctx : any) : vscode.Uri | undefined { - if (ctx?.fsPath) { - return ctx as vscode.Uri; - } else if (ctx?.resourceUri) { - return ctx.resourceUri as vscode.Uri; - } else if (typeof ctx == 'string') { - try { - return vscode.Uri.parse(ctx, true); - } catch (err) { - return vscode.Uri.file(ctx); - } - } - return vscode.window.activeTextEditor?.document?.uri; -} export function activate(context: ExtensionContext): VSNetBeansAPI { globalVars.deactivated = false; @@ -93,19 +69,7 @@ export function activate(context: ExtensionContext): VSNetBeansAPI { clientInit(); //register debugger: - let debugTrackerFactory =new NetBeansDebugAdapterTrackerFactory(); - context.subscriptions.push(vscode.debug.registerDebugAdapterTrackerFactory(extConstants.COMMAND_PREFIX, debugTrackerFactory)); - let configInitialProvider = new NetBeansConfigurationInitialProvider(); - context.subscriptions.push(vscode.debug.registerDebugConfigurationProvider(extConstants.COMMAND_PREFIX, configInitialProvider, vscode.DebugConfigurationProviderTriggerKind.Initial)); - let configDynamicProvider = new NetBeansConfigurationDynamicProvider(context); - context.subscriptions.push(vscode.debug.registerDebugConfigurationProvider(extConstants.COMMAND_PREFIX, configDynamicProvider, vscode.DebugConfigurationProviderTriggerKind.Dynamic)); - let configResolver = new NetBeansConfigurationResolver(); - context.subscriptions.push(vscode.debug.registerDebugConfigurationProvider(extConstants.COMMAND_PREFIX, configResolver)); - context.subscriptions.push(vscode.debug.onDidTerminateDebugSession(((session) => onDidTerminateSession(session)))); - - let debugDescriptionFactory = new NetBeansDebugAdapterDescriptionFactory(); - context.subscriptions.push(vscode.debug.registerDebugAdapterDescriptorFactory(extConstants.COMMAND_PREFIX, debugDescriptionFactory)); - + registerDebugger(context); // initialize Run Configuration initializeRunConfiguration().then(initialized => { if (initialized) { @@ -121,106 +85,6 @@ export function activate(context: ExtensionContext): VSNetBeansAPI { // register commands subscribeCommands(context); - async function findRunConfiguration(uri : vscode.Uri) : Promise<vscode.DebugConfiguration|undefined> { - // do not invoke debug start with no (jdk) configurations, as it would probably create an user prompt - let cfg = vscode.workspace.getConfiguration("launch"); - let c = cfg.get('configurations'); - if (!Array.isArray(c)) { - return undefined; - } - let f = c.filter((v) => v['type'] === extConstants.COMMAND_PREFIX); - if (!f.length) { - return undefined; - } - class P implements vscode.DebugConfigurationProvider { - config : vscode.DebugConfiguration | undefined; - - resolveDebugConfigurationWithSubstitutedVariables(folder: vscode.WorkspaceFolder | undefined, debugConfiguration: vscode.DebugConfiguration, token?: vscode.CancellationToken): vscode.ProviderResult<vscode.DebugConfiguration> { - this.config = debugConfiguration; - return undefined; - } - } - let provider = new P(); - let d = vscode.debug.registerDebugConfigurationProvider(extConstants.COMMAND_PREFIX, provider); - // let vscode to select a debug config - return await vscode.commands.executeCommand('workbench.action.debug.start', { config: { - type: extConstants.COMMAND_PREFIX, - mainClass: uri.toString() - }, noDebug: true}).then((v) => { - d.dispose(); - return provider.config; - }, (err) => { - d.dispose(); - return undefined; - }); - } - - const runDebug = async (noDebug: boolean, testRun: boolean, uri: any, methodName?: string, launchConfiguration?: string, project : boolean = false, ) => { - const docUri = contextUri(uri); - if (docUri) { - // attempt to find the active configuration in the vsode launch settings; undefined if no config is there. - let debugConfig : vscode.DebugConfiguration = await findRunConfiguration(docUri) || { - type: extConstants.COMMAND_PREFIX, - name: "Java Single Debug", - request: "launch" - }; - if (methodName) { - debugConfig['methodName'] = methodName; - } - if (launchConfiguration == '') { - if (debugConfig['launchConfiguration']) { - delete debugConfig['launchConfiguration']; - } - } else { - debugConfig['launchConfiguration'] = launchConfiguration; - } - debugConfig['testRun'] = testRun; - const workspaceFolder = vscode.workspace.getWorkspaceFolder(docUri); - if (project) { - debugConfig['projectFile'] = docUri.toString(); - debugConfig['project'] = true; - } else { - debugConfig['mainClass'] = docUri.toString(); - } - const debugOptions : vscode.DebugSessionOptions = { - noDebug: noDebug, - } - - - const ret = await vscode.debug.startDebugging(workspaceFolder, debugConfig, debugOptions); - return ret ? new Promise((resolve) => { - const listener = vscode.debug.onDidTerminateDebugSession(() => { - listener.dispose(); - resolve(true); - }); - }) : ret; - } - }; - - context.subscriptions.push(commands.registerCommand(extConstants.COMMAND_PREFIX + '.run.test', async (uri, methodName?, launchConfiguration?) => { - await runDebug(true, true, uri, methodName, launchConfiguration); - })); - context.subscriptions.push(commands.registerCommand(extConstants.COMMAND_PREFIX + '.debug.test', async (uri, methodName?, launchConfiguration?) => { - await runDebug(false, true, uri, methodName, launchConfiguration); - })); - context.subscriptions.push(commands.registerCommand(extConstants.COMMAND_PREFIX + '.run.single', async (uri, methodName?, launchConfiguration?) => { - await runDebug(true, false, uri, methodName, launchConfiguration); - })); - context.subscriptions.push(commands.registerCommand(extConstants.COMMAND_PREFIX + '.debug.single', async (uri, methodName?, launchConfiguration?) => { - await runDebug(false, false, uri, methodName, launchConfiguration); - })); - context.subscriptions.push(commands.registerCommand(extConstants.COMMAND_PREFIX + '.project.run', async (node, launchConfiguration?) => { - return runDebug(true, false, contextUri(node)?.toString() || '', undefined, launchConfiguration, true); - })); - context.subscriptions.push(commands.registerCommand(extConstants.COMMAND_PREFIX + '.project.debug', async (node, launchConfiguration?) => { - return runDebug(false, false, contextUri(node)?.toString() || '', undefined, launchConfiguration, true); - })); - context.subscriptions.push(commands.registerCommand(extConstants.COMMAND_PREFIX + '.project.test', async (node, launchConfiguration?) => { - return runDebug(true, true, contextUri(node)?.toString() || '', undefined, launchConfiguration, true); - })); - context.subscriptions.push(commands.registerCommand(extConstants.COMMAND_PREFIX + '.package.test', async (uri, launchConfiguration?) => { - await runDebug(true, true, uri, undefined, launchConfiguration); - })); context.subscriptions.push(commands.registerCommand(extConstants.COMMAND_PREFIX + '.node.properties.edit', async (node) => await PropertiesView.createOrShow(context, node, (await globalVars.clientPromise.client).findTreeViewService()))); @@ -251,182 +115,3 @@ export function deactivate(): Thenable<void> { return globalVars.clientPromise.stopClient(); } - -class NetBeansDebugAdapterTrackerFactory implements vscode.DebugAdapterTrackerFactory { - - createDebugAdapterTracker(_session: vscode.DebugSession): vscode.ProviderResult<vscode.DebugAdapterTracker> { - return { - onDidSendMessage(message: any): void { - if (globalVars.testAdapter && message.type === 'event' && message.event === 'output') { - globalVars.testAdapter.testOutput(message.body.output); - } - } - } - } -} - -class NetBeansDebugAdapterDescriptionFactory implements vscode.DebugAdapterDescriptorFactory { - - createDebugAdapterDescriptor(_session: vscode.DebugSession, _executable: vscode.DebugAdapterExecutable | undefined): vscode.ProviderResult<vscode.DebugAdapterDescriptor> { - return new Promise<vscode.DebugAdapterDescriptor>((resolve, reject) => { - let cnt = 10; - const fnc = () => { - if (globalVars.debugPort < 0) { - if (cnt-- > 0) { - setTimeout(fnc, 1000); - } else { - reject(new Error(l10n.value('jdk.extension.debugger.error_msg.debugAdapterNotInitialized'))); - } - } else { - // resolve(new vscode.DebugAdapterServer(debugPort)); - const socket = net.connect(globalVars.debugPort, "127.0.0.1", () => {}); - socket.on("connect", () => { - const adapter = new StreamDebugAdapter(); - socket.write(globalVars.debugHash ? globalVars.debugHash : ""); - adapter.connect(socket, socket); - resolve(new vscode.DebugAdapterInlineImplementation(adapter)); - }); - } - } - fnc(); - }); - } -} - - -class NetBeansConfigurationInitialProvider implements vscode.DebugConfigurationProvider { - - provideDebugConfigurations(folder: vscode.WorkspaceFolder | undefined, token?: vscode.CancellationToken): vscode.ProviderResult<vscode.DebugConfiguration[]> { - return this.doProvideDebugConfigurations(folder, token); - } - - async doProvideDebugConfigurations(folder: vscode.WorkspaceFolder | undefined, _token?: vscode.CancellationToken): Promise<vscode.DebugConfiguration[]> { - let c : LanguageClient = await globalVars.clientPromise.client; - if (!folder) { - return []; - } - var u : vscode.Uri | undefined; - if (folder && folder.uri) { - u = folder.uri; - } else { - u = vscode.window.activeTextEditor?.document?.uri - } - let result : vscode.DebugConfiguration[] = []; - const configNames : string[] | null | undefined = await vscode.commands.executeCommand(extConstants.COMMAND_PREFIX + '.project.configurations', u?.toString()); - if (configNames) { - let first : boolean = true; - for (let cn of configNames) { - let cname : string; - - if (first) { - // ignore the default config, comes first. - first = false; - continue; - } else { - cname = "Launch Java: " + cn; - } - const debugConfig : vscode.DebugConfiguration = { - name: cname, - type: extConstants.COMMAND_PREFIX, - request: "launch", - launchConfiguration: cn, - }; - result.push(debugConfig); - } - } - return result; - } -} - -class NetBeansConfigurationDynamicProvider implements vscode.DebugConfigurationProvider { - - context: ExtensionContext; - commandValues = new Map<string, string>(); - - constructor(context: ExtensionContext) { - this.context = context; - } - - provideDebugConfigurations(folder: vscode.WorkspaceFolder | undefined, token?: vscode.CancellationToken): vscode.ProviderResult<vscode.DebugConfiguration[]> { - return this.doProvideDebugConfigurations(folder, this.context, this.commandValues, token); - } - - async doProvideDebugConfigurations(folder: vscode.WorkspaceFolder | undefined, context: ExtensionContext, commandValues: Map<string, string>, _token?: vscode.CancellationToken): Promise<vscode.DebugConfiguration[]> { - let c : LanguageClient = await globalVars.clientPromise.client; - if (!folder) { - return []; - } - let result : vscode.DebugConfiguration[] = []; - const attachConnectors : DebugConnector[] | null | undefined = await vscode.commands.executeCommand(extConstants.COMMAND_PREFIX + '.java.attachDebugger.configurations'); - if (attachConnectors) { - for (let ac of attachConnectors) { - const debugConfig : vscode.DebugConfiguration = { - name: ac.name, - type: ac.type, - request: "attach", - }; - for (let i = 0; i < ac.arguments.length; i++) { - let defaultValue: string = ac.defaultValues[i]; - if (!defaultValue.startsWith("${command:")) { - // Create a command that asks for the argument value: - let cmd: string = extConstants.COMMAND_PREFIX + ".java.attachDebugger.connector." + ac.id + "." + ac.arguments[i]; - debugConfig[ac.arguments[i]] = "${command:" + cmd + "}"; - if (!commandValues.has(cmd)) { - commandValues.set(cmd, ac.defaultValues[i]); - let description: string = ac.descriptions[i]; - context.subscriptions.push(commands.registerCommand(cmd, async (ctx) => { - return vscode.window.showInputBox({ - prompt: description, - value: commandValues.get(cmd), - }).then((value) => { - if (value) { - commandValues.set(cmd, value); - } - return value; - }); - })); - } - } else { - debugConfig[ac.arguments[i]] = defaultValue; - } - } - result.push(debugConfig); - } - } - return result; - } -} - -class NetBeansConfigurationResolver implements vscode.DebugConfigurationProvider { - - resolveDebugConfiguration(_folder: vscode.WorkspaceFolder | undefined, config: vscode.DebugConfiguration, _token?: vscode.CancellationToken): vscode.ProviderResult<vscode.DebugConfiguration> { - if (!config.type) { - config.type = extConstants.COMMAND_PREFIX; - } - if (!config.request) { - config.request = 'launch'; - } - if (vscode.window.activeTextEditor) { - config.file = '${file}'; - } - if (!config.classPaths) { - config.classPaths = ['any']; - } - if (!config.console) { - config.console = 'internalConsole'; - } - - return config; - } -} - -function onDidTerminateSession(session: vscode.DebugSession): any { - const config = session.configuration; - if (config.env) { - const file = config.env["MICRONAUT_CONFIG_FILES"]; - if (file) { - vscode.workspace.fs.delete(vscode.Uri.file(file)); - } - } -} - diff --git a/vscode/src/lsp/requests/register.ts b/vscode/src/lsp/requests/register.ts index f09e47f..90bc6f0 100644 --- a/vscode/src/lsp/requests/register.ts +++ b/vscode/src/lsp/requests/register.ts @@ -22,4 +22,4 @@ export const registerRequestListeners = (client: NbLanguageClient) => { const { type, handler } = listener; client.onRequest(type, handler); }) -} \ No newline at end of file +} \ No newline at end of file From e54f522d25a8edfe4aad41e3073503870f5b08c0 Mon Sep 17 00:00:00 2001 From: Achal Talati <achal.talati@oracle.com> Date: Thu, 10 Oct 2024 10:36:13 +0530 Subject: [PATCH 09/17] Fixed incorrect localisation key issues --- vscode/src/commands/cache.ts | 6 +++--- vscode/src/commands/create.ts | 4 ++-- vscode/src/commands/navigation.ts | 2 +- vscode/src/commands/refactor.ts | 2 +- vscode/src/commands/utils.ts | 2 +- vscode/src/debugger/debugger.ts | 2 +- 6 files changed, 9 insertions(+), 9 deletions(-) diff --git a/vscode/src/commands/cache.ts b/vscode/src/commands/cache.ts index 1b69ed6..966106d 100644 --- a/vscode/src/commands/cache.ts +++ b/vscode/src/commands/cache.ts @@ -25,7 +25,7 @@ const deleteCache = async () => { // TODO: Change workspace path to userdir path const storagePath = globalVars.extensionInfo.getWorkspaceStorage()?.fsPath; if (!storagePath) { - window.showErrorMessage(l10n.value("jdk.extenstion.cache.error_msg.cannotFindWrkSpacePath")); + window.showErrorMessage(l10n.value("jdk.extension.cache.error_msg.cannotFindWrkSpacePath")); return; } @@ -42,9 +42,9 @@ const deleteCache = async () => { globalVars.deactivated = true; await globalVars.nbProcessManager?.killProcess(false); await fs.promises.rmdir(userDir, { recursive: true }); - await window.showInformationMessage(l10n.value("jdk.extenstion.message.cacheDeleted"), reloadWindowActionLabel); + await window.showInformationMessage(l10n.value("jdk.extension.message.cacheDeleted"), reloadWindowActionLabel); } catch (err) { - await window.showErrorMessage(l10n.value("jdk.extenstion.error_msg.cacheDeletionError"), reloadWindowActionLabel); + await window.showErrorMessage(l10n.value("jdk.extension.error_msg.cacheDeletionError"), reloadWindowActionLabel); } finally { commands.executeCommand(builtInCommands.reloadWindow); } diff --git a/vscode/src/commands/create.ts b/vscode/src/commands/create.ts index 27f05ef..a7b7018 100644 --- a/vscode/src/commands/create.ts +++ b/vscode/src/commands/create.ts @@ -67,7 +67,7 @@ const newFromTemplate = async (ctx: any, template: any) => { } } } else { - throw l10n.value("jdk.extenstion.error_msg.doesntSupportNewTeamplate", { client }); + throw l10n.value("jdk.extension.error_msg.doesntSupportNewTeamplate", { client }); } } @@ -92,7 +92,7 @@ const newProject = async (ctx: any) => { } } } else { - throw l10n.value("jdk.extenstion.error_msg.doesntSupportNewProject", { client }); + throw l10n.value("jdk.extension.error_msg.doesntSupportNewProject", { client }); } }; diff --git a/vscode/src/commands/navigation.ts b/vscode/src/commands/navigation.ts index 2a5548d..00ab9cf 100644 --- a/vscode/src/commands/navigation.ts +++ b/vscode/src/commands/navigation.ts @@ -72,7 +72,7 @@ const goToTest = async (ctx: any) => { window.showInformationMessage(err?.message || l10n.value("jdk.extension.fileSelector.label.noTestFound")); } } else { - throw l10n.value("jdk.extenstion.error_msg.doesntSupportGoToTest", { client }); + throw l10n.value("jdk.extension.error_msg.doesntSupportGoToTest", { client }); } } diff --git a/vscode/src/commands/refactor.ts b/vscode/src/commands/refactor.ts index 0a013fc..42e62af 100644 --- a/vscode/src/commands/refactor.ts +++ b/vscode/src/commands/refactor.ts @@ -31,7 +31,7 @@ const goToSuperImplementationHandler = async () => { const locations: any[] = await commands.executeCommand(nbCommands.superImpl, uri.toString(), position) || []; return commands.executeCommand(builtInCommands.goToEditorLocations, window.activeTextEditor.document.uri, position, locations.map(location => new Location(Uri.parse(location.uri), new Range(location.range.start.line, location.range.start.character, location.range.end.line, location.range.end.character))), - 'peek', l10n.value('jdk.extenstion.error_msg.noSuperImpl')); + 'peek', l10n.value('jdk.extension.error_msg.noSuperImpl')); } const renameElementHandler = async (offset: any) => { diff --git a/vscode/src/commands/utils.ts b/vscode/src/commands/utils.ts index 025edbb..6c7c4bf 100644 --- a/vscode/src/commands/utils.ts +++ b/vscode/src/commands/utils.ts @@ -109,7 +109,7 @@ export const wrapCommandWithProgress = (lsCommand: string, title: string, log?: } } } else { - reject(l10n.value("jdk.extenstion.progressBar.error_msg.cannotRun", { lsCommand: lsCommand, client: c })); + reject(l10n.value("jdk.extension.progressBar.error_msg.cannotRun", { lsCommand: lsCommand, client: c })); } }); }); diff --git a/vscode/src/debugger/debugger.ts b/vscode/src/debugger/debugger.ts index a5a386b..065cea0 100644 --- a/vscode/src/debugger/debugger.ts +++ b/vscode/src/debugger/debugger.ts @@ -62,7 +62,7 @@ class NetBeansDebugAdapterDescriptionFactory implements vscode.DebugAdapterDescr if (cnt-- > 0) { setTimeout(fnc, 1000); } else { - reject(new Error(l10n.value('jdk.extenstion.debugger.error_msg.debugAdapterNotInitialized'))); + reject(new Error(l10n.value('jdk.extension.debugger.error_msg.debugAdapterNotInitialized'))); } } else { // resolve(new vscode.DebugAdapterServer(debugPort)); From 0551af8535599328b8e6a34cd16986ebac692327 Mon Sep 17 00:00:00 2001 From: Achal Talati <achal.talati@oracle.com> Date: Thu, 10 Oct 2024 14:03:05 +0530 Subject: [PATCH 10/17] Removed Properties View --- THIRD_PARTY_LICENSES.txt | 1495 ----------------- vscode/esbuild.js | 4 +- vscode/package-lock.json | 177 -- vscode/package.json | 9 - vscode/package.nls.ja.json | 1 - vscode/package.nls.json | 1 - vscode/package.nls.zh-cn.json | 1 - vscode/src/commands/commands.ts | 1 - vscode/src/extension.ts | 4 - vscode/src/propertiesView/controlTypes.ts | 84 - .../propertiesView/propertiesHtmlBuilder.ts | 119 -- vscode/src/propertiesView/propertiesView.ts | 242 --- vscode/src/propertiesView/script.ts | 101 -- vscode/src/webviews/nbWebviewHandler.ts | 1 + 14 files changed, 2 insertions(+), 2238 deletions(-) delete mode 100644 vscode/src/propertiesView/controlTypes.ts delete mode 100644 vscode/src/propertiesView/propertiesHtmlBuilder.ts delete mode 100644 vscode/src/propertiesView/propertiesView.ts delete mode 100644 vscode/src/propertiesView/script.ts diff --git a/THIRD_PARTY_LICENSES.txt b/THIRD_PARTY_LICENSES.txt index fff53e1..4a3eed5 100644 --- a/THIRD_PARTY_LICENSES.txt +++ b/THIRD_PARTY_LICENSES.txt @@ -8684,1501 +8684,6 @@ SOFTWARE. ------------------ END OF DEPENDENCY LICENSE -------------------- - - -Dependency: webview-ui-toolkit -============================== - ------------------- START OF DEPENDENCY LICENSE -------------------- -- @vscode/webview-ui-toolkit - - -Copyright (c) Microsoft Corporation. -Copyright 2011 Gary Court. -Copyright 2011 Gary Court. All rights reserved. -a. Adapted Material means material subject to Copyright and Similar -permission under the Copyright and Similar Rights held by the -c. Copyright and Similar Rights means copyright and/or similar rights -specified in Section 2(b)(1)-(2) are not Copyright and Similar -any other exception or limitation to Copyright and Similar Rights -all Copyright and Similar Rights that apply to Your use of the -Rights include other Copyright and Similar Rights. -a. This Public License applies for the term of the Copyright and -Copyright (c) <year> <copyright holders> -Copyright (c) Microsoft Corporation. All rights reserved. -Copyright (c) 2015-2017 Evgeny Poberezkin -Copyright (c) 2018 Chris Holt -Copyright (c) 2017 Evgeny Poberezkin -Copyright (c) 2013 James Halliday -Copyright (c) 2013 Raynos. -Copyright (c) 2013 Thiago de Arruda -Copyright (c) 2014 Dave Justice -Copyright (c) 2013 Alex Kocharin -Copyright OpenJS Foundation and other contributors <https:openjsf.org/> -Copyright Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors -Copyright and related rights for sample code are waived via CC0. Sample -Copyright (c) 2015 Javier Blanco -Copyright Mathias Bynens <https:mathiasbynens.be/> -Copyright (c) 2012 James Halliday -Copyright (c) 2015 David Clark -License Type: MIT - -MIT License - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. - - ---------- Notice --------- -microsoft-vscode-webview-ui-toolkit - -THIRD-PARTY SOFTWARE NOTICES AND INFORMATION -Do Not Translate or Localize - -This project incorporates components from the projects listed below. The original copyright notices and the licenses under which Microsoft received such components are set forth below. Microsoft reserves all rights not expressly granted herein, whether by implication, estoppel or otherwise. - ---- - -tslib 2.2.0 - 0BSD -https://www.typescriptlang.org/ - - - -Permission to use, copy, modify, and/or distribute this software for any -purpose with or without fee is hereby granted. - -THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH -REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY -AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, -INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM -LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR -OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR -PERFORMANCE OF THIS SOFTWARE. - ---- - ---- - -tslib 1.14.1 - 0BSD -https://www.typescriptlang.org/ - - - -Permission to use, copy, modify, and/or distribute this software for any -purpose with or without fee is hereby granted. - -THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH -REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY -AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, -INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM -LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR -OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR -PERFORMANCE OF THIS SOFTWARE. - ---- - ---- - -uri-js 4.4.1 - BSD-2-Clause -https://github.com/garycourt/uri-js - - - -Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - -1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - -2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - -THIS SOFTWARE IS PROVIDED BY GARY COURT "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GARY COURT OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -The views and conclusions contained in the software and documentation are those of the authors and should not be interpreted as representing official policies, either expressed or implied, of Gary Court. - ---- - ---- - -@vscode/codicons 0.0.22 - CC-BY-4.0 -https://github.com/microsoft/vscode-codicons#readme - - -Attribution 4.0 International - -======================================================================= - -Creative Commons Corporation ("Creative Commons") is not a law firm and -does not provide legal services or legal advice. Distribution of -Creative Commons public licenses does not create a lawyer-client or -other relationship. Creative Commons makes its licenses and related -information available on an "as-is" basis. Creative Commons gives no -warranties regarding its licenses, any material licensed under their -terms and conditions, or any related information. Creative Commons -disclaims all liability for damages resulting from their use to the -fullest extent possible. - -Using Creative Commons Public Licenses - -Creative Commons public licenses provide a standard set of terms and -conditions that creators and other rights holders may use to share -original works of authorship and other material subject to copyright -and certain other rights specified in the public license below. The -following considerations are for informational purposes only, are not -exhaustive, and do not form part of our licenses. - -Considerations for licensors: Our public licenses are -intended for use by those authorized to give the public -permission to use material in ways otherwise restricted by -copyright and certain other rights. Our licenses are -irrevocable. Licensors should read and understand the terms -and conditions of the license they choose before applying it. -Licensors should also secure all rights necessary before -applying our licenses so that the public can reuse the -material as expected. Licensors should clearly mark any -material not subject to the license. This includes other CC- -licensed material, or material used under an exception or -limitation to copyright. More considerations for licensors: -wiki.creativecommons.org/Considerations_for_licensors - -Considerations for the public: By using one of our public -licenses, a licensor grants the public permission to use the -licensed material under specified terms and conditions. If -the licensor's permission is not necessary for any reason--for -example, because of any applicable exception or limitation to -copyright--then that use is not regulated by the license. Our -licenses grant only permissions under copyright and certain -other rights that a licensor has authority to grant. Use of -the licensed material may still be restricted for other -reasons, including because others have copyright or other -rights in the material. A licensor may make special requests, -such as asking that all changes be marked or described. -Although not required by our licenses, you are encouraged to -respect those requests where reasonable. More_considerations -for the public: -wiki.creativecommons.org/Considerations_for_licensees - -======================================================================= - -Creative Commons Attribution 4.0 International Public License - -By exercising the Licensed Rights (defined below), You accept and agree -to be bound by the terms and conditions of this Creative Commons -Attribution 4.0 International Public License ("Public License"). To the -extent this Public License may be interpreted as a contract, You are -granted the Licensed Rights in consideration of Your acceptance of -these terms and conditions, and the Licensor grants You such rights in -consideration of benefits the Licensor receives from making the -Licensed Material available under these terms and conditions. - -Section 1 -- Definitions. - -Rights that is derived from or based upon the Licensed Material -and in which the Licensed Material is translated, altered, -arranged, transformed, or otherwise modified in a manner requiring -Licensor. For purposes of this Public License, where the Licensed -Material is a musical work, performance, or sound recording, -Adapted Material is always produced where the Licensed Material is -synched in timed relation with a moving image. - -b. Adapter's License means the license You apply to Your Copyright -and Similar Rights in Your contributions to Adapted Material in -accordance with the terms and conditions of this Public License. - -closely related to copyright including, without limitation, -performance, broadcast, sound recording, and Sui Generis Database -Rights, without regard to how the rights are labeled or -categorized. For purposes of this Public License, the rights -Rights. - -d. Effective Technological Measures means those measures that, in the -absence of proper authority, may not be circumvented under laws -fulfilling obligations under Article 11 of the WIPO Copyright -Treaty adopted on December 20, 1996, and/or similar international -agreements. - -e. Exceptions and Limitations means fair use, fair dealing, and/or -that applies to Your use of the Licensed Material. - -f. Licensed Material means the artistic or literary work, database, -or other material to which the Licensor applied this Public -License. - -g. Licensed Rights means the rights granted to You subject to the -terms and conditions of this Public License, which are limited to -Licensed Material and that the Licensor has authority to license. - -h. Licensor means the individual(s) or entity(ies) granting rights -under this Public License. - -i. Share means to provide material to the public by any means or -process that requires permission under the Licensed Rights, such -as reproduction, public display, public performance, distribution, -dissemination, communication, or importation, and to make material -available to the public including in ways that members of the -public may access the material from a place and at a time -individually chosen by them. - -j. Sui Generis Database Rights means rights other than copyright -resulting from Directive 96/9/EC of the European Parliament and of -the Council of 11 March 1996 on the legal protection of databases, -as amended and/or succeeded, as well as other essentially -equivalent rights anywhere in the world. - -k. You means the individual or entity exercising the Licensed Rights -under this Public License. Your has a corresponding meaning. - -Section 2 -- Scope. - -a. License grant. - -1. Subject to the terms and conditions of this Public License, -the Licensor hereby grants You a worldwide, royalty-free, -non-sublicensable, non-exclusive, irrevocable license to -exercise the Licensed Rights in the Licensed Material to: - -a. reproduce and Share the Licensed Material, in whole or -in part; and - -b. produce, reproduce, and Share Adapted Material. - -2. Exceptions and Limitations. For the avoidance of doubt, where -Exceptions and Limitations apply to Your use, this Public -License does not apply, and You do not need to comply with -its terms and conditions. - -3. Term. The term of this Public License is specified in Section -6(a). - -4. Media and formats; technical modifications allowed. The -Licensor authorizes You to exercise the Licensed Rights in -all media and formats whether now known or hereafter created, -and to make technical modifications necessary to do so. The -Licensor waives and/or agrees not to assert any right or -authority to forbid You from making technical modifications -necessary to exercise the Licensed Rights, including -technical modifications necessary to circumvent Effective -Technological Measures. For purposes of this Public License, -simply making modifications authorized by this Section 2(a) -(4) never produces Adapted Material. - -5. Downstream recipients. - -a. Offer from the Licensor -- Licensed Material. Every -recipient of the Licensed Material automatically -receives an offer from the Licensor to exercise the -Licensed Rights under the terms and conditions of this -Public License. - -b. No downstream restrictions. You may not offer or impose -any additional or different terms or conditions on, or -apply any Effective Technological Measures to, the -Licensed Material if doing so restricts exercise of the -Licensed Rights by any recipient of the Licensed -Material. - -6. No endorsement. Nothing in this Public License constitutes or -may be construed as permission to assert or imply that You -are, or that Your use of the Licensed Material is, connected -with, or sponsored, endorsed, or granted official status by, -the Licensor or others designated to receive attribution as -provided in Section 3(a)(1)(A)(i). - -b. Other rights. - -1. Moral rights, such as the right of integrity, are not -licensed under this Public License, nor are publicity, -privacy, and/or other similar personality rights; however, to -the extent possible, the Licensor waives and/or agrees not to -assert any such rights held by the Licensor to the limited -extent necessary to allow You to exercise the Licensed -Rights, but not otherwise. - -2. Patent and trademark rights are not licensed under this -Public License. - -3. To the extent possible, the Licensor waives any right to -collect royalties from You for the exercise of the Licensed -Rights, whether directly or through a collecting society -under any voluntary or waivable statutory or compulsory -licensing scheme. In all other cases the Licensor expressly -reserves any right to collect such royalties. - -Section 3 -- License Conditions. - -Your exercise of the Licensed Rights is expressly made subject to the -following conditions. - -a. Attribution. - -1. If You Share the Licensed Material (including in modified -form), You must: - -a. retain the following if it is supplied by the Licensor -with the Licensed Material: - -i. identification of the creator(s) of the Licensed -Material and any others designated to receive -attribution, in any reasonable manner requested by -the Licensor (including by pseudonym if -designated); - -ii. a copyright notice; - -iii. a notice that refers to this Public License; - -iv. a notice that refers to the disclaimer of -warranties; - -v. a URI or hyperlink to the Licensed Material to the -extent reasonably practicable; - -b. indicate if You modified the Licensed Material and -retain an indication of any previous modifications; and - -c. indicate the Licensed Material is licensed under this -Public License, and include the text of, or the URI or -hyperlink to, this Public License. - -2. You may satisfy the conditions in Section 3(a)(1) in any -reasonable manner based on the medium, means, and context in -which You Share the Licensed Material. For example, it may be -reasonable to satisfy the conditions by providing a URI or -hyperlink to a resource that includes the required -information. - -3. If requested by the Licensor, You must remove any of the -information required by Section 3(a)(1)(A) to the extent -reasonably practicable. - -4. If You Share Adapted Material You produce, the Adapter's -License You apply must not prevent recipients of the Adapted -Material from complying with this Public License. - -Section 4 -- Sui Generis Database Rights. - -Where the Licensed Rights include Sui Generis Database Rights that -apply to Your use of the Licensed Material: - -a. for the avoidance of doubt, Section 2(a)(1) grants You the right -to extract, reuse, reproduce, and Share all or a substantial -portion of the contents of the database; - -b. if You include all or a substantial portion of the database -contents in a database in which You have Sui Generis Database -Rights, then the database in which You have Sui Generis Database -Rights (but not its individual contents) is Adapted Material; and - -c. You must comply with the conditions in Section 3(a) if You Share -all or a substantial portion of the contents of the database. - -For the avoidance of doubt, this Section 4 supplements and does not -replace Your obligations under this Public License where the Licensed - -Section 5 -- Disclaimer of Warranties and Limitation of Liability. - -a. UNLESS OTHERWISE SEPARATELY UNDERTAKEN BY THE LICENSOR, TO THE -EXTENT POSSIBLE, THE LICENSOR OFFERS THE LICENSED MATERIAL AS-IS -AND AS-AVAILABLE, AND MAKES NO REPRESENTATIONS OR WARRANTIES OF -ANY KIND CONCERNING THE LICENSED MATERIAL, WHETHER EXPRESS, -IMPLIED, STATUTORY, OR OTHER. THIS INCLUDES, WITHOUT LIMITATION, -WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR -PURPOSE, NON-INFRINGEMENT, ABSENCE OF LATENT OR OTHER DEFECTS, -ACCURACY, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR NOT -KNOWN OR DISCOVERABLE. WHERE DISCLAIMERS OF WARRANTIES ARE NOT -ALLOWED IN FULL OR IN PART, THIS DISCLAIMER MAY NOT APPLY TO YOU. - -b. TO THE EXTENT POSSIBLE, IN NO EVENT WILL THE LICENSOR BE LIABLE -TO YOU ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION, -NEGLIGENCE) OR OTHERWISE FOR ANY DIRECT, SPECIAL, INDIRECT, -INCIDENTAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY, OR OTHER LOSSES, -COSTS, EXPENSES, OR DAMAGES ARISING OUT OF THIS PUBLIC LICENSE OR -USE OF THE LICENSED MATERIAL, EVEN IF THE LICENSOR HAS BEEN -ADVISED OF THE POSSIBILITY OF SUCH LOSSES, COSTS, EXPENSES, OR -DAMAGES. WHERE A LIMITATION OF LIABILITY IS NOT ALLOWED IN FULL OR -IN PART, THIS LIMITATION MAY NOT APPLY TO YOU. - -c. The disclaimer of warranties and limitation of liability provided -above shall be interpreted in a manner that, to the extent -possible, most closely approximates an absolute disclaimer and -waiver of all liability. - -Section 6 -- Term and Termination. - -Similar Rights licensed here. However, if You fail to comply with -this Public License, then Your rights under this Public License -terminate automatically. - -b. Where Your right to use the Licensed Material has terminated under -Section 6(a), it reinstates: - -1. automatically as of the date the violation is cured, provided -it is cured within 30 days of Your discovery of the -violation; or - -2. upon express reinstatement by the Licensor. - -For the avoidance of doubt, this Section 6(b) does not affect any -right the Licensor may have to seek remedies for Your violations -of this Public License. - -c. For the avoidance of doubt, the Licensor may also offer the -Licensed Material under separate terms or conditions or stop -distributing the Licensed Material at any time; however, doing so -will not terminate this Public License. - -d. Sections 1, 5, 6, 7, and 8 survive termination of this Public -License. - -Section 7 -- Other Terms and Conditions. - -a. The Licensor shall not be bound by any additional or different -terms or conditions communicated by You unless expressly agreed. - -b. Any arrangements, understandings, or agreements regarding the -Licensed Material not stated herein are separate from and -independent of the terms and conditions of this Public License. - -Section 8 -- Interpretation. - -a. For the avoidance of doubt, this Public License does not, and -shall not be interpreted to, reduce, limit, restrict, or impose -conditions on any use of the Licensed Material that could lawfully -be made without permission under this Public License. - -b. To the extent possible, if any provision of this Public License is -deemed unenforceable, it shall be automatically reformed to the -minimum extent necessary to make it enforceable. If the provision -cannot be reformed, it shall be severed from this Public License -without affecting the enforceability of the remaining terms and -conditions. - -c. No term or condition of this Public License will be waived and no -failure to comply consented to unless expressly agreed to by the -Licensor. - -d. Nothing in this Public License constitutes or may be interpreted -as a limitation upon, or waiver of, any privileges and immunities -that apply to the Licensor or You, including from the legal -processes of any jurisdiction or authority. - -======================================================================= - -Creative Commons is not a party to its public -licenses. Notwithstanding, Creative Commons may elect to apply one of -its public licenses to material it publishes and in those instances -will be considered the “Licensor.” The text of the Creative Commons -public licenses is dedicated to the public domain under the CC0 Public -Domain Dedication. Except for the limited purpose of indicating that -material is shared under a Creative Commons public license or as -otherwise permitted by the Creative Commons policies published at -creativecommons.org/policies, Creative Commons does not authorize the -use of the trademark "Creative Commons" or any other trademark or logo -of Creative Commons without its prior written consent including, -without limitation, in connection with any unauthorized modifications -to any of its public licenses or any other arrangements, -understandings, or agreements concerning use of licensed material. For -the avoidance of doubt, this paragraph does not form part of the -public licenses. - -Creative Commons may be contacted at creativecommons.org. - ---- - ---- - -@microsoft/fast-element 1.4.0 - MIT -https://github.com/Microsoft/fast#readme - -MIT License - - -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - ---- - ---- - -@microsoft/fast-foundation 1.24.7 - MIT -https://github.com/Microsoft/fast#readme - - -MIT License - - -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - ---- - ---- - -@microsoft/fast-web-utilities 4.8.0 - MIT -https://github.com/Microsoft/fast#readme - -MIT License - - -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - ---- - ---- - -@microsoft/tsdoc 0.12.24 - MIT -https://tsdoc.org/ - - - -MIT License - -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -"Software"), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - ---- - ---- - -@microsoft/tsdoc-config 0.13.9 - MIT -https://tsdoc.org/ - - - -MIT License - -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -"Software"), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - ---- - ---- - -ajv 6.12.6 - MIT -https://github.com/ajv-validator/ajv - - -The MIT License (MIT) - - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. - ---- - ---- - -exenv-es6 1.0.0 - MIT -https://github.com/chrisdholt/exenv-es6#readme - - -MIT License - - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. - ---- - ---- - -fast-deep-equal 3.1.3 - MIT -https://github.com/epoberezkin/fast-deep-equal#readme - - -MIT License - - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. - ---- - ---- - -fast-json-stable-stringify 2.1.0 - MIT -https://github.com/epoberezkin/fast-json-stable-stringify - - -This software is released under the MIT license: - - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - ---- - ---- - -function-bind 1.1.1 - MIT -https://github.com/Raynos/function-bind - - - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. - ---- - ---- - -has 1.0.3 - MIT -https://github.com/tarruda/has - - - -Permission is hereby granted, free of charge, to any person -obtaining a copy of this software and associated documentation -files (the "Software"), to deal in the Software without -restriction, including without limitation the rights to use, -copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the -Software is furnished to do so, subject to the following -conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -OTHER DEALINGS IN THE SOFTWARE. - ---- - ---- - -is-core-module 2.3.0 - MIT -https://github.com/inspect-js/is-core-module - - -The MIT License (MIT) - - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - ---- - ---- - -jju 1.4.0 - MIT -http://rlidwka.github.io/jju/ - - -(The MIT License) - - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. - ---- - ---- - -json-schema-traverse 0.4.1 - MIT -https://github.com/epoberezkin/json-schema-traverse#readme - - -MIT License - - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. - ---- - ---- - -lodash-es 4.17.21 - MIT -https://lodash.com/custom-builds - -copyright Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors <http://underscorejs.org/> - - -Based on Underscore.js, copyright Jeremy Ashkenas, -DocumentCloud and Investigative Reporters & Editors <http://underscorejs.org/> - -This software consists of voluntary contributions made by many -individuals. For exact contribution history, see the revision history -available at https://github.com/lodash/lodash - -The following license applies to all parts of this software except as -documented below: - -==== - -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -"Software"), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -==== - -code is defined as all source code displayed within the prose of the -documentation. - -CC0: http://creativecommons.org/publicdomain/zero/1.0/ - -==== - -Files located in the node_modules and vendor directories are externally -maintained libraries used by this software which have their own -licenses; we recommend you read them, as their terms may differ from the -terms above. - ---- - ---- - -path-parse 1.0.6 - MIT -https://github.com/jbgutierrez/path-parse#readme - - -The MIT License (MIT) - - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. - ---- - ---- - -punycode 2.1.1 - MIT -https://mths.be/punycode - - - -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -"Software"), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - ---- - ---- - -resolve 1.19.0 - MIT -https://github.com/browserify/resolve#readme - - -MIT License - - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. - ---- - ---- - -tabbable 5.2.0 - MIT -https://github.com/focus-trap/tabbable#readme - -The MIT License (MIT) - - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. - ---- - - ------------------- END OF DEPENDENCY LICENSE -------------------- - - - - ------------------- START OF DEPENDENCY LICENSE -------------------- -- @microsoft/fast-element -License Type: MIT - FAST - https://www.fast.design/ - - MIT License - - Copyright (c) Microsoft Corporation. All rights reserved. - - Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to deal - in the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in all - copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - SOFTWARE ------------------- END OF DEPENDENCY LICENSE -------------------- - - - - ------------------- START OF DEPENDENCY LICENSE -------------------- -- @microsoft/fast-foundation -License Type: MIT - FAST - https://www.fast.design/ - - MIT License - - Copyright (c) Microsoft Corporation. All rights reserved. - - Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to deal - in the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in all - copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - SOFTWARE - ---------- Acknowledgements --------- -# Acknowledgements - -* A huge thank-you to [TypeScript](www.typescriptlang.org/) for providing an amazing language, build tools, and at least one [code sample](./src/utilities/apply-mixins.ts) we've shamelessly stolen. -* Big thanks to https://github.com/fkleuver and the https://github.com/aurelia/aurelia project for [Dependency Injection](./src/di/di.ts) code and core tests. - - ------------------- END OF DEPENDENCY LICENSE -------------------- - - - - ------------------- START OF DEPENDENCY LICENSE -------------------- -- @microsoft/fast-web-utilities -License Type: MIT - FAST - https://www.fast.design/ - - MIT License - - Copyright (c) Microsoft Corporation. All rights reserved. - - Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to deal - in the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in all - copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - SOFTWARE ------------------- END OF DEPENDENCY LICENSE -------------------- - - - - ------------------- START OF DEPENDENCY LICENSE -------------------- -- exenv-es6 - - -Copyright (c) 2018 Chris Holt -License Type: MIT -MIT License - - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. - ------------------- END OF DEPENDENCY LICENSE -------------------- - - - - ------------------- START OF DEPENDENCY LICENSE -------------------- -- tabbable - - -Copyright (c) 2015 David Clark -License Type: MIT -The MIT License (MIT) - - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. - - ------------------- END OF DEPENDENCY LICENSE -------------------- - - - - ------------------- START OF DEPENDENCY LICENSE -------------------- -- tslib - - -Copyright (c) Microsoft Corporation. -License Type: 0BSD - -Permission to use, copy, modify, and/or distribute this software for any -purpose with or without fee is hereby granted. - -THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH -REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY -AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, -INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM -LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR -OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR -PERFORMANCE OF THIS SOFTWARE. - - ---------- Notice --------- -/*! ***************************************************************************** - -Permission to use, copy, modify, and/or distribute this software for any -purpose with or without fee is hereby granted. - -THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH -REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY -AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, -INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM -LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR -OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR -PERFORMANCE OF THIS SOFTWARE. -***************************************************************************** */ - - - ------------------- END OF DEPENDENCY LICENSE -------------------- - - - - ------------------- START OF DEPENDENCY LICENSE -------------------- -- @microsoft/fast-react-wrapper - - -Copyright (c) 2017 Google LLC. All rights reserved. - -License Type: MIT - FAST - https://www.fast.design/ - - MIT License - - Copyright (c) Microsoft Corporation. All rights reserved. - - Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to deal - in the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in all - copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - SOFTWARE - ---------- Acknowledgements --------- -# Acknowledgements - -We'd like to extend a massive thank you to [the Lit team](https://github.com/lit/lit) for their work on [the Lit Labs React Wrapper](https://github.com/lit/lit/tree/main/packages/labs/react). This awesome group of people worked out many of the details of how best to wrap a Web Component for a great experience in React. The FAST React Wrapper was able to leverage a substantial bit of that code for its own wrapper. The FAST React Wrapper re-shapes the API and layers on some additional features that enable the wrapper to leverage FAST's metadata, making the wrapping of any FAST Web Component more streamlined. The FAST wrapper also enables a less verbose registration process when integrating with a Design System. - -We've provided the Lit Labs React Wrapper license below. - ---- - -BSD 3-Clause License - - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - -1. Redistributions of source code must retain the above copyright notice, this -list of conditions and the following disclaimer. - -2. Redistributions in binary form must reproduce the above copyright notice, -this list of conditions and the following disclaimer in the documentation -and/or other materials provided with the distribution. - -3. Neither the name of the copyright holder nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE -FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - - ------------------- END OF DEPENDENCY LICENSE -------------------- - - - - ------------------- START OF DEPENDENCY LICENSE -------------------- -- react - - -Copyright (c) Facebook, Inc. and its affiliates. -License Type: MIT -MIT License - - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. - ------------------- END OF DEPENDENCY LICENSE -------------------- - - - - ------------------- START OF DEPENDENCY LICENSE -------------------- -- loose-envify -License Type: MIT -The MIT License (MIT) - -Copyright (c) 2015 Andres Suarez <zertosh@gmail.com> - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. ------------------- END OF DEPENDENCY LICENSE -------------------- - - - - ------------------- START OF DEPENDENCY LICENSE -------------------- -- js-tokens - - -Copyright (c) 2014, 2015 Simon Lydell -Copyright 2015 Simon Lydell -Copyright 2014 Simon Lydell -Copyright 2014, 2015 Simon Lydell -License Type: MIT -The MIT License (MIT) - - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. - ------------------- END OF DEPENDENCY LICENSE -------------------- - - - - ------------------- START OF DEPENDENCY LICENSE -------------------- -- object-assign - - -Copyright (c) Sindre Sorhus <sindresorhus@gmail.com> (sindresorhus.com) -License Type: MIT -The MIT License (MIT) - - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. - ------------------- END OF DEPENDENCY LICENSE -------------------- - - - - ------------------- START OF DEPENDENCY LICENSE -------------------- -- prop-types - - -Copyright (c) 2013-present, Facebook, Inc. -License Type: MIT -MIT License - - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. - ------------------- END OF DEPENDENCY LICENSE -------------------- - - Dependency: VS Code tooling for localizing Visual Studio Code extensions ======================================================================== diff --git a/vscode/esbuild.js b/vscode/esbuild.js index 3b07eaf..dbcdf5d 100644 --- a/vscode/esbuild.js +++ b/vscode/esbuild.js @@ -11,9 +11,7 @@ const baseConfig = { const scriptConfig = { ...baseConfig, target: "es2020", - format: "esm", - entryPoints: ["./src/propertiesView/script.ts"], - outfile: "./out/script.js", + format: "esm" }; const watchConfig = { diff --git a/vscode/package-lock.json b/vscode/package-lock.json index 8ead438..c80e54c 100644 --- a/vscode/package-lock.json +++ b/vscode/package-lock.json @@ -11,7 +11,6 @@ "dependencies": { "@vscode/debugadapter": "^1.65.0", "@vscode/l10n": "^0.0.18", - "@vscode/webview-ui-toolkit": "^1.2.2", "jsonc-parser": "3.3.1", "vscode-languageclient": "^8.1.0" }, @@ -386,42 +385,6 @@ "node": ">=12" } }, - "node_modules/@microsoft/fast-element": { - "version": "1.12.0", - "resolved": "https://registry.npmjs.org/@microsoft/fast-element/-/fast-element-1.12.0.tgz", - "integrity": "sha512-gQutuDHPKNxUEcQ4pypZT4Wmrbapus+P9s3bR/SEOLsMbNqNoXigGImITygI5zhb+aA5rzflM6O8YWkmRbGkPA==" - }, - "node_modules/@microsoft/fast-foundation": { - "version": "2.49.0", - "resolved": "https://registry.npmjs.org/@microsoft/fast-foundation/-/fast-foundation-2.49.0.tgz", - "integrity": "sha512-Wk4e4QXFVtT5hPwhMfHyGY30kixM0td8aDs7bAD6NM2z2SCBNvpTTWp+FCjx0I0lpUMlMenb6wsw7pMWQreRkQ==", - "dependencies": { - "@microsoft/fast-element": "^1.12.0", - "@microsoft/fast-web-utilities": "^5.4.1", - "tabbable": "^5.2.0", - "tslib": "^1.13.0" - } - }, - "node_modules/@microsoft/fast-react-wrapper": { - "version": "0.1.48", - "resolved": "https://registry.npmjs.org/@microsoft/fast-react-wrapper/-/fast-react-wrapper-0.1.48.tgz", - "integrity": "sha512-9NvEjru9Kn5ZKjomAMX6v+eF0DR+eDkxKDwDfi+Wb73kTbrNzcnmlwd4diN15ygH97kldgj2+lpvI4CKLQQWLg==", - "dependencies": { - "@microsoft/fast-element": "^1.9.0", - "@microsoft/fast-foundation": "^2.41.1" - }, - "peerDependencies": { - "react": ">=16.9.0" - } - }, - "node_modules/@microsoft/fast-web-utilities": { - "version": "5.4.1", - "resolved": "https://registry.npmjs.org/@microsoft/fast-web-utilities/-/fast-web-utilities-5.4.1.tgz", - "integrity": "sha512-ReWYncndjV3c8D8iq9tp7NcFNc1vbVHvcBFPME2nNFKNbS1XCesYZGlIlf3ot5EmuOXPlrzUHOWzQ2vFpIkqDg==", - "dependencies": { - "exenv-es6": "^1.1.1" - } - }, "node_modules/@types/glob": { "version": "7.1.3", "dev": true, @@ -512,19 +475,6 @@ "node": ">=16" } }, - "node_modules/@vscode/webview-ui-toolkit": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@vscode/webview-ui-toolkit/-/webview-ui-toolkit-1.2.2.tgz", - "integrity": "sha512-xIQoF4FC3Xh6d7KNKIoIezSiFWYFuf6gQMdDyKueKBFGeKwaHWEn+dY2g3makvvEsNMEDji/woEwvg9QSbuUsw==", - "dependencies": { - "@microsoft/fast-element": "^1.6.2", - "@microsoft/fast-foundation": "^2.38.0", - "@microsoft/fast-react-wrapper": "^0.1.18" - }, - "peerDependencies": { - "react": ">=16.9.0" - } - }, "node_modules/agent-base": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.1.tgz", @@ -948,11 +898,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/exenv-es6": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/exenv-es6/-/exenv-es6-1.1.1.tgz", - "integrity": "sha512-vlVu3N8d6yEMpMsEm+7sUBAI81aqYYuEvfK0jNqmdb/OPXzzH7QWDDnVjMvDSY47JdHEqx/dfC/q8WkfoTmpGQ==" - }, "node_modules/fill-range": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", @@ -1245,12 +1190,6 @@ "dev": true, "license": "ISC" }, - "node_modules/js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "peer": true - }, "node_modules/js-yaml": { "version": "4.1.0", "dev": true, @@ -1317,18 +1256,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/loose-envify": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", - "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", - "peer": true, - "dependencies": { - "js-tokens": "^3.0.0 || ^4.0.0" - }, - "bin": { - "loose-envify": "cli.js" - } - }, "node_modules/mimic-fn": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", @@ -1654,18 +1581,6 @@ "safe-buffer": "^5.1.0" } }, - "node_modules/react": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz", - "integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==", - "peer": true, - "dependencies": { - "loose-envify": "^1.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/readable-stream": { "version": "2.3.8", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", @@ -1851,11 +1766,6 @@ "url": "https://github.com/chalk/supports-color?sponsor=1" } }, - "node_modules/tabbable": { - "version": "5.3.3", - "resolved": "https://registry.npmjs.org/tabbable/-/tabbable-5.3.3.tgz", - "integrity": "sha512-QD9qKY3StfbZqWOPLp0++pOrAVb/HbUi5xCc8cUo4XjP19808oaMiDzn0leBY5mCespIBM0CIZePzZjgzR83kA==" - }, "node_modules/table-parser": { "version": "0.1.3", "dev": true, @@ -1876,11 +1786,6 @@ "node": ">=8.0" } }, - "node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" - }, "node_modules/typescript": { "version": "4.6.4", "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.6.4.tgz", @@ -2208,39 +2113,6 @@ "dev": true, "optional": true }, - "@microsoft/fast-element": { - "version": "1.12.0", - "resolved": "https://registry.npmjs.org/@microsoft/fast-element/-/fast-element-1.12.0.tgz", - "integrity": "sha512-gQutuDHPKNxUEcQ4pypZT4Wmrbapus+P9s3bR/SEOLsMbNqNoXigGImITygI5zhb+aA5rzflM6O8YWkmRbGkPA==" - }, - "@microsoft/fast-foundation": { - "version": "2.49.0", - "resolved": "https://registry.npmjs.org/@microsoft/fast-foundation/-/fast-foundation-2.49.0.tgz", - "integrity": "sha512-Wk4e4QXFVtT5hPwhMfHyGY30kixM0td8aDs7bAD6NM2z2SCBNvpTTWp+FCjx0I0lpUMlMenb6wsw7pMWQreRkQ==", - "requires": { - "@microsoft/fast-element": "^1.12.0", - "@microsoft/fast-web-utilities": "^5.4.1", - "tabbable": "^5.2.0", - "tslib": "^1.13.0" - } - }, - "@microsoft/fast-react-wrapper": { - "version": "0.1.48", - "resolved": "https://registry.npmjs.org/@microsoft/fast-react-wrapper/-/fast-react-wrapper-0.1.48.tgz", - "integrity": "sha512-9NvEjru9Kn5ZKjomAMX6v+eF0DR+eDkxKDwDfi+Wb73kTbrNzcnmlwd4diN15ygH97kldgj2+lpvI4CKLQQWLg==", - "requires": { - "@microsoft/fast-element": "^1.9.0", - "@microsoft/fast-foundation": "^2.41.1" - } - }, - "@microsoft/fast-web-utilities": { - "version": "5.4.1", - "resolved": "https://registry.npmjs.org/@microsoft/fast-web-utilities/-/fast-web-utilities-5.4.1.tgz", - "integrity": "sha512-ReWYncndjV3c8D8iq9tp7NcFNc1vbVHvcBFPME2nNFKNbS1XCesYZGlIlf3ot5EmuOXPlrzUHOWzQ2vFpIkqDg==", - "requires": { - "exenv-es6": "^1.1.1" - } - }, "@types/glob": { "version": "7.1.3", "dev": true, @@ -2318,16 +2190,6 @@ "semver": "^7.6.2" } }, - "@vscode/webview-ui-toolkit": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@vscode/webview-ui-toolkit/-/webview-ui-toolkit-1.2.2.tgz", - "integrity": "sha512-xIQoF4FC3Xh6d7KNKIoIezSiFWYFuf6gQMdDyKueKBFGeKwaHWEn+dY2g3makvvEsNMEDji/woEwvg9QSbuUsw==", - "requires": { - "@microsoft/fast-element": "^1.6.2", - "@microsoft/fast-foundation": "^2.38.0", - "@microsoft/fast-react-wrapper": "^0.1.18" - } - }, "agent-base": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.1.tgz", @@ -2596,11 +2458,6 @@ "version": "4.0.0", "dev": true }, - "exenv-es6": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/exenv-es6/-/exenv-es6-1.1.1.tgz", - "integrity": "sha512-vlVu3N8d6yEMpMsEm+7sUBAI81aqYYuEvfK0jNqmdb/OPXzzH7QWDDnVjMvDSY47JdHEqx/dfC/q8WkfoTmpGQ==" - }, "fill-range": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", @@ -2779,12 +2636,6 @@ "version": "2.0.0", "dev": true }, - "js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "peer": true - }, "js-yaml": { "version": "4.1.0", "dev": true, @@ -2833,15 +2684,6 @@ "is-unicode-supported": "^0.1.0" } }, - "loose-envify": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", - "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", - "peer": true, - "requires": { - "js-tokens": "^3.0.0 || ^4.0.0" - } - }, "mimic-fn": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", @@ -3059,15 +2901,6 @@ "safe-buffer": "^5.1.0" } }, - "react": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz", - "integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==", - "peer": true, - "requires": { - "loose-envify": "^1.1.0" - } - }, "readable-stream": { "version": "2.3.8", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", @@ -3193,11 +3026,6 @@ "has-flag": "^4.0.0" } }, - "tabbable": { - "version": "5.3.3", - "resolved": "https://registry.npmjs.org/tabbable/-/tabbable-5.3.3.tgz", - "integrity": "sha512-QD9qKY3StfbZqWOPLp0++pOrAVb/HbUi5xCc8cUo4XjP19808oaMiDzn0leBY5mCespIBM0CIZePzZjgzR83kA==" - }, "table-parser": { "version": "0.1.3", "dev": true, @@ -3214,11 +3042,6 @@ "is-number": "^7.0.0" } }, - "tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" - }, "typescript": { "version": "4.6.4", "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.6.4.tgz", diff --git a/vscode/package.json b/vscode/package.json index 47d167d..33b6975 100644 --- a/vscode/package.json +++ b/vscode/package.json @@ -374,10 +374,6 @@ } ], "commands": [ - { - "command": "jdk.node.properties.edit", - "title": "%jdk.node.properties.edit%" - }, { "command": "jdk.workspace.compile", "title": "%jdk.workspace.compile%", @@ -594,10 +590,6 @@ "command": "jdk.foundProjects.deleteEntry", "when": "false" }, - { - "command": "jdk.node.properties.edit", - "when": "false" - }, { "command": "jdk:Edit:org.openide.actions.DeleteAction", "when": "false" @@ -796,7 +788,6 @@ "dependencies": { "@vscode/debugadapter": "^1.65.0", "@vscode/l10n": "^0.0.18", - "@vscode/webview-ui-toolkit": "^1.2.2", "jsonc-parser": "3.3.1", "vscode-languageclient": "^8.1.0" } diff --git a/vscode/package.nls.ja.json b/vscode/package.nls.ja.json index 175e7dd..9a1db31 100755 --- a/vscode/package.nls.ja.json +++ b/vscode/package.nls.ja.json @@ -1,5 +1,4 @@ { - "jdk.node.properties.edit": "プロパティ", "jdk.views.run.config": "実行構成", "jdk.views.explorer.projects": "プロジェクト", "jdk.workspace.compile": "ワークスペースのコンパイル", diff --git a/vscode/package.nls.json b/vscode/package.nls.json index 8c70670..a0bdba3 100644 --- a/vscode/package.nls.json +++ b/vscode/package.nls.json @@ -1,5 +1,4 @@ { - "jdk.node.properties.edit": "Properties", "jdk.views.run.config": "Run Configuration", "jdk.views.explorer.projects": "Projects", "jdk.workspace.compile": "Compile Workspace", diff --git a/vscode/package.nls.zh-cn.json b/vscode/package.nls.zh-cn.json index ea84538..5a17533 100755 --- a/vscode/package.nls.zh-cn.json +++ b/vscode/package.nls.zh-cn.json @@ -1,5 +1,4 @@ { - "jdk.node.properties.edit": "属性", "jdk.views.run.config": "运行配置", "jdk.views.explorer.projects": "项目", "jdk.workspace.compile": "编译工作区", diff --git a/vscode/src/commands/commands.ts b/vscode/src/commands/commands.ts index 8a73960..cd5fbe6 100644 --- a/vscode/src/commands/commands.ts +++ b/vscode/src/commands/commands.ts @@ -45,7 +45,6 @@ export const extCommands = { abstractMethodsComplete: appendPrefixToCommand('java.complete.abstract.methods'), startupCondition: appendPrefixToCommand('startup.condition'), nbEventListener: appendPrefixToCommand('addEventListener'), - editNodeProps: appendPrefixToCommand('node.properties.edit'), selectEditorProjs: appendPrefixToCommand('select.editor.projects'), attachDebugger: appendPrefixToCommand("java.attachDebugger.connector"), } diff --git a/vscode/src/extension.ts b/vscode/src/extension.ts index 6ab0582..568661a 100644 --- a/vscode/src/extension.ts +++ b/vscode/src/extension.ts @@ -31,7 +31,6 @@ import { NbTestAdapter } from './testAdapter'; import { SetTextEditorDecorationParams} from './lsp/protocol'; import * as launchConfigurations from './launchConfigurations'; import { initializeRunConfiguration, runConfigurationProvider, runConfigurationNodeProvider, configureRunSettings } from './runConfiguration'; -import { PropertiesView } from './propertiesView/propertiesView'; import { extConstants } from './constants'; import { ExtensionInfo } from './extensionInfo'; import { ClientPromise } from './lsp/clientPromise'; @@ -85,9 +84,6 @@ export function activate(context: ExtensionContext): VSNetBeansAPI { // register commands subscribeCommands(context); - context.subscriptions.push(commands.registerCommand(extConstants.COMMAND_PREFIX + '.node.properties.edit', - async (node) => await PropertiesView.createOrShow(context, node, (await globalVars.clientPromise.client).findTreeViewService()))); - const archiveFileProvider = <vscode.TextDocumentContentProvider> { provideTextDocumentContent: async (uri: vscode.Uri, token: vscode.CancellationToken): Promise<string> => { return await commands.executeCommand(extConstants.COMMAND_PREFIX + '.get.archive.file.content', uri.toString()); diff --git a/vscode/src/propertiesView/controlTypes.ts b/vscode/src/propertiesView/controlTypes.ts deleted file mode 100644 index 039e4a5..0000000 --- a/vscode/src/propertiesView/controlTypes.ts +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { EnumType, Typed } from "../typesUtil"; - -export type ID = number; - -export const PropertyTypes = { - String: "java.lang.String", - Boolean: "java.lang.Boolean", - Properties: "java.util.Properties", - Unknown: "unknown", -} as const;// unfortunate but necessary duplication -export type PropertyTypeMap = { - "java.lang.String": string; - "java.lang.Boolean": boolean; - "java.util.Properties": Record<string, string>; - "unknown": unknown -}; -export type Property<T extends keyof PropertyTypeMap = keyof PropertyTypeMap> = T extends T ? { - preferred: boolean; - displayName: string; - shortName: string; - htmlName?: string; - write: boolean; - hidden: boolean; - expert: boolean; - type: T; - value: PropertyTypeMap[T]; - name: string; -} : never; // Distributive type -export type Properties = { - preferred: boolean; - displayName: string; - shortName: string; - htmlName?: string; - hidden: boolean; - expert: boolean; - name: string; - properties: Property[]; -}; - -export type PropertyMessage = { - name: string; - value: string | boolean | Record<string, string>; -}; - -export type Command = "Save" | "Cancel" | "Error" | "Info"; -export const CommandKey: EnumType<Command> = { - Save: "Save", - Error: "Error", - Info: "Info", - Cancel: "Cancel" -}; - -export type MessageCommon<T extends Command> = Typed<T>; -export type InfoMessage = MessageCommon<typeof CommandKey.Info> & { - info: string; -}; -export type ErrMessage = MessageCommon<typeof CommandKey.Error> & { - error: string; - stack?: string; -}; -export type SaveMessage = MessageCommon<typeof CommandKey.Save> & { - properties: PropertyMessage[]; -}; -export type CancelMessage = MessageCommon<typeof CommandKey.Cancel>; -export type Message = InfoMessage | ErrMessage | SaveMessage | CancelMessage; \ No newline at end of file diff --git a/vscode/src/propertiesView/propertiesHtmlBuilder.ts b/vscode/src/propertiesView/propertiesHtmlBuilder.ts deleted file mode 100644 index 09ac325..0000000 --- a/vscode/src/propertiesView/propertiesHtmlBuilder.ts +++ /dev/null @@ -1,119 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import * as vscode from 'vscode'; -import { PropertyTypes, Properties, Property } from "./controlTypes"; - -export function makeHtmlForProperties(name: string, nonce: string, scriptUri: vscode.Uri, properties: Properties): string { - return `<!DOCTYPE html> - <html lang="en"> - - <head> - <meta charset="UTF-8"> - <meta name="viewport" content="width=device-width, initial-scale=1.0"> - <meta http-equiv="Content-Security-Policy" content="default-src 'none'; script-src 'nonce-${nonce}';"> - <title>${name} - - - -

${name} Properties

- - - ${makePropertiesTable(properties)} - - - - - - -
-
- Save - Cancel -
- - - - ` -}; - -function wrapToTable(name: string, content: string, separator: string = ":"): string { - return `${name}${separator}${content}`; -} - -function makePropertiesTable(properties: Properties): string { - let html = ""; - for (const prop of properties.properties) { - html += makePropAccess(prop); - } - return html; -} - -function makePropAccess(prop: Property): string { - let out: string; - switch (prop.type) { - case PropertyTypes.String: - out = makeStringAccess(prop); - break; - case PropertyTypes.Boolean: - out = makeBoolAccess(prop); - break; - case PropertyTypes.Properties: - out = makePropertiesAccess(prop); - break; - default: - out = prop.value + ""; - break; - } - return wrapToTable(prop.displayName, out) + '\n'; -} - -function makeStringAccess(prop: Property) { - return ``; -} - -function makeBoolAccess(prop: Property) { - return ``; -} - -function makePropertiesAccess(prop: Property) { - return `
${prop.displayName} - ${makePropTable(prop)} -
`; -} - -function makePropTable(prop: Property) { - let out = ""; - for (const key in prop.value) { - out += makePropRow(prop, key) + '\n'; - } - return out; -} - -function makePropRow(prop: Property, key: string) { - return wrapToTable(asTextField(key, prop.write, "name"), asTextField(prop.value[key], prop.write, "value"), " = "); -} - -function asTextField(value: string, enabled: boolean, name: string) { - return `` -} - -function encode(value: string): string { - return value.replace(/\"/g, """); -} \ No newline at end of file diff --git a/vscode/src/propertiesView/propertiesView.ts b/vscode/src/propertiesView/propertiesView.ts deleted file mode 100644 index 5ea0ca6..0000000 --- a/vscode/src/propertiesView/propertiesView.ts +++ /dev/null @@ -1,242 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import * as vscode from 'vscode'; -import { CommandKey, ID, Message, PropertyMessage, Properties, Property, PropertyTypes } from './controlTypes'; -import { assertNever, isObject, isRecord, isString, IsType } from '../typesUtil'; -import { makeHtmlForProperties } from './propertiesHtmlBuilder'; -import { TreeViewService, TreeNodeListener, Visualizer } from '../explorer'; -import { NodeChangeType } from '../lsp/protocol'; - -function isVisualizer(node : any) : node is Visualizer { - return node?.id && node?.rootId; -} -export class PropertiesView { - private static readonly COMMAND_PREFIX = "java."; - private static readonly COMMAND_GET_NODE_PROPERTIES = PropertiesView.COMMAND_PREFIX + "node.properties.get"; // NOI18N - private static readonly COMMAND_SET_NODE_PROPERTIES = PropertiesView.COMMAND_PREFIX + "node.properties.set"; // NOI18N - - private static extensionUri: vscode.Uri; - private static scriptPath: vscode.Uri; - private static panels: Record = {}; - - private readonly _panel: vscode.WebviewPanel; - private readonly id: ID; - private readonly name: string; - private readonly _disposables: vscode.Disposable[] = []; - - private properties?: Properties; - - public static async createOrShow(context: vscode.ExtensionContext, node: any, treeService? : TreeViewService) { - if (!node) - return; - if (!isVisualizer(node)) { - return; - } - const id = node.id ? Number(node.id) : 0; - // If we already have a panel, show it. - const current = PropertiesView.panels[id]; - - let view : PropertiesView; - - // the listener will remove/close the properties view, if the associated node gets destroyed. - class L implements TreeNodeListener { - nodeDestroyed(n : Visualizer) : void { - if (view) { - /* - vscode.window.showInformationMessage(`${node.label} has been removed.`); - */ - view.dispose(); - } - } - } - - try { - if (current) { - await current.load(); - current._panel.reveal(); - return; - } - if (!PropertiesView.extensionUri) { - PropertiesView.extensionUri = context.extensionUri; - PropertiesView.scriptPath = vscode.Uri.joinPath(context.extensionUri, 'out', 'script.js'); - } else if (PropertiesView.extensionUri !== context.extensionUri) - throw new Error("Extension paths differ."); - // Otherwise, create a new panel. - PropertiesView.panels[id] = view = new PropertiesView(id, node.tooltip + " " + node.label); - - if (treeService) { - treeService.addNodeChangeListener(node, new L(), NodeChangeType.DESTROY); - } - } catch (e: unknown) { - console.log(e); - } - } - - private constructor(id: ID, name: string) { - this.id = id; - this.name = name; - this._panel = vscode.window.createWebviewPanel('Properties', 'Properties', vscode.ViewColumn.One, { - // Enable javascript in the webview - enableScripts: true, - }); - - // Set the webview's html content - this.load().then(() => - this.setHtml()).catch((e) => { - console.error(e); - this.dispose(); - }); - - // Listen for when the panel is disposed - // This happens when the user closes the panel or when the panel is closed programatically - this._panel.onDidDispose(() => this.dispose(), null, this._disposables); - - // Update the content based on view changes - this._panel.onDidChangeViewState( - () => { - if (this._panel.visible) { - try { - this.setHtml(); - } catch (e: unknown) { - console.error(e); - this.dispose(); - } - } - }, - null, - this._disposables - ); - - // Handle messages from the webview - this._panel.webview.onDidReceiveMessage( - message => { - try { - this.processMessage(message); - } catch (e: unknown) { - console.error(e); - this.dispose(); - } - }, - undefined, - this._disposables - ); - } - - private async load() { - const props = await this.get(); - if (props.size === 0) { - throw new Error("No properties."); - } - this.properties = props.values().next().value; - } - - private async get(): Promise> { - const resp = await vscode.commands.executeCommand(PropertiesView.COMMAND_GET_NODE_PROPERTIES, this.id); - if (!isObject(resp)) { - // TODO - possibly report protocol error ? - return new Map(); - } - return new Map(Object.entries(resp)); // TODO - validate cast - } - - private save(properties: PropertyMessage[]) { - if (!this.properties) return; - - for (const prop of properties) - this.mergeProps(prop, this.properties?.properties); - - const msg: Record = {}; - msg[this.properties.name] = this.properties; - - vscode.commands.executeCommand(PropertiesView.COMMAND_SET_NODE_PROPERTIES, this.id, msg) - .then(done => { - if (isRecord(isRecord.bind(null, isString) as IsType>, done)) { - this.processSaveError(done); - } - }, err => vscode.window.showErrorMessage(err.message, { modal: true, detail: err.stack })); - } - - private processSaveError(errObj: Record>) { - if (Object.keys(errObj).length === 0) - return; - let out = ""; - for (const propertiesName of Object.keys(errObj)) { - for (const property of Object.entries(errObj[propertiesName])) - out += `${propertiesName}.${property[0]}: ${property[1]}\n`; - } - vscode.window.showErrorMessage("Saving of properties failed.", { modal: true, detail: out }); - } - - private mergeProps(prop: PropertyMessage, props?: Property[]): void { - const p = props?.find(p => p.name === prop.name); - if (p && Object.values(PropertyTypes).includes(p.type)) - p.value = prop.value; - } - - private processMessage(message: Message) { - switch (message._type) { - case CommandKey.Save: - this.save(message.properties); - case CommandKey.Cancel: - this.dispose(); - break; - case CommandKey.Error: - console.error(message.error); - if (message.stack) - console.error(message.stack); - this.dispose(); - break; - case CommandKey.Info: - console.log(message.info); - break; - default: - assertNever(message, "Got unknown message: " + JSON.stringify(message)); - } - } - - private static getNonce() { - let text = ""; - const possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; - for (let i = 0; i < 32; i++) { - text += possible.charAt(Math.floor(Math.random() * possible.length)); - } - return text; - } - - private setHtml() { - if (!this.properties) - throw new Error("No properties to show."); - const script = this._panel.webview.asWebviewUri(PropertiesView.scriptPath); - const html = makeHtmlForProperties(this.name, PropertiesView.getNonce(), script, this.properties); - this._panel.webview.html = html; - } - - public dispose() { - delete PropertiesView.panels[this.id]; - // Clean up our resources - this._panel.dispose(); - while (this._disposables.length) { - const x = this._disposables.pop(); - if (x) { - x.dispose(); - } - } - } -} diff --git a/vscode/src/propertiesView/script.ts b/vscode/src/propertiesView/script.ts deleted file mode 100644 index 4aafbf9..0000000 --- a/vscode/src/propertiesView/script.ts +++ /dev/null @@ -1,101 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { provideVSCodeDesignSystem, vsCodeButton, vsCodeTextField, vsCodeDivider, vsCodeCheckbox, Button, TextField, Checkbox } from "@vscode/webview-ui-toolkit"; -import { isError, asClass, isClass } from "../typesUtil"; -import { CommandKey, Message, PropertyMessage } from "./controlTypes"; - -provideVSCodeDesignSystem().register(vsCodeButton(), vsCodeTextField(), vsCodeDivider(), vsCodeCheckbox()); - -const vscode = acquireVsCodeApi(); -document.addEventListener("DOMContentLoaded", () => { - try { - asClass(Button, document.getElementById('save')) - .addEventListener('click', () => { - try { - if (validate()) - sendMessage({ _type: CommandKey.Save, properties: getProperties() }); - } catch (e: unknown) { - handleError(e); - } - }); - asClass(Button, document.getElementById('cancel')) - .addEventListener('click', () => { - sendMessage({ _type: CommandKey.Cancel }); - }); - } catch (e: unknown) { - handleError(e); - } -}); - -function handleError(error: unknown) { - if (isError(error)) - sendMessage({ _type: CommandKey.Error, error: error.message, stack: error.stack }); - else - sendMessage({ _type: CommandKey.Error, error: JSON.stringify(error) }); -} - -function sendMessage(message: Message) { - vscode.postMessage(message); -} - -function getProperties(): PropertyMessage[] { - const out: PropertyMessage[] = []; - const elements = document.getElementsByName("input"); - for (let i = 0; i < elements.length; ++i) { - const element = elements.item(i); - if (element) - out.push(getProperty(element)); - } - return out; -} - -function getProperty(element: HTMLElement): PropertyMessage { - if (isClass(TextField, element)) { - return makeProperty(element.value, element?.id); - } else if (isClass(Checkbox, element)) { - return makeProperty(element.checked, element?.id); - } else if (isClass(HTMLTableElement, element)) { - return makeProperty(parseProperties(element), element?.id); - } - throw new Error("Unknown HTML Element type."); -} - -function makeProperty(value: string | boolean | Record, name?: string): PropertyMessage { - if (name) - return { name: name, value: value }; - throw new Error("HTML Element have no ID."); -} - -function parseProperties(table: HTMLTableElement): Record { - const out: Record = {}; - for (let i = 0; i < table.rows.length; ++i) { - readProperty(out, table.rows.item(i)?.cells); - } - return out; -} - -function readProperty(out: Record, cells?: HTMLCollectionOf | null) { - out[asClass(TextField, cells?.item(0)?.getElementsByClassName("name").item(0)).value] - = asClass(TextField, cells?.item(1)?.getElementsByClassName("value").item(0)).value; -} - -function validate(): boolean { - return true; // no validation needed ATM -} \ No newline at end of file diff --git a/vscode/src/webviews/nbWebviewHandler.ts b/vscode/src/webviews/nbWebviewHandler.ts index f7ff079..b2f5386 100644 --- a/vscode/src/webviews/nbWebviewHandler.ts +++ b/vscode/src/webviews/nbWebviewHandler.ts @@ -26,6 +26,7 @@ export const showHtmlPage = async (params: HtmlPageParams): Promise => { const match = /(.*)<\/title>/i.exec(data); const name = match && match.length > 1 ? match[1] : ''; const resourceDir = Uri.joinPath(globalVars.extensionInfo.getGlobalStorage(), params.id); + // TODO: @vscode/codeicons is a devDependency not a prod dependency. So do we ever reach this code? const distPath = Uri.joinPath(globalVars.extensionInfo.getExtensionStorageUri(), 'node_modules', '@vscode/codicons', 'dist'); workspace.fs.createDirectory(resourceDir); let view = window.createWebviewPanel('htmlView', name, ViewColumn.Beside, { From b73b94a5211a04dd1572d0426db9838629f5a98d Mon Sep 17 00:00:00 2001 From: Achal Talati <achal.talati@oracle.com> Date: Thu, 10 Oct 2024 14:16:42 +0530 Subject: [PATCH 11/17] Refactored NB language client listeners --- vscode/src/extension.ts | 15 +++----- vscode/src/lsp/initializer.ts | 4 +-- .../{ => listeners}/notifications/handlers.ts | 0 .../{ => listeners}/notifications/register.ts | 0 .../lsp/{ => listeners}/requests/handlers.ts | 0 .../lsp/{ => listeners}/requests/register.ts | 0 .../listeners/textDocumentContentProvider.ts | 36 +++++++++++++++++++ 7 files changed, 43 insertions(+), 12 deletions(-) rename vscode/src/lsp/{ => listeners}/notifications/handlers.ts (100%) rename vscode/src/lsp/{ => listeners}/notifications/register.ts (100%) rename vscode/src/lsp/{ => listeners}/requests/handlers.ts (100%) rename vscode/src/lsp/{ => listeners}/requests/register.ts (100%) create mode 100644 vscode/src/lsp/listeners/textDocumentContentProvider.ts diff --git a/vscode/src/extension.ts b/vscode/src/extension.ts index 568661a..5fb6c14 100644 --- a/vscode/src/extension.ts +++ b/vscode/src/extension.ts @@ -23,7 +23,7 @@ 'use strict'; -import { commands, workspace, ExtensionContext, TextEditorDecorationType } from 'vscode'; +import { ExtensionContext, TextEditorDecorationType } from 'vscode'; import * as vscode from 'vscode'; @@ -41,6 +41,8 @@ import { subscribeCommands } from './commands/register'; import { VSNetBeansAPI } from './lsp/types'; import { registerListenersBeforeClientInit } from './listener'; import { registerDebugger } from './debugger/debugger'; +import { registerConfigChangeListeners } from './configurations/listener'; +import { registerFileProviders } from './lsp/listeners/textDocumentContentProvider'; export let LOGGER: ExtensionLogger; export namespace globalVars { @@ -83,15 +85,8 @@ export function activate(context: ExtensionContext): VSNetBeansAPI { // register commands subscribeCommands(context); - - const archiveFileProvider = <vscode.TextDocumentContentProvider> { - provideTextDocumentContent: async (uri: vscode.Uri, token: vscode.CancellationToken): Promise<string> => { - return await commands.executeCommand(extConstants.COMMAND_PREFIX + '.get.archive.file.content', uri.toString()); - } - }; - context.subscriptions.push(workspace.registerTextDocumentContentProvider('jar', archiveFileProvider)); - context.subscriptions.push(workspace.registerTextDocumentContentProvider('nbjrt', archiveFileProvider)); - + registerFileProviders(context); + launchConfigurations.updateLaunchConfig(); // register completions: diff --git a/vscode/src/lsp/initializer.ts b/vscode/src/lsp/initializer.ts index 1cda051..3831296 100644 --- a/vscode/src/lsp/initializer.ts +++ b/vscode/src/lsp/initializer.ts @@ -25,8 +25,8 @@ import { attachNbProcessListeners, launchNbcode } from "./nbcode"; import { NbLanguageClient } from "./nbLanguageClient"; import { NbTestAdapter } from "../testAdapter"; import { registerListenersAfterClientInit } from "../listener"; -import { registerNotificationListeners } from "./notifications/register"; -import { registerRequestListeners } from "./requests/register"; +import { registerNotificationListeners } from "./listeners/notifications/register"; +import { registerRequestListeners } from "./listeners/requests/register"; import { TreeViewService, Visualizer } from "../explorer"; import { commands, TextEditor, TreeView, window, workspace } from "vscode"; import { extConstants } from "../constants"; diff --git a/vscode/src/lsp/notifications/handlers.ts b/vscode/src/lsp/listeners/notifications/handlers.ts similarity index 100% rename from vscode/src/lsp/notifications/handlers.ts rename to vscode/src/lsp/listeners/notifications/handlers.ts diff --git a/vscode/src/lsp/notifications/register.ts b/vscode/src/lsp/listeners/notifications/register.ts similarity index 100% rename from vscode/src/lsp/notifications/register.ts rename to vscode/src/lsp/listeners/notifications/register.ts diff --git a/vscode/src/lsp/requests/handlers.ts b/vscode/src/lsp/listeners/requests/handlers.ts similarity index 100% rename from vscode/src/lsp/requests/handlers.ts rename to vscode/src/lsp/listeners/requests/handlers.ts diff --git a/vscode/src/lsp/requests/register.ts b/vscode/src/lsp/listeners/requests/register.ts similarity index 100% rename from vscode/src/lsp/requests/register.ts rename to vscode/src/lsp/listeners/requests/register.ts diff --git a/vscode/src/lsp/listeners/textDocumentContentProvider.ts b/vscode/src/lsp/listeners/textDocumentContentProvider.ts new file mode 100644 index 0000000..e887c96 --- /dev/null +++ b/vscode/src/lsp/listeners/textDocumentContentProvider.ts @@ -0,0 +1,36 @@ +/* + Copyright (c) 2023-2024, Oracle and/or its affiliates. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +import { ExtensionContext, TextDocumentContentProvider, Uri, commands, workspace } from "vscode"; +import { nbCommands } from "../../commands/commands"; +import { Disposable } from "vscode-languageclient"; + +const archiveFileProvider = <TextDocumentContentProvider> { + provideTextDocumentContent: async (uri: Uri): Promise<string> => { + return await commands.executeCommand(nbCommands.archiveFileContent, uri.toString()); + } +}; + +const textDocumentContentProvider: Disposable[] = [ + workspace.registerTextDocumentContentProvider('nbjrt', archiveFileProvider), + workspace.registerTextDocumentContentProvider('jar', archiveFileProvider) +] + +export const registerFileProviders = (context: ExtensionContext) => { + textDocumentContentProvider.forEach((provider: Disposable) =>{ + context.subscriptions.push(provider); + }) +} From 85adc15d588c0dc3af638a291a67592a565c7fd2 Mon Sep 17 00:00:00 2001 From: Achal Talati <achal.talati@oracle.com> Date: Thu, 10 Oct 2024 14:30:45 +0530 Subject: [PATCH 12/17] Fixed import issues and refactored util functions --- vscode/src/commands/create.ts | 2 +- vscode/src/extension.ts | 1 - vscode/src/lsp/initializer.ts | 3 +- vscode/src/lsp/launchOptions.ts | 2 +- .../lsp/listeners/notifications/handlers.ts | 14 ++--- .../lsp/listeners/notifications/register.ts | 5 +- vscode/src/lsp/listeners/requests/handlers.ts | 17 +++--- vscode/src/lsp/listeners/requests/register.ts | 5 +- vscode/src/lsp/utils.ts | 2 - vscode/src/typesUtil.ts | 61 ------------------- vscode/src/utils.ts | 7 +++ 11 files changed, 33 insertions(+), 86 deletions(-) delete mode 100644 vscode/src/typesUtil.ts diff --git a/vscode/src/commands/create.ts b/vscode/src/commands/create.ts index a7b7018..9b0d66e 100644 --- a/vscode/src/commands/create.ts +++ b/vscode/src/commands/create.ts @@ -22,7 +22,7 @@ import * as fs from 'fs'; import { ICommand } from "./types"; import { globalVars } from "../extension"; import { getContextUri, isNbCommandRegistered } from "./utils"; -import { isString } from "../typesUtil"; +import { isString } from "../utils"; const newFromTemplate = async (ctx: any, template: any) => { const client: LanguageClient = await globalVars.clientPromise.client; diff --git a/vscode/src/extension.ts b/vscode/src/extension.ts index 5fb6c14..e9b40d2 100644 --- a/vscode/src/extension.ts +++ b/vscode/src/extension.ts @@ -60,7 +60,6 @@ export namespace globalVars { export function activate(context: ExtensionContext): VSNetBeansAPI { - globalVars.deactivated = false; globalVars.clientPromise = new ClientPromise(); globalVars.extensionInfo = new ExtensionInfo(context); LOGGER = new ExtensionLogger(extConstants.SERVER_NAME); diff --git a/vscode/src/lsp/initializer.ts b/vscode/src/lsp/initializer.ts index 3831296..d9c25c4 100644 --- a/vscode/src/lsp/initializer.ts +++ b/vscode/src/lsp/initializer.ts @@ -105,7 +105,8 @@ const serverBuilder = () => { } export const clientInit = () => { - const connection: () => Promise<StreamInfo> = serverOptionsBuilder(); + globalVars.deactivated = false; + const connection: () => Promise<StreamInfo> = serverBuilder(); const client = NbLanguageClient.build(connection, LOGGER); LOGGER.log('Language Client: Starting'); diff --git a/vscode/src/lsp/launchOptions.ts b/vscode/src/lsp/launchOptions.ts index 7db45e5..c9c24c2 100644 --- a/vscode/src/lsp/launchOptions.ts +++ b/vscode/src/lsp/launchOptions.ts @@ -16,7 +16,7 @@ import { builtInConfigKeys, configKeys } from "../configurations/configuration" import { isDarkColorThemeHandler, isNbJavacDisabledHandler, jdkHomeValueHandler, lspServerVmOptionsHandler, projectSearchRootsValueHandler, userdirHandler } from "../configurations/handlers"; import { l10n } from "../localiser"; -import { isString } from "../typesUtil"; +import { isString } from "../utils"; import { userDefinedLaunchOptionsType } from "./types" export const getUserConfigLaunchOptionsDefaults = (): userDefinedLaunchOptionsType => { diff --git a/vscode/src/lsp/listeners/notifications/handlers.ts b/vscode/src/lsp/listeners/notifications/handlers.ts index 151db35..2236fad 100644 --- a/vscode/src/lsp/listeners/notifications/handlers.ts +++ b/vscode/src/lsp/listeners/notifications/handlers.ts @@ -14,14 +14,14 @@ limitations under the License. */ import { LogMessageNotification, MessageType, TelemetryEventNotification } from "vscode-languageclient"; -import { notificationOrRequestListenerType } from "../types"; -import { asRanges, ShowStatusMessageParams, StatusMessageRequest, TestProgressNotification, TextEditorDecorationDisposeNotification, TextEditorDecorationSetNotification } from "../protocol"; +import { notificationOrRequestListenerType } from "../../types"; +import { asRanges, ShowStatusMessageParams, StatusMessageRequest, TestProgressNotification, TextEditorDecorationDisposeNotification, TextEditorDecorationSetNotification } from "../../protocol"; import { commands, window, workspace } from "vscode"; -import { isNbJavacDisabledHandler, updateConfigurationValue } from "../../configurations/handlers"; -import { l10n } from "../../localiser"; -import { configKeys } from "../../configurations/configuration"; -import { builtInCommands } from "../../commands/commands"; -import { globalVars, LOGGER } from "../../extension"; +import { isNbJavacDisabledHandler, updateConfigurationValue } from "../../../configurations/handlers"; +import { l10n } from "../../../localiser"; +import { configKeys } from "../../../configurations/configuration"; +import { builtInCommands } from "../../../commands/commands"; +import { globalVars, LOGGER } from "../../../extension"; const checkInstallNbJavac = (msg: string) => { const NO_JAVA_SUPPORT = "Cannot initialize Java support"; diff --git a/vscode/src/lsp/listeners/notifications/register.ts b/vscode/src/lsp/listeners/notifications/register.ts index c5470a9..0759aed 100644 --- a/vscode/src/lsp/listeners/notifications/register.ts +++ b/vscode/src/lsp/listeners/notifications/register.ts @@ -13,8 +13,9 @@ See the License for the specific language governing permissions and limitations under the License. */ -import { NbLanguageClient } from "../nbLanguageClient" -import { notificationOrRequestListenerType } from "../types" + +import { NbLanguageClient } from "../../nbLanguageClient" +import { notificationOrRequestListenerType } from "../../types" import { notificationListeners } from "./handlers" export const registerNotificationListeners = (client: NbLanguageClient) => { diff --git a/vscode/src/lsp/listeners/requests/handlers.ts b/vscode/src/lsp/listeners/requests/handlers.ts index 703bca8..71bc544 100644 --- a/vscode/src/lsp/listeners/requests/handlers.ts +++ b/vscode/src/lsp/listeners/requests/handlers.ts @@ -14,14 +14,15 @@ limitations under the License. */ import { QuickPickItem, Uri, window, workspace, WorkspaceConfiguration } from "vscode"; -import { globalVars, LOGGER } from "../../extension"; -import { notificationOrRequestListenerType } from "../types"; -import { ExecInHtmlPageRequest, HtmlPageRequest, InputBoxRequest, InputBoxStep, MutliStepInputRequest, QuickPickRequest, QuickPickStep, SaveDocumentRequestParams, SaveDocumentsRequest, TextEditorDecorationCreateRequest, UpdateConfigurationRequest } from "../protocol"; -import { InputStep, MultiStepInput } from "../../utils"; -import { runConfigurationUpdateAll } from "../../runConfiguration"; -import { isError, isString } from "../../typesUtil"; -import { LogLevel } from "../../logger"; -import { execInHtmlPage, showHtmlPage } from "../../webviews/nbWebviewHandler"; +import { globalVars, LOGGER } from "../../../extension"; +import { notificationOrRequestListenerType } from "../../types"; +import { ExecInHtmlPageRequest, HtmlPageRequest, InputBoxRequest, InputBoxStep, MutliStepInputRequest, QuickPickRequest, QuickPickStep, SaveDocumentRequestParams, SaveDocumentsRequest, TextEditorDecorationCreateRequest, UpdateConfigurationRequest } from "../../protocol"; +import { InputStep, MultiStepInput } from "../../../utils"; +import { runConfigurationUpdateAll } from "../../../runConfiguration"; +import { isError } from "../../../utils"; +import { isString } from "../../../utils"; +import { LogLevel } from "../../../logger"; +import { execInHtmlPage, showHtmlPage } from "../../../webviews/nbWebviewHandler"; const textEditorDecorationCreateRequestHandler = (param: any) => { let decorationType = window.createTextEditorDecorationType(param); diff --git a/vscode/src/lsp/listeners/requests/register.ts b/vscode/src/lsp/listeners/requests/register.ts index 90bc6f0..bb333ea 100644 --- a/vscode/src/lsp/listeners/requests/register.ts +++ b/vscode/src/lsp/listeners/requests/register.ts @@ -13,8 +13,9 @@ See the License for the specific language governing permissions and limitations under the License. */ -import { NbLanguageClient } from "../nbLanguageClient" -import { notificationOrRequestListenerType } from "../types" + +import { NbLanguageClient } from "../../nbLanguageClient" +import { notificationOrRequestListenerType } from "../../types" import { requestListeners } from "./handlers" export const registerRequestListeners = (client: NbLanguageClient) => { diff --git a/vscode/src/lsp/utils.ts b/vscode/src/lsp/utils.ts index c31b223..1cf8b41 100644 --- a/vscode/src/lsp/utils.ts +++ b/vscode/src/lsp/utils.ts @@ -17,8 +17,6 @@ import * as fs from 'fs'; import * as path from 'path'; import * as os from 'os'; import { globalVars, LOGGER } from '../extension'; -import { TextDocumentFilter } from 'vscode-languageclient'; -import { extensions } from 'vscode'; import { extConstants } from '../constants'; export const enableDisableModules = ( diff --git a/vscode/src/typesUtil.ts b/vscode/src/typesUtil.ts deleted file mode 100644 index cb8d176..0000000 --- a/vscode/src/typesUtil.ts +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -export type IsType<T> = (obj: unknown) => obj is T; - -function assertType<T>(obj: unknown, isTypeTest: IsType<T>, errorMessage?: string): asserts obj is T { - if (!isTypeTest(obj)) - throw new Error(errorMessage || "Object isn't of expected type."); -} - -export type Constructor<T> = new (...args: any) => T; - -export function isClass<T>(cls: Constructor<T>, obj: unknown): obj is T { - return obj instanceof cls; -} -export function isClassTest<T>(cls: Constructor<T>): ((obj: unknown) => obj is T) { - return (obj: unknown): obj is T => isClass(cls, obj); -} -export function asClass<T>(cls: Constructor<T>, obj: unknown, errorMessage?: string): T { - assertType(obj, isClassTest(cls), errorMessage); - return obj; -} - -export function isString(obj: unknown): obj is string { - return typeof obj === 'string'; -} - -export function isObject(obj: unknown): obj is object { - return typeof obj === 'object' && obj !== null; -} - -export function isRecord<K extends PropertyKey, T>(typeTest: IsType<T>, obj: unknown): obj is Record<K, T> { - return isObject(obj) && Object.values(obj).every(typeTest); -} - -export function isError(obj: unknown): obj is Error { - return obj instanceof Error; -} - -export function assertNever(_obj: never, errorMessage?: string): never { - throw new Error(errorMessage || "Shouldn't reach here."); -} - -export type EnumType<T extends PropertyKey> = { readonly [P in T]: P }; -export type Typed<T extends PropertyKey> = { _type: T }; \ No newline at end of file diff --git a/vscode/src/utils.ts b/vscode/src/utils.ts index 08adbc4..6843678 100644 --- a/vscode/src/utils.ts +++ b/vscode/src/utils.ts @@ -279,3 +279,10 @@ export const calculateChecksum = async (filePath: string, algorithm: string = 's } export const appendPrefixToCommand = (command: string) => `${extConstants.COMMAND_PREFIX}.${command}`; + +export function isString(obj: unknown): obj is string { + return typeof obj === 'string'; +} +export function isError(obj: unknown): obj is Error { + return obj instanceof Error; +} \ No newline at end of file From c82fb8617da0de171f3e5e913a3dd80c67d45611 Mon Sep 17 00:00:00 2001 From: Achal Talati <achal.talati@oracle.com> Date: Thu, 10 Oct 2024 13:50:31 +0530 Subject: [PATCH 13/17] Refactored views, moved listeners to appropriate directories and removed unused imports --- vscode/src/commands/commands.ts | 5 +- vscode/src/commands/register.ts | 4 +- vscode/src/commands/runConfiguration.ts | 31 ++ vscode/src/configurations/listener.ts | 40 ++ vscode/src/debugger/debugger.ts | 110 +++-- vscode/src/extension.ts | 33 +- vscode/src/extensionInfo.ts | 11 +- vscode/src/lsp/initializer.ts | 53 +-- vscode/src/lsp/listeners/requests/handlers.ts | 14 +- vscode/src/lsp/nbLanguageClient.ts | 2 +- vscode/src/lsp/protocol.ts | 3 +- vscode/src/runConfiguration.ts | 234 ----------- .../src/test/suite/general/explorer.test.ts | 2 +- .../src/test/suite/general/extension.test.ts | 2 +- vscode/src/utils.ts | 20 +- .../TestViewController.ts} | 37 +- vscode/src/views/initializer.ts | 74 ++++ vscode/src/{ => views}/listener.ts | 28 +- vscode/src/{explorer.ts => views/projects.ts} | 383 +++++++++--------- vscode/src/views/runConfiguration.ts | 167 ++++++++ 20 files changed, 674 insertions(+), 579 deletions(-) create mode 100644 vscode/src/commands/runConfiguration.ts create mode 100644 vscode/src/configurations/listener.ts delete mode 100644 vscode/src/runConfiguration.ts rename vscode/src/{testAdapter.ts => views/TestViewController.ts} (92%) create mode 100644 vscode/src/views/initializer.ts rename vscode/src/{ => views}/listener.ts (61%) rename vscode/src/{explorer.ts => views/projects.ts} (67%) create mode 100644 vscode/src/views/runConfiguration.ts diff --git a/vscode/src/commands/commands.ts b/vscode/src/commands/commands.ts index cd5fbe6..85a90bb 100644 --- a/vscode/src/commands/commands.ts +++ b/vscode/src/commands/commands.ts @@ -18,7 +18,7 @@ import { appendPrefixToCommand } from "../utils"; export const extCommands = { configureRunSettings: appendPrefixToCommand('workspace.configureRunSettings'), - newFromTemplate : appendPrefixToCommand('workspace.new'), + newFromTemplate: appendPrefixToCommand('workspace.new'), newProject: appendPrefixToCommand('workspace.newproject'), openTest: appendPrefixToCommand('open.test'), deleteCache: appendPrefixToCommand('delete.cache'), @@ -47,6 +47,8 @@ export const extCommands = { nbEventListener: appendPrefixToCommand('addEventListener'), selectEditorProjs: appendPrefixToCommand('select.editor.projects'), attachDebugger: appendPrefixToCommand("java.attachDebugger.connector"), + loadWorkspaceTests: appendPrefixToCommand("load.workspace.tests"), + projectDeleteEntry: "javals.foundProjects.deleteEntry" } export const builtInCommands = { @@ -59,6 +61,7 @@ export const builtInCommands = { quickAccess: 'workbench.action.quickOpen', openSettings: 'workbench.action.openSettings', startDebug: 'workbench.action.debug.start', + focusReplDebug: 'workbench.debug.action.focusRepl', } export const nbCommands = { diff --git a/vscode/src/commands/register.ts b/vscode/src/commands/register.ts index 22b4dd5..7e2e5c3 100644 --- a/vscode/src/commands/register.ts +++ b/vscode/src/commands/register.ts @@ -23,6 +23,7 @@ import { registerBuildOperationCommands } from "./buildOperations"; import { registerRefactorCommands } from "./refactor"; import { registerUtilCommands } from "./utilCommands"; import { registerDebugCommands } from "./debug"; +import { registerRunConfigurationCommands } from "./runConfiguration"; type ICommandModules = Record<string, ICommand[]>; @@ -34,7 +35,8 @@ const commandModules: ICommandModules = { buildOperations: registerBuildOperationCommands, refactor: registerRefactorCommands, util: registerUtilCommands, - debug: registerDebugCommands + debug: registerDebugCommands, + runConfiguration: registerRunConfigurationCommands } export const subscribeCommands = (context: ExtensionContext) => { diff --git a/vscode/src/commands/runConfiguration.ts b/vscode/src/commands/runConfiguration.ts new file mode 100644 index 0000000..0d78fe6 --- /dev/null +++ b/vscode/src/commands/runConfiguration.ts @@ -0,0 +1,31 @@ +/* + Copyright (c) 2023-2024, Oracle and/or its affiliates. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +import { extCommands } from "./commands"; +import { globalVars } from "../extension"; +import { configureRunSettings } from "../views/runConfiguration"; +import { ICommand } from "./types"; + + +const configureRunSettingsHandler = (...params: any[]) => { + configureRunSettings(globalVars.extensionInfo.getExtensionContext(), params); +} + + +export const registerRunConfigurationCommands: ICommand[] = [{ + command: extCommands.configureRunSettings, + handler: configureRunSettingsHandler +}] \ No newline at end of file diff --git a/vscode/src/configurations/listener.ts b/vscode/src/configurations/listener.ts new file mode 100644 index 0000000..646c189 --- /dev/null +++ b/vscode/src/configurations/listener.ts @@ -0,0 +1,40 @@ +/* + Copyright (c) 2023-2024, Oracle and/or its affiliates. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +import { ConfigurationChangeEvent, ExtensionContext, workspace } from "vscode"; +import { globalVars } from "../extension"; +import { userConfigsListened } from "./configuration"; +import { Disposable } from "vscode-languageclient"; + +const configChangeHandler = (params: ConfigurationChangeEvent) => { + userConfigsListened.forEach((config: string) => { + const doesAffect = params.affectsConfiguration(config); + if (doesAffect) { + globalVars.clientPromise.restartExtension(globalVars.nbProcessManager, true); + } + }); +} + +const configChangeListener = workspace.onDidChangeConfiguration(configChangeHandler); + + +const listeners: Disposable[] = [configChangeListener]; + +export const registerConfigChangeListeners = (context: ExtensionContext) => { + listeners.forEach((listener: Disposable)=>{ + context.subscriptions.push(listener); + }); +} \ No newline at end of file diff --git a/vscode/src/debugger/debugger.ts b/vscode/src/debugger/debugger.ts index 065cea0..ae792a2 100644 --- a/vscode/src/debugger/debugger.ts +++ b/vscode/src/debugger/debugger.ts @@ -24,9 +24,11 @@ import { l10n } from '../localiser'; import { StreamDebugAdapter } from './streamDebugAdapter'; import { globalVars } from '../extension'; import { extCommands, nbCommands } from '../commands/commands'; +import { argumentsNode, environmentVariablesNode, vmOptionsNode, workingDirectoryNode } from '../views/runConfiguration'; +import { initializeRunConfiguration } from '../utils'; export function registerDebugger(context: ExtensionContext): void { - let debugTrackerFactory =new NetBeansDebugAdapterTrackerFactory(); + let debugTrackerFactory = new NetBeansDebugAdapterTrackerFactory(); context.subscriptions.push(vscode.debug.registerDebugAdapterTrackerFactory(extConstants.COMMAND_PREFIX, debugTrackerFactory)); let configInitialProvider = new NetBeansConfigurationInitialProvider(); context.subscriptions.push(vscode.debug.registerDebugConfigurationProvider(extConstants.COMMAND_PREFIX, configInitialProvider, vscode.DebugConfigurationProviderTriggerKind.Initial)); @@ -36,7 +38,12 @@ export function registerDebugger(context: ExtensionContext): void { context.subscriptions.push(vscode.debug.registerDebugConfigurationProvider(extConstants.COMMAND_PREFIX, configResolver)); context.subscriptions.push(vscode.debug.onDidTerminateDebugSession(((session) => onDidTerminateSession(session)))); let debugDescriptionFactory = new NetBeansDebugAdapterDescriptionFactory(); - context.subscriptions.push(vscode.debug.registerDebugAdapterDescriptorFactory(extConstants.COMMAND_PREFIX, debugDescriptionFactory)); + context.subscriptions.push(vscode.debug.registerDebugAdapterDescriptorFactory(extConstants.COMMAND_PREFIX, debugDescriptionFactory)); + initializeRunConfiguration().then(initialized => { + if (initialized) { + context.subscriptions.push(vscode.debug.registerDebugConfigurationProvider(extConstants.COMMAND_PREFIX, new RunConfigurationProvider())); + } + }); }; class NetBeansDebugAdapterTrackerFactory implements vscode.DebugAdapterTrackerFactory { @@ -66,13 +73,13 @@ class NetBeansDebugAdapterDescriptionFactory implements vscode.DebugAdapterDescr } } else { // resolve(new vscode.DebugAdapterServer(debugPort)); - const socket = net.connect(globalVars.debugPort, "127.0.0.1", () => {}); - socket.on("connect", () => { - const adapter = new StreamDebugAdapter(); - socket.write(globalVars.debugHash ? globalVars.debugHash : ""); - adapter.connect(socket, socket); - resolve(new vscode.DebugAdapterInlineImplementation(adapter)); - }); + const socket = net.connect(globalVars.debugPort, "127.0.0.1", () => { }); + socket.on("connect", () => { + const adapter = new StreamDebugAdapter(); + socket.write(globalVars.debugHash ? globalVars.debugHash : ""); + adapter.connect(socket, socket); + resolve(new vscode.DebugAdapterInlineImplementation(adapter)); + }); } } fnc(); @@ -84,26 +91,26 @@ class NetBeansDebugAdapterDescriptionFactory implements vscode.DebugAdapterDescr class NetBeansConfigurationInitialProvider implements vscode.DebugConfigurationProvider { provideDebugConfigurations(folder: vscode.WorkspaceFolder | undefined, token?: vscode.CancellationToken): vscode.ProviderResult<vscode.DebugConfiguration[]> { - return this.doProvideDebugConfigurations(folder, token); + return this.doProvideDebugConfigurations(folder, token); } - async doProvideDebugConfigurations(folder: vscode.WorkspaceFolder | undefined, _token?: vscode.CancellationToken): Promise<vscode.DebugConfiguration[]> { - let c : LanguageClient = await globalVars.clientPromise.client; + async doProvideDebugConfigurations(folder: vscode.WorkspaceFolder | undefined, _token?: vscode.CancellationToken): Promise<vscode.DebugConfiguration[]> { + let c: LanguageClient = await globalVars.clientPromise.client; if (!folder) { return []; } - var u : vscode.Uri | undefined; + var u: vscode.Uri | undefined; if (folder && folder.uri) { u = folder.uri; } else { u = vscode.window.activeTextEditor?.document?.uri } - let result : vscode.DebugConfiguration[] = []; - const configNames : string[] | null | undefined = await vscode.commands.executeCommand(nbCommands.projectConfigurations, u?.toString()); + let result: vscode.DebugConfiguration[] = []; + const configNames: string[] | null | undefined = await vscode.commands.executeCommand(nbCommands.projectConfigurations, u?.toString()); if (configNames) { - let first : boolean = true; + let first: boolean = true; for (let cn of configNames) { - let cname : string; + let cname: string; if (first) { // ignore the default config, comes first. @@ -112,7 +119,7 @@ class NetBeansConfigurationInitialProvider implements vscode.DebugConfigurationP } else { cname = "Launch Java: " + cn; } - const debugConfig : vscode.DebugConfiguration = { + const debugConfig: vscode.DebugConfiguration = { name: cname, type: extConstants.COMMAND_PREFIX, request: "launch", @@ -135,19 +142,19 @@ class NetBeansConfigurationDynamicProvider implements vscode.DebugConfigurationP } provideDebugConfigurations(folder: vscode.WorkspaceFolder | undefined, token?: vscode.CancellationToken): vscode.ProviderResult<vscode.DebugConfiguration[]> { - return this.doProvideDebugConfigurations(folder, this.context, this.commandValues, token); + return this.doProvideDebugConfigurations(folder, this.context, this.commandValues, token); } - async doProvideDebugConfigurations(folder: vscode.WorkspaceFolder | undefined, context: ExtensionContext, commandValues: Map<string, string>, _token?: vscode.CancellationToken): Promise<vscode.DebugConfiguration[]> { - let c : LanguageClient = await globalVars.clientPromise.client; + async doProvideDebugConfigurations(folder: vscode.WorkspaceFolder | undefined, context: ExtensionContext, commandValues: Map<string, string>, _token?: vscode.CancellationToken): Promise<vscode.DebugConfiguration[]> { + let c: LanguageClient = await globalVars.clientPromise.client; if (!folder) { return []; } - let result : vscode.DebugConfiguration[] = []; - const attachConnectors : DebugConnector[] | null | undefined = await vscode.commands.executeCommand(extCommands.attachDebugger); + let result: vscode.DebugConfiguration[] = []; + const attachConnectors: DebugConnector[] | null | undefined = await vscode.commands.executeCommand(extCommands.attachDebugger); if (attachConnectors) { for (let ac of attachConnectors) { - const debugConfig : vscode.DebugConfiguration = { + const debugConfig: vscode.DebugConfiguration = { name: ac.name, type: ac.type, request: "attach", @@ -207,6 +214,61 @@ class NetBeansConfigurationResolver implements vscode.DebugConfigurationProvider } } +class RunConfigurationProvider implements vscode.DebugConfigurationProvider { + + resolveDebugConfiguration(_folder: vscode.WorkspaceFolder | undefined, config: vscode.DebugConfiguration, _token?: vscode.CancellationToken): vscode.ProviderResult<vscode.DebugConfiguration> { + return new Promise<vscode.DebugConfiguration>(resolve => { + resolve(config); + }); + } + + resolveDebugConfigurationWithSubstitutedVariables?(_folder: vscode.WorkspaceFolder | undefined, config: vscode.DebugConfiguration, _token?: vscode.CancellationToken): vscode.ProviderResult<vscode.DebugConfiguration> { + return new Promise<vscode.DebugConfiguration>(resolve => { + const args = argumentsNode.getValue(); + if (args) { + if (!config.args) { + config.args = args; + } else { + config.args = `${config.args} ${args}`; + } + } + + const vmArgs = vmOptionsNode.getValue(); + if (vmArgs) { + if (!config.vmArgs) { + config.vmArgs = vmArgs; + } else { + config.vmArgs = `${config.vmArgs} ${vmArgs}`; + } + } + + const env = environmentVariablesNode.getValue(); + if (env) { + const envs = env.split(','); + if (!config.env) { + config.env = {}; + } + for (let val of envs) { + val = val.trim(); + const div = val.indexOf('='); + if (div > 0) { // div === 0 means bad format (no ENV name) + config.env[val.substring(0, div)] = val.substring(div + 1, val.length); + } + } + } + + const cwd = workingDirectoryNode.getValue(); + if (cwd) { + config.cwd = cwd; + } + + resolve(config); + }); + } + +} + + function onDidTerminateSession(session: vscode.DebugSession): any { const config = session.configuration; if (config.env) { diff --git a/vscode/src/extension.ts b/vscode/src/extension.ts index e9b40d2..b63c22f 100644 --- a/vscode/src/extension.ts +++ b/vscode/src/extension.ts @@ -27,10 +27,9 @@ import { ExtensionContext, TextEditorDecorationType } from 'vscode'; import * as vscode from 'vscode'; -import { NbTestAdapter } from './testAdapter'; -import { SetTextEditorDecorationParams} from './lsp/protocol'; +import { NbTestAdapter } from './views/TestViewController'; +import { SetTextEditorDecorationParams } from './lsp/protocol'; import * as launchConfigurations from './launchConfigurations'; -import { initializeRunConfiguration, runConfigurationProvider, runConfigurationNodeProvider, configureRunSettings } from './runConfiguration'; import { extConstants } from './constants'; import { ExtensionInfo } from './extensionInfo'; import { ClientPromise } from './lsp/clientPromise'; @@ -39,14 +38,14 @@ import { NbProcessManager } from './lsp/nbProcessManager'; import { clientInit } from './lsp/initializer'; import { subscribeCommands } from './commands/register'; import { VSNetBeansAPI } from './lsp/types'; -import { registerListenersBeforeClientInit } from './listener'; import { registerDebugger } from './debugger/debugger'; import { registerConfigChangeListeners } from './configurations/listener'; import { registerFileProviders } from './lsp/listeners/textDocumentContentProvider'; +import { createViews } from './views/initializer'; export let LOGGER: ExtensionLogger; export namespace globalVars { - export const listeners = new Map<string, string[]>(); + export const listeners = new Map<string, string[]>(); export let extensionInfo: ExtensionInfo; export let clientPromise: ClientPromise; export let debugPort: number = -1; @@ -65,34 +64,22 @@ export function activate(context: ExtensionContext): VSNetBeansAPI { LOGGER = new ExtensionLogger(extConstants.SERVER_NAME); globalVars.clientPromise.initialize(); - registerListenersBeforeClientInit(); + registerConfigChangeListeners(context); clientInit(); - //register debugger: registerDebugger(context); - // initialize Run Configuration - initializeRunConfiguration().then(initialized => { - if (initialized) { - context.subscriptions.push(vscode.debug.registerDebugConfigurationProvider(extConstants.COMMAND_PREFIX, runConfigurationProvider)); - context.subscriptions.push(vscode.window.registerTreeDataProvider('run-config', runConfigurationNodeProvider)); - context.subscriptions.push(vscode.commands.registerCommand(extConstants.COMMAND_PREFIX + '.workspace.configureRunSettings', (...params: any[]) => { - configureRunSettings(context, params); - })); - vscode.commands.executeCommand('setContext', 'runConfigurationInitialized', true); - } - }); - - // register commands subscribeCommands(context); + createViews(context); registerFileProviders(context); - + launchConfigurations.updateLaunchConfig(); // register completions: launchConfigurations.registerCompletion(context); + return Object.freeze({ - version : extConstants.API_VERSION, - apiVersion : extConstants.API_VERSION + version: extConstants.API_VERSION, + apiVersion: extConstants.API_VERSION }); } diff --git a/vscode/src/extensionInfo.ts b/vscode/src/extensionInfo.ts index 39ba28d..65500f6 100644 --- a/vscode/src/extensionInfo.ts +++ b/vscode/src/extensionInfo.ts @@ -16,10 +16,11 @@ import { Disposable, ExtensionContext } from "vscode"; export class ExtensionInfo { - constructor(private context: ExtensionContext){} + constructor(private context: ExtensionContext) { } - getGlobalStorage = () => this.context.globalStorageUri; - getWorkspaceStorage = () => this.context.storageUri; - getExtensionStorageUri = () => this.context.extensionUri; - pushSubscription = (listener: Disposable) => this.context.subscriptions.push(listener); + getGlobalStorage = () => this.context.globalStorageUri; + getWorkspaceStorage = () => this.context.storageUri; + getExtensionStorageUri = () => this.context.extensionUri; + getExtensionContext = () => this.context; + pushSubscription = (listener: Disposable) => this.context.subscriptions.push(listener); } \ No newline at end of file diff --git a/vscode/src/lsp/initializer.ts b/vscode/src/lsp/initializer.ts index d9c25c4..7bffddb 100644 --- a/vscode/src/lsp/initializer.ts +++ b/vscode/src/lsp/initializer.ts @@ -20,17 +20,12 @@ import { configKeys } from "../configurations/configuration"; import { enableDisableModules } from "./utils"; import * as net from 'net'; import { ChildProcess } from "child_process"; -import { getConfigurationValue, isNbJavacDisabledHandler } from "../configurations/handlers"; +import { isNbJavacDisabledHandler } from "../configurations/handlers"; import { attachNbProcessListeners, launchNbcode } from "./nbcode"; import { NbLanguageClient } from "./nbLanguageClient"; -import { NbTestAdapter } from "../testAdapter"; -import { registerListenersAfterClientInit } from "../listener"; +import { registerListenersAfterClientInit } from "../views/listener"; import { registerNotificationListeners } from "./listeners/notifications/register"; import { registerRequestListeners } from "./listeners/requests/register"; -import { TreeViewService, Visualizer } from "../explorer"; -import { commands, TextEditor, TreeView, window, workspace } from "vscode"; -import { extConstants } from "../constants"; -import { extCommands } from "../commands/commands"; const establishConnection = () => new Promise<StreamInfo>((resolve, reject) => { const nbProcess = globalVars.nbProcessManager?.getProcess(); @@ -108,51 +103,17 @@ export const clientInit = () => { globalVars.deactivated = false; const connection: () => Promise<StreamInfo> = serverBuilder(); const client = NbLanguageClient.build(connection, LOGGER); - + LOGGER.log('Language Client: Starting'); client.start().then(() => { - globalVars.testAdapter = new NbTestAdapter(); - + + registerListenersAfterClientInit(); registerNotificationListeners(client); registerRequestListeners(client); - + LOGGER.log('Language Client: Ready'); globalVars.clientPromise.initializedSuccessfully(client); - - createProjectView(client); + }).catch(globalVars.clientPromise.setClient[1]); } - - -async function createProjectView(client : NbLanguageClient) { - const ts : TreeViewService = client.findTreeViewService(); - let tv : TreeView<Visualizer> = await ts.createView('foundProjects', 'Projects', { canSelectMany : false }); - - async function revealActiveEditor(ed? : TextEditor) { - const uri = window.activeTextEditor?.document?.uri; - if (!uri || uri.scheme.toLowerCase() !== 'file') { - return; - } - if (!tv.visible) { - return; - } - let vis : Visualizer | undefined = await ts.findPath(tv, uri.toString()); - if (!vis) { - return; - } - tv.reveal(vis, { select : true, focus : false, expand : false }); - } - const netbeansConfig = workspace.getConfiguration(extConstants.COMMAND_PREFIX); - globalVars.extensionInfo.pushSubscription(window.onDidChangeActiveTextEditor(ed => { - if (netbeansConfig.get("revealActiveInProjects")) { - revealActiveEditor(ed); - } - })); - globalVars.extensionInfo.pushSubscription(commands.registerCommand(extCommands.selectEditorProjs, () => revealActiveEditor())); - - // attempt to reveal NOW: - if (getConfigurationValue(configKeys.revealInActivteProj)) { - revealActiveEditor(); - } -} \ No newline at end of file diff --git a/vscode/src/lsp/listeners/requests/handlers.ts b/vscode/src/lsp/listeners/requests/handlers.ts index 71bc544..9013880 100644 --- a/vscode/src/lsp/listeners/requests/handlers.ts +++ b/vscode/src/lsp/listeners/requests/handlers.ts @@ -18,7 +18,7 @@ import { globalVars, LOGGER } from "../../../extension"; import { notificationOrRequestListenerType } from "../../types"; import { ExecInHtmlPageRequest, HtmlPageRequest, InputBoxRequest, InputBoxStep, MutliStepInputRequest, QuickPickRequest, QuickPickStep, SaveDocumentRequestParams, SaveDocumentsRequest, TextEditorDecorationCreateRequest, UpdateConfigurationRequest } from "../../protocol"; import { InputStep, MultiStepInput } from "../../../utils"; -import { runConfigurationUpdateAll } from "../../../runConfiguration"; +import { runConfigurationUpdateAll } from "../../../views/runConfiguration"; import { isError } from "../../../utils"; import { isString } from "../../../utils"; import { LogLevel } from "../../../logger"; @@ -31,7 +31,7 @@ const textEditorDecorationCreateRequestHandler = (param: any) => { } const multiStepInputRequestHandler = async (param: any) => { - const client = await globalVars.clientPromise.client; + const client = await globalVars.clientPromise.client; const data: { [name: string]: readonly QuickPickItem[] | string } = {}; async function nextStep(input: MultiStepInput, step: number, state: { [name: string]: readonly QuickPickItem[] | string }): Promise<InputStep | void> { const inputStep = await client.sendRequest(MutliStepInputRequest.step, { inputId: param.id, step, data: state }); @@ -69,11 +69,11 @@ const multiStepInputRequestHandler = async (param: any) => { return data; } -const inputBoxRequestHandler = async (param: any) =>{ +const inputBoxRequestHandler = async (param: any) => { return await window.showInputBox({ title: param.title, prompt: param.prompt, value: param.value, password: param.password }); } -const saveDocumentRequestHandler = async (request : SaveDocumentRequestParams) => { +const saveDocumentRequestHandler = async (request: SaveDocumentRequestParams) => { const uriList = request.documents.map(s => { let re = /^file:\/(?:\/\/)?([A-Za-z]):\/(.*)$/.exec(s); if (!re) { @@ -114,11 +114,11 @@ const quickPickRequestHandler = async (param: any) => { return selected ? Array.isArray(selected) ? selected : [selected] : undefined; } - -export const requestListeners : notificationOrRequestListenerType[] = [{ + +export const requestListeners: notificationOrRequestListenerType[] = [{ type: TextEditorDecorationCreateRequest.type, handler: textEditorDecorationCreateRequestHandler -},{ +}, { type: MutliStepInputRequest.type, handler: multiStepInputRequestHandler }, { diff --git a/vscode/src/lsp/nbLanguageClient.ts b/vscode/src/lsp/nbLanguageClient.ts index ee611a6..fa94c13 100644 --- a/vscode/src/lsp/nbLanguageClient.ts +++ b/vscode/src/lsp/nbLanguageClient.ts @@ -15,7 +15,7 @@ */ import { LanguageClient, LanguageClientOptions, ServerOptions } from 'vscode-languageclient/node'; import { CloseAction, CloseHandlerResult, DocumentSelector, ErrorAction, ErrorHandlerResult, Message, RevealOutputChannelOn } from "vscode-languageclient"; -import { createTreeViewService, TreeViewService } from "../explorer"; +import { createTreeViewService, TreeViewService } from "../views/projects"; import { OutputChannel, workspace } from "vscode"; import { extConstants } from "../constants"; import { userConfigsListenedByServer } from '../configurations/configuration'; diff --git a/vscode/src/lsp/protocol.ts b/vscode/src/lsp/protocol.ts index 2a83258..fc3dd60 100644 --- a/vscode/src/lsp/protocol.ts +++ b/vscode/src/lsp/protocol.ts @@ -23,8 +23,7 @@ import * as vscode from 'vscode'; import { ProtocolNotificationType, ProtocolRequestType, - ShowMessageParams, - NotificationType + ShowMessageParams } from 'vscode-languageclient'; import { diff --git a/vscode/src/runConfiguration.ts b/vscode/src/runConfiguration.ts deleted file mode 100644 index 7b0ce68..0000000 --- a/vscode/src/runConfiguration.ts +++ /dev/null @@ -1,234 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -/* This file has been modified for Oracle Java SE extension */ - -import * as vscode from 'vscode'; -import { homedir } from 'os'; -import { l10n } from './localiser'; - -export async function initializeRunConfiguration(): Promise<boolean> { - if (vscode.workspace.name || vscode.workspace.workspaceFile) { - const java = await vscode.workspace.findFiles('**/*.java', '**/node_modules/**', 1); - if (java?.length > 0) { - return true; - } - } else { - for (let doc of vscode.workspace.textDocuments) { - if (doc.fileName?.endsWith(".java")) { - return true; - } - } - } - return false; -} - -class RunConfigurationProvider implements vscode.DebugConfigurationProvider { - - resolveDebugConfiguration(_folder: vscode.WorkspaceFolder | undefined, config: vscode.DebugConfiguration, _token?: vscode.CancellationToken): vscode.ProviderResult<vscode.DebugConfiguration> { - return new Promise<vscode.DebugConfiguration>(resolve => { - resolve(config); - }); - } - - resolveDebugConfigurationWithSubstitutedVariables?(_folder: vscode.WorkspaceFolder | undefined, config: vscode.DebugConfiguration, _token?: vscode.CancellationToken): vscode.ProviderResult<vscode.DebugConfiguration> { - return new Promise<vscode.DebugConfiguration>(resolve => { - const args = argumentsNode.getValue(); - if (args) { - if (!config.args) { - config.args = args; - } else { - config.args = `${config.args} ${args}`; - } - } - - const vmArgs = vmOptionsNode.getValue(); - if (vmArgs) { - if (!config.vmArgs) { - config.vmArgs = vmArgs; - } else { - config.vmArgs = `${config.vmArgs} ${vmArgs}`; - } - } - - const env = environmentVariablesNode.getValue(); - if (env) { - const envs = env.split(','); - if (!config.env) { - config.env = {}; - } - for (let val of envs) { - val = val.trim(); - const div = val.indexOf('='); - if (div > 0) { // div === 0 means bad format (no ENV name) - config.env[val.substring(0, div)] = val.substring(div + 1, val.length); - } - } - } - - const cwd = workingDirectoryNode.getValue(); - if (cwd) { - config.cwd = cwd; - } - - resolve(config); - }); - } - -} -export const runConfigurationProvider = new RunConfigurationProvider(); - -class RunConfigurationNodeProvider implements vscode.TreeDataProvider<vscode.TreeItem> { - - private _onDidChangeTreeData: vscode.EventEmitter<vscode.TreeItem | undefined | null> = new vscode.EventEmitter<vscode.TreeItem | undefined | null>(); - readonly onDidChangeTreeData: vscode.Event<vscode.TreeItem | undefined | null> = this._onDidChangeTreeData.event; - - refresh(): void { - this._onDidChangeTreeData.fire(undefined); - } - - getTreeItem(element: vscode.TreeItem): vscode.TreeItem { - return element; - } - - getChildren(element?: vscode.TreeItem): vscode.ProviderResult<vscode.TreeItem[]> { - if (!element) { - return [ argumentsNode, vmOptionsNode, environmentVariablesNode, workingDirectoryNode ]; - } - return []; - } - -} -export const runConfigurationNodeProvider = new RunConfigurationNodeProvider(); - -class RunConfigurationNode extends vscode.TreeItem { - - prompt: string; - hint: string; - value: string | undefined; - - settingsKey: string; - - constructor(label: string, prompt: string, hint: string, settingsKey: string) { - super(label); - this.contextValue = 'configureRunSettings'; - this.collapsibleState = vscode.TreeItemCollapsibleState.None; - - this.prompt = prompt; - this.hint = hint; - - this.settingsKey = settingsKey; - - this.value = this.getConfig().get(this.settingsKey); - this.updateNode(); - } - - public configure(_context: vscode.ExtensionContext) { - vscode.window.showInputBox( - { - prompt: this.prompt, - value: this.value, - placeHolder: this.hint, - ignoreFocusOut: true - } - ).then(async val => { - if (val !== undefined) { - const value = val.toString().trim(); - this.setValue(value ? value : undefined); - } - }); - } - - public getValue(): string | undefined { - return this.value; - } - - setValue(value: string | undefined) { - this.value = value; - this.getConfig().update(this.settingsKey, this.value, vscode.workspace.name || vscode.workspace.workspaceFile ? null : true); - this.updateNode(); - } - - updateNode(reload?: boolean) { - if (reload) { - this.value = this.getConfig().get(this.settingsKey) as string; - } - this.description = this.value ? this.value : l10n.value("jdk.extension.runConfig.default.label"); - this.tooltip = `${this.label} ${this.description}`; - runConfigurationNodeProvider.refresh(); - } - - getConfig(): vscode.WorkspaceConfiguration { - return vscode.workspace.getConfiguration('jdk.runConfig'); - } - -} - -class ArgumentsNode extends RunConfigurationNode { - - constructor() { - super(l10n.value("jdk.extension.runConfig.arguments.label"), l10n.value("jdk.extension.runConfig.arguments.prompt"), l10n.value("jdk.extension.runConfig.example.label",{data:"foo bar"}), 'arguments'); - } - -} -const argumentsNode = new ArgumentsNode(); - -class VMOptionsNode extends RunConfigurationNode { - - constructor() { - super(l10n.value("jdk.extension.runConfig.vmoptions.label"), l10n.value("jdk.extension.runConfig.vmoptions.prompt"), l10n.value("jdk.extension.runConfig.example.label",{data:"-Xmx512m -Xms256m"}), 'vmOptions'); - } - -} -const vmOptionsNode = new VMOptionsNode(); - -class EnvironmentVariablesNode extends RunConfigurationNode { - - constructor() { - super(l10n.value("jdk.extension.runConfig.env.label"), l10n.value("jdk.extension.runConfig.env.prompt"), l10n.value("jdk.extension.runConfig.example.label",{data:"var1=one, varTwo=2"}), 'env'); - } - -} -const environmentVariablesNode = new EnvironmentVariablesNode(); - -class WorkingDirectoryNode extends RunConfigurationNode { - - constructor() { - super(l10n.value("jdk.extension.runConfig.wrkdir.label"), l10n.value("jdk.extension.runConfig.wrkdir.prompt"), WorkingDirectoryNode.getExample(), 'cwd'); - } - - static getExample(): string { - const dir = homedir(); - return l10n.value("jdk.extension.runConfig.example.label",{data:dir}); - } - -} -const workingDirectoryNode = new WorkingDirectoryNode(); - -export function configureRunSettings(context: vscode.ExtensionContext, ...params: any[]) { - if (params[0][0]) { - (params[0][0] as RunConfigurationNode).configure(context); - } -} -export function runConfigurationUpdateAll() { - argumentsNode.updateNode(true); - vmOptionsNode.updateNode(true); - environmentVariablesNode.updateNode(true); - workingDirectoryNode.updateNode(true); -} diff --git a/vscode/src/test/suite/general/explorer.test.ts b/vscode/src/test/suite/general/explorer.test.ts index 1e844ac..0e4afba 100644 --- a/vscode/src/test/suite/general/explorer.test.ts +++ b/vscode/src/test/suite/general/explorer.test.ts @@ -26,7 +26,7 @@ import * as assert from 'assert'; // as well as import your extension to test it import * as vscode from 'vscode'; import { awaitClient } from '../../testutils'; -import * as myExplorer from '../../../explorer'; +import * as myExplorer from '../../../views/projects'; suite('Explorer Test Suite', () => { vscode.window.showInformationMessage('Start explorer tests.'); diff --git a/vscode/src/test/suite/general/extension.test.ts b/vscode/src/test/suite/general/extension.test.ts index 03370a6..098d54b 100644 --- a/vscode/src/test/suite/general/extension.test.ts +++ b/vscode/src/test/suite/general/extension.test.ts @@ -26,7 +26,7 @@ import * as assert from 'assert'; import * as fs from 'fs'; import * as path from 'path'; import * as vscode from 'vscode'; -import * as myExplorer from '../../../explorer'; +import * as myExplorer from '../../../views/projects'; import { CodeAction, commands, extensions, Selection, Uri, window, workspace, TreeItem } from 'vscode'; import { assertWorkspace, awaitClient, dumpJava, findClusters, getFilePaths, openFile, prepareProject, replaceCode } from '../../testutils'; import { FORMATTED_POM_XML, SAMPLE_CODE_FORMAT_DOCUMENT, SAMPLE_CODE_SORT_IMPORTS, SAMPLE_CODE_UNUSED_IMPORTS } from '../../constants'; diff --git a/vscode/src/utils.ts b/vscode/src/utils.ts index 6843678..77d0719 100644 --- a/vscode/src/utils.ts +++ b/vscode/src/utils.ts @@ -281,8 +281,24 @@ export const calculateChecksum = async (filePath: string, algorithm: string = 's export const appendPrefixToCommand = (command: string) => `${extConstants.COMMAND_PREFIX}.${command}`; export function isString(obj: unknown): obj is string { - return typeof obj === 'string'; + return typeof obj === 'string'; } export function isError(obj: unknown): obj is Error { - return obj instanceof Error; + return obj instanceof Error; +} + +export async function initializeRunConfiguration(): Promise<boolean> { + if (vscode.workspace.name || vscode.workspace.workspaceFile) { + const java = await vscode.workspace.findFiles('**/*.java', '**/node_modules/**', 1); + if (java?.length > 0) { + return true; + } + } else { + for (let doc of vscode.workspace.textDocuments) { + if (doc.fileName?.endsWith(".java")) { + return true; + } + } + } + return false; } \ No newline at end of file diff --git a/vscode/src/testAdapter.ts b/vscode/src/views/TestViewController.ts similarity index 92% rename from vscode/src/testAdapter.ts rename to vscode/src/views/TestViewController.ts index fb93fc1..70fe93f 100644 --- a/vscode/src/testAdapter.ts +++ b/vscode/src/views/TestViewController.ts @@ -23,13 +23,14 @@ import { commands, debug, tests, workspace, CancellationToken, TestController, TestItem, TestRunProfileKind, TestRunRequest, Uri, TestRun, TestMessage, Location, Position, MarkdownString } from "vscode"; import * as path from 'path'; -import { asRange, TestCase, TestSuite } from "./lsp/protocol"; -import { extConstants } from "./constants"; +import { asRange, TestCase, TestSuite } from "../lsp/protocol"; +import { extConstants } from "../constants"; +import { extCommands, builtInCommands } from "../commands/commands" export class NbTestAdapter { private readonly testController: TestController; - private disposables: { dispose(): void }[] = []; + private disposables: { dispose(): void }[] = []; private currentRun: TestRun | undefined; private itemsToRun: Set<TestItem> | undefined; private started: boolean = false; @@ -45,7 +46,7 @@ export class NbTestAdapter { async load(): Promise<void> { for (let workspaceFolder of workspace.workspaceFolders || []) { - const loadedTests: any = await commands.executeCommand(extConstants.COMMAND_PREFIX + '.load.workspace.tests', workspaceFolder.uri.toString()); + const loadedTests: any = await commands.executeCommand(extCommands.loadWorkspaceTests, workspaceFolder.uri.toString()); if (loadedTests) { loadedTests.forEach((suite: TestSuite) => { this.updateTests(suite); @@ -56,7 +57,7 @@ export class NbTestAdapter { async run(request: TestRunRequest, cancellation: CancellationToken): Promise<void> { if (!this.currentRun) { - commands.executeCommand('workbench.debug.action.focusRepl'); + commands.executeCommand(builtInCommands.focusReplDebug); cancellation.onCancellationRequested(() => this.cancel()); this.currentRun = this.testController.createTestRun(request); this.itemsToRun = new Set(); @@ -68,7 +69,7 @@ export class NbTestAdapter { this.set(item, 'enqueued'); const idx = item.id.indexOf(':'); if (!cancellation.isCancellationRequested) { - await commands.executeCommand(request.profile?.kind === TestRunProfileKind.Debug ? extConstants.COMMAND_PREFIX + '.debug.single' : extConstants.COMMAND_PREFIX + '.run.single', item.uri.toString(), idx < 0 ? undefined : item.id.slice(idx + 1)); + await commands.executeCommand(request.profile?.kind === TestRunProfileKind.Debug ? extCommands.debugSingle : extCommands.runSingle, item.uri.toString(), idx < 0 ? undefined : item.id.slice(idx + 1)); } } } @@ -76,7 +77,7 @@ export class NbTestAdapter { this.testController.items.forEach(item => this.set(item, 'enqueued')); for (let workspaceFolder of workspace.workspaceFolders || []) { if (!cancellation.isCancellationRequested) { - await commands.executeCommand(request.profile?.kind === TestRunProfileKind.Debug ? extConstants.COMMAND_PREFIX + '.debug.test': extConstants.COMMAND_PREFIX + '.run.test', workspaceFolder.uri.toString()); + await commands.executeCommand(request.profile?.kind === TestRunProfileKind.Debug ? extCommands.debugTest : extCommands.runTest, workspaceFolder.uri.toString()); } } } @@ -89,7 +90,7 @@ export class NbTestAdapter { } } - set(item: TestItem, state: 'enqueued' | 'started' | 'passed' | 'failed' | 'skipped' | 'errored', message?: TestMessage | readonly TestMessage[], noPassDown? : boolean): void { + set(item: TestItem, state: 'enqueued' | 'started' | 'passed' | 'failed' | 'skipped' | 'errored', message?: TestMessage | readonly TestMessage[], noPassDown?: boolean): void { if (this.currentRun) { switch (state) { case 'enqueued': @@ -119,12 +120,12 @@ export class NbTestAdapter { } dispose(): void { - this.cancel(); - for (const disposable of this.disposables) { - disposable.dispose(); - } - this.disposables = []; - } + this.cancel(); + for (const disposable of this.disposables) { + disposable.dispose(); + } + this.disposables = []; + } testOutput(output: string): void { if (this.currentRun && output) { @@ -287,15 +288,15 @@ export class NbTestAdapter { return undefined; } - selectParent(parents: Map<TestItem, string>): {test: TestItem, label: string} | undefined { - let ret: {test: TestItem, label: string} | undefined = undefined; + selectParent(parents: Map<TestItem, string>): { test: TestItem, label: string } | undefined { + let ret: { test: TestItem, label: string } | undefined = undefined; parents.forEach((label, parentTest) => { if (ret) { if (parentTest.id.replace(/#\w*/g, '').length > ret.test.id.replace(/#\w*/g, '').length) { - ret = {test: parentTest, label}; + ret = { test: parentTest, label }; } } else { - ret = {test: parentTest, label}; + ret = { test: parentTest, label }; } }); return ret; diff --git a/vscode/src/views/initializer.ts b/vscode/src/views/initializer.ts new file mode 100644 index 0000000..f068f5b --- /dev/null +++ b/vscode/src/views/initializer.ts @@ -0,0 +1,74 @@ +/* + Copyright (c) 2023-2024, Oracle and/or its affiliates. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ +import { ExtensionContext, window, commands, TreeView, TextEditor, workspace } from "vscode"; +import { globalVars } from "../extension"; +import { runConfigurationNodeProvider } from "./runConfiguration"; +import { NbLanguageClient } from "../lsp/nbLanguageClient"; +import { TreeViewService, Visualizer } from "./projects"; +import { extConstants } from "../constants"; +import { builtInCommands, extCommands } from "../commands/commands"; +import { getConfigurationValue } from "../configurations/handlers"; +import { configKeys } from "../configurations/configuration"; +import { initializeRunConfiguration } from "../utils"; +import { NbTestAdapter } from "./TestViewController"; + +export async function createViews(context: ExtensionContext) { + createRunConfigurationView(context); + const client = await globalVars.clientPromise.client; + createProjectView(client); + globalVars.testAdapter = new NbTestAdapter(); +} +function createRunConfigurationView(context: ExtensionContext) { + initializeRunConfiguration().then(initialized => { + if (initialized) { + context.subscriptions.push(window.registerTreeDataProvider('run-config', runConfigurationNodeProvider)); + commands.executeCommand(builtInCommands.setCustomContext, 'runConfigurationInitialized', true); + } + }); +} + + +async function createProjectView(client: NbLanguageClient) { + const ts: TreeViewService = client.findTreeViewService(); + let tv: TreeView<Visualizer> = await ts.createView('foundProjects', 'Projects', { canSelectMany: false }); + + async function revealActiveEditor(ed?: TextEditor) { + const uri = window.activeTextEditor?.document?.uri; + if (!uri || uri.scheme.toLowerCase() !== 'file') { + return; + } + if (!tv.visible) { + return; + } + let vis: Visualizer | undefined = await ts.findPath(tv, uri.toString()); + if (!vis) { + return; + } + tv.reveal(vis, { select: true, focus: false, expand: false }); + } + const netbeansConfig = workspace.getConfiguration(extConstants.COMMAND_PREFIX); + globalVars.extensionInfo.pushSubscription(window.onDidChangeActiveTextEditor(ed => { + if (netbeansConfig.get("revealActiveInProjects")) { + revealActiveEditor(ed); + } + })); + globalVars.extensionInfo.pushSubscription(commands.registerCommand(extCommands.selectEditorProjs, () => revealActiveEditor())); + + // attempt to reveal NOW: + if (getConfigurationValue(configKeys.revealInActivteProj)) { + revealActiveEditor(); + } +} \ No newline at end of file diff --git a/vscode/src/listener.ts b/vscode/src/views/listener.ts similarity index 61% rename from vscode/src/listener.ts rename to vscode/src/views/listener.ts index 4713376..ed2c761 100644 --- a/vscode/src/listener.ts +++ b/vscode/src/views/listener.ts @@ -13,19 +13,12 @@ See the License for the specific language governing permissions and limitations under the License. */ -import { ConfigurationChangeEvent, Disposable, TextEditor, window, workspace } from "vscode"; -import { userConfigsListened } from "./configurations/configuration"; -import { globalVars } from "./extension"; -import { asRanges } from "./lsp/protocol"; - -const configChangeHandler = (params: ConfigurationChangeEvent) => { - userConfigsListened.forEach((config: string) => { - const doesAffect = params.affectsConfiguration(config); - if (doesAffect) { - globalVars.clientPromise.restartExtension(globalVars.nbProcessManager, true); - } - }); -} + +import { Disposable, TextEditor, window } from "vscode"; +import { globalVars } from "../extension"; +import { asRanges } from "../lsp/protocol"; + + const visibleTextEditorsChangeHandler = (editors: readonly TextEditor[]) => { editors.forEach((editor: any) => { @@ -39,19 +32,10 @@ const visibleTextEditorsChangeHandler = (editors: readonly TextEditor[]) => { }); } -const configChangeListener = workspace.onDidChangeConfiguration(configChangeHandler); const visibleTextEditorsChangeListener = window.onDidChangeVisibleTextEditors(visibleTextEditorsChangeHandler); -const beforeInitlisteners: Disposable[] = [configChangeListener]; const afterInitlisteners: Disposable[] = [visibleTextEditorsChangeListener]; - -export const registerListenersBeforeClientInit = () => { - beforeInitlisteners.forEach(listener => { - globalVars.extensionInfo.pushSubscription(listener); - }); -} - export const registerListenersAfterClientInit = () => { afterInitlisteners.forEach(listener => { globalVars.extensionInfo.pushSubscription(listener); diff --git a/vscode/src/explorer.ts b/vscode/src/views/projects.ts similarity index 67% rename from vscode/src/explorer.ts rename to vscode/src/views/projects.ts index eacb7fb..5f48453 100644 --- a/vscode/src/explorer.ts +++ b/vscode/src/views/projects.ts @@ -18,11 +18,12 @@ */ import * as vscode from 'vscode'; import { ThemeIcon } from 'vscode'; -import { LanguageClient } from 'vscode-languageclient/node'; -import { NbLanguageClient } from './lsp/nbLanguageClient'; -import { NodeChangedParams, NodeInfoNotification, NodeInfoRequest, GetResourceParams, NodeChangeType, NodeChangesParams } from './lsp/protocol'; -import { l10n } from './localiser'; -const doLog : boolean = false; +import { LanguageClient } from 'vscode-languageclient/node'; +import { NbLanguageClient } from '../lsp/nbLanguageClient'; +import { NodeChangedParams, NodeInfoNotification, NodeInfoRequest, GetResourceParams, NodeChangeType, NodeChangesParams } from '../lsp/protocol'; +import { l10n } from '../localiser'; +import { extCommands } from '../commands/commands'; +const doLog: boolean = false; const EmptyIcon = "EMPTY_ICON"; /** @@ -33,19 +34,19 @@ export interface TreeNodeListener { * Node has been destroyed. No more events will be delivered. * @param n the node */ - nodeDestroyed?(n : Visualizer) : void; + nodeDestroyed?(n: Visualizer): void; /** * Node itself (description, icon, ...) has been changed. * @param n the node */ - nodeChanged?(n : Visualizer) : void; + nodeChanged?(n: Visualizer): void; /** * Node's children changed. * @param n the node */ - nodeChildrenChanged?(n : Visualizer) : void; + nodeChildrenChanged?(n: Visualizer): void; /** * Informs that some properties of the node changed. If list of properties is undefined, then @@ -53,7 +54,7 @@ export interface TreeNodeListener { * @param n the node * @param properties list of changed properties or undefined. */ - nodePropertiesChanged?(n : Visualizer, properties?: String[]) : void; + nodePropertiesChanged?(n: Visualizer, properties?: String[]): void; } /** @@ -64,45 +65,44 @@ class CachedImage { /** * Base URI of the image, if available. */ - public baseUri? : vscode.Uri, + public baseUri?: vscode.Uri, /** * Icon URI as sent by the LSP server. Images translated to ThemeIcons have this field undefined. */ - public iconUri? : vscode.Uri, + public iconUri?: vscode.Uri, /** * Local resource or theme icon. */ - public icon? : string | ThemeIcon, + public icon?: string | ThemeIcon, /** * Additional matched values */ - public values? : string[], - ) {} + public values?: string[], + ) { } } class ViewInfo { constructor( - readonly treeView : vscode.TreeView<Visualizer>, - readonly visProvider : VisualizerProvider) - {} + readonly treeView: vscode.TreeView<Visualizer>, + readonly visProvider: VisualizerProvider) { } } -export class TreeViewService extends vscode.Disposable { - - private handler : vscode.Disposable | undefined; - private client : NbLanguageClient; - private trees : Map<string, ViewInfo> = new Map(); - private images : Map<number | vscode.Uri, CachedImage> = new Map(); - private providers : Map<number, VisualizerProvider> = new Map(); - log : vscode.OutputChannel; - private entries : ImageEntry[] = []; - - constructor (log : vscode.OutputChannel, c : NbLanguageClient, dd : vscode.Disposable[]) { - super(() => { - this.disposeAllViews(); +export class TreeViewService extends vscode.Disposable { + + private handler: vscode.Disposable | undefined; + private client: NbLanguageClient; + private trees: Map<string, ViewInfo> = new Map(); + private images: Map<number | vscode.Uri, CachedImage> = new Map(); + private providers: Map<number, VisualizerProvider> = new Map(); + log: vscode.OutputChannel; + private entries: ImageEntry[] = []; + + constructor(log: vscode.OutputChannel, c: NbLanguageClient, dd: vscode.Disposable[]) { + super(() => { + this.disposeAllViews(); for (const d of dd) { d?.dispose(); } @@ -114,11 +114,11 @@ export class TreeViewService extends vscode.Disposable { dd.push(vscode.extensions.onDidChange(() => this.refreshImages())); } - getClient() : NbLanguageClient { + getClient(): NbLanguageClient { return this.client; } - private disposeAllViews() : void { + private disposeAllViews(): void { for (let tree of this.trees.values()) { tree.visProvider.dispose(); tree.treeView.dispose(); @@ -128,23 +128,24 @@ export class TreeViewService extends vscode.Disposable { this.handler?.dispose(); } - public async createView(id : string, title? : string, options? : - Partial<vscode.TreeViewOptions<any> & { - providerInitializer : (provider : CustomizableTreeDataProvider<Visualizer>) => void } - >) : Promise<vscode.TreeView<Visualizer>> { - let tv : ViewInfo | undefined = this.trees.get(id); + public async createView(id: string, title?: string, options?: + Partial<vscode.TreeViewOptions<any> & { + providerInitializer: (provider: CustomizableTreeDataProvider<Visualizer>) => void + } + >): Promise<vscode.TreeView<Visualizer>> { + let tv: ViewInfo | undefined = this.trees.get(id); if (tv) { return tv.treeView; } const res = await createViewProvider(this.client, id); this.providers.set(res.getRoot().data.id, res); options?.providerInitializer?.(res) - let opts : vscode.TreeViewOptions<Visualizer> = { - treeDataProvider : res, + let opts: vscode.TreeViewOptions<Visualizer> = { + treeDataProvider: res, canSelectMany: true, showCollapseAll: true, } - + if (options?.canSelectMany !== undefined) { opts.canSelectMany = options.canSelectMany; } @@ -158,9 +159,9 @@ export class TreeViewService extends vscode.Disposable { return view; } - private listeners: Map<string, {types : NodeChangeType[], listener : TreeNodeListener}[]> = new Map(); + private listeners: Map<string, { types: NodeChangeType[], listener: TreeNodeListener }[]> = new Map(); - private removeListenerRegistration(key : string, data : {types : NodeChangeType[], listener : TreeNodeListener}) : void { + private removeListenerRegistration(key: string, data: { types: NodeChangeType[], listener: TreeNodeListener }): void { let a = this.listeners.get(key); if (!a) { return; @@ -172,10 +173,10 @@ export class TreeViewService extends vscode.Disposable { this.listeners.delete(key); } } -} + } - public addNodeChangeListener(node : Visualizer, listener : TreeNodeListener, ...types : NodeChangeType[]) : vscode.Disposable { - const listenerKey = node.rootId + ':' + (node.id || ''); + public addNodeChangeListener(node: Visualizer, listener: TreeNodeListener, ...types: NodeChangeType[]): vscode.Disposable { + const listenerKey = node.rootId + ':' + (node.id || ''); let a = this.listeners.get(listenerKey); if (a === undefined) { a = []; @@ -183,9 +184,9 @@ export class TreeViewService extends vscode.Disposable { } const data = { types, listener }; a.push(data); - + let success = false; - const r = this.client.sendRequest(NodeInfoRequest.changes, { rootId : node.rootId, nodeId: Number(node.id), types }); + const r = this.client.sendRequest(NodeInfoRequest.changes, { rootId: node.rootId, nodeId: Number(node.id), types }); r.catch(() => { // remove the listener registration this.removeListenerRegistration(listenerKey, data); @@ -196,8 +197,8 @@ export class TreeViewService extends vscode.Disposable { }); } - private nodeChanged(params : NodeChangedParams) : void { - let p : VisualizerProvider | undefined = this.providers.get(params.rootId); + private nodeChanged(params: NodeChangedParams): void { + let p: VisualizerProvider | undefined = this.providers.get(params.rootId); if (!p) { return; } @@ -238,8 +239,8 @@ export class TreeViewService extends vscode.Disposable { * @param nodeData * @returns icon specification or undefined */ - async fetchImageUri(nodeData : NodeInfoRequest.Data) : Promise<vscode.Uri | string | ThemeIcon | undefined> { - let res : vscode.Uri | string | ThemeIcon | undefined = this.imageUri(nodeData); + async fetchImageUri(nodeData: NodeInfoRequest.Data): Promise<vscode.Uri | string | ThemeIcon | undefined> { + let res: vscode.Uri | string | ThemeIcon | undefined = this.imageUri(nodeData); if (res) { return res; @@ -247,14 +248,14 @@ export class TreeViewService extends vscode.Disposable { if (!nodeData?.iconDescriptor) { return undefined; } - let ci : CachedImage | undefined; + let ci: CachedImage | undefined; ci = this.images.get(nodeData.iconDescriptor.baseUri); if (ci != null) { return ci?.iconUri; } - const p : GetResourceParams = { - acceptEncoding: [ 'base64' ], - uri : nodeData.iconDescriptor.baseUri + const p: GetResourceParams = { + acceptEncoding: ['base64'], + uri: nodeData.iconDescriptor.baseUri }; let iconData = await this.client.sendRequest(NodeInfoRequest.getresource, p); if (!iconData?.content) { @@ -266,27 +267,27 @@ export class TreeViewService extends vscode.Disposable { return ci.iconUri; } - imageUri(nodeData : NodeInfoRequest.Data) : vscode.Uri | string | ThemeIcon | undefined { + imageUri(nodeData: NodeInfoRequest.Data): vscode.Uri | string | ThemeIcon | undefined { if (nodeData.id < 0) { return undefined; } - let ci : CachedImage | undefined; + let ci: CachedImage | undefined; if (nodeData.iconDescriptor?.baseUri) { const r = this.findProductIcon(nodeData.iconDescriptor.baseUri, nodeData.name, nodeData.contextValue); // override the icon with local. if (r) { if (r === EmptyIcon) { - ci = new CachedImage(nodeData.iconDescriptor.baseUri, undefined, undefined, [ nodeData.name, nodeData.contextValue ]); + ci = new CachedImage(nodeData.iconDescriptor.baseUri, undefined, undefined, [nodeData.name, nodeData.contextValue]); } - ci = new CachedImage(nodeData.iconDescriptor.baseUri, undefined, r, [ nodeData.name, nodeData.contextValue ]); + ci = new CachedImage(nodeData.iconDescriptor.baseUri, undefined, r, [nodeData.name, nodeData.contextValue]); this.images.set(nodeData.iconIndex, ci); } } if (!ci) { // hardcode visual vscode's File icons for regular files: if (nodeData.resourceUri && nodeData.contextValue.includes('is:file')) { - const uri : vscode.Uri | undefined = nodeData.iconUri ? vscode.Uri.parse(nodeData.iconUri) : undefined; + const uri: vscode.Uri | undefined = nodeData.iconUri ? vscode.Uri.parse(nodeData.iconUri) : undefined; // do not cache return ThemeIcon.File; } @@ -294,16 +295,16 @@ export class TreeViewService extends vscode.Disposable { return ci?.icon ? ci.icon : ci?.iconUri; } - public setTranslations(entries : ImageEntry[]) { + public setTranslations(entries: ImageEntry[]) { this.entries = entries; } - public findProductIcon(res : vscode.Uri, ...values: string[]) : string | ThemeIcon | undefined { - const s : string = res.toString(); + public findProductIcon(res: vscode.Uri, ...values: string[]): string | ThemeIcon | undefined { + const s: string = res.toString(); outer: for (let e of this.entries) { if (e.uriRegexp.test(s)) { if (e.valueRegexps) { - let s : string = " " + values.join(" ") + " "; + let s: string = " " + values.join(" ") + " "; for (let vr of e.valueRegexps) { if (!vr.test(s)) { continue outer; @@ -325,7 +326,7 @@ export class TreeViewService extends vscode.Disposable { } else { resultIcon = new ThemeIcon(e.codeicon); } - + return resultIcon; } } @@ -333,7 +334,7 @@ export class TreeViewService extends vscode.Disposable { } public refreshImages() { - let newEntries : ImageEntry[] = []; + let newEntries: ImageEntry[] = []; for (const ext of vscode.extensions.all) { const iconMapping = ext.packageJSON?.contributes && ext.packageJSON?.contributes['netbeans.iconMapping']; if (Array.isArray(iconMapping)) { @@ -341,7 +342,7 @@ export class TreeViewService extends vscode.Disposable { const reString = m?.uriExpression; if (reString) { try { - let re : RegExp = new RegExp(reString); + let re: RegExp = new RegExp(reString); let vals = []; if (m?.valueMatch) { for (const vm of m.valueMatch) { @@ -360,8 +361,8 @@ export class TreeViewService extends vscode.Disposable { this.setTranslations(newEntries); } - public async findPath(tree : vscode.TreeView<Visualizer>, selectData : any) : Promise<Visualizer | undefined> { - let selected : ViewInfo | undefined; + public async findPath(tree: vscode.TreeView<Visualizer>, selectData: any): Promise<Visualizer | undefined> { + let selected: ViewInfo | undefined; for (let vinfo of this.trees.values()) { if (vinfo.treeView === tree) { @@ -377,28 +378,28 @@ export class TreeViewService extends vscode.Disposable { } export interface TreeItemDecorator<T> extends vscode.Disposable { - decorateTreeItem(element: T, item : vscode.TreeItem): vscode.TreeItem | Thenable<vscode.TreeItem>; + decorateTreeItem(element: T, item: vscode.TreeItem): vscode.TreeItem | Thenable<vscode.TreeItem>; decorateChildren(element: T, children: Visualizer[]): Visualizer[] | Thenable<Visualizer[]>; } export interface CustomizableTreeDataProvider<T> extends vscode.TreeDataProvider<T> { - fireItemChange(item? : T) : void; - addItemDecorator(deco : TreeItemDecorator<T>) : vscode.Disposable; - getRoot() : T; + fireItemChange(item?: T): void; + addItemDecorator(deco: TreeItemDecorator<T>): vscode.Disposable; + getRoot(): T; } class VisualizerProvider extends vscode.Disposable implements CustomizableTreeDataProvider<Visualizer> { private root: Visualizer; - private treeData : Map<number, Visualizer> = new Map(); - private decorators : TreeItemDecorator<Visualizer>[] = []; - + private treeData: Map<number, Visualizer> = new Map(); + private decorators: TreeItemDecorator<Visualizer>[] = []; + constructor( private client: LanguageClient, - private ts : TreeViewService, - private log : vscode.OutputChannel, - readonly id : string, - rootData : NodeInfoRequest.Data, - uri : vscode.Uri | string | ThemeIcon | undefined + private ts: TreeViewService, + private log: vscode.OutputChannel, + readonly id: string, + rootData: NodeInfoRequest.Data, + uri: vscode.Uri | string | ThemeIcon | undefined ) { super(() => this.disconnect()); this.root = new Visualizer(rootData.id, rootData.id, rootData, uri); @@ -408,18 +409,18 @@ class VisualizerProvider extends vscode.Disposable implements CustomizableTreeDa private _onDidChangeTreeData: vscode.EventEmitter<Visualizer | undefined | null | void> = new vscode.EventEmitter<Visualizer | undefined | null | void>(); readonly onDidChangeTreeData: vscode.Event<Visualizer | undefined | null | void> = this._onDidChangeTreeData.event; - private disconnect() : void { + private disconnect(): void { // nothing at the moment. for (let deco of this.decorators) { deco.dispose(); } } - item(id : number) : Visualizer | undefined { + item(id: number): Visualizer | undefined { return this.treeData.get(id); } - fireItemChange(item : Visualizer | undefined) : void { + fireItemChange(item: Visualizer | undefined): void { if (doLog) { this.log.appendLine(`Firing change on ${item?.idstring()}`); } @@ -430,7 +431,7 @@ class VisualizerProvider extends vscode.Disposable implements CustomizableTreeDa } } - addItemDecorator(decoInstance : TreeItemDecorator<Visualizer>) : vscode.Disposable { + addItemDecorator(decoInstance: TreeItemDecorator<Visualizer>): vscode.Disposable { this.decorators.push(decoInstance); const self = this; return new vscode.Disposable(() => { @@ -442,42 +443,42 @@ class VisualizerProvider extends vscode.Disposable implements CustomizableTreeDa }); } - refresh(params : NodeChangedParams): void { - if (this.root.data.id === params.rootId) { - let v : Visualizer | undefined; - if (this. root.data.id == params.nodeId || !params.nodeId) { - v = this.root; - } else { - v = this.treeData.get(params.nodeId); - } - if (v) { - if (this.delayedFire.has(v)) { - if (doLog) { - this.log.appendLine(`Delaying change on ${v.idstring()}`); - } - v.pendingChange = true; - } else { - this.fireItemChange(v); + refresh(params: NodeChangedParams): void { + if (this.root.data.id === params.rootId) { + let v: Visualizer | undefined; + if (this.root.data.id == params.nodeId || !params.nodeId) { + v = this.root; + } else { + v = this.treeData.get(params.nodeId); + } + if (v) { + if (this.delayedFire.has(v)) { + if (doLog) { + this.log.appendLine(`Delaying change on ${v.idstring()}`); } + v.pendingChange = true; + } else { + this.fireItemChange(v); } } + } } - async findTreeItem(toSelect : any) : Promise<Visualizer | undefined> { - let path : number[] = await this.client.sendRequest(NodeInfoRequest.findparams, { - selectData : toSelect, - rootNodeId : Number(this.root.id) + async findTreeItem(toSelect: any): Promise<Visualizer | undefined> { + let path: number[] = await this.client.sendRequest(NodeInfoRequest.findparams, { + selectData: toSelect, + rootNodeId: Number(this.root.id) }); if (!path) { return; } - let current : Visualizer = this.root; + let current: Visualizer = this.root; if (path.length > 1 && path[0] == Number(this.root.id)) { path.shift(); } - + for (let nodeId of path) { - let children : Visualizer[]; + let children: Visualizer[]; if (current.children) { children = Array.from(current.children.values()); } else { @@ -486,7 +487,7 @@ class VisualizerProvider extends vscode.Disposable implements CustomizableTreeDa if (!children) { return undefined; } - let selected : Visualizer | null = null; + let selected: Visualizer | null = null; for (let c of children) { if (c.id == String(nodeId)) { selected = c; @@ -501,39 +502,39 @@ class VisualizerProvider extends vscode.Disposable implements CustomizableTreeDa return current; } - getRoot() : Visualizer { + getRoot(): Visualizer { return this.root.copy(); } - getParent(element : Visualizer) : Visualizer | null | Thenable<Visualizer | null> { + getParent(element: Visualizer): Visualizer | null | Thenable<Visualizer | null> { // rely on that children was called first return element.parent; } getTreeItem(element: Visualizer): vscode.TreeItem | Thenable<vscode.TreeItem> { - const n : number = Number(element.id); + const n: number = Number(element.id); const self = this; if (doLog) { this.log.appendLine(`Doing getTreeItem on ${element.idstring()}`); } return this.wrap(async (arr) => { - const pn : number = Number(element.parent?.id) || -1; + const pn: number = Number(element.parent?.id) || -1; let fetched = await this.queryVisualizer(element, arr, () => this.fetchItem(pn, n)); - let origin : vscode.TreeItem; + let origin: vscode.TreeItem; if (fetched) { element.update(fetched); origin = await self.getTreeItem2(fetched); } else { // fire a change, this was unexpected - const pn : number = Number(element.parent?.id) || -1; + const pn: number = Number(element.parent?.id) || -1; let pv = this.treeData.get(pn); if (pv) { this.fireItemChange(pv); } origin = element; } - let ti : vscode.TreeItem = new vscode.TreeItem(origin.label || "", origin.collapsibleState); + let ti: vscode.TreeItem = new vscode.TreeItem(origin.label || "", origin.collapsibleState); // See #4113 -- vscode broke icons display, if resourceUri is defined in TreeItem. We're OK with files, // but folders can have a semantic icon, so let hide resourceUri from vscode for folders. @@ -564,8 +565,8 @@ class VisualizerProvider extends vscode.Disposable implements CustomizableTreeDa * @param fn the code to execute * @returns value of the code function */ - async wrap<X>(fn : (pending : Visualizer[]) => Thenable<X>) : Promise<X> { - let arr : Visualizer[] = []; + async wrap<X>(fn: (pending: Visualizer[]) => Thenable<X>): Promise<X> { + let arr: Visualizer[] = []; try { return await fn(arr); } finally { @@ -576,7 +577,7 @@ class VisualizerProvider extends vscode.Disposable implements CustomizableTreeDa /** * Just creates a string list from visualizer IDs. Diagnostics only. */ - private visualizerList(arr : Visualizer[]) : string { + private visualizerList(arr: Visualizer[]): string { let s = ""; for (let v of arr) { s += v.idstring() + " "; @@ -587,7 +588,7 @@ class VisualizerProvider extends vscode.Disposable implements CustomizableTreeDa /** * Do not use directly, use wrap(). Fires delayed events for visualizers that have no pending queries. */ - private releaseVisualizersAndFire(list : Visualizer[] | undefined) { + private releaseVisualizersAndFire(list: Visualizer[] | undefined) { if (!list) { list = Array.from(this.delayedFire); } @@ -626,7 +627,7 @@ class VisualizerProvider extends vscode.Disposable implements CustomizableTreeDa * @param fn code to execute * @returns code's result */ - async queryVisualizer<X>(element : Visualizer | undefined, pending : Visualizer[], fn : () => Promise<X>) : Promise<X> { + async queryVisualizer<X>(element: Visualizer | undefined, pending: Visualizer[], fn: () => Promise<X>): Promise<X> { if (!element) { return fn(); } @@ -642,29 +643,29 @@ class VisualizerProvider extends vscode.Disposable implements CustomizableTreeDa async getTreeItem2(element: Visualizer): Promise<vscode.TreeItem> { const n = Number(element.id); if (this.decorators.length == 0) { - return element; + return element; } - let list : TreeItemDecorator<Visualizer>[] = [...this.decorators]; - - async function f(item : vscode.TreeItem) : Promise<vscode.TreeItem> { + let list: TreeItemDecorator<Visualizer>[] = [...this.decorators]; + + async function f(item: vscode.TreeItem): Promise<vscode.TreeItem> { const deco = list.shift(); if (!deco) { - return item; + return item; } const decorated = deco.decorateTreeItem(element, item); if (decorated instanceof vscode.TreeItem) { - return f(decorated); + return f(decorated); } else { - return (decorated as Thenable<vscode.TreeItem>).then(f); + return (decorated as Thenable<vscode.TreeItem>).then(f); } } return f(element.copy()); } - delayedFire : Set<Visualizer> = new Set<Visualizer>(); + delayedFire: Set<Visualizer> = new Set<Visualizer>(); - async fetchItem(parent : number, n : number) : Promise<Visualizer | undefined> { - let d = await this.client.sendRequest(NodeInfoRequest.info, { nodeId : n }); + async fetchItem(parent: number, n: number): Promise<Visualizer | undefined> { + let d = await this.client.sendRequest(NodeInfoRequest.info, { nodeId: n }); if (!d || d?.id < 0) { return undefined; } @@ -673,9 +674,9 @@ class VisualizerProvider extends vscode.Disposable implements CustomizableTreeDa if (d.command) { // PENDING: provide an API to register command (+ parameters) -> command translators. if (d.command === 'vscode.open') { - v.command = { command : d.command, title: '', arguments: [v.resourceUri]}; + v.command = { command: d.command, title: '', arguments: [v.resourceUri] }; } else { - v.command = { command : d.command, title: '', arguments: [v]}; + v.command = { command: d.command, title: '', arguments: [v] }; } } return v; @@ -688,22 +689,22 @@ class VisualizerProvider extends vscode.Disposable implements CustomizableTreeDa this.log.appendLine(`Doing getChildren on ${e?.idstring()}`); } - let decos : TreeItemDecorator<Visualizer>[] = [...this.decorators]; + let decos: TreeItemDecorator<Visualizer>[] = [...this.decorators]; const parent = e || this.root; - async function collectResults(list : Visualizer[], arr: any, element: Visualizer): Promise<Visualizer[]> { - let res : Visualizer[] = []; - let now : Visualizer[] | undefined; - const pn : number = Number(element.id) || -1; + async function collectResults(list: Visualizer[], arr: any, element: Visualizer): Promise<Visualizer[]> { + let res: Visualizer[] = []; + let now: Visualizer[] | undefined; + const pn: number = Number(element.id) || -1; for (let i = 0; i < arr.length; i++) { - const old : Visualizer | undefined = self.treeData.get(arr[i]); - let v : Visualizer | undefined = await self.queryVisualizer(old, list, () => self.fetchItem(pn, arr[i])); + const old: Visualizer | undefined = self.treeData.get(arr[i]); + let v: Visualizer | undefined = await self.queryVisualizer(old, list, () => self.fetchItem(pn, arr[i])); if (v) { res.push(v); } } if (decos.length > 0) { - async function f(orig: Visualizer[]) : Promise<Visualizer[]> { + async function f(orig: Visualizer[]): Promise<Visualizer[]> { const deco = decos.shift(); if (!deco) { return orig; @@ -711,9 +712,9 @@ class VisualizerProvider extends vscode.Disposable implements CustomizableTreeDa // decorateChildren(element: T, item : Visualizer, children: Visualizer[]): Visualizer[] | Thenable<Visualizer[]>; const decorated = deco.decorateChildren(parent, orig); if (Array.isArray(decorated)) { - return f(decorated); + return f(decorated); } else { - return (decorated as Thenable<Visualizer[]>).then(f); + return (decorated as Thenable<Visualizer[]>).then(f); } } @@ -723,7 +724,7 @@ class VisualizerProvider extends vscode.Disposable implements CustomizableTreeDa now = element.updateChildren(res, self); for (let i = 0; i < now.length; i++) { const v = now[i]; - const n : number = Number(v.id || -1); + const n: number = Number(v.id || -1); self.treeData.set(n, v); v.parent = element; } @@ -731,17 +732,17 @@ class VisualizerProvider extends vscode.Disposable implements CustomizableTreeDa } return self.wrap((list) => self.queryVisualizer(e, list, () => { - return this.client.sendRequest(NodeInfoRequest.children, { nodeId : parent.data.id}).then(async (arr) => { - return collectResults(list, arr, parent); - }); - } + return this.client.sendRequest(NodeInfoRequest.children, { nodeId: parent.data.id }).then(async (arr) => { + return collectResults(list, arr, parent); + }); + } )); } - removeVisualizers(vis : number[]) { - let ch : number[] = []; + removeVisualizers(vis: number[]) { + let ch: number[] = []; vis.forEach(a => { - let v : Visualizer | undefined = this.treeData.get(a); + let v: Visualizer | undefined = this.treeData.get(a); if (v && v.children) { ch.push(...v.children.keys()); this.treeData.delete(a); @@ -757,16 +758,16 @@ class VisualizerProvider extends vscode.Disposable implements CustomizableTreeDa let visualizerSerial = 1; export class Visualizer extends vscode.TreeItem { - - visId : number; - pendingQueries : number = 0; - pendingChange : boolean = false; + + visId: number; + pendingQueries: number = 0; + pendingChange: boolean = false; constructor( - public rootId : number, - explicitId : number, - public data : NodeInfoRequest.Data, - public image : vscode.Uri | string | ThemeIcon | undefined + public rootId: number, + explicitId: number, + public data: NodeInfoRequest.Data, + public image: vscode.Uri | string | ThemeIcon | undefined ) { super(data.id < 0 ? "< obsolete >" : data.label, data.collapsibleState); this.visId = visualizerSerial++; @@ -777,13 +778,13 @@ export class Visualizer extends vscode.TreeItem { this.collapsibleState = data.collapsibleState; this.iconPath = image; if (data.resourceUri) { - this.resourceUri = vscode.Uri.parse(data.resourceUri); + this.resourceUri = vscode.Uri.parse(data.resourceUri); } this.contextValue = data.contextValue; } - copy() : Visualizer { - let v : Visualizer = new Visualizer(this.rootId, Number(this.id), this.data, this.image); + copy(): Visualizer { + let v: Visualizer = new Visualizer(this.rootId, Number(this.id), this.data, this.image); v.id = this.id; v.label = this.label; v.description = this.description; @@ -797,11 +798,11 @@ export class Visualizer extends vscode.TreeItem { parent: Visualizer | null = null; children: Map<number, Visualizer> | null = null; - idstring() : string { + idstring(): string { return `[${this.id} : ${this.visId} - "${this.label}"]`; } - update(other : Visualizer) : Visualizer { + update(other: Visualizer): Visualizer { this.label = other.label; this.description = other.description; this.tooltip = other.tooltip; @@ -816,14 +817,14 @@ export class Visualizer extends vscode.TreeItem { return this; } - updateChildren(newChildren : Visualizer[], provider : VisualizerProvider) : Visualizer[] { - let toRemove : number[] = []; - let ch : Map<number, Visualizer> = new Map(); + updateChildren(newChildren: Visualizer[], provider: VisualizerProvider): Visualizer[] { + let toRemove: number[] = []; + let ch: Map<number, Visualizer> = new Map(); for (let i = 0; i < newChildren.length; i++) { let c = newChildren[i]; - const n : number = Number(c.id || -1); - const v : Visualizer | undefined = this.children?.get(n); + const n: number = Number(c.id || -1); + const v: Visualizer | undefined = this.children?.get(n); if (v) { v.update(c); newChildren[i] = c = v; @@ -848,21 +849,21 @@ export class Visualizer extends vscode.TreeItem { class ImageEntry { constructor( - readonly uriRegexp : RegExp, - readonly codeicon : string, - readonly iconPath? : string, - readonly valueRegexps? : RegExp[], + readonly uriRegexp: RegExp, + readonly codeicon: string, + readonly iconPath?: string, + readonly valueRegexps?: RegExp[], readonly color?: string - ) {} + ) { } } class ImageTranslator { - private entries : ImageEntry[] = []; + private entries: ImageEntry[] = []; - public setTranslations(entries : ImageEntry[]) { + public setTranslations(entries: ImageEntry[]) { this.entries = entries; } - public findProductIcon(res : string) : string | undefined { + public findProductIcon(res: string): string | undefined { for (let e of this.entries) { if (e.uriRegexp.exec(res)) { return e.codeicon; @@ -872,7 +873,7 @@ class ImageTranslator { } } -export async function createViewProvider(c : NbLanguageClient, id : string) : Promise<VisualizerProvider> { +export async function createViewProvider(c: NbLanguageClient, id: string): Promise<VisualizerProvider> { const ts = c.findTreeViewService(); const client = ts.getClient(); const res = client.sendRequest(NodeInfoRequest.explorermanager, { explorerId: id }).then(async node => { @@ -894,7 +895,7 @@ export async function createViewProvider(c : NbLanguageClient, id : string) : Pr * @param viewTitle title for the new view, optional. * @returns promise of the tree view instance. */ -export async function createTreeView<T>(c: NbLanguageClient, viewId: string, viewTitle? : string, options? : Partial<vscode.TreeViewOptions<any>>) : Promise<vscode.TreeView<Visualizer>> { +export async function createTreeView<T>(c: NbLanguageClient, viewId: string, viewTitle?: string, options?: Partial<vscode.TreeViewOptions<any>>): Promise<vscode.TreeView<Visualizer>> { let ts = c.findTreeViewService(); return ts.createView(viewId, viewTitle, options); } @@ -902,17 +903,17 @@ export async function createTreeView<T>(c: NbLanguageClient, viewId: string, vie /** * Registers the treeview service with the language server. */ -export function createTreeViewService(log : vscode.OutputChannel, c : NbLanguageClient): TreeViewService { - const d = vscode.commands.registerCommand("javals.foundProjects.deleteEntry", async function (this: any, args: any) { - let v = args as Visualizer; - let ok = await c.sendRequest(NodeInfoRequest.destroy, { nodeId : v.data.id }); - if (!ok) { - vscode.window.showErrorMessage(l10n.value("jdk.explorer.error_message.cannotDeleteNode" , { - label:v.label - })); - } - }); - const ts : TreeViewService = new TreeViewService(log, c, [ d ]); - return ts; +export function createTreeViewService(log: vscode.OutputChannel, c: NbLanguageClient): TreeViewService { + const d = vscode.commands.registerCommand(extCommands.projectDeleteEntry, async function (this: any, args: any) { + let v = args as Visualizer; + let ok = await c.sendRequest(NodeInfoRequest.destroy, { nodeId: v.data.id }); + if (!ok) { + vscode.window.showErrorMessage(l10n.value("jdk.explorer.error_message.cannotDeleteNode", { + label: v.label + })); + } + }); + const ts: TreeViewService = new TreeViewService(log, c, [d]); + return ts; } diff --git a/vscode/src/views/runConfiguration.ts b/vscode/src/views/runConfiguration.ts new file mode 100644 index 0000000..943e970 --- /dev/null +++ b/vscode/src/views/runConfiguration.ts @@ -0,0 +1,167 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/* This file has been modified for Oracle Java SE extension */ + +import * as vscode from 'vscode'; +import { homedir } from 'os'; +import { l10n } from '../localiser'; + + + + + +class RunConfigurationNodeProvider implements vscode.TreeDataProvider<vscode.TreeItem> { + + private _onDidChangeTreeData: vscode.EventEmitter<vscode.TreeItem | undefined | null> = new vscode.EventEmitter<vscode.TreeItem | undefined | null>(); + readonly onDidChangeTreeData: vscode.Event<vscode.TreeItem | undefined | null> = this._onDidChangeTreeData.event; + + refresh(): void { + this._onDidChangeTreeData.fire(undefined); + } + + getTreeItem(element: vscode.TreeItem): vscode.TreeItem { + return element; + } + + getChildren(element?: vscode.TreeItem): vscode.ProviderResult<vscode.TreeItem[]> { + if (!element) { + return [argumentsNode, vmOptionsNode, environmentVariablesNode, workingDirectoryNode]; + } + return []; + } + +} +export const runConfigurationNodeProvider = new RunConfigurationNodeProvider(); + +class RunConfigurationNode extends vscode.TreeItem { + + prompt: string; + hint: string; + value: string | undefined; + + settingsKey: string; + + constructor(label: string, prompt: string, hint: string, settingsKey: string) { + super(label); + this.contextValue = 'configureRunSettings'; + this.collapsibleState = vscode.TreeItemCollapsibleState.None; + + this.prompt = prompt; + this.hint = hint; + + this.settingsKey = settingsKey; + + this.value = this.getConfig().get(this.settingsKey); + this.updateNode(); + } + + public configure(_context: vscode.ExtensionContext) { + vscode.window.showInputBox( + { + prompt: this.prompt, + value: this.value, + placeHolder: this.hint, + ignoreFocusOut: true + } + ).then(async val => { + if (val !== undefined) { + const value = val.toString().trim(); + this.setValue(value ? value : undefined); + } + }); + } + + public getValue(): string | undefined { + return this.value; + } + + setValue(value: string | undefined) { + this.value = value; + this.getConfig().update(this.settingsKey, this.value, vscode.workspace.name || vscode.workspace.workspaceFile ? null : true); + this.updateNode(); + } + + updateNode(reload?: boolean) { + if (reload) { + this.value = this.getConfig().get(this.settingsKey) as string; + } + this.description = this.value ? this.value : l10n.value("jdk.extension.runConfig.default.label"); + this.tooltip = `${this.label} ${this.description}`; + runConfigurationNodeProvider.refresh(); + } + + getConfig(): vscode.WorkspaceConfiguration { + return vscode.workspace.getConfiguration('jdk.runConfig'); + } + +} + +class ArgumentsNode extends RunConfigurationNode { + + constructor() { + super(l10n.value("jdk.extension.runConfig.arguments.label"), l10n.value("jdk.extension.runConfig.arguments.prompt"), l10n.value("jdk.extension.runConfig.example.label", { data: "foo bar" }), 'arguments'); + } + +} +export const argumentsNode = new ArgumentsNode(); + +class VMOptionsNode extends RunConfigurationNode { + + constructor() { + super(l10n.value("jdk.extension.runConfig.vmoptions.label"), l10n.value("jdk.extension.runConfig.vmoptions.prompt"), l10n.value("jdk.extension.runConfig.example.label", { data: "-Xmx512m -Xms256m" }), 'vmOptions'); + } + +} +export const vmOptionsNode = new VMOptionsNode(); + +class EnvironmentVariablesNode extends RunConfigurationNode { + + constructor() { + super(l10n.value("jdk.extension.runConfig.env.label"), l10n.value("jdk.extension.runConfig.env.prompt"), l10n.value("jdk.extension.runConfig.example.label", { data: "var1=one, varTwo=2" }), 'env'); + } + +} +export const environmentVariablesNode = new EnvironmentVariablesNode(); + +class WorkingDirectoryNode extends RunConfigurationNode { + + constructor() { + super(l10n.value("jdk.extension.runConfig.wrkdir.label"), l10n.value("jdk.extension.runConfig.wrkdir.prompt"), WorkingDirectoryNode.getExample(), 'cwd'); + } + + static getExample(): string { + const dir = homedir(); + return l10n.value("jdk.extension.runConfig.example.label", { data: dir }); + } + +} +export const workingDirectoryNode = new WorkingDirectoryNode(); + +export function configureRunSettings(context: vscode.ExtensionContext, ...params: any[]) { + if (params[0][0]) { + (params[0][0] as RunConfigurationNode).configure(context); + } +} +export function runConfigurationUpdateAll() { + argumentsNode.updateNode(true); + vmOptionsNode.updateNode(true); + environmentVariablesNode.updateNode(true); + workingDirectoryNode.updateNode(true); +} From 2043e39146b80cfe8848c236c3d8639a6690ebd7 Mon Sep 17 00:00:00 2001 From: Achal Talati <achal.talati@oracle.com> Date: Wed, 16 Oct 2024 14:27:16 +0530 Subject: [PATCH 14/17] Improved LOGGER design --- vscode/src/commands/buildOperations.ts | 3 ++- vscode/src/commands/navigation.ts | 3 ++- vscode/src/commands/utils.ts | 8 ++++---- vscode/src/configurations/handlers.ts | 8 ++++---- vscode/src/extension.ts | 3 --- vscode/src/logger.ts | 15 +++++++++------ vscode/src/lsp/clientPromise.ts | 10 +++++----- vscode/src/lsp/initializer.ts | 3 ++- .../src/lsp/listeners/notifications/handlers.ts | 3 ++- vscode/src/lsp/listeners/requests/handlers.ts | 6 +++--- vscode/src/lsp/nbLanguageClient.ts | 4 ++-- vscode/src/lsp/nbProcessManager.ts | 7 +++---- vscode/src/lsp/nbcode.ts | 6 +++--- vscode/src/lsp/utils.ts | 3 ++- vscode/src/webviews/jdkDownloader/action.ts | 9 ++++----- vscode/src/webviews/jdkDownloader/view.ts | 7 +++---- 16 files changed, 50 insertions(+), 48 deletions(-) diff --git a/vscode/src/commands/buildOperations.ts b/vscode/src/commands/buildOperations.ts index 7ca99ca..6c87cb3 100644 --- a/vscode/src/commands/buildOperations.ts +++ b/vscode/src/commands/buildOperations.ts @@ -13,7 +13,8 @@ See the License for the specific language governing permissions and limitations under the License. */ -import { LOGGER } from "../extension"; + +import { LOGGER } from "../logger"; import { l10n } from "../localiser"; import { extCommands, nbCommands } from "./commands"; import { ICommand } from "./types"; diff --git a/vscode/src/commands/navigation.ts b/vscode/src/commands/navigation.ts index 00ab9cf..76bb2bc 100644 --- a/vscode/src/commands/navigation.ts +++ b/vscode/src/commands/navigation.ts @@ -19,7 +19,8 @@ import { l10n } from "../localiser"; import * as path from 'path'; import { ICommand } from "./types"; import { LanguageClient } from "vscode-languageclient/node"; -import { globalVars, LOGGER } from "../extension"; +import { globalVars } from "../extension"; +import { LOGGER } from '../logger'; import { getContextUri, isNbCommandRegistered, wrapCommandWithProgress } from "./utils"; const goToTest = async (ctx: any) => { diff --git a/vscode/src/commands/utils.ts b/vscode/src/commands/utils.ts index 6c7c4bf..bc51344 100644 --- a/vscode/src/commands/utils.ts +++ b/vscode/src/commands/utils.ts @@ -17,9 +17,9 @@ import { commands, OutputChannel, ProgressLocation, Uri, window } from "vscode"; import { nbCommands } from "./commands"; import { ProjectActionParams } from "../lsp/protocol"; import { LanguageClient } from "vscode-languageclient/node"; -import { globalVars, LOGGER } from "../extension"; +import { globalVars } from "../extension"; import { l10n } from "../localiser"; -import { LogLevel } from "../logger"; +import { LOGGER } from "../logger"; export const getContextUri = (ctx: any): Uri | undefined => { if (ctx?.fsPath) { @@ -98,14 +98,14 @@ export const wrapCommandWithProgress = (lsCommand: string, title: string, log?: resolve(res); } else { if (log) { - LOGGER.log(`Command ${lsCommand} takes too long to start`, LogLevel.ERROR); + LOGGER.error(`Command ${lsCommand} takes too long to start`); } reject(res); } }, humanVisibleDelay); } catch (err: any) { if (log) { - LOGGER.log(`command ${lsCommand} executed with error: ${JSON.stringify(err)}`, LogLevel.ERROR); + LOGGER.error(`command ${lsCommand} executed with error: ${JSON.stringify(err)}`); } } } else { diff --git a/vscode/src/configurations/handlers.ts b/vscode/src/configurations/handlers.ts index 7a50ee5..7c88ea6 100644 --- a/vscode/src/configurations/handlers.ts +++ b/vscode/src/configurations/handlers.ts @@ -17,8 +17,8 @@ import { extensions, workspace } from "vscode"; import { builtInConfigKeys, configKeys } from "./configuration"; import { extConstants, NODE_WINDOWS_LABEL } from "../constants"; import * as os from 'os'; -import { globalVars, LOGGER } from "../extension"; -import { LogLevel } from "../logger"; +import { globalVars } from "../extension"; +import { LOGGER } from "../logger"; import * as path from 'path'; import * as fs from 'fs'; @@ -53,7 +53,7 @@ export const projectSearchRootsValueHandler = (): string => { try { projectSearchRoots = os.homedir() as string; } catch (err: any) { - LOGGER.log(`Failed to obtain the user home directory due to: ${err}`, LogLevel.ERROR); + LOGGER.error(`Failed to obtain the user home directory due to: ${err}`); } if (!projectSearchRoots) { projectSearchRoots = os.type() === NODE_WINDOWS_LABEL ? '%USERPROFILE%' : '$HOME'; // The launcher script may perform the env variable substitution @@ -83,7 +83,7 @@ export const lspServerVmOptionsHandler = (): string[] => { } export const isDarkColorThemeHandler = (): boolean => { - const themeName: string = getBuiltinConfigurationValue(builtInConfigKeys.vscodeTheme); + const themeName: string = getBuiltinConfigurationValue(builtInConfigKeys.vscodeTheme); if (!themeName) { return false; } diff --git a/vscode/src/extension.ts b/vscode/src/extension.ts index b63c22f..6ef26ba 100644 --- a/vscode/src/extension.ts +++ b/vscode/src/extension.ts @@ -33,7 +33,6 @@ import * as launchConfigurations from './launchConfigurations'; import { extConstants } from './constants'; import { ExtensionInfo } from './extensionInfo'; import { ClientPromise } from './lsp/clientPromise'; -import { ExtensionLogger } from './logger'; import { NbProcessManager } from './lsp/nbProcessManager'; import { clientInit } from './lsp/initializer'; import { subscribeCommands } from './commands/register'; @@ -43,7 +42,6 @@ import { registerConfigChangeListeners } from './configurations/listener'; import { registerFileProviders } from './lsp/listeners/textDocumentContentProvider'; import { createViews } from './views/initializer'; -export let LOGGER: ExtensionLogger; export namespace globalVars { export const listeners = new Map<string, string[]>(); export let extensionInfo: ExtensionInfo; @@ -61,7 +59,6 @@ export namespace globalVars { export function activate(context: ExtensionContext): VSNetBeansAPI { globalVars.clientPromise = new ClientPromise(); globalVars.extensionInfo = new ExtensionInfo(context); - LOGGER = new ExtensionLogger(extConstants.SERVER_NAME); globalVars.clientPromise.initialize(); registerConfigChangeListeners(context); diff --git a/vscode/src/logger.ts b/vscode/src/logger.ts index 7eca25f..6d544bc 100644 --- a/vscode/src/logger.ts +++ b/vscode/src/logger.ts @@ -14,8 +14,9 @@ limitations under the License. */ import { OutputChannel, window } from "vscode"; +import { extConstants } from "./constants"; -export enum LogLevel { +enum LogLevel { INFO = 'INFO', WARN = 'WARN', ERROR = 'ERROR', @@ -28,16 +29,16 @@ export class ExtensionLogger { this.outChannel = window.createOutputChannel(channelName); } - public log(message: string, level: LogLevel = LogLevel.INFO): void { - this.outChannel.appendLine(`[${level}]: ${message}`); + public log(message: string): void { + this.outChannel.appendLine(`[${LogLevel.INFO}]: ${message}`); } public warn(message: string): void { - this.log(message, LogLevel.WARN); + this.outChannel.appendLine(`[${LogLevel.WARN}]: ${message}`); } public error(message: string): void { - this.log(message, LogLevel.ERROR); + this.outChannel.appendLine(`[${LogLevel.ERROR}]: ${message}`); } public logNoNL(message: string): void { @@ -55,4 +56,6 @@ export class ExtensionLogger { public dispose(): void { this.outChannel.dispose(); } -} \ No newline at end of file +} + +export const LOGGER = new ExtensionLogger(extConstants.SERVER_NAME); \ No newline at end of file diff --git a/vscode/src/lsp/clientPromise.ts b/vscode/src/lsp/clientPromise.ts index dd130d7..960144c 100644 --- a/vscode/src/lsp/clientPromise.ts +++ b/vscode/src/lsp/clientPromise.ts @@ -14,8 +14,8 @@ limitations under the License. */ import { commands } from "vscode"; -import { globalVars, LOGGER } from "../extension"; -import { LogLevel } from "../logger"; +import { globalVars } from "../extension"; +import { LOGGER } from "../logger"; import { NbProcessManager } from "./nbProcessManager"; import { clientInit } from "./initializer"; import { NbLanguageClient } from "./nbLanguageClient"; @@ -60,11 +60,11 @@ export class ClientPromise { public restartExtension = async (nbProcessManager: NbProcessManager | null, notifyKill: boolean) => { if (this.activationPending) { - LOGGER.log("Server activation requested repeatedly, ignoring...", LogLevel.WARN); + LOGGER.warn("Server activation requested repeatedly, ignoring..."); return; } if (!nbProcessManager) { - LOGGER.log("Nbcode Process is null", LogLevel.ERROR); + LOGGER.error("Nbcode Process is null"); return; } try { @@ -73,7 +73,7 @@ export class ClientPromise { this.initialize(); clientInit(); } catch (error) { - LOGGER.log(`Error during activation: ${error}`, LogLevel.ERROR); + LOGGER.error(`Error during activation: ${error}`); throw error; } finally { this.activationPending = false; diff --git a/vscode/src/lsp/initializer.ts b/vscode/src/lsp/initializer.ts index 7bffddb..006113d 100644 --- a/vscode/src/lsp/initializer.ts +++ b/vscode/src/lsp/initializer.ts @@ -15,7 +15,8 @@ */ import { StreamInfo } from "vscode-languageclient/node"; import { getUserConfigLaunchOptionsDefaults } from "./launchOptions"; -import { globalVars, LOGGER } from "../extension"; +import { globalVars } from "../extension"; +import { LOGGER } from '../logger'; import { configKeys } from "../configurations/configuration"; import { enableDisableModules } from "./utils"; import * as net from 'net'; diff --git a/vscode/src/lsp/listeners/notifications/handlers.ts b/vscode/src/lsp/listeners/notifications/handlers.ts index 2236fad..96bf257 100644 --- a/vscode/src/lsp/listeners/notifications/handlers.ts +++ b/vscode/src/lsp/listeners/notifications/handlers.ts @@ -21,7 +21,8 @@ import { isNbJavacDisabledHandler, updateConfigurationValue } from "../../../con import { l10n } from "../../../localiser"; import { configKeys } from "../../../configurations/configuration"; import { builtInCommands } from "../../../commands/commands"; -import { globalVars, LOGGER } from "../../../extension"; +import { globalVars } from "../../../extension"; +import { LOGGER } from '../../../logger'; const checkInstallNbJavac = (msg: string) => { const NO_JAVA_SUPPORT = "Cannot initialize Java support"; diff --git a/vscode/src/lsp/listeners/requests/handlers.ts b/vscode/src/lsp/listeners/requests/handlers.ts index 9013880..918b2c6 100644 --- a/vscode/src/lsp/listeners/requests/handlers.ts +++ b/vscode/src/lsp/listeners/requests/handlers.ts @@ -14,14 +14,14 @@ limitations under the License. */ import { QuickPickItem, Uri, window, workspace, WorkspaceConfiguration } from "vscode"; -import { globalVars, LOGGER } from "../../../extension"; +import { globalVars } from "../../../extension"; import { notificationOrRequestListenerType } from "../../types"; import { ExecInHtmlPageRequest, HtmlPageRequest, InputBoxRequest, InputBoxStep, MutliStepInputRequest, QuickPickRequest, QuickPickStep, SaveDocumentRequestParams, SaveDocumentsRequest, TextEditorDecorationCreateRequest, UpdateConfigurationRequest } from "../../protocol"; import { InputStep, MultiStepInput } from "../../../utils"; import { runConfigurationUpdateAll } from "../../../views/runConfiguration"; import { isError } from "../../../utils"; import { isString } from "../../../utils"; -import { LogLevel } from "../../../logger"; +import { LOGGER } from "../../../logger"; import { execInHtmlPage, showHtmlPage } from "../../../webviews/nbWebviewHandler"; const textEditorDecorationCreateRequestHandler = (param: any) => { @@ -104,7 +104,7 @@ const updateConfigRequestHandler = async (param: any) => { runConfigurationUpdateAll(); }); } catch (err) { - LOGGER.log("Failed to update configuration. Reason: " + (isString(err) ? err : isError(err) ? err.message : "error"), LogLevel.ERROR); + LOGGER.error("Failed to update configuration. Reason: " + (isString(err) ? err : isError(err) ? err.message : "error")); } } } diff --git a/vscode/src/lsp/nbLanguageClient.ts b/vscode/src/lsp/nbLanguageClient.ts index fa94c13..ca2434e 100644 --- a/vscode/src/lsp/nbLanguageClient.ts +++ b/vscode/src/lsp/nbLanguageClient.ts @@ -20,7 +20,7 @@ import { OutputChannel, workspace } from "vscode"; import { extConstants } from "../constants"; import { userConfigsListenedByServer } from '../configurations/configuration'; import { restartWithJDKLater } from './utils'; -import { ExtensionLogger, LogLevel } from '../logger'; +import { ExtensionLogger } from '../logger'; import { globalVars } from '../extension'; @@ -71,7 +71,7 @@ export class NbLanguageClient extends LanguageClient { return { action: ErrorAction.Continue, message: error.message }; }, closed: function (): CloseHandlerResult { - logger.log(`Connection to ${extConstants.SERVER_NAME} closed.`, LogLevel.WARN); + logger.warn(`Connection to ${extConstants.SERVER_NAME} closed.`); if (!globalVars.clientPromise.activationPending) { restartWithJDKLater(10000, false); } diff --git a/vscode/src/lsp/nbProcessManager.ts b/vscode/src/lsp/nbProcessManager.ts index a0bef09..67f9eea 100644 --- a/vscode/src/lsp/nbProcessManager.ts +++ b/vscode/src/lsp/nbProcessManager.ts @@ -13,13 +13,12 @@ See the License for the specific language governing permissions and limitations under the License. */ -import { LOGGER } from "../extension"; import { spawn, ChildProcessByStdio, ChildProcess } from 'child_process'; import { Readable } from "stream"; import { window } from "vscode"; import { l10n } from "../localiser"; import { extConstants } from "../constants"; -import { LogLevel } from "../logger"; +import { LOGGER } from "../logger"; export class NbProcessManager { private process?: ChildProcess | null; @@ -49,7 +48,7 @@ export class NbProcessManager { LOGGER.log("Request to kill LSP server."); if (!this.process) { - LOGGER.log("Cannot kill: No current process", LogLevel.ERROR); + LOGGER.error("Cannot kill: No current process"); return Promise.resolve(); } const processToKill = this.process; @@ -99,7 +98,7 @@ export class NbProcessManager { getStdOut = () => { return this.stdOutText } - + setStdOut = (stdOut: string | null) => { this.stdOutText = stdOut; } diff --git a/vscode/src/lsp/nbcode.ts b/vscode/src/lsp/nbcode.ts index 2cad5be..6b75e45 100644 --- a/vscode/src/lsp/nbcode.ts +++ b/vscode/src/lsp/nbcode.ts @@ -17,13 +17,13 @@ import { window } from "vscode"; import { configKeys } from "../configurations/configuration"; import { extConstants, NODE_WINDOWS_LABEL } from "../constants"; -import { globalVars, LOGGER } from "../extension"; +import { globalVars } from "../extension"; import { prepareNbcodeLaunchOptions, getUserConfigLaunchOptionsDefaults } from "./launchOptions"; import { NbProcessManager } from "./nbProcessManager"; import { findNbcode } from "./utils"; import { l10n } from "../localiser"; import { jdkDownloaderPrompt } from "../webviews/jdkDownloader/prompt"; -import { LogLevel } from "../logger"; +import { LOGGER } from "../logger"; import * as os from 'os'; export const launchNbcode = (): void => { @@ -95,7 +95,7 @@ const processOnCloseHandler = (nbProcessManager: NbProcessManager, code: number) if (match?.length == 1) { LOGGER.log(match[0]); } else { - LOGGER.log("Cannot find org.netbeans.modules.java.lsp.server in the log!", LogLevel.ERROR); + LOGGER.error("Cannot find org.netbeans.modules.java.lsp.server in the log!"); } LOGGER.log(`Please refer to troubleshooting section for more info: https://github.com/oracle/javavscode/blob/main/README.md#troubleshooting`); LOGGER.showOutputChannelUI(false); diff --git a/vscode/src/lsp/utils.ts b/vscode/src/lsp/utils.ts index 1cf8b41..807cf0a 100644 --- a/vscode/src/lsp/utils.ts +++ b/vscode/src/lsp/utils.ts @@ -16,7 +16,8 @@ import * as fs from 'fs'; import * as path from 'path'; import * as os from 'os'; -import { globalVars, LOGGER } from '../extension'; +import { globalVars } from '../extension'; +import { LOGGER } from '../logger'; import { extConstants } from '../constants'; export const enableDisableModules = ( diff --git a/vscode/src/webviews/jdkDownloader/action.ts b/vscode/src/webviews/jdkDownloader/action.ts index 0f9e68d..d1dd5bc 100644 --- a/vscode/src/webviews/jdkDownloader/action.ts +++ b/vscode/src/webviews/jdkDownloader/action.ts @@ -23,8 +23,7 @@ import { calculateChecksum, downloadFileWithProgressBar, httpsGet } from "../../ import * as cp from 'child_process'; import { promisify } from "util"; import { l10n } from "../../localiser"; -import { LOGGER } from "../../extension"; -import { LogLevel } from "../../logger"; +import { LOGGER } from "../../logger"; export class JdkDownloaderAction { public static readonly MANUAL_INSTALLATION_TYPE = "manual"; @@ -101,7 +100,7 @@ export class JdkDownloaderAction { await this.jdkInstallationManager(); } catch (err: any) { window.showErrorMessage(l10n.value("jdk.downloader.error_message.installingJDK", { error: err })); - LOGGER.log(err?.message || "No Error message received", LogLevel.ERROR); + LOGGER.error(err?.message || "No Error message received"); } } @@ -220,7 +219,7 @@ export class JdkDownloaderAction { await exec(extractCommand); LOGGER.log(`Extracting JDK successful`); } catch (err) { - LOGGER.log(`Error while extracting JDK: ${(err as Error).message}`, LogLevel.ERROR); + LOGGER.error(`Error while extracting JDK: ${(err as Error).message}`); throw new Error(l10n.value("jdk.downloader.error_message.extractionError", { jdkType: this.jdkType, jdkVersion: this.jdkVersion @@ -271,7 +270,7 @@ export class JdkDownloaderAction { private installationCleanup = (tempDirPath: string, newDirPath: string) => { fs.unlink(this.downloadFilePath!, async (err) => { if (err) { - LOGGER.log(`Error while installation cleanup: ${err.message}`, LogLevel.ERROR); + LOGGER.error(`Error while installation cleanup: ${err.message}`); window.showErrorMessage(l10n.value("jdk.downloader.error_message.installationCleanup")); } else { if (tempDirPath && fs.existsSync(tempDirPath)) { diff --git a/vscode/src/webviews/jdkDownloader/view.ts b/vscode/src/webviews/jdkDownloader/view.ts index 141d09c..48126ad 100644 --- a/vscode/src/webviews/jdkDownloader/view.ts +++ b/vscode/src/webviews/jdkDownloader/view.ts @@ -20,8 +20,7 @@ import * as os from 'os'; import { JdkDownloaderAction } from './action'; import { downloaderCss } from './styles'; import { l10n } from '../../localiser'; -import { LOGGER } from '../../extension'; -import { LogLevel } from '../../logger'; +import { LOGGER } from '../../logger'; export class JdkDownloaderView { public static readonly OPEN_JDK_LABEL = "OpenJDK"; @@ -52,8 +51,8 @@ export class JdkDownloaderView { }); LOGGER.log("JDK downloader webview created successfully"); } catch (err: any) { - LOGGER.log("Error creating JDK downloader webview:", LogLevel.ERROR) - LOGGER.log(err?.message || "No Error message received", LogLevel.ERROR); + LOGGER.error("Error creating JDK downloader webview:"); + LOGGER.error(err?.message || "No Error message received"); window.showErrorMessage(l10n.value("jdk.downloader.error_message.errorLoadingPage")); } } From 38129b880a554dd72b8eeb5344a6174576ee6119 Mon Sep 17 00:00:00 2001 From: Achal Talati <achal.talati@oracle.com> Date: Tue, 22 Oct 2024 12:52:34 +0530 Subject: [PATCH 15/17] Addressed review comments --- vscode/l10n/bundle.l10n.en.json | 2 +- vscode/src/commands/cache.ts | 2 +- vscode/src/commands/commands.ts | 7 +++++-- vscode/src/configurations/configuration.ts | 1 + vscode/src/configurations/handlers.ts | 7 ++++++- vscode/src/debugger/debugger.ts | 12 ------------ vscode/src/extension.ts | 5 +++-- vscode/src/logger.ts | 14 +++++++++++--- vscode/src/lsp/nbLanguageClient.ts | 4 ++-- vscode/src/test/suite/general/extension.test.ts | 6 +++--- vscode/src/test/testutils.ts | 5 +++-- vscode/src/views/TestViewController.ts | 5 ++--- vscode/src/views/runConfiguration.ts | 4 +++- 13 files changed, 41 insertions(+), 33 deletions(-) diff --git a/vscode/l10n/bundle.l10n.en.json b/vscode/l10n/bundle.l10n.en.json index 0f24d6d..e83915a 100644 --- a/vscode/l10n/bundle.l10n.en.json +++ b/vscode/l10n/bundle.l10n.en.json @@ -45,7 +45,7 @@ "jdk.extension.fileSelector.label.testFilesOrSourceFiles": "Test files or source files associated to each other", "jdk.extension.fileSelector.label.noFileSelected": "No file selected", "jdk.extension.fileSelector.label.noTestFound": "No Test or Tested class found", - "jdk.extension.cache.message.confirmToDeleteCache": "Are you sure you want to delete cache for this workspace?", + "jdk.extension.cache.message.confirmToDeleteCache": "Are you sure you want to delete the cache for this workspace and reload the window?", "jdk.extension.cache.label.confirmation.yes":"Yes", "jdk.extension.cache.label.confirmation.cancel":"Cancel", "jdk.extension.cache.message.cacheCleared":"Cache cleared successfully for this workspace", diff --git a/vscode/src/commands/cache.ts b/vscode/src/commands/cache.ts index 966106d..a9d0cdd 100644 --- a/vscode/src/commands/cache.ts +++ b/vscode/src/commands/cache.ts @@ -33,7 +33,7 @@ const deleteCache = async () => { if (userDir && fs.existsSync(userDir)) { const yes = l10n.value("jdk.extension.cache.label.confirmation.yes") const cancel = l10n.value("jdk.extension.cache.label.confirmation.cancel") - const confirmation = await window.showInformationMessage('Are you sure you want to delete cache for this workspace and reload the window ?', + const confirmation = await window.showInformationMessage(l10n.value("jdk.extension.cache.message.confirmToDeleteCache"), yes, cancel); if (confirmation === yes) { const reloadWindowActionLabel = l10n.value("jdk.extension.cache.label.reloadWindow"); diff --git a/vscode/src/commands/commands.ts b/vscode/src/commands/commands.ts index 85a90bb..f68cfbb 100644 --- a/vscode/src/commands/commands.ts +++ b/vscode/src/commands/commands.ts @@ -48,7 +48,7 @@ export const extCommands = { selectEditorProjs: appendPrefixToCommand('select.editor.projects'), attachDebugger: appendPrefixToCommand("java.attachDebugger.connector"), loadWorkspaceTests: appendPrefixToCommand("load.workspace.tests"), - projectDeleteEntry: "javals.foundProjects.deleteEntry" + projectDeleteEntry: appendPrefixToCommand("foundProjects.deleteEntry") } export const builtInCommands = { @@ -78,5 +78,8 @@ export const nbCommands = { debuggerConfigurations: appendPrefixToCommand('java.attachDebugger.configurations'), runProjectAction: appendPrefixToCommand('project.run.action'), buildWorkspace: appendPrefixToCommand('build.workspace'), - cleanWorkspace: appendPrefixToCommand('clean.workspace') + cleanWorkspace: appendPrefixToCommand('clean.workspace'), + clearProjectCaches: appendPrefixToCommand('clear.project.caches'), + javaProjectPackages: appendPrefixToCommand('java.get.project.packages'), + openStackTrace: appendPrefixToCommand('open.stacktrace') } \ No newline at end of file diff --git a/vscode/src/configurations/configuration.ts b/vscode/src/configurations/configuration.ts index 709b030..5ae389c 100644 --- a/vscode/src/configurations/configuration.ts +++ b/vscode/src/configurations/configuration.ts @@ -24,6 +24,7 @@ export const configKeys = { formatPrefs: 'format', hintPrefs: 'hints', importPrefs: 'java.imports', + runConfig: 'runCofig', runConfigVmOptions: 'runConfig.vmOptions', runConfigCwd: 'runConfig.cwd', verbose: 'verbose', diff --git a/vscode/src/configurations/handlers.ts b/vscode/src/configurations/handlers.ts index 7c88ea6..4bbc64b 100644 --- a/vscode/src/configurations/handlers.ts +++ b/vscode/src/configurations/handlers.ts @@ -13,7 +13,8 @@ See the License for the specific language governing permissions and limitations under the License. */ -import { extensions, workspace } from "vscode"; + +import { extensions, workspace, WorkspaceConfiguration } from "vscode"; import { builtInConfigKeys, configKeys } from "./configuration"; import { extConstants, NODE_WINDOWS_LABEL } from "../constants"; import * as os from 'os'; @@ -22,6 +23,10 @@ import { LOGGER } from "../logger"; import * as path from 'path'; import * as fs from 'fs'; +export const getConfiguration = (key: string = extConstants.COMMAND_PREFIX): WorkspaceConfiguration => { + return workspace.getConfiguration(key); +} + export const getConfigurationValue = <T>(key: string, defaultValue: T | undefined = undefined): T => { const conf = workspace.getConfiguration(extConstants.COMMAND_PREFIX); return defaultValue != undefined ? conf.get(key, defaultValue) : conf.get(key) as T; diff --git a/vscode/src/debugger/debugger.ts b/vscode/src/debugger/debugger.ts index ae792a2..0ce68b2 100644 --- a/vscode/src/debugger/debugger.ts +++ b/vscode/src/debugger/debugger.ts @@ -36,7 +36,6 @@ export function registerDebugger(context: ExtensionContext): void { context.subscriptions.push(vscode.debug.registerDebugConfigurationProvider(extConstants.COMMAND_PREFIX, configDynamicProvider, vscode.DebugConfigurationProviderTriggerKind.Dynamic)); let configResolver = new NetBeansConfigurationResolver(); context.subscriptions.push(vscode.debug.registerDebugConfigurationProvider(extConstants.COMMAND_PREFIX, configResolver)); - context.subscriptions.push(vscode.debug.onDidTerminateDebugSession(((session) => onDidTerminateSession(session)))); let debugDescriptionFactory = new NetBeansDebugAdapterDescriptionFactory(); context.subscriptions.push(vscode.debug.registerDebugAdapterDescriptorFactory(extConstants.COMMAND_PREFIX, debugDescriptionFactory)); initializeRunConfiguration().then(initialized => { @@ -267,14 +266,3 @@ class RunConfigurationProvider implements vscode.DebugConfigurationProvider { } } - - -function onDidTerminateSession(session: vscode.DebugSession): any { - const config = session.configuration; - if (config.env) { - const file = config.env["MICRONAUT_CONFIG_FILES"]; - if (file) { - vscode.workspace.fs.delete(vscode.Uri.file(file)); - } - } -} diff --git a/vscode/src/extension.ts b/vscode/src/extension.ts index 6ef26ba..0740a56 100644 --- a/vscode/src/extension.ts +++ b/vscode/src/extension.ts @@ -83,8 +83,9 @@ export function activate(context: ExtensionContext): VSNetBeansAPI { export function deactivate(): Thenable<void> { - if (globalVars.nbProcessManager?.getProcess() != null) { - globalVars.nbProcessManager?.getProcess()?.kill(); + const process = globalVars.nbProcessManager?.getProcess(); + if (process != null) { + process?.kill(); } return globalVars.clientPromise.stopClient(); } diff --git a/vscode/src/logger.ts b/vscode/src/logger.ts index 6d544bc..c0c4fa4 100644 --- a/vscode/src/logger.ts +++ b/vscode/src/logger.ts @@ -30,15 +30,18 @@ export class ExtensionLogger { } public log(message: string): void { - this.outChannel.appendLine(`[${LogLevel.INFO}]: ${message}`); + const formattedMessage = `[${LogLevel.INFO}]: ${message}`; + this.printLog(formattedMessage); } public warn(message: string): void { - this.outChannel.appendLine(`[${LogLevel.WARN}]: ${message}`); + const formattedMessage = `[${LogLevel.WARN}]: ${message}`; + this.printLog(formattedMessage); } public error(message: string): void { - this.outChannel.appendLine(`[${LogLevel.ERROR}]: ${message}`); + const formattedMessage = `[${LogLevel.ERROR}]: ${message}`; + this.printLog(formattedMessage); } public logNoNL(message: string): void { @@ -56,6 +59,11 @@ export class ExtensionLogger { public dispose(): void { this.outChannel.dispose(); } + + private printLog(message: string): void{ + const timestamp = new Date().toISOString(); + this.outChannel.appendLine(`[${timestamp}] ${message}`); + } } export const LOGGER = new ExtensionLogger(extConstants.SERVER_NAME); \ No newline at end of file diff --git a/vscode/src/lsp/nbLanguageClient.ts b/vscode/src/lsp/nbLanguageClient.ts index ca2434e..a6cbfc1 100644 --- a/vscode/src/lsp/nbLanguageClient.ts +++ b/vscode/src/lsp/nbLanguageClient.ts @@ -62,8 +62,8 @@ export class NbLanguageClient extends LanguageClient { 'wantsJavaSupport': true, 'wantsGroovySupport': false, 'commandPrefix': extConstants.COMMAND_PREFIX, - 'configurationPrefix': 'jdk.', - 'altConfigurationPrefix': 'jdk.' + 'configurationPrefix': `${extConstants.COMMAND_PREFIX}.`, + 'altConfigurationPrefix': `${extConstants.COMMAND_PREFIX}.` } }, errorHandler: { diff --git a/vscode/src/test/suite/general/extension.test.ts b/vscode/src/test/suite/general/extension.test.ts index 098d54b..6af3d68 100644 --- a/vscode/src/test/suite/general/extension.test.ts +++ b/vscode/src/test/suite/general/extension.test.ts @@ -30,7 +30,7 @@ import * as myExplorer from '../../../views/projects'; import { CodeAction, commands, extensions, Selection, Uri, window, workspace, TreeItem } from 'vscode'; import { assertWorkspace, awaitClient, dumpJava, findClusters, getFilePaths, openFile, prepareProject, replaceCode } from '../../testutils'; import { FORMATTED_POM_XML, SAMPLE_CODE_FORMAT_DOCUMENT, SAMPLE_CODE_SORT_IMPORTS, SAMPLE_CODE_UNUSED_IMPORTS } from '../../constants'; -import { extConstants } from '../../../constants'; +import { extCommands } from '../../../commands/commands'; suite('Extension Test Suite', function () { window.showInformationMessage('Start all tests.'); @@ -166,7 +166,7 @@ suite('Extension Test Suite', function () { if (refactorActions && refactorActions.length > 0) { for await (const action of refactorActions) { if (action.command && action.command.arguments) { - if (action.command.command === extConstants.COMMAND_PREFIX + ".surround.with") { + if (action.command.command === extCommands.surroundWith) { //this action has a popup where the user needs to //select a template that should be used for the surround: continue; @@ -197,7 +197,7 @@ suite('Extension Test Suite', function () { assert.strictEqual(tests[1].tests[0].name, 'testTrue', `Invalid test name returned`); console.log("Test: run all workspace tests"); - await vscode.commands.executeCommand(extConstants.COMMAND_PREFIX + '.run.test', workspaceFolder.uri.toString()); + await vscode.commands.executeCommand(extCommands.runTest, workspaceFolder.uri.toString()); console.log(`Test: run all workspace tests finished`); } catch (error) { dumpJava(); diff --git a/vscode/src/test/testutils.ts b/vscode/src/test/testutils.ts index 37efddf..8fff619 100644 --- a/vscode/src/test/testutils.ts +++ b/vscode/src/test/testutils.ts @@ -35,6 +35,7 @@ import { NbLanguageClient } from "../lsp/nbLanguageClient"; import { extConstants } from "../constants"; import { l10n } from "../localiser"; import { globalVars } from "../extension"; +import { nbCommands } from "../commands/commands"; /** * Folder path currently opened in VSCode workspace @@ -164,12 +165,12 @@ export async function waitProjectRecognized(someJavaFile: string): Promise<void> const u: vscode.Uri = vscode.Uri.file(someJavaFile); // clear out possible bad or negative caches. return vscode.commands - .executeCommand(extConstants.COMMAND_PREFIX + ".clear.project.caches") + .executeCommand(nbCommands.clearProjectCaches) .then( // this should assure opening the root with the created project. () => vscode.commands.executeCommand( - extConstants.COMMAND_PREFIX + ".java.get.project.packages", + nbCommands.javaProjectPackages, u.toString() ) ); diff --git a/vscode/src/views/TestViewController.ts b/vscode/src/views/TestViewController.ts index 70fe93f..f99243c 100644 --- a/vscode/src/views/TestViewController.ts +++ b/vscode/src/views/TestViewController.ts @@ -24,8 +24,7 @@ import { commands, debug, tests, workspace, CancellationToken, TestController, TestItem, TestRunProfileKind, TestRunRequest, Uri, TestRun, TestMessage, Location, Position, MarkdownString } from "vscode"; import * as path from 'path'; import { asRange, TestCase, TestSuite } from "../lsp/protocol"; -import { extConstants } from "../constants"; -import { extCommands, builtInCommands } from "../commands/commands" +import { extCommands, builtInCommands, nbCommands } from "../commands/commands" export class NbTestAdapter { @@ -313,7 +312,7 @@ export class NbTestAdapter { } const result = regExp.exec(line); if (result) { - message.appendText(result[1]).appendText('(').appendMarkdown(`[${result[3]}](command:${extConstants.COMMAND_PREFIX}.open.stacktrace?${encodeURIComponent(JSON.stringify([currentTestUri, result[2], result[4], +result[5]]))})`).appendText(')'); + message.appendText(result[1]).appendText('(').appendMarkdown(`[${result[3]}](command:${nbCommands.openStackTrace}?${encodeURIComponent(JSON.stringify([currentTestUri, result[2], result[4], +result[5]]))})`).appendText(')'); } else { message.appendText(line); } diff --git a/vscode/src/views/runConfiguration.ts b/vscode/src/views/runConfiguration.ts index 943e970..09340ad 100644 --- a/vscode/src/views/runConfiguration.ts +++ b/vscode/src/views/runConfiguration.ts @@ -22,6 +22,8 @@ import * as vscode from 'vscode'; import { homedir } from 'os'; import { l10n } from '../localiser'; +import { getConfiguration } from '../configurations/handlers'; +import { configKeys } from '../configurations/configuration'; @@ -108,7 +110,7 @@ class RunConfigurationNode extends vscode.TreeItem { } getConfig(): vscode.WorkspaceConfiguration { - return vscode.workspace.getConfiguration('jdk.runConfig'); + return getConfiguration(configKeys.runConfig); } } From 0175fd15ed8783573490581e2ccab8b3ef228bc0 Mon Sep 17 00:00:00 2001 From: Achal Talati <achal.talati@oracle.com> Date: Thu, 24 Oct 2024 12:21:52 +0530 Subject: [PATCH 16/17] addressed regressions jdk downloader prompt not showing up and views not reloadingon extension restart fixed no workspace present then extension crashing issue fixed jdk.verbose not working fixed runConfig settings not getting persisted regression and refactored configUpdate and get methods fixed dialog ox appearing when project compile and clean are executed --- vscode/src/commands/buildOperations.ts | 5 +++-- vscode/src/commands/utils.ts | 3 ++- vscode/src/configurations/configuration.ts | 3 ++- vscode/src/configurations/handlers.ts | 19 ++++++++++------ vscode/src/extension.ts | 9 ++------ vscode/src/lsp/initializer.ts | 3 ++- vscode/src/lsp/launchOptions.ts | 9 ++++---- vscode/src/lsp/nbcode.ts | 6 +++-- vscode/src/views/initializer.ts | 7 +++--- vscode/src/views/runConfiguration.ts | 25 +++++++-------------- vscode/src/webviews/jdkDownloader/action.ts | 7 +++--- 11 files changed, 48 insertions(+), 48 deletions(-) diff --git a/vscode/src/commands/buildOperations.ts b/vscode/src/commands/buildOperations.ts index 6c87cb3..1d8bebf 100644 --- a/vscode/src/commands/buildOperations.ts +++ b/vscode/src/commands/buildOperations.ts @@ -23,16 +23,17 @@ import { wrapCommandWithProgress, wrapProjectActionWithProgress } from "./utils" const compileWorkspaceHandler = () => { return wrapCommandWithProgress(nbCommands.buildWorkspace, l10n.value('jdk.extension.command.progress.compilingWorkSpace'), LOGGER.getOutputChannel()); } + const cleanWorkspaceHandler = () => { return wrapCommandWithProgress(nbCommands.cleanWorkspace,l10n.value('jdk.extension.command.progress.cleaningWorkSpace'), LOGGER.getOutputChannel()); } const compileProjectHandler = (args: any) => { - return wrapProjectActionWithProgress('build', undefined, l10n.value('jdk.extension.command.progress.compilingProject'), LOGGER.getOutputChannel(), args); + wrapProjectActionWithProgress('build', undefined, l10n.value('jdk.extension.command.progress.compilingProject'), LOGGER.getOutputChannel(), args); } const cleanProjectHandler = (args: any) => { - return wrapProjectActionWithProgress('clean', undefined, l10n.value('jdk.extension.command.progress.cleaningProject'), LOGGER.getOutputChannel(), args); + wrapProjectActionWithProgress('clean', undefined, l10n.value('jdk.extension.command.progress.cleaningProject'), LOGGER.getOutputChannel(), args); } diff --git a/vscode/src/commands/utils.ts b/vscode/src/commands/utils.ts index bc51344..15eae81 100644 --- a/vscode/src/commands/utils.ts +++ b/vscode/src/commands/utils.ts @@ -98,7 +98,7 @@ export const wrapCommandWithProgress = (lsCommand: string, title: string, log?: resolve(res); } else { if (log) { - LOGGER.error(`Command ${lsCommand} takes too long to start`); + LOGGER.error(`Result not obtained while executing ${lsCommand}`); } reject(res); } @@ -107,6 +107,7 @@ export const wrapCommandWithProgress = (lsCommand: string, title: string, log?: if (log) { LOGGER.error(`command ${lsCommand} executed with error: ${JSON.stringify(err)}`); } + reject(err); } } else { reject(l10n.value("jdk.extension.progressBar.error_msg.cannotRun", { lsCommand: lsCommand, client: c })); diff --git a/vscode/src/configurations/configuration.ts b/vscode/src/configurations/configuration.ts index 5ae389c..eea3132 100644 --- a/vscode/src/configurations/configuration.ts +++ b/vscode/src/configurations/configuration.ts @@ -24,9 +24,10 @@ export const configKeys = { formatPrefs: 'format', hintPrefs: 'hints', importPrefs: 'java.imports', - runConfig: 'runCofig', runConfigVmOptions: 'runConfig.vmOptions', + runConfigArguments: 'runConfig.arguments', runConfigCwd: 'runConfig.cwd', + runConfigEnv: 'runConfig.env', verbose: 'verbose', userdir: 'userdir', revealInActivteProj: "revealActiveInProjects" diff --git a/vscode/src/configurations/handlers.ts b/vscode/src/configurations/handlers.ts index 4bbc64b..caf1ea4 100644 --- a/vscode/src/configurations/handlers.ts +++ b/vscode/src/configurations/handlers.ts @@ -14,7 +14,7 @@ limitations under the License. */ -import { extensions, workspace, WorkspaceConfiguration } from "vscode"; +import { ConfigurationTarget, extensions, workspace, WorkspaceConfiguration } from "vscode"; import { builtInConfigKeys, configKeys } from "./configuration"; import { extConstants, NODE_WINDOWS_LABEL } from "../constants"; import * as os from 'os'; @@ -28,12 +28,12 @@ export const getConfiguration = (key: string = extConstants.COMMAND_PREFIX): Wor } export const getConfigurationValue = <T>(key: string, defaultValue: T | undefined = undefined): T => { - const conf = workspace.getConfiguration(extConstants.COMMAND_PREFIX); - return defaultValue != undefined ? conf.get(key, defaultValue) : conf.get(key) as T; + const conf = getConfiguration(); + return defaultValue != undefined ? conf.get(key, defaultValue) : conf.get(key) as T; } -export const updateConfigurationValue = <T>(key: string, newValue: T): void => { - workspace.getConfiguration(extConstants.COMMAND_PREFIX).update(key, newValue); +export const updateConfigurationValue = <T>(key: string, newValue: T, configurationTarget: ConfigurationTarget | boolean | null = null): void => { + getConfiguration().update(key, newValue, configurationTarget); } export const getBuiltinConfigurationValue = <T>(key: string, defaultValue: T | undefined = undefined): T => { @@ -115,8 +115,9 @@ export const isDarkColorThemeHandler = (): boolean => { export const userdirHandler = (): string => { const userdirScope = process.env['nbcode_userdir'] || getConfigurationValue(configKeys.userdir, "local"); - const userdirParentDir = userdirScope === "local" - ? globalVars.extensionInfo.getWorkspaceStorage()?.fsPath + const workspaceStoragePath = globalVars.extensionInfo.getWorkspaceStorage()?.fsPath; + const userdirParentDir = userdirScope === "local" && workspaceStoragePath + ? workspaceStoragePath : globalVars.extensionInfo.getGlobalStorage().fsPath; if (!userdirParentDir) { @@ -142,4 +143,8 @@ export const userdirHandler = (): string => { export const isNbJavacDisabledHandler = (): boolean => { return getConfigurationValue(configKeys.disableNbJavac, false); +} + +export const isNetbeansVerboseEnabled = (): boolean => { + return getConfigurationValue(configKeys.verbose, false); } \ No newline at end of file diff --git a/vscode/src/extension.ts b/vscode/src/extension.ts index 0740a56..852aff0 100644 --- a/vscode/src/extension.ts +++ b/vscode/src/extension.ts @@ -23,10 +23,7 @@ 'use strict'; -import { ExtensionContext, TextEditorDecorationType } from 'vscode'; - - -import * as vscode from 'vscode'; +import { ExtensionContext, TextEditorDecorationType, Uri } from 'vscode'; import { NbTestAdapter } from './views/TestViewController'; import { SetTextEditorDecorationParams } from './lsp/protocol'; import * as launchConfigurations from './launchConfigurations'; @@ -40,7 +37,6 @@ import { VSNetBeansAPI } from './lsp/types'; import { registerDebugger } from './debugger/debugger'; import { registerConfigChangeListeners } from './configurations/listener'; import { registerFileProviders } from './lsp/listeners/textDocumentContentProvider'; -import { createViews } from './views/initializer'; export namespace globalVars { export const listeners = new Map<string, string[]>(); @@ -52,7 +48,7 @@ export namespace globalVars { export let nbProcessManager: NbProcessManager | null; export let testAdapter: NbTestAdapter | undefined; export let decorations = new Map<string, TextEditorDecorationType>(); - export let decorationParamsByUri = new Map<vscode.Uri, SetTextEditorDecorationParams>(); + export let decorationParamsByUri = new Map<Uri, SetTextEditorDecorationParams>(); } @@ -66,7 +62,6 @@ export function activate(context: ExtensionContext): VSNetBeansAPI { registerDebugger(context); subscribeCommands(context); - createViews(context); registerFileProviders(context); launchConfigurations.updateLaunchConfig(); diff --git a/vscode/src/lsp/initializer.ts b/vscode/src/lsp/initializer.ts index 006113d..1c94aeb 100644 --- a/vscode/src/lsp/initializer.ts +++ b/vscode/src/lsp/initializer.ts @@ -27,6 +27,7 @@ import { NbLanguageClient } from "./nbLanguageClient"; import { registerListenersAfterClientInit } from "../views/listener"; import { registerNotificationListeners } from "./listeners/notifications/register"; import { registerRequestListeners } from "./listeners/requests/register"; +import { createViews } from "../views/initializer"; const establishConnection = () => new Promise<StreamInfo>((resolve, reject) => { const nbProcess = globalVars.nbProcessManager?.getProcess(); @@ -112,7 +113,7 @@ export const clientInit = () => { registerListenersAfterClientInit(); registerNotificationListeners(client); registerRequestListeners(client); - + createViews(); LOGGER.log('Language Client: Ready'); globalVars.clientPromise.initializedSuccessfully(client); diff --git a/vscode/src/lsp/launchOptions.ts b/vscode/src/lsp/launchOptions.ts index c9c24c2..d104414 100644 --- a/vscode/src/lsp/launchOptions.ts +++ b/vscode/src/lsp/launchOptions.ts @@ -14,7 +14,7 @@ limitations under the License. */ import { builtInConfigKeys, configKeys } from "../configurations/configuration" -import { isDarkColorThemeHandler, isNbJavacDisabledHandler, jdkHomeValueHandler, lspServerVmOptionsHandler, projectSearchRootsValueHandler, userdirHandler } from "../configurations/handlers"; +import { isDarkColorThemeHandler, isNetbeansVerboseEnabled, jdkHomeValueHandler, lspServerVmOptionsHandler, projectSearchRootsValueHandler, userdirHandler } from "../configurations/handlers"; import { l10n } from "../localiser"; import { isString } from "../utils"; import { userDefinedLaunchOptionsType } from "./types" @@ -31,9 +31,10 @@ export const getUserConfigLaunchOptionsDefaults = (): userDefinedLaunchOptionsTy }, [configKeys.disableProjSearchLimit]: { value: projectSearchRootsValueHandler(), - optionToPass: '-J-Dproject.limitScanRoot=', - },[configKeys.verbose]: { - value: isNbJavacDisabledHandler(), + optionToPass: '-J-Dproject.limitScanRoot=' + }, + [configKeys.verbose]: { + value: isNetbeansVerboseEnabled(), optionToPass: '-J-Dnetbeans.logger.console=' }, [builtInConfigKeys.vscodeTheme]: { diff --git a/vscode/src/lsp/nbcode.ts b/vscode/src/lsp/nbcode.ts index 6b75e45..f110f90 100644 --- a/vscode/src/lsp/nbcode.ts +++ b/vscode/src/lsp/nbcode.ts @@ -67,10 +67,12 @@ const processOnDataHandler = (nbProcessManager: NbProcessManager, text: string, if (nbProcessManager) { globalVars.clientPromise.activationPending = false; } + if(!text.match(/with hash/)){ + LOGGER.logNoNL(text); + } if (nbProcessManager.getStdOut() == null) { return; } - LOGGER.logNoNL(text); isOut ? nbProcessManager.appendStdOut(text) : nbProcessManager.appendStdErr(text); if (nbProcessManager.getStdOut()?.match(/org.netbeans.modules.java.lsp.server/)) { @@ -87,7 +89,7 @@ const processOnCloseHandler = (nbProcessManager: NbProcessManager, code: number) window.showWarningMessage(l10n.value("jdk.extension.lspServer.warning_message.serverExited", { SERVER_NAME: extConstants.SERVER_NAME, code })); } } - if (nbProcessManager.getStdOut()?.match(/Cannot find java/) || (os.type() === NODE_WINDOWS_LABEL && !globalVars.deactivated)) { + if (nbProcessManager.getStdErr()?.match(/Cannot find java/) || (os.type() === NODE_WINDOWS_LABEL && !globalVars.deactivated)) { jdkDownloaderPrompt(); } if (nbProcessManager.getStdOut() != null) { diff --git a/vscode/src/views/initializer.ts b/vscode/src/views/initializer.ts index f068f5b..88228d6 100644 --- a/vscode/src/views/initializer.ts +++ b/vscode/src/views/initializer.ts @@ -25,12 +25,14 @@ import { configKeys } from "../configurations/configuration"; import { initializeRunConfiguration } from "../utils"; import { NbTestAdapter } from "./TestViewController"; -export async function createViews(context: ExtensionContext) { +export async function createViews() { + const context = globalVars.extensionInfo.getExtensionContext(); createRunConfigurationView(context); const client = await globalVars.clientPromise.client; createProjectView(client); globalVars.testAdapter = new NbTestAdapter(); } + function createRunConfigurationView(context: ExtensionContext) { initializeRunConfiguration().then(initialized => { if (initialized) { @@ -59,9 +61,8 @@ async function createProjectView(client: NbLanguageClient) { } tv.reveal(vis, { select: true, focus: false, expand: false }); } - const netbeansConfig = workspace.getConfiguration(extConstants.COMMAND_PREFIX); globalVars.extensionInfo.pushSubscription(window.onDidChangeActiveTextEditor(ed => { - if (netbeansConfig.get("revealActiveInProjects")) { + if (getConfigurationValue(configKeys.revealInActivteProj)) { revealActiveEditor(ed); } })); diff --git a/vscode/src/views/runConfiguration.ts b/vscode/src/views/runConfiguration.ts index 09340ad..97da50d 100644 --- a/vscode/src/views/runConfiguration.ts +++ b/vscode/src/views/runConfiguration.ts @@ -22,13 +22,9 @@ import * as vscode from 'vscode'; import { homedir } from 'os'; import { l10n } from '../localiser'; -import { getConfiguration } from '../configurations/handlers'; +import { getConfigurationValue, updateConfigurationValue } from '../configurations/handlers'; import { configKeys } from '../configurations/configuration'; - - - - class RunConfigurationNodeProvider implements vscode.TreeDataProvider<vscode.TreeItem> { private _onDidChangeTreeData: vscode.EventEmitter<vscode.TreeItem | undefined | null> = new vscode.EventEmitter<vscode.TreeItem | undefined | null>(); @@ -70,7 +66,7 @@ class RunConfigurationNode extends vscode.TreeItem { this.settingsKey = settingsKey; - this.value = this.getConfig().get(this.settingsKey); + this.value = getConfigurationValue(this.settingsKey); this.updateNode(); } @@ -96,29 +92,24 @@ class RunConfigurationNode extends vscode.TreeItem { setValue(value: string | undefined) { this.value = value; - this.getConfig().update(this.settingsKey, this.value, vscode.workspace.name || vscode.workspace.workspaceFile ? null : true); + updateConfigurationValue(this.settingsKey, this.value, vscode.workspace.name || vscode.workspace.workspaceFile ? null : true); this.updateNode(); } updateNode(reload?: boolean) { if (reload) { - this.value = this.getConfig().get(this.settingsKey) as string; + this.value = getConfigurationValue(this.settingsKey) as string; } this.description = this.value ? this.value : l10n.value("jdk.extension.runConfig.default.label"); this.tooltip = `${this.label} ${this.description}`; runConfigurationNodeProvider.refresh(); } - - getConfig(): vscode.WorkspaceConfiguration { - return getConfiguration(configKeys.runConfig); - } - } class ArgumentsNode extends RunConfigurationNode { constructor() { - super(l10n.value("jdk.extension.runConfig.arguments.label"), l10n.value("jdk.extension.runConfig.arguments.prompt"), l10n.value("jdk.extension.runConfig.example.label", { data: "foo bar" }), 'arguments'); + super(l10n.value("jdk.extension.runConfig.arguments.label"), l10n.value("jdk.extension.runConfig.arguments.prompt"), l10n.value("jdk.extension.runConfig.example.label", { data: "foo bar" }), configKeys.runConfigArguments); } } @@ -127,7 +118,7 @@ export const argumentsNode = new ArgumentsNode(); class VMOptionsNode extends RunConfigurationNode { constructor() { - super(l10n.value("jdk.extension.runConfig.vmoptions.label"), l10n.value("jdk.extension.runConfig.vmoptions.prompt"), l10n.value("jdk.extension.runConfig.example.label", { data: "-Xmx512m -Xms256m" }), 'vmOptions'); + super(l10n.value("jdk.extension.runConfig.vmoptions.label"), l10n.value("jdk.extension.runConfig.vmoptions.prompt"), l10n.value("jdk.extension.runConfig.example.label", { data: "-Xmx512m -Xms256m" }), configKeys.runConfigVmOptions); } } @@ -136,7 +127,7 @@ export const vmOptionsNode = new VMOptionsNode(); class EnvironmentVariablesNode extends RunConfigurationNode { constructor() { - super(l10n.value("jdk.extension.runConfig.env.label"), l10n.value("jdk.extension.runConfig.env.prompt"), l10n.value("jdk.extension.runConfig.example.label", { data: "var1=one, varTwo=2" }), 'env'); + super(l10n.value("jdk.extension.runConfig.env.label"), l10n.value("jdk.extension.runConfig.env.prompt"), l10n.value("jdk.extension.runConfig.example.label", { data: "var1=one, varTwo=2" }), configKeys.runConfigEnv); } } @@ -145,7 +136,7 @@ export const environmentVariablesNode = new EnvironmentVariablesNode(); class WorkingDirectoryNode extends RunConfigurationNode { constructor() { - super(l10n.value("jdk.extension.runConfig.wrkdir.label"), l10n.value("jdk.extension.runConfig.wrkdir.prompt"), WorkingDirectoryNode.getExample(), 'cwd'); + super(l10n.value("jdk.extension.runConfig.wrkdir.label"), l10n.value("jdk.extension.runConfig.wrkdir.prompt"), WorkingDirectoryNode.getExample(), configKeys.runConfigCwd); } static getExample(): string { diff --git a/vscode/src/webviews/jdkDownloader/action.ts b/vscode/src/webviews/jdkDownloader/action.ts index d1dd5bc..4b835e5 100644 --- a/vscode/src/webviews/jdkDownloader/action.ts +++ b/vscode/src/webviews/jdkDownloader/action.ts @@ -24,6 +24,8 @@ import * as cp from 'child_process'; import { promisify } from "util"; import { l10n } from "../../localiser"; import { LOGGER } from "../../logger"; +import { updateConfigurationValue } from "../../configurations/handlers"; +import { configKeys } from "../../configurations/configuration"; export class JdkDownloaderAction { public static readonly MANUAL_INSTALLATION_TYPE = "manual"; @@ -90,7 +92,7 @@ export class JdkDownloaderAction { private startInstallation = async () => { try { if (this.installType === JdkDownloaderAction.MANUAL_INSTALLATION_TYPE) { - workspace.getConfiguration('jdk').update('jdkhome', this.installationPath, true); + updateConfigurationValue(configKeys.jdkHome, this.installationPath, true); await this.installationCompletion(); LOGGER.log(`manual JDK installation completed successfully`); @@ -260,8 +262,7 @@ export class JdkDownloaderAction { if (this.osType === 'macOS') { binPath = path.join(newDirectoryPath, 'Contents', 'Home'); } - - workspace.getConfiguration('jdk').update('jdkhome', binPath, true); + updateConfigurationValue(configKeys.jdkHome, binPath, true); LOGGER.log(`Finishing up installation...`); this.installationCleanup(tempDirectoryPath, newDirectoryPath); From cffad1facf51cedbc84146c1399793a981bb537f Mon Sep 17 00:00:00 2001 From: Achal Talati <achal.talati@oracle.com> Date: Thu, 24 Oct 2024 16:40:47 +0530 Subject: [PATCH 17/17] Updated BUILD.md --- BUILD.md | 96 ++++++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 79 insertions(+), 17 deletions(-) diff --git a/BUILD.md b/BUILD.md index 3d9490f..8604f3b 100644 --- a/BUILD.md +++ b/BUILD.md @@ -27,7 +27,7 @@ ## Prerequisities -- JDK, version 11 or later upto JDK 22 +- JDK, version 17 or later - Ant, latest version - Maven, latest version - Node.js, latest LTS (to build VSIX) @@ -36,11 +36,12 @@ ```bash $ git clone --recurse-submodules https://github.com/oracle/javavscode.git +$ cd javavscode # the following target requires git executable to be on PATH: $ ant apply-patches $ ant build-netbeans -#Note if you do not wish to have l10n in scope then add no-l10n before any ant invocation target at beginning as below, by default l10n is enabled +# Note if you do not wish to have l10n in scope then add no-l10n before any ant invocation target at beginning as below, by default l10n is enabled $ ant no-l10n apply-patches $ ant no-l10n build-netbeans ``` @@ -58,26 +59,82 @@ The typical file name is `oracle-java-0.1.0.vsix`. ### Building for Development -If you want to develop the extension, use these steps for building instead: - -```bash -ant build-lsp-server -``` - -This target is faster than building the `.vsix` file. Find the instructions -for running and debugging below. - -### Cleaning +If you're developing the extension, follow these steps to build the project for faster iterations without packaging a `.vsix` file: + +1. Run the following command to build the Language Server Protocol (LSP) server: + ```bash + $ ant build-lsp-server + ``` +2. Navigate to the `vscode` folder: + ```bash + $ cd vscode + ``` +3. Install the necessary Node.js dependencies: + ```bash + $ npm install + ``` +4. Start the build watcher to automatically rebuild the extension on file changes: + ```bash + $ npm run watch + ``` + +This process is faster than packaging the extension as a `.vsix` file, allowing for quicker development cycles. + +### Debugging the Extension + +Follow these steps to debug both the extension's TypeScript code and the NetBeans server code: + +#### 1. Debugging TypeScript Code (VS Code Extension) +1. Open the `vscode` folder in VS Code. +2. Press `F5` to launch and debug the extension. + +#### 2. Debugging NetBeans Code (Server Side) +1. Open the **Command Palette** using `Ctrl+Shift+P` (or `Cmd+Shift+P` on macOS). +2. Search for **Preferences: Open User Settings (JSON)** and select it. +3. Locate or add the `jdk.serverVmOptions` setting in the JSON file and append the following arguments: + ```json + "jdk.serverVmOptions": [ + "-Xdebug", + "-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=8000" + ] + ``` +4. Start your debugging session. The NetBeans server will suspend execution and wait for a remote debugger to connect at port `8000`. + +[NOTE: If you are using multiple profiles in VS Code, then set above configuration in appropriate profile + +This configuration will enable you to debug both the extension’s TypeScript code and the NetBeans server-side code. + +#### To enable Netbeans logs in the output channel of VS Code IDE + +##### Option 1: Enable `jdk.verbose` Logging +1. Open the **Command Palette** using `Ctrl+Shift+P` (or `Cmd+Shift+P` on macOS). +2. Search for **Preferences: Open User Settings (JSON)** and select it. +3. In the settings JSON file, add or update the following setting: + ```json + "jdk.verbose": true + ``` + +##### Option 2: Enable NetBeans Logs via JVM Options +1. Open the **Command Palette** using `Ctrl+Shift+P` (or `Cmd+Shift+P` on macOS). +2. Search for **Preferences: Open User Settings (JSON)** and select it. +3. Locate or add the `jdk.serverVmOptions` setting and append the following argument to the array: + ```json + "jdk.serverVmOptions": ["-J-Dnetbeans.logger.console=true"] + ``` + +Both options will enable logging from the NetBeans server in the VS Code Output Channel. + +### Cleaning the Extension Often it is also important to properly clean everything. Use: ```bash -ant clean-vscode-ext -cd netbeans/ -netbeans$ ant clean +$ ant clean-vscode-ext +$ cd netbeans/ +$ ant clean ``` -### Testing +### Testing the Extension The `java.lsp.server` module has classical (as other NetBeans modules) tests. The most important one is [ServerTest](https://github.com/apache/netbeans/blob/master/java/java.lsp.server/test/unit/src/org/netbeans/modules/java/lsp/server/protocol/ServerTest.java) @@ -99,15 +156,20 @@ $ npm_config_https_proxy=http://your.proxy.com:port ant test-vscode-ext when executing the tests for the first time. That shall overcome the proxy and download an instance of `code` to execute the tests with. -## Working with submodules +## Working with git submodules + This project uses [git submodules](https://git-scm.com/book/en/v2/Git-Tools-Submodules) . In particular netbeans and netbeans-l10n are submodules pointing to specific commits in their respective repositories . + ### Switching Branches + Add the --recurse-submodules flag to the git checkout command to update the submodules during the checkout. ```bash git checkout --recurse-submodules <branch_name> ``` Note:- Merging branches with submodules pointing to different commits can be tricky. Refer the [git submodules](https://git-scm.com/book/en/v2/Git-Tools-Submodules) for more details on the same. + ### Changing submodules versions + ```bash # Fetching changes from remote submodule repositories git submodule update --remote