Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,21 @@
# Change Log

## [3.2.0] 06-Oct-2025
Minimum VS Code version is now 1.104.0.
- Enhancements
- Better error handling when expanding InterSystems and Projects Explorers (#1652)
- Allow opening low-code editors from read-only file systems (#1655)
- Add command for showing all class members, including inherited (#1656)
- Allow commands to be re-run in the Lite Terminal from VS Code shell integration without user confirmation (#1654)
- Allow pasting of multi-line content into Lite Terminal `READ` prompts (#1654)
- Fixes
- Fix stringification of `AggregateError` (#1651)
- Work around VS Code bug to allow re-running of multi-line commands in Lite Terminal from VS Code shell integration (#1654)
- Correctly capture Lite Terminal command output in shell integration (#1654)
- Fix some edge cases where the Lite Terminal cursor and text could get out of sync (#1654)
- Copy Windows filesystem file correctly to `isfs` filesystem (#1658)
- Upgrade dependencies (#1650, #1653)

## [3.0.6] 09-Sep-2025
- Enhancements
- Add `objectscript.unitTest.enabled` setting (#1627)
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,8 @@ To unlock these features (optional):

1. Download and install a beta version from GitHub. This is necessary because Marketplace does not allow publication of extensions that use proposed APIs.
- Go to https://github.com/intersystems-community/vscode-objectscript/releases
- Locate the beta immediately above the release you installed from Marketplace. For instance, if you installed `3.0.6`, look for `3.0.7-beta.1`. This will be functionally identical to the Marketplace version apart from being able to use proposed APIs.
- Download the VSIX file (for example `vscode-objectscript-3.0.7-beta.1.vsix`) and install it. One way to install a VSIX is to drag it from your download folder and drop it onto the list of extensions in the Extensions view of VS Code.
- Locate the beta immediately above the release you installed from Marketplace. For instance, if you installed `3.2.0`, look for `3.2.1-beta.1`. This will be functionally identical to the Marketplace version apart from being able to use proposed APIs.
- Download the VSIX file (for example `vscode-objectscript-3.2.1-beta.1.vsix`) and install it. One way to install a VSIX is to drag it from your download folder and drop it onto the list of extensions in the Extensions view of VS Code.

2. From [Command Palette](https://code.visualstudio.com/docs/getstarted/tips-and-tricks#_command-palette) choose `Preferences: Configure Runtime Arguments`.
3. In the argv.json file that opens, add this line (required for both Stable and Insiders versions of VS Code):
Expand Down
10 changes: 5 additions & 5 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

30 changes: 20 additions & 10 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "vscode-objectscript",
"displayName": "InterSystems ObjectScript",
"description": "InterSystems ObjectScript language support for Visual Studio Code",
"version": "3.0.7-SNAPSHOT",
"version": "3.2.1-SNAPSHOT",
"icon": "images/logo.png",
"aiKey": "InstrumentationKey=9cd75d51-697c-406c-a929-2bcf46e97c64;IngestionEndpoint=https://eastus2-4.in.applicationinsights.azure.com/;LiveEndpoint=https://eastus2.livediagnostics.monitor.azure.com/;ApplicationId=a431c56f-8ccc-4b99-b5e9-fce3763215b1",
"categories": [
Expand Down Expand Up @@ -48,7 +48,7 @@
}
],
"engines": {
"vscode": "^1.93.0"
"vscode": "^1.104.0"
},
"enabledApiProposals": [
"fileSearchProvider",
Expand Down Expand Up @@ -225,7 +225,11 @@
},
{
"command": "vscode-objectscript.showClassDocumentationPreview",
"when": "editorLangId == objectscript-class && activeCustomEditorId == ''"
"when": "false"
},
{
"command": "vscode-objectscript.showAllClassMembers",
"when": "false"
},
{
"command": "vscode-objectscript.exportCurrentFile",
Expand Down Expand Up @@ -535,14 +539,14 @@
"command": "vscode-objectscript.showClassDocumentationPreview",
"group": "navigation@3",
"when": "editorLangId == objectscript-class && activeCustomEditorId == ''"
},
{
"command": "vscode-objectscript.showAllClassMembers",
"group": "navigation@0.9",
"when": "vscode-objectscript.connectActive && editorLangId == objectscript-class && activeCustomEditorId == ''"
}
],
"editor/title/context": [
{
"command": "vscode-objectscript.showClassDocumentationPreview",
"group": "1_open",
"when": "resourceLangId == objectscript-class && activeCustomEditorId == ''"
},
{
"command": "vscode-objectscript.showRESTDebugWebview",
"group": "1_open",
Expand Down Expand Up @@ -1194,6 +1198,12 @@
"command": "vscode-objectscript.openISCDocument",
"title": "Open InterSystems Document...",
"icon": "$(go-to-file)"
},
{
"category": "ObjectScript",
"command": "vscode-objectscript.showAllClassMembers",
"title": "Show All Class Members",
"icon": "$(symbol-class)"
}
],
"keybindings": [
Expand Down Expand Up @@ -1805,7 +1815,7 @@
"test": "node ./out/test/runTest.js",
"lint": "eslint src/**",
"lint-fix": "eslint --fix src/**",
"download-api": "dts dev 1.93.0",
"download-api": "dts dev 1.104.0",
"postinstall": "npm run download-api"
},
"devDependencies": {
Expand All @@ -1816,7 +1826,7 @@
"@types/mocha": "^7.0.2",
"@types/node": "20.17.6",
"@types/semver": "7.5.4",
"@types/vscode": "1.93.0",
"@types/vscode": "1.104.0",
"@types/ws": "8.18.0",
"@types/xmldom": "^0.1.34",
"@typescript-eslint/eslint-plugin": "^8.15.0",
Expand Down
7 changes: 4 additions & 3 deletions src/commands/compile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import {
handleError,
isClassDeployed,
isClassOrRtn,
isCompilable,
lastUsedLocalUri,
notIsfs,
notNull,
Expand Down Expand Up @@ -333,7 +334,7 @@ export async function importAndCompile(
throw error;
})
.then(() => {
if (compileFile) compile([file], flags);
if (compileFile && isCompilable(file.name)) compile([file], flags);
});
}

Expand Down Expand Up @@ -369,7 +370,7 @@ export async function compileOnly(askFlags = false, document?: vscode.TextDocume

const defaultFlags = config().compileFlags;
const flags = askFlags ? await compileFlags() : defaultFlags;
if (!file.fileName.startsWith("\\.vscode\\")) {
if (isCompilable(file.name)) {
compile([file], flags);
}
}
Expand Down Expand Up @@ -444,7 +445,7 @@ async function importFiles(files: vscode.Uri[], noCompile = false) {
)
.then((curFile) => {
if (curFile) {
if (typeof curFile.content == "string") toCompile.push(curFile); // Only compile text files
if (typeof curFile.content == "string" && isCompilable(curFile.name)) toCompile.push(curFile);
return importFile(curFile).then(() => outputChannel.appendLine("Imported file: " + curFile.fileName));
}
});
Expand Down
5 changes: 3 additions & 2 deletions src/commands/documaticPreviewPanel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,10 @@ export class DocumaticPreviewPanel {
*/
public static currentPanel: DocumaticPreviewPanel | undefined;

public static create(): void {
public static create(uri: vscode.Uri): void {
// Get the open document and check that it's an ObjectScript class
const openEditor = vscode.window.activeTextEditor;
const uriString = uri.toString();
const openEditor = vscode.window.visibleTextEditors.find((e) => e.document.uri.toString() == uriString);
if (openEditor === undefined) {
// Need an open document to preview
return;
Expand Down
176 changes: 176 additions & 0 deletions src/commands/showAllClassMembers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
import * as vscode from "vscode";
import { AtelierAPI } from "../api";
import { clsLangId, lsExtensionId } from "../extension";
import { currentFile, handleError, stripClassMemberNameQuotes } from "../utils";
import { DocumentContentProvider } from "../providers/DocumentContentProvider";

export async function showAllClassMembers(uri: vscode.Uri): Promise<void> {
try {
// Determine the name of the class
const uriString = uri.toString();
const textDocument = vscode.workspace.textDocuments.find((td) => td.uri.toString() == uriString);
if (textDocument?.languageId != clsLangId) {
vscode.window.showErrorMessage("The document in the active text editor is not a class definition.", "Dismiss");
return;
}
const file = currentFile(textDocument);
if (!file) {
vscode.window.showErrorMessage("The class definition in the active text editor is malformed.", "Dismiss");
return;
}
const cls = file.name.slice(0, -4);
const api = new AtelierAPI(file.uri);
if (!api.active) {
vscode.window.showErrorMessage("Showing all members of a class requires an active server connection.", "Dismiss");
return;
}
// Get an array of all members
const members: {
Name: string;
Origin: string;
MemberType: "f" | "i" | "m" | "p" | "j" | "a" | "q" | "s" | "t" | "x";
Info: string;
}[] = await api
.actionQuery(
`SELECT Name, Origin, MemberType, Info FROM (
SELECT Name, Origin, 'f' AS MemberType, Parent, Internal, NotInheritable, '('||REPLACE(Properties,',',', ')||') References '||ReferencedClass||(CASE WHEN ReferencedKey IS NOT NULL THEN '('||ReferencedKey||')' ELSE '' END) AS Info FROM %Dictionary.CompiledForeignKey UNION
SELECT Name, Origin, 'i' AS MemberType, Parent, Internal, NotInheritable, (CASE WHEN Properties LIKE '%,%' THEN 'On ('||REPLACE(Properties,',',', ')||') ' WHEN Properties IS NOT NULL THEN 'On '||Properties||' ' ELSE '' END)||(CASE WHEN Type IS NOT NULL THEN '[ Type = '||Type||' ]' ELSE '' END) AS Info FROM %Dictionary.CompiledIndex WHERE NOT (Name %STARTSWITH '$') UNION
SELECT Name, Origin, 'm' AS MemberType, Parent, Internal, NotInheritable, '('||(CASE WHEN FormalSpec IS NULL THEN '' ELSE REPLACE(REPLACE(FormalSpec,',',', '),'=',' = ') END)||')'||(CASE WHEN ReturnType IS NOT NULL THEN ' As '||ReturnType||(CASE WHEN ReturnTypeParams IS NOT NULL THEN '('||REPLACE(ReturnTypeParams,'=',' = ')||')' ELSE '' END) ELSE '' END) AS Info FROM %Dictionary.CompiledMethod WHERE Stub IS NULL UNION
SELECT Name, Origin, 'p' AS MemberType, Parent, Internal, NotInheritable, CASE WHEN Expression IS NOT NULL THEN Expression WHEN _Default IS NOT NULL THEN _Default ELSE Type END AS Info FROM %Dictionary.CompiledParameter UNION
SELECT Name, Origin, 'j' AS MemberType, Parent, Internal, NotInheritable, Type AS Info FROM %Dictionary.CompiledProjection UNION
SELECT Name, Origin, 'a' AS MemberType, Parent, Internal, NotInheritable, CASE WHEN Collection IS NOT NULL THEN Collection||' Of '||Type ELSE Type END AS Info FROM %Dictionary.CompiledProperty UNION
SELECT Name, Origin, 'q' AS MemberType, Parent, Internal, NotInheritable, '('||(CASE WHEN FormalSpec IS NULL THEN '' ELSE REPLACE(REPLACE(FormalSpec,',',', '),'=',' = ') END)||') As '||Type AS Info FROM %Dictionary.CompiledQuery UNION
SELECT Name, Origin, 's' AS MemberType, Parent, Internal, NotInheritable, Type AS Info FROM %Dictionary.CompiledStorage UNION
SELECT Name, Origin, 't' AS MemberType, Parent, Internal, NotInheritable, Event||' '||_Time||' '||Foreach AS Info FROM %Dictionary.CompiledTrigger UNION
SELECT Name, Origin, 'x' AS MemberType, Parent, Internal, 0 AS NotInheritable, MimeType||(CASE WHEN SUBSTR(MimeType,-4) = '/xml' AND XMLNamespace IS NOT NULL THEN ' ('||XMLNamespace||')' ELSE '' END) AS Info FROM %Dictionary.CompiledXData
) WHERE Parent = ? AND ((NotInheritable = 0 AND Internal = 0) OR (Origin = Parent)) ORDER BY Name`.replaceAll(
"\n",
" "
),
[cls]
)
.then((data) => data?.result?.content ?? []);
if (!members.length) {
vscode.window.showWarningMessage(
"The server returned no members for this class. If members are expected, re-compile the class then try again.",
"Dismiss"
);
return;
}
// Prompt the user to pick one
const member = await vscode.window.showQuickPick(
// Convert the query rows into QuickPickItems
members.map((m) => {
const [iconId, memberType] = (() => {
switch (m.MemberType) {
case "m":
return ["method", "Method"];
case "q":
return ["function", "Query"];
case "t":
return ["event", "Trigger"];
case "p":
return ["constant", "Parameter"];
case "i":
return ["array", "Index"];
case "f":
return ["key", "ForeignKey"];
case "x":
return ["struct", "XData"];
case "s":
return ["object", "Storage"];
case "j":
return ["interface", "Projection"];
default:
return ["property", "Property"];
}
})();
let detail = m.Info;
if ("mq".includes(m.MemberType)) {
// Need to beautify the argument list
detail = "";
let inQuotes = false;
let braceDepth = 0;
for (const c of m.Info) {
if (c == '"') {
inQuotes = !inQuotes;
detail += c;
continue;
}
if (!inQuotes) {
if (c == "{") {
braceDepth++;
detail += c;
continue;
} else if (c == "}") {
braceDepth = Math.max(0, braceDepth - 1);
detail += c;
continue;
}
}
if (!inQuotes && braceDepth == 0 && ":&*=".includes(c)) {
detail += c == ":" ? " As " : c == "&" ? "ByRef " : c == "*" ? "Output " : " = ";
} else {
detail += c;
}
}
}
return {
label: m.Name,
description: m.Origin,
detail,
iconPath: new vscode.ThemeIcon(`symbol-${iconId}`),
memberType,
};
}),
{
title: `All members of ${cls}`,
placeHolder: "Pick a member to show it in the editor",
}
);
if (!member) return;
// Show the picked member
const targetUri =
member.description == cls
? uri
: DocumentContentProvider.getUri(
`${member.description}.cls`,
undefined,
undefined,
undefined,
vscode.workspace.getWorkspaceFolder(uri)?.uri
);
const symbols = (
await vscode.commands.executeCommand<vscode.DocumentSymbol[]>("vscode.executeDocumentSymbolProvider", targetUri)
)[0]?.children;
// Find the symbol for this member
const memberType = member.memberType.toLowerCase();
const symbol = symbols?.find(
(s) =>
stripClassMemberNameQuotes(s.name) == member.label &&
(memberType == "method"
? s.detail.toLowerCase().includes(memberType)
: memberType == "property"
? ["property", "relationship"].includes(s.detail.toLowerCase())
: s.detail.toLowerCase() == memberType)
);
if (!symbol) {
vscode.window.showErrorMessage(
`Did not find ${member.memberType} '${member.label}' in class '${member.description}'.`,
"Dismiss"
);
return;
}
// If Language Server is active, selectionRange is the member name.
// Else, range is the first line of the member definition excluding description.
const position = vscode.extensions.getExtension(lsExtensionId)?.isActive
? symbol.selectionRange.start
: symbol.range.start;
await vscode.window.showTextDocument(targetUri, {
selection: new vscode.Range(position, position),
preview: false,
});
} catch (error) {
handleError(error, "Failed to show all class members.");
}
}
Loading