Add an update-option command to update range-descs/line-descs options

update-option will make the range-descs and line-descs option up to
date with the latest buffer modfications, changing the ranges/lines
to where they moved according the modifications since the timestamp
on the option.
This commit is contained in:
Maxime Coste 2017-05-25 08:38:11 +01:00
parent f014eb7052
commit 83d85df26e
5 changed files with 115 additions and 69 deletions

View File

@ -43,16 +43,6 @@
namespace Kakoune
{
StringView option_type_name(Meta::Type<TimestampedList<LineAndSpec>>)
{
return "line-specs";
}
StringView option_type_name(Meta::Type<TimestampedList<RangeAndString>>)
{
return "range-specs";
}
namespace
{
@ -1289,18 +1279,9 @@ const CommandDesc set_option_cmd = {
}
};
const CommandDesc unset_option_cmd = {
"unset-option",
"unset",
"unset-option <scope> <name>: remove <name> option from scope, falling back on parent scope value"
"<scope> can be buffer, window, or current which refers to the narrowest\n"
"scope the option is set in",
ParameterDesc{ {}, ParameterDesc::Flags::None, 2, 2 },
CommandFlags::None,
option_doc_helper,
[](const Context& context, CompletionFlags,
Completions complete_option(const Context& context, CompletionFlags,
CommandParameters params, size_t token_to_complete,
ByteCount pos_in_token) -> Completions
ByteCount pos_in_token)
{
if (token_to_complete == 0)
{
@ -1311,7 +1292,18 @@ const CommandDesc unset_option_cmd = {
return { 0_byte, params[1].length(),
GlobalScope::instance().option_registry().complete_option_name(params[1], pos_in_token) };
return Completions{};
},
}
const CommandDesc unset_option_cmd = {
"unset-option",
"unset",
"unset-option <scope> <name>: remove <name> option from scope, falling back on parent scope value"
"<scope> can be buffer, window, or current which refers to the narrowest\n"
"scope the option is set in",
ParameterDesc{ {}, ParameterDesc::Flags::None, 2, 2 },
CommandFlags::None,
option_doc_helper,
complete_option,
[](const ParametersParser& parser, Context& context, const ShellContext&)
{
auto& options = get_options(parser[0], context, parser[1]);
@ -1321,6 +1313,24 @@ const CommandDesc unset_option_cmd = {
}
};
const CommandDesc update_option_cmd = {
"update-option",
nullptr,
"update-option <scope> <name>: update <name> option from scope\n"
"some option types, such as line-descs or range-descs can be updated to latest buffer timestamp\n"
"<scope> can be buffer, window, or current which refers to the narrowest\n"
"scope the option is set in",
ParameterDesc{ {}, ParameterDesc::Flags::None, 2, 2 },
CommandFlags::None,
option_doc_helper,
complete_option,
[](const ParametersParser& parser, Context& context, const ShellContext&)
{
Option& opt = get_options(parser[0], context, parser[1]).get_local_option(parser[1]);
opt.update(context);
}
};
const CommandDesc declare_option_cmd = {
"declare-option",
"decl",
@ -2142,6 +2152,7 @@ void register_commands()
register_command(source_cmd);
register_command(set_option_cmd);
register_command(unset_option_cmd);
register_command(update_option_cmd);
register_command(declare_option_cmd);
register_command(map_key_cmd);
register_command(unmap_key_cmd);

View File

@ -1177,6 +1177,47 @@ void expand_unprintable(const Context& context, HighlightPass, DisplayBuffer& di
}
}
static void update_line_specs_ifn(const Buffer& buffer, LineAndSpecList& line_flags)
{
if (line_flags.prefix == buffer.timestamp())
return;
auto& lines = line_flags.list;
std::sort(lines.begin(), lines.end(),
[](const LineAndSpec& lhs, const LineAndSpec& rhs)
{ return std::get<0>(lhs) < std::get<0>(rhs); });
auto modifs = compute_line_modifications(buffer, line_flags.prefix);
auto ins_pos = lines.begin();
for (auto it = lines.begin(); it != lines.end(); ++it)
{
auto& line = std::get<0>(*it); // that line is 1 based as it comes from user side
auto modif_it = std::upper_bound(modifs.begin(), modifs.end(), line-1,
[](const LineCount& l, const LineModification& c)
{ return l < c.old_line; });
if (modif_it != modifs.begin())
{
auto& prev = *(modif_it-1);
if (line-1 < prev.old_line + prev.num_removed)
continue; // line removed
line += prev.diff();
}
if (ins_pos != it)
*ins_pos = std::move(*it);
++ins_pos;
}
lines.erase(ins_pos, lines.end());
line_flags.prefix = buffer.timestamp();
}
void option_update(LineAndSpecList& opt, const Context& context)
{
update_line_specs_ifn(context.buffer(), opt);
}
struct FlagLinesHighlighter : Highlighter
{
FlagLinesHighlighter(String option_name, String default_face)
@ -1184,8 +1225,6 @@ struct FlagLinesHighlighter : Highlighter
m_option_name{std::move(option_name)},
m_default_face{std::move(default_face)} {}
using LineAndSpecList = TimestampedList<LineAndSpec>;
static HighlighterAndId create(HighlighterParameters params)
{
if (params.size() != 2)
@ -1207,7 +1246,7 @@ private:
{
auto& line_flags = context.options()[m_option_name].get_mutable<LineAndSpecList>();
auto& buffer = context.buffer();
update_line_flags_ifn(buffer, line_flags);
update_line_specs_ifn(buffer, line_flags);
auto def_face = get_face(m_default_face);
Vector<DisplayLine> display_lines;
@ -1257,7 +1296,7 @@ private:
{
auto& line_flags = context.options()[m_option_name].get_mutable<LineAndSpecList>();
auto& buffer = context.buffer();
update_line_flags_ifn(buffer, line_flags);
update_line_specs_ifn(buffer, line_flags);
ColumnCount width = 0;
try
@ -1274,42 +1313,6 @@ private:
setup.window_range.column -= width;
}
void update_line_flags_ifn(const Buffer& buffer, LineAndSpecList& line_flags)
{
if (line_flags.prefix == buffer.timestamp())
return;
auto& lines = line_flags.list;
std::sort(lines.begin(), lines.end(),
[](const LineAndSpec& lhs, const LineAndSpec& rhs)
{ return std::get<0>(lhs) < std::get<0>(rhs); });
auto modifs = compute_line_modifications(buffer, line_flags.prefix);
auto ins_pos = lines.begin();
for (auto it = lines.begin(); it != lines.end(); ++it)
{
auto& line = std::get<0>(*it); // that line is 1 based as it comes from user side
auto modif_it = std::upper_bound(modifs.begin(), modifs.end(), line-1,
[](const LineCount& l, const LineModification& c)
{ return l < c.old_line; });
if (modif_it != modifs.begin())
{
auto& prev = *(modif_it-1);
if (line-1 < prev.old_line + prev.num_removed)
continue; // line removed
line += prev.diff();
}
if (ins_pos != it)
*ins_pos = std::move(*it);
++ins_pos;
}
lines.erase(ins_pos, lines.end());
line_flags.prefix = buffer.timestamp();
}
String m_option_name;
String m_default_face;
};
@ -1348,7 +1351,7 @@ void option_from_string(StringView str, InclusiveBufferRange& opt)
BufferCoord& get_first(RangeAndString& r) { return std::get<0>(r).first; }
BufferCoord& get_last(RangeAndString& r) { return std::get<0>(r).last; }
static void update_ranges_ifn(const Buffer& buffer, TimestampedList<RangeAndString>& range_and_faces)
static void update_ranges_ifn(const Buffer& buffer, RangeAndStringList& range_and_faces)
{
if (range_and_faces.prefix == buffer.timestamp())
return;
@ -1373,6 +1376,11 @@ static void update_ranges_ifn(const Buffer& buffer, TimestampedList<RangeAndStri
range_and_faces.prefix = buffer.timestamp();
}
void option_update(RangeAndStringList& opt, const Context& context)
{
update_ranges_ifn(context.buffer(), opt);
}
struct RangesHighlighter : Highlighter
{
RangesHighlighter(String option_name)
@ -1386,7 +1394,7 @@ struct RangesHighlighter : Highlighter
const String& option_name = params[0];
// throw if wrong option type
GlobalScope::instance().options()[option_name].get<TimestampedList<RangeAndString>>();
GlobalScope::instance().options()[option_name].get<RangeAndStringList>();
return {"hlranges_" + params[0], make_unique<RangesHighlighter>(option_name)};
}
@ -1395,7 +1403,7 @@ private:
void do_highlight(const Context& context, HighlightPass, DisplayBuffer& display_buffer, BufferRange) override
{
auto& buffer = context.buffer();
auto& range_and_faces = context.options()[m_option_name].get_mutable<TimestampedList<RangeAndString>>();
auto& range_and_faces = context.options()[m_option_name].get_mutable<RangeAndStringList>();
update_ranges_ifn(buffer, range_and_faces);
for (auto& range : range_and_faces.list)
@ -1428,7 +1436,7 @@ struct ReplaceRangesHighlighter : Highlighter
const String& option_name = params[0];
// throw if wrong option type
GlobalScope::instance().options()[option_name].get<TimestampedList<RangeAndString>>();
GlobalScope::instance().options()[option_name].get<RangeAndStringList>();
return {"replace_ranges_" + params[0], make_unique<ReplaceRangesHighlighter>(option_name)};
}
@ -1437,7 +1445,7 @@ private:
void do_highlight(const Context& context, HighlightPass, DisplayBuffer& display_buffer, BufferRange) override
{
auto& buffer = context.buffer();
auto& range_and_faces = context.options()[m_option_name].get_mutable<TimestampedList<RangeAndString>>();
auto& range_and_faces = context.options()[m_option_name].get_mutable<RangeAndStringList>();
update_ranges_ifn(buffer, range_and_faces);
for (auto& range : range_and_faces.list)

View File

@ -3,6 +3,7 @@
#include "color.hh"
#include "highlighter.hh"
#include "option.hh"
namespace Kakoune
{
@ -19,7 +20,22 @@ String option_to_string(InclusiveBufferRange range);
void option_from_string(StringView str, InclusiveBufferRange& opt);
using LineAndSpec = std::tuple<LineCount, String>;
using LineAndSpecList = TimestampedList<LineAndSpec>;
constexpr StringView option_type_name(Meta::Type<LineAndSpecList>)
{
return "line-specs";
}
void option_update(LineAndSpecList& opt, const Context& context);
using RangeAndString = std::tuple<InclusiveBufferRange, String>;
using RangeAndStringList = TimestampedList<RangeAndString>;
constexpr StringView option_type_name(Meta::Type<RangeAndStringList>)
{
return "range-specs";
}
void option_update(RangeAndStringList& opt, const Context& context);
}

View File

@ -14,6 +14,7 @@ namespace Kakoune
{
class OptionManager;
class Context;
enum class OptionFlags
{
@ -52,6 +53,7 @@ public:
virtual String get_as_string() const = 0;
virtual void set_from_string(StringView str) = 0;
virtual void add_from_string(StringView str) = 0;
virtual void update(const Context& context) = 0;
virtual Option* clone(OptionManager& manager) const = 0;
OptionManager& manager() const { return m_manager; }
@ -140,6 +142,10 @@ public:
if (option_add(m_value, str))
m_manager.on_option_changed(*this);
}
void update(const Context& context) override
{
option_update(m_value, context);
}
private:
virtual void validate(const T& value) const {}
T m_value;

View File

@ -218,6 +218,11 @@ inline bool option_add(WorstMatch, StringView)
throw runtime_error("no add operation supported for this option type");
}
inline void option_update(WorstMatch, const Context&)
{
throw runtime_error("no update operation supported for this option type");
}
template<typename EffectiveType, typename LineType, typename ColumnType>
inline void option_from_string(StringView str, LineAndColumn<EffectiveType, LineType, ColumnType>& opt)
{