diff --git a/package-lock.json b/package-lock.json index aa8a7734..5d869e4f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -23,7 +23,7 @@ }, "devDependencies": { "@eslint/js": "^9.39.2", - "@intersystems-community/intersystems-servermanager": "^3.14.0", + "@intersystems-community/intersystems-servermanager": "^3.14.1", "@types/istextorbinary": "2.3.1", "@types/minimatch": "6.0.0", "@types/mocha": "^10.0.10", @@ -301,9 +301,9 @@ } }, "node_modules/@intersystems-community/intersystems-servermanager": { - "version": "3.14.0", - "resolved": "https://registry.npmjs.org/@intersystems-community/intersystems-servermanager/-/intersystems-servermanager-3.14.0.tgz", - "integrity": "sha512-VKitwu5OTCHUT5ABs13/pldNLn4rdjfVBSM0gKnEfR+pPHBTaTgtCARoqiPqGIkLghHl2LToFunCRlTdQhDV+A==", + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/@intersystems-community/intersystems-servermanager/-/intersystems-servermanager-3.14.1.tgz", + "integrity": "sha512-sGOq2agsIdN0dO6AwqciSbt8bV/y2gLBuRpiyq7hUR/7liE7s5a2EbYryN6A+h6J2lHLo/akiFz1ipmckIXvvg==", "dev": true, "license": "MIT" }, diff --git a/package.json b/package.json index 6ec17600..59abf64e 100644 --- a/package.json +++ b/package.json @@ -1718,7 +1718,7 @@ }, "devDependencies": { "@eslint/js": "^9.39.2", - "@intersystems-community/intersystems-servermanager": "^3.14.0", + "@intersystems-community/intersystems-servermanager": "^3.14.1", "@types/istextorbinary": "2.3.1", "@types/minimatch": "6.0.0", "@types/mocha": "^10.0.10", diff --git a/src/api/index.ts b/src/api/index.ts index 9f81253d..5fc2b801 100644 --- a/src/api/index.ts +++ b/src/api/index.ts @@ -2,7 +2,7 @@ import axios from "axios"; import * as httpsModule from "https"; import * as vscode from "vscode"; import * as semver from "semver"; -import { +import BasicAuthorization, { getResolvedConnectionSpec, config, extensionContext, @@ -57,7 +57,7 @@ export interface ConnectionSettings { port: number; superserverPort?: number; pathPrefix?: string; - ns: string; + ns: string | undefined; auth: Authorization; docker?: boolean; dockerService?: string; @@ -171,7 +171,7 @@ export class AtelierAPI { webServer: { scheme, host, port, pathPrefix = "" }, auth, } = connSpec; - this._config.auth = auth; + this._config.auth = auth ?? new BasicAuthorization(); this._config.https = scheme == "https"; this._config.host = host; this._config.port = port; @@ -200,7 +200,7 @@ export class AtelierAPI { public terminalUrl(): string { const { host, https, port, apiVersion, pathPrefix } = this.config; - return apiVersion >= 7 + return apiVersion! >= 7 ? `${https ? "wss" : "ws"}://${host}:${port}${pathPrefix}/api/atelier/v${apiVersion}/%25SYS/terminal` : ""; } @@ -232,7 +232,11 @@ export class AtelierAPI { private setConnection(workspaceFolderName: string, namespace?: string): void { this.configName = workspaceFolderName; - const conn = config("conn", workspaceFolderName); + const rawConn = config("conn", workspaceFolderName); + const conn = { + ...rawConn, + auth: new BasicAuthorization(rawConn.username, rawConn.password), + }; let serverName = workspaceFolderName.toLowerCase(); if (config("intersystems.servers", workspaceFolderName).has(serverName)) { this.externalServer = true; @@ -254,7 +258,7 @@ export class AtelierAPI { webServer: { scheme, host, port, pathPrefix = "" }, auth, superServer, - } = getResolvedConnectionSpec(serverName, config("intersystems.servers", workspaceFolderName).get(serverName)); + } = getResolvedConnectionSpec(serverName, config("intersystems.servers", workspaceFolderName).get(serverName))!; this._config = { serverName, active: this.externalServer ? !inactiveServerIds.has(serverName) : conn.active, @@ -333,7 +337,7 @@ export class AtelierAPI { if (!active || !port || !host) { return Promise.reject(); } - if (minVersion > apiVersion) { + if (minVersion > apiVersion!) { return Promise.reject(`${path} not supported by API version ${apiVersion}`); } const originalPath = path; @@ -348,7 +352,7 @@ export class AtelierAPI { if (!params) { return ""; } - const result = []; + const result: string[] = []; Object.keys(params).forEach((key) => { const value = params[key]; if (typeof value === "boolean") { @@ -379,7 +383,7 @@ export class AtelierAPI { const cookies = this.cookies; const mapKey = this.mapKey(); - let auth: Promise; + let auth: Promise | undefined; let authRequest = authRequestMap.get(mapKey); if (cookies.length || (method === "HEAD" && !originalPath)) { // Only send basic authorization if username and password specified (including blank, for unauthenticated access) @@ -417,7 +421,7 @@ export class AtelierAPI { } }; try { - cookie = await auth; + cookie = await auth!; reqTs = new Date(); const response = await axios.request({ method, @@ -598,7 +602,7 @@ export class AtelierAPI { .slice(data.version.indexOf(") ") + 2) .split(" ") .shift() - ).version; + )!.version; if (this.ns && this.ns.length && !data.namespaces.includes(this.ns) && checkNs) { throw { code: "WrongNamespace", @@ -649,7 +653,7 @@ export class AtelierAPI { const params: Record = {}; name = this.transformNameIfCsp(name); if ( - this.config.apiVersion >= 4 && + this.config.apiVersion! >= 4 && vscode.workspace .getConfiguration( "objectscript", diff --git a/src/commands/addServerNamespaceToWorkspace.ts b/src/commands/addServerNamespaceToWorkspace.ts index 02498848..078dfad6 100644 --- a/src/commands/addServerNamespaceToWorkspace.ts +++ b/src/commands/addServerNamespaceToWorkspace.ts @@ -17,7 +17,9 @@ import { isfsConfig, IsfsUriParam } from "../utils/FileProviderUtil"; * @param message The prefix of the message to show when the server manager API can't be found. * @returns An object containing `serverName` and `namespace`, or `undefined`. */ -async function pickServerAndNamespace(message?: string): Promise<{ serverName: string; namespace: string }> { +async function pickServerAndNamespace( + message?: string +): Promise<{ serverName: string; namespace: string } | undefined> { if (!serverManagerApi) { vscode.window.showErrorMessage( `${ @@ -29,7 +31,7 @@ async function pickServerAndNamespace(message?: string): Promise<{ serverName: s } // Get user's choice of server const options: vscode.QuickPickOptions = { ignoreFocusOut: true }; - const serverName: string = await serverManagerApi.pickServer(undefined, options); + const serverName: string | undefined = await serverManagerApi.pickServer(undefined, options); if (!serverName) { return; } @@ -40,13 +42,13 @@ async function pickServerAndNamespace(message?: string): Promise<{ serverName: s return { serverName, namespace }; } -async function pickNamespaceOnServer(serverName: string): Promise { +async function pickNamespaceOnServer(serverName: string): Promise { // Get its namespace list const uri = vscode.Uri.parse(`isfs://${serverName}:%sys/`); await resolveConnectionSpec(serverName); // Prepare a displayable form of its connection spec as a hint to the user. // This will never return the default value (second parameter) because we only just resolved the connection spec. - const connSpec = getResolvedConnectionSpec(serverName, undefined); + const connSpec = getResolvedConnectionSpec(serverName, undefined)!; const connDisplayString = `${connSpec.webServer.scheme}://${connSpec.webServer.host}:${connSpec.webServer.port}/${connSpec.webServer.pathPrefix}`; // Connect and fetch namespaces const api = new AtelierAPI(uri); @@ -81,9 +83,9 @@ async function pickNamespaceOnServer(serverName: string): Promise { export async function addServerNamespaceToWorkspace(resource?: vscode.Uri): Promise { const TITLE = "Add server namespace to workspace"; let serverName = ""; - let namespace = ""; - if (filesystemSchemas.includes(resource?.scheme)) { - serverName = resource.authority.split(":")[0]; + let namespace: string | undefined = ""; + if (filesystemSchemas.includes(resource?.scheme as string)) { + serverName = resource!.authority.split(":")[0]; if (serverName) { const ANOTHER = "Choose another server"; const choice = await vscode.window.showQuickPick([`Add a '${serverName}' namespace`, ANOTHER], { @@ -111,7 +113,7 @@ export async function addServerNamespaceToWorkspace(resource?: vscode.Uri): Prom } } const wsFolders = vscode.workspace.workspaceFolders ?? []; - let scheme: string; + let scheme: string | undefined; if (wsFolders.length && wsFolders.some((wf) => notIsfs(wf.uri))) { // Don't allow the creation of an editable ISFS folder // if the workspace contains non-ISFS folders already @@ -221,7 +223,7 @@ async function modifyWsFolderUri(uri: vscode.Uri): Promise((resolve) => { - let result: string; + let result: string | undefined; const allItem: vscode.QuickPickItem = { label: "All" }; const quickPick = vscode.window.createQuickPick(); quickPick.title = "Pick a specific web application to show, or show all"; @@ -352,7 +354,7 @@ async function modifyWsFolderUri(uri: vscode.Uri): Promise { - let wsFolder: vscode.WorkspaceFolder; + let wsFolder: vscode.WorkspaceFolder | null | undefined; if (!wsFolderUri) { // Select a workspace folder to modify wsFolder = await getWsFolder("Pick the workspace folder to modify", false, true); diff --git a/src/commands/compile.ts b/src/commands/compile.ts index a66bacf3..a5502833 100644 --- a/src/commands/compile.ts +++ b/src/commands/compile.ts @@ -89,7 +89,7 @@ export async function importFile( if (!file) return; const api = new AtelierAPI(file.uri); if (!api.active) return Promise.reject(); - if (file.name.split(".").pop().toLowerCase() === "cls" && !skipDeplCheck) { + if (file.name.split(".").pop()!.toLowerCase() === "cls" && !skipDeplCheck) { if (await isClassDeployed(file.name, api)) { vscode.window.showErrorMessage(`Cannot import ${file.name} because it is deployed on the server.`, "Dismiss"); return Promise.reject(); @@ -207,10 +207,10 @@ function updateOthers(others: string[], baseUri: vscode.Uri) { } others.forEach((item) => { const uri = DocumentContentProvider.getUri(item, undefined, undefined, undefined, workspaceFolder?.uri); - if (filesystemSchemas.includes(uri.scheme)) { - fileSystemProvider.fireFileChanged(uri); - } else if (uri.scheme == OBJECTSCRIPT_FILE_SCHEMA) { - documentContentProvider.update(uri); + if (filesystemSchemas.includes(uri!.scheme)) { + fileSystemProvider.fireFileChanged(uri!); + } else if (uri!.scheme == OBJECTSCRIPT_FILE_SCHEMA) { + documentContentProvider.update(uri!); } }); } @@ -234,7 +234,7 @@ export async function loadChanges( Promise.allSettled( data.result.content.map(async (doc) => { if (doc.status.length) return; - const file = files.find((f) => f.name == doc.name); + const file = files.find((f) => f.name == doc.name)!; const mtime = Number(new Date(doc.ts + "Z")); workspaceState.update(`${file.uniqueId}:mtime`, mtime > 0 ? mtime : undefined); if (notIsfs(file.uri)) { @@ -312,8 +312,8 @@ function updateStorage(content: string[], storage: string[]): string[] { function storageToMap(storage: string[]): Map { const map: Map = new Map(); - let k: string; - let v = []; + let k: string | undefined; + let v: string[] = []; for (const line of storage) { if (line.startsWith("Storage ")) { k = line.slice("Storage ".length, line.length); @@ -412,7 +412,7 @@ export async function importAndCompile(document?: vscode.TextDocument, askFlags } } -export async function compileOnly(document?: vscode.TextDocument, askFlags = false): Promise { +export async function compileOnly(document?: vscode.TextDocument | null, askFlags = false): Promise { document = document || (vscode.window.activeTextEditor && vscode.window.activeTextEditor.document @@ -468,7 +468,7 @@ export async function namespaceCompile(): Promise { .then(() => { // Always fetch server changes, even when compile failed or got cancelled const file = currentFile(); - return loadChanges([file]); + return loadChanges([file!]); }) ); } @@ -526,7 +526,7 @@ export async function compileExplorerItems(nodes: NodeBase[]): Promise { const conf = vscode.workspace.getConfiguration("objectscript", wsFolder); const api = new AtelierAPI(wsFolder.uri); if (namespace) api.setNamespace(namespace); - const docs = []; + const docs: string[] = []; for (const node of nodes) { if (node instanceof PackageNode) { switch (node.category) { @@ -659,7 +659,7 @@ export async function importArbitraryFiles(): Promise { }); if (!uris?.length) return; // Filter out non-importable files - uris = uris.filter((uri) => supportedExts.includes(uri.path.split(".").pop().toLowerCase())); + uris = uris.filter((uri) => supportedExts.includes(uri.path.split(".").pop()!.toLowerCase())); if (uris.length == 0) { vscode.window.showErrorMessage("No selected files are importable.", "Dismiss"); return; @@ -689,6 +689,7 @@ export async function importArbitraryFiles(): Promise { } }) .filter(notNull) + .map((f) => f!) ); if (filesToList.length == 0) { vscode.window.showErrorMessage("Failed to read the text of every selected file.", "Dismiss"); @@ -754,9 +755,9 @@ export async function importArbitraryFiles(): Promise { } }); if (readOnly.length) { - docsToImport = docsToImport.filter((qpi) => { + docsToImport = docsToImport!.filter((qpi) => { const nameSplit = qpi.label.split("."); - return !readOnly.includes(`${nameSplit.slice(0, -1).join(".")}.${nameSplit.pop().toUpperCase()}`); + return !readOnly.includes(`${nameSplit.slice(0, -1).join(".")}.${nameSplit.pop()!.toUpperCase()}`); }); } }); diff --git a/src/commands/connectFolderToServerNamespace.ts b/src/commands/connectFolderToServerNamespace.ts index b4076888..2e56ddfb 100644 --- a/src/commands/connectFolderToServerNamespace.ts +++ b/src/commands/connectFolderToServerNamespace.ts @@ -32,7 +32,7 @@ export async function connectFolderToServerNamespace(): Promise { .filter((folder) => notIsfs(folder.uri)) .map((folder) => { const config = vscode.workspace.getConfiguration("objectscript", folder); - const conn: ConnSettings = config.get("conn"); + const conn: ConnSettings = config.get("conn")!; return { label: folder.name, description: folder.uri.fsPath, @@ -47,14 +47,14 @@ export async function connectFolderToServerNamespace(): Promise { return; } const pick = - items.length == 1 && !items[0].detail.startsWith("Currently") + items.length == 1 && !items[0].detail!.startsWith("Currently") ? items[0] : await vscode.window.showQuickPick(items, { title: "Pick a folder" }); if (!pick) return; const folder = vscode.workspace.workspaceFolders.find((el) => el.name === pick.label); // Get user's choice of server const options: vscode.QuickPickOptions = {}; - const serverName: string = await serverManagerApi.pickServer(folder, options); + const serverName: string | undefined = await serverManagerApi.pickServer(folder, options); if (!serverName) { return; } @@ -62,27 +62,27 @@ export async function connectFolderToServerNamespace(): Promise { // Prepare a displayable form of its connection spec as a hint to the user // This will never return the default value (second parameter) because we only just resolved the connection spec. const connSpec = getResolvedConnectionSpec(serverName, undefined); - const connDisplayString = `${connSpec.webServer.scheme}://${connSpec.webServer.host}:${connSpec.webServer.port}/${connSpec.webServer.pathPrefix}`; + const connDisplayString = `${connSpec!.webServer.scheme}://${connSpec!.webServer.host}:${connSpec!.webServer.port}/${connSpec!.webServer.pathPrefix}`; // Connect and fetch namespaces const api = new AtelierAPI(vscode.Uri.parse(`isfs://${serverName}/?ns=%SYS`)); const serverConf = vscode.workspace .getConfiguration("intersystems", folder) .inspect<{ [key: string]: any }>("servers"); if ( - serverConf.workspaceFolderValue && - typeof serverConf.workspaceFolderValue[serverName] == "object" && - !(serverConf.workspaceValue && typeof serverConf.workspaceValue[serverName] == "object") + serverConf!.workspaceFolderValue && + typeof serverConf!.workspaceFolderValue[serverName] == "object" && + !(serverConf!.workspaceValue && typeof serverConf!.workspaceValue[serverName] == "object") ) { // Need to manually set connection info if the server is defined at the workspace folder level - api.setConnSpec(serverName, connSpec); + api.setConnSpec(serverName, connSpec!); } - const allNamespaces: string[] = await api + const allNamespaces: string[] | undefined = await api .serverInfo(false) .then((data) => data.result.content.namespaces) .catch(async (error) => { if (error?.statusCode == 401 && !api.config.auth.resolved()) { // Attempt to resolve username and password and try again - const newSpec = await resolveUsernameAndPassword(api.config.serverName, connSpec); + const newSpec = await resolveUsernameAndPassword(api.config.serverName, connSpec!); if (newSpec) { // We were able to resolve credentials, so try again api.setConnSpec(api.config.serverName, newSpec); @@ -131,7 +131,7 @@ export async function connectFolderToServerNamespace(): Promise { // the server may be configured at the workspace folder level. const answer = await vscode.window.showQuickPick( [ - { label: `Workspace Folder ${folder.name}`, detail: displayableUri(folder.uri) }, + { label: `Workspace Folder ${folder!.name}`, detail: displayableUri(folder!.uri) }, { label: "Workspace File", detail: displayableUri(vscode.workspace.workspaceFile) }, ], { title: "Store the server connection at the workspace or folder level?" } @@ -139,7 +139,7 @@ export async function connectFolderToServerNamespace(): Promise { if (!answer) return; if (answer.label == "Workspace File") { // Enable the connection at the workspace level - const conn: any = config.inspect("conn").workspaceValue; + const conn: any = config.inspect("conn")!.workspaceValue; await config.update( "conn", { ...conn, server: serverName, ns: namespace, active: true }, @@ -149,7 +149,7 @@ export async function connectFolderToServerNamespace(): Promise { } } // Enable the connection at the workspace folder level - const conn: any = config.inspect("conn").workspaceFolderValue; + const conn: any = config.inspect("conn")!.workspaceFolderValue; await config.update( "conn", { ...conn, server: serverName, ns: namespace, active: true }, diff --git a/src/commands/delete.ts b/src/commands/delete.ts index b0ea3051..676886fb 100644 --- a/src/commands/delete.ts +++ b/src/commands/delete.ts @@ -19,7 +19,7 @@ function deleteList(items: string[], wsFolder: vscode.WorkspaceFolder, namespace if (file.result.ext && wsFolder.uri.scheme == FILESYSTEM_SCHEMA) { // Only process source control output if we're in an isfs folder const uri = DocumentContentProvider.getUri(file.result.name); - fireOtherStudioAction(OtherStudioAction.DeletedDocument, uri, file.result.ext); + fireOtherStudioAction(OtherStudioAction.DeletedDocument, uri!, file.result.ext); } }); outputChannel.appendLine(`Deleted items: ${files.filter((el) => el.result).length}`); diff --git a/src/commands/export.ts b/src/commands/export.ts index 29b7cee5..40f5751c 100644 --- a/src/commands/export.ts +++ b/src/commands/export.ts @@ -22,8 +22,8 @@ import { pickDocuments } from "../utils/documentPicker"; import { NodeBase } from "../explorer/nodes"; import { updateIndex } from "../utils/documentIndex"; -export function getCategory(fileName: string, addCategory: any | boolean): string { - const fileExt = fileName.split(".").pop().toLowerCase(); +export function getCategory(fileName: string, addCategory: any | boolean): string | null { + const fileExt = fileName.split(".").pop()!.toLowerCase(); if (typeof addCategory === "object") { for (const pattern of Object.keys(addCategory)) { if (new RegExp(`^${pattern}$`).test(fileName)) { @@ -49,11 +49,13 @@ export function getCategory(fileName: string, addCategory: any | boolean): strin export function getFileName( folder: string, name: string, - split: boolean, - addCategory: boolean, - map: { - [key: string]: string; - }, + split: boolean | undefined, + addCategory: boolean | undefined, + map: + | { + [key: string]: string; + } + | undefined, sep = path.sep ): string { if (name.includes("/")) { @@ -75,7 +77,7 @@ export function getFileName( } } fileNameArray = name.split("."); - fileExt = fileNameArray.pop().toLowerCase(); + fileExt = fileNameArray.pop()!.toLowerCase(); } else { // This is some other type of file (LUT,HL7,...) const lastDot = name.lastIndexOf("."); @@ -200,7 +202,7 @@ export async function exportAll(): Promise { } else if (filterIsValid(filter)) { filters.push(`Name LIKE '%${filter}%'`); } - let files: vscode.QuickPickItem[] = await api + let files: vscode.QuickPickItem[] | undefined = await api .actionQuery("SELECT Name FROM %Library.RoutineMgr_StudioOpenDialog('*',1,1,?,1,0,?,?,0,?)", [ api.ns == "%SYS" ? "1" : "0", generated ? "1" : "0", @@ -268,7 +270,7 @@ export async function exportCurrentFile(): Promise { // Only export files opened from the explorer return; } - return exportList([currentFile(openDoc).name], vscode.workspace.getWorkspaceFolder(openDoc.uri)); + return exportList([currentFile(openDoc)!.name], vscode.workspace.getWorkspaceFolder(openDoc.uri)!); } export async function exportDocumentsToXMLFile(): Promise { @@ -293,7 +295,7 @@ export async function exportDocumentsToXMLFile(): Promise { } const api = new AtelierAPI(wsFolder.uri); // Make sure the server has the xml endpoints - if (api.config.apiVersion < 7) { + if (api.config.apiVersion! < 7) { vscode.window.showErrorMessage( "'Export Documents to XML File...' command requires InterSystems IRIS version 2023.2 or above.", "Dismiss" diff --git a/src/commands/jumpToTagAndOffset.ts b/src/commands/jumpToTagAndOffset.ts index a07782ea..7b60c736 100644 --- a/src/commands/jumpToTagAndOffset.ts +++ b/src/commands/jumpToTagAndOffset.ts @@ -72,7 +72,7 @@ export async function openErrorLocation(): Promise { if (!location) { return; } - const [, label, offset, routine] = location.trim().match(regex); + const [, label, offset, routine] = location.trim().match(regex)!; // Get the uri for the routine const uri = DocumentContentProvider.getUri(`${routine}.int`); if (!uri) { diff --git a/src/commands/newFile.ts b/src/commands/newFile.ts index 7e153daf..0568afd6 100644 --- a/src/commands/newFile.ts +++ b/src/commands/newFile.ts @@ -364,7 +364,7 @@ export async function newFile(type: NewFileType): Promise { } // Check if workspace folder has an active connection - let api = new AtelierAPI(wsFolder.uri); + let api: AtelierAPI | undefined = new AtelierAPI(wsFolder.uri); if (!api.active) { if (wsFolder.uri.scheme == FILESYSTEM_SCHEMA) { vscode.window.showErrorMessage( @@ -538,8 +538,8 @@ export async function newFile(type: NewFileType): Promise { ); // Create the type-specific elements prompts, then use them to generate the content - let clsContent: string; - let cls: string; + let clsContent: string | undefined; + let cls: string | undefined; if (type == NewFileType.BusinessOperation) { // Create the prompts for the invocation style and adapter class inputSteps.push( @@ -809,7 +809,7 @@ ClassMethod Transform(source As ${sourceCls}, ByRef target As ${targetCls}) As % const [, desc, assistCls] = results; // Determine the context class, if possible - let contextClass: string; + let contextClass: string | undefined; switch (assistCls) { case "Ens.Alerting.Rule.CreateAlertAssist": contextClass = "Ens.Alerting.Context.CreateAlert"; @@ -829,9 +829,9 @@ ClassMethod Transform(source As ${sourceCls}, ByRef target As ${targetCls}) As % } // Prompt for the production, if required - let production: string; + let production: string | undefined; if (Object.keys(ruleAssists).length && assistCls in ruleAssists && ruleAssists[assistCls].hasProduction) { - const productions: string[] = await api + const productions: string[] = await api! .getEnsClassList(11) .then((data) => data.result.content) .catch(() => []); @@ -972,7 +972,7 @@ ClassMethod %OnDashboardAction(pAction As %String, pContext As %ZEN.proxyObject) cls = results[0]; const [, desc, msgType] = results; - let respClass: string; + let respClass: string | undefined; if (msgType == "Request") { // Prompt the user for the response type const respClasses: vscode.QuickPickItem[] = api @@ -1079,10 +1079,10 @@ Class ${cls}${superclass ? ` Extends ${superclass}` : ""} let clsUri: vscode.Uri; if (wsFolder.uri.scheme == FILESYSTEM_SCHEMA) { // Generate the URI - clsUri = DocumentContentProvider.getUri(`${cls}.cls`, undefined, undefined, undefined, wsFolder.uri); + clsUri = DocumentContentProvider.getUri(`${cls}.cls`, undefined, undefined, undefined, wsFolder.uri)!; } else { // Try to infer the URI from the document index - clsUri = inferDocUri(`${cls}.cls`, wsFolder) ?? (await promptForDocUri(cls, wsFolder)); + clsUri = inferDocUri(`${cls}.cls`, wsFolder) ?? (await promptForDocUri(cls!, wsFolder))!; } if (clsUri && clsContent) { diff --git a/src/commands/project.ts b/src/commands/project.ts index c3254097..29974e16 100644 --- a/src/commands/project.ts +++ b/src/commands/project.ts @@ -158,7 +158,7 @@ export async function createProject(node: NodeBase | undefined, api?: AtelierAPI export async function deleteProject(node: ProjectNode | undefined): Promise { let api: AtelierAPI; - let project: string; + let project: string | undefined; if (node instanceof ProjectNode) { api = new AtelierAPI(node.wsFolder.uri); if (node.namespace) api.setNamespace(node.namespace); @@ -373,7 +373,7 @@ function sodItemToPickAdditionsItem( delim = "/"; } result.fullName = parent + delim + item.Name; - result.label = " ".repeat(parentPad + 2) + result.label; + result.label = " ".repeat(parentPad! + 2) + result.label; result.description = result.fullName; } if (item.Type && (item.Type == 9 || item.Type == 10)) { @@ -514,7 +514,7 @@ async function pickAdditions( handleError(error, "Failed to get namespace contents."); }); }; - const expandItem = (itemIdx: number): Promise => { + const expandItem = (itemIdx: number): Promise | undefined => { const selected = quickPick.selectedItems; const item = quickPick.items[itemIdx]; quickPick.items[itemIdx].buttons = [ @@ -572,8 +572,8 @@ async function pickAdditions( quickPick.busy = true; // Change value of correct parameter in array if (button.tooltip == "System") { - sys = button.toggle.checked ? "1" : "0"; - if (["RTN", "INC", "OTH"].includes(category)) { + sys = button.toggle!.checked ? "1" : "0"; + if (["RTN", "INC", "OTH"].includes(category!)) { parameters[0] = sys; } else if (category != undefined) { parameters[1] = sys; @@ -582,8 +582,8 @@ async function pickAdditions( parameters[4] = sys; } } else { - gen = button.toggle.checked ? "1" : "0"; - if (["RTN", "INC", "OTH"].includes(category)) { + gen = button.toggle!.checked ? "1" : "0"; + if (["RTN", "INC", "OTH"].includes(category!)) { parameters[1] = gen; } else if (category != undefined) { parameters[2] = gen; @@ -598,7 +598,7 @@ async function pickAdditions( quickPick.onDidTriggerItemButton((event) => { quickPick.busy = true; const itemIdx = quickPick.items.findIndex((i) => i.fullName === event.item.fullName); - if (event.button.tooltip.charAt(0) == "E") { + if (event.button.tooltip!.charAt(0) == "E") { // Expand this item expandItem(itemIdx); } else { @@ -627,8 +627,8 @@ async function pickAdditions( ); if ( itemIdx != -1 && - quickPick.items[itemIdx].buttons.length && - quickPick.items[itemIdx].buttons[0].tooltip.charAt(0) == "E" + quickPick.items[itemIdx].buttons!.length && + quickPick.items[itemIdx].buttons![0].tooltip!.charAt(0) == "E" ) { // Expand this item quickPick.busy = true; @@ -673,7 +673,7 @@ export async function modifyProject( for (const pick of picks) { // Determine the type of this item let type: string; - const ext: string = pick.split(".").pop().toLowerCase(); + const ext: string = pick.split(".").pop()!.toLowerCase(); if (["mac", "int", "inc"].includes(ext)) { type = "MAC"; } else if (ext == "cls") { @@ -681,7 +681,7 @@ export async function modifyProject( } else if (ext == "pkg") { type = "PKG"; } else if (pick.includes("/")) { - if (pick.split("/").pop().includes(".")) { + if (pick.split("/").pop()!.includes(".")) { type = "CSP"; } else { type = "DIR"; @@ -780,7 +780,7 @@ export async function modifyProject( // This is a file, so remove it const fileName = isfsDocumentName(nodeOrUri); let prjFileName = fileName.startsWith("/") ? fileName.slice(1) : fileName; - const ext = prjFileName.split(".").pop().toLowerCase(); + const ext = prjFileName.split(".").pop()!.toLowerCase(); prjFileName = ext == "cls" ? prjFileName.slice(0, -4) : prjFileName; const prjType = fileName.includes("/") ? "CSP" @@ -952,7 +952,7 @@ function isfsFolderForProject(project: string, api: AtelierAPI): number { */ export async function addIsfsFileToProject(project: string, fileName: string, api: AtelierAPI): Promise { let prjFileName = fileName.startsWith("/") ? fileName.slice(1) : fileName; - const ext = prjFileName.split(".").pop().toLowerCase(); + const ext = prjFileName.split(".").pop()!.toLowerCase(); prjFileName = ext == "cls" ? prjFileName.slice(0, -4) : prjFileName; const prjType = fileName.includes("/") ? "CSP" @@ -1031,10 +1031,10 @@ export function addWorkspaceFolderForProject(node: ProjectNode): void { async function handleCommandArg( nodeOrUri: NodeBase | vscode.Uri | undefined -): Promise<{ node: NodeBase; api: AtelierAPI; project: string } | undefined> { - let node: NodeBase; +): Promise<{ node: NodeBase | undefined; api: AtelierAPI; project: string } | undefined> { + let node: NodeBase | undefined; let api: AtelierAPI; - let project: string; + let project: string | undefined; if (nodeOrUri instanceof NodeBase) { // Called from Projects Explorer node = nodeOrUri; @@ -1054,10 +1054,10 @@ async function handleCommandArg( api = new AtelierAPI(connUri); } if (!project) { - project = await pickProject(api); + project = await pickProject(api!); if (!project) return; } - return { node, api, project }; + return { node, api: api!, project }; } export async function modifyProjectMetadata(nodeOrUri: NodeBase | vscode.Uri | undefined): Promise { diff --git a/src/commands/restDebugPanel.ts b/src/commands/restDebugPanel.ts index 29030211..f7234e21 100644 --- a/src/commands/restDebugPanel.ts +++ b/src/commands/restDebugPanel.ts @@ -72,7 +72,7 @@ export class RESTDebugPanel { vscode.window.showErrorMessage("REST service debugging webview requires an active server connection.", "Dismiss"); return; } - if (api.config.apiVersion < 2) { + if (api.config.apiVersion! < 2) { vscode.window.showErrorMessage( "REST service debugging webview requires Atelier API version 2 or above.", "Dismiss" @@ -83,7 +83,7 @@ export class RESTDebugPanel { if (this.currentPanel !== undefined) { // Can only have one panel open at once if (!this.currentPanel._panel.visible) { - if (openEditor.document.uri.toString() == this._file.toString()) { + if (openEditor.document.uri.toString() == this._file!.toString()) { // The open panel is for this document, so show it this.currentPanel._panel.reveal(vscode.ViewColumn.Active); return; @@ -522,7 +522,7 @@ export class RESTDebugPanel { // Make sure the original document is the active text editor this._panel.dispose(); - await vscode.window.showTextDocument(RESTDebugPanel._file, { + await vscode.window.showTextDocument(RESTDebugPanel._file!, { preview: false, viewColumn: vscode.ViewColumn.Active, }); @@ -592,7 +592,7 @@ export class RESTDebugPanel { await new Promise((resolve) => setTimeout(resolve, 500)); // Start the debugging session - await vscode.debug.startDebugging(vscode.workspace.getWorkspaceFolder(RESTDebugPanel._file), { + await vscode.debug.startDebugging(vscode.workspace.getWorkspaceFolder(RESTDebugPanel._file!), { type: "objectscript", request: "attach", name: "REST", diff --git a/src/commands/serverActions.ts b/src/commands/serverActions.ts index 48a698e0..23176c50 100644 --- a/src/commands/serverActions.ts +++ b/src/commands/serverActions.ts @@ -57,7 +57,7 @@ export async function serverActions(): Promise { }); } } - const connectionActionsHandler = async (action: ServerAction): Promise => { + const connectionActionsHandler = async (action: ServerAction): Promise => { if (!action) { return; } @@ -95,7 +95,7 @@ export async function serverActions(): Promise { } // Filter out the current namespace - allNamespaces = allNamespaces.filter((ns) => ns.toLowerCase() != api.config.ns.toLowerCase()); + allNamespaces = allNamespaces.filter((ns) => ns.toLowerCase() != api.config.ns!.toLowerCase()); if (!allNamespaces.length) { vscode.window.showErrorMessage(`You don't have access to any other namespaces.`, "Dismiss"); return; @@ -204,7 +204,7 @@ export async function serverActions(): Promise { } if ( (!vscode.window.activeTextEditor && wsUri && filesystemSchemas.includes(wsUri.scheme)) || - filesystemSchemas.includes(vscode.window.activeTextEditor?.document.uri.scheme) + filesystemSchemas.includes(vscode.window.activeTextEditor?.document.uri.scheme as string) ) { actions.push({ id: "serverCommandMenu", diff --git a/src/commands/showAllClassMembers.ts b/src/commands/showAllClassMembers.ts index dba84ec4..13b4cfe3 100644 --- a/src/commands/showAllClassMembers.ts +++ b/src/commands/showAllClassMembers.ts @@ -166,7 +166,7 @@ SELECT Name, Origin, 'x' AS MemberType, Parent, Internal, 0 AS NotInheritable, M const position = vscode.extensions.getExtension(lsExtensionId)?.isActive ? symbol.selectionRange.start : symbol.range.start; - await vscode.window.showTextDocument(targetUri, { + await vscode.window.showTextDocument(targetUri!, { selection: new vscode.Range(position, position), preview: false, }); diff --git a/src/commands/showPlanPanel.ts b/src/commands/showPlanPanel.ts index ce87b1d9..ddfb1d7b 100644 --- a/src/commands/showPlanPanel.ts +++ b/src/commands/showPlanPanel.ts @@ -8,7 +8,7 @@ import { iscIcon } from "../extension"; const viewType = "isc-show-plan"; const viewTitle = "Show Plan"; -let panel: vscode.WebviewPanel; +let panel: vscode.WebviewPanel | undefined; /** Escape any HTML characters so they are rendered literally */ function htmlEncode(str: string): string { @@ -82,7 +82,7 @@ export async function showPlanWebview(args: { vscode.window.showErrorMessage("Show Plan requires an active server connection.", "Dismiss"); return; } - if (lt(api.config.serverVersion, "2024.1.0")) { + if (lt(api.config.serverVersion!, "2024.1.0")) { vscode.window.showErrorMessage("Show Plan requires InterSystems IRIS version 2024.1 or above.", "Dismiss"); return; } @@ -111,7 +111,7 @@ export async function showPlanWebview(args: { const planXML: string = await api .actionQuery("SELECT %SYSTEM.QUERY_PLAN(?,,,,,?) XML", [ args.sqlQuery.trimEnd(), - `{${!lt(api.config.serverVersion, "2026.1.0") ? '"format":"LINEAR-XML",' : ""}"selectmode":"${args.selectMode}"${args.imports.length ? `,"packages":"$LFS(\\"${[...new Set(args.imports)].join(",")}\\")"` : ""}${args.includes.length ? `,"includeFiles":"$LFS(\\"${[...new Set(args.includes)].join(",")}\\")"` : ""}}`, + `{${!lt(api.config.serverVersion!, "2026.1.0") ? '"format":"LINEAR-XML",' : ""}"selectmode":"${args.selectMode}"${args.imports.length ? `,"packages":"$LFS(\\"${[...new Set(args.imports)].join(",")}\\")"` : ""}${args.includes.length ? `,"includeFiles":"$LFS(\\"${[...new Set(args.includes)].join(",")}\\")"` : ""}}`, ]) .then((data) => data?.result?.content[0]?.XML) .catch((error) => { @@ -129,7 +129,7 @@ export async function showPlanWebview(args: { // Loop through the child elements of the plan let capturePlan = false; let planText = ""; - let planChild = (planElem.firstChild); + let planChild = (planElem!.firstChild); while (planChild) { switch (planChild.nodeName) { case "sql": @@ -140,16 +140,16 @@ export async function showPlanWebview(args: { planHTML += `\n
\n`; break; case "warning": - planHTML += `

Warning

\n

\n${formatTextBlock(planChild.textContent)}

\n
\n`; + planHTML += `

Warning

\n

\n${formatTextBlock(planChild.textContent!)}

\n
\n`; break; case "info": - planHTML += `

Information

\n${formatTextBlock(planChild.textContent)}
\n`; + planHTML += `

Information

\n${formatTextBlock(planChild.textContent!)}
\n`; break; case "cost": { planHTML += `

Relative Cost `; // The plan might not have a cost const cost = planChild.attributes.getNamedItem("value")?.value; - planHTML += +cost ? `= ${cost}` : "Unavailable"; + planHTML += +cost! ? `= ${cost}` : "Unavailable"; planHTML += "

\n"; capturePlan = true; break; @@ -171,7 +171,7 @@ export async function showPlanWebview(args: { moduleText += moduleChild.textContent; moduleChild = moduleChild.nextSibling; } - planHTML += `

Module ${planChild.attributes.item(0).value}

\n${formatTextBlock(moduleText)}
\n`; + planHTML += `

Module ${planChild.attributes.item(0)!.value}

\n${formatTextBlock(moduleText)}
\n`; break; } case "subquery": { @@ -181,7 +181,7 @@ export async function showPlanWebview(args: { subqueryText += subqueryChild.textContent; subqueryChild = subqueryChild.nextSibling; } - planHTML += `

Subquery ${planChild.attributes.item(0).value}

\n${formatTextBlock(subqueryText)}
\n`; + planHTML += `

Subquery ${planChild.attributes.item(0)!.value}

\n${formatTextBlock(subqueryText)}
\n`; break; } } diff --git a/src/commands/studio.ts b/src/commands/studio.ts index 787b28ec..6543b9ea 100644 --- a/src/commands/studio.ts +++ b/src/commands/studio.ts @@ -239,11 +239,11 @@ export class StudioActions { } } - const fileExt = fileName.split(".").pop().toLowerCase(); + const fileExt = fileName.split(".").pop()!.toLowerCase(); const isCorrectMethod = (text: string) => fileExt === "cls" ? text.match("Method " + method) : text.startsWith(method); - vscode.window.showTextDocument(DocumentContentProvider.getUri(fileName), { preview: false }).then( + vscode.window.showTextDocument(DocumentContentProvider.getUri(fileName)!, { preview: false }).then( (newEditor) => { if (method) { const document = newEditor.document; @@ -289,7 +289,7 @@ export class StudioActions { return Promise.resolve(); } - private userAction(action, afterUserAction = false, answer = "", msg = "", type = 0): Thenable { + private userAction(action, afterUserAction = false, answer = "", msg = "", type = 0): Thenable | undefined { if (!action || action.id == "") { return; } @@ -515,7 +515,7 @@ export class StudioActions { public getServerInfo(): { server: string; namespace: string } { return { server: `${this.api.config.host}:${this.api.config.port}${this.api.config.pathPrefix}`, - namespace: this.api.config.ns, + namespace: this.api.config.ns!, }; } } @@ -574,7 +574,7 @@ export async function fireOtherStudioAction( action: OtherStudioAction, uri: vscode.Uri, userAction?: UserAction -): Promise { +): Promise { if (vscode.workspace.getConfiguration("objectscript.serverSourceControl", uri)?.get("disableOtherActionTriggers")) { return; } diff --git a/src/commands/studioMigration.ts b/src/commands/studioMigration.ts index 3527e15f..65ead120 100644 --- a/src/commands/studioMigration.ts +++ b/src/commands/studioMigration.ts @@ -166,7 +166,7 @@ export async function loadStudioSnippets(): Promise { * * Store the editor background color in the user `settings.json` file under `workbench.colorCustomizations`. * * Activate the modified theme. */ -export async function loadStudioColors(languageServerExt: vscode.Extension | undefined): Promise { +export async function loadStudioColors(languageServerExt: vscode.Extension | null | undefined): Promise { // Check that we're on windows if (process.platform != "win32") { vscode.window.showErrorMessage("Loading Studio syntax colors is only supported on Windows.", "Dismiss"); @@ -202,7 +202,7 @@ export async function loadStudioColors(languageServerExt: vscode.Extension ); await vscode.workspace.fs.writeFile(tempRoutineUri, new TextEncoder().encode("ROUTINE temp\n")); await vscode.workspace.openTextDocument(tempRoutineUri); - let legend: vscode.SemanticTokensLegend = await vscode.commands + let legend: vscode.SemanticTokensLegend | undefined = await vscode.commands .executeCommand("vscode.provideDocumentSemanticTokensLegend", tempRoutineUri) // Swallow any errors .then( @@ -267,7 +267,7 @@ export async function loadStudioColors(languageServerExt: vscode.Extension } if (!line.startsWith(" ")) { // This is a header line so check if it's the start of a Language - const langMatch = lineTrim.split("\\").pop().match(langRegex); + const langMatch = lineTrim.split("\\").pop()!.match(langRegex); if (langMatch != null) { currentLanguage = Number(langMatch[1]); } @@ -325,10 +325,10 @@ export async function loadStudioColors(languageServerExt: vscode.Extension // Modify the theme const editorConfig = vscode.workspace.getConfiguration("editor"); const workbenchConfig = vscode.workspace.getConfiguration("workbench"); - const tokensConfig = editorConfig.get("semanticTokenColorCustomizations"); + const tokensConfig: any = editorConfig.get("semanticTokenColorCustomizations"); tokensConfig[`[${themeName}]`] = { rules }; await editorConfig.update("semanticTokenColorCustomizations", tokensConfig, true); - const colorsConfig = workbenchConfig.get("colorCustomizations"); + const colorsConfig: any = workbenchConfig.get("colorCustomizations"); colorsConfig[`[${themeName}]`] = { "editor.background": editorBackground }; await workbenchConfig.update("colorCustomizations", colorsConfig, true); diff --git a/src/commands/unitTest.ts b/src/commands/unitTest.ts index 3e850c79..6a0ff98e 100644 --- a/src/commands/unitTest.ts +++ b/src/commands/unitTest.ts @@ -61,9 +61,9 @@ const textDecoder = new TextDecoder(); /** Find the root `TestItem` for `uri` */ function rootItemForItem(testController: vscode.TestController, uri: vscode.Uri): vscode.TestItem | undefined { - let rootItem: vscode.TestItem; + let rootItem: vscode.TestItem | undefined; for (const [, i] of testController.items) { - if (uriIsAncestorOf(i.uri, uri)) { + if (uriIsAncestorOf(i.uri!, uri)) { rootItem = i; break; } @@ -80,14 +80,14 @@ async function addTestItemsForClass(testController: vscode.TestController, paren parent.uri ); if (parentSymbols?.length == 1 && parentSymbols[0].kind == vscode.SymbolKind.Class) { - const rootItem = rootItemForItem(testController, parent.uri); + const rootItem = rootItemForItem(testController, parent.uri!); if (rootItem) { // Add this class to our cache // Need to do this here because we need the // DocumentSymbols to accurately determine the class const classes = classesForRoot.get(rootItem); - classes.set(parentSymbols[0].name, parent); - classesForRoot.set(rootItem, classes); + classes!.set(parentSymbols[0].name, parent); + classesForRoot.set(rootItem, classes!); } parent.range = parentSymbols[0].range; // Add an item for each Test* method defined in this class @@ -104,10 +104,10 @@ async function addTestItemsForClass(testController: vscode.TestController, paren parent.children.add(newItem); } }); - if (filesystemSchemas.includes(parent.uri.scheme)) { + if (filesystemSchemas.includes(parent.uri!.scheme)) { // Query the server to find inherited Test* methods - const api = new AtelierAPI(parent.uri); - const workspaceFolder = vscode.workspace.getWorkspaceFolder(parent.uri).name; + const api = new AtelierAPI(parent.uri!); + const workspaceFolder = vscode.workspace.getWorkspaceFolder(parent.uri!)!.name; const methodsMap: Map = new Map(); const inheritedMethods: { Name: string; Origin: string }[] = await api .actionQuery( @@ -156,7 +156,7 @@ async function addTestItemsForClass(testController: vscode.TestController, paren /** Get the array of `objectscript.unitTest.relativeTestRoots` for workspace folder `uri`. */ function relativeTestRootsForUri(uri: vscode.Uri): string[] { - let roots: string[] = vscode.workspace.getConfiguration("objectscript.unitTest", uri).get("relativeTestRoots"); + let roots: string[] = vscode.workspace.getConfiguration("objectscript.unitTest", uri).get("relativeTestRoots")!; roots = roots.map((r) => r.replaceAll("\\", "/")); // VS Code URIs always use / as a separator if (roots.length > 1) { // Filter out any duplicate roots, or roots that are a subdirectory of another root @@ -179,7 +179,7 @@ function createRootItemsForWorkspaceFolder( ? "Server connection is inactive" : api.ns == "%SYS" ? "Connected to the %SYS namespace" - : api.config.apiVersion < 8 + : api.config.apiVersion! < 8 ? "Must be connected to InterSystems IRIS version 2023.3 or above" : filesystemSchemas.includes(folder.uri.scheme) && csp ? "Web application folder" @@ -217,20 +217,20 @@ async function getTestItemForClass( uri: vscode.Uri, create = false ): Promise { - let item: vscode.TestItem; + let item: vscode.TestItem | undefined; const rootItem = rootItemForItem(testController, uri); if (rootItem && !rootItem.error) { // Walk the directory path until we reach a dead end or the TestItem for this class - let docPath = uri.path.slice(rootItem.uri.path.length); + let docPath = uri.path.slice(rootItem.uri!.path.length); docPath = docPath.startsWith("/") ? docPath.slice(1) : docPath; const docPathParts = docPath.split("/"); item = rootItem; for (const part of docPathParts) { - const currUri = item.uri.with({ path: `${item.uri.path}${!item.uri.path.endsWith("/") ? "/" : ""}${part}` }); + const currUri = item.uri!.with({ path: `${item.uri!.path}${!item.uri!.path.endsWith("/") ? "/" : ""}${part}` }); let currItem = item.children.get(currUri.toString()); if (!currItem && create) { // We're allowed to create non-existent directory TestItems as we walk the path - await testController.resolveHandler(item); + await testController.resolveHandler!(item); currItem = item.children.get(currUri.toString()); } item = currItem; @@ -258,10 +258,10 @@ function replaceRootTestItems(testController: vscode.TestController): void { async function childrenForServerSideFolderItem( item: vscode.TestItem ): Promise>> { - const { project, system, generated, mapped } = isfsConfig(item.uri); + const { project, system, generated, mapped } = isfsConfig(item.uri!); let query: string; let parameters: string[]; - let folder = !item.uri.path.endsWith("/") ? item.uri.path + "/" : item.uri.path; + let folder = !item.uri!.path.endsWith("/") ? item.uri!.path + "/" : item.uri!.path; folder = folder.startsWith("/") ? folder.slice(1) : folder; if (folder == "/") { // Treat this the same as an empty folder @@ -269,7 +269,7 @@ async function childrenForServerSideFolderItem( } folder = folder.replace(/\//g, "."); const folderLen = String(folder.length + 1); // Need the + 1 because SUBSTR is 1 indexed - const api = new AtelierAPI(item.uri); + const api = new AtelierAPI(item.uri!); if (project) { query = "SELECT DISTINCT CASE " + @@ -293,7 +293,7 @@ async function childrenForServerSideFolderItem( folderLen, folderLen, folderLen, - fileSpecFromURI(item.uri), + fileSpecFromURI(item.uri!), "1", "1", system ? "1" : "0", @@ -311,8 +311,8 @@ async function childrenForServerSideFolderItem( /** Create a child `TestItem` of `item` with label `child`. */ function addChildItem(testController: vscode.TestController, item: vscode.TestItem, child: string): void { - const newUri = item.uri.with({ - path: `${item.uri.path}${!item.uri.path.endsWith("/") ? "/" : ""}${child}`, + const newUri = item.uri!.with({ + path: `${item.uri!.path}${!item.uri!.path.endsWith("/") ? "/" : ""}${child}`, }); if (!item.children.get(newUri.toString())) { // Only add the item if it doesn't already exist @@ -324,7 +324,7 @@ function addChildItem(testController: vscode.TestController, item: vscode.TestIt /** Determine the class name of `item` in `root` */ function classNameForItem(item: vscode.TestItem, root: vscode.TestItem): string | undefined { - let cls: string; + let cls: string | undefined; const classes = classesForRoot.get(root); if (classes) { for (const element of classes) { @@ -359,7 +359,7 @@ async function addItemForClassUri(testController: vscode.TestController, uri: vs const item = await getTestItemForClass(testController, uri, true); if (item && item.canResolveChildren && !item.children.size) { // Resolve the methods - testController.resolveHandler(item); + testController.resolveHandler!(item); } } } @@ -372,7 +372,7 @@ async function runHandler( debug = false ): Promise { const action = debug ? "debug" : "run"; - let root: vscode.TestItem; + let root: vscode.TestItem | undefined; const asyncRequest: Atelier.AsyncUnitTestRequest = { request: "unittest", tests: [], @@ -382,9 +382,9 @@ async function runHandler( try { // Determine the test root for this run - let roots: vscode.TestItem[]; + let roots: (vscode.TestItem | undefined)[]; if (request.include?.length) { - roots = [...new Set(request.include.map((i) => rootItemForItem(testController, i.uri)))]; + roots = [...new Set(request.include.map((i) => rootItemForItem(testController, i.uri!)))]; } else { // Run was launched from controller's root level // Ignore any roots that have errors @@ -396,8 +396,8 @@ async function runHandler( const picked = await vscode.window.showQuickPick( roots.map((i) => { return { - label: i.label, - detail: displayableUri(i.uri), + label: i!.label, + detail: displayableUri(i!.uri!), item: i, }; }), @@ -417,13 +417,13 @@ async function runHandler( // Need a root to continue return; } - sendUnitTestTelemetryEvent(root.uri, debug); + sendUnitTestTelemetryEvent(root.uri!, debug); // Add the initial items to the queue to process const queue: vscode.TestItem[] = []; if (request.include?.length) { request.include.forEach((i) => { - if (uriIsAncestorOf(root.uri, i.uri)) { + if (uriIsAncestorOf(root!.uri!, i.uri!)) { queue.push(i); } }); @@ -433,16 +433,16 @@ async function runHandler( // Get the autoload configuration for the root const autoload = vscode.workspace.getConfiguration("objectscript.unitTest.autoload", root.uri); - const autoloadFolder: string = autoload.get("folder"); - const autoloadXml: boolean = autoload.get("xml"); - const autoloadUdl: boolean = autoload.get("udl"); - const autoloadEnabled: boolean = autoloadFolder != "" && (autoloadXml || autoloadUdl) && notIsfs(root.uri); + const autoloadFolder: string = autoload.get("folder")!; + const autoloadXml: boolean = autoload.get("xml")!; + const autoloadUdl: boolean = autoload.get("udl")!; + const autoloadEnabled: boolean = autoloadFolder != "" && (autoloadXml || autoloadUdl) && notIsfs(root.uri!); const autoloadProcessed: string[] = []; // Process every test that was queued // Recurse down to leaves (methods) and build a map of their parents (classes) while (queue.length > 0 && !token.isCancellationRequested) { - const test = queue.pop(); + const test = queue.pop()!; // Skip tests the user asked to exclude if (request.exclude?.length && request.exclude.some((excludedTest) => excludedTest.id === test.id)) { @@ -451,8 +451,8 @@ async function runHandler( if (autoloadEnabled) { // Process any autoload folders needed by this item - const basePath = root.uri.path.endsWith("/") ? root.uri.path.slice(0, -1) : root.uri.path; - const directories = ["", ...test.uri.path.slice(basePath.length + 1).split("/")]; + const basePath = root.uri!.path.endsWith("/") ? root.uri!.path.slice(0, -1) : root.uri!.path; + const directories = ["", ...test.uri!.path.slice(basePath.length + 1).split("/")]; if (directories[directories.length - 1].toLowerCase().endsWith(".cls")) { // Remove the class name directories.pop(); @@ -465,7 +465,7 @@ async function runHandler( // Look for XML or UDL files in the autoload folder const files = await vscode.workspace.findFiles( new vscode.RelativePattern( - test.uri.with({ path: `${basePath}${testPath}/${autoloadFolder}` }), + test.uri!.with({ path: `${basePath}${testPath}/${autoloadFolder}` }), `**/*.{${autoloadXml ? "xml,XML" : ""}${autoloadXml && autoloadUdl ? "," : ""}${ autoloadUdl ? "cls,CLS,mac,MAC,int,INT,inc,INC" : "" }}` @@ -488,16 +488,16 @@ async function runHandler( // Resolve children if not already done if (test.canResolveChildren && !test.children.size) { - await testController.resolveHandler(test); + await testController.resolveHandler!(test); } - if (test.uri.path.toLowerCase().endsWith(".cls")) { + if (test.uri!.path.toLowerCase().endsWith(".cls")) { if (test.id.includes(methodIdSeparator)) { // This is a method item // Will only reach this code if this item is in request.include // Look up the name of this class - const cls = classNameForItem(test.parent, root); + const cls = classNameForItem(test.parent!, root); if (cls) { // Check if there's a test object for the parent class already const clsObjIdx = asyncRequest.tests.findIndex((t) => t.class == cls); @@ -514,15 +514,15 @@ async function runHandler( class: cls, methods: [test.label], }); - if (notIsfs(test.parent.uri)) { + if (notIsfs(test.parent!.uri!)) { // Add this class to the list to load if (asyncRequest.load == undefined) asyncRequest.load = []; asyncRequest.load.push({ - file: test.parent.uri.fsPath, - content: textDecoder.decode(await vscode.workspace.fs.readFile(test.parent.uri)).split(/\r?\n/), + file: test.parent!.uri!.fsPath, + content: textDecoder.decode(await vscode.workspace.fs.readFile(test.parent!.uri!)).split(/\r?\n/), }); } - clsItemsRun.push(test.parent); + clsItemsRun.push(test.parent!); } } } else { @@ -538,8 +538,8 @@ async function runHandler( // Determine the methods to run clsObj.methods = []; test.children.forEach((i) => { - if (!request.exclude.some((excludedTest) => excludedTest.id === i.id)) { - clsObj.methods.push(i.label); + if (!request.exclude!.some((excludedTest) => excludedTest.id === i.id)) { + clsObj.methods!.push(i.label); } }); if (clsObj.methods.length == 0) { @@ -551,12 +551,12 @@ async function runHandler( delete clsObj.methods; } } - if (notIsfs(test.uri)) { + if (notIsfs(test.uri!)) { // Add this class to the list to load if (asyncRequest.load == undefined) asyncRequest.load = []; asyncRequest.load.push({ - file: test.uri.fsPath, - content: textDecoder.decode(await vscode.workspace.fs.readFile(test.uri)).split(/\r?\n/), + file: test.uri!.fsPath, + content: textDecoder.decode(await vscode.workspace.fs.readFile(test.uri!)).split(/\r?\n/), }); } asyncRequest.tests.push(clsObj); @@ -586,8 +586,8 @@ async function runHandler( asyncRequest.console = vscode.workspace.getConfiguration("objectscript.unitTest", root.uri).get("showOutput"); // Send the queue request - const api = new AtelierAPI(root.uri); - const queueResp: Atelier.Response = await api.queueAsync(asyncRequest, true).catch((error) => { + const api = new AtelierAPI(root.uri!); + const queueResp: Atelier.Response | undefined = await api.queueAsync(asyncRequest, true).catch((error) => { handleError(error, `Error creating job to ${action} tests.`); return undefined; }); @@ -624,7 +624,7 @@ async function runHandler( let currentOutputItem: vscode.TestItem | undefined; // The workspace folder that we're running tests in - const workspaceFolder = vscode.workspace.getWorkspaceFolder(root.uri); + const workspaceFolder = vscode.workspace.getWorkspaceFolder(root.uri!); // A map of all documents that we've computed symbols for const documentSymbols: Map = new Map(); @@ -642,19 +642,19 @@ async function runHandler( if (indent == 4) { if (consoleLine.endsWith("...")) { // This is the beginning of a class - currentOutputItem = classes.get(consoleLine.trim().split(" ")[0]); + currentOutputItem = classes!.get(consoleLine.trim().split(" ")[0]); } else { // This is the end of a class if (currentOutputItem != undefined && currentOutputItem.id.includes(methodIdSeparator)) { - currentOutputItem = currentOutputItem.parent; + currentOutputItem = currentOutputItem.parent!; } } } else if (indent == 6 && consoleLine.endsWith("...")) { // This is the beginning of a method if (currentOutputItem != undefined) { if (currentOutputItem.id.includes(methodIdSeparator)) { - currentOutputItem = currentOutputItem.parent.children.get( - `${currentOutputItem.parent.id}${methodIdSeparator}${consoleLine.trim().slice(4).split("(")[0]}` + currentOutputItem = currentOutputItem.parent!.children.get( + `${currentOutputItem.parent!.id}${methodIdSeparator}${consoleLine.trim().slice(4).split("(")[0]}` ); } else { currentOutputItem = currentOutputItem.children.get( @@ -669,7 +669,7 @@ async function runHandler( if (currentOutputItem != undefined) { testRun.appendOutput( `${consoleLine}\r\n`, - new vscode.Location(currentOutputItem.uri, currentOutputItem.range), + new vscode.Location(currentOutputItem.uri!, currentOutputItem.range!), currentOutputItem ); } else { @@ -685,7 +685,7 @@ async function runHandler( if (Array.isArray(pollResp.result)) { // Process results for (const testResult of pollResp.result) { - const clsItem = classes.get(testResult.class); + const clsItem = classes!.get(testResult.class); if (clsItem) { if (testResult.method) { // This is a method's result @@ -709,10 +709,10 @@ async function runHandler( ); if (failure.location) { if (failure.location.document.toLowerCase().endsWith(".cls")) { - let locationUri: vscode.Uri; - if (classes.has(failure.location.document.slice(0, -4))) { + let locationUri: vscode.Uri | null | undefined; + if (classes!.has(failure.location.document.slice(0, -4))) { // This is one of the known test classes - locationUri = classes.get(failure.location.document.slice(0, -4)).uri; + locationUri = classes!.get(failure.location.document.slice(0, -4))!.uri; } else { // This is some other class. There's a chance that // the class won't exist after the tests are run @@ -720,7 +720,7 @@ async function runHandler( // because it will often be useful to the user. locationUri = DocumentContentProvider.getUri( failure.location.document, - workspaceFolder.name, + workspaceFolder!.name, failure.location.namespace ); } @@ -749,7 +749,7 @@ async function runHandler( const locationLine = methodOffsetToLine( locationSymbols, fileText, - failure.location.label, + failure.location.label!, failure.location.offset ); if (locationLine != undefined) { @@ -773,7 +773,7 @@ async function runHandler( // because it will often be useful to the user. const locationUri = DocumentContentProvider.getUri( failure.location.document, - workspaceFolder.name, + workspaceFolder!.name, failure.location.namespace ); if (locationUri) { @@ -859,7 +859,7 @@ async function runHandler( } else if (debug && queueResp.result.content?.debugId && pollResp.result?.content?.debugReady) { // Make sure the activeTextEditor's document is in the same workspace folder as the test // root so the debugger connects to the correct server and runs in the correct namespace - const rootWsFolderIdx = vscode.workspace.getWorkspaceFolder(root.uri)?.index; + const rootWsFolderIdx = vscode.workspace.getWorkspaceFolder(root.uri!)?.index; if ( !vscode.window.activeTextEditor?.document.uri || vscode.workspace.getWorkspaceFolder(vscode.window.activeTextEditor.document.uri)?.index != rootWsFolderIdx @@ -875,7 +875,9 @@ async function runHandler( } if (!shown) { // Show the first test class. Ugly but necessary. - await vscode.window.showTextDocument(classesForRoot.get(root).get(asyncRequest.tests[0].class)?.uri); + await vscode.window.showTextDocument( + classesForRoot.get(root)!.get(asyncRequest.tests[0].class)?.uri as vscode.Uri + ); } } // Start the debugging session @@ -951,16 +953,16 @@ export function setUpTestController(context: vscode.ExtensionContext): vscode.Di if (!item) return; // Can't resolve "undefined" item.busy = true; try { - if (item.uri.path.toLowerCase().endsWith(".cls")) { + if (item.uri!.path.toLowerCase().endsWith(".cls")) { // Compute items for the Test* methods in this class await addTestItemsForClass(testController, item); } else { - if (notIsfs(item.uri)) { + if (notIsfs(item.uri!)) { // Read the local directory for non-autoload subdirectories and classes const autoload = vscode.workspace.getConfiguration("objectscript.unitTest.autoload", item.uri); - const autoloadFolder: string = autoload.get("folder"); - const autoloadEnabled: boolean = autoloadFolder != "" && (autoload.get("xml") || autoload.get("udl")); - (await vscode.workspace.fs.readDirectory(item.uri)).forEach((element) => { + const autoloadFolder: string = autoload.get("folder")!; + const autoloadEnabled: boolean = autoloadFolder != "" && (autoload.get("xml") || autoload.get("udl"))!; + (await vscode.workspace.fs.readDirectory(item.uri!)).forEach((element) => { if ( (element[1] == vscode.FileType.Directory && !element[0].startsWith("_") && // %UnitTest.Manager skips subfolders that start with _ @@ -990,7 +992,7 @@ export function setUpTestController(context: vscode.ExtensionContext): vscode.Di // Create new roots replaceRootTestItems(testController); // Resolve children for the roots - testController.items.forEach((item) => testController.resolveHandler(item)); + testController.items.forEach((item) => testController.resolveHandler!(item)); }; // Create the run and debug profiles const runProfile = testController.createRunProfile( @@ -1029,7 +1031,7 @@ export function setUpTestController(context: vscode.ExtensionContext): vscode.Di : r ); Promise.allSettled([ - vscode.extensions.getExtension(extensionId).activate(), + vscode.extensions.getExtension(extensionId)!.activate(), languageServer && !languageServer.isActive ? Promise.allSettled([languageServer.activate(), waitForResponse(1)]) : Promise.resolve(), @@ -1050,7 +1052,7 @@ export function setUpTestController(context: vscode.ExtensionContext): vscode.Di // Remove from our cache of classes const classes = classesForRoot.get(rootItem); if (classes) { - let cls: string; + let cls: string | undefined; for (const element of classes) { if (element[1].id == item.id) { cls = element[0]; @@ -1063,7 +1065,7 @@ export function setUpTestController(context: vscode.ExtensionContext): vscode.Di } } } - item.parent.children.delete(uri.toString()); + item.parent!.children.delete(uri.toString()); result = true; } } @@ -1080,7 +1082,7 @@ export function setUpTestController(context: vscode.ExtensionContext): vscode.Di // Update root items if needed e.removed.forEach((wf) => { testController.items.forEach((i) => { - if (uriIsAncestorOf(wf.uri, i.uri)) { + if (uriIsAncestorOf(wf.uri, i.uri!)) { // Remove this TestItem classesForRoot.delete(i); testController.items.delete(i.id); @@ -1100,7 +1102,7 @@ export function setUpTestController(context: vscode.ExtensionContext): vscode.Di const replace: vscode.TestItem[] = []; testController.items.forEach((item) => { if ( - (notIsfs(item.uri) && e.affectsConfiguration("objectscript.unitTest", item.uri)) || + (notIsfs(item.uri!) && e.affectsConfiguration("objectscript.unitTest", item.uri)) || e.affectsConfiguration("objectscript.conn", item.uri) || e.affectsConfiguration("intersystems.servers", item.uri) ) { @@ -1111,7 +1113,7 @@ export function setUpTestController(context: vscode.ExtensionContext): vscode.Di replace.forEach((item) => { classesForRoot.delete(item); testController.items.delete(item.id); - const folder = vscode.workspace.getWorkspaceFolder(item.uri); + const folder = vscode.workspace.getWorkspaceFolder(item.uri!); if (folder) { const newItems = createRootItemsForWorkspaceFolder(testController, folder); newItems.forEach((i) => { @@ -1137,7 +1139,7 @@ export function setUpTestController(context: vscode.ExtensionContext): vscode.Di testController.invalidateTestResults(item); if (item.canResolveChildren) { // Resolve the methods - testController.resolveHandler(item); + testController.resolveHandler!(item); } } } diff --git a/src/commands/viewOthers.ts b/src/commands/viewOthers.ts index fdce801b..b7a493be 100644 --- a/src/commands/viewOthers.ts +++ b/src/commands/viewOthers.ts @@ -18,9 +18,9 @@ export async function viewOthers(forceEditable = false): Promise { item = item.slice(0, colonidx); let uri: vscode.Uri; if (forceEditable) { - uri = DocumentContentProvider.getUri(item, undefined, undefined, forceEditable); + uri = DocumentContentProvider.getUri(item, undefined, undefined, forceEditable)!; } else { - uri = DocumentContentProvider.getUri(item); + uri = DocumentContentProvider.getUri(item)!; } if (item.endsWith(".cls")) { @@ -75,9 +75,9 @@ export async function viewOthers(forceEditable = false): Promise { } else { let uri: vscode.Uri; if (forceEditable) { - uri = DocumentContentProvider.getUri(item, undefined, undefined, forceEditable); + uri = DocumentContentProvider.getUri(item, undefined, undefined, forceEditable)!; } else { - uri = DocumentContentProvider.getUri(item); + uri = DocumentContentProvider.getUri(item)!; } vscode.window.showTextDocument(uri); } @@ -90,11 +90,11 @@ export async function viewOthers(forceEditable = false): Promise { const api = new AtelierAPI(file.uri); if (!api.active) return; let indexarg: string = file.name; - const cursorpos: vscode.Position = vscode.window.activeTextEditor.selection.active; - const fileExt: string = file.name.split(".").pop().toLowerCase(); + const cursorpos: vscode.Position = vscode.window.activeTextEditor!.selection.active; + const fileExt: string = file.name.split(".").pop()!.toLowerCase(); if ( - api.config.apiVersion >= 4 && + api.config.apiVersion! >= 4 && (fileExt === "cls" || fileExt === "mac" || fileExt === "int") && !/^%sqlcq/i.test(indexarg) ) { @@ -108,7 +108,7 @@ export async function viewOthers(forceEditable = false): Promise { symbols = symbols[0].children; } - let currentSymbol: vscode.DocumentSymbol; + let currentSymbol: vscode.DocumentSymbol | undefined; for (const symbol of symbols) { if (symbol.range.contains(cursorpos)) { currentSymbol = symbol; @@ -128,7 +128,7 @@ export async function viewOthers(forceEditable = false): Promise { let isObjectScript = true; if (fileExt === "cls") { - const memberInfo = parseClassMemberDefinition(vscode.window.activeTextEditor.document, currentSymbol); + const memberInfo = parseClassMemberDefinition(vscode.window.activeTextEditor!.document, currentSymbol); if (memberInfo) { const { defEndLine, language } = memberInfo; offset = cursorpos.line - defEndLine; @@ -157,7 +157,7 @@ export async function viewOthers(forceEditable = false): Promise { open(listOthers[0], forceEditable); } else { vscode.window.showQuickPick(listOthers).then((item) => { - open(item, forceEditable); + open(item!, forceEditable); }); } }) diff --git a/src/commands/webSocketTerminal.ts b/src/commands/webSocketTerminal.ts index 12dc2fbf..818313d8 100644 --- a/src/commands/webSocketTerminal.ts +++ b/src/commands/webSocketTerminal.ts @@ -198,7 +198,7 @@ class WebSocketTerminal implements vscode.Pseudoterminal { */ private _moveCursorToLastLine(): void { const currRow = (this._cursorCol - (this._cursorCol % this._cols)) / this._cols; - const newRow = Math.ceil((this._margin + this._input.split("\r\n").pop().length + 1) / this._cols) - 1; + const newRow = Math.ceil((this._margin + this._input.split("\r\n").pop()!.length + 1) / this._cols) - 1; const rowDelta = newRow - currRow; if (rowDelta) this._hideCursorWrite(`\x1b[${rowDelta}B`); } @@ -259,19 +259,19 @@ class WebSocketTerminal implements vscode.Pseudoterminal { // Write the output to the terminal if (this._firstOutputLineSincePrompt) { // Strip leading \r\n since we printed it already - message.text = message.text.startsWith("\r\n") ? message.text.slice(2) : message.text; + message.text = message.text!.startsWith("\r\n") ? message.text!.slice(2) : message.text; this._firstOutputLineSincePrompt = false; } - if (message.text.includes("\x1b[31;1m")) { - if (message.text.includes("\x1b[31;1m")) { + if (message.text!.includes("\x1b[31;1m")) { + if (message.text!.includes("\x1b[31;1m")) { // Report no exit code for interrupts this._promptExitCode = ""; } else { this._promptExitCode = ";1"; } } - this._margin = this._cursorCol = message.text.split("\r\n").pop().length; - this._hideCursorWrite(message.text); + this._margin = this._cursorCol = message.text!.split("\r\n").pop()!.length; + this._hideCursorWrite(message.text!); break; case "prompt": case "read": @@ -280,11 +280,11 @@ class WebSocketTerminal implements vscode.Pseudoterminal { this._hideCursorWrite( `\x1b]633;D${this._promptExitCode}\x07\r\n\x1b]633;A\x07${message.text}\x1b]633;B\x07` ); - this._margin = this._cursorCol = message.text.replace(this._colorsRegex, "").length; - this._prompt = message.text; + this._margin = this._cursorCol = message.text!.replace(this._colorsRegex, "").length; + this._prompt = message.text!; this._promptExitCode = ";0"; // Store the current namespace - this.currentNs = message.ns; + this.currentNs = message.ns!; } // Enable input this._state = message.type; @@ -303,13 +303,13 @@ class WebSocketTerminal implements vscode.Pseudoterminal { case "color": { // Replace the input with the syntax colored text, keeping the cursor at the same spot let cursorLine = Math.ceil((this._cursorCol + 1) / this._cols) - 1; - if (message.text.includes("\r\n")) { - const lines = message.text.replace(this._colorsRegex, "").split("\r\n"); + if (message.text!.includes("\r\n")) { + const lines = message.text!.replace(this._colorsRegex, "").split("\r\n"); lines.pop(); cursorLine += lines.reduce((sum, line) => sum + Math.ceil((line.length + 1) / this._cols), 0); } this._hideCursorWrite( - `\x1b7${cursorLine > 0 ? `\x1b[${cursorLine}A` : ""}\r\x1b[0J${this._prompt}${message.text.replace( + `\x1b7${cursorLine > 0 ? `\x1b[${cursorLine}A` : ""}\r\x1b[0J${this._prompt}${message.text!.replace( /\r\n/g, `\r\n${this.multiLinePrompt}` )}\x1b8` @@ -517,7 +517,7 @@ class WebSocketTerminal implements vscode.Pseudoterminal { // User can't move cursor return; } - if (this._cursorCol < this._margin + this._input.split("\r\n").pop().length) { + if (this._cursorCol < this._margin + this._input.split("\r\n").pop()!.length) { this._cursorCol++; if (this._cursorCol % this._cols == 0) { // Move the cursor to the beginning of the next line @@ -553,7 +553,7 @@ class WebSocketTerminal implements vscode.Pseudoterminal { case keys.ctrlE: { if (this._state == "prompt") { // Move the cursor to the end of the input - const lineLength = this._input.split("\r\n").pop().length; + const lineLength = this._input.split("\r\n").pop()!.length; if (lineLength > this._cursorCol) { this._moveCursor(lineLength - this._cursorCol); } @@ -658,7 +658,7 @@ class WebSocketTerminal implements vscode.Pseudoterminal { // Add a blank "line" to move the cursor to the next viewport row return [""]; } - const chunks = []; + const chunks: string[] = []; for (let i = 0; i < line.length; i += this._cols) { chunks.push(line.slice(i, i + this._cols)); } @@ -759,7 +759,7 @@ function terminalConfigForUri( return; } // Make sure the server has the terminal endpoint - if (api.config.apiVersion < 7) { + if (api.config.apiVersion! < 7) { reportError("Lite Terminal requires InterSystems IRIS version 2023.2 or above.", throwErrors); return; } @@ -782,7 +782,7 @@ function terminalConfigForUri( }; } -export async function launchWebSocketTerminal(targetUri?: vscode.Uri, nsOverride?: string): Promise { +export async function launchWebSocketTerminal(targetUri?: vscode.Uri | null, nsOverride?: string): Promise { // Determine the server to connect to if (targetUri) { // Uri passed as command argument might be for a server we haven't yet resolved @@ -815,7 +815,7 @@ export async function launchWebSocketTerminal(targetUri?: vscode.Uri, nsOverride export class WebSocketTerminalProfileProvider implements vscode.TerminalProfileProvider { async provideTerminalProfile(): Promise { // Determine the server connection to use - const uri: vscode.Uri = await getWsServerConnection("2023.2.0"); + const uri: vscode.Uri | null | undefined = await getWsServerConnection("2023.2.0"); if (uri) { const api = new AtelierAPI(uri); @@ -823,7 +823,7 @@ export class WebSocketTerminalProfileProvider implements vscode.TerminalProfileP await api.serverInfo(); // Get the terminal configuration. Will throw if there's an error. const terminalOpts = terminalConfigForUri(api, uri, true); - return new vscode.TerminalProfile(terminalOpts); + return new vscode.TerminalProfile(terminalOpts!); } else if (uri === undefined) { throw new Error(NO_ELIGIBLE_CONNECTIONS); } else { diff --git a/src/commands/xmlToUdl.ts b/src/commands/xmlToUdl.ts index 66dfbec0..18da70d4 100644 --- a/src/commands/xmlToUdl.ts +++ b/src/commands/xmlToUdl.ts @@ -92,7 +92,7 @@ export async function extractXMLFileContents(xmlUri?: vscode.Uri): Promise } try { // Determine the workspace folder - let wsFolder: vscode.WorkspaceFolder; + let wsFolder: vscode.WorkspaceFolder | null | undefined; if (xmlUri) { wsFolder = vscode.workspace.getWorkspaceFolder(xmlUri); } else { @@ -128,7 +128,7 @@ export async function extractXMLFileContents(xmlUri?: vscode.Uri): Promise return; } xmlUri = uris[0]; - if (xmlUri.path.split(".").pop().toLowerCase() != "xml") { + if (xmlUri.path.split(".").pop()!.toLowerCase() != "xml") { vscode.window.showErrorMessage("The selected file was not XML.", "Dismiss"); return; } diff --git a/src/debug/dbgp.ts b/src/debug/dbgp.ts index 3a95440d..515bb27a 100644 --- a/src/debug/dbgp.ts +++ b/src/debug/dbgp.ts @@ -68,7 +68,7 @@ export class DbgpConnection extends EventEmitter { private _handleDataChunk() { if (!this._messages.length) return; // Shouldn't ever happen - const data: Buffer = this._messages.shift(); + const data: Buffer = this._messages.shift()!; if (this._parsingState === ParsingState.DataLength) { // does data contain a NULL byte? const separatorIndex = data.indexOf("|"); diff --git a/src/debug/debugAdapterFactory.ts b/src/debug/debugAdapterFactory.ts index 738a8870..0c8a29fe 100644 --- a/src/debug/debugAdapterFactory.ts +++ b/src/debug/debugAdapterFactory.ts @@ -16,12 +16,12 @@ export class ObjectScriptDebugAdapterDescriptorFactory // pickProcess may have added a suffix to inform us which folder's connection it used const workspaceFolderIndex = (session.configuration.processId as string)?.split("@")[1]; const workspaceFolderUri = workspaceFolderIndex - ? vscode.workspace.workspaceFolders[parseInt(workspaceFolderIndex)]?.uri + ? vscode.workspace.workspaceFolders![parseInt(workspaceFolderIndex)]?.uri : undefined; debugSession.setupAPI(workspaceFolderUri); const serverId = debugSession.serverId; - let server = this.serverMap.get(serverId); + let server = this.serverMap.get(serverId!); if (!server) { // start listening on a random port server = net @@ -30,12 +30,12 @@ export class ObjectScriptDebugAdapterDescriptorFactory debugSession.start(socket as NodeJS.ReadableStream, socket); }) .listen(0); - this.serverMap.set(serverId, server); + this.serverMap.set(serverId!, server); } // make VS Code connect to this debug server const address = server.address(); - const port = typeof address !== "string" ? address.port : 9000; + const port = typeof address !== "string" ? address!.port : 9000; return new vscode.DebugAdapterServer(port); } diff --git a/src/debug/debugSession.ts b/src/debug/debugSession.ts index 8e592504..91b07449 100644 --- a/src/debug/debugSession.ts +++ b/src/debug/debugSession.ts @@ -40,9 +40,9 @@ interface AttachRequestArguments extends DebugProtocol.AttachRequestArguments { } /** converts a uri from VS Code to a server-side XDebug file URI with respect to source root settings */ -function convertClientPathToDebugger(uri: vscode.Uri, namespace: string): string { +function convertClientPathToDebugger(uri: vscode.Uri, namespace: string): string | undefined { const { scheme, path } = uri; - let fileName: string; + let fileName: string | undefined; if (scheme && schemas.includes(scheme)) { const { ns } = isfsConfig(uri); if (ns) namespace = ns; @@ -69,7 +69,7 @@ export class ObjectScriptDebugSession extends LoggingDebugSession { private _statuses = new Map(); - private _connection: xdebug.Connection; + private _connection: xdebug.Connection | null | undefined; private _namespace: string; @@ -91,7 +91,7 @@ export class ObjectScriptDebugSession extends LoggingDebugSession { private _evalResultProperties = new Map(); - private _workspace: string; + private _workspace: string | undefined; /** If this is a CSPDEBUG session */ private _isCsp = false; @@ -164,8 +164,7 @@ export class ObjectScriptDebugSession extends LoggingDebugSession { /** Check if the target is stopped */ private async _isStopped(): Promise { - return this._connection - .sendStepIntoCommand() + return this._connection!.sendStepIntoCommand() .then((resp: xdebug.StatusResponse) => { if (resp.status == "stopped") { // Target unattached, terminate session @@ -199,16 +198,16 @@ export class ObjectScriptDebugSession extends LoggingDebugSession { }; try { - if (!this._api.active) { + if (!this._api!.active) { throw new Error("Connection not active"); } - this._namespace = this._api.ns; - this._url = this._api.xdebugUrl(); + this._namespace = this._api!.ns; + this._url = this._api!.xdebugUrl(); const socket = new WebSocket(this._url, { rejectUnauthorized: vscode.workspace.getConfiguration("http").get("proxyStrictSSL"), headers: { - cookie: this._api.cookies, + cookie: this._api!.cookies, }, }); @@ -216,8 +215,8 @@ export class ObjectScriptDebugSession extends LoggingDebugSession { if (!this._connection) { return; } - this.sendEvent(new ThreadEvent("exited", this._connection.id)); - this._connection.close(); + this.sendEvent(new ThreadEvent("exited", this._connection!.id)); + this._connection!.close(); this._connection = null; }; this._connection = new xdebug.Connection(socket) @@ -230,15 +229,15 @@ export class ObjectScriptDebugSession extends LoggingDebugSession { this.sendEvent(new OutputEvent(data, "stdout")); }); - await this._connection.waitForInitPacket(); + await this._connection!.waitForInitPacket(); - await this._connection.sendFeatureSetCommand("max_data", 8192); - await this._connection.sendFeatureSetCommand("max_children", 32); - await this._connection.sendFeatureSetCommand("max_depth", 2); - await this._connection.sendFeatureSetCommand("notify_ok", 1); - await this._connection.sendFeatureSetCommand( + await this._connection!.sendFeatureSetCommand("max_data", 8192); + await this._connection!.sendFeatureSetCommand("max_children", 32); + await this._connection!.sendFeatureSetCommand("max_depth", 2); + await this._connection!.sendFeatureSetCommand("notify_ok", 1); + await this._connection!.sendFeatureSetCommand( "step_granularity", - vscode.workspace.getConfiguration("objectscript.debug").get("stepGranularity") + vscode.workspace.getConfiguration("objectscript.debug").get("stepGranularity")! ); this.sendResponse(response); @@ -262,7 +261,7 @@ export class ObjectScriptDebugSession extends LoggingDebugSession { this._debugTargetSet = false; this._isLaunch = true; const debugTarget = `${this._namespace}:${args.program}`; - await this._connection.sendFeatureSetCommand("debug_target", debugTarget, true); + await this._connection!.sendFeatureSetCommand("debug_target", debugTarget, true); sendDebuggerTelemetryEvent("launch"); } catch (error) { this.sendErrorResponse(response, error); @@ -276,18 +275,18 @@ export class ObjectScriptDebugSession extends LoggingDebugSession { try { this._debugTargetSet = this._isLaunch = false; const debugTarget = - args.cspDebugId != undefined ? `CSPDEBUG:${args.cspDebugId}` : `PID:${args.processId.split("@")[0]}`; - await this._connection.sendFeatureSetCommand("debug_target", debugTarget); + args.cspDebugId != undefined ? `CSPDEBUG:${args.cspDebugId}` : `PID:${args.processId!.split("@")[0]}`; + await this._connection!.sendFeatureSetCommand("debug_target", debugTarget); if (args.cspDebugId != undefined) { if (args.isUnitTest) { // Set a watchpoint so the target breaks after the unit tests have finished - await this._connection.sendBreakpointSetCommand( + await this._connection!.sendBreakpointSetCommand( new xdebug.Watchpoint("QQQZZZDebugWatchpointTriggerVar", this._unitTestWatchpointCondition) ); this._isUnitTest = true; } else { // Set a watchpoint so the target breaks after the REST response is sent - await this._connection.sendBreakpointSetCommand(new xdebug.Watchpoint("ok", this._cspWatchpointCondition)); + await this._connection!.sendBreakpointSetCommand(new xdebug.Watchpoint("ok", this._cspWatchpointCondition)); this._isCsp = true; } this.sendResponse(response); @@ -314,7 +313,7 @@ export class ObjectScriptDebugSession extends LoggingDebugSession { args: DebugProtocol.PauseArguments ): Promise { try { - const xdebugResponse = await this._connection.sendBreakCommand(); + const xdebugResponse = await this._connection!.sendBreakCommand(); this.sendResponse(response); this._checkStatus(xdebugResponse); } catch (error) { @@ -330,15 +329,15 @@ export class ObjectScriptDebugSession extends LoggingDebugSession { if (!this._isLaunch && !this._isCsp) { // The debug agent ignores the first run command // for non-CSP attaches, so send one right away - await this._connection.sendRunCommand(); + await this._connection!.sendRunCommand(); // Tell VS Code that we're stopped this.sendResponse(response); - const event: DebugProtocol.StoppedEvent = new StoppedEvent("entry", this._connection.id); + const event: DebugProtocol.StoppedEvent = new StoppedEvent("entry", this._connection!.id); event.body.allThreadsStopped = false; this.sendEvent(event); } else { // Tell the debugger to run the target process - const xdebugResponse = await this._connection.sendRunCommand(); + const xdebugResponse = await this._connection!.sendRunCommand(); this.sendResponse(response); this._checkStatus(xdebugResponse); } @@ -357,7 +356,7 @@ export class ObjectScriptDebugSession extends LoggingDebugSession { // If attach, it will detach from the target // If launch, it will terminate the target try { - const xdebugResponse = await this._connection.sendDetachCommand(); + const xdebugResponse = await this._connection!.sendDetachCommand(); this.sendResponse(response); this._checkStatus(xdebugResponse); } catch (error) { @@ -377,13 +376,13 @@ export class ObjectScriptDebugSession extends LoggingDebugSession { // args.source.path is a file path if the file is local and is a stringified Uri if the file is virtual const uri = - (this._workspaceFolderUri ?? vscode.workspace.workspaceFolders[0]?.uri)?.scheme == "file" - ? vscode.Uri.file(args.source.path) - : vscode.Uri.parse(args.source.path); + (this._workspaceFolderUri ?? vscode.workspace.workspaceFolders![0]?.uri)?.scheme == "file" + ? vscode.Uri.file(args.source.path!) + : vscode.Uri.parse(args.source.path!); const wsFolder = vscode.workspace.getWorkspaceFolder(uri); if (!wsFolder || (this._workspaceFolderUri && wsFolder.uri.toString() != this._workspaceFolderUri.toString())) { response.body = { - breakpoints: args.breakpoints.map(() => { + breakpoints: args.breakpoints!.map(() => { return { verified: false, message: "This file is not from the same workspace folder as the debug target", @@ -397,7 +396,7 @@ export class ObjectScriptDebugSession extends LoggingDebugSession { const xdebugUri = convertClientPathToDebugger(uri, this._namespace); if (!xdebugUri) { response.body = { - breakpoints: args.breakpoints.map(() => { + breakpoints: args.breakpoints!.map(() => { return { verified: false, message: "Failed to determine the class or routine name of this file", @@ -408,11 +407,11 @@ export class ObjectScriptDebugSession extends LoggingDebugSession { this.sendResponse(response); return; } - const [, fileName] = xdebugUri.match(/\|([^|]+)$/); - const fileExt = fileName.split(".").pop().toLowerCase(); + const [, fileName] = xdebugUri.match(/\|([^|]+)$/)!; + const fileExt = fileName.split(".").pop()!.toLowerCase(); const languageServer: boolean = vscode.extensions.getExtension(lsExtensionId)?.isActive ?? false; - const currentList = await this._connection.sendBreakpointListCommand(); + const currentList = await this._connection!.sendBreakpointListCommand(); currentList.breakpoints .filter((breakpoint) => { if (breakpoint instanceof xdebug.LineBreakpoint) { @@ -421,7 +420,7 @@ export class ObjectScriptDebugSession extends LoggingDebugSession { return false; }) .map((breakpoint) => { - this._connection.sendBreakpointRemoveCommand(breakpoint); + this._connection!.sendBreakpointRemoveCommand(breakpoint); }); let xdebugBreakpoints: (xdebug.ConditionalBreakpoint | xdebug.ClassLineBreakpoint | xdebug.LineBreakpoint)[] = []; @@ -433,11 +432,11 @@ export class ObjectScriptDebugSession extends LoggingDebugSession { )[0].children; } xdebugBreakpoints = await Promise.all( - args.breakpoints.map(async (breakpoint) => { + args.breakpoints!.map(async (breakpoint) => { const line = breakpoint.line; if (fileExt == "cls") { // Find the class member that this breakpoint is in - let currentSymbol: vscode.DocumentSymbol; + let currentSymbol: vscode.DocumentSymbol | undefined; for (const symbol of symbols) { if (symbol.range.contains(new vscode.Position(line, 0))) { currentSymbol = symbol; @@ -541,7 +540,7 @@ export class ObjectScriptDebugSession extends LoggingDebugSession { reason: "failed", }; } else { - await this._connection.sendBreakpointSetCommand(breakpoint); + await this._connection!.sendBreakpointSetCommand(breakpoint); vscodeBreakpoints[index] = { verified: true, line: breakpoint.line }; } } catch (error) { @@ -573,7 +572,7 @@ export class ObjectScriptDebugSession extends LoggingDebugSession { ): void { if ( args.variablesReference !== undefined && - [0, 1].includes(this._contexts.get(args.variablesReference).id) && + [0, 1].includes(this._contexts.get(args.variablesReference)!.id) && !args.name.includes("(") ) { // This is an unsubscripted private or public local variable @@ -600,7 +599,7 @@ export class ObjectScriptDebugSession extends LoggingDebugSession { try { await this._waitForDebugTarget(); - const currentList = await this._connection.sendBreakpointListCommand(); + const currentList = await this._connection!.sendBreakpointListCommand(); currentList.breakpoints .filter((breakpoint) => { if (breakpoint instanceof xdebug.Watchpoint) { @@ -609,7 +608,7 @@ export class ObjectScriptDebugSession extends LoggingDebugSession { return false; }) .map((breakpoint) => { - this._connection.sendBreakpointRemoveCommand(breakpoint); + this._connection!.sendBreakpointRemoveCommand(breakpoint); }); let xdebugWatchpoints: xdebug.Watchpoint[] = []; @@ -623,7 +622,7 @@ export class ObjectScriptDebugSession extends LoggingDebugSession { await Promise.all( xdebugWatchpoints.map(async (breakpoint, index) => { try { - await this._connection.sendBreakpointSetCommand(breakpoint); + await this._connection!.sendBreakpointSetCommand(breakpoint); vscodeWatchpoints[index] = { verified: true, instructionReference: breakpoint.variable }; } catch (error) { vscodeWatchpoints[index] = { @@ -651,7 +650,7 @@ export class ObjectScriptDebugSession extends LoggingDebugSession { protected threadsRequest(response: DebugProtocol.ThreadsResponse): void { // runtime supports now threads so just return a default thread. response.body = { - threads: [new Thread(this._connection.id, `Thread ${this._connection.id}]`)], + threads: [new Thread(this._connection!.id, `Thread ${this._connection!.id}]`)], }; this.sendResponse(response); } @@ -661,16 +660,16 @@ export class ObjectScriptDebugSession extends LoggingDebugSession { args: DebugProtocol.StackTraceArguments ): Promise { try { - const stack = await this._connection.sendStackGetCommand(); + const stack = await this._connection!.sendStackGetCommand(); /** Is set to true if we're at the CSP or unit test ending watchpoint. * We need to do this so VS Code doesn't try to open the source of * a stack frame before the debug session terminates. */ let noStack = false; const stackFrames = await Promise.all( - stack.stack.map(async (stackFrame: xdebug.StackFrame, index): Promise => { + stack.stack.map(async (stackFrame: xdebug.StackFrame, index): Promise => { if (noStack) return; // Stack frames won't be sent - const [, namespace, docName] = decodeURI(stackFrame.fileUri).match(/^dbgp:\/\/\|([^|]+)\|(.*)$/); + const [, namespace, docName] = decodeURI(stackFrame.fileUri).match(/^dbgp:\/\/\|([^|]+)\|(.*)$/)!; const fileUri = DocumentContentProvider.getUri( docName, this._workspace, @@ -678,7 +677,7 @@ export class ObjectScriptDebugSession extends LoggingDebugSession { undefined, this._workspaceFolderUri ); - const source = new Source(docName, fileUri.toString()); + const source = new Source(docName, fileUri!.toString()); let line = stackFrame.line + 1; const place = `${stackFrame.method}+${stackFrame.methodOffset}`; const stackFrameId = this._stackFrameIdCounter++; @@ -687,19 +686,19 @@ export class ObjectScriptDebugSession extends LoggingDebugSession { const unitTest = this._isUnitTest && source.name.startsWith("%Api.Atelier.v"); if (csp || unitTest) { // Check if we're at our special watchpoint - const { result } = await this._connection.sendEvalCommand( + const { result } = await this._connection!.sendEvalCommand( csp ? this._cspWatchpointCondition : this._unitTestWatchpointCondition ); if (result.type == "int" && result.value == "1") { // Stop the debugging session - const xdebugResponse = await this._connection.sendDetachCommand(); + const xdebugResponse = await this._connection!.sendDetachCommand(); this._checkStatus(xdebugResponse); noStack = true; return; } } } - const fileText = await this._getFileText(fileUri); + const fileText = await this._getFileText(fileUri!); const hasCmdLoc = typeof stackFrame.cmdBeginLine == "number"; if (!fileText.length) { // Can't get the source for the document @@ -738,15 +737,15 @@ export class ObjectScriptDebugSession extends LoggingDebugSession { return { id: stackFrameId, name: place, - source: noSource ? null : source, + source: (noSource ? null : source)!, line, - column: hasCmdLoc ? stackFrame.cmdBeginPos + 1 : 0, - endLine: hasCmdLoc ? stackFrame.cmdEndLine + lineDiff : undefined, + column: hasCmdLoc ? stackFrame.cmdBeginPos! + 1 : 0, + endLine: hasCmdLoc ? stackFrame.cmdEndLine! + lineDiff : undefined, endColumn: hasCmdLoc ? (stackFrame.cmdEndPos == 0 ? // A command that ends at position zero means "rest of this line" - fileText.split(/\r?\n/)[stackFrame.cmdEndLine + lineDiff - 1].length - : stackFrame.cmdEndPos) + 1 + fileText.split(/\r?\n/)[stackFrame.cmdEndLine! + lineDiff - 1]!.length + : stackFrame.cmdEndPos)! + 1 : undefined, }; }) @@ -755,7 +754,7 @@ export class ObjectScriptDebugSession extends LoggingDebugSession { this._break = false; if (!noStack) { response.body = { - stackFrames, + stackFrames: stackFrames as StackFrame[], }; } this.sendResponse(response); @@ -805,10 +804,10 @@ export class ObjectScriptDebugSession extends LoggingDebugSession { if (this._contexts.has(variablesReference)) { // VS Code is requesting the variables for a SCOPE, so we have to do a context_get const context = this._contexts.get(variablesReference); - properties = await context.getProperties(); + properties = await context!.getProperties(); } else if (this._properties.has(variablesReference)) { // VS Code is requesting the subelements for a variable, so we have to do a property_get - const property = this._properties.get(variablesReference); + const property = this._properties.get(variablesReference)!; if (property.hasChildren) { if (property.children.length === property.numberOfChildren) { properties = property.children; @@ -820,7 +819,7 @@ export class ObjectScriptDebugSession extends LoggingDebugSession { } } else if (this._evalResultProperties.has(variablesReference)) { // the children of properties returned from an eval command are always inlined, so we simply resolve them - const property = this._evalResultProperties.get(variablesReference); + const property = this._evalResultProperties.get(variablesReference)!; properties = property.hasChildren ? property.children : []; } else { throw new Error("Unknown variable reference"); @@ -900,7 +899,7 @@ export class ObjectScriptDebugSession extends LoggingDebugSession { args: DebugProtocol.ContinueArguments ): Promise { try { - const xdebugResponse = await this._connection.sendRunCommand(); + const xdebugResponse = await this._connection!.sendRunCommand(); this.sendResponse(response); this._checkStatus(xdebugResponse); } catch (error) { @@ -910,7 +909,7 @@ export class ObjectScriptDebugSession extends LoggingDebugSession { protected async nextRequest(response: DebugProtocol.NextResponse, args: DebugProtocol.NextArguments): Promise { try { - const xdebugResponse = await this._connection.sendStepOverCommand(); + const xdebugResponse = await this._connection!.sendStepOverCommand(); this.sendResponse(response); this._checkStatus(xdebugResponse); } catch (error) { @@ -923,7 +922,7 @@ export class ObjectScriptDebugSession extends LoggingDebugSession { args: DebugProtocol.StepInArguments ): Promise { try { - const xdebugResponse = await this._connection.sendStepIntoCommand(); + const xdebugResponse = await this._connection!.sendStepIntoCommand(); this.sendResponse(response); this._checkStatus(xdebugResponse); } catch (error) { @@ -936,7 +935,7 @@ export class ObjectScriptDebugSession extends LoggingDebugSession { args: DebugProtocol.StepOutArguments ): Promise { try { - const xdebugResponse = await this._connection.sendStepOutCommand(); + const xdebugResponse = await this._connection!.sendStepOutCommand(); this.sendResponse(response); this._checkStatus(xdebugResponse); } catch (error) { @@ -949,7 +948,7 @@ export class ObjectScriptDebugSession extends LoggingDebugSession { args: DebugProtocol.EvaluateArguments ): Promise { try { - const { result } = await this._connection.sendEvalCommand(args.expression); + const { result } = await this._connection!.sendEvalCommand(args.expression); if (result) { const displayValue = formatPropertyValue(result); let variablesReference: number; @@ -976,18 +975,18 @@ export class ObjectScriptDebugSession extends LoggingDebugSession { ): Promise { try { const { value, name, variablesReference } = args; - let property = null; + let property: xdebug.Property | null | undefined = null; if (this._contexts.has(variablesReference)) { // VS Code is requesting the variables for a SCOPE, so we have to do a context_get const context = this._contexts.get(variablesReference); - const properties = await context.getProperties(); + const properties = await context!.getProperties(); property = properties.find((el) => el.name === name); } else if (this._properties.has(variablesReference)) { // VS Code is requesting the subelements for a variable, so we have to do a property_get property = this._properties.get(variablesReference); } - property.value = value; - await this._connection.sendPropertySetCommand(property); + property!.value = value; + await this._connection!.sendPropertySetCommand(property!); response.body = { value: args.value, diff --git a/src/debug/utils.ts b/src/debug/utils.ts index 3ba3a3b3..54718ec3 100644 --- a/src/debug/utils.ts +++ b/src/debug/utils.ts @@ -15,7 +15,7 @@ export function formatPropertyValue(property: BaseProperty): string { } } else { // for null, uninitialized, resource, etc. show the type - displayValue = property.value || property.type === "string" ? property.value : property.type; + displayValue = property.value || property.type === "string" ? property.value! : property.type; if (property.type === "string") { displayValue = '"' + displayValue + '"'; } else if (property.type === "bool") { diff --git a/src/debug/xdebugConnection.ts b/src/debug/xdebugConnection.ts index bc513045..d09b121b 100644 --- a/src/debug/xdebugConnection.ts +++ b/src/debug/xdebugConnection.ts @@ -24,11 +24,11 @@ export class InitPacket { */ public constructor(document: XMLDocument, connection: Connection) { const documentElement = document.documentElement; - this.fileUri = documentElement.getAttribute("fileuri"); - this.language = documentElement.getAttribute("language"); - this.protocolVersion = documentElement.getAttribute("protocol_version"); - this.ideKey = documentElement.getAttribute("idekey"); - this.engineVersion = documentElement.getElementsByTagName("engine")[0].getAttribute("version"); + this.fileUri = documentElement.getAttribute("fileuri")!; + this.language = documentElement.getAttribute("language")!; + this.protocolVersion = documentElement.getAttribute("protocol_version")!; + this.ideKey = documentElement.getAttribute("idekey")!; + this.engineVersion = documentElement.getElementsByTagName("engine")[0].getAttribute("version")!; this.connection = connection; } } @@ -62,12 +62,12 @@ export class Response { const documentElement = document.documentElement; if (documentElement.firstChild && documentElement.firstChild.nodeName === "error") { const errorNode = documentElement.firstChild as Element; - const code = parseInt(errorNode.getAttribute("code"), 10); - const message = errorNode.textContent; + const code = parseInt(errorNode.getAttribute("code")!, 10); + const message = errorNode.textContent!; throw new XDebugError(message, code); } - this.transactionId = parseInt(documentElement.getAttribute("transaction_id"), 10); - this.command = documentElement.getAttribute("command"); + this.transactionId = parseInt(documentElement.getAttribute("transaction_id")!, 10); + this.command = documentElement.getAttribute("command")!; this.connection = connection; } } @@ -91,24 +91,24 @@ export class StatusResponse extends Response { public constructor(document: XMLDocument, connection: Connection) { super(document, connection); const documentElement = document.documentElement; - this.status = documentElement.getAttribute("status"); - this.reason = documentElement.getAttribute("reason"); + this.status = documentElement.getAttribute("status")!; + this.reason = documentElement.getAttribute("reason")!; if (documentElement.hasChildNodes()) { const messageNode = documentElement.firstChild as Element; if (messageNode.hasAttribute("exception")) { this.exception = { - message: messageNode.textContent, - name: messageNode.getAttribute("exception"), + message: messageNode.textContent!, + name: messageNode.getAttribute("exception")!, }; if (messageNode.hasAttribute("code")) { - this.exception.code = parseInt(messageNode.getAttribute("code"), 10); + this.exception.code = parseInt(messageNode.getAttribute("code")!, 10); } } if (messageNode.hasAttribute("filename")) { - this.fileUri = messageNode.getAttribute("filename"); + this.fileUri = messageNode.getAttribute("filename")!; } if (messageNode.hasAttribute("lineno")) { - this.line = parseInt(messageNode.getAttribute("lineno"), 10); + this.line = parseInt(messageNode.getAttribute("lineno")!, 10); } } } @@ -121,7 +121,7 @@ export type BreakpointState = "enabled" | "disabled"; export abstract class Breakpoint { /** dynamically detects the type of breakpoint and returns the appropiate object */ public static fromXml(breakpointNode: Element, connection: Connection): Breakpoint { - switch (breakpointNode.getAttribute("type")) { + switch (breakpointNode.getAttribute("type")!) { case "line": return new LineBreakpoint(breakpointNode, connection); case "conditional": @@ -129,7 +129,7 @@ export abstract class Breakpoint { case "watch": return new Watchpoint(breakpointNode, connection); default: - throw new Error(`Invalid type ${breakpointNode.getAttribute("type")}`); + throw new Error(`Invalid type ${breakpointNode.getAttribute("type")!}`); } } /** Unique ID which is used for modifying the breakpoint (only when received through breakpoint_list) */ @@ -151,9 +151,9 @@ export abstract class Breakpoint { // from XML const breakpointNode: Element = rest[0]; this.connection = rest[1]; - this.type = breakpointNode.getAttribute("type") as BreakpointType; - this.id = parseInt(breakpointNode.getAttribute("id"), 10); - this.state = breakpointNode.getAttribute("state") as BreakpointState; + this.type = breakpointNode.getAttribute("type")! as BreakpointType; + this.id = parseInt(breakpointNode.getAttribute("id")!, 10); + this.state = breakpointNode.getAttribute("state")! as BreakpointState; } else { this.type = rest[0]; if (rest[1] !== undefined) { @@ -183,8 +183,8 @@ export class LineBreakpoint extends Breakpoint { const breakpointNode: Element = rest[0]; const connection: Connection = rest[1]; super(breakpointNode, connection); - this.line = parseInt(breakpointNode.getAttribute("lineno"), 10); - this.fileUri = breakpointNode.getAttribute("filename"); + this.line = parseInt(breakpointNode.getAttribute("lineno")!, 10); + this.fileUri = breakpointNode.getAttribute("filename")!; } else { // construct from arguments super("line", rest[2]); @@ -205,8 +205,8 @@ export class ClassLineBreakpoint extends LineBreakpoint { const breakpointNode: Element = rest[0]; const connection: Connection = rest[1]; super(breakpointNode, connection); - this.line = parseInt(breakpointNode.getAttribute("lineno"), 10); - this.fileUri = breakpointNode.getAttribute("filename"); + this.line = parseInt(breakpointNode.getAttribute("lineno")!, 10); + this.fileUri = breakpointNode.getAttribute("filename")!; } else { super(rest[0], rest[1], rest[4]); this.method = rest[2]; @@ -226,8 +226,8 @@ export class RoutineLineBreakpoint extends LineBreakpoint { const breakpointNode: Element = rest[0]; const connection: Connection = rest[1]; super(breakpointNode, connection); - this.line = parseInt(breakpointNode.getAttribute("lineno"), 10); - this.fileUri = breakpointNode.getAttribute("filename"); + this.line = parseInt(breakpointNode.getAttribute("lineno")!, 10); + this.fileUri = breakpointNode.getAttribute("filename")!; } else { super(rest[0], rest[1], rest[4]); this.method = rest[2]; @@ -254,7 +254,7 @@ export class ConditionalBreakpoint extends Breakpoint { const breakpointNode: Element = rest[0]; const connection: Connection = rest[1]; super(breakpointNode, connection); - this.expression = breakpointNode.getAttribute("expression"); // Base64 encoded? + this.expression = breakpointNode.getAttribute("expression")!; // Base64 encoded? } else { // from arguments super("conditional", rest[3]); @@ -283,7 +283,7 @@ export class ClassConditionalBreakpoint extends ConditionalBreakpoint { const breakpointNode: Element = rest[0]; const connection: Connection = rest[1]; super(breakpointNode, connection); - this.expression = breakpointNode.getAttribute("expression"); // Base64 encoded? + this.expression = breakpointNode.getAttribute("expression")!; // Base64 encoded? } else { super(rest[0], rest[1], rest[2], rest[5]); this.method = rest[3]; @@ -310,7 +310,7 @@ export class RoutineConditionalBreakpoint extends ConditionalBreakpoint { const breakpointNode: Element = rest[0]; const connection: Connection = rest[1]; super(breakpointNode, connection); - this.expression = breakpointNode.getAttribute("expression"); // Base64 encoded? + this.expression = breakpointNode.getAttribute("expression")!; // Base64 encoded? } else { super(rest[0], rest[1], rest[2], rest[5]); this.method = rest[3]; @@ -335,7 +335,7 @@ export class Watchpoint extends Breakpoint { const breakpointNode: Element = rest[0]; const connection: Connection = rest[1]; super(breakpointNode, connection); - const expr = breakpointNode.getAttribute("expression"); // Base64 encoded? + const expr = breakpointNode.getAttribute("expression")!; // Base64 encoded? if (expr.includes("|")) { this.variable = expr.slice(0, expr.indexOf("|")); this.expression = expr.slice(expr.indexOf("|") + 1); @@ -356,7 +356,7 @@ export class BreakpointSetResponse extends Response { public breakpointId: number; public constructor(document: XMLDocument, connection: Connection) { super(document, connection); - this.breakpointId = parseInt(document.documentElement.getAttribute("id"), 10); + this.breakpointId = parseInt(document.documentElement.getAttribute("id")!, 10); } } @@ -405,14 +405,14 @@ export class StackFrame { * @param {Connection} connection */ public constructor(stackFrameNode: Element, connection: Connection) { - this.method = iconv.encode(stackFrameNode.getAttribute("method"), ENCODING) + ""; - this.fileUri = iconv.encode(stackFrameNode.getAttribute("filename"), ENCODING) + ""; - this.type = stackFrameNode.getAttribute("type"); - this.line = parseInt(stackFrameNode.getAttribute("lineno"), 10); - this.methodOffset = parseInt(stackFrameNode.getAttribute("methodoffset"), 10); - this.level = parseInt(stackFrameNode.getAttribute("level"), 10); - const cmdBegin = stackFrameNode.getAttribute("cmdbegin"); - const cmdEnd = stackFrameNode.getAttribute("cmdend"); + this.method = iconv.encode(stackFrameNode.getAttribute("method")!, ENCODING) + ""; + this.fileUri = iconv.encode(stackFrameNode.getAttribute("filename")!, ENCODING) + ""; + this.type = stackFrameNode.getAttribute("type")!; + this.line = parseInt(stackFrameNode.getAttribute("lineno")!, 10); + this.methodOffset = parseInt(stackFrameNode.getAttribute("methodoffset")!, 10); + this.level = parseInt(stackFrameNode.getAttribute("level")!, 10); + const cmdBegin = stackFrameNode.getAttribute("cmdbegin")!; + const cmdEnd = stackFrameNode.getAttribute("cmdend")!; if (cmdBegin && cmdEnd) { const [cmdBeginLine, cmdBeginPos] = cmdBegin.split(":"); const [cmdEndLine, cmdEndPos] = cmdEnd.split(":"); @@ -449,7 +449,7 @@ export class SourceResponse extends Response { public source: string; public constructor(document: XMLDocument, connection: Connection) { super(document, connection); - this.source = Buffer.from(document.documentElement.textContent, "base64").toString(); + this.source = Buffer.from(document.documentElement.textContent!, "base64").toString(); } } @@ -466,8 +466,8 @@ export class Context { * @param {StackFrame} stackFrame */ public constructor(contextNode: Element, stackFrame: StackFrame) { - this.id = parseInt(contextNode.getAttribute("id"), 10); - this.name = contextNode.getAttribute("name"); + this.id = parseInt(contextNode.getAttribute("id")!, 10); + this.name = contextNode.getAttribute("name")!; this.stackFrame = stackFrame; } /** @@ -511,27 +511,27 @@ export abstract class BaseProperty { /** the number of children this property has, if any. Useful for showing array length. */ public numberOfChildren: number; /** the value of the property for primitive types */ - public value: string; + public value: string | undefined; /** children that were already included in the response */ public children: BaseProperty[]; public constructor(propertyNode: Element) { if (propertyNode.hasAttribute("name")) { - this.name = iconv.encode(propertyNode.getAttribute("name"), ENCODING) + ""; + this.name = iconv.encode(propertyNode.getAttribute("name")!, ENCODING) + ""; } - this.type = propertyNode.getAttribute("type"); + this.type = propertyNode.getAttribute("type")!; if (propertyNode.hasAttribute("classname")) { - this.class = propertyNode.getAttribute("classname"); + this.class = propertyNode.getAttribute("classname")!; } - this.hasChildren = !!parseInt(propertyNode.getAttribute("children"), 10); + this.hasChildren = !!parseInt(propertyNode.getAttribute("children")!, 10); if (this.hasChildren) { - this.numberOfChildren = parseInt(propertyNode.getAttribute("numchildren"), 10); + this.numberOfChildren = parseInt(propertyNode.getAttribute("numchildren")!, 10); } else { - const encoding = propertyNode.getAttribute("encoding"); + const encoding = propertyNode.getAttribute("encoding")!; if (encoding && encoding !== "none") { - this.value = iconv.encode(propertyNode.textContent, encoding) + ""; + this.value = iconv.encode(propertyNode.textContent!, encoding) + ""; } else { - this.value = iconv.encode(propertyNode.textContent, ENCODING) + ""; + this.value = iconv.encode(propertyNode.textContent!, ENCODING) + ""; } } if (this.value === "") { @@ -559,7 +559,7 @@ export class Property extends BaseProperty { */ public constructor(propertyNode: Element, context: Context) { super(propertyNode); - this.fullName = propertyNode.getAttribute("fullname"); + this.fullName = propertyNode.getAttribute("fullname")!; this.context = context; if (this.hasChildren) { this.children = Array.from(propertyNode.childNodes).map( @@ -605,7 +605,7 @@ export class PropertyGetResponse extends Response { */ public constructor(document: XMLDocument, property: Property) { super(document, property.context.stackFrame.connection); - this.children = Array.from(document.documentElement.firstChild.childNodes).map( + this.children = Array.from(document.documentElement.firstChild!.childNodes).map( (propertyNode: Element): Property => new Property(propertyNode, property.context) ); } @@ -662,7 +662,7 @@ export class FeatureSetResponse extends Response { public feature: string; public constructor(document: XMLDocument, connection: Connection) { super(document, connection); - this.feature = document.documentElement.getAttribute("feature"); + this.feature = document.documentElement.getAttribute("feature")!; } } @@ -673,8 +673,8 @@ export class FeatureGetResponse extends Response { public supported: boolean; public constructor(document: XMLDocument, connection: Connection) { super(document, connection); - this.feature = document.documentElement.getAttribute("feature"); - this.supported = document.documentElement.getAttribute("supported") === "1"; + this.feature = document.documentElement.getAttribute("feature")!; + this.supported = document.documentElement.getAttribute("supported")! === "1"; } } @@ -754,20 +754,20 @@ export class Connection extends DbgpConnection { if (response.documentElement.nodeName === "init") { this._initPromiseResolveFn(new InitPacket(response, this)); } else { - const transactionId = parseInt(response.documentElement.getAttribute("transaction_id"), 10); + const transactionId = parseInt(response.documentElement.getAttribute("transaction_id")!, 10); if (this._pendingCommands.has(transactionId)) { - const command = this._pendingCommands.get(transactionId); + const command = this._pendingCommands.get(transactionId)!; this._pendingCommands.delete(transactionId); this._pendingExecuteCommand = false; command.resolveFn(response); } if (this._commandQueue.length > 0) { - const command = this._commandQueue.shift(); + const command = this._commandQueue.shift()!; this._executeCommand(command).catch(command.rejectFn); } if (response.documentElement.nodeName === "stream") { - const type = response.documentElement.getAttribute("type"); - const data = Buffer.from(response.documentElement.textContent, "base64").toString(); + const type = response.documentElement.getAttribute("type")!; + const data = Buffer.from(response.documentElement.textContent!, "base64").toString(); this.sendEvent(type, data); } } @@ -976,7 +976,7 @@ export class Connection extends DbgpConnection { /** Sends a property_get command */ public async sendPropertySetCommand(property: Property): Promise { - const value = Buffer.from(property.value).toString("base64"); + const value = Buffer.from(property.value!).toString("base64"); return new PropertySetResponse( await this._enqueueCommand( "property_set", diff --git a/src/explorer/explorer.ts b/src/explorer/explorer.ts index 22efd95c..5a905f51 100644 --- a/src/explorer/explorer.ts +++ b/src/explorer/explorer.ts @@ -75,7 +75,7 @@ export function registerExplorerOpen(): vscode.Disposable { } // Remove the item from the project let prjFileName = fullName.startsWith("/") ? fullName.slice(1) : fullName; - const ext = prjFileName.split(".").pop().toLowerCase(); + const ext = prjFileName.split(".").pop()!.toLowerCase(); prjFileName = ext == "cls" ? prjFileName.slice(0, -4) : prjFileName; const prjType = prjFileName.includes("/") ? "CSP" @@ -135,13 +135,13 @@ function wasDoubleClick(uri: vscode.Uri): boolean { export class ObjectScriptExplorerProvider implements vscode.TreeDataProvider { public onDidChange?: vscode.Event; - public onDidChangeTreeData: vscode.Event; + public onDidChangeTreeData: vscode.Event; - private _onDidChangeTreeData: vscode.EventEmitter; + private _onDidChangeTreeData: vscode.EventEmitter; private _showExtraForWorkspace: { [key: string]: string[] }[] = []; public constructor() { - this._onDidChangeTreeData = new vscode.EventEmitter(); + this._onDidChangeTreeData = new vscode.EventEmitter(); this.onDidChangeTreeData = this._onDidChangeTreeData.event; } @@ -173,7 +173,7 @@ export class ObjectScriptExplorerProvider implements vscode.TreeDataProvider= 0) { diff --git a/src/explorer/nodes.ts b/src/explorer/nodes.ts index 05a77fcb..9bb64ecb 100644 --- a/src/explorer/nodes.ts +++ b/src/explorer/nodes.ts @@ -15,7 +15,7 @@ interface NodeOptions { /** Get the URI for this leaf node */ function getLeafNodeUri(node: NodeBase): vscode.Uri { - return DocumentContentProvider.getUri(node.fullName, undefined, node?.namespace, false, node.wsFolder.uri, true); + return DocumentContentProvider.getUri(node.fullName, undefined, node?.namespace, false, node.wsFolder.uri, true)!; } const inactiveMsg = "Server connection is inactive"; @@ -61,7 +61,7 @@ export class RootNode extends NodeBase { public readonly contextValue: string; private readonly _category: string; private readonly isCsp: boolean; - private readonly iconPath: vscode.ThemeIcon; + private readonly iconPath: vscode.ThemeIcon | undefined; public constructor( label: string, @@ -97,7 +97,7 @@ export class RootNode extends NodeBase { const path = this instanceof PackageNode || this.isCsp ? this.fullName + "/" : ""; return this.getList(path, this._category, false) .then((data) => - data + data! .filter((el) => { if (this._category === "OTH") { return el.Type === "100"; @@ -148,7 +148,7 @@ export class RootNode extends NodeBase { path: string, category: string, flat: boolean - ): Promise<{ Name: string; Type: string; fullName: string }[]> { + ): Promise<{ Name: string; Type: string; fullName: string }[] | undefined> { const sql = "SELECT Name, Type FROM %Library.RoutineMgr_StudioOpenDialog(?,?,?,?,?,?,?)"; let spec = ""; switch (category) { @@ -192,9 +192,9 @@ export class RootNode extends NodeBase { let nsCspApps: string[] | undefined = cspApps.get(cspAppsKey); if (nsCspApps == undefined) { nsCspApps = await api.getCSPApps().then((data) => data.result.content || []); - cspApps.set(cspAppsKey, nsCspApps); + cspApps.set(cspAppsKey, nsCspApps!); } - return nsCspApps.map((cspApp) => { + return nsCspApps!.map((cspApp) => { return { Name: cspApp.slice(1), fullName: cspApp.slice(1), Type: "10" }; }); } else { @@ -226,7 +226,7 @@ export class RootNode extends NodeBase { public getItemsForExport(): Promise { const path = this instanceof PackageNode || this.isCsp ? this.fullName + "/" : ""; const cat = this.isCsp ? "CSP" : "ALL"; - return this.getList(path, cat, true).then((data) => data.map((el) => el.Name)); + return this.getList(path, cat, true).then((data) => data!.map((el) => el.Name)); } } @@ -324,9 +324,9 @@ export class RoutineNode extends NodeBase { } export class WorkspaceNode extends NodeBase { - public eventEmitter: vscode.EventEmitter; + public eventEmitter: vscode.EventEmitter; public uniqueId: string; - public constructor(label: string, eventEmitter: vscode.EventEmitter, options: NodeOptions) { + public constructor(label: string, eventEmitter: vscode.EventEmitter, options: NodeOptions) { super(label, label, options); this.uniqueId = `serverNode:${this.namespace}:${this.namespace ? ":extra:" : ""}`; this.options.generated = workspaceState.get(`ExplorerGenerated:${this.uniqueId}`); @@ -335,7 +335,7 @@ export class WorkspaceNode extends NodeBase { } public getTreeItem(): vscode.TreeItem { - const flags = []; + const flags: string[] = []; this.options.generated && flags.push(":generated:"); this.options.system && flags.push(":system:"); const api = new AtelierAPI(this.wsFolder.uri); @@ -356,7 +356,7 @@ export class WorkspaceNode extends NodeBase { public async getChildren(_element: NodeBase): Promise { if (!new AtelierAPI(this.wsFolder.uri).active) return [new InactiveNode("", "", { wsFolder: this.wsFolder })]; - const children = []; + const children: RootNode[] = []; let node: RootNode; node = new RootNode( @@ -426,7 +426,7 @@ export class ProjectNode extends NodeBase { } public async getChildren(_element: NodeBase): Promise { - const children = []; + const children: ProjectRootNode[] = []; let node: ProjectRootNode; // Technically a project is a "document", so tell the server that we're opening it @@ -523,9 +523,9 @@ export class ProjectRootNode extends RootNode { "SELECT DISTINCT $PIECE(SUBSTR(sod.Name,?+1),'/') AS Name FROM %Library.RoutineMgr_StudioOpenDialog('*.cspall',1,1,1,1,0,1) AS sod " + "JOIN %Studio.Project_ProjectItemsList(?,1) AS pil ON SUBSTR(sod.Name,2) %STARTSWITH ? AND (" + "(pil.Type = 'DIR' AND SUBSTR(sod.Name,2) %STARTSWITH pil.Name||'/') OR (pil.Type = 'CSP' AND SUBSTR(sod.Name,2) = pil.Name))"; - parameters = [l, this.options.project, this.fullName + "/"]; + parameters = [l, this.options.project!, this.fullName + "/"]; } else { - parameters = [l, l, l, this.options.project, this.fullName + "."]; + parameters = [l, l, l, this.options.project!, this.fullName + "."]; if (this.category == "CLS") { query = "SELECT DISTINCT CASE " + @@ -556,7 +556,7 @@ export class ProjectRootNode extends RootNode { "WHEN Type = 'CSP' OR Type = 'DIR' THEN $PIECE(Name,'/') " + "WHEN (Type != 'CSP' AND Type != 'DIR' AND $LENGTH(Name,'.') > 2) OR Type = 'CLS' OR Type = 'PKG' THEN $PIECE(Name,'.') " + "ELSE Name END Name FROM %Studio.Project_ProjectItemsList(?,1) WHERE "; - parameters = [this.options.project]; + parameters = [this.options.project!]; if (this.category == "CLS") { query += "Type = 'PKG' OR Type = 'CLS'"; } else if (this.category == "RTN") { @@ -594,7 +594,7 @@ export class ProjectRootNode extends RootNode { } } else { if (entry.includes(".")) { - if (["mac", "int", "inc"].includes(entry.split(".").pop().toLowerCase())) { + if (["mac", "int", "inc"].includes(entry.split(".").pop()!.toLowerCase())) { return new RoutineNode(entry, fullName, this.options); } else { return new ClassNode(entry, fullName, this.options); @@ -613,11 +613,11 @@ export class ProjectRootNode extends RootNode { } export class ProjectsServerNsNode extends NodeBase { - public eventEmitter: vscode.EventEmitter; + public eventEmitter: vscode.EventEmitter; public constructor( label: string, - eventEmitter: vscode.EventEmitter, + eventEmitter: vscode.EventEmitter, wsFolder: vscode.WorkspaceFolder, namespace?: string ) { diff --git a/src/explorer/projectsExplorer.ts b/src/explorer/projectsExplorer.ts index 1dcfec2e..b6c421e1 100644 --- a/src/explorer/projectsExplorer.ts +++ b/src/explorer/projectsExplorer.ts @@ -4,8 +4,8 @@ import { handleError, notIsfs, notNull } from "../utils"; import { NodeBase, ProjectsServerNsNode } from "./nodes"; export class ProjectsExplorerProvider implements vscode.TreeDataProvider { - public onDidChangeTreeData: vscode.Event; - private _onDidChangeTreeData: vscode.EventEmitter; + public onDidChangeTreeData: vscode.Event; + private _onDidChangeTreeData: vscode.EventEmitter; /** Connection info for all workspace folder roots */ private readonly _roots: string[] = []; @@ -13,7 +13,7 @@ export class ProjectsExplorerProvider implements vscode.TreeDataProvider(); + this._onDidChangeTreeData = new vscode.EventEmitter(); this.onDidChangeTreeData = this._onDidChangeTreeData.event; } @@ -60,7 +60,7 @@ export class ProjectsExplorerProvider implements vscode.TreeDataProvider(); -type ConnConfig = Pick & { +type ConnConfig = Pick & { "docker-compose"?: any; server?: any; links?: any; + username?: string; + password?: string; }; export function config(setting: "conn", workspaceFolderName?: string): ConnConfig; @@ -175,7 +178,7 @@ export function config(setting?: string, workspaceFolderName?: string): any { } let prefix: string; const workspaceFolder = vscode.workspace.workspaceFolders?.find( - (el) => el.name.toLowerCase() === workspaceFolderName.toLowerCase() + (el) => el.name.toLowerCase() === workspaceFolderName!.toLowerCase() ); if (setting && setting.startsWith("intersystems")) { return vscode.workspace.getConfiguration(setting, workspaceFolder); @@ -183,22 +186,21 @@ export function config(setting?: string, workspaceFolderName?: string): any { prefix = "objectscript"; } - if (["conn", "export"].includes(setting)) { + if (["conn", "export"].includes(setting!)) { if (workspaceFolderName && workspaceFolderName !== "") { if (workspaceFolderName.match(/.+:\d+$/)) { const { port, hostname: host, auth, query } = url.parse("http://" + workspaceFolderName, true); const { ns = "USER", https = false } = query; const [username, password] = (auth || "_SYSTEM:SYS").split(":"); - const authorization = serverManagerApi.defaultAuth(); - authorization.resolve({ username, accessToken: password }); if (setting == "conn") { return { active: true, https, ns, host, - port: +port, - auth: authorization, + port: +port!, + username, + password, } as ConnConfig; } else if (setting == "export") { return {}; @@ -210,7 +212,7 @@ export function config(setting?: string, workspaceFolderName?: string): any { return setting && setting.length ? result.get(setting) : result; } -let reporter: TelemetryReporter; +let reporter: TelemetryReporter | undefined; export let checkingConnection = false; @@ -247,8 +249,16 @@ export async function resolveConnectionSpec( return; } } - - let connSpec = await serverManagerApi.getServerSpec(serverName, scope); + const rawConnSpec = await serverManagerApi.getServerSpec(serverName, scope); + let connSpec; + if (rawConnSpec) { + connSpec = { + ...rawConnSpec, + // Some old server managers does not set name as the types suggest. + name: rawConnSpec.name ?? serverName, + auth: rawConnSpec.auth ?? new BasicAuthorization(rawConnSpec.username, rawConnSpec.password), + }; + } if (!connSpec && uri) { // Caller passed uri as a signal to process any docker-compose settings @@ -265,10 +275,10 @@ export async function resolveConnectionSpec( pathPrefix: serverForUri.pathPrefix, }, superServer: { - port: serverForUri.superserverPort, + port: serverForUri.superserverPort!, }, description: `Server for workspace folder '${serverName}'`, - auth: serverManagerApi.defaultAuth(), + auth: new BasicAuthorization(), }; } } @@ -277,7 +287,7 @@ export async function resolveConnectionSpec( if (connSpec) { const accessToken = await resolvePassword(connSpec); if (connSpec.auth.resolve({ accessToken })) { - resolvedConnSpecs.set(serverName, connSpec); + resolvedConnSpecs.set(serverName, connSpec as ResolvedConnSpec); } } } @@ -286,7 +296,7 @@ async function resolvePassword( serverSpec: serverManager.IServerSpec, ignoreUnauthenticated = false ): Promise { - if (!(serverSpec.auth.resolved() as boolean) || ignoreUnauthenticated) { + if (!serverSpec.auth?.resolved() || ignoreUnauthenticated) { const scopes = [serverSpec.name, serverSpec.auth?.username || ""]; // Handle Server Manager extension version < 3.8.0 @@ -329,14 +339,17 @@ export async function resolveUsernameAndPassword( } /** Accessor for the cache of resolved connection specs */ -export function getResolvedConnectionSpec(key: string, dflt: ResolvedConnSpec): ResolvedConnSpec { +export function getResolvedConnectionSpec( + key: string, + dflt: ResolvedConnSpec | undefined +): ResolvedConnSpec | undefined { let spec = resolvedConnSpecs.get(key); if (spec) { return spec; } // Try a case-insensitive match - key = resolvedConnSpecs.keys().find((oneKey) => oneKey.toLowerCase() === key.toLowerCase()); + key = resolvedConnSpecs.keys().find((oneKey) => oneKey.toLowerCase() === key.toLowerCase())!; if (key) { spec = resolvedConnSpecs.get(key); if (spec) { @@ -496,7 +509,7 @@ export async function checkConnection( api.config.serverName, vscode.workspace.getConfiguration("intersystems.servers", uri).get(api.config.serverName) ); - const newSpec = await resolveUsernameAndPassword(api.config.serverName, oldSpec); + const newSpec = await resolveUsernameAndPassword(api.config.serverName, oldSpec!); if (newSpec) { // We were able to resolve credentials, so try again await workspaceState.update(wsKey + ":password", newSpec.auth?.accessToken); @@ -597,15 +610,15 @@ export async function checkConnection( */ function setConnectionState(configName: string, active: boolean) { const connConfig: vscode.WorkspaceConfiguration = config("", configName); - const target: vscode.ConfigurationTarget = connConfig.inspect("conn").workspaceFolderValue + const target: vscode.ConfigurationTarget = connConfig.inspect("conn")!.workspaceFolderValue ? vscode.ConfigurationTarget.WorkspaceFolder : vscode.ConfigurationTarget.Workspace; const targetConfig: any = - connConfig.inspect("conn").workspaceFolderValue || connConfig.inspect("conn").workspaceValue; + connConfig.inspect("conn")!.workspaceFolderValue || connConfig.inspect("conn")!.workspaceValue; return connConfig.update("conn", { ...targetConfig, active }, target); } -function languageServer(install = true): vscode.Extension { +function languageServer(install = true): vscode.Extension | undefined { let extension = vscode.extensions.getExtension(lsExtensionId); async function languageServerInstall() { @@ -681,7 +694,7 @@ function proposedApiPrompt(active: boolean, added?: readonly vscode.WorkspaceFol const systemModes: Map = new Map(); /** Output a message notifying the user of the SystemMode of any servers they are connected to. */ -async function systemModeWarning(wsFolders: readonly vscode.WorkspaceFolder[]): Promise { +async function systemModeWarning(wsFolders: readonly vscode.WorkspaceFolder[] | undefined): Promise { if (!wsFolders || wsFolders.length == 0) return; for (const wsFolder of wsFolders) { const api = new AtelierAPI(wsFolder.uri), @@ -714,7 +727,7 @@ async function systemModeWarning(wsFolders: readonly vscode.WorkspaceFolder[]): ); outputChannel.show(true); } - systemModes.set(mapKey, systemMode); + systemModes.set(mapKey, systemMode!); } } @@ -723,7 +736,7 @@ async function systemModeWarning(wsFolders: readonly vscode.WorkspaceFolder[]): * that are showing the contents of a server-side project. * This must be done because technically a project is a "document". */ -async function fireOpenProjectUserAction(wsFolders: readonly vscode.WorkspaceFolder[]): Promise { +async function fireOpenProjectUserAction(wsFolders: readonly vscode.WorkspaceFolder[] | undefined): Promise { if (!wsFolders || wsFolders.length == 0) return; for (const wsFolder of wsFolders) { if (notIsfs(wsFolder.uri)) return; @@ -759,7 +772,7 @@ function setExplorerContextKeys(): void { } /** Cache the lists of web apps and abstract document types for all server-namespaces in `wsFolders` */ -async function updateWebAndAbstractDocsCaches(wsFolders: readonly vscode.WorkspaceFolder[]): Promise { +async function updateWebAndAbstractDocsCaches(wsFolders: readonly vscode.WorkspaceFolder[] | undefined): Promise { if (!wsFolders?.length) return; const keys: Set = new Set(); const connections: { key: string; api: AtelierAPI }[] = []; @@ -804,14 +817,14 @@ export function sendStudioAddinTelemetryEvent(addInName: string): void { } /** Send a telemetry event with details of each folder in `wsFolders` */ -function sendWsFolderTelemetryEvent(wsFolders: readonly vscode.WorkspaceFolder[], added = false): void { +function sendWsFolderTelemetryEvent(wsFolders: readonly vscode.WorkspaceFolder[] | undefined, added = false): void { if (!reporter || !wsFolders?.length) return; wsFolders.forEach((wsFolder) => { const api = new AtelierAPI(wsFolder.uri); const { csp, project, ns } = isfsConfig(wsFolder.uri); const serverSide = filesystemSchemas.includes(wsFolder.uri.scheme); const conf = vscode.workspace.getConfiguration("objectscript", wsFolder); - reporter.sendTelemetryEvent("workspaceFolder", { + reporter!.sendTelemetryEvent("workspaceFolder", { scheme: wsFolder.uri.scheme, added: String(added), isWeb: serverSide ? String(csp) : undefined, @@ -884,7 +897,7 @@ export async function activate(context: vscode.ExtensionContext): Promise - ["file", ...schemas].reduce((acc, scheme) => acc.concat(list.map((language) => ({ scheme, language }))), []); + ["file", ...schemas].reduce( + (acc, scheme) => acc.concat(list.map((language) => ({ scheme, language }))), + [] as { scheme: string; language: any }[] + ); const diagnosticProvider = new ObjectScriptDiagnosticProvider(); @@ -1070,14 +1086,14 @@ export async function activate(context: vscode.ExtensionContext): Promise { - if (vscode.workspace.workspaceFolders?.length > 1) { + if (vscode.workspace.workspaceFolders!.length > 1) { const workspaceFolder = currentWorkspaceFolder(); if (workspaceFolder && workspaceFolder != workspaceState.get("workspaceFolder")) { await workspaceState.update("workspaceFolder", workspaceFolder); @@ -1214,7 +1230,7 @@ export async function activate(context: vscode.ExtensionContext): Promise { sendCommandTelemetryEvent("pickProcess"); const system = config.system; - let connectionUri = vscode.window.activeTextEditor?.document.uri; + let connectionUri: vscode.Uri | null | undefined = vscode.window.activeTextEditor?.document.uri; if (connectionUri) { // Ignore active editor if its document is outside the workspace (e.g. user settings.json) connectionUri = vscode.workspace.getWorkspaceFolder(connectionUri)?.uri; @@ -1279,8 +1295,8 @@ export async function activate(context: vscode.ExtensionContext): Promise { if (value) { - const workspaceFolderIndex = vscode.workspace.workspaceFolders.findIndex( - (folder) => folder.uri.toString() === connectionUri.toString() + const workspaceFolderIndex = vscode.workspace.workspaceFolders!.findIndex( + (folder) => folder.uri.toString() === connectionUri!.toString() ); return workspaceFolderIndex < 0 ? value.label : `${value.label}@${workspaceFolderIndex}`; } @@ -1368,7 +1384,7 @@ export async function activate(context: vscode.ExtensionContext): Promise { sendCommandTelemetryEvent("previewXml"); - previewXMLAsUDL(vscode.window.activeTextEditor); + previewXMLAsUDL(vscode.window.activeTextEditor!); }), vscode.commands.registerCommand("vscode-objectscript.addServerNamespaceToWorkspace", (resource?: vscode.Uri) => { sendCommandTelemetryEvent("addServerNamespaceToWorkspace"); @@ -1445,7 +1461,7 @@ export async function activate(context: vscode.ExtensionContext): Promise { @@ -1926,7 +1942,7 @@ export async function activate(context: vscode.ExtensionContext): Promise { // Log out of all CSP sessions await logoutOfSessions(); } + +// A copy of the BasicAuthorization class from ServerManager +// We use it to patch older version of getServerSpec. +export default class BasicAuthorization implements Authorization { + #username?: string; + #password?: string; + constructor(username?: string, password?: string) { + this.#username = username; + this.#password = password; + } + + public get username(): string { + return this.#username || ""; + } + + public get password(): string | undefined { + return this.#password; + } + + public get accessToken(): string | undefined { + return this.#password; + } + + public get httpAuthorizationHeader(): string { + return `Basic ${Buffer.from(`${this.#username}:${this.#password}`).toString("base64")}`; + } + + public resolved(): this is ResolvedAuthorization { + return this.username !== "" && this.#password !== undefined; + } + + public resolve(param: { accessToken: string; username?: string }): this is ResolvedAuthorization { + this.#username = param.username ?? this.#username; + this.#password = param.accessToken ?? this.#password; + return this.resolved(); + } + + public clear(): asserts this is Authorization { + this.#password = undefined; + } + + public get credentials(): { auth: { username: string; password: string }; headers?: Record } { + return { + auth: { + username: this.username, + password: this.password!, + }, + headers: {}, + }; + } + + public clone(): BasicAuthorization { + return new BasicAuthorization(this.#username, this.#password); + } +} diff --git a/src/providers/DocumentContentProvider.ts b/src/providers/DocumentContentProvider.ts index 6e314b2a..edf9d8f1 100644 --- a/src/providers/DocumentContentProvider.ts +++ b/src/providers/DocumentContentProvider.ts @@ -45,9 +45,9 @@ export class DocumentContentProvider implements vscode.TextDocumentContentProvid } /** Returns the `Uri` of `name` in `workspaceFolder` if it exists */ - private static findLocalUri(name: string, workspaceFolder: string): vscode.Uri { + private static findLocalUri(name: string, workspaceFolder: string | undefined): vscode.Uri | undefined { if (!workspaceFolder) return; - const wsFolder = vscode.workspace.workspaceFolders.find((wf) => wf.name == workspaceFolder); + const wsFolder = vscode.workspace.workspaceFolders!.find((wf) => wf.name == workspaceFolder); if (!wsFolder) return; if (!notIsfs(wsFolder.uri)) return; const conf = vscode.workspace.getConfiguration("objectscript.export", wsFolder); @@ -121,7 +121,7 @@ export class DocumentContentProvider implements vscode.TextDocumentContentProvid vfs?: boolean, wFolderUri?: vscode.Uri, forceServerCopy = false - ): vscode.Uri { + ): vscode.Uri | null { let scheme = vfs ? FILESYSTEM_SCHEMA : OBJECTSCRIPT_FILE_SCHEMA; const isCsp = name.includes("/"); @@ -131,8 +131,8 @@ export class DocumentContentProvider implements vscode.TextDocumentContentProvid wFolderUri = uriOfWorkspaceFolder(workspaceFolder); } else if (!workspaceFolder) { // Make sure workspaceFolder is set correctly if only wFolderUri was passed - workspaceFolder = vscode.workspace.workspaceFolders.find( - (wf) => wf.uri.toString() == wFolderUri.toString() + workspaceFolder = vscode.workspace.workspaceFolders!.find( + (wf) => wf.uri.toString() == wFolderUri!.toString() )?.name; } let uri: vscode.Uri; @@ -144,7 +144,7 @@ export class DocumentContentProvider implements vscode.TextDocumentContentProvid namespace = ""; } const params = new URLSearchParams(wFolderUri.query); - const cspParam = params.has(IsfsUriParam.CSP) && ["", "1"].includes(params.get(IsfsUriParam.CSP)); + const cspParam = params.has(IsfsUriParam.CSP) && ["", "1"].includes(params.get(IsfsUriParam.CSP)!); const lastDot = name.lastIndexOf("."); let uriPath = isCsp ? name : name.slice(0, lastDot).replace(/\./g, "/") + "." + name.slice(lastDot + 1); if (!isCsp && /.\.G?[1-9]\.int$/i.test(name)) { @@ -202,7 +202,7 @@ export class DocumentContentProvider implements vscode.TextDocumentContentProvid const fileName = name .split(".") .slice(0, -1) - .join(fileExt.match(/cls/i) ? "/" : "."); + .join(fileExt!.match(/cls/i) ? "/" : "."); name = fileName + "." + fileExt; uri = vscode.Uri.file(name).with({ scheme: scheme, diff --git a/src/providers/DocumentFormattingEditProvider.ts b/src/providers/DocumentFormattingEditProvider.ts index 1f4b24ef..b100a50c 100644 --- a/src/providers/DocumentFormattingEditProvider.ts +++ b/src/providers/DocumentFormattingEditProvider.ts @@ -19,7 +19,7 @@ export class DocumentFormattingEditProvider implements vscode.DocumentFormatting } private commands(document: vscode.TextDocument, options: vscode.FormattingOptions): vscode.TextEdit[] { - const edits = []; + const edits: vscode.TextEdit[] = []; let indent = 1; const isClass = document.fileName.toLowerCase().endsWith(".cls"); @@ -166,8 +166,8 @@ export class DocumentFormattingEditProvider implements vscode.DocumentFormatting } // keep strings and comments - const keepList = []; - const restorePattern = []; + const keepList: string[] = []; + const restorePattern: string[] = []; const toKeep = (str) => { keepList.push(str); restorePattern.push(String.fromCharCode(keepList.length)); @@ -225,13 +225,13 @@ export class DocumentFormattingEditProvider implements vscode.DocumentFormatting } private functions(document: vscode.TextDocument, options: vscode.FormattingOptions): vscode.TextEdit[] { - const edits = []; + const edits: vscode.TextEdit[] = []; for (let i = 0; i < document.lineCount; i++) { const line = document.lineAt(i); const pattern = /(? { + public async resolveDocumentLink( + link: StudioLink, + token: vscode.CancellationToken + ): Promise { const editor = await vscode.window .showTextDocument(link.uri) .then(undefined, (error) => handleError(error, "Failed to resolve DocumentLink to a specific location.")); @@ -63,7 +66,7 @@ export class DocumentLinkProvider implements vscode.DocumentLinkProvider { // add the offset of the method if it is a class if (link.methodname) { - const symbols = await vscode.commands.executeCommand("vscode.executeDocumentSymbolProvider", link.uri); + const symbols: any = await vscode.commands.executeCommand("vscode.executeDocumentSymbolProvider", link.uri); const method = symbols[0].children.find( (info) => (info.detail === "ClassMethod" || info.detail === "Method") && info.name === link.methodname ); diff --git a/src/providers/FileSystemProvider/FileSearchProvider.ts b/src/providers/FileSystemProvider/FileSearchProvider.ts index 5f519c00..1936ebf5 100644 --- a/src/providers/FileSystemProvider/FileSearchProvider.ts +++ b/src/providers/FileSystemProvider/FileSearchProvider.ts @@ -9,7 +9,7 @@ export class FileSearchProvider implements vscode.FileSearchProvider { query: vscode.FileSearchQuery, options: vscode.FileSearchOptions, token: vscode.CancellationToken - ): Promise { + ): Promise { let counter = 0; // Replace all back slashes with forward slashes let pattern = query.pattern.replace(/\\/g, "/"); @@ -28,18 +28,19 @@ export class FileSearchProvider implements vscode.FileSearchProvider { for (const c of pattern) regexStr += `${[".", "/"].includes(c) ? "[./]" : c}.*`; const patternRegex = new RegExp(regexStr, "i"); if (token.isCancellationRequested) return; - return projectContentsFromUri(options.folder, true).then((docs) => - docs - .map((doc: ProjectItem) => - !token.isCancellationRequested && - // The document matches the query - (!pattern.length || patternRegex.test(doc.Name)) && - // We haven't hit the max number of results - (!options.maxResults || ++counter <= options.maxResults) - ? DocumentContentProvider.getUri(doc.Name, "", "", true, options.folder) - : null - ) - .filter(notNull) + return projectContentsFromUri(options.folder, true).then( + (docs) => + docs! + .map((doc: ProjectItem) => + !token.isCancellationRequested && + // The document matches the query + (!pattern.length || patternRegex.test(doc.Name)) && + // We haven't hit the max number of results + (!options.maxResults || ++counter <= options.maxResults) + ? DocumentContentProvider.getUri(doc.Name, "", "", true, options.folder) + : null + ) + .filter(notNull) as vscode.Uri[] ); } // When this is called without a query.pattern every file is supposed to be returned, so do not provide a filter @@ -48,16 +49,17 @@ export class FileSearchProvider implements vscode.FileSearchProvider { ? `Name LIKE '${!csp ? likePattern.replace(/\//g, ".") : likePattern}' ESCAPE '\\'` : ""; if (token.isCancellationRequested) return; - return studioOpenDialogFromURI(options.folder, { flat: true, filter }).then((data) => - data.result.content - .map((doc: { Name: string; Type: number }) => - !token.isCancellationRequested && - // We haven't hit the max number of results - (!options.maxResults || ++counter <= options.maxResults) - ? DocumentContentProvider.getUri(doc.Name, "", "", true, options.folder) - : null - ) - .filter(notNull) + return studioOpenDialogFromURI(options.folder, { flat: true, filter })!.then( + (data) => + data.result.content + .map((doc: { Name: string; Type: number }) => + !token.isCancellationRequested && + // We haven't hit the max number of results + (!options.maxResults || ++counter <= options.maxResults) + ? DocumentContentProvider.getUri(doc.Name, "", "", true, options.folder) + : null + ) + .filter(notNull) as vscode.Uri[] ); } } diff --git a/src/providers/FileSystemProvider/FileSystemProvider.ts b/src/providers/FileSystemProvider/FileSystemProvider.ts index ab296899..0e1f0611 100644 --- a/src/providers/FileSystemProvider/FileSystemProvider.ts +++ b/src/providers/FileSystemProvider/FileSystemProvider.ts @@ -88,7 +88,7 @@ export function generateFileContent( } } - const fileExt = fileName.split(".").pop().toLowerCase(); + const fileExt = fileName.split(".").pop()!.toLowerCase(); const csp = fileName.startsWith("/"); if (fileExt === "cls" && !csp) { const className = fileName.split(".").slice(0, -1).join("."); @@ -104,12 +104,12 @@ export function generateFileContent( // Replace that with one to match fileName. while (sourceLines.length > 0) { const nextLine = sourceLines.shift(); - const classNameMatch = nextLine.match(classNameRegex); + const classNameMatch = nextLine!.match(classNameRegex); if (classNameMatch) { - content.push(...preamble, nextLine.replace(classNameMatch[1], fileName.slice(0, -4)), ...sourceLines); + content.push(...preamble, nextLine!.replace(classNameMatch[1], fileName.slice(0, -4)), ...sourceLines); break; } - preamble.push(nextLine); + preamble.push(nextLine!); } if (!content.length) { // Transfer sourceLines verbatim in cases where no class header line is found @@ -192,12 +192,12 @@ export function isCSP(uri: vscode.Uri): boolean { path: path.dirname(uri.path), }) .toString(); - if (cspFilesInProjectFolder.has(parent) && cspFilesInProjectFolder.get(parent).includes(path.basename(uri.path))) { + if (cspFilesInProjectFolder.has(parent) && cspFilesInProjectFolder.get(parent)!.includes(path.basename(uri.path))) { return true; } // Read the parent directory and file is not CSP OR haven't read the parent directory yet // Use the file extension to guess if it's a web app file - const additionalExts: string[] = vscode.workspace + const additionalExts: string[] | undefined = vscode.workspace .getConfiguration("objectscript.projects", uri) .get("webAppFileExtensions"); return [ @@ -220,8 +220,8 @@ export function isCSP(uri: vscode.Uri): boolean { "ico", "xml", "txt", - ...additionalExts, - ].includes(uri.path.split(".").pop().toLowerCase()); + ...additionalExts!, + ].includes(uri.path.split(".").pop()!.toLowerCase()); } return csp; } @@ -237,7 +237,7 @@ export function isfsDocumentName(uri: vscode.Uri, csp?: boolean, pkg = false): s if (csp == undefined) csp = isCSP(uri); const doc = csp ? uri.path : uri.path.slice(1).replace(/\//g, "."); // Add the .PKG extension to non-web folders if called from StudioActions - return pkg && !csp && !doc.split("/").pop().includes(".") ? `${doc}.PKG` : doc; + return pkg && !csp && !doc.split("/").pop()!.includes(".") ? `${doc}.PKG` : doc; } /** @@ -339,7 +339,7 @@ export class FileSystemProvider implements vscode.FileSystemProvider { if (entryPromise instanceof File) { // previously resolved as a file result = entryPromise; - } else if (entryPromise instanceof Promise && uri.path.split("/").pop()?.split(".").length > 1) { + } else if (entryPromise instanceof Promise && uri.path.split("/").pop()!.split(".").length > 1) { // apparently a file, so resolve ahead of adding permissions result = await entryPromise; } else { @@ -351,7 +351,7 @@ export class FileSystemProvider implements vscode.FileSystemProvider { const serverName = isfsDocumentName(uri); if (serverName.slice(-4).toLowerCase() == ".cls") { if (await isClassDeployed(serverName, api)) { - result.permissions |= vscode.FilePermission.Readonly; + result.permissions = result.permissions! | vscode.FilePermission.Readonly; return result; } } @@ -362,7 +362,7 @@ export class FileSystemProvider implements vscode.FileSystemProvider { const statusObj = await api.actionQuery(query, [serverName]); const docStatus = statusObj.result?.content?.pop(); if (docStatus) { - result.permissions = docStatus.editable ? undefined : result.permissions | vscode.FilePermission.Readonly; + result.permissions = docStatus.editable ? undefined : result.permissions! | vscode.FilePermission.Readonly; } } } @@ -381,7 +381,7 @@ export class FileSystemProvider implements vscode.FileSystemProvider { if (project) { // Get all items in the project return projectContentsFromUri(uri).then((entries) => - entries.map((entry) => { + entries!.map((entry) => { const csp = ["CSP", "DIR"].includes(entry.Type); if (!entry.Name.includes(".")) { if (!parent.entries.has(entry.Name)) { @@ -403,7 +403,7 @@ export class FileSystemProvider implements vscode.FileSystemProvider { const mapkey = uri.toString(); let mapvalue: string[] = []; if (cspFilesInProjectFolder.has(mapkey)) { - mapvalue = cspFilesInProjectFolder.get(mapkey); + mapvalue = cspFilesInProjectFolder.get(mapkey)!; } mapvalue.push(entry.Name); cspFilesInProjectFolder.set(mapkey, mapvalue); @@ -434,7 +434,7 @@ export class FileSystemProvider implements vscode.FileSystemProvider { } } const cspSubfolders = Array.from(cspSubfolderMap.entries()); - return studioOpenDialogFromURI(uri) + return studioOpenDialogFromURI(uri)! .then((data) => data.result.content || []) .then((data) => { const results = data @@ -479,7 +479,7 @@ export class FileSystemProvider implements vscode.FileSystemProvider { const identity = username.includes("*") ? `Users using ${username.slice(1, -1)}` : `User '${username}'`; const message = `${identity} cannot list ${ csp ? `web application '${uri.path}'` : "namespace" - } contents. If they do not have READ permission on the default code database of the ${api.config.ns.toUpperCase()} namespace then grant it and retry. If the problem remains then execute the following SQL in that namespace:\n\t GRANT EXECUTE ON %Library.RoutineMgr_StudioOpenDialog TO ${ + } contents. If they do not have READ permission on the default code database of the ${api.config.ns!.toUpperCase()} namespace then grant it and retry. If the problem remains then execute the following SQL in that namespace:\n\t GRANT EXECUTE ON %Library.RoutineMgr_StudioOpenDialog TO ${ username.includes("*") ? "" : username }`; handleError(message); @@ -510,7 +510,7 @@ export class FileSystemProvider implements vscode.FileSystemProvider { validateUriIsCanonical(uri); // Use _lookup() instead of _lookupAsFile() so we send // our cached mtime with the GET /doc request if we have it - return this._lookup(uri, true).then((file: File) => file.data); + return this._lookup(uri, true).then((file: File) => file.data!); } public writeFile( @@ -535,7 +535,7 @@ export class FileSystemProvider implements vscode.FileSystemProvider { const api = new AtelierAPI(uri); let created = false; let update = false; - const fileExt = fileName.split(".").pop().toLowerCase(); + const fileExt = fileName.split(".").pop()!.toLowerCase(); // Use _lookup() instead of _lookupAsFile() so we send // our cached mtime with the GET /doc request if we have it return this._lookup(uri) @@ -695,11 +695,11 @@ export class FileSystemProvider implements vscode.FileSystemProvider { }) .toString(); const mapvalue = cspFilesInProjectFolder.get(parentUriStr); - const idx = mapvalue.indexOf(path.basename(uri.path)); + const idx = mapvalue!.indexOf(path.basename(uri.path)); if (idx != -1) { - mapvalue.splice(idx, 1); - if (mapvalue.length) { - cspFilesInProjectFolder.set(parentUriStr, mapvalue); + mapvalue!.splice(idx, 1); + if (mapvalue!.length) { + cspFilesInProjectFolder.set(parentUriStr, mapvalue!); } else { cspFilesInProjectFolder.delete(parentUriStr); } @@ -728,7 +728,7 @@ export class FileSystemProvider implements vscode.FileSystemProvider { // Ignore the recursive flag for project folders toDeletePromise = projectContentsFromUri(uri, true); } else { - toDeletePromise = studioOpenDialogFromURI(uri, options.recursive ? { flat: true } : undefined).then( + toDeletePromise = studioOpenDialogFromURI(uri, options.recursive ? { flat: true } : undefined)!.then( (data) => data.result.content ); } @@ -756,7 +756,7 @@ export class FileSystemProvider implements vscode.FileSystemProvider { if (doc.status == "") { this.processDeletedDoc( doc, - DocumentContentProvider.getUri(doc.name, undefined, undefined, true, uri), + DocumentContentProvider.getUri(doc.name, undefined, undefined, true, uri)!, doc.name.includes("/"), project.length > 0 ); @@ -801,10 +801,10 @@ export class FileSystemProvider implements vscode.FileSystemProvider { } public async rename(oldUri: vscode.Uri, newUri: vscode.Uri, options: { overwrite: boolean }): Promise { - if (!oldUri.path.split("/").pop().includes(".")) { + if (!oldUri.path.split("/").pop()!.includes(".")) { throw new vscode.FileSystemError("Cannot rename a package/folder"); } - if (oldUri.path.split(".").pop().toLowerCase() != newUri.path.split(".").pop().toLowerCase()) { + if (oldUri.path.split(".").pop()!.toLowerCase() != newUri.path.split(".").pop()!.toLowerCase()) { throw new vscode.FileSystemError("Cannot change a file's extension during rename"); } if (vscode.workspace.getWorkspaceFolder(oldUri) != vscode.workspace.getWorkspaceFolder(newUri)) { @@ -813,13 +813,13 @@ export class FileSystemProvider implements vscode.FileSystemProvider { validateUriIsCanonical(oldUri); validateUriIsCanonical(newUri, true); // Check if the destination exists - let newFileStat: vscode.FileStat; + let newFileStat: vscode.FileStat | undefined; try { newFileStat = await vscode.workspace.fs.stat(newUri); if (!options.overwrite) { // If it does and we can't overwrite it, throw an error throw vscode.FileSystemError.FileExists(newUri); - } else if (newFileStat.permissions & vscode.FilePermission.Readonly) { + } else if (newFileStat.permissions! & vscode.FilePermission.Readonly) { // If the file is read-only, throw an error // This can happen if the target class is deployed, // or the document is marked read-only by source control @@ -901,7 +901,7 @@ export class FileSystemProvider implements vscode.FileSystemProvider { if (isfsConfig(uri).project) { compileListPromise = projectContentsFromUri(uri, true); } else { - compileListPromise = studioOpenDialogFromURI(uri, { flat: true }).then((data) => data.result.content); + compileListPromise = studioOpenDialogFromURI(uri, { flat: true })!.then((data) => data.result.content); } compileList.push(...(await compileListPromise.then((data) => data.map((e) => e.Name)))); } else { @@ -964,7 +964,7 @@ export class FileSystemProvider implements vscode.FileSystemProvider { ...filesToUpdate.map((f) => { return { type: vscode.FileChangeType.Changed, - uri: DocumentContentProvider.getUri(f, undefined, undefined, undefined, originalUri), + uri: DocumentContentProvider.getUri(f, undefined, undefined, undefined, originalUri)!, }; }) ); @@ -982,7 +982,7 @@ export class FileSystemProvider implements vscode.FileSystemProvider { ).map((f: string) => { return { type: vscode.FileChangeType.Changed, - uri: DocumentContentProvider.getUri(f, undefined, undefined, undefined, originalUri), + uri: DocumentContentProvider.getUri(f, undefined, undefined, undefined, originalUri)!, }; }) ); @@ -998,8 +998,8 @@ export class FileSystemProvider implements vscode.FileSystemProvider { private async _lookup(uri: vscode.Uri, fillInPath?: boolean): Promise { const api = new AtelierAPI(uri); const config = api.config; - const rootName = `${config.auth.username}@${config.host}:${config.port}${config.pathPrefix}/${config.ns.toUpperCase()}`; - let entry: Entry = this.superRoot.entries.get(rootName); + const rootName = `${config.auth.username}@${config.host}:${config.port}${config.pathPrefix}/${config.ns!.toUpperCase()}`; + let entry: Entry | undefined = this.superRoot.entries.get(rootName); if (!entry) { entry = new Directory(rootName, ""); this.superRoot.entries.set(rootName, entry); diff --git a/src/providers/FileSystemProvider/TextSearchProvider.ts b/src/providers/FileSystemProvider/TextSearchProvider.ts index d9e62636..0929c54d 100644 --- a/src/providers/FileSystemProvider/TextSearchProvider.ts +++ b/src/providers/FileSystemProvider/TextSearchProvider.ts @@ -34,9 +34,9 @@ function searchMatchToLine( let line = match.line ? Number(match.line) : null; if (match.member !== undefined) { // This is an attribute of a class member - if (match.member == "Storage" && match.attr.includes(",") && match.attrline == undefined) { + if (match.member == "Storage" && match.attr!.includes(",") && match.attrline == undefined) { // This is inside a Storage definition - const xmlTags = match.attr.split(","); + const xmlTags = match.attr!.split(","); const storageRegex = new RegExp(`^Storage ${xmlTags[0]}`); let inStorage = false; for (let i = 0; i < content.length; i++) { @@ -90,14 +90,14 @@ function searchMatchToLine( } else { if (match.attr === "Description") { // This is in the description - line = descLineToDocLine(content, match.attrline, i); - } else if (match.attrline || ["Code", "Data", "SqlQuery"].includes(match.attr)) { - if (["Code", "Data", "SqlQuery"].includes(match.attr)) { + line = descLineToDocLine(content, match.attrline!, i); + } else if (match.attrline || ["Code", "Data", "SqlQuery"].includes(match.attr!)) { + if (["Code", "Data", "SqlQuery"].includes(match.attr!)) { // This is in the implementation line = memend + (match.attrline ?? 1); } else { // This is a keyword with a multiline value - line = i + (match.attrline - 1 || 0); + line = i + (match.attrline! - 1 || 0); } } else { // This is in the class member definition @@ -163,7 +163,7 @@ function searchMatchToLine( if (content[i].match(classMatchPattern)) { if (match.attr == "Description") { // This is in the class description - line = descLineToDocLine(content, match.attrline, i); + line = descLineToDocLine(content, match.attrline!, i); break; } else if (match.attr == "Super" || match.attr == "Name") { // This is in the class definition line @@ -220,7 +220,7 @@ async function processSearchResults( results: number, maxResults: number, token: vscode.CancellationToken -): Promise { +): Promise { if (token.isCancellationRequested) { return; } @@ -228,7 +228,7 @@ async function processSearchResults( if (token.isCancellationRequested) { return; } - let message: vscode.TextSearchCompleteMessage; + let message: vscode.TextSearchCompleteMessage | undefined; const rejected = fileResults.filter((r) => r.status == "rejected").length; if (rejected > 0) { outputChannel.appendLine("Search errors:"); @@ -258,7 +258,7 @@ async function processSearchResults( function removeConfigExcludes(folder: vscode.Uri, excludes: string[]): string[] { // Function to get one of the two kinds of exclude settings as an array const getConfigExcludes = (key: string) => { - return Object.entries(vscode.workspace.getConfiguration(key, folder).get("exclude")) + return Object.entries(vscode.workspace.getConfiguration(key, folder).get("exclude")!) .filter((value) => value[1] === true) .map((value) => value[0]); }; @@ -289,7 +289,7 @@ export class TextSearchProvider implements vscode.TextSearchProvider { options: vscode.TextSearchOptions, progress: vscode.Progress, token: vscode.CancellationToken - ): Promise { + ): Promise { const api = new AtelierAPI(options.folder); const rateLimiter = new RateLimiter(50); if (!api.active) { @@ -359,13 +359,13 @@ export class TextSearchProvider implements vscode.TextSearchProvider { return; } - const uri = DocumentContentProvider.getUri(file.doc, "", "", true, options.folder); + const uri = DocumentContentProvider.getUri(file.doc, "", "", true, options.folder)!; const content = decoder.decode(await vscode.workspace.fs.readFile(uri)).split(/\r?\n/); const contentLength = content.length; // Find all lines that we have matches on const multilineMethodArgs: boolean = vscode.workspace .getConfiguration("objectscript", options.folder) - .get("multilineMethodArgs"); + .get("multilineMethodArgs")!; const lines = file.matches .map((match: SearchMatch) => token.isCancellationRequested ? null : searchMatchToLine(content, match, file.doc, multilineMethodArgs) @@ -378,26 +378,26 @@ export class TextSearchProvider implements vscode.TextSearchProvider { if (token.isCancellationRequested) { return; } - const text = content[line]; + const text = content[line!]; const regex = new RegExp( query.isRegExp ? query.pattern : query.pattern.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&"), query.isCaseSensitive ? "g" : "gi" ); - let regexMatch: RegExpExecArray; + let regexMatch: RegExpExecArray | null; const matchRanges: vscode.Range[] = []; const previewRanges: vscode.Range[] = []; - while ((regexMatch = regex.exec(text)) !== null && counter < options.maxResults) { + while ((regexMatch = regex.exec(text)) !== null && counter < options.maxResults!) { const start = regexMatch.index; const end = start + regexMatch[0].length; - matchRanges.push(new vscode.Range(line, start, line, end)); + matchRanges.push(new vscode.Range(line!, start, line!, end)); previewRanges.push(new vscode.Range(0, start, 0, end)); counter++; } if (matchRanges.length && previewRanges.length) { if (options.beforeContext) { // Add preceding context lines that aren't themselves result lines - const previewFrom = Math.max(line - options.beforeContext, 0); - for (let i = previewFrom; i < line; i++) { + const previewFrom = Math.max(line! - options.beforeContext, 0); + for (let i = previewFrom; i < line!; i++) { if (!matchedLines.has(i)) { progress.report({ uri, @@ -417,8 +417,8 @@ export class TextSearchProvider implements vscode.TextSearchProvider { }); if (options.afterContext) { // Add following context lines that aren't themselves result lines - const previewTo = Math.min(line + options.afterContext, contentLength - 1); - for (let i = line + 1; i <= previewTo; i++) { + const previewTo = Math.min(line! + options.afterContext, contentLength - 1); + for (let i = line! + 1; i <= previewTo; i++) { if (!matchedLines.has(i)) { progress.report({ uri, @@ -444,7 +444,7 @@ export class TextSearchProvider implements vscode.TextSearchProvider { options.includes = deduplicateGlobArray(options.includes); options.excludes = deduplicateGlobArray(removeConfigExcludes(options.folder, options.excludes)); - if (api.config.apiVersion >= 6) { + if (api.config.apiVersion! >= 6) { // Build the request object const request: AsyncSearchRequest = { request: "search", @@ -559,8 +559,8 @@ export class TextSearchProvider implements vscode.TextSearchProvider { let groupLen = 0; let group: string[] = []; for (const doc of prjContents) { - group.push(doc); - groupLen += doc.length; + group.push(doc!); + groupLen += doc!.length; if (groupLen >= 1300) { // Be conservative because we really don't want ugly 414 errors requestGroups.push(group); diff --git a/src/providers/LowCodeEditorProvider.ts b/src/providers/LowCodeEditorProvider.ts index 296620ad..24215474 100644 --- a/src/providers/LowCodeEditorProvider.ts +++ b/src/providers/LowCodeEditorProvider.ts @@ -36,14 +36,14 @@ export class LowCodeEditorProvider implements vscode.CustomTextEditorProvider { return this._errorMessage(`${document.fileName} is a malformed class definition.`); } const api = new AtelierAPI(document.uri); - if (!vscode.workspace.fs.isWritableFileSystem(document.uri.scheme) && lt(api.config.serverVersion, "2025.3.0")) { + if (!vscode.workspace.fs.isWritableFileSystem(document.uri.scheme) && lt(api.config.serverVersion!, "2025.3.0")) { return this._errorMessage(`File system '${document.uri.scheme}' is read-only.`); } const className = file.name.slice(0, -4); if (!api.active) { return this._errorMessage("Server connection is not active."); } - if (lt(api.config.serverVersion, "2023.1.0")) { + if (lt(api.config.serverVersion!, "2023.1.0")) { return this._errorMessage( "Opening a low-code editor in VS Code requires InterSystems IRIS version 2023.1 or above." ); @@ -66,14 +66,14 @@ export class LowCodeEditorProvider implements vscode.CustomTextEditorProvider { } else if (queryData.result.content[0].Rule) { webApp = this._rule; } else if (queryData.result.content[0].DTL) { - if (lt(api.config.serverVersion, "2025.1.0")) { + if (lt(api.config.serverVersion!, "2025.1.0")) { return this._errorMessage( "Opening the DTL editor in VS Code requires InterSystems IRIS version 2025.1 or above." ); } webApp = this._dtl; } else if (queryData.result.content[0].BPL) { - if (lt(api.config.serverVersion, "2026.1.0")) { + if (lt(api.config.serverVersion!, "2026.1.0")) { return this._errorMessage( "Opening the BPL editor in VS Code requires InterSystems IRIS version 2026.1 or above." ); diff --git a/src/providers/ObjectScriptClassSymbolProvider.ts b/src/providers/ObjectScriptClassSymbolProvider.ts index cb2c5bf4..10db56c3 100644 --- a/src/providers/ObjectScriptClassSymbolProvider.ts +++ b/src/providers/ObjectScriptClassSymbolProvider.ts @@ -6,7 +6,7 @@ export class ObjectScriptClassSymbolProvider implements vscode.DocumentSymbolPro token: vscode.CancellationToken ): Thenable { return new Promise((resolve) => { - let classItSelf = null; + let classItSelf: vscode.DocumentSymbol | null = null; let symbols: vscode.DocumentSymbol[] = []; let inComment = false; @@ -66,7 +66,7 @@ export class ObjectScriptClassSymbolProvider implements vscode.DocumentSymbolPro } } symbols.push({ - children: undefined, + children: undefined!, detail: method[1], kind: vscode.SymbolKind.Method, name: method[2].replace(/"/g, ""), @@ -78,7 +78,7 @@ export class ObjectScriptClassSymbolProvider implements vscode.DocumentSymbolPro const index = line.text.match(/^(Index|ForegnKey) (%?\b\w+\b)/i); if (index) { symbols.push({ - children: undefined, + children: undefined!, detail: index[1], kind: vscode.SymbolKind.Key, name: index[2], @@ -99,7 +99,7 @@ export class ObjectScriptClassSymbolProvider implements vscode.DocumentSymbolPro } } symbols.push({ - children: undefined, + children: undefined!, detail: property[1], kind: vscode.SymbolKind.Property, name: property[2], @@ -111,7 +111,7 @@ export class ObjectScriptClassSymbolProvider implements vscode.DocumentSymbolPro const parameter = line.text.match(/^(Parameter) (%?\b\w+\b)/i); if (parameter) { symbols.push({ - children: undefined, + children: undefined!, detail: parameter[1], kind: vscode.SymbolKind.Constant, name: parameter[2], @@ -134,7 +134,7 @@ export class ObjectScriptClassSymbolProvider implements vscode.DocumentSymbolPro } } symbols.push({ - children: undefined, + children: undefined!, detail: other[1], kind: vscode.SymbolKind.Struct, name: other[2], @@ -144,7 +144,7 @@ export class ObjectScriptClassSymbolProvider implements vscode.DocumentSymbolPro } } - resolve([classItSelf]); + resolve([classItSelf!]); }); } } diff --git a/src/providers/ObjectScriptCodeLensProvider.ts b/src/providers/ObjectScriptCodeLensProvider.ts index f73a9052..a81b69dd 100644 --- a/src/providers/ObjectScriptCodeLensProvider.ts +++ b/src/providers/ObjectScriptCodeLensProvider.ts @@ -81,7 +81,7 @@ function getSqlQuery(document: vscode.TextDocument, startLine: number, startChar if ( result.length && !["SELECT", "DECLARE", "UPDATE", "DELETE", "TRUNCATE", "INSERT"].includes( - result.trimStart().split(/\s+/).shift().toUpperCase() + result.trimStart().split(/\s+/).shift()!.toUpperCase() ) ) { // Can only generate plans for certain SQL statements @@ -109,8 +109,8 @@ function scanCodeBlock( let inCStyleComment = false; for (let i = start; i < end; i++) { const line = document.lineAt(i).text; - let commentStart: number; - let commentEnd: number; + let commentStart: number | undefined; + let commentEnd: number | undefined; if (!inCStyleComment) { const commentMatch = line.match(commentRegex); if (commentMatch) { @@ -138,20 +138,20 @@ function scanCodeBlock( // Check if the match is commented out or in a string literal if ( ((commentStart == undefined && commentEnd == undefined) || - (commentStart != undefined && eSqlMatch.index < commentStart) || - (commentEnd != undefined && eSqlMatch.index > commentEnd)) && + (commentStart != undefined && eSqlMatch.index! < commentStart) || + (commentEnd != undefined && eSqlMatch.index! > commentEnd)) && // There are an even number of, or zero, quotes preceding the match - line.slice(0, eSqlMatch.index).split('"').length % 2 == 1 + line.slice(0, eSqlMatch.index!).split('"').length % 2 == 1 ) { const sqlQuery = getSqlQuery( document, i, - eSqlMatch.index + eSqlMatch[0].length, + eSqlMatch.index! + eSqlMatch[0].length, `)${(eSqlMatch[1] ?? "").split("").reverse().join("")}` ); if (sqlQuery) { result.push( - new vscode.CodeLens(new vscode.Range(i, eSqlMatch.index, i, eSqlMatch.index + eSqlMatch[0].length), { + new vscode.CodeLens(new vscode.Range(i, eSqlMatch.index!, i, eSqlMatch.index! + eSqlMatch[0].length), { title: "Show Plan", tooltip: "Show the plan for this query", command: "vscode-objectscript.showPlanWebview", @@ -187,7 +187,7 @@ export class ObjectScriptCodeLensProvider implements vscode.CodeLensProvider { public async provideCodeLenses( document: vscode.TextDocument, token: vscode.CancellationToken - ): Promise { + ): Promise { if (![clsLangId, macLangId, intLangId].includes(document.languageId)) return; const file = currentFile(document); if (!file) return; // Document is malformed @@ -198,9 +198,9 @@ export class ObjectScriptCodeLensProvider implements vscode.CodeLensProvider { if (!symbols?.length || token.isCancellationRequested) return; const api = new AtelierAPI(document.uri); const conf = vscode.workspace.getConfiguration("objectscript.debug"); - const debugThisMethod: boolean = conf.get("debugThisMethod") && api.active; - const copyToClipboard: boolean = conf.get("copyToClipboard"); - const showPlan = api.active && gte(api.config.serverVersion, "2024.1.0"); + const debugThisMethod: boolean = conf.get("debugThisMethod")! && api.active; + const copyToClipboard: boolean = conf.get("copyToClipboard")!; + const showPlan = api.active && gte(api.config.serverVersion!, "2024.1.0"); const result: vscode.CodeLens[] = []; if (document.languageId == clsLangId) { if (!symbols[0].children.length) return; @@ -245,7 +245,7 @@ export class ObjectScriptCodeLensProvider implements vscode.CodeLensProvider { symbols[0].children.forEach((symbol, idx) => { const type = symbol.detail.toLowerCase(); if (!["xdata", "method", "classmethod", "query", "trigger"].includes(type)) return; - let symbolLine: number; + let symbolLine: number | undefined; if (languageServer) { symbolLine = symbol.selectionRange.start.line; } else { @@ -259,17 +259,17 @@ export class ObjectScriptCodeLensProvider implements vscode.CodeLensProvider { switch (type) { case "xdata": { if (api.active) { - let cmd: vscode.Command; + let cmd: vscode.Command | undefined; if ( (symbol.name == "RuleDefinition" && superclasses.includes("Ens.Rule.Definition") && - gte(api.config.serverVersion, "2023.1.0")) || + gte(api.config.serverVersion!, "2023.1.0")) || (symbol.name == "DTL" && superclasses.includes("Ens.DataTransformDTL") && - gte(api.config.serverVersion, "2025.1.0")) || + gte(api.config.serverVersion!, "2025.1.0")) || (symbol.name == "BPL" && superclasses.includes("Ens.BusinessProcessBPL") && - gte(api.config.serverVersion, "2026.1.0")) + gte(api.config.serverVersion!, "2026.1.0")) ) { cmd = { title: "Reopen in Low-Code Editor", @@ -285,7 +285,7 @@ export class ObjectScriptCodeLensProvider implements vscode.CodeLensProvider { arguments: [`/${className}.cls`, document.uri], }; } - if (cmd) result.push(new vscode.CodeLens(this.range(symbolLine), cmd)); + if (cmd) result.push(new vscode.CodeLens(this.range(symbolLine!), cmd)); } break; } @@ -294,7 +294,7 @@ export class ObjectScriptCodeLensProvider implements vscode.CodeLensProvider { case "trigger": case "query": { // Capture the entire text of the class member definition up to the implementation - const memberInfo = parseClassMemberDefinition(document, symbol, symbolLine); + const memberInfo = parseClassMemberDefinition(document, symbol, symbolLine!); if (!memberInfo) break; const { definition, defEndLine, language, isPrivate } = memberInfo; if (showPlan) { @@ -307,7 +307,7 @@ export class ObjectScriptCodeLensProvider implements vscode.CodeLensProvider { const sqlQuery = getSqlQuery(document, defEndLine + 1, 0, "}"); if (sqlQuery) { result.push( - new vscode.CodeLens(this.range(symbolLine), { + new vscode.CodeLens(this.range(symbolLine!), { title: "Show Plan", tooltip: "Show the plan for this query", command: "vscode-objectscript.showPlanWebview", @@ -342,7 +342,7 @@ export class ObjectScriptCodeLensProvider implements vscode.CodeLensProvider { ) { const argsMatch = definition.match(new RegExp(`${displayName}\\(([^)]*)\\)`)); result.push( - this.addDebugThisMethod(symbolLine, [ + this.addDebugThisMethod(symbolLine!, [ `##class(${className}).${displayName}`, argsMatch && typeof argsMatch[1] == "string" && argsMatch[1].trim().length > 0, ]) @@ -354,7 +354,7 @@ export class ObjectScriptCodeLensProvider implements vscode.CodeLensProvider { (type == "classmethod" || (type == "query" && displayName[0] != '"')) ) { result.push( - this.addCopyToClipboard(symbolLine, [ + this.addCopyToClipboard(symbolLine!, [ `##class(${className}).${displayName}${type == "query" ? "Func" : ""}()`, ]) ); diff --git a/src/providers/ObjectScriptCompletionItemProvider.ts b/src/providers/ObjectScriptCompletionItemProvider.ts index 670001a7..230cfb20 100644 --- a/src/providers/ObjectScriptCompletionItemProvider.ts +++ b/src/providers/ObjectScriptCompletionItemProvider.ts @@ -40,7 +40,7 @@ export class ObjectScriptCompletionItemProvider implements vscode.CompletionItem ); } } - const completions = [] + const completions: any[] = ([] as any[]) .concat( this.classes(document, position, token, context), this.macrolist(document, position, token, context), @@ -99,11 +99,11 @@ export class ObjectScriptCompletionItemProvider implements vscode.CompletionItem const range = document.getWordRangeAtPosition(position, pattern); const text = range ? document.getText(range) : ""; if (range) { - const [, prefix, macro = ""] = text.toLowerCase().match(pattern); + const [, prefix, macro = ""] = text.toLowerCase().match(pattern)!; const file = currentFile(); const api = new AtelierAPI(); return api - .getmacrolist(file.name, []) + .getmacrolist(file!.name, []) .then((data) => data.result.content.macros) .then((list) => list.filter((el) => el.toLowerCase().startsWith(macro))) .then((list) => list.map((el) => "$$$" + el)) @@ -174,7 +174,7 @@ export class ObjectScriptCompletionItemProvider implements vscode.CompletionItem return { ...el, label: this._formatter.function(el.label as string), - insertText: this._formatter.function(el.insertText), + insertText: this._formatter.function(el.insertText!), range, }; }), @@ -236,7 +236,7 @@ export class ObjectScriptCompletionItemProvider implements vscode.CompletionItem position: vscode.Position, token: vscode.CancellationToken, context: vscode.CompletionContext - ): vscode.CompletionItem[] { + ): vscode.CompletionItem[] | null { const range = document.getWordRangeAtPosition(position, /%?\b\w+[\w\d]*\b/); const kind = vscode.CompletionItemKind.Variable; if (context.triggerKind === vscode.CompletionTriggerKind.Invoke) { @@ -273,14 +273,14 @@ export class ObjectScriptCompletionItemProvider implements vscode.CompletionItem const searchText = document.getText(range); const method = (el) => ({ - documentation: el.desc.length ? new vscode.MarkdownString(el.desc.join("")) : null, + documentation: el.desc.length ? new vscode.MarkdownString(el.desc.join("")) : null!, insertText: new vscode.SnippetString(`${el.name}($0)`), kind: vscode.CompletionItemKind.Method, label: el.name, }); const parameter = (el) => ({ - documentation: el.desc.length ? new vscode.MarkdownString(el.desc.join("")) : null, + documentation: el.desc.length ? new vscode.MarkdownString(el.desc.join("")) : null!, insertText: new vscode.SnippetString(`${el.name}`), kind: vscode.CompletionItemKind.Constant, label: `${el.name}`, @@ -288,7 +288,7 @@ export class ObjectScriptCompletionItemProvider implements vscode.CompletionItem }); const property = (el) => ({ - documentation: el.desc.length ? new vscode.MarkdownString(el.desc.join("")) : null, + documentation: el.desc.length ? new vscode.MarkdownString(el.desc.join("")) : null!, insertText: new vscode.SnippetString(`${el.name}`), kind: vscode.CompletionItemKind.Property, label: el.name, @@ -306,10 +306,10 @@ export class ObjectScriptCompletionItemProvider implements vscode.CompletionItem return classDef.methods("class").then((data) => data.filter(search).map(method)); } - if (curFile.fileName.endsWith("cls")) { + if (curFile!.fileName.endsWith("cls")) { const selfRef = textBefore.match(/(? data.filter(search).map(parameter)); } @@ -333,7 +333,7 @@ export class ObjectScriptCompletionItemProvider implements vscode.CompletionItem let pattern = /##class\(([^)]*)\)/i; let range = document.getWordRangeAtPosition(position, pattern); let text = range ? document.getText(range) : ""; - let [, className] = range ? text.match(pattern) : ""; + let [, className] = (range ? text.match(pattern)! : "") as [string, string | undefined]; if (!range) { pattern = /(\b(?:Of|As)\b (%?\b[a-zA-Z][a-zA-Z0-9]*(?:\.[a-zA-Z][a-zA-Z0-9]+)*\b\.?)?(?! of))/i; range = document.getWordRangeAtPosition(position, pattern); @@ -353,18 +353,18 @@ export class ObjectScriptCompletionItemProvider implements vscode.CompletionItem className = text.split(/\s|\(/).pop(); } if (range) { - const percent = className.startsWith("%"); - const library = percent && className.indexOf(".") < 0; + const percent = className!.startsWith("%"); + const library = percent && className!.indexOf(".") < 0; className = className || ""; const searchName = className.replace(/(^%|")/, "").toLowerCase(); const part = className.split(".").length; - const params = []; + const params: string[] = []; let sql = ""; /// Classes from the current class's package - if (part === 1 && curFile.fileName.endsWith("cls")) { - const packageName = curFile.name.split(".").slice(0, -2).join("."); - const className2 = curFile.name.split(".").slice(0, -1).join("."); + if (part === 1 && curFile!.fileName.endsWith("cls")) { + const packageName = curFile!.name.split(".").slice(0, -2).join("."); + const className2 = curFile!.name.split(".").slice(0, -1).join("."); const part2 = packageName.split(".").length + 1; sql += ` SELECT @@ -442,7 +442,7 @@ export class ObjectScriptCompletionItemProvider implements vscode.CompletionItem ): vscode.ProviderResult { const range = document.getWordRangeAtPosition(position, /\$system(\.\b\w+\b)?(\.\b\w+\b)?\./i); const text = range ? document.getText(range) : ""; - const [, className] = text.match(/\$system(\.\b\w+\b)?(\.\b\w+\b)?\./i); + const [, className] = text.match(/\$system(\.\b\w+\b)?(\.\b\w+\b)?\./i)!; const api = new AtelierAPI(); if (!className) { @@ -466,7 +466,7 @@ export class ObjectScriptCompletionItemProvider implements vscode.CompletionItem .content.methods.filter((el) => !el.private) .filter((el) => !el.internal) .map((el) => ({ - documentation: el.desc.length ? new vscode.MarkdownString(el.desc.join("")) : null, + documentation: el.desc.length ? new vscode.MarkdownString(el.desc.join("")) : null!, insertText: new vscode.SnippetString(`${el.name}($0)`), kind: vscode.CompletionItemKind.Method, label: el.name, diff --git a/src/providers/ObjectScriptDefinitionProvider.ts b/src/providers/ObjectScriptDefinitionProvider.ts index 1012e4af..feb403d3 100644 --- a/src/providers/ObjectScriptDefinitionProvider.ts +++ b/src/providers/ObjectScriptDefinitionProvider.ts @@ -24,7 +24,7 @@ export class ObjectScriptDefinitionProvider implements vscode.DefinitionProvider if (selfRef) { const selfEntity = document.getText(selfRef).substr(2); const range = new vscode.Range(position.line, selfRef.start.character + 2, position.line, selfRef.end.character); - const classDefinition = new ClassDefinition(file.name); + const classDefinition = new ClassDefinition(file!.name); return classDefinition.getMemberLocations(selfEntity).then((locations): vscode.DefinitionLink[] => locations.map( (location): vscode.DefinitionLink => ({ @@ -41,9 +41,9 @@ export class ObjectScriptDefinitionProvider implements vscode.DefinitionProvider const macroMatch = macroText.match(/^\${3}(\b\w+\b)$/); if (macroMatch) { const [, macro] = macroMatch; - return this.macro(workspaceFolderName, currentFile(), macro).then((data) => + return this.macro(workspaceFolderName, currentFile()!, macro).then((data) => data && data.document.length - ? new vscode.Location(DocumentContentProvider.getUri(data.document), new vscode.Position(data.line, 0)) + ? new vscode.Location(DocumentContentProvider.getUri(data.document)!, new vscode.Position(data.line, 0)) : null ); } @@ -77,7 +77,7 @@ export class ObjectScriptDefinitionProvider implements vscode.DefinitionProvider const [keyword, name] = part.split(" "); const start = pos + keyword.length + 1; if (this.isValid(position, start, name.length)) { - return [this.makePropertyDefinition(document, name)]; + return [this.makePropertyDefinition(document, name)!]; } } pos += part.length; @@ -87,7 +87,7 @@ export class ObjectScriptDefinitionProvider implements vscode.DefinitionProvider pos = 0; for (const part of parts) { if (part.match(onPropertyList)) { - const listProperties = /\(([^)]+)\)/.exec(part)[1].split(/\s*,\s*/); + const listProperties = /\(([^)]+)\)/.exec(part)![1].split(/\s*,\s*/); return listProperties .map((name) => { name = name.trim(); @@ -105,7 +105,7 @@ export class ObjectScriptDefinitionProvider implements vscode.DefinitionProvider pos = 0; for (const part of parts) { if (part.match(asClassList)) { - const listClasses = /\(([^)]+)\)/.exec(part)[1].split(/\s*,\s*/); + const listClasses = /\(([^)]+)\)/.exec(part)![1].split(/\s*,\s*/); return listClasses .map((name) => { name = name.trim(); @@ -149,7 +149,7 @@ export class ObjectScriptDefinitionProvider implements vscode.DefinitionProvider pos = 0; for (const part of parts) { if (part.match(asRoutineList)) { - const listRoutines = /\(([^)]+)\)/.exec(part)[1].split(","); + const listRoutines = /\(([^)]+)\)/.exec(part)![1].split(","); for (let name of listRoutines) { name = name.trim(); const start = pos + part.indexOf(name); @@ -174,7 +174,7 @@ export class ObjectScriptDefinitionProvider implements vscode.DefinitionProvider pos = 0; for (const part of parts) { if (part.match(asLabelRoutineCall)) { - const [, routine] = part.match(/\^(%?\b\w+\b)/); + const [, routine] = part.match(/\^(%?\b\w+\b)/)!; const start = pos + part.indexOf(routine) - 1; const length = routine.length + 1; return this.getFullRoutineName(routine).then((routineName) => [ @@ -195,7 +195,7 @@ export class ObjectScriptDefinitionProvider implements vscode.DefinitionProvider const classRef = /##class\(([^)]+)\)(?:\\$this)?\.(#?%?[a-zA-Z][a-zA-Z0-9]*)/i; const classRefRange = document.getWordRangeAtPosition(position, classRef); if (classRefRange) { - const [, className, entity] = document.getText(classRefRange).match(classRef); + const [, className, entity] = document.getText(classRefRange).match(classRef)!; const start = classRefRange.start.character + 8; if (this.isValid(position, start, className.length)) { return [ @@ -274,11 +274,11 @@ export class ObjectScriptDefinitionProvider implements vscode.DefinitionProvider new vscode.Position(position.line, start + length) ), targetRange: new vscode.Range(firstLinePos, firstLinePos), - targetUri: DocumentContentProvider.getUri(name, workspaceFolder), + targetUri: DocumentContentProvider.getUri(name, workspaceFolder)!, }; } - public makePropertyDefinition(document: vscode.TextDocument, name: string): vscode.DefinitionLink { + public makePropertyDefinition(document: vscode.TextDocument, name: string): vscode.DefinitionLink | null { const property = new RegExp(`(?<=^Property\\s)\\b${name}\\b`, "i"); let descrLine = -1; for (let i = 0; i < document.lineCount; i++) { @@ -292,12 +292,12 @@ export class ObjectScriptDefinitionProvider implements vscode.DefinitionProvider const propertyMatch = line.match(property); if (propertyMatch) { const targetSelectionRange = new vscode.Range( - new vscode.Position(i, propertyMatch.index), - new vscode.Position(i, propertyMatch.index + name.length) + new vscode.Position(i, propertyMatch.index!), + new vscode.Position(i, propertyMatch.index! + name.length) ); const targetRange = new vscode.Range( new vscode.Position(descrLine >= 0 ? descrLine : i, 0), - new vscode.Position(i, propertyMatch.index + line.length) + new vscode.Position(i, propertyMatch.index! + line.length) ); return { targetUri: document.uri, @@ -323,7 +323,7 @@ export class ObjectScriptDefinitionProvider implements vscode.DefinitionProvider new vscode.Position(position.line, start + length) ), targetRange: new vscode.Range(firstLinePos, firstLinePos), - targetUri: DocumentContentProvider.getUri(name, workspaceFolder), + targetUri: DocumentContentProvider.getUri(name, workspaceFolder)!, }; } @@ -334,7 +334,7 @@ export class ObjectScriptDefinitionProvider implements vscode.DefinitionProvider ): Promise<{ document: string; line: number }> { const fileName = file.name; const api = new AtelierAPI(); - let includes = []; + let includes: string[] = []; if (fileName.toLowerCase().endsWith("cls")) { const classDefinition = new ClassDefinition(fileName); includes = await classDefinition.includeCode(); diff --git a/src/providers/ObjectScriptDiagnosticProvider.ts b/src/providers/ObjectScriptDiagnosticProvider.ts index a903827e..2941ffd7 100644 --- a/src/providers/ObjectScriptDiagnosticProvider.ts +++ b/src/providers/ObjectScriptDiagnosticProvider.ts @@ -324,12 +324,12 @@ export class ObjectScriptDiagnosticProvider { } const pattern = /(? { const word = document.getWordRangeAtPosition(position); const text = document.getText( - new vscode.Range(new vscode.Position(position.line, 0), new vscode.Position(position.line, word.end.character)) + new vscode.Range(new vscode.Position(position.line, 0), new vscode.Position(position.line, word!.end.character)) ); const file = currentFile(); @@ -32,9 +32,9 @@ export class ObjectScriptHoverProvider implements vscode.HoverProvider { const range = document.getWordRangeAtPosition(position, /\^?\$+\b\w+\b$/); let search = dollarsMatch.shift(); const [dollars, value] = dollarsMatch; - search = search.toUpperCase(); + search = search!.toUpperCase(); if (dollars === "$$$") { - return this.macro(file.name, value).then((contents) => ({ + return this.macro(file!.name, value).then((contents) => ({ contents: [contents.join("")], range, })); @@ -44,7 +44,7 @@ export class ObjectScriptHoverProvider implements vscode.HoverProvider { found = found || structuredSystemVariables.find((el) => el.label === search || el.alias.includes(search)); if (found) { return { - contents: [found.documentation.join(""), this.documentationLink(found.link)], + contents: [found.documentation.join(""), this.documentationLink(found.link)!], range, }; } @@ -56,7 +56,7 @@ export class ObjectScriptHoverProvider implements vscode.HoverProvider { public async macro(fileName: string, macro: string): Promise { const api = new AtelierAPI(); - let includes = []; + let includes: string[] = []; if (fileName.toLowerCase().endsWith(".cls")) { const classDefinition = new ClassDefinition(fileName); includes = await classDefinition.includeCode(); @@ -74,7 +74,7 @@ export class ObjectScriptHoverProvider implements vscode.HoverProvider { public commands(document: vscode.TextDocument, position: vscode.Position): vscode.ProviderResult { const word = document.getWordRangeAtPosition(position); const text = document.getText( - new vscode.Range(new vscode.Position(position.line, 0), new vscode.Position(position.line, word.end.character)) + new vscode.Range(new vscode.Position(position.line, 0), new vscode.Position(position.line, word!.end.character)) ); const commandMatch = text.match(/^\s+\b[a-z]+\b$/i); if (commandMatch) { @@ -82,14 +82,14 @@ export class ObjectScriptHoverProvider implements vscode.HoverProvider { const command = commands.find((el) => el.label === search || el.alias.includes(search)); if (search) { return { - contents: [command.documentation.join(""), this.documentationLink(command.link)], + contents: [command!.documentation.join(""), this.documentationLink(command!.link)!], range: word, }; } } } - public documentationLink(link: string): string | null { + public documentationLink(link: string | undefined): string | null | undefined { if (link) { return `[Online documentation](${ link.startsWith("http") ? "" : "https://docs.intersystems.com/irislatest" diff --git a/src/providers/WorkspaceSymbolProvider.ts b/src/providers/WorkspaceSymbolProvider.ts index b1b991ac..28596e33 100644 --- a/src/providers/WorkspaceSymbolProvider.ts +++ b/src/providers/WorkspaceSymbolProvider.ts @@ -35,7 +35,7 @@ export class WorkspaceSymbolProvider implements vscode.WorkspaceSymbolProvider { * because we aren't including ranges. They will be resolved later. */ private _queryResultToSymbols(data: any, wsFolder: vscode.WorkspaceFolder): any[] { - const result = []; + const result: any[] = []; const uris: Map = new Map(); for (const element of data.result.content) { const kind: vscode.SymbolKind = (() => { @@ -68,7 +68,7 @@ export class WorkspaceSymbolProvider implements vscode.WorkspaceSymbolProvider { let uri: vscode.Uri; if (uris.has(element.Parent)) { - uri = uris.get(element.Parent); + uri = uris.get(element.Parent)!; } else { uri = DocumentContentProvider.getUri( `${element.Parent}.cls`, @@ -78,7 +78,7 @@ export class WorkspaceSymbolProvider implements vscode.WorkspaceSymbolProvider { wsFolder.uri, // Only "file" scheme is fully supported for client-side editing wsFolder.uri.scheme != "file" - ); + )!; uris.set(element.Parent, uri); } @@ -97,7 +97,7 @@ export class WorkspaceSymbolProvider implements vscode.WorkspaceSymbolProvider { public async provideWorkspaceSymbols( query: string, token: vscode.CancellationToken - ): Promise { + ): Promise { if (!vscode.workspace.workspaceFolders?.length) return; // Convert query to a LIKE compatible pattern const pattern = queryToFuzzyLike(query); diff --git a/src/utils/FileProviderUtil.ts b/src/utils/FileProviderUtil.ts index 451c6e2a..30d8345c 100644 --- a/src/utils/FileProviderUtil.ts +++ b/src/utils/FileProviderUtil.ts @@ -32,12 +32,12 @@ export function isfsConfig(uri: vscode.Uri): IsfsUriConfig { mapped: params.get(IsfsUriParam.Mapped) != "0", filter: params.get(IsfsUriParam.Filter) ?? "", project: params.get(IsfsUriParam.Project) ?? "", - csp: ["", "1"].includes(params.get(IsfsUriParam.CSP)), + csp: ["", "1"].includes(params.get(IsfsUriParam.CSP)!), ns: params.get(IsfsUriParam.NS) || undefined, }; } -export async function projectContentsFromUri(uri: vscode.Uri, flat = false): Promise { +export async function projectContentsFromUri(uri: vscode.Uri, flat = false): Promise { const api = new AtelierAPI(uri); if (!api.active) { return; @@ -162,7 +162,7 @@ export function fileSpecFromURI(uri: vscode.Uri): string { export function studioOpenDialogFromURI( uri: vscode.Uri, overrides: { flat?: boolean; filter?: string } = { flat: false, filter: "" } -): Promise { +): Promise | undefined { const api = new AtelierAPI(uri); if (!api.active) return; const { system, generated, mapped } = isfsConfig(uri); @@ -174,7 +174,7 @@ export function studioOpenDialogFromURI( overrides?.flat ? "1" : "0", "0", // NotStudio (0 means hide globals and OBJ files) generated ? "1" : "0", - overrides.filter, + overrides.filter!, "0", // RoundTime (0 means no rounding) mapped ? "1" : "0", ]); diff --git a/src/utils/classDefinition.ts b/src/utils/classDefinition.ts index d4e841ca..2c477d76 100644 --- a/src/utils/classDefinition.ts +++ b/src/utils/classDefinition.ts @@ -5,7 +5,7 @@ import { DocumentContentProvider } from "../providers/DocumentContentProvider"; export class ClassDefinition { public get uri(): vscode.Uri { - return DocumentContentProvider.getUri(this._classFileName, this._workspaceFolder, this._namespace); + return DocumentContentProvider.getUri(this._classFileName, this._workspaceFolder, this._namespace)!; } public static normalizeClassName(className: string, withExtension = false): string { @@ -13,8 +13,8 @@ export class ClassDefinition { } private _className: string; private _classFileName: string; - private _workspaceFolder: string; - private _namespace: string; + private _workspaceFolder: string | undefined; + private _namespace: string | undefined; public constructor(className: string, workspaceFolder?: string, namespace?: string) { this._workspaceFolder = workspaceFolder; @@ -31,11 +31,11 @@ export class ClassDefinition { } public async methods(scope: "any" | "class" | "instance" = "any"): Promise { - const methods = []; + const methods: any[] = []; const filterScope = (method) => scope === "any" || method.scope === scope; const api = new AtelierAPI(this.uri); const getMethods = (content) => { - const extend = []; + const extend: any[] = []; content.forEach((el) => { methods.push(...el.content.methods); extend.push(...el.content.super.map((extendName) => ClassDefinition.normalizeClassName(extendName, true))); @@ -49,10 +49,10 @@ export class ClassDefinition { } public async properties(): Promise { - const properties = []; + const properties: any[] = []; const api = new AtelierAPI(this.uri); const getProperties = (content) => { - const extend = []; + const extend: any[] = []; content.forEach((el) => { properties.push(...el.content.properties); extend.push(...el.content.super.map((extendName) => ClassDefinition.normalizeClassName(extendName, true))); @@ -66,10 +66,10 @@ export class ClassDefinition { } public async parameters(): Promise { - const parameters = []; + const parameters: any[] = []; const api = new AtelierAPI(this.uri); const getParameters = (content) => { - const extend = []; + const extend: any[] = []; content.forEach((el) => { parameters.push(...el.content.parameters); extend.push(...el.content.super.map((extendName) => ClassDefinition.normalizeClassName(extendName, true))); @@ -116,7 +116,7 @@ export class ClassDefinition { .then((data) => data); } - public async getMemberLocation(name: string): Promise { + public async getMemberLocation(name: string): Promise { let pattern; if (name.startsWith("#")) { pattern = `(Parameter) ${name.substr(1)}(?=[( ;])`; diff --git a/src/utils/documentIndex.ts b/src/utils/documentIndex.ts index 922f2970..f4bcc9fb 100644 --- a/src/utils/documentIndex.ts +++ b/src/utils/documentIndex.ts @@ -52,7 +52,7 @@ async function getCurrentFile( uri: vscode.Uri, forceText = false, content?: string[] | Buffer -): Promise { +): Promise { if (content) { // forceText is always true when content is passed return currentFileFromContent(uri, Buffer.isBuffer(content) ? textDecoder.decode(content) : content.join("\n")); @@ -191,10 +191,11 @@ export async function indexWorkspaceFolder(wsFolder: vscode.WorkspaceFolder): Pr // Index classes and routines that currently exist vscode.workspace.findFiles(new vscode.RelativePattern(wsFolder, "{**/*}")).then((files) => files.forEach((file) => - fsRateLimiter.call(() => { + fsRateLimiter.call(async () => { if (isClassOrRtn(file.path) || isImportableLocalFile(file)) { return updateIndexInternal(file, documents, uris, true); } + return undefined; }) ) ); @@ -217,7 +218,7 @@ export async function indexWorkspaceFolder(wsFolder: vscode.WorkspaceFolder): Pr if (notToSync(uri)) { return; } - if (!uri.path.split("/").pop().includes(".")) { + if (!uri.path.split("/").pop()!.includes(".")) { // Ignore creation and change events for folders return; } @@ -254,7 +255,7 @@ export async function indexWorkspaceFolder(wsFolder: vscode.WorkspaceFolder): Pr } const api = new AtelierAPI(uri); const conf = vscode.workspace.getConfiguration("objectscript", wsFolder); - const syncLocalChanges: string = conf.get("syncLocalChanges"); + const syncLocalChanges: string = conf.get("syncLocalChanges")!; const vscodeChange = touchedByVSCode.has(uriString); const sync = api.active && (syncLocalChanges == "all" || (syncLocalChanges == "vscodeOnly" && vscodeChange)); touchedByVSCode.delete(uriString); @@ -263,8 +264,8 @@ export async function indexWorkspaceFolder(wsFolder: vscode.WorkspaceFolder): Pr if (change.addedOrChanged) { // Create or update the document on the server try { - const willCompile = conf.get("compileOnSave") && isCompilable(change.addedOrChanged.name); - await importFile(change.addedOrChanged, willCompile); + const willCompile = conf.get("compileOnSave") && isCompilable(change.addedOrChanged.name); + await importFile(change.addedOrChanged, willCompile!); outputImport(change.addedOrChanged.name, uri); if (willCompile) { // Compile right away if this document is in the active text editor. @@ -293,7 +294,7 @@ export async function indexWorkspaceFolder(wsFolder: vscode.WorkspaceFolder): Pr const api = new AtelierAPI(uri); const syncLocalChanges: string = vscode.workspace .getConfiguration("objectscript", wsFolder) - .get("syncLocalChanges"); + .get("syncLocalChanges")!; const sync: boolean = api.active && (syncLocalChanges == "all" || (syncLocalChanges == "vscodeOnly" && touchedByVSCode.has(uriString))); for (const subUriString of uris.keys()) { @@ -352,7 +353,7 @@ async function updateIndexInternal( if (!file) return result; result.addedOrChanged = file; if (isImportableLocalFile(uri) && sync) { - sendClientSideSyncTelemetryEvent(file.fileName.split(".").pop().toLowerCase()); + sendClientSideSyncTelemetryEvent(file.fileName.split(".").pop()!.toLowerCase()); } const documentUris = documents.get(file.name) ?? []; if (documentUris.some((u) => u.toString() == uriString)) { @@ -424,8 +425,10 @@ export function allDocumentsInWorkspace(wsFolder: vscode.WorkspaceFolder): strin } /** Get the class/routine name of the document in `uri` */ -export function getDocumentForUri(uri: vscode.Uri): string { - return wsFolderIndex.get(vscode.workspace.getWorkspaceFolder(uri)?.uri.toString())?.uris.get(uri.toString()); +export function getDocumentForUri(uri: vscode.Uri): string | undefined { + return wsFolderIndex + .get(vscode.workspace.getWorkspaceFolder(uri)?.uri.toString() as string) + ?.uris.get(uri.toString()); } /** @@ -468,7 +471,7 @@ export function inferDocName(uri: vscode.Uri): string | undefined { // is necessary for the rare situaions for documents in /foo/bar/ have a different mapping than /foo/ // and the target URI is in /foo/bar/ or a subfolder of it. const containingPathsSorted = Array.from(containingPaths).sort((a, b) => b.length - a.length); - let result: string; + let result: string | undefined; for (const prefix of containingPathsSorted) { if (uri.path.startsWith(prefix)) { // We've identified the leading path segments that don't contribute to the document diff --git a/src/utils/documentPicker.ts b/src/utils/documentPicker.ts index e9388a41..cca9b28e 100644 --- a/src/utils/documentPicker.ts +++ b/src/utils/documentPicker.ts @@ -39,7 +39,7 @@ function createMultiSelectItem( delim = "/"; } result.fullName = parent + delim + item.Name; - result.label = " ".repeat(parentPad + 2) + result.label; + result.label = " ".repeat(parentPad! + 2) + result.label; result.description = result.fullName; } if (item.Type == 9 || item.Type == 10) { @@ -193,11 +193,11 @@ export async function pickDocuments(api: AtelierAPI, prompt?: string): Promise i.fullName === event.item.fullName); - if (event.button.tooltip.charAt(0) == "E") { + if (event.button.tooltip!.charAt(0) == "E") { // Expand this item expandItem(itemIdx); } else { @@ -233,8 +233,8 @@ export async function pickDocuments(api: AtelierAPI, prompt?: string): Promise { +): Promise { let sys: "0" | "1" = "0"; let gen: "0" | "1" = "0"; let map: "0" | "1" = "1"; @@ -298,7 +298,7 @@ export async function pickDocument( // Hiding these files in other cases will improve performance. const showWeb = typeof api.wsOrFile == "object" && !notIsfs(api.wsOrFile) && isfsConfig(api.wsOrFile).csp; - return new Promise((resolve) => { + return new Promise((resolve) => { const quickPick = vscode.window.createQuickPick(); quickPick.prompt = "You may also type a full document name with extension into the filter box and press 'Enter' to select it."; @@ -356,11 +356,11 @@ export async function pickDocument( quickPick.hide(); } if (button.tooltip == "System") { - sys = button.toggle.checked ? "1" : "0"; + sys = button.toggle!.checked ? "1" : "0"; } else if (button.tooltip == "Generated") { - gen = button.toggle.checked ? "1" : "0"; + gen = button.toggle!.checked ? "1" : "0"; } else { - map = button.toggle.checked ? "1" : "0"; + map = button.toggle!.checked ? "1" : "0"; } // Refresh the items list getItems(); diff --git a/src/utils/index.ts b/src/utils/index.ts index ae29465a..fc633d8b 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -171,10 +171,10 @@ function otherDocExtsForUri(uri: vscode.Uri): string[] { } /** Determine the server name of a non-`isfs` non-ObjectScript file (any file that's not CLS,MAC,INT,INC). */ -export function getServerDocName(uri: vscode.Uri): string { +export function getServerDocName(uri: vscode.Uri): string | undefined { const wsFolder = vscode.workspace.getWorkspaceFolder(uri); if (!wsFolder) return; - const cspIdx = uri.path.lastIndexOf(cspAppsForUri(uri).find((cspApp) => uri.path.includes(cspApp + "/"))); + const cspIdx = uri.path.lastIndexOf(cspAppsForUri(uri).find((cspApp) => uri.path.includes(cspApp + "/"))!); if (cspIdx != -1) { return uri.path.slice(cspIdx); } else if (uri.path.toLowerCase().endsWith(".dfi")) { @@ -184,8 +184,8 @@ export function getServerDocName(uri: vscode.Uri): string { if (relativeFilePath == "") return; // uri isn't in the workspace folder. Should never happen. // Check for matching export settings first. If no match, use base name. const config = vscode.workspace.getConfiguration("objectscript.export", uri); - const folder: string = config.get("folder"); - const addCategory: boolean = config.get("addCategory"); + const folder: string = config.get("folder")!; + const addCategory: boolean = config.get("addCategory")!; let root = [ typeof folder == "string" && folder.length ? folder : null, addCategory ? getCategory(uri.fsPath, addCategory) : null, @@ -218,7 +218,7 @@ export function isImportableLocalFile(uri: vscode.Uri): boolean { if (!vscode.workspace.getWorkspaceFolder(uri)) return false; return ( cspAppsForUri(uri).some((cspApp) => uri.path.includes(cspApp + "/")) || - otherDocExtsForUri(uri).includes(uri.path.split(".").pop().toLowerCase()) + otherDocExtsForUri(uri).includes(uri.path.split(".").pop()!.toLowerCase()) ); } @@ -228,14 +228,17 @@ export const classNameRegex = /^[ \t]*Class[ \t]+(%?[\p{L}\d\u{100}-\u{ffff}]+(? /** A regex for extracting the name and type of a routine from its content */ export const routineNameTypeRegex = /^ROUTINE ([^\s]+)(?:\s*\[\s*Type\s*=\s*\b([a-z]{3})\b)?/i; -export function currentFileFromContent(uri: vscode.Uri, content: string | Buffer): CurrentTextFile | CurrentBinaryFile { +export function currentFileFromContent( + uri: vscode.Uri, + content: string | Buffer +): CurrentTextFile | CurrentBinaryFile | null { const fileName = uri.fsPath; const workspaceFolder = workspaceFolderOfUri(uri); if (!workspaceFolder) { // No workspace folders are open return null; } - const fileExt = fileName.split(".").pop().toLowerCase(); + const fileExt = fileName.split(".").pop()!.toLowerCase(); if ( notIsfs(uri) && !isClassOrRtn(uri.path) && @@ -258,7 +261,7 @@ export function currentFileFromContent(uri: vscode.Uri, content: string | Buffer [, name, ext = "mac"] = match; } } else { - name = notIsfs(uri) ? getServerDocName(uri) : isfsDocumentName(uri); + name = notIsfs(uri) ? getServerDocName(uri)! : isfsDocumentName(uri); } if (!name) { return null; @@ -288,7 +291,7 @@ export function currentFileFromContent(uri: vscode.Uri, content: string | Buffer } } -export function currentFile(document?: vscode.TextDocument): CurrentTextFile { +export function currentFile(document?: vscode.TextDocument | null): CurrentTextFile | null { document = document || (vscode.window.activeTextEditor && vscode.window.activeTextEditor.document @@ -298,7 +301,7 @@ export function currentFile(document?: vscode.TextDocument): CurrentTextFile { return null; } const fileName = document.fileName; - const fileExt = fileName.split(".").pop().toLowerCase(); + const fileExt = fileName.split(".").pop()!.toLowerCase(); if ( notIsfs(document.uri) && !isClassOrRtn(document.uri.path) && @@ -324,7 +327,7 @@ export function currentFile(document?: vscode.TextDocument): CurrentTextFile { [, name, ext = "mac"] = match; } } else { - name = notIsfs(uri) ? getServerDocName(uri) : isfsDocumentName(uri); + name = notIsfs(uri) ? getServerDocName(uri)! : isfsDocumentName(uri); } if (!name) { return null; @@ -385,7 +388,7 @@ export function connectionTarget(uri?: vscode.Uri): ConnectionTarget { result.configName = parts.length === 2 ? parts[0] : firstFolder.uri.authority; result.apiTarget = firstFolder.uri; } else { - result.configName = workspaceState.get("workspaceFolder") || firstFolder ? firstFolder.name : ""; + result.configName = workspaceState.get("workspaceFolder") || firstFolder ? firstFolder!.name : ""; result.apiTarget = result.configName; } } @@ -411,7 +414,7 @@ export function currentWorkspaceFolder(document?: vscode.TextDocument): string { if (firstFolder && schemas.includes(firstFolder.uri.scheme)) { return firstFolder.uri.authority; } else { - return workspaceState.get("workspaceFolder") || firstFolder ? firstFolder.name : ""; + return workspaceState.get("workspaceFolder") || firstFolder ? firstFolder!.name : ""; } } @@ -426,7 +429,7 @@ export function workspaceFolderOfUri(uri: vscode.Uri): string { return vscode.workspace.getWorkspaceFolder(uri)?.name ?? ""; } else { const rootUri = uri.with({ path: "/" }).toString(); - const foundFolder = vscode.workspace.workspaceFolders.find( + const foundFolder = vscode.workspace.workspaceFolders!.find( (workspaceFolder) => workspaceFolder.uri.toString() == rootUri ); return foundFolder ? foundFolder.name : uri.authority; @@ -491,14 +494,19 @@ export async function portFromDockerCompose( return { docker: false, port: null, superserverPort: null }; } - const result = { port: null, superserverPort: null, docker: true, service }; + const result: { port: number | null; superserverPort: number | null; docker: boolean; service?: string } = { + port: null, + superserverPort: null, + docker: true, + service, + }; const workspaceFolder = uriOfWorkspaceFolder(workspaceFolderName); if (!workspaceFolder) { // No workspace folders are open return { docker: false, port: null, superserverPort: null }; } const workspaceFolderPath = workspaceFolder.fsPath; - const workspaceRootPath = vscode.workspace.workspaceFolders[0].uri.fsPath; + const workspaceRootPath = vscode.workspace.workspaceFolders![0].uri.fsPath; const cwd: string = await fileExists(vscode.Uri.file(path.join(workspaceFolderPath, file))).then(async (exists) => { if (exists) { @@ -631,7 +639,9 @@ interface WSServerRootFolderData { const wsServerRootFolders = new Map(); /** Cache information about redirection of `.vscode` folder contents for server-side folders */ -export async function addWsServerRootFolderData(wsFolders: readonly vscode.WorkspaceFolder[]): Promise { +export async function addWsServerRootFolderData( + wsFolders: readonly vscode.WorkspaceFolder[] | undefined +): Promise { if (!wsFolders?.length) return; return Promise.allSettled( wsFolders.map(async (wsFolder) => { @@ -662,10 +672,10 @@ export async function addWsServerRootFolderData(wsFolders: readonly vscode.Works webApps = await api .getCSPApps(false, "%SYS") .then((data) => data.result.content ?? []) - .catch(() => []); - cspApps.set(key, webApps); + .catch((): string[] => []); + cspApps.set(key, webApps!); } - value.canRedirectDotvscode = webApps.includes("/_vscode"); + value.canRedirectDotvscode = webApps!.includes("/_vscode"); } wsServerRootFolders.set(wsFolder.uri.toString(), value); }) @@ -772,7 +782,7 @@ export function parseClassMemberDefinition( document: vscode.TextDocument, symbol: vscode.DocumentSymbol, symbolLine?: number -): { definition: string; defEndLine: number; language: string; isPrivate: boolean } { +): { definition: string; defEndLine: number; language: string; isPrivate: boolean } | undefined { const languageServer: boolean = vscode.extensions.getExtension(lsExtensionId)?.isActive ?? false; if (symbolLine == undefined) { if (languageServer) { @@ -786,9 +796,9 @@ export function parseClassMemberDefinition( } } } - let definition: string; - let defEndLine: number; - for (let defLine = symbolLine; defLine < document.lineCount; defLine++) { + let definition: string | undefined; + let defEndLine: number | undefined; + for (let defLine = symbolLine!; defLine < document.lineCount; defLine++) { const line = document.lineAt(defLine); if (line.text.trimEnd().endsWith("{")) { definition = document.getText( @@ -804,7 +814,7 @@ export function parseClassMemberDefinition( const privateMatch = definitionNoDelimitedValues.match(privateRegex); return { definition, - defEndLine, + defEndLine: defEndLine!, language: languageMatch && languageMatch[1] ? languageMatch[1].toLowerCase() : "objectscript", isPrivate: privateMatch != null, }; @@ -838,10 +848,10 @@ export function methodOffsetToLine( method: string, offset = 0 ): number | undefined { - let line: number; + let line: number | undefined; const languageServer: boolean = vscode.extensions.getExtension(lsExtensionId)?.isActive ?? false; // Find the DocumentSymbol for this method - let currentSymbol: vscode.DocumentSymbol; + let currentSymbol: vscode.DocumentSymbol | undefined; for (const symbol of members) { if (stripClassMemberNameQuotes(symbol.name) === method && symbol.detail.toLowerCase().includes("method")) { currentSymbol = symbol; @@ -881,7 +891,7 @@ export function base64EncodeContent(content: Buffer): string[] { // Output is 4 chars for each 3 input, so 24573/3*4 = 32764 const chunkSize = 24573; let start = 0; - const result = []; + const result: string[] = []; while (start < content.byteLength) { result.push(content.toString("base64", start, start + chunkSize)); start += chunkSize; @@ -891,12 +901,12 @@ export function base64EncodeContent(content: Buffer): string[] { /** Returns `true` if `uri` has a class file extension */ export function isClass(uriOrName: string): boolean { - return "cls" == uriOrName.split(".").pop().toLowerCase(); + return "cls" == uriOrName.split(".").pop()!.toLowerCase(); } /** Returns `true` if `uri` has a class or routine file extension */ export function isClassOrRtn(uriOrName: string): boolean { - return ["cls", "mac", "int", "inc"].includes(uriOrName.split(".").pop().toLowerCase()); + return ["cls", "mac", "int", "inc"].includes(uriOrName.split(".").pop()!.toLowerCase()); } interface ConnQPItem extends vscode.QuickPickItem { @@ -920,7 +930,7 @@ export async function getWsServerConnection(minVersion?: string): Promise(fn: () => Promise): Promise { + async call(fn: () => Promise | undefined): Promise { // Acquire a slot in the semaphore. Will not reject. await this._semaphore.acquire(); try { // Execute the provided function - return await fn(); + return (await fn())!; } finally { // Always release the slot in the semaphore after the function completes this._semaphore.release(); diff --git a/tsconfig.base.json b/tsconfig.base.json index 0a87024f..583acc95 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -5,6 +5,7 @@ "noUnusedLocals": true, "noUnusedParameters": false, "experimentalDecorators": true, - "resolveJsonModule": true + "resolveJsonModule": true, + "strictNullChecks": true, } }