home/src/main.cc

305 lines
11 KiB
C++
Raw Normal View History

2013-04-09 20:05:40 +02:00
#include "assert.hh"
2011-09-02 18:51:20 +02:00
#include "buffer.hh"
#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"
#include "debug.hh"
2013-04-09 20:05:40 +02:00
#include "event_manager.hh"
#include "file.hh"
#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"
#include "ncurses.hh"
2013-04-09 20:05:40 +02:00
#include "option_manager.hh"
#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>
#include <locale>
#include <signal.h>
2011-09-02 18:51:20 +02:00
using namespace Kakoune;
void run_unit_tests();
String runtime_directory()
{
char buffer[2048];
#if defined(__linux__) || defined(__CYGWIN__)
ssize_t res = readlink("/proc/self/exe", buffer, 2048);
kak_assert(res != -1);
buffer[res] = '\0';
#elif defined(__APPLE__)
uint32_t bufsize = 2048;
_NSGetExecutablePath(buffer, &bufsize);
2013-07-28 16:40:02 +02:00
char* canonical_path = realpath(buffer, nullptr);
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");
return String(buffer, ptr);
}
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(); });
shell_manager.register_env_var("timestamp",
[](const String& name, const Context& context)
2013-05-13 14:23:07 +02:00
{ return to_string(context.buffer().timestamp()); });
shell_manager.register_env_var("selection",
[](const String& name, const Context& context)
2013-05-27 14:22:21 +02:00
{ const Range& sel = context.editor().main_selection();
return content(context.buffer(), sel); });
shell_manager.register_env_var("selections",
[](const String& name, const Context& context) {
auto sels = context.editor().selections_content();
String res;
for (size_t i = 0; i < sels.size(); ++i)
{
res += escape(sels[i], ':', '\\');
if (i != sels.size() - 1)
res += ':';
}
return res;
});
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)
{ return context.options()[name.substr(4_byte)].get_as_string(); });
shell_manager.register_env_var("reg_.+",
[](const String& name, const Context& context)
{ return RegisterManager::instance()[name[4]].values(context)[0]; });
shell_manager.register_env_var("socket",
[](const String& name, const Context& context)
{ return Server::instance().filename(); });
shell_manager.register_env_var("client",
[](const String& name, const Context& context)
{ 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)
{ return to_string(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)
{ return to_string(context.editor().main_selection().last().column + 1); });
shell_manager.register_env_var("selection_desc",
[](const String& name, const Context& context)
{ auto& sel = context.editor().main_selection();
auto beg = sel.min();
return to_string(beg.line + 1) + ':' + to_string(beg.column + 1) + '+' +
to_string((int)context.buffer().distance(beg, sel.max())+1); });
2013-04-18 14:28:53 +02:00
shell_manager.register_env_var("window_width",
[](const String& name, const Context& context)
2013-05-13 14:23:07 +02:00
{ return to_string(context.window().dimensions().column); });
2013-04-18 14:28:53 +02:00
shell_manager.register_env_var("window_height",
[](const String& name, const Context& context)
2013-05-13 14:23:07 +02:00
{ return to_string(context.window().dimensions().line); });
}
void register_registers()
{
RegisterManager& register_manager = RegisterManager::instance();
register_manager.register_dynamic_register('%', [](const Context& context) { return std::vector<String>(1, context.buffer().display_name()); });
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;
});
}
}
void create_local_client(const String& init_command)
2012-10-17 17:49:34 +02:00
{
class LocalNCursesUI : public NCursesUI
{
~LocalNCursesUI()
{
if (not ClientManager::instance().empty() and fork())
{
2013-04-03 18:51:40 +02:00
this->NCursesUI::~NCursesUI();
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(
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
}
void signal_handler(int signal)
2013-01-23 13:46:18 +01:00
{
2013-04-12 01:28:22 +02:00
NCursesUI::abort();
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();
}
int main(int argc, char* argv[])
{
2011-09-02 18:51:20 +02:00
try
{
setlocale(LC_ALL, "");
signal(SIGSEGV, signal_handler);
signal(SIGFPE, signal_handler);
signal(SIGQUIT, signal_handler);
signal(SIGTERM, signal_handler);
std::vector<String> params;
for (size_t i = 1; i < argc; ++i)
params.push_back(argv[i]);
ParametersParser parser(params, { { "c", true }, { "e", true }, { "n", false } });
EventManager event_manager;
String init_command;
if (parser.has_option("e"))
init_command = parser.option_value("e");
if (parser.has_option("c"))
{
try
{
auto client = connect_to(parser.option_value("c"),
std::unique_ptr<UserInterface>{new NCursesUI{}},
init_command);
while (true)
event_manager.handle_next_events();
}
catch (peer_disconnected&)
{
puts("disconnected");
}
return 0;
}
GlobalOptions global_options;
GlobalHooks global_hooks;
ShellManager shell_manager;
CommandManager command_manager;
BufferManager buffer_manager;
RegisterManager register_manager;
HighlighterRegistry highlighter_registry;
FilterRegistry filter_registry;
ColorRegistry color_registry;
ClientManager client_manager;
run_unit_tests();
register_env_vars();
register_registers();
register_commands();
register_highlighters();
register_filters();
write_debug("*** This is the debug buffer, where debug info will be written ***");
2013-05-13 14:23:07 +02:00
write_debug("pid: " + to_string(getpid()));
write_debug("utf-8 test: é á ï");
Server server;
if (not parser.has_option("n")) try
2012-06-12 15:10:33 +02:00
{
Context initialisation_context;
command_manager.execute("source " + runtime_directory() + "/kakrc",
initialisation_context);
2012-06-12 15:10:33 +02:00
}
catch (Kakoune::runtime_error& error)
{
write_debug("error while parsing kakrc: "_str + error.what());
2012-06-12 15:10:33 +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);
}
if (parser.positional_count() != 0) try
{
// 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);
}
}
catch (Kakoune::runtime_error& error)
{
write_debug("error while opening command line files: "_str + error.what());
}
else
new Buffer("*scratch*", Buffer::Flags::None);
create_local_client(init_command);
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)
{
on_assert_failed(("uncaught exception:\n"_str + error.what()).c_str());
2013-02-22 18:45:59 +01:00
return -1;
}
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;
}