Skip to content

Commit 6995e28

Browse files
committed
Add Filesystem API to jars
1 parent 53b3ba9 commit 6995e28

File tree

5 files changed

+338
-88
lines changed

5 files changed

+338
-88
lines changed

package.json

+26-16
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,8 @@
7171
{
7272
"id": "java",
7373
"extensions": [
74-
".cfr"
74+
".cfr",
75+
".class"
7576
]
7677
},
7778
{
@@ -427,6 +428,11 @@
427428
"category": "Metals",
428429
"title": "Run all Scalafix rules"
429430
},
431+
{
432+
"command": "metals.show-libraries-folder",
433+
"category": "Metals",
434+
"title": "Show libraries folder in file explorer"
435+
},
430436
{
431437
"command": "metals.show-tasty",
432438
"category": "Metals",
@@ -592,33 +598,37 @@
592598
}
593599
],
594600
"commandPalette": [
601+
{
602+
"command": "metals.show-libraries-folder",
603+
"when": "metals:enabled"
604+
},
595605
{
596606
"command": "metals.show-tasty",
597-
"when": "metals:enabled && resourceExtname==.scala || metals:enabled && resourceExtname==.tasty || metals:enabled && resourceExtname==.tasty-decoded"
607+
"when": "metals:enabled && resourceScheme != metalsfs && resourceExtname==.scala || metals:enabled && resourceExtname==.tasty-decoded"
598608
},
599609
{
600610
"command": "metals.show-cfr",
601-
"when": "metals:enabled && resourceExtname==.java || metals:enabled && resourceExtname==.scala || metals:enabled && resourceExtname==.class || metals:enabled && resourceExtname==.cfr"
611+
"when": "metals:enabled && resourceScheme != metalsfs && resourceExtname==.java || metals:enabled && resourceScheme != metalsfs && resourceExtname==.scala || metals:enabled && resourceExtname==.cfr"
602612
},
603613
{
604614
"command": "metals.show-javap",
605-
"when": "metals:enabled && resourceExtname==.java || metals:enabled && resourceExtname==.scala || metals:enabled && resourceExtname==.class || metals:enabled && resourceExtname==.javap"
615+
"when": "metals:enabled && resourceScheme != metalsfs && resourceExtname==.java || metals:enabled && resourceScheme != metalsfs && resourceExtname==.scala || metals:enabled && resourceScheme == metalsfs && resourceExtname==.class || metals:enabled && resourceExtname==.javap"
606616
},
607617
{
608618
"command": "metals.show-javap-verbose",
609-
"when": "metals:enabled && resourceExtname==.java || metals:enabled && resourceExtname==.scala || metals:enabled && resourceExtname==.class || metals:enabled && resourceExtname==.javap-verbose"
619+
"when": "metals:enabled && resourceScheme != metalsfs && resourceExtname==.java || metals:enabled && resourceScheme != metalsfs && resourceExtname==.scala || metals:enabled && resourceScheme == metalsfs && resourceExtname==.class || metals:enabled && resourceExtname==.javap-verbose"
610620
},
611621
{
612622
"command": "metals.show-semanticdb-compact",
613-
"when": "metals:enabled && resourceExtname==.java || metals:enabled && resourceExtname==.scala || metals:enabled && resourceExtname==.semanticdb || metals:enabled && resourceExtname==.semanticdb-compact"
623+
"when": "metals:enabled && resourceExtname==.java || metals:enabled && resourceExtname==.scala || metals:enabled && resourceScheme == metalsfs && resourceExtname==.class || metals:enabled && resourceExtname==.semanticdb-compact"
614624
},
615625
{
616626
"command": "metals.show-semanticdb-detailed",
617-
"when": "metals:enabled && resourceExtname==.java || metals:enabled && resourceExtname==.scala || metals:enabled && resourceExtname==.semanticdb || metals:enabled && resourceExtname==.semanticdb-detailed"
627+
"when": "metals:enabled && resourceExtname==.java || metals:enabled && resourceExtname==.scala || metals:enabled && resourceScheme == metalsfs && resourceExtname==.class || metals:enabled && resourceExtname==.semanticdb-detailed"
618628
},
619629
{
620630
"command": "metals.show-semanticdb-proto",
621-
"when": "metals:enabled && resourceExtname==.java || metals:enabled && resourceExtname==.scala || metals:enabled && resourceExtname==.semanticdb || metals:enabled && resourceExtname==.semanticdb-proto"
631+
"when": "metals:enabled && resourceExtname==.java || metals:enabled && resourceExtname==.scala || metals:enabled && resourceScheme == metalsfs && resourceExtname==.class || metals:enabled && resourceExtname==.semanticdb-proto"
622632
},
623633
{
624634
"command": "metals.reveal-active-file",
@@ -686,11 +696,11 @@
686696
},
687697
{
688698
"command": "metals.new-scala-file",
689-
"when": "metals:enabled"
699+
"when": "metals:enabled && resourceScheme != metalsfs"
690700
},
691701
{
692702
"command": "metals.new-java-file",
693-
"when": "metals:enabled"
703+
"when": "metals:enabled && resourceScheme != metalsfs"
694704
},
695705
{
696706
"command": "metals.new-scala-project",
@@ -728,12 +738,12 @@
728738
"explorer/context": [
729739
{
730740
"command": "metals.new-scala-file",
731-
"when": "metals:enabled",
741+
"when": "metals:enabled && resourceScheme != metalsfs",
732742
"group": "navigation@1"
733743
},
734744
{
735745
"command": "metals.new-java-file",
736-
"when": "metals:enabled",
746+
"when": "metals:enabled && resourceScheme != metalsfs",
737747
"group": "navigation@2"
738748
},
739749
{
@@ -745,22 +755,22 @@
745755
"metals.analyze": [
746756
{
747757
"command": "metals.show-tasty",
748-
"when": "metals:enabled && resourceExtname==.scala || metals:enabled && resourceExtname==.tasty || metals:enabled && resourceExtname==.tasty-decoded",
758+
"when": "metals:enabled && resourceScheme != metalsfs && resourceExtname==.scala || metals:enabled && resourceExtname==.tasty || metals:enabled && resourceExtname==.tasty-decoded",
749759
"group": "metals-1@1"
750760
},
751761
{
752762
"command": "metals.show-cfr",
753-
"when": "metals:enabled && resourceExtname==.java || metals:enabled && resourceExtname==.scala || metals:enabled && resourceExtname==.class",
763+
"when": "metals:enabled && resourceScheme != metalsfs && resourceExtname==.java || metals:enabled && resourceScheme != metalsfs && resourceExtname==.scala || metals:enabled && resourceScheme != metalsfs && resourceExtname==.class",
754764
"group": "metals-2@1"
755765
},
756766
{
757767
"command": "metals.show-javap",
758-
"when": "metals:enabled && resourceExtname==.java || metals:enabled && resourceExtname==.scala || metals:enabled && resourceExtname==.class",
768+
"when": "metals:enabled && resourceScheme != metalsfs && resourceExtname==.java || metals:enabled && resourceScheme != metalsfs && resourceExtname==.scala || metals:enabled && resourceExtname==.class",
759769
"group": "metals-3@1"
760770
},
761771
{
762772
"command": "metals.show-javap-verbose",
763-
"when": "metals:enabled && resourceExtname==.java || metals:enabled && resourceExtname==.scala || metals:enabled && resourceExtname==.class",
773+
"when": "metals:enabled && resourceScheme != metalsfs && resourceExtname==.java || metals:enabled && resourceScheme != metalsfs && resourceExtname==.scala || metals:enabled && resourceExtname==.class",
764774
"group": "metals-3@2"
765775
},
766776
{

src/extension.ts

+103-58
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import {
2828
ProviderResult,
2929
Hover,
3030
TextDocument,
31+
FileSystemProvider,
3132
} from "vscode";
3233
import {
3334
LanguageClient,
@@ -78,7 +79,11 @@ import {
7879
startFindInFilesProvider,
7980
} from "./findInFiles";
8081
import * as ext from "./hoverExtension";
81-
import { decodeAndShowFile, MetalsFileProvider } from "./metalsContentProvider";
82+
import {
83+
decodeAndShowFile,
84+
DecodeExtension,
85+
MetalsFileProvider,
86+
} from "./metalsContentProvider";
8287
import {
8388
getJavaHomeFromConfig,
8489
getTextDocumentPositionParams,
@@ -91,6 +96,7 @@ import { getServerVersion } from "./getServerVersion";
9196
import { getCoursierMirrorPath } from "./mirrors";
9297
import { DoctorProvider } from "./doctor";
9398
import { showReleaseNotes } from "./releaseNotesProvider";
99+
import MetalsFileSystemProvider from "./metalsFileSystemProvider";
94100

95101
const outputChannel = window.createOutputChannel("Metals");
96102
const openSettingsAction = "Open settings";
@@ -99,6 +105,8 @@ const downloadJava = "Download Java";
99105
const installJava11Action = "Install Java (JDK 11)";
100106
const installJava17Action = "Install Java (JDK 17)";
101107

108+
const librariesURI = Uri.parse("metalsfs:/metalsLibraries");
109+
102110
let treeViews: MetalsTreeViews | undefined;
103111
let currentClient: LanguageClient | undefined;
104112

@@ -358,6 +366,7 @@ function launchMetals(
358366
icons: "vscode",
359367
inputBoxProvider: true,
360368
isVirtualDocumentSupported: true,
369+
isLibraryFileSystemSupported: true,
361370
openFilesOnRenameProvider: true,
362371
openNewWindowProvider: true,
363372
quickPickProvider: true,
@@ -373,8 +382,8 @@ function launchMetals(
373382
documentSelector: [
374383
{ scheme: "file", language: "scala" },
375384
{ scheme: "file", language: "java" },
376-
{ scheme: "jar", language: "scala" },
377-
{ scheme: "jar", language: "java" },
385+
{ scheme: "metalsfs", language: "scala" },
386+
{ scheme: "metalsfs", language: "java" },
378387
],
379388
synchronize: {
380389
configurationSection: "metals",
@@ -449,6 +458,18 @@ function launchMetals(
449458
);
450459
}
451460

461+
function registerFileSystemProvider(
462+
scheme: string,
463+
provider: FileSystemProvider
464+
) {
465+
context.subscriptions.push(
466+
workspace.registerFileSystemProvider(scheme, provider, {
467+
isCaseSensitive: true,
468+
isReadonly: true,
469+
})
470+
);
471+
}
472+
452473
function registerTextDocumentContentProvider(
453474
scheme: string,
454475
provider: TextDocumentContentProvider
@@ -459,52 +480,26 @@ function launchMetals(
459480
}
460481

461482
const metalsFileProvider = new MetalsFileProvider(client);
462-
463483
registerTextDocumentContentProvider("metalsDecode", metalsFileProvider);
464-
registerTextDocumentContentProvider("jar", metalsFileProvider);
465-
466-
registerCommand("metals.show-cfr", async (uri: Uri) => {
467-
await decodeAndShowFile(client, metalsFileProvider, uri, "cfr");
468-
});
469-
470-
registerCommand("metals.show-javap-verbose", async (uri: Uri) => {
471-
await decodeAndShowFile(client, metalsFileProvider, uri, "javap-verbose");
472-
});
473-
474-
registerCommand("metals.show-javap", async (uri: Uri) => {
475-
await decodeAndShowFile(client, metalsFileProvider, uri, "javap");
476-
});
477-
478-
registerCommand("metals.show-semanticdb-compact", async (uri: Uri) => {
479-
await decodeAndShowFile(
480-
client,
481-
metalsFileProvider,
482-
uri,
483-
"semanticdb-compact"
484-
);
485-
});
486-
487-
registerCommand("metals.show-semanticdb-detailed", async (uri: Uri) => {
488-
await decodeAndShowFile(
489-
client,
490-
metalsFileProvider,
491-
uri,
492-
"semanticdb-detailed"
493-
);
494-
});
495484

496-
registerCommand("metals.show-semanticdb-proto", async (uri: Uri) => {
497-
await decodeAndShowFile(
498-
client,
499-
metalsFileProvider,
500-
uri,
501-
"semanticdb-proto"
502-
);
503-
});
485+
registerCommand("metals.show-libraries-folder", async () =>
486+
addLibrariesFolder()
487+
);
504488

505-
registerCommand("metals.show-tasty", async (uri: Uri) => {
506-
await decodeAndShowFile(client, metalsFileProvider, uri, "tasty-decoded");
507-
});
489+
const decodeCommands: [string, DecodeExtension][] = [
490+
["cfr", "cfr"],
491+
["javap-verbose", "javap-verbose"],
492+
["javap", "javap"],
493+
["semanticdb-compact", "semanticdb-compact"],
494+
["semanticdb-detailed", "semanticdb-detailed"],
495+
["semanticdb-proto", "semanticdb-proto"],
496+
["tasty", "tasty-decoded"],
497+
];
498+
decodeCommands.forEach((command) =>
499+
registerCommand(`metals.show-${command[0]}`, async (uri: Uri) => {
500+
await decodeAndShowFile(client, metalsFileProvider, uri, command[1]);
501+
})
502+
);
508503

509504
registerCommand(
510505
"metals.restartServer",
@@ -658,7 +653,7 @@ function launchMetals(
658653
codeLensRefresher
659654
);
660655
languages.registerCodeLensProvider(
661-
{ scheme: "jar", language: "scala" },
656+
{ scheme: "metalsfs", language: "scala" },
662657
codeLensRefresher
663658
);
664659

@@ -725,6 +720,19 @@ function launchMetals(
725720
case ClientCommands.ReloadDoctor:
726721
doctorProvider.reloadOrRefreshDoctor(params);
727722
break;
723+
case "metals-library-filesystem-ready": {
724+
const metalsFileSystemProvider = new MetalsFileSystemProvider(
725+
client,
726+
librariesURI
727+
);
728+
registerFileSystemProvider(
729+
librariesURI.scheme,
730+
metalsFileSystemProvider
731+
);
732+
733+
metalsFileSystemProvider.reinitialiseURI(librariesURI);
734+
break;
735+
}
728736
case ClientCommands.FocusDiagnostics:
729737
commands.executeCommand(ClientCommands.FocusDiagnostics);
730738
break;
@@ -898,7 +906,7 @@ function launchMetals(
898906
registerCommand("metals.reveal-active-file", () => {
899907
if (treeViews) {
900908
const editor = window.visibleTextEditors.find((e) =>
901-
isSupportedLanguage(e.document.languageId)
909+
isSupportedDocument(e.document)
902910
);
903911
if (editor) {
904912
const params = getTextDocumentPositionParams(editor);
@@ -975,7 +983,6 @@ function launchMetals(
975983
client,
976984
findInFilesProvider,
977985
findInFilesView,
978-
metalsFileProvider,
979986
outputChannel
980987
)
981988
);
@@ -1021,7 +1028,7 @@ function launchMetals(
10211028
);
10221029

10231030
window.onDidChangeActiveTextEditor((editor) => {
1024-
if (editor && isSupportedLanguage(editor.document.languageId)) {
1031+
if (editor && isSupportedDocument(editor.document)) {
10251032
client.sendNotification(
10261033
MetalsDidFocus.type,
10271034
editor.document.uri.toString()
@@ -1277,14 +1284,52 @@ function detectLaunchConfigurationChanges() {
12771284
);
12781285
}
12791286

1280-
function isSupportedLanguage(languageId: TextDocument["languageId"]): boolean {
1281-
switch (languageId) {
1282-
case "scala":
1283-
case "sc":
1284-
case "java":
1285-
return true;
1286-
default:
1287-
return false;
1287+
function isSupportedDocument(textDocument: TextDocument): boolean {
1288+
return (
1289+
["metalsfs", "file"].includes(textDocument.uri.scheme) &&
1290+
["scala", "sc", "java"].includes(textDocument.languageId)
1291+
);
1292+
}
1293+
1294+
function addLibrariesFolder() {
1295+
const libraryFolderName = "Metals - Libraries";
1296+
1297+
// filesystem can be persistent across VSCode sessions so may already exist
1298+
const newLibraryFolder = {
1299+
uri: librariesURI,
1300+
name: libraryFolderName,
1301+
};
1302+
const folderByUri = workspace.getWorkspaceFolder(librariesURI);
1303+
if (folderByUri && folderByUri.name != libraryFolderName) {
1304+
// wrong name on libraries folder
1305+
workspace.updateWorkspaceFolders(folderByUri.index, 1, newLibraryFolder);
1306+
} else {
1307+
const folderByName = workspace.workspaceFolders?.find(
1308+
(folder) => folder.name == libraryFolderName
1309+
);
1310+
if (folderByName && folderByName.uri.toString != librariesURI.toString) {
1311+
if (folderByUri) {
1312+
// too many libraries folders
1313+
workspace.updateWorkspaceFolders(folderByName.index, 1);
1314+
} else {
1315+
// wrong root on libraries folder
1316+
workspace.updateWorkspaceFolders(
1317+
folderByName.index,
1318+
1,
1319+
newLibraryFolder
1320+
);
1321+
}
1322+
} else if (!folderByUri) {
1323+
// missing libraries folder
1324+
const workspaceCount = workspace.workspaceFolders?.length;
1325+
if (workspaceCount) {
1326+
workspace.updateWorkspaceFolders(
1327+
workspaceCount,
1328+
null,
1329+
newLibraryFolder
1330+
);
1331+
}
1332+
}
12881333
}
12891334
}
12901335

0 commit comments

Comments
 (0)