Skip to content

Commit eed2e9f

Browse files
authored
feat: Support adding referenced libraries via drag and drop (#637)
Signed-off-by: sheche <[email protected]>
1 parent 5e63a42 commit eed2e9f

File tree

2 files changed

+109
-29
lines changed

2 files changed

+109
-29
lines changed

src/controllers/libraryController.ts

Lines changed: 23 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -33,13 +33,6 @@ export class LibraryController implements Disposable {
3333
this.disposable.dispose();
3434
}
3535

36-
public async addLibraryGlobs(libraryGlobs: string[]) {
37-
const setting = Settings.referencedLibraries();
38-
setting.exclude = this.dedupAlreadyCoveredPattern(libraryGlobs, ...setting.exclude);
39-
setting.include = this.updatePatternArray(setting.include, ...libraryGlobs);
40-
Settings.updateReferencedLibraries(setting);
41-
}
42-
4336
public async addLibraries(canSelectFolders?: boolean) {
4437
const workspaceFolder: WorkspaceFolder | undefined = Utility.getDefaultWorkspaceFolder();
4538
const isMac = platform() === "darwin";
@@ -54,7 +47,7 @@ export class LibraryController implements Disposable {
5447
if (!results) {
5548
return;
5649
}
57-
this.addLibraryGlobs(await Promise.all(results.map(async (uri: Uri) => {
50+
addLibraryGlobs(await Promise.all(results.map(async (uri: Uri) => {
5851
// keep the param: `includeWorkspaceFolder` to false here
5952
// since the multi-root is not supported well for invisible projects
6053
const uriPath = workspace.asRelativePath(uri, false);
@@ -74,7 +67,7 @@ export class LibraryController implements Disposable {
7467
});
7568
if (removedPaths.length === 0) {
7669
// No duplicated item in include array, add it into the exclude field
77-
setting.exclude = this.updatePatternArray(setting.exclude, workspace.asRelativePath(removalFsPath, false));
70+
setting.exclude = updatePatternArray(setting.exclude, workspace.asRelativePath(removalFsPath, false));
7871
}
7972
Settings.updateReferencedLibraries(setting);
8073
}
@@ -85,21 +78,28 @@ export class LibraryController implements Disposable {
8578
await Jdtls.refreshLibraries(workspaceFolder.uri.toString());
8679
}
8780
}
81+
}
8882

89-
/**
90-
* Check if the `update` patterns are already covered by `origin` patterns and return those uncovered
91-
*/
92-
private dedupAlreadyCoveredPattern(origin: string[], ...update: string[]): string[] {
93-
return update.filter((newPattern) => {
94-
return !origin.some((originPattern) => {
95-
return minimatch(newPattern, originPattern);
96-
});
83+
export function addLibraryGlobs(libraryGlobs: string[]) {
84+
const setting = Settings.referencedLibraries();
85+
setting.exclude = dedupAlreadyCoveredPattern(libraryGlobs, ...setting.exclude);
86+
setting.include = updatePatternArray(setting.include, ...libraryGlobs);
87+
Settings.updateReferencedLibraries(setting);
88+
}
89+
90+
/**
91+
* Check if the `update` patterns are already covered by `origin` patterns and return those uncovered
92+
*/
93+
function dedupAlreadyCoveredPattern(origin: string[], ...update: string[]): string[] {
94+
return update.filter((newPattern) => {
95+
return !origin.some((originPattern) => {
96+
return minimatch(newPattern, originPattern);
9797
});
98-
}
98+
});
99+
}
99100

100-
private updatePatternArray(origin: string[], ...update: string[]): string[] {
101-
update = this.dedupAlreadyCoveredPattern(origin, ...update);
102-
origin.push(...update);
103-
return _.uniq(origin);
104-
}
101+
function updatePatternArray(origin: string[], ...update: string[]): string[] {
102+
update = dedupAlreadyCoveredPattern(origin, ...update);
103+
origin.push(...update);
104+
return _.uniq(origin);
105105
}

src/views/DragAndDropController.ts

Lines changed: 86 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,14 @@ import { PackageRootNode } from "./packageRootNode";
1818
import { PrimaryTypeNode } from "./PrimaryTypeNode";
1919
import { ProjectNode } from "./projectNode";
2020
import { WorkspaceNode } from "./workspaceNode";
21+
import { addLibraryGlobs } from "../controllers/libraryController";
22+
import { sendError } from "vscode-extension-telemetry-wrapper";
2123

2224
export class DragAndDropController implements TreeDragAndDropController<ExplorerNode> {
2325

2426
dropMimeTypes: string[] = [
2527
Explorer.Mime.JavaProjectExplorer,
28+
Explorer.Mime.TextUriList,
2629
];
2730
dragMimeTypes: string[] = [
2831
Explorer.Mime.TextUriList,
@@ -41,6 +44,33 @@ export class DragAndDropController implements TreeDragAndDropController<Explorer
4144
await this.dropFromJavaProjectExplorer(target, data.value);
4245
return;
4346
}
47+
48+
let uriList: string[] = [];
49+
// an undefined value will be contained with the key 'text/uri-list', so here
50+
// we iterate all the entries to get the uri list.
51+
// see: https://github.com/microsoft/vscode/issues/152031
52+
dataTransfer.forEach((value: DataTransferItem, key: string) => {
53+
if (key === Explorer.Mime.TextUriList && value.value) {
54+
uriList.push(...(value.value as string).split("\n"));
55+
}
56+
});
57+
58+
uriList = uriList.map(u => {
59+
try {
60+
const uri = Uri.parse(u, true /* strict */);
61+
if (uri.scheme !== "file") {
62+
return undefined;
63+
}
64+
return u;
65+
} catch (e) {
66+
sendError(e);
67+
return undefined;
68+
}
69+
}).filter(Boolean) as string[];
70+
71+
if (uriList.length) {
72+
this.dropFromFileExplorer(target, uriList);
73+
}
4474
}
4575

4676
/**
@@ -82,7 +112,7 @@ export class DragAndDropController implements TreeDragAndDropController<Explorer
82112
* @param target the drop node.
83113
* @param uri uri in the data transfer.
84114
*/
85-
public async dropFromJavaProjectExplorer(target: ExplorerNode | undefined, uri: string): Promise<void> {
115+
private async dropFromJavaProjectExplorer(target: ExplorerNode | undefined, uri: string): Promise<void> {
86116
const source: DataNode | undefined = explorerNodeCache.getDataNode(Uri.parse(uri));
87117
if (!this.isDraggableNode(source)) {
88118
return;
@@ -98,18 +128,38 @@ export class DragAndDropController implements TreeDragAndDropController<Explorer
98128
}
99129

100130
if (target instanceof ContainerNode) {
101-
if (target.getContainerType() !== ContainerType.ReferencedLibrary) {
131+
if (target.getContainerType() !== ContainerType.ReferencedLibrary
132+
|| !(target.getParent() as ProjectNode).isUnmanagedFolder()) {
102133
return;
103134
}
104135

105-
if (!(target.getParent() as ProjectNode).isUnmanagedFolder()) {
136+
this.addReferencedLibraries([source?.uri!]);
137+
} else if (target instanceof PackageRootNode || target instanceof PackageNode
138+
|| target instanceof FolderNode) {
139+
await this.move(source!, target);
140+
}
141+
}
142+
143+
/**
144+
* Handle the DnD event which comes from VS Code's file explorer or system file explorer.
145+
* @param target the drop node.
146+
* @param uris uris of the dragged files.
147+
*/
148+
private async dropFromFileExplorer(target: ExplorerNode | undefined, uris: string[]): Promise<void> {
149+
if (!this.isDroppableNode(target)) {
150+
return;
151+
}
152+
153+
if (target instanceof ContainerNode) {
154+
if (target.getContainerType() !== ContainerType.ReferencedLibrary
155+
|| !(target.getParent() as ProjectNode).isUnmanagedFolder()) {
106156
return;
107157
}
108158

109-
// TODO: referenced library
159+
this.addReferencedLibraries(uris);
110160
} else if (target instanceof PackageRootNode || target instanceof PackageNode
111161
|| target instanceof FolderNode) {
112-
await this.move(source!, target);
162+
// TODO: copy the resources to other nodes
113163
}
114164
}
115165

@@ -161,7 +211,7 @@ export class DragAndDropController implements TreeDragAndDropController<Explorer
161211
return false;
162212
}
163213

164-
if (node instanceof DataNode && !node.uri) {
214+
if (node instanceof DataNode && !(node instanceof ContainerNode) && !node.uri) {
165215
return false;
166216
}
167217

@@ -232,4 +282,34 @@ export class DragAndDropController implements TreeDragAndDropController<Explorer
232282

233283
return true;
234284
}
285+
286+
/**
287+
* Parse the input uri strings to pattern strings and set them to
288+
* the setting: 'java.project.referencedLibraries'.
289+
* @param uriStrings uri strings
290+
*/
291+
private async addReferencedLibraries(uriStrings: string[]): Promise<void> {
292+
const pattern = (await Promise.all(uriStrings.map(async uriString => {
293+
try {
294+
const uri = Uri.parse(uriString, true /* strict */);
295+
if (uri.scheme !== "file") {
296+
return undefined;
297+
}
298+
const isDirectory = (await fse.stat(uri.fsPath)).isDirectory();
299+
// only jars and folders can be dropped into referenced libraries.
300+
if (!isDirectory && path.extname(uri.fsPath) !== ".jar") {
301+
return undefined;
302+
}
303+
const uriPath = workspace.asRelativePath(uri, false);
304+
return isDirectory ? uriPath + "/**/*.jar" : uriPath;
305+
} catch (e) {
306+
sendError(e);
307+
return undefined;
308+
}
309+
}))).filter(Boolean);
310+
311+
if (pattern) {
312+
addLibraryGlobs(pattern as string[]);
313+
}
314+
}
235315
}

0 commit comments

Comments
 (0)