@@ -51,10 +51,6 @@ pub(super) const RIGHT_MARGIN: Pixels = px(10.);
5151pub ( super ) const LINE_NUMBER_RIGHT_MARGIN : Pixels = px ( 6. ) ;
5252const FOLD_ICON_WIDTH : Pixels = px ( 14. ) ;
5353const FOLD_ICON_HITBOX_WIDTH : Pixels = px ( 18. ) ;
54- /// Width and height of a line decoration gutter marker.
55- const GUTTER_MARKER_SIZE : Pixels = px ( 12. ) ;
56- /// Space between a gutter marker and the line numbers.
57- const GUTTER_MARKER_GAP : Pixels = px ( 4. ) ;
5854const MAX_HIGHLIGHT_LINE_LENGTH : usize = 10_000 ;
5955const MIN_LINE_NUMBER_DIGITS : usize = 3 ;
6056const MAX_LINE_NUMBER_DIGITS : usize = 7 ;
@@ -1127,7 +1123,9 @@ impl<M: InputModeKind> TextElement<M> {
11271123 None ,
11281124 ) ;
11291125
1130- Self :: gutter_marker_width ( state) + empty_line_number. width + LINE_NUMBER_RIGHT_MARGIN
1126+ Self :: gutter_marker_width ( state, window)
1127+ + empty_line_number. width
1128+ + LINE_NUMBER_RIGHT_MARGIN
11311129 } else if state. is_code_editor ( ) {
11321130 LINE_NUMBER_RIGHT_MARGIN
11331131 } else {
@@ -1147,12 +1145,24 @@ impl<M: InputModeKind> TextElement<M> {
11471145 /// The slot is reserved while line numbers are shown, a marker renderer is set
11481146 /// and a line decoration collection has a provider, so the gutter keeps its
11491147 /// width as marked rows scroll in and out of view.
1150- fn gutter_marker_width ( state : & InputBaseState < M > ) -> Pixels {
1148+ fn gutter_marker_width ( state : & InputBaseState < M > , window : & Window ) -> Pixels {
11511149 if state. mode . line_number ( )
1152- && state. editor_style . gutter_marker_renderer . is_some ( )
1150+ && state. editor_style . gutter_marker_renderer ( ) . is_some ( )
11531151 && state. extras . has_line_decorations ( )
11541152 {
1155- GUTTER_MARKER_SIZE + GUTTER_MARKER_GAP
1153+ let size = state
1154+ . editor_style
1155+ . gutter_marker_size ( )
1156+ . to_pixels ( window. rem_size ( ) ) ;
1157+ if size > Pixels :: ZERO {
1158+ size + state
1159+ . editor_style
1160+ . gutter_marker_gap ( )
1161+ . to_pixels ( window. rem_size ( ) )
1162+ . max ( px ( 0. ) )
1163+ } else {
1164+ px ( 0. )
1165+ }
11561166 } else {
11571167 px ( 0. )
11581168 }
@@ -1464,14 +1474,21 @@ impl<M: InputModeKind> TextElement<M> {
14641474 ) else {
14651475 return LineDecorationLayout :: default ( ) ;
14661476 } ;
1467- let ( decorations, renderer) = {
1477+ let ( decorations, renderer, marker_size ) = {
14681478 let state = self . state . read ( cx) ;
14691479 let renderer = state
14701480 . mode
14711481 . line_number ( )
1472- . then ( || state. editor_style . gutter_marker_renderer . clone ( ) )
1482+ . then ( || state. editor_style . gutter_marker_renderer ( ) . cloned ( ) )
14731483 . flatten ( ) ;
1474- ( state. extras . line_decorations ( first..last + 1 , cx) , renderer)
1484+ (
1485+ state. extras . line_decorations ( first..last + 1 , cx) ,
1486+ renderer,
1487+ state
1488+ . editor_style
1489+ . gutter_marker_size ( )
1490+ . to_pixels ( window. rem_size ( ) ) ,
1491+ )
14751492 } ;
14761493 if decorations. is_empty ( ) {
14771494 return LineDecorationLayout :: default ( ) ;
@@ -1494,18 +1511,15 @@ impl<M: InputModeKind> TextElement<M> {
14941511 if let Some ( color) = decoration. background ( ) {
14951512 layout. backgrounds . push ( ( top, height, color) ) ;
14961513 }
1497- if let ( Some ( marker) , Some ( render) ) = ( decoration. marker ( ) , renderer. as_ref ( ) ) {
1514+ if let ( Some ( marker) , Some ( render) ) = ( decoration. marker ( ) , renderer. as_ref ( ) )
1515+ && marker_size > Pixels :: ZERO
1516+ {
14981517 let origin = point (
14991518 origin_x,
1500- bounds. origin . y + top + ( line_height - GUTTER_MARKER_SIZE ) . half ( ) ,
1519+ bounds. origin . y + top + ( line_height - marker_size ) . half ( ) ,
15011520 ) ;
15021521 let mut element = render ( marker) ;
1503- element. prepaint_as_root (
1504- origin,
1505- size ( GUTTER_MARKER_SIZE , GUTTER_MARKER_SIZE ) . into ( ) ,
1506- window,
1507- cx,
1508- ) ;
1522+ element. prepaint_as_root ( origin, size ( marker_size, marker_size) . into ( ) , window, cx) ;
15091523 layout. markers . push ( element) ;
15101524 }
15111525 }
@@ -2740,7 +2754,7 @@ impl<M: InputModeKind> Element for TextElement<M> {
27402754 wrap_width,
27412755 wrapping_indent,
27422756 line_number_width,
2743- gutter_marker_width : Self :: gutter_marker_width ( state) ,
2757+ gutter_marker_width : Self :: gutter_marker_width ( state, window ) ,
27442758 ghost_lines : None ,
27452759 space_width,
27462760 lines : Rc :: new ( vec ! [ ] ) ,
@@ -3812,13 +3826,15 @@ mod tests {
38123826 let built = Rc :: new ( std:: cell:: Cell :: new ( 0 ) ) ;
38133827 let counter = built. clone ( ) ;
38143828 editor. update ( cx, |state, _| {
3815- state. set_editor_style ( crate :: input:: InputEditorStyle {
3816- gutter_marker_renderer : Some ( Rc :: new ( move |_| {
3817- counter. set ( counter. get ( ) + 1 ) ;
3818- div ( ) . into_any_element ( )
3819- } ) ) ,
3820- ..Default :: default ( )
3821- } ) ;
3829+ state. set_editor_style (
3830+ crate :: input:: InputEditorStyle :: default ( )
3831+ . with_gutter_marker_renderer ( Some ( Rc :: new ( move |_| {
3832+ counter. set ( counter. get ( ) + 1 ) ;
3833+ div ( ) . into_any_element ( )
3834+ } ) ) )
3835+ . with_gutter_marker_size ( gpui:: rems ( 0.75 ) )
3836+ . with_gutter_marker_gap ( gpui:: rems ( 0.25 ) ) ,
3837+ ) ;
38223838 } ) ;
38233839 built
38243840 }
@@ -3831,18 +3847,20 @@ mod tests {
38313847 let laid_out = Rc :: new ( std:: cell:: RefCell :: new ( Vec :: new ( ) ) ) ;
38323848 let record = laid_out. clone ( ) ;
38333849 editor. update ( cx, |state, _| {
3834- state. set_editor_style ( crate :: input:: InputEditorStyle {
3835- gutter_marker_renderer : Some ( Rc :: new ( move |_| {
3836- let record = record. clone ( ) ;
3837- gpui:: canvas (
3838- move |bounds, _, _| record. borrow_mut ( ) . push ( bounds) ,
3839- |_, _, _, _| { } ,
3840- )
3841- . size_full ( )
3842- . into_any_element ( )
3843- } ) ) ,
3844- ..Default :: default ( )
3845- } ) ;
3850+ state. set_editor_style (
3851+ crate :: input:: InputEditorStyle :: default ( )
3852+ . with_gutter_marker_renderer ( Some ( Rc :: new ( move |_| {
3853+ let record = record. clone ( ) ;
3854+ gpui:: canvas (
3855+ move |bounds, _, _| record. borrow_mut ( ) . push ( bounds) ,
3856+ |_, _, _, _| { } ,
3857+ )
3858+ . size ( gpui:: rems ( 0.75 ) )
3859+ . into_any_element ( )
3860+ } ) ) )
3861+ . with_gutter_marker_size ( gpui:: rems ( 0.75 ) )
3862+ . with_gutter_marker_gap ( gpui:: rems ( 0.25 ) ) ,
3863+ ) ;
38463864 } ) ;
38473865 laid_out
38483866 }
@@ -3959,75 +3977,95 @@ mod tests {
39593977 #[ gpui:: test]
39603978 fn gutter_markers_have_a_slot_left_of_the_line_numbers ( cx : & mut TestAppContext ) {
39613979 // Three-digit numbers fill the narrowest column; longer ones widen it.
3962- for ( lines, widest) in [ ( 150 , 99 ) , ( 1_200 , 999 ) ] {
3963- let text = format ! ( "{}\n {}" , "wide " . repeat( 80 ) , "x\n " . repeat( lines) ) ;
3964- let ( editor, window) = decoration_editor ( cx, & text, false ) ;
3965- let mut cx = VisualTestContext :: from_window ( window. into ( ) , cx) ;
3966- cx. update ( |window, cx| {
3967- let laid_out = record_markers ( & editor, cx) ;
3968- let padding = px ( 6. ) ;
3969- editor. update ( cx, |state, _| {
3970- state. set_editor_paddings ( Edges {
3971- left : padding,
3972- ..Default :: default ( )
3980+ for rem_size in [ 16. , 24. , 32. ] {
3981+ for ( lines, widest) in [ ( 150 , 99 ) , ( 1_200 , 999 ) ] {
3982+ let text = format ! ( "{}\n {}" , "wide " . repeat( 80 ) , "x\n " . repeat( lines) ) ;
3983+ let ( editor, window) = decoration_editor ( cx, & text, false ) ;
3984+ let mut cx = VisualTestContext :: from_window ( window. into ( ) , cx) ;
3985+ cx. update ( |window, cx| {
3986+ window. set_rem_size ( px ( rem_size) ) ;
3987+ let laid_out = record_markers ( & editor, cx) ;
3988+ let padding = px ( 6. ) ;
3989+ editor. update ( cx, |state, _| {
3990+ state. set_editor_paddings ( Edges {
3991+ left : padding,
3992+ ..Default :: default ( )
3993+ } ) ;
39733994 } ) ;
3974- } ) ;
3975- window. draw ( cx) . clear ( cx) ;
3976- let line_number_width = |cx : & App | {
3977- editor
3978- . read ( cx)
3979- . last_layout
3980- . as_ref ( )
3981- . unwrap ( )
3982- . line_number_width
3983- } ;
3984- let plain = line_number_width ( cx) ;
3995+ window. draw ( cx) . clear ( cx) ;
3996+ let line_number_width = |cx : & App | {
3997+ editor
3998+ . read ( cx)
3999+ . last_layout
4000+ . as_ref ( )
4001+ . unwrap ( )
4002+ . line_number_width
4003+ } ;
4004+ let plain = line_number_width ( cx) ;
39854005
3986- let collection = editor. update ( cx, |state, cx| {
3987- let height = state. last_layout . as_ref ( ) . unwrap ( ) . line_height ;
3988- state. set_scroll_offset ( point ( px ( 0. ) , -height * ( lines - 4 ) as f32 ) , cx) ;
3989- state. create_line_decorations_collection ( Bands :: new ( true ) , cx)
3990- } ) ;
3991- window. draw ( cx) . clear ( cx) ;
3992- window. draw ( cx) . clear ( cx) ;
3993- assert ! ( editor. read( cx) . visible_row_range( ) . unwrap( ) . start > widest) ;
3994-
3995- // The numbers keep their right edge, so the growth is the slot.
3996- let slot = line_number_width ( cx) - plain;
3997- let input_bounds = editor. read ( cx) . input_bounds ;
3998- let markers_x = || {
3999- let markers = laid_out. borrow ( ) ;
4000- assert ! ( !markers. is_empty( ) ) ;
4001- for marker in markers. iter ( ) {
4002- assert ! (
4003- marker. left( ) >= input_bounds. left( ) - padding
4004- && marker. right( ) <= input_bounds. left( ) + slot,
4005- "{marker:?} is not in the slot left of {lines} line numbers"
4006- ) ;
4007- }
4008- markers
4006+ let collection = editor. update ( cx, |state, cx| {
4007+ let height = state. last_layout . as_ref ( ) . unwrap ( ) . line_height ;
4008+ state. set_scroll_offset ( point ( px ( 0. ) , -height * ( lines - 4 ) as f32 ) , cx) ;
4009+ state. create_line_decorations_collection ( Bands :: new ( true ) , cx)
4010+ } ) ;
4011+ window. draw ( cx) . clear ( cx) ;
4012+ laid_out. borrow_mut ( ) . clear ( ) ;
4013+ window. draw ( cx) . clear ( cx) ;
4014+ assert ! ( editor. read( cx) . visible_row_range( ) . unwrap( ) . start > widest) ;
4015+
4016+ // The numbers keep their right edge, so the growth is the slot.
4017+ let slot = line_number_width ( cx) - plain;
4018+ let input_bounds = editor. read ( cx) . input_bounds ;
4019+ let state = editor. read ( cx) ;
4020+ let layout = state. last_layout . as_ref ( ) . unwrap ( ) ;
4021+ let row_centers = layout
4022+ . visible_buffer_lines
40094023 . iter ( )
4010- . map ( |marker| marker. left ( ) )
4011- . collect :: < Vec < _ > > ( )
4012- } ;
4013- let unscrolled = markers_x ( ) ;
4024+ . map ( |& row| {
4025+ state. row_bounds ( row) . unwrap ( ) . top ( ) + layout. line_height . half ( )
4026+ } )
4027+ . collect :: < Vec < _ > > ( ) ;
4028+ let markers_x = || {
4029+ let markers = laid_out. borrow ( ) ;
4030+ assert ! ( !markers. is_empty( ) ) ;
4031+ for marker in markers. iter ( ) {
4032+ assert ! (
4033+ marker. left( ) >= input_bounds. left( ) - padding
4034+ && marker. right( ) <= input_bounds. left( ) + slot,
4035+ "{marker:?} is not in the slot left of {lines} line numbers"
4036+ ) ;
4037+ assert ! (
4038+ row_centers
4039+ . iter( )
4040+ . any( |& center| ( marker. center( ) . y - center) . abs( ) < px( 0.01 ) ) ,
4041+ "{marker:?} is not centered on a row at rem {rem_size}"
4042+ ) ;
4043+ }
4044+ markers
4045+ . iter ( )
4046+ . map ( |marker| marker. left ( ) )
4047+ . collect :: < Vec < _ > > ( )
4048+ } ;
4049+ let unscrolled = markers_x ( ) ;
40144050
4015- // Scrolling horizontally moves the text, not the gutter.
4016- laid_out. borrow_mut ( ) . clear ( ) ;
4017- editor. update ( cx, |state, cx| {
4018- let offset = state. scroll_handle . offset ( ) ;
4019- state. set_scroll_offset ( point ( px ( -200. ) , offset. y ) , cx) ;
4051+ // Scrolling horizontally moves the text, not the gutter.
4052+ laid_out. borrow_mut ( ) . clear ( ) ;
4053+ editor. update ( cx, |state, cx| {
4054+ let offset = state. scroll_handle . offset ( ) ;
4055+ state. set_scroll_offset ( point ( px ( -200. ) , offset. y ) , cx) ;
4056+ } ) ;
4057+ window. draw ( cx) . clear ( cx) ;
4058+ laid_out. borrow_mut ( ) . clear ( ) ;
4059+ window. draw ( cx) . clear ( cx) ;
4060+ assert ! ( editor. read( cx) . scroll_handle. offset( ) . x < px( 0. ) ) ;
4061+ assert_eq ! ( markers_x( ) [ ..unscrolled. len( ) ] , unscrolled[ ..] ) ;
4062+
4063+ // Without a provider there is no slot.
4064+ collection. clear ( cx) ;
4065+ window. draw ( cx) . clear ( cx) ;
4066+ assert_eq ! ( line_number_width( cx) , plain) ;
40204067 } ) ;
4021- window. draw ( cx) . clear ( cx) ;
4022- window. draw ( cx) . clear ( cx) ;
4023- assert ! ( editor. read( cx) . scroll_handle. offset( ) . x < px( 0. ) ) ;
4024- assert_eq ! ( markers_x( ) [ ..unscrolled. len( ) ] , unscrolled[ ..] ) ;
4025-
4026- // Without a provider there is no slot.
4027- collection. clear ( cx) ;
4028- window. draw ( cx) . clear ( cx) ;
4029- assert_eq ! ( line_number_width( cx) , plain) ;
4030- } ) ;
4068+ }
40314069 }
40324070 }
40334071
0 commit comments