Skip to content

Commit

Permalink
Refactor extension management and add support for annotations
Browse files Browse the repository at this point in the history
  • Loading branch information
MarkusEllyton committed May 13, 2024
1 parent 09f32b8 commit 726f5a4
Show file tree
Hide file tree
Showing 9 changed files with 594 additions and 349 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
### 1.4.0-beta.8
- Update VDMJ plugin jars - add plugin.json metadata to UML plugin/
- Add/update VDMJ HP jars and QuickCheck plugin.
- Add annotation management UI.
- Clean up handling of VDMJ extensions, i.e. libraries, plugins, annotations. They all share common search path now.

### 1.4.0-beta.7
- Update VDMJ plugin jars - add plugin.json metadata to QuickCheck plugin
- Add plugin management UI.
Expand Down
66 changes: 32 additions & 34 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "vdm-vscode",
"version": "1.4.0-beta.7",
"version": "1.4.0-beta.8",
"publisher": "overturetool",
"engines": {
"vscode": "^1.64.0",
Expand Down Expand Up @@ -214,23 +214,13 @@
"category": "VDM"
},
{
"command": "vdm-vscode.addLibraryJarFolders",
"title": "Add folder(s) containing library jars",
"command": "vdm-vscode.addExtensionJarFolders",
"title": "Add folder(s) containing VDMJ extension jars",
"category": "VDM"
},
{
"command": "vdm-vscode.addLibraryJars",
"title": "Add library jars",
"category": "VDM"
},
{
"command": "vdm-vscode.addPluginJarFolders",
"title": "Add folder(s) containing plugin jars",
"category": "VDM"
},
{
"command": "vdm-vscode.addPluginJars",
"title": "Add plugin jars",
"command": "vdm-vscode.addExtensionJars",
"title": "Add VDMJ extension jars",
"category": "VDM"
},
{
Expand All @@ -253,6 +243,12 @@
"category": "VDM",
"enablement": "vdm-vscode.managePlugins"
},
{
"command": "vdm-vscode.manageAnnotations",
"title": "Manage VDMJ Annotations",
"category": "VDM",
"enablement": "vdm-vscode.manageAnnotations"
},
{
"command": "vdm-vscode.javaCodeGen",
"title": "Generate Java Code",
Expand Down Expand Up @@ -548,6 +544,10 @@
"command": "vdm-vscode.managePlugins",
"group": "workspace@1"
},
{
"command": "vdm-vscode.manageAnnotations",
"group": "workspace@1"
},
{
"command": "vdm-vscode.addRunConfiguration",
"group": "workspace@2"
Expand Down Expand Up @@ -792,6 +792,10 @@
"command": "vdm-vscode.managePlugins",
"when": "false"
},
{
"command": "vdm-vscode.manageAnnotations",
"when": "false"
},
{
"command": "vdm-vscode.addRunConfiguration",
"when": "false"
Expand Down Expand Up @@ -822,22 +826,7 @@
},
"default": [],
"scope": "resource",
"markdownDescription": "List of folder and/or file paths that should be included in the class path for the language server. [Find folders](command:vdm-vscode.addFoldersToClassPath), [Find files](command:vdm-vscode.addFilesToClassPath), or type in manually."
},
"vdm-vscode.server.libraries.VDM-Libraries": {
"type": "array",
"items": {
"type": "string"
},
"default": [],
"scope": "resource",
"markdownDescription": "A list containing library jar paths. Adding a folder path will load all library jars in the folder. If using a relative path it is expected to be defined at the 'folder' settings level as the path is expected to be relative to the corresponding project. [Find folders](command:vdm-vscode.addLibraryJarFolders), [Find files](command:vdm-vscode.addLibraryJars), or type in manually."
},
"vdm-vscode.server.libraries.includeDefaultLibraries": {
"type": "boolean",
"default": true,
"scope": "resource",
"description": "Include the default libraries that are packaged with the extension."
"markdownDescription": "List of folder and/or file paths that should be included in the class path for the language server. Examples of things to add to the classpath are external format readers and QuickCheck strategies; to add libraries, plugins or annotations, instead use the configuration 'Server > Extension Search Paths' for better integration. [Find folders](command:vdm-vscode.addFoldersToClassPath), [Find files](command:vdm-vscode.addFilesToClassPath), or type in manually."
},
"vdm-vscode.server.highPrecision": {
"type": "boolean",
Expand All @@ -851,13 +840,13 @@
"scope": "resource",
"description": "Activate the VDMJ -strict flag that highlights (with warnings) where a specification is taking advantage of lenient parsing rule."
},
"vdm-vscode.server.plugins.searchPaths": {
"vdm-vscode.server.extensionSearchPaths": {
"type": "array",
"items": {
"type": "string"
},
"scope": "resource",
"description": "Specifies additional paths to search for plugins. The paths can either point directly to a plugin jar or a directory containing multiple plugin jars. [Add a directory to the search path](command:vdm-vscode.addPluginJarFolders), [Add a jar to the search path](command:vdm-vscode.addLibraryJars), or type in a path manually."
"markdownDescription": "Specifies additional paths to search for VDMJ extensions, i.e. plugins, libraries and annotations. Other types of extensions are not natively supported by the extension, and will need to be added to the class path through the 'Server > Class Path Additions' configuration. The paths can either point directly to an extension jar or a directory containing multiple extension jars. [Add directories to the search path](command:vdm-vscode.addExtensionJarFolders), [Add jars to the search path](command:vdm-vscode.addExtensionJars), or type a path manually."
},
"vdm-vscode.server.plugins.enabled": {
"type": "object",
Expand All @@ -866,7 +855,16 @@
"type": "boolean"
},
"scope": "resource",
"description": "Plugins to enable/disable. If a plugin is enabled in the User settings but explicitly disabled in the Workspace or Workspace Folder settings, the Workspace settings take precedence. This settings only affects plugins found in the search paths. Use [Manage all plugins](command:vdm-vscode.addLibraryJarFolders) to adjust settings."
"markdownDescription": "Plugins to enable/disable. If a plugin is enabled in the User settings but explicitly disabled in the Workspace or Workspace Folder settings, the Workspace settings take precedence. This setting only affects plugins found in the search paths."
},
"vdm-vscode.server.annotations.enabled": {
"type": "object",
"additionalProperties": {
"description": "Enable annotation with given name",
"type": "boolean"
},
"scope": "resource",
"markdownDescription": "Annotations to enable/disable. If an annotation is enabled in the User settings but explicitly disabled in the Workspace or Workspace Folder settings, the Workspace settings take precedence. This setting only affects annotations found in the search paths."
}
}
},
Expand Down
4 changes: 4 additions & 0 deletions src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ import * as Util from "./util/Util";
import { RTLogViewHandler } from "./handlers/RTLogViewHandler";
import { FMUHandler } from "./handlers/FMUHandler";
import { ManagePluginsHandler } from "./handlers/ManagePluginsHandler";
import { ManageAnnotationsHandler } from "./handlers/ManageAnnotationsHandler";
import { VDMJExtensionsHandler } from "./handlers/VDMJExtensionsHandler";

let clientManager: ClientManager;

Expand Down Expand Up @@ -103,7 +105,9 @@ export async function activate(context: ExtensionContext) {

// Initialise handlers
context.subscriptions.push(new AddLibraryHandler(clientManager));
context.subscriptions.push(new VDMJExtensionsHandler());
context.subscriptions.push(new ManagePluginsHandler(clientManager));
context.subscriptions.push(new ManageAnnotationsHandler(clientManager));
context.subscriptions.push(new AddRunConfigurationHandler());
context.subscriptions.push(new AddExampleHandler());
context.subscriptions.push(new JavaCodeGenHandler(clientManager));
Expand Down
157 changes: 2 additions & 155 deletions src/handlers/AddLibraryHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,9 @@ import * as Util from "../util/Util";

// Encoding library
import * as iconv from "iconv-lite";
import { getFilesFromDirRecur } from "../util/DirectoriesUtil";
import { getExtensionPath } from "../util/ExtensionUtil";
import { JarFile } from "../util/JarFile";
import { Readable } from "stream";

type LibraryType = "builtin" | "user";

interface LibrarySource {
type: LibraryType;
jarPath: string;
}
import { LibrarySource, VDMJExtensionsHandler } from "./VDMJExtensionsHandler";

interface LibraryMetadata {
name: string;
Expand Down Expand Up @@ -55,12 +47,6 @@ export class AddLibraryHandler extends AutoDisposable {
Util.registerCommand(this._disposables, "vdm-vscode.addLibrary", (inputUri: Uri) =>
this.addLibrary(workspace.getWorkspaceFolder(inputUri))
);
Util.registerCommand(this._disposables, "vdm-vscode.addLibraryJarFolders", () =>
Util.addToSettingsArray(true, "VDM libraries", "vdm-vscode.server.libraries", "VDM-Libraries")
);
Util.registerCommand(this._disposables, "vdm-vscode.addLibraryJars", () =>
Util.addToSettingsArray(false, "VDM libraries", "vdm-vscode.server.libraries", "VDM-Libraries")
);
}

// Utils
Expand Down Expand Up @@ -206,140 +192,6 @@ export class AddLibraryHandler extends AutoDisposable {
}

// Library resolution
public static getIncludedLibrariesFolderPath(wsFolder: WorkspaceFolder): string {
// Get the standard or high precision path of the included library jars folder
const libPath: string = Path.resolve(
getExtensionPath(),
"resources",
"jars",
workspace.getConfiguration("vdm-vscode.server", wsFolder)?.highPrecision ? "vdmj_hp" : "vdmj" ?? "vdmj",
"libs"
);

if (!Fs.existsSync(libPath)) {
console.log("Invalid path for default libraries: " + libPath);
return "";
}

return libPath;
}

private static resolveJarPathsFromSettings(
jarPaths: string[],
resolveFailedPaths: string[],
settingsLevel: string,
rootUri?: Uri
): string[] {
// Resolve jar paths, flatten directories, filter duplicate jar names and inform the user
const visitedJarPaths: Map<string, string> = new Map<string, string>();
return jarPaths
.flatMap((originalPath: string) => {
let resolvedPath: string = originalPath;
if (rootUri && !Path.isAbsolute(originalPath)) {
// Path should be relative to the project
resolvedPath = Path.resolve(...[rootUri.fsPath, originalPath]);
}
if (!Fs.existsSync(resolvedPath)) {
resolveFailedPaths.push(originalPath);
return [];
}
return Fs.lstatSync(resolvedPath).isDirectory() ? getFilesFromDirRecur(resolvedPath, "jar") : [resolvedPath];
})
.filter((jarPath: string) => {
const jarName: string = Path.basename(jarPath);
if (!visitedJarPaths.has(jarName)) {
visitedJarPaths.set(jarName, jarPath);
return true;
}
window.showInformationMessage(
`The library jar '${jarName}' is in multiple paths for the setting level ${settingsLevel}. Using the path '${visitedJarPaths.get(
jarName
)}'.`
);
return false;
});
}

public static getUserLibrarySources(wsFolder: WorkspaceFolder): LibrarySource[] {
// Get library jars specified by the user at the folder level setting - if not defined at this level then the "next up" level where it is defined is returned.
let folderSettings: string[] = (workspace.getConfiguration("vdm-vscode.server.libraries", wsFolder.uri)?.get("VDM-Libraries") ??
[]) as string[];

// Get library jars specified by the user at the user or workspace level setting - if the workspace level setting is defined then it is returned instead of the user level setting.
let userOrWorkspaceSettings: string[] = (workspace.getConfiguration("vdm-vscode.server.libraries")?.get("VDM-Libraries") ??
[]) as string[];
const resolveFailedPaths: string[] = [];
const jarPathsFromSettings: string[] = AddLibraryHandler.resolveJarPathsFromSettings(
folderSettings,
resolveFailedPaths,
"Folder",
wsFolder.uri
);

// Determine if settings are equal, e.g. if the setting is not defined at the folder level.
if (
folderSettings.length !== userOrWorkspaceSettings.length ||
!folderSettings.every((ujp: string) => userOrWorkspaceSettings.find((fjp: string) => fjp === ujp))
) {
// If the settings are not equal then merge them and in case of duplicate jar names the folder level takes precedence over the workspace/user level.
jarPathsFromSettings.push(
...AddLibraryHandler.resolveJarPathsFromSettings(userOrWorkspaceSettings, resolveFailedPaths, "User or Workspace").filter(
(uwsPath: string) => {
const uwsPathName: string = Path.basename(uwsPath);
const existingJarPath: string = jarPathsFromSettings.find(
(fsPath: string) => Path.basename(fsPath) === Path.basename(uwsPath)
);
if (existingJarPath) {
window.showInformationMessage(
`The library jar ${uwsPathName} has been defined on multiple setting levels. The path '${existingJarPath}' from the 'folder' level is being used.`
);
return false;
}
return true;
}
)
);
}

if (resolveFailedPaths.length > 0) {
const msg: string = `Unable to resolve the following VDM library jar/folder paths: <${resolveFailedPaths.reduce(
(prev, curr) => (curr += `> <${prev}`)
)}>. These can be changed in the settings.`;
console.log(msg);
window
.showInformationMessage(msg, ...["Go to settings"])
.then(() => commands.executeCommand("workbench.action.openSettings", "vdm-vscode.server.libraries"));
}

return jarPathsFromSettings.map((jarPath) => ({
type: "user",
jarPath,
}));
}

public static getDefaultLibrarySources(wsFolder: WorkspaceFolder, userDefinedLibrarySources: LibrarySource[]): LibrarySource[] {
let includedJarsPaths: string[] = getFilesFromDirRecur(this.getIncludedLibrariesFolderPath(wsFolder), "jar");

if (userDefinedLibrarySources.length > 0) {
includedJarsPaths = includedJarsPaths.filter((ijp: string) => {
const jarName: string = Path.basename(ijp);
const existingLibrarySource = userDefinedLibrarySources.find((userLib) => Path.basename(userLib.jarPath) === jarName);
if (existingLibrarySource) {
window.showInformationMessage(
`The included library jar '${jarName}' is also defined by the user in the path '${existingLibrarySource.jarPath}'. Ignoring the version included with the extension.`
);
return false;
}
return true;
});
}

return includedJarsPaths.map((jarPath) => ({
type: "builtin",
jarPath,
}));
}

private async getLibInfoFromSource(source: LibrarySource, dialect: VdmDialect): Promise<SourcedLibraryMetadata[]> {
const jarFile = await JarFile.open(source.jarPath);
const jsonData = JSON.parse((await jarFile.readFile("META-INF/library.json")).toString());
Expand Down Expand Up @@ -369,12 +221,7 @@ export class AddLibraryHandler extends AutoDisposable {
}

private async getAllLibInfo(dialect: VdmDialect, wsFolder: WorkspaceFolder): Promise<LibrarySourceMap> {
const libraries: LibrarySource[] = AddLibraryHandler.getUserLibrarySources(wsFolder);

if (workspace.getConfiguration("vdm-vscode.server.libraries", wsFolder).includeDefaultLibraries) {
const defaultLibraries = AddLibraryHandler.getDefaultLibrarySources(wsFolder, libraries);
libraries.push(...defaultLibraries);
}
const libraries: LibrarySource[] = VDMJExtensionsHandler.getAllLibrarySources(wsFolder);

if (libraries.length === 0) {
return new Map();
Expand Down
Loading

0 comments on commit 726f5a4

Please sign in to comment.