This is an automated email from the ASF dual-hosted git repository. jhorvath pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/netbeans.git
commit 8016c9a8961fcfe3a83e909568af8a3e5808dd2c Author: Ondřej Douda <ondrej.do...@oracle.com> AuthorDate: Tue Jun 20 17:51:53 2023 +0200 Fix Properties save. + Save Error handling. + Review fixes. + Command prefix fix. --- .../server/explorer/NodePropertiesProvider.java | 5 +- java/java.lsp.server/vscode/src/extension.ts | 266 +++++++++++---------- .../vscode/src/propertiesView/controlTypes.ts | 14 +- .../src/propertiesView/propertiesHtmlBuilder.ts | 18 +- .../vscode/src/propertiesView/propertiesView.ts | 44 ++-- .../vscode/src/propertiesView/script.ts | 10 +- java/java.lsp.server/vscode/src/typesUtil.ts | 17 +- 7 files changed, 200 insertions(+), 174 deletions(-) diff --git a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/explorer/NodePropertiesProvider.java b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/explorer/NodePropertiesProvider.java index 9aabebca10..ccf68d153d 100644 --- a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/explorer/NodePropertiesProvider.java +++ b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/explorer/NodePropertiesProvider.java @@ -52,8 +52,9 @@ import org.openide.util.lookup.ServiceProvider; public class NodePropertiesProvider extends CodeActionsProvider { private static final Logger LOG = Logger.getLogger(NodePropertiesProvider.class.getName()); - private static final String COMMAND_GET_NODE_PROPERTIES = "java.node.properties.get"; // NOI18N - private static final String COMMAND_SET_NODE_PROPERTIES = "java.node.properties.set"; // NOI18N + private static final String COMMAND_PREFIX = "nbls."; + private static final String COMMAND_GET_NODE_PROPERTIES = COMMAND_PREFIX + "node.properties.get"; // NOI18N + private static final String COMMAND_SET_NODE_PROPERTIES = COMMAND_PREFIX + "node.properties.set"; // NOI18N private static final String PROP_NAME = "propName"; // NOI18N private static final String PROP_DNAME = "propDispName"; // NOI18N diff --git a/java/java.lsp.server/vscode/src/extension.ts b/java/java.lsp.server/vscode/src/extension.ts index b3bef4980b..1ed5bb07c9 100644 --- a/java/java.lsp.server/vscode/src/extension.ts +++ b/java/java.lsp.server/vscode/src/extension.ts @@ -21,10 +21,10 @@ import { commands, window, workspace, ExtensionContext, ProgressLocation, TextEditorDecorationType } from 'vscode'; import { - LanguageClient, - LanguageClientOptions, - ServerOptions, - StreamInfo + LanguageClient, + LanguageClientOptions, + ServerOptions, + StreamInfo } from 'vscode-languageclient/node'; import { @@ -45,11 +45,10 @@ import * as path from 'path'; import { ChildProcess } from 'child_process'; import * as vscode from 'vscode'; import * as launcher from './nbcode'; -import { NbTestAdapter } from './testAdapter'; -import { - asRanges, StatusMessageRequest, ShowStatusMessageParams, QuickPickRequest, InputBoxRequest, MutliStepInputRequest, TestProgressNotification, DebugConnector, - TextEditorDecorationCreateRequest, TextEditorDecorationSetNotification, TextEditorDecorationDisposeNotification, HtmlPageRequest, HtmlPageParams, - ExecInHtmlPageRequest, SetTextEditorDecorationParams, ProjectActionParams, UpdateConfigurationRequest, QuickPickStep, InputBoxStep +import {NbTestAdapter} from './testAdapter'; +import { asRanges, StatusMessageRequest, ShowStatusMessageParams, QuickPickRequest, InputBoxRequest, MutliStepInputRequest, TestProgressNotification, DebugConnector, + TextEditorDecorationCreateRequest, TextEditorDecorationSetNotification, TextEditorDecorationDisposeNotification, HtmlPageRequest, HtmlPageParams, + ExecInHtmlPageRequest, SetTextEditorDecorationParams, ProjectActionParams, UpdateConfigurationRequest, QuickPickStep, InputBoxStep } from './protocol'; import * as launchConfigurations from './launchConfigurations'; import { createTreeViewService, TreeViewService, TreeItemDecorator, Visualizer, CustomizableTreeDataProvider } from './explorer'; @@ -64,14 +63,14 @@ const API_VERSION : string = "1.0"; const DATABASE: string = 'Database'; let client: Promise<NbLanguageClient>; let testAdapter: NbTestAdapter | undefined; -let nbProcess: ChildProcess | null = null; +let nbProcess : ChildProcess | null = null; let debugPort: number = -1; let consoleLog: boolean = !!process.env['ENABLE_CONSOLE_LOG']; export class NbLanguageClient extends LanguageClient { private _treeViewService: TreeViewService; - constructor(id: string, name: string, s: ServerOptions, log: vscode.OutputChannel, c: LanguageClientOptions) { + constructor (id : string, name: string, s : ServerOptions, log : vscode.OutputChannel, c : LanguageClientOptions) { super(id, name, s, c); this._treeViewService = createTreeViewService(log, this); } @@ -108,7 +107,7 @@ export function enableConsoleLog() { console.log("enableConsoleLog"); } -export function findClusters(myPath: string): string[] { +export function findClusters(myPath : string): string[] { let clusters = []; for (let e of vscode.extensions.all) { if (e.extensionPath === myPath) { @@ -135,8 +134,8 @@ export function findClusters(myPath: string): string[] { } // for tests only ! -export function awaitClient(): Promise<NbLanguageClient> { - const c: Promise<NbLanguageClient> = client; +export function awaitClient() : Promise<NbLanguageClient> { + const c : Promise<NbLanguageClient> = client; if (c && !(c instanceof InitialPromise)) { return c; } @@ -144,7 +143,7 @@ export function awaitClient(): Promise<NbLanguageClient> { if (!nbcode) { return Promise.reject(new Error("Extension not installed.")); } - const t: Thenable<NbLanguageClient> = nbcode.activate().then(nc => { + const t : Thenable<NbLanguageClient> = nbcode.activate().then(nc => { if (client === undefined || client instanceof InitialPromise) { throw new Error("Client not available"); } else { @@ -154,9 +153,9 @@ export function awaitClient(): Promise<NbLanguageClient> { return Promise.resolve(t); } -function findJDK(onChange: (path: string | null) => void): void { - let nowDark: boolean = isDarkColorTheme(); - let nowJavaEnabled: boolean = isJavaSupportEnabled(); +function findJDK(onChange: (path : string | null) => void): void { + let nowDark : boolean = isDarkColorTheme(); + let nowJavaEnabled : boolean = isJavaSupportEnabled(); function find(): string | null { let nbJdk = workspace.getConfiguration('netbeans').get('jdkhome'); if (nbJdk) { @@ -184,7 +183,7 @@ function findJDK(onChange: (path: string | null) => void): void { if (timeout) { return; } - let interested: boolean = false; + let interested : boolean = false; if (params.affectsConfiguration('netbeans') || params.affectsConfiguration('java')) { interested = true; } else if (params.affectsConfiguration('workbench.colorTheme')) { @@ -213,11 +212,11 @@ function findJDK(onChange: (path: string | null) => void): void { } interface VSNetBeansAPI { - version: string; + version : string; apiVersion: string; } -function contextUri(ctx: any): vscode.Uri | undefined { +function contextUri(ctx : any) : vscode.Uri | undefined { if (ctx?.fsPath) { return ctx as vscode.Uri; } else if (ctx?.resourceUri) { @@ -244,14 +243,14 @@ function contextUri(ctx: any): vscode.Uri | undefined { * @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> { +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, + action : action, + configuration : configuration, } as ProjectActionParams; for (let item of args) { - let u: vscode.Uri | undefined; + let u : vscode.Uri | undefined; if (item?.fsPath) { items.push((item.fsPath as vscode.Uri).toString()); } else if (item?.resourceUri) { @@ -263,10 +262,10 @@ function wrapProjectActionWithProgress(action: string, configuration: string | u return wrapCommandWithProgress('java.project.run.action', title, log, showOutput, actionParams, ...items); } -function wrapCommandWithProgress(lsCommand: string, title: string, log?: vscode.OutputChannel, showOutput?: boolean, ...args: any[]): Thenable<unknown> { +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 client; + let c : LanguageClient = await client; const commands = await vscode.commands.getCommands(); if (commands.includes(lsCommand)) { p.report({ message: title }); @@ -301,7 +300,7 @@ function wrapCommandWithProgress(lsCommand: string, title: string, log?: vscode. * 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<NbLanguageClient> { - constructor(f: (resolve: (value: NbLanguageClient | PromiseLike<NbLanguageClient>) => void, reject: (reason?: any) => void) => void) { + constructor(f : (resolve: (value: NbLanguageClient | PromiseLike<NbLanguageClient>) => void, reject: (reason?: any) => void) => void) { super(f); } } @@ -310,7 +309,7 @@ class InitialPromise extends Promise<NbLanguageClient> { * Determines the outcome, if there's a conflict betwee RH Java and us: disable java, enable java, ask the user. * @returns false, if java should be disablde; true, if enabled. Undefined if no config is present, ask the user */ -function shouldEnableConflictingJavaSupport(): boolean | undefined { +function shouldEnableConflictingJavaSupport() : boolean | undefined { // backwards compatibility; remove in NBLS 19 if (vscode.extensions.getExtension('oracle-labs-graalvm.gcn')) { return false; @@ -335,8 +334,9 @@ function shouldEnableConflictingJavaSupport(): boolean | undefined { export function activate(context: ExtensionContext): VSNetBeansAPI { let log = vscode.window.createOutputChannel("Apache NetBeans Language Server"); - var clientResolve: (x: NbLanguageClient) => void; - var clientReject: (err: any) => void; + + 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) => { @@ -348,7 +348,7 @@ export function activate(context: ExtensionContext): VSNetBeansAPI { let conf = workspace.getConfiguration(); if (conf.get("netbeans.conflict.check")) { if (conf.get("netbeans.javaSupport.enabled")) { - const e: boolean | undefined = shouldEnableConflictingJavaSupport(); + const e : boolean | undefined = shouldEnableConflictingJavaSupport(); if (!e && vscode.extensions.getExtension('redhat.java')) { if (e === false) { // do not ask, an extension wants us to disable on conflict @@ -396,7 +396,7 @@ export function activate(context: ExtensionContext): VSNetBeansAPI { }); //register debugger: - let debugTrackerFactory = new NetBeansDebugAdapterTrackerFactory(); + let debugTrackerFactory =new NetBeansDebugAdapterTrackerFactory(); context.subscriptions.push(vscode.debug.registerDebugAdapterTrackerFactory('java+', debugTrackerFactory)); let configInitialProvider = new NetBeansConfigurationInitialProvider(); context.subscriptions.push(vscode.debug.registerDebugConfigurationProvider('java+', configInitialProvider, vscode.DebugConfigurationProviderTriggerKind.Initial)); @@ -413,7 +413,7 @@ export function activate(context: ExtensionContext): VSNetBeansAPI { // initialize Run Configuration initializeRunConfiguration().then(initialized => { - if (initialized) { + if (initialized) { context.subscriptions.push(vscode.debug.registerDebugConfigurationProvider('java+', new DBConfigurationProvider())); context.subscriptions.push(vscode.debug.registerDebugConfigurationProvider('java+', runConfigurationProvider)); context.subscriptions.push(vscode.debug.registerDebugConfigurationProvider('java', runConfigurationProvider)); @@ -437,7 +437,7 @@ export function activate(context: ExtensionContext): VSNetBeansAPI { 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) { + for(let r of res) { if (typeof r === 'string') { let newFile = vscode.Uri.parse(r as string); await vscode.window.showTextDocument(newFile, { preview: false }); @@ -524,7 +524,7 @@ export function activate(context: ExtensionContext): VSNetBeansAPI { await commands.executeCommand(selected.userData.command.command, ...(selected.userData.command.arguments || [])); } })); - const mergeWithLaunchConfig = (dconfig: vscode.DebugConfiguration) => { + const mergeWithLaunchConfig = (dconfig : vscode.DebugConfiguration) => { const folder = vscode.workspace.workspaceFolders?.[0]; const uri = folder?.uri; if (uri) { @@ -546,11 +546,11 @@ export function activate(context: ExtensionContext): VSNetBeansAPI { } } - const runDebug = async (noDebug: boolean, testRun: boolean, uri: any, methodName?: string, launchConfiguration?: string, project: boolean = false,) => { + const runDebug = async (noDebug: boolean, testRun: boolean, uri: any, methodName?: string, launchConfiguration?: string, project : boolean = false, ) => { const docUri = contextUri(uri); if (docUri) { const workspaceFolder = vscode.workspace.getWorkspaceFolder(docUri); - const debugConfig: vscode.DebugConfiguration = { + const debugConfig : vscode.DebugConfiguration = { type: "java+", name: "Java Single Debug", request: "launch", @@ -562,10 +562,10 @@ export function activate(context: ExtensionContext): VSNetBeansAPI { debugConfig['projectFile'] = docUri.toString(); debugConfig['project'] = true; } else { - debugConfig['mainClass'] = docUri.toString(); + debugConfig['mainClass'] = docUri.toString(); } mergeWithLaunchConfig(debugConfig); - const debugOptions: vscode.DebugSessionOptions = { + const debugOptions : vscode.DebugSessionOptions = { noDebug: noDebug, } const ret = await vscode.debug.startDebugging(workspaceFolder, debugConfig, debugOptions); @@ -623,40 +623,42 @@ export function activate(context: ExtensionContext): VSNetBeansAPI { context.subscriptions.push(commands.registerCommand('nbls.startup.condition', async () => { return client; })); - context.subscriptions.push(commands.registerCommand('nbls.node.properties.edit', async (node) => await PropertiesView.createOrShow(context, node))); + + context.subscriptions.push(commands.registerCommand('nbls.node.properties.edit', + async (node) => await PropertiesView.createOrShow(context, node))); launchConfigurations.updateLaunchConfig(); // register completions: launchConfigurations.registerCompletion(context); return Object.freeze({ - version: API_VERSION, - apiVersion: API_VERSION + version : API_VERSION, + apiVersion : API_VERSION }); } /** * Pending maintenance (install) task, activations should be chained after it. */ -let maintenance: Promise<void> | null; +let maintenance : Promise<void> | null; /** * Pending activation flag. Will be cleared when the process produces some message or fails. */ -let activationPending: boolean = false; +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 { +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]; + let setClient : [(c : NbLanguageClient) => void, (err : any) => void]; client = new Promise<NbLanguageClient>((clientOK, clientErr) => { setClient = [ - function (c: NbLanguageClient) { + function (c : NbLanguageClient) { clientOK(c); if (clientResolve) { clientResolve(c); @@ -670,7 +672,7 @@ function activateWithJDK(specifiedJDK: string | null, context: ExtensionContext, ] //setClient = [ clientOK, clientErr ]; }); - const a: Promise<void> | null = maintenance; + const a : Promise<void> | null = maintenance; commands.executeCommand('setContext', 'nbJavaLSReady', false); commands.executeCommand('setContext', 'dbAddConnectionPresent', true); @@ -690,7 +692,7 @@ function activateWithJDK(specifiedJDK: string | null, context: ExtensionContext, } -function killNbProcess(notifyKill: boolean, log: vscode.OutputChannel, specProcess?: ChildProcess): Promise<void> { +function killNbProcess(notifyKill : boolean, log : vscode.OutputChannel, specProcess?: ChildProcess) : Promise<void> { const p = nbProcess; handleLog(log, "Request to kill LSP server."); if (p && (!specProcess || specProcess == p)) { @@ -699,7 +701,7 @@ function killNbProcess(notifyKill: boolean, log: vscode.OutputChannel, specProce } return new Promise((resolve, reject) => { nbProcess = null; - p.on('close', function (code: number) { + p.on('close', function(code: number) { handleLog(log, "LSP server closed: " + p.pid) resolve(); }); @@ -722,23 +724,23 @@ function killNbProcess(notifyKill: boolean, log: vscode.OutputChannel, specProce * 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 { +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']; + const themeList : object[] = ext.packageJSON?.contributes && ext.packageJSON?.contributes['themes']; if (!themeList) { continue; } - let t: any; + let t : any; for (t of themeList) { if (t.id !== themeName) { continue; } const uiTheme = t.uiTheme; - if (typeof (uiTheme) == 'string') { + if (typeof(uiTheme) == 'string') { if (uiTheme.includes('-dark') || uiTheme.includes('-black')) { return true; } @@ -748,15 +750,15 @@ function isDarkColorTheme(): boolean { return false; } -function isJavaSupportEnabled(): boolean { +function isJavaSupportEnabled() : boolean { return workspace.getConfiguration('netbeans')?.get('javaSupport.enabled') as boolean; } -function doActivateWithJDK(specifiedJDK: string | null, context: ExtensionContext, log: vscode.OutputChannel, notifyKill: boolean, - setClient: [(c: NbLanguageClient) => void, (err: any) => void] +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) { + let restartWithJDKLater : ((time: number, n: boolean) => void) = function restartLater(time: number, n : boolean) { handleLog(log, `Restart of Apache Language Server requested in ${(time / 1000)} s.`); setTimeout(() => { activateWithJDK(specifiedJDK, context, log, n); @@ -764,7 +766,7 @@ function doActivateWithJDK(specifiedJDK: string | null, context: ExtensionContex }; const netbeansConfig = workspace.getConfiguration('netbeans'); - const beVerbose: boolean = netbeansConfig.get('verbose', false); + const beVerbose : boolean = netbeansConfig.get('verbose', false); let userdir = process.env['nbcode_userdir'] || netbeansConfig.get('userdir', 'local'); switch (userdir) { case 'local': @@ -772,19 +774,19 @@ function doActivateWithJDK(specifiedJDK: string | null, context: ExtensionContex userdir = context.storagePath; break; } - // fallthru + // fallthru case 'global': userdir = context.globalStoragePath; break; default: - // assume storage is path on disk + // assume storage is path on disk } let info = { - clusters: findClusters(context.extensionPath), + clusters : findClusters(context.extensionPath), extensionPath: context.extensionPath, - storagePath: userdir, - jdkHome: specifiedJDK, + storagePath : userdir, + jdkHome : specifiedJDK, verbose: beVerbose }; let launchMsg = `Launching Apache NetBeans Language Server with ${specifiedJDK ? specifiedJDK : 'default system JDK'} and userdir ${userdir}`; @@ -792,7 +794,7 @@ function doActivateWithJDK(specifiedJDK: string | null, context: ExtensionContex vscode.window.setStatusBarMessage(launchMsg, 2000); let ideRunning = new Promise((resolve, reject) => { - let stdOut: string | null = ''; + let stdOut : string | null = ''; function logAndWaitForEnabled(text: string, isOut: boolean) { if (p == nbProcess) { activationPending = false; @@ -809,7 +811,7 @@ function doActivateWithJDK(specifiedJDK: string | null, context: ExtensionContex stdOut = null; } } - let extras: string[] = ["--modules", "--list", "-J-XX:PerfMaxStringConstLength=10240"]; + let extras : string[] = ["--modules", "--list", "-J-XX:PerfMaxStringConstLength=10240"]; if (isDarkColorTheme()) { extras.push('--laf', 'com.formdev.flatlaf.FlatDarkLaf'); } @@ -821,14 +823,14 @@ function doActivateWithJDK(specifiedJDK: string | null, context: ExtensionContex 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) { + p.stdout.on('data', function(d: any) { logAndWaitForEnabled(d.toString(), true); }); - p.stderr.on('data', function (d: any) { + p.stderr.on('data', function(d: any) { logAndWaitForEnabled(d.toString(), false); }); nbProcess = p; - p.on('close', function (code: number) { + p.on('close', function(code: number) { if (p == nbProcess) { nbProcess = null; } @@ -894,18 +896,18 @@ function doActivateWithJDK(specifiedJDK: string | null, context: ExtensionContex }); }); const conf = workspace.getConfiguration(); - let documentSelectors: DocumentSelector = [ - { language: 'java' }, - { language: 'yaml', pattern: '**/{application,bootstrap}*.yml' }, - { language: 'properties', pattern: '**/{application,bootstrap}*.properties' }, - { language: 'jackpot-hint' }, - { language: 'xml', pattern: '**/pom.xml' }, - { pattern: '**/build.gradle' } + let documentSelectors : DocumentSelector = [ + { language: 'java' }, + { language: 'yaml', pattern: '**/{application,bootstrap}*.yml' }, + { language: 'properties', pattern: '**/{application,bootstrap}*.properties' }, + { language: 'jackpot-hint' }, + { language: 'xml', pattern: '**/pom.xml' }, + { pattern: '**/build.gradle'} ]; const enableJava = isJavaSupportEnabled(); - const enableGroovy: boolean = conf.get("netbeans.groovySupport.enabled") as boolean; + const enableGroovy : boolean = conf.get("netbeans.groovySupport.enabled") as boolean; if (enableGroovy) { - documentSelectors.push({ language: 'groovy' }); + documentSelectors.push({ language: 'groovy'}); } // Options to control the language client let clientOptions: LanguageClientOptions = { @@ -923,20 +925,20 @@ function doActivateWithJDK(specifiedJDK: string | null, context: ExtensionContex outputChannel: log, revealOutputChannelOn: RevealOutputChannelOn.Never, progressOnInitialization: true, - initializationOptions: { - 'nbcodeCapabilities': { - 'statusBarMessageSupport': true, - 'testResultsSupport': true, - 'showHtmlPageSupport': true, - 'wantsJavaSupport': enableJava, - 'wantsGroovySupport': enableGroovy + initializationOptions : { + 'nbcodeCapabilities' : { + 'statusBarMessageSupport' : true, + 'testResultsSupport' : true, + 'showHtmlPageSupport' : true, + 'wantsJavaSupport' : enableJava, + 'wantsGroovySupport' : enableGroovy } }, errorHandler: { - error: function (error: Error, _message: Message, count: number): ErrorHandlerResult { + error : function(error: Error, _message: Message, count: number): ErrorHandlerResult { return { action: ErrorAction.Continue, message: error.message }; }, - closed: function (): CloseHandlerResult { + closed : function(): CloseHandlerResult { handleLog(log, "Connection to Apache NetBeans Language Server closed."); if (!activationPending) { restartWithJDKLater(10000, false); @@ -948,11 +950,11 @@ function doActivateWithJDK(specifiedJDK: string | null, context: ExtensionContex let c = new NbLanguageClient( - 'java', - 'NetBeans Java', - connection, - log, - clientOptions + 'java', + 'NetBeans Java', + connection, + log, + clientOptions ); handleLog(log, 'Language Client: Starting'); c.start().then(() => { @@ -1062,7 +1064,7 @@ function doActivateWithJDK(specifiedJDK: string | null, context: ExtensionContex handleLog(log, 'Language Client: Ready'); setClient[0](c); commands.executeCommand('setContext', 'nbJavaLSReady', true); - + if (enableJava) { // create project explorer: //c.findTreeViewService().createView('foundProjects', 'Projects', { canSelectMany : false }); @@ -1071,7 +1073,7 @@ function doActivateWithJDK(specifiedJDK: string | null, context: ExtensionContex createDatabaseView(c); if (enableJava) { - c.findTreeViewService().createView('cloud.resources', undefined, { canSelectMany: false }); + c.findTreeViewService().createView('cloud.resources', undefined, { canSelectMany : false }); } }).catch(setClient[1]); }).catch((reason) => { @@ -1081,11 +1083,11 @@ function doActivateWithJDK(specifiedJDK: string | null, context: ExtensionContex }); class Decorator implements TreeItemDecorator<Visualizer> { - private provider: CustomizableTreeDataProvider<Visualizer>; - private setCommand: vscode.Disposable; + private provider : CustomizableTreeDataProvider<Visualizer>; + private setCommand : vscode.Disposable; private initialized: boolean = false; - constructor(provider: CustomizableTreeDataProvider<Visualizer>, client: NbLanguageClient) { + constructor(provider : CustomizableTreeDataProvider<Visualizer>, client : NbLanguageClient) { this.provider = provider; this.setCommand = vscode.commands.registerCommand('java.local.db.set.preferred.connection', (n) => this.setPreferred(n)); } @@ -1097,7 +1099,7 @@ function doActivateWithJDK(specifiedJDK: string | null, context: ExtensionContex return children; } - async decorateTreeItem(vis: Visualizer, item: vscode.TreeItem): Promise<vscode.TreeItem> { + async decorateTreeItem(vis : Visualizer, item : vscode.TreeItem) : Promise<vscode.TreeItem> { if (!(item.contextValue && item.contextValue.match(/class:org.netbeans.api.db.explorer.DatabaseConnection/))) { return item; } @@ -1121,22 +1123,22 @@ function doActivateWithJDK(specifiedJDK: string | null, context: ExtensionContex } } - function createDatabaseView(c: NbLanguageClient) { - let decoRegister: CustomizableTreeDataProvider<Visualizer>; - c.findTreeViewService().createView('database.connections', undefined, { - canSelectMany: true, + function createDatabaseView(c : NbLanguageClient) { + let decoRegister : CustomizableTreeDataProvider<Visualizer>; + c.findTreeViewService().createView('database.connections', undefined , { + canSelectMany : true, - providerInitializer: (customizable) => + providerInitializer : (customizable) => customizable.addItemDecorator(new Decorator(customizable, c)) }); } - async function createProjectView(ctx: ExtensionContext, client: NbLanguageClient) { - const ts: TreeViewService = client.findTreeViewService(); - let tv: vscode.TreeView<Visualizer> = await ts.createView('foundProjects', 'Projects', { canSelectMany: false }); + async function createProjectView(ctx : ExtensionContext, 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) { + async function revealActiveEditor(ed? : vscode.TextEditor) { const uri = window.activeTextEditor?.document?.uri; if (!uri || uri.scheme.toLowerCase() !== 'file') { return; @@ -1144,11 +1146,11 @@ function doActivateWithJDK(specifiedJDK: string | null, context: ExtensionContex if (!tv.visible) { return; } - let vis: Visualizer | undefined = await ts.findPath(tv, uri.toString()); + let vis : Visualizer | undefined = await ts.findPath(tv, uri.toString()); if (!vis) { return; } - tv.reveal(vis, { select: true, focus: false, expand: false }); + tv.reveal(vis, { select : true, focus : false, expand : false }); } ctx.subscriptions.push(window.onDidChangeActiveTextEditor(ed => { @@ -1167,7 +1169,7 @@ function doActivateWithJDK(specifiedJDK: string | null, context: ExtensionContex const webviews = new Map<string, vscode.Webview>(); - async function showHtmlPage(params: HtmlPageParams): Promise<void> { + async function showHtmlPage(params : HtmlPageParams): Promise<void> { return new Promise(resolve => { let data = params.text; const match = /<title>(.*)<\/title>/i.exec(data); @@ -1203,12 +1205,12 @@ function doActivateWithJDK(specifiedJDK: string | null, context: ExtensionContex }); view.onDidDispose(() => { resolve(); - workspace.fs.delete(resourceDir, { recursive: true }); + workspace.fs.delete(resourceDir, {recursive: true}); }); }); } - async function execInHtmlPage(params: HtmlPageParams): Promise<boolean> { + async function execInHtmlPage(params : HtmlPageParams): Promise<boolean> { return new Promise(resolve => { const webview = webviews.get(params.id); if (webview) { @@ -1223,8 +1225,8 @@ function doActivateWithJDK(specifiedJDK: string | null, context: ExtensionContex }); } - function showStatusBarMessage(params: ShowStatusMessageParams) { - let decorated: string = params.message; + function showStatusBarMessage(params : ShowStatusMessageParams) { + let decorated : string = params.message; let defTimeout; switch (params.type) { @@ -1250,22 +1252,22 @@ function doActivateWithJDK(specifiedJDK: string | null, context: ExtensionContex } } - function checkInstallNbJavac(msg: string) { + function checkInstallNbJavac(msg : string) { const NO_JAVA_SUPPORT = "Cannot initialize Java support"; if (msg.startsWith(NO_JAVA_SUPPORT)) { const yes = "Install GPLv2+CPEx code"; window.showErrorMessage("Additional Java Support is needed", yes).then(reply => { if (yes === reply) { vscode.window.setStatusBarMessage("Preparing Apache NetBeans Language Server for additional installation", 2000); - restartWithJDKLater = function () { + restartWithJDKLater = function() { handleLog(log, "Ignoring request for restart of Apache NetBeans Language Server"); }; maintenance = new Promise((resolve, reject) => { - const kill: Promise<void> = killNbProcess(false, log); + 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) { + let logData = function(d: any) { handleLogNoNL(log, d.toString()); }; installProcess.stdout.on('data', logData); @@ -1273,7 +1275,7 @@ function doActivateWithJDK(specifiedJDK: string | null, context: ExtensionContex 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) { + 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. @@ -1342,15 +1344,15 @@ 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 client; + async doProvideDebugConfigurations(folder: vscode.WorkspaceFolder | undefined, _token?: vscode.CancellationToken): Promise<vscode.DebugConfiguration[]> { + let c : LanguageClient = await client; if (!folder) { return []; } - var u: vscode.Uri | undefined; + var u : vscode.Uri | undefined; if (folder && folder.uri) { u = folder.uri; } else { @@ -1359,9 +1361,9 @@ class NetBeansConfigurationInitialProvider implements vscode.DebugConfigurationP let result : vscode.DebugConfiguration[] = []; const configNames : string[] | null | undefined = await vscode.commands.executeCommand('java.project.configurations', 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. @@ -1370,7 +1372,7 @@ class NetBeansConfigurationInitialProvider implements vscode.DebugConfigurationP } else { cname = "Launch Java: " + cn; } - const debugConfig: vscode.DebugConfiguration = { + const debugConfig : vscode.DebugConfiguration = { name: cname, type: "java+", request: "launch", @@ -1393,11 +1395,11 @@ 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 client; + async doProvideDebugConfigurations(folder: vscode.WorkspaceFolder | undefined, context: ExtensionContext, commandValues: Map<string, string>, _token?: vscode.CancellationToken): Promise<vscode.DebugConfiguration[]> { + let c : LanguageClient = await client; if (!folder) { return []; } @@ -1405,7 +1407,7 @@ class NetBeansConfigurationDynamicProvider implements vscode.DebugConfigurationP const attachConnectors : DebugConnector[] | null | undefined = await vscode.commands.executeCommand('java.attachDebugger.configurations'); if (attachConnectors) { for (let ac of attachConnectors) { - const debugConfig: vscode.DebugConfiguration = { + const debugConfig : vscode.DebugConfiguration = { name: ac.name, type: ac.type, request: "attach", diff --git a/java/java.lsp.server/vscode/src/propertiesView/controlTypes.ts b/java/java.lsp.server/vscode/src/propertiesView/controlTypes.ts index aabe501b85..e6561aa4a5 100644 --- a/java/java.lsp.server/vscode/src/propertiesView/controlTypes.ts +++ b/java/java.lsp.server/vscode/src/propertiesView/controlTypes.ts @@ -4,16 +4,16 @@ * * Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. */ -import { EnumType, KeyOfArray, Typed } from "../typesUtil"; +import { EnumType, Typed } from "../typesUtil"; export type ID = number; -export const PropTypes: KeyOfArray<PropTypeMap> = [ - "java.lang.String", - "java.lang.Boolean", - "java.util.Properties", - "unknown" -];// unfortunate but necessary duplication +export const PropTypes = { + String: "java.lang.String", + Boolean: "java.lang.Boolean", + Properties: "java.util.Properties", + Unknown: "unknown", +} as const;// unfortunate but necessary duplication export type PropTypeMap = { "java.lang.String": string; "java.lang.Boolean": boolean; diff --git a/java/java.lsp.server/vscode/src/propertiesView/propertiesHtmlBuilder.ts b/java/java.lsp.server/vscode/src/propertiesView/propertiesHtmlBuilder.ts index e638d2432b..616d82cf05 100644 --- a/java/java.lsp.server/vscode/src/propertiesView/propertiesHtmlBuilder.ts +++ b/java/java.lsp.server/vscode/src/propertiesView/propertiesHtmlBuilder.ts @@ -5,7 +5,7 @@ * Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. */ import * as vscode from 'vscode'; -import { Properties, Property } from "./controlTypes"; +import { PropTypes, Properties, Property } from "./controlTypes"; export function makeHtmlForProperties(name: string, nonce: string, scriptUri: vscode.Uri, properties: Properties): string { return `<!DOCTYPE html> @@ -55,13 +55,13 @@ function makePropertiesTable(properties: Properties): string { function makePropAccess(prop: Property): string { let out: string; switch (prop.propType) { - case 'java.lang.String': + case PropTypes.String: out = makeStringAccess(prop); break; - case 'java.lang.Boolean': + case PropTypes.Boolean: out = makeBoolAccess(prop); break; - case 'java.util.Properties': + case PropTypes.Properties: out = makePropertiesAccess(prop); break; default: @@ -71,21 +71,21 @@ function makePropAccess(prop: Property): string { return wrapToTable(prop.propDispName, out) + '\n'; } -function makeStringAccess(prop: Property<'java.lang.String'>) { +function makeStringAccess(prop: Property<typeof PropTypes.String>) { return `<vscode-text-field name="input" id="${prop.propName}" value="${encode(prop.propValue)}" ${prop.propWrite ? "" : "disabled"}></vscode-text-field>`; } -function makeBoolAccess(prop: Property<'java.lang.Boolean'>) { +function makeBoolAccess(prop: Property<typeof PropTypes.Boolean>) { return `<vscode-checkbox name="input" id="${prop.propName}" ${prop.propWrite ? "" : "disabled"} ${prop.propValue ? "checked" : ""}></vscode-checkbox>`; } -function makePropertiesAccess(prop: Property<'java.util.Properties'>) { +function makePropertiesAccess(prop: Property<typeof PropTypes.Properties>) { return `<details><summary><b>${prop.propDispName}</b></summary><table name="input" id="${prop.propName}"> ${makePropTable(prop)} </table></details>`; } -function makePropTable(prop: Property<'java.util.Properties'>) { +function makePropTable(prop: Property<typeof PropTypes.Properties>) { let out = ""; for (const key in prop.propValue) { out += makePropRow(prop, key) + '\n'; @@ -93,7 +93,7 @@ function makePropTable(prop: Property<'java.util.Properties'>) { return out; } -function makePropRow(prop: Property<'java.util.Properties'>, key: string) { +function makePropRow(prop: Property<typeof PropTypes.Properties>, key: string) { return wrapToTable(asTextField(key, prop.propWrite, "name"), asTextField(prop.propValue[key], prop.propWrite, "value"), " = "); } diff --git a/java/java.lsp.server/vscode/src/propertiesView/propertiesView.ts b/java/java.lsp.server/vscode/src/propertiesView/propertiesView.ts index 16d6ffedc5..e823f593cd 100644 --- a/java/java.lsp.server/vscode/src/propertiesView/propertiesView.ts +++ b/java/java.lsp.server/vscode/src/propertiesView/propertiesView.ts @@ -7,12 +7,13 @@ import * as vscode from 'vscode'; import { CommandKey, ID, Message, MessageProp, Properties, Property, PropTypes } from './controlTypes'; -import { asClass, assertNever } from '../typesUtil'; +import { assertNever, isRecord, isString, IsType } from '../typesUtil'; import { makeHtmlForProperties } from './propertiesHtmlBuilder'; export class PropertiesView { - private static readonly COMMAND_GET_NODE_PROPERTIES = "java.node.properties.get"; // NOI18N - private static readonly COMMAND_SET_NODE_PROPERTIES = "java.node.properties.set"; // NOI18N + private static readonly COMMAND_PREFIX = "nbls."; + 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; @@ -104,36 +105,49 @@ export class PropertiesView { if (props.size === 0) { throw new Error("No properties."); } - this.properties = props.values().next().value; - console.log("Props "+this.properties); } - private async get() : Promise<Map<String,Properties>> { + private async get(): Promise<Map<String, Properties>> { let resp = await vscode.commands.executeCommand(PropertiesView.COMMAND_GET_NODE_PROPERTIES, this.id); if (!resp) { // TODO - possibly report protocol error ? return new Map<String, Properties>(); } - return new Map<String, Properties>(Object.entries(resp)); - + return new Map<String, Properties>(Object.entries(resp)); // TODO - validate cast } private save(properties: MessageProp[]) { - if (!this.properties) { - return; - } + if (!this.properties) return; + for (const prop of properties) this.mergeProps(prop, this.properties?.props); - let msg = new Map<String,Properties>(); - msg.set(this.properties.propName, this.properties); - vscode.commands.executeCommand(PropertiesView.COMMAND_SET_NODE_PROPERTIES, this.id, msg); + const msg: Record<string, Properties> = {}; + msg[this.properties.propName] = this.properties; + + vscode.commands.executeCommand(PropertiesView.COMMAND_SET_NODE_PROPERTIES, this.id, msg) + .then(done => { + if (isRecord(isRecord.bind(null, isString) as IsType<Record<string, string>>, done)) { + this.processSaveError(done); + } + }, err => vscode.window.showErrorMessage(err.message, { modal: true, detail: err.stack })); + } + + private processSaveError(errObj: Record<string, Record<string, string>>) { + 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: MessageProp, props?: Property[]): void { const p = props?.find(p => p.propName === prop.name); - if (p && PropTypes.includes(p.propType)) + if (p && Object.values(PropTypes).includes(p.propType)) p.propValue = prop.value; } diff --git a/java/java.lsp.server/vscode/src/propertiesView/script.ts b/java/java.lsp.server/vscode/src/propertiesView/script.ts index 6fe32a10de..1f800f5b5d 100644 --- a/java/java.lsp.server/vscode/src/propertiesView/script.ts +++ b/java/java.lsp.server/vscode/src/propertiesView/script.ts @@ -13,7 +13,7 @@ provideVSCodeDesignSystem().register(vsCodeButton(), vsCodeTextField(), vsCodeDi const vscode = acquireVsCodeApi(); document.addEventListener("DOMContentLoaded", () => { try { - asClass(Button, document.getElementById('save'), "Not save.") + asClass(Button, document.getElementById('save')) .addEventListener('click', () => { try { if (validate()) @@ -22,7 +22,7 @@ document.addEventListener("DOMContentLoaded", () => { handleError(e); } }); - asClass(Button, document.getElementById('cancel'), "Not cancel.") + asClass(Button, document.getElementById('cancel')) .addEventListener('click', () => { sendMessage({ _type: CommandKey.Cancel }); }); @@ -61,11 +61,13 @@ function getProperty(element: HTMLElement): MessageProp { } else if (isClass(HTMLTableElement, element)) { return makeProperty(parseProperties(element), element?.id); } - return { name: element?.id || "", value: element?.nodeValue || "" }; + throw new Error("Unknown HTML Element type."); } function makeProperty(value: string | boolean | Record<string, string>, name?: string): MessageProp { - return { name: name || "NoID", value: value }; + if (name) + return { name: name, value: value }; + throw new Error("HTML Element have no ID."); } function parseProperties(table: HTMLTableElement): Record<string, string> { diff --git a/java/java.lsp.server/vscode/src/typesUtil.ts b/java/java.lsp.server/vscode/src/typesUtil.ts index b79dbc1c48..6c9d58f748 100644 --- a/java/java.lsp.server/vscode/src/typesUtil.ts +++ b/java/java.lsp.server/vscode/src/typesUtil.ts @@ -25,6 +25,18 @@ export function asClass<T>(cls: Constructor<T>, obj: unknown, errorMessage?: str 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.keys(obj).every(typeTest); +} + export function isError(obj: unknown): obj is Error { return obj instanceof Error; } @@ -33,10 +45,5 @@ export function assertNever(_obj: never, errorMessage?: string): never { throw new Error(errorMessage || "Shouldn't reach here."); } -export type KeyOfArray<T> = TupleUnion<keyof T>; -export type TupleUnion<U extends PropertyKey, R extends PropertyKey[] = []> = { - [S in U]: Exclude<U, S> extends never ? [...R, S] : TupleUnion<Exclude<U, S>, [...R, S]>; -}[U]; - 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 --------------------------------------------------------------------- To unsubscribe, e-mail: commits-unsubscr...@netbeans.apache.org For additional commands, e-mail: commits-h...@netbeans.apache.org For further information about the NetBeans mailing lists, visit: https://cwiki.apache.org/confluence/display/NETBEANS/Mailing+lists