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 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 namespace
{ {
@ -1289,6 +1279,21 @@ const CommandDesc set_option_cmd = {
} }
}; };
Completions complete_option(const Context& context, CompletionFlags,
CommandParameters params, size_t token_to_complete,
ByteCount pos_in_token)
{
if (token_to_complete == 0)
{
static constexpr auto scopes = { "buffer", "window", "current" };
return { 0_byte, params[0].length(), complete(params[0], pos_in_token, scopes) };
}
else if (token_to_complete == 1)
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 = { const CommandDesc unset_option_cmd = {
"unset-option", "unset-option",
"unset", "unset",
@ -1298,20 +1303,7 @@ const CommandDesc unset_option_cmd = {
ParameterDesc{ {}, ParameterDesc::Flags::None, 2, 2 }, ParameterDesc{ {}, ParameterDesc::Flags::None, 2, 2 },
CommandFlags::None, CommandFlags::None,
option_doc_helper, option_doc_helper,
[](const Context& context, CompletionFlags, complete_option,
CommandParameters params, size_t token_to_complete,
ByteCount pos_in_token) -> Completions
{
if (token_to_complete == 0)
{
static constexpr auto scopes = { "buffer", "window", "current" };
return { 0_byte, params[0].length(), complete(params[0], pos_in_token, scopes) };
}
else if (token_to_complete == 1)
return { 0_byte, params[1].length(),
GlobalScope::instance().option_registry().complete_option_name(params[1], pos_in_token) };
return Completions{};
},
[](const ParametersParser& parser, Context& context, const ShellContext&) [](const ParametersParser& parser, Context& context, const ShellContext&)
{ {
auto& options = get_options(parser[0], context, parser[1]); 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 = { const CommandDesc declare_option_cmd = {
"declare-option", "declare-option",
"decl", "decl",
@ -2142,6 +2152,7 @@ void register_commands()
register_command(source_cmd); register_command(source_cmd);
register_command(set_option_cmd); register_command(set_option_cmd);
register_command(unset_option_cmd); register_command(unset_option_cmd);
register_command(update_option_cmd);
register_command(declare_option_cmd); register_command(declare_option_cmd);
register_command(map_key_cmd); register_command(map_key_cmd);
register_command(unmap_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 struct FlagLinesHighlighter : Highlighter
{ {
FlagLinesHighlighter(String option_name, String default_face) FlagLinesHighlighter(String option_name, String default_face)
@ -1184,8 +1225,6 @@ struct FlagLinesHighlighter : Highlighter
m_option_name{std::move(option_name)}, m_option_name{std::move(option_name)},
m_default_face{std::move(default_face)} {} m_default_face{std::move(default_face)} {}
using LineAndSpecList = TimestampedList<LineAndSpec>;
static HighlighterAndId create(HighlighterParameters params) static HighlighterAndId create(HighlighterParameters params)
{ {
if (params.size() != 2) if (params.size() != 2)
@ -1207,7 +1246,7 @@ private:
{ {
auto& line_flags = context.options()[m_option_name].get_mutable<LineAndSpecList>(); auto& line_flags = context.options()[m_option_name].get_mutable<LineAndSpecList>();
auto& buffer = context.buffer(); 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); auto def_face = get_face(m_default_face);
Vector<DisplayLine> display_lines; Vector<DisplayLine> display_lines;
@ -1257,7 +1296,7 @@ private:
{ {
auto& line_flags = context.options()[m_option_name].get_mutable<LineAndSpecList>(); auto& line_flags = context.options()[m_option_name].get_mutable<LineAndSpecList>();
auto& buffer = context.buffer(); auto& buffer = context.buffer();
update_line_flags_ifn(buffer, line_flags); update_line_specs_ifn(buffer, line_flags);
ColumnCount width = 0; ColumnCount width = 0;
try try
@ -1274,42 +1313,6 @@ private:
setup.window_range.column -= width; 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_option_name;
String m_default_face; 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_first(RangeAndString& r) { return std::get<0>(r).first; }
BufferCoord& get_last(RangeAndString& r) { return std::get<0>(r).last; } 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()) if (range_and_faces.prefix == buffer.timestamp())
return; return;
@ -1373,6 +1376,11 @@ static void update_ranges_ifn(const Buffer& buffer, TimestampedList<RangeAndStri
range_and_faces.prefix = buffer.timestamp(); range_and_faces.prefix = buffer.timestamp();
} }
void option_update(RangeAndStringList& opt, const Context& context)
{
update_ranges_ifn(context.buffer(), opt);
}
struct RangesHighlighter : Highlighter struct RangesHighlighter : Highlighter
{ {
RangesHighlighter(String option_name) RangesHighlighter(String option_name)
@ -1386,7 +1394,7 @@ struct RangesHighlighter : Highlighter
const String& option_name = params[0]; const String& option_name = params[0];
// throw if wrong option type // 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)}; 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 void do_highlight(const Context& context, HighlightPass, DisplayBuffer& display_buffer, BufferRange) override
{ {
auto& buffer = context.buffer(); 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); update_ranges_ifn(buffer, range_and_faces);
for (auto& range : range_and_faces.list) for (auto& range : range_and_faces.list)
@ -1428,7 +1436,7 @@ struct ReplaceRangesHighlighter : Highlighter
const String& option_name = params[0]; const String& option_name = params[0];
// throw if wrong option type // 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)}; 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 void do_highlight(const Context& context, HighlightPass, DisplayBuffer& display_buffer, BufferRange) override
{ {
auto& buffer = context.buffer(); 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); update_ranges_ifn(buffer, range_and_faces);
for (auto& range : range_and_faces.list) for (auto& range : range_and_faces.list)

View File

@ -3,6 +3,7 @@
#include "color.hh" #include "color.hh"
#include "highlighter.hh" #include "highlighter.hh"
#include "option.hh"
namespace Kakoune namespace Kakoune
{ {
@ -19,7 +20,22 @@ String option_to_string(InclusiveBufferRange range);
void option_from_string(StringView str, InclusiveBufferRange& opt); void option_from_string(StringView str, InclusiveBufferRange& opt);
using LineAndSpec = std::tuple<LineCount, String>; 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 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 OptionManager;
class Context;
enum class OptionFlags enum class OptionFlags
{ {
@ -52,6 +53,7 @@ public:
virtual String get_as_string() const = 0; virtual String get_as_string() const = 0;
virtual void set_from_string(StringView str) = 0; virtual void set_from_string(StringView str) = 0;
virtual void add_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; virtual Option* clone(OptionManager& manager) const = 0;
OptionManager& manager() const { return m_manager; } OptionManager& manager() const { return m_manager; }
@ -140,6 +142,10 @@ public:
if (option_add(m_value, str)) if (option_add(m_value, str))
m_manager.on_option_changed(*this); m_manager.on_option_changed(*this);
} }
void update(const Context& context) override
{
option_update(m_value, context);
}
private: private:
virtual void validate(const T& value) const {} virtual void validate(const T& value) const {}
T m_value; 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"); 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> template<typename EffectiveType, typename LineType, typename ColumnType>
inline void option_from_string(StringView str, LineAndColumn<EffectiveType, LineType, ColumnType>& opt) inline void option_from_string(StringView str, LineAndColumn<EffectiveType, LineType, ColumnType>& opt)
{ {