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