2013-04-12 16:28:13 +04:00
# include "normal.hh"
# include "buffer.hh"
# include "buffer_manager.hh"
# include "client_manager.hh"
# include "command_manager.hh"
# include "commands.hh"
2014-12-23 16:34:21 +03:00
# include "containers.hh"
2013-04-12 16:28:13 +04:00
# include "context.hh"
2014-07-11 03:27:04 +04:00
# include "debug.hh"
# include "face_registry.hh"
2014-10-23 21:55:45 +04:00
# include "flags.hh"
2013-04-12 16:28:13 +04:00
# include "file.hh"
# include "option_manager.hh"
# include "register_manager.hh"
# include "selectors.hh"
# include "shell_manager.hh"
# include "string.hh"
2013-05-17 00:20:14 +04:00
# include "user_interface.hh"
2014-07-11 03:27:04 +04:00
# include "window.hh"
2013-04-12 16:28:13 +04:00
namespace Kakoune
{
using namespace std : : placeholders ;
2013-12-15 18:38:04 +04:00
enum class SelectMode
{
Replace ,
Extend ,
Append ,
} ;
2014-01-01 22:45:28 +04:00
template < SelectMode mode = SelectMode : : Replace , typename Func >
void select ( Context & context , Func func )
2013-12-14 17:21:07 +04:00
{
2014-01-01 22:45:28 +04:00
auto & buffer = context . buffer ( ) ;
auto & selections = context . selections ( ) ;
if ( mode = = SelectMode : : Append )
2013-12-14 17:21:07 +04:00
{
2014-01-01 22:45:28 +04:00
auto & sel = selections . main ( ) ;
auto res = func ( buffer , sel ) ;
if ( res . captures ( ) . empty ( ) )
res . captures ( ) = sel . captures ( ) ;
selections . push_back ( res ) ;
selections . set_main_index ( selections . size ( ) - 1 ) ;
}
else
{
for ( auto & sel : selections )
2013-12-14 22:38:14 +04:00
{
2014-01-01 22:45:28 +04:00
auto res = func ( buffer , sel ) ;
if ( mode = = SelectMode : : Extend )
sel . merge_with ( res ) ;
else
2013-12-14 17:21:07 +04:00
{
2014-01-28 23:05:49 +04:00
sel . anchor ( ) = res . anchor ( ) ;
sel . cursor ( ) = res . cursor ( ) ;
2013-12-14 17:21:07 +04:00
}
2014-01-01 22:45:28 +04:00
if ( not res . captures ( ) . empty ( ) )
sel . captures ( ) = std : : move ( res . captures ( ) ) ;
2013-12-14 22:38:14 +04:00
}
2013-12-14 17:21:07 +04:00
}
2014-01-01 22:45:28 +04:00
selections . sort_and_merge_overlapping ( ) ;
selections . check_invariant ( ) ;
}
template < SelectMode mode , typename T >
class Select
{
public :
constexpr Select ( T t ) : m_func ( t ) { }
2014-11-28 16:58:36 +03:00
void operator ( ) ( Context & context , NormalParams ) { select < mode > ( context , m_func ) ; }
2013-12-14 17:21:07 +04:00
private :
T m_func ;
} ;
template < SelectMode mode = SelectMode : : Replace , typename T >
2014-01-01 22:45:28 +04:00
constexpr Select < mode , T > make_select ( T func )
{
return Select < mode , T > ( func ) ;
}
2013-12-14 17:21:07 +04:00
2013-12-14 22:38:14 +04:00
template < SelectMode mode = SelectMode : : Replace >
2014-05-25 23:28:32 +04:00
void select_coord ( Buffer & buffer , ByteCoord coord , SelectionList & selections )
2013-12-14 22:38:14 +04:00
{
2013-12-16 17:39:02 +04:00
coord = buffer . clamp ( coord ) ;
2013-12-14 22:38:14 +04:00
if ( mode = = SelectMode : : Replace )
2014-05-13 02:25:15 +04:00
selections = SelectionList { buffer , coord } ;
2013-12-14 22:38:14 +04:00
else if ( mode = = SelectMode : : Extend )
{
for ( auto & sel : selections )
2014-01-28 23:05:49 +04:00
sel . cursor ( ) = coord ;
2013-12-14 22:38:14 +04:00
selections . sort_and_merge_overlapping ( ) ;
}
}
2013-12-14 17:21:07 +04:00
2013-04-12 16:28:13 +04:00
template < InsertMode mode >
2014-11-28 16:58:36 +03:00
void enter_insert_mode ( Context & context , NormalParams )
2013-04-12 16:28:13 +04:00
{
2013-11-14 22:09:15 +04:00
context . input_handler ( ) . insert ( mode ) ;
2013-04-12 16:28:13 +04:00
}
2014-11-28 16:58:36 +03:00
void repeat_last_insert ( Context & context , NormalParams )
2013-04-12 16:28:13 +04:00
{
2013-11-14 22:09:15 +04:00
context . input_handler ( ) . repeat_last_insert ( ) ;
2013-04-12 16:28:13 +04:00
}
2014-10-20 22:18:38 +04:00
bool show_auto_info_ifn ( StringView title , StringView info ,
2013-10-11 01:51:16 +04:00
const Context & context )
2013-05-17 00:20:14 +04:00
{
2014-07-08 23:24:51 +04:00
if ( context . options ( ) [ " autoinfo " ] . get < int > ( ) < 1 or not context . has_ui ( ) )
2013-05-17 00:20:14 +04:00
return false ;
2014-07-11 03:27:04 +04:00
Face face = get_face ( " Information " ) ;
2014-11-08 22:08:23 +03:00
context . ui ( ) . info_show ( title , info , CharCoord { } , face , InfoStyle : : Prompt ) ;
2013-05-17 00:20:14 +04:00
return true ;
}
2013-07-27 17:58:06 +04:00
template < typename Cmd >
2014-09-23 16:45:18 +04:00
void on_next_key_with_autoinfo ( const Context & context , KeymapMode keymap_mode , Cmd cmd ,
2014-10-20 22:18:38 +04:00
StringView title , StringView info )
2013-07-27 17:58:06 +04:00
{
2013-10-11 01:51:16 +04:00
const bool hide = show_auto_info_ifn ( title , info , context ) ;
2014-09-23 16:45:18 +04:00
context . input_handler ( ) . on_next_key (
keymap_mode , [ hide , cmd ] ( Key key , Context & context ) mutable {
2013-07-27 17:58:06 +04:00
if ( hide )
context . ui ( ) . info_hide ( ) ;
cmd ( key , context ) ;
} ) ;
}
2013-04-12 16:28:13 +04:00
template < SelectMode mode >
2014-11-28 16:58:36 +03:00
void goto_commands ( Context & context , NormalParams params )
2013-04-12 16:28:13 +04:00
{
2014-11-28 16:58:36 +03:00
if ( params . count ! = 0 )
2013-04-12 16:28:13 +04:00
{
context . push_jump ( ) ;
2014-11-28 16:58:36 +03:00
select_coord < mode > ( context . buffer ( ) , LineCount { params . count - 1 } , context . selections ( ) ) ;
2013-04-12 16:28:13 +04:00
if ( context . has_window ( ) )
2014-11-28 16:58:36 +03:00
context . window ( ) . center_line ( LineCount { params . count - 1 } ) ;
2013-04-12 16:28:13 +04:00
}
else
2013-05-17 00:20:14 +04:00
{
2014-09-23 16:45:18 +04:00
on_next_key_with_autoinfo ( context , KeymapMode : : Goto ,
[ ] ( Key key , Context & context ) {
2013-04-12 16:28:13 +04:00
if ( key . modifiers ! = Key : : Modifiers : : None )
return ;
2013-12-16 17:39:02 +04:00
auto & buffer = context . buffer ( ) ;
2013-04-12 16:28:13 +04:00
switch ( tolower ( key . key ) )
{
case ' g ' :
case ' k ' :
context . push_jump ( ) ;
2014-05-07 22:51:01 +04:00
select_coord < mode > ( buffer , ByteCoord { 0 , 0 } , context . selections ( ) ) ;
2013-04-12 16:28:13 +04:00
break ;
case ' l ' :
2014-01-01 22:45:28 +04:00
select < mode > ( context , select_to_eol ) ;
2013-04-12 16:28:13 +04:00
break ;
case ' h ' :
2014-01-01 22:45:28 +04:00
select < mode > ( context , select_to_eol_reverse ) ;
2013-04-12 16:28:13 +04:00
break ;
case ' j ' :
{
context . push_jump ( ) ;
2013-12-16 17:39:02 +04:00
select_coord < mode > ( buffer , buffer . line_count ( ) - 1 , context . selections ( ) ) ;
2013-04-12 16:28:13 +04:00
break ;
}
case ' e ' :
context . push_jump ( ) ;
2013-12-16 17:39:02 +04:00
select_coord < mode > ( buffer , buffer . back_coord ( ) , context . selections ( ) ) ;
2013-04-12 16:28:13 +04:00
break ;
case ' t ' :
if ( context . has_window ( ) )
{
auto line = context . window ( ) . position ( ) . line ;
2013-12-16 17:39:02 +04:00
select_coord < mode > ( buffer , line , context . selections ( ) ) ;
2013-04-12 16:28:13 +04:00
}
break ;
case ' b ' :
if ( context . has_window ( ) )
{
auto & window = context . window ( ) ;
auto line = window . position ( ) . line + window . dimensions ( ) . line - 1 ;
2013-12-16 17:39:02 +04:00
select_coord < mode > ( buffer , line , context . selections ( ) ) ;
2013-04-12 16:28:13 +04:00
}
break ;
case ' c ' :
if ( context . has_window ( ) )
{
auto & window = context . window ( ) ;
auto line = window . position ( ) . line + window . dimensions ( ) . line / 2 ;
2013-12-16 17:39:02 +04:00
select_coord < mode > ( buffer , line , context . selections ( ) ) ;
2013-04-12 16:28:13 +04:00
}
break ;
case ' a ' :
{
auto & buffer_manager = BufferManager : : instance ( ) ;
auto it = buffer_manager . begin ( ) ;
2013-12-16 17:39:02 +04:00
if ( it - > get ( ) = = & buffer and + + it = = buffer_manager . end ( ) )
2013-04-12 16:28:13 +04:00
break ;
2015-01-26 22:40:48 +03:00
Buffer & target = * * it ;
BufferManager : : instance ( ) . set_last_used_buffer ( buffer ) ;
2013-04-12 16:28:13 +04:00
context . push_jump ( ) ;
2015-01-26 22:40:48 +03:00
context . change_buffer ( target ) ;
2013-04-12 16:28:13 +04:00
break ;
}
case ' f ' :
{
2014-03-29 12:55:45 +04:00
const Selection & sel = context . selections ( ) . main ( ) ;
2013-06-03 20:58:09 +04:00
String filename = content ( buffer , sel ) ;
2013-05-21 16:01:04 +04:00
static constexpr char forbidden [ ] = { ' \' ' , ' \\ ' , ' \0 ' } ;
2013-04-12 16:28:13 +04:00
for ( auto c : forbidden )
if ( contains ( filename , c ) )
return ;
2015-01-15 22:25:41 +03:00
auto paths = context . options ( ) [ " path " ] . get < Vector < String , MemoryDomain : : Options > > ( ) ;
2013-06-03 20:58:09 +04:00
const String & buffer_name = buffer . name ( ) ;
2013-04-12 16:28:13 +04:00
auto it = find ( reversed ( buffer_name ) , ' / ' ) ;
if ( it ! = buffer_name . rend ( ) )
paths . insert ( paths . begin ( ) , String { buffer_name . begin ( ) , it . base ( ) } ) ;
String path = find_file ( filename , paths ) ;
2013-05-21 16:01:04 +04:00
if ( path . empty ( ) )
throw runtime_error ( " unable to find file ' " + filename + " ' " ) ;
2014-05-05 16:09:59 +04:00
Buffer * buffer = create_buffer_from_file ( path ) ;
if ( buffer = = nullptr )
throw runtime_error ( " unable to open file ' " + path + " ' " ) ;
if ( buffer ! = & context . buffer ( ) )
{
BufferManager : : instance ( ) . set_last_used_buffer ( * buffer ) ;
context . push_jump ( ) ;
context . change_buffer ( * buffer ) ;
}
2013-04-12 16:28:13 +04:00
break ;
}
2014-04-08 02:39:12 +04:00
case ' . ' :
{
context . push_jump ( ) ;
auto pos = buffer . last_modification_coord ( ) ;
if ( buffer [ pos . line ] . length ( ) = = pos . column + 1 )
2014-05-07 22:51:01 +04:00
pos = ByteCoord { pos . line + 1 , 0 } ;
2014-04-08 02:39:12 +04:00
select_coord < mode > ( buffer , pos , context . selections ( ) ) ;
break ;
}
2013-04-12 16:28:13 +04:00
}
2013-10-11 01:51:16 +04:00
} , " goto " ,
2014-04-08 02:39:12 +04:00
" g,k: buffer top \n "
" l: line end \n "
" h: line begin \n "
" j: buffer bottom \n "
" e: buffer end \n "
" t: window top \n "
" b: window bottom \n "
" c: window center \n "
" a: last buffer \n "
" f: file \n "
" .: last buffer change \n " ) ;
2013-05-17 00:20:14 +04:00
}
2013-04-12 16:28:13 +04:00
}
2014-11-28 16:58:36 +03:00
void view_commands ( Context & context , NormalParams params )
2013-04-12 16:28:13 +04:00
{
2014-09-23 16:45:18 +04:00
on_next_key_with_autoinfo ( context , KeymapMode : : View ,
2014-11-28 16:58:36 +03:00
[ params ] ( Key key , Context & context ) {
2013-04-12 16:28:13 +04:00
if ( key . modifiers ! = Key : : Modifiers : : None or not context . has_window ( ) )
return ;
2014-01-28 23:05:49 +04:00
LineCount cursor_line = context . selections ( ) . main ( ) . cursor ( ) . line ;
2013-04-12 16:28:13 +04:00
Window & window = context . window ( ) ;
switch ( tolower ( key . key ) )
{
2013-04-16 15:54:04 +04:00
case ' v ' :
2013-04-12 16:28:13 +04:00
case ' c ' :
2013-12-16 18:01:40 +04:00
context . window ( ) . center_line ( cursor_line ) ;
2013-04-12 16:28:13 +04:00
break ;
case ' t ' :
2013-12-16 18:01:40 +04:00
context . window ( ) . display_line_at ( cursor_line , 0 ) ;
2013-04-12 16:28:13 +04:00
break ;
case ' b ' :
2013-12-16 18:01:40 +04:00
context . window ( ) . display_line_at ( cursor_line , window . dimensions ( ) . line - 1 ) ;
2013-04-12 16:28:13 +04:00
break ;
2013-07-26 21:52:05 +04:00
case ' h ' :
2014-11-28 16:58:36 +03:00
context . window ( ) . scroll ( - std : : max < CharCount > ( 1 , params . count ) ) ;
2013-07-26 21:52:05 +04:00
break ;
2013-04-12 16:28:13 +04:00
case ' j ' :
2014-11-28 16:58:36 +03:00
context . window ( ) . scroll ( std : : max < LineCount > ( 1 , params . count ) ) ;
2013-04-12 16:28:13 +04:00
break ;
case ' k ' :
2014-11-28 16:58:36 +03:00
context . window ( ) . scroll ( - std : : max < LineCount > ( 1 , params . count ) ) ;
2013-04-12 16:28:13 +04:00
break ;
2013-07-24 03:38:30 +04:00
case ' l ' :
2014-11-28 16:58:36 +03:00
context . window ( ) . scroll ( std : : max < CharCount > ( 1 , params . count ) ) ;
2013-07-24 03:38:30 +04:00
break ;
2013-04-12 16:28:13 +04:00
}
2013-10-11 01:51:16 +04:00
} , " view " ,
" v,c: center cursor \n "
" t: cursor on top \n "
" b: cursor on bottom \n "
" h: scroll left \n "
" j: scroll down \n "
" k: scroll up \n "
" l: scroll right \n " ) ;
2013-04-12 16:28:13 +04:00
}
2014-11-28 16:58:36 +03:00
void replace_with_char ( Context & context , NormalParams )
2013-04-12 16:28:13 +04:00
{
2014-09-23 16:45:18 +04:00
on_next_key_with_autoinfo ( context , KeymapMode : : None ,
[ ] ( Key key , Context & context ) {
2014-01-05 19:14:58 +04:00
if ( not iswprint ( key . key ) )
2013-07-02 17:03:20 +04:00
return ;
2013-12-15 22:07:51 +04:00
ScopedEdition edition ( context ) ;
Buffer & buffer = context . buffer ( ) ;
2013-12-16 00:52:57 +04:00
SelectionList & selections = context . selections ( ) ;
2015-01-12 16:58:41 +03:00
Vector < String > strings ;
2013-12-16 00:52:57 +04:00
for ( auto & sel : selections )
{
CharCount count = char_length ( buffer , sel ) ;
strings . emplace_back ( key . key , count ) ;
}
2014-05-25 23:28:32 +04:00
selections . insert ( strings , InsertMode : : Replace ) ;
2013-10-11 01:51:16 +04:00
} , " replace with char " , " enter char to replace with \n " ) ;
2013-04-12 16:28:13 +04:00
}
2013-07-12 16:55:30 +04:00
Codepoint to_lower ( Codepoint cp ) { return tolower ( cp ) ; }
Codepoint to_upper ( Codepoint cp ) { return toupper ( cp ) ; }
Codepoint swap_case ( Codepoint cp )
2013-04-12 16:28:13 +04:00
{
2013-04-17 21:31:31 +04:00
Codepoint res = std : : tolower ( cp ) ;
return res = = cp ? std : : toupper ( cp ) : res ;
2013-04-12 16:28:13 +04:00
}
2013-07-12 16:55:30 +04:00
template < Codepoint ( * func ) ( Codepoint ) >
2014-11-28 16:58:36 +03:00
void for_each_char ( Context & context , NormalParams )
2013-04-12 16:28:13 +04:00
{
2013-12-15 22:07:51 +04:00
ScopedEdition edition ( context ) ;
2015-01-12 16:58:41 +03:00
Vector < String > sels = context . selections_content ( ) ;
2013-04-12 16:28:13 +04:00
for ( auto & sel : sels )
{
for ( auto & c : sel )
2013-07-12 16:55:30 +04:00
c = func ( c ) ;
2013-04-12 16:28:13 +04:00
}
2014-05-25 23:28:32 +04:00
context . selections ( ) . insert ( sels , InsertMode : : Replace ) ;
2013-04-12 16:28:13 +04:00
}
2014-11-28 16:58:36 +03:00
void command ( Context & context , NormalParams )
2013-04-12 16:28:13 +04:00
{
2014-08-15 02:51:24 +04:00
if ( not CommandManager : : has_instance ( ) )
return ;
2013-11-14 22:09:15 +04:00
context . input_handler ( ) . prompt (
2014-07-11 03:27:04 +04:00
" : " , " " , get_face ( " Prompt " ) ,
2013-11-05 01:53:10 +04:00
std : : bind ( & CommandManager : : complete , & CommandManager : : instance ( ) , _1 , _2 , _3 , _4 ) ,
2014-11-01 22:31:13 +03:00
[ ] ( StringView cmdline , PromptEvent event , Context & context ) {
2014-02-12 02:16:17 +04:00
if ( context . has_ui ( ) )
{
context . ui ( ) . info_hide ( ) ;
2014-07-08 23:24:51 +04:00
if ( event = = PromptEvent : : Change and context . options ( ) [ " autoinfo " ] . get < int > ( ) > 0 )
2014-02-12 02:16:17 +04:00
{
2014-10-30 02:22:54 +03:00
auto info = CommandManager : : instance ( ) . command_info ( context , cmdline ) ;
2014-07-11 03:27:04 +04:00
Face col = get_face ( " Information " ) ;
2014-02-12 02:16:17 +04:00
if ( not info . first . empty ( ) and not info . second . empty ( ) )
2014-11-08 22:08:23 +03:00
context . ui ( ) . info_show ( info . first , info . second , CharCoord { } , col , InfoStyle : : Prompt ) ;
2014-02-12 02:16:17 +04:00
}
}
2013-12-03 23:48:53 +04:00
if ( event = = PromptEvent : : Validate )
CommandManager : : instance ( ) . execute ( cmdline , context ) ;
2013-04-12 16:28:13 +04:00
} ) ;
}
2014-12-12 02:21:11 +03:00
template < bool replace >
2014-11-28 16:58:36 +03:00
void pipe ( Context & context , NormalParams )
2013-04-12 16:28:13 +04:00
{
2014-12-12 02:21:11 +03:00
const char * prompt = replace ? " pipe: " : " pipe-to: " ;
2014-07-11 03:27:04 +04:00
context . input_handler ( ) . prompt ( prompt , " " , get_face ( " Prompt " ) , shell_complete ,
2014-11-01 22:31:13 +03:00
[ ] ( StringView cmdline , PromptEvent event , Context & context )
2013-04-12 16:28:13 +04:00
{
if ( event ! = PromptEvent : : Validate )
return ;
2014-06-21 14:31:08 +04:00
StringView real_cmd ;
2013-10-11 03:31:03 +04:00
if ( cmdline . empty ( ) )
2014-06-21 14:31:08 +04:00
real_cmd = context . main_sel_register_value ( " | " ) ;
2013-10-11 03:31:03 +04:00
else
{
2014-11-01 22:31:13 +03:00
RegisterManager : : instance ( ) [ ' | ' ] = String { cmdline } ;
2013-10-11 03:31:03 +04:00
real_cmd = cmdline ;
}
if ( real_cmd . empty ( ) )
return ;
2013-12-15 22:07:51 +04:00
Buffer & buffer = context . buffer ( ) ;
SelectionList & selections = context . selections ( ) ;
2014-12-12 02:21:11 +03:00
if ( replace )
{
2015-01-12 16:58:41 +03:00
Vector < String > strings ;
2014-12-12 02:21:11 +03:00
for ( auto & sel : selections )
{
auto str = content ( buffer , sel ) ;
bool insert_eol = str . back ( ) ! = ' \n ' ;
if ( insert_eol )
str + = ' \n ' ;
str = ShellManager : : instance ( ) . pipe ( str , real_cmd , context ,
{ } , EnvVarMap { } ) ;
if ( insert_eol and str . back ( ) = = ' \n ' )
str = str . substr ( 0 , str . length ( ) - 1 ) ;
strings . push_back ( std : : move ( str ) ) ;
}
ScopedEdition edition ( context ) ;
selections . insert ( strings , InsertMode : : Replace ) ;
}
else
2013-07-24 16:38:26 +04:00
{
2014-12-12 02:21:11 +03:00
for ( auto & sel : selections )
ShellManager : : instance ( ) . pipe (
content ( buffer , sel ) , real_cmd , context , { } ,
EnvVarMap { } ) ;
2013-07-24 16:38:26 +04:00
}
2014-12-12 02:21:11 +03:00
} ) ;
}
template < InsertMode mode >
void insert_output ( Context & context , NormalParams )
{
const char * prompt = mode = = InsertMode : : Insert ? " insert-output: " : " append-output: " ;
context . input_handler ( ) . prompt ( prompt , " " , get_face ( " Prompt " ) , shell_complete ,
[ ] ( StringView cmdline , PromptEvent event , Context & context )
{
if ( event ! = PromptEvent : : Validate )
return ;
StringView real_cmd ;
if ( cmdline . empty ( ) )
real_cmd = context . main_sel_register_value ( " | " ) ;
else
{
RegisterManager : : instance ( ) [ ' | ' ] = String { cmdline } ;
real_cmd = cmdline ;
}
if ( real_cmd . empty ( ) )
return ;
auto str = ShellManager : : instance ( ) . eval ( real_cmd , context , { } ,
EnvVarMap { } ) ;
2013-12-15 22:07:51 +04:00
ScopedEdition edition ( context ) ;
2014-12-12 02:21:11 +03:00
context . selections ( ) . insert ( str , mode ) ;
2013-04-12 16:28:13 +04:00
} ) ;
}
2013-12-15 18:38:04 +04:00
template < Direction direction , SelectMode mode >
void select_next_match ( const Buffer & buffer , SelectionList & selections ,
const Regex & regex )
{
if ( mode = = SelectMode : : Replace )
{
for ( auto & sel : selections )
2014-09-25 16:29:53 +04:00
sel = keep_direction ( find_next_match < direction > ( buffer , sel , regex ) , sel ) ;
2013-12-15 18:38:04 +04:00
}
if ( mode = = SelectMode : : Extend )
{
for ( auto & sel : selections )
sel . merge_with ( find_next_match < direction > ( buffer , sel , regex ) ) ;
}
else if ( mode = = SelectMode : : Append )
{
2014-09-25 16:29:53 +04:00
auto sel = keep_direction (
find_next_match < direction > ( buffer , selections . main ( ) , regex ) ,
selections . main ( ) ) ;
selections . push_back ( std : : move ( sel ) ) ;
2013-12-15 18:38:04 +04:00
selections . set_main_index ( selections . size ( ) - 1 ) ;
}
selections . sort_and_merge_overlapping ( ) ;
}
2014-11-28 16:58:36 +03:00
void yank ( Context & context , NormalParams params )
2013-04-12 16:28:13 +04:00
{
2014-11-28 16:58:36 +03:00
RegisterManager : : instance ( ) [ params . reg ] = context . selections_content ( ) ;
2013-12-15 18:25:23 +04:00
context . print_status ( { " yanked " + to_string ( context . selections ( ) . size ( ) ) +
2014-12-11 22:36:41 +03:00
" selections to register " + StringView { params . reg } ,
get_face ( " Information " ) } ) ;
2013-04-12 16:28:13 +04:00
}
2014-11-28 16:58:36 +03:00
void erase_selections ( Context & context , NormalParams params )
2013-04-12 16:28:13 +04:00
{
2014-11-28 16:58:36 +03:00
RegisterManager : : instance ( ) [ params . reg ] = context . selections_content ( ) ;
2013-12-15 22:07:51 +04:00
ScopedEdition edition ( context ) ;
2014-05-25 23:28:32 +04:00
context . selections ( ) . erase ( ) ;
2014-05-29 08:48:40 +04:00
context . selections ( ) . avoid_eol ( ) ;
2013-04-12 16:28:13 +04:00
}
2014-11-28 16:58:36 +03:00
void change ( Context & context , NormalParams params )
2013-04-12 16:28:13 +04:00
{
2014-11-28 16:58:36 +03:00
RegisterManager : : instance ( ) [ params . reg ] = context . selections_content ( ) ;
enter_insert_mode < InsertMode : : Replace > ( context , params ) ;
2013-04-12 16:28:13 +04:00
}
2013-12-15 22:07:51 +04:00
constexpr InsertMode adapt_for_linewise ( InsertMode mode )
2013-04-12 16:28:13 +04:00
{
2013-12-15 22:07:51 +04:00
return ( ( mode = = InsertMode : : Append ) ?
InsertMode : : InsertAtNextLineBegin :
( ( mode = = InsertMode : : Insert ) ?
InsertMode : : InsertAtLineBegin :
( ( mode = = InsertMode : : Replace ) ?
InsertMode : : Replace : InsertMode : : Insert ) ) ) ;
2013-04-12 16:28:13 +04:00
}
2013-12-15 22:07:51 +04:00
template < InsertMode mode >
2014-11-28 16:58:36 +03:00
void paste ( Context & context , NormalParams params )
2013-04-12 16:28:13 +04:00
{
2014-11-28 16:58:36 +03:00
auto strings = RegisterManager : : instance ( ) [ params . reg ] . values ( context ) ;
2014-07-03 02:58:58 +04:00
InsertMode effective_mode = mode ;
2013-04-12 16:28:13 +04:00
for ( auto & str : strings )
{
if ( not str . empty ( ) and str . back ( ) = = ' \n ' )
{
2014-07-03 02:58:58 +04:00
effective_mode = adapt_for_linewise ( mode ) ;
2013-04-12 16:28:13 +04:00
break ;
}
}
2014-01-31 00:46:15 +04:00
ScopedEdition edition ( context ) ;
2014-07-03 02:58:58 +04:00
context . selections ( ) . insert ( strings , effective_mode ) ;
2013-04-12 16:28:13 +04:00
}
2014-07-03 03:25:39 +04:00
template < InsertMode mode >
2014-11-28 16:58:36 +03:00
void paste_all ( Context & context , NormalParams params )
2014-07-03 03:25:39 +04:00
{
2014-11-28 16:58:36 +03:00
auto strings = RegisterManager : : instance ( ) [ params . reg ] . values ( context ) ;
2014-07-03 03:25:39 +04:00
InsertMode effective_mode = mode ;
String all ;
2015-01-12 16:58:41 +03:00
Vector < ByteCount > offsets ;
2014-07-03 03:25:39 +04:00
for ( auto & str : strings )
{
if ( not str . empty ( ) and str . back ( ) = = ' \n ' )
effective_mode = adapt_for_linewise ( mode ) ;
all + = str ;
offsets . push_back ( all . length ( ) ) ;
}
auto & selections = context . selections ( ) ;
{
ScopedEdition edition ( context ) ;
selections . insert ( all , effective_mode , true ) ;
}
const Buffer & buffer = context . buffer ( ) ;
2015-01-12 16:58:41 +03:00
Vector < Selection > result ;
2014-07-03 03:25:39 +04:00
for ( auto & selection : selections )
{
ByteCount pos = 0 ;
for ( auto offset : offsets )
{
result . push_back ( { buffer . advance ( selection . min ( ) , pos ) ,
buffer . advance ( selection . min ( ) , offset - 1 ) } ) ;
pos = offset ;
}
}
selections = std : : move ( result ) ;
}
2013-04-12 16:28:13 +04:00
template < typename T >
2014-01-26 22:53:34 +04:00
void regex_prompt ( Context & context , const String prompt , T func )
2013-04-12 16:28:13 +04:00
{
2014-05-14 03:27:41 +04:00
SelectionList selections = context . selections ( ) ;
2014-07-11 03:27:04 +04:00
context . input_handler ( ) . prompt ( prompt , " " , get_face ( " Prompt " ) , complete_nothing ,
2014-11-01 22:31:13 +03:00
[ = ] ( StringView str , PromptEvent event , Context & context ) mutable {
2014-01-26 22:53:34 +04:00
try
2013-04-12 16:28:13 +04:00
{
2014-05-08 22:33:14 +04:00
if ( event ! = PromptEvent : : Change and context . has_ui ( ) )
context . ui ( ) . info_hide ( ) ;
2014-05-14 03:27:41 +04:00
selections . update ( ) ;
2014-01-26 22:53:34 +04:00
context . selections ( ) = selections ;
2014-07-11 03:27:04 +04:00
context . input_handler ( ) . set_prompt_face ( get_face ( " Prompt " ) ) ;
2014-01-26 22:53:34 +04:00
if ( event = = PromptEvent : : Abort )
return ;
if ( event = = PromptEvent : : Change and
( str . empty ( ) or not context . options ( ) [ " incsearch " ] . get < bool > ( ) ) )
return ;
if ( event = = PromptEvent : : Validate )
context . push_jump ( ) ;
2014-11-01 22:31:13 +03:00
Regex regex = str . empty ( ) ? Regex { }
: Regex { str . begin ( ) , str . end ( ) } ;
func ( std : : move ( regex ) , event , context ) ;
2014-01-26 22:53:34 +04:00
}
2014-10-13 16:12:33 +04:00
catch ( RegexError & err )
2014-01-26 22:53:34 +04:00
{
if ( event = = PromptEvent : : Validate )
2013-04-12 16:28:13 +04:00
throw runtime_error ( " regex error: " _str + err . what ( ) ) ;
2014-01-26 22:53:34 +04:00
else
2014-07-11 03:27:04 +04:00
context . input_handler ( ) . set_prompt_face ( get_face ( " Error " ) ) ;
2013-04-12 16:28:13 +04:00
}
2014-05-08 22:33:14 +04:00
catch ( std : : runtime_error & err )
{
if ( event = = PromptEvent : : Validate )
throw runtime_error ( " regex error: " _str + err . what ( ) ) ;
else
{
2014-07-11 03:27:04 +04:00
context . input_handler ( ) . set_prompt_face ( get_face ( " Error " ) ) ;
2014-05-08 22:33:14 +04:00
if ( context . has_ui ( ) )
{
2014-07-11 03:27:04 +04:00
Face face = get_face ( " Information " ) ;
2014-11-08 22:08:23 +03:00
context . ui ( ) . info_show ( " regex error " , err . what ( ) , CharCoord { } , face , InfoStyle : : Prompt ) ;
2014-05-08 22:33:14 +04:00
}
}
}
2014-01-26 22:53:34 +04:00
catch ( runtime_error & )
2013-04-12 16:28:13 +04:00
{
2014-01-26 22:53:34 +04:00
context . selections ( ) = selections ;
// only validation should propagate errors,
// incremental search should not.
if ( event = = PromptEvent : : Validate )
throw ;
2013-04-12 16:28:13 +04:00
}
} ) ;
}
2014-01-26 22:53:34 +04:00
template < SelectMode mode , Direction direction >
2014-11-28 16:58:36 +03:00
void search ( Context & context , NormalParams )
2014-01-26 22:53:34 +04:00
{
regex_prompt ( context , direction = = Forward ? " search: " : " reverse search: " ,
2014-07-30 22:15:16 +04:00
[ ] ( Regex ex , PromptEvent event , Context & context ) {
2014-01-26 22:53:34 +04:00
if ( ex . empty ( ) )
2014-06-21 14:31:08 +04:00
ex = Regex { context . main_sel_register_value ( " / " ) . str ( ) } ;
2014-07-30 22:15:16 +04:00
else if ( event = = PromptEvent : : Validate )
2014-01-26 22:53:34 +04:00
RegisterManager : : instance ( ) [ ' / ' ] = String { ex . str ( ) } ;
if ( not ex . empty ( ) and not ex . str ( ) . empty ( ) )
select_next_match < direction , mode > ( context . buffer ( ) , context . selections ( ) , ex ) ;
} ) ;
}
template < SelectMode mode , Direction direction >
2014-11-28 16:58:36 +03:00
void search_next ( Context & context , NormalParams params )
2014-01-26 22:53:34 +04:00
{
2014-06-21 14:31:08 +04:00
StringView str = context . main_sel_register_value ( " / " ) ;
2014-01-26 22:53:34 +04:00
if ( not str . empty ( ) )
{
try
{
2014-06-21 14:31:08 +04:00
Regex ex { str . begin ( ) , str . end ( ) } ;
2014-01-26 22:53:34 +04:00
do {
select_next_match < direction , mode > ( context . buffer ( ) , context . selections ( ) , ex ) ;
2014-11-28 16:58:36 +03:00
} while ( - - params . count > 0 ) ;
2014-01-26 22:53:34 +04:00
}
2014-10-13 16:12:33 +04:00
catch ( RegexError & err )
2014-01-26 22:53:34 +04:00
{
throw runtime_error ( " regex error: " _str + err . what ( ) ) ;
}
}
else
throw runtime_error ( " no search pattern " ) ;
}
template < bool smart >
2014-11-28 16:58:36 +03:00
void use_selection_as_search_pattern ( Context & context , NormalParams )
2014-01-26 22:53:34 +04:00
{
2015-01-12 16:58:41 +03:00
Vector < String > patterns ;
2014-01-26 22:53:34 +04:00
auto & sels = context . selections ( ) ;
const auto & buffer = context . buffer ( ) ;
for ( auto & sel : sels )
{
auto begin = utf8 : : make_iterator ( buffer . iterator_at ( sel . min ( ) ) ) ;
auto end = utf8 : : make_iterator ( buffer . iterator_at ( sel . max ( ) ) ) + 1 ;
auto content = " \\ Q " + String { begin . base ( ) , end . base ( ) } + " \\ E " ;
if ( smart )
{
if ( begin = = buffer . begin ( ) or ( is_word ( * begin ) and not is_word ( * ( begin - 1 ) ) ) )
content = " \\ b " + content ;
if ( end = = buffer . end ( ) or ( is_word ( * ( end - 1 ) ) and not is_word ( * end ) ) )
content = content + " \\ b " ;
}
patterns . push_back ( std : : move ( content ) ) ;
}
RegisterManager : : instance ( ) [ ' / ' ] = patterns ;
}
2014-11-28 16:58:36 +03:00
void select_regex ( Context & context , NormalParams )
2013-04-12 16:28:13 +04:00
{
2014-07-30 22:15:16 +04:00
regex_prompt ( context , " select: " , [ ] ( Regex ex , PromptEvent event , Context & context ) {
2013-04-12 16:28:13 +04:00
if ( ex . empty ( ) )
2014-06-21 14:31:08 +04:00
ex = Regex { context . main_sel_register_value ( " / " ) . str ( ) } ;
2014-07-30 22:15:16 +04:00
else if ( event = = PromptEvent : : Validate )
2013-04-12 16:28:13 +04:00
RegisterManager : : instance ( ) [ ' / ' ] = String { ex . str ( ) } ;
2013-05-13 16:28:03 +04:00
if ( not ex . empty ( ) and not ex . str ( ) . empty ( ) )
2014-05-25 23:28:32 +04:00
select_all_matches ( context . selections ( ) , ex ) ;
2013-04-12 16:28:13 +04:00
} ) ;
}
2014-11-28 16:58:36 +03:00
void split_regex ( Context & context , NormalParams )
2013-04-12 16:28:13 +04:00
{
2014-07-30 22:15:16 +04:00
regex_prompt ( context , " split: " , [ ] ( Regex ex , PromptEvent event , Context & context ) {
2013-04-12 16:28:13 +04:00
if ( ex . empty ( ) )
2014-06-21 14:31:08 +04:00
ex = Regex { context . main_sel_register_value ( " / " ) . str ( ) } ;
2014-07-30 22:15:16 +04:00
else if ( event = = PromptEvent : : Validate )
2013-04-12 16:28:13 +04:00
RegisterManager : : instance ( ) [ ' / ' ] = String { ex . str ( ) } ;
2013-05-13 16:28:03 +04:00
if ( not ex . empty ( ) and not ex . str ( ) . empty ( ) )
2014-05-25 23:28:32 +04:00
split_selections ( context . selections ( ) , ex ) ;
2013-04-12 16:28:13 +04:00
} ) ;
}
2014-11-28 16:58:36 +03:00
void split_lines ( Context & context , NormalParams )
2013-04-12 16:28:13 +04:00
{
2013-12-15 18:25:23 +04:00
auto & selections = context . selections ( ) ;
2013-12-14 22:38:14 +04:00
auto & buffer = context . buffer ( ) ;
2015-01-12 16:58:41 +03:00
Vector < Selection > res ;
2013-12-14 22:38:14 +04:00
for ( auto & sel : selections )
{
2014-01-28 23:05:49 +04:00
if ( sel . anchor ( ) . line = = sel . cursor ( ) . line )
2013-12-13 03:56:53 +04:00
{
2013-12-14 22:38:14 +04:00
res . push_back ( std : : move ( sel ) ) ;
continue ;
2013-12-13 03:56:53 +04:00
}
2013-12-14 22:38:14 +04:00
auto min = sel . min ( ) ;
auto max = sel . max ( ) ;
2014-09-19 16:45:24 +04:00
res . push_back ( keep_direction ( { min , { min . line , buffer [ min . line ] . length ( ) - 1 } } , sel ) ) ;
2013-12-14 22:38:14 +04:00
for ( auto line = min . line + 1 ; line < max . line ; + + line )
2014-09-19 16:45:24 +04:00
res . push_back ( keep_direction ( { line , { line , buffer [ line ] . length ( ) - 1 } } , sel ) ) ;
res . push_back ( keep_direction ( { max . line , max } , sel ) ) ;
2013-12-14 22:38:14 +04:00
}
selections = std : : move ( res ) ;
2013-04-12 16:28:13 +04:00
}
2014-12-28 14:16:51 +03:00
void join_lines_select_spaces ( Context & context , NormalParams )
2013-04-12 16:28:13 +04:00
{
2013-12-14 22:38:14 +04:00
auto & buffer = context . buffer ( ) ;
2015-01-12 16:58:41 +03:00
Vector < Selection > selections ;
2014-01-04 00:41:47 +04:00
for ( auto & sel : context . selections ( ) )
{
2014-09-25 22:26:27 +04:00
const LineCount min_line = sel . min ( ) . line ;
const LineCount max_line = sel . max ( ) . line ;
auto end_line = std : : min ( buffer . line_count ( ) - 1 ,
max_line + ( min_line = = max_line ? 1 : 0 ) ) ;
for ( LineCount line = min_line ; line < end_line ; + + line )
2014-01-04 00:41:47 +04:00
{
auto begin = buffer . iterator_at ( { line , buffer [ line ] . length ( ) - 1 } ) ;
auto end = begin + 1 ;
skip_while ( end , buffer . end ( ) , is_horizontal_blank ) ;
selections . push_back ( { begin . coord ( ) , ( end - 1 ) . coord ( ) } ) ;
}
}
if ( selections . empty ( ) )
return ;
context . selections ( ) = selections ;
2013-12-15 22:07:51 +04:00
ScopedEdition edition ( context ) ;
2014-05-25 23:28:32 +04:00
context . selections ( ) . insert ( " " _str , InsertMode : : Replace ) ;
2013-04-12 16:28:13 +04:00
}
2014-12-28 14:16:51 +03:00
void join_lines ( Context & context , NormalParams params )
2013-04-23 20:54:31 +04:00
{
2014-05-13 02:25:15 +04:00
SelectionList sels { context . selections ( ) } ;
auto restore_sels = on_scope_end ( [ & ] {
sels . update ( ) ;
context . selections ( ) = std : : move ( sels ) ;
} ) ;
2014-12-28 14:16:51 +03:00
join_lines_select_spaces ( context , params ) ;
2013-04-23 20:54:31 +04:00
}
2013-04-12 16:28:13 +04:00
template < bool matching >
2014-11-28 16:58:36 +03:00
void keep ( Context & context , NormalParams )
2013-04-12 16:28:13 +04:00
{
constexpr const char * prompt = matching ? " keep matching: " : " keep not matching: " ;
2014-07-30 22:15:16 +04:00
regex_prompt ( context , prompt , [ ] ( const Regex & ex , PromptEvent , Context & context ) {
2013-10-26 21:46:21 +04:00
if ( ex . empty ( ) )
return ;
2013-06-03 20:58:09 +04:00
const Buffer & buffer = context . buffer ( ) ;
2015-01-12 16:58:41 +03:00
Vector < Selection > keep ;
2013-12-15 18:25:23 +04:00
for ( auto & sel : context . selections ( ) )
2013-04-12 16:28:13 +04:00
{
2014-10-13 16:12:33 +04:00
if ( regex_search ( buffer . iterator_at ( sel . min ( ) ) ,
utf8 : : next ( buffer . iterator_at ( sel . max ( ) ) , buffer . end ( ) ) , ex ) = = matching )
2013-04-12 16:28:13 +04:00
keep . push_back ( sel ) ;
}
if ( keep . empty ( ) )
throw runtime_error ( " no selections remaining " ) ;
2014-06-01 19:01:38 +04:00
context . set_selections ( std : : move ( keep ) ) ;
2013-04-12 16:28:13 +04:00
} ) ;
}
2014-11-28 16:58:36 +03:00
void keep_pipe ( Context & context , NormalParams )
2014-05-05 21:00:24 +04:00
{
context . input_handler ( ) . prompt (
2014-07-11 03:27:04 +04:00
" keep pipe: " , " " , get_face ( " Prompt " ) , shell_complete ,
2014-11-01 22:31:13 +03:00
[ ] ( StringView cmdline , PromptEvent event , Context & context ) {
2014-05-05 21:00:24 +04:00
if ( event ! = PromptEvent : : Validate )
return ;
const Buffer & buffer = context . buffer ( ) ;
auto & shell_manager = ShellManager : : instance ( ) ;
2015-01-12 16:58:41 +03:00
Vector < Selection > keep ;
2014-05-05 21:00:24 +04:00
for ( auto & sel : context . selections ( ) )
{
int status = 0 ;
shell_manager . pipe ( content ( buffer , sel ) , cmdline , context ,
{ } , EnvVarMap { } , & status ) ;
if ( status = = 0 )
keep . push_back ( sel ) ;
}
if ( keep . empty ( ) )
throw runtime_error ( " no selections remaining " ) ;
2014-06-01 19:01:38 +04:00
context . set_selections ( std : : move ( keep ) ) ;
2014-05-05 21:00:24 +04:00
} ) ;
}
2013-10-30 12:45:47 +04:00
template < bool indent_empty = false >
2014-11-28 16:58:36 +03:00
void indent ( Context & context , NormalParams )
2013-04-12 16:28:13 +04:00
{
2013-10-14 16:47:43 +04:00
CharCount indent_width = context . options ( ) [ " indentwidth " ] . get < int > ( ) ;
String indent = indent_width = = 0 ? " \t " : String { ' ' , indent_width } ;
2013-04-12 16:28:13 +04:00
2013-12-14 22:38:14 +04:00
auto & buffer = context . buffer ( ) ;
2015-01-12 16:58:41 +03:00
Vector < Selection > sels ;
2014-11-28 16:22:54 +03:00
LineCount last_line = 0 ;
2013-12-15 18:25:23 +04:00
for ( auto & sel : context . selections ( ) )
2013-12-14 22:38:14 +04:00
{
2014-11-28 16:22:54 +03:00
for ( auto line = std : : max ( last_line , sel . min ( ) . line ) ; line < sel . max ( ) . line + 1 ; + + line )
2013-12-14 22:38:14 +04:00
{
if ( indent_empty or buffer [ line ] . length ( ) > 1 )
2014-05-11 22:44:19 +04:00
sels . push_back ( { line , line } ) ;
2013-12-14 22:38:14 +04:00
}
2014-11-28 16:22:54 +03:00
// avoid reindenting the same line if multiple selections are on it
last_line = sel . max ( ) . line + 1 ;
2013-12-14 22:38:14 +04:00
}
2014-03-29 13:02:09 +04:00
if ( not sels . empty ( ) )
{
ScopedEdition edition ( context ) ;
2014-05-14 02:22:54 +04:00
SelectionList selections { buffer , std : : move ( sels ) } ;
2014-05-25 23:28:32 +04:00
selections . insert ( indent , InsertMode : : Insert ) ;
2014-03-29 13:02:09 +04:00
}
2013-04-12 16:28:13 +04:00
}
2013-11-14 04:20:49 +04:00
template < bool deindent_incomplete = true >
2014-11-28 16:58:36 +03:00
void deindent ( Context & context , NormalParams )
2013-04-12 16:28:13 +04:00
{
2013-10-14 16:47:43 +04:00
CharCount tabstop = context . options ( ) [ " tabstop " ] . get < int > ( ) ;
CharCount indent_width = context . options ( ) [ " indentwidth " ] . get < int > ( ) ;
if ( indent_width = = 0 )
indent_width = tabstop ;
2013-10-08 22:38:10 +04:00
2013-12-14 22:38:14 +04:00
auto & buffer = context . buffer ( ) ;
2015-01-12 16:58:41 +03:00
Vector < Selection > sels ;
2014-11-28 16:22:54 +03:00
LineCount last_line = 0 ;
2013-12-15 18:25:23 +04:00
for ( auto & sel : context . selections ( ) )
2013-12-14 22:38:14 +04:00
{
2014-11-28 16:22:54 +03:00
for ( auto line = std : : max ( sel . min ( ) . line , last_line ) ;
line < sel . max ( ) . line + 1 ; + + line )
2013-12-14 22:38:14 +04:00
{
CharCount width = 0 ;
2014-10-03 16:39:13 +04:00
auto content = buffer [ line ] ;
2013-12-14 22:38:14 +04:00
for ( auto column = 0 _byte ; column < content . length ( ) ; + + column )
2013-10-08 22:38:10 +04:00
{
2013-12-14 22:38:14 +04:00
const char c = content [ column ] ;
if ( c = = ' \t ' )
width = ( width / tabstop + 1 ) * tabstop ;
else if ( c = = ' ' )
+ + width ;
else
{
if ( deindent_incomplete and width ! = 0 )
2014-05-11 22:44:19 +04:00
sels . push_back ( { line , ByteCoord { line , column - 1 } } ) ;
2013-12-14 22:38:14 +04:00
break ;
}
if ( width = = indent_width )
2013-10-08 22:38:10 +04:00
{
2014-05-11 22:44:19 +04:00
sels . push_back ( { line , ByteCoord { line , column } } ) ;
2013-12-14 22:38:14 +04:00
break ;
2013-10-08 22:38:10 +04:00
}
}
2013-12-14 22:38:14 +04:00
}
2014-11-28 16:22:54 +03:00
// avoid reindenting the same line if multiple selections are on it
last_line = sel . max ( ) . line + 1 ;
2013-12-14 22:38:14 +04:00
}
2014-03-29 13:02:09 +04:00
if ( not sels . empty ( ) )
{
ScopedEdition edition ( context ) ;
2014-05-14 02:22:54 +04:00
SelectionList selections { context . buffer ( ) , std : : move ( sels ) } ;
2014-05-25 23:28:32 +04:00
selections . erase ( ) ;
2014-03-29 13:02:09 +04:00
}
2013-04-12 16:28:13 +04:00
}
2013-10-08 22:38:10 +04:00
template < ObjectFlags flags , SelectMode mode = SelectMode : : Replace >
2014-11-28 16:58:36 +03:00
void select_object ( Context & context , NormalParams params )
2013-04-12 16:28:13 +04:00
{
2014-11-28 16:58:36 +03:00
int level = params . count < = 0 ? 0 : params . count - 1 ;
2014-09-23 16:45:18 +04:00
on_next_key_with_autoinfo ( context , KeymapMode : : None ,
[ level ] ( Key key , Context & context ) {
2013-10-08 22:24:56 +04:00
if ( key . modifiers ! = Key : : Modifiers : : None )
return ;
const Codepoint c = key . key ;
static constexpr struct
2013-04-12 16:28:13 +04:00
{
2013-10-08 22:24:56 +04:00
Codepoint key ;
Selection ( * func ) ( const Buffer & , const Selection & , ObjectFlags ) ;
} selectors [ ] = {
2014-05-27 00:44:57 +04:00
{ ' w ' , select_word < Word > } ,
{ ' W ' , select_word < WORD > } ,
{ ' s ' , select_sentence } ,
{ ' p ' , select_paragraph } ,
2014-06-11 17:00:45 +04:00
{ ' ' , select_whitespaces } ,
2014-05-27 00:44:57 +04:00
{ ' i ' , select_indent } ,
2014-05-27 12:50:12 +04:00
{ ' n ' , select_number } ,
2013-04-12 16:28:13 +04:00
} ;
2013-10-08 22:24:56 +04:00
for ( auto & sel : selectors )
{
if ( c = = sel . key )
2014-01-01 22:45:28 +04:00
return select < mode > ( context , std : : bind ( sel . func , _1 , _2 , flags ) ) ;
2013-10-08 22:24:56 +04:00
}
2013-04-12 16:28:13 +04:00
2014-04-03 04:19:49 +04:00
static const struct
2013-10-08 22:24:56 +04:00
{
CodepointPair pair ;
Codepoint name ;
} surrounding_pairs [ ] = {
{ { ' ( ' , ' ) ' } , ' b ' } ,
{ { ' { ' , ' } ' } , ' B ' } ,
{ { ' [ ' , ' ] ' } , ' r ' } ,
2014-01-23 23:45:09 +04:00
{ { ' < ' , ' > ' } , ' a ' } ,
2014-01-01 17:59:25 +04:00
{ { ' " ' , ' " ' } , ' Q ' } ,
{ { ' \' ' , ' \' ' } , ' q ' } ,
2014-02-15 04:35:01 +04:00
{ { ' ` ' , ' ` ' } , ' g ' } ,
2013-10-08 22:24:56 +04:00
} ;
2014-01-01 22:45:28 +04:00
for ( auto & sur : surrounding_pairs )
2013-10-08 22:24:56 +04:00
{
if ( sur . pair . first = = c or sur . pair . second = = c or
( sur . name ! = 0 and sur . name = = c ) )
2014-01-01 22:45:28 +04:00
return select < mode > ( context , std : : bind ( select_surrounding , _1 , _2 ,
sur . pair , level , flags ) ) ;
2013-10-08 22:24:56 +04:00
}
2013-10-11 01:51:16 +04:00
} , " select object " ,
" b,(,): parenthesis block \n "
" B,{,}: braces block \n "
" r,[,]: brackets block \n "
2014-01-28 01:11:45 +04:00
" a,<,>: angle block \n "
2014-01-01 17:59:25 +04:00
" \" ,Q: double quote string \n "
" ',q: single quote string \n "
2014-02-15 04:35:01 +04:00
" `,g: grave quote string \n "
2013-10-11 01:51:16 +04:00
" w: word \n "
" W: WORD \n "
" s: sentence \n "
" p: paragraph \n "
2014-06-11 17:00:45 +04:00
" ␣: whitespaces \n "
2013-10-11 01:51:16 +04:00
" i: indent \n " ) ;
2013-04-12 16:28:13 +04:00
}
template < Key : : NamedKey key >
2014-11-28 16:58:36 +03:00
void scroll ( Context & context , NormalParams )
2013-04-12 16:28:13 +04:00
{
static_assert ( key = = Key : : PageUp or key = = Key : : PageDown ,
2013-04-16 16:08:15 +04:00
" scrool only implements PageUp and PageDown " ) ;
2013-04-12 16:28:13 +04:00
Window & window = context . window ( ) ;
Buffer & buffer = context . buffer ( ) ;
2014-05-07 22:51:01 +04:00
CharCoord position = window . position ( ) ;
2013-04-12 16:28:13 +04:00
LineCount cursor_line = 0 ;
if ( key = = Key : : PageUp )
{
position . line - = ( window . dimensions ( ) . line - 2 ) ;
cursor_line = position . line ;
}
else if ( key = = Key : : PageDown )
{
position . line + = ( window . dimensions ( ) . line - 2 ) ;
cursor_line = position . line + window . dimensions ( ) . line - 1 ;
}
2013-06-03 21:10:28 +04:00
auto cursor_pos = utf8 : : advance ( buffer . iterator_at ( position . line ) ,
buffer . iterator_at ( position . line + 1 ) ,
2013-04-12 16:28:13 +04:00
position . column ) ;
2013-12-21 00:10:08 +04:00
select_coord ( buffer , cursor_pos . coord ( ) , context . selections ( ) ) ;
2013-04-12 16:28:13 +04:00
window . set_position ( position ) ;
}
2014-11-28 16:58:36 +03:00
void rotate_selections ( Context & context , NormalParams params )
2013-04-12 16:28:13 +04:00
{
2014-11-28 16:58:36 +03:00
context . selections ( ) . rotate_main ( params . count ! = 0 ? params . count : 1 ) ;
2013-04-12 16:28:13 +04:00
}
2014-11-28 16:58:36 +03:00
void rotate_selections_content ( Context & context , NormalParams params )
2013-10-02 22:10:31 +04:00
{
2014-11-28 16:58:36 +03:00
int group = params . count ;
2014-03-27 03:42:10 +04:00
int count = 1 ;
2013-12-16 00:37:07 +04:00
auto strings = context . selections_content ( ) ;
2014-03-27 03:42:10 +04:00
if ( group = = 0 or group > ( int ) strings . size ( ) )
group = ( int ) strings . size ( ) ;
count = count % group ;
for ( auto it = strings . begin ( ) ; it ! = strings . end ( ) ; )
{
auto end = std : : min ( strings . end ( ) , it + group ) ;
std : : rotate ( it , end - count , end ) ;
it = end ;
}
2014-05-25 23:28:32 +04:00
context . selections ( ) . insert ( strings , InsertMode : : Replace ) ;
2014-03-27 03:42:10 +04:00
context . selections ( ) . rotate_main ( count ) ;
2013-10-02 22:10:31 +04:00
}
2013-04-12 16:28:13 +04:00
enum class SelectFlags
{
None = 0 ,
Reverse = 1 ,
Inclusive = 2 ,
Extend = 4
} ;
2014-10-23 21:55:45 +04:00
template < > struct WithBitOps < SelectFlags > : std : : true_type { } ;
2013-04-12 16:28:13 +04:00
template < SelectFlags flags >
2014-11-28 16:58:36 +03:00
void select_to_next_char ( Context & context , NormalParams params )
2013-04-12 16:28:13 +04:00
{
2014-09-23 16:45:18 +04:00
on_next_key_with_autoinfo ( context , KeymapMode : : None ,
2014-11-28 16:58:36 +03:00
[ params ] ( Key key , Context & context ) {
2013-12-14 17:21:07 +04:00
select < flags & SelectFlags : : Extend ? SelectMode : : Extend : SelectMode : : Replace > (
2014-01-01 22:45:28 +04:00
context ,
2013-04-12 16:28:13 +04:00
std : : bind ( flags & SelectFlags : : Reverse ? select_to_reverse : select_to ,
2014-11-28 16:58:36 +03:00
_1 , _2 , key . key , params . count , flags & SelectFlags : : Inclusive ) ) ;
2013-11-14 04:12:15 +04:00
} , " select to next char " , " enter char to select to " ) ;
2013-04-12 16:28:13 +04:00
}
2014-12-10 00:59:47 +03:00
static bool is_basic_alpha ( Codepoint c )
{
return ( c > = ' a ' and c < = ' z ' ) or ( c > = ' A ' and c < = ' Z ' ) ;
}
2014-11-28 16:58:36 +03:00
void start_or_end_macro_recording ( Context & context , NormalParams )
2013-04-12 16:28:13 +04:00
{
2013-11-14 22:09:15 +04:00
if ( context . input_handler ( ) . is_recording ( ) )
context . input_handler ( ) . stop_recording ( ) ;
2013-04-12 16:28:13 +04:00
else
2014-09-23 16:45:18 +04:00
on_next_key_with_autoinfo ( context , KeymapMode : : None ,
[ ] ( Key key , Context & context ) {
2014-12-10 00:59:47 +03:00
if ( key . modifiers = = Key : : Modifiers : : None and is_basic_alpha ( key . key ) )
2014-07-01 11:48:52 +04:00
context . input_handler ( ) . start_recording ( tolower ( key . key ) ) ;
2013-10-11 01:51:16 +04:00
} , " record macro " , " enter macro name " ) ;
2013-04-12 16:28:13 +04:00
}
2014-11-28 16:58:36 +03:00
void end_macro_recording ( Context & context , NormalParams )
2014-07-30 04:08:55 +04:00
{
if ( context . input_handler ( ) . is_recording ( ) )
context . input_handler ( ) . stop_recording ( ) ;
}
2014-11-28 16:58:36 +03:00
void replay_macro ( Context & context , NormalParams params )
2013-04-12 16:28:13 +04:00
{
2014-09-23 16:45:18 +04:00
on_next_key_with_autoinfo ( context , KeymapMode : : None ,
2014-11-28 16:58:36 +03:00
[ params ] ( Key key , Context & context ) mutable {
2014-12-10 00:59:47 +03:00
if ( key . modifiers = = Key : : Modifiers : : None and is_basic_alpha ( key . key ) )
2013-04-12 16:28:13 +04:00
{
2014-12-10 00:59:47 +03:00
static bool running_macros [ 26 ] = { } ;
2014-07-01 11:48:52 +04:00
const char name = tolower ( key . key ) ;
2014-12-10 00:59:47 +03:00
const size_t idx = ( size_t ) ( name - ' a ' ) ;
if ( running_macros [ idx ] )
2013-05-17 16:22:48 +04:00
throw runtime_error ( " recursive macros call detected " ) ;
2015-01-06 16:40:56 +03:00
ArrayView < String > reg_val = RegisterManager : : instance ( ) [ name ] . values ( context ) ;
2013-04-12 16:28:13 +04:00
if ( not reg_val . empty ( ) )
{
2014-12-10 00:59:47 +03:00
running_macros [ idx ] = true ;
auto stop = on_scope_end ( [ & ] { running_macros [ idx ] = false ; } ) ;
2013-04-12 16:28:13 +04:00
auto keys = parse_keys ( reg_val [ 0 ] ) ;
2013-12-15 22:07:51 +04:00
ScopedEdition edition ( context ) ;
2014-11-28 16:58:36 +03:00
do { exec_keys ( keys , context ) ; } while ( - - params . count > 0 ) ;
2013-04-12 16:28:13 +04:00
}
}
2013-10-11 01:51:16 +04:00
} , " replay macro " , " enter macro name " ) ;
2013-04-12 16:28:13 +04:00
}
2013-07-02 16:55:34 +04:00
template < Direction direction >
2014-11-28 16:58:36 +03:00
void jump ( Context & context , NormalParams )
2013-04-12 16:28:13 +04:00
{
2013-07-02 16:55:34 +04:00
auto jump = ( direction = = Forward ) ?
2013-04-12 16:28:13 +04:00
context . jump_forward ( ) : context . jump_backward ( ) ;
2013-05-27 21:23:59 +04:00
Buffer & buffer = const_cast < Buffer & > ( jump . buffer ( ) ) ;
2013-04-12 16:28:13 +04:00
BufferManager : : instance ( ) . set_last_used_buffer ( buffer ) ;
if ( & buffer ! = & context . buffer ( ) )
2013-12-21 00:10:08 +04:00
context . change_buffer ( buffer ) ;
2013-12-15 18:25:23 +04:00
context . selections ( ) = jump ;
2013-04-12 16:28:13 +04:00
}
2014-11-28 16:58:36 +03:00
void save_selections ( Context & context , NormalParams )
2013-10-02 21:48:50 +04:00
{
2013-10-08 22:28:57 +04:00
context . push_jump ( ) ;
2013-12-15 18:25:23 +04:00
context . print_status ( { " saved " + to_string ( context . selections ( ) . size ( ) ) +
2014-07-11 03:27:04 +04:00
" selections " , get_face ( " Information " ) } ) ;
2013-10-02 21:48:50 +04:00
}
2014-11-28 16:58:36 +03:00
void align ( Context & context , NormalParams )
2013-05-15 20:47:50 +04:00
{
2013-12-15 18:25:23 +04:00
auto & selections = context . selections ( ) ;
2013-05-15 20:47:50 +04:00
auto & buffer = context . buffer ( ) ;
2013-11-06 23:11:46 +04:00
const CharCount tabstop = context . options ( ) [ " tabstop " ] . get < int > ( ) ;
2013-05-15 20:47:50 +04:00
2015-01-12 16:58:41 +03:00
Vector < Vector < const Selection * > > columns ;
2013-09-18 21:54:04 +04:00
LineCount last_line = - 1 ;
size_t column = 0 ;
2013-05-15 20:47:50 +04:00
for ( auto & sel : selections )
2013-09-02 16:30:46 +04:00
{
2014-01-28 23:05:49 +04:00
auto line = sel . cursor ( ) . line ;
if ( sel . anchor ( ) . line ! = line )
2013-09-02 16:30:46 +04:00
throw runtime_error ( " align cannot work with multi line selections " ) ;
2013-09-18 21:54:04 +04:00
column = ( line = = last_line ) ? column + 1 : 0 ;
if ( column > = columns . size ( ) )
columns . resize ( column + 1 ) ;
columns [ column ] . push_back ( & sel ) ;
last_line = line ;
2013-09-02 16:30:46 +04:00
}
2013-05-15 20:47:50 +04:00
2013-11-06 23:11:46 +04:00
const bool use_tabs = context . options ( ) [ " aligntab " ] . get < bool > ( ) ;
2013-09-18 21:54:04 +04:00
for ( auto & col : columns )
2013-05-15 20:47:50 +04:00
{
2013-11-06 23:11:46 +04:00
CharCount maxcol = 0 ;
2013-09-18 21:54:04 +04:00
for ( auto & sel : col )
2014-01-28 23:05:49 +04:00
maxcol = std : : max ( get_column ( buffer , tabstop , sel - > cursor ( ) ) , maxcol ) ;
2013-09-18 21:54:04 +04:00
for ( auto & sel : col )
{
2013-11-14 16:10:51 +04:00
auto insert_coord = sel - > min ( ) ;
2014-01-28 23:05:49 +04:00
auto lastcol = get_column ( buffer , tabstop , sel - > cursor ( ) ) ;
2013-11-06 23:11:46 +04:00
String padstr ;
if ( not use_tabs )
padstr = String { ' ' , maxcol - lastcol } ;
else
{
auto inscol = get_column ( buffer , tabstop , insert_coord ) ;
auto targetcol = maxcol - ( lastcol - inscol ) ;
auto tabcol = inscol - ( inscol % tabstop ) ;
auto tabs = ( targetcol - tabcol ) / tabstop ;
auto spaces = targetcol - ( tabs ? ( tabcol + tabs * tabstop ) : inscol ) ;
padstr = String { ' \t ' , tabs } + String { ' ' , spaces } ;
}
buffer . insert ( buffer . iterator_at ( insert_coord ) , std : : move ( padstr ) ) ;
2013-09-18 21:54:04 +04:00
}
2014-07-22 03:41:10 +04:00
selections . update ( ) ;
2013-05-15 20:47:50 +04:00
}
}
2014-11-28 16:58:36 +03:00
void copy_indent ( Context & context , NormalParams params )
2013-11-22 03:20:21 +04:00
{
2014-11-28 16:58:36 +03:00
int selection = params . count ;
2013-11-22 03:20:21 +04:00
auto & buffer = context . buffer ( ) ;
2013-12-15 18:25:23 +04:00
auto & selections = context . selections ( ) ;
2015-01-12 16:58:41 +03:00
Vector < LineCount > lines ;
2013-11-22 03:20:21 +04:00
for ( auto sel : selections )
{
for ( LineCount l = sel . min ( ) . line ; l < sel . max ( ) . line + 1 ; + + l )
lines . push_back ( l ) ;
}
if ( selection > selections . size ( ) )
throw runtime_error ( " invalid selection index " ) ;
if ( selection = = 0 )
2014-02-28 02:19:37 +04:00
selection = context . selections ( ) . main_index ( ) + 1 ;
2013-11-22 03:20:21 +04:00
2014-06-09 22:26:53 +04:00
auto ref_line = selections [ selection - 1 ] . min ( ) . line ;
2014-10-03 16:39:13 +04:00
auto line = buffer [ ref_line ] ;
2013-11-22 03:20:21 +04:00
auto it = line . begin ( ) ;
while ( it ! = line . end ( ) and is_horizontal_blank ( * it ) )
+ + it ;
2014-06-09 22:26:53 +04:00
const StringView indent = line . substr ( 0 _byte , ( int ) ( it - line . begin ( ) ) ) ;
2013-11-22 03:20:21 +04:00
2013-12-15 22:07:51 +04:00
ScopedEdition edition { context } ;
2013-11-22 03:20:21 +04:00
for ( auto & l : lines )
{
2014-06-09 22:26:53 +04:00
if ( l = = ref_line )
continue ;
2014-10-03 16:39:13 +04:00
auto line = buffer [ l ] ;
2013-11-22 03:20:21 +04:00
ByteCount i = 0 ;
while ( i < line . length ( ) and is_horizontal_blank ( line [ i ] ) )
+ + i ;
buffer . erase ( buffer . iterator_at ( l ) , buffer . iterator_at ( { l , i } ) ) ;
buffer . insert ( buffer . iterator_at ( l ) , indent ) ;
}
}
2014-11-28 16:58:36 +03:00
void tabs_to_spaces ( Context & context , NormalParams params )
2014-03-02 05:04:26 +04:00
{
auto & buffer = context . buffer ( ) ;
const CharCount opt_tabstop = context . options ( ) [ " tabstop " ] . get < int > ( ) ;
2014-11-28 16:58:36 +03:00
const CharCount tabstop = params . count = = 0 ? opt_tabstop : params . count ;
2015-01-12 16:58:41 +03:00
Vector < Selection > tabs ;
Vector < String > spaces ;
2014-03-02 05:04:26 +04:00
for ( auto & sel : context . selections ( ) )
{
for ( auto it = buffer . iterator_at ( sel . min ( ) ) ,
2014-06-10 16:30:37 +04:00
end = buffer . iterator_at ( sel . max ( ) ) + 1 ; it ! = end ; + + it )
2014-03-02 05:04:26 +04:00
{
if ( * it = = ' \t ' )
{
CharCount col = get_column ( buffer , opt_tabstop , it . coord ( ) ) ;
CharCount end_col = ( col / tabstop + 1 ) * tabstop ;
2014-06-10 16:30:37 +04:00
tabs . push_back ( { it . coord ( ) } ) ;
spaces . push_back ( String { ' ' , end_col - col } ) ;
2014-03-02 05:04:26 +04:00
}
}
}
2014-06-10 16:30:37 +04:00
if ( not tabs . empty ( ) )
SelectionList { buffer , std : : move ( tabs ) } . insert ( spaces , InsertMode : : Replace ) ;
2014-03-02 05:04:26 +04:00
}
2014-11-28 16:58:36 +03:00
void spaces_to_tabs ( Context & context , NormalParams params )
2014-03-02 05:04:26 +04:00
{
auto & buffer = context . buffer ( ) ;
const CharCount opt_tabstop = context . options ( ) [ " tabstop " ] . get < int > ( ) ;
2014-11-28 16:58:36 +03:00
const CharCount tabstop = params . count = = 0 ? opt_tabstop : params . count ;
2015-01-12 16:58:41 +03:00
Vector < Selection > spaces ;
2014-03-02 05:04:26 +04:00
for ( auto & sel : context . selections ( ) )
{
for ( auto it = buffer . iterator_at ( sel . min ( ) ) ,
end = buffer . iterator_at ( sel . max ( ) ) + 1 ; it ! = end ; )
{
if ( * it = = ' ' )
{
auto spaces_beg = it ;
auto spaces_end = spaces_beg + 1 ;
CharCount col = get_column ( buffer , opt_tabstop , spaces_end . coord ( ) ) ;
while ( * spaces_end = = ' ' and ( col % tabstop ) ! = 0 )
{
+ + spaces_end ;
+ + col ;
}
if ( ( col % tabstop ) = = 0 )
2014-06-10 16:30:37 +04:00
spaces . push_back ( { spaces_beg . coord ( ) , ( spaces_end - 1 ) . coord ( ) } ) ;
2014-08-29 02:19:18 +04:00
else if ( * spaces_end = = ' \t ' )
spaces . push_back ( { spaces_beg . coord ( ) , spaces_end . coord ( ) } ) ;
2014-06-10 16:30:37 +04:00
it = spaces_end ;
2014-03-02 05:04:26 +04:00
}
else
+ + it ;
}
}
2014-06-10 16:30:37 +04:00
if ( not spaces . empty ( ) )
SelectionList { buffer , std : : move ( spaces ) } . insert ( " \t " _str , InsertMode : : Replace ) ;
2014-03-02 05:04:26 +04:00
}
2014-11-28 16:58:36 +03:00
void undo ( Context & context , NormalParams )
2013-12-15 23:51:09 +04:00
{
2014-05-12 22:10:25 +04:00
Buffer & buffer = context . buffer ( ) ;
size_t timestamp = buffer . timestamp ( ) ;
bool res = buffer . undo ( ) ;
if ( res )
2013-12-15 23:51:09 +04:00
{
2014-05-12 22:10:25 +04:00
auto ranges = compute_modified_ranges ( buffer , timestamp ) ;
2014-06-01 18:57:12 +04:00
if ( not ranges . empty ( ) )
2014-06-01 19:01:38 +04:00
context . set_selections ( std : : move ( ranges ) ) ;
2014-12-19 02:17:38 +03:00
context . selections ( ) . avoid_eol ( ) ;
2013-12-15 23:51:09 +04:00
}
else if ( not res )
2014-07-11 03:27:04 +04:00
context . print_status ( { " nothing left to undo " , get_face ( " Information " ) } ) ;
2013-12-15 23:51:09 +04:00
}
2014-11-28 16:58:36 +03:00
void redo ( Context & context , NormalParams )
2013-12-15 23:51:09 +04:00
{
using namespace std : : placeholders ;
2014-05-12 22:10:25 +04:00
Buffer & buffer = context . buffer ( ) ;
size_t timestamp = buffer . timestamp ( ) ;
bool res = buffer . redo ( ) ;
if ( res )
2013-12-15 23:51:09 +04:00
{
2014-05-12 22:10:25 +04:00
auto ranges = compute_modified_ranges ( buffer , timestamp ) ;
2014-06-01 18:57:12 +04:00
if ( not ranges . empty ( ) )
2014-06-01 19:01:38 +04:00
context . set_selections ( std : : move ( ranges ) ) ;
2014-12-19 02:17:38 +03:00
context . selections ( ) . avoid_eol ( ) ;
2013-12-15 23:51:09 +04:00
}
2014-05-12 22:10:25 +04:00
2013-12-15 23:51:09 +04:00
else if ( not res )
2014-07-11 03:27:04 +04:00
context . print_status ( { " nothing left to redo " , get_face ( " Information " ) } ) ;
2013-12-15 23:51:09 +04:00
}
2014-12-12 16:57:02 +03:00
void exec_user_mappings ( Context & context , NormalParams params )
{
on_next_key_with_autoinfo ( context , KeymapMode : : None ,
[ params ] ( Key key , Context & context ) mutable {
if ( not context . keymaps ( ) . is_mapped ( key , KeymapMode : : User ) )
return ;
auto mapping = context . keymaps ( ) . get_mapping ( key , KeymapMode : : User ) ;
ScopedEdition edition ( context ) ;
exec_keys ( mapping , context ) ;
} , " user mapping " , " enter user key " ) ;
}
2013-04-12 16:28:13 +04:00
template < typename T >
class Repeated
{
public :
constexpr Repeated ( T t ) : m_func ( t ) { }
2014-11-28 16:58:36 +03:00
void operator ( ) ( Context & context , NormalParams params )
2013-04-12 16:28:13 +04:00
{
2013-12-15 22:07:51 +04:00
ScopedEdition edition ( context ) ;
2014-11-28 16:58:36 +03:00
do { m_func ( context , { 0 , params . reg } ) ; } while ( - - params . count > 0 ) ;
2013-04-12 16:28:13 +04:00
}
private :
T m_func ;
} ;
template < typename T >
constexpr Repeated < T > repeated ( T func ) { return Repeated < T > ( func ) ; }
2013-07-02 16:55:34 +04:00
template < typename Type , Direction direction , SelectMode mode = SelectMode : : Replace >
2014-11-28 16:58:36 +03:00
void move ( Context & context , NormalParams params )
2013-07-02 16:55:34 +04:00
{
2013-12-15 18:14:52 +04:00
kak_assert ( mode = = SelectMode : : Replace or mode = = SelectMode : : Extend ) ;
2014-11-28 16:58:36 +03:00
Type offset ( std : : max ( params . count , 1 ) ) ;
2013-12-15 18:14:52 +04:00
if ( direction = = Backward )
offset = - offset ;
2013-12-15 18:25:23 +04:00
auto & selections = context . selections ( ) ;
2013-12-15 18:14:52 +04:00
for ( auto & sel : selections )
{
2014-01-28 23:05:49 +04:00
auto cursor = context . has_window ( ) ? context . window ( ) . offset_coord ( sel . cursor ( ) , offset )
: context . buffer ( ) . offset_coord ( sel . cursor ( ) , offset ) ;
2013-12-15 18:14:52 +04:00
2014-01-28 23:05:49 +04:00
sel . anchor ( ) = mode = = SelectMode : : Extend ? sel . anchor ( ) : cursor ;
2014-05-14 23:56:27 +04:00
sel . cursor ( ) = cursor ;
2013-12-15 18:14:52 +04:00
}
2014-05-14 23:56:27 +04:00
selections . avoid_eol ( ) ;
2013-12-15 18:14:52 +04:00
selections . sort_and_merge_overlapping ( ) ;
2013-07-02 16:55:34 +04:00
}
2013-04-12 16:28:13 +04:00
KeyMap keymap =
{
2014-07-08 23:25:09 +04:00
{ ' h ' , { " move left " , move < CharCount , Backward > } } ,
{ ' j ' , { " move down " , move < LineCount , Forward > } } ,
{ ' k ' , { " move up " , move < LineCount , Backward > } } ,
{ ' l ' , { " move right " , move < CharCount , Forward > } } ,
{ ' H ' , { " extend left " , move < CharCount , Backward , SelectMode : : Extend > } } ,
{ ' J ' , { " extend down " , move < LineCount , Forward , SelectMode : : Extend > } } ,
{ ' K ' , { " extend up " , move < LineCount , Backward , SelectMode : : Extend > } } ,
{ ' L ' , { " extend right " , move < CharCount , Forward , SelectMode : : Extend > } } ,
{ ' t ' , { " select to next character " , select_to_next_char < SelectFlags : : None > } } ,
{ ' f ' , { " select to next character included " , select_to_next_char < SelectFlags : : Inclusive > } } ,
{ ' T ' , { " extend to next character " , select_to_next_char < SelectFlags : : Extend > } } ,
{ ' F ' , { " extend to next character included " , select_to_next_char < SelectFlags : : Inclusive | SelectFlags : : Extend > } } ,
{ alt ( ' t ' ) , { " select to previous character " , select_to_next_char < SelectFlags : : Reverse > } } ,
{ alt ( ' f ' ) , { " select to previous character included " , select_to_next_char < SelectFlags : : Inclusive | SelectFlags : : Reverse > } } ,
{ alt ( ' T ' ) , { " extend to previous character " , select_to_next_char < SelectFlags : : Extend | SelectFlags : : Reverse > } } ,
{ alt ( ' F ' ) , { " extend to previous character included " , select_to_next_char < SelectFlags : : Inclusive | SelectFlags : : Extend | SelectFlags : : Reverse > } } ,
{ ' d ' , { " erase selected text " , erase_selections } } ,
{ ' c ' , { " change selected text " , change } } ,
{ ' i ' , { " insert before selected text " , enter_insert_mode < InsertMode : : Insert > } } ,
{ ' I ' , { " insert at line begin " , enter_insert_mode < InsertMode : : InsertAtLineBegin > } } ,
{ ' a ' , { " insert after selected text " , enter_insert_mode < InsertMode : : Append > } } ,
{ ' A ' , { " insert at line end " , enter_insert_mode < InsertMode : : AppendAtLineEnd > } } ,
{ ' o ' , { " insert on new line below " , enter_insert_mode < InsertMode : : OpenLineBelow > } } ,
{ ' O ' , { " insert on new line above " , enter_insert_mode < InsertMode : : OpenLineAbove > } } ,
{ ' r ' , { " replace with character " , replace_with_char } } ,
{ ' g ' , { " go to location " , goto_commands < SelectMode : : Replace > } } ,
{ ' G ' , { " extend to location " , goto_commands < SelectMode : : Extend > } } ,
{ ' v ' , { " move view " , view_commands } } ,
{ ' y ' , { " yank selected text " , yank } } ,
{ ' p ' , { " paste after selected text " , repeated ( paste < InsertMode : : Append > ) } } ,
{ ' P ' , { " paste before selected text " , repeated ( paste < InsertMode : : Insert > ) } } ,
{ alt ( ' p ' ) , { " paste every yanked selection after selected text " , paste_all < InsertMode : : Append > } } ,
{ alt ( ' P ' ) , { " paste every yanked selection before selected text " , paste_all < InsertMode : : Insert > } } ,
{ ' R ' , { " replace selected text with yanked text " , paste < InsertMode : : Replace > } } ,
{ ' s ' , { " select regex matches in selected text " , select_regex } } ,
{ ' S ' , { " split selected text on regex matches " , split_regex } } ,
{ alt ( ' s ' ) , { " split selected text on line ends " , split_lines } } ,
{ ' . ' , { " repeat last insert command " , repeat_last_insert } } ,
2014-11-28 16:58:36 +03:00
{ ' % ' , { " select whole buffer " , [ ] ( Context & context , NormalParams ) { select_buffer ( context . selections ( ) ) ; } } } ,
2014-07-08 23:25:09 +04:00
{ ' : ' , { " enter command prompt " , command } } ,
2014-12-12 02:21:11 +03:00
{ ' | ' , { " pipe each selection through filter and replace with output " , pipe < true > } } ,
{ alt ( ' | ' ) , { " pipe each selection through command and ignore output " , pipe < false > } } ,
{ ' ! ' , { " insert command output " , insert_output < InsertMode : : Insert > } } ,
{ alt ( ' ! ' ) , { " append command output " , insert_output < InsertMode : : Append > } } ,
2014-11-28 16:58:36 +03:00
{ ' ' , { " remove all selection except main " , [ ] ( Context & context , NormalParams p ) { keep_selection ( context . selections ( ) , p . count ? p . count - 1 : context . selections ( ) . main_index ( ) ) ; } } } ,
{ alt ( ' ' ) , { " remove main selection " , [ ] ( Context & context , NormalParams p ) { remove_selection ( context . selections ( ) , p . count ? p . count - 1 : context . selections ( ) . main_index ( ) ) ; } } } ,
{ ' ; ' , { " reduce selections to their cursor " , [ ] ( Context & context , NormalParams ) { clear_selections ( context . selections ( ) ) ; } } } ,
{ alt ( ' ; ' ) , { " swap selections cursor and anchor " , [ ] ( Context & context , NormalParams ) { flip_selections ( context . selections ( ) ) ; } } } ,
2014-07-08 23:25:09 +04:00
{ ' w ' , { " select to next word start " , repeated ( make_select < SelectMode : : Replace > ( select_to_next_word < Word > ) ) } } ,
{ ' e ' , { " select to next word end " , repeated ( make_select < SelectMode : : Replace > ( select_to_next_word_end < Word > ) ) } } ,
{ ' b ' , { " select to prevous word start " , repeated ( make_select < SelectMode : : Replace > ( select_to_previous_word < Word > ) ) } } ,
{ ' W ' , { " extend to next word start " , repeated ( make_select < SelectMode : : Extend > ( select_to_next_word < Word > ) ) } } ,
{ ' E ' , { " extend to next word end " , repeated ( make_select < SelectMode : : Extend > ( select_to_next_word_end < Word > ) ) } } ,
{ ' B ' , { " extend to prevous word start " , repeated ( make_select < SelectMode : : Extend > ( select_to_previous_word < Word > ) ) } } ,
{ alt ( ' w ' ) , { " select to next WORD start " , repeated ( make_select < SelectMode : : Replace > ( select_to_next_word < WORD > ) ) } } ,
{ alt ( ' e ' ) , { " select to next WORD end " , repeated ( make_select < SelectMode : : Replace > ( select_to_next_word_end < WORD > ) ) } } ,
{ alt ( ' b ' ) , { " select to prevous WORD start " , repeated ( make_select < SelectMode : : Replace > ( select_to_previous_word < WORD > ) ) } } ,
{ alt ( ' W ' ) , { " extend to next WORD start " , repeated ( make_select < SelectMode : : Extend > ( select_to_next_word < WORD > ) ) } } ,
{ alt ( ' E ' ) , { " extend to next WORD end " , repeated ( make_select < SelectMode : : Extend > ( select_to_next_word_end < WORD > ) ) } } ,
{ alt ( ' B ' ) , { " extend to prevous WORD start " , repeated ( make_select < SelectMode : : Extend > ( select_to_previous_word < WORD > ) ) } } ,
{ alt ( ' l ' ) , { " select to line end " , repeated ( make_select < SelectMode : : Replace > ( select_to_eol ) ) } } ,
{ alt ( ' L ' ) , { " extend to line end " , repeated ( make_select < SelectMode : : Extend > ( select_to_eol ) ) } } ,
{ alt ( ' h ' ) , { " select to line begin " , repeated ( make_select < SelectMode : : Replace > ( select_to_eol_reverse ) ) } } ,
{ alt ( ' H ' ) , { " extend to line begin " , repeated ( make_select < SelectMode : : Extend > ( select_to_eol_reverse ) ) } } ,
{ ' x ' , { " select line " , repeated ( make_select < SelectMode : : Replace > ( select_line ) ) } } ,
{ ' X ' , { " extend line " , repeated ( make_select < SelectMode : : Extend > ( select_line ) ) } } ,
{ alt ( ' x ' ) , { " extend selections to whole lines " , make_select < SelectMode : : Replace > ( select_lines ) } } ,
{ alt ( ' X ' ) , { " crop selections to whole lines " , make_select < SelectMode : : Replace > ( trim_partial_lines ) } } ,
{ ' m ' , { " select to matching character " , make_select < SelectMode : : Replace > ( select_matching ) } } ,
{ ' M ' , { " extend to matching character " , make_select < SelectMode : : Extend > ( select_matching ) } } ,
{ ' / ' , { " select next given regex match " , search < SelectMode : : Replace , Forward > } } ,
{ ' ? ' , { " extend with next given regex match " , search < SelectMode : : Extend , Forward > } } ,
{ alt ( ' / ' ) , { " select previous given regex match " , search < SelectMode : : Replace , Backward > } } ,
{ alt ( ' ? ' ) , { " extend with previous given regex match " , search < SelectMode : : Extend , Backward > } } ,
{ ' n ' , { " select next current search pattern match " , search_next < SelectMode : : Replace , Forward > } } ,
{ ' N ' , { " extend with next current search pattern match " , search_next < SelectMode : : Append , Forward > } } ,
{ alt ( ' n ' ) , { " select previous current search pattern match " , search_next < SelectMode : : Replace , Backward > } } ,
{ alt ( ' N ' ) , { " extend with previous current search pattern match " , search_next < SelectMode : : Append , Backward > } } ,
{ ' * ' , { " set search pattern to main selection content " , use_selection_as_search_pattern < true > } } ,
{ alt ( ' * ' ) , { " set search pattern to main selection content, do not detect words " , use_selection_as_search_pattern < false > } } ,
{ ' u ' , { " undo " , undo } } ,
{ ' U ' , { " redo " , redo } } ,
{ alt ( ' i ' ) , { " select inner object " , select_object < ObjectFlags : : ToBegin | ObjectFlags : : ToEnd | ObjectFlags : : Inner > } } ,
{ alt ( ' a ' ) , { " select whole object " , select_object < ObjectFlags : : ToBegin | ObjectFlags : : ToEnd > } } ,
{ ' [ ' , { " select to object start " , select_object < ObjectFlags : : ToBegin > } } ,
2014-10-16 22:43:09 +04:00
{ ' ] ' , { " select to object end " , select_object < ObjectFlags : : ToEnd > } } ,
2014-07-08 23:25:09 +04:00
{ ' { ' , { " extend to object start " , select_object < ObjectFlags : : ToBegin , SelectMode : : Extend > } } ,
2014-10-16 22:43:09 +04:00
{ ' } ' , { " extend to object end " , select_object < ObjectFlags : : ToEnd , SelectMode : : Extend > } } ,
2014-09-23 16:37:56 +04:00
{ alt ( ' [ ' ) , { " select to inner object start " , select_object < ObjectFlags : : ToBegin | ObjectFlags : : Inner > } } ,
2014-10-16 22:43:09 +04:00
{ alt ( ' ] ' ) , { " select to inner object end " , select_object < ObjectFlags : : ToEnd | ObjectFlags : : Inner > } } ,
2014-09-23 16:37:56 +04:00
{ alt ( ' { ' ) , { " extend to inner object start " , select_object < ObjectFlags : : ToBegin | ObjectFlags : : Inner , SelectMode : : Extend > } } ,
2014-10-16 22:43:09 +04:00
{ alt ( ' } ' ) , { " extend to inner object end " , select_object < ObjectFlags : : ToEnd | ObjectFlags : : Inner , SelectMode : : Extend > } } ,
2014-07-08 23:25:09 +04:00
2014-12-28 14:16:51 +03:00
{ alt ( ' j ' ) , { " join lines " , join_lines } } ,
{ alt ( ' J ' ) , { " join lines and select spaces " , join_lines_select_spaces } } ,
2014-07-08 23:25:09 +04:00
{ alt ( ' k ' ) , { " keep selections matching given regex " , keep < true > } } ,
{ alt ( ' K ' ) , { " keep selections not matching given regex " , keep < false > } } ,
{ ' $ ' , { " pipe each selection through shell command and keep the ones whose command succeed " , keep_pipe } } ,
{ ' < ' , { " deindent " , deindent < true > } } ,
{ ' > ' , { " indent " , indent < false > } } ,
{ alt ( ' > ' ) , { " indent, including empty lines " , indent < true > } } ,
{ alt ( ' < ' ) , { " deindent, not including incomplete indent " , deindent < false > } } ,
{ ctrl ( ' i ' ) , { " jump forward in jump list " , jump < Forward > } } ,
{ ctrl ( ' o ' ) , { " jump backward in jump list " , jump < Backward > } } ,
{ ctrl ( ' s ' ) , { " push current selections in jump list " , save_selections } } ,
{ alt ( ' r ' ) , { " rotate main selection " , rotate_selections } } ,
{ alt ( ' R ' ) , { " rotate selections content " , rotate_selections_content } } ,
{ ' q ' , { " replay recorded macro " , replay_macro } } ,
{ ' Q ' , { " start or end macro recording " , start_or_end_macro_recording } } ,
2014-07-30 04:08:55 +04:00
{ Key : : Escape , { " end macro recording " , end_macro_recording } } ,
2014-07-08 23:25:09 +04:00
{ ' ` ' , { " convert to lower case in selections " , for_each_char < to_lower > } } ,
{ ' ~ ' , { " convert to upper case in selections " , for_each_char < to_upper > } } ,
{ alt ( ' ` ' ) , { " swap case in selections " , for_each_char < swap_case > } } ,
{ ' & ' , { " align selection cursors " , align } } ,
{ alt ( ' & ' ) , { " copy indentation " , copy_indent } } ,
{ ' @ ' , { " convert tabs to spaces in selections " , tabs_to_spaces } } ,
{ alt ( ' @ ' ) , { " convert spaces to tabs in selections " , spaces_to_tabs } } ,
2014-12-12 16:57:02 +03:00
{ ' , ' , { " user mappings " , exec_user_mappings } } ,
2014-07-08 23:25:09 +04:00
{ Key : : Left , { " move left " , move < CharCount , Backward > } } ,
{ Key : : Down , { " move down " , move < LineCount , Forward > } } ,
{ Key : : Up , { " move up " , move < LineCount , Backward > } } ,
{ Key : : Right , { " move right " , move < CharCount , Forward > } } ,
2015-01-28 02:33:22 +03:00
{ ctrl ( ' b ' ) , { " scroll one page up " , scroll < Key : : PageUp > } } ,
{ ctrl ( ' f ' ) , { " scroll one page down " , scroll < Key : : PageDown > } } ,
2014-07-08 23:25:09 +04:00
{ Key : : PageUp , { " scroll one page up " , scroll < Key : : PageUp > } } ,
{ Key : : PageDown , { " scroll one page down " , scroll < Key : : PageDown > } } ,
2013-04-12 16:28:13 +04:00
} ;
}