2013-04-09 20:05:40 +02:00
|
|
|
#include "assert.hh"
|
2011-09-02 18:51:20 +02:00
|
|
|
#include "buffer.hh"
|
2011-09-08 16:30:36 +02:00
|
|
|
#include "buffer_manager.hh"
|
2013-04-09 20:05:40 +02:00
|
|
|
#include "client_manager.hh"
|
|
|
|
#include "color_registry.hh"
|
|
|
|
#include "command_manager.hh"
|
|
|
|
#include "commands.hh"
|
|
|
|
#include "context.hh"
|
2011-10-07 16:16:38 +02:00
|
|
|
#include "debug.hh"
|
2013-04-09 20:05:40 +02:00
|
|
|
#include "event_manager.hh"
|
|
|
|
#include "file.hh"
|
2011-12-02 15:28:27 +01:00
|
|
|
#include "filters.hh"
|
2013-04-09 20:05:40 +02:00
|
|
|
#include "highlighters.hh"
|
2012-04-03 14:01:01 +02:00
|
|
|
#include "hook_manager.hh"
|
2012-02-16 15:25:16 +01:00
|
|
|
#include "ncurses.hh"
|
2013-04-09 20:05:40 +02:00
|
|
|
#include "option_manager.hh"
|
2012-12-18 21:41:13 +01:00
|
|
|
#include "parameters_parser.hh"
|
2013-04-09 20:05:40 +02:00
|
|
|
#include "register_manager.hh"
|
|
|
|
#include "remote.hh"
|
|
|
|
#include "shell_manager.hh"
|
|
|
|
#include "string.hh"
|
2013-04-18 14:28:53 +02:00
|
|
|
#include "window.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;
|
|
|
|
|
2013-04-12 14:28:13 +02:00
|
|
|
void run_unit_tests();
|
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);
|
2013-04-09 20:04:11 +02:00
|
|
|
kak_assert(res != -1);
|
2012-09-10 20:10:18 +02:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
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)
|
2013-04-15 14:28:21 +02:00
|
|
|
{ return ClientManager::instance().get_client(context).name(); });
|
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)); });
|
2013-04-18 14:28:53 +02:00
|
|
|
shell_manager.register_env_var("window_width",
|
|
|
|
[](const String& name, const Context& context)
|
|
|
|
{ return int_to_str((int)context.window().dimensions().column); });
|
|
|
|
shell_manager.register_env_var("window_height",
|
|
|
|
[](const String& name, const Context& context)
|
|
|
|
{ return int_to_str((int)context.window().dimensions().line); });
|
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{};
|
2013-04-15 18:50:45 +02:00
|
|
|
static Client* client = ClientManager::instance().create_client(
|
2013-01-11 19:17:21 +01:00
|
|
|
std::unique_ptr<UserInterface>{ui}, init_command);
|
2013-04-15 18:50:45 +02:00
|
|
|
signal(SIGHUP, [](int) {
|
|
|
|
if (client)
|
|
|
|
ClientManager::instance().remove_client(*client);
|
|
|
|
client = nullptr;
|
|
|
|
});
|
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
|
|
|
{
|
2013-04-12 01:28:22 +02:00
|
|
|
NCursesUI::abort();
|
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)
|
|
|
|
{
|
2013-04-11 13:57:35 +02:00
|
|
|
write_debug("error while parsing kakrc: "_str + error.what());
|
2012-06-12 15:10:33 +02:00
|
|
|
}
|
2013-04-15 18:55:41 +02:00
|
|
|
catch (Kakoune::client_removed&)
|
|
|
|
{
|
|
|
|
write_debug("error while parsing kakrc: asked to quit");
|
|
|
|
}
|
2012-10-16 17:15:09 +02:00
|
|
|
|
2013-04-19 13:45:44 +02:00
|
|
|
{
|
|
|
|
Context empty_context;
|
|
|
|
global_hooks.run_hook("KakBegin", "", empty_context);
|
|
|
|
}
|
|
|
|
|
2013-04-29 13:50:13 +02:00
|
|
|
if (parser.positional_count() != 0) try
|
2012-12-28 13:51:14 +01:00
|
|
|
{
|
|
|
|
// 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);
|
|
|
|
}
|
|
|
|
}
|
2013-04-29 13:50:13 +02:00
|
|
|
catch (Kakoune::runtime_error& error)
|
|
|
|
{
|
|
|
|
write_debug("error while opening command line files: "_str + error.what());
|
|
|
|
}
|
2012-12-28 13:51:14 +01:00
|
|
|
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();
|
2013-04-19 13:45:44 +02:00
|
|
|
|
|
|
|
{
|
|
|
|
Context empty_context;
|
|
|
|
global_hooks.run_hook("KakEnd", "", empty_context);
|
|
|
|
}
|
2011-09-02 18:51:20 +02:00
|
|
|
}
|
2011-09-09 21:24:18 +02:00
|
|
|
catch (Kakoune::exception& error)
|
|
|
|
{
|
2013-04-11 13:57:35 +02:00
|
|
|
on_assert_failed(("uncaught exception:\n"_str + error.what()).c_str());
|
2013-02-22 18:45:59 +01:00
|
|
|
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;
|
|
|
|
}
|