|
1 | 1 | 'use strict'; |
2 | 2 |
|
3 | | -import { commands, window, ExtensionContext, workspace, Position, Uri, TextDocument } from 'vscode'; |
4 | | -import { LanguageClient, FormattingOptions } from 'vscode-languageclient'; |
| 3 | +import { existsSync } from 'fs'; |
| 4 | +import * as path from 'path'; |
| 5 | +import { commands, ExtensionContext, Position, TextDocument, Uri, window, workspace } from 'vscode'; |
| 6 | +import { FormattingOptions, LanguageClient, WorkspaceEdit, CreateFile, RenameFile, DeleteFile, TextDocumentEdit } from 'vscode-languageclient'; |
5 | 7 | import { Commands as javaCommands } from './commands'; |
6 | | -import { GetRefactorEditRequest, RefactorWorkspaceEdit, RenamePosition } from './protocol'; |
| 8 | +import { GetRefactorEditRequest, MoveRequest, RefactorWorkspaceEdit, RenamePosition, GetMoveDestinationsRequest } from './protocol'; |
7 | 9 |
|
8 | 10 | export function registerCommands(languageClient: LanguageClient, context: ExtensionContext) { |
9 | 11 | registerApplyRefactorCommand(languageClient, context); |
@@ -73,22 +75,172 @@ function registerApplyRefactorCommand(languageClient: LanguageClient, context: E |
73 | 75 | commandArguments, |
74 | 76 | }); |
75 | 77 |
|
76 | | - if (!result || !result.edit) { |
| 78 | + await applyRefactorEdit(languageClient, result); |
| 79 | + } else if (command === 'moveFile') { |
| 80 | + if (!commandInfo || !commandInfo.uri) { |
77 | 81 | return; |
78 | 82 | } |
79 | 83 |
|
80 | | - const edit = languageClient.protocol2CodeConverter.asWorkspaceEdit(result.edit); |
81 | | - if (edit) { |
82 | | - await workspace.applyEdit(edit); |
| 84 | + await moveFile(languageClient, [Uri.parse(commandInfo.uri)]); |
| 85 | + } |
| 86 | + })); |
| 87 | +} |
| 88 | + |
| 89 | +async function applyRefactorEdit(languageClient: LanguageClient, refactorEdit: RefactorWorkspaceEdit) { |
| 90 | + if (!refactorEdit) { |
| 91 | + return; |
| 92 | + } |
| 93 | + |
| 94 | + if (refactorEdit.errorMessage) { |
| 95 | + window.showErrorMessage(refactorEdit.errorMessage); |
| 96 | + return; |
| 97 | + } |
| 98 | + |
| 99 | + if (refactorEdit.edit) { |
| 100 | + const edit = languageClient.protocol2CodeConverter.asWorkspaceEdit(refactorEdit.edit); |
| 101 | + if (edit) { |
| 102 | + await workspace.applyEdit(edit); |
| 103 | + } |
| 104 | + } |
| 105 | + |
| 106 | + if (refactorEdit.command) { |
| 107 | + if (refactorEdit.command.arguments) { |
| 108 | + await commands.executeCommand(refactorEdit.command.command, ...refactorEdit.command.arguments); |
| 109 | + } else { |
| 110 | + await commands.executeCommand(refactorEdit.command.command); |
| 111 | + } |
| 112 | + } |
| 113 | +} |
| 114 | + |
| 115 | +async function moveFile(languageClient: LanguageClient, fileUris: Uri[]) { |
| 116 | + if (!hasCommonParent(fileUris)) { |
| 117 | + window.showErrorMessage("Moving files from different directories are not supported. Please make sure they are from the same directory."); |
| 118 | + return; |
| 119 | + } |
| 120 | + |
| 121 | + const moveDestinations = await languageClient.sendRequest(GetMoveDestinationsRequest.type, { |
| 122 | + moveKind: 'moveResource', |
| 123 | + sourceUris: fileUris.map(uri => uri.toString()) |
| 124 | + }); |
| 125 | + if (!moveDestinations || !moveDestinations.destinations || !moveDestinations.destinations.length) { |
| 126 | + window.showErrorMessage("Cannot find available Java packages to move the selected files to."); |
| 127 | + return; |
| 128 | + } |
| 129 | + |
| 130 | + const packageNodeItems = moveDestinations.destinations.map((packageNode) => { |
| 131 | + const packageUri: Uri = packageNode.uri ? Uri.parse(packageNode.uri) : null; |
| 132 | + const displayPath: string = packageUri ? workspace.asRelativePath(packageUri, true) : packageNode.path; |
| 133 | + return { |
| 134 | + label: (packageNode.isParentOfSelectedFile ? '* ' : '') + packageNode.displayName, |
| 135 | + description: displayPath, |
| 136 | + packageNode, |
| 137 | + } |
| 138 | + }); |
| 139 | + |
| 140 | + let placeHolder = (fileUris.length === 1) ? `Choose the target package for ${getFileNameFromUri(fileUris[0])}.` |
| 141 | + : `Choose the target package for ${fileUris.length} selected files.`; |
| 142 | + let selectPackageNodeItem = await window.showQuickPick(packageNodeItems, { |
| 143 | + placeHolder, |
| 144 | + }); |
| 145 | + if (!selectPackageNodeItem) { |
| 146 | + return; |
| 147 | + } |
| 148 | + |
| 149 | + const packageUri: Uri = selectPackageNodeItem.packageNode.uri ? Uri.parse(selectPackageNodeItem.packageNode.uri) : null; |
| 150 | + if (packageUri && packageUri.fsPath) { |
| 151 | + const duplicatedFiles: string[] = []; |
| 152 | + const moveUris: Uri[] = []; |
| 153 | + for (const uri of fileUris) { |
| 154 | + const fileName: string = getFileNameFromUri(uri); |
| 155 | + if (existsSync(path.join(packageUri.fsPath, fileName))) { |
| 156 | + duplicatedFiles.push(fileName); |
| 157 | + } else { |
| 158 | + moveUris.push(uri); |
83 | 159 | } |
| 160 | + } |
| 161 | + |
| 162 | + if (duplicatedFiles.length) { |
| 163 | + window.showWarningMessage(`The files '${duplicatedFiles.join(',')}' already exist in the package '${selectPackageNodeItem.packageNode.displayName}'. The move operation will ignore them.`); |
| 164 | + } |
| 165 | + |
| 166 | + if (!moveUris.length) { |
| 167 | + return; |
| 168 | + } |
| 169 | + |
| 170 | + fileUris = moveUris; |
| 171 | + } |
| 172 | + |
| 173 | + const refactorEdit: RefactorWorkspaceEdit = await languageClient.sendRequest(MoveRequest.type, { |
| 174 | + moveKind: 'moveResource', |
| 175 | + sourceUris: fileUris.map(uri => uri.toString()), |
| 176 | + params: null, |
| 177 | + destination: selectPackageNodeItem.packageNode, |
| 178 | + updateReferences: true, |
| 179 | + }); |
| 180 | + |
| 181 | + await applyRefactorEdit(languageClient, refactorEdit); |
| 182 | + if (refactorEdit && refactorEdit.edit) { |
| 183 | + await saveEdit(refactorEdit.edit); |
| 184 | + } |
| 185 | +} |
| 186 | + |
| 187 | +function getFileNameFromUri(uri: Uri): string { |
| 188 | + return uri.fsPath.replace(/^.*[\\\/]/, ''); |
| 189 | +} |
| 190 | + |
| 191 | +function hasCommonParent(uris: Uri[]): boolean { |
| 192 | + if (uris == null || uris.length <= 1) { |
| 193 | + return true; |
| 194 | + } |
| 195 | + |
| 196 | + const firstParent: string = path.dirname(uris[0].fsPath); |
| 197 | + for (let i = 1; i < uris.length; i++) { |
| 198 | + const parent = path.dirname(uris[i].fsPath); |
| 199 | + if (path.relative(firstParent, parent) !== '.') { |
| 200 | + return false; |
| 201 | + } |
| 202 | + } |
84 | 203 |
|
85 | | - if (result.command) { |
86 | | - if (result.command.arguments) { |
87 | | - await commands.executeCommand(result.command.command, ...result.command.arguments); |
88 | | - } else { |
89 | | - await commands.executeCommand(result.command.command); |
| 204 | + return true; |
| 205 | +} |
| 206 | + |
| 207 | +async function saveEdit(edit: WorkspaceEdit) { |
| 208 | + if (!edit) { |
| 209 | + return; |
| 210 | + } |
| 211 | + |
| 212 | + const touchedFiles: Set<string> = new Set<string>(); |
| 213 | + if (edit.changes) { |
| 214 | + for (const uri of Object.keys(edit.changes)) { |
| 215 | + touchedFiles.add(uri); |
| 216 | + } |
| 217 | + } |
| 218 | + |
| 219 | + if (edit.documentChanges) { |
| 220 | + for (const change of edit.documentChanges) { |
| 221 | + const kind = (<any> change).kind; |
| 222 | + if (kind === 'rename') { |
| 223 | + if (touchedFiles.has((<RenameFile> change).oldUri)) { |
| 224 | + touchedFiles.delete((<RenameFile> change).oldUri); |
| 225 | + touchedFiles.add((<RenameFile> change).newUri); |
90 | 226 | } |
| 227 | + } else if (kind === 'delete') { |
| 228 | + if (touchedFiles.has((<DeleteFile> change).uri)) { |
| 229 | + touchedFiles.delete((<DeleteFile> change).uri); |
| 230 | + } |
| 231 | + } else if (!kind) { |
| 232 | + touchedFiles.add((<TextDocumentEdit> change).textDocument.uri); |
91 | 233 | } |
92 | 234 | } |
93 | | - })); |
| 235 | + } |
| 236 | + |
| 237 | + for (const fileUri of touchedFiles) { |
| 238 | + const uri: Uri = Uri.parse(fileUri); |
| 239 | + const document: TextDocument = await workspace.openTextDocument(uri); |
| 240 | + if (document == null) { |
| 241 | + continue; |
| 242 | + } |
| 243 | + |
| 244 | + await document.save(); |
| 245 | + } |
94 | 246 | } |
0 commit comments