publish codemirrot helix package flow
Some checks failed
Publish codemirror-helix / publish (push) Failing after 2m16s
Some checks failed
Publish codemirror-helix / publish (push) Failing after 2m16s
This commit is contained in:
123
packages/codemirror-helix/src/view.ts
Normal file
123
packages/codemirror-helix/src/view.ts
Normal file
@@ -0,0 +1,123 @@
|
||||
import {
|
||||
Decoration,
|
||||
EditorView,
|
||||
ViewPlugin,
|
||||
WidgetType,
|
||||
showPanel,
|
||||
type DecorationSet,
|
||||
type Panel,
|
||||
type ViewUpdate,
|
||||
} from "@codemirror/view";
|
||||
import { getMode, helixState } from "./state";
|
||||
|
||||
class EolCursorWidget extends WidgetType {
|
||||
toDOM() {
|
||||
const span = document.createElement("span");
|
||||
span.className = "cm-helix-block cm-helix-block-eol";
|
||||
span.textContent = " ";
|
||||
return span;
|
||||
}
|
||||
ignoreEvent() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
const blockMark = Decoration.mark({ class: "cm-helix-block" });
|
||||
const selectionMark = Decoration.mark({ class: "cm-helix-selection" });
|
||||
const eolCursor = Decoration.widget({ widget: new EolCursorWidget(), side: 1 });
|
||||
|
||||
// Draws the selection ranges + a block cursor at each head while in
|
||||
// normal/select mode. We render the selection ourselves (rather than relying
|
||||
// on drawSelection, which the host theme can leave invisible) so multi-cursor
|
||||
// selections are always clearly highlighted.
|
||||
export const blockCursor = ViewPlugin.fromClass(
|
||||
class {
|
||||
decorations: DecorationSet;
|
||||
constructor(view: EditorView) {
|
||||
this.decorations = this.build(view);
|
||||
}
|
||||
update(update: ViewUpdate) {
|
||||
if (
|
||||
update.docChanged ||
|
||||
update.selectionSet ||
|
||||
update.viewportChanged ||
|
||||
update.startState.field(helixState).mode !== update.state.field(helixState).mode
|
||||
) {
|
||||
this.decorations = this.build(update.view);
|
||||
}
|
||||
}
|
||||
build(view: EditorView): DecorationSet {
|
||||
if (getMode(view.state) === "insert") return Decoration.none;
|
||||
const deco: ReturnType<typeof blockMark.range>[] = [];
|
||||
for (const range of view.state.selection.ranges) {
|
||||
if (!range.empty) deco.push(selectionMark.range(range.from, range.to));
|
||||
const pos = range.head;
|
||||
const line = view.state.doc.lineAt(pos);
|
||||
deco.push(pos < line.to ? blockMark.range(pos, pos + 1) : eolCursor.range(pos));
|
||||
}
|
||||
return Decoration.set(deco, true);
|
||||
}
|
||||
},
|
||||
{ decorations: (plugin) => plugin.decorations },
|
||||
);
|
||||
|
||||
export const modeEditorClass = EditorView.editorAttributes.compute([helixState], (state) => ({
|
||||
class: `cm-helix cm-helix-${getMode(state)}`,
|
||||
}));
|
||||
|
||||
function statusPanel(view: EditorView): Panel {
|
||||
const dom = document.createElement("div");
|
||||
dom.className = "cm-helix-status";
|
||||
const render = () => {
|
||||
const s = view.state.field(helixState);
|
||||
const label = s.mode === "insert" ? "INS" : s.mode === "select" ? "SEL" : "NOR";
|
||||
const count = s.count ? ` ${s.count}` : "";
|
||||
const reg = s.register ? ` "${s.register}` : "";
|
||||
const pend = s.pending ? ` ${s.pending.type}` : "";
|
||||
const sels = view.state.selection.ranges.length;
|
||||
const multi = sels > 1 ? ` ${sels} sels` : "";
|
||||
dom.textContent = `${label}${count}${reg}${pend}${multi}`;
|
||||
dom.dataset.mode = s.mode;
|
||||
};
|
||||
render();
|
||||
return {
|
||||
dom,
|
||||
update: (u) => {
|
||||
if (u.docChanged || u.selectionSet || u.transactions.length) render();
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export const helixStatusPanel = showPanel.of(statusPanel);
|
||||
|
||||
export const helixTheme = EditorView.baseTheme({
|
||||
"&.cm-helix-normal .cm-cursor, &.cm-helix-normal .cm-cursorLayer": { display: "none" },
|
||||
"&.cm-helix-select .cm-cursor, &.cm-helix-select .cm-cursorLayer": { display: "none" },
|
||||
".cm-helix-selection": { backgroundColor: "rgba(120,160,255,0.32)" },
|
||||
".cm-helix-block": { backgroundColor: "rgba(125,165,255,0.7)", borderRadius: "1px" },
|
||||
".cm-helix-block-eol": { display: "inline-block", width: "0.55em" },
|
||||
".cm-helix-status": {
|
||||
padding: "1px 10px",
|
||||
font: "11px ui-monospace, SFMono-Regular, Menlo, monospace",
|
||||
letterSpacing: "0.05em",
|
||||
},
|
||||
".cm-helix-status[data-mode=insert]": { color: "#16a34a" },
|
||||
".cm-helix-status[data-mode=select]": { color: "#d97706" },
|
||||
".cm-helix-status[data-mode=normal]": { color: "#2563eb" },
|
||||
".cm-helix-prompt": {
|
||||
display: "flex",
|
||||
gap: "0.5em",
|
||||
alignItems: "center",
|
||||
padding: "2px 10px",
|
||||
font: "12px ui-monospace, SFMono-Regular, Menlo, monospace",
|
||||
},
|
||||
".cm-helix-prompt label": { opacity: 0.7 },
|
||||
".cm-helix-prompt input": {
|
||||
flex: "1",
|
||||
border: "none",
|
||||
outline: "none",
|
||||
background: "transparent",
|
||||
color: "inherit",
|
||||
font: "inherit",
|
||||
},
|
||||
});
|
||||
Reference in New Issue
Block a user