From d33554a1ccbc58692d824c29e98d0d576d7afa6d Mon Sep 17 00:00:00 2001 From: Maxime Coste Date: Fri, 6 Jun 2014 00:48:18 +0100 Subject: [PATCH 1/6] Add support for long names for registers We can now access register / with the name slash, * with star, and | with pipe Fixes #23 --- src/command_manager.cc | 4 +--- src/commands.cc | 4 +--- src/main.cc | 2 +- src/register_manager.cc | 21 +++++++++++++++++++-- src/register_manager.hh | 3 ++- 5 files changed, 24 insertions(+), 10 deletions(-) diff --git a/src/command_manager.cc b/src/command_manager.cc index 9ae3cbc5..a99f89b3 100644 --- a/src/command_manager.cc +++ b/src/command_manager.cc @@ -313,9 +313,7 @@ String eval_token(const Token& token, Context& context, return ShellManager::instance().eval(content, context, shell_params, env_vars); case Token::Type::RegisterExpand: - if (content.length() != 1) - throw runtime_error("wrong register name: " + content); - return RegisterManager::instance()[content[0]].values(context)[0]; + return RegisterManager::instance()[content].values(context)[0]; case Token::Type::OptionExpand: return context.options()[content].get_as_string(); case Token::Type::RawEval: diff --git a/src/commands.cc b/src/commands.cc index 988fbeb1..90d72f56 100644 --- a/src/commands.cc +++ b/src/commands.cc @@ -1269,9 +1269,7 @@ const CommandDesc set_register_cmd = { CommandCompleter{}, [](const ParametersParser& parser, Context& context) { - if (parser[0].length() != 1) - throw runtime_error("register names are single character"); - RegisterManager::instance()[parser[0][0]] = memoryview(parser[1]); + RegisterManager::instance()[parser[0]] = memoryview(parser[1]); } }; diff --git a/src/main.cc b/src/main.cc index 46163d7e..346c7cd7 100644 --- a/src/main.cc +++ b/src/main.cc @@ -106,7 +106,7 @@ void register_env_vars() }, { "reg_.+", [](StringView name, const Context& context) -> String - { return RegisterManager::instance()[name[4]].values(context)[0]; } + { return RegisterManager::instance()[name.substr(4_byte)].values(context)[0]; } }, { "client_env_.+", [](StringView name, const Context& context) -> String diff --git a/src/register_manager.cc b/src/register_manager.cc index 0fb17141..f3b6b06e 100644 --- a/src/register_manager.cc +++ b/src/register_manager.cc @@ -1,6 +1,7 @@ #include "register_manager.hh" #include "assert.hh" +#include "id_map.hh" #include "utils.hh" namespace Kakoune @@ -55,9 +56,25 @@ private: RegisterRetriever m_function; }; -Register& RegisterManager::operator[](char reg) +Register& RegisterManager::operator[](StringView reg) { - auto& reg_ptr = m_registers[reg]; + if (reg.length() == 1) + return (*this)[reg[0]]; + + static const id_map reg_names = { + { "slash", '/' }, + { "dquote", '"' }, + { "pipe", '|' } + }; + auto it = reg_names.find(reg); + if (it == reg_names.end()) + throw runtime_error("no such register: " + reg); + return (*this)[it->second]; +} + +Register& RegisterManager::operator[](Codepoint c) +{ + auto& reg_ptr = m_registers[c]; if (not reg_ptr) reg_ptr.reset(new StaticRegister()); return *reg_ptr; diff --git a/src/register_manager.hh b/src/register_manager.hh index 7021c53b..97f84fc0 100644 --- a/src/register_manager.hh +++ b/src/register_manager.hh @@ -16,7 +16,8 @@ using RegisterRetriever = std::function (const Context&)>; class RegisterManager : public Singleton { public: - Register& operator[](char reg); + Register& operator[](StringView reg); + Register& operator[](Codepoint c); void register_dynamic_register(char reg, RegisterRetriever function); protected: From be8f875b4f2a2d87135fb1c40c2b6ed1201bb180 Mon Sep 17 00:00:00 2001 From: Maxime Coste Date: Fri, 6 Jun 2014 13:57:23 +0100 Subject: [PATCH 2/6] Remove -env-var-params support in :def command It is not used --- src/commands.cc | 33 ++++++--------------------------- 1 file changed, 6 insertions(+), 27 deletions(-) diff --git a/src/commands.cc b/src/commands.cc index 90d72f56..ba80c52f 100644 --- a/src/commands.cc +++ b/src/commands.cc @@ -530,18 +530,6 @@ const CommandDesc rm_hook_cmd = { } }; -EnvVarMap params_to_env_var_map(const ParametersParser& parser) -{ - std::unordered_map vars; - char param_name[] = "param0"; - for (size_t i = 0; i < parser.positional_count(); ++i) - { - param_name[sizeof(param_name) - 2] = '0' + i; - vars[param_name] = parser[i]; - } - return vars; -} - std::vector params_to_shell(const ParametersParser& parser) { std::vector vars; @@ -578,14 +566,6 @@ void define_command(const ParametersParser& parser, Context& context) String commands = parser[1]; Command cmd; ParameterDesc desc; - if (parser.has_option("env-params")) - { - desc = ParameterDesc{ SwitchMap{}, ParameterDesc::Flags::SwitchesAsPositional }; - cmd = [=](const ParametersParser& parser, Context& context) { - CommandManager::instance().execute(commands, context, {}, - params_to_env_var_map(parser)); - }; - } if (parser.has_option("shell-params")) { desc = ParameterDesc{ SwitchMap{}, ParameterDesc::Flags::SwitchesAsPositional }; @@ -668,16 +648,15 @@ const CommandDesc define_command_cmd = { nullptr, "def : define a command named corresponding to ", ParameterDesc{ - SwitchMap{ { "env-params", { false, "pass parameters as env variables param0..paramN" } }, - { "shell-params", { false, "pass parameters to each shell escape as $0..$N" } }, - { "allow-override", { false, "allow overriding existing command" } }, + SwitchMap{ { "shell-params", { false, "pass parameters to each shell escape as $0..$N" } }, + { "allow-override", { false, "allow overriding an existing command" } }, + { "hidden", { false, "do not display the command in completion candidates" } }, + { "alias", { true, "define an alias for this command" } }, + { "docstring", { true, "define the documentation string for command" } }, { "file-completion", { false, "complete parameters using filename completion" } }, { "client-completion", { false, "complete parameters using client name completion" } }, { "buffer-completion", { false, "complete parameters using buffer name completion" } }, - { "shell-completion", { true, "complete the parameters using the given shell-script" } }, - { "hidden", { false, "do not display the command as completion candidate" } }, - { "alias", { true, "define an alias for this command" } }, - { "docstring", { true, "set docstring for command" } } }, + { "shell-completion", { true, "complete the parameters using the given shell-script" } } }, ParameterDesc::Flags::None, 2, 2 }, From 732d1c3bd19405bb3144f0b56ec1d10758ab8ec5 Mon Sep 17 00:00:00 2001 From: Maxime Coste Date: Fri, 6 Jun 2014 13:58:35 +0100 Subject: [PATCH 3/6] Improve commands documentation --- src/commands.cc | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/src/commands.cc b/src/commands.cc index ba80c52f..818919a5 100644 --- a/src/commands.cc +++ b/src/commands.cc @@ -483,9 +483,14 @@ CandidateList complete_scope(StringView prefix) const CommandDesc add_hook_cmd = { "hook", nullptr, - "hook : add to be executed on hook in context", + "hook : add in to be executed on hook \n" + "scope can be: \n" + " * global: hook is executed for any buffer or window\n" + " * buffer: hook is executed only for the current buffer\n" + " (and any window for that buffer)\n" + " * window: hook is executed only for the current window\n", ParameterDesc{ - SwitchMap{ { "id", { true, "set hook id" } } }, + SwitchMap{ { "id", { true, "set hook id, see rmhooks" } } }, ParameterDesc::Flags::None, 4, 4 }, CommandFlags::None, @@ -520,7 +525,7 @@ const CommandDesc add_hook_cmd = { const CommandDesc rm_hook_cmd = { "rmhooks", nullptr, - "rmhooks : remove all hooks that whose id is ", + "rmhooks : remove all hooks whose id is ", ParameterDesc{ SwitchMap{}, ParameterDesc::Flags::None, 2, 2 }, CommandFlags::None, CommandCompleter{}, @@ -696,7 +701,8 @@ const CommandDesc echo_cmd = { const CommandDesc debug_cmd = { "debug", nullptr, - "debug ...: write debug informations in debug buffer", + "debug : write some debug informations in the debug buffer\n" + " existing commands: info", ParameterDesc{ SwitchMap{}, ParameterDesc::Flags::SwitchesOnlyAtStart, 1 }, CommandFlags::None, CommandCompleter{}, @@ -794,7 +800,7 @@ const CommandDesc declare_option_cmd = { "decl", nullptr, "decl [value]: declare option of type .\n" - "set its initial value to if given\n" + "set its initial value to if given and if the option did not exist\n" "Available types:\n" " int: integer\n" " bool: boolean (true/false or yes/no)\n" @@ -847,7 +853,6 @@ const CommandDesc declare_option_cmd = { } }; - KeymapManager& get_keymap_manager(const String& scope, Context& context) { if (prefix_match("global", scope)) @@ -1037,7 +1042,7 @@ const CommandDesc exec_string_cmd = { const CommandDesc eval_string_cmd = { "eval", nullptr, - "eval : execute commands as if entered by user", + "eval ...: execute commands as if entered by user", context_wrap_params, CommandFlags::None, CommandCompleter{}, From 052d877ee64a0002dde538ae8f9a11da54f89191 Mon Sep 17 00:00:00 2001 From: Maxime Coste Date: Mon, 9 Jun 2014 13:26:54 +0100 Subject: [PATCH 4/6] Safer implementation of signal handlers in ncurses.cc On recent ncurses implementation on cygwin, the old method provoked freezes. Avoid calling ncurses functions in signal handlers. We still call an unsafe function (EventManager::force_signal)... --- src/ncurses.cc | 51 ++++++++++++++++++++++++++++++++++++-------------- src/ncurses.hh | 2 +- 2 files changed, 38 insertions(+), 15 deletions(-) diff --git a/src/ncurses.cc b/src/ncurses.cc index 67f73951..78e96746 100644 --- a/src/ncurses.cc +++ b/src/ncurses.cc @@ -140,15 +140,19 @@ static void set_color(WINDOW* window, ColorPair colors) } } +static sig_atomic_t resize_pending = 0; + void on_term_resize(int) { - ungetch(KEY_RESIZE); + resize_pending = 1; EventManager::instance().force_signal(0); } +static sig_atomic_t ctrl_c_pending = 0; + void on_sigint(int) { - ungetch(CTRL('c')); + ctrl_c_pending = 1; EventManager::instance().force_signal(0); } @@ -267,6 +271,8 @@ void NCursesUI::draw(const DisplayBuffer& display_buffer, const DisplayLine& status_line, const DisplayLine& mode_line) { + check_resize(); + LineCount line_index = 0; for (const DisplayLine& line : display_buffer.lines()) { @@ -315,8 +321,29 @@ void NCursesUI::draw(const DisplayBuffer& display_buffer, m_dirty = true; } +void NCursesUI::check_resize() +{ + if (resize_pending) + { + int fd = open("/dev/tty", O_RDWR); + winsize ws; + if (ioctl(fd, TIOCGWINSZ, (void*)&ws) == 0) + { + close(fd); + resizeterm(ws.ws_row, ws.ws_col); + update_dimensions(); + } + resize_pending = false; + } +} + bool NCursesUI::is_key_available() { + check_resize(); + + if (ctrl_c_pending) + return true; + timeout(0); const int c = getch(); if (c != ERR) @@ -327,6 +354,14 @@ bool NCursesUI::is_key_available() Key NCursesUI::get_key() { + check_resize(); + + if (ctrl_c_pending) + { + ctrl_c_pending = false; + return ctrl('c'); + } + const int c = getch(); if (c > 0 and c < 27) { @@ -344,18 +379,6 @@ Key NCursesUI::get_key() else return Key::Escape; } - else if (c == KEY_RESIZE) - { - int fd = open("/dev/tty", O_RDWR); - winsize ws; - if (fd != -1 and ioctl(fd, TIOCGWINSZ, (void*)&ws) == 0) - { - close(fd); - resizeterm(ws.ws_row, ws.ws_col); - update_dimensions(); - } - return Key::Invalid; - } else switch (c) { case KEY_BACKSPACE: case 127: return Key::Backspace; diff --git a/src/ncurses.hh b/src/ncurses.hh index 1c40c48f..c8f49e6e 100644 --- a/src/ncurses.hh +++ b/src/ncurses.hh @@ -45,7 +45,7 @@ public: static void abort(); private: - friend void on_term_resize(int); + void check_resize(); void redraw(); void draw_line(const DisplayLine& line, CharCount col_index) const; From cf2609de1ce80e069045bc4f567c79d22345ca39 Mon Sep 17 00:00:00 2001 From: Maxime Coste Date: Mon, 9 Jun 2014 13:44:45 +0100 Subject: [PATCH 5/6] Tweak prompt completion behaviour Always select the common prefix if we just updated the list of completions. The previous behaviour was to ignore it if we had it already typed. Do that only if it was already displayed. --- src/input_handler.cc | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/input_handler.cc b/src/input_handler.cc index 523a1660..ee136e24 100644 --- a/src/input_handler.cc +++ b/src/input_handler.cc @@ -451,12 +451,14 @@ public: const bool reverse = (key == Key::BackTab); CandidateList& candidates = m_completions.candidates; // first try, we need to ask our completer for completions + bool updated_completions = false; if (candidates.empty()) { refresh_completions(CompletionFlags::None); if (candidates.empty()) return; + updated_completions = true; } bool did_prefix = false; if (m_current_completion == -1 and @@ -481,7 +483,10 @@ public: m_current_completion = it - candidates.begin(); CharCount start = line.char_count_to(m_completions.start); - did_prefix = prefix != line.substr(start, m_line_editor.cursor_pos() - start); + // When we just updated completions, select the common + // prefix even if it was the currently entered text. + did_prefix = updated_completions or + prefix != line.substr(start, m_line_editor.cursor_pos() - start); } } if (not did_prefix) @@ -545,6 +550,7 @@ private: { if (not m_completer) return; + m_current_completion = -1; const String& line = m_line_editor.line(); m_completions = m_completer(context(), flags, line, line.byte_count_to(m_line_editor.cursor_pos())); From 4834504508bdbde70c536bad28260cbaabc6ceec Mon Sep 17 00:00:00 2001 From: Maxime Coste Date: Mon, 9 Jun 2014 19:26:53 +0100 Subject: [PATCH 6/6] Do not touch the reference line in copy_indent --- src/normal.cc | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/normal.cc b/src/normal.cc index 3c78e1f2..d176f527 100644 --- a/src/normal.cc +++ b/src/normal.cc @@ -1156,15 +1156,19 @@ void copy_indent(Context& context, int selection) if (selection == 0) selection = context.selections().main_index() + 1; - const String& line = buffer[selections[selection-1].min().line]; + auto ref_line = selections[selection-1].min().line; + const String& line = buffer[ref_line]; auto it = line.begin(); while (it != line.end() and is_horizontal_blank(*it)) ++it; - const String indent{line.begin(), it}; + const StringView indent = line.substr(0_byte, (int)(it-line.begin())); ScopedEdition edition{context}; for (auto& l : lines) { + if (l == ref_line) + continue; + auto& line = buffer[l]; ByteCount i = 0; while (i < line.length() and is_horizontal_blank(line[i]))