1
1
mirror of https://github.com/mawww/kakoune.git synced 2024-08-17 00:30:26 +03:00

Compare commits

...

10 Commits

Author SHA1 Message Date
Bob
af35cb348a
Merge edafb6b968 into 374a7a8c99 2024-06-24 15:00:52 +03:00
Maxime Coste
374a7a8c99 Remove some selection copies in pipe and throw early if read only
Throwing early avoids losing selections in this case.
2024-06-24 21:18:17 +10:00
Maxime Coste
6674fe7587 Merge remote-tracking branch 'stacyharper/elixir-heex' 2024-06-23 11:14:34 +10:00
Maxime Coste
4a00a6edea Fix trailing whitespaces 2024-06-23 11:03:50 +10:00
Tobias Pisani
6493ddad42 Allow individual show-whitespace options to be turned off
This is especially useful to use the indent guides without the  other
parameters, but in general it can be a useful option.

It could be worth considering cleaning up these options to default off instead, but
the default also seems useful, so i consider this ok, as it might be the more advanced
usecase.
2024-06-23 11:03:03 +10:00
Maxime Coste
880ad98a30 Add a -script switch to the fifo to cater for grep/make use cases
Avoid the brittle `exit; %arg{@}` trick
2024-06-22 17:02:54 +10:00
Willow Barraco
0bb355557e
elixir: detect heex additionaly to leex 2024-06-19 12:45:20 +02:00
Maxime Coste
ba504431dc Small code style tweak 2024-06-15 16:09:14 +10:00
Maxime Coste
c4684d0d84 Store instruction pointers directly in ThreadedRegexVM::Thread
The previous tradeoff of having a very small Thread struct is not
necessary anymore as we do not memcpy Threads on swap_next since
d708b77186.

This requires offsets to be used instead of indices for jump/split
ops.
2024-06-15 14:36:33 +10:00
Maxime Coste
c84942c2ac Add perf-annotate highlighting to perf.kak 2024-06-15 11:45:19 +10:00
18 changed files with 129 additions and 91 deletions

View File

@ -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*:::

View File

@ -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

View File

@ -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 }
}

View File

@ -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}

View File

@ -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
}

View File

@ -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{@} # pass arguments for "$@" above, exit to avoid evaluating them
set-option buffer filetype make
set-option buffer jump_current_line 0
}

View File

@ -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);
}

View File

@ -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);

View File

@ -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;

View File

@ -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)
{

View File

@ -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,14 @@ 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};
m_program.instructions[--split_pos].param.split = CompiledRegex::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 +801,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 +810,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 +842,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 +861,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 +1041,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 +1081,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 +1105,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 +1145,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: [";

View File

@ -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

View File

@ -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);

View File

@ -6,7 +6,7 @@
namespace Kakoune
{
namespace
namespace
{
// Avoid including all of <algorithm> just for this.
constexpr auto max(auto lhs, auto rhs) { return lhs > rhs ? lhs : rhs;}
@ -69,7 +69,7 @@ void String::Data::reserve(size_t new_capacity)
if (current_capacity != 0 and new_capacity <= current_capacity)
return;
if (!is_long() and new_capacity <= Short::capacity)
if (!is_long() and new_capacity <= Short::capacity)
return;
kak_assert(new_capacity <= Long::max_capacity);

View File

@ -169,9 +169,9 @@ public:
using Alloc = Allocator<char, MemoryDomain::String>;
Data() { set_empty(); }
Data(NoCopy, const char* data, size_t size) : u{Long{const_cast<char*>(data),
Data(NoCopy, const char* data, size_t size) : u{Long{const_cast<char*>(data),
size,
/*capacity=*/0,
/*capacity=*/0,
/*mode=*/Long::active_mask}} {}
Data(const char* data, size_t size, size_t capacity);

View File

@ -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];
}

View File

@ -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); }
}

View File

@ -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;