blob: f2f5abaf24ced09faa26dc7cf6a8e42923f396b6 [file] [edit]
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @format
*/
import * as vscode from 'vscode';
import {
HoverParams,
HoverRequest,
LanguageClient,
} from 'vscode-languageclient/node';
type HoverWithVerbosity = HoverParams & {
verbosityLevel: number;
};
/** Register the VS Code-specific hover provider that supports +/- controls. */
export function registerHoverProvider(
context: vscode.ExtensionContext,
getClient: () => LanguageClient,
): void {
// Use a WeakMap so overlapping async hover requests don't race on a single
// mutable slot. Each VerboseHover instance maps to its verbosity level, so
// the lookup for `previousHover` is stable even when requests interleave.
const hoverLevels = new WeakMap<vscode.Hover, number>();
context.subscriptions.push(
vscode.languages.registerHoverProvider(
[
{scheme: 'file', language: 'python'},
{scheme: 'untitled', language: 'python'},
{scheme: 'vscode-notebook-cell', language: 'python'},
{scheme: 'inmemory', language: 'python'},
],
({
async provideHover(
document: vscode.TextDocument,
position: vscode.Position,
token: vscode.CancellationToken,
hoverContext?: vscode.HoverContext,
) {
const client = getClient();
const previousLevel =
hoverContext?.previousHover != null
? hoverLevels.get(hoverContext.previousHover) ?? 0
: 0;
// The server distinguishes only two states (compact vs. expanded),
// so clamp to [0, 1]; otherwise repeated "+" clicks accumulate a
// counter that then takes as many "-" clicks to collapse.
const verbosityLevel = Math.min(
1,
Math.max(0, previousLevel + (hoverContext?.verbosityDelta ?? 0)),
);
const params = {
...client.code2ProtocolConverter.asTextDocumentPositionParams(
document,
position,
),
verbosityLevel,
} as HoverWithVerbosity;
const result = await client.sendRequest(
HoverRequest.type,
params,
token,
);
if (!result || token.isCancellationRequested) {
return undefined;
}
const canIncreaseVerbosity =
(result as typeof result & {canIncreaseVerbosity?: boolean})
.canIncreaseVerbosity ?? false;
const hover = client.protocol2CodeConverter.asHover(result);
if (!hover) {
return undefined;
}
const verboseHover = new vscode.VerboseHover(
hover.contents,
hover.range,
canIncreaseVerbosity,
verbosityLevel > 0,
);
hoverLevels.set(verboseHover, verbosityLevel);
return verboseHover;
},
} as unknown as vscode.HoverProvider),
),
);
}