fix(editor): keep tooltips full width at the editor's right edge (#1203)

On WebKit, CodeMirror positions tooltips absolutely inside the editor, but the tooltip space still reported the whole window as the right bound. CodeMirror never shifted a tooltip near the editor's right edge to the left, the box was squeezed into the space left of that edge, and the next measurement read the squeezed width, so it could end as a one-character column. The tooltip space is now the editor's own horizontal extent (clamped to the window) and tooltips size to their content, which fixes hover, inline blame, debugger values, signature help and hints together.

Co-authored-by: ntbang0901 <[email protected]>
Цей коміт міститься в:
ntbang0901 authored and GitHub committed 2026-11-04 23:10:51 +08:00
1 джерело bdc108a61b
коміт 8f77d7077c
3 files changed
+43 -8

No files matched your search

+16 -8
Переглянути файл
@@ -319,15 +319,23 @@ export interface SetupOptions {
}
/**
* Tooltips (hints, docs, completion) stay below the editor's top edge: one
* above the first lines flips below them instead of hiding under the tabs.
* Tooltips (hints, docs, completion) stay inside the editor horizontally and
* below its top edge. WebKit positions them `absolute` within `.cm-editor`, so
* a window-wide right bound makes CodeMirror skip the shift left and the
* browser squeezes the box into the space left of the editor's edge.
*/
const tooltipSpace = tooltips({
tooltipSpace: (view) => {
const top = view.scrollDOM.getBoundingClientRect().top;
return { top, left: 0, right: window.innerWidth, bottom: window.innerHeight };
},
});
export function tooltipBounds(view: { scrollDOM: Element; dom: Element }) {
const { top } = view.scrollDOM.getBoundingClientRect();
const { left, right } = view.dom.getBoundingClientRect();
return {
top,
left: Math.max(0, left),
right: Math.min(window.innerWidth, right),
bottom: window.innerHeight,
};
}
const tooltipSpace = tooltips({ tooltipSpace: tooltipBounds });
export function baseExtensions(o: SetupOptions): Extension {
return [
+3
Переглянути файл
@@ -248,6 +248,9 @@ export const openideaTheme = EditorView.theme({
// Overlays
".cm-tooltip": {
// Natural width, so a measurement taken while squeezed is not fed back (#1188).
width: "max-content",
maxWidth: "100%",
backgroundColor: "var(--bg-popup)",
color: "var(--fg)",
border: "1px solid var(--border-popup)",
+24
Переглянути файл
@@ -0,0 +1,24 @@
import { afterEach, beforeEach, describe, expect, it } from "vitest";
import { tooltipBounds } from "./setup";
const el = (r: Partial<DOMRect>) => ({ getBoundingClientRect: () => r as DOMRect }) as Element;
const original = window.innerWidth;
beforeEach(() => {
Object.defineProperty(window, "innerWidth", { value: 1200, configurable: true });
});
afterEach(() => {
Object.defineProperty(window, "innerWidth", { value: original, configurable: true });
});
describe("tooltipBounds", () => {
it.each([
["editor flush with the right edge", { left: 300, right: 1200 }, 300, 1200],
["narrow split pane", { left: 0, right: 400 }, 0, 400],
["editor in the middle", { left: 250, right: 750 }, 250, 750],
["editor spilling outside the window", { left: -20, right: 1500 }, 0, 1200],
])("%s", (_, rect, left, right) => {
const b = tooltipBounds({ scrollDOM: el({ top: 80 }), dom: el(rect) });
expect(b).toMatchObject({ top: 80, left, right });
});
});