2693: Encapsulate inlay hints activation r=matklad a=matklad



Co-authored-by: Aleksey Kladov <aleksey.kladov@gmail.com>
This commit is contained in:
bors[bot] 2019-12-30 19:25:01 +00:00 committed by GitHub
commit 17dda0972a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 139 additions and 155 deletions

View file

@ -1,3 +1,6 @@
import * as vscode from 'vscode';
import * as lc from 'vscode-languageclient';
import { Ctx, Cmd } from '../ctx'; import { Ctx, Cmd } from '../ctx';
import { analyzerStatus } from './analyzer_status'; import { analyzerStatus } from './analyzer_status';
@ -7,8 +10,7 @@ import { onEnter } from './on_enter';
import { parentModule } from './parent_module'; import { parentModule } from './parent_module';
import { syntaxTree } from './syntax_tree'; import { syntaxTree } from './syntax_tree';
import { expandMacro } from './expand_macro'; import { expandMacro } from './expand_macro';
import * as inlayHints from './inlay_hints'; import { run, runSingle } from './runnables';
import * as runnables from './runnables';
function collectGarbage(ctx: Ctx): Cmd { function collectGarbage(ctx: Ctx): Cmd {
return async () => { return async () => {
@ -16,15 +18,27 @@ function collectGarbage(ctx: Ctx): Cmd {
}; };
} }
function showReferences(ctx: Ctx): Cmd {
return (uri: string, position: lc.Position, locations: lc.Location[]) => {
vscode.commands.executeCommand(
'editor.action.showReferences',
vscode.Uri.parse(uri),
ctx.client.protocol2CodeConverter.asPosition(position),
locations.map(ctx.client.protocol2CodeConverter.asLocation),
);
};
}
export { export {
analyzerStatus, analyzerStatus,
expandMacro, expandMacro,
joinLines, joinLines,
matchingBrace, matchingBrace,
parentModule, parentModule,
runnables,
syntaxTree, syntaxTree,
onEnter, onEnter,
inlayHints,
collectGarbage, collectGarbage,
run,
runSingle,
showReferences,
}; };

View file

@ -1,16 +0,0 @@
export class LineBuffer {
private outBuffer: string = '';
public processOutput(chunk: string, cb: (line: string) => void) {
this.outBuffer += chunk;
let eolIndex = this.outBuffer.indexOf('\n');
while (eolIndex >= 0) {
// line includes the EOL
const line = this.outBuffer.slice(0, eolIndex + 1);
cb(line);
this.outBuffer = this.outBuffer.slice(eolIndex + 1);
eolIndex = this.outBuffer.indexOf('\n');
}
}
}

View file

@ -1,7 +1,67 @@
import * as vscode from 'vscode'; import * as vscode from 'vscode';
import * as lc from 'vscode-languageclient'; import * as lc from 'vscode-languageclient';
import { Server } from '../server'; import { Ctx, Cmd } from '../ctx';
export function run(ctx: Ctx): Cmd {
let prevRunnable: RunnableQuickPick | undefined;
return async () => {
const editor = ctx.activeRustEditor;
if (!editor) return;
const textDocument: lc.TextDocumentIdentifier = {
uri: editor.document.uri.toString(),
};
const params: RunnablesParams = {
textDocument,
position: ctx.client.code2ProtocolConverter.asPosition(
editor.selection.active,
),
};
const runnables = await ctx.client.sendRequest<Runnable[]>(
'rust-analyzer/runnables',
params,
);
const items: RunnableQuickPick[] = [];
if (prevRunnable) {
items.push(prevRunnable);
}
for (const r of runnables) {
if (
prevRunnable &&
JSON.stringify(prevRunnable.runnable) === JSON.stringify(r)
) {
continue;
}
items.push(new RunnableQuickPick(r));
}
const item = await vscode.window.showQuickPick(items);
if (!item) return;
item.detail = 'rerun';
prevRunnable = item;
const task = createTask(item.runnable);
return await vscode.tasks.executeTask(task);
};
}
export function runSingle(ctx: Ctx): Cmd {
return async (runnable: Runnable) => {
const editor = ctx.activeRustEditor;
if (!editor) return;
const task = createTask(runnable);
task.group = vscode.TaskGroup.Build;
task.presentationOptions = {
reveal: vscode.TaskRevealKind.Always,
panel: vscode.TaskPanelKind.Dedicated,
clear: true,
};
return vscode.tasks.executeTask(task);
};
}
interface RunnablesParams { interface RunnablesParams {
textDocument: lc.TextDocumentIdentifier; textDocument: lc.TextDocumentIdentifier;
@ -67,63 +127,3 @@ function createTask(spec: Runnable): vscode.Task {
t.presentationOptions.clear = true; t.presentationOptions.clear = true;
return t; return t;
} }
let prevRunnable: RunnableQuickPick | undefined;
export async function handle(): Promise<vscode.TaskExecution | undefined> {
const editor = vscode.window.activeTextEditor;
if (editor == null || editor.document.languageId !== 'rust') {
return;
}
const textDocument: lc.TextDocumentIdentifier = {
uri: editor.document.uri.toString(),
};
const params: RunnablesParams = {
textDocument,
position: Server.client.code2ProtocolConverter.asPosition(
editor.selection.active,
),
};
const runnables = await Server.client.sendRequest<Runnable[]>(
'rust-analyzer/runnables',
params,
);
const items: RunnableQuickPick[] = [];
if (prevRunnable) {
items.push(prevRunnable);
}
for (const r of runnables) {
if (
prevRunnable &&
JSON.stringify(prevRunnable.runnable) === JSON.stringify(r)
) {
continue;
}
items.push(new RunnableQuickPick(r));
}
const item = await vscode.window.showQuickPick(items);
if (!item) {
return;
}
item.detail = 'rerun';
prevRunnable = item;
const task = createTask(item.runnable);
return await vscode.tasks.executeTask(task);
}
export async function handleSingle(runnable: Runnable) {
const editor = vscode.window.activeTextEditor;
if (editor == null || editor.document.languageId !== 'rust') {
return;
}
const task = createTask(runnable);
task.group = vscode.TaskGroup.Build;
task.presentationOptions = {
reveal: vscode.TaskRevealKind.Always,
panel: vscode.TaskPanelKind.Dedicated,
clear: true,
};
return vscode.tasks.executeTask(task);
}

View file

@ -1,14 +1,49 @@
import * as vscode from 'vscode'; import * as vscode from 'vscode';
import { Range, TextDocumentChangeEvent, TextEditor } from 'vscode'; import * as lc from 'vscode-languageclient';
import { TextDocumentIdentifier } from 'vscode-languageclient'; import { Server } from './server';
import { Server } from '../server'; import { Ctx } from './ctx';
export function activateInlayHints(ctx: Ctx) {
const hintsUpdater = new HintsUpdater();
hintsUpdater.refreshHintsForVisibleEditors().then(() => {
// vscode may ignore top level hintsUpdater.refreshHintsForVisibleEditors()
// so update the hints once when the focus changes to guarantee their presence
let editorChangeDisposable: vscode.Disposable | null = null;
editorChangeDisposable = vscode.window.onDidChangeActiveTextEditor(
_ => {
if (editorChangeDisposable !== null) {
editorChangeDisposable.dispose();
}
return hintsUpdater.refreshHintsForVisibleEditors();
},
);
ctx.pushCleanup(
vscode.window.onDidChangeVisibleTextEditors(_ =>
hintsUpdater.refreshHintsForVisibleEditors(),
),
);
ctx.pushCleanup(
vscode.workspace.onDidChangeTextDocument(e =>
hintsUpdater.refreshHintsForVisibleEditors(e),
),
);
ctx.pushCleanup(
vscode.workspace.onDidChangeConfiguration(_ =>
hintsUpdater.toggleHintsDisplay(
Server.config.displayInlayHints,
),
),
);
});
}
interface InlayHintsParams { interface InlayHintsParams {
textDocument: TextDocumentIdentifier; textDocument: lc.TextDocumentIdentifier;
} }
interface InlayHint { interface InlayHint {
range: Range; range: vscode.Range;
kind: string; kind: string;
label: string; label: string;
} }
@ -19,7 +54,7 @@ const typeHintDecorationType = vscode.window.createTextEditorDecorationType({
}, },
}); });
export class HintsUpdater { class HintsUpdater {
private displayHints = true; private displayHints = true;
public async toggleHintsDisplay(displayHints: boolean): Promise<void> { public async toggleHintsDisplay(displayHints: boolean): Promise<void> {
@ -32,11 +67,10 @@ export class HintsUpdater {
} }
public async refreshHintsForVisibleEditors( public async refreshHintsForVisibleEditors(
cause?: TextDocumentChangeEvent, cause?: vscode.TextDocumentChangeEvent,
): Promise<void> { ): Promise<void> {
if (!this.displayHints) { if (!this.displayHints) return;
return;
}
if ( if (
cause !== undefined && cause !== undefined &&
(cause.contentChanges.length === 0 || (cause.contentChanges.length === 0 ||
@ -79,7 +113,7 @@ export class HintsUpdater {
} }
private async updateDecorationsFromServer( private async updateDecorationsFromServer(
editor: TextEditor, editor: vscode.TextEditor,
): Promise<void> { ): Promise<void> {
const newHints = await this.queryHints(editor.document.uri.toString()); const newHints = await this.queryHints(editor.document.uri.toString());
if (newHints !== null) { if (newHints !== null) {

View file

@ -2,8 +2,8 @@ import * as vscode from 'vscode';
import * as lc from 'vscode-languageclient'; import * as lc from 'vscode-languageclient';
import * as commands from './commands'; import * as commands from './commands';
import { HintsUpdater } from './commands/inlay_hints'; import { activateInlayHints } from './inlay_hints';
import { StatusDisplay } from './commands/watch_status'; import { StatusDisplay } from './status_display';
import * as events from './events'; import * as events from './events';
import * as notifications from './notifications'; import * as notifications from './notifications';
import { Server } from './server'; import { Server } from './server';
@ -13,6 +13,8 @@ let ctx!: Ctx;
export async function activate(context: vscode.ExtensionContext) { export async function activate(context: vscode.ExtensionContext) {
ctx = new Ctx(context); ctx = new Ctx(context);
// Commands which invokes manually via command pallet, shortcut, etc.
ctx.registerCommand('analyzerStatus', commands.analyzerStatus); ctx.registerCommand('analyzerStatus', commands.analyzerStatus);
ctx.registerCommand('collectGarbage', commands.collectGarbage); ctx.registerCommand('collectGarbage', commands.collectGarbage);
ctx.registerCommand('matchingBrace', commands.matchingBrace); ctx.registerCommand('matchingBrace', commands.matchingBrace);
@ -20,30 +22,11 @@ export async function activate(context: vscode.ExtensionContext) {
ctx.registerCommand('parentModule', commands.parentModule); ctx.registerCommand('parentModule', commands.parentModule);
ctx.registerCommand('syntaxTree', commands.syntaxTree); ctx.registerCommand('syntaxTree', commands.syntaxTree);
ctx.registerCommand('expandMacro', commands.expandMacro); ctx.registerCommand('expandMacro', commands.expandMacro);
ctx.registerCommand('run', commands.run);
function disposeOnDeactivation(disposable: vscode.Disposable) { // Internal commands which are invoked by the server.
context.subscriptions.push(disposable); ctx.registerCommand('runSingle', commands.runSingle);
} ctx.registerCommand('showReferences', commands.showReferences);
function registerCommand(name: string, f: any) {
disposeOnDeactivation(vscode.commands.registerCommand(name, f));
}
// Commands are requests from vscode to the language server
registerCommand('rust-analyzer.run', commands.runnables.handle);
// Unlike the above this does not send requests to the language server
registerCommand('rust-analyzer.runSingle', commands.runnables.handleSingle);
registerCommand(
'rust-analyzer.showReferences',
(uri: string, position: lc.Position, locations: lc.Location[]) => {
vscode.commands.executeCommand(
'editor.action.showReferences',
vscode.Uri.parse(uri),
Server.client.protocol2CodeConverter.asPosition(position),
locations.map(Server.client.protocol2CodeConverter.asLocation),
);
},
);
if (Server.config.enableEnhancedTyping) { if (Server.config.enableEnhancedTyping) {
ctx.overrideCommand('type', commands.onEnter); ctx.overrideCommand('type', commands.onEnter);
@ -52,7 +35,7 @@ export async function activate(context: vscode.ExtensionContext) {
const watchStatus = new StatusDisplay( const watchStatus = new StatusDisplay(
Server.config.cargoWatchOptions.command, Server.config.cargoWatchOptions.command,
); );
disposeOnDeactivation(watchStatus); ctx.pushCleanup(watchStatus);
// Notifications are events triggered by the language server // Notifications are events triggered by the language server
const allNotifications: [string, lc.GenericNotificationHandler][] = [ const allNotifications: [string, lc.GenericNotificationHandler][] = [
@ -84,38 +67,7 @@ export async function activate(context: vscode.ExtensionContext) {
} }
if (Server.config.displayInlayHints) { if (Server.config.displayInlayHints) {
const hintsUpdater = new HintsUpdater(); activateInlayHints(ctx);
hintsUpdater.refreshHintsForVisibleEditors().then(() => {
// vscode may ignore top level hintsUpdater.refreshHintsForVisibleEditors()
// so update the hints once when the focus changes to guarantee their presence
let editorChangeDisposable: vscode.Disposable | null = null;
editorChangeDisposable = vscode.window.onDidChangeActiveTextEditor(
_ => {
if (editorChangeDisposable !== null) {
editorChangeDisposable.dispose();
}
return hintsUpdater.refreshHintsForVisibleEditors();
},
);
disposeOnDeactivation(
vscode.window.onDidChangeVisibleTextEditors(_ =>
hintsUpdater.refreshHintsForVisibleEditors(),
),
);
disposeOnDeactivation(
vscode.workspace.onDidChangeTextDocument(e =>
hintsUpdater.refreshHintsForVisibleEditors(e),
),
);
disposeOnDeactivation(
vscode.workspace.onDidChangeConfiguration(_ =>
hintsUpdater.toggleHintsDisplay(
Server.config.displayInlayHints,
),
),
);
});
} }
} }

View file

@ -3,7 +3,7 @@ import * as vscode from 'vscode';
const spinnerFrames = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏']; const spinnerFrames = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
export class StatusDisplay implements vscode.Disposable { export class StatusDisplay implements vscode.Disposable {
public packageName?: string; packageName?: string;
private i = 0; private i = 0;
private statusBarItem: vscode.StatusBarItem; private statusBarItem: vscode.StatusBarItem;
@ -19,7 +19,7 @@ export class StatusDisplay implements vscode.Disposable {
this.statusBarItem.hide(); this.statusBarItem.hide();
} }
public show() { show() {
this.packageName = undefined; this.packageName = undefined;
this.timer = this.timer =
@ -39,7 +39,7 @@ export class StatusDisplay implements vscode.Disposable {
this.statusBarItem.show(); this.statusBarItem.show();
} }
public hide() { hide() {
if (this.timer) { if (this.timer) {
clearInterval(this.timer); clearInterval(this.timer);
this.timer = undefined; this.timer = undefined;
@ -48,7 +48,7 @@ export class StatusDisplay implements vscode.Disposable {
this.statusBarItem.hide(); this.statusBarItem.hide();
} }
public dispose() { dispose() {
if (this.timer) { if (this.timer) {
clearInterval(this.timer); clearInterval(this.timer);
this.timer = undefined; this.timer = undefined;
@ -57,7 +57,7 @@ export class StatusDisplay implements vscode.Disposable {
this.statusBarItem.dispose(); this.statusBarItem.dispose();
} }
public handleProgressNotification(params: ProgressParams) { handleProgressNotification(params: ProgressParams) {
const { token, value } = params; const { token, value } = params;
if (token !== 'rustAnalyzer/cargoWatcher') { if (token !== 'rustAnalyzer/cargoWatcher') {
return; return;