diff --git a/colors/black-on-white.kak b/colors/black-on-white.kak new file mode 100644 index 00000000..f5e5b64e --- /dev/null +++ b/colors/black-on-white.kak @@ -0,0 +1,53 @@ +# Black-on-bright-white colorscheme for minimal distraction & maximal contrast. +# Works well with e-ink screens. + +# For Code +face global value black +face global type black +face global variable black +face global module black +face global function black +face global string black +face global keyword black +face global operator black +face global attribute black +face global comment black +face global documentation comment +face global meta black +face global builtin black + +# For markup +face global title black +face global header black +face global mono black +face global block black +face global link black +face global bullet black +face global list black + +# builtin faces +face global Default black,bright-white +face global PrimarySelection black,rgb:cccccc+fg +face global SecondarySelection black,rgb:e0e0e0+fg +face global PrimaryCursor bright-white,black+fg +face global SecondaryCursor bright-white,rgb:777777+fg +face global PrimaryCursorEol black,rgb:777777+fg +face global SecondaryCursorEol black,rgb:aaaaaa+fg +face global LineNumbers black,bright-white +face global LineNumberCursor bright-white,black +face global MenuForeground bright-white,black+fg +face global MenuBackground black,rgb:e0e0e0+fg +face global MenuInfo black # Overridden by MenuForeground and MenuBackground +face global Information black,rgb:e0e0e0 +face global Error bright-white,black +face global DiagnosticError black +face global DiagnosticWarning black +face global StatusLine black,bright-white +face global StatusLineMode black,bright-white +face global StatusLineInfo black,bright-white +face global StatusLineValue black,bright-white +face global StatusCursor bright-white,black +face global Prompt bright-white,black +face global MatchingChar black,bright-white+b +face global Whitespace black,bright-white+fd +face global BufferPadding black,bright-white diff --git a/doc/pages/buffers.asciidoc b/doc/pages/buffers.asciidoc index fc750c7b..ae7c04e9 100644 --- a/doc/pages/buffers.asciidoc +++ b/doc/pages/buffers.asciidoc @@ -59,5 +59,5 @@ for examples. When the write end of the fifo is closed, the buffer becomes an ordinary <>. When the buffer is deleted, Kakoune closes the read end of the fifo, so any program writing to it -will receive `SIGPIPE`. This is useful as it permits to stop the writing +will receive `SIGPIPE`. This is useful as it permits stopping the writing program when the buffer is deleted. diff --git a/doc/pages/expansions.asciidoc b/doc/pages/expansions.asciidoc index d2374398..674f5707 100644 --- a/doc/pages/expansions.asciidoc +++ b/doc/pages/expansions.asciidoc @@ -417,7 +417,7 @@ The following expansions are supported (with required context _in italics_): _in window scope_ + list of coordinates and dimensions of the buffer-space available on the current window, in the following format: - ` ` + ` ` Values in the above list that do not mention a context are available everywhere. diff --git a/doc/pages/highlighters.asciidoc b/doc/pages/highlighters.asciidoc index ffd15fa1..098089dd 100644 --- a/doc/pages/highlighters.asciidoc +++ b/doc/pages/highlighters.asciidoc @@ -113,7 +113,7 @@ add-highlighter window/ regex //\h*(TODO:)[^\n]* 0:cyan 1:yellow,red capture_id can be either the capture number, or its name if a named capture is used in the regex (See - <>) + <>) *dynregex* : ...:: similar to regex, but expand (like a command parameter would) the diff --git a/doc/pages/keys.asciidoc b/doc/pages/keys.asciidoc index e117b532..e26d6925 100644 --- a/doc/pages/keys.asciidoc +++ b/doc/pages/keys.asciidoc @@ -817,7 +817,7 @@ The following keys are recognized by this mode to help with editing **:: expand the typed expansions in currently entered text - (See <>) + (See <>) **, **:: escape to normal mode for a single command diff --git a/doc/pages/modes.asciidoc b/doc/pages/modes.asciidoc index 0424509c..754dda5b 100644 --- a/doc/pages/modes.asciidoc +++ b/doc/pages/modes.asciidoc @@ -85,7 +85,7 @@ See prompt commands <>. Mode entered with ``, `` and various combinations of `[]{}` keys. It aims at crafting semantic selections, often called *text-objects*. -See object commands <>. +See object commands <>. === User mode diff --git a/rc/detection/file.kak b/rc/detection/file.kak index 99e0b05b..c7073865 100644 --- a/rc/detection/file.kak +++ b/rc/detection/file.kak @@ -22,5 +22,5 @@ define-command -hidden file-detection %{ evaluate-commands %sh{ fi } } -hook global BufOpenFile .* file-detection -hook global BufWritePost .* file-detection +hook -group file-detection global BufOpenFile .* file-detection +hook -group file-detection global BufWritePost .* file-detection diff --git a/rc/filetype/go.kak b/rc/filetype/go.kak index a0a33ae4..c993c889 100644 --- a/rc/filetype/go.kak +++ b/rc/filetype/go.kak @@ -56,8 +56,8 @@ evaluate-commands %sh{ keywords='break default func interface select case defer go map struct chan else goto package switch const fallthrough if range type continue for import return var' - types='bool byte chan complex128 complex64 error float32 float64 int int16 int32 - int64 int8 interface intptr map rune string struct uint uint16 uint32 uint64 uint8' + types='any bool byte chan comparable complex128 complex64 error float32 float64 int int16 int32 + int64 int8 interface intptr map rune string struct uint uint16 uint32 uint64 uint8 uintptr' values='false true nil iota' functions='append cap close complex copy delete imag len make new panic print println real recover' diff --git a/rc/filetype/i3.kak b/rc/filetype/i3.kak index 6b37eb96..73c3ed5f 100644 --- a/rc/filetype/i3.kak +++ b/rc/filetype/i3.kak @@ -63,7 +63,7 @@ add-highlighter shared/i3/single_string/ regex "#[0-9a-fA-F]{6}" 0:value # attributes add-highlighter shared/i3/code/ regex "client\.(background|statusline|background|separator|statusline)" 1:attribute -add-highlighter shared/i3/code/ regex "client\.(focused_inactive|focused|unfocused|urgent|inactive_workspace|urgent_workspace|focused_workspace|active_workspace|placeholder)" 1:attribute +add-highlighter shared/i3/code/ regex "client\.(focused_inactive|focused_tab_title|focused|unfocused|urgent|inactive_workspace|urgent_workspace|focused_workspace|active_workspace|placeholder)" 1:attribute # Commands # ‾‾‾‾‾‾‾‾ diff --git a/rc/tools/autorestore.kak b/rc/tools/autorestore.kak index d3e60994..52febe3a 100644 --- a/rc/tools/autorestore.kak +++ b/rc/tools/autorestore.kak @@ -39,7 +39,7 @@ define-command autorestore-restore-buffer \ ## Replace the content of the buffer with the content of the backup file echo -debug Restoring file: ${newer} - execute-keys -draft %{ %d!cat\"${newer}\"d } + execute-keys -draft %{%d!cat\"${newer}\"jd} ## If the backup file has to be removed, issue the command once ## the current buffer has been saved diff --git a/rc/tools/format.kak b/rc/tools/format.kak index 72d71430..2435af0c 100644 --- a/rc/tools/format.kak +++ b/rc/tools/format.kak @@ -14,7 +14,8 @@ define-command format-selections -docstring "Format the selections individually" echo "fail 'The option ''formatcmd'' must be set'" fi } - evaluate-commands -draft -no-hooks -save-regs '|' %{ + evaluate-commands -draft -no-hooks -save-regs 'e|' %{ + set-register e nop set-register '|' %{ format_in="$(mktemp "${TMPDIR:-/tmp}"/kak-formatter.XXXXXX)" format_out="$(mktemp "${TMPDIR:-/tmp}"/kak-formatter.XXXXXX)" @@ -24,12 +25,13 @@ define-command format-selections -docstring "Format the selections individually" if [ $? -eq 0 ]; then cat "$format_out" else - printf 'eval -client %s %%{ fail formatter returned an error %s }\n' "$kak_client" "$?" | kak -p "$kak_session" + echo "set-register e fail formatter returned an error (exit code $?)" >"$kak_command_fifo" cat "$format_in" fi rm -f "$format_in" "$format_out" } execute-keys '|' + %reg{e} } } diff --git a/rc/tools/git.kak b/rc/tools/git.kak index 3b53ebd1..e65374c6 100644 --- a/rc/tools/git.kak +++ b/rc/tools/git.kak @@ -157,7 +157,7 @@ define-command -params 1.. \ update_diff() { ( cd_bufdir - git --no-pager diff -U0 "$kak_buffile" | perl -e ' + git --no-pager diff --no-ext-diff -U0 "$kak_buffile" | perl -e ' $flags = $ENV{"kak_timestamp"}; foreach $line () { if ($line =~ /@@ -(\d+)(?:,(\d+))? \+(\d+)(?:,(\d+))?/) { diff --git a/src/commands.cc b/src/commands.cc index 2b419e76..3cfd8b73 100644 --- a/src/commands.cc +++ b/src/commands.cc @@ -2086,14 +2086,7 @@ const CommandDesc execute_keys_cmd = { ScopedSetBool disable_keymaps(context.keymaps_disabled(), not parser.get_switch("with-maps")); ScopedSetBool disable_hoooks(context.hooks_disabled(), not parser.get_switch("with-hooks")); - KeyList keys; - for (auto& param : parser) - { - KeyList param_keys = parse_keys(param); - keys.insert(keys.end(), param_keys.begin(), param_keys.end()); - } - - for (auto& key : keys) + for (auto& key : parser | transform(parse_keys) | flatten()) context.input_handler().handle_key(key); }); } diff --git a/src/constexpr_utils.hh b/src/constexpr_utils.hh index d9ba2abb..f83faacb 100644 --- a/src/constexpr_utils.hh +++ b/src/constexpr_utils.hh @@ -28,6 +28,9 @@ struct Array T m_data[N]; }; +template requires (std::is_same_v and ...) +Array(T, U...) -> Array; + template constexpr Array make_array(const T (&data)[N], std::index_sequence) { diff --git a/src/input_handler.cc b/src/input_handler.cc index 8adbbcee..9b1857a8 100644 --- a/src/input_handler.cc +++ b/src/input_handler.cc @@ -1694,18 +1694,11 @@ InputHandler::ScopedForceNormal::~ScopedForceNormal() if (not m_mode) return; - kak_assert(m_handler.m_mode_stack.size() > 1); - if (m_mode == m_handler.m_mode_stack.back().get()) m_handler.pop_mode(m_mode); - else - { - auto it = find_if(m_handler.m_mode_stack, - [this](const RefPtr& m) - { return m.get() == m_mode; }); - kak_assert(it != m_handler.m_mode_stack.end()); + else if (auto it = find(m_handler.m_mode_stack, m_mode); + it != m_handler.m_mode_stack.end()) m_handler.m_mode_stack.erase(it); - } } static bool is_valid(Key key) diff --git a/src/normal.cc b/src/normal.cc index 15f07211..25df66b3 100644 --- a/src/normal.cc +++ b/src/normal.cc @@ -1,7 +1,5 @@ #include "normal.hh" -#include - #include "buffer.hh" #include "buffer_manager.hh" #include "buffer_utils.hh" @@ -31,8 +29,6 @@ namespace Kakoune { -using namespace std::placeholders; - enum class SelectMode { Replace, @@ -1302,7 +1298,15 @@ void select_object(Context& context, NormalParams params) auto obj_it = find(selectors | transform(&ObjectType::key), key).base(); if (obj_it != std::end(selectors)) return select_and_set_last( - context, std::bind(obj_it->func, _1, _2, count, flags)); + context, [=](Context& context, Selection& sel) { return obj_it->func(context, sel, count, flags); }); + + static constexpr auto regex_selector = [=](StringView open, StringView close, int count) { + return [open=Regex{open, RegexCompileFlags::Backward}, + close=Regex{close, RegexCompileFlags::Backward}, + count](Context& context, Selection& sel) { + return select_surrounding(context, sel, open, close, count, flags); + }; + }; if (key == 'c') { @@ -1323,17 +1327,13 @@ void select_object(Context& context, NormalParams params) struct error : runtime_error { error(size_t) : runtime_error{"desc parsing failed, expected ,"} {} }; - auto params = cmdline | split(',', '\\') | - transform(unescape<',', '\\'>) | static_gather(); - + auto params = cmdline | split(',', '\\') + | transform(unescape<',', '\\'>) + | static_gather(); if (params[0].empty() or params[1].empty()) throw error{0}; - select_and_set_last( - context, std::bind(select_surrounding, _1, _2, - Regex{params[0], RegexCompileFlags::Backward}, - Regex{params[1], RegexCompileFlags::Backward}, - count, flags)); + select_and_set_last(context, regex_selector(params[0], params[1], count)); }); return; } @@ -1350,10 +1350,10 @@ void select_object(Context& context, NormalParams params) return; } - static constexpr struct SurroundingPair + static constexpr struct { - char opening; - char closing; + char open; + char close; char name; } surrounding_pairs[] = { { '(', ')', 'b' }, @@ -1364,28 +1364,15 @@ void select_object(Context& context, NormalParams params) { '\'', '\'', 'q' }, { '`', '`', 'g' }, }; - auto pair_it = find_if(surrounding_pairs, - [key](const SurroundingPair& s) { - return key == s.opening or key == s.closing or - (s.name != 0 and key == s.name); - }); - if (pair_it != std::end(surrounding_pairs)) + if (auto it = find_if(surrounding_pairs, [key](auto s) { return key == s.open or key == s.close or key == s.name; }); + it != std::end(surrounding_pairs)) return select_and_set_last( - context, std::bind(select_surrounding, _1, _2, - Regex{format("\\Q{}", pair_it->opening), RegexCompileFlags::Backward}, - Regex{format("\\Q{}", pair_it->closing), RegexCompileFlags::Backward}, - count, flags)); + context, regex_selector(format("\\Q{}", it->open), format("\\Q{}", it->close), count)); - if (not key.codepoint()) - return; - - const Codepoint cp = *key.codepoint(); - if (is_punctuation(cp, {})) + if (auto cp = key.codepoint(); cp and is_punctuation(*cp, {})) { - auto re = Regex{"\\Q" + to_string(cp), RegexCompileFlags::Backward}; - return select_and_set_last( - context, std::bind(select_surrounding, _1, _2, - re, re, count, flags)); + auto re = "\\Q" + to_string(*cp); + return select_and_set_last(context, regex_selector(re, re, count)); } }, get_title(), build_autoinfo_for_mapping(context, KeymapMode::Object, @@ -1537,11 +1524,10 @@ void select_to_next_char(Context& context, NormalParams params) constexpr auto new_flags = flags & SelectFlags::Extend ? SelectMode::Extend : SelectMode::Replace; select_and_set_last( - context, - std::bind(flags & SelectFlags::Reverse ? select_to_reverse - : select_to, - _1, _2, *cp, params.count, - flags & SelectFlags::Inclusive)); + context, [cp=*cp, count=params.count] (auto& context, auto& sel) { + auto& func = flags & SelectFlags::Reverse ? select_to_reverse : select_to; + return func(context, sel, cp, count, flags & SelectFlags::Inclusive); + }); }, get_title(), "enter char to select to"); } diff --git a/src/ranges.cc b/src/ranges.cc index 11885ad1..b8fb6703 100644 --- a/src/ranges.cc +++ b/src/ranges.cc @@ -7,24 +7,29 @@ namespace Kakoune { UnitTest test_ranges{[] { - auto check_equal = [](auto&& container, ConstArrayView expected) { + using Strs = ConstArrayView; + auto check_equal = [](auto&& container, auto&& expected) { kak_assert(std::equal(container.begin(), container.end(), expected.begin(), expected.end())); }; - check_equal("a,b,c"_sv | split(','), {"a", "b", "c"}); - check_equal(",b,c"_sv | split(','), {"", "b", "c"}); - check_equal(",b,"_sv | split(','), {"", "b", ""}); - check_equal(","_sv | split(','), {"", ""}); - check_equal(""_sv | split(','), {}); + check_equal("a,b,c"_sv | split(','), Strs{"a", "b", "c"}); + check_equal(",b,c"_sv | split(','), Strs{"", "b", "c"}); + check_equal(",b,"_sv | split(','), Strs{"", "b", ""}); + check_equal(","_sv | split(','), Strs{"", ""}); + check_equal(""_sv | split(','), Strs{}); - check_equal("a,b,c,"_sv | split_after(','), {"a,", "b,", "c,"}); - check_equal("a,b,c"_sv | split_after(','), {"a,", "b,", "c"}); + check_equal("a,b,c,"_sv | split_after(','), Strs{"a,", "b,", "c,"}); + check_equal("a,b,c"_sv | split_after(','), Strs{"a,", "b,", "c"}); check_equal(R"(a\,,\,b,\,)"_sv | split(',', '\\') - | transform(unescape<',', '\\'>), {"a,", ",b", ","}); + | transform(unescape<',', '\\'>), Strs{"a,", ",b", ","}); check_equal(R"(\,\,)"_sv | split(',', '\\') - | transform(unescape<',', '\\'>), {",,"}); + | transform(unescape<',', '\\'>), Strs{",,"}); check_equal(R"(\\,\\,)"_sv | split(',', '\\') - | transform(unescape<',', '\\'>), {R"(\)", R"(\)", ""}); + | transform(unescape<',', '\\'>), Strs{R"(\)", R"(\)", ""}); + + check_equal(Array{""_sv, "abc"_sv, ""_sv, "def"_sv, ""_sv} | flatten(), "abcdef"_sv); + check_equal(Vector{"", ""} | flatten(), ""_sv); + check_equal(Vector{} | flatten(), ""_sv); }}; } diff --git a/src/ranges.hh b/src/ranges.hh index cf436880..a86b9e2b 100644 --- a/src/ranges.hh +++ b/src/ranges.hh @@ -24,13 +24,33 @@ decltype(auto) operator| (Range&& range, ViewFactory factory) } template -struct decay_range_impl { using type = std::remove_cvref_t; }; +struct DecayRangeImpl { using type = std::remove_cvref_t; }; template -struct decay_range_impl { using type = Range&; }; +struct DecayRangeImpl { using type = Range&; }; template -using decay_range = typename decay_range_impl::type; +using DecayRange = typename DecayRangeImpl::type; + +template +struct RangeHolderImpl { using type = std::remove_cvref_t; }; + +template +struct RangeHolderImpl { + struct type + { + Range* range{}; + + decltype(auto) begin() { return std::begin(*range); } + decltype(auto) end() { return std::end(*range); } + + type& operator=(Range& r) { range = &r; return *this; } + operator Range&() { return *range; } + }; +}; + +template +using RangeHolder = typename RangeHolderImpl::type; template struct ReverseView @@ -47,11 +67,11 @@ struct ReverseView Range m_range; }; -inline auto reverse() +constexpr auto reverse() { return ViewFactory{[](auto&& range) { using Range = decltype(range); - return ReverseView>{std::forward(range)}; + return ReverseView>{std::forward(range)}; }}; } @@ -71,11 +91,11 @@ struct SkipView size_t m_skip_count; }; -inline auto skip(size_t count) +constexpr auto skip(size_t count) { return ViewFactory{[count](auto&& range) { using Range = decltype(range); - return SkipView>{std::forward(range), count}; + return SkipView>{std::forward(range), count}; }}; } @@ -89,11 +109,11 @@ struct DropView size_t m_drop_count; }; -inline auto drop(size_t count) +constexpr auto drop(size_t count) { return ViewFactory{[count](auto&& range) { using Range = decltype(range); - return DropView>{std::forward(range), count}; + return DropView>{std::forward(range), count}; }}; } @@ -147,11 +167,11 @@ struct FilterView }; template -inline auto filter(Filter f) +constexpr auto filter(Filter f) { return ViewFactory{[f = std::move(f)](auto&& range) { using Range = decltype(range); - return FilterView, Filter>{std::forward(range), std::move(f)}; + return FilterView, Filter>{std::forward(range), std::move(f)}; }}; } @@ -193,11 +213,11 @@ struct EnumerateView Range m_range; }; -inline auto enumerate() +constexpr auto enumerate() { return ViewFactory{[](auto&& range) { using Range = decltype(range); - return EnumerateView>{std::forward(range)}; + return EnumerateView>{std::forward(range)}; }}; } @@ -252,11 +272,11 @@ struct TransformView }; template -inline auto transform(Transform t) +constexpr auto transform(Transform t) { return ViewFactory{[t = std::move(t)](auto&& range) { using Range = decltype(range); - return TransformView, Transform>{std::forward(range), std::move(t)}; + return TransformView, Transform>{std::forward(range), std::move(t)}; }}; } @@ -267,7 +287,7 @@ template requires std::is_same_v : std::true_type {}; template -inline auto transform(M T::*member) +constexpr auto transform(M T::*member) { return transform([member](auto&& arg) -> decltype(auto) { using Arg = decltype(arg); @@ -364,7 +384,7 @@ auto split(Element separator) { return ViewFactory{[s = std::move(separator)](auto&& range) { using Range = decltype(range); - return SplitView, false, false, Element, ValueType>{std::forward(range), std::move(s), {}}; + return SplitView, false, false, Element, ValueType>{std::forward(range), std::move(s), {}}; }}; } @@ -373,7 +393,7 @@ auto split_after(Element separator) { return ViewFactory{[s = std::move(separator)](auto&& range) { using Range = decltype(range); - return SplitView, false, true, Element, ValueType>{std::forward(range), std::move(s), {}}; + return SplitView, false, true, Element, ValueType>{std::forward(range), std::move(s), {}}; }}; } @@ -382,7 +402,79 @@ auto split(Element separator, Element escaper) { return ViewFactory{[s = std::move(separator), e = std::move(escaper)](auto&& range) { using Range = decltype(range); - return SplitView, true, false, Element, ValueType>{std::forward(range), std::move(s), std::move(e)}; + return SplitView, true, false, Element, ValueType>{std::forward(range), std::move(s), std::move(e)}; + }}; +} + +template +struct FlattenedView +{ + using OuterIt = IteratorOf; + using InnerRange = ValueOf; + using InnerIt = IteratorOf; + + struct Iterator + { + using value_type = typename std::iterator_traits::value_type; + using iterator_category = std::forward_iterator_tag; + using difference_type = std::size_t; + using reference = value_type&; + + Iterator() = default; + Iterator(OuterIt begin, OuterIt end) : m_outer_it{begin}, m_outer_end{end} + { + find_next_inner(); + } + + decltype(auto) operator*() { return *m_inner_it; } + + Iterator& operator++() + { + if (++m_inner_it == std::end(m_inner_range)) + { + ++m_outer_it; + find_next_inner(); + } + return *this; + } + Iterator operator++(int) { auto copy = *this; ++*this; return copy; } + + friend bool operator==(const Iterator& lhs, const Iterator& rhs) + { + return lhs.m_outer_it == rhs.m_outer_it and lhs.m_inner_it == rhs.m_inner_it; + } + + void find_next_inner() + { + m_inner_it = InnerIt{}; + for (; m_outer_it != m_outer_end; ++m_outer_it) + { + m_inner_range = *m_outer_it; + if (std::begin(m_inner_range) != std::end(m_inner_range)) + { + m_inner_it = std::begin(m_inner_range); + return; + } + } + } + + OuterIt m_outer_it{}; + OuterIt m_outer_end{}; + InnerIt m_inner_it{}; + RangeHolder m_inner_range; + }; + + Iterator begin() const { return {std::begin(m_range), std::end(m_range)}; } + Iterator end() const { return {std::end(m_range), std::end(m_range)}; } + + Range m_range; +}; + +constexpr auto flatten() +{ + return ViewFactory{[](auto&& range){ + using Range = decltype(range); + return FlattenedView>{std::forward(range)}; }}; } @@ -434,7 +526,7 @@ struct ConcatView }; template -ConcatView, decay_range> concatenated(Range1&& range1, Range2&& range2) +ConcatView, DecayRange> concatenated(Range1&& range1, Range2&& range2) { return {range1, range2}; } diff --git a/src/ref_ptr.hh b/src/ref_ptr.hh index 235ee2a3..4b576039 100644 --- a/src/ref_ptr.hh +++ b/src/ref_ptr.hh @@ -88,8 +88,8 @@ struct RefPtr acquire(); } - friend bool operator==(const RefPtr& lhs, const RefPtr& rhs) { return lhs.m_ptr == rhs.m_ptr; } - friend bool operator!=(const RefPtr& lhs, const RefPtr& rhs) { return lhs.m_ptr != rhs.m_ptr; } + friend bool operator==(const RefPtr& lhs, const RefPtr& rhs) = default; + friend bool operator==(const RefPtr& lhs, const T* rhs) { return lhs.m_ptr == rhs; } private: T* m_ptr = nullptr; diff --git a/src/remote.cc b/src/remote.cc index e6992c20..63a7fecf 100644 --- a/src/remote.cc +++ b/src/remote.cc @@ -605,8 +605,8 @@ const String& session_directory() String session_path(StringView session) { - if (contains(session, '/')) - throw runtime_error{"session names cannot have slashes"}; + if (not all_of(session, is_identifier)) + throw runtime_error{format("invalid session name: '{}'", session)}; return format("{}/{}", session_directory(), session); } @@ -614,7 +614,10 @@ static sockaddr_un session_addr(StringView session) { sockaddr_un addr; addr.sun_family = AF_UNIX; - strcpy(addr.sun_path, session_path(session).c_str()); + String path = session_path(session); + if (path.length() + 1 > sizeof addr.sun_path) + throw runtime_error{format("socket path too long: '{}'", path)}; + strcpy(addr.sun_path, path.c_str()); return addr; } @@ -848,9 +851,6 @@ private: Server::Server(String session_name, bool is_daemon) : m_session{std::move(session_name)}, m_is_daemon{is_daemon} { - if (not all_of(m_session, is_identifier)) - throw runtime_error{format("invalid session name: '{}'", m_session)}; - int listen_sock = socket(AF_UNIX, SOCK_STREAM, 0); fcntl(listen_sock, F_SETFD, FD_CLOEXEC); sockaddr_un addr = session_addr(m_session); @@ -885,9 +885,6 @@ Server::Server(String session_name, bool is_daemon) bool Server::rename_session(StringView name) { - if (not all_of(name, is_identifier)) - throw runtime_error{format("invalid session name: '{}'", name)}; - String old_socket_file = session_path(m_session); String new_socket_file = session_path(name); diff --git a/test/regression/3909-crash-on-closing-buffer-with-user-mapping/cmd b/test/regression/3909-crash-on-closing-buffer-with-user-mapping/cmd new file mode 100644 index 00000000..b870f425 --- /dev/null +++ b/test/regression/3909-crash-on-closing-buffer-with-user-mapping/cmd @@ -0,0 +1 @@ +i,d diff --git a/test/regression/3909-crash-on-closing-buffer-with-user-mapping/in b/test/regression/3909-crash-on-closing-buffer-with-user-mapping/in new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/test/regression/3909-crash-on-closing-buffer-with-user-mapping/in @@ -0,0 +1 @@ + diff --git a/test/regression/3909-crash-on-closing-buffer-with-user-mapping/out b/test/regression/3909-crash-on-closing-buffer-with-user-mapping/out new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/test/regression/3909-crash-on-closing-buffer-with-user-mapping/out @@ -0,0 +1 @@ + diff --git a/test/regression/3909-crash-on-closing-buffer-with-user-mapping/rc b/test/regression/3909-crash-on-closing-buffer-with-user-mapping/rc new file mode 100644 index 00000000..13e1f91e --- /dev/null +++ b/test/regression/3909-crash-on-closing-buffer-with-user-mapping/rc @@ -0,0 +1,2 @@ +edit -scratch +map global user d :delete-buffer