Skip to content

Commit 76030eb

Browse files
committed
Auto merge of rust-lang#12277 - listochkin:show-implementations-display-error, r=listochkin
"Show implementations" link display error fix While VSCode [uses it's own implementation for URIs](https://github.com/microsoft/vscode-uri) which notably doesn't have any limits of URI size, the renderer itself relies on Web platform engine, that limits the length of the URLs and bails out when the attribute length of an `href` inside `a` tag is too long. Command URIs have a form of `command:command-name?arguments`, where `arguments` is a percent-encoded array of data we want to pass along to the command function. For "Show References" this is a list of all file URIs with locations of every reference, and it can get quite long. This PR introduces another intermediary `linkToCommand` command. When we render a command link, a reference to a command with all its arguments is stored in a map, and instead a `linkToCommand` link is rendered with the key to that map. For now the map is cleaned up periodically (I've set it to every 10 minutes). In general case we'll probably need to introduce TTLs or flags to denote ephemeral links (like these in hover popups) and persistent links and clean those separately. But for now simply keeping the last few links in the map should be good enough. Likewise, we could add code to remove a target command from the map after the link is clicked, but assuming most links in hover sheets won't be clicked anyway this code won't change the overall memory use much. Closes rust-lang#9926
2 parents 187bd7d + e87e1bc commit 76030eb

File tree

3 files changed

+48
-3
lines changed

3 files changed

+48
-3
lines changed

editors/code/src/client.ts

+35-3
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,46 @@ import { WorkspaceEdit } from "vscode";
77
import { Workspace } from "./ctx";
88
import { updateConfig } from "./config";
99
import { substituteVariablesInEnv } from "./config";
10+
import { randomUUID } from "crypto";
1011

1112
export interface Env {
1213
[name: string]: string;
1314
}
1415

15-
function renderCommand(cmd: ra.CommandLink) {
16-
return `[${cmd.title}](command:${cmd.command}?${encodeURIComponent(
17-
JSON.stringify(cmd.arguments)
16+
// Command URIs have a form of command:command-name?arguments, where
17+
// arguments is a percent-encoded array of data we want to pass along to
18+
// the command function. For "Show References" this is a list of all file
19+
// URIs with locations of every reference, and it can get quite long.
20+
//
21+
// To work around it we use an intermediary linkToCommand command. When
22+
// we render a command link, a reference to a command with all its arguments
23+
// is stored in a map, and instead a linkToCommand link is rendered
24+
// with the key to that map.
25+
export const LINKED_COMMANDS = new Map<string, ra.CommandLink>();
26+
27+
// For now the map is cleaned up periodically (I've set it to every
28+
// 10 minutes). In general case we'll probably need to introduce TTLs or
29+
// flags to denote ephemeral links (like these in hover popups) and
30+
// persistent links and clean those separately. But for now simply keeping
31+
// the last few links in the map should be good enough. Likewise, we could
32+
// add code to remove a target command from the map after the link is
33+
// clicked, but assuming most links in hover sheets won't be clicked anyway
34+
// this code won't change the overall memory use much.
35+
setInterval(function cleanupOlderCommandLinks() {
36+
// keys are returned in insertion order, we'll keep a few
37+
// of recent keys available, and clean the rest
38+
const keys = [...LINKED_COMMANDS.keys()];
39+
const keysToRemove = keys.slice(0, keys.length - 10);
40+
for (const key of keysToRemove) {
41+
LINKED_COMMANDS.delete(key);
42+
}
43+
}, 10 * 60 * 1000);
44+
45+
function renderCommand(cmd: ra.CommandLink): string {
46+
const commandId = randomUUID();
47+
LINKED_COMMANDS.set(commandId, cmd);
48+
return `[${cmd.title}](command:rust-analyzer.linkToCommand?${encodeURIComponent(
49+
JSON.stringify([commandId])
1850
)} '${cmd.tooltip}')`;
1951
}
2052

editors/code/src/commands.ts

+11
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import { AstInspector } from "./ast_inspector";
1111
import { isRustDocument, isCargoTomlDocument, sleep, isRustEditor } from "./util";
1212
import { startDebugSession, makeDebugConfig } from "./debug";
1313
import { LanguageClient } from "vscode-languageclient/node";
14+
import { LINKED_COMMANDS } from "./client";
1415

1516
export * from "./ast_inspector";
1617
export * from "./run";
@@ -928,3 +929,13 @@ export function newDebugConfig(ctx: Ctx): Cmd {
928929
await makeDebugConfig(ctx, item.runnable);
929930
};
930931
}
932+
933+
export function linkToCommand(ctx: Ctx): Cmd {
934+
return async (commandId: string) => {
935+
const link = LINKED_COMMANDS.get(commandId);
936+
if (ctx.client && link) {
937+
const { command, arguments: args = [] } = link;
938+
await vscode.commands.executeCommand(command, ...args);
939+
}
940+
};
941+
}

editors/code/src/main.ts

+2
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,8 @@ async function initCommonContext(context: vscode.ExtensionContext, ctx: Ctx) {
160160
ctx.registerCommand("resolveCodeAction", commands.resolveCodeAction);
161161
ctx.registerCommand("applyActionGroup", commands.applyActionGroup);
162162
ctx.registerCommand("gotoLocation", commands.gotoLocation);
163+
164+
ctx.registerCommand("linkToCommand", commands.linkToCommand);
163165
}
164166

165167
export async function deactivate() {

0 commit comments

Comments
 (0)