mirror of
https://github.com/l0ng-ai/openidea.git
synced 2026-11-08 08:52:12 +00:00
Find-in-file matches showed yellow background with the editor's green syntax text underneath, making hits unreadable. Give .cm-openidea-findMatch and .cm-openidea-findMatch-current the per-theme --fg-search-match color, matching how .match rules elsewhere already set it. Fixes #1733
375 lines
15 KiB
TypeScript
375 lines
15 KiB
TypeScript
/**
|
||
* Editor look & feel after PyCharm's new UI (Islands): white editor island,
|
||
* right-aligned muted line numbers (caret row darker), a hairline between the
|
||
* gutter and the text, thin VCS bars, fold chevrons on gutter hover, caret
|
||
* row tint across gutter and text. Colours only via tokens; editor-specific
|
||
* ones are read through `--editor-*` locals with fallbacks to the base tokens.
|
||
*/
|
||
import { EditorView } from "@codemirror/view";
|
||
import { BRACKET_COLORS } from "./rainbowBrackets";
|
||
|
||
/** The `--editor-*` locals of `findStyles`, for the `&` rule. */
|
||
export const findLocals = {
|
||
"--editor-search": "var(--bg-search-match, color-mix(in srgb, var(--warning) 30%, transparent))",
|
||
"--editor-search-current": "var(--bg-search-match-current, color-mix(in srgb, var(--warning) 48%, transparent))",
|
||
};
|
||
|
||
/** Find in file highlights (cm/find.ts), also for viewers with their own theme (diff). */
|
||
export const findStyles = {
|
||
".cm-openidea-findMatch": {
|
||
backgroundColor: "var(--editor-search)",
|
||
color: "var(--fg-search-match, inherit)",
|
||
borderRadius: "2px",
|
||
},
|
||
".cm-openidea-findExcluded": {
|
||
textDecoration: "line-through",
|
||
textDecorationColor: "var(--fg-muted)",
|
||
},
|
||
".cm-openidea-findScope": {
|
||
backgroundColor: "var(--editor-selection-inactive, var(--bg-editor-selection-inactive))",
|
||
},
|
||
".cm-openidea-findMatch-current": {
|
||
backgroundColor: "var(--editor-search-current)",
|
||
color: "var(--fg-search-match, inherit)",
|
||
outline: "1px solid color-mix(in srgb, var(--editor-search-current) 60%, var(--fg-strong))",
|
||
},
|
||
};
|
||
|
||
/** The caret and Settings › Editor › General › Appearance caret options (`caretExtension`), also for viewers. */
|
||
export const caretStyles = {
|
||
".cm-cursor, .cm-dropCursor": {
|
||
borderLeft: "2px solid var(--caret, var(--fg-strong))",
|
||
marginLeft: "-1px",
|
||
},
|
||
// Use block caret (Settings › Editor › General › Appearance)
|
||
"&.cm-openidea-blockCaret .cm-cursor": {
|
||
borderLeft: "none",
|
||
width: "1ch",
|
||
marginLeft: "0",
|
||
background: "color-mix(in srgb, var(--caret, var(--fg-strong)) 55%, transparent)",
|
||
},
|
||
// Use smooth caret blinking: fade through CodeMirror's blink keyframes instead of stepping; rounded caret.
|
||
"&.cm-openidea-smoothCaretBlink.cm-focused > .cm-scroller > .cm-cursorLayer": {
|
||
animationTimingFunction: "cubic-bezier(0.7, 0, 0.3, 1)",
|
||
},
|
||
"&.cm-openidea-smoothCaretBlink .cm-cursor": { borderRadius: "1px" },
|
||
"&.cm-openidea-smoothCaretBlink.cm-openidea-blockCaret .cm-cursor": { borderRadius: "2px" },
|
||
// Use smooth caret movement: Snappy lands at once and settles, Gliding travels visibly.
|
||
"&.cm-openidea-caretMotion-snappy": { "--openidea-caret-motion": "110ms cubic-bezier(0.16, 1, 0.3, 1)" },
|
||
"&.cm-openidea-caretMotion-gliding": { "--openidea-caret-motion": "180ms cubic-bezier(0.45, 0, 0.25, 1)" },
|
||
".cm-cursorLayer.cm-openidea-caretGlide > .cm-cursor": {
|
||
transition:
|
||
"left var(--openidea-caret-motion), top var(--openidea-caret-motion), width var(--openidea-caret-motion), height var(--openidea-caret-motion)",
|
||
},
|
||
"@media (prefers-reduced-motion: reduce)": {
|
||
".cm-cursorLayer.cm-openidea-caretGlide > .cm-cursor": { transition: "none" },
|
||
},
|
||
};
|
||
|
||
/** The error stripe and scrollbar lane (cm/lanes.ts), also for the VCS viewers. */
|
||
export const overviewStyles = {
|
||
".cm-openidea-overview": {
|
||
position: "absolute",
|
||
top: "0",
|
||
right: "0",
|
||
bottom: "0",
|
||
width: "12px",
|
||
cursor: "default",
|
||
zIndex: "3",
|
||
},
|
||
// Over an outer scroller (an aligned diff): stays at the top of its viewport, taking no room.
|
||
".cm-openidea-overview-sticky": {
|
||
position: "sticky",
|
||
bottom: "auto",
|
||
flex: "none",
|
||
alignSelf: "flex-end",
|
||
},
|
||
".cm-openidea-overviewMark": {
|
||
position: "absolute",
|
||
left: "3px",
|
||
width: "6px",
|
||
minHeight: "3px",
|
||
borderRadius: "1px",
|
||
pointerEvents: "auto",
|
||
cursor: "pointer",
|
||
},
|
||
".cm-openidea-overviewMark-thin": { left: "4px", width: "4px", minHeight: "2px" },
|
||
".cm-openidea-overviewMark-full": { left: "0", width: "100%", minHeight: "2px", borderRadius: "0" },
|
||
// The scrollbar thumb wins over the marks under it (cm/lanes.ts, #752).
|
||
".cm-openidea-overview-onThumb .cm-openidea-overviewMark": { pointerEvents: "none" },
|
||
// The lane owns the scrollbar and its thumb (cm/lanes.ts): the native one is hidden so a press
|
||
// on the scrollbar can never reach the text layer and start a text selection.
|
||
"&.cm-openidea-withOverview .cm-scroller::-webkit-scrollbar:vertical": { width: "0" },
|
||
".cm-openidea-overviewThumb": {
|
||
position: "absolute",
|
||
right: "2px",
|
||
width: "6px",
|
||
borderRadius: "3px",
|
||
background: "var(--scrollbar-thumb)",
|
||
pointerEvents: "auto",
|
||
cursor: "default",
|
||
},
|
||
".cm-openidea-overview-onThumb .cm-openidea-overviewThumb": { background: "var(--scrollbar-thumb-hover)" },
|
||
".cm-openidea-overviewCaret": {
|
||
position: "absolute",
|
||
left: "2px",
|
||
right: "2px",
|
||
height: "2px",
|
||
pointerEvents: "none",
|
||
background: "color-mix(in srgb, var(--fg) 30%, transparent)",
|
||
},
|
||
};
|
||
|
||
export const openideaTheme = EditorView.theme({
|
||
"&": {
|
||
height: "100%",
|
||
backgroundColor: "var(--bg-editor, var(--bg-island, var(--bg-app)))",
|
||
color: "var(--fg-editor, var(--fg))",
|
||
"--editor-line-number": "var(--fg-line-number, var(--fg-gutter))",
|
||
"--editor-line-number-current": "var(--fg-line-number-current, var(--fg-muted))",
|
||
"--editor-gutter-border": "var(--border-gutter, var(--border))",
|
||
"--editor-caret-row": "var(--bg-current-line)",
|
||
"--editor-selection": "var(--bg-editor-selection, color-mix(in srgb, var(--accent) 24%, transparent))",
|
||
"--editor-selection-inactive":
|
||
"var(--bg-editor-selection-inactive, color-mix(in srgb, var(--accent) 14%, transparent))",
|
||
"--editor-indent-guide": "var(--border-indent-guide, var(--border))",
|
||
...findLocals,
|
||
"--editor-brace": "var(--bg-matched-brace, color-mix(in srgb, var(--accent) 22%, transparent))",
|
||
"--editor-fold-chip": "var(--bg-fold-placeholder, color-mix(in srgb, var(--fg-muted) 14%, transparent))",
|
||
},
|
||
"&.cm-focused": { outline: "none" },
|
||
".cm-scroller": {
|
||
// The scroll container keeps the arrow cursor so its scrollbar does not turn into the I-beam.
|
||
fontFamily: "var(--openidea-editor-font, var(--font-mono))",
|
||
fontWeight: "var(--fw-code, 400)",
|
||
fontSize: "var(--openidea-editor-font-size, var(--fs-code))",
|
||
lineHeight: "var(--openidea-editor-line-height, var(--lh-code))",
|
||
fontVariantLigatures: "none",
|
||
overscrollBehavior: "contain",
|
||
},
|
||
".cm-content": {
|
||
padding: "4px 0 8px 0",
|
||
caretColor: "var(--caret, var(--fg-strong))",
|
||
cursor: "text",
|
||
},
|
||
".cm-line": { padding: "0 16px 0 6px" },
|
||
...caretStyles,
|
||
"&.cm-openidea-ligatures .cm-scroller, &.cm-openidea-ligatures .cm-openidea-sticky": {
|
||
fontVariantLigatures: "normal",
|
||
},
|
||
"&.cm-openidea-methodSeparators .cm-openidea-methodSeparator": {
|
||
boxShadow: "inset 0 1px 0 var(--border-method-separator, var(--border))",
|
||
},
|
||
"&.cm-openidea-hideGutter-run .cm-openidea-gutter-run, &.cm-openidea-hideGutter-bookmarks .cm-openidea-bookmarks, &.cm-openidea-hideGutter-colors .cm-openidea-gutter-colors":
|
||
{
|
||
display: "none !important",
|
||
},
|
||
// Inheritance markers share one gutter: each group hides its own icons.
|
||
"&.cm-openidea-hideGutter-inheritors :is(.cm-openidea-lineMarker-implemented, .cm-openidea-lineMarker-overridden), &.cm-openidea-hideGutter-supers :is(.cm-openidea-lineMarker-implementing, .cm-openidea-lineMarker-overriding)":
|
||
{ display: "none" },
|
||
// Distraction Free Mode: no gutter; the text column (n chars + line padding) is centred.
|
||
"&.cm-openidea-distractionFree": { "--openidea-df-width": "calc(var(--openidea-df-columns, 80) * 1ch + 22px)" },
|
||
"&.cm-openidea-distractionFree .cm-gutters": { display: "none" },
|
||
"&.cm-openidea-distractionFree .cm-content": {
|
||
marginInlineStart: "max(0px, calc((100% - var(--openidea-df-width)) / 2))",
|
||
},
|
||
"&.cm-openidea-distractionFree .cm-content.cm-lineWrapping": { maxWidth: "var(--openidea-df-width)" },
|
||
".cm-fat-cursor": { background: "color-mix(in srgb, var(--caret, var(--fg-strong)) 45%, transparent)" },
|
||
".cm-selectionBackground": { background: "var(--editor-selection-inactive)" },
|
||
"&.cm-focused > .cm-scroller > .cm-selectionLayer .cm-selectionBackground": {
|
||
background: "var(--editor-selection)",
|
||
},
|
||
// Row backgrounds go on a pseudo-element under the selection layer: on the line itself they would hide it.
|
||
".cm-activeLine": { backgroundColor: "transparent", "--openidea-line-bg": "var(--editor-caret-row)" },
|
||
".cm-activeLine, .cm-openidea-lineBg": { position: "relative" },
|
||
".cm-activeLine::before, .cm-openidea-lineBg::before": {
|
||
content: '""',
|
||
position: "absolute",
|
||
inset: "0",
|
||
zIndex: "-1000",
|
||
background: "var(--openidea-line-bg)",
|
||
pointerEvents: "none",
|
||
},
|
||
".cm-selectionMatch": {
|
||
backgroundColor: "var(--bg-identifier-under-caret, color-mix(in srgb, var(--fg) 9%, transparent))",
|
||
},
|
||
// IDEA's MATCHED_BRACE_ATTRIBUTES (bold in dark schemes only) and MATCHED_TAG_NAME (never bold).
|
||
".cm-matchingBracket, &.cm-focused .cm-matchingBracket": {
|
||
backgroundColor: "var(--editor-brace)",
|
||
color: "inherit",
|
||
fontWeight: "var(--matched-brace-weight, var(--fw-code-bold, 700))",
|
||
borderRadius: "1px",
|
||
},
|
||
".cm-matchingTag, &.cm-focused .cm-matchingTag": {
|
||
backgroundColor: "var(--bg-matched-tag, var(--editor-brace))",
|
||
fontWeight: "inherit",
|
||
},
|
||
".cm-nonmatchingBracket, &.cm-focused .cm-nonmatchingBracket": {
|
||
color: "var(--danger)",
|
||
backgroundColor: "color-mix(in srgb, var(--danger) 14%, transparent)",
|
||
},
|
||
".cm-specialChar": { color: "var(--danger)" },
|
||
|
||
// Gutter: numbers | VCS bar | fold chevrons | hairline
|
||
".cm-gutters": {
|
||
cursor: "default",
|
||
backgroundColor: "var(--bg-editor-gutter, var(--bg-editor, var(--bg-island, var(--bg-app))))",
|
||
color: "var(--editor-line-number)",
|
||
border: "none",
|
||
borderRight: "1px solid var(--editor-gutter-border)",
|
||
WebkitUserSelect: "none",
|
||
userSelect: "none",
|
||
},
|
||
".cm-lineNumbers .cm-gutterElement": {
|
||
minWidth: "32px",
|
||
padding: "0 8px 0 10px",
|
||
textAlign: "right",
|
||
fontVariantNumeric: "tabular-nums",
|
||
},
|
||
".cm-activeLineGutter": { backgroundColor: "var(--editor-caret-row)" },
|
||
".cm-lineNumbers .cm-activeLineGutter": { color: "var(--editor-line-number-current)" },
|
||
".cm-openidea-lane": { width: "4px" },
|
||
".cm-openidea-lane .cm-gutterElement": { padding: "0", position: "relative" },
|
||
".cm-openidea-laneBar": {
|
||
position: "absolute",
|
||
top: "0",
|
||
bottom: "0",
|
||
left: "0",
|
||
width: "3px",
|
||
borderRadius: "0 1px 1px 0",
|
||
background: "var(--lane-color)",
|
||
},
|
||
".cm-openidea-laneDeleted": {
|
||
position: "absolute",
|
||
left: "0",
|
||
top: "-3px",
|
||
width: "0",
|
||
height: "0",
|
||
borderTop: "3px solid transparent",
|
||
borderBottom: "3px solid transparent",
|
||
borderLeft: "6px solid var(--lane-color)",
|
||
zIndex: "1",
|
||
},
|
||
".cm-foldGutter": { width: "16px" },
|
||
".cm-foldGutter .cm-gutterElement": {
|
||
display: "flex",
|
||
alignItems: "center",
|
||
justifyContent: "center",
|
||
color: "var(--fg-fold-marker, var(--fg-gutter))",
|
||
cursor: "pointer",
|
||
transition: "opacity var(--dur-fast) var(--ease)",
|
||
},
|
||
".cm-foldGutter .cm-openidea-foldOpen": { opacity: "0", display: "inline-flex" },
|
||
".cm-foldGutter .cm-openidea-foldClosed": { display: "inline-flex" },
|
||
".cm-gutters:hover .cm-foldGutter .cm-openidea-foldOpen": { opacity: "1" },
|
||
".cm-foldGutter .cm-gutterElement:hover": { color: "var(--fg-secondary)" },
|
||
".cm-foldPlaceholder": {
|
||
backgroundColor: "var(--editor-fold-chip)",
|
||
border: "none",
|
||
color: "var(--fg-muted)",
|
||
borderRadius: "var(--radius-small)",
|
||
padding: "0 5px",
|
||
margin: "0 2px",
|
||
cursor: "pointer",
|
||
},
|
||
".cm-foldPlaceholder:hover": { backgroundColor: "color-mix(in srgb, var(--fg-muted) 24%, transparent)" },
|
||
|
||
// Indent guides (cm/indentGuides.ts)
|
||
// Above the text so a selection, or a mark's background, never covers part of a guide.
|
||
".cm-openidea-guides": { position: "relative" },
|
||
".cm-openidea-guides::after": {
|
||
content: '""',
|
||
position: "absolute",
|
||
inset: "0",
|
||
pointerEvents: "none",
|
||
backgroundImage:
|
||
"repeating-linear-gradient(to right, var(--editor-indent-guide) 0 1px, transparent 1px calc(var(--openidea-guide-step) * 1ch))",
|
||
backgroundSize: "calc(var(--openidea-guide-levels) * var(--openidea-guide-step) * 1ch) 100%",
|
||
backgroundRepeat: "no-repeat",
|
||
backgroundPosition: "6px 0",
|
||
},
|
||
|
||
// Rainbow brackets (cm/rainbowBrackets.ts); `*` too: the syntax highlighter's span sits inside and would win.
|
||
...Object.fromEntries(
|
||
Array.from({ length: BRACKET_COLORS }, (_, i) => [
|
||
`.cm-rainbow-bracket-${i + 1}, .cm-rainbow-bracket-${i + 1} *`,
|
||
{
|
||
color: `var(--syn-bracket-${i + 1})`,
|
||
fontWeight: `var(--syn-bracket-${i + 1}-bold, inherit)`,
|
||
fontStyle: `var(--syn-bracket-${i + 1}-italic, inherit)`,
|
||
},
|
||
]),
|
||
),
|
||
".cm-rainbow-bracket-unmatched, .cm-rainbow-bracket-unmatched *": { color: "var(--syn-invalid)" },
|
||
|
||
// Whitespace rendering
|
||
".cm-highlightSpace": {
|
||
backgroundImage: "radial-gradient(circle at 50% 55%, var(--fg-whitespace, var(--fg-faint)) 11%, transparent 12%)",
|
||
backgroundPosition: "center",
|
||
},
|
||
".cm-highlightTab": { backgroundImage: "none", position: "relative" },
|
||
".cm-highlightTab::before": {
|
||
content: '"→"',
|
||
position: "absolute",
|
||
left: "0",
|
||
color: "var(--fg-whitespace, var(--fg-faint))",
|
||
pointerEvents: "none",
|
||
},
|
||
|
||
// Find in file (cm/find.ts)
|
||
...findStyles,
|
||
|
||
// 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)",
|
||
borderRadius: "var(--radius-popup)",
|
||
boxShadow: "var(--shadow-popup)",
|
||
},
|
||
".cm-tooltip .cm-tooltip-arrow:before": {
|
||
borderTopColor: "var(--border-popup)",
|
||
borderBottomColor: "var(--border-popup)",
|
||
},
|
||
".cm-tooltip .cm-tooltip-arrow:after": { borderTopColor: "var(--bg-popup)", borderBottomColor: "var(--bg-popup)" },
|
||
".cm-panels": { backgroundColor: "var(--bg-elev)", color: "var(--fg)" },
|
||
".cm-panels.cm-panels-top": { borderBottom: "1px solid var(--border)" },
|
||
".cm-panels.cm-panels-bottom": { borderTop: "1px solid var(--border)" },
|
||
|
||
...overviewStyles,
|
||
|
||
// Sticky lines (cm/stickyScroll.ts)
|
||
".cm-openidea-sticky": {
|
||
position: "absolute",
|
||
top: "0",
|
||
left: "0",
|
||
right: "12px",
|
||
zIndex: "4",
|
||
backgroundColor: "var(--bg-editor, var(--bg-island, var(--bg-app)))",
|
||
borderBottom: "1px solid var(--editor-gutter-border)",
|
||
boxShadow: "0 3px 6px -2px color-mix(in srgb, var(--fg-strong) 12%, transparent)",
|
||
fontFamily: "var(--openidea-editor-font, var(--font-mono))",
|
||
fontWeight: "var(--fw-code, 400)",
|
||
fontSize: "var(--openidea-editor-font-size, var(--fs-code))",
|
||
lineHeight: "var(--openidea-editor-line-height, var(--lh-code))",
|
||
overflow: "hidden",
|
||
cursor: "pointer",
|
||
},
|
||
".cm-openidea-stickyLine": { display: "flex", whiteSpace: "pre" },
|
||
".cm-openidea-stickyLine:hover": { backgroundColor: "var(--editor-caret-row)" },
|
||
".cm-openidea-stickyNumber": {
|
||
flex: "none",
|
||
textAlign: "right",
|
||
color: "var(--editor-line-number)",
|
||
boxSizing: "border-box",
|
||
paddingRight: "8px",
|
||
},
|
||
".cm-openidea-stickyText": { flex: "1", overflow: "hidden" },
|
||
|
||
// Soft-wrap: continuation rows indent slightly, like IDEA's wrap sign column.
|
||
"&.cm-lineWrapping .cm-line, .cm-lineWrapping .cm-line": { paddingRight: "24px" },
|
||
});
|