diff --git a/package-lock.json b/package-lock.json index 23761dba..f22cb461 100644 --- a/package-lock.json +++ b/package-lock.json @@ -262,9 +262,9 @@ "dev": true }, "@types/vscode": { - "version": "1.53.0", - "resolved": "https://devdiv.pkgs.visualstudio.com/_packaging/CloudKernel/npm/registry/@types/vscode/-/vscode-1.53.0.tgz", - "integrity": "sha1-R7U3F69lYvKtBRcbychQCCSjkFw=", + "version": "1.59.0", + "resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.59.0.tgz", + "integrity": "sha512-Zg38rusx2nU6gy6QdF7v4iqgxNfxzlBlDhrRCjOiPQp+sfaNrp3f9J6OHIhpGNN1oOAca4+9Hq0+8u3jwzPMlQ==", "dev": true }, "@ungap/promise-all-settled": { @@ -1226,7 +1226,7 @@ }, "browserify-aes": { "version": "1.2.0", - "resolved": "http://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", + "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==", "dev": true, "requires": { @@ -2195,7 +2195,7 @@ }, "create-hash": { "version": "1.2.0", - "resolved": "http://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", + "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", "dev": true, "requires": { @@ -2208,7 +2208,7 @@ }, "create-hmac": { "version": "1.1.7", - "resolved": "http://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", + "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", "dev": true, "requires": { @@ -2489,7 +2489,7 @@ }, "diffie-hellman": { "version": "5.0.3", - "resolved": "http://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", + "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", "integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==", "dev": true, "requires": { @@ -4472,7 +4472,7 @@ }, "load-json-file": { "version": "1.1.0", - "resolved": "http://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=", "dev": true, "requires": { @@ -5765,7 +5765,7 @@ }, "os-locale": { "version": "1.4.0", - "resolved": "http://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz", + "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz", "integrity": "sha1-IPnxeuKe00XoveWDsT0gCYA8FNk=", "dev": true, "requires": { @@ -6957,7 +6957,7 @@ }, "sha.js": { "version": "2.4.11", - "resolved": "http://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", + "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", "dev": true, "requires": { @@ -8222,7 +8222,7 @@ }, "tty-browserify": { "version": "0.0.0", - "resolved": "http://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz", + "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz", "integrity": "sha1-oVe6QC2iTpv5V/mqadUk7tQpAaY=", "dev": true }, diff --git a/package.json b/package.json index 4f5841e0..fd7e44fc 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,7 @@ "explorer" ], "engines": { - "vscode": "^1.53.0" + "vscode": "^1.59.0" }, "repository": { "type": "git", @@ -31,6 +31,7 @@ "onCommand:java.project.create", "onCommand:java.view.package.exportJar", "onCommand:java.view.package.revealInProjectExplorer", + "onCommand:java.view.package.newJavaClass", "onView:javaProjectExplorer" ], "license": "MIT", @@ -257,6 +258,11 @@ } ], "menus": { + "file/newFile": [ + { + "command": "java.view.package.newJavaClass" + } + ], "commandPalette": [ { "command": "java.view.package.exportJar", @@ -310,10 +316,6 @@ "command": "java.project.refreshLibraries", "when": "false" }, - { - "command": "java.view.package.newJavaClass", - "when": "false" - }, { "command": "java.view.package.newPackage", "when": "false" @@ -595,7 +597,7 @@ "@types/mocha": "^8.2.1", "@types/node": "^8.10.66", "@types/semver": "^7.3.8", - "@types/vscode": "1.53.0", + "@types/vscode": "1.59.0", "copy-webpack-plugin": "^6.4.1", "glob": "^7.1.6", "gulp": "^4.0.2", diff --git a/src/commands.ts b/src/commands.ts index ba6bbb1b..276c08f2 100644 --- a/src/commands.ts +++ b/src/commands.ts @@ -94,6 +94,11 @@ export namespace Commands { export const WORKBENCH_ACTION_FILES_OPENFOLDER = "workbench.action.files.openFolder"; export const WORKBENCH_ACTION_FILES_OPENFILEFOLDER = "workbench.action.files.openFileFolder"; + + /** + * Commands from JLS + */ + export const LIST_SOURCEPATHS = "java.project.listSourcePaths"; } export function executeJavaLanguageServerCommand(...rest: any[]) { diff --git a/src/explorerCommands/new.ts b/src/explorerCommands/new.ts index 7d15b9c1..b8ca4303 100644 --- a/src/explorerCommands/new.ts +++ b/src/explorerCommands/new.ts @@ -3,19 +3,29 @@ import * as fse from "fs-extra"; import * as path from "path"; -import { QuickPickItem, Uri, window, workspace, WorkspaceEdit } from "vscode"; +import { commands, languages, QuickPickItem, SnippetString, TextEditor, Uri, window, workspace, WorkspaceEdit, WorkspaceFolder } from "vscode"; +import { Commands } from "../../extension.bundle"; import { NodeKind } from "../java/nodeData"; import { DataNode } from "../views/dataNode"; import { checkJavaQualifiedName } from "./utility"; export async function newJavaClass(node?: DataNode): Promise { - if (!node?.uri || !canCreateClass(node)) { - return; + let packageFsPath: string | undefined; + if (!node) { + packageFsPath = await inferPackageFsPath(); + } else { + if (!node?.uri || !canCreateClass(node)) { + return; + } + + packageFsPath = await getPackageFsPath(node); } - const packageFsPath: string = await getPackageFsPath(node); - if (!packageFsPath) { + if (packageFsPath === undefined) { + // User canceled return; + } else if (packageFsPath.length === 0) { + return newUntiledJavaFile(); } const className: string | undefined = await window.showInputBox({ @@ -27,7 +37,7 @@ export async function newJavaClass(node?: DataNode): Promise { return checkMessage; } - if (await fse.pathExists(getNewFilePath(packageFsPath, value))) { + if (await fse.pathExists(getNewFilePath(packageFsPath!, value))) { return "Class already exists."; } @@ -47,6 +57,57 @@ export async function newJavaClass(node?: DataNode): Promise { workspace.applyEdit(workspaceEdit); } +async function newUntiledJavaFile(): Promise { + await commands.executeCommand("workbench.action.files.newUntitledFile"); + const textEditor: TextEditor | undefined = window.activeTextEditor; + if (!textEditor) { + return; + } + await languages.setTextDocumentLanguage(textEditor.document, "java"); + const snippets: string[] = []; + snippets.push(`public \${1|class,interface,enum,abstract class,@interface|} \${2:Main} {`); + snippets.push(`\t\${0}`); + snippets.push("}"); + snippets.push(""); + textEditor.insertSnippet(new SnippetString(snippets.join("\n"))); +} + +async function inferPackageFsPath(): Promise { + 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 + } + + if (!window.activeTextEditor) { + if (sourcePaths?.length === 1) { + return sourcePaths[0]; + } + 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 (sourcePaths) { + for (const sourcePath of sourcePaths) { + if (!path.relative(sourcePath, filePath).startsWith("..")) { + return path.dirname(window.activeTextEditor.document.uri.fsPath); + } + } + } + + return ""; +} + function canCreateClass(node: DataNode): boolean { if (node.nodeData.kind === NodeKind.Project || node.nodeData.kind === NodeKind.PackageRoot || @@ -58,7 +119,7 @@ function canCreateClass(node: DataNode): boolean { return false; } -async function getPackageFsPath(node: DataNode): Promise { +async function getPackageFsPath(node: DataNode): Promise { if (node.nodeData.kind === NodeKind.Project) { const childrenNodes: DataNode[] = await node.getChildren() as DataNode[]; const packageRoots: any[] = childrenNodes.filter((child) => { @@ -90,7 +151,7 @@ async function getPackageFsPath(node: DataNode): Promise { ignoreFocusOut: true, }, ); - return choice ? choice.fsPath : ""; + return choice?.fsPath; } } else if (node.nodeData.kind === NodeKind.PrimaryType) { return node.uri ? path.dirname(Uri.parse(node.uri).fsPath) : ""; @@ -116,7 +177,7 @@ export async function newPackage(node?: DataNode): Promise { const nodeKind = node.nodeData.kind; if (nodeKind === NodeKind.Project) { defaultValue = ""; - packageRootPath = await getPackageFsPath(node); + packageRootPath = await getPackageFsPath(node) || ""; } else if (nodeKind === NodeKind.PackageRoot) { defaultValue = ""; packageRootPath = Uri.parse(node.uri).fsPath; @@ -175,3 +236,16 @@ function getNewPackagePath(packageRootPath: string, packageName: string): string interface ISourceRootPickItem extends QuickPickItem { fsPath: string; } + +interface IListCommandResult { + status: boolean; + message: string; + data?: ISourcePath[]; +} + +interface ISourcePath { + path: string; + displayPath: string; + projectName: string; + projectType: string; +} diff --git a/src/views/dependencyExplorer.ts b/src/views/dependencyExplorer.ts index fcafeb8c..f9c1f155 100644 --- a/src/views/dependencyExplorer.ts +++ b/src/views/dependencyExplorer.ts @@ -97,11 +97,7 @@ export class DependencyExplorer implements Disposable { // register keybinding commands context.subscriptions.push( instrumentOperationAsVsCodeCommand(Commands.VIEW_PACKAGE_NEW_JAVA_CLASS, async (node?: DataNode) => { - let cmdNode = getCmdNode(this._dependencyViewer.selection, node); - if (!cmdNode) { - cmdNode = await this.promptForProjectNode(); - } - newJavaClass(cmdNode); + newJavaClass(node); }), instrumentOperationAsVsCodeCommand(Commands.VIEW_PACKAGE_NEW_JAVA_PACKAGE, async (node?: DataNode) => { let cmdNode = getCmdNode(this._dependencyViewer.selection, node);