2023-12-20 13:31:10 -07:00
use alacritty_terminal ::{
event ::{ Event , EventListener , Notify , OnResize , WindowSize } ,
2023-12-21 09:53:31 -07:00
event_loop ::{ EventLoop , Msg , Notifier } ,
2023-12-20 13:31:10 -07:00
grid ::Dimensions ,
2024-01-11 16:20:15 -07:00
index ::{ Boundary , Column , Direction , Line , Point , Side } ,
2023-12-22 15:08:24 -07:00
selection ::{ Selection , SelectionType } ,
2023-12-20 13:31:10 -07:00
sync ::FairMutex ,
term ::{
cell ::Flags ,
2023-12-31 12:50:54 +03:00
color ::{ self , Colors } ,
2024-01-11 14:32:11 -07:00
search ::RegexSearch ,
2024-02-05 21:01:16 -07:00
viewport_to_point , Config , TermDamage , TermMode ,
2023-12-20 13:31:10 -07:00
} ,
2023-12-31 12:50:54 +03:00
tty ::{ self , Options } ,
2024-08-23 14:48:42 +03:00
vte ::ansi ::{ Color , CursorShape , NamedColor , Rgb } ,
2024-01-02 12:21:46 -07:00
Term ,
2023-12-20 13:31:10 -07:00
} ;
2024-01-17 16:09:56 +01:00
use cosmic ::{
iced ::advanced ::graphics ::text ::font_system ,
2024-01-30 08:59:19 +01:00
iced ::mouse ::ScrollDelta ,
2024-01-17 16:09:56 +01:00
widget ::{ pane_grid , segmented_button } ,
} ;
2023-12-20 13:31:10 -07:00
use cosmic_text ::{
2024-05-01 12:16:49 -06:00
Attrs , AttrsList , Buffer , BufferLine , CacheKeyFlags , Family , LineEnding , Metrics , Shaping ,
Weight , Wrap ,
2023-12-20 13:31:10 -07:00
} ;
2024-01-11 23:22:39 +03:00
use indexmap ::IndexSet ;
2023-12-20 13:31:10 -07:00
use std ::{
borrow ::Cow ,
2023-12-22 14:31:01 -07:00
collections ::HashMap ,
2024-02-09 15:45:46 -07:00
io , mem ,
2024-01-31 15:05:21 -07:00
sync ::{
atomic ::{ AtomicU32 , Ordering } ,
Arc , Weak ,
} ,
2023-12-20 13:31:10 -07:00
time ::Instant ,
} ;
use tokio ::sync ::mpsc ;
2023-12-20 14:26:31 -07:00
pub use alacritty_terminal ::grid ::Scroll as TerminalScroll ;
2024-02-09 15:45:46 -07:00
use crate ::{
2024-02-22 11:18:13 -07:00
config ::{ ColorSchemeKind , Config as AppConfig , ProfileId } ,
2024-02-09 15:45:46 -07:00
mouse_reporter ::MouseReporter ,
} ;
2024-01-11 17:47:24 +03:00
2024-06-27 09:06:31 +02:00
/// Minimum contrast between a fixed cursor color and the cell's background.
/// Duplicated from alacritty
pub const MIN_CURSOR_CONTRAST : f64 = 1.5 ;
2024-11-17 01:47:50 +01:00
/// Maximum number of linewraps followed outside of the viewport during search highlighting.
/// Duplicated from you guessed it.
/// A regex expression can start or end outside the visible screen. Therefore, without this constant, some regular expressions would not match at the top and bottom.
pub const MAX_SEARCH_LINES : usize = 100 ;
/// https://github.com/alacritty/alacritty/blob/4a7728bf7fac06a35f27f6c4f31e0d9214e5152b/alacritty/src/config/ui_config.rs#L36-L39
fn url_regex_search ( ) -> RegexSearch {
let url_regex = " (ipfs:|ipns:|magnet:|mailto:|gemini://|gopher://|https://|http://|news:|file:|git://|ssh:|ftp://) \
[ ^ \ u { 0000 } - \ u { 001 F } \ u { 007 F } - \ u { 009 F } < > \ " \\ s{-} \\ ^⟨⟩`]+ " ;
RegexSearch ::new ( url_regex ) . unwrap ( )
}
2023-12-20 13:31:10 -07:00
#[ derive(Clone, Copy, Debug) ]
pub struct Size {
2023-12-21 15:09:18 -07:00
pub width : u32 ,
pub height : u32 ,
pub cell_width : f32 ,
pub cell_height : f32 ,
2023-12-20 13:31:10 -07:00
}
impl Dimensions for Size {
fn total_lines ( & self ) -> usize {
self . screen_lines ( )
}
fn screen_lines ( & self ) -> usize {
( ( self . height as f32 ) / self . cell_height ) . floor ( ) as usize
}
fn columns ( & self ) -> usize {
( ( self . width as f32 ) / self . cell_width ) . floor ( ) as usize
}
}
impl From < Size > for WindowSize {
fn from ( size : Size ) -> Self {
Self {
num_lines : size . screen_lines ( ) as u16 ,
num_cols : size . columns ( ) as u16 ,
cell_width : size . cell_width as u16 ,
cell_height : size . cell_height as u16 ,
}
}
}
#[ derive(Clone) ]
2023-12-21 15:09:18 -07:00
pub struct EventProxy (
2024-01-17 16:09:56 +01:00
pane_grid ::Pane ,
2023-12-20 13:31:10 -07:00
segmented_button ::Entity ,
2024-10-29 18:19:23 -06:00
mpsc ::UnboundedSender < ( pane_grid ::Pane , segmented_button ::Entity , Event ) > ,
2023-12-20 13:31:10 -07:00
) ;
impl EventListener for EventProxy {
fn send_event ( & self , event : Event ) {
//TODO: handle error
2024-10-29 18:19:23 -06:00
let _ = self . 2. send ( ( self . 0 , self . 1 , event ) ) ;
2023-12-20 13:31:10 -07:00
}
}
2024-01-03 17:46:49 +03:00
fn as_bright ( mut color : Color ) -> Color {
if let Color ::Named ( named ) = color {
color = Color ::Named ( named . to_bright ( ) ) ;
}
color
}
2024-01-12 01:41:37 +03:00
fn as_dim ( mut color : Color ) -> Color {
if let Color ::Named ( named ) = color {
color = Color ::Named ( named . to_dim ( ) ) ;
}
color
}
2024-01-31 15:05:21 -07:00
pub static WINDOW_BG_COLOR : AtomicU32 = AtomicU32 ::new ( 0xFF000000 ) ;
2023-12-20 13:31:10 -07:00
fn convert_color ( colors : & Colors , color : Color ) -> cosmic_text ::Color {
let rgb = match color {
Color ::Named ( named_color ) = > match colors [ named_color ] {
Some ( rgb ) = > rgb ,
2024-04-14 20:58:19 +01:00
None = > {
if named_color = = NamedColor ::Background {
2024-01-31 11:20:15 -07:00
// Allow using an unset background
2024-01-31 15:05:21 -07:00
return cosmic_text ::Color ( WINDOW_BG_COLOR . load ( Ordering ::SeqCst ) ) ;
2024-04-14 20:58:19 +01:00
} else {
2024-01-31 11:20:15 -07:00
log ::warn! ( " missing named color {:?} " , named_color ) ;
Rgb ::default ( )
}
2024-04-14 20:58:19 +01:00
}
2023-12-20 13:31:10 -07:00
} ,
Color ::Spec ( rgb ) = > rgb ,
2024-04-14 20:58:19 +01:00
Color ::Indexed ( index ) = > {
if let Some ( rgb ) = colors [ index as usize ] {
rgb
} else {
2023-12-20 13:31:10 -07:00
log ::warn! ( " missing indexed color {} " , index ) ;
Rgb ::default ( )
}
2024-04-14 20:58:19 +01:00
}
2023-12-20 13:31:10 -07:00
} ;
cosmic_text ::Color ::rgb ( rgb . r , rgb . g , rgb . b )
}
2024-01-17 16:09:56 +01:00
type TabModel = segmented_button ::Model < segmented_button ::SingleSelect > ;
2024-08-20 04:52:34 +03:00
2024-01-17 16:09:56 +01:00
pub struct TerminalPaneGrid {
pub panes : pane_grid ::State < TabModel > ,
pub panes_created : usize ,
pub focus : pane_grid ::Pane ,
}
impl TerminalPaneGrid {
pub fn new ( model : TabModel ) -> Self {
let ( panes , pane ) = pane_grid ::State ::new ( model ) ;
let mut terminal_ids = HashMap ::new ( ) ;
terminal_ids . insert ( pane , cosmic ::widget ::Id ::unique ( ) ) ;
Self {
panes ,
panes_created : 1 ,
focus : pane ,
}
}
pub fn active ( & self ) -> Option < & TabModel > {
self . panes . get ( self . focus )
}
pub fn active_mut ( & mut self ) -> Option < & mut TabModel > {
self . panes . get_mut ( self . focus )
}
}
2024-01-11 23:22:39 +03:00
#[ derive(Debug, PartialEq, Eq, Hash, Clone) ]
pub struct Metadata {
pub bg : cosmic_text ::Color ,
pub underline_color : cosmic_text ::Color ,
pub flags : Flags ,
}
impl Metadata {
fn new ( bg : cosmic_text ::Color , underline_color : cosmic_text ::Color ) -> Self {
let flags = Flags ::empty ( ) ;
2024-01-17 16:41:35 +01:00
Self {
bg ,
underline_color ,
flags ,
}
2024-01-11 23:22:39 +03:00
}
fn with_underline_color ( self , underline_color : cosmic_text ::Color ) -> Self {
2024-01-17 16:41:35 +01:00
Self {
underline_color ,
.. self
}
2024-01-11 23:22:39 +03:00
}
fn with_flags ( self , flags : Flags ) -> Self {
Self { flags , .. self }
}
}
2023-12-20 13:31:10 -07:00
pub struct Terminal {
2024-02-09 15:45:46 -07:00
pub context_menu : Option < cosmic ::iced ::Point > ,
pub metadata_set : IndexSet < Metadata > ,
pub needs_update : bool ,
pub profile_id_opt : Option < ProfileId > ,
2024-02-20 10:21:30 -07:00
pub tab_title_override : Option < String > ,
2023-12-21 15:09:18 -07:00
pub term : Arc < FairMutex < Term < EventProxy > > > ,
2024-11-17 01:47:50 +01:00
pub url_regex_search : RegexSearch ,
pub regex_matches : Vec < alacritty_terminal ::term ::search ::Match > ,
pub active_regex_match : Option < alacritty_terminal ::term ::search ::Match > ,
2024-02-09 15:45:46 -07:00
bold_font_weight : Weight ,
buffer : Arc < Buffer > ,
2023-12-20 13:31:10 -07:00
colors : Colors ,
2024-02-09 15:45:46 -07:00
default_attrs : Attrs < 'static > ,
2024-01-12 01:58:57 +03:00
dim_font_weight : Weight ,
2024-02-09 15:45:46 -07:00
mouse_reporter : MouseReporter ,
2023-12-20 13:31:10 -07:00
notifier : Notifier ,
2024-01-11 14:31:20 -07:00
search_regex_opt : Option < RegexSearch > ,
search_value : String ,
2024-02-09 15:45:46 -07:00
size : Size ,
use_bright_bold : bool ,
2024-08-23 14:06:57 +03:00
zoom_adj : i8 ,
2023-12-20 13:31:10 -07:00
}
impl Terminal {
//TODO: error handling
pub fn new (
2024-01-17 16:09:56 +01:00
pane : pane_grid ::Pane ,
2023-12-20 13:31:10 -07:00
entity : segmented_button ::Entity ,
2024-10-29 18:19:23 -06:00
event_tx : mpsc ::UnboundedSender < ( pane_grid ::Pane , segmented_button ::Entity , Event ) > ,
2023-12-31 12:50:54 +03:00
config : Config ,
2024-01-14 12:33:28 -07:00
options : Options ,
2024-01-11 17:47:24 +03:00
app_config : & AppConfig ,
2023-12-21 09:44:44 -07:00
colors : Colors ,
2024-02-09 15:45:46 -07:00
profile_id_opt : Option < ProfileId > ,
2024-02-20 10:21:30 -07:00
tab_title_override : Option < String > ,
2024-02-09 15:45:46 -07:00
) -> Result < Self , io ::Error > {
2024-01-11 17:47:24 +03:00
let font_stretch = app_config . typed_font_stretch ( ) ;
let font_weight = app_config . font_weight ;
2024-01-12 01:58:57 +03:00
let dim_font_weight = app_config . dim_font_weight ;
2024-01-11 17:47:24 +03:00
let bold_font_weight = app_config . bold_font_weight ;
let use_bright_bold = app_config . use_bright_bold ;
2023-12-20 13:31:10 -07:00
let metrics = Metrics ::new ( 14.0 , 20.0 ) ;
2024-01-11 23:22:39 +03:00
let default_bg = convert_color ( & colors , Color ::Named ( NamedColor ::Background ) ) ;
let default_fg = convert_color ( & colors , Color ::Named ( NamedColor ::Foreground ) ) ;
let mut metadata_set = IndexSet ::new ( ) ;
let default_metada = Metadata ::new ( default_bg , default_fg ) ;
let ( default_metada_idx , _ ) = metadata_set . insert_full ( default_metada ) ;
2023-12-20 13:31:10 -07:00
//TODO: set color to default fg
let default_attrs = Attrs ::new ( )
. family ( Family ::Monospace )
2024-01-10 01:19:04 +03:00
. weight ( Weight ( font_weight ) )
. stretch ( font_stretch )
2024-01-11 23:22:39 +03:00
. color ( default_fg )
. metadata ( default_metada_idx ) ;
2023-12-20 13:31:10 -07:00
let mut buffer = Buffer ::new_empty ( metrics ) ;
let ( cell_width , cell_height ) = {
let mut font_system = font_system ( ) . write ( ) . unwrap ( ) ;
2024-02-05 15:59:07 +01:00
let font_system = font_system . raw ( ) ;
buffer . set_wrap ( font_system , Wrap ::None ) ;
2023-12-20 13:31:10 -07:00
// Use size of space to determine cell size
2024-02-05 15:59:07 +01:00
buffer . set_text ( font_system , " " , default_attrs , Shaping ::Advanced ) ;
let layout = buffer . line_layout ( font_system , 0 ) . unwrap ( ) ;
2024-01-17 23:51:40 +03:00
let w = layout [ 0 ] . w ;
buffer . set_monospace_width ( font_system , Some ( w ) ) ;
( w , metrics . line_height )
2023-12-20 13:31:10 -07:00
} ;
2023-12-21 09:53:31 -07:00
let size = Size {
2023-12-20 13:31:10 -07:00
width : ( 80.0 * cell_width ) . ceil ( ) as u32 ,
height : ( 24.0 * cell_height ) . ceil ( ) as u32 ,
cell_width ,
cell_height ,
} ;
2024-01-17 16:09:56 +01:00
let event_proxy = EventProxy ( pane , entity , event_tx ) ;
2023-12-20 13:31:10 -07:00
let term = Arc ::new ( FairMutex ::new ( Term ::new (
2023-12-31 12:50:54 +03:00
config ,
2023-12-20 13:31:10 -07:00
& size ,
event_proxy . clone ( ) ,
) ) ) ;
let window_id = 0 ;
2024-02-09 15:45:46 -07:00
let pty = tty ::new ( & options , size . into ( ) , window_id ) ? ;
2023-12-20 13:31:10 -07:00
2024-04-05 16:35:17 +02:00
let pty_event_loop = EventLoop ::new ( term . clone ( ) , event_proxy , pty , options . hold , false ) ? ;
2023-12-20 13:31:10 -07:00
let notifier = Notifier ( pty_event_loop . channel ( ) ) ;
2023-12-21 09:53:31 -07:00
let _pty_join_handle = pty_event_loop . spawn ( ) ;
2023-12-20 13:31:10 -07:00
2024-02-09 15:45:46 -07:00
Ok ( Self {
2024-11-17 01:47:50 +01:00
active_regex_match : None ,
url_regex_search : url_regex_search ( ) ,
regex_matches : Vec ::new ( ) ,
2024-01-10 01:19:04 +03:00
bold_font_weight : Weight ( bold_font_weight ) ,
2023-12-20 13:31:10 -07:00
buffer : Arc ::new ( buffer ) ,
2024-02-09 15:45:46 -07:00
colors ,
2023-12-22 15:00:50 -07:00
context_menu : None ,
2024-02-09 15:45:46 -07:00
default_attrs ,
dim_font_weight : Weight ( dim_font_weight ) ,
metadata_set ,
mouse_reporter : Default ::default ( ) ,
2024-01-04 14:25:31 -07:00
needs_update : true ,
2024-02-09 15:45:46 -07:00
notifier ,
profile_id_opt ,
2024-01-11 14:31:20 -07:00
search_regex_opt : None ,
search_value : String ::new ( ) ,
2024-02-09 15:45:46 -07:00
size ,
2024-02-20 10:21:30 -07:00
tab_title_override ,
2024-02-09 15:45:46 -07:00
term ,
use_bright_bold ,
2024-08-23 14:06:57 +03:00
zoom_adj : Default ::default ( ) ,
2024-02-09 15:45:46 -07:00
} )
2023-12-20 13:31:10 -07:00
}
pub fn buffer_weak ( & self ) -> Weak < Buffer > {
Arc ::downgrade ( & self . buffer )
}
/// Get the internal [`Buffer`]
pub fn with_buffer < F : FnOnce ( & Buffer ) -> T , T > ( & self , f : F ) -> T {
f ( & self . buffer )
}
/// Get the internal [`Buffer`], mutably
pub fn with_buffer_mut < F : FnOnce ( & mut Buffer ) -> T , T > ( & mut self , f : F ) -> T {
f ( Arc ::make_mut ( & mut self . buffer ) )
}
pub fn colors ( & self ) -> & Colors {
& self . colors
}
pub fn default_attrs ( & self ) -> & Attrs < 'static > {
& self . default_attrs
}
pub fn size ( & self ) -> Size {
self . size
}
2024-08-20 04:52:34 +03:00
pub fn zoom_adj ( & self ) -> i8 {
self . zoom_adj
}
pub fn set_zoom_adj ( & mut self , value : i8 ) {
self . zoom_adj = value ;
}
2023-12-20 13:31:10 -07:00
pub fn redraw ( & self ) -> bool {
self . buffer . redraw ( )
}
pub fn set_redraw ( & mut self , redraw : bool ) {
self . with_buffer_mut ( | buffer | buffer . set_redraw ( redraw ) ) ;
}
2023-12-20 15:01:47 -07:00
pub fn input_no_scroll < I : Into < Cow < 'static , [ u8 ] > > > ( & self , input : I ) {
2023-12-20 13:31:10 -07:00
self . notifier . notify ( input ) ;
}
2023-12-20 15:01:47 -07:00
pub fn input_scroll < I : Into < Cow < 'static , [ u8 ] > > > ( & self , input : I ) {
self . input_no_scroll ( input ) ;
self . scroll ( TerminalScroll ::Bottom ) ;
}
2023-12-21 22:13:17 -07:00
pub fn paste ( & self , value : String ) {
// This code is ported from alacritty
let bracketed_paste = {
let term = self . term . lock ( ) ;
term . mode ( ) . contains ( TermMode ::BRACKETED_PASTE )
} ;
if bracketed_paste {
self . input_no_scroll ( & b " \x1b [200~ " [ .. ] ) ;
self . input_no_scroll ( value . replace ( '\x1b' , " " ) . into_bytes ( ) ) ;
self . input_scroll ( & b " \x1b [201~ " [ .. ] ) ;
} else {
// In non-bracketed (ie: normal) mode, terminal applications cannot distinguish
// pasted data from keystrokes.
// In theory, we should construct the keystrokes needed to produce the data we are
// pasting... since that's neither practical nor sensible (and probably an impossible
// task to solve in a general way), we'll just replace line breaks (windows and unix
// style) with a single carriage return (\r, which is what the Enter key produces).
self . input_scroll ( value . replace ( " \r \n " , " \r " ) . replace ( '\n' , " \r " ) . into_bytes ( ) ) ;
}
}
2023-12-20 19:54:18 -07:00
pub fn resize ( & mut self , width : u32 , height : u32 ) {
2023-12-20 13:31:10 -07:00
if width ! = self . size . width | | height ! = self . size . height {
let instant = Instant ::now ( ) ;
self . size . width = width ;
self . size . height = height ;
self . notifier . on_resize ( self . size . into ( ) ) ;
self . term . lock ( ) . resize ( self . size ) ;
self . with_buffer_mut ( | buffer | {
let mut font_system = font_system ( ) . write ( ) . unwrap ( ) ;
2024-06-12 09:50:07 -06:00
buffer . set_size ( font_system . raw ( ) , Some ( width as f32 ) , Some ( height as f32 ) ) ;
2023-12-20 13:31:10 -07:00
} ) ;
2024-02-08 11:28:41 -07:00
self . needs_update = true ;
2023-12-20 13:31:10 -07:00
log ::debug! ( " resize {:?} " , instant . elapsed ( ) ) ;
}
}
2023-12-20 14:26:31 -07:00
pub fn scroll ( & self , scroll : TerminalScroll ) {
self . term . lock ( ) . scroll_display ( scroll ) ;
}
2023-12-20 14:57:44 -07:00
pub fn scroll_to ( & self , ratio : f32 ) {
let mut term = self . term . lock ( ) ;
let grid = term . grid ( ) ;
let total = grid . history_size ( ) + grid . screen_lines ( ) ;
let old_display_offset = grid . display_offset ( ) as i32 ;
let new_display_offset =
( ( total as f32 ) * ( 1.0 - ratio ) ) as i32 - grid . screen_lines ( ) as i32 ;
term . scroll_display ( TerminalScroll ::Delta (
new_display_offset - old_display_offset ,
) ) ;
}
2023-12-21 10:26:17 -07:00
pub fn scrollbar ( & self ) -> Option < ( f32 , f32 ) > {
2023-12-20 14:26:31 -07:00
let term = self . term . lock ( ) ;
let grid = term . grid ( ) ;
2023-12-21 10:26:17 -07:00
if grid . history_size ( ) > 0 {
let total = grid . history_size ( ) + grid . screen_lines ( ) ;
let start = total - grid . display_offset ( ) - grid . screen_lines ( ) ;
let end = total - grid . display_offset ( ) ;
Some ( (
( start as f32 ) / ( total as f32 ) ,
( end as f32 ) / ( total as f32 ) ,
) )
} else {
None
}
2023-12-20 14:26:31 -07:00
}
2024-01-11 14:31:20 -07:00
pub fn search ( & mut self , value : & str , forwards : bool ) {
//TODO: set max lines, run in thread?
{
let mut term = self . term . lock ( ) ;
if self . search_value ! = value {
match RegexSearch ::new ( value ) {
Ok ( search_regex ) = > {
self . search_regex_opt = Some ( search_regex ) ;
self . search_value = value . to_string ( ) ;
term . selection = None ;
}
Err ( err ) = > {
log ::warn! ( " failed to parse regex {:?}: {} " , value , err ) ;
return ;
}
}
}
2024-04-15 20:47:12 +01:00
let Some ( search_regex ) = & mut self . search_regex_opt else {
return ;
2024-01-11 14:31:20 -07:00
} ;
// Determine search origin
2024-01-11 16:20:15 -07:00
let grid = term . grid ( ) ;
2024-01-11 14:31:20 -07:00
let search_origin = match term
. selection
. as_ref ( )
. and_then ( | selection | selection . to_range ( & term ) )
{
Some ( range ) = > {
2024-01-11 16:20:15 -07:00
//TODO: determine correct search_origin, along with side below
2024-01-11 14:31:20 -07:00
if forwards {
2024-01-11 16:20:15 -07:00
range . end . add ( grid , Boundary ::Grid , 1 )
2024-01-11 14:31:20 -07:00
} else {
2024-01-11 16:20:15 -07:00
range . start . sub ( grid , Boundary ::Grid , 1 )
2024-01-11 14:31:20 -07:00
}
}
None = > {
if forwards {
Point ::new ( Line ( - ( grid . history_size ( ) as i32 ) ) , Column ( 0 ) )
} else {
Point ::new (
Line ( grid . screen_lines ( ) as i32 - 1 ) ,
Column ( grid . columns ( ) - 1 ) ,
)
}
}
} ;
// Find next search match
2024-02-05 15:59:07 +01:00
if let Some ( search_match ) = term . search_next (
2024-01-11 14:31:20 -07:00
search_regex ,
search_origin ,
if forwards {
Direction ::Right
} else {
Direction ::Left
} ,
2024-01-11 16:20:15 -07:00
//TODO: determine correct side, along with search_origin above
2024-01-11 14:31:20 -07:00
if forwards { Side ::Left } else { Side ::Right } ,
None ,
) {
2024-02-05 15:59:07 +01:00
// Scroll to match
if forwards {
term . scroll_to_point ( * search_match . end ( ) ) ;
} else {
term . scroll_to_point ( * search_match . start ( ) ) ;
2024-01-11 14:31:20 -07:00
}
2024-02-05 15:59:07 +01:00
// Set selection to match
let mut selection =
Selection ::new ( SelectionType ::Simple , * search_match . start ( ) , Side ::Left ) ;
selection . update ( * search_match . end ( ) , Side ::Right ) ;
term . selection = Some ( selection ) ;
2024-01-11 14:31:20 -07:00
}
}
self . update ( ) ;
}
2023-12-22 15:08:24 -07:00
pub fn select_all ( & mut self ) {
{
let mut term = self . term . lock ( ) ;
let grid = term . grid ( ) ;
let start = Point ::new ( Line ( - ( grid . history_size ( ) as i32 ) ) , Column ( 0 ) ) ;
2024-02-22 12:01:50 -07:00
let mut end_line = grid . bottommost_line ( ) ;
while end_line . 0 > 0 {
2024-02-26 15:19:54 -07:00
if ! grid [ end_line ] . is_clear ( ) {
2024-02-22 12:01:50 -07:00
break ;
}
end_line . 0 - = 1 ;
}
2024-02-26 15:19:54 -07:00
let end = Point ::new ( end_line , Column ( grid . columns ( ) - 1 ) ) ;
2023-12-22 15:08:24 -07:00
let mut selection = Selection ::new ( SelectionType ::Lines , start , Side ::Left ) ;
selection . update ( end , Side ::Right ) ;
term . selection = Some ( selection ) ;
}
self . update ( ) ;
}
2024-01-09 10:16:32 -07:00
pub fn set_config (
& mut self ,
2024-01-11 17:47:24 +03:00
config : & AppConfig ,
2024-02-22 11:18:13 -07:00
themes : & HashMap < ( String , ColorSchemeKind ) , Colors > ,
2024-01-09 10:16:32 -07:00
) {
2023-12-22 14:31:01 -07:00
let mut update_cell_size = false ;
let mut update = false ;
2024-08-20 04:52:34 +03:00
let zoom_adj = self . zoom_adj ;
2024-01-10 01:19:04 +03:00
if self . default_attrs . stretch ! = config . typed_font_stretch ( ) {
2024-01-11 11:33:39 -07:00
self . default_attrs = self . default_attrs . stretch ( config . typed_font_stretch ( ) ) ;
2024-01-10 01:19:04 +03:00
update_cell_size = true ;
}
if self . default_attrs . weight . 0 ! = config . font_weight {
2024-01-11 11:33:39 -07:00
self . default_attrs = self . default_attrs . weight ( Weight ( config . font_weight ) ) ;
2024-01-10 01:19:04 +03:00
update_cell_size = true ;
}
2024-01-12 01:58:57 +03:00
if self . dim_font_weight . 0 ! = config . dim_font_weight {
self . dim_font_weight = Weight ( config . dim_font_weight ) ;
update_cell_size = true ;
}
2024-01-10 01:19:04 +03:00
if self . bold_font_weight . 0 ! = config . font_weight {
self . bold_font_weight = Weight ( config . bold_font_weight ) ;
update_cell_size = true ;
}
2024-01-11 17:47:24 +03:00
if self . use_bright_bold ! = config . use_bright_bold {
self . use_bright_bold = config . use_bright_bold ;
update_cell_size = true ;
}
2024-01-03 20:06:55 +03:00
let metrics = config . metrics ( zoom_adj ) ;
2023-12-22 14:31:01 -07:00
if metrics ! = self . buffer . metrics ( ) {
{
let mut font_system = font_system ( ) . write ( ) . unwrap ( ) ;
self . with_buffer_mut ( | buffer | buffer . set_metrics ( font_system . raw ( ) , metrics ) ) ;
}
update_cell_size = true ;
}
2024-02-22 11:18:13 -07:00
if let Some ( colors ) = themes . get ( & config . syntax_theme ( self . profile_id_opt ) ) {
2023-12-22 14:31:01 -07:00
let mut changed = false ;
for i in 0 .. color ::COUNT {
if self . colors [ i ] ! = colors [ i ] {
self . colors [ i ] = colors [ i ] ;
changed = true ;
}
}
if changed {
update = true ;
}
}
2024-08-16 14:30:58 +03:00
// NOTE: this is done on every set_config because the changed boolean above does not capture
2024-04-19 14:09:32 -06:00
// WINDOW_BG changes
2024-08-16 14:30:58 +03:00
let default_colors_updated = self . update_default_colors ( config ) ;
2024-04-19 14:09:32 -06:00
2023-12-22 14:31:01 -07:00
if update_cell_size {
self . update_cell_size ( ) ;
2024-08-16 14:30:58 +03:00
} else if update | | default_colors_updated {
2023-12-22 14:31:01 -07:00
self . update ( ) ;
}
}
2024-08-16 14:30:58 +03:00
pub fn update_default_colors ( & mut self , config : & AppConfig ) -> bool {
2024-02-08 12:37:02 -07:00
let default_bg = convert_color ( & self . colors , Color ::Named ( NamedColor ::Background ) ) ;
let default_fg = convert_color ( & self . colors , Color ::Named ( NamedColor ::Foreground ) ) ;
2024-08-16 14:30:58 +03:00
let new_default_metadata = Metadata ::new ( default_bg , default_fg ) ;
let curr_metada_idx = self . default_attrs ( ) . metadata ;
2024-02-08 12:37:02 -07:00
2024-08-16 14:30:58 +03:00
let updated = new_default_metadata ! = self . metadata_set [ curr_metada_idx ] ;
if updated {
self . metadata_set . clear ( ) ;
let ( default_metadata_idx , _ ) = self . metadata_set . insert_full ( new_default_metadata ) ;
self . default_attrs = Attrs ::new ( )
. family ( Family ::Monospace )
. weight ( Weight ( config . font_weight ) )
. stretch ( config . typed_font_stretch ( ) )
. color ( default_fg )
. metadata ( default_metadata_idx ) ;
}
updated
2024-02-08 12:37:02 -07:00
}
2023-12-22 14:31:01 -07:00
pub fn update_cell_size ( & mut self ) {
let default_attrs = self . default_attrs ;
let ( cell_width , cell_height ) = {
let mut font_system = font_system ( ) . write ( ) . unwrap ( ) ;
self . with_buffer_mut ( | buffer | {
buffer . set_wrap ( font_system . raw ( ) , Wrap ::None ) ;
// Use size of space to determine cell size
buffer . set_text ( font_system . raw ( ) , " " , default_attrs , Shaping ::Advanced ) ;
let layout = buffer . line_layout ( font_system . raw ( ) , 0 ) . unwrap ( ) ;
2024-01-17 23:51:40 +03:00
let w = layout [ 0 ] . w ;
buffer . set_monospace_width ( font_system . raw ( ) , Some ( w ) ) ;
( w , buffer . metrics ( ) . line_height )
2023-12-22 14:31:01 -07:00
} )
} ;
let old_size = self . size ;
self . size = Size {
width : 0 ,
height : 0 ,
cell_width ,
cell_height ,
} ;
self . resize ( old_size . width , old_size . height ) ;
self . update ( ) ;
}
2023-12-20 13:31:10 -07:00
pub fn update ( & mut self ) -> bool {
2024-01-11 02:31:28 +03:00
// LEFT‑ TO‑ RIGHT ISOLATE character.
// This will be added to the beginning of lines to force the shaper to treat detected RTL
// lines as LTR. RTL text would still be rendered correctly. But this fixes the wrong
// behavior of it being aligned to the right.
2024-01-11 11:33:39 -07:00
const LRI : char = '\u{2066}' ;
2024-01-11 02:31:28 +03:00
2023-12-20 13:31:10 -07:00
let instant = Instant ::now ( ) ;
2024-01-11 23:22:39 +03:00
// Only keep default
self . metadata_set . truncate ( 1 ) ;
2023-12-20 13:31:10 -07:00
//TODO: is redraw needed after all events?
//TODO: use LineDamageBounds
{
2023-12-21 09:53:31 -07:00
let buffer = Arc ::make_mut ( & mut self . buffer ) ;
2023-12-20 13:31:10 -07:00
2023-12-20 14:26:31 -07:00
let mut line_i = 0 ;
let mut last_point = None ;
2024-01-11 02:31:28 +03:00
let mut text = String ::from ( LRI ) ;
2023-12-20 13:31:10 -07:00
let mut attrs_list = AttrsList ::new ( self . default_attrs ) ;
{
2024-02-05 21:01:16 -07:00
let mut term = self . term . lock ( ) ;
//TODO: use damage?
match term . damage ( ) {
TermDamage ::Full = > { }
TermDamage ::Partial ( _damage_lines ) = > { }
}
term . reset_damage ( ) ;
2024-11-17 01:47:50 +01:00
self . regex_matches . clear ( ) ;
2024-12-22 10:40:56 +01:00
{
let mut regex_matches : Vec < _ > =
visible_regex_match_iter ( & term , & mut self . url_regex_search ) . collect ( ) ;
self . regex_matches
. extend ( regex_matches . drain ( .. ) . flat_map ( | rm | -> Vec < _ > {
HintPostProcessor ::new ( & term , & mut self . url_regex_search , rm ) . collect ( )
} ) ) ;
}
2024-11-17 01:47:50 +01:00
2023-12-21 21:37:39 -07:00
let grid = term . grid ( ) ;
2023-12-20 13:31:10 -07:00
for indexed in grid . display_iter ( ) {
2023-12-20 14:26:31 -07:00
if indexed . point . line ! = last_point . unwrap_or ( indexed . point ) . line {
2023-12-20 13:31:10 -07:00
while line_i > = buffer . lines . len ( ) {
buffer . lines . push ( BufferLine ::new (
" " ,
2024-05-01 12:16:49 -06:00
LineEnding ::default ( ) ,
2023-12-20 13:31:10 -07:00
AttrsList ::new ( self . default_attrs ) ,
Shaping ::Advanced ,
) ) ;
buffer . set_redraw ( true ) ;
}
2024-05-01 12:16:49 -06:00
if buffer . lines [ line_i ] . set_text (
text . clone ( ) ,
LineEnding ::default ( ) ,
attrs_list . clone ( ) ,
) {
2023-12-20 13:31:10 -07:00
buffer . set_redraw ( true ) ;
}
2023-12-20 14:26:31 -07:00
line_i + = 1 ;
2023-12-20 13:31:10 -07:00
text . clear ( ) ;
2024-01-11 02:31:28 +03:00
text . push ( LRI ) ;
2023-12-20 13:31:10 -07:00
attrs_list . clear_spans ( ) ;
}
//TODO: use indexed.point.column?
//TODO: skip leading spacer?
if indexed . cell . flags . contains ( Flags ::WIDE_CHAR_SPACER ) {
// Skip wide spacers (cells after wide characters)
continue ;
}
let start = text . len ( ) ;
2024-02-05 21:01:16 -07:00
// Tab skip/stop is handled by alacritty_terminal
text . push ( match indexed . cell . c {
'\t' = > ' ' ,
c = > c ,
} ) ;
2023-12-20 13:31:10 -07:00
if let Some ( zerowidth ) = indexed . cell . zerowidth ( ) {
for & c in zerowidth {
text . push ( c ) ;
}
}
let end = text . len ( ) ;
let mut attrs = self . default_attrs ;
2023-12-31 13:12:50 +03:00
2024-01-12 01:41:37 +03:00
let cell_fg = if indexed . cell . flags . contains ( Flags ::DIM ) {
as_dim ( indexed . cell . fg )
} else if self . use_bright_bold & & indexed . cell . flags . contains ( Flags ::BOLD ) {
as_bright ( indexed . cell . fg )
} else {
indexed . cell . fg
} ;
2024-01-03 17:46:49 +03:00
2023-12-31 13:12:50 +03:00
let ( mut fg , mut bg ) = if indexed . cell . flags . contains ( Flags ::INVERSE ) {
(
convert_color ( & self . colors , indexed . cell . bg ) ,
2024-01-03 17:46:49 +03:00
convert_color ( & self . colors , cell_fg ) ,
2023-12-31 13:12:50 +03:00
)
} else {
(
2024-01-03 17:46:49 +03:00
convert_color ( & self . colors , cell_fg ) ,
2023-12-31 13:12:50 +03:00
convert_color ( & self . colors , indexed . cell . bg ) ,
)
} ;
2023-12-21 21:37:39 -07:00
2024-01-04 09:19:09 +03:00
if indexed . cell . flags . contains ( Flags ::HIDDEN ) {
fg = bg ;
}
2023-12-21 21:37:39 -07:00
// Change color if cursor
2024-08-23 14:48:42 +03:00
if indexed . point = = grid . cursor . point
2024-08-27 18:33:10 +03:00
& & term . renderable_content ( ) . cursor . shape = = CursorShape ::Block
2024-08-23 14:48:42 +03:00
{
//Use specific cursor color if requested
if term . colors ( ) [ NamedColor ::Cursor ] . is_some ( ) {
2024-01-08 20:58:07 +03:00
fg = bg ;
2024-08-23 14:48:42 +03:00
bg = convert_color ( term . colors ( ) , Color ::Named ( NamedColor ::Cursor ) ) ;
} else if self . colors [ NamedColor ::Cursor ] . is_some ( ) {
//Use specific theme cursor color if exists
fg = bg ;
bg = convert_color ( & self . colors , Color ::Named ( NamedColor ::Cursor ) ) ;
} else {
mem ::swap ( & mut fg , & mut bg ) ;
}
let fg_rgb = Rgb {
r : fg . r ( ) ,
g : fg . g ( ) ,
b : fg . b ( ) ,
} ;
let bg_rgb = Rgb {
r : bg . r ( ) ,
g : bg . g ( ) ,
b : bg . b ( ) ,
} ;
let contrast = fg_rgb . contrast ( bg_rgb ) ;
if contrast < MIN_CURSOR_CONTRAST {
fg = convert_color ( & self . colors , Color ::Named ( NamedColor ::Background ) ) ;
bg = convert_color ( & self . colors , Color ::Named ( NamedColor ::Foreground ) ) ;
2024-01-08 20:58:07 +03:00
}
2023-12-20 13:31:10 -07:00
}
2023-12-21 21:37:39 -07:00
// Change color if selected
if let Some ( selection ) = & term . selection {
if let Some ( range ) = selection . to_range ( & term ) {
if range . contains ( indexed . point ) {
//TODO: better handling of selection
mem ::swap ( & mut fg , & mut bg ) ;
}
}
}
2024-01-02 12:43:17 -07:00
// Convert foreground to linear
2024-01-17 13:41:17 -07:00
attrs = attrs . color ( fg ) ;
2024-01-11 23:22:39 +03:00
2024-01-17 16:41:35 +01:00
let underline_color = indexed
. cell
. underline_color ( )
2024-01-11 23:22:39 +03:00
. map ( | c | convert_color ( & self . colors , c ) )
. unwrap_or ( fg ) ;
2024-11-17 01:47:50 +01:00
let mut flags = indexed . cell . flags ;
if let Some ( active_match ) = & self . active_regex_match {
if active_match . contains ( & indexed . point ) {
flags | = Flags ::UNDERLINE ;
}
}
2024-01-11 23:22:39 +03:00
let metadata = Metadata ::new ( bg , fg )
2024-11-17 01:47:50 +01:00
. with_flags ( flags )
2024-01-11 23:22:39 +03:00
. with_underline_color ( underline_color ) ;
let ( meta_idx , _ ) = self . metadata_set . insert_full ( metadata ) ;
attrs = attrs . metadata ( meta_idx ) ;
2023-12-20 13:31:10 -07:00
//TODO: more flags
if indexed . cell . flags . contains ( Flags ::BOLD ) {
2024-01-10 01:19:04 +03:00
attrs = attrs . weight ( self . bold_font_weight ) ;
2024-01-12 01:58:57 +03:00
} else if indexed . cell . flags . contains ( Flags ::DIM ) {
// if DIM and !BOLD
attrs = attrs . weight ( self . dim_font_weight ) ;
2023-12-20 13:31:10 -07:00
}
if indexed . cell . flags . contains ( Flags ::ITALIC ) {
2024-01-02 11:47:31 -07:00
//TODO: automatically use fake italic
attrs = attrs . cache_key_flags ( CacheKeyFlags ::FAKE_ITALIC ) ;
2023-12-20 13:31:10 -07:00
}
if attrs ! = attrs_list . defaults ( ) {
attrs_list . add_span ( start .. end , attrs ) ;
}
2023-12-20 14:26:31 -07:00
last_point = Some ( indexed . point ) ;
2023-12-20 13:31:10 -07:00
}
}
//TODO: do not repeat!
while line_i > = buffer . lines . len ( ) {
buffer . lines . push ( BufferLine ::new (
" " ,
2024-05-01 12:16:49 -06:00
LineEnding ::default ( ) ,
2023-12-20 13:31:10 -07:00
AttrsList ::new ( self . default_attrs ) ,
Shaping ::Advanced ,
) ) ;
buffer . set_redraw ( true ) ;
}
2024-05-01 12:16:49 -06:00
if buffer . lines [ line_i ] . set_text ( text , LineEnding ::default ( ) , attrs_list ) {
2023-12-20 13:31:10 -07:00
buffer . set_redraw ( true ) ;
}
2023-12-20 14:26:31 -07:00
line_i + = 1 ;
2023-12-20 13:31:10 -07:00
2023-12-20 14:26:31 -07:00
if buffer . lines . len ( ) ! = line_i {
buffer . lines . truncate ( line_i ) ;
2023-12-20 13:31:10 -07:00
buffer . set_redraw ( true ) ;
}
2024-02-08 11:28:41 -07:00
// Shape and trim shape run cache
2023-12-20 13:31:10 -07:00
{
let mut font_system = font_system ( ) . write ( ) . unwrap ( ) ;
buffer . shape_until_scroll ( font_system . raw ( ) , true ) ;
2024-02-08 11:28:41 -07:00
font_system . raw ( ) . shape_run_cache . trim ( 1024 ) ;
2023-12-20 13:31:10 -07:00
}
}
log ::debug! ( " buffer update {:?} " , instant . elapsed ( ) ) ;
self . buffer . redraw ( )
}
2023-12-21 22:21:01 -07:00
pub fn viewport_to_point ( & self , point : Point < usize > ) -> Point {
let term = self . term . lock ( ) ;
viewport_to_point ( term . grid ( ) . display_offset ( ) , point )
}
2024-01-30 08:59:19 +01:00
pub fn report_mouse (
& mut self ,
event : cosmic ::iced ::Event ,
modifiers : & cosmic ::iced ::keyboard ::Modifiers ,
x : u32 ,
y : u32 ,
) {
let term_lock = self . term . lock ( ) ;
let mode = term_lock . mode ( ) ;
2024-11-09 07:26:04 -05:00
2024-02-05 15:59:07 +01:00
#[ allow(clippy::collapsible_else_if) ]
2024-01-30 08:59:19 +01:00
if mode . contains ( TermMode ::SGR_MOUSE ) {
if let Some ( code ) = self . mouse_reporter . sgr_mouse_code ( event , modifiers , x , y ) {
self . input_no_scroll ( code )
}
} else {
if let Some ( code ) = self . mouse_reporter . normal_mouse_code (
event ,
modifiers ,
mode . contains ( TermMode ::UTF8_MOUSE ) ,
x ,
y ,
) {
self . input_no_scroll ( code )
}
}
}
2024-11-09 07:26:04 -05:00
2024-01-31 15:43:20 +01:00
pub fn scroll_mouse (
& mut self ,
delta : ScrollDelta ,
modifiers : & cosmic ::iced ::keyboard ::Modifiers ,
x : u32 ,
y : u32 ,
) {
let term_lock = self . term . lock ( ) ;
let mode = term_lock . mode ( ) ;
2024-11-09 07:26:04 -05:00
2024-01-31 15:43:20 +01:00
if mode . contains ( TermMode ::SGR_MOUSE ) {
2024-11-09 07:26:04 -05:00
let codes = self . mouse_reporter . sgr_mouse_wheel_scroll (
2024-01-31 15:43:20 +01:00
self . size ( ) . cell_width ,
self . size ( ) . cell_height ,
delta ,
modifiers ,
x ,
y ,
) ;
2024-11-09 07:26:04 -05:00
for code in codes {
self . notifier . notify ( code ) ;
}
2024-01-31 15:43:20 +01:00
} else {
2024-04-15 21:10:32 +01:00
MouseReporter ::report_mouse_wheel_as_arrows (
2024-01-31 15:43:20 +01:00
self ,
self . size ( ) . cell_width ,
self . size ( ) . cell_height ,
delta ,
) ;
}
2024-01-30 08:59:19 +01:00
}
2023-12-20 13:31:10 -07:00
}
2024-11-17 01:47:50 +01:00
/// Iterate over all visible regex matches.
/// This includes the screen +- 100 lines (MAX_SEARCH_LINES).
/// display/hint.rs
pub fn visible_regex_match_iter < ' a , T > (
term : & ' a Term < T > ,
regex : & ' a mut RegexSearch ,
) -> impl Iterator < Item = alacritty_terminal ::term ::search ::Match > + ' a {
let viewport_start = Line ( - ( term . grid ( ) . display_offset ( ) as i32 ) ) ;
let viewport_end = viewport_start + term . bottommost_line ( ) ;
let mut start = term . line_search_left ( Point ::new ( viewport_start , Column ( 0 ) ) ) ;
let mut end = term . line_search_right ( Point ::new ( viewport_end , Column ( 0 ) ) ) ;
start . line = start . line . max ( viewport_start - MAX_SEARCH_LINES ) ;
end . line = end . line . min ( viewport_end + MAX_SEARCH_LINES ) ;
alacritty_terminal ::term ::search ::RegexIter ::new ( start , end , Direction ::Right , term , regex )
. skip_while ( move | rm | rm . end ( ) . line < viewport_start )
. take_while ( move | rm | rm . start ( ) . line < = viewport_end )
}
2024-12-22 10:40:56 +01:00
/** Copy of <https: / / github.com / alacritty / alacritty / blob / 4a7728bf7fac06a35f27f6c4f31e0d9214e5152b / alacritty / src / display / hint.rs#L433C1-L572C1> */
/// Iterator over all post-processed matches inside an existing hint match.
struct HintPostProcessor < ' a , T > {
/// Regex search DFAs.
regex : & ' a mut RegexSearch ,
/// Terminal reference.
term : & ' a Term < T > ,
/// Next hint match in the iterator.
next_match : Option < alacritty_terminal ::term ::search ::Match > ,
/// Start point for the next search.
start : Point ,
/// End point for the hint match iterator.
end : Point ,
}
impl < ' a , T > HintPostProcessor < ' a , T > {
/// Create a new iterator for an unprocessed match.
fn new (
term : & ' a Term < T > ,
regex : & ' a mut RegexSearch ,
regex_match : alacritty_terminal ::term ::search ::Match ,
) -> Self {
let mut post_processor = Self {
next_match : None ,
start : * regex_match . start ( ) ,
end : * regex_match . end ( ) ,
term ,
regex ,
} ;
// Post-process the first hint match.
post_processor . next_processed_match ( regex_match ) ;
post_processor
}
/// Apply some hint post processing heuristics.
///
/// This will check the end of the hint and make it shorter if certain characters are determined
/// to be unlikely to be intentionally part of the hint.
///
/// This is most useful for identifying URLs appropriately.
fn hint_post_processing (
& self ,
regex_match : & alacritty_terminal ::term ::search ::Match ,
) -> Option < alacritty_terminal ::term ::search ::Match > {
let mut iter = self . term . grid ( ) . iter_from ( * regex_match . start ( ) ) ;
let mut c = iter . cell ( ) . c ;
// Truncate uneven number of brackets.
let end = * regex_match . end ( ) ;
let mut open_parents = 0 ;
let mut open_brackets = 0 ;
loop {
match c {
'(' = > open_parents + = 1 ,
'[' = > open_brackets + = 1 ,
')' = > {
if open_parents = = 0 {
alacritty_terminal ::grid ::BidirectionalIterator ::prev ( & mut iter ) ;
break ;
} else {
open_parents - = 1 ;
}
}
']' = > {
if open_brackets = = 0 {
alacritty_terminal ::grid ::BidirectionalIterator ::prev ( & mut iter ) ;
break ;
} else {
open_brackets - = 1 ;
}
}
_ = > ( ) ,
}
if iter . point ( ) = = end {
break ;
}
match iter . next ( ) {
Some ( indexed ) = > c = indexed . cell . c ,
None = > break ,
}
}
// Truncate trailing characters which are likely to be delimiters.
let start = * regex_match . start ( ) ;
while iter . point ( ) ! = start {
if ! matches! ( c , '.' | ',' | ':' | ';' | '?' | '!' | '(' | '[' | '\'' ) {
break ;
}
match alacritty_terminal ::grid ::BidirectionalIterator ::prev ( & mut iter ) {
Some ( indexed ) = > c = indexed . cell . c ,
None = > break ,
}
}
if start > iter . point ( ) {
None
} else {
Some ( start ..= iter . point ( ) )
}
}
/// Loop over submatches until a non-empty post-processed match is found.
fn next_processed_match ( & mut self , mut regex_match : alacritty_terminal ::term ::search ::Match ) {
self . next_match = loop {
if let Some ( next_match ) = self . hint_post_processing ( & regex_match ) {
self . start = next_match . end ( ) . add ( self . term , Boundary ::Grid , 1 ) ;
break Some ( next_match ) ;
}
self . start = regex_match . start ( ) . add ( self . term , Boundary ::Grid , 1 ) ;
if self . start > self . end {
return ;
}
match self
. term
. regex_search_right ( self . regex , self . start , self . end )
{
Some ( rm ) = > regex_match = rm ,
None = > return ,
}
} ;
}
}
impl < ' a , T > Iterator for HintPostProcessor < ' a , T > {
type Item = alacritty_terminal ::term ::search ::Match ;
fn next ( & mut self ) -> Option < Self ::Item > {
let next_match = self . next_match . take ( ) ? ;
if self . start < = self . end {
if let Some ( rm ) = self
. term
. regex_search_right ( self . regex , self . start , self . end )
{
self . next_processed_match ( rm ) ;
}
}
Some ( next_match )
}
}
2023-12-20 13:31:10 -07:00
impl Drop for Terminal {
fn drop ( & mut self ) {
// Ensure shutdown on terminal drop
2024-04-05 16:35:17 +02:00
if let Err ( err ) = self . notifier . 0. send ( Msg ::Shutdown ) {
log ::warn! ( " Failed to send shutdown message on dropped terminal: {err} " ) ;
}
2023-12-20 13:31:10 -07:00
}
}