diff --git a/package.json b/package.json index 3aef9b4f..d291dc69 100644 --- a/package.json +++ b/package.json @@ -139,6 +139,11 @@ "command": "java.view.package.newPackage", "title": "%contributes.commands.java.view.package.newPackage%", "category": "Java" + }, + { + "command": "java.view.package.moveFileToTrash", + "title": "%contributes.commands.java.view.package.moveFileToTrash%", + "category": "Java" } ], "configuration": { @@ -176,6 +181,13 @@ } } }, + "keybindings": [ + { + "command": "java.view.package.moveFileToTrash", + "key": "delete", + "when": "java:projectManagerActivated && focusedView == javaProjectExplorer" + } + ], "menus": { "commandPalette": [ { @@ -234,6 +246,10 @@ "command": "java.view.package.newPackage", "when": "false" }, + { + "command": "java.view.package.moveFileToTrash", + "when": "false" + }, { "command": "java.project.build.workspace", "when": "false" @@ -322,17 +338,32 @@ { "command": "java.view.package.revealFileInOS", "when": "view == javaProjectExplorer && viewItem =~ /java:(?=.*?\\b\\+uri\\b)/", - "group": "path@10" + "group": "6_copypath@10" }, { "command": "java.view.package.copyFilePath", "when": "view == javaProjectExplorer && viewItem =~ /java:(?=.*?\\b\\+uri\\b)/", - "group": "path@20" + "group": "6_copypath@20" }, { "command": "java.view.package.copyRelativeFilePath", "when": "view == javaProjectExplorer && viewItem =~ /java:(?=.*?\\b\\+uri\\b)/", - "group": "path@25" + "group": "6_copypath@25" + }, + { + "command": "java.view.package.moveFileToTrash", + "when": "view == javaProjectExplorer && viewItem =~ /java:(package|packageRoot)(?=.*?\\b\\+source\\b)(?=.*?\\b\\+uri\\b)/", + "group": "7_modification@10" + }, + { + "command": "java.view.package.moveFileToTrash", + "when": "view == javaProjectExplorer && viewItem =~ /java:file(?=.*?\\b\\+uri\\b)/", + "group": "7_modification@10" + }, + { + "command": "java.view.package.moveFileToTrash", + "when": "view == javaProjectExplorer && viewItem =~ /java:type(?=.*?\\b\\+uri\\b)/", + "group": "7_modification@10" }, { "command": "java.view.package.newJavaClass", diff --git a/package.nls.json b/package.nls.json index 175238ee..43e92b23 100644 --- a/package.nls.json +++ b/package.nls.json @@ -17,6 +17,7 @@ "contributes.commands.java.view.package.copyRelativeFilePath": "Copy Relative Path", "contributes.commands.java.view.package.newJavaClass": "New Java Class", "contributes.commands.java.view.package.newPackage": "New Package", + "contributes.commands.java.view.package.moveFileToTrash": "Delete", "configuration.java.dependency.showMembers": "Show the members in the explorer", "configuration.java.dependency.syncWithFolderExplorer": "Synchronize Java Projects explorer selection with folder explorer", "configuration.java.dependency.autoRefresh": "Synchronize Java Projects explorer with changes", diff --git a/package.nls.zh.json b/package.nls.zh.json index 3b5ec71e..1b7e332e 100644 --- a/package.nls.zh.json +++ b/package.nls.zh.json @@ -17,6 +17,7 @@ "contributes.commands.java.view.package.copyRelativeFilePath": "复制相对路径", "contributes.commands.java.view.package.newJavaClass": "创建 Java 类", "contributes.commands.java.view.package.newPackage": "创建包", + "contributes.commands.java.view.package.moveFileToTrash": "删除", "configuration.java.dependency.showMembers": "在 Java 项目管理器中显示成员", "configuration.java.dependency.syncWithFolderExplorer": "在 Java 项目管理器中同步关联当前打开的文件", "configuration.java.dependency.autoRefresh": "在 Java 项目管理器中自动同步修改", diff --git a/src/commands.ts b/src/commands.ts index fd078262..618aa92d 100644 --- a/src/commands.ts +++ b/src/commands.ts @@ -36,6 +36,8 @@ export namespace Commands { export const VIEW_PACKAGE_NEW_JAVA_PACKAGE = "java.view.package.newPackage"; + export const VIEW_PACKAGE_MOVE_FILE_TO_TRASH = "java.view.package.moveFileToTrash"; + export const VIEW_PACKAGE_REVEAL_IN_PROJECT_EXPLORER = "java.view.package.revealInProjectExplorer"; export const JAVA_PROJECT_CREATE = "java.project.create"; diff --git a/src/constants.ts b/src/constants.ts index 074841d0..5cc5031b 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -14,6 +14,8 @@ export namespace Explorer { PackageRoot = "packageRoot", Package = "package", Jar = "jar", + File = "file", + Type = "type", } } diff --git a/src/explorerCommands/delete.ts b/src/explorerCommands/delete.ts new file mode 100644 index 00000000..380679c5 --- /dev/null +++ b/src/explorerCommands/delete.ts @@ -0,0 +1,44 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +import { Uri, window, workspace } from "vscode"; +import { DataNode } from "../views/dataNode"; +import { ExplorerNode } from "../views/explorerNode"; +import { isMutable } from "./utils"; + +const confirmMessage = "Move to Recycle Bin"; + +export async function deleteFiles(node: DataNode, selectedNode: ExplorerNode): Promise { + // if command not invoked by context menu, use selected node in explorer + if (!node) { + node = selectedNode as DataNode; + // avoid delete dependency files + if (!isMutable(node)) { + return; + } + } + + const children = await node.getChildren(); + const isFolder = children && children.length !== 0; + const message = getInformationMessage(node.name, isFolder); + + const answer: string | undefined = await window.showInformationMessage( + message, + { modal: true }, + confirmMessage, + ); + + if (answer === confirmMessage) { + workspace.fs.delete(Uri.parse(node.uri), { + recursive: true, + useTrash: true, + }); + } +} + +function getInformationMessage(name: string, isFolder: boolean): string { + const folderMsg = isFolder ? " and its contents" : ""; + const msg = `Are you sure you want to delete \'${name}\'${folderMsg}?\n\n`; + const additionMsg = "You can restore from the Recycle Bin."; + return msg + additionMsg; +} diff --git a/src/explorerCommands/utils.ts b/src/explorerCommands/utils.ts new file mode 100644 index 00000000..561bfdcd --- /dev/null +++ b/src/explorerCommands/utils.ts @@ -0,0 +1,13 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +import { DataNode } from "../views/dataNode"; + +export function isMutable(node: DataNode): boolean { + const packageExp = /java:(package|packageRoot)(?=.*?\b\+source\b)(?=.*?\b\+uri\b)/; + const fileExp = /java:file(?=.*?\b\+uri\b)/; + const typeExp = /java:type(?=.*?\b\+uri\b)/; + + const contextValue = node.computeContextValue(); + return packageExp.test(contextValue) || fileExp.test(contextValue) || typeExp.test(contextValue); +} diff --git a/src/views/PrimaryTypeNode.ts b/src/views/PrimaryTypeNode.ts index 7f473c3e..3b7dfb49 100644 --- a/src/views/PrimaryTypeNode.ts +++ b/src/views/PrimaryTypeNode.ts @@ -4,6 +4,7 @@ import { Command, commands, DocumentSymbol, SymbolInformation, SymbolKind, TextDocument, ThemeIcon, Uri, workspace } from "vscode"; import { createUuid, sendOperationEnd, sendOperationStart } from "vscode-extension-telemetry-wrapper"; import { Commands } from "../commands"; +import { Explorer } from "../constants"; import { INodeData, TypeKind } from "../java/nodeData"; import { Settings } from "../settings"; import { DataNode } from "./dataNode"; @@ -88,4 +89,17 @@ export class PrimaryTypeNode extends DataNode { arguments: [this.uri], }; } + + protected get contextValue(): string { + const context = Explorer.ContextValueType.Type; + const type = this.nodeData.metaData[PrimaryTypeNode.K_TYPE_KIND]; + + if (type === TypeKind.Enum) { + return `${context}+enum`; + } else if (type === TypeKind.Interface) { + return `${context}+interface`; + } else { + return `${context}+class`; + } + } } diff --git a/src/views/dataNode.ts b/src/views/dataNode.ts index d3d4d8a3..d8a25482 100644 --- a/src/views/dataNode.ts +++ b/src/views/dataNode.ts @@ -74,7 +74,7 @@ export abstract class DataNode extends ExplorerNode { } } - protected computeContextValue(): string { + public computeContextValue(): string { let contextValue = this.contextValue; if (this.uri && this.uri.startsWith("file:")) { contextValue = `${contextValue || ""}+uri`; diff --git a/src/views/dependencyDataProvider.ts b/src/views/dependencyDataProvider.ts index 2d79fc90..4715af12 100644 --- a/src/views/dependencyDataProvider.ts +++ b/src/views/dependencyDataProvider.ts @@ -46,9 +46,9 @@ export class DependencyDataProvider implements TreeDataProvider { context.subscriptions.push(instrumentOperationAsVsCodeCommand(Commands.VIEW_PACKAGE_COPY_RELATIVE_FILE_PATH, (node: INodeData) => commands.executeCommand("copyRelativeFilePath", Uri.parse(node.uri)))); context.subscriptions.push(instrumentOperationAsVsCodeCommand(Commands.VIEW_PACKAGE_OPEN_FILE, (uri) => - commands.executeCommand(Commands.VSCODE_OPEN, Uri.parse(uri)))); + commands.executeCommand(Commands.VSCODE_OPEN, Uri.parse(uri), { preserveFocus: true }))); context.subscriptions.push(instrumentOperationAsVsCodeCommand(Commands.VIEW_PACKAGE_OUTLINE, (uri, range) => - window.showTextDocument(Uri.parse(uri), { selection: range }))); + window.showTextDocument(Uri.parse(uri), { selection: range }))); context.subscriptions.push(instrumentOperationAsVsCodeCommand(Commands.JAVA_PROJECT_BUILD_WORKSPACE, () => commands.executeCommand(Commands.JAVA_BUILD_WORKSPACE))); context.subscriptions.push(instrumentOperationAsVsCodeCommand(Commands.JAVA_PROJECT_CLEAN_WORKSPACE, () => diff --git a/src/views/dependencyExplorer.ts b/src/views/dependencyExplorer.ts index fa651583..52afeebf 100644 --- a/src/views/dependencyExplorer.ts +++ b/src/views/dependencyExplorer.ts @@ -7,6 +7,7 @@ import { commands, Disposable, ExtensionContext, TextEditor, TreeView, TreeViewV import { instrumentOperationAsVsCodeCommand } from "vscode-extension-telemetry-wrapper"; import { Commands } from "../commands"; import { Build } from "../constants"; +import { deleteFiles } from "../explorerCommands/delete"; import { isStandardServerReady } from "../extension"; import { Jdtls } from "../java/jdtls"; import { INodeData } from "../java/nodeData"; @@ -72,6 +73,13 @@ export class DependencyExplorer implements Disposable { this.reveal(uri); }), ); + + context.subscriptions.push( + instrumentOperationAsVsCodeCommand(Commands.VIEW_PACKAGE_MOVE_FILE_TO_TRASH, (node: DataNode) => { + const firstSelectedNode = this._dependencyViewer.selection[0]; + deleteFiles(node, firstSelectedNode); + }), + ); } public dispose(): void { diff --git a/src/views/fileNode.ts b/src/views/fileNode.ts index 9b3f1b38..35297ceb 100644 --- a/src/views/fileNode.ts +++ b/src/views/fileNode.ts @@ -3,6 +3,7 @@ import { Command, ThemeColor, ThemeIcon, TreeItem, TreeItemCollapsibleState } from "vscode"; import { Commands } from "../commands"; +import { Explorer } from "../constants"; import { INodeData } from "../java/nodeData"; import { DataNode } from "./dataNode"; import { ExplorerNode } from "./explorerNode"; @@ -35,4 +36,8 @@ export class FileNode extends DataNode { arguments: [this.uri], }; } + + protected get contextValue(): string { + return Explorer.ContextValueType.File; + } }