use super::{ display_map::ToDisplayPoint, DisplayPoint, Editor, EditorSnapshot, ToPoint, MAX_LINE_LEN, }; use crate::{ display_map::{BlockStyle, DisplaySnapshot}, EditorStyle, }; use gpui::{ px, relative, AnyElement, Bounds, Element, Hsla, Line, Pixels, Size, Style, TextRun, TextSystem, }; use language::{CursorShape, Selection}; use std::{ops::Range, sync::Arc}; use sum_tree::Bias; enum FoldMarkers {} struct SelectionLayout { head: DisplayPoint, cursor_shape: CursorShape, is_newest: bool, is_local: bool, range: Range, active_rows: Range, } impl SelectionLayout { fn new( selection: Selection, line_mode: bool, cursor_shape: CursorShape, map: &DisplaySnapshot, is_newest: bool, is_local: bool, ) -> Self { let point_selection = selection.map(|p| p.to_point(&map.buffer_snapshot)); let display_selection = point_selection.map(|p| p.to_display_point(map)); let mut range = display_selection.range(); let mut head = display_selection.head(); let mut active_rows = map.prev_line_boundary(point_selection.start).1.row() ..map.next_line_boundary(point_selection.end).1.row(); // vim visual line mode if line_mode { let point_range = map.expand_to_line(point_selection.range()); range = point_range.start.to_display_point(map)..point_range.end.to_display_point(map); } // any vim visual mode (including line mode) if cursor_shape == CursorShape::Block && !range.is_empty() && !selection.reversed { if head.column() > 0 { head = map.clip_point(DisplayPoint::new(head.row(), head.column() - 1), Bias::Left) } else if head.row() > 0 && head != map.max_point() { head = map.clip_point( DisplayPoint::new(head.row() - 1, map.line_len(head.row() - 1)), Bias::Left, ); // updating range.end is a no-op unless you're cursor is // on the newline containing a multi-buffer divider // in which case the clip_point may have moved the head up // an additional row. range.end = DisplayPoint::new(head.row() + 1, 0); active_rows.end = head.row(); } } Self { head, cursor_shape, is_newest, is_local, range, active_rows, } } } pub struct EditorElement { style: Arc, } impl EditorElement { pub fn new(style: EditorStyle) -> Self { Self { style: Arc::new(style), } } // fn attach_mouse_handlers( // position_map: &Arc, // has_popovers: bool, // visible_bounds: Bounds, // text_bounds: Bounds, // gutter_bounds: Bounds, // bounds: Bounds, // cx: &mut ViewContext, // ) { // enum EditorElementMouseHandlers {} // let view_id = cx.view_id(); // cx.scene().push_mouse_region( // MouseRegion::new::(view_id, view_id, visible_bounds) // .on_down(MouseButton::Left, { // let position_map = position_map.clone(); // move |event, editor, cx| { // if !Self::mouse_down( // editor, // event.platform_event, // position_map.as_ref(), // text_bounds, // gutter_bounds, // cx, // ) { // cx.propagate_event(); // } // } // }) // .on_down(MouseButton::Right, { // let position_map = position_map.clone(); // move |event, editor, cx| { // if !Self::mouse_right_down( // editor, // event.position, // position_map.as_ref(), // text_bounds, // cx, // ) { // cx.propagate_event(); // } // } // }) // .on_up(MouseButton::Left, { // let position_map = position_map.clone(); // move |event, editor, cx| { // if !Self::mouse_up( // editor, // event.position, // event.cmd, // event.shift, // event.alt, // position_map.as_ref(), // text_bounds, // cx, // ) { // cx.propagate_event() // } // } // }) // .on_drag(MouseButton::Left, { // let position_map = position_map.clone(); // move |event, editor, cx| { // if event.end { // return; // } // if !Self::mouse_dragged( // editor, // event.platform_event, // position_map.as_ref(), // text_bounds, // cx, // ) { // cx.propagate_event() // } // } // }) // .on_move({ // let position_map = position_map.clone(); // move |event, editor, cx| { // if !Self::mouse_moved( // editor, // event.platform_event, // &position_map, // text_bounds, // cx, // ) { // cx.propagate_event() // } // } // }) // .on_move_out(move |_, editor: &mut Editor, cx| { // if has_popovers { // hide_hover(editor, cx); // } // }) // .on_scroll({ // let position_map = position_map.clone(); // move |event, editor, cx| { // if !Self::scroll( // editor, // event.position, // *event.delta.raw(), // event.delta.precise(), // &position_map, // bounds, // cx, // ) { // cx.propagate_event() // } // } // }), // ); // enum GutterHandlers {} // let view_id = cx.view_id(); // let region_id = cx.view_id() + 1; // cx.scene().push_mouse_region( // MouseRegion::new::(view_id, region_id, gutter_bounds).on_hover( // |hover, editor: &mut Editor, cx| { // editor.gutter_hover( // &GutterHover { // hovered: hover.started, // }, // cx, // ); // }, // ), // ) // } // fn mouse_down( // editor: &mut Editor, // MouseButtonEvent { // position, // modifiers: // Modifiers { // shift, // ctrl, // alt, // cmd, // .. // }, // mut click_count, // .. // }: MouseButtonEvent, // position_map: &PositionMap, // text_bounds: Bounds, // gutter_bounds: Bounds, // cx: &mut EventContext, // ) -> bool { // if gutter_bounds.contains_point(position) { // click_count = 3; // Simulate triple-click when clicking the gutter to select lines // } else if !text_bounds.contains_point(position) { // return false; // } // let point_for_position = position_map.point_for_position(text_bounds, position); // let position = point_for_position.previous_valid; // if shift && alt { // editor.select( // SelectPhase::BeginColumnar { // position, // goal_column: point_for_position.exact_unclipped.column(), // }, // cx, // ); // } else if shift && !ctrl && !alt && !cmd { // editor.select( // SelectPhase::Extend { // position, // click_count, // }, // cx, // ); // } else { // editor.select( // SelectPhase::Begin { // position, // add: alt, // click_count, // }, // cx, // ); // } // true // } // fn mouse_right_down( // editor: &mut Editor, // position: gpui::Point, // position_map: &PositionMap, // text_bounds: Bounds, // cx: &mut EventContext, // ) -> bool { // if !text_bounds.contains_point(position) { // return false; // } // let point_for_position = position_map.point_for_position(text_bounds, position); // mouse_context_menu::deploy_context_menu( // editor, // position, // point_for_position.previous_valid, // cx, // ); // true // } // fn mouse_up( // editor: &mut Editor, // position: gpui::Point, // cmd: bool, // shift: bool, // alt: bool, // position_map: &PositionMap, // text_bounds: Bounds, // cx: &mut EventContext, // ) -> bool { // let end_selection = editor.has_pending_selection(); // let pending_nonempty_selections = editor.has_pending_nonempty_selection(); // if end_selection { // editor.select(SelectPhase::End, cx); // } // if !pending_nonempty_selections && cmd && text_bounds.contains_point(position) { // let point = position_map.point_for_position(text_bounds, position); // let could_be_inlay = point.as_valid().is_none(); // if shift || could_be_inlay { // go_to_fetched_type_definition(editor, point, alt, cx); // } else { // go_to_fetched_definition(editor, point, alt, cx); // } // return true; // } // end_selection // } // fn mouse_dragged( // editor: &mut Editor, // MouseMovedEvent { // modifiers: Modifiers { cmd, shift, .. }, // position, // .. // }: MouseMovedEvent, // position_map: &PositionMap, // text_bounds: Bounds, // cx: &mut EventContext, // ) -> bool { // // This will be handled more correctly once https://github.com/zed-industries/zed/issues/1218 is completed // // Don't trigger hover popover if mouse is hovering over context menu // let point = if text_bounds.contains_point(position) { // position_map // .point_for_position(text_bounds, position) // .as_valid() // } else { // None // }; // update_go_to_definition_link( // editor, // point.map(GoToDefinitionTrigger::Text), // cmd, // shift, // cx, // ); // if editor.has_pending_selection() { // let mut scroll_delta = gpui::Point::zero(); // let vertical_margin = position_map.line_height.min(text_bounds.height() / 3.0); // let top = text_bounds.origin_y() + vertical_margin; // let bottom = text_bounds.lower_left().y() - vertical_margin; // if position.y() < top { // scroll_delta.set_y(-scale_vertical_mouse_autoscroll_delta(top - position.y())) // } // if position.y() > bottom { // scroll_delta.set_y(scale_vertical_mouse_autoscroll_delta(position.y() - bottom)) // } // let horizontal_margin = position_map.line_height.min(text_bounds.width() / 3.0); // let left = text_bounds.origin_x() + horizontal_margin; // let right = text_bounds.upper_right().x() - horizontal_margin; // if position.x() < left { // scroll_delta.set_x(-scale_horizontal_mouse_autoscroll_delta( // left - position.x(), // )) // } // if position.x() > right { // scroll_delta.set_x(scale_horizontal_mouse_autoscroll_delta( // position.x() - right, // )) // } // let point_for_position = position_map.point_for_position(text_bounds, position); // editor.select( // SelectPhase::Update { // position: point_for_position.previous_valid, // goal_column: point_for_position.exact_unclipped.column(), // scroll_position: (position_map.snapshot.scroll_position() + scroll_delta) // .clamp(gpui::Point::zero(), position_map.scroll_max), // }, // cx, // ); // hover_at(editor, point, cx); // true // } else { // hover_at(editor, point, cx); // false // } // } // fn mouse_moved( // editor: &mut Editor, // MouseMovedEvent { // modifiers: Modifiers { shift, cmd, .. }, // position, // .. // }: MouseMovedEvent, // position_map: &PositionMap, // text_bounds: Bounds, // cx: &mut ViewContext, // ) -> bool { // // This will be handled more correctly once https://github.com/zed-industries/zed/issues/1218 is completed // // Don't trigger hover popover if mouse is hovering over context menu // if text_bounds.contains_point(position) { // let point_for_position = position_map.point_for_position(text_bounds, position); // match point_for_position.as_valid() { // Some(point) => { // update_go_to_definition_link( // editor, // Some(GoToDefinitionTrigger::Text(point)), // cmd, // shift, // cx, // ); // hover_at(editor, Some(point), cx); // } // None => { // update_inlay_link_and_hover_points( // &position_map.snapshot, // point_for_position, // editor, // cmd, // shift, // cx, // ); // } // } // } else { // update_go_to_definition_link(editor, None, cmd, shift, cx); // hover_at(editor, None, cx); // } // true // } // fn scroll( // editor: &mut Editor, // position: gpui::Point, // mut delta: gpui::Point, // precise: bool, // position_map: &PositionMap, // bounds: Bounds, // cx: &mut ViewContext, // ) -> bool { // if !bounds.contains_point(position) { // return false; // } // let line_height = position_map.line_height; // let max_glyph_width = position_map.em_width; // let axis = if precise { // //Trackpad // position_map.snapshot.ongoing_scroll.filter(&mut delta) // } else { // //Not trackpad // delta *= vec2f(max_glyph_width, line_height); // None //Resets ongoing scroll // }; // let scroll_position = position_map.snapshot.scroll_position(); // let x = (scroll_position.x() * max_glyph_width - delta.x()) / max_glyph_width; // let y = (scroll_position.y() * line_height - delta.y()) / line_height; // let scroll_position = vec2f(x, y).clamp(gpui::Point::zero(), position_map.scroll_max); // editor.scroll(scroll_position, axis, cx); // true // } // fn paint_background( // &self, // gutter_bounds: Bounds, // text_bounds: Bounds, // layout: &LayoutState, // cx: &mut ViewContext, // ) { // let bounds = gutter_bounds.union_rect(text_bounds); // let scroll_top = // layout.position_map.snapshot.scroll_position().y() * layout.position_map.line_height; // cx.scene().push_quad(Quad { // bounds: gutter_bounds, // background: Some(self.style.gutter_background), // border: Border::new(0., Color::transparent_black()).into(), // corner_radii: Default::default(), // }); // cx.scene().push_quad(Quad { // bounds: text_bounds, // background: Some(self.style.background), // border: Border::new(0., Color::transparent_black()).into(), // corner_radii: Default::default(), // }); // if let EditorMode::Full = layout.mode { // let mut active_rows = layout.active_rows.iter().peekable(); // while let Some((start_row, contains_non_empty_selection)) = active_rows.next() { // let mut end_row = *start_row; // while active_rows.peek().map_or(false, |r| { // *r.0 == end_row + 1 && r.1 == contains_non_empty_selection // }) { // active_rows.next().unwrap(); // end_row += 1; // } // if !contains_non_empty_selection { // let origin = vec2f( // bounds.origin_x(), // bounds.origin_y() + (layout.position_map.line_height * *start_row as f32) // - scroll_top, // ); // let size = vec2f( // bounds.width(), // layout.position_map.line_height * (end_row - start_row + 1) as f32, // ); // cx.scene().push_quad(Quad { // bounds: Bounds::new(origin, size), // background: Some(self.style.active_line_background), // border: Border::default().into(), // corner_radii: Default::default(), // }); // } // } // if let Some(highlighted_rows) = &layout.highlighted_rows { // let origin = vec2f( // bounds.origin_x(), // bounds.origin_y() // + (layout.position_map.line_height * highlighted_rows.start as f32) // - scroll_top, // ); // let size = vec2f( // bounds.width(), // layout.position_map.line_height * highlighted_rows.len() as f32, // ); // cx.scene().push_quad(Quad { // bounds: Bounds::new(origin, size), // background: Some(self.style.highlighted_line_background), // border: Border::default().into(), // corner_radii: Default::default(), // }); // } // let scroll_left = // layout.position_map.snapshot.scroll_position().x() * layout.position_map.em_width; // for (wrap_position, active) in layout.wrap_guides.iter() { // let x = // (text_bounds.origin_x() + wrap_position + layout.position_map.em_width / 2.) // - scroll_left; // if x < text_bounds.origin_x() // || (layout.show_scrollbars && x > self.scrollbar_left(&bounds)) // { // continue; // } // let color = if *active { // self.style.active_wrap_guide // } else { // self.style.wrap_guide // }; // cx.scene().push_quad(Quad { // bounds: Bounds::new( // vec2f(x, text_bounds.origin_y()), // vec2f(1., text_bounds.height()), // ), // background: Some(color), // border: Border::new(0., Color::transparent_black()).into(), // corner_radii: Default::default(), // }); // } // } // } // fn paint_gutter( // &mut self, // bounds: Bounds, // visible_bounds: Bounds, // layout: &mut LayoutState, // editor: &mut Editor, // cx: &mut ViewContext, // ) { // let line_height = layout.position_map.line_height; // let scroll_position = layout.position_map.snapshot.scroll_position(); // let scroll_top = scroll_position.y() * line_height; // let show_gutter = matches!( // settings::get::(cx).git.git_gutter, // Some(GitGutterSetting::TrackedFiles) // ); // if show_gutter { // Self::paint_diff_hunks(bounds, layout, cx); // } // for (ix, line) in layout.line_number_layouts.iter().enumerate() { // if let Some(line) = line { // let line_origin = bounds.origin() // + vec2f( // bounds.width() - line.width() - layout.gutter_padding, // ix as f32 * line_height - (scroll_top % line_height), // ); // line.paint(line_origin, visible_bounds, line_height, cx); // } // } // for (ix, fold_indicator) in layout.fold_indicators.iter_mut().enumerate() { // if let Some(indicator) = fold_indicator.as_mut() { // let position = vec2f( // bounds.width() - layout.gutter_padding, // ix as f32 * line_height - (scroll_top % line_height), // ); // let centering_offset = vec2f( // (layout.gutter_padding + layout.gutter_margin - indicator.size().x()) / 2., // (line_height - indicator.size().y()) / 2., // ); // let indicator_origin = bounds.origin() + position + centering_offset; // indicator.paint(indicator_origin, visible_bounds, editor, cx); // } // } // if let Some((row, indicator)) = layout.code_actions_indicator.as_mut() { // let mut x = 0.; // let mut y = *row as f32 * line_height - scroll_top; // x += ((layout.gutter_padding + layout.gutter_margin) - indicator.size().x()) / 2.; // y += (line_height - indicator.size().y()) / 2.; // indicator.paint(bounds.origin() + vec2f(x, y), visible_bounds, editor, cx); // } // } // fn paint_diff_hunks(bounds: Bounds, layout: &mut LayoutState, cx: &mut ViewContext) { // let diff_style = &theme::current(cx).editor.diff.clone(); // let line_height = layout.position_map.line_height; // let scroll_position = layout.position_map.snapshot.scroll_position(); // let scroll_top = scroll_position.y() * line_height; // for hunk in &layout.display_hunks { // let (display_row_range, status) = match hunk { // //TODO: This rendering is entirely a horrible hack // &DisplayDiffHunk::Folded { display_row: row } => { // let start_y = row as f32 * line_height - scroll_top; // let end_y = start_y + line_height; // let width = diff_style.removed_width_em * line_height; // let highlight_origin = bounds.origin() + vec2f(-width, start_y); // let highlight_size = vec2f(width * 2., end_y - start_y); // let highlight_bounds = Bounds::new(highlight_origin, highlight_size); // cx.scene().push_quad(Quad { // bounds: highlight_bounds, // background: Some(diff_style.modified), // border: Border::new(0., Color::transparent_black()).into(), // corner_radii: (1. * line_height).into(), // }); // continue; // } // DisplayDiffHunk::Unfolded { // display_row_range, // status, // } => (display_row_range, status), // }; // let color = match status { // DiffHunkStatus::Added => diff_style.inserted, // DiffHunkStatus::Modified => diff_style.modified, // //TODO: This rendering is entirely a horrible hack // DiffHunkStatus::Removed => { // let row = display_row_range.start; // let offset = line_height / 2.; // let start_y = row as f32 * line_height - offset - scroll_top; // let end_y = start_y + line_height; // let width = diff_style.removed_width_em * line_height; // let highlight_origin = bounds.origin() + vec2f(-width, start_y); // let highlight_size = vec2f(width * 2., end_y - start_y); // let highlight_bounds = Bounds::new(highlight_origin, highlight_size); // cx.scene().push_quad(Quad { // bounds: highlight_bounds, // background: Some(diff_style.deleted), // border: Border::new(0., Color::transparent_black()).into(), // corner_radii: (1. * line_height).into(), // }); // continue; // } // }; // let start_row = display_row_range.start; // let end_row = display_row_range.end; // let start_y = start_row as f32 * line_height - scroll_top; // let end_y = end_row as f32 * line_height - scroll_top; // let width = diff_style.width_em * line_height; // let highlight_origin = bounds.origin() + vec2f(-width, start_y); // let highlight_size = vec2f(width * 2., end_y - start_y); // let highlight_bounds = Bounds::new(highlight_origin, highlight_size); // cx.scene().push_quad(Quad { // bounds: highlight_bounds, // background: Some(color), // border: Border::new(0., Color::transparent_black()).into(), // corner_radii: (diff_style.corner_radius * line_height).into(), // }); // } // } // fn paint_text( // &mut self, // bounds: Bounds, // visible_bounds: Bounds, // layout: &mut LayoutState, // editor: &mut Editor, // cx: &mut ViewContext, // ) { // let style = &self.style; // let scroll_position = layout.position_map.snapshot.scroll_position(); // let start_row = layout.visible_display_row_range.start; // let scroll_top = scroll_position.y() * layout.position_map.line_height; // let max_glyph_width = layout.position_map.em_width; // let scroll_left = scroll_position.x() * max_glyph_width; // let content_origin = bounds.origin() + vec2f(layout.gutter_margin, 0.); // let line_end_overshoot = 0.15 * layout.position_map.line_height; // let whitespace_setting = editor.buffer.read(cx).settings_at(0, cx).show_whitespaces; // cx.scene().push_layer(Some(bounds)); // cx.scene().push_cursor_region(CursorRegion { // bounds, // style: if !editor.link_go_to_definition_state.definitions.is_empty() { // CursorStyle::PointingHand // } else { // CursorStyle::IBeam // }, // }); // let fold_corner_radius = // self.style.folds.ellipses.corner_radius_factor * layout.position_map.line_height; // for (id, range, color) in layout.fold_ranges.iter() { // self.paint_highlighted_range( // range.clone(), // *color, // fold_corner_radius, // fold_corner_radius * 2., // layout, // content_origin, // scroll_top, // scroll_left, // bounds, // cx, // ); // for bound in range_to_bounds( // &range, // content_origin, // scroll_left, // scroll_top, // &layout.visible_display_row_range, // line_end_overshoot, // &layout.position_map, // ) { // cx.scene().push_cursor_region(CursorRegion { // bounds: bound, // style: CursorStyle::PointingHand, // }); // let display_row = range.start.row(); // let buffer_row = DisplayPoint::new(display_row, 0) // .to_point(&layout.position_map.snapshot.display_snapshot) // .row; // let view_id = cx.view_id(); // cx.scene().push_mouse_region( // MouseRegion::new::(view_id, *id as usize, bound) // .on_click(MouseButton::Left, move |_, editor: &mut Editor, cx| { // editor.unfold_at(&UnfoldAt { buffer_row }, cx) // }) // .with_notify_on_hover(true) // .with_notify_on_click(true), // ) // } // } // for (range, color) in &layout.highlighted_ranges { // self.paint_highlighted_range( // range.clone(), // *color, // 0., // line_end_overshoot, // layout, // content_origin, // scroll_top, // scroll_left, // bounds, // cx, // ); // } // let mut cursors = SmallVec::<[Cursor; 32]>::new(); // let corner_radius = 0.15 * layout.position_map.line_height; // let mut invisible_display_ranges = SmallVec::<[Range; 32]>::new(); // for (selection_style, selections) in &layout.selections { // for selection in selections { // self.paint_highlighted_range( // selection.range.clone(), // selection_style.selection, // corner_radius, // corner_radius * 2., // layout, // content_origin, // scroll_top, // scroll_left, // bounds, // cx, // ); // if selection.is_local && !selection.range.is_empty() { // invisible_display_ranges.push(selection.range.clone()); // } // if !selection.is_local || editor.show_local_cursors(cx) { // let cursor_position = selection.head; // if layout // .visible_display_row_range // .contains(&cursor_position.row()) // { // let cursor_row_layout = &layout.position_map.line_layouts // [(cursor_position.row() - start_row) as usize] // .line; // let cursor_column = cursor_position.column() as usize; // let cursor_character_x = cursor_row_layout.x_for_index(cursor_column); // let mut block_width = // cursor_row_layout.x_for_index(cursor_column + 1) - cursor_character_x; // if block_width == 0.0 { // block_width = layout.position_map.em_width; // } // let block_text = if let CursorShape::Block = selection.cursor_shape { // layout // .position_map // .snapshot // .chars_at(cursor_position) // .next() // .and_then(|(character, _)| { // let font_id = // cursor_row_layout.font_for_index(cursor_column)?; // let text = character.to_string(); // Some(cx.text_layout_cache().layout_str( // &text, // cursor_row_layout.font_size(), // &[( // text.chars().count(), // RunStyle { // font_id, // color: style.background, // underline: Default::default(), // }, // )], // )) // }) // } else { // None // }; // let x = cursor_character_x - scroll_left; // let y = cursor_position.row() as f32 * layout.position_map.line_height // - scroll_top; // if selection.is_newest { // editor.pixel_position_of_newest_cursor = Some(vec2f( // bounds.origin_x() + x + block_width / 2., // bounds.origin_y() + y + layout.position_map.line_height / 2., // )); // } // cursors.push(Cursor { // color: selection_style.cursor, // block_width, // origin: vec2f(x, y), // line_height: layout.position_map.line_height, // shape: selection.cursor_shape, // block_text, // }); // } // } // } // } // if let Some(visible_text_bounds) = bounds.intersection(visible_bounds) { // for (ix, line_with_invisibles) in layout.position_map.line_layouts.iter().enumerate() { // let row = start_row + ix as u32; // line_with_invisibles.draw( // layout, // row, // scroll_top, // content_origin, // scroll_left, // visible_text_bounds, // whitespace_setting, // &invisible_display_ranges, // visible_bounds, // cx, // ) // } // } // cx.scene().push_layer(Some(bounds)); // for cursor in cursors { // cursor.paint(content_origin, cx); // } // cx.scene().pop_layer(); // if let Some((position, context_menu)) = layout.context_menu.as_mut() { // cx.scene().push_stacking_context(None, None); // let cursor_row_layout = // &layout.position_map.line_layouts[(position.row() - start_row) as usize].line; // let x = cursor_row_layout.x_for_index(position.column() as usize) - scroll_left; // let y = (position.row() + 1) as f32 * layout.position_map.line_height - scroll_top; // let mut list_origin = content_origin + vec2f(x, y); // let list_width = context_menu.size().x(); // let list_height = context_menu.size().y(); // // Snap the right edge of the list to the right edge of the window if // // its horizontal bounds overflow. // if list_origin.x() + list_width > cx.window_size().x() { // list_origin.set_x((cx.window_size().x() - list_width).max(0.)); // } // if list_origin.y() + list_height > bounds.max_y() { // list_origin.set_y(list_origin.y() - layout.position_map.line_height - list_height); // } // context_menu.paint( // list_origin, // Bounds::from_points(gpui::Point::zero(), vec2f(f32::MAX, f32::MAX)), // Let content bleed outside of editor // editor, // cx, // ); // cx.scene().pop_stacking_context(); // } // if let Some((position, hover_popovers)) = layout.hover_popovers.as_mut() { // cx.scene().push_stacking_context(None, None); // // This is safe because we check on layout whether the required row is available // let hovered_row_layout = // &layout.position_map.line_layouts[(position.row() - start_row) as usize].line; // // Minimum required size: Take the first popover, and add 1.5 times the minimum popover // // height. This is the size we will use to decide whether to render popovers above or below // // the hovered line. // let first_size = hover_popovers[0].size(); // let height_to_reserve = first_size.y() // + 1.5 * MIN_POPOVER_LINE_HEIGHT as f32 * layout.position_map.line_height; // // Compute Hovered Point // let x = hovered_row_layout.x_for_index(position.column() as usize) - scroll_left; // let y = position.row() as f32 * layout.position_map.line_height - scroll_top; // let hovered_point = content_origin + vec2f(x, y); // if hovered_point.y() - height_to_reserve > 0.0 { // // There is enough space above. Render popovers above the hovered point // let mut current_y = hovered_point.y(); // for hover_popover in hover_popovers { // let size = hover_popover.size(); // let mut popover_origin = vec2f(hovered_point.x(), current_y - size.y()); // let x_out_of_bounds = bounds.max_x() - (popover_origin.x() + size.x()); // if x_out_of_bounds < 0.0 { // popover_origin.set_x(popover_origin.x() + x_out_of_bounds); // } // hover_popover.paint( // popover_origin, // Bounds::from_points(gpui::Point::zero(), vec2f(f32::MAX, f32::MAX)), // Let content bleed outside of editor // editor, // cx, // ); // current_y = popover_origin.y() - HOVER_POPOVER_GAP; // } // } else { // // There is not enough space above. Render popovers below the hovered point // let mut current_y = hovered_point.y() + layout.position_map.line_height; // for hover_popover in hover_popovers { // let size = hover_popover.size(); // let mut popover_origin = vec2f(hovered_point.x(), current_y); // let x_out_of_bounds = bounds.max_x() - (popover_origin.x() + size.x()); // if x_out_of_bounds < 0.0 { // popover_origin.set_x(popover_origin.x() + x_out_of_bounds); // } // hover_popover.paint( // popover_origin, // Bounds::from_points(gpui::Point::zero(), vec2f(f32::MAX, f32::MAX)), // Let content bleed outside of editor // editor, // cx, // ); // current_y = popover_origin.y() + size.y() + HOVER_POPOVER_GAP; // } // } // cx.scene().pop_stacking_context(); // } // cx.scene().pop_layer(); // } // fn scrollbar_left(&self, bounds: &Bounds) -> f32 { // bounds.max_x() - self.style.theme.scrollbar.width // } // fn paint_scrollbar( // &mut self, // bounds: Bounds, // layout: &mut LayoutState, // editor: &Editor, // cx: &mut ViewContext, // ) { // enum ScrollbarMouseHandlers {} // if layout.mode != EditorMode::Full { // return; // } // let style = &self.style.theme.scrollbar; // let top = bounds.min_y(); // let bottom = bounds.max_y(); // let right = bounds.max_x(); // let left = self.scrollbar_left(&bounds); // let row_range = &layout.scrollbar_row_range; // let max_row = layout.max_row as f32 + (row_range.end - row_range.start); // let mut height = bounds.height(); // let mut first_row_y_offset = 0.0; // // Impose a minimum height on the scrollbar thumb // let row_height = height / max_row; // let min_thumb_height = // style.min_height_factor * cx.font_cache.line_height(self.style.text.font_size); // let thumb_height = (row_range.end - row_range.start) * row_height; // if thumb_height < min_thumb_height { // first_row_y_offset = (min_thumb_height - thumb_height) / 2.0; // height -= min_thumb_height - thumb_height; // } // let y_for_row = |row: f32| -> f32 { top + first_row_y_offset + row * row_height }; // let thumb_top = y_for_row(row_range.start) - first_row_y_offset; // let thumb_bottom = y_for_row(row_range.end) + first_row_y_offset; // let track_bounds = Bounds::from_points(vec2f(left, top), vec2f(right, bottom)); // let thumb_bounds = Bounds::from_points(vec2f(left, thumb_top), vec2f(right, thumb_bottom)); // if layout.show_scrollbars { // cx.scene().push_quad(Quad { // bounds: track_bounds, // border: style.track.border.into(), // 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(); // 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, // }; // let mut push_region = |start: DisplayPoint, end: DisplayPoint| { // let start_y = y_for_row(start.row() as f32); // let mut end_y = y_for_row(end.row() as f32); // if end_y - start_y < 1. { // end_y = start_y + 1.; // } // let bounds = Bounds::from_points(vec2f(left, start_y), vec2f(right, end_y)); // cx.scene().push_quad(Quad { // bounds, // background: Some(color), // border: border.into(), // corner_radii: style.thumb.corner_radii.into(), // }) // }; // let background_ranges = editor // .background_highlight_row_ranges::( // start_anchor..end_anchor, // &layout.position_map.snapshot, // 50000, // ); // for row in background_ranges { // let start = row.start(); // let end = row.end(); // push_region(*start, *end); // } // } // if layout.is_singleton && scrollbar_settings.git_diff { // let diff_style = scrollbar_theme.git.clone(); // for hunk in layout // .position_map // .snapshot // .buffer_snapshot // .git_diff_hunks_in_range(0..(max_row.floor() as u32)) // { // let start_display = Point::new(hunk.buffer_range.start, 0) // .to_display_point(&layout.position_map.snapshot.display_snapshot); // let end_display = Point::new(hunk.buffer_range.end, 0) // .to_display_point(&layout.position_map.snapshot.display_snapshot); // let start_y = y_for_row(start_display.row() as f32); // let mut end_y = if hunk.buffer_range.start == hunk.buffer_range.end { // y_for_row((end_display.row() + 1) as f32) // } else { // y_for_row((end_display.row()) as f32) // }; // if end_y - start_y < 1. { // end_y = start_y + 1.; // } // let bounds = Bounds::from_points(vec2f(left, start_y), vec2f(right, end_y)); // let color = match hunk.status() { // DiffHunkStatus::Added => diff_style.inserted, // DiffHunkStatus::Modified => diff_style.modified, // DiffHunkStatus::Removed => diff_style.deleted, // }; // let border = Border { // width: 1., // color: style.thumb.border.color, // overlay: false, // top: false, // right: true, // bottom: false, // left: true, // }; // cx.scene().push_quad(Quad { // bounds, // background: Some(color), // border: border.into(), // corner_radii: style.thumb.corner_radii.into(), // }) // } // } // cx.scene().push_quad(Quad { // bounds: thumb_bounds, // border: style.thumb.border.into(), // background: style.thumb.background_color, // corner_radii: style.thumb.corner_radii.into(), // }); // } // cx.scene().push_cursor_region(CursorRegion { // bounds: track_bounds, // style: CursorStyle::Arrow, // }); // let region_id = cx.view_id(); // cx.scene().push_mouse_region( // MouseRegion::new::(region_id, region_id, track_bounds) // .on_move(move |event, editor: &mut Editor, cx| { // if event.pressed_button.is_none() { // editor.scroll_manager.show_scrollbar(cx); // } // }) // .on_down(MouseButton::Left, { // let row_range = row_range.clone(); // move |event, editor: &mut Editor, cx| { // let y = event.position.y(); // if y < thumb_top || thumb_bottom < y { // let center_row = ((y - top) * max_row as f32 / height).round() as u32; // let top_row = center_row // .saturating_sub((row_range.end - row_range.start) as u32 / 2); // let mut position = editor.scroll_position(cx); // position.set_y(top_row as f32); // editor.set_scroll_position(position, cx); // } else { // editor.scroll_manager.show_scrollbar(cx); // } // } // }) // .on_drag(MouseButton::Left, { // move |event, editor: &mut Editor, cx| { // if event.end { // return; // } // let y = event.prev_mouse_position.y(); // let new_y = event.position.y(); // if thumb_top < y && y < thumb_bottom { // let mut position = editor.scroll_position(cx); // position.set_y(position.y() + (new_y - y) * (max_row as f32) / height); // if position.y() < 0.0 { // position.set_y(0.); // } // editor.set_scroll_position(position, cx); // } // } // }), // ); // } // #[allow(clippy::too_many_arguments)] // fn paint_highlighted_range( // &self, // range: Range, // color: Color, // corner_radius: f32, // line_end_overshoot: f32, // layout: &LayoutState, // content_origin: gpui::Point, // scroll_top: f32, // scroll_left: f32, // bounds: Bounds, // cx: &mut ViewContext, // ) { // let start_row = layout.visible_display_row_range.start; // let end_row = layout.visible_display_row_range.end; // if range.start != range.end { // let row_range = if range.end.column() == 0 { // cmp::max(range.start.row(), start_row)..cmp::min(range.end.row(), end_row) // } else { // cmp::max(range.start.row(), start_row)..cmp::min(range.end.row() + 1, end_row) // }; // let highlighted_range = HighlightedRange { // color, // line_height: layout.position_map.line_height, // corner_radius, // start_y: content_origin.y() // + row_range.start as f32 * layout.position_map.line_height // - scroll_top, // lines: row_range // .into_iter() // .map(|row| { // let line_layout = // &layout.position_map.line_layouts[(row - start_row) as usize].line; // HighlightedRangeLine { // start_x: if row == range.start.row() { // content_origin.x() // + line_layout.x_for_index(range.start.column() as usize) // - scroll_left // } else { // content_origin.x() - scroll_left // }, // end_x: if row == range.end.row() { // content_origin.x() // + line_layout.x_for_index(range.end.column() as usize) // - scroll_left // } else { // content_origin.x() + line_layout.width() + line_end_overshoot // - scroll_left // }, // } // }) // .collect(), // }; // highlighted_range.paint(bounds, cx); // } // } // fn paint_blocks( // &mut self, // bounds: Bounds, // visible_bounds: Bounds, // layout: &mut LayoutState, // editor: &mut Editor, // cx: &mut ViewContext, // ) { // let scroll_position = layout.position_map.snapshot.scroll_position(); // let scroll_left = scroll_position.x() * layout.position_map.em_width; // let scroll_top = scroll_position.y() * layout.position_map.line_height; // for block in &mut layout.blocks { // let mut origin = bounds.origin() // + vec2f( // 0., // block.row as f32 * layout.position_map.line_height - scroll_top, // ); // if !matches!(block.style, BlockStyle::Sticky) { // origin += vec2f(-scroll_left, 0.); // } // block.element.paint(origin, visible_bounds, editor, cx); // } // } // fn column_pixels(&self, column: usize, cx: &ViewContext) -> f32 { // let style = &self.style; // cx.text_layout_cache() // .layout_str( // " ".repeat(column).as_str(), // style.text.font_size, // &[( // column, // RunStyle { // font_id: style.text.font_id, // color: Color::black(), // underline: Default::default(), // }, // )], // ) // .width() // } // fn max_line_number_width(&self, snapshot: &EditorSnapshot, cx: &ViewContext) -> f32 { // let digit_count = (snapshot.max_buffer_row() as f32 + 1.).log10().floor() as usize + 1; // self.column_pixels(digit_count, cx) // } //Folds contained in a hunk are ignored apart from shrinking visual size //If a fold contains any hunks then that fold line is marked as modified // fn layout_git_gutters( // &self, // display_rows: Range, // snapshot: &EditorSnapshot, // ) -> Vec { // let buffer_snapshot = &snapshot.buffer_snapshot; // let buffer_start_row = DisplayPoint::new(display_rows.start, 0) // .to_point(snapshot) // .row; // let buffer_end_row = DisplayPoint::new(display_rows.end, 0) // .to_point(snapshot) // .row; // buffer_snapshot // .git_diff_hunks_in_range(buffer_start_row..buffer_end_row) // .map(|hunk| diff_hunk_to_display(hunk, snapshot)) // .dedup() // .collect() // } // fn calculate_relative_line_numbers( // &self, // snapshot: &EditorSnapshot, // rows: &Range, // relative_to: Option, // ) -> HashMap { // let mut relative_rows: HashMap = Default::default(); // let Some(relative_to) = relative_to else { // return relative_rows; // }; // let start = rows.start.min(relative_to); // let end = rows.end.max(relative_to); // let buffer_rows = snapshot // .buffer_rows(start) // .take(1 + (end - start) as usize) // .collect::>(); // let head_idx = relative_to - start; // let mut delta = 1; // let mut i = head_idx + 1; // while i < buffer_rows.len() as u32 { // if buffer_rows[i as usize].is_some() { // if rows.contains(&(i + start)) { // relative_rows.insert(i + start, delta); // } // delta += 1; // } // i += 1; // } // delta = 1; // i = head_idx.min(buffer_rows.len() as u32 - 1); // while i > 0 && buffer_rows[i as usize].is_none() { // i -= 1; // } // while i > 0 { // i -= 1; // if buffer_rows[i as usize].is_some() { // if rows.contains(&(i + start)) { // relative_rows.insert(i + start, delta); // } // delta += 1; // } // } // relative_rows // } // fn layout_line_numbers( // &self, // rows: Range, // active_rows: &BTreeMap, // newest_selection_head: DisplayPoint, // is_singleton: bool, // snapshot: &EditorSnapshot, // cx: &ViewContext, // ) -> ( // Vec>, // Vec>, // ) { // let style = &self.style; // let include_line_numbers = snapshot.mode == EditorMode::Full; // let mut line_number_layouts = Vec::with_capacity(rows.len()); // let mut fold_statuses = Vec::with_capacity(rows.len()); // let mut line_number = String::new(); // let is_relative = settings::get::(cx).relative_line_numbers; // let relative_to = if is_relative { // Some(newest_selection_head.row()) // } else { // None // }; // let relative_rows = self.calculate_relative_line_numbers(&snapshot, &rows, relative_to); // for (ix, row) in snapshot // .buffer_rows(rows.start) // .take((rows.end - rows.start) as usize) // .enumerate() // { // let display_row = rows.start + ix as u32; // let (active, color) = if active_rows.contains_key(&display_row) { // (true, style.line_number_active) // } else { // (false, style.line_number) // }; // if let Some(buffer_row) = row { // if include_line_numbers { // line_number.clear(); // let default_number = buffer_row + 1; // let number = relative_rows // .get(&(ix as u32 + rows.start)) // .unwrap_or(&default_number); // write!(&mut line_number, "{}", number).unwrap(); // line_number_layouts.push(Some(cx.text_layout_cache().layout_str( // &line_number, // style.text.font_size, // &[( // line_number.len(), // RunStyle { // font_id: style.text.font_id, // color, // underline: Default::default(), // }, // )], // ))); // fold_statuses.push( // is_singleton // .then(|| { // snapshot // .fold_for_line(buffer_row) // .map(|fold_status| (fold_status, buffer_row, active)) // }) // .flatten(), // ) // } // } else { // fold_statuses.push(None); // line_number_layouts.push(None); // } // } // (line_number_layouts, fold_statuses) // } // fn layout_lines( // &mut self, // rows: Range, // line_number_layouts: &[Option], // snapshot: &EditorSnapshot, // cx: &ViewContext, // ) -> Vec { // if rows.start >= rows.end { // return Vec::new(); // } // // When the editor is empty and unfocused, then show the placeholder. // if snapshot.is_empty() { // let placeholder_style = self // .style // .placeholder_text // .as_ref() // .unwrap_or(&self.style.text); // let placeholder_text = snapshot.placeholder_text(); // let placeholder_lines = placeholder_text // .as_ref() // .map_or("", AsRef::as_ref) // .split('\n') // .skip(rows.start as usize) // .chain(iter::repeat("")) // .take(rows.len()); // placeholder_lines // .map(|line| { // cx.text_layout_cache().layout_str( // line, // placeholder_style.font_size, // &[( // line.len(), // RunStyle { // font_id: placeholder_style.font_id, // color: placeholder_style.color, // underline: Default::default(), // }, // )], // ) // }) // .map(|line| LineWithInvisibles { // line, // invisibles: Vec::new(), // }) // .collect() // } else { // let style = &self.style; // let chunks = snapshot.highlighted_chunks(rows.clone(), true, style); // LineWithInvisibles::from_chunks( // chunks, // &style.text, // cx.text_layout_cache(), // cx.font_cache(), // MAX_LINE_LEN, // rows.len() as usize, // line_number_layouts, // snapshot.mode, // ) // } // } // #[allow(clippy::too_many_arguments)] // fn layout_blocks( // &mut self, // rows: Range, // snapshot: &EditorSnapshot, // editor_width: f32, // scroll_width: f32, // gutter_padding: f32, // gutter_width: f32, // em_width: f32, // text_x: f32, // line_height: f32, // style: &EditorStyle, // line_layouts: &[LineWithInvisibles], // editor: &mut Editor, // cx: &mut ViewContext, // ) -> (f32, Vec) { // let mut block_id = 0; // let scroll_x = snapshot.scroll_anchor.offset.x(); // let (fixed_blocks, non_fixed_blocks) = snapshot // .blocks_in_range(rows.clone()) // .partition::, _>(|(_, block)| match block { // TransformBlock::ExcerptHeader { .. } => false, // TransformBlock::Custom(block) => block.style() == BlockStyle::Fixed, // }); // let mut render_block = |block: &TransformBlock, width: f32, block_id: usize| { // let mut element = match block { // TransformBlock::Custom(block) => { // let align_to = block // .position() // .to_point(&snapshot.buffer_snapshot) // .to_display_point(snapshot); // let anchor_x = text_x // + if rows.contains(&align_to.row()) { // line_layouts[(align_to.row() - rows.start) as usize] // .line // .x_for_index(align_to.column() as usize) // } else { // layout_line(align_to.row(), snapshot, style, cx.text_layout_cache()) // .x_for_index(align_to.column() as usize) // }; // block.render(&mut BlockContext { // view_context: cx, // anchor_x, // gutter_padding, // line_height, // scroll_x, // gutter_width, // em_width, // block_id, // }) // } // TransformBlock::ExcerptHeader { // id, // buffer, // range, // starts_new_buffer, // .. // } => { // let tooltip_style = theme::current(cx).tooltip.clone(); // let include_root = editor // .project // .as_ref() // .map(|project| project.read(cx).visible_worktrees(cx).count() > 1) // .unwrap_or_default(); // let jump_icon = project::File::from_dyn(buffer.file()).map(|file| { // let jump_path = ProjectPath { // worktree_id: file.worktree_id(cx), // path: file.path.clone(), // }; // let jump_anchor = range // .primary // .as_ref() // .map_or(range.context.start, |primary| primary.start); // let jump_position = language::ToPoint::to_point(&jump_anchor, buffer); // enum JumpIcon {} // MouseEventHandler::new::((*id).into(), cx, |state, _| { // let style = style.jump_icon.style_for(state); // Svg::new("icons/arrow_up_right.svg") // .with_color(style.color) // .constrained() // .with_width(style.icon_width) // .aligned() // .contained() // .with_style(style.container) // .constrained() // .with_width(style.button_width) // .with_height(style.button_width) // }) // .with_cursor_style(CursorStyle::PointingHand) // .on_click(MouseButton::Left, move |_, editor, cx| { // if let Some(workspace) = editor // .workspace // .as_ref() // .and_then(|(workspace, _)| workspace.upgrade(cx)) // { // workspace.update(cx, |workspace, cx| { // Editor::jump( // workspace, // jump_path.clone(), // jump_position, // jump_anchor, // cx, // ); // }); // } // }) // .with_tooltip::( // (*id).into(), // "Jump to Buffer".to_string(), // Some(Box::new(crate::OpenExcerpts)), // tooltip_style.clone(), // cx, // ) // .aligned() // .flex_float() // }); // if *starts_new_buffer { // let editor_font_size = style.text.font_size; // let style = &style.diagnostic_path_header; // let font_size = (style.text_scale_factor * editor_font_size).round(); // let path = buffer.resolve_file_path(cx, include_root); // let mut filename = None; // let mut parent_path = None; // // Can't use .and_then() because `.file_name()` and `.parent()` return references :( // if let Some(path) = path { // filename = path.file_name().map(|f| f.to_string_lossy().to_string()); // parent_path = // path.parent().map(|p| p.to_string_lossy().to_string() + "/"); // } // Flex::row() // .with_child( // Label::new( // filename.unwrap_or_else(|| "untitled".to_string()), // style.filename.text.clone().with_font_size(font_size), // ) // .contained() // .with_style(style.filename.container) // .aligned(), // ) // .with_children(parent_path.map(|path| { // Label::new(path, style.path.text.clone().with_font_size(font_size)) // .contained() // .with_style(style.path.container) // .aligned() // })) // .with_children(jump_icon) // .contained() // .with_style(style.container) // .with_padding_left(gutter_padding) // .with_padding_right(gutter_padding) // .expanded() // .into_any_named("path header block") // } else { // let text_style = style.text.clone(); // Flex::row() // .with_child(Label::new("⋯", text_style)) // .with_children(jump_icon) // .contained() // .with_padding_left(gutter_padding) // .with_padding_right(gutter_padding) // .expanded() // .into_any_named("collapsed context") // } // } // }; // element.layout( // SizeConstraint { // min: gpui::Point::zero(), // max: vec2f(width, block.height() as f32 * line_height), // }, // editor, // cx, // ); // element // }; // let mut fixed_block_max_width = 0f32; // let mut blocks = Vec::new(); // for (row, block) in fixed_blocks { // let element = render_block(block, f32::INFINITY, block_id); // block_id += 1; // fixed_block_max_width = fixed_block_max_width.max(element.size().x() + em_width); // blocks.push(BlockLayout { // row, // element, // style: BlockStyle::Fixed, // }); // } // for (row, block) in non_fixed_blocks { // let style = match block { // TransformBlock::Custom(block) => block.style(), // TransformBlock::ExcerptHeader { .. } => BlockStyle::Sticky, // }; // let width = match style { // BlockStyle::Sticky => editor_width, // BlockStyle::Flex => editor_width // .max(fixed_block_max_width) // .max(gutter_width + scroll_width), // BlockStyle::Fixed => unreachable!(), // }; // let element = render_block(block, width, block_id); // block_id += 1; // blocks.push(BlockLayout { // row, // element, // style, // }); // } // ( // scroll_width.max(fixed_block_max_width - gutter_width), // blocks, // ) // } } #[derive(Debug)] pub struct LineWithInvisibles { pub line: Line, invisibles: Vec, } // impl LineWithInvisibles { // fn from_chunks<'a>( // chunks: impl Iterator>, // text_style: &TextStyle, // text_layout_cache: &TextLayoutCache, // font_cache: &Arc, // max_line_len: usize, // max_line_count: usize, // line_number_layouts: &[Option], // editor_mode: EditorMode, // ) -> Vec { // let mut layouts = Vec::with_capacity(max_line_count); // let mut line = String::new(); // let mut invisibles = Vec::new(); // let mut styles = Vec::new(); // let mut non_whitespace_added = false; // let mut row = 0; // let mut line_exceeded_max_len = false; // for highlighted_chunk in chunks.chain([HighlightedChunk { // chunk: "\n", // style: None, // is_tab: false, // }]) { // for (ix, mut line_chunk) in highlighted_chunk.chunk.split('\n').enumerate() { // if ix > 0 { // layouts.push(Self { // line: text_layout_cache.layout_str(&line, text_style.font_size, &styles), // invisibles: invisibles.drain(..).collect(), // }); // line.clear(); // styles.clear(); // row += 1; // line_exceeded_max_len = false; // non_whitespace_added = false; // if row == max_line_count { // return layouts; // } // } // if !line_chunk.is_empty() && !line_exceeded_max_len { // let text_style = if let Some(style) = highlighted_chunk.style { // text_style // .clone() // .highlight(style, font_cache) // .map(Cow::Owned) // .unwrap_or_else(|_| Cow::Borrowed(text_style)) // } else { // Cow::Borrowed(text_style) // }; // if line.len() + line_chunk.len() > max_line_len { // let mut chunk_len = max_line_len - line.len(); // while !line_chunk.is_char_boundary(chunk_len) { // chunk_len -= 1; // } // line_chunk = &line_chunk[..chunk_len]; // line_exceeded_max_len = true; // } // styles.push(( // line_chunk.len(), // RunStyle { // font_id: text_style.font_id, // color: text_style.color, // underline: text_style.underline, // }, // )); // if editor_mode == EditorMode::Full { // // Line wrap pads its contents with fake whitespaces, // // avoid printing them // let inside_wrapped_string = line_number_layouts // .get(row) // .and_then(|layout| layout.as_ref()) // .is_none(); // if highlighted_chunk.is_tab { // if non_whitespace_added || !inside_wrapped_string { // invisibles.push(Invisible::Tab { // line_start_offset: line.len(), // }); // } // } else { // invisibles.extend( // line_chunk // .chars() // .enumerate() // .filter(|(_, line_char)| { // let is_whitespace = line_char.is_whitespace(); // non_whitespace_added |= !is_whitespace; // is_whitespace // && (non_whitespace_added || !inside_wrapped_string) // }) // .map(|(whitespace_index, _)| Invisible::Whitespace { // line_offset: line.len() + whitespace_index, // }), // ) // } // } // line.push_str(line_chunk); // } // } // } // layouts // } // fn draw( // &self, // layout: &LayoutState, // row: u32, // scroll_top: f32, // content_origin: gpui::Point, // scroll_left: f32, // visible_text_bounds: Bounds, // whitespace_setting: ShowWhitespaceSetting, // selection_ranges: &[Range], // visible_bounds: Bounds, // cx: &mut ViewContext, // ) { // let line_height = layout.position_map.line_height; // let line_y = row as f32 * line_height - scroll_top; // self.line.paint( // content_origin + vec2f(-scroll_left, line_y), // visible_text_bounds, // line_height, // cx, // ); // self.draw_invisibles( // &selection_ranges, // layout, // content_origin, // scroll_left, // line_y, // row, // visible_bounds, // line_height, // whitespace_setting, // cx, // ); // } // fn draw_invisibles( // &self, // selection_ranges: &[Range], // layout: &LayoutState, // content_origin: gpui::Point, // scroll_left: f32, // line_y: f32, // row: u32, // visible_bounds: Bounds, // line_height: f32, // whitespace_setting: ShowWhitespaceSetting, // cx: &mut ViewContext, // ) { // let allowed_invisibles_regions = match whitespace_setting { // ShowWhitespaceSetting::None => return, // ShowWhitespaceSetting::Selection => Some(selection_ranges), // ShowWhitespaceSetting::All => None, // }; // for invisible in &self.invisibles { // let (&token_offset, invisible_symbol) = match invisible { // Invisible::Tab { line_start_offset } => (line_start_offset, &layout.tab_invisible), // Invisible::Whitespace { line_offset } => (line_offset, &layout.space_invisible), // }; // let x_offset = self.line.x_for_index(token_offset); // let invisible_offset = // (layout.position_map.em_width - invisible_symbol.width()).max(0.0) / 2.0; // let origin = content_origin + vec2f(-scroll_left + x_offset + invisible_offset, line_y); // if let Some(allowed_regions) = allowed_invisibles_regions { // let invisible_point = DisplayPoint::new(row, token_offset as u32); // if !allowed_regions // .iter() // .any(|region| region.start <= invisible_point && invisible_point < region.end) // { // continue; // } // } // invisible_symbol.paint(origin, visible_bounds, line_height, cx); // } // } // } #[derive(Debug, Clone, Copy, PartialEq, Eq)] enum Invisible { Tab { line_start_offset: usize }, Whitespace { line_offset: usize }, } impl Element for EditorElement { type ElementState = (); fn id(&self) -> Option { None } fn initialize( &mut self, view_state: &mut Editor, element_state: Option, cx: &mut gpui::ViewContext, ) -> Self::ElementState { () } fn layout( &mut self, view_state: &mut Editor, element_state: &mut Self::ElementState, cx: &mut gpui::ViewContext, ) -> gpui::LayoutId { let mut style = Style::default(); style.size.width = relative(1.).into(); style.size.height = relative(1.).into(); cx.request_layout(&style, None) } fn paint( &mut self, bounds: Bounds, view_state: &mut Editor, element_state: &mut Self::ElementState, cx: &mut gpui::ViewContext, ) { let text_style = cx.text_style(); let layout_text = cx.text_system().layout_text( "hello world", text_style.font_size * cx.rem_size(), &[text_style.to_run("hello world".len())], None, ); } } // impl EditorElement { // type LayoutState = LayoutState; // type PaintState = (); // fn layout( // &mut self, // constraint: SizeConstraint, // editor: &mut Editor, // cx: &mut ViewContext, // ) -> (gpui::Point, Self::LayoutState) { // let mut size = constraint.max; // if size.x().is_infinite() { // unimplemented!("we don't yet handle an infinite width constraint on buffer elements"); // } // let snapshot = editor.snapshot(cx); // let style = self.style.clone(); // let line_height = (style.text.font_size * style.line_height_scalar).round(); // let gutter_padding; // let gutter_width; // let gutter_margin; // if snapshot.show_gutter { // let em_width = style.text.em_width(cx.font_cache()); // gutter_padding = (em_width * style.gutter_padding_factor).round(); // gutter_width = self.max_line_number_width(&snapshot, cx) + gutter_padding * 2.0; // gutter_margin = -style.text.descent(cx.font_cache()); // } else { // gutter_padding = 0.0; // gutter_width = 0.0; // gutter_margin = 0.0; // }; // let text_width = size.x() - gutter_width; // let em_width = style.text.em_width(cx.font_cache()); // let em_advance = style.text.em_advance(cx.font_cache()); // let overscroll = vec2f(em_width, 0.); // let snapshot = { // editor.set_visible_line_count(size.y() / line_height, cx); // let editor_width = text_width - gutter_margin - overscroll.x() - em_width; // let wrap_width = match editor.soft_wrap_mode(cx) { // SoftWrap::None => (MAX_LINE_LEN / 2) as f32 * em_advance, // SoftWrap::EditorWidth => editor_width, // SoftWrap::Column(column) => editor_width.min(column as f32 * em_advance), // }; // if editor.set_wrap_width(Some(wrap_width), cx) { // editor.snapshot(cx) // } else { // snapshot // } // }; // let wrap_guides = editor // .wrap_guides(cx) // .iter() // .map(|(guide, active)| (self.column_pixels(*guide, cx), *active)) // .collect(); // let scroll_height = (snapshot.max_point().row() + 1) as f32 * line_height; // if let EditorMode::AutoHeight { max_lines } = snapshot.mode { // size.set_y( // scroll_height // .min(constraint.max_along(Axis::Vertical)) // .max(constraint.min_along(Axis::Vertical)) // .max(line_height) // .min(line_height * max_lines as f32), // ) // } else if let EditorMode::SingleLine = snapshot.mode { // size.set_y(line_height.max(constraint.min_along(Axis::Vertical))) // } else if size.y().is_infinite() { // size.set_y(scroll_height); // } // let gutter_size = vec2f(gutter_width, size.y()); // let text_size = vec2f(text_width, size.y()); // let autoscroll_horizontally = editor.autoscroll_vertically(size.y(), line_height, cx); // let mut snapshot = editor.snapshot(cx); // let scroll_position = snapshot.scroll_position(); // // The scroll position is a fractional point, the whole number of which represents // // the top of the window in terms of display rows. // let start_row = scroll_position.y() as u32; // let height_in_lines = size.y() / line_height; // let max_row = snapshot.max_point().row(); // // Add 1 to ensure selections bleed off screen // let end_row = 1 + cmp::min( // (scroll_position.y() + height_in_lines).ceil() as u32, // max_row, // ); // let start_anchor = if start_row == 0 { // Anchor::min() // } else { // snapshot // .buffer_snapshot // .anchor_before(DisplayPoint::new(start_row, 0).to_offset(&snapshot, Bias::Left)) // }; // let end_anchor = if end_row > max_row { // Anchor::max() // } else { // snapshot // .buffer_snapshot // .anchor_before(DisplayPoint::new(end_row, 0).to_offset(&snapshot, Bias::Right)) // }; // let mut selections: Vec<(SelectionStyle, Vec)> = Vec::new(); // let mut active_rows = BTreeMap::new(); // let mut fold_ranges = Vec::new(); // let is_singleton = editor.is_singleton(cx); // let highlighted_rows = editor.highlighted_rows(); // let theme = theme::current(cx); // let highlighted_ranges = editor.background_highlights_in_range( // start_anchor..end_anchor, // &snapshot.display_snapshot, // theme.as_ref(), // ); // fold_ranges.extend( // snapshot // .folds_in_range(start_anchor..end_anchor) // .map(|anchor| { // let start = anchor.start.to_point(&snapshot.buffer_snapshot); // ( // start.row, // start.to_display_point(&snapshot.display_snapshot) // ..anchor.end.to_display_point(&snapshot), // ) // }), // ); // let mut newest_selection_head = None; // if editor.show_local_selections { // let mut local_selections: Vec> = editor // .selections // .disjoint_in_range(start_anchor..end_anchor, cx); // local_selections.extend(editor.selections.pending(cx)); // let mut layouts = Vec::new(); // let newest = editor.selections.newest(cx); // for selection in local_selections.drain(..) { // let is_empty = selection.start == selection.end; // let is_newest = selection == newest; // let layout = SelectionLayout::new( // selection, // editor.selections.line_mode, // editor.cursor_shape, // &snapshot.display_snapshot, // is_newest, // true, // ); // if is_newest { // newest_selection_head = Some(layout.head); // } // for row in cmp::max(layout.active_rows.start, start_row) // ..=cmp::min(layout.active_rows.end, end_row) // { // let contains_non_empty_selection = active_rows.entry(row).or_insert(!is_empty); // *contains_non_empty_selection |= !is_empty; // } // layouts.push(layout); // } // selections.push((style.selection, layouts)); // } // if let Some(collaboration_hub) = &editor.collaboration_hub { // // When following someone, render the local selections in their color. // if let Some(leader_id) = editor.leader_peer_id { // if let Some(collaborator) = collaboration_hub.collaborators(cx).get(&leader_id) { // if let Some(participant_index) = collaboration_hub // .user_participant_indices(cx) // .get(&collaborator.user_id) // { // if let Some((local_selection_style, _)) = selections.first_mut() { // *local_selection_style = // style.selection_style_for_room_participant(participant_index.0); // } // } // } // } // let mut remote_selections = HashMap::default(); // for selection in snapshot.remote_selections_in_range( // &(start_anchor..end_anchor), // collaboration_hub.as_ref(), // cx, // ) { // let selection_style = if let Some(participant_index) = selection.participant_index { // style.selection_style_for_room_participant(participant_index.0) // } else { // style.absent_selection // }; // // Don't re-render the leader's selections, since the local selections // // match theirs. // if Some(selection.peer_id) == editor.leader_peer_id { // continue; // } // remote_selections // .entry(selection.replica_id) // .or_insert((selection_style, Vec::new())) // .1 // .push(SelectionLayout::new( // selection.selection, // selection.line_mode, // selection.cursor_shape, // &snapshot.display_snapshot, // false, // false, // )); // } // selections.extend(remote_selections.into_values()); // } // let scrollbar_settings = &settings::get::(cx).scrollbar; // let show_scrollbars = match scrollbar_settings.show { // ShowScrollbar::Auto => { // // Git // (is_singleton && scrollbar_settings.git_diff && snapshot.buffer_snapshot.has_git_diffs()) // || // // Selections // (is_singleton && scrollbar_settings.selections && !highlighted_ranges.is_empty()) // // Scrollmanager // || editor.scroll_manager.scrollbars_visible() // } // ShowScrollbar::System => editor.scroll_manager.scrollbars_visible(), // ShowScrollbar::Always => true, // ShowScrollbar::Never => false, // }; // let fold_ranges: Vec<(BufferRow, Range, Color)> = fold_ranges // .into_iter() // .map(|(id, fold)| { // let color = self // .style // .folds // .ellipses // .background // .style_for(&mut cx.mouse_state::(id as usize)) // .color; // (id, fold, color) // }) // .collect(); // let head_for_relative = newest_selection_head.unwrap_or_else(|| { // let newest = editor.selections.newest::(cx); // SelectionLayout::new( // newest, // editor.selections.line_mode, // editor.cursor_shape, // &snapshot.display_snapshot, // true, // true, // ) // .head // }); // let (line_number_layouts, fold_statuses) = self.layout_line_numbers( // start_row..end_row, // &active_rows, // head_for_relative, // is_singleton, // &snapshot, // cx, // ); // let display_hunks = self.layout_git_gutters(start_row..end_row, &snapshot); // let scrollbar_row_range = scroll_position.y()..(scroll_position.y() + height_in_lines); // let mut max_visible_line_width = 0.0; // let line_layouts = // self.layout_lines(start_row..end_row, &line_number_layouts, &snapshot, cx); // for line_with_invisibles in &line_layouts { // if line_with_invisibles.line.width() > max_visible_line_width { // max_visible_line_width = line_with_invisibles.line.width(); // } // } // let style = self.style.clone(); // let longest_line_width = layout_line( // snapshot.longest_row(), // &snapshot, // &style, // cx.text_layout_cache(), // ) // .width(); // let scroll_width = longest_line_width.max(max_visible_line_width) + overscroll.x(); // let em_width = style.text.em_width(cx.font_cache()); // let (scroll_width, blocks) = self.layout_blocks( // start_row..end_row, // &snapshot, // size.x(), // scroll_width, // gutter_padding, // gutter_width, // em_width, // gutter_width + gutter_margin, // line_height, // &style, // &line_layouts, // editor, // cx, // ); // let scroll_max = vec2f( // ((scroll_width - text_size.x()) / em_width).max(0.0), // max_row as f32, // ); // let clamped = editor.scroll_manager.clamp_scroll_left(scroll_max.x()); // let autoscrolled = if autoscroll_horizontally { // editor.autoscroll_horizontally( // start_row, // text_size.x(), // scroll_width, // em_width, // &line_layouts, // cx, // ) // } else { // false // }; // if clamped || autoscrolled { // snapshot = editor.snapshot(cx); // } // let style = editor.style(cx); // let mut context_menu = None; // let mut code_actions_indicator = None; // if let Some(newest_selection_head) = newest_selection_head { // if (start_row..end_row).contains(&newest_selection_head.row()) { // if editor.context_menu_visible() { // context_menu = // editor.render_context_menu(newest_selection_head, style.clone(), cx); // } // let active = matches!( // editor.context_menu.read().as_ref(), // Some(crate::ContextMenu::CodeActions(_)) // ); // code_actions_indicator = editor // .render_code_actions_indicator(&style, active, cx) // .map(|indicator| (newest_selection_head.row(), indicator)); // } // } // let visible_rows = start_row..start_row + line_layouts.len() as u32; // let mut hover = editor.hover_state.render( // &snapshot, // &style, // visible_rows, // editor.workspace.as_ref().map(|(w, _)| w.clone()), // cx, // ); // let mode = editor.mode; // let mut fold_indicators = editor.render_fold_indicators( // fold_statuses, // &style, // editor.gutter_hovered, // line_height, // gutter_margin, // cx, // ); // if let Some((_, context_menu)) = context_menu.as_mut() { // context_menu.layout( // SizeConstraint { // min: gpui::Point::zero(), // max: vec2f( // cx.window_size().x() * 0.7, // (12. * line_height).min((size.y() - line_height) / 2.), // ), // }, // editor, // cx, // ); // } // if let Some((_, indicator)) = code_actions_indicator.as_mut() { // indicator.layout( // SizeConstraint::strict_along( // Axis::Vertical, // line_height * style.code_actions.vertical_scale, // ), // editor, // cx, // ); // } // for fold_indicator in fold_indicators.iter_mut() { // if let Some(indicator) = fold_indicator.as_mut() { // indicator.layout( // SizeConstraint::strict_along( // Axis::Vertical, // line_height * style.code_actions.vertical_scale, // ), // editor, // cx, // ); // } // } // if let Some((_, hover_popovers)) = hover.as_mut() { // for hover_popover in hover_popovers.iter_mut() { // hover_popover.layout( // SizeConstraint { // min: gpui::Point::zero(), // max: vec2f( // (120. * em_width) // Default size // .min(size.x() / 2.) // Shrink to half of the editor width // .max(MIN_POPOVER_CHARACTER_WIDTH * em_width), // Apply minimum width of 20 characters // (16. * line_height) // Default size // .min(size.y() / 2.) // Shrink to half of the editor height // .max(MIN_POPOVER_LINE_HEIGHT * line_height), // Apply minimum height of 4 lines // ), // }, // editor, // cx, // ); // } // } // let invisible_symbol_font_size = self.style.text.font_size / 2.0; // let invisible_symbol_style = RunStyle { // color: self.style.whitespace, // font_id: self.style.text.font_id, // underline: Default::default(), // }; // ( // size, // LayoutState { // mode, // position_map: Arc::new(PositionMap { // size, // scroll_max, // line_layouts, // line_height, // em_width, // em_advance, // snapshot, // }), // visible_display_row_range: start_row..end_row, // wrap_guides, // gutter_size, // gutter_padding, // text_size, // scrollbar_row_range, // show_scrollbars, // is_singleton, // max_row, // gutter_margin, // active_rows, // highlighted_rows, // highlighted_ranges, // fold_ranges, // line_number_layouts, // display_hunks, // blocks, // selections, // context_menu, // code_actions_indicator, // fold_indicators, // tab_invisible: cx.text_layout_cache().layout_str( // "→", // invisible_symbol_font_size, // &[("→".len(), invisible_symbol_style)], // ), // space_invisible: cx.text_layout_cache().layout_str( // "•", // invisible_symbol_font_size, // &[("•".len(), invisible_symbol_style)], // ), // hover_popovers: hover, // }, // ) // } // fn paint( // &mut self, // bounds: Bounds, // visible_bounds: Bounds, // layout: &mut Self::LayoutState, // editor: &mut Editor, // cx: &mut ViewContext, // ) -> Self::PaintState { // let visible_bounds = bounds.intersection(visible_bounds).unwrap_or_default(); // cx.scene().push_layer(Some(visible_bounds)); // let gutter_bounds = Bounds::new(bounds.origin(), layout.gutter_size); // let text_bounds = Bounds::new( // bounds.origin() + vec2f(layout.gutter_size.x(), 0.0), // layout.text_size, // ); // Self::attach_mouse_handlers( // &layout.position_map, // layout.hover_popovers.is_some(), // visible_bounds, // text_bounds, // gutter_bounds, // bounds, // cx, // ); // self.paint_background(gutter_bounds, text_bounds, layout, cx); // if layout.gutter_size.x() > 0. { // self.paint_gutter(gutter_bounds, visible_bounds, layout, editor, cx); // } // self.paint_text(text_bounds, visible_bounds, layout, editor, cx); // cx.scene().push_layer(Some(bounds)); // if !layout.blocks.is_empty() { // self.paint_blocks(bounds, visible_bounds, layout, editor, cx); // } // self.paint_scrollbar(bounds, layout, &editor, cx); // cx.scene().pop_layer(); // cx.scene().pop_layer(); // } // fn rect_for_text_range( // &self, // range_utf16: Range, // bounds: Bounds, // _: Bounds, // layout: &Self::LayoutState, // _: &Self::PaintState, // _: &Editor, // _: &ViewContext, // ) -> Option> { // let text_bounds = Bounds::new( // bounds.origin() + vec2f(layout.gutter_size.x(), 0.0), // layout.text_size, // ); // let content_origin = text_bounds.origin() + vec2f(layout.gutter_margin, 0.); // let scroll_position = layout.position_map.snapshot.scroll_position(); // let start_row = scroll_position.y() as u32; // let scroll_top = scroll_position.y() * layout.position_map.line_height; // let scroll_left = scroll_position.x() * layout.position_map.em_width; // let range_start = OffsetUtf16(range_utf16.start) // .to_display_point(&layout.position_map.snapshot.display_snapshot); // if range_start.row() < start_row { // return None; // } // let line = &layout // .position_map // .line_layouts // .get((range_start.row() - start_row) as usize)? // .line; // let range_start_x = line.x_for_index(range_start.column() as usize); // let range_start_y = range_start.row() as f32 * layout.position_map.line_height; // Some(Bounds::new( // content_origin // + vec2f( // range_start_x, // range_start_y + layout.position_map.line_height, // ) // - vec2f(scroll_left, scroll_top), // vec2f( // layout.position_map.em_width, // layout.position_map.line_height, // ), // )) // } // fn debug( // &self, // bounds: Bounds, // _: &Self::LayoutState, // _: &Self::PaintState, // _: &Editor, // _: &ViewContext, // ) -> json::Value { // json!({ // "type": "BufferElement", // "bounds": bounds.to_json() // }) // } // } type BufferRow = u32; // pub struct LayoutState { // position_map: Arc, // gutter_size: gpui::Point, // gutter_padding: f32, // gutter_margin: f32, // text_size: gpui::Point, // mode: EditorMode, // wrap_guides: SmallVec<[(f32, bool); 2]>, // visible_display_row_range: Range, // active_rows: BTreeMap, // highlighted_rows: Option>, // line_number_layouts: Vec>, // display_hunks: Vec, // blocks: Vec, // highlighted_ranges: Vec<(Range, Color)>, // fold_ranges: Vec<(BufferRow, Range, Color)>, // selections: Vec<(SelectionStyle, Vec)>, // scrollbar_row_range: Range, // show_scrollbars: bool, // is_singleton: bool, // max_row: u32, // context_menu: Option<(DisplayPoint, AnyElement)>, // code_actions_indicator: Option<(u32, AnyElement)>, // hover_popovers: Option<(DisplayPoint, Vec>)>, // fold_indicators: Vec>>, // tab_invisible: Line, // space_invisible: Line, // } struct PositionMap { size: Size, line_height: Pixels, scroll_max: Size, em_width: Pixels, em_advance: Pixels, line_layouts: Vec, snapshot: EditorSnapshot, } #[derive(Debug, Copy, Clone)] pub struct PointForPosition { pub previous_valid: DisplayPoint, pub next_valid: DisplayPoint, pub exact_unclipped: DisplayPoint, pub column_overshoot_after_line_end: u32, } impl PointForPosition { #[cfg(test)] pub fn valid(valid: DisplayPoint) -> Self { Self { previous_valid: valid, next_valid: valid, exact_unclipped: valid, column_overshoot_after_line_end: 0, } } pub fn as_valid(&self) -> Option { if self.previous_valid == self.exact_unclipped && self.next_valid == self.exact_unclipped { Some(self.previous_valid) } else { None } } } impl PositionMap { fn point_for_position( &self, text_bounds: Bounds, position: gpui::Point, ) -> PointForPosition { let scroll_position = self.snapshot.scroll_position(); let position = position - text_bounds.origin; let y = position.y.max(px(0.)).min(self.size.width); let x = position.x + (scroll_position.x * self.em_width); let row = (y / self.line_height + scroll_position.y).into(); let (column, x_overshoot_after_line_end) = if let Some(line) = self .line_layouts .get(row as usize - scroll_position.y.into()) .map(|line_with_spaces| &line_with_spaces.line) { if let Some(ix) = line.index_for_x(x) { (ix as u32, 0.0) } else { (line.len() as u32, px(0.).max(x - line.width())) } } else { (0, x) }; let mut exact_unclipped = DisplayPoint::new(row, column); let previous_valid = self.snapshot.clip_point(exact_unclipped, Bias::Left); let next_valid = self.snapshot.clip_point(exact_unclipped, Bias::Right); let column_overshoot_after_line_end = (x_overshoot_after_line_end / self.em_advance) as u32; *exact_unclipped.column_mut() += column_overshoot_after_line_end; PointForPosition { previous_valid, next_valid, exact_unclipped, column_overshoot_after_line_end, } } } struct BlockLayout { row: u32, element: AnyElement, style: BlockStyle, } fn layout_line( row: u32, snapshot: &EditorSnapshot, style: &EditorStyle, text_system: &TextSystem, ) -> Line { let mut line = snapshot.line(row); if line.len() > MAX_LINE_LEN { let mut len = MAX_LINE_LEN; while !line.is_char_boundary(len) { len -= 1; } line.truncate(len); } text_system.layout_str( &line, style.text.font_size, &[TextRun { len: snapshot.line_len(row) as usize, font: style.text.font.clone(), color: Hsla::black(), underline: Default::default(), }], ) } #[derive(Debug)] pub struct Cursor { origin: gpui::Point, block_width: Pixels, line_height: Pixels, color: Hsla, shape: CursorShape, block_text: Option, } impl Cursor { // pub fn new( // origin: gpui::Point, // block_width: f32, // line_height: f32, // color: Color, // shape: CursorShape, // block_text: Option, // ) -> Cursor { // Cursor { // origin, // block_width, // line_height, // color, // shape, // block_text, // } // } // pub fn bounding_rect(&self, origin: gpui::Point) -> Bounds { // Bounds::new( // self.origin + origin, // vec2f(self.block_width, self.line_height), // ) // } // pub fn paint(&self, origin: gpui::Point, cx: &mut WindowContext) { // let bounds = match self.shape { // CursorShape::Bar => Bounds::new(self.origin + origin, vec2f(2.0, self.line_height)), // CursorShape::Block | CursorShape::Hollow => Bounds::new( // self.origin + origin, // vec2f(self.block_width, self.line_height), // ), // CursorShape::Underscore => Bounds::new( // self.origin + origin + gpui::Point::new(0.0, self.line_height - 2.0), // vec2f(self.block_width, 2.0), // ), // }; // //Draw background or border quad // if matches!(self.shape, CursorShape::Hollow) { // cx.scene().push_quad(Quad { // bounds, // background: None, // border: Border::all(1., self.color).into(), // corner_radii: Default::default(), // }); // } else { // cx.scene().push_quad(Quad { // bounds, // background: Some(self.color), // border: Default::default(), // corner_radii: Default::default(), // }); // } // if let Some(block_text) = &self.block_text { // block_text.paint(self.origin + origin, bounds, self.line_height, cx); // } // } // pub fn shape(&self) -> CursorShape { // self.shape // } } #[derive(Debug)] pub struct HighlightedRange { pub start_y: Pixels, pub line_height: Pixels, pub lines: Vec, pub color: Hsla, pub corner_radius: Pixels, } #[derive(Debug)] pub struct HighlightedRangeLine { pub start_x: f32, pub end_x: f32, } impl HighlightedRange { // pub fn paint(&self, bounds: Bounds, cx: &mut WindowContext) { // if self.lines.len() >= 2 && self.lines[0].start_x > self.lines[1].end_x { // self.paint_lines(self.start_y, &self.lines[0..1], bounds, cx); // self.paint_lines( // self.start_y + self.line_height, // &self.lines[1..], // bounds, // cx, // ); // } else { // self.paint_lines(self.start_y, &self.lines, bounds, cx); // } // } // fn paint_lines( // &self, // start_y: f32, // lines: &[HighlightedRangeLine], // bounds: Bounds, // cx: &mut WindowContext, // ) { // if lines.is_empty() { // return; // } // let mut path = PathBuilder::new(); // let first_line = lines.first().unwrap(); // let last_line = lines.last().unwrap(); // let first_top_left = vec2f(first_line.start_x, start_y); // let first_top_right = vec2f(first_line.end_x, start_y); // let curve_height = vec2f(0., self.corner_radius); // let curve_width = |start_x: f32, end_x: f32| { // let max = (end_x - start_x) / 2.; // let width = if max < self.corner_radius { // max // } else { // self.corner_radius // }; // vec2f(width, 0.) // }; // let top_curve_width = curve_width(first_line.start_x, first_line.end_x); // path.reset(first_top_right - top_curve_width); // path.curve_to(first_top_right + curve_height, first_top_right); // let mut iter = lines.iter().enumerate().peekable(); // while let Some((ix, line)) = iter.next() { // let bottom_right = vec2f(line.end_x, start_y + (ix + 1) as f32 * self.line_height); // if let Some((_, next_line)) = iter.peek() { // let next_top_right = vec2f(next_line.end_x, bottom_right.y()); // match next_top_right.x().partial_cmp(&bottom_right.x()).unwrap() { // Ordering::Equal => { // path.line_to(bottom_right); // } // Ordering::Less => { // let curve_width = curve_width(next_top_right.x(), bottom_right.x()); // path.line_to(bottom_right - curve_height); // if self.corner_radius > 0. { // path.curve_to(bottom_right - curve_width, bottom_right); // } // path.line_to(next_top_right + curve_width); // if self.corner_radius > 0. { // path.curve_to(next_top_right + curve_height, next_top_right); // } // } // Ordering::Greater => { // let curve_width = curve_width(bottom_right.x(), next_top_right.x()); // path.line_to(bottom_right - curve_height); // if self.corner_radius > 0. { // path.curve_to(bottom_right + curve_width, bottom_right); // } // path.line_to(next_top_right - curve_width); // if self.corner_radius > 0. { // path.curve_to(next_top_right + curve_height, next_top_right); // } // } // } // } else { // let curve_width = curve_width(line.start_x, line.end_x); // path.line_to(bottom_right - curve_height); // if self.corner_radius > 0. { // path.curve_to(bottom_right - curve_width, bottom_right); // } // let bottom_left = vec2f(line.start_x, bottom_right.y()); // path.line_to(bottom_left + curve_width); // if self.corner_radius > 0. { // path.curve_to(bottom_left - curve_height, bottom_left); // } // } // } // if first_line.start_x > last_line.start_x { // let curve_width = curve_width(last_line.start_x, first_line.start_x); // let second_top_left = vec2f(last_line.start_x, start_y + self.line_height); // path.line_to(second_top_left + curve_height); // if self.corner_radius > 0. { // path.curve_to(second_top_left + curve_width, second_top_left); // } // let first_bottom_left = vec2f(first_line.start_x, second_top_left.y()); // path.line_to(first_bottom_left - curve_width); // if self.corner_radius > 0. { // path.curve_to(first_bottom_left - curve_height, first_bottom_left); // } // } // path.line_to(first_top_left + curve_height); // if self.corner_radius > 0. { // path.curve_to(first_top_left + top_curve_width, first_top_left); // } // path.line_to(first_top_right - top_curve_width); // cx.scene().push_path(path.build(self.color, Some(bounds))); // } } // fn range_to_bounds( // range: &Range, // content_origin: gpui::Point, // scroll_left: f32, // scroll_top: f32, // visible_row_range: &Range, // line_end_overshoot: f32, // position_map: &PositionMap, // ) -> impl Iterator> { // let mut bounds: SmallVec<[Bounds; 1]> = SmallVec::new(); // if range.start == range.end { // return bounds.into_iter(); // } // let start_row = visible_row_range.start; // let end_row = visible_row_range.end; // let row_range = if range.end.column() == 0 { // cmp::max(range.start.row(), start_row)..cmp::min(range.end.row(), end_row) // } else { // cmp::max(range.start.row(), start_row)..cmp::min(range.end.row() + 1, end_row) // }; // let first_y = // content_origin.y() + row_range.start as f32 * position_map.line_height - scroll_top; // for (idx, row) in row_range.enumerate() { // let line_layout = &position_map.line_layouts[(row - start_row) as usize].line; // let start_x = if row == range.start.row() { // content_origin.x() + line_layout.x_for_index(range.start.column() as usize) // - scroll_left // } else { // content_origin.x() - scroll_left // }; // let end_x = if row == range.end.row() { // content_origin.x() + line_layout.x_for_index(range.end.column() as usize) - scroll_left // } else { // content_origin.x() + line_layout.width() + line_end_overshoot - scroll_left // }; // bounds.push(Bounds::from_points( // vec2f(start_x, first_y + position_map.line_height * idx as f32), // vec2f(end_x, first_y + position_map.line_height * (idx + 1) as f32), // )) // } // bounds.into_iter() // } pub fn scale_vertical_mouse_autoscroll_delta(delta: f32) -> f32 { delta.powf(1.5) / 100.0 } fn scale_horizontal_mouse_autoscroll_delta(delta: f32) -> f32 { delta.powf(1.2) / 300.0 } // #[cfg(test)] // mod tests { // use super::*; // use crate::{ // display_map::{BlockDisposition, BlockProperties}, // editor_tests::{init_test, update_test_language_settings}, // Editor, MultiBuffer, // }; // use gpui::TestAppContext; // use language::language_settings; // use log::info; // use std::{num::NonZeroU32, sync::Arc}; // use util::test::sample_text; // #[gpui::test] // fn test_layout_line_numbers(cx: &mut TestAppContext) { // init_test(cx, |_| {}); // let editor = cx // .add_window(|cx| { // let buffer = MultiBuffer::build_simple(&sample_text(6, 6, 'a'), cx); // Editor::new(EditorMode::Full, buffer, None, None, cx) // }) // .root(cx); // let element = EditorElement::new(editor.read_with(cx, |editor, cx| editor.style(cx))); // let layouts = editor.update(cx, |editor, cx| { // let snapshot = editor.snapshot(cx); // element // .layout_line_numbers( // 0..6, // &Default::default(), // DisplayPoint::new(0, 0), // false, // &snapshot, // cx, // ) // .0 // }); // assert_eq!(layouts.len(), 6); // let relative_rows = editor.update(cx, |editor, cx| { // let snapshot = editor.snapshot(cx); // element.calculate_relative_line_numbers(&snapshot, &(0..6), Some(3)) // }); // assert_eq!(relative_rows[&0], 3); // assert_eq!(relative_rows[&1], 2); // assert_eq!(relative_rows[&2], 1); // // current line has no relative number // assert_eq!(relative_rows[&4], 1); // assert_eq!(relative_rows[&5], 2); // // works if cursor is before screen // let relative_rows = editor.update(cx, |editor, cx| { // let snapshot = editor.snapshot(cx); // element.calculate_relative_line_numbers(&snapshot, &(3..6), Some(1)) // }); // assert_eq!(relative_rows.len(), 3); // assert_eq!(relative_rows[&3], 2); // assert_eq!(relative_rows[&4], 3); // assert_eq!(relative_rows[&5], 4); // // works if cursor is after screen // let relative_rows = editor.update(cx, |editor, cx| { // let snapshot = editor.snapshot(cx); // element.calculate_relative_line_numbers(&snapshot, &(0..3), Some(6)) // }); // assert_eq!(relative_rows.len(), 3); // assert_eq!(relative_rows[&0], 5); // assert_eq!(relative_rows[&1], 4); // assert_eq!(relative_rows[&2], 3); // } // #[gpui::test] // async fn test_vim_visual_selections(cx: &mut TestAppContext) { // init_test(cx, |_| {}); // let editor = cx // .add_window(|cx| { // let buffer = MultiBuffer::build_simple(&(sample_text(6, 6, 'a') + "\n"), cx); // Editor::new(EditorMode::Full, buffer, None, None, cx) // }) // .root(cx); // let mut element = EditorElement::new(editor.read_with(cx, |editor, cx| editor.style(cx))); // let (_, state) = editor.update(cx, |editor, cx| { // editor.cursor_shape = CursorShape::Block; // editor.change_selections(None, cx, |s| { // s.select_ranges([ // Point::new(0, 0)..Point::new(1, 0), // Point::new(3, 2)..Point::new(3, 3), // Point::new(5, 6)..Point::new(6, 0), // ]); // }); // element.layout( // SizeConstraint::new(vec2f(500., 500.), vec2f(500., 500.)), // editor, // cx, // ) // }); // assert_eq!(state.selections.len(), 1); // let local_selections = &state.selections[0].1; // assert_eq!(local_selections.len(), 3); // // moves cursor back one line // assert_eq!(local_selections[0].head, DisplayPoint::new(0, 6)); // assert_eq!( // local_selections[0].range, // DisplayPoint::new(0, 0)..DisplayPoint::new(1, 0) // ); // // moves cursor back one column // assert_eq!( // local_selections[1].range, // DisplayPoint::new(3, 2)..DisplayPoint::new(3, 3) // ); // assert_eq!(local_selections[1].head, DisplayPoint::new(3, 2)); // // leaves cursor on the max point // assert_eq!( // local_selections[2].range, // DisplayPoint::new(5, 6)..DisplayPoint::new(6, 0) // ); // assert_eq!(local_selections[2].head, DisplayPoint::new(6, 0)); // // active lines does not include 1 (even though the range of the selection does) // assert_eq!( // state.active_rows.keys().cloned().collect::>(), // vec![0, 3, 5, 6] // ); // // multi-buffer support // // in DisplayPoint co-ordinates, this is what we're dealing with: // // 0: [[file // // 1: header]] // // 2: aaaaaa // // 3: bbbbbb // // 4: cccccc // // 5: // // 6: ... // // 7: ffffff // // 8: gggggg // // 9: hhhhhh // // 10: // // 11: [[file // // 12: header]] // // 13: bbbbbb // // 14: cccccc // // 15: dddddd // let editor = cx // .add_window(|cx| { // let buffer = MultiBuffer::build_multi( // [ // ( // &(sample_text(8, 6, 'a') + "\n"), // vec![ // Point::new(0, 0)..Point::new(3, 0), // Point::new(4, 0)..Point::new(7, 0), // ], // ), // ( // &(sample_text(8, 6, 'a') + "\n"), // vec![Point::new(1, 0)..Point::new(3, 0)], // ), // ], // cx, // ); // Editor::new(EditorMode::Full, buffer, None, None, cx) // }) // .root(cx); // let mut element = EditorElement::new(editor.read_with(cx, |editor, cx| editor.style(cx))); // let (_, state) = editor.update(cx, |editor, cx| { // editor.cursor_shape = CursorShape::Block; // editor.change_selections(None, cx, |s| { // s.select_display_ranges([ // DisplayPoint::new(4, 0)..DisplayPoint::new(7, 0), // DisplayPoint::new(10, 0)..DisplayPoint::new(13, 0), // ]); // }); // element.layout( // SizeConstraint::new(vec2f(500., 500.), vec2f(500., 500.)), // editor, // cx, // ) // }); // assert_eq!(state.selections.len(), 1); // let local_selections = &state.selections[0].1; // assert_eq!(local_selections.len(), 2); // // moves cursor on excerpt boundary back a line // // and doesn't allow selection to bleed through // assert_eq!( // local_selections[0].range, // DisplayPoint::new(4, 0)..DisplayPoint::new(6, 0) // ); // assert_eq!(local_selections[0].head, DisplayPoint::new(5, 0)); // // moves cursor on buffer boundary back two lines // // and doesn't allow selection to bleed through // assert_eq!( // local_selections[1].range, // DisplayPoint::new(10, 0)..DisplayPoint::new(11, 0) // ); // assert_eq!(local_selections[1].head, DisplayPoint::new(10, 0)); // } // #[gpui::test] // fn test_layout_with_placeholder_text_and_blocks(cx: &mut TestAppContext) { // init_test(cx, |_| {}); // let editor = cx // .add_window(|cx| { // let buffer = MultiBuffer::build_simple("", cx); // Editor::new(EditorMode::Full, buffer, None, None, cx) // }) // .root(cx); // editor.update(cx, |editor, cx| { // editor.set_placeholder_text("hello", cx); // editor.insert_blocks( // [BlockProperties { // style: BlockStyle::Fixed, // disposition: BlockDisposition::Above, // height: 3, // position: Anchor::min(), // render: Arc::new(|_| Empty::new().into_any()), // }], // None, // cx, // ); // // Blur the editor so that it displays placeholder text. // cx.blur(); // }); // let mut element = EditorElement::new(editor.read_with(cx, |editor, cx| editor.style(cx))); // let (size, mut state) = editor.update(cx, |editor, cx| { // element.layout( // SizeConstraint::new(vec2f(500., 500.), vec2f(500., 500.)), // editor, // cx, // ) // }); // assert_eq!(state.position_map.line_layouts.len(), 4); // assert_eq!( // state // .line_number_layouts // .iter() // .map(Option::is_some) // .collect::>(), // &[false, false, false, true] // ); // // Don't panic. // let bounds = Bounds::new(Default::default(), size); // editor.update(cx, |editor, cx| { // element.paint(bounds, bounds, &mut state, editor, cx); // }); // } // #[gpui::test] // fn test_all_invisibles_drawing(cx: &mut TestAppContext) { // const TAB_SIZE: u32 = 4; // let input_text = "\t \t|\t| a b"; // let expected_invisibles = vec![ // Invisible::Tab { // line_start_offset: 0, // }, // Invisible::Whitespace { // line_offset: TAB_SIZE as usize, // }, // Invisible::Tab { // line_start_offset: TAB_SIZE as usize + 1, // }, // Invisible::Tab { // line_start_offset: TAB_SIZE as usize * 2 + 1, // }, // Invisible::Whitespace { // line_offset: TAB_SIZE as usize * 3 + 1, // }, // Invisible::Whitespace { // line_offset: TAB_SIZE as usize * 3 + 3, // }, // ]; // assert_eq!( // expected_invisibles.len(), // input_text // .chars() // .filter(|initial_char| initial_char.is_whitespace()) // .count(), // "Hardcoded expected invisibles differ from the actual ones in '{input_text}'" // ); // init_test(cx, |s| { // s.defaults.show_whitespaces = Some(ShowWhitespaceSetting::All); // s.defaults.tab_size = NonZeroU32::new(TAB_SIZE); // }); // let actual_invisibles = // collect_invisibles_from_new_editor(cx, EditorMode::Full, &input_text, 500.0); // assert_eq!(expected_invisibles, actual_invisibles); // } // #[gpui::test] // fn test_invisibles_dont_appear_in_certain_editors(cx: &mut TestAppContext) { // init_test(cx, |s| { // s.defaults.show_whitespaces = Some(ShowWhitespaceSetting::All); // s.defaults.tab_size = NonZeroU32::new(4); // }); // for editor_mode_without_invisibles in [ // EditorMode::SingleLine, // EditorMode::AutoHeight { max_lines: 100 }, // ] { // let invisibles = collect_invisibles_from_new_editor( // cx, // editor_mode_without_invisibles, // "\t\t\t| | a b", // 500.0, // ); // assert!(invisibles.is_empty(), // "For editor mode {editor_mode_without_invisibles:?} no invisibles was expected but got {invisibles:?}"); // } // } // #[gpui::test] // fn test_wrapped_invisibles_drawing(cx: &mut TestAppContext) { // let tab_size = 4; // let input_text = "a\tbcd ".repeat(9); // let repeated_invisibles = [ // Invisible::Tab { // line_start_offset: 1, // }, // Invisible::Whitespace { // line_offset: tab_size as usize + 3, // }, // Invisible::Whitespace { // line_offset: tab_size as usize + 4, // }, // Invisible::Whitespace { // line_offset: tab_size as usize + 5, // }, // ]; // let expected_invisibles = std::iter::once(repeated_invisibles) // .cycle() // .take(9) // .flatten() // .collect::>(); // assert_eq!( // expected_invisibles.len(), // input_text // .chars() // .filter(|initial_char| initial_char.is_whitespace()) // .count(), // "Hardcoded expected invisibles differ from the actual ones in '{input_text}'" // ); // info!("Expected invisibles: {expected_invisibles:?}"); // init_test(cx, |_| {}); // // Put the same string with repeating whitespace pattern into editors of various size, // // take deliberately small steps during resizing, to put all whitespace kinds near the wrap point. // let resize_step = 10.0; // let mut editor_width = 200.0; // while editor_width <= 1000.0 { // update_test_language_settings(cx, |s| { // s.defaults.tab_size = NonZeroU32::new(tab_size); // s.defaults.show_whitespaces = Some(ShowWhitespaceSetting::All); // s.defaults.preferred_line_length = Some(editor_width as u32); // s.defaults.soft_wrap = Some(language_settings::SoftWrap::PreferredLineLength); // }); // let actual_invisibles = // collect_invisibles_from_new_editor(cx, EditorMode::Full, &input_text, editor_width); // // Whatever the editor size is, ensure it has the same invisible kinds in the same order // // (no good guarantees about the offsets: wrapping could trigger padding and its tests should check the offsets). // let mut i = 0; // for (actual_index, actual_invisible) in actual_invisibles.iter().enumerate() { // i = actual_index; // match expected_invisibles.get(i) { // Some(expected_invisible) => match (expected_invisible, actual_invisible) { // (Invisible::Whitespace { .. }, Invisible::Whitespace { .. }) // | (Invisible::Tab { .. }, Invisible::Tab { .. }) => {} // _ => { // panic!("At index {i}, expected invisible {expected_invisible:?} does not match actual {actual_invisible:?} by kind. Actual invisibles: {actual_invisibles:?}") // } // }, // None => panic!("Unexpected extra invisible {actual_invisible:?} at index {i}"), // } // } // let missing_expected_invisibles = &expected_invisibles[i + 1..]; // assert!( // missing_expected_invisibles.is_empty(), // "Missing expected invisibles after index {i}: {missing_expected_invisibles:?}" // ); // editor_width += resize_step; // } // } // fn collect_invisibles_from_new_editor( // cx: &mut TestAppContext, // editor_mode: EditorMode, // input_text: &str, // editor_width: f32, // ) -> Vec { // info!( // "Creating editor with mode {editor_mode:?}, width {editor_width} and text '{input_text}'" // ); // let editor = cx // .add_window(|cx| { // let buffer = MultiBuffer::build_simple(&input_text, cx); // Editor::new(editor_mode, buffer, None, None, cx) // }) // .root(cx); // let mut element = EditorElement::new(editor.read_with(cx, |editor, cx| editor.style(cx))); // let (_, layout_state) = editor.update(cx, |editor, cx| { // editor.set_soft_wrap_mode(language_settings::SoftWrap::EditorWidth, cx); // editor.set_wrap_width(Some(editor_width), cx); // element.layout( // SizeConstraint::new(vec2f(editor_width, 500.), vec2f(editor_width, 500.)), // editor, // cx, // ) // }); // layout_state // .position_map // .line_layouts // .iter() // .map(|line_with_invisibles| &line_with_invisibles.invisibles) // .flatten() // .cloned() // .collect() // } // }