2012-05-07 05:13:34 +02:00
# include "commands.hh"
2013-04-09 19:39:03 +02:00
# include "buffer.hh"
2012-05-07 05:13:34 +02:00
# include "buffer_manager.hh"
2013-04-09 19:39:03 +02:00
# include "client_manager.hh"
# include "color_registry.hh"
# include "command_manager.hh"
# include "completion.hh"
2012-05-07 05:13:34 +02:00
# include "context.hh"
2013-04-09 19:39:03 +02:00
# include "debug.hh"
# include "event_manager.hh"
2012-05-07 05:13:34 +02:00
# include "file.hh"
2012-11-23 13:40:20 +01:00
# include "highlighter.hh"
2013-03-29 19:31:06 +01:00
# include "highlighters.hh"
2013-09-12 23:47:23 +02:00
# include "client.hh"
2013-04-09 19:39:03 +02:00
# include "option_manager.hh"
# include "option_types.hh"
# include "parameters_parser.hh"
2012-05-07 05:13:34 +02:00
# include "register_manager.hh"
2012-05-29 07:22:18 +02:00
# include "shell_manager.hh"
2013-04-09 19:39:03 +02:00
# include "string.hh"
# include "user_interface.hh"
2013-02-25 19:38:20 +01:00
# include "utf8_iterator.hh"
2013-04-09 19:39:03 +02:00
# include "window.hh"
2012-05-07 05:13:34 +02:00
2012-08-29 00:17:37 +02:00
# include <sys/types.h>
# include <sys/stat.h>
# include <fcntl.h>
2012-05-07 05:13:34 +02:00
namespace Kakoune
{
namespace
{
2012-08-15 22:36:45 +02:00
Buffer * open_or_create ( const String & filename , Context & context )
2012-05-07 05:13:34 +02:00
{
2012-10-16 14:59:39 +02:00
Buffer * buffer = create_buffer_from_file ( filename ) ;
if ( not buffer )
2012-05-07 05:13:34 +02:00
{
2013-04-04 18:50:00 +02:00
context . print_status ( { " new file " + filename , get_color ( " StatusLine " ) } ) ;
2012-11-20 19:47:56 +01:00
buffer = new Buffer ( filename , Buffer : : Flags : : File | Buffer : : Flags : : New ) ;
2012-05-07 05:13:34 +02:00
}
return buffer ;
}
2012-08-29 00:17:37 +02:00
Buffer * open_fifo ( const String & name , const String & filename , Context & context )
{
2013-03-25 19:11:26 +01:00
int fd = open ( parse_filename ( filename ) . c_str ( ) , O_RDONLY ) ;
2013-01-30 19:08:16 +01:00
fcntl ( fd , F_SETFD , FD_CLOEXEC ) ;
2012-08-29 00:17:37 +02:00
if ( fd < 0 )
throw runtime_error ( " unable to open " + filename ) ;
2013-03-22 18:39:00 +01:00
2013-04-10 18:54:01 +02:00
BufferManager : : instance ( ) . delete_buffer_if_exists ( name ) ;
2013-03-22 18:39:00 +01:00
2012-11-21 13:43:10 +01:00
Buffer * buffer = new Buffer ( name , Buffer : : Flags : : Fifo | Buffer : : Flags : : NoUndo ) ;
2012-08-29 00:17:37 +02:00
2013-01-10 18:54:40 +01:00
auto watcher = new FDWatcher ( fd , [ buffer ] ( FDWatcher & watcher ) {
2013-02-28 18:51:11 +01:00
constexpr size_t buffer_size = 1024 * 16 ;
char data [ buffer_size ] ;
ssize_t count = read ( watcher . fd ( ) , data , buffer_size ) ;
2013-06-06 19:39:53 +02:00
buffer - > insert ( buffer - > end ( ) - 1 , count > 0 ? String ( data , data + count )
: " *** kak: fifo closed *** \n " ) ;
2012-11-20 19:48:27 +01:00
if ( count < = 0 )
{
2013-04-09 20:04:11 +02:00
kak_assert ( buffer - > flags ( ) & Buffer : : Flags : : Fifo ) ;
2012-11-20 19:48:27 +01:00
buffer - > flags ( ) & = ~ Buffer : : Flags : : Fifo ;
2013-11-20 23:18:02 +01:00
buffer - > flags ( ) & = ~ Buffer : : Flags : : NoUndo ;
2013-01-10 18:54:40 +01:00
close ( watcher . fd ( ) ) ;
delete & watcher ;
2012-11-20 19:48:27 +01:00
}
2012-08-29 00:17:37 +02:00
} ) ;
2013-04-11 14:29:10 +02:00
buffer - > hooks ( ) . add_hook ( " BufClose " , " " ,
2013-01-10 18:54:40 +01:00
[ buffer , watcher ] ( const String & , const Context & ) {
// Check if fifo is still alive, else watcher is already dead
if ( buffer - > flags ( ) & Buffer : : Flags : : Fifo )
{
close ( watcher - > fd ( ) ) ;
delete watcher ;
}
} ) ;
2012-08-29 00:17:37 +02:00
return buffer ;
}
2014-02-08 02:02:58 +01:00
static const ParameterDesc edit_params {
2014-02-11 23:23:44 +01:00
SwitchMap { { " scratch " , { false , " create a scratch buffer, not linked to a file " } } ,
2014-02-11 23:16:17 +01:00
{ " fifo " , { true , " create a buffer reading its content from a named fifo " } } } ,
2014-02-08 02:02:58 +01:00
ParameterDesc : : Flags : : None , 1 , 3
} ;
2014-02-12 10:02:09 +01:00
static const char * edit_desc = " edit <switches> <filename>: open the given filename in a buffer " ;
2012-05-07 05:13:34 +02:00
template < bool force_reload >
2014-02-08 02:02:58 +01:00
void edit ( const ParametersParser & parser , Context & context )
2012-05-07 05:13:34 +02:00
{
2013-03-25 19:58:23 +01:00
const String name = parser [ 0 ] ;
2012-05-07 05:13:34 +02:00
Buffer * buffer = nullptr ;
if ( not force_reload )
2013-03-21 19:09:31 +01:00
buffer = BufferManager : : instance ( ) . get_buffer_ifp ( name ) ;
2012-05-07 05:13:34 +02:00
if ( not buffer )
2012-08-07 23:20:11 +02:00
{
if ( parser . has_option ( " scratch " ) )
2013-04-11 23:09:17 +02:00
{
BufferManager : : instance ( ) . delete_buffer_if_exists ( name ) ;
2012-11-20 19:47:56 +01:00
buffer = new Buffer ( name , Buffer : : Flags : : None ) ;
2013-04-11 23:09:17 +02:00
}
2012-08-29 00:17:37 +02:00
else if ( parser . has_option ( " fifo " ) )
buffer = open_fifo ( name , parser . option_value ( " fifo " ) , context ) ;
2012-08-07 23:20:11 +02:00
else
2012-08-29 00:17:37 +02:00
buffer = open_or_create ( name , context ) ;
2012-08-07 23:20:11 +02:00
}
2012-05-07 05:13:34 +02:00
2012-09-28 14:14:49 +02:00
BufferManager : : instance ( ) . set_last_used_buffer ( * buffer ) ;
2012-11-07 14:04:47 +01:00
2013-03-27 14:27:12 +01:00
const size_t param_count = parser . positional_count ( ) ;
2012-11-12 19:59:25 +01:00
if ( buffer ! = & context . buffer ( ) or param_count > 1 )
context . push_jump ( ) ;
2012-11-07 14:04:47 +01:00
if ( buffer ! = & context . buffer ( ) )
2013-12-20 21:10:08 +01:00
context . change_buffer ( * buffer ) ;
2012-05-07 05:13:34 +02:00
2013-08-28 20:05:01 +02:00
if ( param_count > 1 and not parser [ 1 ] . empty ( ) )
2012-05-07 05:13:34 +02:00
{
2013-05-17 14:09:42 +02:00
int line = std : : max ( 0 , str_to_int ( parser [ 1 ] ) - 1 ) ;
2013-08-28 20:05:01 +02:00
int column = param_count > 2 and not parser [ 2 ] . empty ( ) ?
2013-05-17 14:09:42 +02:00
std : : max ( 0 , str_to_int ( parser [ 2 ] ) - 1 ) : 0 ;
2012-05-07 05:13:34 +02:00
2013-12-15 15:25:23 +01:00
context . selections ( ) = context . buffer ( ) . clamp ( { line , column } ) ;
2012-11-07 14:04:47 +01:00
if ( context . has_window ( ) )
2014-01-28 20:05:49 +01:00
context . window ( ) . center_line ( context . selections ( ) . main ( ) . cursor ( ) . line ) ;
2012-05-07 05:13:34 +02:00
}
}
2014-02-08 02:02:58 +01:00
static const ParameterDesc write_params {
2014-02-11 23:23:44 +01:00
SwitchMap { } ,
2014-02-08 02:02:58 +01:00
ParameterDesc : : Flags : : None , 0 , 1
} ;
2012-05-07 05:13:34 +02:00
2014-02-12 10:02:09 +01:00
static const char * write_desc = " write [filename]: write the current buffer to it's file or to [filename] if specified " ;
2014-02-08 02:02:58 +01:00
void write_buffer ( const ParametersParser & parser , Context & context )
{
2012-08-11 11:48:54 +02:00
Buffer & buffer = context . buffer ( ) ;
2012-08-07 23:20:53 +02:00
2014-02-08 02:02:58 +01:00
if ( parser . positional_count ( ) = = 0 and ! ( buffer . flags ( ) & Buffer : : Flags : : File ) )
2012-11-20 19:47:56 +01:00
throw runtime_error ( " cannot write a non file buffer without a filename " ) ;
2012-08-07 23:20:53 +02:00
2014-02-08 02:02:58 +01:00
String filename = parser . positional_count ( ) = = 0 ? buffer . name ( )
: parse_filename ( parser [ 0 ] ) ;
2012-05-07 05:13:34 +02:00
write_buffer_to_file ( buffer , filename ) ;
}
2014-02-08 02:02:58 +01:00
static const ParameterDesc no_params {
2014-02-11 23:23:44 +01:00
SwitchMap { } ,
2014-02-08 02:02:58 +01:00
ParameterDesc : : Flags : : None , 0 , 0
} ;
2012-08-14 14:20:18 +02:00
2014-02-12 10:02:09 +01:00
static const char * write_all_desc = " write all buffers that are associated to a file " ;
2014-02-08 02:02:58 +01:00
void write_all_buffers ( const ParametersParser & parser , Context & context )
{
2012-08-14 14:20:18 +02:00
for ( auto & buffer : BufferManager : : instance ( ) )
{
2012-11-20 19:47:56 +01:00
if ( ( buffer - > flags ( ) & Buffer : : Flags : : File ) and buffer - > is_modified ( ) )
2012-08-14 14:20:18 +02:00
write_buffer_to_file ( * buffer , buffer - > name ( ) ) ;
}
}
2014-02-12 10:02:09 +01:00
static const char * quit_desc = " quit current client, and the kakoune session if the client is the last (if not running in daemon mode) " ;
2012-05-07 05:13:34 +02:00
template < bool force >
2014-02-08 02:02:58 +01:00
void quit ( const ParametersParser & parser , Context & context )
2012-05-07 05:13:34 +02:00
{
2012-10-30 14:00:44 +01:00
if ( not force and ClientManager : : instance ( ) . count ( ) = = 1 )
2012-05-07 05:13:34 +02:00
{
std : : vector < String > names ;
for ( auto & buffer : BufferManager : : instance ( ) )
{
2012-12-03 13:33:05 +01:00
if ( ( buffer - > flags ( ) & Buffer : : Flags : : File ) and buffer - > is_modified ( ) )
2012-08-08 19:36:40 +02:00
names . push_back ( buffer - > name ( ) ) ;
2012-05-07 05:13:34 +02:00
}
if ( not names . empty ( ) )
{
String message = " modified buffers remaining: [ " ;
for ( auto it = names . begin ( ) ; it ! = names . end ( ) ; + + it )
{
if ( it ! = names . begin ( ) )
message + = " , " ;
message + = * it ;
}
message + = " ] " ;
2012-06-30 00:44:14 +02:00
throw runtime_error ( message ) ;
2012-05-07 05:13:34 +02:00
}
}
2012-11-20 18:54:35 +01:00
// unwind back to this client event handler.
2012-10-30 14:00:44 +01:00
throw client_removed { } ;
2012-05-07 05:13:34 +02:00
}
2014-02-12 10:02:09 +01:00
static const char * write_and_quit_desc = " write current buffer and quit current client " ;
2012-05-07 05:13:34 +02:00
template < bool force >
2014-02-08 02:02:58 +01:00
void write_and_quit ( const ParametersParser & parser , Context & context )
2012-05-07 05:13:34 +02:00
{
2014-02-08 02:02:58 +01:00
write_buffer ( parser , context ) ;
quit < force > ( ParametersParser { memoryview < String > { } , no_params } , context ) ;
2012-05-07 05:13:34 +02:00
}
2014-02-11 23:23:44 +01:00
static const ParameterDesc single_name_params { SwitchMap { } , ParameterDesc : : Flags : : None , 1 , 1 } ;
2012-05-07 05:13:34 +02:00
2014-02-12 10:02:09 +01:00
static const char * show_buffer_desc = " buffer <name>: set buffer to edit in current client " ;
2014-02-08 02:02:58 +01:00
void show_buffer ( const ParametersParser & parser , Context & context )
{
Buffer & buffer = BufferManager : : instance ( ) . get_buffer ( parser [ 0 ] ) ;
2013-03-21 19:09:31 +01:00
BufferManager : : instance ( ) . set_last_used_buffer ( buffer ) ;
2012-11-07 14:04:47 +01:00
2013-03-21 19:09:31 +01:00
if ( & buffer ! = & context . buffer ( ) )
2012-11-07 14:04:47 +01:00
{
2012-11-12 19:59:25 +01:00
context . push_jump ( ) ;
2013-12-20 21:10:08 +01:00
context . change_buffer ( buffer ) ;
2012-11-07 14:04:47 +01:00
}
2012-05-29 00:51:12 +02:00
}
2014-02-11 23:23:44 +01:00
static const ParameterDesc single_opt_name_params { SwitchMap { } , ParameterDesc : : Flags : : None , 0 , 1 } ;
2014-02-08 02:02:58 +01:00
2014-02-12 10:02:09 +01:00
static const char * delete_buffer_desc = " delbuf [name]: delete the current buffer or the buffer named <name> if given " ;
2012-12-28 14:07:35 +01:00
template < bool force >
2014-02-08 02:02:58 +01:00
void delete_buffer ( const ParametersParser & parser , Context & context )
2012-05-29 00:51:12 +02:00
{
BufferManager & manager = BufferManager : : instance ( ) ;
2014-02-08 02:02:58 +01:00
Buffer & buffer = parser . positional_count ( ) = = 0 ? context . buffer ( ) : manager . get_buffer ( parser [ 0 ] ) ;
2013-03-21 19:09:31 +01:00
if ( not force and ( buffer . flags ( ) & Buffer : : Flags : : File ) and buffer . is_modified ( ) )
throw runtime_error ( " buffer " + buffer . name ( ) + " is modified " ) ;
2012-05-29 00:51:12 +02:00
2012-11-07 14:02:23 +01:00
if ( manager . count ( ) = = 1 )
2013-03-21 19:09:31 +01:00
throw runtime_error ( " buffer " + buffer . name ( ) + " is the last one " ) ;
2012-11-07 14:02:23 +01:00
2013-04-10 18:54:01 +02:00
manager . delete_buffer ( buffer ) ;
2012-05-07 05:13:34 +02:00
}
2014-02-12 10:02:09 +01:00
static const char * set_buffer_name_desc = " namebuf <name>: change current buffer name " ;
2014-02-08 02:02:58 +01:00
void set_buffer_name ( const ParametersParser & parser , Context & context )
2013-04-22 13:48:18 +02:00
{
if ( not context . buffer ( ) . set_name ( parser [ 0 ] ) )
throw runtime_error ( " unable to change buffer name to " + parser [ 0 ] ) ;
}
2014-02-12 10:02:09 +01:00
static const char * define_highlighter_desc = " defhl <name>: define a new reusable highlighter " ;
2014-02-08 02:02:58 +01:00
void define_highlighter ( const ParametersParser & parser , Context & context )
2012-12-09 18:58:58 +01:00
{
2014-02-08 02:02:58 +01:00
const String & name = parser [ 0 ] ;
2013-12-03 23:03:10 +01:00
DefinedHighlighters : : instance ( ) . append ( { name , HighlighterGroup { } } ) ;
2012-12-09 18:58:58 +01:00
}
2014-02-08 02:02:58 +01:00
static const ParameterDesc add_highlighter_params {
2014-02-11 23:23:44 +01:00
SwitchMap { { " group " , { true , " add highlighter to named group " } } ,
2014-02-11 23:16:17 +01:00
{ " def-group " , { true , " add highlighter to reusable defined group " } } } ,
2014-02-12 10:02:09 +01:00
ParameterDesc : : Flags : : SwitchesOnlyAtStart , 1
2014-02-08 02:02:58 +01:00
} ;
2014-02-12 10:02:09 +01:00
static const char * add_highlighter_desc = " addhl <switches> <type> <type params>...: add an highlighter to current window " ;
2014-02-08 02:02:58 +01:00
void add_highlighter ( const ParametersParser & parser , Context & context )
2012-05-07 05:13:34 +02:00
{
2012-06-30 00:44:14 +02:00
HighlighterRegistry & registry = HighlighterRegistry : : instance ( ) ;
2012-05-25 07:07:37 +02:00
2012-06-30 00:44:14 +02:00
auto begin = parser . begin ( ) ;
2012-08-01 14:27:34 +02:00
const String & name = * begin ;
2012-07-31 00:06:50 +02:00
std : : vector < String > highlighter_params ;
for ( + + begin ; begin ! = parser . end ( ) ; + + begin )
2012-08-01 14:27:34 +02:00
highlighter_params . push_back ( * begin ) ;
2012-05-07 05:13:34 +02:00
2013-12-03 23:03:10 +01:00
if ( parser . has_option ( " group " ) and parser . has_option ( " def-group " ) )
throw runtime_error ( " -group and -def-group cannot be specified together " ) ;
HighlighterGroup * group = nullptr ;
if ( parser . has_option ( " def-group " ) )
group = & DefinedHighlighters : : instance ( ) . get_group ( parser . option_value ( " def-group " ) , ' / ' ) ;
else
{
HighlighterGroup & window_hl = context . window ( ) . highlighters ( ) ;
group = parser . has_option ( " group " ) ?
& window_hl . get_group ( parser . option_value ( " group " ) , ' / ' )
: & window_hl ;
}
2012-06-30 00:44:14 +02:00
2013-12-03 23:03:10 +01:00
group - > append ( registry [ name ] ( highlighter_params ) ) ;
2012-05-07 05:13:34 +02:00
}
2014-02-08 02:02:58 +01:00
static const ParameterDesc rm_highlighter_params {
2014-02-11 23:23:44 +01:00
SwitchMap { { " group " , { true , " remove highlighter from given group " } } } ,
2014-02-11 23:16:17 +01:00
ParameterDesc : : Flags : : None , 1 , 1
2014-02-08 02:02:58 +01:00
} ;
2012-05-25 07:07:37 +02:00
2014-02-12 10:02:09 +01:00
static const char * rm_highlighter_desc = " rmhl <switches> <name>: remove highlighter <name> from current window " ;
2014-02-08 02:02:58 +01:00
void rm_highlighter ( const ParametersParser & parser , Context & context )
{
2013-12-03 23:03:10 +01:00
HighlighterGroup & window_hl = context . window ( ) . highlighters ( ) ;
2012-06-30 00:44:14 +02:00
HighlighterGroup & group = parser . has_option ( " group " ) ?
2013-12-03 23:03:10 +01:00
window_hl . get_group ( parser . option_value ( " group " ) , ' / ' )
: window_hl ;
2012-06-30 00:44:14 +02:00
2012-08-11 11:48:54 +02:00
group . remove ( parser [ 0 ] ) ;
2012-05-07 05:13:34 +02:00
}
2013-04-11 14:29:10 +02:00
static HookManager & get_hook_manager ( const String & scope , Context & context )
2012-05-07 05:13:34 +02:00
{
2013-10-25 01:00:44 +02:00
if ( prefix_match ( " global " , scope ) )
2013-04-11 14:29:10 +02:00
return GlobalHooks : : instance ( ) ;
2013-10-25 01:00:44 +02:00
else if ( prefix_match ( " buffer " , scope ) )
2013-04-11 14:29:10 +02:00
return context . buffer ( ) . hooks ( ) ;
2013-10-25 01:00:44 +02:00
else if ( prefix_match ( " window " , scope ) )
2013-04-11 14:29:10 +02:00
return context . window ( ) . hooks ( ) ;
throw runtime_error ( " error: no such hook container " + scope ) ;
}
2012-05-07 05:13:34 +02:00
2014-02-08 02:02:58 +01:00
static const ParameterDesc add_hook_params {
2014-02-11 23:23:44 +01:00
SwitchMap { { " id " , { true , " set hook id " } } } , ParameterDesc : : Flags : : None , 4 , 4
2014-02-08 02:02:58 +01:00
} ;
2014-02-12 10:02:09 +01:00
static const char * add_hook_desc = " hook <switches> <scope> <hook_name> <command>: add <command> to be executed on hook <hook_name> in <scope> context " ;
2014-02-08 02:02:58 +01:00
void add_hook ( const ParametersParser & parser , Context & context )
2013-04-11 14:29:10 +02:00
{
2012-07-31 14:22:57 +02:00
// copy so that the lambda gets a copy as well
2013-04-11 14:29:10 +02:00
Regex regex ( parser [ 2 ] . begin ( ) , parser [ 2 ] . end ( ) ) ;
String command = parser [ 3 ] ;
2013-01-17 13:44:14 +01:00
auto hook_func = [ = ] ( const String & param , Context & context ) {
2012-08-29 14:15:13 +02:00
if ( boost : : regex_match ( param . begin ( ) , param . end ( ) , regex ) )
2013-03-20 19:06:15 +01:00
CommandManager : : instance ( ) . execute ( command , context , { } ,
{ { " hook_param " , param } } ) ;
2012-05-07 05:13:34 +02:00
} ;
2013-04-11 14:29:10 +02:00
String id = parser . has_option ( " id " ) ? parser . option_value ( " id " ) : " " ;
get_hook_manager ( parser [ 0 ] , context ) . add_hook ( parser [ 1 ] , id , hook_func ) ;
}
2012-05-07 05:13:34 +02:00
2014-02-08 02:02:58 +01:00
static const ParameterDesc rm_hooks_params {
2014-02-11 23:23:44 +01:00
SwitchMap { } , ParameterDesc : : Flags : : None , 2 , 2
2014-02-08 02:02:58 +01:00
} ;
2014-02-12 10:02:09 +01:00
static const char * rm_hooks_desc = " rmhooks <id>: remove all hooks that whose id is <id> " ;
2014-02-08 02:02:58 +01:00
void rm_hooks ( const ParametersParser & parser , Context & context )
2013-04-11 14:29:10 +02:00
{
get_hook_manager ( parser [ 0 ] , context ) . remove_hooks ( parser [ 1 ] ) ;
2012-05-07 05:13:34 +02:00
}
2014-02-08 02:02:58 +01:00
EnvVarMap params_to_env_var_map ( const ParametersParser & parser )
2012-05-29 07:43:09 +02:00
{
std : : unordered_map < String , String > vars ;
char param_name [ ] = " param0 " ;
2014-02-08 02:02:58 +01:00
for ( size_t i = 0 ; i < parser . positional_count ( ) ; + + i )
2012-05-29 07:43:09 +02:00
{
2013-01-04 18:39:13 +01:00
param_name [ sizeof ( param_name ) - 2 ] = ' 0 ' + i ;
2014-02-08 02:02:58 +01:00
vars [ param_name ] = parser [ i ] ;
2012-05-29 07:43:09 +02:00
}
return vars ;
}
2014-02-08 02:02:58 +01:00
std : : vector < String > params_to_shell ( const ParametersParser & parser )
2012-05-07 05:13:34 +02:00
{
2014-02-08 02:02:58 +01:00
std : : vector < String > vars ;
for ( size_t i = 0 ; i < parser . positional_count ( ) ; + + i )
vars . push_back ( parser [ i ] ) ;
return vars ;
}
2012-05-07 05:13:34 +02:00
2014-02-08 02:02:58 +01:00
static const ParameterDesc define_command_params {
2014-02-11 23:23:44 +01:00
SwitchMap { { " env-params " , { false , " pass parameters as env variables param0..paramN " } } ,
2014-02-11 23:16:17 +01:00
{ " shell-params " , { false , " pass parameters to each shell escape as $0..$N " } } ,
{ " allow-override " , { false , " allow overriding existing command " } } ,
{ " file-completion " , { false , " complete parameters using filename completion " } } ,
{ " hidden " , { false , " do not display the command as completion candidate " } } ,
{ " shell-completion " , { true , " complete the parameters using the given shell-script " } } } ,
2014-02-08 02:02:58 +01:00
ParameterDesc : : Flags : : None ,
2 , 2
} ;
2014-02-12 10:02:09 +01:00
static const char * define_command_desc = " def <switches> <name> <commands>: define a command named <name> corresponding to <commands> " ;
2014-02-08 02:02:58 +01:00
void define_command ( const ParametersParser & parser , Context & context )
{
2012-05-25 07:07:37 +02:00
auto begin = parser . begin ( ) ;
2012-08-01 14:27:34 +02:00
const String & cmd_name = * begin ;
2012-06-02 17:49:35 +02:00
if ( CommandManager : : instance ( ) . command_defined ( cmd_name ) and
not parser . has_option ( " allow-override " ) )
throw runtime_error ( " command ' " + cmd_name + " ' already defined " ) ;
2013-11-12 20:38:19 +01:00
CommandFlags flags = CommandFlags : : None ;
if ( parser . has_option ( " hidden " ) )
flags = CommandFlags : : Hidden ;
2012-08-01 14:27:34 +02:00
String commands = parser [ 1 ] ;
2012-05-25 07:07:37 +02:00
Command cmd ;
2014-02-08 02:02:58 +01:00
ParameterDesc desc ;
2012-05-25 07:07:37 +02:00
if ( parser . has_option ( " env-params " ) )
2012-05-07 05:13:34 +02:00
{
2014-02-11 23:23:44 +01:00
desc = ParameterDesc { SwitchMap { } , ParameterDesc : : Flags : : None } ;
2014-02-08 02:02:58 +01:00
cmd = [ = ] ( const ParametersParser & parser , Context & context ) {
2012-09-09 17:10:53 +02:00
CommandManager : : instance ( ) . execute ( commands , context , { } ,
2014-02-08 02:02:58 +01:00
params_to_env_var_map ( parser ) ) ;
2012-05-25 07:07:37 +02:00
} ;
2012-05-07 05:13:34 +02:00
}
2012-09-09 17:10:53 +02:00
if ( parser . has_option ( " shell-params " ) )
{
2014-02-11 23:23:44 +01:00
desc = ParameterDesc { SwitchMap { } , ParameterDesc : : Flags : : None } ;
2014-02-08 02:02:58 +01:00
cmd = [ = ] ( const ParametersParser & parser , Context & context ) {
CommandManager : : instance ( ) . execute ( commands , context , params_to_shell ( parser ) ) ;
2012-09-09 17:10:53 +02:00
} ;
}
2012-05-07 05:13:34 +02:00
else
{
2014-02-11 23:23:44 +01:00
desc = ParameterDesc { SwitchMap { } , ParameterDesc : : Flags : : None , 0 , 0 } ;
2014-02-08 02:02:58 +01:00
cmd = [ = ] ( const ParametersParser & parser , Context & context ) {
2012-07-31 14:22:57 +02:00
CommandManager : : instance ( ) . execute ( commands , context ) ;
2012-05-25 07:07:37 +02:00
} ;
2012-05-07 05:13:34 +02:00
}
2012-05-29 07:22:18 +02:00
2012-09-12 14:21:42 +02:00
CommandCompleter completer ;
if ( parser . has_option ( " file-completion " ) )
{
2013-11-04 22:53:10 +01:00
completer = [ ] ( const Context & context , CompletionFlags flags ,
CommandParameters params ,
2012-10-11 00:41:48 +02:00
size_t token_to_complete , ByteCount pos_in_token )
2012-09-12 14:21:42 +02:00
{
2014-01-26 17:14:02 +01:00
const String & prefix = params [ token_to_complete ] ;
auto & ignored_files = context . options ( ) [ " ignored_files " ] . get < Regex > ( ) ;
return Completions { 0 _byte , prefix . length ( ) ,
complete_filename ( prefix , ignored_files ,
pos_in_token ) } ;
2012-09-12 14:21:42 +02:00
} ;
}
else if ( parser . has_option ( " shell-completion " ) )
2012-05-29 07:22:18 +02:00
{
String shell_cmd = parser . option_value ( " shell-completion " ) ;
2013-11-04 22:53:10 +01:00
completer = [ = ] ( const Context & context , CompletionFlags flags ,
CommandParameters params ,
2012-10-11 00:41:48 +02:00
size_t token_to_complete , ByteCount pos_in_token )
2012-05-29 07:22:18 +02:00
{
2013-11-04 22:53:10 +01:00
if ( flags = = CompletionFlags : : Fast ) // no shell on fast completion
2014-01-26 17:14:02 +01:00
return Completions { } ;
2013-05-13 14:23:07 +02:00
EnvVarMap vars = {
{ " token_to_complete " , to_string ( token_to_complete ) } ,
{ " pos_in_token " , to_string ( pos_in_token ) }
} ;
String output = ShellManager : : instance ( ) . eval ( shell_cmd , context , params , vars ) ;
2014-01-26 17:14:02 +01:00
return Completions { 0 _byte , params [ token_to_complete ] . length ( ) , split ( output , ' \n ' ) } ;
2012-05-29 07:22:18 +02:00
} ;
}
2014-02-12 10:02:09 +01:00
CommandManager : : instance ( ) . register_command ( cmd_name , cmd , " " , desc , flags , completer ) ;
2012-05-07 05:13:34 +02:00
}
2014-02-08 02:02:58 +01:00
static const ParameterDesc echo_message_params {
2014-02-11 23:23:44 +01:00
SwitchMap { { " color " , { true , " set message color " } } } ,
ParameterDesc : : Flags : : SwitchesOnlyAtStart
2014-02-08 02:02:58 +01:00
} ;
2014-02-12 10:02:09 +01:00
static const char * echo_message_desc = " echo <params>...: display given parameters in the status line " ;
2014-02-08 02:02:58 +01:00
void echo_message ( const ParametersParser & parser , Context & context )
2012-05-07 05:13:34 +02:00
{
String message ;
2013-04-12 14:22:01 +02:00
for ( auto & param : parser )
2012-08-01 14:27:34 +02:00
message + = param + " " ;
2013-04-25 18:51:54 +02:00
ColorPair color = get_color ( parser . has_option ( " color " ) ?
parser . option_value ( " color " ) : " StatusLine " ) ;
2013-04-12 14:22:01 +02:00
context . print_status ( { std : : move ( message ) , color } ) ;
2012-05-07 05:13:34 +02:00
}
2014-02-08 02:02:58 +01:00
static const ParameterDesc write_debug_message_params {
2014-02-11 23:23:44 +01:00
SwitchMap { } ,
ParameterDesc : : Flags : : SwitchesOnlyAtStart
2014-02-08 02:02:58 +01:00
} ;
2014-02-12 10:02:09 +01:00
static const char * write_debug_message_desc = " debug <params>...: write given parameters in the debug buffer " ;
2014-02-08 02:02:58 +01:00
void write_debug_message ( const ParametersParser & parser , Context & )
2013-04-10 13:45:17 +02:00
{
String message ;
2014-02-08 02:02:58 +01:00
for ( auto & param : parser )
2013-04-10 13:45:17 +02:00
message + = param + " " ;
write_debug ( message ) ;
}
2014-02-08 02:02:58 +01:00
static const ParameterDesc exec_commands_in_file_params {
2014-02-11 23:23:44 +01:00
SwitchMap { } ,
2014-02-08 02:02:58 +01:00
ParameterDesc : : Flags : : None ,
1 , 1
} ;
2014-02-12 10:02:09 +01:00
static const char * exec_commands_in_file_desc = " source <filename>: execute commands contained in <filename> " ;
2014-02-08 02:02:58 +01:00
void exec_commands_in_file ( const ParametersParser & parser ,
2012-08-06 22:02:11 +02:00
Context & context )
2012-05-07 05:13:34 +02:00
{
2014-02-08 02:02:58 +01:00
String file_content = read_file ( parse_filename ( parser [ 0 ] ) ) ;
2013-12-04 01:27:19 +01:00
try
{
CommandManager : : instance ( ) . execute ( file_content , context ) ;
}
catch ( Kakoune : : runtime_error & err )
{
2014-02-08 02:02:58 +01:00
write_debug ( " error while executing commands in file ' " + parser [ 0 ]
2013-12-04 01:27:19 +01:00
+ " ' \n " + err . what ( ) ) ;
throw ;
}
2012-05-07 05:13:34 +02:00
}
2013-10-30 10:38:40 +01:00
static OptionManager & get_options ( const String & scope , const Context & context )
2012-05-07 05:13:34 +02:00
{
2013-10-30 10:38:40 +01:00
if ( prefix_match ( " global " , scope ) )
return GlobalOptions : : instance ( ) ;
else if ( prefix_match ( " buffer " , scope ) )
return context . buffer ( ) . options ( ) ;
else if ( prefix_match ( " window " , scope ) )
return context . window ( ) . options ( ) ;
else if ( prefix_match ( scope , " buffer= " ) )
return BufferManager : : instance ( ) . get_buffer ( scope . substr ( 7 _byte ) ) . options ( ) ;
throw runtime_error ( " error: no such option container " + scope ) ;
2013-03-22 13:44:40 +01:00
}
2014-02-08 02:02:58 +01:00
static const ParameterDesc set_option_params {
2014-02-11 23:23:44 +01:00
SwitchMap { { " add " , { false , " add to option rather than replacing it " } } } ,
ParameterDesc : : Flags : : SwitchesOnlyAtStart ,
2014-02-08 02:02:58 +01:00
3 , 3
} ;
2014-02-12 10:02:09 +01:00
static const char * set_option_desc = " set <switches> <scope> <name> <value>: set option <name> in <scope> to <value> " ;
2014-02-08 02:02:58 +01:00
void set_option ( const ParametersParser & parser , Context & context )
{
2013-10-30 10:38:40 +01:00
Option & opt = get_options ( parser [ 0 ] , context ) . get_local_option ( parser [ 1 ] ) ;
2013-03-29 19:34:57 +01:00
if ( parser . has_option ( " add " ) )
2013-10-30 10:38:40 +01:00
opt . add_from_string ( parser [ 2 ] ) ;
2013-03-29 19:34:57 +01:00
else
2013-10-30 10:38:40 +01:00
opt . set_from_string ( parser [ 2 ] ) ;
2013-03-03 17:25:40 +01:00
}
2014-02-08 02:02:58 +01:00
static const ParameterDesc declare_option_params {
2014-02-11 23:23:44 +01:00
SwitchMap { { " hidden " , { false , " do not display option name when completing " } } } ,
ParameterDesc : : Flags : : SwitchesOnlyAtStart ,
2014-02-08 02:02:58 +01:00
2 , 3
} ;
2014-02-12 10:02:09 +01:00
static const char * declare_option_desc =
" decl <type> <name> [value]: declare option <name> of type <type>, with initial value <value> if given \n "
" Available types: \n "
" int: integer \n "
" bool: boolean (true/false or yes/no) \n "
" str: character string \n "
" regex: regular expression \n "
" int-list: list of integers \n "
" str-list: list of character strings \n "
" line-flag-list: list of line flags \n " ;
2014-02-08 02:02:58 +01:00
void declare_option ( const ParametersParser & parser , Context & context )
2013-03-03 17:25:40 +01:00
{
Option * opt = nullptr ;
2013-11-12 20:21:07 +01:00
Option : : Flags flags = Option : : Flags : : None ;
if ( parser . has_option ( " hidden " ) )
flags = Option : : Flags : : Hidden ;
GlobalOptions & opts = GlobalOptions : : instance ( ) ;
if ( parser [ 0 ] = = " int " )
opt = & opts . declare_option < int > ( parser [ 1 ] , 0 , flags ) ;
if ( parser [ 0 ] = = " bool " )
opt = & opts . declare_option < bool > ( parser [ 1 ] , 0 , flags ) ;
else if ( parser [ 0 ] = = " str " )
opt = & opts . declare_option < String > ( parser [ 1 ] , " " , flags ) ;
else if ( parser [ 0 ] = = " regex " )
opt = & opts . declare_option < Regex > ( parser [ 1 ] , Regex { } , flags ) ;
else if ( parser [ 0 ] = = " int-list " )
opt = & opts . declare_option < std : : vector < int > > ( parser [ 1 ] , { } , flags ) ;
else if ( parser [ 0 ] = = " str-list " )
opt = & opts . declare_option < std : : vector < String > > ( parser [ 1 ] , { } , flags ) ;
else if ( parser [ 0 ] = = " line-flag-list " )
opt = & opts . declare_option < std : : vector < LineAndFlag > > ( parser [ 1 ] , { } , flags ) ;
2013-03-03 17:25:40 +01:00
else
2013-11-12 20:21:07 +01:00
throw runtime_error ( " unknown type " + parser [ 0 ] ) ;
2013-03-03 17:25:40 +01:00
2013-11-12 20:21:07 +01:00
if ( parser . positional_count ( ) = = 3 )
opt - > set_from_string ( parser [ 2 ] ) ;
2012-05-07 05:13:34 +02:00
}
2013-10-25 01:01:17 +02:00
KeymapManager & get_keymap_manager ( const String & scope , Context & context )
{
if ( prefix_match ( " global " , scope ) )
return GlobalKeymaps : : instance ( ) ;
else if ( prefix_match ( " buffer " , scope ) )
return context . buffer ( ) . keymaps ( ) ;
else if ( prefix_match ( " window " , scope ) )
return context . window ( ) . keymaps ( ) ;
throw runtime_error ( " error: no such keymap container " + scope ) ;
}
KeymapMode parse_keymap_mode ( const String & str )
{
if ( prefix_match ( " normal " , str ) ) return KeymapMode : : Normal ;
if ( prefix_match ( " insert " , str ) ) return KeymapMode : : Insert ;
if ( prefix_match ( " menu " , str ) ) return KeymapMode : : Menu ;
if ( prefix_match ( " prompt " , str ) ) return KeymapMode : : Prompt ;
throw runtime_error ( " unknown keymap mode ' " + str + " ' " ) ;
}
2014-02-08 02:02:58 +01:00
static const ParameterDesc map_key_params {
2014-02-11 23:23:44 +01:00
SwitchMap { } , ParameterDesc : : Flags : : None , 4 , 4
2014-02-08 02:02:58 +01:00
} ;
2013-10-25 01:01:17 +02:00
2014-02-12 10:02:09 +01:00
static const char * map_key_desc =
" map <mode> <key> <keys>: map <key> to <keys> in given mode. \n "
" Valid modes: \n "
" normal \n "
" insert \n "
" menu \n "
" prompt \n " ;
2014-02-08 02:02:58 +01:00
void map_key ( const ParametersParser & parser , Context & context )
{
KeymapManager & keymaps = get_keymap_manager ( parser [ 0 ] , context ) ;
KeymapMode keymap_mode = parse_keymap_mode ( parser [ 1 ] ) ;
2013-10-25 01:01:17 +02:00
2014-02-08 02:02:58 +01:00
KeyList key = parse_keys ( parser [ 2 ] ) ;
2013-10-25 01:01:17 +02:00
if ( key . size ( ) ! = 1 )
throw runtime_error ( " only a single key can be mapped " ) ;
2014-02-08 02:02:58 +01:00
KeyList mapping = parse_keys ( parser [ 3 ] ) ;
2013-10-25 01:01:17 +02:00
keymaps . map_key ( key [ 0 ] , keymap_mode , std : : move ( mapping ) ) ;
}
2014-02-08 02:02:58 +01:00
const ParameterDesc context_wrap_params = {
2014-02-11 23:23:44 +01:00
SwitchMap { { " client " , { true , " run in given client context " } } ,
2014-02-11 23:16:17 +01:00
{ " try-client " , { true , " run in given client context if it exists, or else in the current one " } } ,
{ " draft " , { false , " run in a disposable context " } } ,
{ " itersel " , { false , " run once for each selection with that selection as the only one " } } } ,
2014-02-11 23:23:44 +01:00
ParameterDesc : : Flags : : SwitchesOnlyAtStart , 1
2014-02-08 02:02:58 +01:00
} ;
2013-01-17 14:06:06 +01:00
template < typename Func >
2014-02-08 02:02:58 +01:00
void context_wrap ( const ParametersParser & parser , Context & context , Func func )
2012-05-07 05:13:34 +02:00
{
2013-12-07 14:44:23 +01:00
ClientManager & cm = ClientManager : : instance ( ) ;
Context * real_context = & context ;
if ( parser . has_option ( " client " ) )
real_context = & cm . get_client ( parser . option_value ( " client " ) ) . context ( ) ;
else if ( parser . has_option ( " try-client " ) )
{
Client * client = cm . get_client_ifp ( parser . option_value ( " try-client " ) ) ;
if ( client )
real_context = & client - > context ( ) ;
}
2013-01-17 13:58:09 +01:00
2013-03-22 13:42:29 +01:00
if ( parser . has_option ( " draft " ) )
2013-02-07 19:25:07 +01:00
{
2013-12-20 21:10:08 +01:00
InputHandler input_handler ( real_context - > buffer ( ) , real_context - > selections ( ) , real_context - > name ( ) ) ;
2013-11-06 00:50:44 +01:00
2013-12-15 19:07:51 +01:00
// We do not want this draft context to commit undo groups if the real one is
// going to commit the whole thing later
if ( real_context - > is_editing ( ) )
input_handler . context ( ) . disable_undo_handling ( ) ;
2013-11-06 00:50:44 +01:00
if ( parser . has_option ( " itersel " ) )
{
2013-12-20 21:10:08 +01:00
DynamicSelectionList sels { real_context - > buffer ( ) , real_context - > selections ( ) } ;
2014-01-06 21:10:46 +01:00
ScopedEdition edition { input_handler . context ( ) } ;
2013-11-06 00:50:44 +01:00
for ( auto & sel : sels )
{
2013-12-17 00:51:57 +01:00
input_handler . context ( ) . selections ( ) = sel ;
2013-11-14 21:51:25 +01:00
func ( parser , input_handler . context ( ) ) ;
2013-11-06 00:50:44 +01:00
}
}
else
2013-11-14 21:51:25 +01:00
func ( parser , input_handler . context ( ) ) ;
2013-02-07 19:25:07 +01:00
}
else
2013-11-06 00:50:44 +01:00
{
if ( parser . has_option ( " itersel " ) )
throw runtime_error ( " -itersel makes no sense without -draft " ) ;
2013-12-07 14:44:23 +01:00
func ( parser , * real_context ) ;
2013-11-06 00:50:44 +01:00
}
2013-04-09 14:21:48 +02:00
// force redraw of this client window
2013-12-07 14:44:23 +01:00
if ( real_context ! = & context and real_context - > has_window ( ) )
real_context - > window ( ) . forget_timestamp ( ) ;
2012-12-03 18:57:57 +01:00
}
2014-02-12 10:02:09 +01:00
static const char * exec_string_desc = " exec <switches> <keys>: execute given keys as if entered by user " ;
2014-02-08 02:02:58 +01:00
void exec_string ( const ParametersParser & parser , Context & context )
2012-12-03 18:57:57 +01:00
{
2014-02-08 02:02:58 +01:00
context_wrap ( parser , context , [ ] ( const ParametersParser & parser , Context & context ) {
2013-01-17 14:06:06 +01:00
KeyList keys ;
for ( auto & param : parser )
{
KeyList param_keys = parse_keys ( param ) ;
keys . insert ( keys . end ( ) , param_keys . begin ( ) , param_keys . end ( ) ) ;
}
exec_keys ( keys , context ) ;
2013-01-17 13:58:09 +01:00
} ) ;
2013-01-17 14:06:06 +01:00
}
2013-01-17 13:58:09 +01:00
2014-02-12 10:02:09 +01:00
static const char * eval_string_desc = " eval <switches> <keys>: execute commands as if entered by user " ;
2014-02-08 02:02:58 +01:00
void eval_string ( const ParametersParser & parser , Context & context )
2013-01-17 14:06:06 +01:00
{
2014-02-08 02:02:58 +01:00
context_wrap ( parser , context , [ ] ( const ParametersParser & parser , Context & context ) {
2013-01-17 14:06:06 +01:00
String command ;
for ( auto & param : parser )
command + = param + " " ;
CommandManager : : instance ( ) . execute ( command , context ) ;
} ) ;
2012-05-07 05:13:34 +02:00
}
2014-02-11 23:16:17 +01:00
static const ParameterDesc menu_params {
2014-02-11 23:23:44 +01:00
SwitchMap { { " auto-single " , { false , " instantly validate if only one item is available " } } ,
2014-02-11 23:16:17 +01:00
{ " select-cmds " , { false , " each item specify an additional command to run when selected " } } }
} ;
2012-05-29 01:50:11 +02:00
2014-02-12 10:02:09 +01:00
static const char * menu_desc = " menu <switches> <name1> <commands1> <name2> <commands2>...: display a menu and execute commands for the selected item " ;
2014-02-08 02:02:58 +01:00
void menu ( const ParametersParser & parser , Context & context )
{
2012-12-15 19:11:22 +01:00
const bool with_select_cmds = parser . has_option ( " select-cmds " ) ;
const size_t modulo = with_select_cmds ? 3 : 2 ;
const size_t count = parser . positional_count ( ) ;
if ( count = = 0 or ( count % modulo ) ! = 0 )
2012-05-18 07:20:46 +02:00
throw wrong_argument_count ( ) ;
2012-12-15 19:11:22 +01:00
if ( count = = modulo and parser . has_option ( " auto-single " ) )
2012-05-29 01:50:11 +02:00
{
2012-08-01 14:27:34 +02:00
CommandManager : : instance ( ) . execute ( parser [ 1 ] , context ) ;
2012-05-29 01:50:11 +02:00
return ;
}
2012-08-30 21:14:28 +02:00
std : : vector < String > choices ;
2012-09-03 14:22:02 +02:00
std : : vector < String > commands ;
2012-12-15 19:11:22 +01:00
std : : vector < String > select_cmds ;
for ( int i = 0 ; i < count ; i + = modulo )
2012-09-03 14:22:02 +02:00
{
2012-08-30 21:14:28 +02:00
choices . push_back ( parser [ i ] ) ;
2012-09-03 14:22:02 +02:00
commands . push_back ( parser [ i + 1 ] ) ;
2012-12-15 19:11:22 +01:00
if ( with_select_cmds )
select_cmds . push_back ( parser [ i + 2 ] ) ;
2012-09-03 14:22:02 +02:00
}
2012-05-18 07:20:46 +02:00
2013-11-14 19:09:15 +01:00
context . input_handler ( ) . menu ( choices ,
2012-12-14 19:38:11 +01:00
[ = ] ( int choice , MenuEvent event , Context & context ) {
if ( event = = MenuEvent : : Validate and choice > = 0 and choice < commands . size ( ) )
2012-09-03 14:22:02 +02:00
CommandManager : : instance ( ) . execute ( commands [ choice ] , context ) ;
2012-12-15 19:11:22 +01:00
if ( event = = MenuEvent : : Select and choice > = 0 and choice < select_cmds . size ( ) )
CommandManager : : instance ( ) . execute ( select_cmds [ choice ] , context ) ;
2013-01-29 13:49:01 +01:00
} ) ;
2012-05-18 07:20:46 +02:00
}
2014-02-08 02:02:58 +01:00
static const ParameterDesc info_params {
2014-02-11 23:23:44 +01:00
SwitchMap { { " anchor " , { true , " set info anchoring (left, right, or cursor) " } } ,
2014-02-11 23:16:17 +01:00
{ " title " , { true , " set info title " } } } ,
2014-02-08 02:02:58 +01:00
ParameterDesc : : Flags : : None , 0 , 1
} ;
2012-12-14 19:04:34 +01:00
2014-02-12 10:02:09 +01:00
static const char * info_desc = " info <switches> <params>...: display an info box with the params as content " ;
2014-02-08 02:02:58 +01:00
void info ( const ParametersParser & parser , Context & context )
{
2012-12-14 19:04:34 +01:00
context . ui ( ) . info_hide ( ) ;
if ( parser . positional_count ( ) > 0 )
2012-12-14 19:25:27 +01:00
{
2013-01-29 18:56:14 +01:00
MenuStyle style = MenuStyle : : Prompt ;
2013-10-10 23:51:16 +02:00
DisplayCoord pos = context . ui ( ) . dimensions ( ) ;
pos . column - = 1 ;
2013-01-29 18:56:14 +01:00
if ( parser . has_option ( " anchor " ) )
{
style = MenuStyle : : Inline ;
2013-12-15 15:25:23 +01:00
const auto & sel = context . selections ( ) . main ( ) ;
2014-01-28 20:05:49 +01:00
auto it = sel . cursor ( ) ;
2013-01-29 18:56:14 +01:00
String anchor = parser . option_value ( " anchor " ) ;
if ( anchor = = " left " )
2013-05-24 14:25:50 +02:00
it = sel . min ( ) ;
2013-01-29 18:56:14 +01:00
else if ( anchor = = " right " )
2013-05-24 14:25:50 +02:00
it = sel . max ( ) ;
2013-01-29 18:56:14 +01:00
else if ( anchor ! = " cursor " )
throw runtime_error ( " anchor param must be one of [left, right, cursor] " ) ;
pos = context . window ( ) . display_position ( it ) ;
}
2013-10-10 23:51:16 +02:00
const String & title = parser . has_option ( " title " ) ? parser . option_value ( " title " ) : " " ;
context . ui ( ) . info_show ( title , parser [ 0 ] , pos , get_color ( " Information " ) , style ) ;
2012-12-14 19:25:27 +01:00
}
2012-12-14 19:04:34 +01:00
}
2014-02-08 02:02:58 +01:00
static const ParameterDesc try_catch_params {
2014-02-11 23:23:44 +01:00
SwitchMap { } , ParameterDesc : : Flags : : None , 1 , 3
2014-02-08 02:02:58 +01:00
} ;
2014-02-12 10:02:09 +01:00
static const char * try_catch_desc = " try <command> [catch <error_command>]: execute command in current context, and if an error is raised, execute <error_command> if specified. \n The error is not propagated further. " ;
2014-02-08 02:02:58 +01:00
void try_catch ( const ParametersParser & parser , Context & context )
2012-06-04 16:27:34 +02:00
{
2014-02-08 02:02:58 +01:00
if ( parser . positional_count ( ) = = 2 )
2012-06-04 16:27:34 +02:00
throw wrong_argument_count ( ) ;
2013-10-31 20:22:00 +01:00
2014-02-08 02:02:58 +01:00
const bool do_catch = parser . positional_count ( ) = = 3 ;
if ( do_catch and parser [ 1 ] ! = " catch " )
2013-10-31 20:22:00 +01:00
throw runtime_error ( " usage: try <commands> [catch <on error commands>] " ) ;
2012-06-04 16:27:34 +02:00
CommandManager & command_manager = CommandManager : : instance ( ) ;
try
{
2014-02-08 02:02:58 +01:00
command_manager . execute ( parser [ 0 ] , context ) ;
2012-06-04 16:27:34 +02:00
}
catch ( Kakoune : : runtime_error & e )
{
2013-10-31 20:22:00 +01:00
if ( do_catch )
2014-02-08 02:02:58 +01:00
command_manager . execute ( parser [ 2 ] , context ) ;
2012-06-04 16:27:34 +02:00
}
}
2014-02-08 02:02:58 +01:00
static const ParameterDesc define_color_alias_params {
2014-02-11 23:23:44 +01:00
SwitchMap { } , ParameterDesc : : Flags : : None , 2 , 2
2014-02-08 02:02:58 +01:00
} ;
2014-02-12 10:02:09 +01:00
static const char * define_color_alias_desc = " colalias <name> <color>: set <name> to refer to color <color> (which can be an alias itself) " ;
2014-02-08 02:02:58 +01:00
void define_color_alias ( const ParametersParser & parser , Context & context )
2012-09-17 21:01:11 +02:00
{
2014-02-08 02:02:58 +01:00
ColorRegistry : : instance ( ) . register_alias ( parser [ 0 ] , parser [ 1 ] , true ) ;
2012-09-17 21:01:11 +02:00
}
2014-02-08 02:02:58 +01:00
static const ParameterDesc set_client_name_params {
2014-02-11 23:23:44 +01:00
SwitchMap { } , ParameterDesc : : Flags : : None , 1 , 1
2014-02-08 02:02:58 +01:00
} ;
2014-02-12 10:02:09 +01:00
static const char * set_client_name_desc = " nameclient <name>: set current client name to <name> " ;
2014-02-08 02:02:58 +01:00
void set_client_name ( const ParametersParser & parser , Context & context )
2012-12-03 18:57:23 +01:00
{
2014-02-08 02:02:58 +01:00
if ( ClientManager : : instance ( ) . validate_client_name ( parser [ 0 ] ) )
context . set_name ( parser [ 0 ] ) ;
else if ( context . name ( ) ! = parser [ 0 ] )
throw runtime_error ( " client name ' " + parser [ 0 ] + " ' is not unique " ) ;
2012-12-03 18:57:23 +01:00
}
2014-02-08 02:02:58 +01:00
static const ParameterDesc set_register_params {
2014-02-11 23:23:44 +01:00
SwitchMap { } , ParameterDesc : : Flags : : None , 2 , 2
2014-02-08 02:02:58 +01:00
} ;
2013-03-18 22:43:48 +01:00
2014-02-12 10:02:09 +01:00
static const char * set_register_desc = " reg <name> <value>: set register <name> to <value> " ;
2014-02-08 02:02:58 +01:00
void set_register ( const ParametersParser & parser , Context & context )
{
if ( parser [ 0 ] . length ( ) ! = 1 )
2013-03-18 22:43:48 +01:00
throw runtime_error ( " register names are single character " ) ;
2014-02-08 02:02:58 +01:00
RegisterManager : : instance ( ) [ parser [ 0 ] [ 0 ] ] = memoryview < String > ( parser [ 1 ] ) ;
2013-03-18 22:43:48 +01:00
}
2014-02-08 02:02:58 +01:00
static const ParameterDesc change_working_directory_params {
2014-02-11 23:23:44 +01:00
SwitchMap { } , ParameterDesc : : Flags : : None , 1 , 1
2014-02-08 02:02:58 +01:00
} ;
2013-03-25 19:19:44 +01:00
2014-02-12 10:02:09 +01:00
static const char * change_working_directory_desc = " cd <dir>: change server working directory to <dir> " ;
2014-02-08 02:02:58 +01:00
void change_working_directory ( const ParametersParser & parser , Context & )
{
if ( chdir ( parse_filename ( parser [ 0 ] ) . c_str ( ) ) ! = 0 )
throw runtime_error ( " cannot change to directory " + parser [ 0 ] ) ;
2013-03-25 19:19:44 +01:00
}
2013-03-27 14:14:13 +01:00
template < typename GetRootGroup >
CommandCompleter group_rm_completer ( GetRootGroup get_root_group )
{
2013-11-04 22:53:10 +01:00
return [ = ] ( const Context & context , CompletionFlags flags ,
CommandParameters params , size_t token_to_complete ,
2014-01-26 17:14:02 +01:00
ByteCount pos_in_token ) - > Completions {
2013-03-27 14:14:13 +01:00
auto & root_group = get_root_group ( context ) ;
2014-01-26 17:14:02 +01:00
const String & arg = params [ token_to_complete ] ;
2013-03-27 14:14:13 +01:00
if ( token_to_complete = = 1 and params [ 0 ] = = " -group " )
2014-01-26 17:14:02 +01:00
return { 0 _byte , arg . length ( ) , root_group . complete_group_id ( arg , pos_in_token ) } ;
2013-03-27 14:14:13 +01:00
else if ( token_to_complete = = 2 and params [ 0 ] = = " -group " )
2014-01-26 17:14:02 +01:00
return { 0 _byte , arg . length ( ) , root_group . get_group ( params [ 1 ] , ' / ' ) . complete_id ( arg , pos_in_token ) } ;
return { 0 _byte , arg . length ( ) , root_group . complete_id ( arg , pos_in_token ) } ;
2013-03-27 14:14:13 +01:00
} ;
}
template < typename FactoryRegistry , typename GetRootGroup >
CommandCompleter group_add_completer ( GetRootGroup get_root_group )
{
2013-11-04 22:53:10 +01:00
return [ = ] ( const Context & context , CompletionFlags flags ,
CommandParameters params , size_t token_to_complete ,
2014-01-26 17:14:02 +01:00
ByteCount pos_in_token ) - > Completions {
2013-03-27 14:14:13 +01:00
auto & root_group = get_root_group ( context ) ;
2014-01-26 17:14:02 +01:00
const String & arg = params [ token_to_complete ] ;
2013-03-27 14:14:13 +01:00
if ( token_to_complete = = 1 and params [ 0 ] = = " -group " )
2014-01-26 17:14:02 +01:00
return { 0 _byte , arg . length ( ) , root_group . complete_group_id ( arg , pos_in_token ) } ;
2013-03-27 14:14:13 +01:00
else if ( token_to_complete = = 0 or ( token_to_complete = = 2 and params [ 0 ] = = " -group " ) )
2014-01-26 17:14:02 +01:00
return { 0 _byte , arg . length ( ) , FactoryRegistry : : instance ( ) . complete_name ( arg , pos_in_token ) } ;
return Completions { } ;
2013-03-27 14:14:13 +01:00
} ;
}
2013-02-18 18:57:08 +01:00
class RegisterRestorer
{
public :
RegisterRestorer ( char name , const Context & context )
: m_name ( name )
{
memoryview < String > save = RegisterManager : : instance ( ) [ name ] . values ( context ) ;
m_save = std : : vector < String > ( save . begin ( ) , save . end ( ) ) ;
}
~ RegisterRestorer ( )
{ RegisterManager : : instance ( ) [ m_name ] = m_save ; }
private :
std : : vector < String > m_save ;
char m_name ;
} ;
}
void exec_keys ( const KeyList & keys , Context & context )
{
RegisterRestorer quote ( ' " ' , context ) ;
RegisterRestorer slash ( ' / ' , context ) ;
2013-12-15 19:07:51 +01:00
ScopedEdition edition ( context ) ;
2013-02-18 18:57:08 +01:00
2013-09-11 19:54:30 +02:00
for ( auto & key : keys )
2013-11-14 19:09:15 +01:00
context . input_handler ( ) . handle_key ( key ) ;
2012-05-07 05:13:34 +02:00
}
2014-01-26 17:14:02 +01:00
CandidateList complete_scope ( const String & prefix )
{
CandidateList res ;
for ( auto scope : { " global " , " buffer " , " window " } )
{
if ( prefix_match ( scope , prefix ) )
res . emplace_back ( scope ) ;
}
return res ;
}
2012-05-07 05:13:34 +02:00
void register_commands ( )
{
CommandManager & cm = CommandManager : : instance ( ) ;
2014-02-12 10:02:09 +01:00
cm . register_commands ( { " nop " } , [ ] ( const ParametersParser & , Context & ) { } , " do nothing " , { } ) ;
2013-01-03 18:52:07 +01:00
2013-03-13 19:01:59 +01:00
PerArgumentCommandCompleter filename_completer ( {
2013-11-04 22:53:10 +01:00
[ ] ( const Context & context , CompletionFlags flags , const String & prefix , ByteCount cursor_pos )
2014-01-26 17:14:02 +01:00
{ return Completions { 0 _byte , prefix . length ( ) ,
complete_filename ( prefix ,
context . options ( ) [ " ignored_files " ] . get < Regex > ( ) ,
cursor_pos ) } ; }
2013-03-13 19:01:59 +01:00
} ) ;
2014-02-12 10:02:09 +01:00
cm . register_commands ( { " edit " , " e " } , edit < false > , edit_desc , edit_params , CommandFlags : : None , filename_completer ) ;
cm . register_commands ( { " edit! " , " e! " } , edit < true > , edit_desc , edit_params , CommandFlags : : None , filename_completer ) ;
cm . register_commands ( { " write " , " w " } , write_buffer , write_desc , write_params , CommandFlags : : None , filename_completer ) ;
cm . register_commands ( { " writeall " , " wa " } , write_all_buffers , write_all_desc , no_params ) ;
cm . register_commands ( { " quit " , " q " } , quit < false > , quit_desc , no_params ) ;
cm . register_commands ( { " quit! " , " q! " } , quit < true > , quit_desc , no_params ) ;
cm . register_command ( " wq " , write_and_quit < false > , write_and_quit_desc , no_params ) ;
cm . register_command ( " wq! " , write_and_quit < true > , write_and_quit_desc , no_params ) ;
2012-05-29 05:34:54 +02:00
PerArgumentCommandCompleter buffer_completer ( {
2013-11-04 22:53:10 +01:00
[ ] ( const Context & context , CompletionFlags flags , const String & prefix , ByteCount cursor_pos )
2014-01-26 17:14:02 +01:00
{ return Completions { 0 _byte , prefix . length ( ) ,
BufferManager : : instance ( ) . complete_buffername ( prefix , cursor_pos ) } ; }
2012-05-29 05:34:54 +02:00
} ) ;
2014-02-12 10:02:09 +01:00
cm . register_commands ( { " buffer " , " b " } , show_buffer , show_buffer_desc , single_name_params , CommandFlags : : None , buffer_completer ) ;
cm . register_commands ( { " delbuf " , " db " } , delete_buffer < false > , delete_buffer_desc , single_opt_name_params , CommandFlags : : None , buffer_completer ) ;
cm . register_commands ( { " delbuf! " , " db! " } , delete_buffer < true > , delete_buffer_desc , single_opt_name_params , CommandFlags : : None , buffer_completer ) ;
cm . register_commands ( { " namebuf " , " nb " } , set_buffer_name , set_buffer_name_desc , single_name_params ) ;
2012-05-29 05:34:54 +02:00
2013-03-27 14:14:13 +01:00
auto get_highlighters = [ ] ( const Context & c ) - > HighlighterGroup & { return c . window ( ) . highlighters ( ) ; } ;
2014-02-12 10:02:09 +01:00
cm . register_commands ( { " addhl " , " ah " } , add_highlighter , add_highlighter_desc , add_highlighter_params , CommandFlags : : None , group_add_completer < HighlighterRegistry > ( get_highlighters ) ) ;
cm . register_commands ( { " rmhl " , " rh " } , rm_highlighter , rm_highlighter_desc , rm_highlighter_params , CommandFlags : : None , group_rm_completer ( get_highlighters ) ) ;
cm . register_commands ( { " defhl " , " dh " } , define_highlighter , define_highlighter_desc , single_name_params ) ;
2012-05-29 05:34:54 +02:00
2014-02-12 10:02:09 +01:00
cm . register_command ( " hook " , add_hook , add_hook_desc , add_hook_params , CommandFlags : : None ,
2014-01-26 17:14:02 +01:00
[ ] ( const Context & context , CompletionFlags flags ,
CommandParameters params , size_t token_to_complete , ByteCount pos_in_token )
{
if ( token_to_complete = = 0 )
return Completions { 0 _byte , params [ 0 ] . length ( ) ,
complete_scope ( params [ 0 ] . substr ( 0 _byte , pos_in_token ) ) } ;
else if ( token_to_complete = = 3 )
{
auto & cm = CommandManager : : instance ( ) ;
return cm . complete ( context , flags , params [ 3 ] , pos_in_token ) ;
}
return Completions { } ;
} ) ;
2014-02-12 10:02:09 +01:00
cm . register_command ( " rmhooks " , rm_hooks , rm_hooks_desc , rm_hooks_params ) ;
2012-05-07 05:13:34 +02:00
2014-02-12 10:02:09 +01:00
cm . register_command ( " source " , exec_commands_in_file , exec_commands_in_file_desc , exec_commands_in_file_params , CommandFlags : : None , filename_completer ) ;
2012-05-07 05:13:34 +02:00
2014-02-12 10:02:09 +01:00
cm . register_command ( " exec " , exec_string , exec_string_desc , context_wrap_params ) ;
cm . register_command ( " eval " , eval_string , eval_string_desc , context_wrap_params ) ;
cm . register_command ( " menu " , menu , menu_desc , menu_params ) ;
cm . register_command ( " info " , info , info_desc , info_params ) ;
cm . register_command ( " try " , try_catch , try_catch_desc , try_catch_params ) ;
cm . register_command ( " reg " , set_register , set_register_desc , set_register_params ) ;
2012-05-07 05:13:34 +02:00
2014-02-12 10:02:09 +01:00
cm . register_command ( " def " , define_command , define_command_desc , define_command_params ) ;
cm . register_command ( " decl " , declare_option , declare_option_desc , declare_option_params ) ;
2013-04-10 13:45:17 +02:00
2014-02-12 10:02:09 +01:00
cm . register_command ( " echo " , echo_message , echo_message_desc , echo_message_params ) ;
cm . register_command ( " debug " , write_debug_message , write_debug_message_desc , write_debug_message_params ) ;
2012-05-07 05:13:34 +02:00
2014-02-12 10:02:09 +01:00
cm . register_command ( " set " , set_option , set_option_desc , set_option_params , CommandFlags : : None ,
2014-01-26 17:14:02 +01:00
[ ] ( const Context & context , CompletionFlags ,
CommandParameters params , size_t token_to_complete ,
ByteCount pos_in_token ) - > Completions
2013-10-30 10:38:40 +01:00
{
if ( token_to_complete = = 0 )
2014-01-26 17:14:02 +01:00
return { 0 _byte , params [ 0 ] . length ( ) ,
complete_scope ( params [ 0 ] . substr ( 0 _byte , pos_in_token ) ) } ;
2013-10-30 10:38:40 +01:00
else if ( token_to_complete = = 1 )
{
OptionManager & options = get_options ( params [ 0 ] , context ) ;
2014-01-26 17:14:02 +01:00
return { 0 _byte , params [ 1 ] . length ( ) ,
options . complete_option_name ( params [ 1 ] , pos_in_token ) } ;
2013-10-30 10:38:40 +01:00
}
2014-01-26 17:14:02 +01:00
return Completions { } ;
2013-10-30 10:38:40 +01:00
} ) ;
2012-09-17 19:01:13 +02:00
2014-02-12 10:02:09 +01:00
cm . register_commands ( { " colalias " , " ca " } , define_color_alias , define_color_alias_desc , define_color_alias_params ) ;
cm . register_commands ( { " nameclient " , " nc " } , set_client_name , set_client_name_desc , set_client_name_params ) ;
2013-03-18 22:43:48 +01:00
2014-02-12 10:02:09 +01:00
cm . register_command ( " cd " , change_working_directory , change_working_directory_desc , change_working_directory_params , CommandFlags : : None , filename_completer ) ;
cm . register_command ( " map " , map_key , map_key_desc , map_key_params ) ;
2012-05-07 05:13:34 +02:00
}
}