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:
Maxime Coste 2011-12-02 14:28:27 +00:00
parent 8e06e168d9
commit 94d59cc4dd
8 changed files with 232 additions and 0 deletions

View File

@ -227,6 +227,9 @@ void Buffer::apply_modification(const BufferModification& modification)
void Buffer::append_modification(BufferModification&& modification)
{
for (auto filter : m_filters)
filter.second(*this, modification);
apply_modification(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);
}
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);
}
}

View File

@ -7,6 +7,10 @@
#include <memory>
#include "line_and_column.hh"
#include "filter.hh"
#include "exception.hh"
#include "completion.hh"
#include "idvaluemap.hh"
namespace Kakoune
{
@ -149,6 +153,18 @@ public:
void register_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
// iterator is on
BufferIterator iterator_at_line_begin(const BufferIterator& iterator) const;
@ -190,6 +206,8 @@ private:
size_t m_last_save_undo_index;
std::vector<BufferModificationListener*> m_modification_listeners;
idvaluemap<std::string, FilterFunc> m_filters;
};
}

18
src/filter.hh Normal file
View 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
View 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
View 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
View 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
View File

@ -0,0 +1,11 @@
#ifndef filters_hh_INCLUDED
#define filters_hh_INCLUDED
namespace Kakoune
{
void register_filters();
}
#endif // filters_hh_INCLUDED

View File

@ -9,6 +9,8 @@
#include "debug.hh"
#include "highlighters.hh"
#include "highlighter_registry.hh"
#include "filters.hh"
#include "filter_registry.hh"
#include "hooks_manager.hh"
#include <unordered_map>
@ -458,6 +460,32 @@ void rm_highlighter(const CommandParameters& params, const Context& context)
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)
{
if (params.size() < 3)
@ -678,6 +706,7 @@ int main(int argc, char* argv[])
BufferManager buffer_manager;
RegisterManager register_manager;
HighlighterRegistry highlighter_registry;
FilterRegistry filter_registry;
HooksManager hooks_manager;
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)
{ 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>{ "source" }, exec_commands_in_file,
PerArgumentCommandCompleter{ complete_filename });
register_highlighters();
register_filters();
try
{