aboutsummaryrefslogtreecommitdiff
path: root/editors/code/src
diff options
context:
space:
mode:
Diffstat (limited to 'editors/code/src')
-rw-r--r--editors/code/src/config.ts11
-rw-r--r--editors/code/src/ctx.ts12
-rw-r--r--editors/code/src/inlay_hints.ts290
-rw-r--r--editors/code/src/installation/interfaces.ts15
-rw-r--r--editors/code/src/installation/server.ts28
-rw-r--r--editors/code/src/rust-analyzer-api.ts22
-rw-r--r--editors/code/src/util.ts12
7 files changed, 233 insertions, 157 deletions
diff --git a/editors/code/src/config.ts b/editors/code/src/config.ts
index bf915102c..b72206d3c 100644
--- a/editors/code/src/config.ts
+++ b/editors/code/src/config.ts
@@ -1,6 +1,6 @@
1import * as os from "os"; 1import * as os from "os";
2import * as vscode from 'vscode'; 2import * as vscode from 'vscode';
3import { BinarySource } from "./installation/interfaces"; 3import { ArtifactSource } from "./installation/interfaces";
4import { log } from "./util"; 4import { log } from "./util";
5 5
6const RA_LSP_DEBUG = process.env.__RA_LSP_SERVER_DEBUG; 6const RA_LSP_DEBUG = process.env.__RA_LSP_SERVER_DEBUG;
@@ -114,12 +114,12 @@ export class Config {
114 } 114 }
115 } 115 }
116 116
117 get serverSource(): null | BinarySource { 117 get serverSource(): null | ArtifactSource {
118 const serverPath = RA_LSP_DEBUG ?? this.cfg.get<null | string>("serverPath"); 118 const serverPath = RA_LSP_DEBUG ?? this.cfg.get<null | string>("serverPath");
119 119
120 if (serverPath) { 120 if (serverPath) {
121 return { 121 return {
122 type: BinarySource.Type.ExplicitPath, 122 type: ArtifactSource.Type.ExplicitPath,
123 path: Config.replaceTildeWithHomeDir(serverPath) 123 path: Config.replaceTildeWithHomeDir(serverPath)
124 }; 124 };
125 } 125 }
@@ -129,11 +129,12 @@ export class Config {
129 if (!prebuiltBinaryName) return null; 129 if (!prebuiltBinaryName) return null;
130 130
131 return { 131 return {
132 type: BinarySource.Type.GithubRelease, 132 type: ArtifactSource.Type.GithubRelease,
133 dir: this.ctx.globalStoragePath, 133 dir: this.ctx.globalStoragePath,
134 file: prebuiltBinaryName, 134 file: prebuiltBinaryName,
135 storage: this.ctx.globalState, 135 storage: this.ctx.globalState,
136 version: Config.extensionVersion, 136 tag: Config.extensionVersion,
137 askBeforeDownload: this.cfg.get("updates.askBeforeDownload") as boolean,
137 repo: { 138 repo: {
138 name: "rust-analyzer", 139 name: "rust-analyzer",
139 owner: "rust-analyzer", 140 owner: "rust-analyzer",
diff --git a/editors/code/src/ctx.ts b/editors/code/src/ctx.ts
index b4e983a0c..25ef38aed 100644
--- a/editors/code/src/ctx.ts
+++ b/editors/code/src/ctx.ts
@@ -3,7 +3,7 @@ import * as lc from 'vscode-languageclient';
3 3
4import { Config } from './config'; 4import { Config } from './config';
5import { createClient } from './client'; 5import { createClient } from './client';
6import { isRustDocument } from './util'; 6import { isRustEditor, RustEditor } from './util';
7 7
8export class Ctx { 8export class Ctx {
9 private constructor( 9 private constructor(
@@ -22,17 +22,15 @@ export class Ctx {
22 return res; 22 return res;
23 } 23 }
24 24
25 get activeRustEditor(): vscode.TextEditor | undefined { 25 get activeRustEditor(): RustEditor | undefined {
26 const editor = vscode.window.activeTextEditor; 26 const editor = vscode.window.activeTextEditor;
27 return editor && isRustDocument(editor.document) 27 return editor && isRustEditor(editor)
28 ? editor 28 ? editor
29 : undefined; 29 : undefined;
30 } 30 }
31 31
32 get visibleRustEditors(): vscode.TextEditor[] { 32 get visibleRustEditors(): RustEditor[] {
33 return vscode.window.visibleTextEditors.filter( 33 return vscode.window.visibleTextEditors.filter(isRustEditor);
34 editor => isRustDocument(editor.document),
35 );
36 } 34 }
37 35
38 registerCommand(name: string, factory: (ctx: Ctx) => Cmd) { 36 registerCommand(name: string, factory: (ctx: Ctx) => Cmd) {
diff --git a/editors/code/src/inlay_hints.ts b/editors/code/src/inlay_hints.ts
index 08d3a64a7..e1a82e03e 100644
--- a/editors/code/src/inlay_hints.ts
+++ b/editors/code/src/inlay_hints.ts
@@ -1,156 +1,214 @@
1import * as lc from "vscode-languageclient";
1import * as vscode from 'vscode'; 2import * as vscode from 'vscode';
2import * as ra from './rust-analyzer-api'; 3import * as ra from './rust-analyzer-api';
3 4
4import { Ctx } from './ctx'; 5import { Ctx, Disposable } from './ctx';
5import { log, sendRequestWithRetry, isRustDocument } from './util'; 6import { sendRequestWithRetry, isRustDocument, RustDocument, RustEditor } from './util';
6 7
7export function activateInlayHints(ctx: Ctx) {
8 const hintsUpdater = new HintsUpdater(ctx);
9 vscode.window.onDidChangeVisibleTextEditors(
10 async _ => hintsUpdater.refresh(),
11 null,
12 ctx.subscriptions
13 );
14 8
15 vscode.workspace.onDidChangeTextDocument( 9export function activateInlayHints(ctx: Ctx) {
16 async event => { 10 const maybeUpdater = {
17 if (event.contentChanges.length === 0) return; 11 updater: null as null | HintsUpdater,
18 if (!isRustDocument(event.document)) return; 12 onConfigChange() {
19 await hintsUpdater.refresh(); 13 if (!ctx.config.displayInlayHints) {
14 return this.dispose();
15 }
16 if (!this.updater) this.updater = new HintsUpdater(ctx);
20 }, 17 },
21 null, 18 dispose() {
22 ctx.subscriptions 19 this.updater?.dispose();
23 ); 20 this.updater = null;
21 }
22 };
23
24 ctx.pushCleanup(maybeUpdater);
24 25
25 vscode.workspace.onDidChangeConfiguration( 26 vscode.workspace.onDidChangeConfiguration(
26 async _ => hintsUpdater.setEnabled(ctx.config.displayInlayHints), 27 maybeUpdater.onConfigChange, maybeUpdater, ctx.subscriptions
27 null,
28 ctx.subscriptions
29 ); 28 );
30 29
31 ctx.pushCleanup({ 30 maybeUpdater.onConfigChange();
32 dispose() { 31}
33 hintsUpdater.clear(); 32
33
34const typeHints = {
35 decorationType: vscode.window.createTextEditorDecorationType({
36 after: {
37 color: new vscode.ThemeColor('rust_analyzer.inlayHint'),
38 fontStyle: "normal",
34 } 39 }
35 }); 40 }),
36 41
37 // XXX: we don't await this, thus Promise rejections won't be handled, but 42 toDecoration(hint: ra.InlayHint.TypeHint, conv: lc.Protocol2CodeConverter): vscode.DecorationOptions {
38 // this should never throw in fact... 43 return {
39 void hintsUpdater.setEnabled(ctx.config.displayInlayHints); 44 range: conv.asRange(hint.range),
40} 45 renderOptions: { after: { contentText: `: ${hint.label}` } }
46 };
47 }
48};
49
50const paramHints = {
51 decorationType: vscode.window.createTextEditorDecorationType({
52 before: {
53 color: new vscode.ThemeColor('rust_analyzer.inlayHint'),
54 fontStyle: "normal",
55 }
56 }),
41 57
42const typeHintDecorationType = vscode.window.createTextEditorDecorationType({ 58 toDecoration(hint: ra.InlayHint.ParamHint, conv: lc.Protocol2CodeConverter): vscode.DecorationOptions {
43 after: { 59 return {
44 color: new vscode.ThemeColor('rust_analyzer.inlayHint'), 60 range: conv.asRange(hint.range),
45 fontStyle: "normal", 61 renderOptions: { before: { contentText: `${hint.label}: ` } }
46 }, 62 };
47});
48
49const parameterHintDecorationType = vscode.window.createTextEditorDecorationType({
50 before: {
51 color: new vscode.ThemeColor('rust_analyzer.inlayHint'),
52 fontStyle: "normal",
53 },
54});
55
56class HintsUpdater {
57 private pending = new Map<string, vscode.CancellationTokenSource>();
58 private ctx: Ctx;
59 private enabled: boolean;
60
61 constructor(ctx: Ctx) {
62 this.ctx = ctx;
63 this.enabled = false;
64 } 63 }
64};
65 65
66 async setEnabled(enabled: boolean): Promise<void> { 66class HintsUpdater implements Disposable {
67 log.debug({ enabled, prev: this.enabled }); 67 private sourceFiles = new Map<string, RustSourceFile>(); // map Uri -> RustSourceFile
68 private readonly disposables: Disposable[] = [];
68 69
69 if (this.enabled === enabled) return; 70 constructor(private readonly ctx: Ctx) {
70 this.enabled = enabled; 71 vscode.window.onDidChangeVisibleTextEditors(
72 this.onDidChangeVisibleTextEditors,
73 this,
74 this.disposables
75 );
71 76
72 if (this.enabled) { 77 vscode.workspace.onDidChangeTextDocument(
73 return await this.refresh(); 78 this.onDidChangeTextDocument,
74 } else { 79 this,
75 return this.clear(); 80 this.disposables
76 } 81 );
82
83 // Set up initial cache shape
84 ctx.visibleRustEditors.forEach(editor => this.sourceFiles.set(
85 editor.document.uri.toString(),
86 {
87 document: editor.document,
88 inlaysRequest: null,
89 cachedDecorations: null
90 }
91 ));
92
93 this.syncCacheAndRenderHints();
77 } 94 }
78 95
79 clear() { 96 dispose() {
80 this.ctx.visibleRustEditors.forEach(it => { 97 this.sourceFiles.forEach(file => file.inlaysRequest?.cancel());
81 this.setTypeDecorations(it, []); 98 this.ctx.visibleRustEditors.forEach(editor => this.renderDecorations(editor, { param: [], type: [] }));
82 this.setParameterDecorations(it, []); 99 this.disposables.forEach(d => d.dispose());
83 }); 100 }
101
102 onDidChangeTextDocument({ contentChanges, document }: vscode.TextDocumentChangeEvent) {
103 if (contentChanges.length === 0 || !isRustDocument(document)) return;
104 this.syncCacheAndRenderHints();
84 } 105 }
85 106
86 async refresh() { 107 private syncCacheAndRenderHints() {
87 if (!this.enabled) return; 108 // FIXME: make inlayHints request pass an array of files?
88 await Promise.all(this.ctx.visibleRustEditors.map(it => this.refreshEditor(it))); 109 this.sourceFiles.forEach((file, uri) => this.fetchHints(file).then(hints => {
110 if (!hints) return;
111
112 file.cachedDecorations = this.hintsToDecorations(hints);
113
114 for (const editor of this.ctx.visibleRustEditors) {
115 if (editor.document.uri.toString() === uri) {
116 this.renderDecorations(editor, file.cachedDecorations);
117 }
118 }
119 }));
89 } 120 }
90 121
91 private async refreshEditor(editor: vscode.TextEditor): Promise<void> { 122 onDidChangeVisibleTextEditors() {
92 const newHints = await this.queryHints(editor.document.uri.toString()); 123 const newSourceFiles = new Map<string, RustSourceFile>();
93 if (newHints == null) return; 124
94 125 // Rerendering all, even up-to-date editors for simplicity
95 const newTypeDecorations = newHints 126 this.ctx.visibleRustEditors.forEach(async editor => {
96 .filter(hint => hint.kind === ra.InlayKind.TypeHint) 127 const uri = editor.document.uri.toString();
97 .map(hint => ({ 128 const file = this.sourceFiles.get(uri) ?? {
98 range: this.ctx.client.protocol2CodeConverter.asRange(hint.range), 129 document: editor.document,
99 renderOptions: { 130 inlaysRequest: null,
100 after: { 131 cachedDecorations: null
101 contentText: `: ${hint.label}`, 132 };
102 }, 133 newSourceFiles.set(uri, file);
103 }, 134
104 })); 135 // No text documents changed, so we may try to use the cache
105 this.setTypeDecorations(editor, newTypeDecorations); 136 if (!file.cachedDecorations) {
106 137 file.inlaysRequest?.cancel();
107 const newParameterDecorations = newHints 138
108 .filter(hint => hint.kind === ra.InlayKind.ParameterHint) 139 const hints = await this.fetchHints(file);
109 .map(hint => ({ 140 if (!hints) return;
110 range: this.ctx.client.protocol2CodeConverter.asRange(hint.range), 141
111 renderOptions: { 142 file.cachedDecorations = this.hintsToDecorations(hints);
112 before: { 143 }
113 contentText: `${hint.label}: `, 144
114 }, 145 this.renderDecorations(editor, file.cachedDecorations);
115 }, 146 });
116 })); 147
117 this.setParameterDecorations(editor, newParameterDecorations); 148 // Cancel requests for no longer visible (disposed) source files
149 this.sourceFiles.forEach((file, uri) => {
150 if (!newSourceFiles.has(uri)) file.inlaysRequest?.cancel();
151 });
152
153 this.sourceFiles = newSourceFiles;
118 } 154 }
119 155
120 private setTypeDecorations( 156 private renderDecorations(editor: RustEditor, decorations: InlaysDecorations) {
121 editor: vscode.TextEditor, 157 editor.setDecorations(typeHints.decorationType, decorations.type);
122 decorations: vscode.DecorationOptions[], 158 editor.setDecorations(paramHints.decorationType, decorations.param);
123 ) {
124 editor.setDecorations(
125 typeHintDecorationType,
126 this.enabled ? decorations : [],
127 );
128 } 159 }
129 160
130 private setParameterDecorations( 161 private hintsToDecorations(hints: ra.InlayHint[]): InlaysDecorations {
131 editor: vscode.TextEditor, 162 const decorations: InlaysDecorations = { type: [], param: [] };
132 decorations: vscode.DecorationOptions[], 163 const conv = this.ctx.client.protocol2CodeConverter;
133 ) { 164
134 editor.setDecorations( 165 for (const hint of hints) {
135 parameterHintDecorationType, 166 switch (hint.kind) {
136 this.enabled ? decorations : [], 167 case ra.InlayHint.Kind.TypeHint: {
137 ); 168 decorations.type.push(typeHints.toDecoration(hint, conv));
169 continue;
170 }
171 case ra.InlayHint.Kind.ParamHint: {
172 decorations.param.push(paramHints.toDecoration(hint, conv));
173 continue;
174 }
175 }
176 }
177 return decorations;
138 } 178 }
139 179
140 private async queryHints(documentUri: string): Promise<ra.InlayHint[] | null> { 180 private async fetchHints(file: RustSourceFile): Promise<null | ra.InlayHint[]> {
141 this.pending.get(documentUri)?.cancel(); 181 file.inlaysRequest?.cancel();
142 182
143 const tokenSource = new vscode.CancellationTokenSource(); 183 const tokenSource = new vscode.CancellationTokenSource();
144 this.pending.set(documentUri, tokenSource); 184 file.inlaysRequest = tokenSource;
145 185
146 const request = { textDocument: { uri: documentUri } }; 186 const request = { textDocument: { uri: file.document.uri.toString() } };
147 187
148 return sendRequestWithRetry(this.ctx.client, ra.inlayHints, request, tokenSource.token) 188 return sendRequestWithRetry(this.ctx.client, ra.inlayHints, request, tokenSource.token)
149 .catch(_ => null) 189 .catch(_ => null)
150 .finally(() => { 190 .finally(() => {
151 if (!tokenSource.token.isCancellationRequested) { 191 if (file.inlaysRequest === tokenSource) {
152 this.pending.delete(documentUri); 192 file.inlaysRequest = null;
153 } 193 }
154 }); 194 });
155 } 195 }
156} 196}
197
198interface InlaysDecorations {
199 type: vscode.DecorationOptions[];
200 param: vscode.DecorationOptions[];
201}
202
203interface RustSourceFile {
204 /*
205 * Source of the token to cancel in-flight inlay hints request if any.
206 */
207 inlaysRequest: null | vscode.CancellationTokenSource;
208 /**
209 * Last applied decorations.
210 */
211 cachedDecorations: null | InlaysDecorations;
212
213 document: RustDocument;
214}
diff --git a/editors/code/src/installation/interfaces.ts b/editors/code/src/installation/interfaces.ts
index e40839e4b..50b635921 100644
--- a/editors/code/src/installation/interfaces.ts
+++ b/editors/code/src/installation/interfaces.ts
@@ -14,14 +14,14 @@ export interface ArtifactReleaseInfo {
14} 14}
15 15
16/** 16/**
17 * Represents the source of a binary artifact which is either specified by the user 17 * Represents the source of a an artifact which is either specified by the user
18 * explicitly, or bundled by this extension from GitHub releases. 18 * explicitly, or bundled by this extension from GitHub releases.
19 */ 19 */
20export type BinarySource = BinarySource.ExplicitPath | BinarySource.GithubRelease; 20export type ArtifactSource = ArtifactSource.ExplicitPath | ArtifactSource.GithubRelease;
21 21
22export namespace BinarySource { 22export namespace ArtifactSource {
23 /** 23 /**
24 * Type tag for `BinarySource` discriminated union. 24 * Type tag for `ArtifactSource` discriminated union.
25 */ 25 */
26 export const enum Type { ExplicitPath, GithubRelease } 26 export const enum Type { ExplicitPath, GithubRelease }
27 27
@@ -56,13 +56,18 @@ export namespace BinarySource {
56 /** 56 /**
57 * Tag of github release that denotes a version required by this extension. 57 * Tag of github release that denotes a version required by this extension.
58 */ 58 */
59 version: string; 59 tag: string;
60 60
61 /** 61 /**
62 * Object that provides `get()/update()` operations to store metadata 62 * Object that provides `get()/update()` operations to store metadata
63 * about the actual binary, e.g. its actual version. 63 * about the actual binary, e.g. its actual version.
64 */ 64 */
65 storage: vscode.Memento; 65 storage: vscode.Memento;
66
67 /**
68 * Ask for the user permission before downloading the artifact.
69 */
70 askBeforeDownload: boolean;
66 } 71 }
67 72
68} 73}
diff --git a/editors/code/src/installation/server.ts b/editors/code/src/installation/server.ts
index 6a6cf4f8c..ef1c45ff6 100644
--- a/editors/code/src/installation/server.ts
+++ b/editors/code/src/installation/server.ts
@@ -3,12 +3,12 @@ import * as path from "path";
3import { promises as dns } from "dns"; 3import { promises as dns } from "dns";
4import { spawnSync } from "child_process"; 4import { spawnSync } from "child_process";
5 5
6import { BinarySource } from "./interfaces"; 6import { ArtifactSource } from "./interfaces";
7import { fetchArtifactReleaseInfo } from "./fetch_artifact_release_info"; 7import { fetchArtifactReleaseInfo } from "./fetch_artifact_release_info";
8import { downloadArtifact } from "./download_artifact"; 8import { downloadArtifact } from "./download_artifact";
9import { log, assert } from "../util"; 9import { log, assert } from "../util";
10 10
11export async function ensureServerBinary(source: null | BinarySource): Promise<null | string> { 11export async function ensureServerBinary(source: null | ArtifactSource): Promise<null | string> {
12 if (!source) { 12 if (!source) {
13 vscode.window.showErrorMessage( 13 vscode.window.showErrorMessage(
14 "Unfortunately we don't ship binaries for your platform yet. " + 14 "Unfortunately we don't ship binaries for your platform yet. " +
@@ -22,7 +22,7 @@ export async function ensureServerBinary(source: null | BinarySource): Promise<n
22 } 22 }
23 23
24 switch (source.type) { 24 switch (source.type) {
25 case BinarySource.Type.ExplicitPath: { 25 case ArtifactSource.Type.ExplicitPath: {
26 if (isBinaryAvailable(source.path)) { 26 if (isBinaryAvailable(source.path)) {
27 return source.path; 27 return source.path;
28 } 28 }
@@ -34,11 +34,11 @@ export async function ensureServerBinary(source: null | BinarySource): Promise<n
34 ); 34 );
35 return null; 35 return null;
36 } 36 }
37 case BinarySource.Type.GithubRelease: { 37 case ArtifactSource.Type.GithubRelease: {
38 const prebuiltBinaryPath = path.join(source.dir, source.file); 38 const prebuiltBinaryPath = path.join(source.dir, source.file);
39 39
40 const installedVersion: null | string = getServerVersion(source.storage); 40 const installedVersion: null | string = getServerVersion(source.storage);
41 const requiredVersion: string = source.version; 41 const requiredVersion: string = source.tag;
42 42
43 log.debug("Installed version:", installedVersion, "required:", requiredVersion); 43 log.debug("Installed version:", installedVersion, "required:", requiredVersion);
44 44
@@ -46,12 +46,14 @@ export async function ensureServerBinary(source: null | BinarySource): Promise<n
46 return prebuiltBinaryPath; 46 return prebuiltBinaryPath;
47 } 47 }
48 48
49 const userResponse = await vscode.window.showInformationMessage( 49 if (source.askBeforeDownload) {
50 `Language server version ${source.version} for rust-analyzer is not installed. ` + 50 const userResponse = await vscode.window.showInformationMessage(
51 "Do you want to download it now?", 51 `Language server version ${source.tag} for rust-analyzer is not installed. ` +
52 "Download now", "Cancel" 52 "Do you want to download it now?",
53 ); 53 "Download now", "Cancel"
54 if (userResponse !== "Download now") return null; 54 );
55 if (userResponse !== "Download now") return null;
56 }
55 57
56 if (!await downloadServer(source)) return null; 58 if (!await downloadServer(source)) return null;
57 59
@@ -60,9 +62,9 @@ export async function ensureServerBinary(source: null | BinarySource): Promise<n
60 } 62 }
61} 63}
62 64
63async function downloadServer(source: BinarySource.GithubRelease): Promise<boolean> { 65async function downloadServer(source: ArtifactSource.GithubRelease): Promise<boolean> {
64 try { 66 try {
65 const releaseInfo = await fetchArtifactReleaseInfo(source.repo, source.file, source.version); 67 const releaseInfo = await fetchArtifactReleaseInfo(source.repo, source.file, source.tag);
66 68
67 await downloadArtifact(releaseInfo, source.file, source.dir, "language server"); 69 await downloadArtifact(releaseInfo, source.file, source.dir, "language server");
68 await setServerVersion(source.storage, releaseInfo.releaseName); 70 await setServerVersion(source.storage, releaseInfo.releaseName);
diff --git a/editors/code/src/rust-analyzer-api.ts b/editors/code/src/rust-analyzer-api.ts
index c5a010e94..bd6e3ada0 100644
--- a/editors/code/src/rust-analyzer-api.ts
+++ b/editors/code/src/rust-analyzer-api.ts
@@ -86,14 +86,20 @@ export interface Runnable {
86export const runnables = request<RunnablesParams, Vec<Runnable>>("runnables"); 86export const runnables = request<RunnablesParams, Vec<Runnable>>("runnables");
87 87
88 88
89export const enum InlayKind { 89
90 TypeHint = "TypeHint", 90export type InlayHint = InlayHint.TypeHint | InlayHint.ParamHint;
91 ParameterHint = "ParameterHint", 91
92} 92export namespace InlayHint {
93export interface InlayHint { 93 export const enum Kind {
94 range: lc.Range; 94 TypeHint = "TypeHint",
95 kind: InlayKind; 95 ParamHint = "ParameterHint",
96 label: string; 96 }
97 interface Common {
98 range: lc.Range;
99 label: string;
100 }
101 export type TypeHint = Common & { kind: Kind.TypeHint };
102 export type ParamHint = Common & { kind: Kind.ParamHint };
97} 103}
98export interface InlayHintsParams { 104export interface InlayHintsParams {
99 textDocument: lc.TextDocumentIdentifier; 105 textDocument: lc.TextDocumentIdentifier;
diff --git a/editors/code/src/util.ts b/editors/code/src/util.ts
index 7c95769bb..95a5f1227 100644
--- a/editors/code/src/util.ts
+++ b/editors/code/src/util.ts
@@ -1,7 +1,6 @@
1import * as lc from "vscode-languageclient"; 1import * as lc from "vscode-languageclient";
2import * as vscode from "vscode"; 2import * as vscode from "vscode";
3import { strict as nativeAssert } from "assert"; 3import { strict as nativeAssert } from "assert";
4import { TextDocument } from "vscode";
5 4
6export function assert(condition: boolean, explanation: string): asserts condition { 5export function assert(condition: boolean, explanation: string): asserts condition {
7 try { 6 try {
@@ -67,9 +66,16 @@ function sleep(ms: number) {
67 return new Promise(resolve => setTimeout(resolve, ms)); 66 return new Promise(resolve => setTimeout(resolve, ms));
68} 67}
69 68
70export function isRustDocument(document: TextDocument) { 69export type RustDocument = vscode.TextDocument & { languageId: "rust" };
70export type RustEditor = vscode.TextEditor & { document: RustDocument; id: string };
71
72export function isRustDocument(document: vscode.TextDocument): document is RustDocument {
71 return document.languageId === 'rust' 73 return document.languageId === 'rust'
72 // SCM diff views have the same URI as the on-disk document but not the same content 74 // SCM diff views have the same URI as the on-disk document but not the same content
73 && document.uri.scheme !== 'git' 75 && document.uri.scheme !== 'git'
74 && document.uri.scheme !== 'svn'; 76 && document.uri.scheme !== 'svn';
75} \ No newline at end of file 77}
78
79export function isRustEditor(editor: vscode.TextEditor): editor is RustEditor {
80 return isRustDocument(editor.document);
81}