From dd8845caaf3fb268c79ddbb62e94d65231809232 Mon Sep 17 00:00:00 2001 From: Jinbo Wang Date: Fri, 23 Feb 2024 13:28:34 +0800 Subject: [PATCH] Add 'New Java File' menu to file explorer & adjust the existing file explorer menu order (#820) --- .azure-pipelines/nightly.yml | 2 +- .azure-pipelines/rc.yml | 2 +- package.json | 47 +++++++++++++++++++-- src/explorerCommands/new.ts | 74 +++++++++++++++++++++++++++++---- src/views/dependencyExplorer.ts | 12 +++--- 5 files changed, 117 insertions(+), 20 deletions(-) diff --git a/.azure-pipelines/nightly.yml b/.azure-pipelines/nightly.yml index bc920bd1..f7892014 100644 --- a/.azure-pipelines/nightly.yml +++ b/.azure-pipelines/nightly.yml @@ -70,7 +70,7 @@ extends: - task: SFP.build-tasks.custom-build-task-1.EsrpCodeSigning@2 displayName: ESRP CodeSigning inputs: - ConnectedServiceName: vscjavaci_codesign + ConnectedServiceName: vscjavaci_esrp_codesign FolderPath: server Pattern: com.microsoft.jdtls.ext.*.jar signConfigType: inlineSignParams diff --git a/.azure-pipelines/rc.yml b/.azure-pipelines/rc.yml index 2168ff37..671854a9 100644 --- a/.azure-pipelines/rc.yml +++ b/.azure-pipelines/rc.yml @@ -65,7 +65,7 @@ extends: - task: SFP.build-tasks.custom-build-task-1.EsrpCodeSigning@2 displayName: ESRP CodeSigning inputs: - ConnectedServiceName: vscjavaci_codesign + ConnectedServiceName: vscjavaci_esrp_codesign FolderPath: server Pattern: com.microsoft.jdtls.ext.*.jar signConfigType: inlineSignParams diff --git a/package.json b/package.json index 30096a82..f1289931 100644 --- a/package.json +++ b/package.json @@ -547,17 +547,22 @@ { "command": "java.view.package.revealInProjectExplorer", "when": "resourceFilename =~ /(.*\\.gradle)|(.*\\.gradle\\.kts)|(pom\\.xml)$/ && java:serverMode == Standard", - "group": "navigation@100" + "group": "1_javaactions" }, { "command": "java.view.package.revealInProjectExplorer", "when": "resourceExtname == .java && java:serverMode == Standard", - "group": "navigation@100" + "group": "1_javaactions" }, { "command": "_java.project.create.from.fileexplorer.menu", "when": "explorerResourceIsFolder", - "group": "navigation@10" + "group": "1_javaactions" + }, + { + "submenu": "javaProject.newJavaFile", + "when": "explorerResourceIsFolder", + "group": "1_javaactions" } ], "editor/title": [ @@ -791,6 +796,38 @@ "group": "new2@40", "when": "view == javaProjectExplorer && (viewItem =~ /java:(file|folder|project)/ || viewItem =~ /java:(packageRoot)(?=.*?\\b\\+resource\\b)/)" } + ], + "javaProject.newJavaFile": [ + { + "command": "java.view.package.newJavaClass", + "group": "new@10", + "when": "explorerResourceIsFolder" + }, + { + "command": "java.view.package.newJavaInterface", + "group": "new@20", + "when": "explorerResourceIsFolder" + }, + { + "command": "java.view.package.newJavaEnum", + "group": "new@30", + "when": "explorerResourceIsFolder" + }, + { + "command": "java.view.package.newJavaRecord", + "group": "new@40", + "when": "explorerResourceIsFolder" + }, + { + "command": "java.view.package.newJavaAnnotation", + "group": "new@50", + "when": "explorerResourceIsFolder" + }, + { + "command": "java.view.package.newJavaAbstractClass", + "group": "new@60", + "when": "explorerResourceIsFolder" + } ] }, "submenus": [ @@ -805,6 +842,10 @@ { "id": "javaProject.new", "label": "%contributes.submenus.javaProject.new%" + }, + { + "id": "javaProject.newJavaFile", + "label": "%contributes.commands.java.view.menus.file.newJavaClass%" } ], "views": { diff --git a/src/explorerCommands/new.ts b/src/explorerCommands/new.ts index 248f57e1..736c49c7 100644 --- a/src/explorerCommands/new.ts +++ b/src/explorerCommands/new.ts @@ -12,7 +12,7 @@ import { NodeKind } from "../java/nodeData"; import { DataNode } from "../views/dataNode"; import { resourceRoots } from "../views/packageRootNode"; import { checkJavaQualifiedName } from "./utility"; -import { sendError, setUserError } from "vscode-extension-telemetry-wrapper"; +import { sendError, sendInfo, setUserError } from "vscode-extension-telemetry-wrapper"; // tslint:disable no-var-requires const stringInterpolate = require("fmtr"); @@ -134,7 +134,7 @@ export async function newJavaFile(): Promise { return newUntitledJavaFile(); } - const includeRecord = !(await isVersionLessThan(Uri.file(packageFsPath).toString(), 16)); + const includeRecord = isLanguageServerReady() && !(await isVersionLessThan(Uri.file(packageFsPath).toString(), 16)); const supportedTypes: string[] = JavaType.getDisplayNames(true, includeRecord); const typeName: string | undefined = await window.showQuickPick(supportedTypes, { @@ -148,12 +148,18 @@ export async function newJavaFile(): Promise { newJavaFile0(packageFsPath, JavaType.fromDisplayName(typeName)); } -// Create a new Java file from the context menu of Java Projects view. -export async function newJavaFileWithSpecificType(javaType: JavaType, node?: DataNode): Promise { +// Create a new Java file from the context menu of Java Projects view or File Explorer. +export async function newJavaFileWithSpecificType(javaType: JavaType, node?: DataNode | Uri): Promise { + sendInfo("", { + "triggernewfilefrom": (node instanceof Uri && node?.fsPath) ? "fileExplorer" : "javaProjectExplorer", + "javatype": javaType.label, + }); let packageFsPath: string | undefined; if (!node) { packageFsPath = await inferPackageFsPath(); - } else { + } else if (node instanceof Uri && node?.fsPath) { // File Explorer + packageFsPath = node?.fsPath; + } else if (node instanceof DataNode) { // Java Projects view if (!node?.uri || !canCreateClass(node)) { return; } @@ -292,19 +298,27 @@ async function newUntitledJavaFile(): Promise { textEditor.insertSnippet(new SnippetString(snippets.join("\n"))); } -async function inferPackageFsPath(): Promise { +function isLanguageServerReady(): boolean { const javaLanguageSupport: Extension | undefined = extensions.getExtension(ExtensionName.JAVA_LANGUAGE_SUPPORT); if (!javaLanguageSupport || !javaLanguageSupport.isActive) { - return ""; + return false; } const extensionApi: any = javaLanguageSupport.exports; if (!extensionApi) { - return ""; + return false; } if (extensionApi.serverMode !== "Standard" || extensionApi.status !== "Started") { - return ""; + return false; + } + + return true; +} + +async function inferPackageFsPath(): Promise { + if (!isLanguageServerReady()) { + return getPackageFsPathFromActiveEditor(); } let sourcePaths: string[] | undefined; @@ -342,6 +356,25 @@ async function inferPackageFsPath(): Promise { return ""; } +function getPackageFsPathFromActiveEditor() { + if (!window.activeTextEditor) { + return ""; + } + + const fileUri: Uri = window.activeTextEditor.document.uri; + const workspaceFolder: WorkspaceFolder | undefined = workspace.getWorkspaceFolder(fileUri); + if (!workspaceFolder) { + return ""; + } + + const filePath: string = window.activeTextEditor.document.uri.fsPath; + if (filePath.endsWith(".java")) { + return path.dirname(filePath); + } + + return ""; +} + function canCreateClass(node: DataNode): boolean { if (node.nodeData.kind === NodeKind.Project || node.nodeData.kind === NodeKind.PackageRoot || @@ -383,6 +416,10 @@ async function isVersionLessThan(fileUri: string, targetVersion: number): Promis } async function resolvePackageName(filePath: string): Promise { + if (!isLanguageServerReady()) { + return guessPackageName(filePath); + } + let sourcePaths: string[] = []; const result: IListCommandResult = await commands.executeCommand(Commands.EXECUTE_WORKSPACE_COMMAND, Commands.LIST_SOURCEPATHS); @@ -404,6 +441,25 @@ async function resolvePackageName(filePath: string): Promise { return ""; } +function guessPackageName(filePath: string): string { + const packagePath: string = path.dirname(filePath); + const knownSourcePathPrefixes: string[] = [ + "src/main/java/", + "src/test/java/", + "src\\main\\java\\", + "src\\test\\java\\", + ]; + + for (const prefix of knownSourcePathPrefixes) { + const index: number = packagePath.lastIndexOf(prefix); + if (index > -1) { + return packagePath.substring(index + prefix.length).replace(/[\\\/]/g, "."); + } + } + + return ""; +} + function isPrefix(parentPath: string, filePath: string): boolean { const relative = path.relative(parentPath, filePath); return !relative || (!relative.startsWith('..') && !path.isAbsolute(relative)); diff --git a/src/views/dependencyExplorer.ts b/src/views/dependencyExplorer.ts index af248add..8841b4d8 100644 --- a/src/views/dependencyExplorer.ts +++ b/src/views/dependencyExplorer.ts @@ -116,22 +116,22 @@ export class DependencyExplorer implements Disposable { instrumentOperationAsVsCodeCommand(Commands.VIEW_PACKAGE_NEW, async (node: DataNode) => { newResource(node); }), - instrumentOperationAsVsCodeCommand(Commands.VIEW_PACKAGE_NEW_JAVA_CLASS, async (node?: DataNode) => { + instrumentOperationAsVsCodeCommand(Commands.VIEW_PACKAGE_NEW_JAVA_CLASS, async (node?: DataNode | Uri) => { newJavaFileWithSpecificType(JavaType.CLASS, node); }), - instrumentOperationAsVsCodeCommand(Commands.VIEW_PACKAGE_NEW_JAVA_INTERFACE, async (node?: DataNode) => { + instrumentOperationAsVsCodeCommand(Commands.VIEW_PACKAGE_NEW_JAVA_INTERFACE, async (node?: DataNode | Uri) => { newJavaFileWithSpecificType(JavaType.INTERFACE, node); }), - instrumentOperationAsVsCodeCommand(Commands.VIEW_PACKAGE_NEW_JAVA_ENUM, async (node?: DataNode) => { + instrumentOperationAsVsCodeCommand(Commands.VIEW_PACKAGE_NEW_JAVA_ENUM, async (node?: DataNode | Uri) => { newJavaFileWithSpecificType(JavaType.ENUM, node); }), - instrumentOperationAsVsCodeCommand(Commands.VIEW_PACKAGE_NEW_JAVA_RECORD, async (node?: DataNode) => { + instrumentOperationAsVsCodeCommand(Commands.VIEW_PACKAGE_NEW_JAVA_RECORD, async (node?: DataNode | Uri) => { newJavaFileWithSpecificType(JavaType.RECORD, node); }), - instrumentOperationAsVsCodeCommand(Commands.VIEW_PACKAGE_NEW_JAVA_ANNOTATION, async (node?: DataNode) => { + instrumentOperationAsVsCodeCommand(Commands.VIEW_PACKAGE_NEW_JAVA_ANNOTATION, async (node?: DataNode | Uri) => { newJavaFileWithSpecificType(JavaType.ANNOTATION, node); }), - instrumentOperationAsVsCodeCommand(Commands.VIEW_PACKAGE_NEW_JAVA_ABSTRACT_CLASS, async (node?: DataNode) => { + instrumentOperationAsVsCodeCommand(Commands.VIEW_PACKAGE_NEW_JAVA_ABSTRACT_CLASS, async (node?: DataNode | Uri) => { newJavaFileWithSpecificType(JavaType.ABSTRACT_CLASS, node); }), instrumentOperationAsVsCodeCommand(Commands.VIEW_PACKAGE_NEW_FILE, async (node: DataNode) => {