summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--doc/pages/execeval.asciidoc5
-rw-r--r--doc/pages/scopes.asciidoc22
-rw-r--r--rc/windowing/detection.kak25
-rw-r--r--src/alias_registry.hh3
-rw-r--r--src/commands.cc28
-rw-r--r--src/context.cc8
-rw-r--r--src/context.hh8
-rw-r--r--src/face_registry.hh2
-rw-r--r--src/highlighter_group.hh2
-rw-r--r--src/hook_manager.hh2
-rw-r--r--src/insert_completer.cc5
-rw-r--r--src/keymap_manager.hh2
-rw-r--r--src/option_manager.cc9
-rw-r--r--src/option_manager.hh2
-rw-r--r--src/scope.cc10
-rw-r--r--src/scope.hh2
16 files changed, 99 insertions, 36 deletions
diff --git a/doc/pages/execeval.asciidoc b/doc/pages/execeval.asciidoc
index 4606c634..98ad8cf0 100644
--- a/doc/pages/execeval.asciidoc
+++ b/doc/pages/execeval.asciidoc
@@ -64,3 +64,8 @@ are then restored when the keys have been executed: */*, *"*, *|*, *^*,
*-with-hooks*::
Execute keys and trigger existing hooks.
(See <<hooks#,`:doc hooks`>>)
+
+== Local scope in *evaluate-commands*
+
+When using *evaluate-commands* a new scope, named `local` is inserted.
+See <<scopes#,`:doc scopes`>>
diff --git a/doc/pages/scopes.asciidoc b/doc/pages/scopes.asciidoc
index b6711278..02bb5470 100644
--- a/doc/pages/scopes.asciidoc
+++ b/doc/pages/scopes.asciidoc
@@ -36,15 +36,25 @@ Scopes are named as follows:
*global*::
global context linked to the instance of Kakoune
+*local*::
+ A local scope is inserted by each *evaluate-commands* invocations
+ for its duration. Nested *evaluate-commands* each inject a new
+ local scope whose parent is the previous local scope.
+
+ A local scope is intended for temporarily overwriting some scoped
+ value, such as an option or an alias.
+
+
The following order of priority applies to the above scopes:
---------------------------
-window ]> buffer ]> global
---------------------------
+-----------------------------------
+local ]> window ]> buffer ]> global
+-----------------------------------
-The above priority line implies that objects can have individual values that
-will be resolved first in the *window* scope (highest priority), then in
-the *buffer* scope, and finally in the *global* scope (lowest priority).
+The above priority line implies that objects can have individual values
+that will be resolved first in the *local* scope (if it exists), then the
+*window* scope, then in the *buffer* scope, and finally in the *global*
+scope.
Normally, the *buffer* scope keyword means the scope associated with the
currently active buffer, but it's possible to specify any existing buffer by
diff --git a/rc/windowing/detection.kak b/rc/windowing/detection.kak
index ebdaf208..2fde81cd 100644
--- a/rc/windowing/detection.kak
+++ b/rc/windowing/detection.kak
@@ -48,7 +48,7 @@ define-command terminal -params 1.. -docstring %{
Example usage:
terminal sh
- with-option windowing_placement horizontal terminal sh
+ evaluate-commands %{ set local windowing_placement horizontal; terminal sh }
See also the 'new' command.
} %{
@@ -56,29 +56,6 @@ define-command terminal -params 1.. -docstring %{
}
complete-command terminal shell
-# TODO Move this?
-define-command with-option -params 3.. -docstring %{
- with-option <option_name> <new_value> <command> [<arguments>]: evaluate a command with a modified option
-} %{
- evaluate-commands -save-regs s %{
- evaluate-commands set-register s %exp{%%opt{%arg{1}}}
- set-option current %arg{1} %arg{2}
- try %{
- evaluate-commands %sh{
- shift 2
- for arg
- do
- printf "'%s' " "$(printf %s "$arg" | sed "s/'/''/g")"
- done
- }
- } catch %{
- set-option current %arg{1} %reg{s}
- fail "with-option: %val{error}"
- }
- set-option current %arg{1} %reg{s}
- }
-}
-
hook -group windowing global KakBegin .* %{
evaluate-commands %sh{
set -- ${kak_opt_windowing_modules}
diff --git a/src/alias_registry.hh b/src/alias_registry.hh
index c35cdf70..1b7a49eb 100644
--- a/src/alias_registry.hh
+++ b/src/alias_registry.hh
@@ -12,6 +12,9 @@ class AliasRegistry : public SafeCountable
{
public:
AliasRegistry(AliasRegistry& parent) : SafeCountable{}, m_parent(&parent) {}
+
+ void reparent(AliasRegistry& parent) { m_parent = &parent; }
+
void add_alias(String alias, String command);
void remove_alias(StringView alias);
StringView operator[](StringView alias) const;
diff --git a/src/commands.cc b/src/commands.cc
index 4404ee70..f217a9fa 100644
--- a/src/commands.cc
+++ b/src/commands.cc
@@ -48,6 +48,24 @@ namespace Kakoune
extern const char* version;
+struct LocalScope : Scope
+{
+ LocalScope(Context& context)
+ : Scope(context.scope()), m_context{context}
+ {
+ m_context.m_local_scopes.push_back(this);
+ }
+
+ ~LocalScope()
+ {
+ kak_assert(not m_context.m_local_scopes.empty() and m_context.m_local_scopes.back() == this);
+ m_context.m_local_scopes.pop_back();
+ }
+
+private:
+ Context& m_context;
+};
+
namespace
{
@@ -215,21 +233,21 @@ const ParameterDesc double_params{ {}, ParameterDesc::Flags::None, 2, 2 };
static Completions complete_scope(const Context&, CompletionFlags,
StringView prefix, ByteCount cursor_pos)
{
- static constexpr StringView scopes[] = { "global", "buffer", "window", };
+ static constexpr StringView scopes[] = { "global", "buffer", "window", "local"};
return { 0_byte, cursor_pos, complete(prefix, cursor_pos, scopes) };
}
static Completions complete_scope_including_current(const Context&, CompletionFlags,
StringView prefix, ByteCount cursor_pos)
{
- static constexpr StringView scopes[] = { "global", "buffer", "window", "current" };
+ static constexpr StringView scopes[] = { "global", "buffer", "window", "local", "current" };
return { 0_byte, cursor_pos, complete(prefix, cursor_pos, scopes) };
}
static Completions complete_scope_no_global(const Context&, CompletionFlags,
StringView prefix, ByteCount cursor_pos)
{
- static constexpr StringView scopes[] = { "buffer", "window", "current" };
+ static constexpr StringView scopes[] = { "buffer", "window", "local", "current" };
return { 0_byte, cursor_pos, complete(prefix, cursor_pos, scopes) };
}
@@ -395,6 +413,8 @@ Scope* get_scope_ifp(StringView scope, const Context& context)
return &context.buffer();
else if (prefix_match("window", scope))
return &context.window();
+ else if (prefix_match("local", scope))
+ return context.local_scope();
else if (prefix_match(scope, "buffer="))
return &BufferManager::instance().get_buffer(scope.substr(7_byte));
return nullptr;
@@ -2168,6 +2188,7 @@ const CommandDesc execute_keys_cmd = {
}
};
+
const CommandDesc evaluate_commands_cmd = {
"evaluate-commands",
"eval",
@@ -2186,6 +2207,7 @@ const CommandDesc evaluate_commands_cmd = {
const bool no_hooks = context.hooks_disabled() or parser.get_switch("no-hooks");
ScopedSetBool disable_hooks(context.hooks_disabled(), no_hooks);
+ LocalScope local_scope{context};
if (parser.get_switch("verbatim"))
CommandManager::instance().execute_single_command(parser | gather<Vector<String>>(), context, shell_context);
else
diff --git a/src/context.cc b/src/context.cc
index 71a5becd..62171674 100644
--- a/src/context.cc
+++ b/src/context.cc
@@ -51,8 +51,10 @@ Client& Context::client() const
return *m_client;
}
-Scope& Context::scope() const
+Scope& Context::scope(bool allow_local) const
{
+ if (allow_local and not m_local_scopes.empty())
+ return *m_local_scopes.back();
if (has_window())
return window();
if (has_buffer())
@@ -70,6 +72,8 @@ void Context::set_window(Window& window)
{
kak_assert(&window.buffer() == &buffer());
m_window.reset(&window);
+ if (not m_local_scopes.empty())
+ m_local_scopes.front()->reparent(window);
}
void Context::print_status(DisplayLine status) const
@@ -314,6 +318,8 @@ void Context::change_buffer(Buffer& buffer, Optional<FunctionRef<void()>> set_se
ScopedSelectionEdition selection_edition{*this};
selections_write_only() = SelectionList{buffer, Selection{}};
}
+ if (not m_local_scopes.empty())
+ m_local_scopes.front()->reparent(buffer);
}
if (has_input_handler())
diff --git a/src/context.hh b/src/context.hh
index 7834bde4..7ff4166e 100644
--- a/src/context.hh
+++ b/src/context.hh
@@ -42,6 +42,8 @@ private:
using LastSelectFunc = std::function<void (Context&)>;
+struct LocalScope;
+
// A Context is used to access non singleton objects for various services
// in commands.
//
@@ -97,7 +99,10 @@ public:
void set_client(Client& client);
void set_window(Window& window);
- Scope& scope() const;
+ friend struct LocalScope;
+
+ Scope& scope(bool allow_local = true) const;
+ Scope* local_scope() const { return m_local_scopes.empty() ? nullptr : m_local_scopes.back(); }
OptionManager& options() const { return scope().options(); }
HookManager& hooks() const { return scope().hooks(); }
@@ -155,6 +160,7 @@ private:
SafePtr<InputHandler> m_input_handler;
SafePtr<Window> m_window;
SafePtr<Client> m_client;
+ std::vector<Scope*> m_local_scopes;
class SelectionHistory {
public:
diff --git a/src/face_registry.hh b/src/face_registry.hh
index e95a02fd..88e52980 100644
--- a/src/face_registry.hh
+++ b/src/face_registry.hh
@@ -24,6 +24,8 @@ class FaceRegistry : public SafeCountable
public:
FaceRegistry(FaceRegistry& parent) : SafeCountable{}, m_parent(&parent) {}
+ void reparent(FaceRegistry& parent) { m_parent = &parent; }
+
Face operator[](StringView facedesc) const;
Face operator[](const FaceSpec& facespec) const;
void add_face(StringView name, StringView facedesc, bool override = false);
diff --git a/src/highlighter_group.hh b/src/highlighter_group.hh
index a0873dc0..1502b796 100644
--- a/src/highlighter_group.hh
+++ b/src/highlighter_group.hh
@@ -43,6 +43,8 @@ class Highlighters : public SafeCountable
public:
Highlighters(Highlighters& parent) : SafeCountable{}, m_parent{&parent}, m_group{HighlightPass::All} {}
+ void reparent(Highlighters& parent) { m_parent = &parent; }
+
HighlighterGroup& group() { return m_group; }
const HighlighterGroup& group() const { return m_group; }
diff --git a/src/hook_manager.hh b/src/hook_manager.hh
index 29baa033..228a4f23 100644
--- a/src/hook_manager.hh
+++ b/src/hook_manager.hh
@@ -123,6 +123,8 @@ public:
HookManager(HookManager& parent);
~HookManager();
+ void reparent(HookManager& parent) { m_parent = &parent; }
+
void add_hook(Hook hook, String group, HookFlags flags,
Regex filter, String commands, Context& context);
void remove_hooks(const Regex& regex);
diff --git a/src/insert_completer.cc b/src/insert_completer.cc
index c50fbe00..3e1bcd2d 100644
--- a/src/insert_completer.cc
+++ b/src/insert_completer.cc
@@ -403,7 +403,10 @@ InsertCompletion complete_line(const SelectionList& sels,
}
InsertCompleter::InsertCompleter(Context& context)
- : m_context(context), m_options(context.options()), m_faces(context.faces())
+ : m_context(context),
+ // local scopes might go away before completion ends, make sure to register on a long lived one
+ m_options(context.scope(false).options()),
+ m_faces(context.scope(false).faces())
{
m_options.register_watcher(*this);
}
diff --git a/src/keymap_manager.hh b/src/keymap_manager.hh
index 208b44f1..4819be05 100644
--- a/src/keymap_manager.hh
+++ b/src/keymap_manager.hh
@@ -31,6 +31,8 @@ class KeymapManager
public:
KeymapManager(KeymapManager& parent) : m_parent(&parent) {}
+ void reparent(KeymapManager& parent) { m_parent = &parent; }
+
using KeyList = Vector<Key, MemoryDomain::Mapping>;
void map_key(Key key, KeymapMode mode, KeyList mapping, String docstring);
void unmap_key(Key key, KeymapMode mode);
diff --git a/src/option_manager.cc b/src/option_manager.cc
index 5dd3d612..0a77548f 100644
--- a/src/option_manager.cc
+++ b/src/option_manager.cc
@@ -28,6 +28,15 @@ OptionManager::~OptionManager()
kak_assert(m_watchers.empty());
}
+void OptionManager::reparent(OptionManager& parent)
+{
+ if (m_parent)
+ m_parent->unregister_watcher(*this);
+
+ m_parent = &parent;
+ parent.register_watcher(*this);
+}
+
void OptionManager::register_watcher(OptionManagerWatcher& watcher) const
{
kak_assert(not contains(m_watchers, &watcher));
diff --git a/src/option_manager.hh b/src/option_manager.hh
index e4dc230f..3a4bb5a5 100644
--- a/src/option_manager.hh
+++ b/src/option_manager.hh
@@ -89,6 +89,8 @@ public:
OptionManager(OptionManager& parent);
~OptionManager();
+ void reparent(OptionManager& parent);
+
Option& operator[] (StringView name);
const Option& operator[] (StringView name) const;
Option& get_local_option(StringView name);
diff --git a/src/scope.cc b/src/scope.cc
index 64bcd445..fe33f6f5 100644
--- a/src/scope.cc
+++ b/src/scope.cc
@@ -4,6 +4,16 @@
namespace Kakoune
{
+void Scope::reparent(Scope& parent)
+{
+ m_options.reparent(parent.m_options);
+ m_hooks.reparent(parent.m_hooks);
+ m_keymaps.reparent(parent.m_keymaps);
+ m_aliases.reparent(parent.m_aliases);
+ m_faces.reparent(parent.m_faces);
+ m_highlighters.reparent(parent.m_highlighters);
+}
+
GlobalScope::GlobalScope()
: m_option_registry(m_options)
{
diff --git a/src/scope.hh b/src/scope.hh
index 870e1159..b98be7bb 100644
--- a/src/scope.hh
+++ b/src/scope.hh
@@ -36,6 +36,8 @@ public:
Highlighters& highlighters() { return m_highlighters; }
const Highlighters& highlighters() const { return m_highlighters; }
+ void reparent(Scope& parent);
+
private:
friend class GlobalScope;
Scope() = default;