From 86247bf657b3643cd81999c95f1cfb6c43c3f999 Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Mon, 5 Jun 2023 15:48:36 +0200 Subject: [PATCH] editor: Highlight search results Z-1292 --- assets/settings/default.json | 4 +- crates/editor/src/editor_settings.rs | 2 + crates/editor/src/element.rs | 47 ++- crates/theme/src/theme.rs | 1 + styles/src/styleTree/editor.ts | 507 ++++++++++++++------------- 5 files changed, 305 insertions(+), 256 deletions(-) diff --git a/assets/settings/default.json b/assets/settings/default.json index bd73bcbf08032946736159393e8385107f5873d1..8576c1ed6535303e187ae094a5ffa761fd80c4fd 100644 --- a/assets/settings/default.json +++ b/assets/settings/default.json @@ -71,7 +71,9 @@ // "never" "show": "auto", // Whether to show git diff indicators in the scrollbar. - "git_diff": true + "git_diff": true, + // Whether to show selections in the scrollbar. + "selections": true }, "project_panel": { // Whether to show the git status in the project panel. diff --git a/crates/editor/src/editor_settings.rs b/crates/editor/src/editor_settings.rs index 387d4d2c340d2a3bb5b648d8232880de2d8f7fe1..f4499b5651cc158c66df3faad7f0ecf707e01bb6 100644 --- a/crates/editor/src/editor_settings.rs +++ b/crates/editor/src/editor_settings.rs @@ -15,6 +15,7 @@ pub struct EditorSettings { pub struct Scrollbar { pub show: ShowScrollbar, pub git_diff: bool, + pub selections: bool, } #[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq)] @@ -39,6 +40,7 @@ pub struct EditorSettingsContent { pub struct ScrollbarContent { pub show: Option, pub git_diff: Option, + pub selections: Option, } impl Setting for EditorSettings { diff --git a/crates/editor/src/element.rs b/crates/editor/src/element.rs index d6f9a2e90682f72d33d06ee1b37d813e35764094..5f7843f721dc133cc1fb234a88bbcb1e8b7cb705 100644 --- a/crates/editor/src/element.rs +++ b/crates/editor/src/element.rs @@ -1008,6 +1008,7 @@ impl EditorElement { bounds: RectF, layout: &mut LayoutState, cx: &mut ViewContext, + editor: &Editor, ) { enum ScrollbarMouseHandlers {} if layout.mode != EditorMode::Full { @@ -1050,9 +1051,49 @@ impl EditorElement { background: style.track.background_color, ..Default::default() }); + let scrollbar_settings = settings::get::(cx).scrollbar; + let theme = theme::current(cx); + let scrollbar_theme = &theme.editor.scrollbar; + if layout.is_singleton && scrollbar_settings.selections { + let start_anchor = Anchor::min(); + let end_anchor = Anchor::max(); + for (row, _) in &editor.background_highlights_in_range( + start_anchor..end_anchor, + &layout.position_map.snapshot, + &theme, + ) { + let start_display = row.start; + let end_display = row.end; + let start_y = y_for_row(start_display.row() as f32); + let mut end_y = y_for_row((end_display.row()) as f32); + if end_y - start_y < 1. { + end_y = start_y + 1.; + } + let bounds = RectF::from_points(vec2f(left, start_y), vec2f(right, end_y)); + + let color = scrollbar_theme.selections; + + let border = Border { + width: 1., + color: style.thumb.border.color, + overlay: false, + top: false, + right: true, + bottom: false, + left: true, + }; + + scene.push_quad(Quad { + bounds, + background: Some(color), + border, + corner_radius: style.thumb.corner_radius, + }) + } + } - if layout.is_singleton && settings::get::(cx).scrollbar.git_diff { - let diff_style = theme::current(cx).editor.scrollbar.git.clone(); + if layout.is_singleton && scrollbar_settings.git_diff { + let diff_style = scrollbar_theme.git.clone(); for hunk in layout .position_map .snapshot @@ -2359,7 +2400,7 @@ impl Element for EditorElement { if !layout.blocks.is_empty() { self.paint_blocks(scene, bounds, visible_bounds, layout, editor, cx); } - self.paint_scrollbar(scene, bounds, layout, cx); + self.paint_scrollbar(scene, bounds, layout, cx, &editor); scene.pop_layer(); scene.pop_layer(); diff --git a/crates/theme/src/theme.rs b/crates/theme/src/theme.rs index c7563ec87a3dfd20f3f2682a2a6e9848cecf5279..95d6348c626aafc28a032716eede382b62e0019f 100644 --- a/crates/theme/src/theme.rs +++ b/crates/theme/src/theme.rs @@ -700,6 +700,7 @@ pub struct Scrollbar { pub width: f32, pub min_height_factor: f32, pub git: GitDiffColors, + pub selections: Color, } #[derive(Clone, Deserialize, Default)] diff --git a/styles/src/styleTree/editor.ts b/styles/src/styleTree/editor.ts index 55f3da6e900f79a0f02f936b73dde9c376043027..859f9fe1b9345df04c8d40821e76743e899c0ef1 100644 --- a/styles/src/styleTree/editor.ts +++ b/styles/src/styleTree/editor.ts @@ -6,273 +6,276 @@ import hoverPopover from "./hoverPopover" import { buildSyntax } from "../theme/syntax" export default function editor(colorScheme: ColorScheme) { - const { isLight } = colorScheme + const { isLight } = colorScheme - let layer = colorScheme.highest + let layer = colorScheme.highest - const autocompleteItem = { - cornerRadius: 6, - padding: { - bottom: 2, - left: 6, - right: 6, - top: 2, - }, - } + const autocompleteItem = { + cornerRadius: 6, + padding: { + bottom: 2, + left: 6, + right: 6, + top: 2, + }, + } - function diagnostic(layer: Layer, styleSet: StyleSets) { - return { - textScaleFactor: 0.857, - header: { - border: border(layer, { - top: true, - }), - }, - message: { - text: text(layer, "sans", styleSet, "default", { size: "sm" }), - highlightText: text(layer, "sans", styleSet, "default", { - size: "sm", - weight: "bold", - }), - }, - } + function diagnostic(layer: Layer, styleSet: StyleSets) { + return { + textScaleFactor: 0.857, + header: { + border: border(layer, { + top: true, + }), + }, + message: { + text: text(layer, "sans", styleSet, "default", { size: "sm" }), + highlightText: text(layer, "sans", styleSet, "default", { + size: "sm", + weight: "bold", + }), + }, } + } - const syntax = buildSyntax(colorScheme) + const syntax = buildSyntax(colorScheme) - return { - textColor: syntax.primary.color, - background: background(layer), - activeLineBackground: withOpacity(background(layer, "on"), 0.75), - highlightedLineBackground: background(layer, "on"), - // Inline autocomplete suggestions, Co-pilot suggestions, etc. - suggestion: syntax.predictive, - codeActions: { - indicator: { - color: foreground(layer, "variant"), + return { + textColor: syntax.primary.color, + background: background(layer), + activeLineBackground: withOpacity(background(layer, "on"), 0.75), + highlightedLineBackground: background(layer, "on"), + // Inline autocomplete suggestions, Co-pilot suggestions, etc. + suggestion: syntax.predictive, + codeActions: { + indicator: { + color: foreground(layer, "variant"), - clicked: { - color: foreground(layer, "base"), - }, - hover: { - color: foreground(layer, "on"), - }, - active: { - color: foreground(layer, "on"), - }, - }, - verticalScale: 0.55, + clicked: { + color: foreground(layer, "base"), }, - folds: { - iconMarginScale: 2.5, - foldedIcon: "icons/chevron_right_8.svg", - foldableIcon: "icons/chevron_down_8.svg", - indicator: { - color: foreground(layer, "variant"), - - clicked: { - color: foreground(layer, "base"), - }, - hover: { - color: foreground(layer, "on"), - }, - active: { - color: foreground(layer, "on"), - }, - }, - ellipses: { - textColor: colorScheme.ramps.neutral(0.71).hex(), - cornerRadiusFactor: 0.15, - background: { - // Copied from hover_popover highlight - color: colorScheme.ramps.neutral(0.5).alpha(0.0).hex(), - - hover: { - color: colorScheme.ramps.neutral(0.5).alpha(0.5).hex(), - }, - - clicked: { - color: colorScheme.ramps.neutral(0.5).alpha(0.7).hex(), - }, - }, - }, - foldBackground: foreground(layer, "variant"), + hover: { + color: foreground(layer, "on"), }, - diff: { - deleted: isLight - ? colorScheme.ramps.red(0.5).hex() - : colorScheme.ramps.red(0.4).hex(), - modified: isLight - ? colorScheme.ramps.yellow(0.5).hex() - : colorScheme.ramps.yellow(0.5).hex(), - inserted: isLight - ? colorScheme.ramps.green(0.4).hex() - : colorScheme.ramps.green(0.5).hex(), - removedWidthEm: 0.275, - widthEm: 0.15, - cornerRadius: 0.05, + active: { + color: foreground(layer, "on"), }, - /** Highlights matching occurrences of what is under the cursor - * as well as matched brackets - */ - documentHighlightReadBackground: withOpacity( - foreground(layer, "accent"), - 0.1 - ), - documentHighlightWriteBackground: colorScheme.ramps - .neutral(0.5) - .alpha(0.4) - .hex(), // TODO: This was blend * 2 - errorColor: background(layer, "negative"), - gutterBackground: background(layer), - gutterPaddingFactor: 3.5, - lineNumber: withOpacity(foreground(layer), 0.35), - lineNumberActive: foreground(layer), - renameFade: 0.6, - unnecessaryCodeFade: 0.5, - selection: colorScheme.players[0], - whitespace: colorScheme.ramps.neutral(0.5).hex(), - guestSelections: [ - colorScheme.players[1], - colorScheme.players[2], - colorScheme.players[3], - colorScheme.players[4], - colorScheme.players[5], - colorScheme.players[6], - colorScheme.players[7], - ], - autocomplete: { - background: background(colorScheme.middle), - cornerRadius: 8, - padding: 4, - margin: { - left: -14, - }, - border: border(colorScheme.middle), - shadow: colorScheme.popoverShadow, - matchHighlight: foreground(colorScheme.middle, "accent"), - item: autocompleteItem, - hoveredItem: { - ...autocompleteItem, - matchHighlight: foreground( - colorScheme.middle, - "accent", - "hovered" - ), - background: background(colorScheme.middle, "hovered"), - }, - selectedItem: { - ...autocompleteItem, - matchHighlight: foreground( - colorScheme.middle, - "accent", - "active" - ), - background: background(colorScheme.middle, "active"), - }, + }, + verticalScale: 0.55, + }, + folds: { + iconMarginScale: 2.5, + foldedIcon: "icons/chevron_right_8.svg", + foldableIcon: "icons/chevron_down_8.svg", + indicator: { + color: foreground(layer, "variant"), + + clicked: { + color: foreground(layer, "base"), }, - diagnosticHeader: { - background: background(colorScheme.middle), - iconWidthFactor: 1.5, - textScaleFactor: 0.857, - border: border(colorScheme.middle, { - bottom: true, - top: true, - }), - code: { - ...text(colorScheme.middle, "mono", { size: "sm" }), - margin: { - left: 10, - }, - }, - source: { - text: text(colorScheme.middle, "sans", { - size: "sm", - weight: "bold", - }), - }, - message: { - highlightText: text(colorScheme.middle, "sans", { - size: "sm", - weight: "bold", - }), - text: text(colorScheme.middle, "sans", { size: "sm" }), - }, + hover: { + color: foreground(layer, "on"), }, - diagnosticPathHeader: { - background: background(colorScheme.middle), - textScaleFactor: 0.857, - filename: text(colorScheme.middle, "mono", { size: "sm" }), - path: { - ...text(colorScheme.middle, "mono", { size: "sm" }), - margin: { - left: 12, - }, - }, + active: { + color: foreground(layer, "on"), }, - errorDiagnostic: diagnostic(colorScheme.middle, "negative"), - warningDiagnostic: diagnostic(colorScheme.middle, "warning"), - informationDiagnostic: diagnostic(colorScheme.middle, "accent"), - hintDiagnostic: diagnostic(colorScheme.middle, "warning"), - invalidErrorDiagnostic: diagnostic(colorScheme.middle, "base"), - invalidHintDiagnostic: diagnostic(colorScheme.middle, "base"), - invalidInformationDiagnostic: diagnostic(colorScheme.middle, "base"), - invalidWarningDiagnostic: diagnostic(colorScheme.middle, "base"), - hoverPopover: hoverPopover(colorScheme), - linkDefinition: { - color: syntax.linkUri.color, - underline: syntax.linkUri.underline, + }, + ellipses: { + textColor: colorScheme.ramps.neutral(0.71).hex(), + cornerRadiusFactor: 0.15, + background: { + // Copied from hover_popover highlight + color: colorScheme.ramps.neutral(0.5).alpha(0.0).hex(), + + hover: { + color: colorScheme.ramps.neutral(0.5).alpha(0.5).hex(), + }, + + clicked: { + color: colorScheme.ramps.neutral(0.5).alpha(0.7).hex(), + }, }, - jumpIcon: { - color: foreground(layer, "on"), - iconWidth: 20, - buttonWidth: 20, - cornerRadius: 6, - padding: { - top: 6, - bottom: 6, - left: 6, - right: 6, - }, - hover: { - background: background(layer, "on", "hovered"), - }, + }, + foldBackground: foreground(layer, "variant"), + }, + diff: { + deleted: isLight + ? colorScheme.ramps.red(0.5).hex() + : colorScheme.ramps.red(0.4).hex(), + modified: isLight + ? colorScheme.ramps.yellow(0.5).hex() + : colorScheme.ramps.yellow(0.5).hex(), + inserted: isLight + ? colorScheme.ramps.green(0.4).hex() + : colorScheme.ramps.green(0.5).hex(), + removedWidthEm: 0.275, + widthEm: 0.15, + cornerRadius: 0.05, + }, + /** Highlights matching occurrences of what is under the cursor + * as well as matched brackets + */ + documentHighlightReadBackground: withOpacity( + foreground(layer, "accent"), + 0.1 + ), + documentHighlightWriteBackground: colorScheme.ramps + .neutral(0.5) + .alpha(0.4) + .hex(), // TODO: This was blend * 2 + errorColor: background(layer, "negative"), + gutterBackground: background(layer), + gutterPaddingFactor: 3.5, + lineNumber: withOpacity(foreground(layer), 0.35), + lineNumberActive: foreground(layer), + renameFade: 0.6, + unnecessaryCodeFade: 0.5, + selection: colorScheme.players[0], + whitespace: colorScheme.ramps.neutral(0.5).hex(), + guestSelections: [ + colorScheme.players[1], + colorScheme.players[2], + colorScheme.players[3], + colorScheme.players[4], + colorScheme.players[5], + colorScheme.players[6], + colorScheme.players[7], + ], + autocomplete: { + background: background(colorScheme.middle), + cornerRadius: 8, + padding: 4, + margin: { + left: -14, + }, + border: border(colorScheme.middle), + shadow: colorScheme.popoverShadow, + matchHighlight: foreground(colorScheme.middle, "accent"), + item: autocompleteItem, + hoveredItem: { + ...autocompleteItem, + matchHighlight: foreground( + colorScheme.middle, + "accent", + "hovered" + ), + background: background(colorScheme.middle, "hovered"), + }, + selectedItem: { + ...autocompleteItem, + matchHighlight: foreground( + colorScheme.middle, + "accent", + "active" + ), + background: background(colorScheme.middle, "active"), + }, + }, + diagnosticHeader: { + background: background(colorScheme.middle), + iconWidthFactor: 1.5, + textScaleFactor: 0.857, + border: border(colorScheme.middle, { + bottom: true, + top: true, + }), + code: { + ...text(colorScheme.middle, "mono", { size: "sm" }), + margin: { + left: 10, }, - scrollbar: { - width: 12, - minHeightFactor: 1.0, - track: { - border: border(layer, "variant", { left: true }), - }, - thumb: { - background: withOpacity(background(layer, "inverted"), 0.3), - border: { - width: 1, - color: borderColor(layer, "variant"), - top: false, - right: true, - left: true, - bottom: false, - }, - }, - git: { - deleted: isLight - ? withOpacity(colorScheme.ramps.red(0.5).hex(), 0.8) - : withOpacity(colorScheme.ramps.red(0.4).hex(), 0.8), - modified: isLight - ? withOpacity(colorScheme.ramps.yellow(0.5).hex(), 0.8) - : withOpacity(colorScheme.ramps.yellow(0.4).hex(), 0.8), - inserted: isLight - ? withOpacity(colorScheme.ramps.green(0.5).hex(), 0.8) - : withOpacity(colorScheme.ramps.green(0.4).hex(), 0.8), - }, + }, + source: { + text: text(colorScheme.middle, "sans", { + size: "sm", + weight: "bold", + }), + }, + message: { + highlightText: text(colorScheme.middle, "sans", { + size: "sm", + weight: "bold", + }), + text: text(colorScheme.middle, "sans", { size: "sm" }), + }, + }, + diagnosticPathHeader: { + background: background(colorScheme.middle), + textScaleFactor: 0.857, + filename: text(colorScheme.middle, "mono", { size: "sm" }), + path: { + ...text(colorScheme.middle, "mono", { size: "sm" }), + margin: { + left: 12, }, - compositionMark: { - underline: { - thickness: 1.0, - color: borderColor(layer), - }, + }, + }, + errorDiagnostic: diagnostic(colorScheme.middle, "negative"), + warningDiagnostic: diagnostic(colorScheme.middle, "warning"), + informationDiagnostic: diagnostic(colorScheme.middle, "accent"), + hintDiagnostic: diagnostic(colorScheme.middle, "warning"), + invalidErrorDiagnostic: diagnostic(colorScheme.middle, "base"), + invalidHintDiagnostic: diagnostic(colorScheme.middle, "base"), + invalidInformationDiagnostic: diagnostic(colorScheme.middle, "base"), + invalidWarningDiagnostic: diagnostic(colorScheme.middle, "base"), + hoverPopover: hoverPopover(colorScheme), + linkDefinition: { + color: syntax.linkUri.color, + underline: syntax.linkUri.underline, + }, + jumpIcon: { + color: foreground(layer, "on"), + iconWidth: 20, + buttonWidth: 20, + cornerRadius: 6, + padding: { + top: 6, + bottom: 6, + left: 6, + right: 6, + }, + hover: { + background: background(layer, "on", "hovered"), + }, + }, + scrollbar: { + width: 12, + minHeightFactor: 1.0, + track: { + border: border(layer, "variant", { left: true }), + }, + thumb: { + background: withOpacity(background(layer, "inverted"), 0.3), + border: { + width: 1, + color: borderColor(layer, "variant"), + top: false, + right: true, + left: true, + bottom: false, }, - syntax, - } + }, + git: { + deleted: isLight + ? withOpacity(colorScheme.ramps.red(0.5).hex(), 0.8) + : withOpacity(colorScheme.ramps.red(0.4).hex(), 0.8), + modified: isLight + ? withOpacity(colorScheme.ramps.yellow(0.5).hex(), 0.8) + : withOpacity(colorScheme.ramps.yellow(0.4).hex(), 0.8), + inserted: isLight + ? withOpacity(colorScheme.ramps.green(0.5).hex(), 0.8) + : withOpacity(colorScheme.ramps.green(0.4).hex(), 0.8), + }, + selections: isLight + ? withOpacity(colorScheme.ramps.blue(0.5).hex(), 0.8) + : withOpacity(colorScheme.ramps.blue(0.4).hex(), 0.8) + }, + compositionMark: { + underline: { + thickness: 1.0, + color: borderColor(layer), + }, + }, + syntax, + } }