diff --git a/doc/pages/changelog.asciidoc b/doc/pages/changelog.asciidoc index 615982dd..4e706c89 100644 --- a/doc/pages/changelog.asciidoc +++ b/doc/pages/changelog.asciidoc @@ -3,6 +3,11 @@ This changelog contains major and/or breaking changes to Kakoune between released versions. +== Development version + +* `%var{...}` now expands to list of strings, `$kak_quoted_...` now work + as expected with these. + == Kakoune v2020.01.16 * Expose history tree through `$kak_history` and diff --git a/src/buffer_utils.cc b/src/buffer_utils.cc index 8c0f3f80..cbead31d 100644 --- a/src/buffer_utils.cc +++ b/src/buffer_utils.cc @@ -228,30 +228,35 @@ auto to_string(Buffer::HistoryId id) return to_string(static_cast(id)); } -String history_as_string(const Vector& history, Quoting quoting) +static String modification_as_string(const Buffer::Modification& modification) { - auto format_history_node = [&](const Buffer::HistoryNode& node) { + return format("{}{}.{}|{}", + modification.type == Buffer::Modification::Type::Insert ? '+' : '-', + modification.coord.line, modification.coord.column, + modification.content->strview()); +} + +Vector history_as_strings(const Vector& history) +{ + Vector res; + for (auto& node : history) + { auto seconds = std::chrono::duration_cast(node.committed.time_since_epoch()); - return format("{} {} {}{}{}", - node.parent, - seconds.count(), - node.redo_child, - node.undo_group.empty() ? "" : " ", - undo_group_as_string(node.undo_group, quoting)); + res.push_back(to_string(node.parent)); + res.push_back(to_string(seconds.count())); + res.push_back(to_string(node.redo_child)); + for (auto& modification : node.undo_group) + res.push_back(modification_as_string(modification)); }; - return join(history | transform(format_history_node), ' ', false); + return res; } -String undo_group_as_string(const Buffer::UndoGroup& undo_group, Quoting quoting) +Vector undo_group_as_strings(const Buffer::UndoGroup& undo_group) { - auto modification_as_string = [&](const Buffer::Modification& modification) { - auto quote = quoter(quoting); - return quote(format("{}{}.{}|{}", - modification.type == Buffer::Modification::Type::Insert ? '+' : '-', - modification.coord.line, modification.coord.column, - modification.content->strview())); - }; - return join(undo_group | transform(modification_as_string), ' ', false); + Vector res; + for (auto& modification : undo_group) + res.push_back(modification_as_string(modification)); + return res; } } diff --git a/src/buffer_utils.hh b/src/buffer_utils.hh index dbbef984..8a9c6d24 100644 --- a/src/buffer_utils.hh +++ b/src/buffer_utils.hh @@ -84,8 +84,8 @@ void reload_file_buffer(Buffer& buffer); void write_to_debug_buffer(StringView str); -String history_as_string(const Vector& history, Quoting quoting); -String undo_group_as_string(const Buffer::UndoGroup& undo_group, Quoting quoting); +Vector history_as_strings(const Vector& history); +Vector undo_group_as_strings(const Buffer::UndoGroup& undo_group); } diff --git a/src/command_manager.cc b/src/command_manager.cc index 82985b5d..e4b57de6 100644 --- a/src/command_manager.cc +++ b/src/command_manager.cc @@ -362,7 +362,10 @@ expand_token(const Token& token, const Context& context, const ShellContext& she auto it = shell_context.env_vars.find(content); if (it != shell_context.env_vars.end()) return {it->value}; - return {ShellManager::instance().get_val(content, context, Quoting::Kakoune)}; + if constexpr (single) + return join(ShellManager::instance().get_val(content, context), false, ' '); + else + return ShellManager::instance().get_val(content, context); } case Token::Type::ArgExpand: { diff --git a/src/main.cc b/src/main.cc index 79e0793d..f16e9f9a 100644 --- a/src/main.cc +++ b/src/main.cc @@ -156,179 +156,193 @@ String config_directory() return format("{}/.config/kak", homedir()); } +static auto main_sel_first(const SelectionList& selections) +{ + auto beg = &*selections.begin(), end = &*selections.end(); + auto main = beg + selections.main_index(); + using View = ConstArrayView; + return concatenated(View{main, end}, View{beg, main}); +} + static const EnvVarDesc builtin_env_vars[] = { { "bufname", false, - [](StringView name, const Context& context, Quoting quoting) -> String - { return context.buffer().display_name(); } + [](StringView name, const Context& context) -> Vector + { return {context.buffer().display_name()}; } }, { "buffile", false, - [](StringView name, const Context& context, Quoting quoting) -> String - { return context.buffer().name(); } + [](StringView name, const Context& context) -> Vector + { return {context.buffer().name()}; } }, { "buflist", false, - [](StringView name, const Context& context, Quoting quoting) - { return join(BufferManager::instance() | - transform(&Buffer::display_name) | transform(quoter(quoting)), ' ', false); } + [](StringView name, const Context& context) -> Vector + { return BufferManager::instance() | transform(&Buffer::display_name) | gather(); } }, { "buf_line_count", false, - [](StringView name, const Context& context, Quoting quoting) -> String - { return to_string(context.buffer().line_count()); } + [](StringView name, const Context& context) -> Vector + { return {to_string(context.buffer().line_count())}; } }, { "timestamp", false, - [](StringView name, const Context& context, Quoting quoting) -> String - { return to_string(context.buffer().timestamp()); } + [](StringView name, const Context& context) -> Vector + { return {to_string(context.buffer().timestamp())}; } }, { "history_id", false, - [](StringView name, const Context& context, Quoting quoting) -> String - { return to_string((size_t)context.buffer().current_history_id()); } + [](StringView name, const Context& context) -> Vector + { return {to_string((size_t)context.buffer().current_history_id())}; } }, { "selection", false, - [](StringView name, const Context& context, Quoting quoting) + [](StringView name, const Context& context) -> Vector { const Selection& sel = context.selections().main(); - return content(context.buffer(), sel); } + return {content(context.buffer(), sel)}; } }, { "selections", false, - [](StringView name, const Context& context, Quoting quoting) - { return join(context.selections_content() | transform(quoter(quoting)), ' ', false); } + [](StringView name, const Context& context) -> Vector + { return context.selections_content(); } }, { "runtime", false, - [](StringView name, const Context& context, Quoting quoting) - { return runtime_directory(); } + [](StringView name, const Context& context) -> Vector + { return {runtime_directory()}; } }, { "config", false, - [](StringView name, const Context& context, Quoting quoting) - { return config_directory(); } + [](StringView name, const Context& context) -> Vector + { return {config_directory()}; } }, { "version", false, - [](StringView name, const Context& context, Quoting quoting) -> String - { return version; } + [](StringView name, const Context& context) -> Vector + { return {version}; } }, { "opt_", true, - [](StringView name, const Context& context, Quoting quoting) - { return context.options()[name.substr(4_byte)].get_as_string(quoting); } + [](StringView name, const Context& context) -> Vector + { return context.options()[name.substr(4_byte)].get_as_strings(); } }, { "main_reg_", true, - [](StringView name, const Context& context, Quoting quoting) - { return context.main_sel_register_value(name.substr(9_byte)).str(); } + [](StringView name, const Context& context) -> Vector + { return {context.main_sel_register_value(name.substr(9_byte)).str()}; } }, { "reg_", true, - [](StringView name, const Context& context, Quoting quoting) - { return join(RegisterManager::instance()[name.substr(4_byte)].get(context) | - transform(quoter(quoting)), ' ', false); } + [](StringView name, const Context& context) + { return RegisterManager::instance()[name.substr(4_byte)].get(context) | + gather>(); } }, { "client_env_", true, - [](StringView name, const Context& context, Quoting quoting) - { return context.client().get_env_var(name.substr(11_byte)).str(); } + [](StringView name, const Context& context) -> Vector + { return {context.client().get_env_var(name.substr(11_byte)).str()}; } }, { "session", false, - [](StringView name, const Context& context, Quoting quoting) -> String - { return Server::instance().session(); } + [](StringView name, const Context& context) -> Vector + { return {Server::instance().session()}; } }, { "client", false, - [](StringView name, const Context& context, Quoting quoting) -> String - { return context.name(); } + [](StringView name, const Context& context) -> Vector + { return {context.name()}; } }, { "client_pid", false, - [](StringView name, const Context& context, Quoting quoting) -> String - { return to_string(context.client().pid()); } + [](StringView name, const Context& context) -> Vector + { return {to_string(context.client().pid())}; } }, { "client_list", false, - [](StringView name, const Context& context, Quoting quoting) -> String - { return join(ClientManager::instance() | + [](StringView name, const Context& context) -> Vector + { return ClientManager::instance() | transform([](const std::unique_ptr& c) -> const String& - { return c->context().name(); }), ' ', false); } + { return c->context().name(); }) | gather(); } }, { "modified", false, - [](StringView name, const Context& context, Quoting quoting) -> String - { return context.buffer().is_modified() ? "true" : "false"; } + [](StringView name, const Context& context) -> Vector + { return {context.buffer().is_modified() ? "true" : "false"}; } }, { "cursor_line", false, - [](StringView name, const Context& context, Quoting quoting) -> String - { return to_string(context.selections().main().cursor().line + 1); } + [](StringView name, const Context& context) -> Vector + { return {to_string(context.selections().main().cursor().line + 1)}; } }, { "cursor_column", false, - [](StringView name, const Context& context, Quoting quoting) -> String - { return to_string(context.selections().main().cursor().column + 1); } + [](StringView name, const Context& context) -> Vector + { return {to_string(context.selections().main().cursor().column + 1)}; } }, { "cursor_char_value", false, - [](StringView name, const Context& context, Quoting quoting) -> String + [](StringView name, const Context& context) -> Vector { auto coord = context.selections().main().cursor(); auto& buffer = context.buffer(); - return to_string((size_t)utf8::codepoint(buffer.iterator_at(coord), buffer.end())); } + return {to_string((size_t)utf8::codepoint(buffer.iterator_at(coord), buffer.end()))}; } }, { "cursor_char_column", false, - [](StringView name, const Context& context, Quoting quoting) -> String + [](StringView name, const Context& context) -> Vector { auto coord = context.selections().main().cursor(); - return to_string(context.buffer()[coord.line].char_count_to(coord.column) + 1); } + return {to_string(context.buffer()[coord.line].char_count_to(coord.column) + 1)}; } }, { "cursor_display_column", false, - [](StringView name, const Context& context, Quoting quoting) -> String + [](StringView name, const Context& context) -> Vector { auto coord = context.selections().main().cursor(); - return to_string(get_column(context.buffer(), - context.options()["tabstop"].get(), - coord) + 1); } + return {to_string(get_column(context.buffer(), + context.options()["tabstop"].get(), + coord) + 1)}; } }, { "cursor_byte_offset", false, - [](StringView name, const Context& context, Quoting quoting) -> String + [](StringView name, const Context& context) -> Vector { auto cursor = context.selections().main().cursor(); - return to_string(context.buffer().distance({0,0}, cursor)); } + return {to_string(context.buffer().distance({0,0}, cursor))}; } }, { "selection_desc", false, - [](StringView name, const Context& context, Quoting quoting) - { return selection_to_string(ColumnType::Byte, context.buffer(), - context.selections().main()); } + [](StringView name, const Context& context) -> Vector + { return {selection_to_string(ColumnType::Byte, context.buffer(), context.selections().main())}; } }, { "selections_desc", false, - [](StringView name, const Context& context, Quoting quoting) - { return selection_list_to_string(ColumnType::Byte, context.selections()); } + [](StringView name, const Context& context) -> Vector + { return main_sel_first(context.selections()) | + transform([&buffer=context.buffer()](const Selection& sel) { + return selection_to_string(ColumnType::Byte, buffer, sel); + }) | gather(); } }, { "selections_char_desc", false, - [](StringView name, const Context& context, Quoting quoting) - { return selection_list_to_string(ColumnType::Codepoint, context.selections()); } + [](StringView name, const Context& context) -> Vector + { return main_sel_first(context.selections()) | + transform([&buffer=context.buffer()](const Selection& sel) { + return selection_to_string(ColumnType::Codepoint, buffer, sel); + }) | gather(); } }, { "selections_display_column_desc", false, - [](StringView name, const Context& context, Quoting quoting) - { return selection_list_to_string(ColumnType::DisplayColumn, - context.selections(), - context.options()["tabstop"].get()); } + [](StringView name, const Context& context) -> Vector + { return main_sel_first(context.selections()) | + transform([&buffer=context.buffer(), tabstop=context.options()["tabstop"].get()](const Selection& sel) { + return selection_to_string(ColumnType::DisplayColumn, buffer, sel, tabstop); + }) | gather(); } }, { "selection_length", false, - [](StringView name, const Context& context, Quoting quoting) -> String - { return to_string(char_length(context.buffer(), context.selections().main())); } + [](StringView name, const Context& context) -> Vector + { return {to_string(char_length(context.buffer(), context.selections().main()))}; } }, { "selections_length", false, - [](StringView name, const Context& context, Quoting quoting) - { return join(context.selections() | - transform([&](const Selection& s) - { return to_string(char_length(context.buffer(), s)); }), ' ', false); } + [](StringView name, const Context& context) -> Vector + { return context.selections() | + transform([&](const Selection& s) { + return to_string(char_length(context.buffer(), s)); + }) | gather>(); } }, { "window_width", false, - [](StringView name, const Context& context, Quoting quoting) -> String - { return to_string(context.window().dimensions().column); } + [](StringView name, const Context& context) -> Vector + { return {to_string(context.window().dimensions().column)}; } }, { "window_height", false, - [](StringView name, const Context& context, Quoting quoting) -> String - { return to_string(context.window().dimensions().line); } + [](StringView name, const Context& context) -> Vector + { return {to_string(context.window().dimensions().line)}; } }, { "user_modes", false, - [](StringView name, const Context& context, Quoting quoting) -> String - { return join(context.keymaps().user_modes(), ' ', false); } + [](StringView name, const Context& context) -> Vector + { return context.keymaps().user_modes(); } }, { "window_range", false, - [](StringView name, const Context& context, Quoting quoting) -> String + [](StringView name, const Context& context) -> Vector { auto setup = context.window().compute_display_setup(context); - return format("{} {} {} {}", setup.window_pos.line, setup.window_pos.column, - setup.window_range.line, setup.window_range.column); + return {format("{} {} {} {}", setup.window_pos.line, setup.window_pos.column, + setup.window_range.line, setup.window_range.column)}; } }, { "history", false, - [](StringView name, const Context& context, Quoting quoting) -> String - { return history_as_string(context.buffer().history(), quoting); } + [](StringView name, const Context& context) -> Vector + { return history_as_strings(context.buffer().history()); } }, { "uncommitted_modifications", false, - [](StringView name, const Context& context, Quoting quoting) -> String - { return undo_group_as_string(context.buffer().current_undo_group(), quoting); } + [](StringView name, const Context& context) -> Vector + { return undo_group_as_strings(context.buffer().current_undo_group()); } } }; diff --git a/src/shell_manager.cc b/src/shell_manager.cc index 9c0604dd..8d70dd1c 100644 --- a/src/shell_manager.cc +++ b/src/shell_manager.cc @@ -152,8 +152,8 @@ Vector generate_env(StringView cmdline, const Context& context, const Sh auto var_it = shell_context.env_vars.find(name); try { - const String& value = var_it != shell_context.env_vars.end() ? - var_it->value : ShellManager::instance().get_val(name, context, quoting); + String value = var_it != shell_context.env_vars.end() ? + var_it->value : join(ShellManager::instance().get_val(name, context) | transform(quoter(quoting)), ' ', false); StringView quoted{match[1].first, match[1].second}; kak_env.push_back(format("kak_{}{}={}", quoted, name, value)); @@ -327,7 +327,7 @@ std::pair ShellManager::eval( return { std::move(stdout_contents), WIFEXITED(status) ? WEXITSTATUS(status) : -1 }; } -String ShellManager::get_val(StringView name, const Context& context, Quoting quoting) const +Vector ShellManager::get_val(StringView name, const Context& context) const { auto env_var = find_if(m_env_vars, [name](const EnvVarDesc& desc) { return desc.prefix ? prefix_match(name, desc.str) : name == desc.str; @@ -336,7 +336,7 @@ String ShellManager::get_val(StringView name, const Context& context, Quoting qu if (env_var == m_env_vars.end()) throw runtime_error("no such env var: " + name); - return env_var->func(name, context, quoting); + return env_var->func(name, context); } CandidateList ShellManager::complete_env_var(StringView prefix, diff --git a/src/shell_manager.hh b/src/shell_manager.hh index ebfe3cca..7dc00f83 100644 --- a/src/shell_manager.hh +++ b/src/shell_manager.hh @@ -18,11 +18,9 @@ struct ShellContext EnvVarMap env_vars; }; -enum class Quoting; - struct EnvVarDesc { - using Retriever = String (*)(StringView name, const Context&, Quoting quoting); + using Retriever = Vector (*)(StringView name, const Context&); StringView str; bool prefix; @@ -46,7 +44,7 @@ public: Flags flags = Flags::WaitForStdout, const ShellContext& shell_context = {}); - String get_val(StringView name, const Context& context, Quoting quoting) const; + Vector get_val(StringView name, const Context& context) const; CandidateList complete_env_var(StringView prefix, ByteCount cursor_pos) const; diff --git a/test/compose/backward-search/kak_quoted_selections_desc b/test/compose/backward-search/kak_selections_desc similarity index 100% rename from test/compose/backward-search/kak_quoted_selections_desc rename to test/compose/backward-search/kak_selections_desc diff --git a/test/compose/history/kak_quoted_history b/test/compose/history/kak_quoted_history index acd0fedc..5265a633 100644 --- a/test/compose/history/kak_quoted_history +++ b/test/compose/history/kak_quoted_history @@ -1 +1 @@ -- $timestamp 1 0 $timestamp - '+0.5|m' '+0.6|i' '+0.7|d' '+0.8|d' '+0.9|l' '+0.10|e' '-0.8|dle' +'-' '$timestamp' '1' '0' '$timestamp' '-' '+0.5|m' '+0.6|i' '+0.7|d' '+0.8|d' '+0.9|l' '+0.10|e' '-0.8|dle' diff --git a/test/compose/history/rc b/test/compose/history/rc index 7f83692e..da4c3cd7 100644 --- a/test/compose/history/rc +++ b/test/compose/history/rc @@ -1,6 +1,6 @@ # Make our expansion have a predictable timestamp hook global ClientClose .* %{ nop %sh{ - kak -f 'ghfec$timestamp2fec$timestamp' kak_quoted_history + kak -f 'ghflwc$timestamp3flwc$timestamp' kak_quoted_history } } diff --git a/test/indent/c-family/indent-if-brace/kak_quoted_selections_desc b/test/indent/c-family/indent-if-brace/kak_selections_desc similarity index 100% rename from test/indent/c-family/indent-if-brace/kak_quoted_selections_desc rename to test/indent/c-family/indent-if-brace/kak_selections_desc diff --git a/test/normal/concat-paste-line/kak_quoted_selections_desc b/test/normal/concat-paste-line/kak_selections_desc similarity index 100% rename from test/normal/concat-paste-line/kak_quoted_selections_desc rename to test/normal/concat-paste-line/kak_selections_desc diff --git a/test/normal/repeat-select/repeat-end-paragraph/kak_quoted_selections_desc b/test/normal/repeat-select/repeat-end-paragraph/kak_selections_desc similarity index 100% rename from test/normal/repeat-select/repeat-end-paragraph/kak_quoted_selections_desc rename to test/normal/repeat-select/repeat-end-paragraph/kak_selections_desc diff --git a/test/normal/repeat-select/repeat-find-char/kak_quoted_selections_desc b/test/normal/repeat-select/repeat-find-char/kak_selections_desc similarity index 100% rename from test/normal/repeat-select/repeat-find-char/kak_quoted_selections_desc rename to test/normal/repeat-select/repeat-find-char/kak_selections_desc diff --git a/test/regression/0-open-below-should-not-move-cursor-on-eol/kak_quoted_selections_desc b/test/regression/0-open-below-should-not-move-cursor-on-eol/kak_selections_desc similarity index 100% rename from test/regression/0-open-below-should-not-move-cursor-on-eol/kak_quoted_selections_desc rename to test/regression/0-open-below-should-not-move-cursor-on-eol/kak_selections_desc diff --git a/test/regression/1504-assertion-on-incorrect-pipe-use/kak_quoted_selections_desc b/test/regression/1504-assertion-on-incorrect-pipe-use/kak_selections_desc similarity index 100% rename from test/regression/1504-assertion-on-incorrect-pipe-use/kak_quoted_selections_desc rename to test/regression/1504-assertion-on-incorrect-pipe-use/kak_selections_desc diff --git a/test/regression/1580-A-not-moving-to-eol/kak_quoted_selections_desc b/test/regression/1580-A-not-moving-to-eol/kak_selections_desc similarity index 100% rename from test/regression/1580-A-not-moving-to-eol/kak_quoted_selections_desc rename to test/regression/1580-A-not-moving-to-eol/kak_selections_desc diff --git a/test/regression/1902-regex-lookbehind-ignore-before-current-pos/kak_quoted_selections_desc b/test/regression/1902-regex-lookbehind-ignore-before-current-pos/kak_selections_desc similarity index 100% rename from test/regression/1902-regex-lookbehind-ignore-before-current-pos/kak_quoted_selections_desc rename to test/regression/1902-regex-lookbehind-ignore-before-current-pos/kak_selections_desc diff --git a/test/regression/2078-assert-on-restoring-invalid-selections/kak_quoted_selections_desc b/test/regression/2078-assert-on-restoring-invalid-selections/kak_selections_desc similarity index 100% rename from test/regression/2078-assert-on-restoring-invalid-selections/kak_quoted_selections_desc rename to test/regression/2078-assert-on-restoring-invalid-selections/kak_selections_desc diff --git a/test/regression/2420-discrepancy-in-star-behaviour/kak_quoted_selections_desc b/test/regression/2420-discrepancy-in-star-behaviour/kak_selections_desc similarity index 100% rename from test/regression/2420-discrepancy-in-star-behaviour/kak_quoted_selections_desc rename to test/regression/2420-discrepancy-in-star-behaviour/kak_selections_desc diff --git a/test/regression/595-smart-search-unicode/kak_quoted_selections_desc b/test/regression/595-smart-search-unicode/kak_selections_desc similarity index 100% rename from test/regression/595-smart-search-unicode/kak_quoted_selections_desc rename to test/regression/595-smart-search-unicode/kak_selections_desc diff --git a/test/regression/699-to-eol-from-eol/kak_quoted_selections_desc b/test/regression/699-to-eol-from-eol/kak_selections_desc similarity index 100% rename from test/regression/699-to-eol-from-eol/kak_quoted_selections_desc rename to test/regression/699-to-eol-from-eol/kak_selections_desc diff --git a/test/shell/quoted-range/out b/test/shell/quoted-range/out index 2488b23f..0637f152 100644 --- a/test/shell/quoted-range/out +++ b/test/shell/quoted-range/out @@ -1 +1 @@ -1 '1.1,1.1|a b' '1.2,1.2|b c' +'1' '1.1,1.1|a b' '1.2,1.2|b c'