summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorMaxime Coste <frrrwww@gmail.com>2011-12-02 14:28:27 +0000
committerMaxime Coste <frrrwww@gmail.com>2011-12-02 14:28:27 +0000
commit94d59cc4dd19a0e45b537ac854bf3fdaa6824679 (patch)
tree3462c3214123f9fc959700e668a34443bdee245a /src
parent8e06e168d9c699c102bc0ea729a0814411e47660 (diff)
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.
Diffstat (limited to 'src')
-rw-r--r--src/buffer.cc21
-rw-r--r--src/buffer.hh18
-rw-r--r--src/filter.hh18
-rw-r--r--src/filter_registry.cc38
-rw-r--r--src/filter_registry.hh42
-rw-r--r--src/filters.cc45
-rw-r--r--src/filters.hh11
-rw-r--r--src/main.cc39
8 files changed, 232 insertions, 0 deletions
diff --git a/src/buffer.cc b/src/buffer.cc
index 0587cf45..2d2e106c 100644
--- a/src/buffer.cc
+++ b/src/buffer.cc
@@ -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);
+}
+
}
diff --git a/src/buffer.hh b/src/buffer.hh
index 5a336f23..0221e46a 100644
--- a/src/buffer.hh
+++ b/src/buffer.hh
@@ -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;
};
}
diff --git a/src/filter.hh b/src/filter.hh
new file mode 100644
index 00000000..3368ee5c
--- /dev/null
+++ b/src/filter.hh
@@ -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
diff --git a/src/filter_registry.cc b/src/filter_registry.cc
new file mode 100644
index 00000000..d6a71ba0
--- /dev/null
+++ b/src/filter_registry.cc
@@ -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);
+}
+
+}
diff --git a/src/filter_registry.hh b/src/filter_registry.hh
new file mode 100644
index 00000000..4fbb8df7
--- /dev/null
+++ b/src/filter_registry.hh
@@ -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
+
diff --git a/src/filters.cc b/src/filters.cc
new file mode 100644
index 00000000..73ae91cb
--- /dev/null
+++ b/src/filters.cc
@@ -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"));
+}
+
+}
diff --git a/src/filters.hh b/src/filters.hh
new file mode 100644
index 00000000..0d88da46
--- /dev/null
+++ b/src/filters.hh
@@ -0,0 +1,11 @@
+#ifndef filters_hh_INCLUDED
+#define filters_hh_INCLUDED
+
+namespace Kakoune
+{
+
+void register_filters();
+
+}
+
+#endif // filters_hh_INCLUDED
diff --git a/src/main.cc b/src/main.cc
index 36cf3445..3b290854 100644
--- a/src/main.cc
+++ b/src/main.cc
@@ -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
{