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 a7b6c833..94b728a9 100644 --- a/src/commands.cc +++ b/src/commands.cc @@ -484,9 +484,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, @@ -521,7 +526,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{}, @@ -531,18 +536,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; @@ -579,14 +572,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 }; @@ -669,16 +654,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 }, @@ -718,7 +702,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{}, @@ -816,7 +801,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" @@ -869,7 +854,6 @@ const CommandDesc declare_option_cmd = { } }; - KeymapManager& get_keymap_manager(const String& scope, Context& context) { if (prefix_match("global", scope)) @@ -1064,7 +1048,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{}, @@ -1275,9 +1259,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/input_handler.cc b/src/input_handler.cc index d5312305..bfbe12a7 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())); 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/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; diff --git a/src/normal.cc b/src/normal.cc index 0ceaeade..0e4498c5 100644 --- a/src/normal.cc +++ b/src/normal.cc @@ -1078,15 +1078,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])) 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: