@ -4,8 +4,9 @@ use helix_core::{
movement ::{ self , Direction } ,
movement ::{ self , Direction } ,
object , pos_at_coords ,
object , pos_at_coords ,
regex ::{ self , Regex } ,
regex ::{ self , Regex } ,
register , search , selection , Change , ChangeSet , Position , Range , Rope , RopeSlice , Selection ,
register ::{ self , Register , Registers } ,
SmallVec , Tendril , Transaction ,
search , selection , Change , ChangeSet , Position , Range , Rope , RopeSlice , Selection , SmallVec ,
Tendril , Transaction ,
} ;
} ;
use helix_view ::{
use helix_view ::{
@ -39,7 +40,7 @@ use crossterm::event::{KeyCode, KeyEvent};
use once_cell ::sync ::Lazy ;
use once_cell ::sync ::Lazy ;
pub struct Context < ' a > {
pub struct Context < ' a > {
pub register: helix_view ::RegisterSelection ,
pub selected_ register: helix_view ::RegisterSelection ,
pub count : Option < std ::num ::NonZeroUsize > ,
pub count : Option < std ::num ::NonZeroUsize > ,
pub editor : & ' a mut Editor ,
pub editor : & ' a mut Editor ,
@ -65,6 +66,11 @@ impl<'a> Context<'a> {
self . editor . current ( )
self . editor . current ( )
}
}
#[ inline ]
pub fn current_with_registers ( & mut self ) -> ( & mut View , & mut Document , & mut Registers ) {
self . editor . current_with_registers ( )
}
/// Push a new component onto the compositor.
/// Push a new component onto the compositor.
pub fn push_layer ( & mut self , mut component : Box < dyn Component > ) {
pub fn push_layer ( & mut self , mut component : Box < dyn Component > ) {
self . callback = Some ( Box ::new ( | compositor : & mut Compositor | {
self . callback = Some ( Box ::new ( | compositor : & mut Compositor | {
@ -631,7 +637,7 @@ pub fn select_all(cx: &mut Context) {
}
}
pub fn select_regex ( cx : & mut Context ) {
pub fn select_regex ( cx : & mut Context ) {
let prompt = ui ::regex_prompt ( cx , "select:" . to_string ( ) , move | view , doc , regex| {
let prompt = ui ::regex_prompt ( cx , "select:" . to_string ( ) , move | view , doc , _, regex| {
let text = doc . text ( ) . slice ( .. ) ;
let text = doc . text ( ) . slice ( .. ) ;
if let Some ( selection ) = selection ::select_on_matches ( text , doc . selection ( view . id ) , & regex )
if let Some ( selection ) = selection ::select_on_matches ( text , doc . selection ( view . id ) , & regex )
{
{
@ -643,7 +649,7 @@ pub fn select_regex(cx: &mut Context) {
}
}
pub fn split_selection ( cx : & mut Context ) {
pub fn split_selection ( cx : & mut Context ) {
let prompt = ui ::regex_prompt ( cx , "split:" . to_string ( ) , move | view , doc , regex| {
let prompt = ui ::regex_prompt ( cx , "split:" . to_string ( ) , move | view , doc , _, regex| {
let text = doc . text ( ) . slice ( .. ) ;
let text = doc . text ( ) . slice ( .. ) ;
let selection = selection ::split_on_matches ( text , doc . selection ( view . id ) , & regex ) ;
let selection = selection ::split_on_matches ( text , doc . selection ( view . id ) , & regex ) ;
doc . set_selection ( view . id , selection ) ;
doc . set_selection ( view . id , selection ) ;
@ -714,20 +720,24 @@ pub fn search(cx: &mut Context) {
let contents = doc . text ( ) . slice ( .. ) . to_string ( ) ;
let contents = doc . text ( ) . slice ( .. ) . to_string ( ) ;
let view_id = view . id ;
let view_id = view . id ;
let prompt = ui ::regex_prompt ( cx , "search:" . to_string ( ) , move | view , doc , regex | {
let prompt = ui ::regex_prompt (
cx ,
"search:" . to_string ( ) ,
move | view , doc , registers , regex | {
search_impl ( doc , view , & contents , & regex , false ) ;
search_impl ( doc , view , & contents , & regex , false ) ;
// TODO: only store on enter (accept), not update
// TODO: only store on enter (accept), not update
register ::set ( '\\' , vec! [ regex . as_str ( ) . to_string ( ) ] ) ;
registers . write ( '\\' , vec! [ regex . as_str ( ) . to_string ( ) ] ) ;
} ) ;
} ,
) ;
cx . push_layer ( Box ::new ( prompt ) ) ;
cx . push_layer ( Box ::new ( prompt ) ) ;
}
}
// can't search next for ""compose"" for some reason
// can't search next for ""compose"" for some reason
pub fn search_next_impl ( cx : & mut Context , extend : bool ) {
pub fn search_next_impl ( cx : & mut Context , extend : bool ) {
if let Some ( query ) = register ::get ( '\\' ) {
let ( view , doc , registers ) = cx . current_with_registers ( ) ;
if let Some ( query ) = registers . read ( '\\' ) {
let query = query . first ( ) . unwrap ( ) ;
let query = query . first ( ) . unwrap ( ) ;
let ( view , doc ) = cx . current ( ) ;
let contents = doc . text ( ) . slice ( .. ) . to_string ( ) ;
let contents = doc . text ( ) . slice ( .. ) . to_string ( ) ;
let regex = Regex ::new ( query ) . unwrap ( ) ;
let regex = Regex ::new ( query ) . unwrap ( ) ;
search_impl ( doc , view , & contents , & regex , extend ) ;
search_impl ( doc , view , & contents , & regex , extend ) ;
@ -747,7 +757,7 @@ pub fn search_selection(cx: &mut Context) {
let contents = doc . text ( ) . slice ( .. ) ;
let contents = doc . text ( ) . slice ( .. ) ;
let query = doc . selection ( view . id ) . primary ( ) . fragment ( contents ) ;
let query = doc . selection ( view . id ) . primary ( ) . fragment ( contents ) ;
let regex = regex ::escape ( & query ) ;
let regex = regex ::escape ( & query ) ;
register::set ( '\\' , vec! [ regex ] ) ;
cx. editor . registers . write ( '\\' , vec! [ regex ] ) ;
search_next ( cx ) ;
search_next ( cx ) ;
}
}
@ -794,7 +804,7 @@ pub fn extend_line(cx: &mut Context) {
// heuristic: append changes to history after each command, unless we're in insert mode
// heuristic: append changes to history after each command, unless we're in insert mode
fn delete_selection_impl ( reg : cha r, doc : & mut Document , view_id : ViewId ) {
fn delete_selection_impl ( reg : & mut Registe r, doc : & mut Document , view_id : ViewId ) {
// first yank the selection
// first yank the selection
let values : Vec < String > = doc
let values : Vec < String > = doc
. selection ( view_id )
. selection ( view_id )
@ -802,7 +812,7 @@ fn delete_selection_impl(reg: char, doc: &mut Document, view_id: ViewId) {
. map ( Cow ::into_owned )
. map ( Cow ::into_owned )
. collect ( ) ;
. collect ( ) ;
reg ister::set ( reg , values ) ;
reg . write ( values ) ;
// then delete
// then delete
let transaction =
let transaction =
@ -815,8 +825,9 @@ fn delete_selection_impl(reg: char, doc: &mut Document, view_id: ViewId) {
}
}
pub fn delete_selection ( cx : & mut Context ) {
pub fn delete_selection ( cx : & mut Context ) {
let reg = cx . register . name ( ) ;
let reg_name = cx . selected_register . name ( ) ;
let ( view , doc ) = cx . current ( ) ;
let ( view , doc , registers ) = cx . current_with_registers ( ) ;
let reg = registers . get_or_insert ( reg_name ) ;
delete_selection_impl ( reg , doc , view . id ) ;
delete_selection_impl ( reg , doc , view . id ) ;
doc . append_changes_to_history ( view . id ) ;
doc . append_changes_to_history ( view . id ) ;
@ -826,8 +837,9 @@ pub fn delete_selection(cx: &mut Context) {
}
}
pub fn change_selection ( cx : & mut Context ) {
pub fn change_selection ( cx : & mut Context ) {
let reg = cx . register . name ( ) ;
let reg_name = cx . selected_register . name ( ) ;
let ( view , doc ) = cx . current ( ) ;
let ( view , doc , registers ) = cx . current_with_registers ( ) ;
let reg = registers . get_or_insert ( reg_name ) ;
delete_selection_impl ( reg , doc , view . id ) ;
delete_selection_impl ( reg , doc , view . id ) ;
enter_insert_mode ( doc ) ;
enter_insert_mode ( doc ) ;
}
}
@ -2227,10 +2239,12 @@ pub fn yank(cx: &mut Context) {
let msg = format! (
let msg = format! (
"yanked {} selection(s) to register {}" ,
"yanked {} selection(s) to register {}" ,
values . len ( ) ,
values . len ( ) ,
cx . register. name ( )
cx . selected_ register. name ( )
) ;
) ;
register ::set ( cx . register . name ( ) , values ) ;
cx . editor
. registers
. write ( cx . selected_register . name ( ) , values ) ;
cx . editor . set_status ( msg )
cx . editor . set_status ( msg )
}
}
@ -2241,8 +2255,12 @@ enum Paste {
After ,
After ,
}
}
fn paste_impl ( reg : char , doc : & mut Document , view : & View , action : Paste ) -> Option < Transaction > {
fn paste_impl (
if let Some ( values ) = register ::get ( reg ) {
values : & [ String ] ,
doc : & mut Document ,
view : & View ,
action : Paste ,
) -> Option < Transaction > {
let repeat = std ::iter ::repeat (
let repeat = std ::iter ::repeat (
values
values
. last ( )
. last ( )
@ -2253,12 +2271,11 @@ fn paste_impl(reg: char, doc: &mut Document, view: &View, action: Paste) -> Opti
// if any of values ends \n it's linewise paste
// if any of values ends \n it's linewise paste
let linewise = values . iter ( ) . any ( | value | value . ends_with ( '\n' ) ) ;
let linewise = values . iter ( ) . any ( | value | value . ends_with ( '\n' ) ) ;
let mut values = values . i nto_i ter( ) . map ( Tendril ::from ) . chain ( repeat ) ;
let mut values = values . i ter( ) . cloned ( ) . map ( Tendril ::from ) . chain ( repeat ) ;
let text = doc . text ( ) ;
let text = doc . text ( ) ;
let transaction =
let transaction = Transaction ::change_by_selection ( text , doc . selection ( view . id ) , | range | {
Transaction ::change_by_selection ( doc . text ( ) , doc . selection ( view . id ) , | range | {
let pos = match ( action , linewise ) {
let pos = match ( action , linewise ) {
// paste linewise before
// paste linewise before
( Paste ::Before , true ) = > text . line_to_char ( text . char_to_line ( range . from ( ) ) ) ,
( Paste ::Before , true ) = > text . line_to_char ( text . char_to_line ( range . from ( ) ) ) ,
@ -2271,17 +2288,15 @@ fn paste_impl(reg: char, doc: &mut Document, view: &View, action: Paste) -> Opti
} ;
} ;
( pos , pos , Some ( values . next ( ) . unwrap ( ) ) )
( pos , pos , Some ( values . next ( ) . unwrap ( ) ) )
} ) ;
} ) ;
return Some ( transaction ) ;
}
Some ( transaction )
None
}
}
pub fn replace_with_yanked ( cx : & mut Context ) {
pub fn replace_with_yanked ( cx : & mut Context ) {
let reg = cx . register . name ( ) ;
let reg_name = cx . selected_register . name ( ) ;
let ( view , doc , registers ) = cx . current_with_registers ( ) ;
if let Some ( values ) = register ::get ( reg ) {
let ( view , doc ) = cx . current ( ) ;
if let Some ( values ) = registers . read ( reg_name ) {
if let Some ( yank ) = values . first ( ) {
if let Some ( yank ) = values . first ( ) {
let transaction =
let transaction =
Transaction ::change_by_selection ( doc . text ( ) , doc . selection ( view . id ) , | range | {
Transaction ::change_by_selection ( doc . text ( ) , doc . selection ( view . id ) , | range | {
@ -2307,20 +2322,26 @@ pub fn replace_with_yanked(cx: &mut Context) {
// default insert
// default insert
pub fn paste_after ( cx : & mut Context ) {
pub fn paste_after ( cx : & mut Context ) {
let reg = cx . register. name ( ) ;
let reg _name = cx . selected_ register. name ( ) ;
let ( view , doc ) = cx . current ( ) ;
let ( view , doc , registers ) = cx . current _with_registers ( ) ;
if let Some ( transaction ) = paste_impl ( reg , doc , view , Paste ::After ) {
if let Some ( transaction ) = registers
. read ( reg_name )
. and_then ( | values | paste_impl ( values , doc , view , Paste ::After ) )
{
doc . apply ( & transaction , view . id ) ;
doc . apply ( & transaction , view . id ) ;
doc . append_changes_to_history ( view . id ) ;
doc . append_changes_to_history ( view . id ) ;
}
}
}
}
pub fn paste_before ( cx : & mut Context ) {
pub fn paste_before ( cx : & mut Context ) {
let reg = cx . register. name ( ) ;
let reg _name = cx . selected_ register. name ( ) ;
let ( view , doc ) = cx . current ( ) ;
let ( view , doc , registers ) = cx . current _with_registers ( ) ;
if let Some ( transaction ) = paste_impl ( reg , doc , view , Paste ::Before ) {
if let Some ( transaction ) = registers
. read ( reg_name )
. and_then ( | values | paste_impl ( values , doc , view , Paste ::Before ) )
{
doc . apply ( & transaction , view . id ) ;
doc . apply ( & transaction , view . id ) ;
doc . append_changes_to_history ( view . id ) ;
doc . append_changes_to_history ( view . id ) ;
}
}
@ -2494,7 +2515,7 @@ pub fn join_selections(cx: &mut Context) {
pub fn keep_selections ( cx : & mut Context ) {
pub fn keep_selections ( cx : & mut Context ) {
// keep selections matching regex
// keep selections matching regex
let prompt = ui ::regex_prompt ( cx , "keep:" . to_string ( ) , move | view , doc , regex| {
let prompt = ui ::regex_prompt ( cx , "keep:" . to_string ( ) , move | view , doc , _, regex| {
let text = doc . text ( ) . slice ( .. ) ;
let text = doc . text ( ) . slice ( .. ) ;
if let Some ( selection ) = selection ::keep_matches ( text , doc . selection ( view . id ) , & regex ) {
if let Some ( selection ) = selection ::keep_matches ( text , doc . selection ( view . id ) , & regex ) {
@ -2788,7 +2809,7 @@ pub fn select_register(cx: &mut Context) {
..
..
} = event
} = event
{
{
cx . editor . register. select ( ch ) ;
cx . editor . selected_ register. select ( ch ) ;
}
}
} )
} )
}
}