Skip to content

Commit

Permalink
Merge pull request #288 from bdm-k/issue-275
Browse files Browse the repository at this point in the history
Improve tooltip position to avoid hiding the line math section
  • Loading branch information
artisticat1 committed Apr 27, 2024
2 parents aad8adf + 30aabb9 commit 28e5a04
Show file tree
Hide file tree
Showing 2 changed files with 99 additions and 32 deletions.
125 changes: 94 additions & 31 deletions src/editor_extensions/math_tooltip.ts
Original file line number Diff line number Diff line change
@@ -1,58 +1,121 @@
import { Tooltip, showTooltip, EditorView } from "@codemirror/view";
import { StateField, EditorState } from "@codemirror/state";
import { Tooltip, showTooltip, EditorView, ViewUpdate } from "@codemirror/view";
import { StateField, EditorState, EditorSelection, StateEffect } from "@codemirror/state";
import { renderMath, finishRenderMath, editorLivePreviewField } from "obsidian";
import { Context } from "src/utils/context";
import { getLatexSuiteConfig } from "src/snippets/codemirror/config";

const updateTooltipEffect = StateEffect.define<Tooltip[]>();

export const cursorTooltipField = StateField.define<readonly Tooltip[]>({
create: getCursorTooltips,
create: () => [],

update(tooltips, tr) {
if (!tr.docChanged && !tr.selection) return tooltips;
return getCursorTooltips(tr.state);
for (const effect of tr.effects) {
if (effect.is(updateTooltipEffect)) return effect.value;
}

return tooltips;
},

provide: (f) => showTooltip.computeN([f], (state) => state.field(f)),
});

function getCursorTooltips(state: EditorState): readonly Tooltip[] {
const settings = getLatexSuiteConfig(state);
const ctx = Context.fromState(state);
// update the tooltip by dispatching an updateTooltipEffect
export function handleMathTooltip(update: ViewUpdate) {
const shouldUpdate = update.docChanged || update.selectionSet;
if (!shouldUpdate) return;

if (!ctx.mode.inMath()) {
return [];
const settings = getLatexSuiteConfig(update.state);
const ctx = Context.fromState(update.state);

if (!shouldShowTooltip(update.state, ctx)) {
const currTooltips = update.state.field(cursorTooltipField);
// a little optimization:
// If the tooltip is not currently shown and there is no need to show it,
// we don't dispatch an transaction.
if (currTooltips.length > 0) {
update.view.dispatch({
effects: [updateTooltipEffect.of([])],
});
}
return;
}

const isLivePreview = state.field(editorLivePreviewField);
if (ctx.mode.blockMath && isLivePreview) return [];
/*
* process when there is a need to show the tooltip: from here
*/

const bounds = ctx.getBounds();
if (!bounds) return [];
// HACK: eqnBounds is not null because shouldShowTooltip was true
const eqnBounds = ctx.getBounds();
const eqn = update.state.sliceDoc(eqnBounds.start, eqnBounds.end);

const eqn = state.sliceDoc(bounds.start, bounds.end);
const above = settings.mathPreviewPositionIsAbove;
const create = () => {
const dom = document.createElement("div");
dom.addClass("cm-tooltip-cursor");

// Don't render an empty equation
if (eqn.trim() === "") return [];
const renderedEqn = renderMath(eqn, ctx.mode.blockMath || ctx.mode.codeMath);
dom.appendChild(renderedEqn);
finishRenderMath();

return { dom };
};

let newTooltips: Tooltip[] = [];

if (ctx.mode.blockMath || ctx.mode.codeMath) {
newTooltips = [{
pos: above ? eqnBounds.start : eqnBounds.end,
above: above,
strictSide: true,
arrow: true,
create: create,
}];
} else if (ctx.mode.inlineMath && above) {
newTooltips = [{
pos: eqnBounds.start,
above: true,
strictSide: true,
arrow: true,
create: create,
}];
} else if (ctx.mode.inlineMath && !above) {
const endRange = EditorSelection.range(eqnBounds.end, eqnBounds.end);

return [
{
pos: (ctx.mode.inlineMath || settings.mathPreviewPositionIsAbove) ? bounds.start : bounds.end,
above: settings.mathPreviewPositionIsAbove,
newTooltips = [{
pos: Math.max(
eqnBounds.start,
// the beginning position of the visual line where eqnBounds.end is
// located
update.view.moveToLineBoundary(endRange, false).anchor,
),
above: false,
strictSide: true,
arrow: true,
create: () => {
const dom = document.createElement("div");
dom.className = "cm-tooltip-cursor";
create: create,
}];
}

const renderedEqn = renderMath(eqn, ctx.mode.blockMath || ctx.mode.codeMath);
dom.appendChild(renderedEqn);
finishRenderMath();
update.view.dispatch({
effects: [updateTooltipEffect.of(newTooltips)]
});
}

return { dom };
}
}
];
function shouldShowTooltip(state: EditorState, ctx: Context): boolean {
if (!ctx.mode.inMath()) return false;

const isLivePreview = state.field(editorLivePreviewField);
if (ctx.mode.blockMath && isLivePreview) return false;

// FIXME: eqnBounds can be null
const eqnBounds = ctx.getBounds();
if (!eqnBounds) return false;

// Don't render an empty equation
const eqn = state.sliceDoc(eqnBounds.start, eqnBounds.end).trim();
if (eqn === "") return false;

return true;
}

export const cursorTooltipBaseTheme = EditorView.baseTheme({
Expand Down
6 changes: 5 additions & 1 deletion src/latex_suite.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,14 @@ import { concealPlugin } from "./editor_extensions/conceal";

import { LatexSuiteCMSettings } from "./settings/settings";
import { colorPairedBracketsPluginLowestPrec, highlightCursorBracketsPlugin } from "./editor_extensions/highlight_brackets";
import { cursorTooltipBaseTheme, cursorTooltipField } from "./editor_extensions/math_tooltip";
import { cursorTooltipBaseTheme, cursorTooltipField, handleMathTooltip } from "./editor_extensions/math_tooltip";
import { isComposing } from "./utils/editor_utils";

export const handleUpdate = (update: ViewUpdate) => {
// The math tooltip handler is driven by view updates because it utilizes
// information about visual line, which is not available in EditorState
handleMathTooltip(update);

const cursorTriggeredByChange = update.state.field(cursorTriggerStateField, false);

// Remove all tabstops when the user manually moves the cursor (e.g. on mouse click; using arrow keys)
Expand Down

0 comments on commit 28e5a04

Please sign in to comment.