diff --git a/package.json b/package.json index 9bdb4752..5b5ea3a6 100644 --- a/package.json +++ b/package.json @@ -257,6 +257,11 @@ "title": "%contributes.commands.java.view.package.newPackage%", "category": "Java" }, + { + "command": "java.view.fileExplorer.newPackage", + "title": "%contributes.commands.java.view.fileExplorer.newPackage%", + "category": "Java" + }, { "command": "java.view.package.newFile", "title": "%contributes.commands.java.view.package.newFile%", @@ -502,6 +507,10 @@ "command": "java.view.package.deleteFilePermanently", "when": "false" }, + { + "command": "java.view.fileExplorer.newPackage", + "when": "false" + }, { "command": "java.project.build.workspace", "when": "false" @@ -545,24 +554,29 @@ ], "explorer/context": [ { - "command": "java.view.package.revealInProjectExplorer", - "when": "resourceFilename =~ /(.*\\.gradle)|(.*\\.gradle\\.kts)|(pom\\.xml)$/ && java:serverMode == Standard", - "group": "1_javaactions" + "submenu": "javaProject.newJavaFile", + "when": "explorerResourceIsFolder", + "group": "1_javaactions@10" }, { - "command": "java.view.package.revealInProjectExplorer", - "when": "resourceExtname == .java && java:serverMode == Standard", - "group": "1_javaactions" + "command": "java.view.fileExplorer.newPackage", + "when": "explorerResourceIsFolder", + "group": "1_javaactions@20" }, { "command": "_java.project.create.from.fileexplorer.menu", "when": "explorerResourceIsFolder", - "group": "1_javaactions" + "group": "1_javaactions@30" }, { - "submenu": "javaProject.newJavaFile", - "when": "explorerResourceIsFolder", - "group": "1_javaactions" + "command": "java.view.package.revealInProjectExplorer", + "when": "resourceFilename =~ /(.*\\.gradle)|(.*\\.gradle\\.kts)|(pom\\.xml)$/ && java:serverMode == Standard", + "group": "1_javaactions@40" + }, + { + "command": "java.view.package.revealInProjectExplorer", + "when": "resourceExtname == .java && java:serverMode == Standard", + "group": "1_javaactions@40" } ], "editor/title": [ diff --git a/package.nls.json b/package.nls.json index adf5b5cb..8feb8343 100644 --- a/package.nls.json +++ b/package.nls.json @@ -35,6 +35,7 @@ "contributes.commands.java.view.package.renameFile": "Rename", "contributes.commands.java.view.package.moveFileToTrash": "Delete", "contributes.commands.java.view.package.deleteFilePermanently": "Delete Permanently", + "contributes.commands.java.view.fileExplorer.newPackage": "New Java Package...", "contributes.submenus.javaProject.new": "New", "contributes.commands.java.view.menus.file.newJavaClass": "New Java File", "configuration.java.dependency.showMembers": "Show the members in the explorer", diff --git a/src/commands.ts b/src/commands.ts index e84a5fe9..a2564835 100644 --- a/src/commands.ts +++ b/src/commands.ts @@ -54,6 +54,8 @@ export namespace Commands { export const VIEW_PACKAGE_NEW_JAVA_PACKAGE = "java.view.package.newPackage"; + export const VIEW_EXPLORER_NEW_PACKAGE = "java.view.fileExplorer.newPackage"; + export const VIEW_PACKAGE_RENAME_FILE = "java.view.package.renameFile"; export const VIEW_PACKAGE_MOVE_FILE_TO_TRASH = "java.view.package.moveFileToTrash"; diff --git a/src/explorerCommands/new.ts b/src/explorerCommands/new.ts index 736c49c7..84ab3360 100644 --- a/src/explorerCommands/new.ts +++ b/src/explorerCommands/new.ts @@ -321,15 +321,7 @@ async function inferPackageFsPath(): Promise { return getPackageFsPathFromActiveEditor(); } - let sourcePaths: string[] | undefined; - try { - const result = await commands.executeCommand(Commands.EXECUTE_WORKSPACE_COMMAND, Commands.LIST_SOURCEPATHS); - if (result && result.data && result.data.length) { - sourcePaths = result.data.map((entry) => entry.path); - } - } catch (e) { - // do nothing - } + let sourcePaths: string[] | undefined = (await getSourceRoots())?.data?.map((sourcePath) => sourcePath.path); if (!window.activeTextEditor) { if (sourcePaths?.length === 1) { @@ -420,14 +412,10 @@ async function resolvePackageName(filePath: string): Promise { return guessPackageName(filePath); } - let sourcePaths: string[] = []; - const result: IListCommandResult = - await commands.executeCommand(Commands.EXECUTE_WORKSPACE_COMMAND, Commands.LIST_SOURCEPATHS); - if (result && result.data && result.data.length) { - sourcePaths = result.data.map((sourcePath) => sourcePath.path).sort((a, b) => b.length - a.length); - } + let sourcePaths: string[] = (await getSourceRoots())?.data?.map( + (sourcePath) => sourcePath.path).sort((a, b) => b.length - a.length) ?? []; - if (!sourcePaths || !sourcePaths.length) { + if (!sourcePaths?.length) { return ""; } @@ -513,40 +501,30 @@ function getNewFilePath(basePath: string, className: string): string { return path.join(basePath, ...className.split(".")) + ".java"; } -export async function newPackage(node?: DataNode): Promise { - if (!node?.uri || !canCreatePackage(node)) { +export async function newPackage(node: DataNode | Uri | undefined): Promise { + if (!node) { return; } - let defaultValue: string; - let packageRootPath: string; - const nodeKind = node.nodeData.kind; - if (nodeKind === NodeKind.Project) { - defaultValue = ""; - packageRootPath = await getPackageFsPath(node) || ""; - } else if (nodeKind === NodeKind.PackageRoot) { - defaultValue = ""; - packageRootPath = Uri.parse(node.uri).fsPath; - } else if (nodeKind === NodeKind.Package) { - defaultValue = node.nodeData.name + "."; - packageRootPath = getPackageRootPath(Uri.parse(node.uri).fsPath, node.nodeData.name); - } else if (nodeKind === NodeKind.PrimaryType) { - const primaryTypeNode = node; - packageRootPath = primaryTypeNode.getPackageRootPath(); - if (packageRootPath === "") { - window.showErrorMessage("Failed to get the package root path."); - return; - } - const packagePath = await getPackageFsPath(node); - if (!packagePath) { - window.showErrorMessage("Failed to get the package path."); - return; - } - defaultValue = path.relative(packageRootPath, packagePath).replace(/[\\\/]/g, ".") + "."; - } else { + const isUri = node instanceof Uri; + if (!isUri && (!node.uri || !canCreatePackage(node))) { + return; + } + + sendInfo("", { + "triggernewpackagefrom": isUri ? "fileExplorer" : "javaProjectExplorer", + }); + + let {defaultValue, packageRootPath} = (isUri ? await getPackageInformationFromUri(node) + : await getPackageInformationFromNode(node)) || {}; + if (defaultValue === undefined || packageRootPath === undefined) { return; } + if (defaultValue.length > 0 && !defaultValue.endsWith(".")) { + defaultValue += "."; + } + const packageName: string | undefined = await window.showInputBox({ value: defaultValue, placeHolder: "Input the package name", @@ -573,6 +551,74 @@ export async function newPackage(node?: DataNode): Promise { await fse.ensureDir(getNewPackagePath(packageRootPath, packageName)); } +async function getPackageInformationFromUri(uri: Uri): Promise | undefined> { + const defaultValue = { + defaultValue: "", + packageRootPath: uri.fsPath, + }; + if (!isLanguageServerReady()) { + return defaultValue; + } + + let sourcePaths: string[] = (await getSourceRoots())?.data?.map( + (sourcePath) => sourcePath.path).sort((a, b) => b.length - a.length) ?? []; + + if (!sourcePaths?.length) { + return defaultValue; + } + + for (const sourcePath of sourcePaths) { + if (isPrefix(sourcePath, uri.fsPath)) { + const relative = path.relative(sourcePath, uri.fsPath); + return { + defaultValue: relative.replace(/[/\\]/g, "."), + packageRootPath: sourcePath, + }; + } + } + + return defaultValue; +} + + +async function getPackageInformationFromNode(node: DataNode): Promise | undefined> { + const nodeKind = node.nodeData.kind; + if (nodeKind === NodeKind.Project) { + return { + packageRootPath: await getPackageFsPath(node) || "", + defaultValue: "", + } + } else if (nodeKind === NodeKind.PackageRoot) { + return { + packageRootPath: Uri.parse(node.uri!).fsPath, + defaultValue: "", + } + } else if (nodeKind === NodeKind.Package) { + return { + packageRootPath: getPackageRootPath(Uri.parse(node.uri!).fsPath, node.nodeData.name), + defaultValue: node.nodeData.name + ".", + } + } else if (nodeKind === NodeKind.PrimaryType) { + const primaryTypeNode = node; + const packageRootPath = primaryTypeNode.getPackageRootPath(); + if (packageRootPath === "") { + window.showErrorMessage("Failed to get the package root path."); + return undefined; + } + const packagePath = await getPackageFsPath(node); + if (!packagePath) { + window.showErrorMessage("Failed to get the package path."); + return undefined; + } + return { + packageRootPath: packageRootPath, + defaultValue: path.relative(packageRootPath, packagePath).replace(/[/\\]/g, "."), + } + } + + return undefined; +} + /** * Check if the create package command is available for the given node. * Currently the check logic is the same as the create class command. @@ -708,3 +754,15 @@ function getBasePath(node: DataNode): string | undefined { return undefined; } } + +async function getSourceRoots(): Promise{ + try { + const result = await commands.executeCommand(Commands.EXECUTE_WORKSPACE_COMMAND, Commands.LIST_SOURCEPATHS); + if (result?.data?.length) { + return result; + } + } catch (e) { + // ignore; + } + return undefined; +} diff --git a/src/views/dependencyExplorer.ts b/src/views/dependencyExplorer.ts index 8841b4d8..9afb27b8 100644 --- a/src/views/dependencyExplorer.ts +++ b/src/views/dependencyExplorer.ts @@ -147,6 +147,9 @@ export class DependencyExplorer implements Disposable { } newPackage(cmdNode); }), + instrumentOperationAsVsCodeCommand(Commands.VIEW_EXPLORER_NEW_PACKAGE, (node: Uri) => { + newPackage(node); + }), instrumentOperationAsVsCodeCommand(Commands.VIEW_PACKAGE_REVEAL_FILE_OS, (node?: DataNode) => { const cmdNode = getCmdNode(this._dependencyViewer.selection, node); if (cmdNode?.uri) {