editor/code: Enable noUncheckedIndexedAccess
ts option
https://www.typescriptlang.org/tsconfig#noUncheckedIndexedAccess
This commit is contained in:
parent
bb35d8fa8e
commit
72a3883a71
@ -2,6 +2,7 @@ import * as vscode from "vscode";
|
||||
|
||||
import { Ctx, Disposable } from "./ctx";
|
||||
import { RustEditor, isRustEditor } from "./util";
|
||||
import { unwrapUndefinable } from "./undefinable";
|
||||
|
||||
// FIXME: consider implementing this via the Tree View API?
|
||||
// https://code.visualstudio.com/api/extension-guides/tree-view
|
||||
@ -164,8 +165,9 @@ export class AstInspector implements vscode.HoverProvider, vscode.DefinitionProv
|
||||
if (!parsedRange) return;
|
||||
|
||||
const [begin, end] = parsedRange.slice(1).map((off) => this.positionAt(doc, +off));
|
||||
|
||||
return new vscode.Range(begin, end);
|
||||
const actualBegin = unwrapUndefinable(begin);
|
||||
const actualEnd = unwrapUndefinable(end);
|
||||
return new vscode.Range(actualBegin, actualEnd);
|
||||
}
|
||||
|
||||
// Memoize the last value, otherwise the CPU is at 100% single core
|
||||
|
@ -9,6 +9,7 @@ import { WorkspaceEdit } from "vscode";
|
||||
import { Config, prepareVSCodeConfig } from "./config";
|
||||
import { randomUUID } from "crypto";
|
||||
import { sep as pathSeparator } from "path";
|
||||
import { unwrapUndefinable } from "./undefinable";
|
||||
|
||||
export interface Env {
|
||||
[name: string]: string;
|
||||
@ -323,10 +324,12 @@ export async function createClient(
|
||||
}
|
||||
for (const [group, { index, items }] of groups) {
|
||||
if (items.length === 1) {
|
||||
result[index] = items[0];
|
||||
const item = unwrapUndefinable(items[0]);
|
||||
result[index] = item;
|
||||
} else {
|
||||
const action = new vscode.CodeAction(group);
|
||||
action.kind = items[0].kind;
|
||||
const item = unwrapUndefinable(items[0]);
|
||||
action.kind = item.kind;
|
||||
action.command = {
|
||||
command: "rust-analyzer.applyActionGroup",
|
||||
title: "",
|
||||
|
@ -20,6 +20,7 @@ import { startDebugSession, makeDebugConfig } from "./debug";
|
||||
import { LanguageClient } from "vscode-languageclient/node";
|
||||
import { LINKED_COMMANDS } from "./client";
|
||||
import { DependencyId } from "./dependencies_provider";
|
||||
import { unwrapUndefinable } from "./undefinable";
|
||||
|
||||
export * from "./ast_inspector";
|
||||
export * from "./run";
|
||||
@ -129,7 +130,8 @@ export function matchingBrace(ctx: CtxInit): Cmd {
|
||||
),
|
||||
});
|
||||
editor.selections = editor.selections.map((sel, idx) => {
|
||||
const active = client.protocol2CodeConverter.asPosition(response[idx]);
|
||||
const position = unwrapUndefinable(response[idx]);
|
||||
const active = client.protocol2CodeConverter.asPosition(position);
|
||||
const anchor = sel.isEmpty ? active : sel.anchor;
|
||||
return new vscode.Selection(anchor, active);
|
||||
});
|
||||
@ -231,7 +233,7 @@ export function parentModule(ctx: CtxInit): Cmd {
|
||||
if (!locations) return;
|
||||
|
||||
if (locations.length === 1) {
|
||||
const loc = locations[0];
|
||||
const loc = unwrapUndefinable(locations[0]);
|
||||
|
||||
const uri = client.protocol2CodeConverter.asUri(loc.targetUri);
|
||||
const range = client.protocol2CodeConverter.asRange(loc.targetRange);
|
||||
@ -331,7 +333,13 @@ async function revealParentChain(document: RustDocument, ctx: CtxInit) {
|
||||
} while (!ctx.dependencies?.contains(documentPath));
|
||||
parentChain.reverse();
|
||||
for (const idx in parentChain) {
|
||||
await ctx.treeView?.reveal(parentChain[idx], { select: true, expand: true });
|
||||
const treeView = ctx.treeView;
|
||||
if (!treeView) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const dependency = unwrapUndefinable(parentChain[idx]);
|
||||
await treeView.reveal(dependency, { select: true, expand: true });
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -4,6 +4,7 @@ import * as path from "path";
|
||||
import * as vscode from "vscode";
|
||||
import { Env } from "./client";
|
||||
import { log } from "./util";
|
||||
import { expectNotUndefined, unwrapUndefinable } from "./undefinable";
|
||||
|
||||
export type RunnableEnvCfg =
|
||||
| undefined
|
||||
@ -338,7 +339,7 @@ export function substituteVariablesInEnv(env: Env): Env {
|
||||
const depRe = new RegExp(/\${(?<depName>.+?)}/g);
|
||||
let match = undefined;
|
||||
while ((match = depRe.exec(value))) {
|
||||
const depName = match.groups!.depName;
|
||||
const depName = unwrapUndefinable(match.groups?.depName);
|
||||
deps.add(depName);
|
||||
// `depName` at this point can have a form of `expression` or
|
||||
// `prefix:expression`
|
||||
@ -356,7 +357,7 @@ export function substituteVariablesInEnv(env: Env): Env {
|
||||
if (match) {
|
||||
const { prefix, body } = match.groups!;
|
||||
if (prefix === "env") {
|
||||
const envName = body;
|
||||
const envName = unwrapUndefinable(body);
|
||||
envWithDeps[dep] = {
|
||||
value: process.env[envName] ?? "",
|
||||
deps: [],
|
||||
@ -384,13 +385,12 @@ export function substituteVariablesInEnv(env: Env): Env {
|
||||
do {
|
||||
leftToResolveSize = toResolve.size;
|
||||
for (const key of toResolve) {
|
||||
if (envWithDeps[key].deps.every((dep) => resolved.has(dep))) {
|
||||
envWithDeps[key].value = envWithDeps[key].value.replace(
|
||||
/\${(?<depName>.+?)}/g,
|
||||
(_wholeMatch, depName) => {
|
||||
return envWithDeps[depName].value;
|
||||
}
|
||||
);
|
||||
const item = unwrapUndefinable(envWithDeps[key]);
|
||||
if (item.deps.every((dep) => resolved.has(dep))) {
|
||||
item.value = item.value.replace(/\${(?<depName>.+?)}/g, (_wholeMatch, depName) => {
|
||||
const item = unwrapUndefinable(envWithDeps[depName]);
|
||||
return item.value;
|
||||
});
|
||||
resolved.add(key);
|
||||
toResolve.delete(key);
|
||||
}
|
||||
@ -399,7 +399,8 @@ export function substituteVariablesInEnv(env: Env): Env {
|
||||
|
||||
const resolvedEnv: Env = {};
|
||||
for (const key of Object.keys(env)) {
|
||||
resolvedEnv[key] = envWithDeps[`env:${key}`].value;
|
||||
const item = unwrapUndefinable(envWithDeps[`env:${key}`]);
|
||||
resolvedEnv[key] = item.value;
|
||||
}
|
||||
return resolvedEnv;
|
||||
}
|
||||
@ -418,20 +419,19 @@ function substituteVSCodeVariableInString(val: string): string {
|
||||
function computeVscodeVar(varName: string): string | null {
|
||||
const workspaceFolder = () => {
|
||||
const folders = vscode.workspace.workspaceFolders ?? [];
|
||||
if (folders.length === 1) {
|
||||
// TODO: support for remote workspaces?
|
||||
return folders[0].uri.fsPath;
|
||||
} else if (folders.length > 1) {
|
||||
// could use currently opened document to detect the correct
|
||||
// workspace. However, that would be determined by the document
|
||||
// user has opened on Editor startup. Could lead to
|
||||
// unpredictable workspace selection in practice.
|
||||
// It's better to pick the first one
|
||||
return folders[0].uri.fsPath;
|
||||
} else {
|
||||
// no workspace opened
|
||||
return "";
|
||||
}
|
||||
const folder = folders[0];
|
||||
// TODO: support for remote workspaces?
|
||||
const fsPath: string =
|
||||
folder === undefined
|
||||
? // no workspace opened
|
||||
""
|
||||
: // could use currently opened document to detect the correct
|
||||
// workspace. However, that would be determined by the document
|
||||
// user has opened on Editor startup. Could lead to
|
||||
// unpredictable workspace selection in practice.
|
||||
// It's better to pick the first one
|
||||
folder.uri.fsPath;
|
||||
return fsPath;
|
||||
};
|
||||
// https://code.visualstudio.com/docs/editor/variables-reference
|
||||
const supportedVariables: { [k: string]: () => string } = {
|
||||
@ -454,7 +454,11 @@ function computeVscodeVar(varName: string): string | null {
|
||||
};
|
||||
|
||||
if (varName in supportedVariables) {
|
||||
return supportedVariables[varName]();
|
||||
const fn = expectNotUndefined(
|
||||
supportedVariables[varName],
|
||||
`${varName} should not be undefined here`
|
||||
);
|
||||
return fn();
|
||||
} else {
|
||||
// return "${" + varName + "}";
|
||||
return null;
|
||||
|
@ -6,6 +6,7 @@ import * as ra from "./lsp_ext";
|
||||
import { Cargo, getRustcId, getSysroot } from "./toolchain";
|
||||
import { Ctx } from "./ctx";
|
||||
import { prepareEnv } from "./run";
|
||||
import { unwrapUndefinable } from "./undefinable";
|
||||
|
||||
const debugOutput = vscode.window.createOutputChannel("Debug");
|
||||
type DebugConfigProvider = (
|
||||
@ -105,12 +106,13 @@ async function getDebugConfiguration(
|
||||
const workspaceFolders = vscode.workspace.workspaceFolders!;
|
||||
const isMultiFolderWorkspace = workspaceFolders.length > 1;
|
||||
const firstWorkspace = workspaceFolders[0];
|
||||
const workspace =
|
||||
const maybeWorkspace =
|
||||
!isMultiFolderWorkspace || !runnable.args.workspaceRoot
|
||||
? firstWorkspace
|
||||
: workspaceFolders.find((w) => runnable.args.workspaceRoot?.includes(w.uri.fsPath)) ||
|
||||
firstWorkspace;
|
||||
|
||||
const workspace = unwrapUndefinable(maybeWorkspace);
|
||||
const wsFolder = path.normalize(workspace.uri.fsPath);
|
||||
const workspaceQualifier = isMultiFolderWorkspace ? `:${workspace.name}` : "";
|
||||
function simplifyPath(p: string): string {
|
||||
@ -130,12 +132,8 @@ async function getDebugConfiguration(
|
||||
sourceFileMap[`/rustc/${commitHash}/`] = rustlib;
|
||||
}
|
||||
|
||||
const debugConfig = knownEngines[debugEngine.id](
|
||||
runnable,
|
||||
simplifyPath(executable),
|
||||
env,
|
||||
sourceFileMap
|
||||
);
|
||||
const provider = unwrapUndefinable(knownEngines[debugEngine.id]);
|
||||
const debugConfig = provider(runnable, simplifyPath(executable), env, sourceFileMap);
|
||||
if (debugConfig.type in debugOptions.engineSettings) {
|
||||
const settingsMap = (debugOptions.engineSettings as any)[debugConfig.type];
|
||||
for (var key in settingsMap) {
|
||||
|
@ -4,6 +4,7 @@ import * as fs from "fs";
|
||||
import { CtxInit } from "./ctx";
|
||||
import * as ra from "./lsp_ext";
|
||||
import { FetchDependencyListResult } from "./lsp_ext";
|
||||
import { unwrapUndefinable } from "./undefinable";
|
||||
|
||||
export class RustDependenciesProvider
|
||||
implements vscode.TreeDataProvider<Dependency | DependencyFile>
|
||||
@ -49,7 +50,12 @@ export class RustDependenciesProvider
|
||||
}
|
||||
|
||||
getTreeItem(element: Dependency | DependencyFile): vscode.TreeItem | Thenable<vscode.TreeItem> {
|
||||
if (element.id! in this.dependenciesMap) return this.dependenciesMap[element.id!];
|
||||
const dependenciesMap = this.dependenciesMap;
|
||||
const elementId = element.id!;
|
||||
if (elementId in dependenciesMap) {
|
||||
const dependency = unwrapUndefinable(dependenciesMap[elementId]);
|
||||
return dependency;
|
||||
}
|
||||
return element;
|
||||
}
|
||||
|
||||
|
@ -2,6 +2,7 @@ import * as anser from "anser";
|
||||
import * as vscode from "vscode";
|
||||
import { ProviderResult, Range, TextEditorDecorationType, ThemeColor, window } from "vscode";
|
||||
import { Ctx } from "./ctx";
|
||||
import { unwrapUndefinable } from "./undefinable";
|
||||
|
||||
export const URI_SCHEME = "rust-analyzer-diagnostics-view";
|
||||
|
||||
@ -195,7 +196,8 @@ export class AnsiDecorationProvider implements vscode.Disposable {
|
||||
// anser won't return both the RGB and the color name at the same time,
|
||||
// so just fake a single foreground control char with the palette number:
|
||||
const spans = anser.ansiToJson(`\x1b[38;5;${paletteColor}m`);
|
||||
const rgb = spans[1].fg;
|
||||
const span = unwrapUndefinable(spans[1]);
|
||||
const rgb = span.fg;
|
||||
|
||||
if (rgb) {
|
||||
return `rgb(${rgb})`;
|
||||
|
19
editors/code/src/nullable.ts
Normal file
19
editors/code/src/nullable.ts
Normal file
@ -0,0 +1,19 @@
|
||||
export type NotNull<T> = T extends null ? never : T;
|
||||
|
||||
export type Nullable<T> = T | null;
|
||||
|
||||
function isNotNull<T>(input: Nullable<T>): input is NotNull<T> {
|
||||
return input !== null;
|
||||
}
|
||||
|
||||
function expectNotNull<T>(input: Nullable<T>, msg: string): NotNull<T> {
|
||||
if (isNotNull(input)) {
|
||||
return input;
|
||||
}
|
||||
|
||||
throw new TypeError(msg);
|
||||
}
|
||||
|
||||
export function unwrapNullable<T>(input: Nullable<T>): NotNull<T> {
|
||||
return expectNotNull(input, `unwrapping \`null\``);
|
||||
}
|
@ -6,6 +6,7 @@ import * as tasks from "./tasks";
|
||||
import { CtxInit } from "./ctx";
|
||||
import { makeDebugConfig } from "./debug";
|
||||
import { Config, RunnableEnvCfg } from "./config";
|
||||
import { unwrapUndefinable } from "./undefinable";
|
||||
|
||||
const quickPickButtons = [
|
||||
{ iconPath: new vscode.ThemeIcon("save"), tooltip: "Save as a launch.json configuration." },
|
||||
@ -68,12 +69,14 @@ export async function selectRunnable(
|
||||
quickPick.onDidHide(() => close()),
|
||||
quickPick.onDidAccept(() => close(quickPick.selectedItems[0])),
|
||||
quickPick.onDidTriggerButton(async (_button) => {
|
||||
await makeDebugConfig(ctx, quickPick.activeItems[0].runnable);
|
||||
const runnable = unwrapUndefinable(quickPick.activeItems[0]).runnable;
|
||||
await makeDebugConfig(ctx, runnable);
|
||||
close();
|
||||
}),
|
||||
quickPick.onDidChangeActive((active) => {
|
||||
if (showButtons && active.length > 0) {
|
||||
if (active[0].label.startsWith("cargo")) {
|
||||
quickPick.onDidChangeActive((activeList) => {
|
||||
if (showButtons && activeList.length > 0) {
|
||||
const active = unwrapUndefinable(activeList[0]);
|
||||
if (active.label.startsWith("cargo")) {
|
||||
// save button makes no sense for `cargo test` or `cargo check`
|
||||
quickPick.buttons = [];
|
||||
} else if (quickPick.buttons.length === 0) {
|
||||
|
@ -1,10 +1,11 @@
|
||||
import * as vscode from "vscode";
|
||||
|
||||
import { assert } from "./util";
|
||||
import { unwrapUndefinable } from "./undefinable";
|
||||
|
||||
export async function applySnippetWorkspaceEdit(edit: vscode.WorkspaceEdit) {
|
||||
if (edit.entries().length === 1) {
|
||||
const [uri, edits] = edit.entries()[0];
|
||||
const [uri, edits] = unwrapUndefinable(edit.entries()[0]);
|
||||
const editor = await editorFromUri(uri);
|
||||
if (editor) await applySnippetTextEdits(editor, edits);
|
||||
return;
|
||||
@ -68,7 +69,8 @@ export async function applySnippetTextEdits(editor: vscode.TextEditor, edits: vs
|
||||
});
|
||||
if (selections.length > 0) editor.selections = selections;
|
||||
if (selections.length === 1) {
|
||||
editor.revealRange(selections[0], vscode.TextEditorRevealType.InCenterIfOutsideViewport);
|
||||
const selection = unwrapUndefinable(selections[0]);
|
||||
editor.revealRange(selection, vscode.TextEditorRevealType.InCenterIfOutsideViewport);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2,6 +2,7 @@ import * as vscode from "vscode";
|
||||
import * as toolchain from "./toolchain";
|
||||
import { Config } from "./config";
|
||||
import { log } from "./util";
|
||||
import { unwrapUndefinable } from "./undefinable";
|
||||
|
||||
// This ends up as the `type` key in tasks.json. RLS also uses `cargo` and
|
||||
// our configuration should be compatible with it so use the same key.
|
||||
@ -120,7 +121,8 @@ export async function buildCargoTask(
|
||||
|
||||
const fullCommand = [...cargoCommand, ...args];
|
||||
|
||||
exec = new vscode.ProcessExecution(fullCommand[0], fullCommand.slice(1), definition);
|
||||
const processName = unwrapUndefinable(fullCommand[0]);
|
||||
exec = new vscode.ProcessExecution(processName, fullCommand.slice(1), definition);
|
||||
}
|
||||
|
||||
return new vscode.Task(
|
||||
|
@ -4,6 +4,8 @@ import * as path from "path";
|
||||
import * as readline from "readline";
|
||||
import * as vscode from "vscode";
|
||||
import { execute, log, memoizeAsync } from "./util";
|
||||
import { unwrapNullable } from "./nullable";
|
||||
import { unwrapUndefinable } from "./undefinable";
|
||||
|
||||
interface CompilationArtifact {
|
||||
fileName: string;
|
||||
@ -93,7 +95,8 @@ export class Cargo {
|
||||
throw new Error("Multiple compilation artifacts are not supported.");
|
||||
}
|
||||
|
||||
return artifacts[0].fileName;
|
||||
const artifact = unwrapUndefinable(artifacts[0]);
|
||||
return artifact.fileName;
|
||||
}
|
||||
|
||||
private async runCargo(
|
||||
@ -142,7 +145,9 @@ export async function getRustcId(dir: string): Promise<string> {
|
||||
const data = await execute(`${rustcPath} -V -v`, { cwd: dir });
|
||||
const rx = /commit-hash:\s(.*)$/m;
|
||||
|
||||
return rx.exec(data)![1];
|
||||
const result = unwrapNullable(rx.exec(data));
|
||||
const first = unwrapUndefinable(result[1]);
|
||||
return first;
|
||||
}
|
||||
|
||||
/** Mirrors `toolchain::cargo()` implementation */
|
||||
|
19
editors/code/src/undefinable.ts
Normal file
19
editors/code/src/undefinable.ts
Normal file
@ -0,0 +1,19 @@
|
||||
export type NotUndefined<T> = T extends undefined ? never : T;
|
||||
|
||||
export type Undefinable<T> = T | undefined;
|
||||
|
||||
function isNotUndefined<T>(input: Undefinable<T>): input is NotUndefined<T> {
|
||||
return input !== undefined;
|
||||
}
|
||||
|
||||
export function expectNotUndefined<T>(input: Undefinable<T>, msg: string): NotUndefined<T> {
|
||||
if (isNotUndefined(input)) {
|
||||
return input;
|
||||
}
|
||||
|
||||
throw new TypeError(msg);
|
||||
}
|
||||
|
||||
export function unwrapUndefinable<T>(input: Undefinable<T>): NotUndefined<T> {
|
||||
return expectNotUndefined(input, `unwrapping \`undefined\``);
|
||||
}
|
@ -14,8 +14,7 @@
|
||||
// to update typescript version without any code change.
|
||||
"useUnknownInCatchVariables": false,
|
||||
"exactOptionalPropertyTypes": false,
|
||||
"noPropertyAccessFromIndexSignature": false,
|
||||
"noUncheckedIndexedAccess": false
|
||||
"noPropertyAccessFromIndexSignature": false
|
||||
},
|
||||
"exclude": ["node_modules", ".vscode-test"],
|
||||
"include": ["src", "tests"]
|
||||
|
Loading…
Reference in New Issue
Block a user