From aa2250b3b722ac3290b446417ff5ee7dd8ae515f Mon Sep 17 00:00:00 2001 From: Cora Sutton Date: Sat, 30 Apr 2022 18:26:52 -0500 Subject: [PATCH 01/73] Fix nulls/undefineds and improve repl info parser for params --- src/providers/infoparser.ts | 58 ++++++++++++++++++++++++------------- 1 file changed, 38 insertions(+), 20 deletions(-) diff --git a/src/providers/infoparser.ts b/src/providers/infoparser.ts index fd27a266a..dadd28569 100644 --- a/src/providers/infoparser.ts +++ b/src/providers/infoparser.ts @@ -1,6 +1,7 @@ import { SignatureInformation, ParameterInformation, MarkdownString } from 'vscode'; import * as tokenCursor from '../cursor-doc/token-cursor'; import { getConfig } from '../config'; +import { isString } from 'lodash'; export type Completion = | [string, string] @@ -10,8 +11,16 @@ export type Completion = export class REPLInfoParser { private _name: string | undefined = undefined; + /* + * Different arities of arglists for a symbol. + * e.g. "[]" or "[xs]" or "[s re]\n[s re limit]" + */ private _arglist: string | undefined = undefined; + /* + * Different forms a special form can take. + * e.g. "(do exprs*)" or "(Classname. args*)\n(new Classname args*)" + */ private _formsString: string | undefined = undefined; private _docString: string | undefined = undefined; @@ -99,7 +108,7 @@ export class REPLInfoParser { getHover(): MarkdownString { const hover = new MarkdownString(); - if (this._name !== '') { + if (isString(this._name) && this._name !== '') { if (!this._specialForm || this._isMacro) { hover.appendCodeblock(this._name, 'clojure'); if (this._arglist) { @@ -117,7 +126,10 @@ export class REPLInfoParser { } else { hover.appendText('\n'); } - hover.appendMarkdown(this._docString); + + if (isString(this._docString)) { + hover.appendMarkdown(this._docString); + } } return hover; } @@ -145,27 +157,33 @@ export class REPLInfoParser { } getSignatures(symbol: string): SignatureInformation[] | undefined { - if (this._name !== '') { + if (isString(this._name) && this._name !== '') { const argLists = this._arglist ? this._arglist : this._formsString; - if (argLists) { - return argLists + if (isString(argLists) && argLists !== '') { + // Break down arglist or formsString into the different arglists/arities, + // removing empty strings since those make no sense as arglists. + const argListStrings = argLists .split('\n') - .map((argList) => argList.trim()) - .map((argList) => { - if (argList !== '') { - const signature = new SignatureInformation(`(${symbol} ${argList})`); - // Skip parameter help on special forms and forms with optional arguments, for now - if (this._arglist && !argList.match(/\?/)) { - signature.parameters = this.getParameters(symbol, argList); - } - if (this._docString && getConfig().showDocstringInParameterHelp) { - signature.documentation = new MarkdownString(this._docString); - } - return signature; - } else { - return undefined; + .map((a) => a.trim()) + .filter((a) => a !== ''); + + const signatures = argListStrings.map((argList) => { + const signature = new SignatureInformation(`(${symbol} ${argList})`); + // Skip parameter help on special forms and forms with optional arguments, for now + if (this._arglist && !argList.match(/\?/)) { + const parameters = this.getParameters(symbol, argList); + + if (parameters) { + signature.parameters = parameters; } - }); + } + if (this._docString && getConfig().showDocstringInParameterHelp) { + signature.documentation = new MarkdownString(this._docString); + } + return signature; + }); + + return signatures; } } return undefined; From a697c11e6b7a3be82a8a071bd01009724c0ff81c Mon Sep 17 00:00:00 2001 From: Cora Sutton Date: Sat, 30 Apr 2022 18:27:28 -0500 Subject: [PATCH 02/73] Stop using deprecated function and use our own instead --- src/utilities.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/utilities.ts b/src/utilities.ts index d79a8750c..b85c08e43 100644 --- a/src/utilities.ts +++ b/src/utilities.ts @@ -10,7 +10,6 @@ import * as outputWindow from './results-output/results-doc'; import * as cljsLib from '../out/cljs-lib/cljs-lib'; import * as url from 'url'; import { isUndefined } from 'lodash'; -import { isNullOrUndefined } from 'util'; const specialWords = ['-', '+', '/', '*']; //TODO: Add more here const syntaxQuoteSymbol = '`'; @@ -23,6 +22,9 @@ export function stripAnsi(str: string) { ); } +export const isNullOrUndefined = (object: unknown): object is null | undefined => + object === null || object === undefined; + export const isDefined = (value: T | undefined | null): value is T => { return !isNullOrUndefined(value); }; From 02d317d9df7a39225563a2e3e61632ee9c2b8710 Mon Sep 17 00:00:00 2001 From: Cora Sutton Date: Sat, 30 Apr 2022 18:27:48 -0500 Subject: [PATCH 03/73] Fix an undefined value in utilities --- src/utilities.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utilities.ts b/src/utilities.ts index b85c08e43..32feeca9f 100644 --- a/src/utilities.ts +++ b/src/utilities.ts @@ -90,7 +90,7 @@ async function quickPickMulti(opts: { values: string[]; saveAs: string; placeHol // Testing facility. // Recreated every time we create a new quickPick -let quickPickActive: Promise; +let quickPickActive: Promise | undefined; function quickPick( itemsToPick: string[], From 02960cacba7653ee90f5fbf6212055ba77f896a1 Mon Sep 17 00:00:00 2001 From: Cora Sutton Date: Sat, 30 Apr 2022 18:39:00 -0500 Subject: [PATCH 04/73] Help typescript out with some type assertions in cursor-doc model --- src/cursor-doc/model.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/cursor-doc/model.ts b/src/cursor-doc/model.ts index fcca51ff1..71ec97206 100644 --- a/src/cursor-doc/model.ts +++ b/src/cursor-doc/model.ts @@ -155,12 +155,12 @@ export class LineInputModel implements EditableModel { } return x; }) - .filter((x) => x !== null) + .filter((x) => x !== null) as number[] ); this.insertedLines = new Set( Array.from(this.insertedLines) - .map((x): [number, number] => { + .map((x) => { const [a, b] = x; if (a > start && a < start + deleted) { return null; @@ -170,12 +170,12 @@ export class LineInputModel implements EditableModel { } return [a, b]; }) - .filter((x) => x !== null) + .filter((x) => x !== null) as [number, number][] ); this.deletedLines = new Set( Array.from(this.deletedLines) - .map((x): [number, number] => { + .map((x) => { const [a, b] = x; if (a > start && a < start + deleted) { return null; @@ -185,7 +185,7 @@ export class LineInputModel implements EditableModel { } return [a, b]; }) - .filter((x) => x !== null) + .filter((x) => x !== null) as [number, number][] ); } @@ -224,7 +224,7 @@ export class LineInputModel implements EditableModel { const seen = new Set(); this.dirtyLines.sort(); while (this.dirtyLines.length) { - let nextIdx = this.dirtyLines.shift(); + let nextIdx = this.dirtyLines.shift() as number; if (seen.has(nextIdx)) { continue; } // already processed. From 0f721241c040896e03fa2ce14100cc232e370cc2 Mon Sep 17 00:00:00 2001 From: Cora Sutton Date: Sat, 30 Apr 2022 19:27:07 -0500 Subject: [PATCH 05/73] Fix null checks for project-types --- src/nrepl/project-types.ts | 86 ++++++++++++++++++++++++++------------ 1 file changed, 59 insertions(+), 27 deletions(-) diff --git a/src/nrepl/project-types.ts b/src/nrepl/project-types.ts index fe546463c..42c6e5b02 100644 --- a/src/nrepl/project-types.ts +++ b/src/nrepl/project-types.ts @@ -21,7 +21,7 @@ export type ProjectType = { processShellWin: boolean; processShellUnix: boolean; commandLine: (connectSequence: ReplConnectSequence, cljsType: CljsTypes) => any; - useWhenExists: string; + useWhenExists: string | undefined; nReplPortFile: string[]; }; @@ -30,8 +30,14 @@ function nreplPortFileRelativePath(connectSequence: ReplConnectSequence): string if (connectSequence.nReplPortFile) { subPath = path.join(...connectSequence.nReplPortFile); } else { - const projectType: ProjectType | string = connectSequence.projectType; - subPath = path.join(...getProjectTypeForName(projectType).nReplPortFile); + const projectTypeName: ProjectType | string = connectSequence.projectType; + const projectType = getProjectTypeForName(projectTypeName); + utilities.assertIsDefined( + projectType, + `Expected a project type given project type name of ${projectTypeName}` + ); + + subPath = path.join(...projectType.nReplPortFile); } return subPath; } @@ -69,7 +75,9 @@ export function nreplPortFileUri(connectSequence: ReplConnectSequence): vscode.U } export function shadowConfigFile(): vscode.Uri { - return vscode.Uri.joinPath(state.getProjectRootUri(), 'shadow-cljs.edn'); + const projectRootUri = state.getProjectRootUri(); + utilities.assertIsDefined(projectRootUri, 'Expected a project root URI'); + return vscode.Uri.joinPath(projectRootUri, 'shadow-cljs.edn'); } export async function shadowBuilds(): Promise { @@ -85,7 +93,7 @@ export async function shadowBuilds(): Promise { ]; } -export function leinShadowBuilds(defproject: any): string[] { +export function leinShadowBuilds(defproject: any): string[] | undefined { if (defproject) { const shadowIndex = defproject.indexOf('shadow-cljs'); if (shadowIndex > -1) { @@ -109,14 +117,17 @@ export function leinShadowBuilds(defproject: any): string[] { async function selectShadowBuilds( connectSequence: ReplConnectSequence, foundBuilds: string[] -): Promise<{ selectedBuilds: string[]; args: string[] }> { +): Promise<{ selectedBuilds: string[] | undefined; args: string[] }> { + const projectRootUri = state.getProjectRootUri(); + utilities.assertIsDefined(projectRootUri, 'Expected a project root URI'); + const menuSelections = connectSequence.menuSelections, selectedBuilds = menuSelections ? menuSelections.cljsLaunchBuilds : await utilities.quickPickMulti({ values: foundBuilds.filter((x) => x[0] == ':'), placeHolder: 'Select builds to start', - saveAs: `${state.getProjectRootUri().toString()}/shadow-cljs-jack-in`, + saveAs: `${projectRootUri.toString()}/shadow-cljs-jack-in`, }), aliases: string[] = menuSelections && menuSelections.cljAliases ? menuSelections.cljAliases.map(keywordize) : []; // TODO do the same as clj to prompt the user with a list of aliases @@ -129,8 +140,11 @@ async function selectShadowBuilds( } async function leinDefProject(): Promise { + const projectRootUri = state.getProjectRootUri(); + utilities.assertIsDefined(projectRootUri, 'Expected a project root URI'); + const bytes = await vscode.workspace.fs.readFile( - vscode.Uri.joinPath(state.getProjectRootUri(), 'project.clj') + vscode.Uri.joinPath(projectRootUri, 'project.clj') ); const data = new TextDecoder('utf-8').decode(bytes); try { @@ -145,9 +159,9 @@ async function leinDefProject(): Promise { async function leinProfilesAndAlias( defproject: any, connectSequence: ReplConnectSequence -): Promise<{ profiles: string[]; alias: string }> { +): Promise<{ profiles: string[]; alias: string | undefined }> { let profiles: string[] = [], - alias: string; + alias: string | undefined; if (defproject) { const aliasesIndex = defproject.indexOf('aliases'); @@ -164,13 +178,17 @@ async function leinProfilesAndAlias( const aliasesMap = defproject[aliasesIndex + 1]; aliases = Object.keys(aliasesMap).map((v, k) => v); if (aliases.length) { + const projectRootUri = state.getProjectRootUri(); + utilities.assertIsDefined(projectRootUri, 'Expected a project root URI'); + + state.getProjectRootUri(); aliases.unshift('No alias'); alias = await utilities.quickPickSingle({ values: aliases, - saveAs: `${state.getProjectRootUri().toString()}/lein-cli-alias`, + saveAs: `${projectRootUri.toString()}/lein-cli-alias`, placeHolder: 'Choose alias to launch with', }); - alias = alias == 'No alias' ? undefined : alias; + alias = alias === 'No alias' ? undefined : alias; } } } catch (error) { @@ -193,9 +211,12 @@ async function leinProfilesAndAlias( if (projectProfiles.length) { profiles = projectProfiles.map(keywordize); if (profiles.length) { + const projectRootUri = state.getProjectRootUri(); + utilities.assertIsDefined(projectRootUri, 'Expected a project root URI'); + profiles = await utilities.quickPickMulti({ values: profiles, - saveAs: `${state.getProjectRootUri().toString()}/lein-cli-profiles`, + saveAs: `${projectRootUri.toString()}/lein-cli-profiles`, placeHolder: 'Pick any profiles to launch with', }); } @@ -211,9 +232,15 @@ export enum JackInDependency { 'cider/piggieback' = 'cider/piggieback', } -const NREPL_VERSION = () => getConfig().jackInDependencyVersions['nrepl'], - CIDER_NREPL_VERSION = () => getConfig().jackInDependencyVersions['cider-nrepl'], - PIGGIEBACK_VERSION = () => getConfig().jackInDependencyVersions['cider/piggieback']; +const jackInDependencyVersions = getConfig().jackInDependencyVersions; +utilities.assertIsDefined( + jackInDependencyVersions, + 'Expected jackInDependencyVersions to be set in the config!' +); + +const NREPL_VERSION = () => jackInDependencyVersions['nrepl'], + CIDER_NREPL_VERSION = () => jackInDependencyVersions['cider-nrepl'], + PIGGIEBACK_VERSION = () => jackInDependencyVersions['cider/piggieback']; const cliDependencies = () => { return { @@ -386,11 +413,10 @@ const projectTypes: { [id: string]: ProjectType } = { const chan = state.outputChannel(); const defproject = await leinDefProject(); - const foundBuilds = leinShadowBuilds(defproject), - { selectedBuilds, args: extraArgs } = await selectShadowBuilds( - connectSequence, - foundBuilds - ); + const foundBuilds = leinShadowBuilds(defproject); + utilities.assertIsDefined(foundBuilds, 'Expected to find lein shadow builds for project!'); + + const { selectedBuilds } = await selectShadowBuilds(connectSequence, foundBuilds); if (selectedBuilds && selectedBuilds.length) { return leinCommandLine(['shadow', 'watch', ...selectedBuilds], cljsType, connectSequence); @@ -448,16 +474,20 @@ const projectTypes: { [id: string]: ProjectType } = { async function cljCommandLine(connectSequence: ReplConnectSequence, cljsType: CljsTypes) { const out: string[] = []; let depsUri: vscode.Uri; + + const projectRootUri = state.getProjectRootUri(); + utilities.assertIsDefined(projectRootUri, 'Expected a project root URI'); + try { - depsUri = vscode.Uri.joinPath(state.getProjectRootUri(), 'deps.edn'); + depsUri = vscode.Uri.joinPath(projectRootUri, 'deps.edn'); } catch { - depsUri = vscode.Uri.file(path.join(state.getProjectRootUri().fsPath, 'deps.edn')); + depsUri = vscode.Uri.file(path.join(projectRootUri.fsPath, 'deps.edn')); } let parsed; if (connectSequence.projectType !== 'generic') { void vscode.workspace.fs.stat(depsUri); const bytes = await vscode.workspace.fs.readFile( - vscode.Uri.joinPath(state.getProjectRootUri(), 'deps.edn') + vscode.Uri.joinPath(projectRootUri, 'deps.edn') ); const data = new TextDecoder('utf-8').decode(bytes); try { @@ -510,7 +540,7 @@ async function cljCommandLine(connectSequence: ReplConnectSequence, cljsType: Cl if (projectAliases.length) { aliases = await utilities.quickPickMulti({ values: projectAliases.map(keywordize), - saveAs: `${state.getProjectRootUri().toString()}/clj-cli-aliases`, + saveAs: `${projectRootUri.toString()}/clj-cli-aliases`, placeHolder: 'Pick any aliases to launch with', }); } @@ -616,13 +646,15 @@ export function getProjectTypeForName(name: string) { } export async function detectProjectTypes(): Promise { - const rootUri = state.getProjectRootUri(); + const projectRootUri = state.getProjectRootUri(); + utilities.assertIsDefined(projectRootUri, 'Expected a project root URI'); const cljProjTypes = ['generic', 'cljs-only', 'babashka', 'nbb']; for (const clj in projectTypes) { if (projectTypes[clj].useWhenExists) { try { const projectFileName = projectTypes[clj].useWhenExists; - const uri = vscode.Uri.joinPath(rootUri, projectFileName); + utilities.assertIsDefined(projectFileName, 'Expected there to be a project filename!'); + const uri = vscode.Uri.joinPath(projectRootUri, projectFileName); await vscode.workspace.fs.readFile(uri); cljProjTypes.push(clj); } catch { From 942665007d612287c542d5c2cea5e26c1ae0cc14 Mon Sep 17 00:00:00 2001 From: Cora Sutton Date: Sat, 30 Apr 2022 19:27:38 -0500 Subject: [PATCH 06/73] Fix null checks for completion provider --- src/providers/completion.ts | 59 ++++++++++++++++++++++++------------- 1 file changed, 38 insertions(+), 21 deletions(-) diff --git a/src/providers/completion.ts b/src/providers/completion.ts index 45b21d150..e3d7da205 100644 --- a/src/providers/completion.ts +++ b/src/providers/completion.ts @@ -3,12 +3,11 @@ import { Position, CancellationToken, CompletionContext, - Hover, CompletionItemKind, window, CompletionList, CompletionItemProvider, - CompletionItem, + CompletionItem as VSCompletionItem, CompletionItemLabel, } from 'vscode'; import * as util from '../utilities'; @@ -20,6 +19,7 @@ import * as replSession from '../nrepl/repl-session'; import { getClient } from '../lsp/main'; import { CompletionRequest, CompletionResolveRequest } from 'vscode-languageserver-protocol'; import { createConverter } from 'vscode-languageclient/lib/common/protocolConverter'; +import { CompletionItem as LSPCompletionItem } from 'vscode-languageclient'; const mappings = { nil: CompletionItemKind.Value, @@ -35,7 +35,10 @@ const mappings = { const converter = createConverter(undefined, undefined); -const completionProviderOptions = { priority: ['lsp', 'repl'], merge: true }; +const completionProviderOptions: { priority: ['lsp', 'repl']; merge: boolean } = { + priority: ['lsp', 'repl'], + merge: true, +}; const completionFunctions = { lsp: lspCompletions, repl: replCompletions }; @@ -45,25 +48,39 @@ async function provideCompletionItems( token: CancellationToken, context: CompletionContext ) { - let results = []; + let results: (VSCompletionItem | LSPCompletionItem)[] = []; for (const provider of completionProviderOptions.priority) { if (results.length && !completionProviderOptions.merge) { break; } - const completions = await completionFunctions[provider](document, position, token, context); - - if (completions) { - results = [ - ...completions - .concat(results) - .reduce( - (m: Map, o: CompletionItem) => - m.set(o.label, Object.assign(m.get(o.label) || {}, o)), - new Map() - ) - .values(), - ]; + + const completionResult = await completionFunctions[provider]( + document, + position, + token, + context + ); + + if (completionResult === null) { + continue; } + + const completions: (VSCompletionItem | LSPCompletionItem)[] = Array.isArray(completionResult) + ? completionResult + : completionResult.items; + + results = [ + ...completions + .concat(results) + .reduce( + ( + m: Map, + o: VSCompletionItem | LSPCompletionItem + ) => m.set(o.label, Object.assign(m.get(o.label) || {}, o)), + new Map() + ) + .values(), + ]; } return new CompletionList(results.map(converter.asCompletionItem), true); @@ -79,7 +96,7 @@ export default class CalvaCompletionItemProvider implements CompletionItemProvid return provideCompletionItems(document, position, token, context); } - async resolveCompletionItem(item: CompletionItem, token: CancellationToken) { + async resolveCompletionItem(item: VSCompletionItem, token: CancellationToken) { if (util.getConnectedState() && item['data']?.provider === 'repl') { const activeTextEditor = window.activeTextEditor; @@ -121,7 +138,7 @@ async function lspCompletions( ); } -async function lspResolveCompletions(item: CompletionItem, token: CancellationToken) { +async function lspResolveCompletions(item: VSCompletionItem, token: CancellationToken) { const lspClient = await getClient(20); return lspClient.sendRequest( CompletionResolveRequest.type, @@ -135,7 +152,7 @@ async function replCompletions( position: Position, _token: CancellationToken, _context: CompletionContext -): Promise { +): Promise { if (!util.getConnectedState()) { return []; } @@ -171,7 +188,7 @@ async function replCompletions( } }); return results.map((item) => { - const result = new CompletionItem( + const result = new VSCompletionItem( item.candidate, mappings[item.type] || CompletionItemKind.Text ); From c73129ee422a58e764a1a7a6b5915bb8640dddd1 Mon Sep 17 00:00:00 2001 From: Cora Sutton Date: Sat, 30 Apr 2022 19:55:49 -0500 Subject: [PATCH 07/73] Fix nulls in src/connector.ts --- src/connector.ts | 97 +++++++++++++++++++------------ src/results-output/results-doc.ts | 2 +- 2 files changed, 60 insertions(+), 39 deletions(-) diff --git a/src/connector.ts b/src/connector.ts index 935c0711f..9573a86fa 100644 --- a/src/connector.ts +++ b/src/connector.ts @@ -39,6 +39,7 @@ async function readJarContent(uri: string) { } async function readRuntimeConfigs() { + util.assertIsDefined(nClient, 'Expected there to be an nREPL client!'); const classpath = await nClient.session.classpath().catch((e) => { console.error('readRuntimeConfigs:', e); }); @@ -79,7 +80,10 @@ async function connectToHost(hostname: string, port: number, connectSequence: Re host: hostname, port: +port, onError: (e) => { - const scheme = state.getProjectRootUri().scheme; + const projectRootUri = state.getProjectRootUri(); + util.assertIsDefined(projectRootUri, 'Expected a project root URI'); + + const scheme = projectRootUri.scheme; if (scheme === 'vsls') { outputWindow.append('; nREPL connection failed; did the host share the nREPL port?'); } @@ -112,12 +116,9 @@ async function connectToHost(hostname: string, port: number, connectSequence: Re if (connectSequence.afterCLJReplJackInCode) { outputWindow.append(`\n; Evaluating 'afterCLJReplJackInCode'`); - await evaluateInOutputWindow( - connectSequence.afterCLJReplJackInCode, - 'clj', - outputWindow.getNs(), - {} - ); + const ns = outputWindow.getNs(); + util.assertIsDefined(ns, 'Expected outputWindow to have a namespace!'); + await evaluateInOutputWindow(connectSequence.afterCLJReplJackInCode, 'clj', ns, {}); } outputWindow.appendPrompt(); @@ -188,7 +189,10 @@ function setUpCljsRepl(session, build) { } async function getFigwheelMainBuilds() { - const res = await vscode.workspace.fs.readDirectory(state.getProjectRootUri()); + const projectRootUri = state.getProjectRootUri(); + util.assertIsDefined(projectRootUri, 'Expected a project root URI'); + + const res = await vscode.workspace.fs.readDirectory(projectRootUri); const builds = res .filter(([name, type]) => type !== vscode.FileType.Directory && name.match(/\.cljs\.edn/)) .map(([name, _]) => name.replace(/\.cljs\.edn$/, '')); @@ -371,20 +375,28 @@ function createCLJSReplType( 'cljsReplTypeHasBuilds', cljsType.buildsRequired ); - let initCode = cljsType.connectCode, - build: string = null; + let initCode: typeof cljsType.connectCode | undefined = cljsType.connectCode, + build: string | null = null; if (menuSelections && menuSelections.cljsDefaultBuild && useDefaultBuild) { build = menuSelections.cljsDefaultBuild; useDefaultBuild = false; } else { if (typeof initCode === 'object' || initCode.includes('%BUILD%')) { + const projectRootUri = state.getProjectRootUri(); + util.assertIsDefined(projectRootUri, 'Expected a project root URI'); + + const buildsForSelection = startedBuilds + ? startedBuilds + : await figwheelOrShadowBuilds(cljsTypeName); + util.assertIsDefined( + buildsForSelection, + 'Expected there to be figwheel or shadowcljs builds!' + ); + build = await util.quickPickSingle({ - values: startedBuilds ? startedBuilds : await figwheelOrShadowBuilds(cljsTypeName), + values: buildsForSelection, placeHolder: 'Select which build to connect to', - saveAs: `${state.getProjectRootUri().toString()}/${cljsTypeName.replace( - ' ', - '-' - )}-build`, + saveAs: `${projectRootUri.toString()}/${cljsTypeName.replace(' ', '-')}-build`, autoSelect: true, }); } @@ -415,12 +427,10 @@ function createCLJSReplType( ); }, connected: (result, out, err) => { - if (cljsType.isConnectedRegExp) { - return ( - [...out, result].find((x) => { - return x.search(cljsType.isConnectedRegExp) >= 0; - }) != undefined - ); + const { isConnectedRegExp } = cljsType; + + if (isConnectedRegExp) { + return [...out, result].find((x) => x.search(isConnectedRegExp) >= 0) !== undefined; } else { return true; } @@ -431,22 +441,23 @@ function createCLJSReplType( replType.start = async (session, name, checkFn) => { let startCode = cljsType.startCode; if (!hasStarted) { - if (startCode.includes('%BUILDS')) { + if (startCode && startCode.includes('%BUILDS')) { let builds: string[]; if (menuSelections && menuSelections.cljsLaunchBuilds) { builds = menuSelections.cljsLaunchBuilds; } else { const allBuilds = await figwheelOrShadowBuilds(cljsTypeName); + util.assertIsDefined(allBuilds, 'Expected there to be figwheel or shadowcljs builds!'); + const projectRootUri = state.getProjectRootUri(); + util.assertIsDefined(projectRootUri, 'Expected a project root URI'); + builds = allBuilds.length <= 1 ? allBuilds : await util.quickPickMulti({ values: allBuilds, placeHolder: 'Please select which builds to start', - saveAs: `${state.getProjectRootUri().toString()}/${cljsTypeName.replace( - ' ', - '-' - )}-builds`, + saveAs: `${projectRootUri.toString()}/${cljsTypeName.replace(' ', '-')}-builds`, }); } if (builds) { @@ -476,7 +487,7 @@ function createCLJSReplType( outputWindow.append('; Aborted starting cljs repl.'); throw 'Aborted'; } - } else { + } else if (startCode) { outputWindow.append('; Starting cljs repl for: ' + projectTypeName + '...'); return evalConnectCode( session, @@ -494,11 +505,11 @@ function createCLJSReplType( } replType.started = (result, out, err) => { - if (cljsType.isReadyToStartRegExp && !hasStarted) { + const { isReadyToStartRegExp } = cljsType; + + if (isReadyToStartRegExp && !hasStarted) { const started = - [...out, ...err].find((x) => { - return x.search(cljsType.isReadyToStartRegExp) >= 0; - }) != undefined; + [...out, ...err].find((x) => x.search(isReadyToStartRegExp) >= 0) !== undefined; if (started) { hasStarted = true; } @@ -512,7 +523,7 @@ function createCLJSReplType( return replType; } -async function makeCljsSessionClone(session, repl: ReplType, projectTypeName: string) { +async function makeCljsSessionClone(session, repl: ReplType, projectTypeName: string | undefined) { outputWindow.append('; Creating cljs repl session...'); let newCljsSession = await session.clone(); newCljsSession.replType = 'cljs'; @@ -521,7 +532,8 @@ async function makeCljsSessionClone(session, repl: ReplType, projectTypeName: st outputWindow.append( '; The Calva Connection Log might have more connection progress information.' ); - if (repl.start != undefined) { + if (repl.start !== undefined) { + util.assertIsDefined(repl.started, "Expected repl to have a 'started' check function!"); if (await repl.start(newCljsSession, repl.name, repl.started)) { state.analytics().logEvent('REPL', 'StartedCLJS', repl.name).send(); outputWindow.append('; Cljs builds started'); @@ -534,6 +546,9 @@ async function makeCljsSessionClone(session, repl: ReplType, projectTypeName: st return [null, null]; } } + + util.assertIsDefined(repl.connect, 'Expected repl to have a connect function!'); + if (await repl.connect(newCljsSession, repl.name, repl.connected)) { state.analytics().logEvent('REPL', 'ConnectedCLJS', repl.name).send(); setStateValue('cljs', (cljsSession = newCljsSession)); @@ -588,7 +603,7 @@ async function promptForNreplUrlAndConnect(port, connectSequence: ReplConnectSeq return true; } -export let nClient: NReplClient; +export let nClient: NReplClient | undefined; export let cljSession: NReplSession; export let cljsSession: NReplSession; @@ -694,13 +709,17 @@ export default { status.update(); if (nClient) { - if (state.getProjectRootUri().scheme === 'vsls') { + const projectRootUri = state.getProjectRootUri(); + util.assertIsDefined(projectRootUri, 'Expected a project root URI'); + + if (projectRootUri.scheme === 'vsls') { nClient.disconnect(); } else { // the connection may be ended before // the REPL client was connected. nClient.close(); } + liveShareSupport.didDisconnectRepl(); nClient = undefined; } @@ -708,7 +727,7 @@ export default { callback(); }, toggleCLJCSession: () => { - let newSession: NReplSession; + let newSession: NReplSession | undefined; if (getStateValue('connected')) { if (replSession.getSession('cljc') == replSession.getSession('cljs')) { @@ -727,8 +746,10 @@ export default { }, switchCljsBuild: async () => { const cljSession = replSession.getSession('clj'); - const cljsTypeName: string = state.extensionContext.workspaceState.get('selectedCljsTypeName'), - cljTypeName: string = state.extensionContext.workspaceState.get('selectedCljTypeName'); + const cljsTypeName: string | undefined = + state.extensionContext.workspaceState.get('selectedCljsTypeName'), + cljTypeName: string | undefined = + state.extensionContext.workspaceState.get('selectedCljTypeName'); state.analytics().logEvent('REPL', 'switchCljsBuild', cljsTypeName).send(); const [session, build] = await makeCljsSessionClone( diff --git a/src/results-output/results-doc.ts b/src/results-output/results-doc.ts index 144e12c05..6d3d8b021 100644 --- a/src/results-output/results-doc.ts +++ b/src/results-output/results-doc.ts @@ -88,7 +88,7 @@ export function getSession(): NReplSession | undefined { return _sessionInfo[_sessionType].session; } -export function setSession(session: NReplSession, newNs?: string): void { +export function setSession(session: NReplSession | undefined, newNs?: string): void { if (session) { if (session.replType) { _sessionType = session.replType; From 3dd6b85e643e18449128351bf79f1e92eaa2a953 Mon Sep 17 00:00:00 2001 From: Cora Sutton Date: Sat, 30 Apr 2022 19:58:01 -0500 Subject: [PATCH 08/73] Fix null in src/converters.ts --- src/converters.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/converters.ts b/src/converters.ts index 0ecdb2efe..d6667bd48 100644 --- a/src/converters.ts +++ b/src/converters.ts @@ -1,5 +1,6 @@ import * as vscode from 'vscode'; import * as calvaLib from '../out/cljs-lib/cljs-lib'; +import { getActiveTextEditor } from './utilities'; type Js2CljsResult = { result: string; @@ -23,7 +24,7 @@ type Js2CljsInvalidResult = { const isJs2CljsResult = (input: any): input is Js2CljsResult => input.result !== undefined; export async function js2cljs() { - const editor = vscode.window.activeTextEditor; + const editor = getActiveTextEditor(); const selection = editor.selection; const doc = editor.document; const js = doc.getText( From 6e6640f4eafd3d00471417ad5703725168b50961 Mon Sep 17 00:00:00 2001 From: Cora Sutton Date: Sat, 30 Apr 2022 20:17:20 -0500 Subject: [PATCH 09/73] Fix nulls in cursor-doc indent and improve performance --- src/cursor-doc/indent.ts | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/src/cursor-doc/indent.ts b/src/cursor-doc/indent.ts index 26b4d23a0..66d693303 100644 --- a/src/cursor-doc/indent.ts +++ b/src/cursor-doc/indent.ts @@ -18,7 +18,7 @@ const indentRules: IndentRules = { */ export interface IndentInformation { /** The first token in the expression (after the open paren/bracket etc.), as a raw string */ - first: string; + first: string | null; /** The indent immediately after the open paren/bracket etc */ startIndent: number; @@ -61,6 +61,17 @@ export function collectIndents( let lastIndent = 0; const indents: IndentInformation[] = []; const rules = config['cljfmt-options']['indents']; + const patterns = _.keys(rules); + const regexpPatterns = patterns.reduce((regexpMap, pattern) => { + const match = pattern.match(/^#"(.*)"$/); + + if (match) { + regexpMap.set(pattern, RegExp(match[1])); + } + + return regexpMap; + }, new Map()); + do { if (!cursor.backwardSexp()) { // this needs some work.. @@ -91,7 +102,10 @@ export function collectIndents( const pattern = isList && - _.find(_.keys(rules), (pattern) => pattern === token || testCljRe(pattern, token)); + patterns.find( + (pattern) => + pattern === token || (regexpPatterns[pattern] && regexpPatterns[pattern].test(token)) + ); const indentRule = pattern ? rules[pattern] : []; indents.unshift({ first: token, @@ -138,11 +152,6 @@ export function collectIndents( return indents; } -const testCljRe = (re, str) => { - const matches = re.match(/^#"(.*)"$/); - return matches && RegExp(matches[1]).test(str); -}; - /** Returns the expected newline indent for the given position, in characters. */ export function getIndent(document: EditableModel, offset: number, config?: any): number { if (!config) { From 5aad863dc81718289ad4769325ac5feeb99c9236 Mon Sep 17 00:00:00 2001 From: Cora Sutton Date: Sat, 30 Apr 2022 21:16:47 -0500 Subject: [PATCH 10/73] Fix nulls in clojure-lexer --- src/cursor-doc/clojure-lexer.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cursor-doc/clojure-lexer.ts b/src/cursor-doc/clojure-lexer.ts index 6ed535e4b..6a0285848 100644 --- a/src/cursor-doc/clojure-lexer.ts +++ b/src/cursor-doc/clojure-lexer.ts @@ -43,7 +43,7 @@ export function validPair(open: string, close: string): boolean { } export interface Token extends LexerToken { - state: ScannerState; + state: ScannerState | null; } // whitespace, excluding newlines @@ -174,7 +174,7 @@ export class Scanner { const tks: Token[] = []; this.state = state; let lex = (this.state.inString ? inString : toplevel).lex(line, this.maxLength); - let tk: LexerToken; + let tk: LexerToken | undefined; do { tk = lex.scan(); if (tk) { From 05108883663a14de5648f94698aab883bb758c23 Mon Sep 17 00:00:00 2001 From: Cora Sutton Date: Sat, 30 Apr 2022 21:17:26 -0500 Subject: [PATCH 11/73] Fix nulls in lexer --- src/cursor-doc/lexer.ts | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/cursor-doc/lexer.ts b/src/cursor-doc/lexer.ts index 4da61f106..2a1c831b8 100644 --- a/src/cursor-doc/lexer.ts +++ b/src/cursor-doc/lexer.ts @@ -3,6 +3,8 @@ * @module lexer */ +import { assertIsDefined } from '../utilities'; + /** * The base Token class. Contains the token type, * the raw string of the token, and the offset into the input line. @@ -36,8 +38,8 @@ export class Lexer { constructor(public source: string, public rules: Rule[], private maxLength) {} /** Returns the next token in this lexer, or null if at the end. If the match fails, throws an Error. */ - scan(): Token { - let token = null, + scan(): Token | undefined { + let token: Token | undefined, length = 0; if (this.position < this.source.length) { if (this.source !== undefined && this.source.length < this.maxLength) { @@ -47,6 +49,7 @@ export class Lexer { const x = rule.r.exec(this.source); if (x && x[0].length > length && this.position + x[0].length == rule.r.lastIndex) { token = rule.fn(this, x); + assertIsDefined(token, 'Expected token!'); token.offset = this.position; token.raw = x[0]; length = x[0].length; @@ -62,9 +65,9 @@ export class Lexer { } } this.position += length; - if (token == null) { + if (token === undefined) { if (this.position == this.source.length) { - return null; + return undefined; } throw new Error( 'Unexpected character at ' + this.position + ': ' + JSON.stringify(this.source) From 347e7eb5ce1f77d55f90b157bb2349e37c328834 Mon Sep 17 00:00:00 2001 From: Cora Sutton Date: Sat, 30 Apr 2022 21:18:11 -0500 Subject: [PATCH 12/73] Fix nulls in paredit.ts --- src/cursor-doc/paredit.ts | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/cursor-doc/paredit.ts b/src/cursor-doc/paredit.ts index cba3ac35e..9bff55ca7 100644 --- a/src/cursor-doc/paredit.ts +++ b/src/cursor-doc/paredit.ts @@ -1,3 +1,4 @@ +import { assertIsDefined } from '../utilities'; import { validPair } from './clojure-lexer'; import { ModelEdit, EditableDocument, ModelEditSelection } from './model'; import { LispTokenCursor } from './token-cursor'; @@ -438,7 +439,7 @@ export function wrapSexpr( start: number = doc.selection.anchor, end: number = doc.selection.active, options = { skipFormat: false } -): Thenable { +): Thenable | undefined { const cursor = doc.getTokenCursor(end); if (cursor.withinString() && open == '"') { open = close = '\\"'; @@ -486,7 +487,7 @@ export function rewrapSexpr( close: string, start: number = doc.selection.anchor, end: number = doc.selection.active -): Thenable { +): Thenable | undefined { const cursor = doc.getTokenCursor(end); if (cursor.backwardList()) { const openStart = cursor.offsetStart - 1, @@ -530,7 +531,7 @@ export function splitSexp(doc: EditableDocument, start: number = doc.selection.a export function joinSexp( doc: EditableDocument, start: number = doc.selection.active -): Thenable { +): Thenable | undefined { const cursor = doc.getTokenCursor(start); cursor.backwardWhitespace(); const prevToken = cursor.getPrevToken(), @@ -560,7 +561,7 @@ export function spliceSexp( doc: EditableDocument, start: number = doc.selection.active, undoStopBefore = true -): Thenable { +): Thenable | undefined { const cursor = doc.getTokenCursor(start); // TODO: this should unwrap the string, not the enclosing list. @@ -671,7 +672,9 @@ export function backwardSlurpSexp( cursor.backwardList(); const tk = cursor.getPrevToken(); if (tk.type == 'open') { - const offset = cursor.clone().previous().offsetStart; + const previous = cursor.clone().previous(); + assertIsDefined(previous, 'Expected a token to be before the cursor!'); + const offset = previous.offsetStart; const open = cursor.getPrevToken().raw; cursor.previous(); cursor.backwardSexp(true, true); @@ -968,6 +971,7 @@ export function growSelectionStack(doc: EditableDocument, range: [number, number export function shrinkSelection(doc: EditableDocument) { if (doc.selectionStack.length) { const latest = doc.selectionStack.pop(); + assertIsDefined(latest, 'Expected a value in selectionStack!'); if ( doc.selectionStack.length && latest.anchor == doc.selection.anchor && From 25277984c7a506ebc240e82812f28fc2d5113267 Mon Sep 17 00:00:00 2001 From: Cora Sutton Date: Sat, 30 Apr 2022 21:19:35 -0500 Subject: [PATCH 13/73] Fix nulls for _rangesForSexpsInList This is an odd one. The types were very different for useRowCol = true and useRowCol = false and so I needed to use generics to clean it up. --- src/cursor-doc/token-cursor.ts | 51 ++++++++++++++++++++++------------ 1 file changed, 34 insertions(+), 17 deletions(-) diff --git a/src/cursor-doc/token-cursor.ts b/src/cursor-doc/token-cursor.ts index 97cfab567..b0118edfa 100644 --- a/src/cursor-doc/token-cursor.ts +++ b/src/cursor-doc/token-cursor.ts @@ -27,7 +27,7 @@ export class TokenCursor { } /** Return the position */ - get rowCol() { + get rowCol(): [number, number] { return [this.line, this.getToken().offset]; } @@ -117,26 +117,21 @@ export class TokenCursor { * If you are particular about which list type that should be considered, supply an `openingBracket`. */ -function _rangesForSexpsInList( +const collectRanges = < + StartFieldKey extends keyof LispTokenCursor, + EndFieldKey extends keyof LispTokenCursor +>( cursor: LispTokenCursor, - useRowCol = false, - openingBracket?: string -): [number, number][] | [[number, number], [number, number]][] { - if (openingBracket !== undefined) { - if (!cursor.backwardListOfType(openingBracket)) { - return undefined; - } - } else { - if (!cursor.backwardList()) { - return undefined; - } - } - const ranges = []; + cursorStartField: StartFieldKey, + cursorEndField: EndFieldKey +): [LispTokenCursor[StartFieldKey], LispTokenCursor[EndFieldKey]][] => { + const ranges: [LispTokenCursor[StartFieldKey], LispTokenCursor[EndFieldKey]][] = []; // TODO: Figure out how to do this ignore skipping more generally in forward/backward this or that. let ignoreCounter = 0; + while (true) { cursor.forwardWhitespace(); - const start = useRowCol ? cursor.rowCol : cursor.offsetStart; + const start = cursor[cursorStartField]; if (cursor.getToken().type === 'ignore') { ignoreCounter++; cursor.forwardSexp(); @@ -144,7 +139,7 @@ function _rangesForSexpsInList( } if (cursor.forwardSexp()) { if (ignoreCounter === 0) { - const end = useRowCol ? cursor.rowCol : cursor.offsetStart; + const end = cursor[cursorEndField]; ranges.push([start, end]); } else { ignoreCounter--; @@ -154,6 +149,28 @@ function _rangesForSexpsInList( } } return ranges; +}; + +function _rangesForSexpsInList( + cursor: LispTokenCursor, + useRowCol = false, + openingBracket?: string +): [number, number][] | [[number, number], [number, number]][] | undefined { + if (openingBracket !== undefined) { + if (!cursor.backwardListOfType(openingBracket)) { + return undefined; + } + } else { + if (!cursor.backwardList()) { + return undefined; + } + } + + if (useRowCol) { + return collectRanges(cursor, 'rowCol', 'rowCol'); + } else { + return collectRanges(cursor, 'offsetStart', 'offsetEnd'); + } } export class LispTokenCursor extends TokenCursor { From be6765b900a1958b7326e66cf28dd503e7d79e9d Mon Sep 17 00:00:00 2001 From: Cora Sutton Date: Sat, 30 Apr 2022 21:22:55 -0500 Subject: [PATCH 14/73] Fix remaining nulls for token-cursor.ts --- src/cursor-doc/token-cursor.ts | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/src/cursor-doc/token-cursor.ts b/src/cursor-doc/token-cursor.ts index b0118edfa..96b192ce9 100644 --- a/src/cursor-doc/token-cursor.ts +++ b/src/cursor-doc/token-cursor.ts @@ -255,7 +255,7 @@ export class LispTokenCursor extends TokenCursor { */ forwardSexp(skipComments = true, skipMetadata = false, skipIgnoredForms = false): boolean { // TODO: Consider using a proper bracket stack - const stack = []; + const stack: string[] = []; let isMetadata = false; this.forwardWhitespace(skipComments); if (this.getToken().type === 'close') { @@ -297,7 +297,7 @@ export class LispTokenCursor extends TokenCursor { break; case 'close': { const close = token.raw; - let open: string; + let open: string | undefined; while ((open = stack.pop())) { if (validPair(open, close)) { this.next(); @@ -322,6 +322,8 @@ export class LispTokenCursor extends TokenCursor { break; } } + + return false; } /** @@ -339,7 +341,7 @@ export class LispTokenCursor extends TokenCursor { skipIgnoredForms = false, skipReaders = true ) { - const stack = []; + const stack: string[] = []; this.backwardWhitespace(skipComments); if (this.getPrevToken().type === 'open') { return false; @@ -381,7 +383,7 @@ export class LispTokenCursor extends TokenCursor { break; case 'open': { const open = tk.raw; - let close: string; + let close: string | undefined; while ((close = stack.pop())) { if (validPair(open, close)) { break; @@ -528,9 +530,9 @@ export class LispTokenCursor extends TokenCursor { * If you are particular about which type of list, supply the `openingBracket`. * @param openingBracket */ - rangeForList(depth: number, openingBracket?: string): [number, number] { + rangeForList(depth: number, openingBracket?: string): [number, number] | undefined { const cursor = this.clone(); - let range: [number, number] = undefined; + let range: [number, number] | undefined = undefined; for (let i = 0; i < depth; i++) { if (openingBracket === undefined) { if (!(cursor.backwardList() && cursor.backwardUpList())) { @@ -648,8 +650,8 @@ export class LispTokenCursor extends TokenCursor { * 8. Else, return `undefined`. * @param offset the current cursor (caret) offset in the document */ - rangeForCurrentForm(offset: number): [number, number] { - let afterCurrentFormOffset: number; + rangeForCurrentForm(offset: number): [number, number] | undefined { + let afterCurrentFormOffset: number | undefined; // console.log(-1, offset); // 0. If `offset` is within or before, a symbol, literal or keyword @@ -782,9 +784,9 @@ export class LispTokenCursor extends TokenCursor { return [currentFormCursor.offsetStart, afterCurrentFormOffset]; } - rangeForDefun(offset: number, commentCreatesTopLevel = true): [number, number] { + rangeForDefun(offset: number, commentCreatesTopLevel = true): [number, number] | undefined { const cursor = this.doc.getTokenCursor(offset); - let lastCandidateRange: [number, number] = cursor.rangeForCurrentForm(offset); + let lastCandidateRange: [number, number] | undefined = cursor.rangeForCurrentForm(offset); while (cursor.forwardList() && cursor.upList()) { const commentCursor = cursor.clone(); commentCursor.backwardDownList(); @@ -915,7 +917,7 @@ export class LispTokenCursor extends TokenCursor { * @param levels how many levels of functions to dig up. * @returns the function name, or undefined if there is no function there. */ - getFunctionName(levels: number = 0): string { + getFunctionName(levels: number = 0): string | undefined { const cursor = this.clone(); if (cursor.backwardFunction(levels)) { cursor.forwardWhitespace(); @@ -931,7 +933,7 @@ export class LispTokenCursor extends TokenCursor { * @param levels how many levels of functions to dig up. * @returns the range of the function sexp/form, or undefined if there is no function there. */ - getFunctionSexpRange(levels: number = 0): [number, number] { + getFunctionSexpRange(levels: number = 0): [number, number] | [undefined, undefined] { const cursor = this.clone(); if (cursor.backwardFunction(levels)) { cursor.forwardWhitespace(); From 46fa6ad3a8f3b97138a4feb52095a41a9990bffc Mon Sep 17 00:00:00 2001 From: Cora Sutton Date: Sat, 30 Apr 2022 22:02:54 -0500 Subject: [PATCH 15/73] Fix more nulls in paredit --- src/cursor-doc/paredit.ts | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/cursor-doc/paredit.ts b/src/cursor-doc/paredit.ts index 9bff55ca7..48adbac21 100644 --- a/src/cursor-doc/paredit.ts +++ b/src/cursor-doc/paredit.ts @@ -130,7 +130,7 @@ export function rangeForDefun( doc: EditableDocument, offset: number = doc.selection.active, commentCreatesTopLevel = true -): [number, number] { +): [number, number] | undefined { const cursor = doc.getTokenCursor(offset); return cursor.rangeForDefun(offset, commentCreatesTopLevel); } @@ -992,7 +992,9 @@ export function raiseSexp( end = doc.selection.active ) { const cursor = doc.getTokenCursor(end); - const [formStart, formEnd] = cursor.rangeForCurrentForm(start); + const formRange = cursor.rangeForCurrentForm(start); + assertIsDefined(formRange, 'Expected to find a range for the current form!'); + const [formStart, formEnd] = formRange; const isCaretTrailing = formEnd - start < start - formStart; const startCursor = doc.getTokenCursor(formStart); const endCursor = startCursor.clone(); @@ -1146,6 +1148,7 @@ function currentSexpsRange( usePairs = false ): [number, number] { const currentSingleRange = cursor.rangeForCurrentForm(offset); + assertIsDefined(currentSingleRange, 'Expected to find a range for the current form!'); if (usePairs) { const ranges = cursor.rangesForSexpsInList(); if (ranges.length > 1) { @@ -1246,6 +1249,7 @@ export function collectWhitespaceInfo( ): WhitespaceInfo { const cursor = doc.getTokenCursor(p); const currentRange = cursor.rangeForCurrentForm(p); + assertIsDefined(currentRange, 'Expected to find a range for the current form!'); const leftWsRight = currentRange[0]; const leftWsCursor = doc.getTokenCursor(leftWsRight); const rightWsLeft = currentRange[1]; @@ -1275,6 +1279,7 @@ export function dragSexprBackwardUp(doc: EditableDocument, p = doc.selection.act const cursor = doc.getTokenCursor(p); const currentRange = cursor.rangeForCurrentForm(p); if (cursor.backwardList() && cursor.backwardUpList()) { + assertIsDefined(currentRange, 'Expected to find a range for the current form!'); const listStart = cursor.offsetStart; const newPosOffset = p - currentRange[0]; const newCursorPos = listStart + newPosOffset; @@ -1314,6 +1319,7 @@ export function dragSexprBackwardUp(doc: EditableDocument, p = doc.selection.act export function dragSexprForwardDown(doc: EditableDocument, p = doc.selection.active) { const wsInfo = collectWhitespaceInfo(doc, p); const currentRange = doc.getTokenCursor(p).rangeForCurrentForm(p); + assertIsDefined(currentRange, 'Expected to find a range for the current form!'); const newPosOffset = p - currentRange[0]; const cursor = doc.getTokenCursor(currentRange[0]); while (cursor.forwardSexp()) { @@ -1352,6 +1358,7 @@ export function dragSexprForwardUp(doc: EditableDocument, p = doc.selection.acti const cursor = doc.getTokenCursor(p); const currentRange = cursor.rangeForCurrentForm(p); if (cursor.forwardList() && cursor.upList()) { + assertIsDefined(currentRange, 'Expected to find a range for the current form!'); const listEnd = cursor.offsetStart; const newPosOffset = p - currentRange[0]; const listWsInfo = collectWhitespaceInfo(doc, listEnd); @@ -1381,6 +1388,7 @@ export function dragSexprForwardUp(doc: EditableDocument, p = doc.selection.acti export function dragSexprBackwardDown(doc: EditableDocument, p = doc.selection.active) { const wsInfo = collectWhitespaceInfo(doc, p); const currentRange = doc.getTokenCursor(p).rangeForCurrentForm(p); + assertIsDefined(currentRange, 'Expected to find a range for the current form!'); const newPosOffset = p - currentRange[0]; const cursor = doc.getTokenCursor(currentRange[1]); while (cursor.backwardSexp()) { @@ -1429,6 +1437,7 @@ export function addRichComment(doc: EditableDocument, p = doc.selection.active, const richComment = `(comment\n ${contents ? adaptContentsToRichComment(contents) : ''}\n )`; let cursor = doc.getTokenCursor(p); const topLevelRange = rangeForDefun(doc, p, false); + assertIsDefined(topLevelRange, 'Expected to find a range for the current defun!'); const isInsideForm = !(p <= topLevelRange[0] || p >= topLevelRange[1]); const checkIfAtStartCursor = doc.getTokenCursor(p); checkIfAtStartCursor.backwardWhitespace(true); From ff9f09de42d7547a5d34c9e5b11aa0403b20e034 Mon Sep 17 00:00:00 2001 From: Cora Sutton Date: Sat, 30 Apr 2022 22:03:28 -0500 Subject: [PATCH 16/73] Fix nulls in undo.ts --- src/cursor-doc/undo.ts | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/cursor-doc/undo.ts b/src/cursor-doc/undo.ts index cf5f124e1..4a2eb763c 100644 --- a/src/cursor-doc/undo.ts +++ b/src/cursor-doc/undo.ts @@ -114,8 +114,10 @@ export class UndoManager { undo(c: T) { if (this.undos.length) { const step = this.undos.pop(); - step.undo(c); - this.redos.push(step); + if (step) { + step.undo(c); + this.redos.push(step); + } } } @@ -123,8 +125,10 @@ export class UndoManager { redo(c: T) { if (this.redos.length) { const step = this.redos.pop(); - step.redo(c); - this.undos.push(step); + if (step) { + step.redo(c); + this.undos.push(step); + } } } } From 6e17118969ef6f7c1521fa0f25aaf8bc3a76f5a7 Mon Sep 17 00:00:00 2001 From: Cora Sutton Date: Sat, 30 Apr 2022 22:25:02 -0500 Subject: [PATCH 17/73] Fix types for evaluating custom snippets --- src/custom-snippets.ts | 28 +++++++++++++++------------- src/evaluate.ts | 1 + 2 files changed, 16 insertions(+), 13 deletions(-) diff --git a/src/custom-snippets.ts b/src/custom-snippets.ts index 7e47b132c..e6b25dd9c 100644 --- a/src/custom-snippets.ts +++ b/src/custom-snippets.ts @@ -31,8 +31,8 @@ async function evaluateCodeOrKey(codeOrKey?: string) { if (snippets.length < 1) { snippets = getConfig().customREPLCommandSnippets; } - const snippetsDict = {}; - const snippetsKeyDict = {}; + const snippetsMap = new Map(); + const snippetsKeyMap = new Map(); const snippetsMenuItems: string[] = []; const editorNS = editor && editor.document && editor.document.languageId === 'clojure' @@ -49,14 +49,14 @@ async function evaluateCodeOrKey(codeOrKey?: string) { if (undefs.length > 0) { configErrors.push({ name: c.name, keys: undefs }); } - const entry = { ...c }; + const entry: customREPLCommandSnippet = { ...c }; entry.ns = entry.ns ? entry.ns : editorNS; entry.repl = entry.repl ? entry.repl : editorRepl; const prefix = entry.key !== undefined ? `${entry.key}: ` : ''; const item = `${prefix}${entry.name} (${entry.repl})`; snippetsMenuItems.push(item); - snippetsDict[item] = entry; - snippetsKeyDict[entry.key] = item; + snippetsMap.set(item, entry); + snippetsKeyMap.set(entry.key, item); }); if (configErrors.length > 0) { @@ -68,7 +68,7 @@ async function evaluateCodeOrKey(codeOrKey?: string) { return; } - let pick: string; + let pick: string | undefined; if (codeOrKey === undefined) { // Without codeOrKey always show snippets menu if (snippetsMenuItems.length > 0) { @@ -94,21 +94,23 @@ async function evaluateCodeOrKey(codeOrKey?: string) { } if (pick === undefined) { // still no pick, but codeOrKey might be one - pick = snippetsKeyDict[codeOrKey]; + pick = snippetsKeyMap.get(codeOrKey); } - const code = pick !== undefined ? snippetsDict[pick].snippet : codeOrKey; - const ns = pick !== undefined ? snippetsDict[pick].ns : editorNS; - const repl = pick !== undefined ? snippetsDict[pick].repl : editorRepl; + + const replSnippet = snippetsMap.get(pick); + + const code = pick !== undefined ? replSnippet?.snippet : codeOrKey; + const ns = pick !== undefined ? replSnippet?.ns : editorNS; + const repl = pick !== undefined ? replSnippet?.repl : editorRepl; const options = {}; if (pick !== undefined) { - options['evaluationSendCodeToOutputWindow'] = - snippetsDict[pick].evaluationSendCodeToOutputWindow; + options['evaluationSendCodeToOutputWindow'] = replSnippet?.evaluationSendCodeToOutputWindow; // don't allow addToHistory if we don't show the code but are inside the repl options['addToHistory'] = state.extensionContext.workspaceState.get('outputWindowActive') && - !snippetsDict[pick].evaluationSendCodeToOutputWindow + !replSnippet?.evaluationSendCodeToOutputWindow ? false : undefined; } diff --git a/src/evaluate.ts b/src/evaluate.ts index 8a8c2386d..8cd981b20 100644 --- a/src/evaluate.ts +++ b/src/evaluate.ts @@ -574,6 +574,7 @@ export type customREPLCommandSnippet = { snippet: string; repl?: string; ns?: string; + evaluationSendCodeToOutputWindow?: boolean; }; export default { From 39c320881f3dd3c5728252aec980727b7dc5c8a7 Mon Sep 17 00:00:00 2001 From: Cora Sutton Date: Sat, 30 Apr 2022 22:25:26 -0500 Subject: [PATCH 18/73] Fix types in repl-session --- src/nrepl/repl-session.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/nrepl/repl-session.ts b/src/nrepl/repl-session.ts index 6c488b6e9..dced71dac 100644 --- a/src/nrepl/repl-session.ts +++ b/src/nrepl/repl-session.ts @@ -3,7 +3,7 @@ import { cljsLib, tryToGetDocument, getFileType } from '../utilities'; import * as outputWindow from '../results-output/results-doc'; import { isUndefined } from 'lodash'; -function getSession(fileType?: string): NReplSession { +function getSession(fileType?: string): NReplSession | undefined { const doc = tryToGetDocument({}); if (isUndefined(fileType)) { @@ -48,7 +48,7 @@ function updateReplSessionType() { cljsLib.setStateValue('current-session-type', replSessionType); } -function getReplSessionTypeFromState() { +function getReplSessionTypeFromState(): string | undefined { return cljsLib.getStateValue('current-session-type'); } From e2622a22d81fa9626f26c6d51af34d71277e0fab Mon Sep 17 00:00:00 2001 From: Cora Sutton Date: Sat, 30 Apr 2022 22:26:57 -0500 Subject: [PATCH 19/73] Fix null checks for clojuredocs --- src/clojuredocs.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/clojuredocs.ts b/src/clojuredocs.ts index 6896c15de..47c20bdc5 100644 --- a/src/clojuredocs.ts +++ b/src/clojuredocs.ts @@ -174,6 +174,7 @@ async function clojureDocsLookup( const symbol = util.getWordAtPosition(doc, position); const ns = namespace.getNamespace(doc); const session = replSession.getSession(util.getFileType(doc)); + util.assertIsDefined(session, 'Expected there to be a repl session!'); const docsFromCider = await clojureDocsCiderNReplLookup(session, symbol, ns); if (docsFromCider) { From 7f770272e1d21fec04e6a8de38e4215422bb8fae Mon Sep 17 00:00:00 2001 From: Cora Sutton Date: Sat, 30 Apr 2022 22:34:14 -0500 Subject: [PATCH 20/73] Fix debugger-related null checks --- src/debugger/calva-debug.ts | 6 ++++-- src/debugger/decorations.ts | 3 ++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/debugger/calva-debug.ts b/src/debugger/calva-debug.ts index d0bc22765..5a44c8852 100644 --- a/src/debugger/calva-debug.ts +++ b/src/debugger/calva-debug.ts @@ -84,8 +84,8 @@ class CalvaDebugSession extends LoggingDebugSession { response: DebugProtocol.InitializeResponse, args: DebugProtocol.InitializeRequestArguments ): void { - this.setDebuggerLinesStartAt1(args.linesStartAt1); - this.setDebuggerColumnsStartAt1(args.columnsStartAt1); + this.setDebuggerLinesStartAt1(!!args.linesStartAt1); + this.setDebuggerColumnsStartAt1(!!args.columnsStartAt1); // Build and return the capabilities of this debug adapter response.body = { @@ -271,6 +271,7 @@ class CalvaDebugSession extends LoggingDebugSession { // Pass scheme in path argument to Source contructor so that if it's a jar file it's handled correctly const source = new Source(basename(debugResponse.file), debugResponse.file); const name = tokenCursor.getFunctionName(); + util.assertIsDefined(name, 'Expected to find a function name!'); const stackFrames = [new StackFrame(0, name, source, line + 1, column + 1)]; response.body = { @@ -439,6 +440,7 @@ function handleNeedDebugInput(response: any): void { } } else { const cljSession = replSession.getSession(CLOJURE_SESSION_NAME); + util.assertIsDefined(cljSession, 'Expected there to be a repl session!'); void cljSession.sendDebugInput(':quit', response.id, response.key); void vscode.window.showInformationMessage( 'Forms containing breakpoints that were not evaluated in the editor (such as if you evaluated a form in the REPL window) cannot be debugged. Evaluate the form in the editor in order to debug it.' diff --git a/src/debugger/decorations.ts b/src/debugger/decorations.ts index e2b40716c..f91f3022b 100644 --- a/src/debugger/decorations.ts +++ b/src/debugger/decorations.ts @@ -33,7 +33,7 @@ const instrumentedSymbolDecorationType = vscode.window.createTextEditorDecoratio async function update( editor: vscode.TextEditor, - cljSession: NReplSession, + cljSession: NReplSession | undefined, lspClient: LanguageClient ): Promise { if (/(\.clj)$/.test(editor.document.fileName)) { @@ -49,6 +49,7 @@ async function update( const docUri = vscode.Uri.parse(namespacePath, true); const decodedDocUri = decodeURIComponent(docUri.toString()); const docSymbols = (await lsp.getDocumentSymbols(lspClient, decodedDocUri))[0].children; + util.assertIsDefined(docSymbols, 'Expected to get document symbols from the LSP server!'); const instrumentedDocSymbols = docSymbols.filter((s) => instrumentedDefs.includes(s.name) ); From 28cf0f3b4e1adf36672b3d4d0819f35a14aed09d Mon Sep 17 00:00:00 2001 From: Cora Sutton Date: Sun, 1 May 2022 00:49:41 -0500 Subject: [PATCH 21/73] Fix more null checks in the debugger --- src/debugger/util.ts | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/debugger/util.ts b/src/debugger/util.ts index 80e4aa177..76ddeb72a 100644 --- a/src/debugger/util.ts +++ b/src/debugger/util.ts @@ -1,7 +1,10 @@ import { LispTokenCursor } from '../cursor-doc/token-cursor'; +import { assertIsDefined } from '../utilities'; function moveCursorPastStringInList(tokenCursor: LispTokenCursor, s: string): void { - const [listOffsetStart, listOffsetEnd] = tokenCursor.rangeForList(1); + const range = tokenCursor.rangeForList(1); + assertIsDefined(range, 'Expected range to be found!'); + const [listOffsetStart, listOffsetEnd] = range; const text = tokenCursor.doc.getText(listOffsetStart, listOffsetEnd - 1); const stringIndexInList = text.indexOf(s); @@ -22,7 +25,9 @@ function moveTokenCursorToBreakpoint( debugResponse: any ): LispTokenCursor { const errorMessage = 'Error finding position of breakpoint'; - const [_, defunEnd] = tokenCursor.rangeForDefun(tokenCursor.offsetStart); + const range = tokenCursor.rangeForDefun(tokenCursor.offsetStart); + assertIsDefined(range, 'Expected range to be found!'); + const [_, defunEnd] = range; let inSyntaxQuote = false; const coor = [...debugResponse.coor]; // Copy the array so we do not modify the one stored in state From 25af9b9ce00e8d630caf4afb06b8ee444b74ba3e Mon Sep 17 00:00:00 2001 From: Cora Sutton Date: Sun, 1 May 2022 00:50:23 -0500 Subject: [PATCH 22/73] Another null check issue fixed, this time in doc-mirror --- src/doc-mirror/index.ts | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/doc-mirror/index.ts b/src/doc-mirror/index.ts index 4d6ec962f..370e67b7e 100644 --- a/src/doc-mirror/index.ts +++ b/src/doc-mirror/index.ts @@ -169,18 +169,21 @@ export class MirroredDocument implements EditableDocument { } public delete(): Thenable { - return vscode.commands.executeCommand('deleteRight'); + return vscode.commands.executeCommand('deleteRight').then((v) => !!v); } public backspace(): Thenable { - return vscode.commands.executeCommand('deleteLeft'); + return vscode.commands.executeCommand('deleteLeft').then((v) => !!v); } } let registered = false; function processChanges(event: vscode.TextDocumentChangeEvent) { - const model = documents.get(event.document).model; + const mirrorDoc = documents.get(event.document); + utilities.assertIsDefined(mirrorDoc, 'Expected to find a mirror document!'); + const model = mirrorDoc.model; + for (const change of event.contentChanges) { // vscode may have a \r\n marker, so it's line offsets are all wrong. const myStartOffset = From 4043c42cbc0dabbbf9faf114c41ad8fa899d7bfe Mon Sep 17 00:00:00 2001 From: Cora Sutton Date: Sun, 1 May 2022 00:50:53 -0500 Subject: [PATCH 23/73] Fix null check issues in src/evaluate.ts --- src/evaluate.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/evaluate.ts b/src/evaluate.ts index 8cd981b20..bacebea19 100644 --- a/src/evaluate.ts +++ b/src/evaluate.ts @@ -153,6 +153,7 @@ async function evaluateCode( if (context.stacktrace) { outputWindow.saveStacktrace(context.stacktrace); outputWindow.append(errMsg, (_, afterResultLocation) => { + util.assertIsDefined(afterResultLocation, 'Expected there to be a location!'); outputWindow.markLastStacktraceRange(afterResultLocation); }); } else { @@ -193,6 +194,7 @@ async function evaluateCode( } } if (context.stacktrace && context.stacktrace.stacktrace) { + util.assertIsDefined(afterResultLocation, 'Expected there to be a location!'); outputWindow.markLastStacktraceRange(afterResultLocation); } }); @@ -409,6 +411,7 @@ async function loadFile( if (doc && doc.languageId == 'clojure' && fileType != 'edn' && getStateValue('connected')) { state.analytics().logEvent('Evaluation', 'LoadFile').send(); + util.assertIsDefined(session, 'Expected there to be a repl session!'); const docUri = outputWindow.isResultsDoc(doc) ? await namespace.getUriForNamespace(session, ns) : doc.uri; @@ -489,6 +492,7 @@ async function requireREPLUtilitiesCommand() { async function copyLastResultCommand() { const chan = state.outputChannel(); const session = replSession.getSession(util.getFileType(util.tryToGetDocument({}))); + util.assertIsDefined(session, 'Expected there to be a repl session!'); const value = await session.eval('*1', session.client.ns).value; if (value !== null) { @@ -503,6 +507,7 @@ async function togglePrettyPrint() { const config = vscode.workspace.getConfiguration('calva'), pprintConfigKey = 'prettyPrintingOptions', pprintOptions = config.get(pprintConfigKey); + util.assertIsDefined(pprintOptions, 'Expected there to be pprint options!'); pprintOptions.enabled = !pprintOptions.enabled; if (pprintOptions.enabled && !(pprintOptions.printEngine || pprintOptions.printFn)) { pprintOptions.printEngine = 'pprint'; @@ -548,6 +553,7 @@ export async function evaluateInOutputWindow( const session = replSession.getSession(sessionType); replSession.updateReplSessionType(); if (outputWindow.getNs() !== ns) { + util.assertIsDefined(session, 'Expected there to be a repl session!'); await session.switchNS(ns); outputWindow.setSession(session, ns); if (options.evaluationSendCodeToOutputWindow !== false) { From 74ab8f9037c97f89a75f0efec1b92350058c7ab4 Mon Sep 17 00:00:00 2001 From: Cora Sutton Date: Sun, 1 May 2022 00:51:26 -0500 Subject: [PATCH 24/73] Fix some null check issues in extension tests --- src/extension-test/unit/common/text-notation.ts | 4 +++- src/extension-test/unit/cursor-doc/clojure-lexer-test.ts | 2 +- src/extension-test/unit/results-output/util-test.ts | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/extension-test/unit/common/text-notation.ts b/src/extension-test/unit/common/text-notation.ts index ed674cf4f..3f7419dac 100644 --- a/src/extension-test/unit/common/text-notation.ts +++ b/src/extension-test/unit/common/text-notation.ts @@ -12,7 +12,9 @@ import * as model from '../../../cursor-doc/model'; * * Selections with direction left->right are denoted with `|<|` at the range boundaries */ -function textNotationToTextAndSelection(s: string): [string, { anchor: number; active: number }] { +function textNotationToTextAndSelection( + s: string +): [string, { anchor: number; active: number | undefined }] { const text = s.replace(/•/g, '\n').replace(/\|?[<>]?\|/g, ''); let anchor: undefined | number = undefined; let active: undefined | number = undefined; diff --git a/src/extension-test/unit/cursor-doc/clojure-lexer-test.ts b/src/extension-test/unit/cursor-doc/clojure-lexer-test.ts index fe2d46db6..e09307b0d 100644 --- a/src/extension-test/unit/cursor-doc/clojure-lexer-test.ts +++ b/src/extension-test/unit/cursor-doc/clojure-lexer-test.ts @@ -677,7 +677,7 @@ describe('Scanner', () => { if (!['reader', 'junk'].includes(rule.name)) { expect(x).toBeNull(); } else { - expect(x.length).toBe(1); + expect(x?.length).toBe(1); } }); }); diff --git a/src/extension-test/unit/results-output/util-test.ts b/src/extension-test/unit/results-output/util-test.ts index e2b445c71..274e76d15 100644 --- a/src/extension-test/unit/results-output/util-test.ts +++ b/src/extension-test/unit/results-output/util-test.ts @@ -25,7 +25,7 @@ describe('addToHistory', () => { }); it('should not push null to history array', () => { const history = []; - const newHistory = util.addToHistory(history, null); + const newHistory = util.addToHistory(history, undefined); expect(newHistory.length).toBe(history.length); }); }); From 6a0474324336dc0cbb4414da7634bb505850f833 Mon Sep 17 00:00:00 2001 From: Cora Sutton Date: Sun, 1 May 2022 00:52:03 -0500 Subject: [PATCH 25/73] Fix null check issues in src/extension.ts --- src/extension.ts | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/src/extension.ts b/src/extension.ts index b4f49d3e1..b01950ce1 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -49,7 +49,9 @@ async function onDidSave(testController: vscode.TestController, document: vscode state.analytics().logEvent('Calva', 'OnSaveTest').send(); } else if (evaluate) { if (!outputWindow.isResultsDoc(document)) { - await eval.loadFile(document, config.getConfig().prettyPrintingOptions); + const pprintOptions = config.getConfig().prettyPrintingOptions; + util.assertIsDefined(pprintOptions, 'Expected there to be pprint options!'); + await eval.loadFile(document, pprintOptions); outputWindow.appendPrompt(); state.analytics().logEvent('Calva', 'OnSaveLoad').send(); } @@ -62,7 +64,7 @@ function onDidOpen(document) { } } -function onDidChangeEditorOrSelection(editor: vscode.TextEditor) { +function onDidChangeEditorOrSelection(editor: vscode.TextEditor | undefined) { replHistory.setReplHistoryCommandsActiveContext(editor); whenContexts.setCursorContextIfChanged(editor); } @@ -106,7 +108,14 @@ async function activate(context: vscode.ExtensionContext) { setStateValue('analytics', new Analytics(context)); state.analytics().logPath('/start').logEvent('LifeCycle', 'Started').send(); - model.initScanner(vscode.workspace.getConfiguration('editor').get('maxTokenizationLineLength')); + const maxTokenizationLineLength = vscode.workspace + .getConfiguration('editor') + .get('maxTokenizationLineLength'); + util.assertIsDefined( + maxTokenizationLineLength, + 'Expected there to be a maxTokenizationLineLength set in the editor config' + ); + model.initScanner(maxTokenizationLineLength); const chan = state.outputChannel(); @@ -118,6 +127,10 @@ async function activate(context: vscode.ExtensionContext) { const clojureExtension = vscode.extensions.getExtension('avli.clojure'); const customCljsRepl = config.getConfig().customCljsRepl; const replConnectSequences = config.getConfig().replConnectSequences; + util.assertIsDefined( + replConnectSequences, + 'Expected there to be a repl connect sequence in the config!' + ); const BUTTON_GOTO_DOC = 'Open the docs'; const BUTTON_OK = 'Got it'; const VIM_DOC_URL = 'https://calva.io/vim/'; @@ -231,7 +244,9 @@ async function activate(context: vscode.ExtensionContext) { ); context.subscriptions.push( vscode.commands.registerCommand('calva.loadFile', async () => { - await eval.loadFile({}, config.getConfig().prettyPrintingOptions); + const pprintOptions = config.getConfig().prettyPrintingOptions; + util.assertIsDefined(pprintOptions, 'Expected pprint options in the config!'); + await eval.loadFile({}, pprintOptions); return new Promise((resolve) => { outputWindow.appendPrompt(resolve); }); From b91edb13743fafd64e8e2200e8599733e3af410a Mon Sep 17 00:00:00 2001 From: Cora Sutton Date: Sun, 1 May 2022 00:53:13 -0500 Subject: [PATCH 26/73] Fix a ton of null check issues in extension.ts for highlighting --- src/highlight/src/extension.ts | 108 +++++++++++++++++++-------------- 1 file changed, 63 insertions(+), 45 deletions(-) diff --git a/src/highlight/src/extension.ts b/src/highlight/src/extension.ts index 77ed1d0b1..d0b41a7c0 100755 --- a/src/highlight/src/extension.ts +++ b/src/highlight/src/extension.ts @@ -1,17 +1,16 @@ import * as vscode from 'vscode'; import { Position, Range } from 'vscode'; import * as isEqual from 'lodash.isequal'; -import { isArray } from 'util'; import * as docMirror from '../../doc-mirror/index'; import { Token, validPair } from '../../cursor-doc/clojure-lexer'; import { LispTokenCursor } from '../../cursor-doc/token-cursor'; -import { tryToGetActiveTextEditor, getActiveTextEditor } from '../../utilities'; +import { tryToGetActiveTextEditor, getActiveTextEditor, assertIsDefined } from '../../utilities'; type StackItem = { char: string; start: Position; end: Position; - pair_idx: number; + pair_idx: number | undefined; opens_comment_form?: boolean; }; @@ -23,7 +22,12 @@ function is_clojure(editor) { } // Exported for integration testing purposes -export let activeEditor: vscode.TextEditor; +export let activeEditor: vscode.TextEditor | undefined; + +const definedActiveEditor = () => { + assertIsDefined(activeEditor, 'Expected there to be an active text editor set!'); + return activeEditor; +}; let lastHighlightedEditor, rainbowColors, @@ -45,8 +49,8 @@ let lastHighlightedEditor, pairsBack: Map = new Map(), pairsForward: Map = new Map(), placedGuidesColor: Map = new Map(), - rainbowTimer = undefined, - matchTimer = undefined, + rainbowTimer: NodeJS.Timeout | undefined = undefined, + matchTimer: NodeJS.Timeout | undefined = undefined, dirty = false; reloadConfig(); @@ -57,7 +61,7 @@ function decorationType(opts) { } function colorDecorationType(color) { - if (isArray(color)) { + if (Array.isArray(color)) { return decorationType({ light: { color: color[0] }, dark: { color: color[1] }, @@ -68,7 +72,7 @@ function colorDecorationType(color) { } function guidesDecorationType_(color, isActive: boolean): vscode.TextEditorDecorationType { - if (isArray(color)) { + if (Array.isArray(color)) { return decorationType({ light: { borderWidth: `0; border-right-width: ${ @@ -105,23 +109,26 @@ function activeGuidesDecorationType(color): vscode.TextEditorDecorationType { } function reset_styles() { + const editor = activeEditor; + assertIsDefined(editor, 'Expected there to be an active text editor!'); + if (rainbowTypes) { - rainbowTypes.forEach((type) => activeEditor.setDecorations(type, [])); + rainbowTypes.forEach((type) => editor.setDecorations(type, [])); } rainbowTypes = rainbowColors.map(colorDecorationType); if (rainbowGuidesTypes) { - rainbowGuidesTypes.forEach((type) => activeEditor.setDecorations(type, [])); + rainbowGuidesTypes.forEach((type) => editor.setDecorations(type, [])); } rainbowGuidesTypes = rainbowColors.map(guidesDecorationType); if (activeGuidesTypes) { - activeGuidesTypes.forEach((type) => activeEditor.setDecorations(type, [])); + activeGuidesTypes.forEach((type) => editor.setDecorations(type, [])); } activeGuidesTypes = rainbowColors.map(activeGuidesDecorationType); if (misplacedType) { - activeEditor.setDecorations(misplacedType, []); + editor.setDecorations(misplacedType, []); } misplacedType = decorationType( misplacedBracketStyle || { @@ -133,7 +140,7 @@ function reset_styles() { ); if (matchedType) { - activeEditor.setDecorations(matchedType, []); + editor.setDecorations(matchedType, []); } matchedType = decorationType( matchedBracketStyle || { @@ -143,12 +150,12 @@ function reset_styles() { ); if (commentFormType) { - activeEditor.setDecorations(commentFormType, []); + editor.setDecorations(commentFormType, []); } commentFormType = decorationType(commentFormStyle || { fontStyle: 'italic' }); if (ignoredFormType) { - activeEditor.setDecorations(ignoredFormType, []); + editor.setDecorations(ignoredFormType, []); } ignoredFormType = decorationType(ignoredFormStyle || { textDecoration: 'none; opacity: 0.5' }); @@ -231,13 +238,16 @@ function updateRainbowBrackets() { reset_styles(); } - const doc = activeEditor.document, + const editor = activeEditor; + assertIsDefined(editor, 'Expected there to be an active text editor!'); + + const doc = editor.document, mirrorDoc = docMirror.getDocument(doc), - rainbow = rainbowTypes.map(() => []), + rainbow = rainbowTypes.map(() => [] as { range: Range }[]), rainbowGuides = rainbowTypes.map(() => []), - misplaced = [], - comment_forms = [], - ignores = [], + misplaced: { range: Range }[] = [], + comment_forms: Range[] = [], + ignores: Range[] = [], len = rainbowTypes.length, colorsEnabled = enableBracketColors && len > 0, guideColorsEnabled = useRainbowIndentGuides && len > 0, @@ -250,7 +260,7 @@ function updateRainbowBrackets() { pairsBack = new Map(); pairsForward = new Map(); placedGuidesColor = new Map(); - activeEditor.visibleRanges.forEach((range) => { + editor.visibleRanges.forEach((range) => { // Find the visible forms const startOffset = doc.offsetAt(range.start), endOffset = doc.offsetAt(range.end), @@ -289,7 +299,7 @@ function updateRainbowBrackets() { } else if (token.type === 'ignore') { const ignoreCursor = cursor.clone(); let ignore_counter = 0; - const ignore_start = activeEditor.document.positionAt(ignoreCursor.offsetStart); + const ignore_start = editor.document.positionAt(ignoreCursor.offsetStart); while (ignoreCursor.getToken().type === 'ignore') { ignore_counter++; ignoreCursor.next(); @@ -298,7 +308,7 @@ function updateRainbowBrackets() { for (let i = 0; i < ignore_counter; i++) { ignoreCursor.forwardSexp(true, true, true); } - const ignore_end = activeEditor.document.positionAt(ignoreCursor.offsetStart); + const ignore_end = editor.document.positionAt(ignoreCursor.offsetStart); ignores.push(new Range(ignore_start, ignore_end)); } } @@ -318,10 +328,11 @@ function updateRainbowBrackets() { if (token.type === 'open') { const readerCursor = cursor.clone(); readerCursor.backwardThroughAnyReader(); - const start = activeEditor.document.positionAt(readerCursor.offsetStart), - end = activeEditor.document.positionAt(cursor.offsetEnd), + + const start = editor.document.positionAt(readerCursor.offsetStart), + end = editor.document.positionAt(cursor.offsetEnd), openRange = new Range(start, end), - openString = activeEditor.document.getText(openRange); + openString = editor.document.getText(openRange); if (colorsEnabled) { const decoration = { range: openRange }; rainbow[colorIndex(stack_depth)].push(decoration); @@ -336,11 +347,12 @@ function updateRainbowBrackets() { }); continue; } else if (token.type === 'close') { - const pos = activeEditor.document.positionAt(cursor.offsetStart), + const pos = editor.document.positionAt(cursor.offsetStart), decoration = { range: new Range(pos, pos.translate(0, 1)) }; let pair_idx = stack.length - 1; - while (pair_idx >= 0 && stack[pair_idx].pair_idx !== undefined) { - pair_idx = stack[pair_idx].pair_idx - 1; + const stackPairIdx = stack[pair_idx]; + while (pair_idx >= 0 && stackPairIdx.pair_idx !== undefined) { + pair_idx = stackPairIdx.pair_idx - 1; } if (pair_idx === undefined || pair_idx < 0 || !validPair(stack[pair_idx].char, char)) { misplaced.push(decoration); @@ -359,9 +371,9 @@ function updateRainbowBrackets() { pair_idx: pair_idx, }); pairsBack.set(position_str(pos), [opening, closing]); - const startOffset = activeEditor.document.offsetAt(pair.start); + const startOffset = editor.document.offsetAt(pair.start); for (let i = 0; i < pair.char.length; ++i) { - pairsForward.set(position_str(activeEditor.document.positionAt(startOffset + i)), [ + pairsForward.set(position_str(editor.document.positionAt(startOffset + i)), [ opening, closing, ]); @@ -373,6 +385,7 @@ function updateRainbowBrackets() { if (guideColorsEnabled || activeGuideEnabled) { const matchPos = pos.translate(0, 1); const openSelection = matchBefore(new vscode.Selection(matchPos, matchPos)); + assertIsDefined(openSelection, 'Expected openSelection to be defined!'); const openSelectionPos = openSelection[0].start; const guideLength = decorateGuide( doc, @@ -391,14 +404,14 @@ function updateRainbowBrackets() { }); for (let i = 0; i < rainbowTypes.length; ++i) { - activeEditor.setDecorations(rainbowTypes[i], rainbow[i]); + editor.setDecorations(rainbowTypes[i], rainbow[i]); if (guideColorsEnabled) { - activeEditor.setDecorations(rainbowGuidesTypes[i], rainbowGuides[i]); + editor.setDecorations(rainbowGuidesTypes[i], rainbowGuides[i]); } } - activeEditor.setDecorations(misplacedType, misplaced); - activeEditor.setDecorations(commentFormType, comment_forms); - activeEditor.setDecorations(ignoredFormType, ignores); + editor.setDecorations(misplacedType, misplaced); + editor.setDecorations(commentFormType, comment_forms); + editor.setDecorations(ignoredFormType, ignores); matchPairs(); if (activeGuideEnabled) { decorateActiveGuides(); @@ -428,8 +441,11 @@ function matchPairs() { return; } + const editor = activeEditor; + assertIsDefined(editor, 'Expected there to be an active text editor!'); + const matches: { range: vscode.Range }[] = []; - activeEditor.selections.forEach((selection) => { + editor.selections.forEach((selection) => { const match_before = matchBefore(selection), match_after = matchAfter(selection); if (match_before) { @@ -441,7 +457,7 @@ function matchPairs() { matches.push({ range: match_after[1] }); } }); - activeEditor.setDecorations(matchedType, matches); + editor.setDecorations(matchedType, matches); } function decorateGuide( @@ -453,7 +469,8 @@ function decorateGuide( let guideLength = 0; for (let lineDelta = 1; lineDelta <= endPos.line - startPos.line; lineDelta++) { const guidePos = startPos.translate(lineDelta, 0); - if (doc.lineAt(guidePos).text.match(/^ */)[0].length >= startPos.character) { + const leadingSpacesMatch = doc.lineAt(guidePos).text.match(/^ */); + if (leadingSpacesMatch && leadingSpacesMatch[0].length >= startPos.character) { const guidesDecoration = { range: new Range(guidePos, guidePos) }; guides.push(guidesDecoration); guideLength++; @@ -464,12 +481,13 @@ function decorateGuide( function decorateActiveGuides() { const activeGuides = []; - activeEditor = getActiveTextEditor(); + const editor = getActiveTextEditor(); + activeEditor = editor; if (activeGuidesTypes) { - activeGuidesTypes.forEach((type) => activeEditor.setDecorations(type, [])); + activeGuidesTypes.forEach((type) => editor.setDecorations(type, [])); } - activeEditor.selections.forEach((selection) => { - const doc = activeEditor.document; + editor.selections.forEach((selection) => { + const doc = editor.document; const mirrorDoc = docMirror.getDocument(doc); const cursor = mirrorDoc.getTokenCursor(doc.offsetAt(selection.start)); const visitedEndPositions = [selection.start]; @@ -490,7 +508,7 @@ function decorateActiveGuides() { if (colorIndex !== undefined) { if (guideRange.contains(selection)) { decorateGuide(doc, startPos, endPos, activeGuides); - activeEditor.setDecorations(activeGuidesTypes[colorIndex], activeGuides); + editor.setDecorations(activeGuidesTypes[colorIndex], activeGuides); } break; } @@ -539,7 +557,7 @@ export function activate(context: vscode.ExtensionContext) { vscode.workspace.onDidChangeTextDocument( (event) => { - if (is_clojure(activeEditor) && event.document === activeEditor.document) { + if (is_clojure(activeEditor) && event.document === activeEditor?.document) { scheduleRainbowBrackets(); } }, From a58fbf16e73093548f28b72107bb8cf68ab9aa76 Mon Sep 17 00:00:00 2001 From: Cora Sutton Date: Sun, 1 May 2022 00:53:38 -0500 Subject: [PATCH 27/73] Fix live-share.ts null check issues --- src/live-share.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/live-share.ts b/src/live-share.ts index 1f989d1e6..abf36bb92 100644 --- a/src/live-share.ts +++ b/src/live-share.ts @@ -3,12 +3,12 @@ import * as vsls from 'vsls'; import * as config from './config'; // Keeps hold of the LiveShare API instance, so that it is requested only once. -let liveShare: vsls.LiveShare = null; +let liveShare: vsls.LiveShare | null = null; // Keeps hold of the LiveShare listener, to prevent it from being disposed immediately. -let liveShareListener: Disposable = null; +let liveShareListener: Disposable | null = null; -let connectedPort: number = null; +let connectedPort: number | null = null; let jackedIn = false; const sharedPorts: Map = new Map(); From 30cc6216669274501cdc8f6a3d8adcd722a79bf1 Mon Sep 17 00:00:00 2001 From: Cora Sutton Date: Sun, 1 May 2022 00:57:38 -0500 Subject: [PATCH 28/73] Fix a number of strict null check issues in lsp code --- src/lsp/download.ts | 23 ++++++++++++++----- src/lsp/main.ts | 55 ++++++++++++++++++++++++++++----------------- 2 files changed, 53 insertions(+), 25 deletions(-) diff --git a/src/lsp/download.ts b/src/lsp/download.ts index 845740bf0..10b532832 100644 --- a/src/lsp/download.ts +++ b/src/lsp/download.ts @@ -18,12 +18,25 @@ async function getLatestVersion(): Promise { } } +const zipFileNames = { + darwin: 'clojure-lsp-native-macos-amd64.zip', + linux: 'clojure-lsp-native-static-linux-amd64.zip', + win32: 'clojure-lsp-native-windows-amd64.zip', +}; + +const validPlatforms = Object.keys(zipFileNames); +type ValidPlatform = keyof typeof zipFileNames; + +function assertValidPlatform(platform: string): asserts platform is ValidPlatform { + if (!validPlatforms.includes(platform)) { + throw new Error(`Expected a valid clojure-lsp platform but got ${platform}`); + } +} + function getZipFileName(platform: string): string { - return { - darwin: 'clojure-lsp-native-macos-amd64.zip', - linux: 'clojure-lsp-native-static-linux-amd64.zip', - win32: 'clojure-lsp-native-windows-amd64.zip', - }[platform]; + assertValidPlatform(platform); + + return zipFileNames[platform]; } function getZipFilePath(extensionPath: string, platform: string): string { diff --git a/src/lsp/main.ts b/src/lsp/main.ts index 40950c597..fe9ec4522 100644 --- a/src/lsp/main.ts +++ b/src/lsp/main.ts @@ -32,7 +32,7 @@ const SERVER_NOT_RUNNING_OR_INITIALIZED_MESSAGE = const lspStatusItem = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Left, 0); let serverVersion: string; let extensionContext: vscode.ExtensionContext; -let clojureLspPath: string; +let clojureLspPath: string | undefined; let testTreeHandler: TestTreeHandler; let lspCommandsRegistered = false; @@ -124,7 +124,11 @@ function createClient(clojureLspPath: string, fallbackFolder: FallbackFolder): L provideCodeActions(document, range, context, token, next) { return next(document, range, context, token); }, - provideCodeLenses: async (document, token, next): Promise => { + provideCodeLenses: async ( + document, + token, + next + ): Promise => { if (config.getConfig().referencesCodeLensEnabled) { return await next(document, token); } @@ -137,7 +141,7 @@ function createClient(clojureLspPath: string, fallbackFolder: FallbackFolder): L return null; }, async provideHover(document, position, token, next) { - let hover: vscode.Hover; + let hover: vscode.Hover | null | undefined; try { hover = await provideHover(document, position); } catch { @@ -266,7 +270,7 @@ function registerLspCommand(command: ClojureLspCommand): vscode.Disposable { const docUri = `${document.uri.scheme}://${document.uri.path}`; const params = [docUri, line, column]; const extraParam = command.extraParamFn ? await command.extraParamFn() : undefined; - if (!command.extraParamFn || (command.extraParamFn && extraParam)) { + if (!command.extraParamFn || extraParam) { sendCommandRequest(command.command, extraParam ? [...params, extraParam] : params); } } @@ -360,10 +364,12 @@ enum FolderType { FROM_NO_CLOJURE_FILE = 3, } -type FallbackFolder = { - uri: vscode.Uri; - type: FolderType; -}; +type FallbackFolder = + | { uri: undefined; type: FolderType.VSCODE } + | { + uri: vscode.Uri; + type: FolderType; + }; /** * Figures out a ”best fit” rootUri for use when starting the clojure-lsp @@ -382,8 +388,8 @@ async function getFallbackFolder(): Promise { } const activeEditor = vscode.window.activeTextEditor; - let clojureFilePath: string; - let folderType: FolderType; + let clojureFilePath: string | undefined; + let folderType: FolderType | undefined; if (activeEditor && activeEditor.document?.languageId === 'clojure') { folderType = activeEditor.document.isUntitled ? FolderType.FROM_UNTITLED_FILE @@ -394,8 +400,10 @@ async function getFallbackFolder(): Promise { } else { for (const document of vscode.workspace.textDocuments) { if (document.languageId === 'clojure') { - folderType = document.isUntitled ? FolderType.FROM_UNTITLED_FILE : FolderType.FROM_FS_FILE; - if (!document.isUntitled) { + if (document.isUntitled) { + folderType = FolderType.FROM_UNTITLED_FILE; + } else { + folderType = FolderType.FROM_FS_FILE; clojureFilePath = document.uri.fsPath; } } @@ -422,6 +430,8 @@ async function getFallbackFolder(): Promise { } } + util.assertIsDefined(folderType, 'Expected there to be a folderType at this point!'); + return { uri: fallbackFolder, type: folderType, @@ -477,6 +487,7 @@ async function startClient(fallbackFolder: FallbackFolder): Promise { }); } setStateValue(LSP_CLIENT_KEY, undefined); + util.assertIsDefined(clojureLspPath, 'Expected there to be a clojure LSP path!'); const client = createClient(clojureLspPath, fallbackFolder); console.log('Starting clojure-lsp at', clojureLspPath); @@ -515,11 +526,14 @@ async function startClient(fallbackFolder: FallbackFolder): Promise { // A quickPick that expects the same input as showInformationMessage does // TODO: How do we make it satisfy the messageFunc interface above? -function quickPick(message: string, actions: { title: string }[]): Promise<{ title: string }> { +function quickPick( + message: string, + actions: { title: string }[] +): Promise<{ title: string } | undefined> { const qp = vscode.window.createQuickPick(); qp.items = actions.map((item) => ({ label: item.title })); qp.title = message; - return new Promise<{ title: string }>((resolve, _reject) => { + return new Promise<{ title: string } | undefined>((resolve, _reject) => { qp.show(); qp.onDidAccept(() => { if (qp.selectedItems.length > 0) { @@ -649,7 +663,7 @@ async function activate(context: vscode.ExtensionContext, handler: TestTreeHandl } } -async function maybeDownloadLspServer(forceDownLoad = false): Promise { +async function maybeDownloadLspServer(forceDownLoad = false): Promise { const userConfiguredClojureLspPath = config.getConfig().clojureLspPath; if (userConfiguredClojureLspPath !== '') { clojureLspPath = userConfiguredClojureLspPath; @@ -672,11 +686,12 @@ async function downloadLSPServerCommand() { async function ensureServerDownloaded(forceDownLoad = false): Promise { const currentVersion = readVersionFile(extensionContext.extensionPath); - const configuredVersion: string = config.getConfig().clojureLspVersion; + const configuredVersion: string | undefined = config.getConfig().clojureLspVersion; clojureLspPath = getClojureLspPath(extensionContext.extensionPath, util.isWindows); - const downloadVersion = ['', 'latest'].includes(configuredVersion) - ? await getLatestVersion() - : configuredVersion; + const downloadVersion = + configuredVersion === undefined || ['', 'latest'].includes(configuredVersion) + ? await getLatestVersion() + : configuredVersion; if ( (currentVersion !== downloadVersion && downloadVersion !== '') || forceDownLoad || @@ -787,7 +802,7 @@ export async function getCljFmtConfig(): Promise { function showMenu(items: vscode.QuickPickItem[], commands: Record) { void vscode.window.showQuickPick(items, { title: 'clojure-lsp' }).then((v) => { - if (commands[v.label]) { + if (v && commands[v.label]) { void vscode.commands.executeCommand(commands[v.label]); } }); From 6e26d72d843dd88feac9a152a79e6241dbce1123 Mon Sep 17 00:00:00 2001 From: Cora Sutton Date: Sun, 1 May 2022 00:58:02 -0500 Subject: [PATCH 29/73] Fix null check issues in providers --- src/providers/annotations.ts | 4 ++-- src/providers/completion.ts | 7 +++++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/providers/annotations.ts b/src/providers/annotations.ts index 86ac02385..0024893c7 100644 --- a/src/providers/annotations.ts +++ b/src/providers/annotations.ts @@ -27,7 +27,7 @@ const evalResultsDecorationType = vscode.window.createTextEditorDecorationType({ rangeBehavior: vscode.DecorationRangeBehavior.ClosedOpen, }); -let resultsLocations: [vscode.Range, vscode.Position, vscode.Location][] = []; +let resultsLocations: [vscode.Range, vscode.Position | undefined, vscode.Location][] = []; function getResultsLocation(pos: vscode.Position): vscode.Location | undefined { for (const [range, _evaluatePosition, location] of resultsLocations) { @@ -141,7 +141,7 @@ function decorateSelection( resultString: string, codeSelection: vscode.Selection, editor: vscode.TextEditor, - evaluatePosition: vscode.Position, + evaluatePosition: vscode.Position | undefined, resultsLocation, status: AnnotationStatus ) { diff --git a/src/providers/completion.ts b/src/providers/completion.ts index e3d7da205..69eea7a39 100644 --- a/src/providers/completion.ts +++ b/src/providers/completion.ts @@ -176,8 +176,11 @@ async function replCompletions( replContext = `${contextStart}__prefix__${contextEnd}`, toplevelIsValidForm = toplevelStartCursor.withinValidList() && replContext != '__prefix__', ns = namespace.getNamespace(document), - client = replSession.getSession(util.getFileType(document)), - res = await client.complete(ns, text, toplevelIsValidForm ? replContext : undefined), + client = replSession.getSession(util.getFileType(document)); + + util.assertIsDefined(client, 'Expected there to be a repl client!'); + + const res = await client.complete(ns, text, toplevelIsValidForm ? replContext : undefined), results = res.completions || []; results.forEach((element) => { From 387326f0e9bcfe1bf289bcc620dd6a35e07f995d Mon Sep 17 00:00:00 2001 From: Cora Sutton Date: Sun, 1 May 2022 00:58:19 -0500 Subject: [PATCH 30/73] Fix strict null check issue in repl-history.ts --- src/results-output/repl-history.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/results-output/repl-history.ts b/src/results-output/repl-history.ts index bd0f2d4af..dbf18ad21 100644 --- a/src/results-output/repl-history.ts +++ b/src/results-output/repl-history.ts @@ -14,7 +14,7 @@ const replHistoryCommandsActiveContext = 'calva:replHistoryCommandsActive'; let historyIndex: number | undefined = undefined; let lastTextAtPrompt: string | undefined = undefined; -function setReplHistoryCommandsActiveContext(editor: vscode.TextEditor): void { +function setReplHistoryCommandsActiveContext(editor: vscode.TextEditor | undefined): void { if (editor && util.getConnectedState() && isResultsDoc(editor.document)) { const document = editor.document; const selection = editor.selection; From c8c7ee78d06827b3144cc0a0c5ae5a7916bd318a Mon Sep 17 00:00:00 2001 From: Cora Sutton Date: Sun, 1 May 2022 00:58:57 -0500 Subject: [PATCH 31/73] Fix strict null check issue in when-contexts.ts --- src/when-contexts.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/when-contexts.ts b/src/when-contexts.ts index 3457663cc..90edf0907 100644 --- a/src/when-contexts.ts +++ b/src/when-contexts.ts @@ -6,7 +6,7 @@ import * as util from './utilities'; let lastContexts: context.CursorContext[] = []; -export function setCursorContextIfChanged(editor: vscode.TextEditor) { +export function setCursorContextIfChanged(editor: vscode.TextEditor | undefined) { if ( !editor || !editor.document || From cb079b5ec542490bd7045675c21078b9011c0394 Mon Sep 17 00:00:00 2001 From: Cora Sutton Date: Sun, 1 May 2022 02:02:08 -0500 Subject: [PATCH 32/73] Fix strict null check issues for src/connector.ts --- src/connector.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/connector.ts b/src/connector.ts index 9573a86fa..f7d930369 100644 --- a/src/connector.ts +++ b/src/connector.ts @@ -652,7 +652,7 @@ export async function connect( return true; } -async function standaloneConnect(connectSequence: ReplConnectSequence) { +async function standaloneConnect(connectSequence: ReplConnectSequence | undefined) { await outputWindow.initResultsDoc(); await outputWindow.openResultsDoc(); From 20d0f2c655e2767b28246b8767d2cd8a24ae8a35 Mon Sep 17 00:00:00 2001 From: Cora Sutton Date: Sun, 1 May 2022 02:02:13 -0500 Subject: [PATCH 33/73] Fix strict null check issues for src/evaluate.ts --- src/evaluate.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/evaluate.ts b/src/evaluate.ts index bacebea19..44e4f3506 100644 --- a/src/evaluate.ts +++ b/src/evaluate.ts @@ -86,6 +86,7 @@ async function evaluateCode( if (code.length > 0) { if (addToHistory) { + util.assertIsDefined(session.replType, 'Expected session to have a repl type!'); replHistory.addToReplHistory(session.replType, code); replHistory.resetState(); } From 1cddd85f0416f8467044d26f7768f4268c8df930 Mon Sep 17 00:00:00 2001 From: Cora Sutton Date: Sun, 1 May 2022 02:02:16 -0500 Subject: [PATCH 34/73] Fix strict null check issues for src/nrepl/bencode.ts --- src/nrepl/bencode.ts | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/nrepl/bencode.ts b/src/nrepl/bencode.ts index b9cd4d133..5dc1e444b 100644 --- a/src/nrepl/bencode.ts +++ b/src/nrepl/bencode.ts @@ -5,6 +5,7 @@ */ import * as stream from 'stream'; import { Buffer } from 'buffer'; +import { assertIsDefined } from '../utilities'; /** Bencode the given JSON object */ const bencode = (value) => { @@ -103,8 +104,9 @@ class BIncrementalDecoder { stack: State[] = []; private complete(data: any) { - if (this.stack.length) { - this.state = this.stack.pop(); + const state = this.stack.pop(); + if (state) { + this.state = state; if (this.state.id == 'list') { this.state.accum.push(data); this.stack.push(this.state); @@ -127,6 +129,7 @@ class BIncrementalDecoder { write(byte: number) { const ch = String.fromCharCode(byte); + let state: State | undefined; if (this.state.id == 'ready') { switch (ch) { case 'i': @@ -139,10 +142,9 @@ class BIncrementalDecoder { this.stack.push({ id: 'list', accum: [] }); break; case 'e': - if (!this.stack.length) { - throw 'unexpected end'; - } - this.state = this.stack.pop(); + state = this.stack.pop(); + assertIsDefined(state, 'Expected there to be states on the stack!'); + this.state = state; if (this.state.id == 'dict') { if (this.state.key !== null) { throw 'Missing value in dict'; From 49a0f0fb3a4091c4071a7a51875e53067bb39fcf Mon Sep 17 00:00:00 2001 From: Cora Sutton Date: Sun, 1 May 2022 02:02:19 -0500 Subject: [PATCH 35/73] Fix strict null check issues for src/nrepl/connectSequence.ts --- src/nrepl/connectSequence.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/nrepl/connectSequence.ts b/src/nrepl/connectSequence.ts index ba16f2b8d..0ae6d92cb 100644 --- a/src/nrepl/connectSequence.ts +++ b/src/nrepl/connectSequence.ts @@ -252,7 +252,7 @@ const defaultCljsTypes: { [id: string]: CljsTypeConfig } = { /** Retrieve the replConnectSequences from the config */ function getCustomConnectSequences(): ReplConnectSequence[] { - const sequences: ReplConnectSequence[] = getConfig().replConnectSequences; + const sequences: ReplConnectSequence[] = getConfig().replConnectSequences || []; for (const sequence of sequences) { if ( @@ -283,7 +283,7 @@ function getConnectSequences(projectTypes: string[]): ReplConnectSequence[] { const customSequences = getCustomConnectSequences(); const defSequences = projectTypes.reduce( (seqs, projectType) => seqs.concat(defaultSequences[projectType]), - [] + [] as ReplConnectSequence[] ); const defSequenceProjectTypes = [...new Set(defSequences.map((s) => s.projectType))]; const sequences = customSequences @@ -306,7 +306,7 @@ async function askForConnectSequence( cljTypes: string[], saveAs: string, logLabel: string -): Promise { +): Promise { // figure out what possible kinds of project we're in const sequences: ReplConnectSequence[] = getConnectSequences(cljTypes); const projectRootUri = state.getProjectRootUri(); @@ -326,6 +326,7 @@ async function askForConnectSequence( return; } const sequence = sequences.find((seq) => seq.name === projectConnectSequenceName); + utilities.assertIsDefined(sequence, 'Expected to find a sequence!'); void state.extensionContext.workspaceState.update('selectedCljTypeName', sequence.projectType); return sequence; } From b9cdce3d5d300515e03b659d6f06824d17d11aee Mon Sep 17 00:00:00 2001 From: Cora Sutton Date: Sun, 1 May 2022 02:02:22 -0500 Subject: [PATCH 36/73] Fix strict null check issues for src/nrepl/index.ts --- src/nrepl/index.ts | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/src/nrepl/index.ts b/src/nrepl/index.ts index 9054d53f8..45e14933b 100644 --- a/src/nrepl/index.ts +++ b/src/nrepl/index.ts @@ -13,6 +13,7 @@ import type { ReplSessionType } from '../config'; import { getStateValue, prettyPrint } from '../../out/cljs-lib/cljs-lib'; import { getConfig } from '../config'; import { log, Direction, loggingEnabled } from './logging'; +import { omit } from 'lodash'; function hasStatus(res: any, status: string): boolean { return res.status && res.status.indexOf(status) > -1; @@ -207,7 +208,7 @@ export class NReplSession { } messageHandlers: { [id: string]: (msg: any) => boolean } = {}; - replType: ReplSessionType = null; + replType: ReplSessionType | null = null; close() { this.client.write({ op: 'close', session: this.sessionId }); @@ -359,11 +360,11 @@ export class NReplSession { ) { const pprintOptions = opts.pprintOptions; opts['pprint'] = pprintOptions.enabled; - delete opts.pprintOptions; + const optsWithoutPprint = omit(opts, 'pprintOptions'); const extraOpts = getServerSidePrinter(pprintOptions); const opMsg = this._createEvalOperationMessage(code, ns, { ...extraOpts, - ...opts, + ...optsWithoutPprint, }); const evaluation = new NReplEvaluation( @@ -381,6 +382,7 @@ export class NReplSession { if (evaluation.onMessage(msg, pprintOptions)) { return true; } + return false; }; this.addRunningID(opMsg.id); this.client.write(opMsg); @@ -433,7 +435,7 @@ export class NReplSession { this, opts.stderr, opts.stdout, - null, + undefined, new Promise((resolve, reject) => { this.messageHandlers[id] = (msg) => { evaluation.setHandlers(resolve, reject); @@ -441,6 +443,7 @@ export class NReplSession { if (evaluation.onMessage(msg, opts.pprintOptions)) { return true; } + return false; }; this.addRunningID(id); this.client.write({ @@ -616,6 +619,7 @@ export class NReplSession { resolve(res); return true; } + return false; }; this.client.write({ op: cmd, @@ -761,9 +765,9 @@ export class NReplEvaluation { constructor( public id: string, public session: NReplSession, - public stderr: (x: string) => void, - public stdout: (x: string) => void, - public stdin: () => Promise, + public stderr: ((x: string) => void) | undefined, + public stdout: ((x: string) => void) | undefined, + public stdin: (() => Promise) | undefined, public value: Promise ) {} From d6080361054b747efe34c0ff0ef3983761037d7a Mon Sep 17 00:00:00 2001 From: Cora Sutton Date: Sun, 1 May 2022 02:02:25 -0500 Subject: [PATCH 37/73] Fix strict null check issues for src/nrepl/jack-in-terminal.ts --- src/nrepl/jack-in-terminal.ts | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/nrepl/jack-in-terminal.ts b/src/nrepl/jack-in-terminal.ts index 2d175dee0..fb1a25743 100644 --- a/src/nrepl/jack-in-terminal.ts +++ b/src/nrepl/jack-in-terminal.ts @@ -2,6 +2,7 @@ import * as vscode from 'vscode'; import * as child from 'child_process'; import * as kill from 'tree-kill'; import * as outputWindow from '../results-output/results-doc'; +import { assertIsDefined } from '../utilities'; export interface JackInTerminalOptions extends vscode.TerminalOptions { name: string; @@ -80,7 +81,7 @@ export class JackInTerminal implements vscode.Pseudoterminal { this.process.on('exit', (status) => { this.writeEmitter.fire(`Jack-in process exited. Status: ${status}\r\n`); }); - this.process.stdout.on('data', (data) => { + this.process.stdout?.on('data', (data) => { const msg = this.dataToString(data); this.writeEmitter.fire(`${msg}\r\n`); // Started nREPL server at 127.0.0.1:1337 @@ -89,9 +90,14 @@ export class JackInTerminal implements vscode.Pseudoterminal { // nbb - nRepl server started on port %d . nrepl-cljs-sci version %s 1337 TODO // TODO: Remove nbb WIP match if (msg.match(/Started nREPL server|nREPL server started/i)) { - const [_, port1, host1, host2, port2, port3] = msg.match( + const connectionInfo = msg.match( /(?:Started nREPL server|nREPL server started)[^\r\n]+?(?:(?:on port (\d+)(?: on host (\S+))?)|([^\s/]+):(\d+))|.*?(\d+) TODO/ ); + assertIsDefined( + connectionInfo, + 'Expected to find connection info in repl started messages!' + ); + const [_, port1, host1, host2, port2, port3] = connectionInfo; this.whenREPLStarted( this.process, host1 ? host1 : host2 ? host2 : 'localhost', @@ -99,7 +105,7 @@ export class JackInTerminal implements vscode.Pseudoterminal { ); } }); - this.process.stderr.on('data', (data) => { + this.process.stderr?.on('data', (data) => { const msg = this.dataToString(data); this.writeEmitter.fire(`${msg}\r\n`); }); @@ -111,8 +117,9 @@ export class JackInTerminal implements vscode.Pseudoterminal { if (this.process && !this.process.killed) { console.log('Closing any ongoing stdin event'); this.writeEmitter.fire('Killing the Jack-in process\r\n'); - this.process.stdin.end(() => { + this.process.stdin?.end(() => { console.log('Killing the Jack-in process'); + assertIsDefined(this.process.pid, 'Expected child process to have a pid!'); kill(this.process.pid); }); } else if (this.process && this.process.killed) { From b33def49b528f45b9bec0f9d71f9d7dbd0018449 Mon Sep 17 00:00:00 2001 From: Cora Sutton Date: Sun, 1 May 2022 02:02:28 -0500 Subject: [PATCH 38/73] Fix strict null check issues for src/nrepl/jack-in.ts --- src/nrepl/jack-in.ts | 32 ++++++++++++++++++-------------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/src/nrepl/jack-in.ts b/src/nrepl/jack-in.ts index 9e31429e2..799e3bfc3 100644 --- a/src/nrepl/jack-in.ts +++ b/src/nrepl/jack-in.ts @@ -14,8 +14,8 @@ import { JackInTerminal, JackInTerminalOptions, createCommandLine } from './jack import * as liveShareSupport from '../live-share'; import { getConfig } from '../config'; -let jackInPTY: JackInTerminal = undefined; -let jackInTerminal: vscode.Terminal = undefined; +let jackInPTY: JackInTerminal | undefined = undefined; +let jackInTerminal: vscode.Terminal | undefined = undefined; function cancelJackInTask() { setTimeout(() => { @@ -25,7 +25,7 @@ function cancelJackInTask() { function resolveEnvVariables(entry: any): any { if (typeof entry === 'string') { - const s = entry.replace(/\$\{env:(\w+)\}/, (_, v) => (process.env[v] ? process.env[v] : '')); + const s = entry.replace(/\$\{env:(\w+)\}/, (_, v) => process.env[v] || ''); return s; } else { return entry; @@ -84,7 +84,7 @@ function executeJackInTask( cancelJackInTask(); } ); - jackInTerminal = (vscode.window).createTerminal({ + jackInTerminal = vscode.window.createTerminal({ name: `Calva Jack-in: ${connectSequence.name}`, pty: jackInPTY, }); @@ -136,7 +136,7 @@ export async function copyJackInCommandToClipboard(): Promise { console.error('An error occurred while initializing project directory.', e); return; } - let projectConnectSequence: ReplConnectSequence; + let projectConnectSequence: ReplConnectSequence | undefined; try { projectConnectSequence = await getProjectConnectSequence(); } catch (e) { @@ -157,7 +157,7 @@ async function getJackInTerminalOptions( projectConnectSequence: ReplConnectSequence ): Promise { const projectTypeName: string = projectConnectSequence.projectType; - let selectedCljsType: CljsTypes; + let selectedCljsType: CljsTypes | undefined; if ( typeof projectConnectSequence.cljsType == 'string' && @@ -172,6 +172,10 @@ async function getJackInTerminalOptions( } const projectType = projectTypes.getProjectTypeForName(projectTypeName); + utilities.assertIsDefined(projectType, 'Expected to find a project type!'); + + const projectRootLocal = state.getProjectRootLocal(); + utilities.assertIsDefined(projectRootLocal, 'Expected to find a local project root!'); let args: string[] = await projectType.commandLine(projectConnectSequence, selectedCljsType); let cmd: string[]; @@ -181,9 +185,7 @@ async function getJackInTerminalOptions( const jarSourceUri = vscode.Uri.file( path.join(state.extensionContext.extensionPath, 'deps.clj.jar') ); - const jarDestUri = vscode.Uri.file( - path.join(state.getProjectRootLocal(), '.calva', 'deps.clj.jar') - ); + const jarDestUri = vscode.Uri.file(path.join(projectRootLocal, '.calva', 'deps.clj.jar')); try { await vscode.workspace.fs.copy(jarSourceUri, jarDestUri, { overwrite: false, @@ -211,13 +213,13 @@ async function getJackInTerminalOptions( ...processEnvObject(projectConnectSequence.jackInEnv), }, isWin: projectTypes.isWin, - cwd: state.getProjectRootLocal(), + cwd: projectRootLocal, useShell: projectTypes.isWin ? projectType.processShellWin : projectType.processShellUnix, }; return terminalOptions; } -async function getProjectConnectSequence(): Promise { +async function getProjectConnectSequence(): Promise { const cljTypes: string[] = await projectTypes.detectProjectTypes(); if (cljTypes.length > 1) { const connectSequence = await askForConnectSequence( @@ -233,13 +235,15 @@ async function getProjectConnectSequence(): Promise { } } -export async function jackIn(connectSequence: ReplConnectSequence, cb?: () => unknown) { +export async function jackIn(connectSequence: ReplConnectSequence | undefined, cb?: () => unknown) { try { await liveShareSupport.setupLiveShareListener(); } catch (e) { console.error('An error occurred while setting up Live Share listener.', e); } - if (state.getProjectRootUri().scheme === 'vsls') { + const projectRootUri = state.getProjectRootUri(); + utilities.assertIsDefined(projectRootUri, 'Expected to find a project root URI!'); + if (projectRootUri.scheme === 'vsls') { outputWindow.append("; Aborting Jack-in, since you're the guest of a live share session."); outputWindow.append( '; Please use this command instead: Connect to a running REPL server in the project.' @@ -251,7 +255,7 @@ export async function jackIn(connectSequence: ReplConnectSequence, cb?: () => un outputWindow.append('; Jacking in...'); await outputWindow.openResultsDoc(); - let projectConnectSequence: ReplConnectSequence = connectSequence; + let projectConnectSequence: ReplConnectSequence | undefined = connectSequence; if (!projectConnectSequence) { try { projectConnectSequence = await getProjectConnectSequence(); From 2dc2abf11094fd52dee12445b546655be4c96367 Mon Sep 17 00:00:00 2001 From: Cora Sutton Date: Sun, 1 May 2022 02:02:30 -0500 Subject: [PATCH 39/73] Fix strict null check issues for src/nrepl/project-types.ts --- src/nrepl/project-types.ts | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/nrepl/project-types.ts b/src/nrepl/project-types.ts index 42c6e5b02..764e7b05f 100644 --- a/src/nrepl/project-types.ts +++ b/src/nrepl/project-types.ts @@ -20,7 +20,7 @@ export type ProjectType = { resolveBundledPathUnix?: () => string; processShellWin: boolean; processShellUnix: boolean; - commandLine: (connectSequence: ReplConnectSequence, cljsType: CljsTypes) => any; + commandLine: (connectSequence: ReplConnectSequence, cljsType: CljsTypes | undefined) => any; useWhenExists: string | undefined; nReplPortFile: string[]; }; @@ -471,7 +471,10 @@ const projectTypes: { [id: string]: ProjectType } = { }, }; -async function cljCommandLine(connectSequence: ReplConnectSequence, cljsType: CljsTypes) { +async function cljCommandLine( + connectSequence: ReplConnectSequence, + cljsType: CljsTypes | undefined +) { const out: string[] = []; let depsUri: vscode.Uri; @@ -582,7 +585,7 @@ async function cljCommandLine(connectSequence: ReplConnectSequence, cljsType: Cl async function leinCommandLine( command: string[], - cljsType: CljsTypes, + cljsType: CljsTypes | undefined, connectSequence: ReplConnectSequence ) { const out: string[] = []; From 407b589846e270cb3a84789c2bd49dde4c1c01d4 Mon Sep 17 00:00:00 2001 From: Cora Sutton Date: Sun, 1 May 2022 02:02:32 -0500 Subject: [PATCH 40/73] Fix strict null check issues for src/nrepl/repl-start.ts --- src/nrepl/repl-start.ts | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/nrepl/repl-start.ts b/src/nrepl/repl-start.ts index deb87e815..7ffa17624 100644 --- a/src/nrepl/repl-start.ts +++ b/src/nrepl/repl-start.ts @@ -61,7 +61,7 @@ async function fetchConfig(configName: string): Promise { async function downloadDram(storageUri: vscode.Uri, configPath: string, filePath: string) { const calva = vscode.extensions.getExtension('betterthantomorrow.calva'); - const calvaVersion = calva.packageJSON.version; + const calvaVersion = calva?.packageJSON.version; const isDebug = process.env['IS_DEBUG'] === 'true'; const branch = isDebug || calvaVersion.match(/-.+$/) ? 'dev' : 'published'; const dramBaseUrl = `${DRAM_BASE_URL}/${branch}/drams`; @@ -167,7 +167,9 @@ export async function startStandaloneRepl( void clojureLsp.startClientCommand(); } - const [mainDoc, mainEditor] = await openStoredDoc(storageUri, tempDirUri, config.files[0]); + const main = await openStoredDoc(storageUri, tempDirUri, config.files[0]); + utilities.assertIsDefined(main, 'Expected to be able to open the stored doc!'); + const [mainDoc, mainEditor] = main; for (const file of config.files.slice(1)) { await openStoredDoc(storageUri, tempDirUri, file); } @@ -186,7 +188,12 @@ export async function startStandaloneRepl( viewColumn: vscode.ViewColumn.One, preserveFocus: false, }); - await eval.loadFile({}, getConfig().prettyPrintingOptions); + const pprintOptions = getConfig().prettyPrintingOptions; + utilities.assertIsDefined( + pprintOptions, + 'Expected there to be pretty printing options configured' + ); + await eval.loadFile({}, pprintOptions); outputWindow.appendPrompt(); }); } From 5e2ed4bbd9fe9ed6a469a1e07e7c2c47eddff9fc Mon Sep 17 00:00:00 2001 From: Cora Sutton Date: Sun, 1 May 2022 02:02:35 -0500 Subject: [PATCH 41/73] Fix strict null check issues for src/paredit/extension.ts --- src/paredit/extension.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/paredit/extension.ts b/src/paredit/extension.ts index e136c7fce..553a413a4 100644 --- a/src/paredit/extension.ts +++ b/src/paredit/extension.ts @@ -109,7 +109,9 @@ const pareditCommands: PareditCommand[] = [ { command: 'paredit.rangeForDefun', handler: (doc: EditableDocument) => { - paredit.selectRange(doc, paredit.rangeForDefun(doc)); + const range = paredit.rangeForDefun(doc); + assertIsDefined(range, 'Expected to find a range for the current defun!'); + paredit.selectRange(doc, range); }, }, { From 6ff6e9bd01ce2078e438374ec1fa647fb88ecc49 Mon Sep 17 00:00:00 2001 From: Cora Sutton Date: Sun, 1 May 2022 02:02:38 -0500 Subject: [PATCH 42/73] Fix strict null check issues for src/project-root.ts --- src/project-root.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/project-root.ts b/src/project-root.ts index 01b5369a2..55b0ffd9f 100644 --- a/src/project-root.ts +++ b/src/project-root.ts @@ -15,8 +15,9 @@ export async function findProjectRootPaths() { const excludeDirsGlob = excludePattern(); const t0 = new Date().getTime(); const rootPaths: string[] = []; - if (vscode.workspace.workspaceFolders?.length > 0) { - const wsRootPaths = vscode.workspace.workspaceFolders.map((f) => f.uri.fsPath); + const workspaceFolders = vscode.workspace.workspaceFolders; + if (workspaceFolders && workspaceFolders.length > 0) { + const wsRootPaths = workspaceFolders.map((f) => f.uri.fsPath); rootPaths.push(...wsRootPaths); } const candidateUris = await vscode.workspace.findFiles(projectFilesGlob, excludeDirsGlob, 10000); From e8c4cb17e9e016b30ab8d23cce4a30c27f60e08c Mon Sep 17 00:00:00 2001 From: Cora Sutton Date: Sun, 1 May 2022 02:02:40 -0500 Subject: [PATCH 43/73] Fix strict null check issues for src/providers/definition.ts --- src/providers/definition.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/providers/definition.ts b/src/providers/definition.ts index cd719090d..c2903fc66 100644 --- a/src/providers/definition.ts +++ b/src/providers/definition.ts @@ -21,6 +21,7 @@ async function provideClojureDefinition(document, position: vscode.Position, _to if (util.getConnectedState() && !posIsEvalPos) { const text = util.getWordAtPosition(document, position); const client = replSession.getSession(util.getFileType(document)); + util.assertIsDefined(client, 'Expected there to be a repl client!'); const info = await client.info(namespace.getNamespace(document), text); if (info.file && info.file.length > 0) { const pos = new vscode.Position(info.line - 1, info.column || 0); From b6b8db4ea5f3ebf279c0c7ff75423629bd9c4432 Mon Sep 17 00:00:00 2001 From: Cora Sutton Date: Sun, 1 May 2022 02:02:42 -0500 Subject: [PATCH 44/73] Fix strict null check issues for src/providers/signature.ts --- src/providers/signature.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/providers/signature.ts b/src/providers/signature.ts index fc15cbbe0..72d6896e9 100644 --- a/src/providers/signature.ts +++ b/src/providers/signature.ts @@ -42,6 +42,7 @@ export async function provideSignatureHelp( if (signatures) { const help = new SignatureHelp(), currentArgsRanges = getCurrentArgsRanges(document, idx); + util.assertIsDefined(currentArgsRanges, 'Expected to find the current args ranges!'); help.signatures = signatures; help.activeSignature = getActiveSignatureIdx(signatures, currentArgsRanges.length); if (signatures[help.activeSignature].parameters !== undefined) { @@ -68,7 +69,7 @@ function getCurrentArgsRanges(document: TextDocument, idx: number): Range[] | un // Are we in a function that gets a threaded first parameter? const { previousRangeIndex, previousFunction } = getPreviousRangeIndexAndFunction(document, idx); const isInThreadFirst: boolean = - (previousRangeIndex > 1 && ['->', 'some->'].includes(previousFunction)) || + (previousRangeIndex > 1 && previousFunction && ['->', 'some->'].includes(previousFunction)) || (previousRangeIndex > 1 && previousRangeIndex % 2 !== 0 && previousFunction === 'cond->'); if (allRanges !== undefined) { @@ -83,7 +84,7 @@ function getActiveSignatureIdx(signatures: SignatureInformation[], currentArgsCo return activeSignatureIdx !== -1 ? activeSignatureIdx : signatures.length - 1; } -function getSymbol(document: TextDocument, idx: number): string { +function getSymbol(document: TextDocument, idx: number): string | undefined { const cursor: LispTokenCursor = docMirror.getDocument(document).getTokenCursor(idx); return cursor.getFunctionName(); } From aea1267926a755e34f78aeb3860761f1a79fdfe4 Mon Sep 17 00:00:00 2001 From: Cora Sutton Date: Sun, 1 May 2022 02:02:44 -0500 Subject: [PATCH 45/73] Fix strict null check issues for src/refresh.ts --- src/refresh.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/refresh.ts b/src/refresh.ts index 36b03e4f6..c97b2a76e 100644 --- a/src/refresh.ts +++ b/src/refresh.ts @@ -22,10 +22,10 @@ function report(res, chan: vscode.OutputChannel) { function refresh(document = {}) { const doc = util.tryToGetDocument(document), - client: NReplSession = replSession.getSession(util.getFileType(doc)), + client: NReplSession | undefined = replSession.getSession(util.getFileType(doc)), chan: vscode.OutputChannel = state.outputChannel(); - if (client != undefined) { + if (client !== undefined) { chan.appendLine('Reloading...'); void client.refresh().then((res) => { report(res, chan); @@ -37,10 +37,10 @@ function refresh(document = {}) { function refreshAll(document = {}) { const doc = util.tryToGetDocument(document), - client: NReplSession = replSession.getSession(util.getFileType(doc)), + client: NReplSession | undefined = replSession.getSession(util.getFileType(doc)), chan: vscode.OutputChannel = state.outputChannel(); - if (client != undefined) { + if (client !== undefined) { chan.appendLine('Reloading all the things...'); void client.refreshAll().then((res) => { report(res, chan); From c6fd3332ba07d9da705fbf700f03a7529c8d1cda Mon Sep 17 00:00:00 2001 From: Cora Sutton Date: Sun, 1 May 2022 02:02:46 -0500 Subject: [PATCH 46/73] Fix strict null check issues for src/results-output/results-doc.ts --- src/results-output/results-doc.ts | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/results-output/results-doc.ts b/src/results-output/results-doc.ts index 6d3d8b021..0b18a6516 100644 --- a/src/results-output/results-doc.ts +++ b/src/results-output/results-doc.ts @@ -177,7 +177,7 @@ export async function initResultsDoc(): Promise { promptCursor.previous(); } while (promptCursor.getPrevToken().type !== 'prompt' && !promptCursor.atStart()); const submitRange = selectionCursor.rangeForCurrentForm(idx); - submitOnEnter = submitRange && submitRange[1] > promptCursor.offsetStart; + submitOnEnter = !!submitRange && submitRange[1] > promptCursor.offsetStart; } } } @@ -227,6 +227,7 @@ export async function revealDocForCurrentNS(preserveFocus: boolean = true) { export async function setNamespaceFromCurrentFile() { const session = replSession.getSession(); + util.assertIsDefined(session, 'Expected there to be a session!'); const ns = namespace.getNamespace(util.tryToGetDocument({})); if (getNs() !== ns && util.isDefined(ns)) { await session.switchNS(ns); @@ -238,6 +239,7 @@ export async function setNamespaceFromCurrentFile() { async function appendFormGrabbingSessionAndNS(topLevel: boolean) { const session = replSession.getSession(); + util.assertIsDefined(session, 'Expected there to be a session!'); const ns = namespace.getNamespace(util.tryToGetDocument({})); const editor = util.getActiveTextEditor(); const doc = editor.document; @@ -338,7 +340,8 @@ async function writeNextOutputBatch() { // Any entries that contain onAppended are not batched with other pending // entries to simplify providing the correct insert position to the callback. if (resultsBuffer[0].onAppended) { - return await writeToResultsDoc(resultsBuffer.shift()); + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + return await writeToResultsDoc(resultsBuffer.shift()!); } // Batch all remaining entries up until another onAppended callback. const [nextText, remaining] = splitEditQueueForTextBatching(resultsBuffer); @@ -433,5 +436,9 @@ export function appendPrompt(onAppended?: OnAppendedCallback) { } function getUriForCurrentNamespace(): Promise { - return namespace.getUriForNamespace(getSession(), getNs()); + const session = getSession(); + util.assertIsDefined(session, 'Expected there to be a current session!'); + const ns = getNs(); + util.assertIsDefined(ns, 'Expected there to be a current namespace!'); + return namespace.getUriForNamespace(session, ns); } From 8425e1a13a5507ac84067dab524de4d695b84746 Mon Sep 17 00:00:00 2001 From: Cora Sutton Date: Sun, 1 May 2022 02:02:48 -0500 Subject: [PATCH 47/73] Fix strict null check issues for src/statusbar.ts --- src/statusbar.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/statusbar.ts b/src/statusbar.ts index 347d610eb..c38ae2e26 100644 --- a/src/statusbar.ts +++ b/src/statusbar.ts @@ -83,7 +83,7 @@ function update(context = state.extensionContext) { connectionStatus.command = 'calva.startOrConnectRepl'; typeStatus.color = colorValue('typeStatusColor', currentConf); const replType = getReplSessionTypeFromState(); - if (replType !== null) { + if (replType !== undefined) { typeStatus.text = ['cljc', config.REPL_FILE_EXT].includes(fileType) ? `cljc/${replType}` : replType; From c3735e3c05906a535ab2d017fbed01c0832b62a6 Mon Sep 17 00:00:00 2001 From: Cora Sutton Date: Sun, 1 May 2022 02:02:51 -0500 Subject: [PATCH 48/73] Fix strict null check issues for src/testRunner.ts --- src/testRunner.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/testRunner.ts b/src/testRunner.ts index 0642e0070..8cef3c5fb 100644 --- a/src/testRunner.ts +++ b/src/testRunner.ts @@ -237,6 +237,7 @@ function reportTests( // FIXME: use cljs session where necessary async function runAllTests(controller: vscode.TestController, document = {}) { const session = getSession(util.getFileType(document)); + util.assertIsDefined(session, 'Expected there to be a current session!'); outputWindow.append('; Running all project tests…'); try { reportTests(controller, session, [await session.testAll()]); @@ -293,6 +294,7 @@ async function runNamespaceTestsImpl( } const session = getSession(util.getFileType(document)); + util.assertIsDefined(session, 'Expected there to be a current session!'); // TODO.marc: Should we be passing the `document` argument to `loadFile`? await evaluate.loadFile({}, disabledPrettyPrinter); @@ -318,6 +320,7 @@ async function runNamespaceTests(controller: vscode.TestController, document: vs return; } const session = getSession(util.getFileType(document)); + util.assertIsDefined(session, 'Expected there to be a current session!'); const ns = namespace.getNamespace(doc); const nss = await considerTestNS(ns, session); void runNamespaceTestsImpl(controller, document, nss); @@ -333,6 +336,7 @@ function getTestUnderCursor() { async function runTestUnderCursor(controller: vscode.TestController) { const doc = util.tryToGetDocument({}); const session = getSession(util.getFileType(doc)); + util.assertIsDefined(session, 'Expected there to be a current session!'); const ns = namespace.getNamespace(doc); const test = getTestUnderCursor(); @@ -376,6 +380,7 @@ function runNamespaceTestsCommand(controller: vscode.TestController) { async function rerunTests(controller: vscode.TestController, document = {}) { const session = getSession(util.getFileType(document)); + util.assertIsDefined(session, 'Expected there to be a current session!'); await evaluate.loadFile({}, disabledPrettyPrinter); outputWindow.append('; Running previously failed tests…'); From 1cdcd92265ab5bc2afa31981ddf7be9abb06906a Mon Sep 17 00:00:00 2001 From: Cora Sutton Date: Sun, 1 May 2022 02:02:54 -0500 Subject: [PATCH 49/73] Fix strict null check issues for src/util/cursor-get-text.ts --- src/util/cursor-get-text.ts | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/util/cursor-get-text.ts b/src/util/cursor-get-text.ts index 6b565ed55..1a976dbae 100644 --- a/src/util/cursor-get-text.ts +++ b/src/util/cursor-get-text.ts @@ -3,6 +3,7 @@ */ import { EditableDocument } from '../cursor-doc/model'; +import { assertIsDefined } from '../utilities'; export type RangeAndText = [[number, number], string] | [undefined, '']; @@ -11,7 +12,10 @@ export function currentTopLevelFunction( active: number = doc.selection.active ): RangeAndText { const defunCursor = doc.getTokenCursor(active); - const defunStart = defunCursor.rangeForDefun(active)[0]; + assertIsDefined(defunCursor, 'Expected a token cursor!'); + const defunRange = defunCursor.rangeForDefun(active); + assertIsDefined(defunRange, 'Expected a range representing the current defun!'); + const defunStart = defunRange[0]; const cursor = doc.getTokenCursor(defunStart); while (cursor.downList()) { cursor.forwardWhitespace(); @@ -41,7 +45,7 @@ export function currentTopLevelForm(doc: EditableDocument): RangeAndText { function rangeOrStartOfFileToCursor( doc: EditableDocument, - foldRange: [number, number], + foldRange: [number, number] | undefined, startFrom: number ): RangeAndText { if (foldRange) { @@ -65,12 +69,17 @@ function rangeOrStartOfFileToCursor( export function currentEnclosingFormToCursor(doc: EditableDocument): RangeAndText { const cursor = doc.getTokenCursor(doc.selection.active); const enclosingRange = cursor.rangeForList(1); + assertIsDefined(enclosingRange, 'Expected to get the range that encloses the current form!'); return rangeOrStartOfFileToCursor(doc, enclosingRange, enclosingRange[0]); } export function currentTopLevelFormToCursor(doc: EditableDocument): RangeAndText { const cursor = doc.getTokenCursor(doc.selection.active); const defunRange = cursor.rangeForDefun(doc.selection.active); + assertIsDefined( + defunRange, + 'Expected to get the range that encloses the current top-level form!' + ); return rangeOrStartOfFileToCursor(doc, defunRange, defunRange[0]); } From fcf14e04179ab763d5b3e601b64b5a679f08b2fd Mon Sep 17 00:00:00 2001 From: Cora Sutton Date: Sun, 1 May 2022 02:03:44 -0500 Subject: [PATCH 50/73] Enable strict null checks for typescript --- tsconfig.json | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/tsconfig.json b/tsconfig.json index a07a64cca..c7ef44e50 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -4,7 +4,7 @@ "tsBuildInfoFile": "tsconfig.tsbuildinfo", "strict": false /* Enable all strict type-checking options. */, // "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */ - // "strictNullChecks": true, /* Enable strict null checks. */ + "strictNullChecks": true, /* Enable strict null checks. */ // "strictFunctionTypes": true, /* Enable strict checking of function types. */ // "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */ // "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */ @@ -18,9 +18,17 @@ "module": "commonjs", "target": "es6", "outDir": "out", - "lib": ["es2020", "dom"], + "lib": [ + "es2020", + "dom" + ], "sourceMap": true, "rootDir": "src" }, - "exclude": ["node_modules", "src/webview", "src/webview.ts", ".vscode-test"] -} + "exclude": [ + "node_modules", + "src/webview", + "src/webview.ts", + ".vscode-test" + ] +} \ No newline at end of file From adfe62af90a5fd5379c809f0613a700ae9449300 Mon Sep 17 00:00:00 2001 From: Cora Sutton Date: Sun, 1 May 2022 02:07:35 -0500 Subject: [PATCH 51/73] Run prettier formatter --- tsconfig.json | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/tsconfig.json b/tsconfig.json index c7ef44e50..1c5fca7f4 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -4,7 +4,7 @@ "tsBuildInfoFile": "tsconfig.tsbuildinfo", "strict": false /* Enable all strict type-checking options. */, // "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */ - "strictNullChecks": true, /* Enable strict null checks. */ + "strictNullChecks": true /* Enable strict null checks. */, // "strictFunctionTypes": true, /* Enable strict checking of function types. */ // "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */ // "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */ @@ -18,17 +18,9 @@ "module": "commonjs", "target": "es6", "outDir": "out", - "lib": [ - "es2020", - "dom" - ], + "lib": ["es2020", "dom"], "sourceMap": true, "rootDir": "src" }, - "exclude": [ - "node_modules", - "src/webview", - "src/webview.ts", - ".vscode-test" - ] -} \ No newline at end of file + "exclude": ["node_modules", "src/webview", "src/webview.ts", ".vscode-test"] +} From b119164e5bbc24afa1aa46b7cdae2cf8af34720c Mon Sep 17 00:00:00 2001 From: Cora Sutton Date: Sun, 1 May 2022 02:16:19 -0500 Subject: [PATCH 52/73] Rename getSession => tryToGetSession and make another getSession getSession will throw an error if no session was found. --- src/clojuredocs.ts | 2 -- src/connector.ts | 10 +++++----- src/debugger/calva-debug.ts | 14 +++++++------- src/debugger/decorations.ts | 2 +- src/evaluate.ts | 12 ++++++------ src/namespace.ts | 2 +- src/nrepl/repl-session.ts | 28 +++++++++++++++++++++------- src/nrepl/repl-start.ts | 2 +- src/providers/completion.ts | 4 ++-- src/providers/definition.ts | 2 +- src/providers/hover.ts | 2 +- src/providers/signature.ts | 2 +- src/refresh.ts | 4 ++-- src/results-output/results-doc.ts | 4 ++-- src/statusbar.ts | 4 ++-- src/testRunner.ts | 12 ++++++------ 16 files changed, 59 insertions(+), 47 deletions(-) diff --git a/src/clojuredocs.ts b/src/clojuredocs.ts index 47c20bdc5..db81ef623 100644 --- a/src/clojuredocs.ts +++ b/src/clojuredocs.ts @@ -174,8 +174,6 @@ async function clojureDocsLookup( const symbol = util.getWordAtPosition(doc, position); const ns = namespace.getNamespace(doc); const session = replSession.getSession(util.getFileType(doc)); - util.assertIsDefined(session, 'Expected there to be a repl session!'); - const docsFromCider = await clojureDocsCiderNReplLookup(session, symbol, ns); if (docsFromCider) { return docsFromCider; diff --git a/src/connector.ts b/src/connector.ts index f7d930369..e56b884c7 100644 --- a/src/connector.ts +++ b/src/connector.ts @@ -730,10 +730,10 @@ export default { let newSession: NReplSession | undefined; if (getStateValue('connected')) { - if (replSession.getSession('cljc') == replSession.getSession('cljs')) { - newSession = replSession.getSession('clj'); - } else if (replSession.getSession('cljc') == replSession.getSession('clj')) { - newSession = replSession.getSession('cljs'); + if (replSession.tryToGetSession('cljc') == replSession.tryToGetSession('cljs')) { + newSession = replSession.tryToGetSession('clj'); + } else if (replSession.tryToGetSession('cljc') == replSession.tryToGetSession('clj')) { + newSession = replSession.tryToGetSession('cljs'); } setStateValue('cljc', newSession); if (outputWindow.isResultsDoc(util.getActiveTextEditor().document)) { @@ -745,7 +745,7 @@ export default { } }, switchCljsBuild: async () => { - const cljSession = replSession.getSession('clj'); + const cljSession = replSession.tryToGetSession('clj'); const cljsTypeName: string | undefined = state.extensionContext.workspaceState.get('selectedCljsTypeName'), cljTypeName: string | undefined = diff --git a/src/debugger/calva-debug.ts b/src/debugger/calva-debug.ts index 5a44c8852..0bef948b6 100644 --- a/src/debugger/calva-debug.ts +++ b/src/debugger/calva-debug.ts @@ -100,7 +100,7 @@ class CalvaDebugSession extends LoggingDebugSession { response: DebugProtocol.AttachResponse, args: DebugProtocol.AttachRequestArguments ): void { - const cljSession = replSession.getSession(CLOJURE_SESSION_NAME); + const cljSession = replSession.tryToGetSession(CLOJURE_SESSION_NAME); this.sendResponse(response); state @@ -114,7 +114,7 @@ class CalvaDebugSession extends LoggingDebugSession { args: DebugProtocol.ContinueArguments, request?: DebugProtocol.Request ): void { - const cljSession = replSession.getSession(CLOJURE_SESSION_NAME); + const cljSession = replSession.tryToGetSession(CLOJURE_SESSION_NAME); if (cljSession) { const { id, key } = getStateValue(DEBUG_RESPONSE_KEY); @@ -146,7 +146,7 @@ class CalvaDebugSession extends LoggingDebugSession { args: DebugProtocol.NextArguments, request?: DebugProtocol.Request ): void { - const cljSession = replSession.getSession(CLOJURE_SESSION_NAME); + const cljSession = replSession.tryToGetSession(CLOJURE_SESSION_NAME); if (cljSession) { const { id, key } = getStateValue(DEBUG_RESPONSE_KEY); @@ -169,7 +169,7 @@ class CalvaDebugSession extends LoggingDebugSession { args: DebugProtocol.StepInArguments, request?: DebugProtocol.Request ): void { - const cljSession = replSession.getSession(CLOJURE_SESSION_NAME); + const cljSession = replSession.tryToGetSession(CLOJURE_SESSION_NAME); if (cljSession) { const { id, key } = getStateValue(DEBUG_RESPONSE_KEY); @@ -192,7 +192,7 @@ class CalvaDebugSession extends LoggingDebugSession { args: DebugProtocol.StepOutArguments, request?: DebugProtocol.Request ): void { - const cljSession = replSession.getSession(CLOJURE_SESSION_NAME); + const cljSession = replSession.tryToGetSession(CLOJURE_SESSION_NAME); if (cljSession) { const { id, key } = getStateValue(DEBUG_RESPONSE_KEY); @@ -325,7 +325,7 @@ class CalvaDebugSession extends LoggingDebugSession { args: DebugProtocol.DisconnectArguments, request?: DebugProtocol.Request ): void { - const cljSession = replSession.getSession(CLOJURE_SESSION_NAME); + const cljSession = replSession.tryToGetSession(CLOJURE_SESSION_NAME); if (cljSession) { const { id, key } = getStateValue(DEBUG_RESPONSE_KEY); @@ -439,7 +439,7 @@ function handleNeedDebugInput(response: any): void { void debug.startDebugging(undefined, CALVA_DEBUG_CONFIGURATION); } } else { - const cljSession = replSession.getSession(CLOJURE_SESSION_NAME); + const cljSession = replSession.tryToGetSession(CLOJURE_SESSION_NAME); util.assertIsDefined(cljSession, 'Expected there to be a repl session!'); void cljSession.sendDebugInput(':quit', response.id, response.key); void vscode.window.showInformationMessage( diff --git a/src/debugger/decorations.ts b/src/debugger/decorations.ts index f91f3022b..9ead209b8 100644 --- a/src/debugger/decorations.ts +++ b/src/debugger/decorations.ts @@ -119,7 +119,7 @@ function triggerUpdateAndRenderDecorations() { const editor = util.tryToGetActiveTextEditor(); if (editor) { timeout = setTimeout(() => { - const cljSession = replSession.getSession('clj'); + const cljSession = replSession.tryToGetSession('clj'); const lspClient = getStateValue(lsp.LSP_CLIENT_KEY); void update(editor, cljSession, lspClient).then(renderInAllVisibleEditors); }, 50); diff --git a/src/evaluate.ts b/src/evaluate.ts index 44e4f3506..fbe344ed5 100644 --- a/src/evaluate.ts +++ b/src/evaluate.ts @@ -228,7 +228,7 @@ async function evaluateSelection(document = {}, options) { const line = codeSelection.start.line; const column = codeSelection.start.character; const filePath = doc.fileName; - const session = replSession.getSession(util.getFileType(doc)); + const session = replSession.tryToGetSession(util.getFileType(doc)); if (code.length > 0) { if (options.debug) { @@ -408,7 +408,7 @@ async function loadFile( const doc = util.tryToGetDocument(document); const fileType = util.getFileType(doc); const ns = namespace.getNamespace(doc); - const session = replSession.getSession(util.getFileType(doc)); + const session = replSession.tryToGetSession(util.getFileType(doc)); if (doc && doc.languageId == 'clojure' && fileType != 'edn' && getStateValue('connected')) { state.analytics().logEvent('Evaluation', 'LoadFile').send(); @@ -451,7 +451,7 @@ async function loadFile( async function evaluateUser(code: string) { const fileType = util.getFileType(util.tryToGetDocument({})), - session = replSession.getSession(fileType); + session = replSession.tryToGetSession(fileType); if (session) { try { await session.eval(code, session.client.ns).value; @@ -473,7 +473,7 @@ async function requireREPLUtilitiesCommand() { sessionType = replSession.getReplSessionTypeFromState(), form = sessionType == 'cljs' ? CLJS_FORM : CLJ_FORM, fileType = util.getFileType(util.tryToGetDocument({})), - session = replSession.getSession(fileType); + session = replSession.tryToGetSession(fileType); if (session) { try { @@ -492,7 +492,7 @@ async function requireREPLUtilitiesCommand() { async function copyLastResultCommand() { const chan = state.outputChannel(); - const session = replSession.getSession(util.getFileType(util.tryToGetDocument({}))); + const session = replSession.tryToGetSession(util.getFileType(util.tryToGetDocument({}))); util.assertIsDefined(session, 'Expected there to be a repl session!'); const value = await session.eval('*1', session.client.ns).value; @@ -551,7 +551,7 @@ export async function evaluateInOutputWindow( const outputDocument = await outputWindow.openResultsDoc(); const evalPos = outputDocument.positionAt(outputDocument.getText().length); try { - const session = replSession.getSession(sessionType); + const session = replSession.tryToGetSession(sessionType); replSession.updateReplSessionType(); if (outputWindow.getNs() !== ns) { util.assertIsDefined(session, 'Expected there to be a repl session!'); diff --git a/src/namespace.ts b/src/namespace.ts index 477b45ac5..dd945bd14 100644 --- a/src/namespace.ts +++ b/src/namespace.ts @@ -72,7 +72,7 @@ export async function createNamespaceFromDocumentIfNotExists(doc) { const document = utilities.tryToGetDocument(doc); if (document) { const ns = getNamespace(document); - const client = replSession.getSession(utilities.getFileType(document)); + const client = replSession.tryToGetSession(utilities.getFileType(document)); if (client) { const nsList = await client.listNamespaces([]); if (nsList['ns-list'] && nsList['ns-list'].includes(ns)) { diff --git a/src/nrepl/repl-session.ts b/src/nrepl/repl-session.ts index dced71dac..d92bb1915 100644 --- a/src/nrepl/repl-session.ts +++ b/src/nrepl/repl-session.ts @@ -1,9 +1,9 @@ import { NReplSession } from '.'; -import { cljsLib, tryToGetDocument, getFileType } from '../utilities'; +import { cljsLib, tryToGetDocument, getFileType, assertIsDefined } from '../utilities'; import * as outputWindow from '../results-output/results-doc'; import { isUndefined } from 'lodash'; -function getSession(fileType?: string): NReplSession | undefined { +function tryToGetSession(fileType?: string): NReplSession | undefined { const doc = tryToGetDocument({}); if (isUndefined(fileType)) { @@ -20,6 +20,14 @@ function getSession(fileType?: string): NReplSession | undefined { } } +function getSession(fileType?: string): NReplSession { + const session = tryToGetSession(fileType); + + assertIsDefined(session, 'Expected to be able to get an nrepl session!'); + + return session; +} + function getReplSessionType(connected: boolean): string | undefined { const doc = tryToGetDocument({}); const fileType = getFileType(doc); @@ -28,12 +36,12 @@ function getReplSessionType(connected: boolean): string | undefined { if (connected) { if (outputWindow.isResultsDoc(doc)) { sessionType = outputWindow.getSessionType(); - } else if (fileType == 'cljs' && getSession('cljs') !== null) { + } else if (fileType == 'cljs' && tryToGetSession('cljs') !== undefined) { sessionType = 'cljs'; - } else if (fileType == 'clj' && getSession('clj') !== null) { + } else if (fileType == 'clj' && tryToGetSession('clj') !== undefined) { sessionType = 'clj'; - } else if (getSession('cljc') !== null) { - sessionType = getSession('cljc') == getSession('clj') ? 'clj' : 'cljs'; + } else if (tryToGetSession('cljc') !== undefined) { + sessionType = tryToGetSession('cljc') == tryToGetSession('clj') ? 'clj' : 'cljs'; } else { sessionType = 'clj'; } @@ -52,4 +60,10 @@ function getReplSessionTypeFromState(): string | undefined { return cljsLib.getStateValue('current-session-type'); } -export { getSession, getReplSessionType, updateReplSessionType, getReplSessionTypeFromState }; +export { + tryToGetSession, + getSession, + getReplSessionType, + updateReplSessionType, + getReplSessionTypeFromState, +}; diff --git a/src/nrepl/repl-start.ts b/src/nrepl/repl-start.ts index 7ffa17624..497668a7a 100644 --- a/src/nrepl/repl-start.ts +++ b/src/nrepl/repl-start.ts @@ -249,7 +249,7 @@ export function startOrConnectRepl() { commands[START_HELLO_CLJS_NODE_OPTION] = START_HELLO_CLJS_NODE_COMMAND; } else { commands[DISCONNECT_OPTION] = DISCONNECT_COMMAND; - if (replSession.getSession('clj')) { + if (replSession.tryToGetSession('clj')) { commands[OPEN_WINDOW_OPTION] = OPEN_WINDOW_COMMAND; } } diff --git a/src/providers/completion.ts b/src/providers/completion.ts index 69eea7a39..65c72525e 100644 --- a/src/providers/completion.ts +++ b/src/providers/completion.ts @@ -102,7 +102,7 @@ export default class CalvaCompletionItemProvider implements CompletionItemProvid util.assertIsDefined(activeTextEditor, 'Expected window to have activeTextEditor defined!'); - const client = replSession.getSession(util.getFileType(activeTextEditor.document)); + const client = replSession.tryToGetSession(util.getFileType(activeTextEditor.document)); if (client) { await namespace.createNamespaceFromDocumentIfNotExists(activeTextEditor.document); const ns = namespace.getDocumentNamespace(); @@ -176,7 +176,7 @@ async function replCompletions( replContext = `${contextStart}__prefix__${contextEnd}`, toplevelIsValidForm = toplevelStartCursor.withinValidList() && replContext != '__prefix__', ns = namespace.getNamespace(document), - client = replSession.getSession(util.getFileType(document)); + client = replSession.tryToGetSession(util.getFileType(document)); util.assertIsDefined(client, 'Expected there to be a repl client!'); diff --git a/src/providers/definition.ts b/src/providers/definition.ts index c2903fc66..7562a817a 100644 --- a/src/providers/definition.ts +++ b/src/providers/definition.ts @@ -20,7 +20,7 @@ async function provideClojureDefinition(document, position: vscode.Position, _to const posIsEvalPos = evalPos && position.isEqual(evalPos); if (util.getConnectedState() && !posIsEvalPos) { const text = util.getWordAtPosition(document, position); - const client = replSession.getSession(util.getFileType(document)); + const client = replSession.tryToGetSession(util.getFileType(document)); util.assertIsDefined(client, 'Expected there to be a repl client!'); const info = await client.info(namespace.getNamespace(document), text); if (info.file && info.file.length > 0) { diff --git a/src/providers/hover.ts b/src/providers/hover.ts index 748781b1f..05d5997e0 100644 --- a/src/providers/hover.ts +++ b/src/providers/hover.ts @@ -13,7 +13,7 @@ export async function provideHover(document: vscode.TextDocument, position: vsco if (util.getConnectedState()) { const text = util.getWordAtPosition(document, position); const ns = namespace.getNamespace(document); - const client = replSession.getSession(util.getFileType(document)); + const client = replSession.tryToGetSession(util.getFileType(document)); if (client) { await namespace.createNamespaceFromDocumentIfNotExists(document); const res = await client.info(ns, text); diff --git a/src/providers/signature.ts b/src/providers/signature.ts index 72d6896e9..374b324f0 100644 --- a/src/providers/signature.ts +++ b/src/providers/signature.ts @@ -34,7 +34,7 @@ export async function provideSignatureHelp( idx = document.offsetAt(position), symbol = getSymbol(document, idx); if (symbol) { - const client = replSession.getSession(util.getFileType(document)); + const client = replSession.tryToGetSession(util.getFileType(document)); if (client) { await namespace.createNamespaceFromDocumentIfNotExists(document); const res = await client.info(ns, symbol), diff --git a/src/refresh.ts b/src/refresh.ts index c97b2a76e..191624120 100644 --- a/src/refresh.ts +++ b/src/refresh.ts @@ -22,7 +22,7 @@ function report(res, chan: vscode.OutputChannel) { function refresh(document = {}) { const doc = util.tryToGetDocument(document), - client: NReplSession | undefined = replSession.getSession(util.getFileType(doc)), + client: NReplSession | undefined = replSession.tryToGetSession(util.getFileType(doc)), chan: vscode.OutputChannel = state.outputChannel(); if (client !== undefined) { @@ -37,7 +37,7 @@ function refresh(document = {}) { function refreshAll(document = {}) { const doc = util.tryToGetDocument(document), - client: NReplSession | undefined = replSession.getSession(util.getFileType(doc)), + client: NReplSession | undefined = replSession.tryToGetSession(util.getFileType(doc)), chan: vscode.OutputChannel = state.outputChannel(); if (client !== undefined) { diff --git a/src/results-output/results-doc.ts b/src/results-output/results-doc.ts index 0b18a6516..7d25e7461 100644 --- a/src/results-output/results-doc.ts +++ b/src/results-output/results-doc.ts @@ -226,7 +226,7 @@ export async function revealDocForCurrentNS(preserveFocus: boolean = true) { } export async function setNamespaceFromCurrentFile() { - const session = replSession.getSession(); + const session = replSession.tryToGetSession(); util.assertIsDefined(session, 'Expected there to be a session!'); const ns = namespace.getNamespace(util.tryToGetDocument({})); if (getNs() !== ns && util.isDefined(ns)) { @@ -238,7 +238,7 @@ export async function setNamespaceFromCurrentFile() { } async function appendFormGrabbingSessionAndNS(topLevel: boolean) { - const session = replSession.getSession(); + const session = replSession.tryToGetSession(); util.assertIsDefined(session, 'Expected there to be a session!'); const ns = namespace.getNamespace(util.tryToGetDocument({})); const editor = util.getActiveTextEditor(); diff --git a/src/statusbar.ts b/src/statusbar.ts index c38ae2e26..d91e9be6e 100644 --- a/src/statusbar.ts +++ b/src/statusbar.ts @@ -4,7 +4,7 @@ import * as util from './utilities'; import * as config from './config'; import status from './status'; import { getStateValue } from '../out/cljs-lib/cljs-lib'; -import { getSession, getReplSessionTypeFromState } from './nrepl/repl-session'; +import { tryToGetSession, getReplSessionTypeFromState } from './nrepl/repl-session'; const connectionStatus = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Left, 1); const typeStatus = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Left, 1); @@ -87,7 +87,7 @@ function update(context = state.extensionContext) { typeStatus.text = ['cljc', config.REPL_FILE_EXT].includes(fileType) ? `cljc/${replType}` : replType; - if (getSession('clj') !== null && getSession('cljs') !== null) { + if (tryToGetSession('clj') !== null && tryToGetSession('cljs') !== null) { typeStatus.command = 'calva.toggleCLJCSession'; typeStatus.tooltip = `Click to use ${replType === 'clj' ? 'cljs' : 'clj'} REPL for cljc`; } else { diff --git a/src/testRunner.ts b/src/testRunner.ts index 8cef3c5fb..2cb2d9873 100644 --- a/src/testRunner.ts +++ b/src/testRunner.ts @@ -7,7 +7,7 @@ import { NReplSession } from './nrepl'; import * as cider from './nrepl/cider'; import * as lsp from './lsp/types'; import * as namespace from './namespace'; -import { getSession, updateReplSessionType } from './nrepl/repl-session'; +import { tryToGetSession, updateReplSessionType } from './nrepl/repl-session'; import * as getText from './util/get-text'; const diagnosticCollection = vscode.languages.createDiagnosticCollection('calva'); @@ -236,7 +236,7 @@ function reportTests( // FIXME: use cljs session where necessary async function runAllTests(controller: vscode.TestController, document = {}) { - const session = getSession(util.getFileType(document)); + const session = tryToGetSession(util.getFileType(document)); util.assertIsDefined(session, 'Expected there to be a current session!'); outputWindow.append('; Running all project tests…'); try { @@ -293,7 +293,7 @@ async function runNamespaceTestsImpl( return; } - const session = getSession(util.getFileType(document)); + const session = tryToGetSession(util.getFileType(document)); util.assertIsDefined(session, 'Expected there to be a current session!'); // TODO.marc: Should we be passing the `document` argument to `loadFile`? @@ -319,7 +319,7 @@ async function runNamespaceTests(controller: vscode.TestController, document: vs if (outputWindow.isResultsDoc(doc)) { return; } - const session = getSession(util.getFileType(document)); + const session = tryToGetSession(util.getFileType(document)); util.assertIsDefined(session, 'Expected there to be a current session!'); const ns = namespace.getNamespace(doc); const nss = await considerTestNS(ns, session); @@ -335,7 +335,7 @@ function getTestUnderCursor() { async function runTestUnderCursor(controller: vscode.TestController) { const doc = util.tryToGetDocument({}); - const session = getSession(util.getFileType(doc)); + const session = tryToGetSession(util.getFileType(doc)); util.assertIsDefined(session, 'Expected there to be a current session!'); const ns = namespace.getNamespace(doc); const test = getTestUnderCursor(); @@ -379,7 +379,7 @@ function runNamespaceTestsCommand(controller: vscode.TestController) { } async function rerunTests(controller: vscode.TestController, document = {}) { - const session = getSession(util.getFileType(document)); + const session = tryToGetSession(util.getFileType(document)); util.assertIsDefined(session, 'Expected there to be a current session!'); await evaluate.loadFile({}, disabledPrettyPrinter); outputWindow.append('; Running previously failed tests…'); From 5da7c79f7ceb882f32c59a7aa2f770ff9f6d9dba Mon Sep 17 00:00:00 2001 From: Cora Sutton Date: Sun, 1 May 2022 02:17:39 -0500 Subject: [PATCH 53/73] Restore whitespace --- src/clojuredocs.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/clojuredocs.ts b/src/clojuredocs.ts index db81ef623..6896c15de 100644 --- a/src/clojuredocs.ts +++ b/src/clojuredocs.ts @@ -174,6 +174,7 @@ async function clojureDocsLookup( const symbol = util.getWordAtPosition(doc, position); const ns = namespace.getNamespace(doc); const session = replSession.getSession(util.getFileType(doc)); + const docsFromCider = await clojureDocsCiderNReplLookup(session, symbol, ns); if (docsFromCider) { return docsFromCider; From ac8f195a09df4cb3b5cf6df16ed54186eaeb2697 Mon Sep 17 00:00:00 2001 From: Cora Sutton Date: Sun, 1 May 2022 02:25:47 -0500 Subject: [PATCH 54/73] Remove assertions that are no longer needed due to new function --- src/connector.ts | 6 ------ src/nrepl/connectSequence.ts | 2 +- src/nrepl/jack-in.ts | 1 - src/nrepl/project-types.ts | 32 +++++++------------------------ src/results-output/results-doc.ts | 1 - src/state.ts | 11 +++++++++-- 6 files changed, 17 insertions(+), 36 deletions(-) diff --git a/src/connector.ts b/src/connector.ts index e56b884c7..51883aca1 100644 --- a/src/connector.ts +++ b/src/connector.ts @@ -81,7 +81,6 @@ async function connectToHost(hostname: string, port: number, connectSequence: Re port: +port, onError: (e) => { const projectRootUri = state.getProjectRootUri(); - util.assertIsDefined(projectRootUri, 'Expected a project root URI'); const scheme = projectRootUri.scheme; if (scheme === 'vsls') { @@ -190,8 +189,6 @@ function setUpCljsRepl(session, build) { async function getFigwheelMainBuilds() { const projectRootUri = state.getProjectRootUri(); - util.assertIsDefined(projectRootUri, 'Expected a project root URI'); - const res = await vscode.workspace.fs.readDirectory(projectRootUri); const builds = res .filter(([name, type]) => type !== vscode.FileType.Directory && name.match(/\.cljs\.edn/)) @@ -383,7 +380,6 @@ function createCLJSReplType( } else { if (typeof initCode === 'object' || initCode.includes('%BUILD%')) { const projectRootUri = state.getProjectRootUri(); - util.assertIsDefined(projectRootUri, 'Expected a project root URI'); const buildsForSelection = startedBuilds ? startedBuilds @@ -449,7 +445,6 @@ function createCLJSReplType( const allBuilds = await figwheelOrShadowBuilds(cljsTypeName); util.assertIsDefined(allBuilds, 'Expected there to be figwheel or shadowcljs builds!'); const projectRootUri = state.getProjectRootUri(); - util.assertIsDefined(projectRootUri, 'Expected a project root URI'); builds = allBuilds.length <= 1 @@ -710,7 +705,6 @@ export default { if (nClient) { const projectRootUri = state.getProjectRootUri(); - util.assertIsDefined(projectRootUri, 'Expected a project root URI'); if (projectRootUri.scheme === 'vsls') { nClient.disconnect(); diff --git a/src/nrepl/connectSequence.ts b/src/nrepl/connectSequence.ts index 0ae6d92cb..c853a8747 100644 --- a/src/nrepl/connectSequence.ts +++ b/src/nrepl/connectSequence.ts @@ -309,7 +309,7 @@ async function askForConnectSequence( ): Promise { // figure out what possible kinds of project we're in const sequences: ReplConnectSequence[] = getConnectSequences(cljTypes); - const projectRootUri = state.getProjectRootUri(); + const projectRootUri = state.tryToGetProjectRootUri(); const saveAsFull = projectRootUri ? `${projectRootUri.toString()}/${saveAs}` : saveAs; void state.extensionContext.workspaceState.update('askForConnectSequenceQuickPick', true); const projectConnectSequenceName = await utilities.quickPickSingle({ diff --git a/src/nrepl/jack-in.ts b/src/nrepl/jack-in.ts index 799e3bfc3..6695cc787 100644 --- a/src/nrepl/jack-in.ts +++ b/src/nrepl/jack-in.ts @@ -242,7 +242,6 @@ export async function jackIn(connectSequence: ReplConnectSequence | undefined, c console.error('An error occurred while setting up Live Share listener.', e); } const projectRootUri = state.getProjectRootUri(); - utilities.assertIsDefined(projectRootUri, 'Expected to find a project root URI!'); if (projectRootUri.scheme === 'vsls') { outputWindow.append("; Aborting Jack-in, since you're the guest of a live share session."); outputWindow.append( diff --git a/src/nrepl/project-types.ts b/src/nrepl/project-types.ts index 764e7b05f..68cf1f434 100644 --- a/src/nrepl/project-types.ts +++ b/src/nrepl/project-types.ts @@ -63,7 +63,7 @@ export function nreplPortFileLocalPath(connectSequence: ReplConnectSequence): st export function nreplPortFileUri(connectSequence: ReplConnectSequence): vscode.Uri { const relativePath = nreplPortFileRelativePath(connectSequence); - const projectRoot = state.getProjectRootUri(); + const projectRoot = state.tryToGetProjectRootUri(); if (projectRoot) { try { return vscode.Uri.joinPath(projectRoot, relativePath); @@ -75,9 +75,7 @@ export function nreplPortFileUri(connectSequence: ReplConnectSequence): vscode.U } export function shadowConfigFile(): vscode.Uri { - const projectRootUri = state.getProjectRootUri(); - utilities.assertIsDefined(projectRootUri, 'Expected a project root URI'); - return vscode.Uri.joinPath(projectRootUri, 'shadow-cljs.edn'); + return vscode.Uri.joinPath(state.getProjectRootUri(), 'shadow-cljs.edn'); } export async function shadowBuilds(): Promise { @@ -118,16 +116,13 @@ async function selectShadowBuilds( connectSequence: ReplConnectSequence, foundBuilds: string[] ): Promise<{ selectedBuilds: string[] | undefined; args: string[] }> { - const projectRootUri = state.getProjectRootUri(); - utilities.assertIsDefined(projectRootUri, 'Expected a project root URI'); - const menuSelections = connectSequence.menuSelections, selectedBuilds = menuSelections ? menuSelections.cljsLaunchBuilds : await utilities.quickPickMulti({ values: foundBuilds.filter((x) => x[0] == ':'), placeHolder: 'Select builds to start', - saveAs: `${projectRootUri.toString()}/shadow-cljs-jack-in`, + saveAs: `${state.getProjectRootUri().toString()}/shadow-cljs-jack-in`, }), aliases: string[] = menuSelections && menuSelections.cljAliases ? menuSelections.cljAliases.map(keywordize) : []; // TODO do the same as clj to prompt the user with a list of aliases @@ -140,11 +135,8 @@ async function selectShadowBuilds( } async function leinDefProject(): Promise { - const projectRootUri = state.getProjectRootUri(); - utilities.assertIsDefined(projectRootUri, 'Expected a project root URI'); - const bytes = await vscode.workspace.fs.readFile( - vscode.Uri.joinPath(projectRootUri, 'project.clj') + vscode.Uri.joinPath(state.getProjectRootUri(), 'project.clj') ); const data = new TextDecoder('utf-8').decode(bytes); try { @@ -178,14 +170,10 @@ async function leinProfilesAndAlias( const aliasesMap = defproject[aliasesIndex + 1]; aliases = Object.keys(aliasesMap).map((v, k) => v); if (aliases.length) { - const projectRootUri = state.getProjectRootUri(); - utilities.assertIsDefined(projectRootUri, 'Expected a project root URI'); - - state.getProjectRootUri(); aliases.unshift('No alias'); alias = await utilities.quickPickSingle({ values: aliases, - saveAs: `${projectRootUri.toString()}/lein-cli-alias`, + saveAs: `${state.getProjectRootUri().toString()}/lein-cli-alias`, placeHolder: 'Choose alias to launch with', }); alias = alias === 'No alias' ? undefined : alias; @@ -211,12 +199,9 @@ async function leinProfilesAndAlias( if (projectProfiles.length) { profiles = projectProfiles.map(keywordize); if (profiles.length) { - const projectRootUri = state.getProjectRootUri(); - utilities.assertIsDefined(projectRootUri, 'Expected a project root URI'); - profiles = await utilities.quickPickMulti({ values: profiles, - saveAs: `${projectRootUri.toString()}/lein-cli-profiles`, + saveAs: `${state.getProjectRootUri().toString()}/lein-cli-profiles`, placeHolder: 'Pick any profiles to launch with', }); } @@ -479,7 +464,6 @@ async function cljCommandLine( let depsUri: vscode.Uri; const projectRootUri = state.getProjectRootUri(); - utilities.assertIsDefined(projectRootUri, 'Expected a project root URI'); try { depsUri = vscode.Uri.joinPath(projectRootUri, 'deps.edn'); @@ -649,15 +633,13 @@ export function getProjectTypeForName(name: string) { } export async function detectProjectTypes(): Promise { - const projectRootUri = state.getProjectRootUri(); - utilities.assertIsDefined(projectRootUri, 'Expected a project root URI'); const cljProjTypes = ['generic', 'cljs-only', 'babashka', 'nbb']; for (const clj in projectTypes) { if (projectTypes[clj].useWhenExists) { try { const projectFileName = projectTypes[clj].useWhenExists; utilities.assertIsDefined(projectFileName, 'Expected there to be a project filename!'); - const uri = vscode.Uri.joinPath(projectRootUri, projectFileName); + const uri = vscode.Uri.joinPath(state.getProjectRootUri(), projectFileName); await vscode.workspace.fs.readFile(uri); cljProjTypes.push(clj); } catch { diff --git a/src/results-output/results-doc.ts b/src/results-output/results-doc.ts index 7d25e7461..880a86561 100644 --- a/src/results-output/results-doc.ts +++ b/src/results-output/results-doc.ts @@ -42,7 +42,6 @@ export const CLJS_CONNECT_GREETINGS = function outputFileDir() { const projectRoot = state.getProjectRootUri(); - util.assertIsDefined(projectRoot, 'Expected there to be a project root!'); try { return vscode.Uri.joinPath(projectRoot, '.calva', 'output-window'); } catch { diff --git a/src/state.ts b/src/state.ts index 3483d1002..7087be0af 100644 --- a/src/state.ts +++ b/src/state.ts @@ -62,11 +62,18 @@ export function setProjectConfig(config) { return setStateValue(PROJECT_CONFIG_MAP, config); } -export function getProjectRootUri(useCache = true): vscode.Uri | undefined { +export function tryToGetProjectRootUri(useCache = true): vscode.Uri | undefined { if (useCache) { return getStateValue(PROJECT_DIR_URI_KEY); } } +export function getProjectRootUri(useCache = true): vscode.Uri { + const projectRootUri = tryToGetProjectRootUri(useCache); + + util.assertIsDefined(projectRootUri, 'Expected to find project root URI!'); + + return projectRootUri; +} const NON_PROJECT_DIR_KEY = 'calva.connect.nonProjectDir'; @@ -104,7 +111,7 @@ export async function setOrCreateNonProjectRoot( ): Promise { let root: vscode.Uri | undefined = undefined; if (preferProjectDir) { - root = getProjectRootUri(); + root = tryToGetProjectRootUri(); } if (!root) { root = await getNonProjectRootDir(context); From c1417dbf5a582f5765bef483850d2268546948fc Mon Sep 17 00:00:00 2001 From: Cora Sutton Date: Sun, 1 May 2022 02:26:35 -0500 Subject: [PATCH 55/73] No need to set to a variable before use --- src/connector.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/connector.ts b/src/connector.ts index 51883aca1..8a7b2feb4 100644 --- a/src/connector.ts +++ b/src/connector.ts @@ -704,9 +704,7 @@ export default { status.update(); if (nClient) { - const projectRootUri = state.getProjectRootUri(); - - if (projectRootUri.scheme === 'vsls') { + if (state.getProjectRootUri().scheme === 'vsls') { nClient.disconnect(); } else { // the connection may be ended before From f310e64e56c8ae4ae4ba3eb434060f1bce8541e4 Mon Sep 17 00:00:00 2001 From: Cora Sutton Date: Sun, 1 May 2022 02:28:35 -0500 Subject: [PATCH 56/73] More formatting --- src/connector.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/connector.ts b/src/connector.ts index 8a7b2feb4..869d1db4a 100644 --- a/src/connector.ts +++ b/src/connector.ts @@ -80,9 +80,7 @@ async function connectToHost(hostname: string, port: number, connectSequence: Re host: hostname, port: +port, onError: (e) => { - const projectRootUri = state.getProjectRootUri(); - - const scheme = projectRootUri.scheme; + const scheme = state.getProjectRootUri().scheme; if (scheme === 'vsls') { outputWindow.append('; nREPL connection failed; did the host share the nREPL port?'); } From 859a6bb6b609172985565a89e018c5a9651e54bc Mon Sep 17 00:00:00 2001 From: Cora Sutton Date: Sun, 1 May 2022 02:39:25 -0500 Subject: [PATCH 57/73] Minimize more changes to connector --- src/connector.ts | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/src/connector.ts b/src/connector.ts index 869d1db4a..0473889a8 100644 --- a/src/connector.ts +++ b/src/connector.ts @@ -186,8 +186,7 @@ function setUpCljsRepl(session, build) { } async function getFigwheelMainBuilds() { - const projectRootUri = state.getProjectRootUri(); - const res = await vscode.workspace.fs.readDirectory(projectRootUri); + const res = await vscode.workspace.fs.readDirectory(state.getProjectRootUri()); const builds = res .filter(([name, type]) => type !== vscode.FileType.Directory && name.match(/\.cljs\.edn/)) .map(([name, _]) => name.replace(/\.cljs\.edn$/, '')); @@ -377,8 +376,6 @@ function createCLJSReplType( useDefaultBuild = false; } else { if (typeof initCode === 'object' || initCode.includes('%BUILD%')) { - const projectRootUri = state.getProjectRootUri(); - const buildsForSelection = startedBuilds ? startedBuilds : await figwheelOrShadowBuilds(cljsTypeName); @@ -390,7 +387,10 @@ function createCLJSReplType( build = await util.quickPickSingle({ values: buildsForSelection, placeHolder: 'Select which build to connect to', - saveAs: `${projectRootUri.toString()}/${cljsTypeName.replace(' ', '-')}-build`, + saveAs: `${state.getProjectRootUri().toString()}/${cljsTypeName.replace( + ' ', + '-' + )}-build`, autoSelect: true, }); } @@ -435,14 +435,14 @@ function createCLJSReplType( replType.start = async (session, name, checkFn) => { let startCode = cljsType.startCode; if (!hasStarted) { - if (startCode && startCode.includes('%BUILDS')) { + util.assertIsDefined(startCode, 'Expected startCode to be defined!'); + if (startCode.includes('%BUILDS')) { let builds: string[]; if (menuSelections && menuSelections.cljsLaunchBuilds) { builds = menuSelections.cljsLaunchBuilds; } else { const allBuilds = await figwheelOrShadowBuilds(cljsTypeName); util.assertIsDefined(allBuilds, 'Expected there to be figwheel or shadowcljs builds!'); - const projectRootUri = state.getProjectRootUri(); builds = allBuilds.length <= 1 @@ -450,7 +450,10 @@ function createCLJSReplType( : await util.quickPickMulti({ values: allBuilds, placeHolder: 'Please select which builds to start', - saveAs: `${projectRootUri.toString()}/${cljsTypeName.replace(' ', '-')}-builds`, + saveAs: `${state.getProjectRootUri().toString()}/${cljsTypeName.replace( + ' ', + '-' + )}-builds`, }); } if (builds) { @@ -480,7 +483,7 @@ function createCLJSReplType( outputWindow.append('; Aborted starting cljs repl.'); throw 'Aborted'; } - } else if (startCode) { + } else { outputWindow.append('; Starting cljs repl for: ' + projectTypeName + '...'); return evalConnectCode( session, From 7ac6f57e1861aed85d104ea74f111dfaff9217b4 Mon Sep 17 00:00:00 2001 From: Cora Sutton Date: Sun, 1 May 2022 02:49:59 -0500 Subject: [PATCH 58/73] Whoops, use a record type not a map type --- src/cursor-doc/indent.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cursor-doc/indent.ts b/src/cursor-doc/indent.ts index 66d693303..bad94c62e 100644 --- a/src/cursor-doc/indent.ts +++ b/src/cursor-doc/indent.ts @@ -66,11 +66,11 @@ export function collectIndents( const match = pattern.match(/^#"(.*)"$/); if (match) { - regexpMap.set(pattern, RegExp(match[1])); + regexpMap[pattern] = RegExp(match[1]); } return regexpMap; - }, new Map()); + }, {} as Record); do { if (!cursor.backwardSexp()) { From 1e921d69914fd56a1bc8db534fe573a06e98e018 Mon Sep 17 00:00:00 2001 From: Cora Sutton Date: Sun, 1 May 2022 03:10:47 -0500 Subject: [PATCH 59/73] Ensure same behavior here --- src/nrepl/connectSequence.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/nrepl/connectSequence.ts b/src/nrepl/connectSequence.ts index c853a8747..726d58524 100644 --- a/src/nrepl/connectSequence.ts +++ b/src/nrepl/connectSequence.ts @@ -252,7 +252,8 @@ const defaultCljsTypes: { [id: string]: CljsTypeConfig } = { /** Retrieve the replConnectSequences from the config */ function getCustomConnectSequences(): ReplConnectSequence[] { - const sequences: ReplConnectSequence[] = getConfig().replConnectSequences || []; + const sequences: ReplConnectSequence[] | undefined = getConfig().replConnectSequences; + utilities.assertIsDefined(sequences, 'Expected there to be repl connect sequences!'); for (const sequence of sequences) { if ( From 61437fe2e60dc784bbfe4e919ac8d74b827efe77 Mon Sep 17 00:00:00 2001 From: Cora Sutton Date: Sun, 1 May 2022 03:21:29 -0500 Subject: [PATCH 60/73] Split project root local fns into tryToGet + get --- src/nrepl/jack-in.ts | 1 - src/nrepl/project-types.ts | 2 +- src/state.ts | 10 +++++++++- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/src/nrepl/jack-in.ts b/src/nrepl/jack-in.ts index 6695cc787..3bf3e96d2 100644 --- a/src/nrepl/jack-in.ts +++ b/src/nrepl/jack-in.ts @@ -175,7 +175,6 @@ async function getJackInTerminalOptions( utilities.assertIsDefined(projectType, 'Expected to find a project type!'); const projectRootLocal = state.getProjectRootLocal(); - utilities.assertIsDefined(projectRootLocal, 'Expected to find a local project root!'); let args: string[] = await projectType.commandLine(projectConnectSequence, selectedCljsType); let cmd: string[]; diff --git a/src/nrepl/project-types.ts b/src/nrepl/project-types.ts index 68cf1f434..2b9d2d10c 100644 --- a/src/nrepl/project-types.ts +++ b/src/nrepl/project-types.ts @@ -50,7 +50,7 @@ function nreplPortFileRelativePath(connectSequence: ReplConnectSequence): string */ export function nreplPortFileLocalPath(connectSequence: ReplConnectSequence): string { const relativePath = nreplPortFileRelativePath(connectSequence); - const projectRoot = state.getProjectRootLocal(); + const projectRoot = state.tryToGetProjectRootLocal(); if (projectRoot) { try { return path.resolve(projectRoot, relativePath); diff --git a/src/state.ts b/src/state.ts index 7087be0af..63f1216cd 100644 --- a/src/state.ts +++ b/src/state.ts @@ -46,12 +46,20 @@ const PROJECT_DIR_KEY = 'connect.projectDir'; const PROJECT_DIR_URI_KEY = 'connect.projectDirNew'; const PROJECT_CONFIG_MAP = 'config'; -export function getProjectRootLocal(useCache = true): string | undefined { +export function tryToGetProjectRootLocal(useCache = true): string | undefined { if (useCache) { return getStateValue(PROJECT_DIR_KEY); } } +export const getProjectRootLocal = (useCache = true): string => { + const projectRootLocal = tryToGetProjectRootLocal(useCache); + + util.assertIsDefined(projectRootLocal, 'Expected to find a local project root!'); + + return projectRootLocal; +}; + export function getProjectConfig(useCache = true) { if (useCache) { return getStateValue(PROJECT_CONFIG_MAP); From d0cd19c4c45e18eeda55db25c09312e357383566 Mon Sep 17 00:00:00 2001 From: Cora Sutton Date: Sun, 1 May 2022 03:26:37 -0500 Subject: [PATCH 61/73] Move undefined check closer to where it is needed --- src/nrepl/project-types.ts | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/src/nrepl/project-types.ts b/src/nrepl/project-types.ts index 2b9d2d10c..ee84f306b 100644 --- a/src/nrepl/project-types.ts +++ b/src/nrepl/project-types.ts @@ -114,18 +114,22 @@ export function leinShadowBuilds(defproject: any): string[] | undefined { async function selectShadowBuilds( connectSequence: ReplConnectSequence, - foundBuilds: string[] + foundBuilds: string[] | undefined ): Promise<{ selectedBuilds: string[] | undefined; args: string[] }> { - const menuSelections = connectSequence.menuSelections, - selectedBuilds = menuSelections - ? menuSelections.cljsLaunchBuilds - : await utilities.quickPickMulti({ - values: foundBuilds.filter((x) => x[0] == ':'), - placeHolder: 'Select builds to start', - saveAs: `${state.getProjectRootUri().toString()}/shadow-cljs-jack-in`, - }), - aliases: string[] = - menuSelections && menuSelections.cljAliases ? menuSelections.cljAliases.map(keywordize) : []; // TODO do the same as clj to prompt the user with a list of aliases + const menuSelections = connectSequence.menuSelections; + let selectedBuilds: string[] | undefined; + if (menuSelections) { + selectedBuilds = menuSelections.cljsLaunchBuilds; + } else { + utilities.assertIsDefined(foundBuilds, 'Expected to have foundBuilds when using the picker!'); + selectedBuilds = await utilities.quickPickMulti({ + values: foundBuilds.filter((x) => x[0] == ':'), + placeHolder: 'Select builds to start', + saveAs: `${state.getProjectRootUri().toString()}/shadow-cljs-jack-in`, + }); + } + const aliases: string[] = + menuSelections && menuSelections.cljAliases ? menuSelections.cljAliases.map(keywordize) : []; // TODO do the same as clj to prompt the user with a list of aliases const aliasesOption = aliases.length > 0 ? `-A${aliases.join('')}` : ''; const args: string[] = []; if (aliasesOption && aliasesOption.length) { @@ -399,7 +403,6 @@ const projectTypes: { [id: string]: ProjectType } = { const defproject = await leinDefProject(); const foundBuilds = leinShadowBuilds(defproject); - utilities.assertIsDefined(foundBuilds, 'Expected to find lein shadow builds for project!'); const { selectedBuilds } = await selectShadowBuilds(connectSequence, foundBuilds); From 07788666a795e836504b5a05e546308c0f8b615c Mon Sep 17 00:00:00 2001 From: Cora Sutton Date: Sun, 1 May 2022 03:29:39 -0500 Subject: [PATCH 62/73] Forgot a bang ! --- src/nrepl/repl-start.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/nrepl/repl-start.ts b/src/nrepl/repl-start.ts index 497668a7a..9ed049e61 100644 --- a/src/nrepl/repl-start.ts +++ b/src/nrepl/repl-start.ts @@ -191,7 +191,7 @@ export async function startStandaloneRepl( const pprintOptions = getConfig().prettyPrintingOptions; utilities.assertIsDefined( pprintOptions, - 'Expected there to be pretty printing options configured' + 'Expected there to be pretty printing options configured!' ); await eval.loadFile({}, pprintOptions); outputWindow.appendPrompt(); From 857054da19615a4ca65c3932fd8e25213d628ab1 Mon Sep 17 00:00:00 2001 From: Cora Sutton Date: Sun, 1 May 2022 03:35:07 -0500 Subject: [PATCH 63/73] Swap out use of regex for much faster substring search --- src/providers/infoparser.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/providers/infoparser.ts b/src/providers/infoparser.ts index dadd28569..fdf607b82 100644 --- a/src/providers/infoparser.ts +++ b/src/providers/infoparser.ts @@ -170,7 +170,7 @@ export class REPLInfoParser { const signatures = argListStrings.map((argList) => { const signature = new SignatureInformation(`(${symbol} ${argList})`); // Skip parameter help on special forms and forms with optional arguments, for now - if (this._arglist && !argList.match(/\?/)) { + if (this._arglist && !argList.includes('?')) { const parameters = this.getParameters(symbol, argList); if (parameters) { From 5106ee89b4d60bf9aeb7cf05b5ac6153154f4359 Mon Sep 17 00:00:00 2001 From: Cora Sutton Date: Sun, 1 May 2022 03:40:06 -0500 Subject: [PATCH 64/73] Use function instead of try + assert --- src/debugger/calva-debug.ts | 3 +-- src/evaluate.ts | 3 +-- src/providers/definition.ts | 3 +-- src/results-output/results-doc.ts | 6 ++---- src/testRunner.ts | 17 ++++++----------- 5 files changed, 11 insertions(+), 21 deletions(-) diff --git a/src/debugger/calva-debug.ts b/src/debugger/calva-debug.ts index 0bef948b6..39db63443 100644 --- a/src/debugger/calva-debug.ts +++ b/src/debugger/calva-debug.ts @@ -439,8 +439,7 @@ function handleNeedDebugInput(response: any): void { void debug.startDebugging(undefined, CALVA_DEBUG_CONFIGURATION); } } else { - const cljSession = replSession.tryToGetSession(CLOJURE_SESSION_NAME); - util.assertIsDefined(cljSession, 'Expected there to be a repl session!'); + const cljSession = replSession.getSession(CLOJURE_SESSION_NAME); void cljSession.sendDebugInput(':quit', response.id, response.key); void vscode.window.showInformationMessage( 'Forms containing breakpoints that were not evaluated in the editor (such as if you evaluated a form in the REPL window) cannot be debugged. Evaluate the form in the editor in order to debug it.' diff --git a/src/evaluate.ts b/src/evaluate.ts index fbe344ed5..41d00640b 100644 --- a/src/evaluate.ts +++ b/src/evaluate.ts @@ -492,8 +492,7 @@ async function requireREPLUtilitiesCommand() { async function copyLastResultCommand() { const chan = state.outputChannel(); - const session = replSession.tryToGetSession(util.getFileType(util.tryToGetDocument({}))); - util.assertIsDefined(session, 'Expected there to be a repl session!'); + const session = replSession.getSession(util.getFileType(util.tryToGetDocument({}))); const value = await session.eval('*1', session.client.ns).value; if (value !== null) { diff --git a/src/providers/definition.ts b/src/providers/definition.ts index 7562a817a..cd719090d 100644 --- a/src/providers/definition.ts +++ b/src/providers/definition.ts @@ -20,8 +20,7 @@ async function provideClojureDefinition(document, position: vscode.Position, _to const posIsEvalPos = evalPos && position.isEqual(evalPos); if (util.getConnectedState() && !posIsEvalPos) { const text = util.getWordAtPosition(document, position); - const client = replSession.tryToGetSession(util.getFileType(document)); - util.assertIsDefined(client, 'Expected there to be a repl client!'); + const client = replSession.getSession(util.getFileType(document)); const info = await client.info(namespace.getNamespace(document), text); if (info.file && info.file.length > 0) { const pos = new vscode.Position(info.line - 1, info.column || 0); diff --git a/src/results-output/results-doc.ts b/src/results-output/results-doc.ts index 880a86561..a98203626 100644 --- a/src/results-output/results-doc.ts +++ b/src/results-output/results-doc.ts @@ -225,8 +225,7 @@ export async function revealDocForCurrentNS(preserveFocus: boolean = true) { } export async function setNamespaceFromCurrentFile() { - const session = replSession.tryToGetSession(); - util.assertIsDefined(session, 'Expected there to be a session!'); + const session = replSession.getSession(); const ns = namespace.getNamespace(util.tryToGetDocument({})); if (getNs() !== ns && util.isDefined(ns)) { await session.switchNS(ns); @@ -237,8 +236,7 @@ export async function setNamespaceFromCurrentFile() { } async function appendFormGrabbingSessionAndNS(topLevel: boolean) { - const session = replSession.tryToGetSession(); - util.assertIsDefined(session, 'Expected there to be a session!'); + const session = replSession.getSession(); const ns = namespace.getNamespace(util.tryToGetDocument({})); const editor = util.getActiveTextEditor(); const doc = editor.document; diff --git a/src/testRunner.ts b/src/testRunner.ts index 2cb2d9873..69b31e744 100644 --- a/src/testRunner.ts +++ b/src/testRunner.ts @@ -7,7 +7,7 @@ import { NReplSession } from './nrepl'; import * as cider from './nrepl/cider'; import * as lsp from './lsp/types'; import * as namespace from './namespace'; -import { tryToGetSession, updateReplSessionType } from './nrepl/repl-session'; +import { getSession, tryToGetSession, updateReplSessionType } from './nrepl/repl-session'; import * as getText from './util/get-text'; const diagnosticCollection = vscode.languages.createDiagnosticCollection('calva'); @@ -236,8 +236,7 @@ function reportTests( // FIXME: use cljs session where necessary async function runAllTests(controller: vscode.TestController, document = {}) { - const session = tryToGetSession(util.getFileType(document)); - util.assertIsDefined(session, 'Expected there to be a current session!'); + const session = getSession(util.getFileType(document)); outputWindow.append('; Running all project tests…'); try { reportTests(controller, session, [await session.testAll()]); @@ -293,8 +292,7 @@ async function runNamespaceTestsImpl( return; } - const session = tryToGetSession(util.getFileType(document)); - util.assertIsDefined(session, 'Expected there to be a current session!'); + const session = getSession(util.getFileType(document)); // TODO.marc: Should we be passing the `document` argument to `loadFile`? await evaluate.loadFile({}, disabledPrettyPrinter); @@ -319,8 +317,7 @@ async function runNamespaceTests(controller: vscode.TestController, document: vs if (outputWindow.isResultsDoc(doc)) { return; } - const session = tryToGetSession(util.getFileType(document)); - util.assertIsDefined(session, 'Expected there to be a current session!'); + const session = getSession(util.getFileType(document)); const ns = namespace.getNamespace(doc); const nss = await considerTestNS(ns, session); void runNamespaceTestsImpl(controller, document, nss); @@ -335,8 +332,7 @@ function getTestUnderCursor() { async function runTestUnderCursor(controller: vscode.TestController) { const doc = util.tryToGetDocument({}); - const session = tryToGetSession(util.getFileType(doc)); - util.assertIsDefined(session, 'Expected there to be a current session!'); + const session = getSession(util.getFileType(doc)); const ns = namespace.getNamespace(doc); const test = getTestUnderCursor(); @@ -379,8 +375,7 @@ function runNamespaceTestsCommand(controller: vscode.TestController) { } async function rerunTests(controller: vscode.TestController, document = {}) { - const session = tryToGetSession(util.getFileType(document)); - util.assertIsDefined(session, 'Expected there to be a current session!'); + const session = getSession(util.getFileType(document)); await evaluate.loadFile({}, disabledPrettyPrinter); outputWindow.append('; Running previously failed tests…'); From 18d77615a81b987189cb4d2193e83e888a95fb16 Mon Sep 17 00:00:00 2001 From: Cora Sutton Date: Sun, 1 May 2022 03:43:06 -0500 Subject: [PATCH 65/73] Compare against the right types --- src/statusbar.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/statusbar.ts b/src/statusbar.ts index d91e9be6e..7972b67ef 100644 --- a/src/statusbar.ts +++ b/src/statusbar.ts @@ -87,7 +87,7 @@ function update(context = state.extensionContext) { typeStatus.text = ['cljc', config.REPL_FILE_EXT].includes(fileType) ? `cljc/${replType}` : replType; - if (tryToGetSession('clj') !== null && tryToGetSession('cljs') !== null) { + if (tryToGetSession('clj') !== undefined && tryToGetSession('cljs') !== undefined) { typeStatus.command = 'calva.toggleCLJCSession'; typeStatus.tooltip = `Click to use ${replType === 'clj' ? 'cljs' : 'clj'} REPL for cljc`; } else { From 3d1aec34f1f5061cacdde100a0d3b53fa63f2c45 Mon Sep 17 00:00:00 2001 From: Cora Sutton Date: Sun, 1 May 2022 03:43:53 -0500 Subject: [PATCH 66/73] Get rid of unused import --- src/testRunner.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/testRunner.ts b/src/testRunner.ts index 69b31e744..0642e0070 100644 --- a/src/testRunner.ts +++ b/src/testRunner.ts @@ -7,7 +7,7 @@ import { NReplSession } from './nrepl'; import * as cider from './nrepl/cider'; import * as lsp from './lsp/types'; import * as namespace from './namespace'; -import { getSession, tryToGetSession, updateReplSessionType } from './nrepl/repl-session'; +import { getSession, updateReplSessionType } from './nrepl/repl-session'; import * as getText from './util/get-text'; const diagnosticCollection = vscode.languages.createDiagnosticCollection('calva'); From b83c26d54d4e2c66e8c02715793a90f62392ecdc Mon Sep 17 00:00:00 2001 From: Cora Sutton Date: Sun, 1 May 2022 13:34:07 -0500 Subject: [PATCH 67/73] Move type checking functions out to an ns without vscode --- src/config.ts | 2 +- src/connector.ts | 17 +++++++++-------- src/cursor-doc/lexer.ts | 2 +- src/cursor-doc/paredit.ts | 2 +- src/debugger/calva-debug.ts | 4 ++-- src/debugger/decorations.ts | 3 ++- src/debugger/util.ts | 2 +- src/doc-mirror/index.ts | 3 ++- src/evaluate.ts | 15 +++++++++------ src/extension.ts | 9 +++++---- src/highlight/src/extension.ts | 3 ++- src/lsp/main.ts | 5 +++-- src/namespace.ts | 3 ++- src/nrepl/bencode.ts | 2 +- src/nrepl/connectSequence.ts | 5 +++-- src/nrepl/jack-in-terminal.ts | 2 +- src/nrepl/jack-in.ts | 3 ++- src/nrepl/project-types.ts | 9 +++++---- src/nrepl/repl-session.ts | 3 ++- src/nrepl/repl-start.ts | 8 +++----- src/paredit/extension.ts | 2 +- src/printer.ts | 2 +- src/providers/completion.ts | 9 +++++---- src/providers/signature.ts | 5 +++-- src/results-output/repl-history.ts | 7 ++++--- src/results-output/results-doc.ts | 7 ++++--- src/state.ts | 5 +++-- src/statusbar.ts | 3 ++- src/testRunner.ts | 7 ++++--- src/type-checks.ts | 21 +++++++++++++++++++++ src/util/cursor-get-text.ts | 2 +- src/utilities.ts | 27 ++------------------------- 32 files changed, 108 insertions(+), 91 deletions(-) create mode 100644 src/type-checks.ts diff --git a/src/config.ts b/src/config.ts index 078675e3e..8d6e6eb30 100644 --- a/src/config.ts +++ b/src/config.ts @@ -5,7 +5,7 @@ import { PrettyPrintingOptions } from './printer'; import { parseEdn } from '../out/cljs-lib/cljs-lib'; import * as state from './state'; import _ = require('lodash'); -import { isDefined } from './utilities'; +import { isDefined } from './type-checks'; const REPL_FILE_EXT = 'calva-repl'; const KEYBINDINGS_ENABLED_CONFIG_KEY = 'calva.keybindingsEnabled'; diff --git a/src/connector.ts b/src/connector.ts index 0473889a8..243622191 100644 --- a/src/connector.ts +++ b/src/connector.ts @@ -25,6 +25,7 @@ import * as clojureDocs from './clojuredocs'; import * as jszip from 'jszip'; import { addEdnConfig } from './config'; import { getJarContents } from './utilities'; +import { assertIsDefined, isNonEmptyString } from './type-checks'; async function readJarContent(uri: string) { try { @@ -39,7 +40,7 @@ async function readJarContent(uri: string) { } async function readRuntimeConfigs() { - util.assertIsDefined(nClient, 'Expected there to be an nREPL client!'); + assertIsDefined(nClient, 'Expected there to be an nREPL client!'); const classpath = await nClient.session.classpath().catch((e) => { console.error('readRuntimeConfigs:', e); }); @@ -56,7 +57,7 @@ async function readRuntimeConfigs() { // maybe we don't need to keep uri -> edn association, but it would make showing errors easier later return files - .filter(([_, config]) => util.isNonEmptyString(config)) + .filter(([_, config]) => isNonEmptyString(config)) .map(([_, config]) => addEdnConfig(config)); } } @@ -114,7 +115,7 @@ async function connectToHost(hostname: string, port: number, connectSequence: Re if (connectSequence.afterCLJReplJackInCode) { outputWindow.append(`\n; Evaluating 'afterCLJReplJackInCode'`); const ns = outputWindow.getNs(); - util.assertIsDefined(ns, 'Expected outputWindow to have a namespace!'); + assertIsDefined(ns, 'Expected outputWindow to have a namespace!'); await evaluateInOutputWindow(connectSequence.afterCLJReplJackInCode, 'clj', ns, {}); } @@ -379,7 +380,7 @@ function createCLJSReplType( const buildsForSelection = startedBuilds ? startedBuilds : await figwheelOrShadowBuilds(cljsTypeName); - util.assertIsDefined( + assertIsDefined( buildsForSelection, 'Expected there to be figwheel or shadowcljs builds!' ); @@ -435,14 +436,14 @@ function createCLJSReplType( replType.start = async (session, name, checkFn) => { let startCode = cljsType.startCode; if (!hasStarted) { - util.assertIsDefined(startCode, 'Expected startCode to be defined!'); + assertIsDefined(startCode, 'Expected startCode to be defined!'); if (startCode.includes('%BUILDS')) { let builds: string[]; if (menuSelections && menuSelections.cljsLaunchBuilds) { builds = menuSelections.cljsLaunchBuilds; } else { const allBuilds = await figwheelOrShadowBuilds(cljsTypeName); - util.assertIsDefined(allBuilds, 'Expected there to be figwheel or shadowcljs builds!'); + assertIsDefined(allBuilds, 'Expected there to be figwheel or shadowcljs builds!'); builds = allBuilds.length <= 1 @@ -529,7 +530,7 @@ async function makeCljsSessionClone(session, repl: ReplType, projectTypeName: st '; The Calva Connection Log might have more connection progress information.' ); if (repl.start !== undefined) { - util.assertIsDefined(repl.started, "Expected repl to have a 'started' check function!"); + assertIsDefined(repl.started, "Expected repl to have a 'started' check function!"); if (await repl.start(newCljsSession, repl.name, repl.started)) { state.analytics().logEvent('REPL', 'StartedCLJS', repl.name).send(); outputWindow.append('; Cljs builds started'); @@ -543,7 +544,7 @@ async function makeCljsSessionClone(session, repl: ReplType, projectTypeName: st } } - util.assertIsDefined(repl.connect, 'Expected repl to have a connect function!'); + assertIsDefined(repl.connect, 'Expected repl to have a connect function!'); if (await repl.connect(newCljsSession, repl.name, repl.connected)) { state.analytics().logEvent('REPL', 'ConnectedCLJS', repl.name).send(); diff --git a/src/cursor-doc/lexer.ts b/src/cursor-doc/lexer.ts index 2a1c831b8..edec1fb0e 100644 --- a/src/cursor-doc/lexer.ts +++ b/src/cursor-doc/lexer.ts @@ -3,7 +3,7 @@ * @module lexer */ -import { assertIsDefined } from '../utilities'; +import { assertIsDefined } from '../type-checks'; /** * The base Token class. Contains the token type, diff --git a/src/cursor-doc/paredit.ts b/src/cursor-doc/paredit.ts index 48adbac21..bef27468b 100644 --- a/src/cursor-doc/paredit.ts +++ b/src/cursor-doc/paredit.ts @@ -1,4 +1,4 @@ -import { assertIsDefined } from '../utilities'; +import { assertIsDefined } from '../type-checks'; import { validPair } from './clojure-lexer'; import { ModelEdit, EditableDocument, ModelEditSelection } from './model'; import { LispTokenCursor } from './token-cursor'; diff --git a/src/debugger/calva-debug.ts b/src/debugger/calva-debug.ts index 39db63443..0786c4271 100644 --- a/src/debugger/calva-debug.ts +++ b/src/debugger/calva-debug.ts @@ -35,8 +35,8 @@ import annotations from '../providers/annotations'; import { NReplSession } from '../nrepl'; import debugDecorations from './decorations'; import { setStateValue, getStateValue } from '../../out/cljs-lib/cljs-lib'; -import * as util from '../utilities'; import * as replSession from '../nrepl/repl-session'; +import { assertIsDefined } from '../type-checks'; const CALVA_DEBUG_CONFIGURATION: DebugConfiguration = { type: 'clojure', @@ -271,7 +271,7 @@ class CalvaDebugSession extends LoggingDebugSession { // Pass scheme in path argument to Source contructor so that if it's a jar file it's handled correctly const source = new Source(basename(debugResponse.file), debugResponse.file); const name = tokenCursor.getFunctionName(); - util.assertIsDefined(name, 'Expected to find a function name!'); + assertIsDefined(name, 'Expected to find a function name!'); const stackFrames = [new StackFrame(0, name, source, line + 1, column + 1)]; response.body = { diff --git a/src/debugger/decorations.ts b/src/debugger/decorations.ts index 9ead209b8..6bac0d72a 100644 --- a/src/debugger/decorations.ts +++ b/src/debugger/decorations.ts @@ -7,6 +7,7 @@ import * as util from '../utilities'; import lsp from '../lsp/main'; import { getStateValue } from '../../out/cljs-lib/cljs-lib'; import * as replSession from '../nrepl/repl-session'; +import { assertIsDefined } from '../type-checks'; let enabled = false; @@ -49,7 +50,7 @@ async function update( const docUri = vscode.Uri.parse(namespacePath, true); const decodedDocUri = decodeURIComponent(docUri.toString()); const docSymbols = (await lsp.getDocumentSymbols(lspClient, decodedDocUri))[0].children; - util.assertIsDefined(docSymbols, 'Expected to get document symbols from the LSP server!'); + assertIsDefined(docSymbols, 'Expected to get document symbols from the LSP server!'); const instrumentedDocSymbols = docSymbols.filter((s) => instrumentedDefs.includes(s.name) ); diff --git a/src/debugger/util.ts b/src/debugger/util.ts index 76ddeb72a..bf531af15 100644 --- a/src/debugger/util.ts +++ b/src/debugger/util.ts @@ -1,5 +1,5 @@ import { LispTokenCursor } from '../cursor-doc/token-cursor'; -import { assertIsDefined } from '../utilities'; +import { assertIsDefined } from '../type-checks'; function moveCursorPastStringInList(tokenCursor: LispTokenCursor, s: string): void { const range = tokenCursor.rangeForList(1); diff --git a/src/doc-mirror/index.ts b/src/doc-mirror/index.ts index 370e67b7e..7c1376321 100644 --- a/src/doc-mirror/index.ts +++ b/src/doc-mirror/index.ts @@ -12,6 +12,7 @@ import { ModelEditSelection, } from '../cursor-doc/model'; import { isUndefined } from 'lodash'; +import { assertIsDefined } from '../type-checks'; const documents = new Map(); @@ -181,7 +182,7 @@ let registered = false; function processChanges(event: vscode.TextDocumentChangeEvent) { const mirrorDoc = documents.get(event.document); - utilities.assertIsDefined(mirrorDoc, 'Expected to find a mirror document!'); + assertIsDefined(mirrorDoc, 'Expected to find a mirror document!'); const model = mirrorDoc.model; for (const change of event.contentChanges) { diff --git a/src/evaluate.ts b/src/evaluate.ts index 41d00640b..899fdf5b7 100644 --- a/src/evaluate.ts +++ b/src/evaluate.ts @@ -15,6 +15,7 @@ import { getStateValue } from '../out/cljs-lib/cljs-lib'; import { getConfig } from './config'; import * as replSession from './nrepl/repl-session'; import * as getText from './util/get-text'; +import { assertIsDefined } from './type-checks'; function interruptAllEvaluations() { if (util.getConnectedState()) { @@ -86,7 +87,7 @@ async function evaluateCode( if (code.length > 0) { if (addToHistory) { - util.assertIsDefined(session.replType, 'Expected session to have a repl type!'); + assertIsDefined(session.replType, 'Expected session to have a repl type!'); replHistory.addToReplHistory(session.replType, code); replHistory.resetState(); } @@ -154,7 +155,7 @@ async function evaluateCode( if (context.stacktrace) { outputWindow.saveStacktrace(context.stacktrace); outputWindow.append(errMsg, (_, afterResultLocation) => { - util.assertIsDefined(afterResultLocation, 'Expected there to be a location!'); + assertIsDefined(afterResultLocation, 'Expected there to be a location!'); outputWindow.markLastStacktraceRange(afterResultLocation); }); } else { @@ -195,7 +196,7 @@ async function evaluateCode( } } if (context.stacktrace && context.stacktrace.stacktrace) { - util.assertIsDefined(afterResultLocation, 'Expected there to be a location!'); + assertIsDefined(afterResultLocation, 'Expected there to be a location!'); outputWindow.markLastStacktraceRange(afterResultLocation); } }); @@ -412,7 +413,8 @@ async function loadFile( if (doc && doc.languageId == 'clojure' && fileType != 'edn' && getStateValue('connected')) { state.analytics().logEvent('Evaluation', 'LoadFile').send(); - util.assertIsDefined(session, 'Expected there to be a repl session!'); + assertIsDefined(session, 'Expected there to be a repl session!'); + assertIsDefined(ns, 'Expected there to be a namespace!'); const docUri = outputWindow.isResultsDoc(doc) ? await namespace.getUriForNamespace(session, ns) : doc.uri; @@ -478,6 +480,7 @@ async function requireREPLUtilitiesCommand() { if (session) { try { await namespace.createNamespaceFromDocumentIfNotExists(util.tryToGetDocument({})); + assertIsDefined(ns, 'Expected there to be a namespace!'); await session.switchNS(ns); await session.eval(form, ns).value; chan.appendLine(`REPL utilities are now available in namespace ${ns}.`); @@ -507,7 +510,7 @@ async function togglePrettyPrint() { const config = vscode.workspace.getConfiguration('calva'), pprintConfigKey = 'prettyPrintingOptions', pprintOptions = config.get(pprintConfigKey); - util.assertIsDefined(pprintOptions, 'Expected there to be pprint options!'); + assertIsDefined(pprintOptions, 'Expected there to be pprint options!'); pprintOptions.enabled = !pprintOptions.enabled; if (pprintOptions.enabled && !(pprintOptions.printEngine || pprintOptions.printFn)) { pprintOptions.printEngine = 'pprint'; @@ -553,7 +556,7 @@ export async function evaluateInOutputWindow( const session = replSession.tryToGetSession(sessionType); replSession.updateReplSessionType(); if (outputWindow.getNs() !== ns) { - util.assertIsDefined(session, 'Expected there to be a repl session!'); + assertIsDefined(session, 'Expected there to be a repl session!'); await session.switchNS(ns); outputWindow.setSession(session, ns); if (options.evaluationSendCodeToOutputWindow !== false) { diff --git a/src/extension.ts b/src/extension.ts index b01950ce1..1dc7c1eee 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -37,6 +37,7 @@ import * as nreplLogging from './nrepl/logging'; import * as converters from './converters'; import * as clojureDocs from './clojuredocs'; +import { assertIsDefined } from './type-checks'; async function onDidSave(testController: vscode.TestController, document: vscode.TextDocument) { const { evaluate, test } = config.getConfig(); @@ -50,7 +51,7 @@ async function onDidSave(testController: vscode.TestController, document: vscode } else if (evaluate) { if (!outputWindow.isResultsDoc(document)) { const pprintOptions = config.getConfig().prettyPrintingOptions; - util.assertIsDefined(pprintOptions, 'Expected there to be pprint options!'); + assertIsDefined(pprintOptions, 'Expected there to be pprint options!'); await eval.loadFile(document, pprintOptions); outputWindow.appendPrompt(); state.analytics().logEvent('Calva', 'OnSaveLoad').send(); @@ -111,7 +112,7 @@ async function activate(context: vscode.ExtensionContext) { const maxTokenizationLineLength = vscode.workspace .getConfiguration('editor') .get('maxTokenizationLineLength'); - util.assertIsDefined( + assertIsDefined( maxTokenizationLineLength, 'Expected there to be a maxTokenizationLineLength set in the editor config' ); @@ -127,7 +128,7 @@ async function activate(context: vscode.ExtensionContext) { const clojureExtension = vscode.extensions.getExtension('avli.clojure'); const customCljsRepl = config.getConfig().customCljsRepl; const replConnectSequences = config.getConfig().replConnectSequences; - util.assertIsDefined( + assertIsDefined( replConnectSequences, 'Expected there to be a repl connect sequence in the config!' ); @@ -245,7 +246,7 @@ async function activate(context: vscode.ExtensionContext) { context.subscriptions.push( vscode.commands.registerCommand('calva.loadFile', async () => { const pprintOptions = config.getConfig().prettyPrintingOptions; - util.assertIsDefined(pprintOptions, 'Expected pprint options in the config!'); + assertIsDefined(pprintOptions, 'Expected pprint options in the config!'); await eval.loadFile({}, pprintOptions); return new Promise((resolve) => { outputWindow.appendPrompt(resolve); diff --git a/src/highlight/src/extension.ts b/src/highlight/src/extension.ts index d0b41a7c0..e9b59ed0a 100755 --- a/src/highlight/src/extension.ts +++ b/src/highlight/src/extension.ts @@ -4,7 +4,8 @@ import * as isEqual from 'lodash.isequal'; import * as docMirror from '../../doc-mirror/index'; import { Token, validPair } from '../../cursor-doc/clojure-lexer'; import { LispTokenCursor } from '../../cursor-doc/token-cursor'; -import { tryToGetActiveTextEditor, getActiveTextEditor, assertIsDefined } from '../../utilities'; +import { tryToGetActiveTextEditor, getActiveTextEditor } from '../../utilities'; +import { assertIsDefined } from '../../type-checks'; type StackItem = { char: string; diff --git a/src/lsp/main.ts b/src/lsp/main.ts index fe9ec4522..f73a73e58 100644 --- a/src/lsp/main.ts +++ b/src/lsp/main.ts @@ -23,6 +23,7 @@ import { provideHover } from '../providers/hover'; import { provideSignatureHelp } from '../providers/signature'; import { isResultsDoc } from '../results-output/results-doc'; import { MessageItem } from 'vscode'; +import { assertIsDefined } from '../type-checks'; const LSP_CLIENT_KEY = 'lspClient'; const LSP_CLIENT_KEY_ERROR = 'lspClientError'; @@ -430,7 +431,7 @@ async function getFallbackFolder(): Promise { } } - util.assertIsDefined(folderType, 'Expected there to be a folderType at this point!'); + assertIsDefined(folderType, 'Expected there to be a folderType at this point!'); return { uri: fallbackFolder, @@ -487,7 +488,7 @@ async function startClient(fallbackFolder: FallbackFolder): Promise { }); } setStateValue(LSP_CLIENT_KEY, undefined); - util.assertIsDefined(clojureLspPath, 'Expected there to be a clojure LSP path!'); + assertIsDefined(clojureLspPath, 'Expected there to be a clojure LSP path!'); const client = createClient(clojureLspPath, fallbackFolder); console.log('Starting clojure-lsp at', clojureLspPath); diff --git a/src/namespace.ts b/src/namespace.ts index dd945bd14..d8e1c5515 100644 --- a/src/namespace.ts +++ b/src/namespace.ts @@ -7,11 +7,12 @@ import * as outputWindow from './results-output/results-doc'; import * as utilities from './utilities'; import * as replSession from './nrepl/repl-session'; import { NReplSession } from './nrepl'; +import { assertIsDefined } from './type-checks'; export function getNamespace(doc?: vscode.TextDocument) { if (outputWindow.isResultsDoc(doc)) { const outputWindowNs = outputWindow.getNs(); - utilities.assertIsDefined(outputWindowNs, 'Expected output window to have a namespace!'); + assertIsDefined(outputWindowNs, 'Expected output window to have a namespace!'); return outputWindowNs; } let ns = 'user'; diff --git a/src/nrepl/bencode.ts b/src/nrepl/bencode.ts index 5dc1e444b..f7ba62bea 100644 --- a/src/nrepl/bencode.ts +++ b/src/nrepl/bencode.ts @@ -5,7 +5,7 @@ */ import * as stream from 'stream'; import { Buffer } from 'buffer'; -import { assertIsDefined } from '../utilities'; +import { assertIsDefined } from '../type-checks'; /** Bencode the given JSON object */ const bencode = (value) => { diff --git a/src/nrepl/connectSequence.ts b/src/nrepl/connectSequence.ts index 726d58524..953ab56e2 100644 --- a/src/nrepl/connectSequence.ts +++ b/src/nrepl/connectSequence.ts @@ -2,6 +2,7 @@ import * as vscode from 'vscode'; import * as state from '../state'; import * as utilities from '../utilities'; import { getConfig } from '../config'; +import { assertIsDefined } from '../type-checks'; enum ProjectTypes { 'Leiningen' = 'Leiningen', @@ -253,7 +254,7 @@ const defaultCljsTypes: { [id: string]: CljsTypeConfig } = { /** Retrieve the replConnectSequences from the config */ function getCustomConnectSequences(): ReplConnectSequence[] { const sequences: ReplConnectSequence[] | undefined = getConfig().replConnectSequences; - utilities.assertIsDefined(sequences, 'Expected there to be repl connect sequences!'); + assertIsDefined(sequences, 'Expected there to be repl connect sequences!'); for (const sequence of sequences) { if ( @@ -327,7 +328,7 @@ async function askForConnectSequence( return; } const sequence = sequences.find((seq) => seq.name === projectConnectSequenceName); - utilities.assertIsDefined(sequence, 'Expected to find a sequence!'); + assertIsDefined(sequence, 'Expected to find a sequence!'); void state.extensionContext.workspaceState.update('selectedCljTypeName', sequence.projectType); return sequence; } diff --git a/src/nrepl/jack-in-terminal.ts b/src/nrepl/jack-in-terminal.ts index fb1a25743..06ba96630 100644 --- a/src/nrepl/jack-in-terminal.ts +++ b/src/nrepl/jack-in-terminal.ts @@ -2,7 +2,7 @@ import * as vscode from 'vscode'; import * as child from 'child_process'; import * as kill from 'tree-kill'; import * as outputWindow from '../results-output/results-doc'; -import { assertIsDefined } from '../utilities'; +import { assertIsDefined } from '../type-checks'; export interface JackInTerminalOptions extends vscode.TerminalOptions { name: string; diff --git a/src/nrepl/jack-in.ts b/src/nrepl/jack-in.ts index 3bf3e96d2..e0ea58ff2 100644 --- a/src/nrepl/jack-in.ts +++ b/src/nrepl/jack-in.ts @@ -13,6 +13,7 @@ import * as outputWindow from '../results-output/results-doc'; import { JackInTerminal, JackInTerminalOptions, createCommandLine } from './jack-in-terminal'; import * as liveShareSupport from '../live-share'; import { getConfig } from '../config'; +import { assertIsDefined } from '../type-checks'; let jackInPTY: JackInTerminal | undefined = undefined; let jackInTerminal: vscode.Terminal | undefined = undefined; @@ -172,7 +173,7 @@ async function getJackInTerminalOptions( } const projectType = projectTypes.getProjectTypeForName(projectTypeName); - utilities.assertIsDefined(projectType, 'Expected to find a project type!'); + assertIsDefined(projectType, 'Expected to find a project type!'); const projectRootLocal = state.getProjectRootLocal(); diff --git a/src/nrepl/project-types.ts b/src/nrepl/project-types.ts index ee84f306b..1a42b5485 100644 --- a/src/nrepl/project-types.ts +++ b/src/nrepl/project-types.ts @@ -8,6 +8,7 @@ import { getConfig } from '../config'; import { keywordize, unKeywordize } from '../util/string'; import { CljsTypes, ReplConnectSequence } from './connectSequence'; import { parseForms, parseEdn } from '../../out/cljs-lib/cljs-lib'; +import { assertIsDefined } from '../type-checks'; export const isWin = /^win/.test(process.platform); @@ -32,7 +33,7 @@ function nreplPortFileRelativePath(connectSequence: ReplConnectSequence): string } else { const projectTypeName: ProjectType | string = connectSequence.projectType; const projectType = getProjectTypeForName(projectTypeName); - utilities.assertIsDefined( + assertIsDefined( projectType, `Expected a project type given project type name of ${projectTypeName}` ); @@ -121,7 +122,7 @@ async function selectShadowBuilds( if (menuSelections) { selectedBuilds = menuSelections.cljsLaunchBuilds; } else { - utilities.assertIsDefined(foundBuilds, 'Expected to have foundBuilds when using the picker!'); + assertIsDefined(foundBuilds, 'Expected to have foundBuilds when using the picker!'); selectedBuilds = await utilities.quickPickMulti({ values: foundBuilds.filter((x) => x[0] == ':'), placeHolder: 'Select builds to start', @@ -222,7 +223,7 @@ export enum JackInDependency { } const jackInDependencyVersions = getConfig().jackInDependencyVersions; -utilities.assertIsDefined( +assertIsDefined( jackInDependencyVersions, 'Expected jackInDependencyVersions to be set in the config!' ); @@ -641,7 +642,7 @@ export async function detectProjectTypes(): Promise { if (projectTypes[clj].useWhenExists) { try { const projectFileName = projectTypes[clj].useWhenExists; - utilities.assertIsDefined(projectFileName, 'Expected there to be a project filename!'); + assertIsDefined(projectFileName, 'Expected there to be a project filename!'); const uri = vscode.Uri.joinPath(state.getProjectRootUri(), projectFileName); await vscode.workspace.fs.readFile(uri); cljProjTypes.push(clj); diff --git a/src/nrepl/repl-session.ts b/src/nrepl/repl-session.ts index d92bb1915..fbcb93486 100644 --- a/src/nrepl/repl-session.ts +++ b/src/nrepl/repl-session.ts @@ -1,7 +1,8 @@ import { NReplSession } from '.'; -import { cljsLib, tryToGetDocument, getFileType, assertIsDefined } from '../utilities'; +import { cljsLib, tryToGetDocument, getFileType } from '../utilities'; import * as outputWindow from '../results-output/results-doc'; import { isUndefined } from 'lodash'; +import { assertIsDefined } from '../type-checks'; function tryToGetSession(fileType?: string): NReplSession | undefined { const doc = tryToGetDocument({}); diff --git a/src/nrepl/repl-start.ts b/src/nrepl/repl-start.ts index 9ed049e61..a8e86e114 100644 --- a/src/nrepl/repl-start.ts +++ b/src/nrepl/repl-start.ts @@ -12,6 +12,7 @@ import * as cljsLib from '../../out/cljs-lib/cljs-lib'; import { ReplConnectSequence } from './connectSequence'; import * as clojureLsp from '../lsp/main'; import * as calvaConfig from '../config'; +import { assertIsDefined } from '../type-checks'; const TEMPLATES_SUB_DIR = 'bundled'; const DRAM_BASE_URL = 'https://raw.githubusercontent.com/BetterThanTomorrow/dram'; @@ -168,7 +169,7 @@ export async function startStandaloneRepl( } const main = await openStoredDoc(storageUri, tempDirUri, config.files[0]); - utilities.assertIsDefined(main, 'Expected to be able to open the stored doc!'); + assertIsDefined(main, 'Expected to be able to open the stored doc!'); const [mainDoc, mainEditor] = main; for (const file of config.files.slice(1)) { await openStoredDoc(storageUri, tempDirUri, file); @@ -189,10 +190,7 @@ export async function startStandaloneRepl( preserveFocus: false, }); const pprintOptions = getConfig().prettyPrintingOptions; - utilities.assertIsDefined( - pprintOptions, - 'Expected there to be pretty printing options configured!' - ); + assertIsDefined(pprintOptions, 'Expected there to be pretty printing options configured!'); await eval.loadFile({}, pprintOptions); outputWindow.appendPrompt(); }); diff --git a/src/paredit/extension.ts b/src/paredit/extension.ts index 553a413a4..b6dd74403 100644 --- a/src/paredit/extension.ts +++ b/src/paredit/extension.ts @@ -13,7 +13,7 @@ import { import * as paredit from '../cursor-doc/paredit'; import * as docMirror from '../doc-mirror/index'; import { EditableDocument } from '../cursor-doc/model'; -import { assertIsDefined } from '../utilities'; +import { assertIsDefined } from '../type-checks'; const onPareditKeyMapChangedEmitter = new EventEmitter(); diff --git a/src/printer.ts b/src/printer.ts index db5d5594a..8af4d2fb7 100644 --- a/src/printer.ts +++ b/src/printer.ts @@ -1,5 +1,5 @@ import { getConfig } from './config'; -import { assertIsDefined } from './utilities'; +import { assertIsDefined } from './type-checks'; export type PrintFnOptions = { name: string; diff --git a/src/providers/completion.ts b/src/providers/completion.ts index 65c72525e..367ace46f 100644 --- a/src/providers/completion.ts +++ b/src/providers/completion.ts @@ -20,6 +20,7 @@ import { getClient } from '../lsp/main'; import { CompletionRequest, CompletionResolveRequest } from 'vscode-languageserver-protocol'; import { createConverter } from 'vscode-languageclient/lib/common/protocolConverter'; import { CompletionItem as LSPCompletionItem } from 'vscode-languageclient'; +import { assertIsDefined } from '../type-checks'; const mappings = { nil: CompletionItemKind.Value, @@ -100,7 +101,7 @@ export default class CalvaCompletionItemProvider implements CompletionItemProvid if (util.getConnectedState() && item['data']?.provider === 'repl') { const activeTextEditor = window.activeTextEditor; - util.assertIsDefined(activeTextEditor, 'Expected window to have activeTextEditor defined!'); + assertIsDefined(activeTextEditor, 'Expected window to have activeTextEditor defined!'); const client = replSession.tryToGetSession(util.getFileType(activeTextEditor.document)); if (client) { @@ -160,14 +161,14 @@ async function replCompletions( const toplevelSelection = select.getFormSelection(document, position, true); - util.assertIsDefined(toplevelSelection, 'Expected a topLevelSelection!'); + assertIsDefined(toplevelSelection, 'Expected a topLevelSelection!'); const toplevel = document.getText(toplevelSelection), toplevelStartOffset = document.offsetAt(toplevelSelection.start), toplevelStartCursor = docMirror.getDocument(document).getTokenCursor(toplevelStartOffset + 1), wordRange = document.getWordRangeAtPosition(position); - util.assertIsDefined(wordRange, 'Expected a wordRange!'); + assertIsDefined(wordRange, 'Expected a wordRange!'); const wordStartLocalOffset = document.offsetAt(wordRange.start) - toplevelStartOffset, wordEndLocalOffset = document.offsetAt(wordRange.end) - toplevelStartOffset, @@ -178,7 +179,7 @@ async function replCompletions( ns = namespace.getNamespace(document), client = replSession.tryToGetSession(util.getFileType(document)); - util.assertIsDefined(client, 'Expected there to be a repl client!'); + assertIsDefined(client, 'Expected there to be a repl client!'); const res = await client.complete(ns, text, toplevelIsValidForm ? replContext : undefined), results = res.completions || []; diff --git a/src/providers/signature.ts b/src/providers/signature.ts index 374b324f0..cad344326 100644 --- a/src/providers/signature.ts +++ b/src/providers/signature.ts @@ -13,6 +13,7 @@ import { LispTokenCursor } from '../cursor-doc/token-cursor'; import * as docMirror from '../doc-mirror/index'; import * as namespace from '../namespace'; import * as replSession from '../nrepl/repl-session'; +import { assertIsDefined } from '../type-checks'; export class CalvaSignatureHelpProvider implements SignatureHelpProvider { async provideSignatureHelp( @@ -42,13 +43,13 @@ export async function provideSignatureHelp( if (signatures) { const help = new SignatureHelp(), currentArgsRanges = getCurrentArgsRanges(document, idx); - util.assertIsDefined(currentArgsRanges, 'Expected to find the current args ranges!'); + assertIsDefined(currentArgsRanges, 'Expected to find the current args ranges!'); help.signatures = signatures; help.activeSignature = getActiveSignatureIdx(signatures, currentArgsRanges.length); if (signatures[help.activeSignature].parameters !== undefined) { const currentArgIdx = currentArgsRanges.findIndex((range) => range.contains(position)), activeSignature = signatures[help.activeSignature]; - util.assertIsDefined(activeSignature, 'Expected activeSignature to be defined!'); + assertIsDefined(activeSignature, 'Expected activeSignature to be defined!'); help.activeParameter = activeSignature.label.match(/&/) !== null ? Math.min(currentArgIdx, activeSignature.parameters.length - 1) diff --git a/src/results-output/repl-history.ts b/src/results-output/repl-history.ts index dbf18ad21..78e959682 100644 --- a/src/results-output/repl-history.ts +++ b/src/results-output/repl-history.ts @@ -9,6 +9,7 @@ import type { ReplSessionType } from '../config'; import { isResultsDoc, getSessionType, getPrompt, append } from './results-doc'; import { addToHistory } from './util'; import { isUndefined } from 'lodash'; +import { assertIsDefined } from '../type-checks'; const replHistoryCommandsActiveContext = 'calva:replHistoryCommandsActive'; let historyIndex: number | undefined = undefined; @@ -113,7 +114,7 @@ function showPreviousReplHistoryEntry(): void { historyIndex = history.length; lastTextAtPrompt = textAtPrompt; } else { - util.assertIsDefined(textAtPrompt, 'Expected to find text at the prompt!'); + assertIsDefined(textAtPrompt, 'Expected to find text at the prompt!'); updateReplHistory(replSessionType, history, textAtPrompt, historyIndex); } historyIndex--; @@ -133,8 +134,8 @@ function showNextReplHistoryEntry(): void { showReplHistoryEntry(lastTextAtPrompt, editor); } else { const textAtPrompt = getTextAfterLastOccurrenceOfSubstring(doc.getText(), getPrompt()); - util.assertIsDefined(textAtPrompt, 'Expected to find text at the prompt!'); - util.assertIsDefined(historyIndex, 'Expected a value for historyIndex!'); + assertIsDefined(textAtPrompt, 'Expected to find text at the prompt!'); + assertIsDefined(historyIndex, 'Expected a value for historyIndex!'); updateReplHistory(replSessionType, history, textAtPrompt, historyIndex); historyIndex++; const nextHistoryEntry = history[historyIndex]; diff --git a/src/results-output/results-doc.ts b/src/results-output/results-doc.ts index a98203626..1472401db 100644 --- a/src/results-output/results-doc.ts +++ b/src/results-output/results-doc.ts @@ -14,6 +14,7 @@ import * as docMirror from '../doc-mirror/index'; import { PrintStackTraceCodelensProvider } from '../providers/codelense'; import * as replSession from '../nrepl/repl-session'; import { splitEditQueueForTextBatching } from './util'; +import { assertIsDefined, isDefined } from '../type-checks'; const RESULTS_DOC_NAME = `output.${config.REPL_FILE_EXT}`; @@ -227,7 +228,7 @@ export async function revealDocForCurrentNS(preserveFocus: boolean = true) { export async function setNamespaceFromCurrentFile() { const session = replSession.getSession(); const ns = namespace.getNamespace(util.tryToGetDocument({})); - if (getNs() !== ns && util.isDefined(ns)) { + if (getNs() !== ns && isDefined(ns)) { await session.switchNS(ns); } setSession(session, ns); @@ -434,8 +435,8 @@ export function appendPrompt(onAppended?: OnAppendedCallback) { function getUriForCurrentNamespace(): Promise { const session = getSession(); - util.assertIsDefined(session, 'Expected there to be a current session!'); + assertIsDefined(session, 'Expected there to be a current session!'); const ns = getNs(); - util.assertIsDefined(ns, 'Expected there to be a current namespace!'); + assertIsDefined(ns, 'Expected there to be a current namespace!'); return namespace.getUriForNamespace(session, ns); } diff --git a/src/state.ts b/src/state.ts index 63f1216cd..e2573a614 100644 --- a/src/state.ts +++ b/src/state.ts @@ -5,6 +5,7 @@ import * as path from 'path'; import * as os from 'os'; import { getStateValue, setStateValue } from '../out/cljs-lib/cljs-lib'; import * as projectRoot from './project-root'; +import { assertIsDefined } from './type-checks'; let extensionContext: vscode.ExtensionContext; export function setExtensionContext(context: vscode.ExtensionContext) { @@ -55,7 +56,7 @@ export function tryToGetProjectRootLocal(useCache = true): string | undefined { export const getProjectRootLocal = (useCache = true): string => { const projectRootLocal = tryToGetProjectRootLocal(useCache); - util.assertIsDefined(projectRootLocal, 'Expected to find a local project root!'); + assertIsDefined(projectRootLocal, 'Expected to find a local project root!'); return projectRootLocal; }; @@ -78,7 +79,7 @@ export function tryToGetProjectRootUri(useCache = true): vscode.Uri | undefined export function getProjectRootUri(useCache = true): vscode.Uri { const projectRootUri = tryToGetProjectRootUri(useCache); - util.assertIsDefined(projectRootUri, 'Expected to find project root URI!'); + assertIsDefined(projectRootUri, 'Expected to find project root URI!'); return projectRootUri; } diff --git a/src/statusbar.ts b/src/statusbar.ts index 7972b67ef..5a3a12c99 100644 --- a/src/statusbar.ts +++ b/src/statusbar.ts @@ -5,6 +5,7 @@ import * as config from './config'; import status from './status'; import { getStateValue } from '../out/cljs-lib/cljs-lib'; import { tryToGetSession, getReplSessionTypeFromState } from './nrepl/repl-session'; +import { assertIsDefined } from './type-checks'; const connectionStatus = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Left, 1); const typeStatus = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Left, 1); @@ -24,7 +25,7 @@ const color = { function colorValue(section: string, currentConf: vscode.WorkspaceConfiguration): string { const configSection = currentConf.inspect(section); - util.assertIsDefined(configSection, () => `Expected config section "${section}" to be defined!`); + assertIsDefined(configSection, () => `Expected config section "${section}" to be defined!`); const { defaultValue, globalValue, workspaceFolderValue, workspaceValue } = configSection; diff --git a/src/testRunner.ts b/src/testRunner.ts index 0642e0070..abbb36cae 100644 --- a/src/testRunner.ts +++ b/src/testRunner.ts @@ -9,6 +9,7 @@ import * as lsp from './lsp/types'; import * as namespace from './namespace'; import { getSession, updateReplSessionType } from './nrepl/repl-session'; import * as getText from './util/get-text'; +import { assertIsDefined, isNonEmptyString } from './type-checks'; const diagnosticCollection = vscode.languages.createDiagnosticCollection('calva'); @@ -68,7 +69,7 @@ function upsertTest( // Cider 0.26 and 0.27 have an issue where context can be an empty array. // https://github.com/clojure-emacs/cider-nrepl/issues/728#issuecomment-996002988 export function assertionName(result: cider.TestResult): string { - if (util.isNonEmptyString(result.context)) { + if (isNonEmptyString(result.context)) { return result.context; } return 'assertion'; @@ -181,9 +182,9 @@ function reportTests( diagnosticCollection.clear(); const recordDiagnostic = (result: cider.TestResult) => { - util.assertIsDefined(result.line, 'Expected cider test result to have a line!'); + assertIsDefined(result.line, 'Expected cider test result to have a line!'); - util.assertIsDefined(result.file, 'Expected cider test result to have a file!'); + assertIsDefined(result.file, 'Expected cider test result to have a file!'); const msg = cider.diagnosticMessage(result); diff --git a/src/type-checks.ts b/src/type-checks.ts new file mode 100644 index 000000000..a094456b8 --- /dev/null +++ b/src/type-checks.ts @@ -0,0 +1,21 @@ +const isNonEmptyString = (value: any): boolean => typeof value == 'string' && value.length > 0; + +const isNullOrUndefined = (object: unknown): object is null | undefined => + object === null || object === undefined; + +const isDefined = (value: T | undefined | null): value is T => { + return !isNullOrUndefined(value); +}; + +// This needs to be a function and not an arrow function +// because assertion types are special. +function assertIsDefined( + value: T | undefined | null, + message: string | (() => string) +): asserts value is T { + if (isNullOrUndefined(value)) { + throw new Error(typeof message === 'string' ? message : message()); + } +} + +export { isNonEmptyString, isNullOrUndefined, isDefined, assertIsDefined }; diff --git a/src/util/cursor-get-text.ts b/src/util/cursor-get-text.ts index 1a976dbae..e760416e7 100644 --- a/src/util/cursor-get-text.ts +++ b/src/util/cursor-get-text.ts @@ -3,7 +3,7 @@ */ import { EditableDocument } from '../cursor-doc/model'; -import { assertIsDefined } from '../utilities'; +import { assertIsDefined } from '../type-checks'; export type RangeAndText = [[number, number], string] | [undefined, '']; diff --git a/src/utilities.ts b/src/utilities.ts index 32feeca9f..54292801c 100644 --- a/src/utilities.ts +++ b/src/utilities.ts @@ -9,7 +9,6 @@ import * as JSZip from 'jszip'; import * as outputWindow from './results-output/results-doc'; import * as cljsLib from '../out/cljs-lib/cljs-lib'; import * as url from 'url'; -import { isUndefined } from 'lodash'; const specialWords = ['-', '+', '/', '*']; //TODO: Add more here const syntaxQuoteSymbol = '`'; @@ -22,32 +21,10 @@ export function stripAnsi(str: string) { ); } -export const isNullOrUndefined = (object: unknown): object is null | undefined => - object === null || object === undefined; - -export const isDefined = (value: T | undefined | null): value is T => { - return !isNullOrUndefined(value); -}; - -// This needs to be a function and not an arrow function -// because assertion types are special. -export function assertIsDefined( - value: T | undefined | null, - message: string | (() => string) -): asserts value is T { - if (isNullOrUndefined(value)) { - throw new Error(typeof message === 'string' ? message : message()); - } -} - export function escapeStringRegexp(s: string): string { return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); } -export function isNonEmptyString(value: any): boolean { - return typeof value == 'string' && value.length > 0; -} - async function quickPickSingle(opts: { title?: string; values: string[]; @@ -200,7 +177,7 @@ function tryToGetDocument( function getDocument(document: vscode.TextDocument | Record): vscode.TextDocument { const doc = tryToGetDocument(document); - if (isUndefined(doc)) { + if (doc === undefined) { throw new Error('Expected an activeTextEditor with a document!'); } @@ -532,7 +509,7 @@ function tryToGetActiveTextEditor(): vscode.TextEditor | undefined { function getActiveTextEditor(): vscode.TextEditor { const editor = tryToGetActiveTextEditor(); - if (isUndefined(editor)) { + if (editor === undefined) { throw new Error('Expected active text editor!'); } From 13d7e828e19fea682b6f1dbb8b085f75644428c6 Mon Sep 17 00:00:00 2001 From: Cora Sutton Date: Sun, 1 May 2022 13:42:52 -0500 Subject: [PATCH 68/73] Fix some null checks --- src/nrepl/project-types.ts | 2 +- src/results-output/repl-history.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/nrepl/project-types.ts b/src/nrepl/project-types.ts index 1a42b5485..2311c44fa 100644 --- a/src/nrepl/project-types.ts +++ b/src/nrepl/project-types.ts @@ -168,7 +168,7 @@ async function leinProfilesAndAlias( leinAlias = menuSelections ? menuSelections.leinAlias : undefined; if (leinAlias) { alias = unKeywordize(leinAlias); - } else if (leinAlias === null) { + } else if (leinAlias === undefined) { alias = undefined; } else { let aliases: string[] = []; diff --git a/src/results-output/repl-history.ts b/src/results-output/repl-history.ts index 78e959682..5b2399b36 100644 --- a/src/results-output/repl-history.ts +++ b/src/results-output/repl-history.ts @@ -126,7 +126,7 @@ function showNextReplHistoryEntry(): void { const doc = editor.document; const replSessionType = getSessionType(); const history = getHistory(replSessionType); - if (!isResultsDoc(doc) || historyIndex === null) { + if (!isResultsDoc(doc) || historyIndex === undefined) { return; } if (historyIndex === history.length - 1) { From bc7faddeb68c2ea06cf4b7e84e6c5658ef7e3890 Mon Sep 17 00:00:00 2001 From: Cora Sutton Date: Sun, 1 May 2022 13:58:04 -0500 Subject: [PATCH 69/73] Fix the token cursor genericized function --- src/cursor-doc/token-cursor.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cursor-doc/token-cursor.ts b/src/cursor-doc/token-cursor.ts index 96b192ce9..6043074d6 100644 --- a/src/cursor-doc/token-cursor.ts +++ b/src/cursor-doc/token-cursor.ts @@ -169,7 +169,7 @@ function _rangesForSexpsInList( if (useRowCol) { return collectRanges(cursor, 'rowCol', 'rowCol'); } else { - return collectRanges(cursor, 'offsetStart', 'offsetEnd'); + return collectRanges(cursor, 'offsetStart', 'offsetStart'); } } From cd0d893d5a1ed2b50aee826efe36c4db98d6e975 Mon Sep 17 00:00:00 2001 From: Cora Sutton Date: Sun, 1 May 2022 14:21:12 -0500 Subject: [PATCH 70/73] Reformat the tsconfig --- tsconfig.json | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/tsconfig.json b/tsconfig.json index 1c5fca7f4..f2433c34e 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -3,8 +3,8 @@ "incremental": true, "tsBuildInfoFile": "tsconfig.tsbuildinfo", "strict": false /* Enable all strict type-checking options. */, - // "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */ "strictNullChecks": true /* Enable strict null checks. */, + // "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */ // "strictFunctionTypes": true, /* Enable strict checking of function types. */ // "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */ // "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */ @@ -18,9 +18,17 @@ "module": "commonjs", "target": "es6", "outDir": "out", - "lib": ["es2020", "dom"], + "lib": [ + "es2020", + "dom" + ], "sourceMap": true, "rootDir": "src" }, - "exclude": ["node_modules", "src/webview", "src/webview.ts", ".vscode-test"] -} + "exclude": [ + "node_modules", + "src/webview", + "src/webview.ts", + ".vscode-test" + ] +} \ No newline at end of file From 6414e325ec038aeb13be870ab8855182e490128b Mon Sep 17 00:00:00 2001 From: Cora Sutton Date: Sun, 1 May 2022 14:45:04 -0500 Subject: [PATCH 71/73] Use prettier to format JSON files in the project --- .vscode/settings.json | 2 +- tsconfig.json | 14 +++----------- 2 files changed, 4 insertions(+), 12 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 8a27c1afc..b75f269d0 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -216,7 +216,7 @@ "editor.defaultFormatter": "vscode.json-language-features" }, "[jsonc]": { - "editor.defaultFormatter": "vscode.json-language-features" + "editor.defaultFormatter": "esbenp.prettier-vscode" }, "peacock.color": "#e48141", "calva.replConnectSequences": [ diff --git a/tsconfig.json b/tsconfig.json index f2433c34e..c47c8c1b0 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -18,17 +18,9 @@ "module": "commonjs", "target": "es6", "outDir": "out", - "lib": [ - "es2020", - "dom" - ], + "lib": ["es2020", "dom"], "sourceMap": true, "rootDir": "src" }, - "exclude": [ - "node_modules", - "src/webview", - "src/webview.ts", - ".vscode-test" - ] -} \ No newline at end of file + "exclude": ["node_modules", "src/webview", "src/webview.ts", ".vscode-test"] +} From f5c1fbd2c478dc86aa00b3e0db261328c68f2bd8 Mon Sep 17 00:00:00 2001 From: Cora Sutton Date: Sun, 1 May 2022 15:12:14 -0500 Subject: [PATCH 72/73] Debug lsp downloads --- src/lsp/download.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/lsp/download.ts b/src/lsp/download.ts index 10b532832..71bae4892 100644 --- a/src/lsp/download.ts +++ b/src/lsp/download.ts @@ -101,6 +101,7 @@ async function unzipFile(zipFilePath: string, extensionPath: string): Promise { + console.log({ extensionPath, version, platform: process.platform }); const zipFileName = getZipFileName(process.platform); const urlPath = `/clojure-lsp/clojure-lsp/releases/download/${version}/${zipFileName}`; const zipFilePath = getZipFilePath(extensionPath, process.platform); From df16f82537e8002560feffd77d34c044b745a306 Mon Sep 17 00:00:00 2001 From: Cora Sutton Date: Sun, 1 May 2022 16:28:43 -0500 Subject: [PATCH 73/73] Add some debugging so we can figure out why integration tests are failing --- src/type-checks.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/type-checks.ts b/src/type-checks.ts index a094456b8..8945a08e1 100644 --- a/src/type-checks.ts +++ b/src/type-checks.ts @@ -14,6 +14,7 @@ function assertIsDefined( message: string | (() => string) ): asserts value is T { if (isNullOrUndefined(value)) { + console.trace({ value, message }); throw new Error(typeof message === 'string' ? message : message()); } }