2011-09-02 18:51:20 +02:00
# include "window.hh"
# include "buffer.hh"
2012-05-03 09:25:13 +02:00
# include "shell_manager.hh"
2012-05-07 05:13:34 +02:00
# include "commands.hh"
2011-09-07 20:16:56 +02:00
# include "command_manager.hh"
2011-09-08 16:30:36 +02:00
# include "buffer_manager.hh"
2011-09-23 16:31:57 +02:00
# include "register_manager.hh"
2011-09-21 16:37:09 +02:00
# include "selectors.hh"
2011-09-09 21:24:18 +02:00
# include "assert.hh"
2011-10-07 16:16:38 +02:00
# include "debug.hh"
2011-11-29 23:37:20 +01:00
# include "highlighters.hh"
2011-12-02 15:28:27 +01:00
# include "filters.hh"
2012-04-03 14:01:01 +02:00
# include "hook_manager.hh"
2012-04-03 15:39:20 +02:00
# include "option_manager.hh"
2012-08-28 22:32:15 +02:00
# include "event_manager.hh"
2012-01-23 14:56:43 +01:00
# include "context.hh"
2012-02-16 15:25:16 +01:00
# include "ncurses.hh"
2012-08-29 21:49:36 +02:00
# include "string.hh"
2012-10-16 17:15:09 +02:00
# include "file.hh"
2012-09-17 19:01:13 +02:00
# include "color_registry.hh"
2012-10-23 22:56:24 +02:00
# include "remote.hh"
2012-10-30 14:00:44 +01:00
# include "client_manager.hh"
2012-12-18 21:41:13 +01:00
# include "parameters_parser.hh"
2011-09-02 18:51:20 +02:00
2012-10-02 10:36:28 +02:00
# if defined(__APPLE__)
# include <mach-o/dyld.h>
# endif
2011-09-02 18:51:20 +02:00
# include <unordered_map>
2013-02-26 14:12:21 +01:00
# include <locale>
2013-03-13 19:59:39 +01:00
# include <signal.h>
2013-02-26 14:12:21 +01:00
2011-09-02 18:51:20 +02:00
using namespace Kakoune ;
2011-09-23 16:27:34 +02:00
using namespace std : : placeholders ;
2011-09-02 18:51:20 +02:00
2012-05-07 05:13:34 +02:00
namespace Kakoune
{
2012-09-26 14:22:24 +02:00
template < InsertMode mode >
2012-08-06 22:02:11 +02:00
void do_insert ( Context & context )
2011-09-20 00:00:29 +02:00
{
2013-01-29 13:49:01 +01:00
context . input_handler ( ) . insert ( mode ) ;
2011-09-02 18:51:20 +02:00
}
2012-08-06 22:02:11 +02:00
void do_repeat_insert ( Context & context )
2011-12-05 15:28:45 +01:00
{
2013-01-29 13:49:01 +01:00
context . input_handler ( ) . repeat_last_insert ( ) ;
2011-12-05 15:28:45 +01:00
}
2012-09-07 14:29:29 +02:00
template < SelectMode mode >
2012-08-06 22:02:11 +02:00
void do_go ( Context & context )
2011-09-22 16:02:07 +02:00
{
2012-08-05 19:39:37 +02:00
int count = context . numeric_param ( ) ;
2011-09-22 16:02:07 +02:00
if ( count ! = 0 )
2011-10-10 16:24:17 +02:00
{
BufferIterator target =
2012-09-05 00:21:42 +02:00
context . editor ( ) . buffer ( ) . iterator_at_line_begin ( count - 1 ) ;
2011-10-10 16:24:17 +02:00
2012-11-12 19:59:25 +01:00
context . push_jump ( ) ;
2012-09-05 00:21:42 +02:00
context . editor ( ) . select ( target ) ;
2012-08-21 20:58:10 +02:00
if ( context . has_window ( ) )
context . window ( ) . center_selection ( ) ;
2011-10-10 16:24:17 +02:00
}
2011-09-22 16:02:07 +02:00
else
2012-10-17 13:14:03 +02:00
context . input_handler ( ) . on_next_key ( [ ] ( const Key & key , Context & context ) {
2012-09-05 00:21:42 +02:00
if ( key . modifiers ! = Key : : Modifiers : : None )
return ;
2012-01-25 00:18:59 +01:00
2012-09-05 00:21:42 +02:00
Editor & editor = context . editor ( ) ;
2012-12-18 19:12:24 +01:00
switch ( tolower ( key . key ) )
2012-09-05 00:21:42 +02:00
{
case ' g ' :
case ' t ' :
2012-11-12 19:59:25 +01:00
context . push_jump ( ) ;
2012-12-18 19:12:24 +01:00
editor . select ( editor . buffer ( ) . begin ( ) , mode ) ;
2012-09-05 00:21:42 +02:00
break ;
case ' l ' :
2012-09-07 14:29:29 +02:00
editor . select ( select_to_eol , mode ) ;
2012-09-05 00:21:42 +02:00
break ;
case ' h ' :
2012-09-07 14:29:29 +02:00
editor . select ( select_to_eol_reverse , mode ) ;
2012-09-05 00:21:42 +02:00
break ;
case ' b ' :
{
2012-11-12 19:59:25 +01:00
context . push_jump ( ) ;
2012-09-05 00:21:42 +02:00
const Buffer & buf = editor . buffer ( ) ;
2012-12-18 19:12:24 +01:00
editor . select ( buf . iterator_at_line_begin ( buf . line_count ( ) - 1 ) , mode ) ;
2012-09-05 00:21:42 +02:00
break ;
}
2013-04-09 14:22:21 +02:00
case ' a ' :
{
auto & buffer_manager = BufferManager : : instance ( ) ;
auto it = buffer_manager . begin ( ) ;
if ( it - > get ( ) = = & context . buffer ( ) and + + it = = buffer_manager . end ( ) )
break ;
context . push_jump ( ) ;
auto & client_manager = ClientManager : : instance ( ) ;
context . change_editor ( client_manager . get_unused_window_for_buffer ( * * it ) ) ;
break ;
}
2013-02-27 19:58:38 +01:00
case ' f ' :
{
2013-03-15 18:20:35 +01:00
String filename = context . editor ( ) . main_selection ( ) . content ( ) ;
2013-02-27 19:58:38 +01:00
static char forbidden [ ] = { ' \' ' , ' \\ ' , ' \0 ' } ;
for ( auto c : forbidden )
if ( contains ( filename , c ) )
return ;
2013-03-13 18:52:55 +01:00
auto paths = context . options ( ) [ " path " ] . get < std : : vector < String > > ( ) ;
2013-02-27 19:58:38 +01:00
const String & buffer_name = context . buffer ( ) . name ( ) ;
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 ) ;
if ( not path . empty ( ) )
CommandManager : : instance ( ) . execute ( " edit ' " + path + " ' " , context ) ;
break ;
}
2012-09-05 00:21:42 +02:00
}
} ) ;
2011-09-22 16:02:07 +02:00
}
2012-09-11 14:01:41 +02:00
void do_replace_with_char ( Context & context )
{
2012-10-17 13:14:03 +02:00
context . input_handler ( ) . on_next_key ( [ ] ( const Key & key , Context & context ) {
2012-12-12 13:52:34 +01:00
Editor & editor = context . editor ( ) ;
SelectionList sels = editor . selections ( ) ;
auto restore_sels = on_scope_end ( [ & ] { editor . select ( std : : move ( sels ) ) ; } ) ;
2013-04-05 19:28:08 +02:00
editor . multi_select ( std : : bind ( select_all_matches , _1 , Regex { " . " } ) ) ;
2013-01-17 18:47:53 +01:00
editor . insert ( codepoint_to_str ( key . key ) , InsertMode : : Replace ) ;
2012-09-11 14:01:41 +02:00
} ) ;
}
2013-03-29 14:24:04 +01:00
Codepoint swap_case ( Codepoint cp )
{
if ( ' A ' < = cp and cp < = ' Z ' )
return cp - ' A ' + ' a ' ;
if ( ' a ' < = cp and cp < = ' z ' )
return cp - ' a ' + ' A ' ;
return cp ;
}
void do_swap_case ( Context & context )
{
Editor & editor = context . editor ( ) ;
std : : vector < String > sels = editor . selections_content ( ) ;
for ( auto & sel : sels )
{
for ( auto & c : sel )
c = swap_case ( c ) ;
}
editor . insert ( sels , InsertMode : : Replace ) ;
}
2012-08-06 22:02:11 +02:00
void do_command ( Context & context )
2011-09-02 18:51:20 +02:00
{
2012-10-17 13:14:03 +02:00
context . input_handler ( ) . prompt (
2013-04-06 13:07:04 +02:00
" : " , get_color ( " Prompt " ) ,
2013-04-04 19:03:59 +02:00
std : : bind ( & CommandManager : : complete , & CommandManager : : instance ( ) , _1 , _2 , _3 ) ,
2012-12-05 19:22:40 +01:00
[ ] ( const String & cmdline , PromptEvent event , Context & context ) {
if ( event = = PromptEvent : : Validate )
CommandManager : : instance ( ) . execute ( cmdline , context ) ;
2013-01-29 13:49:01 +01:00
} ) ;
2011-09-02 18:51:20 +02:00
}
2012-08-06 22:02:11 +02:00
void do_pipe ( Context & context )
2011-12-28 20:04:06 +01:00
{
2013-04-06 13:07:04 +02:00
context . input_handler ( ) . prompt ( " pipe: " , get_color ( " Prompt " ) , complete_nothing ,
2012-12-05 19:22:40 +01:00
[ ] ( const String & cmdline , PromptEvent event , Context & context )
2012-09-03 14:22:02 +02:00
{
2012-12-05 19:22:40 +01:00
if ( event ! = PromptEvent : : Validate )
return ;
2012-09-03 14:22:02 +02:00
Editor & editor = context . editor ( ) ;
std : : vector < String > strings ;
2013-01-04 18:39:13 +01:00
for ( auto & sel : context . editor ( ) . selections ( ) )
strings . push_back ( ShellManager : : instance ( ) . pipe ( { sel . begin ( ) , sel . end ( ) } ,
2013-04-03 18:51:40 +02:00
cmdline , context , { } ,
EnvVarMap { } ) ) ;
2012-10-09 19:25:20 +02:00
editor . insert ( strings , InsertMode : : Replace ) ;
2013-01-29 13:49:01 +01:00
} ) ;
2011-12-28 20:04:06 +01:00
}
2013-01-08 18:46:45 +01:00
template < SelectMode mode , bool forward >
2012-08-06 22:02:11 +02:00
void do_search ( Context & context )
2011-09-02 18:51:20 +02:00
{
2013-04-06 13:07:04 +02:00
const char * prompt = forward ? " search: " : " reverse search: " ;
2012-12-05 19:22:40 +01:00
SelectionList selections = context . editor ( ) . selections ( ) ;
2013-04-06 13:02:16 +02:00
context . input_handler ( ) . prompt ( prompt , get_color ( " Prompt " ) , complete_nothing ,
2012-12-05 19:22:40 +01:00
[ selections ] ( const String & str , PromptEvent event , Context & context ) {
2012-12-11 18:46:20 +01:00
try
2012-12-05 19:22:40 +01:00
{
2012-12-11 18:46:20 +01:00
context . editor ( ) . select ( selections ) ;
2013-02-19 19:05:13 +01:00
if ( event = = PromptEvent : : Abort )
2012-12-11 18:46:20 +01:00
return ;
2013-04-05 19:28:08 +02:00
Regex ex { str } ;
2013-04-06 13:02:16 +02:00
context . input_handler ( ) . set_prompt_colors ( get_color ( " Prompt " ) ) ;
2012-12-11 18:46:20 +01:00
if ( event = = PromptEvent : : Validate )
{
2013-04-05 19:28:08 +02:00
if ( str . empty ( ) )
ex = Regex { RegisterManager : : instance ( ) [ ' / ' ] . values ( context ) [ 0 ] } ;
2012-12-11 18:46:20 +01:00
else
2013-04-05 19:28:08 +02:00
RegisterManager : : instance ( ) [ ' / ' ] = str ;
2012-12-11 18:46:20 +01:00
context . push_jump ( ) ;
}
2013-04-05 19:28:08 +02:00
else if ( str . empty ( ) or not context . options ( ) [ " incsearch " ] . get < bool > ( ) )
2012-12-31 14:12:00 +01:00
return ;
2012-12-11 18:46:20 +01:00
2013-01-08 18:46:45 +01:00
context . editor ( ) . select ( std : : bind ( select_next_match < forward > , _1 , ex ) , mode ) ;
2012-12-05 19:22:40 +01:00
}
2013-04-05 19:28:08 +02:00
catch ( boost : : regex_error & err )
{
if ( event = = PromptEvent : : Validate )
throw runtime_error ( " regex error: " _str + err . what ( ) ) ;
2013-04-06 13:02:16 +02:00
else
context . input_handler ( ) . set_prompt_colors ( get_color ( " Error " ) ) ;
2013-04-05 19:28:08 +02:00
}
2012-12-11 18:46:20 +01:00
catch ( runtime_error & )
{
context . editor ( ) . select ( selections ) ;
// only validation should propagate errors,
// incremental search should not.
if ( event = = PromptEvent : : Validate )
throw ;
}
2013-01-29 13:49:01 +01:00
} ) ;
2011-09-02 18:51:20 +02:00
}
2013-01-08 18:46:45 +01:00
template < SelectMode mode , bool forward >
2012-08-06 22:02:11 +02:00
void do_search_next ( Context & context )
2011-09-24 14:48:58 +02:00
{
2013-04-05 19:28:08 +02:00
const String & str = RegisterManager : : instance ( ) [ ' / ' ] . values ( context ) [ 0 ] ;
if ( not str . empty ( ) )
2012-12-13 13:24:38 +01:00
{
2013-04-05 19:28:08 +02:00
try
{
Regex ex { str } ;
if ( mode = = SelectMode : : Replace )
context . push_jump ( ) ;
int count = context . numeric_param ( ) ;
do {
context . editor ( ) . select ( std : : bind ( select_next_match < forward > , _1 , ex ) , mode ) ;
} while ( - - count > 0 ) ;
}
catch ( boost : : regex_error & err )
{
throw runtime_error ( " regex error: " _str + err . what ( ) ) ;
}
2012-12-13 13:24:38 +01:00
}
2011-09-24 14:48:58 +02:00
else
2013-04-05 19:28:08 +02:00
throw runtime_error ( " no search pattern " ) ;
2011-09-24 14:48:58 +02:00
}
2013-04-02 18:46:33 +02:00
template < bool smart >
2013-02-19 19:04:09 +01:00
void use_selection_as_search_pattern ( Context & context )
{
std : : vector < String > patterns ;
auto & sels = context . editor ( ) . selections ( ) ;
for ( auto & sel : sels )
{
auto begin = sel . begin ( ) ;
auto end = sel . end ( ) ;
auto content = " \\ Q " + context . buffer ( ) . string ( begin , end ) + " \\ E " ;
2013-04-02 18:46:33 +02:00
if ( smart )
{
if ( begin . is_begin ( ) or
( is_word ( utf8 : : codepoint ( begin ) ) and not
is_word ( utf8 : : codepoint ( utf8 : : previous ( begin ) ) ) ) )
content = " \\ b " + content ;
if ( end . is_end ( ) or
( is_word ( utf8 : : codepoint ( utf8 : : previous ( end ) ) ) and not
is_word ( utf8 : : codepoint ( end ) ) ) )
content = content + " \\ b " ;
}
2013-02-19 19:04:09 +01:00
patterns . push_back ( std : : move ( content ) ) ;
}
RegisterManager : : instance ( ) [ ' / ' ] = patterns ;
}
2012-08-06 22:02:11 +02:00
void do_yank ( Context & context )
2011-09-23 16:31:57 +02:00
{
2012-08-05 19:39:37 +02:00
RegisterManager : : instance ( ) [ ' " ' ] = context . editor ( ) . selections_content ( ) ;
2013-04-04 18:50:00 +02:00
context . print_status ( { " yanked " + int_to_str ( context . editor ( ) . selections ( ) . size ( ) ) + " selections " , get_color ( " Information " ) } ) ;
2011-09-23 16:31:57 +02:00
}
2013-04-03 19:14:38 +02:00
void do_cat_yank ( Context & context )
{
auto sels = context . editor ( ) . selections_content ( ) ;
String str ;
for ( auto & sel : sels )
str + = sel ;
RegisterManager : : instance ( ) [ ' " ' ] = memoryview < String > ( str ) ;
2013-04-04 18:50:00 +02:00
context . print_status ( { " concatenated and yanked " +
int_to_str ( sels . size ( ) ) + " selections " , get_color ( " Information " ) } ) ;
2013-04-03 19:14:38 +02:00
}
2012-08-06 22:02:11 +02:00
void do_erase ( Context & context )
2011-09-26 10:59:32 +02:00
{
2012-08-05 19:39:37 +02:00
RegisterManager : : instance ( ) [ ' " ' ] = context . editor ( ) . selections_content ( ) ;
context . editor ( ) . erase ( ) ;
2011-09-26 10:59:32 +02:00
}
2012-08-06 22:02:11 +02:00
void do_change ( Context & context )
2011-09-26 10:59:32 +02:00
{
2012-08-05 19:39:37 +02:00
RegisterManager : : instance ( ) [ ' " ' ] = context . editor ( ) . selections_content ( ) ;
2012-10-09 19:25:20 +02:00
do_insert < InsertMode : : Replace > ( context ) ;
2011-09-26 10:59:32 +02:00
}
2012-10-09 19:37:50 +02:00
static InsertMode adapt_for_linewise ( InsertMode mode )
{
if ( mode = = InsertMode : : Append )
2012-11-26 19:23:50 +01:00
return InsertMode : : InsertAtNextLineBegin ;
2012-10-09 19:37:50 +02:00
if ( mode = = InsertMode : : Insert )
return InsertMode : : InsertAtLineBegin ;
2012-10-29 19:00:28 +01:00
if ( mode = = InsertMode : : Replace )
return InsertMode : : Replace ;
2012-10-09 19:37:50 +02:00
assert ( false ) ;
return InsertMode : : Insert ;
}
2012-10-09 19:25:20 +02:00
template < InsertMode insert_mode >
2012-08-06 22:02:11 +02:00
void do_paste ( Context & context )
2011-09-23 16:31:57 +02:00
{
2012-08-05 19:39:37 +02:00
Editor & editor = context . editor ( ) ;
2012-10-09 19:37:50 +02:00
auto strings = RegisterManager : : instance ( ) [ ' " ' ] . values ( context ) ;
InsertMode mode = insert_mode ;
2013-02-22 18:45:27 +01:00
for ( auto & str : strings )
2012-10-09 19:37:50 +02:00
{
if ( not str . empty ( ) and str . back ( ) = = ' \n ' )
2013-02-22 18:45:27 +01:00
{
2012-10-09 19:37:50 +02:00
mode = adapt_for_linewise ( mode ) ;
2013-02-22 18:45:27 +01:00
break ;
}
2012-10-09 19:37:50 +02:00
}
2013-02-22 18:45:27 +01:00
editor . insert ( strings , mode ) ;
2011-09-23 16:31:57 +02:00
}
2013-04-05 18:29:05 +02:00
template < typename T >
void regex_prompt ( Context & context , const String prompt , T on_validate )
2011-11-16 15:06:01 +01:00
{
2013-04-05 18:29:05 +02:00
context . input_handler ( ) . prompt ( prompt , get_color ( " Prompt " ) , complete_nothing ,
[ = ] ( const String & str , PromptEvent event , Context & context ) {
2013-02-20 14:04:46 +01:00
if ( event = = PromptEvent : : Validate )
2013-04-05 19:28:08 +02:00
{
try
{
on_validate ( Regex { str } , context ) ;
}
catch ( boost : : regex_error & err )
{
throw runtime_error ( " regex error: " _str + err . what ( ) ) ;
}
}
2013-04-04 19:09:34 +02:00
else if ( event = = PromptEvent : : Change )
{
const bool ok = Regex { str , boost : : regex_constants : : no_except } . status ( ) = = 0 ;
context . input_handler ( ) . set_prompt_colors ( get_color ( ok ? " Prompt " : " Error " ) ) ;
}
2013-01-29 13:49:01 +01:00
} ) ;
2011-11-16 15:06:01 +01:00
}
2013-04-05 18:29:05 +02:00
void do_select_regex ( Context & context )
{
2013-04-06 13:07:04 +02:00
regex_prompt ( context , " select: " , [ ] ( Regex ex , Context & context ) {
2013-04-05 18:29:05 +02:00
if ( ex . empty ( ) )
2013-04-05 19:28:08 +02:00
ex = Regex { RegisterManager : : instance ( ) [ ' / ' ] . values ( context ) [ 0 ] } ;
2013-04-05 18:29:05 +02:00
else
2013-04-05 19:28:08 +02:00
RegisterManager : : instance ( ) [ ' / ' ] = String { ex . str ( ) } ;
2013-04-05 18:29:05 +02:00
if ( not ex . empty ( ) )
context . editor ( ) . multi_select ( std : : bind ( select_all_matches , _1 , ex ) ) ;
} ) ;
}
2012-08-06 22:02:11 +02:00
void do_split_regex ( Context & context )
2011-11-21 20:30:44 +01:00
{
2013-04-06 13:07:04 +02:00
regex_prompt ( context , " split: " , [ ] ( Regex ex , Context & context ) {
2013-04-05 18:29:05 +02:00
if ( ex . empty ( ) )
2013-04-05 19:28:08 +02:00
ex = Regex { RegisterManager : : instance ( ) [ ' / ' ] . values ( context ) [ 0 ] } ;
2013-04-05 18:29:05 +02:00
else
2013-04-05 19:28:08 +02:00
RegisterManager : : instance ( ) [ ' / ' ] = String { ex . str ( ) } ;
2013-04-05 18:29:05 +02:00
if ( not ex . empty ( ) )
context . editor ( ) . multi_select ( std : : bind ( split_selection , _1 , ex ) ) ;
} ) ;
2011-11-21 20:30:44 +01:00
}
2013-02-22 18:37:34 +01:00
void do_split_lines ( Context & context )
{
2013-04-05 19:28:08 +02:00
context . editor ( ) . multi_select ( std : : bind ( split_selection , _1 , Regex { " ^ " } ) ) ;
2013-02-22 18:37:34 +01:00
}
2012-08-06 22:02:11 +02:00
void do_join ( Context & context )
2011-11-22 15:24:50 +01:00
{
2012-08-05 19:39:37 +02:00
Editor & editor = context . editor ( ) ;
2012-12-18 19:00:55 +01:00
DynamicSelectionList sels { editor . buffer ( ) , editor . selections ( ) } ;
2012-12-31 13:44:01 +01:00
auto restore_sels = on_scope_end ( [ & ] { editor . select ( ( SelectionList ) std : : move ( sels ) ) ; } ) ;
2012-02-07 15:26:51 +01:00
editor . select ( select_whole_lines ) ;
2012-09-07 14:29:29 +02:00
editor . select ( select_to_eol , SelectMode : : Extend ) ;
2012-12-31 13:44:01 +01:00
editor . multi_select ( [ ] ( const Selection & sel )
{
2013-04-05 19:28:08 +02:00
SelectionList res = select_all_matches ( sel , Regex { " \n \\ h* " } ) ;
2012-12-31 13:44:01 +01:00
// remove last end of line if selected
assert ( std : : is_sorted ( res . begin ( ) , res . end ( ) ,
[ ] ( const Selection & lhs , const Selection & rhs )
{ return lhs . begin ( ) < rhs . begin ( ) ; } ) ) ;
if ( not res . empty ( ) and res . back ( ) . end ( ) = = sel . buffer ( ) . end ( ) )
res . pop_back ( ) ;
return res ;
} ) ;
2012-10-09 19:25:20 +02:00
editor . insert ( " " , InsertMode : : Replace ) ;
2011-11-22 15:24:50 +01:00
}
2013-04-03 19:05:57 +02:00
template < bool matching >
void do_keep ( Context & context )
{
2013-04-06 13:07:04 +02:00
constexpr const char * prompt = matching ? " keep matching: " : " keep not matching: " ;
2013-04-05 19:28:08 +02:00
regex_prompt ( context , prompt , [ ] ( const Regex & ex , Context & context ) {
Editor & editor = context . editor ( ) ;
SelectionList sels = editor . selections ( ) ;
SelectionList keep ;
for ( auto & sel : sels )
2013-04-05 18:29:05 +02:00
{
2013-04-05 19:28:08 +02:00
if ( boost : : regex_search ( sel . begin ( ) , sel . end ( ) , ex ) = = matching )
keep . push_back ( sel ) ;
2013-04-05 18:29:05 +02:00
}
2013-04-05 19:28:08 +02:00
if ( keep . empty ( ) )
throw runtime_error ( " no selections remaining " ) ;
editor . select ( std : : move ( keep ) ) ;
2013-04-05 18:29:05 +02:00
} ) ;
2013-04-03 19:05:57 +02:00
}
2012-11-19 19:22:11 +01:00
void do_indent ( Context & context )
{
2013-03-03 17:25:40 +01:00
size_t width = context . options ( ) [ " indentwidth " ] . get < int > ( ) ;
2012-11-23 18:48:04 +01:00
String indent ( ' ' , width ) ;
2012-11-19 19:22:11 +01:00
Editor & editor = context . editor ( ) ;
2012-12-11 19:51:59 +01:00
DynamicSelectionList sels { editor . buffer ( ) , editor . selections ( ) } ;
auto restore_sels = on_scope_end ( [ & ] { editor . select ( ( SelectionList ) std : : move ( sels ) ) ; } ) ;
2012-11-19 19:22:11 +01:00
editor . select ( select_whole_lines ) ;
2013-04-05 19:28:08 +02:00
editor . multi_select ( std : : bind ( select_all_matches , _1 , Regex { " ^[^ \n ] " } ) ) ;
2012-11-19 19:22:11 +01:00
editor . insert ( indent , InsertMode : : Insert ) ;
}
void do_deindent ( Context & context )
{
2013-03-03 17:25:40 +01:00
int width = context . options ( ) [ " indentwidth " ] . get < int > ( ) ;
2012-11-19 19:22:11 +01:00
Editor & editor = context . editor ( ) ;
2012-12-11 19:51:59 +01:00
DynamicSelectionList sels { editor . buffer ( ) , editor . selections ( ) } ;
auto restore_sels = on_scope_end ( [ & ] { editor . select ( ( SelectionList ) std : : move ( sels ) ) ; } ) ;
2012-11-19 19:22:11 +01:00
editor . select ( select_whole_lines ) ;
editor . multi_select ( std : : bind ( select_all_matches , _1 ,
2013-04-05 19:28:08 +02:00
Regex { " ^ \\ h{1, " + int_to_str ( width ) + " } " } ) ) ;
2012-11-19 19:22:11 +01:00
editor . erase ( ) ;
}
2013-01-07 18:53:27 +01:00
template < SurroundFlags flags >
2012-08-06 22:02:11 +02:00
void do_select_object ( Context & context )
2012-01-04 15:18:08 +01:00
{
2012-10-17 13:14:03 +02:00
context . input_handler ( ) . on_next_key (
2012-09-05 00:21:42 +02:00
[ ] ( const Key & key , Context & context ) {
2012-11-30 18:32:49 +01:00
typedef std : : function < Selection ( const Selection & ) > Selector ;
2012-09-05 00:21:42 +02:00
static const std : : unordered_map < Key , Selector > key_to_selector =
{
2013-01-07 18:53:27 +01:00
{ { Key : : Modifiers : : None , ' ( ' } , std : : bind ( select_surrounding , _1 , CodepointPair { ' ( ' , ' ) ' } , flags ) } ,
{ { Key : : Modifiers : : None , ' ) ' } , std : : bind ( select_surrounding , _1 , CodepointPair { ' ( ' , ' ) ' } , flags ) } ,
{ { Key : : Modifiers : : None , ' b ' } , std : : bind ( select_surrounding , _1 , CodepointPair { ' ( ' , ' ) ' } , flags ) } ,
{ { Key : : Modifiers : : None , ' { ' } , std : : bind ( select_surrounding , _1 , CodepointPair { ' { ' , ' } ' } , flags ) } ,
{ { Key : : Modifiers : : None , ' } ' } , std : : bind ( select_surrounding , _1 , CodepointPair { ' { ' , ' } ' } , flags ) } ,
{ { Key : : Modifiers : : None , ' B ' } , std : : bind ( select_surrounding , _1 , CodepointPair { ' { ' , ' } ' } , flags ) } ,
{ { Key : : Modifiers : : None , ' [ ' } , std : : bind ( select_surrounding , _1 , CodepointPair { ' [ ' , ' ] ' } , flags ) } ,
{ { Key : : Modifiers : : None , ' ] ' } , std : : bind ( select_surrounding , _1 , CodepointPair { ' [ ' , ' ] ' } , flags ) } ,
2013-03-18 22:31:05 +01:00
{ { Key : : Modifiers : : None , ' r ' } , std : : bind ( select_surrounding , _1 , CodepointPair { ' [ ' , ' ] ' } , flags ) } ,
2013-01-07 18:53:27 +01:00
{ { Key : : Modifiers : : None , ' < ' } , std : : bind ( select_surrounding , _1 , CodepointPair { ' < ' , ' > ' } , flags ) } ,
{ { Key : : Modifiers : : None , ' > ' } , std : : bind ( select_surrounding , _1 , CodepointPair { ' < ' , ' > ' } , flags ) } ,
2013-02-27 19:08:13 +01:00
{ { Key : : Modifiers : : None , ' " ' } , std : : bind ( select_surrounding , _1 , CodepointPair { ' " ' , ' " ' } , flags ) } ,
{ { Key : : Modifiers : : None , ' \' ' } , std : : bind ( select_surrounding , _1 , CodepointPair { ' \' ' , ' \' ' } , flags ) } ,
2013-01-07 18:53:27 +01:00
{ { Key : : Modifiers : : None , ' w ' } , std : : bind ( select_whole_word < false > , _1 , flags & SurroundFlags : : Inner ) } ,
{ { Key : : Modifiers : : None , ' W ' } , std : : bind ( select_whole_word < true > , _1 , flags & SurroundFlags : : Inner ) } ,
2012-09-05 00:21:42 +02:00
} ;
auto it = key_to_selector . find ( key ) ;
if ( it ! = key_to_selector . end ( ) )
context . editor ( ) . select ( it - > second ) ;
} ) ;
2012-01-04 15:18:08 +01:00
}
2012-09-07 21:09:23 +02:00
template < Key : : NamedKey key >
void do_scroll ( Context & context )
{
static_assert ( key = = Key : : PageUp or key = = Key : : PageDown ,
" do_scrool only implements PageUp and PageDown " ) ;
Window & window = context . window ( ) ;
Buffer & buffer = context . buffer ( ) ;
2012-10-11 00:41:48 +02:00
DisplayCoord position = window . position ( ) ;
LineCount cursor_line = 0 ;
2012-09-07 21:09:23 +02:00
if ( key = = Key : : PageUp )
{
position . line - = ( window . dimensions ( ) . line - 2 ) ;
2012-10-11 00:41:48 +02:00
cursor_line = position . line ;
2012-09-07 21:09:23 +02:00
}
else if ( key = = Key : : PageDown )
{
position . line + = ( window . dimensions ( ) . line - 2 ) ;
2012-10-11 00:41:48 +02:00
cursor_line = position . line + window . dimensions ( ) . line - 1 ;
2012-09-07 21:09:23 +02:00
}
2012-10-11 00:41:48 +02:00
auto cursor_pos = utf8 : : advance ( buffer . iterator_at_line_begin ( position . line ) ,
buffer . iterator_at_line_end ( position . line ) ,
position . column ) ;
2012-09-07 21:09:23 +02:00
window . select ( cursor_pos ) ;
window . set_position ( position ) ;
}
2013-01-05 18:26:57 +01:00
void do_rotate_selections ( Context & context )
{
int count = context . numeric_param ( ) ;
if ( count = = 0 )
count = 1 ;
2013-03-15 18:48:59 +01:00
context . editor ( ) . rotate_selections ( count ) ;
2013-04-04 19:03:59 +02:00
}
2013-01-05 18:26:57 +01:00
2012-11-20 18:55:17 +01:00
enum class SelectFlags
2012-09-05 00:30:59 +02:00
{
2012-11-20 18:55:17 +01:00
None = 0 ,
Reverse = 1 ,
Inclusive = 2 ,
Extend = 4
} ;
constexpr SelectFlags operator | ( SelectFlags lhs , SelectFlags rhs )
{
return ( SelectFlags ) ( ( int ) lhs | ( int ) rhs ) ;
}
constexpr bool operator & ( SelectFlags lhs , SelectFlags rhs )
{
return ( ( int ) lhs & ( int ) rhs ) ! = 0 ;
2012-09-05 00:30:59 +02:00
}
2012-11-20 18:55:17 +01:00
template < SelectFlags flags >
2012-09-05 00:30:59 +02:00
void select_to_next_char ( Context & context )
{
int param = context . numeric_param ( ) ;
2012-10-17 13:14:03 +02:00
context . input_handler ( ) . on_next_key ( [ param ] ( const Key & key , Context & context ) {
2012-09-05 00:30:59 +02:00
context . editor ( ) . select (
2012-09-07 14:29:29 +02:00
std : : bind ( flags & SelectFlags : : Reverse ? select_to_reverse : select_to ,
_1 , key . key , param , flags & SelectFlags : : Inclusive ) ,
flags & SelectFlags : : Extend ? SelectMode : : Extend : SelectMode : : Replace ) ;
2012-09-05 00:30:59 +02:00
} ) ;
}
2013-02-18 18:58:07 +01:00
void start_or_end_macro_recording ( Context & context )
{
if ( context . input_handler ( ) . is_recording ( ) )
context . input_handler ( ) . stop_recording ( ) ;
else
context . input_handler ( ) . on_next_key ( [ ] ( const Key & key , Context & context ) {
if ( key . modifiers = = Key : : Modifiers : : None )
context . input_handler ( ) . start_recording ( key . key ) ;
} ) ;
}
void replay_macro ( Context & context )
{
2013-02-21 13:35:20 +01:00
static bool running_macro = false ;
if ( running_macro )
throw runtime_error ( " nested macros not supported " ) ;
2013-02-18 18:58:07 +01:00
int count = context . numeric_param ( ) ;
context . input_handler ( ) . on_next_key ( [ count ] ( const Key & key , Context & context ) mutable {
if ( key . modifiers = = Key : : Modifiers : : None )
{
memoryview < String > reg_val = RegisterManager : : instance ( ) [ key . key ] . values ( context ) ;
if ( not reg_val . empty ( ) )
{
2013-02-21 13:35:20 +01:00
running_macro = true ;
auto stop_macro = on_scope_end ( [ & ] { running_macro = false ; } ) ;
2013-03-15 14:16:15 +01:00
auto keys = parse_keys ( reg_val [ 0 ] ) ;
2013-02-18 18:58:07 +01:00
scoped_edition edition ( context . editor ( ) ) ;
2013-03-15 14:16:15 +01:00
do { exec_keys ( keys , context ) ; } while ( - - count > 0 ) ;
2013-02-18 18:58:07 +01:00
}
}
} ) ;
}
2012-11-12 20:41:03 +01:00
enum class JumpDirection { Forward , Backward } ;
template < JumpDirection direction >
void jump ( Context & context )
2012-11-12 19:59:25 +01:00
{
2012-11-12 20:41:03 +01:00
auto jump = ( direction = = JumpDirection : : Forward ) ?
2012-12-11 19:51:59 +01:00
context . jump_forward ( ) : context . jump_backward ( ) ;
2012-11-12 19:59:25 +01:00
2012-12-11 19:51:59 +01:00
Buffer & buffer = const_cast < Buffer & > ( jump . front ( ) . buffer ( ) ) ;
2012-11-12 20:41:03 +01:00
BufferManager : : instance ( ) . set_last_used_buffer ( buffer ) ;
if ( & buffer ! = & context . buffer ( ) )
2012-11-12 19:59:25 +01:00
{
auto & manager = ClientManager : : instance ( ) ;
2012-11-12 20:41:03 +01:00
context . change_editor ( manager . get_unused_window_for_buffer ( buffer ) ) ;
2012-11-12 19:59:25 +01:00
}
2012-11-30 18:32:49 +01:00
context . editor ( ) . select ( SelectionList { jump } ) ;
2012-11-12 19:59:25 +01:00
}
2012-09-10 20:10:18 +02:00
String runtime_directory ( )
{
char buffer [ 2048 ] ;
# if defined(__linux__)
ssize_t res = readlink ( " /proc/self/exe " , buffer , 2048 ) ;
assert ( res ! = - 1 ) ;
buffer [ res ] = ' \0 ' ;
# elif defined(__APPLE__)
uint32_t bufsize = 2048 ;
_NSGetExecutablePath ( buffer , & bufsize ) ;
char * canonical_path = realpath ( buffer , NULL ) ;
strncpy ( buffer , canonical_path , 2048 ) ;
free ( canonical_path ) ;
# else
# error "finding executable path is not implemented on this platform"
# endif
char * ptr = strrchr ( buffer , ' / ' ) ;
if ( not ptr )
2012-11-08 13:38:02 +01:00
throw runtime_error ( " unable to determine runtime directory " ) ;
2012-09-10 20:10:18 +02:00
return String ( buffer , ptr ) ;
}
2013-02-18 14:01:24 +01:00
template < typename T >
class Repeated
{
public :
constexpr Repeated ( T t ) : m_func ( t ) { }
void operator ( ) ( Context & context )
{
2013-03-28 18:39:42 +01:00
scoped_edition edition ( context . editor ( ) ) ;
2013-02-18 14:01:24 +01:00
int count = context . numeric_param ( ) ;
do { m_func ( context ) ; } while ( - - count > 0 ) ;
}
private :
T m_func ;
} ;
template < typename T >
constexpr Repeated < T > repeated ( T func ) { return Repeated < T > ( func ) ; }
template < SelectMode mode , typename T >
class Select
{
public :
constexpr Select ( T t ) : m_func ( t ) { }
void operator ( ) ( Context & context )
{
context . editor ( ) . select ( m_func , mode ) ;
}
private :
T m_func ;
} ;
template < SelectMode mode , typename T >
constexpr Select < mode , T > select ( T func ) { return Select < mode , T > ( func ) ; }
2012-08-06 22:02:11 +02:00
std : : unordered_map < Key , std : : function < void ( Context & context ) > > keymap =
2011-09-02 18:51:20 +02:00
{
2012-10-02 14:10:00 +02:00
{ { Key : : Modifiers : : None , ' h ' } , [ ] ( Context & context ) { context . editor ( ) . move_selections ( - CharCount ( std : : max ( context . numeric_param ( ) , 1 ) ) ) ; } } ,
{ { Key : : Modifiers : : None , ' j ' } , [ ] ( Context & context ) { context . editor ( ) . move_selections ( LineCount ( std : : max ( context . numeric_param ( ) , 1 ) ) ) ; } } ,
{ { Key : : Modifiers : : None , ' k ' } , [ ] ( Context & context ) { context . editor ( ) . move_selections ( - LineCount ( std : : max ( context . numeric_param ( ) , 1 ) ) ) ; } } ,
{ { Key : : Modifiers : : None , ' l ' } , [ ] ( Context & context ) { context . editor ( ) . move_selections ( CharCount ( std : : max ( context . numeric_param ( ) , 1 ) ) ) ; } } ,
{ { Key : : Modifiers : : None , ' H ' } , [ ] ( Context & context ) { context . editor ( ) . move_selections ( - CharCount ( std : : max ( context . numeric_param ( ) , 1 ) ) , SelectMode : : Extend ) ; } } ,
{ { Key : : Modifiers : : None , ' J ' } , [ ] ( Context & context ) { context . editor ( ) . move_selections ( LineCount ( std : : max ( context . numeric_param ( ) , 1 ) ) , SelectMode : : Extend ) ; } } ,
{ { Key : : Modifiers : : None , ' K ' } , [ ] ( Context & context ) { context . editor ( ) . move_selections ( - LineCount ( std : : max ( context . numeric_param ( ) , 1 ) ) , SelectMode : : Extend ) ; } } ,
{ { Key : : Modifiers : : None , ' L ' } , [ ] ( Context & context ) { context . editor ( ) . move_selections ( CharCount ( std : : max ( context . numeric_param ( ) , 1 ) ) , SelectMode : : Extend ) ; } } ,
2011-10-07 16:03:25 +02:00
2012-09-07 14:29:29 +02:00
{ { Key : : Modifiers : : None , ' t ' } , select_to_next_char < SelectFlags : : None > } ,
{ { Key : : Modifiers : : None , ' f ' } , select_to_next_char < SelectFlags : : Inclusive > } ,
{ { Key : : Modifiers : : None , ' T ' } , select_to_next_char < SelectFlags : : Extend > } ,
{ { Key : : Modifiers : : None , ' F ' } , select_to_next_char < SelectFlags : : Inclusive | SelectFlags : : Extend > } ,
{ { Key : : Modifiers : : Alt , ' t ' } , select_to_next_char < SelectFlags : : Reverse > } ,
{ { Key : : Modifiers : : Alt , ' f ' } , select_to_next_char < SelectFlags : : Inclusive | SelectFlags : : Reverse > } ,
{ { Key : : Modifiers : : Alt , ' T ' } , select_to_next_char < SelectFlags : : Extend | SelectFlags : : Reverse > } ,
{ { Key : : Modifiers : : Alt , ' F ' } , select_to_next_char < SelectFlags : : Inclusive | SelectFlags : : Extend | SelectFlags : : Reverse > } ,
2011-09-22 16:35:28 +02:00
2011-12-21 15:29:28 +01:00
{ { Key : : Modifiers : : None , ' d ' } , do_erase } ,
{ { Key : : Modifiers : : None , ' c ' } , do_change } ,
2012-09-26 14:22:24 +02:00
{ { Key : : Modifiers : : None , ' i ' } , do_insert < InsertMode : : Insert > } ,
{ { Key : : Modifiers : : None , ' I ' } , do_insert < InsertMode : : InsertAtLineBegin > } ,
{ { Key : : Modifiers : : None , ' a ' } , do_insert < InsertMode : : Append > } ,
{ { Key : : Modifiers : : None , ' A ' } , do_insert < InsertMode : : AppendAtLineEnd > } ,
{ { Key : : Modifiers : : None , ' o ' } , do_insert < InsertMode : : OpenLineBelow > } ,
{ { Key : : Modifiers : : None , ' O ' } , do_insert < InsertMode : : OpenLineAbove > } ,
2012-09-11 14:01:41 +02:00
{ { Key : : Modifiers : : None , ' r ' } , do_replace_with_char } ,
2011-09-22 11:24:16 +02:00
2012-09-07 14:29:29 +02:00
{ { Key : : Modifiers : : None , ' g ' } , do_go < SelectMode : : Replace > } ,
{ { Key : : Modifiers : : None , ' G ' } , do_go < SelectMode : : Extend > } ,
2011-09-22 11:24:16 +02:00
2011-12-21 15:29:28 +01:00
{ { Key : : Modifiers : : None , ' y ' } , do_yank } ,
2013-04-03 19:14:38 +02:00
{ { Key : : Modifiers : : None , ' Y ' } , do_cat_yank } ,
2013-02-22 18:45:27 +01:00
{ { Key : : Modifiers : : None , ' p ' } , repeated ( do_paste < InsertMode : : Append > ) } ,
{ { Key : : Modifiers : : None , ' P ' } , repeated ( do_paste < InsertMode : : Insert > ) } ,
2012-10-09 19:25:20 +02:00
{ { Key : : Modifiers : : Alt , ' p ' } , do_paste < InsertMode : : Replace > } ,
2011-09-23 16:31:57 +02:00
2011-12-21 15:29:28 +01:00
{ { Key : : Modifiers : : None , ' s ' } , do_select_regex } ,
2013-02-22 18:37:34 +01:00
{ { Key : : Modifiers : : None , ' S ' } , do_split_regex } ,
{ { Key : : Modifiers : : Alt , ' s ' } , do_split_lines } ,
2011-11-16 15:06:01 +01:00
2011-12-21 15:29:28 +01:00
{ { Key : : Modifiers : : None , ' . ' } , do_repeat_insert } ,
2011-12-05 15:28:45 +01:00
2012-08-06 22:02:11 +02:00
{ { Key : : Modifiers : : None , ' % ' } , [ ] ( Context & context ) { context . editor ( ) . clear_selections ( ) ; context . editor ( ) . select ( select_whole_buffer ) ; } } ,
2011-09-24 15:45:25 +02:00
2012-08-05 19:39:37 +02:00
{ { Key : : Modifiers : : None , ' : ' } , do_command } ,
2011-12-28 20:04:06 +01:00
{ { Key : : Modifiers : : None , ' | ' } , do_pipe } ,
2012-08-06 22:02:11 +02:00
{ { Key : : Modifiers : : None , ' ' } , [ ] ( Context & context ) { int count = context . numeric_param ( ) ;
if ( count = = 0 ) context . editor ( ) . clear_selections ( ) ;
else context . editor ( ) . keep_selection ( count - 1 ) ; } } ,
{ { Key : : Modifiers : : Alt , ' ' } , [ ] ( Context & context ) { int count = context . numeric_param ( ) ;
2012-11-19 19:03:56 +01:00
if ( count = = 0 ) context . editor ( ) . flip_selections ( ) ;
2012-08-06 22:02:11 +02:00
else context . editor ( ) . remove_selection ( count - 1 ) ; } } ,
2013-02-18 14:01:24 +01:00
{ { Key : : Modifiers : : None , ' w ' } , repeated ( select < SelectMode : : Replace > ( select_to_next_word < false > ) ) } ,
{ { Key : : Modifiers : : None , ' e ' } , repeated ( select < SelectMode : : Replace > ( select_to_next_word_end < false > ) ) } ,
{ { Key : : Modifiers : : None , ' b ' } , repeated ( select < SelectMode : : Replace > ( select_to_previous_word < false > ) ) } ,
{ { Key : : Modifiers : : None , ' W ' } , repeated ( select < SelectMode : : Extend > ( select_to_next_word < false > ) ) } ,
{ { Key : : Modifiers : : None , ' E ' } , repeated ( select < SelectMode : : Extend > ( select_to_next_word_end < false > ) ) } ,
{ { Key : : Modifiers : : None , ' B ' } , repeated ( select < SelectMode : : Extend > ( select_to_previous_word < false > ) ) } ,
2013-02-27 21:21:11 +01:00
{ { Key : : Modifiers : : Alt , ' w ' } , repeated ( select < SelectMode : : Replace > ( select_to_next_word < true > ) ) } ,
{ { Key : : Modifiers : : Alt , ' e ' } , repeated ( select < SelectMode : : Replace > ( select_to_next_word_end < true > ) ) } ,
{ { Key : : Modifiers : : Alt , ' b ' } , repeated ( select < SelectMode : : Replace > ( select_to_previous_word < true > ) ) } ,
{ { Key : : Modifiers : : Alt , ' W ' } , repeated ( select < SelectMode : : Extend > ( select_to_next_word < true > ) ) } ,
{ { Key : : Modifiers : : Alt , ' E ' } , repeated ( select < SelectMode : : Extend > ( select_to_next_word_end < true > ) ) } ,
{ { Key : : Modifiers : : Alt , ' B ' } , repeated ( select < SelectMode : : Extend > ( select_to_previous_word < true > ) ) } ,
{ { Key : : Modifiers : : Alt , ' l ' } , repeated ( select < SelectMode : : Replace > ( select_to_eol ) ) } ,
{ { Key : : Modifiers : : Alt , ' L ' } , repeated ( select < SelectMode : : Extend > ( select_to_eol ) ) } ,
{ { Key : : Modifiers : : Alt , ' h ' } , repeated ( select < SelectMode : : Replace > ( select_to_eol_reverse ) ) } ,
{ { Key : : Modifiers : : Alt , ' H ' } , repeated ( select < SelectMode : : Extend > ( select_to_eol_reverse ) ) } ,
2013-02-18 14:01:24 +01:00
{ { Key : : Modifiers : : None , ' x ' } , repeated ( select < SelectMode : : Replace > ( select_line ) ) } ,
{ { Key : : Modifiers : : None , ' X ' } , repeated ( select < SelectMode : : Extend > ( select_line ) ) } ,
2013-02-27 21:21:11 +01:00
{ { Key : : Modifiers : : Alt , ' x ' } , select < SelectMode : : Replace > ( select_whole_lines ) } ,
2013-02-18 14:01:24 +01:00
{ { Key : : Modifiers : : None , ' m ' } , select < SelectMode : : Replace > ( select_matching ) } ,
{ { Key : : Modifiers : : None , ' M ' } , select < SelectMode : : Extend > ( select_matching ) } ,
2012-08-05 19:39:37 +02:00
2013-01-08 18:46:45 +01:00
{ { Key : : Modifiers : : None , ' / ' } , do_search < SelectMode : : Replace , true > } ,
{ { Key : : Modifiers : : None , ' ? ' } , do_search < SelectMode : : Extend , true > } ,
2013-02-27 21:21:11 +01:00
{ { Key : : Modifiers : : Alt , ' / ' } , do_search < SelectMode : : Replace , false > } ,
{ { Key : : Modifiers : : Alt , ' ? ' } , do_search < SelectMode : : Extend , false > } ,
2013-01-08 18:46:45 +01:00
{ { Key : : Modifiers : : None , ' n ' } , do_search_next < SelectMode : : Replace , true > } ,
2013-03-15 18:20:35 +01:00
{ { Key : : Modifiers : : Alt , ' n ' } , do_search_next < SelectMode : : ReplaceMain , true > } ,
2013-01-08 18:46:45 +01:00
{ { Key : : Modifiers : : None , ' N ' } , do_search_next < SelectMode : : Append , true > } ,
2013-04-02 18:46:33 +02:00
{ { Key : : Modifiers : : None , ' * ' } , use_selection_as_search_pattern < true > } ,
{ { Key : : Modifiers : : Alt , ' * ' } , use_selection_as_search_pattern < false > } ,
2012-08-05 19:39:37 +02:00
2013-04-04 18:50:00 +02:00
{ { Key : : Modifiers : : None , ' u ' } , repeated ( [ ] ( Context & context ) { if ( not context . editor ( ) . undo ( ) ) { context . print_status ( { " nothing left to undo " , get_color ( " Information " ) } ) ; } } ) } ,
{ { Key : : Modifiers : : None , ' U ' } , repeated ( [ ] ( Context & context ) { if ( not context . editor ( ) . redo ( ) ) { context . print_status ( { " nothing left to redo " , get_color ( " Information " ) } ) ; } } ) } ,
2011-12-21 15:29:28 +01:00
2013-01-07 18:53:27 +01:00
{ { Key : : Modifiers : : Alt , ' i ' } , do_select_object < SurroundFlags : : ToBegin | SurroundFlags : : ToEnd | SurroundFlags : : Inner > } ,
{ { Key : : Modifiers : : Alt , ' a ' } , do_select_object < SurroundFlags : : ToBegin | SurroundFlags : : ToEnd > } ,
2013-02-18 14:01:24 +01:00
{ { Key : : Modifiers : : None , ' ] ' } , do_select_object < SurroundFlags : : ToEnd > } ,
{ { Key : : Modifiers : : None , ' [ ' } , do_select_object < SurroundFlags : : ToBegin > } ,
2012-01-04 15:18:08 +01:00
2013-02-27 21:21:11 +01:00
{ { Key : : Modifiers : : Alt , ' j ' } , do_join } ,
2011-12-21 15:29:28 +01:00
2013-04-03 19:05:57 +02:00
{ { Key : : Modifiers : : Alt , ' k ' } , do_keep < true > } ,
{ { Key : : Modifiers : : Alt , ' K ' } , do_keep < false > } ,
2012-11-19 19:22:11 +01:00
{ { Key : : Modifiers : : None , ' < ' } , do_deindent } ,
{ { Key : : Modifiers : : None , ' > ' } , do_indent } ,
2013-02-27 21:21:11 +01:00
{ { Key : : Modifiers : : Alt , ' c ' } , [ ] ( Context & context ) { if ( context . has_window ( ) ) context . window ( ) . center_selection ( ) ; } } ,
2012-09-07 21:09:23 +02:00
{ { Key : : Modifiers : : None , Key : : PageUp } , do_scroll < Key : : PageUp > } ,
{ { Key : : Modifiers : : None , Key : : PageDown } , do_scroll < Key : : PageDown > } ,
2012-11-12 19:59:25 +01:00
2012-11-12 20:41:03 +01:00
{ { Key : : Modifiers : : Control , ' i ' } , jump < JumpDirection : : Forward > } ,
{ { Key : : Modifiers : : Control , ' o ' } , jump < JumpDirection : : Backward > } ,
2013-01-05 18:26:57 +01:00
2013-02-27 21:21:11 +01:00
{ { Key : : Modifiers : : Alt , ' r ' } , do_rotate_selections } ,
2013-02-18 18:58:07 +01:00
{ { Key : : Modifiers : : None , ' q ' } , start_or_end_macro_recording } ,
2013-02-27 21:13:06 +01:00
{ { Key : : Modifiers : : None , ' Q ' } , replay_macro } ,
2013-03-29 14:24:04 +01:00
{ { Key : : Modifiers : : None , ' ~ ' } , do_swap_case } ,
2011-10-25 16:28:20 +02:00
} ;
2012-05-03 09:24:27 +02:00
}
2012-03-21 20:27:36 +01:00
void run_unit_tests ( ) ;
2012-10-23 22:56:24 +02:00
void register_env_vars ( )
{
ShellManager & shell_manager = ShellManager : : instance ( ) ;
shell_manager . register_env_var ( " bufname " ,
[ ] ( const String & name , const Context & context )
2013-03-26 19:28:44 +01:00
{ return context . buffer ( ) . display_name ( ) ; } ) ;
2013-03-09 14:20:31 +01:00
shell_manager . register_env_var ( " timestamp " ,
[ ] ( const String & name , const Context & context )
{ return int_to_str ( context . buffer ( ) . timestamp ( ) ) ; } ) ;
2012-10-23 22:56:24 +02:00
shell_manager . register_env_var ( " selection " ,
[ ] ( const String & name , const Context & context )
2013-03-21 19:10:18 +01:00
{ return context . editor ( ) . main_selection ( ) . content ( ) ; } ) ;
shell_manager . register_env_var ( " selections " ,
[ ] ( const String & name , const Context & context )
{ auto sels = context . editor ( ) . selections_content ( ) ;
return std : : accumulate ( sels . begin ( ) , sels . end ( ) , " " _str ,
[ ] ( const String & lhs , const String & rhs ) { return lhs . empty ( ) ? rhs : lhs + " , " + rhs ; } ) ; } ) ;
2012-10-23 22:56:24 +02:00
shell_manager . register_env_var ( " runtime " ,
[ ] ( const String & name , const Context & context )
{ return runtime_directory ( ) ; } ) ;
shell_manager . register_env_var ( " opt_.+ " ,
[ ] ( const String & name , const Context & context )
2013-03-03 17:25:40 +01:00
{ return context . options ( ) [ name . substr ( 4 _byte ) ] . get_as_string ( ) ; } ) ;
2012-10-23 22:56:24 +02:00
shell_manager . register_env_var ( " reg_.+ " ,
[ ] ( const String & name , const Context & context )
{ return RegisterManager : : instance ( ) [ name [ 4 ] ] . values ( context ) [ 0 ] ; } ) ;
2012-11-06 14:25:25 +01:00
shell_manager . register_env_var ( " socket " ,
[ ] ( const String & name , const Context & context )
{ return Server : : instance ( ) . filename ( ) ; } ) ;
2013-01-07 13:59:09 +01:00
shell_manager . register_env_var ( " client " ,
[ ] ( const String & name , const Context & context )
{ return ClientManager : : instance ( ) . get_client_name ( context ) ; } ) ;
2013-01-21 13:58:54 +01:00
shell_manager . register_env_var ( " cursor_line " ,
[ ] ( const String & name , const Context & context )
2013-03-15 18:20:35 +01:00
{ return int_to_str ( ( int ) context . editor ( ) . main_selection ( ) . last ( ) . line ( ) + 1 ) ; } ) ;
2013-01-21 13:58:54 +01:00
shell_manager . register_env_var ( " cursor_column " ,
[ ] ( const String & name , const Context & context )
2013-03-15 18:20:35 +01:00
{ return int_to_str ( ( int ) context . editor ( ) . main_selection ( ) . last ( ) . column ( ) + 1 ) ; } ) ;
2013-04-03 19:05:57 +02:00
shell_manager . register_env_var ( " selection_desc " ,
[ ] ( const String & name , const Context & context )
{ auto & sel = context . editor ( ) . main_selection ( ) ;
auto beg = sel . begin ( ) ;
return int_to_str ( ( int ) beg . line ( ) + 1 ) + ' : ' + int_to_str ( ( int ) beg . column ( ) + 1 ) + ' + ' + int_to_str ( ( int ) ( sel . end ( ) - beg ) ) ; } ) ;
2012-10-23 22:56:24 +02:00
}
void register_registers ( )
{
RegisterManager & register_manager = RegisterManager : : instance ( ) ;
2013-03-26 13:40:04 +01:00
register_manager . register_dynamic_register ( ' % ' , [ ] ( const Context & context ) { return std : : vector < String > ( 1 , context . buffer ( ) . display_name ( ) ) ; } ) ;
2012-10-23 22:56:24 +02:00
register_manager . register_dynamic_register ( ' . ' , [ ] ( const Context & context ) { return context . editor ( ) . selections_content ( ) ; } ) ;
for ( size_t i = 0 ; i < 10 ; + + i )
{
2013-01-04 18:39:13 +01:00
register_manager . register_dynamic_register ( ' 0 ' + i ,
[ i ] ( const Context & context ) {
std : : vector < String > result ;
for ( auto & sel : context . editor ( ) . selections ( ) )
result . emplace_back ( i < sel . captures ( ) . size ( ) ? sel . captures ( ) [ i ] : " " ) ;
return result ;
} ) ;
2012-10-23 22:56:24 +02:00
}
}
2012-12-28 13:51:14 +01:00
void create_local_client ( const String & init_command )
2012-10-17 17:49:34 +02:00
{
2012-11-29 18:56:08 +01:00
class LocalNCursesUI : public NCursesUI
{
~ LocalNCursesUI ( )
{
if ( not ClientManager : : instance ( ) . empty ( ) and fork ( ) )
{
2013-04-03 18:51:40 +02:00
this - > NCursesUI : : ~ NCursesUI ( ) ;
2012-11-29 18:56:08 +01:00
puts ( " detached from terminal \n " ) ;
exit ( 0 ) ;
}
}
} ;
UserInterface * ui = new LocalNCursesUI { } ;
2012-10-31 14:23:44 +01:00
ClientManager : : instance ( ) . create_client (
2013-01-11 19:17:21 +01:00
std : : unique_ptr < UserInterface > { ui } , init_command ) ;
2012-10-17 17:49:34 +02:00
}
2013-02-26 14:13:37 +01:00
void signal_handler ( int signal )
2013-01-23 13:46:18 +01:00
{
endwin ( ) ;
2013-02-26 14:13:37 +01:00
const char * text = nullptr ;
switch ( signal )
{
case SIGSEGV : text = " SIGSEGV " ; break ;
case SIGFPE : text = " SIGFPE " ; break ;
case SIGQUIT : text = " SIGQUIT " ; break ;
case SIGTERM : text = " SIGTERM " ; break ;
}
on_assert_failed ( text ) ;
2013-01-23 13:46:18 +01:00
abort ( ) ;
}
2012-10-21 13:02:24 +02:00
int main ( int argc , char * argv [ ] )
{
2011-09-02 18:51:20 +02:00
try
{
2013-02-27 19:01:07 +01:00
std : : locale : : global ( std : : locale ( " " ) ) ;
2013-02-26 14:13:37 +01:00
signal ( SIGSEGV , signal_handler ) ;
signal ( SIGFPE , signal_handler ) ;
signal ( SIGQUIT , signal_handler ) ;
signal ( SIGTERM , signal_handler ) ;
2012-12-18 21:41:13 +01:00
std : : vector < String > params ;
for ( size_t i = 1 ; i < argc ; + + i )
params . push_back ( argv [ i ] ) ;
2012-12-19 18:57:10 +01:00
ParametersParser parser ( params , { { " c " , true } , { " e " , true } , { " n " , false } } ) ;
2012-12-18 21:41:13 +01:00
EventManager event_manager ;
2012-10-23 22:56:24 +02:00
2012-12-18 21:41:13 +01:00
String init_command ;
if ( parser . has_option ( " e " ) )
init_command = parser . option_value ( " e " ) ;
if ( parser . has_option ( " c " ) )
2012-10-23 22:56:24 +02:00
{
2012-10-30 14:00:44 +01:00
try
{
2013-03-13 19:59:39 +01:00
auto client = connect_to ( parser . option_value ( " c " ) ,
std : : unique_ptr < UserInterface > { new NCursesUI { } } ,
init_command ) ;
2012-10-30 14:00:44 +01:00
while ( true )
event_manager . handle_next_events ( ) ;
}
catch ( peer_disconnected & )
{
puts ( " disconnected " ) ;
}
2012-10-23 22:56:24 +02:00
return 0 ;
}
2012-11-22 13:50:29 +01:00
GlobalOptions global_options ;
GlobalHooks global_hooks ;
2012-10-23 22:56:24 +02:00
ShellManager shell_manager ;
CommandManager command_manager ;
BufferManager buffer_manager ;
RegisterManager register_manager ;
HighlighterRegistry highlighter_registry ;
FilterRegistry filter_registry ;
ColorRegistry color_registry ;
2012-10-30 14:00:44 +01:00
ClientManager client_manager ;
2012-10-23 22:56:24 +02:00
run_unit_tests ( ) ;
register_env_vars ( ) ;
register_registers ( ) ;
register_commands ( ) ;
register_highlighters ( ) ;
register_filters ( ) ;
2012-11-19 19:03:01 +01:00
write_debug ( " *** This is the debug buffer, where debug info will be written *** " ) ;
write_debug ( " pid: " + int_to_str ( getpid ( ) ) ) ;
2012-10-23 22:56:24 +02:00
write_debug ( " utf-8 test: é á ï " ) ;
2012-10-29 13:44:53 +01:00
Server server ;
2012-10-23 22:56:24 +02:00
2012-12-19 18:57:10 +01:00
if ( not parser . has_option ( " n " ) ) try
2012-06-12 15:10:33 +02:00
{
2012-08-28 14:10:44 +02:00
Context initialisation_context ;
2012-09-10 20:10:18 +02:00
command_manager . execute ( " source " + runtime_directory ( ) + " /kakrc " ,
initialisation_context ) ;
2012-06-12 15:10:33 +02:00
}
catch ( Kakoune : : runtime_error & error )
{
2012-10-17 17:49:34 +02:00
write_debug ( " error while parsing kakrc: " + error . description ( ) ) ;
2012-06-12 15:10:33 +02:00
}
2012-10-16 17:15:09 +02:00
2012-12-28 13:51:14 +01:00
if ( parser . positional_count ( ) ! = 0 )
{
// create buffers in reverse order so that the first given buffer
// is the most recently created one.
for ( int i = parser . positional_count ( ) - 1 ; i > = 0 ; - - i )
{
const String & file = parser [ i ] ;
if ( not create_buffer_from_file ( file ) )
new Buffer ( file , Buffer : : Flags : : New | Buffer : : Flags : : File ) ;
}
}
else
new Buffer ( " *scratch* " , Buffer : : Flags : : None ) ;
create_local_client ( init_command ) ;
2012-10-30 14:00:44 +01:00
while ( not client_manager . empty ( ) )
2012-10-16 17:15:09 +02:00
event_manager . handle_next_events ( ) ;
2011-09-02 18:51:20 +02:00
}
2011-09-09 21:24:18 +02:00
catch ( Kakoune : : exception & error )
{
2013-02-22 18:45:59 +01:00
on_assert_failed ( ( " uncaught exception: \n " + error . description ( ) ) . c_str ( ) ) ;
return - 1 ;
}
2013-03-22 14:29:22 +01:00
catch ( std : : exception & error )
{
on_assert_failed ( ( " uncaught exception: \n " _str + error . what ( ) ) . c_str ( ) ) ;
return - 1 ;
}
2013-02-22 18:45:59 +01:00
catch ( . . . )
{
on_assert_failed ( " uncaught exception " ) ;
2011-09-09 21:24:18 +02:00
return - 1 ;
}
2011-09-02 18:51:20 +02:00
return 0 ;
}