Buffer: add filter support
filters are functions called prior to applying a modification to a buffer. They can manipulate the modification to change the editor behaviour.
This commit is contained in:
parent
8e06e168d9
commit
94d59cc4dd
|
@ -227,6 +227,9 @@ void Buffer::apply_modification(const BufferModification& modification)
|
||||||
|
|
||||||
void Buffer::append_modification(BufferModification&& modification)
|
void Buffer::append_modification(BufferModification&& modification)
|
||||||
{
|
{
|
||||||
|
for (auto filter : m_filters)
|
||||||
|
filter.second(*this, modification);
|
||||||
|
|
||||||
apply_modification(modification);
|
apply_modification(modification);
|
||||||
m_current_undo_group.push_back(std::move(modification));
|
m_current_undo_group.push_back(std::move(modification));
|
||||||
}
|
}
|
||||||
|
@ -277,4 +280,22 @@ void Buffer::unregister_modification_listener(BufferModificationListener* listen
|
||||||
m_modification_listeners.erase(it);
|
m_modification_listeners.erase(it);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Buffer::add_filter(FilterAndId&& filter)
|
||||||
|
{
|
||||||
|
if (m_filters.contains(filter.first))
|
||||||
|
throw filter_id_not_unique(filter.first);
|
||||||
|
m_filters.append(filter);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Buffer::remove_filter(const std::string& id)
|
||||||
|
{
|
||||||
|
m_filters.remove(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
CandidateList Buffer::complete_filterid(const std::string& prefix,
|
||||||
|
size_t cursor_pos)
|
||||||
|
{
|
||||||
|
return m_filters.complete_id<str_to_str>(prefix, cursor_pos);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,10 @@
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
#include "line_and_column.hh"
|
#include "line_and_column.hh"
|
||||||
|
#include "filter.hh"
|
||||||
|
#include "exception.hh"
|
||||||
|
#include "completion.hh"
|
||||||
|
#include "idvaluemap.hh"
|
||||||
|
|
||||||
namespace Kakoune
|
namespace Kakoune
|
||||||
{
|
{
|
||||||
|
@ -149,6 +153,18 @@ public:
|
||||||
void register_modification_listener(BufferModificationListener* listener);
|
void register_modification_listener(BufferModificationListener* listener);
|
||||||
void unregister_modification_listener(BufferModificationListener* listener);
|
void unregister_modification_listener(BufferModificationListener* listener);
|
||||||
|
|
||||||
|
struct filter_id_not_unique : public runtime_error
|
||||||
|
{
|
||||||
|
filter_id_not_unique(const std::string& id)
|
||||||
|
: runtime_error("filter id not unique: " + id) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
void add_filter(FilterAndId&& filter);
|
||||||
|
void remove_filter(const std::string& id);
|
||||||
|
|
||||||
|
CandidateList complete_filterid(const std::string& prefix,
|
||||||
|
size_t cursor_pos = std::string::npos);
|
||||||
|
|
||||||
// returns an iterator pointing to the first character of the line
|
// returns an iterator pointing to the first character of the line
|
||||||
// iterator is on
|
// iterator is on
|
||||||
BufferIterator iterator_at_line_begin(const BufferIterator& iterator) const;
|
BufferIterator iterator_at_line_begin(const BufferIterator& iterator) const;
|
||||||
|
@ -190,6 +206,8 @@ private:
|
||||||
size_t m_last_save_undo_index;
|
size_t m_last_save_undo_index;
|
||||||
|
|
||||||
std::vector<BufferModificationListener*> m_modification_listeners;
|
std::vector<BufferModificationListener*> m_modification_listeners;
|
||||||
|
|
||||||
|
idvaluemap<std::string, FilterFunc> m_filters;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
18
src/filter.hh
Normal file
18
src/filter.hh
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
#ifndef filter_hh_INCLUDED
|
||||||
|
#define filter_hh_INCLUDED
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
|
namespace Kakoune
|
||||||
|
{
|
||||||
|
|
||||||
|
class Buffer;
|
||||||
|
class BufferModification;
|
||||||
|
|
||||||
|
typedef std::function<void (Buffer& buffer, BufferModification& modification)> FilterFunc;
|
||||||
|
typedef std::pair<std::string, FilterFunc> FilterAndId;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // filter_hh_INCLUDED
|
38
src/filter_registry.cc
Normal file
38
src/filter_registry.cc
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
#include "filter_registry.hh"
|
||||||
|
|
||||||
|
#include "exception.hh"
|
||||||
|
#include "buffer.hh"
|
||||||
|
|
||||||
|
namespace Kakoune
|
||||||
|
{
|
||||||
|
|
||||||
|
struct factory_not_found : public runtime_error
|
||||||
|
{
|
||||||
|
factory_not_found() : runtime_error("filter factory not found") {}
|
||||||
|
};
|
||||||
|
|
||||||
|
void FilterRegistry::register_factory(const std::string& name,
|
||||||
|
const FilterFactory& factory)
|
||||||
|
{
|
||||||
|
assert(not m_factories.contains(name));
|
||||||
|
m_factories.append(std::make_pair(name, factory));
|
||||||
|
}
|
||||||
|
|
||||||
|
void FilterRegistry::add_filter_to_buffer(Buffer& buffer,
|
||||||
|
const std::string& name,
|
||||||
|
const FilterParameters& parameters)
|
||||||
|
{
|
||||||
|
auto it = m_factories.find(name);
|
||||||
|
if (it == m_factories.end())
|
||||||
|
throw factory_not_found();
|
||||||
|
|
||||||
|
buffer.add_filter(it->second(buffer, parameters));
|
||||||
|
}
|
||||||
|
|
||||||
|
CandidateList FilterRegistry::complete_filter(const std::string& prefix,
|
||||||
|
size_t cursor_pos)
|
||||||
|
{
|
||||||
|
return m_factories.complete_id<str_to_str>(prefix, cursor_pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
42
src/filter_registry.hh
Normal file
42
src/filter_registry.hh
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
#ifndef filter_registry_h_INCLUDED
|
||||||
|
#define filter_registry_h_INCLUDED
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
|
#include "filter.hh"
|
||||||
|
#include "utils.hh"
|
||||||
|
#include "completion.hh"
|
||||||
|
#include "idvaluemap.hh"
|
||||||
|
|
||||||
|
namespace Kakoune
|
||||||
|
{
|
||||||
|
|
||||||
|
class Window;
|
||||||
|
|
||||||
|
typedef std::vector<std::string> FilterParameters;
|
||||||
|
|
||||||
|
typedef std::function<FilterAndId (Buffer& buffer,
|
||||||
|
const FilterParameters& params)> FilterFactory;
|
||||||
|
|
||||||
|
class FilterRegistry : public Singleton<FilterRegistry>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
void register_factory(const std::string& name,
|
||||||
|
const FilterFactory& factory);
|
||||||
|
|
||||||
|
void add_filter_to_buffer(Buffer& window,
|
||||||
|
const std::string& factory_name,
|
||||||
|
const FilterParameters& parameters);
|
||||||
|
|
||||||
|
CandidateList complete_filter(const std::string& prefix,
|
||||||
|
size_t cursor_pos);
|
||||||
|
|
||||||
|
private:
|
||||||
|
idvaluemap<std::string, FilterFactory> m_factories;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // filter_registry_h_INCLUDED
|
||||||
|
|
45
src/filters.cc
Normal file
45
src/filters.cc
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
#include "filters.hh"
|
||||||
|
#include "filter_registry.hh"
|
||||||
|
#include "buffer.hh"
|
||||||
|
|
||||||
|
namespace Kakoune
|
||||||
|
{
|
||||||
|
|
||||||
|
void preserve_indent(Buffer& buffer, BufferModification& modification)
|
||||||
|
{
|
||||||
|
if (modification.type == BufferModification::Insert and
|
||||||
|
modification.content == "\n")
|
||||||
|
{
|
||||||
|
BufferIterator line_begin = buffer.iterator_at_line_begin(modification.position - 1);
|
||||||
|
BufferIterator first_non_white = line_begin;
|
||||||
|
while ((*first_non_white == '\t' or *first_non_white == ' ') and
|
||||||
|
not first_non_white.is_end())
|
||||||
|
++first_non_white;
|
||||||
|
|
||||||
|
modification.content += buffer.string(line_begin, first_non_white);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template<void (*filter_func)(Buffer&, BufferModification&)>
|
||||||
|
class SimpleFilterFactory
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
SimpleFilterFactory(const std::string& id) : m_id(id) {}
|
||||||
|
|
||||||
|
FilterAndId operator()(Buffer& buffer,
|
||||||
|
const FilterParameters& params) const
|
||||||
|
{
|
||||||
|
return FilterAndId(m_id, FilterFunc(filter_func));
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
std::string m_id;
|
||||||
|
};
|
||||||
|
|
||||||
|
void register_filters()
|
||||||
|
{
|
||||||
|
FilterRegistry& registry = FilterRegistry::instance();
|
||||||
|
|
||||||
|
registry.register_factory("preserve_indent", SimpleFilterFactory<preserve_indent>("preserve_indent"));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
11
src/filters.hh
Normal file
11
src/filters.hh
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
#ifndef filters_hh_INCLUDED
|
||||||
|
#define filters_hh_INCLUDED
|
||||||
|
|
||||||
|
namespace Kakoune
|
||||||
|
{
|
||||||
|
|
||||||
|
void register_filters();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // filters_hh_INCLUDED
|
39
src/main.cc
39
src/main.cc
|
@ -9,6 +9,8 @@
|
||||||
#include "debug.hh"
|
#include "debug.hh"
|
||||||
#include "highlighters.hh"
|
#include "highlighters.hh"
|
||||||
#include "highlighter_registry.hh"
|
#include "highlighter_registry.hh"
|
||||||
|
#include "filters.hh"
|
||||||
|
#include "filter_registry.hh"
|
||||||
#include "hooks_manager.hh"
|
#include "hooks_manager.hh"
|
||||||
|
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
|
@ -458,6 +460,32 @@ void rm_highlighter(const CommandParameters& params, const Context& context)
|
||||||
context.window->remove_highlighter(params[0]);
|
context.window->remove_highlighter(params[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void add_filter(const CommandParameters& params, const Context& context)
|
||||||
|
{
|
||||||
|
if (params.size() < 1)
|
||||||
|
throw wrong_argument_count();
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
FilterRegistry& registry = FilterRegistry::instance();
|
||||||
|
FilterParameters filter_params(params.begin()+1, params.end());
|
||||||
|
registry.add_filter_to_buffer(*context.buffer, params[0],
|
||||||
|
filter_params);
|
||||||
|
}
|
||||||
|
catch (runtime_error& err)
|
||||||
|
{
|
||||||
|
print_status("error: " + err.description());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void rm_filter(const CommandParameters& params, const Context& context)
|
||||||
|
{
|
||||||
|
if (params.size() != 1)
|
||||||
|
throw wrong_argument_count();
|
||||||
|
|
||||||
|
context.buffer->remove_filter(params[0]);
|
||||||
|
}
|
||||||
|
|
||||||
void add_hook(const CommandParameters& params, const Context& context)
|
void add_hook(const CommandParameters& params, const Context& context)
|
||||||
{
|
{
|
||||||
if (params.size() < 3)
|
if (params.size() < 3)
|
||||||
|
@ -678,6 +706,7 @@ int main(int argc, char* argv[])
|
||||||
BufferManager buffer_manager;
|
BufferManager buffer_manager;
|
||||||
RegisterManager register_manager;
|
RegisterManager register_manager;
|
||||||
HighlighterRegistry highlighter_registry;
|
HighlighterRegistry highlighter_registry;
|
||||||
|
FilterRegistry filter_registry;
|
||||||
HooksManager hooks_manager;
|
HooksManager hooks_manager;
|
||||||
|
|
||||||
command_manager.register_command(std::vector<std::string>{ "e", "edit" }, edit,
|
command_manager.register_command(std::vector<std::string>{ "e", "edit" }, edit,
|
||||||
|
@ -701,12 +730,22 @@ int main(int argc, char* argv[])
|
||||||
[&](const std::string& prefix, size_t cursor_pos)
|
[&](const std::string& prefix, size_t cursor_pos)
|
||||||
{ return main_context.window->complete_highlighterid(prefix, cursor_pos); }
|
{ return main_context.window->complete_highlighterid(prefix, cursor_pos); }
|
||||||
});
|
});
|
||||||
|
command_manager.register_command(std::vector<std::string>{ "af", "addfilter" }, add_filter,
|
||||||
|
PerArgumentCommandCompleter {
|
||||||
|
std::bind(&FilterRegistry::complete_filter, &filter_registry, _1, _2)
|
||||||
|
});
|
||||||
|
command_manager.register_command(std::vector<std::string>{ "rf", "rmfilter" }, rm_filter,
|
||||||
|
PerArgumentCommandCompleter {
|
||||||
|
[&](const std::string& prefix, size_t cursor_pos)
|
||||||
|
{ return main_context.buffer->complete_filterid(prefix, cursor_pos); }
|
||||||
|
});
|
||||||
command_manager.register_command(std::vector<std::string>{ "hook" }, add_hook);
|
command_manager.register_command(std::vector<std::string>{ "hook" }, add_hook);
|
||||||
|
|
||||||
command_manager.register_command(std::vector<std::string>{ "source" }, exec_commands_in_file,
|
command_manager.register_command(std::vector<std::string>{ "source" }, exec_commands_in_file,
|
||||||
PerArgumentCommandCompleter{ complete_filename });
|
PerArgumentCommandCompleter{ complete_filename });
|
||||||
|
|
||||||
register_highlighters();
|
register_highlighters();
|
||||||
|
register_filters();
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in New Issue
Block a user