mirror of
https://github.com/mawww/kakoune.git
synced 2024-10-05 17:18:00 +03:00
Compare commits
21 Commits
96198dfa8c
...
c1e638162c
Author | SHA1 | Date | |
---|---|---|---|
|
c1e638162c | ||
|
80fcfebca8 | ||
|
cbdd200e73 | ||
|
374a7a8c99 | ||
|
6674fe7587 | ||
|
4a00a6edea | ||
|
6493ddad42 | ||
|
880ad98a30 | ||
|
0bb355557e | ||
|
ba504431dc | ||
|
c4684d0d84 | ||
|
c84942c2ac | ||
|
700265b25d | ||
|
8045712595 | ||
|
966deb514e | ||
|
fe8f0f3371 | ||
|
faf83b10e2 | ||
|
9c185249a2 | ||
|
2754e27cf2 | ||
|
5c393c198b | ||
|
c773620151 |
@ -1,6 +1,6 @@
|
||||
freebsd_task:
|
||||
freebsd_instance:
|
||||
image_family: freebsd-13-2
|
||||
image_family: freebsd-13-3
|
||||
matrix:
|
||||
- name: freebsd_clang
|
||||
env:
|
||||
@ -27,6 +27,7 @@ linux_task:
|
||||
- name: linux_gcc
|
||||
container:
|
||||
image: gcc:10
|
||||
memory: 8G
|
||||
env:
|
||||
CXX: g++
|
||||
test_script: make CXX=$CXX -j$(nproc) test
|
||||
|
@ -41,22 +41,27 @@ highlighter is replaced with the new one.
|
||||
using the `Whitespace` face, with the following *options*:
|
||||
|
||||
*-lf* <separator>:::
|
||||
a one character long separator that will replace line feeds
|
||||
a one character long separator that will replace line feeds,
|
||||
or an empty string to ignore them.
|
||||
|
||||
*-spc* <separator>:::
|
||||
a one character long separator that will replace spaces
|
||||
a one character long separator that will replace spaces,
|
||||
or an empty string to ignore them.
|
||||
|
||||
*-nbsp* <separator>:::
|
||||
a one character long separator that will replace non-breakable spaces
|
||||
a one character long separator that will replace non-breakable spaces,
|
||||
or an empty string to ignore them.
|
||||
|
||||
*-tab* <separator>:::
|
||||
a one character long separator that will replace tabulations
|
||||
a one character long separator that will replace tabulations,
|
||||
or an empty string to ignore them.
|
||||
|
||||
*-tabpad* <separator>:::
|
||||
a one character long separator that will be appended to tabulations to honor the *tabstop* option
|
||||
|
||||
*-indent* <separator>:::
|
||||
a one character long separator that will replace the first space in indentation according to the *indentwidth* option
|
||||
a one character long separator that will replace the first space in indentation
|
||||
according to the *indentwidth* option, or an empty string to ignore them.
|
||||
This will use the `WhitespaceIndent` face.
|
||||
|
||||
*-only-trailing*:::
|
||||
|
@ -8,7 +8,7 @@ hook global BufCreate .*[.](ex|exs) %{
|
||||
set-option buffer filetype elixir
|
||||
}
|
||||
|
||||
hook global BufCreate .*[.]html[.]l?eex %{
|
||||
hook global BufCreate .*[.]html[.][lh]?eex %{
|
||||
set-option buffer filetype eex
|
||||
}
|
||||
|
||||
@ -64,6 +64,7 @@ add-highlighter shared/elixir/single_string region "'" "(?<!\\)(?:\\\\)*'" fill
|
||||
add-highlighter shared/elixir/comment region '#' '$' fill comment
|
||||
|
||||
add-highlighter shared/elixir/leex region -match-capture '~L("""|")' '(?<!\\)(?:\\\\)*("""|")' ref eex
|
||||
add-highlighter shared/elixir/heex region -match-capture '~H("""|")' '(?<!\\)(?:\\\\)*("""|")' ref eex
|
||||
|
||||
add-highlighter shared/elixir/double_string/base default-region fill string
|
||||
add-highlighter shared/elixir/double_string/interpolation region -recurse \{ \Q#{ \} fill builtin
|
||||
|
@ -16,3 +16,17 @@ hook -group perf-report-highlight global WinSetOption filetype=perf-report %{
|
||||
|
||||
map window normal <ret> ': perf-report-focus<ret>'
|
||||
}
|
||||
|
||||
provide-module perf-annotate %{
|
||||
require-module gas
|
||||
add-highlighter shared/perf-annotate group
|
||||
add-highlighter shared/perf-annotate/gas ref gas
|
||||
add-highlighter shared/perf-annotate/above_threshold regex '^\h+([1-9]|\d{2})\.\d+\b' 0:red
|
||||
add-highlighter shared/perf-annotate/below_threshold regex '^\h+0\.\d+\b' 0:green
|
||||
}
|
||||
|
||||
hook -group perf-annotate-highlight global WinSetOption filetype=perf-annotate %{
|
||||
require-module perf-annotate
|
||||
add-highlighter window/perf-annotate ref perf-annotate
|
||||
hook -once -always window WinSetOption filetype=.* %{ remove-highlighter window/perf-annotate }
|
||||
}
|
||||
|
@ -1,12 +1,14 @@
|
||||
provide-module fifo %{
|
||||
|
||||
define-command -params .. -docstring %{
|
||||
fifo [-name <buffer-name>] [-scroll] [--] <command>...: run command in a fifo buffer
|
||||
fifo [-name <name>] [-scroll] [-script <script>] [--] <args>...: run command in a fifo buffer
|
||||
if <script> is used, eval it with <args> as '$@', else pass arguments directly to the shell
|
||||
} fifo %{ evaluate-commands %sh{
|
||||
name='*fifo*'
|
||||
while true; do
|
||||
case "$1" in
|
||||
"-scroll") scroll="-scroll"; shift ;;
|
||||
"-script") script="$2"; shift 2 ;;
|
||||
"-name") name="$2"; shift 2 ;;
|
||||
"--") shift; break ;;
|
||||
*) break ;;
|
||||
@ -14,7 +16,11 @@ define-command -params .. -docstring %{
|
||||
done
|
||||
output=$(mktemp -d "${TMPDIR:-/tmp}"/kak-fifo.XXXXXXXX)/fifo
|
||||
mkfifo ${output}
|
||||
( eval "$@" > ${output} 2>&1 & ) > /dev/null 2>&1 < /dev/null
|
||||
if [ -n "$script" ]; then
|
||||
( eval "$script" > ${output} 2>&1 & ) > /dev/null 2>&1 < /dev/null
|
||||
else
|
||||
( "$@" > ${output} 2>&1 & ) > /dev/null 2>&1 < /dev/null
|
||||
fi
|
||||
|
||||
printf %s\\n "
|
||||
edit! -fifo ${output} ${scroll} ${name}
|
||||
|
@ -12,8 +12,7 @@ define-command -params .. -docstring %{
|
||||
Passing no argument will perform a literal-string grep for the current selection
|
||||
} grep %{
|
||||
evaluate-commands -try-client %opt{toolsclient} %{
|
||||
fifo -name *grep* %{
|
||||
shift 2
|
||||
fifo -name *grep* -script %{
|
||||
trap - INT QUIT
|
||||
if [ $# -eq 0 ]; then
|
||||
case "$kak_opt_grepcmd" in
|
||||
@ -29,7 +28,7 @@ define-command -params .. -docstring %{
|
||||
esac
|
||||
fi
|
||||
$kak_opt_grepcmd "$@" 2>&1 | tr -d '\r'
|
||||
} 'exit;' %arg{@} # pass arguments for "$@" above, exit to avoid evaluating them
|
||||
} -- %arg{@}
|
||||
set-option buffer filetype grep
|
||||
set-option buffer jump_current_line 0
|
||||
}
|
||||
|
@ -13,11 +13,10 @@ define-command -params .. -docstring %{
|
||||
All the optional arguments are forwarded to the make utility
|
||||
} make %{
|
||||
evaluate-commands -try-client %opt{toolsclient} %{
|
||||
fifo -scroll -name *make* %{
|
||||
shift 2
|
||||
fifo -scroll -name *make* -script %{
|
||||
trap - INT QUIT
|
||||
$kak_opt_makecmd "$@"
|
||||
} 'exit;' %arg{@} # pass arguments for "$@" above, exit to avoid evaluating them
|
||||
} -- %arg{@}
|
||||
set-option buffer filetype make
|
||||
set-option buffer jump_current_line 0
|
||||
}
|
||||
|
@ -424,7 +424,7 @@ StringView Context::main_sel_register_value(StringView reg) const
|
||||
return RegisterManager::instance()[reg].get_main(*this, index);
|
||||
}
|
||||
|
||||
void Context::set_name(String name) {
|
||||
void Context::set_name(String name) {
|
||||
String old_name = std::exchange(m_name, std::move(name));
|
||||
hooks().run_hook(Hook::ClientRenamed, format("{}:{}", old_name, m_name), *this);
|
||||
}
|
||||
|
@ -239,7 +239,7 @@ struct HashMap
|
||||
constexpr bool contains(const KeyType& key) const { return find_index(key) >= 0; }
|
||||
|
||||
template<typename KeyType> requires IsHashCompatible<Key, std::remove_cvref_t<KeyType>>
|
||||
constexpr EffectiveValue& operator[](KeyType&& key)
|
||||
constexpr EffectiveValue& operator[](KeyType&& key)
|
||||
{
|
||||
const auto hash = hash_value(key);
|
||||
auto index = find_index(key, hash);
|
||||
|
@ -981,18 +981,14 @@ struct ShowWhitespacesHighlighter : Highlighter
|
||||
bool only_trailing = (bool) parser.get_switch("only-trailing");
|
||||
auto get_param = [&](StringView param, StringView fallback) {
|
||||
StringView value = parser.get_switch(param).value_or(fallback);
|
||||
if (value.char_length() != 1)
|
||||
throw runtime_error{format("-{} expects a single character parameter", param)};
|
||||
if (value.char_length() > 1)
|
||||
throw runtime_error{format("-{} expects a single character or empty parameter", param)};
|
||||
return value.str();
|
||||
};
|
||||
|
||||
String indent = parser.get_switch("indent").value_or("│").str();
|
||||
if (indent.char_length() > 1)
|
||||
throw runtime_error{format("-indent expects a single character or empty parameter")};
|
||||
|
||||
return std::make_unique<ShowWhitespacesHighlighter>(
|
||||
get_param("tab", "→"), get_param("tabpad", " "), get_param("spc", "·"),
|
||||
get_param("lf", "¬"), get_param("nbsp", "⍽"), indent, only_trailing);
|
||||
get_param("lf", "¬"), get_param("nbsp", "⍽"), get_param("indent", "│"), only_trailing);
|
||||
}
|
||||
|
||||
private:
|
||||
@ -1043,28 +1039,28 @@ private:
|
||||
if (it != end)
|
||||
atom_it = line.split(atom_it, it.coord());
|
||||
|
||||
if (cp == '\t')
|
||||
if (cp == '\t' and not m_tab.empty() and not m_tabpad.empty())
|
||||
{
|
||||
const ColumnCount column = get_column(buffer, tabstop, coord);
|
||||
const ColumnCount count = tabstop - (column % tabstop);
|
||||
atom_it->replace(m_tab + String(m_tabpad[(CharCount)0], count - m_tab.column_length()));
|
||||
}
|
||||
else if (cp == ' ') {
|
||||
if (m_indent.empty() or indentwidth == 0 or not is_indentation) {
|
||||
else if (cp == ' ' and is_indentation and indentwidth > 0 and not m_indent.empty()) {
|
||||
const ColumnCount column = get_column(buffer, tabstop, coord);
|
||||
if (column % indentwidth == 0 and column != 0) {
|
||||
atom_it->replace(m_indent);
|
||||
face = indentface;
|
||||
}
|
||||
else {
|
||||
atom_it->replace(m_spc);
|
||||
} else {
|
||||
const ColumnCount column = get_column(buffer, tabstop, coord);
|
||||
if (column % indentwidth == 0 and column != 0) {
|
||||
atom_it->replace(m_indent);
|
||||
face = indentface;
|
||||
} else {
|
||||
atom_it->replace(m_spc);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (cp == '\n')
|
||||
else if (cp == ' ' and not m_spc.empty()) {
|
||||
atom_it->replace(m_spc);
|
||||
}
|
||||
else if (cp == '\n' and not m_lf.empty())
|
||||
atom_it->replace(m_lf);
|
||||
else if (cp == 0xA0 or cp == 0x202F)
|
||||
else if ((cp == 0xA0 or cp == 0x202F) and not m_nbsp.empty())
|
||||
atom_it->replace(m_nbsp);
|
||||
atom_it->face = merge_faces(atom_it->face, face);
|
||||
break;
|
||||
|
@ -527,11 +527,11 @@ public:
|
||||
m_line = m_line.substr(0, m_cursor_pos)
|
||||
+ m_line.substr(m_cursor_pos+1);
|
||||
}
|
||||
else if (key == alt('f'))
|
||||
else if (key == ctrl(Key::Right) or key == alt('f'))
|
||||
to_next_word_begin<Word>(m_cursor_pos, m_line);
|
||||
else if (key == alt('F'))
|
||||
to_next_word_begin<WORD>(m_cursor_pos, m_line);
|
||||
else if (key == alt('b'))
|
||||
else if (key == ctrl(Key::Left) or key == alt('b'))
|
||||
to_prev_word_begin<Word>(m_cursor_pos, m_line);
|
||||
else if (key == alt('B'))
|
||||
to_prev_word_begin<WORD>(m_cursor_pos, m_line);
|
||||
|
@ -589,13 +589,13 @@ void pipe(Context& context, NormalParams params)
|
||||
return;
|
||||
|
||||
Buffer& buffer = context.buffer();
|
||||
SelectionList selections = context.selections();
|
||||
if (replace)
|
||||
{
|
||||
buffer.throw_if_read_only();
|
||||
ScopedEdition edition(context);
|
||||
ForwardChangesTracker changes_tracker;
|
||||
size_t timestamp = buffer.timestamp();
|
||||
Vector<Selection> new_sels;
|
||||
SelectionList selections = context.selections();
|
||||
for (auto& sel : selections)
|
||||
{
|
||||
const auto beg = changes_tracker.get_new_coord_tolerant(sel.min());
|
||||
@ -614,20 +614,27 @@ void pipe(Context& context, NormalParams params)
|
||||
|
||||
auto new_end = apply_diff(buffer, beg, in, out);
|
||||
if (new_end != beg)
|
||||
new_sels.push_back(keep_direction({beg, buffer.char_prev(new_end), std::move(sel.captures())}, sel));
|
||||
{
|
||||
auto& min = sel.min();
|
||||
auto& max = sel.max();
|
||||
min = beg;
|
||||
max = buffer.char_prev(new_end);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (new_end != BufferCoord{})
|
||||
new_end = buffer.char_prev(new_end);
|
||||
new_sels.push_back({new_end, new_end, std::move(sel.captures())});
|
||||
sel.set(new_end);
|
||||
}
|
||||
|
||||
changes_tracker.update(buffer, timestamp);
|
||||
}
|
||||
context.selections_write_only().set(std::move(new_sels), selections.main_index());
|
||||
selections.force_timestamp(timestamp);
|
||||
context.selections_write_only() = std::move(selections);
|
||||
}
|
||||
else
|
||||
{
|
||||
SelectionList& selections = context.selections();
|
||||
const auto old_main = selections.main_index();
|
||||
for (int i = 0; i < selections.size(); ++i)
|
||||
{
|
||||
|
@ -706,7 +706,7 @@ private:
|
||||
{
|
||||
auto& node = get_node(index);
|
||||
|
||||
const uint32_t start_pos = (uint32_t)m_program.instructions.size();
|
||||
const OpIndex start_pos = op_count();
|
||||
const bool ignore_case = node.ignore_case;
|
||||
|
||||
const bool save = (node.op == ParsedRegex::Alternation or node.op == ParsedRegex::Sequence) and
|
||||
@ -746,14 +746,17 @@ private:
|
||||
if (child != index+1)
|
||||
push_inst(CompiledRegex::Split);
|
||||
}
|
||||
auto split_pos = m_program.instructions.size();
|
||||
auto split_pos = op_count();
|
||||
|
||||
const auto end = node.children_end;
|
||||
for (auto child : Children<>{m_parsed_regex, index})
|
||||
{
|
||||
auto node = compile_node<direction>(child);
|
||||
if (child != index+1)
|
||||
m_program.instructions[--split_pos].param.split = CompiledRegex::Param::Split{.target = node, .prioritize_parent = true};
|
||||
{
|
||||
--split_pos;
|
||||
m_program.instructions[split_pos].param.split = {.offset = offset(node, split_pos), .prioritize_parent = true};
|
||||
}
|
||||
if (get_node(child).children_end != end)
|
||||
{
|
||||
auto jump = push_inst(CompiledRegex::Jump);
|
||||
@ -801,8 +804,8 @@ private:
|
||||
break;
|
||||
}
|
||||
|
||||
for (auto& offset : goto_inner_end_offsets)
|
||||
m_program.instructions[offset].param.jump_target = m_program.instructions.size();
|
||||
for (auto& index : goto_inner_end_offsets)
|
||||
m_program.instructions[index].param.jump_offset = offset(op_count(), index);
|
||||
|
||||
if (save)
|
||||
push_inst(CompiledRegex::Save, {.save_index=int16_t(node.value * 2 + (forward ? 1 : 0))});
|
||||
@ -810,19 +813,29 @@ private:
|
||||
return start_pos;
|
||||
}
|
||||
|
||||
OpIndex op_count() const
|
||||
{
|
||||
return static_cast<OpIndex>(m_program.instructions.size());
|
||||
}
|
||||
|
||||
static OpIndex offset(OpIndex to, OpIndex from)
|
||||
{
|
||||
return static_cast<OpIndex>(to - from);
|
||||
}
|
||||
|
||||
template<RegexMode direction>
|
||||
OpIndex compile_node(ParsedRegex::NodeIndex index)
|
||||
{
|
||||
auto& node = get_node(index);
|
||||
|
||||
const OpIndex start_pos = (OpIndex)m_program.instructions.size();
|
||||
const OpIndex start_pos = op_count();
|
||||
Vector<OpIndex> goto_ends;
|
||||
|
||||
auto& quantifier = node.quantifier;
|
||||
|
||||
if (quantifier.allows_none())
|
||||
{
|
||||
auto split_pos = push_inst(CompiledRegex::Split, {.split={.target=0, .prioritize_parent=quantifier.greedy}});
|
||||
auto split_pos = push_inst(CompiledRegex::Split, {.split={.offset=0, .prioritize_parent=quantifier.greedy}});
|
||||
goto_ends.push_back(split_pos);
|
||||
}
|
||||
|
||||
@ -832,18 +845,18 @@ private:
|
||||
inner_pos = compile_node_inner<direction>(index);
|
||||
|
||||
if (quantifier.allows_infinite_repeat())
|
||||
push_inst(CompiledRegex::Split, {.split = {.target=inner_pos, .prioritize_parent=not quantifier.greedy}});
|
||||
push_inst(CompiledRegex::Split, {.split = {.offset=offset(inner_pos, op_count()), .prioritize_parent=not quantifier.greedy}});
|
||||
// Write the node as an optional match for the min -> max counts
|
||||
else for (int i = std::max((int16_t)1, quantifier.min); // STILL UGLY !
|
||||
i < quantifier.max; ++i)
|
||||
{
|
||||
auto split_pos = push_inst(CompiledRegex::Split, {.split={.target=0, .prioritize_parent=quantifier.greedy}});
|
||||
auto split_pos = push_inst(CompiledRegex::Split, {.split={.offset=0, .prioritize_parent=quantifier.greedy}});
|
||||
goto_ends.push_back(split_pos);
|
||||
compile_node_inner<direction>(index);
|
||||
}
|
||||
|
||||
for (auto offset : goto_ends)
|
||||
m_program.instructions[offset].param.split.target = m_program.instructions.size();
|
||||
for (auto index : goto_ends)
|
||||
m_program.instructions[index].param.split.offset = offset(op_count(), index);
|
||||
|
||||
return start_pos;
|
||||
}
|
||||
@ -851,7 +864,7 @@ private:
|
||||
OpIndex push_inst(CompiledRegex::Op op, CompiledRegex::Param param = {})
|
||||
{
|
||||
constexpr auto max_instructions = std::numeric_limits<OpIndex>::max();
|
||||
const auto res = m_program.instructions.size();
|
||||
const auto res = op_count();
|
||||
if (res >= max_instructions)
|
||||
throw regex_error(format("regex compiled to more than {} instructions", max_instructions));
|
||||
m_program.instructions.push_back({ op, 0, param });
|
||||
@ -1031,7 +1044,7 @@ private:
|
||||
{
|
||||
auto& inst = m_program.instructions[i];
|
||||
if (is_jump(inst.op))
|
||||
m_program.instructions[inst.param.jump_target].last_step = 0xffff; // tag as jump target
|
||||
m_program.instructions[i + inst.param.jump_offset].last_step = 0xffff; // tag as jump target
|
||||
}
|
||||
|
||||
for (auto block_begin = begin; block_begin < end; )
|
||||
@ -1071,11 +1084,11 @@ private:
|
||||
String dump_regex(const CompiledRegex& program)
|
||||
{
|
||||
String res;
|
||||
int count = 0;
|
||||
int index = 0;
|
||||
for (auto& inst : program.instructions)
|
||||
{
|
||||
char buf[20];
|
||||
format_to(buf, " {:03} ", count++);
|
||||
format_to(buf, " {:03} ", index);
|
||||
res += buf;
|
||||
switch (inst.op)
|
||||
{
|
||||
@ -1095,13 +1108,13 @@ String dump_regex(const CompiledRegex& program)
|
||||
res += format("character type {}\n", to_underlying(inst.param.character_type));
|
||||
break;
|
||||
case CompiledRegex::Jump:
|
||||
res += format("jump {}\n", inst.param.jump_target);
|
||||
res += format("jump {} ({:03})\n", inst.param.jump_offset, index + inst.param.jump_offset);
|
||||
break;
|
||||
case CompiledRegex::Split:
|
||||
{
|
||||
res += format("split (prioritize {}) {}\n",
|
||||
res += format("split (prioritize {}) {} ({:03})\n",
|
||||
(inst.param.split.prioritize_parent) ? "parent" : "child",
|
||||
inst.param.split.target);
|
||||
inst.param.split.offset, index + inst.param.split.offset);
|
||||
break;
|
||||
}
|
||||
case CompiledRegex::Save:
|
||||
@ -1135,6 +1148,7 @@ String dump_regex(const CompiledRegex& program)
|
||||
case CompiledRegex::Match:
|
||||
res += "match\n";
|
||||
}
|
||||
++index;
|
||||
}
|
||||
auto dump_start_desc = [&](const CompiledRegex::StartDesc& desc, StringView name) {
|
||||
res += name + " start desc: [";
|
||||
|
@ -54,11 +54,11 @@ struct CharacterClass
|
||||
if (ignore_case)
|
||||
cp = to_lower(cp);
|
||||
|
||||
for (auto& range : ranges)
|
||||
for (auto& [min, max] : ranges)
|
||||
{
|
||||
if (cp < range.min)
|
||||
if (cp < min)
|
||||
break;
|
||||
else if (cp <= range.max)
|
||||
else if (cp <= max)
|
||||
return not negative;
|
||||
}
|
||||
|
||||
@ -106,11 +106,11 @@ struct CompiledRegex : UseMemoryDomain<MemoryDomain::Regex>
|
||||
} literal;
|
||||
int16_t character_class_index;
|
||||
CharacterType character_type;
|
||||
int16_t jump_target;
|
||||
int16_t jump_offset;
|
||||
int16_t save_index;
|
||||
struct Split
|
||||
{
|
||||
int16_t target;
|
||||
int16_t offset;
|
||||
bool prioritize_parent : 1;
|
||||
} split;
|
||||
bool line_start;
|
||||
@ -351,10 +351,10 @@ private:
|
||||
--saves.refcount;
|
||||
};
|
||||
|
||||
struct alignas(int32_t) Thread
|
||||
struct Thread
|
||||
{
|
||||
int16_t inst;
|
||||
int16_t saves;
|
||||
const CompiledRegex::Instruction* inst;
|
||||
int saves;
|
||||
};
|
||||
|
||||
using StartDesc = CompiledRegex::StartDesc;
|
||||
@ -370,8 +370,7 @@ private:
|
||||
|
||||
// Steps a thread until it consumes the current character, matches or fail
|
||||
[[gnu::always_inline]]
|
||||
void step_thread(const CompiledRegex::Instruction* instructions, const Iterator& pos, Codepoint cp,
|
||||
uint16_t current_step, Thread thread, const ExecConfig& config)
|
||||
void step_thread(const Iterator& pos, Codepoint cp, uint16_t current_step, Thread thread, const ExecConfig& config)
|
||||
{
|
||||
auto failed = [this, &thread]() {
|
||||
release_saves(thread.saves);
|
||||
@ -382,7 +381,7 @@ private:
|
||||
|
||||
while (true)
|
||||
{
|
||||
auto& inst = instructions[thread.inst++];
|
||||
auto& inst = *thread.inst++;
|
||||
// if this instruction was already executed for this step in another thread,
|
||||
// then this thread is redundant and can be dropped
|
||||
if (inst.last_step == current_step)
|
||||
@ -416,19 +415,20 @@ private:
|
||||
return consumed();
|
||||
return failed();
|
||||
case CompiledRegex::CharClass:
|
||||
if (pos == config.end)
|
||||
return failed();
|
||||
return m_program.character_classes[inst.param.character_class_index].matches(cp) ? consumed() : failed();
|
||||
if (pos != config.end and
|
||||
m_program.character_classes[inst.param.character_class_index].matches(cp))
|
||||
return consumed();
|
||||
return failed();
|
||||
case CompiledRegex::CharType:
|
||||
if (pos == config.end)
|
||||
return failed();
|
||||
return is_ctype(inst.param.character_type, cp) ? consumed() : failed();
|
||||
if (pos != config.end and is_ctype(inst.param.character_type, cp))
|
||||
return consumed();
|
||||
return failed();
|
||||
case CompiledRegex::Jump:
|
||||
thread.inst = inst.param.jump_target;
|
||||
thread.inst = &inst + inst.param.jump_offset;
|
||||
break;
|
||||
case CompiledRegex::Split:
|
||||
if (auto target = inst.param.split.target;
|
||||
instructions[target].last_step != current_step)
|
||||
if (auto* target = &inst + inst.param.split.offset;
|
||||
target->last_step != current_step)
|
||||
{
|
||||
if (thread.saves >= 0)
|
||||
++m_saves[thread.saves].refcount;
|
||||
@ -478,7 +478,8 @@ private:
|
||||
m_captures = -1;
|
||||
m_threads.ensure_initial_capacity();
|
||||
|
||||
const int16_t first_inst = forward ? 0 : m_program.first_backward_inst;
|
||||
ConstArrayView<CompiledRegex::Instruction> insts{m_program.instructions};
|
||||
const auto* first_inst = insts.begin() + (forward ? 0 : m_program.first_backward_inst);
|
||||
m_threads.push_current({first_inst, -1});
|
||||
|
||||
const auto* start_desc = (forward ? m_program.forward_start_desc : m_program.backward_start_desc).get();
|
||||
@ -486,7 +487,6 @@ private:
|
||||
|
||||
constexpr bool search = mode & RegexMode::Search;
|
||||
constexpr bool any_match = mode & RegexMode::AnyMatch;
|
||||
ConstArrayView<CompiledRegex::Instruction> insts{m_program.instructions};
|
||||
uint16_t current_step = -1;
|
||||
m_found_match = false;
|
||||
while (true) // Iterate on all codepoints and once at the end
|
||||
@ -506,7 +506,7 @@ private:
|
||||
Codepoint cp = codepoint(next, config);
|
||||
|
||||
while (not m_threads.current_is_empty())
|
||||
step_thread(insts.pointer(), pos, cp, current_step, m_threads.pop_current(), config);
|
||||
step_thread(pos, cp, current_step, m_threads.pop_current(), config);
|
||||
|
||||
if (pos == config.end or
|
||||
(m_threads.next_is_empty() and (not search or m_found_match)) or
|
||||
|
@ -40,7 +40,7 @@ void HistoryRegister::set(Context& context, ConstArrayView<String> values, bool
|
||||
return;
|
||||
}
|
||||
|
||||
for (auto&& entry : values | reverse())
|
||||
for (auto&& entry : values | reverse())
|
||||
{
|
||||
m_content.erase(std::remove(m_content.begin(), m_content.end(), entry), m_content.end());
|
||||
m_content.insert(m_content.begin(), entry);
|
||||
|
@ -1,22 +1,27 @@
|
||||
#include "string.hh"
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include "assert.hh"
|
||||
#include "unit_tests.hh"
|
||||
|
||||
namespace Kakoune
|
||||
{
|
||||
namespace
|
||||
{
|
||||
// Avoid including all of <algorithm> just for this.
|
||||
constexpr auto max(auto lhs, auto rhs) { return lhs > rhs ? lhs : rhs;}
|
||||
constexpr auto min(auto lhs, auto rhs) { return lhs < rhs ? lhs : rhs;}
|
||||
}
|
||||
|
||||
String::Data::Data(const char* data, size_t size, size_t capacity)
|
||||
{
|
||||
if (capacity > Short::capacity)
|
||||
{
|
||||
if (capacity & 1)
|
||||
++capacity;
|
||||
|
||||
kak_assert(capacity < Long::max_capacity);
|
||||
kak_assert(capacity <= Long::max_capacity);
|
||||
u.l.ptr = Alloc{}.allocate(capacity+1);
|
||||
u.l.size = size;
|
||||
u.l.capacity = capacity;
|
||||
u.l.capacity = (capacity & Long::max_capacity);
|
||||
u.l.mode = Long::active_mask;
|
||||
|
||||
if (data != nullptr)
|
||||
memcpy(u.l.ptr, data, size);
|
||||
@ -60,26 +65,28 @@ String::Data& String::Data::operator=(Data&& other) noexcept
|
||||
template<bool copy>
|
||||
void String::Data::reserve(size_t new_capacity)
|
||||
{
|
||||
if (capacity() != 0 and new_capacity <= capacity())
|
||||
auto const current_capacity = capacity();
|
||||
if (current_capacity != 0 and new_capacity <= current_capacity)
|
||||
return;
|
||||
|
||||
if (is_long())
|
||||
new_capacity = std::max(u.l.capacity * 2, new_capacity);
|
||||
if (!is_long() and new_capacity <= Short::capacity)
|
||||
return;
|
||||
|
||||
if (new_capacity & 1)
|
||||
++new_capacity;
|
||||
kak_assert(new_capacity <= Long::max_capacity);
|
||||
new_capacity = max(new_capacity, // Do not upgrade new_capacity to be over limit.
|
||||
min(current_capacity * 2, Long::max_capacity));
|
||||
|
||||
kak_assert(new_capacity < Long::max_capacity);
|
||||
char* new_ptr = Alloc{}.allocate(new_capacity+1);
|
||||
if (copy)
|
||||
{
|
||||
memcpy(new_ptr, data(), size()+1);
|
||||
u.l.size = size();
|
||||
}
|
||||
release();
|
||||
|
||||
u.l.size = size();
|
||||
u.l.ptr = new_ptr;
|
||||
u.l.capacity = new_capacity;
|
||||
u.l.capacity = (new_capacity & Long::max_capacity);
|
||||
u.l.mode = Long::active_mask;
|
||||
}
|
||||
|
||||
template void String::Data::reserve<true>(size_t);
|
||||
@ -89,6 +96,7 @@ void String::Data::force_size(size_t new_size)
|
||||
{
|
||||
reserve<false>(new_size);
|
||||
set_size(new_size);
|
||||
data()[new_size] = 0;
|
||||
}
|
||||
|
||||
void String::Data::append(const char* str, size_t len)
|
||||
@ -131,17 +139,47 @@ void String::Data::set_size(size_t size)
|
||||
if (is_long())
|
||||
u.l.size = size;
|
||||
else
|
||||
u.s.size = (size << 1) | 1;
|
||||
u.s.remaining_size = Short::capacity - size;
|
||||
}
|
||||
|
||||
void String::Data::set_short(const char* data, size_t size)
|
||||
{
|
||||
u.s.size = (size << 1) | 1;
|
||||
kak_assert(size <= Short::capacity);
|
||||
u.s.remaining_size = Short::capacity - size;
|
||||
if (data != nullptr)
|
||||
memcpy(u.s.string, data, size);
|
||||
u.s.string[size] = 0;
|
||||
}
|
||||
|
||||
UnitTest test_data{[]{
|
||||
using Data = String::Data;
|
||||
{ // Basic data usage.
|
||||
Data data;
|
||||
kak_assert(data.size() == 0);
|
||||
kak_assert(not data.is_long());
|
||||
kak_assert(data.capacity() == 23);
|
||||
|
||||
// Should be SSO-ed.
|
||||
data.append("test", 4);
|
||||
kak_assert(data.size() == 4);
|
||||
kak_assert(data.capacity() == 23);
|
||||
kak_assert(not data.is_long());
|
||||
kak_assert(data.data() == StringView("test"));
|
||||
}
|
||||
{
|
||||
char large_buf[2048];
|
||||
memset(large_buf, 'x', 2048);
|
||||
Data data(large_buf, 2048);
|
||||
kak_assert(data.size() == 2048);
|
||||
kak_assert(data.capacity() >= 2048);
|
||||
|
||||
data.clear();
|
||||
kak_assert(data.size() == 0);
|
||||
kak_assert(not data.is_long());
|
||||
kak_assert(data.capacity() == 23);
|
||||
}
|
||||
}};
|
||||
|
||||
const String String::ms_empty;
|
||||
|
||||
}
|
||||
|
@ -1,12 +1,13 @@
|
||||
#ifndef string_hh_INCLUDED
|
||||
#define string_hh_INCLUDED
|
||||
|
||||
#include <climits>
|
||||
#include <cstddef>
|
||||
#include "memory.hh"
|
||||
#include "hash.hh"
|
||||
#include "units.hh"
|
||||
#include "utf8.hh"
|
||||
|
||||
#include <climits>
|
||||
|
||||
namespace Kakoune
|
||||
{
|
||||
@ -156,17 +157,22 @@ public:
|
||||
|
||||
// String data storage using small string optimization.
|
||||
//
|
||||
// the LSB of the last byte is used to flag if we are using the small buffer
|
||||
// or an allocated one. On big endian systems that means the allocated
|
||||
// capacity must be pair, on little endian systems that means the allocated
|
||||
// capacity cannot use its most significant byte, so we effectively limit
|
||||
// capacity to 2^24 on 32bit arch, and 2^60 on 64.
|
||||
// The MSB of the last byte is used to flag if we are using the allocated buffer
|
||||
// (1) or in-situ storage, the small one (0). That means the allocated capacity
|
||||
// cannot use its most significant byte, so we effectively limit capacity to
|
||||
// 2^24 on 32bit arch, and 2^56 on 64bit.
|
||||
//
|
||||
// There is also a special NoCopy mode in which the data referred to is un-owned.
|
||||
// It is indicated by being in Long mode with capacity == 0.
|
||||
struct Data
|
||||
{
|
||||
using Alloc = Allocator<char, MemoryDomain::String>;
|
||||
|
||||
Data() { set_empty(); }
|
||||
Data(NoCopy, const char* data, size_t size) : u{Long{const_cast<char*>(data), size, 0}} {}
|
||||
Data(NoCopy, const char* data, size_t size) : u{Long{const_cast<char*>(data),
|
||||
size,
|
||||
/*capacity=*/0,
|
||||
/*mode=*/Long::active_mask}} {}
|
||||
|
||||
Data(const char* data, size_t size, size_t capacity);
|
||||
Data(const char* data, size_t size) : Data(data, size, size) {}
|
||||
@ -177,8 +183,8 @@ public:
|
||||
Data& operator=(const Data& other);
|
||||
Data& operator=(Data&& other) noexcept;
|
||||
|
||||
bool is_long() const { return (u.s.size & 1) == 0; }
|
||||
size_t size() const { return is_long() ? u.l.size : (u.s.size >> 1); }
|
||||
bool is_long() const { return (u.l.mode& Long::active_mask) > 0; }
|
||||
size_t size() const { return is_long() ? u.l.size : (Short::capacity - u.s.remaining_size); }
|
||||
size_t capacity() const { return is_long() ? u.l.capacity : Short::capacity; }
|
||||
|
||||
const char* data() const { return is_long() ? u.l.ptr : u.s.string; }
|
||||
@ -194,20 +200,27 @@ public:
|
||||
private:
|
||||
struct Long
|
||||
{
|
||||
static constexpr size_t max_capacity =
|
||||
(size_t)1 << 8 * (sizeof(size_t) - 1);
|
||||
static constexpr size_t capacity_bits = CHAR_BIT * (sizeof(size_t) - 1);
|
||||
static constexpr size_t max_capacity = ((size_t)1 << capacity_bits) - 1;
|
||||
|
||||
char* ptr;
|
||||
size_t size;
|
||||
size_t capacity;
|
||||
size_t capacity : capacity_bits;
|
||||
unsigned char mode;
|
||||
static constexpr unsigned char active_mask = 0b1000'0000;
|
||||
};
|
||||
static_assert(sizeof(Long) == sizeof(char*) * 3);
|
||||
|
||||
struct Short
|
||||
{
|
||||
static constexpr size_t capacity = sizeof(Long) - 2;
|
||||
char string[capacity+1];
|
||||
unsigned char size;
|
||||
static constexpr size_t capacity = sizeof(Long) - 1;
|
||||
char string[capacity];
|
||||
// When string is full remaining_size will be 0 and be the null terminator.
|
||||
// When string is empty remaining size will be 23 (0b00010111)
|
||||
// and not collide with Long::active_mask.
|
||||
unsigned char remaining_size;
|
||||
};
|
||||
static_assert(offsetof(Long, mode) == offsetof(Short, remaining_size));
|
||||
|
||||
union
|
||||
{
|
||||
@ -217,11 +230,11 @@ public:
|
||||
|
||||
void release()
|
||||
{
|
||||
if (is_long() and u.l.capacity != 0)
|
||||
if (is_long() and (u.l.capacity != 0))
|
||||
Alloc{}.deallocate(u.l.ptr, u.l.capacity+1);
|
||||
}
|
||||
|
||||
void set_empty() { u.s.size = 1; u.s.string[0] = 0; }
|
||||
void set_empty() { u.s.remaining_size = Short::capacity; u.s.string[0] = '\0'; }
|
||||
void set_short(const char* data, size_t size);
|
||||
};
|
||||
|
||||
|
@ -202,7 +202,7 @@ InplaceString<23> to_string(Grouped val)
|
||||
InplaceString<23> res;
|
||||
for (int pos = 0, len = ungrouped.m_length; pos != len; ++pos)
|
||||
{
|
||||
if (res.m_length and ((len - pos) % 3) == 0)
|
||||
if (res.m_length and ((len - pos) % 3) == 0)
|
||||
res.m_data[res.m_length++] = ',';
|
||||
res.m_data[res.m_length++] = ungrouped.m_data[pos];
|
||||
}
|
||||
|
@ -144,10 +144,10 @@ decltype(auto) to_string(const StronglyTypedNumber<RealType, ValueType>& val)
|
||||
namespace detail
|
||||
{
|
||||
|
||||
template<typename T> requires std::is_convertible_v<T, StringView>
|
||||
template<typename T> requires std::is_convertible_v<T, StringView>
|
||||
StringView format_param(const T& val) { return val; }
|
||||
|
||||
template<typename T> requires (not std::is_convertible_v<T, StringView>)
|
||||
template<typename T> requires (not std::is_convertible_v<T, StringView>)
|
||||
decltype(auto) format_param(const T& val) { return to_string(val); }
|
||||
|
||||
}
|
||||
|
@ -377,7 +377,7 @@ void TerminalUI::Screen::output(bool force, bool synchronized, Writer& writer)
|
||||
{
|
||||
for (int line = 0; line < (int)size.line; ++line)
|
||||
{
|
||||
auto hash = hash_line(lines[line]);
|
||||
auto hash = hash_line(lines[line]);
|
||||
if (hash == hashes[line])
|
||||
continue;
|
||||
hashes[line] = hash;
|
||||
|
Loading…
Reference in New Issue
Block a user