Add support for querying client environement variables
At connection, a remote client sends all its environement to the server, which then provides access to client env through kak_client_env_VAR_NAME variables in the shell.
This commit is contained in:
parent
33de42610d
commit
203a7732f5
|
@ -14,10 +14,13 @@ namespace Kakoune
|
||||||
|
|
||||||
Client::Client(std::unique_ptr<UserInterface>&& ui,
|
Client::Client(std::unique_ptr<UserInterface>&& ui,
|
||||||
std::unique_ptr<Window>&& window,
|
std::unique_ptr<Window>&& window,
|
||||||
SelectionList selections, String name)
|
SelectionList selections,
|
||||||
|
EnvVarMap env_vars,
|
||||||
|
String name)
|
||||||
: m_ui{std::move(ui)}, m_window{std::move(window)},
|
: m_ui{std::move(ui)}, m_window{std::move(window)},
|
||||||
m_input_handler{m_window->buffer(), std::move(selections),
|
m_input_handler{m_window->buffer(), std::move(selections),
|
||||||
std::move(name)}
|
std::move(name)},
|
||||||
|
m_env_vars(env_vars)
|
||||||
{
|
{
|
||||||
context().set_client(*this);
|
context().set_client(*this);
|
||||||
context().set_window(*m_window);
|
context().set_window(*m_window);
|
||||||
|
@ -151,4 +154,13 @@ void Client::check_buffer_fs_timestamp()
|
||||||
reload_buffer(context(), filename);
|
reload_buffer(context(), filename);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const String& Client::get_env_var(const String& name) const
|
||||||
|
{
|
||||||
|
auto it = m_env_vars.find(name);
|
||||||
|
static String empty{};
|
||||||
|
if (it == m_env_vars.end())
|
||||||
|
return empty;
|
||||||
|
return it->second;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
#include "utils.hh"
|
#include "utils.hh"
|
||||||
#include "display_buffer.hh"
|
#include "display_buffer.hh"
|
||||||
#include "input_handler.hh"
|
#include "input_handler.hh"
|
||||||
|
#include "env_vars.hh"
|
||||||
|
|
||||||
namespace Kakoune
|
namespace Kakoune
|
||||||
{
|
{
|
||||||
|
@ -17,7 +18,9 @@ class Client : public SafeCountable
|
||||||
public:
|
public:
|
||||||
Client(std::unique_ptr<UserInterface>&& ui,
|
Client(std::unique_ptr<UserInterface>&& ui,
|
||||||
std::unique_ptr<Window>&& window,
|
std::unique_ptr<Window>&& window,
|
||||||
SelectionList selections, String name);
|
SelectionList selections,
|
||||||
|
EnvVarMap env_vars,
|
||||||
|
String name);
|
||||||
~Client();
|
~Client();
|
||||||
|
|
||||||
// handle all the keys currently available in the user interface
|
// handle all the keys currently available in the user interface
|
||||||
|
@ -37,12 +40,16 @@ public:
|
||||||
|
|
||||||
void change_buffer(Buffer& buffer);
|
void change_buffer(Buffer& buffer);
|
||||||
|
|
||||||
|
const String& get_env_var(const String& name) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
DisplayLine generate_mode_line() const;
|
DisplayLine generate_mode_line() const;
|
||||||
|
|
||||||
std::unique_ptr<UserInterface> m_ui;
|
std::unique_ptr<UserInterface> m_ui;
|
||||||
std::unique_ptr<Window> m_window;
|
std::unique_ptr<Window> m_window;
|
||||||
|
|
||||||
|
EnvVarMap m_env_vars;
|
||||||
|
|
||||||
InputHandler m_input_handler;
|
InputHandler m_input_handler;
|
||||||
|
|
||||||
DisplayLine m_status_line;
|
DisplayLine m_status_line;
|
||||||
|
|
|
@ -25,12 +25,14 @@ String ClientManager::generate_name() const
|
||||||
}
|
}
|
||||||
|
|
||||||
Client* ClientManager::create_client(std::unique_ptr<UserInterface>&& ui,
|
Client* ClientManager::create_client(std::unique_ptr<UserInterface>&& ui,
|
||||||
|
EnvVarMap env_vars,
|
||||||
const String& init_commands)
|
const String& init_commands)
|
||||||
{
|
{
|
||||||
Buffer& buffer = **BufferManager::instance().begin();
|
Buffer& buffer = **BufferManager::instance().begin();
|
||||||
WindowAndSelections ws = get_free_window(buffer);
|
WindowAndSelections ws = get_free_window(buffer);
|
||||||
Client* client = new Client{std::move(ui), std::move(ws.window),
|
Client* client = new Client{std::move(ui), std::move(ws.window),
|
||||||
std::move(ws.selections), generate_name()};
|
std::move(ws.selections), std::move(env_vars),
|
||||||
|
generate_name()};
|
||||||
m_clients.emplace_back(client);
|
m_clients.emplace_back(client);
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
|
|
@ -22,7 +22,7 @@ public:
|
||||||
~ClientManager();
|
~ClientManager();
|
||||||
|
|
||||||
Client* create_client(std::unique_ptr<UserInterface>&& ui,
|
Client* create_client(std::unique_ptr<UserInterface>&& ui,
|
||||||
const String& init_cmd);
|
EnvVarMap env_vars, const String& init_cmd);
|
||||||
|
|
||||||
bool empty() const { return m_clients.empty(); }
|
bool empty() const { return m_clients.empty(); }
|
||||||
size_t count() const { return m_clients.size(); }
|
size_t count() const { return m_clients.size(); }
|
||||||
|
|
20
src/env_vars.cc
Normal file
20
src/env_vars.cc
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
#include "env_vars.hh"
|
||||||
|
|
||||||
|
namespace Kakoune
|
||||||
|
{
|
||||||
|
|
||||||
|
EnvVarMap get_env_vars()
|
||||||
|
{
|
||||||
|
EnvVarMap env_vars;
|
||||||
|
for (char** it = environ; *it; ++it)
|
||||||
|
{
|
||||||
|
const char* name = *it;
|
||||||
|
const char* value = name;
|
||||||
|
while (*value != 0 and *value != '=')
|
||||||
|
++value;
|
||||||
|
env_vars[String{name, value}] = (*value == '=') ? value+1 : value;
|
||||||
|
}
|
||||||
|
return env_vars;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
18
src/env_vars.hh
Normal file
18
src/env_vars.hh
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
#ifndef env_vars_hh_INCLUDED
|
||||||
|
#define env_vars_hh_INCLUDED
|
||||||
|
|
||||||
|
#include "string.hh"
|
||||||
|
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
|
namespace Kakoune
|
||||||
|
{
|
||||||
|
|
||||||
|
using EnvVarMap = std::unordered_map<String, String>;
|
||||||
|
|
||||||
|
EnvVarMap get_env_vars();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // env_vars_hh_INCLUDED
|
||||||
|
|
|
@ -101,6 +101,10 @@ void register_env_vars()
|
||||||
"reg_.+",
|
"reg_.+",
|
||||||
[](const String& name, const Context& context) -> String
|
[](const String& name, const Context& context) -> String
|
||||||
{ return RegisterManager::instance()[name[4]].values(context)[0]; }
|
{ return RegisterManager::instance()[name[4]].values(context)[0]; }
|
||||||
|
}, {
|
||||||
|
"client_env_.+",
|
||||||
|
[](const String& name, const Context& context) -> String
|
||||||
|
{ return context.client().get_env_var(name.substr(11_byte)); }
|
||||||
}, {
|
}, {
|
||||||
"session",
|
"session",
|
||||||
[](const String& name, const Context& context) -> String
|
[](const String& name, const Context& context) -> String
|
||||||
|
@ -189,7 +193,7 @@ void create_local_client(const String& init_command)
|
||||||
|
|
||||||
UserInterface* ui = new LocalNCursesUI{};
|
UserInterface* ui = new LocalNCursesUI{};
|
||||||
static Client* client = ClientManager::instance().create_client(
|
static Client* client = ClientManager::instance().create_client(
|
||||||
std::unique_ptr<UserInterface>{ui}, init_command);
|
std::unique_ptr<UserInterface>{ui}, get_env_vars(), init_command);
|
||||||
signal(SIGHUP, [](int) {
|
signal(SIGHUP, [](int) {
|
||||||
if (client)
|
if (client)
|
||||||
ClientManager::instance().remove_client(*client);
|
ClientManager::instance().remove_client(*client);
|
||||||
|
@ -221,6 +225,7 @@ int run_client(const String& session, const String& init_command)
|
||||||
EventManager event_manager;
|
EventManager event_manager;
|
||||||
auto client = connect_to(session,
|
auto client = connect_to(session,
|
||||||
std::unique_ptr<UserInterface>{new NCursesUI{}},
|
std::unique_ptr<UserInterface>{new NCursesUI{}},
|
||||||
|
get_env_vars(),
|
||||||
init_command);
|
init_command);
|
||||||
while (true)
|
while (true)
|
||||||
event_manager.handle_next_events();
|
event_manager.handle_next_events();
|
||||||
|
|
|
@ -74,6 +74,17 @@ public:
|
||||||
write(memoryview<T>(vec));
|
write(memoryview<T>(vec));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<typename Key, typename Val>
|
||||||
|
void write(const std::unordered_map<Key, Val>& map)
|
||||||
|
{
|
||||||
|
write<uint32_t>(map.size());
|
||||||
|
for (auto& val : map)
|
||||||
|
{
|
||||||
|
write(val.first);
|
||||||
|
write(val.second);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void write(Color color)
|
void write(Color color)
|
||||||
{
|
{
|
||||||
write(color.color);
|
write(color.color);
|
||||||
|
@ -210,6 +221,20 @@ DisplayBuffer read<DisplayBuffer>(int socket)
|
||||||
return db;
|
return db;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<typename Key, typename Val>
|
||||||
|
std::unordered_map<Key, Val> read_map(int socket)
|
||||||
|
{
|
||||||
|
uint32_t size = read<uint32_t>(socket);
|
||||||
|
std::unordered_map<Key, Val> res;
|
||||||
|
while (size--)
|
||||||
|
{
|
||||||
|
auto key = read<Key>(socket);
|
||||||
|
auto val = read<Val>(socket);
|
||||||
|
res.insert({std::move(key), std::move(val)});
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
class RemoteUI : public UserInterface
|
class RemoteUI : public UserInterface
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
@ -368,12 +393,15 @@ void RemoteUI::set_input_callback(InputCallback callback)
|
||||||
}
|
}
|
||||||
|
|
||||||
RemoteClient::RemoteClient(int socket, std::unique_ptr<UserInterface>&& ui,
|
RemoteClient::RemoteClient(int socket, std::unique_ptr<UserInterface>&& ui,
|
||||||
|
const EnvVarMap& env_vars,
|
||||||
const String& init_command)
|
const String& init_command)
|
||||||
: m_ui(std::move(ui)), m_dimensions(m_ui->dimensions()),
|
: m_ui(std::move(ui)), m_dimensions(m_ui->dimensions()),
|
||||||
m_socket_watcher{socket, [this](FDWatcher&){ process_next_message(); }}
|
m_socket_watcher{socket, [this](FDWatcher&){ process_next_message(); }}
|
||||||
{
|
{
|
||||||
Message msg(socket);
|
Message msg(socket);
|
||||||
msg.write(init_command.c_str(), (int)init_command.length()+1);
|
msg.write(init_command.c_str(), (int)init_command.length()+1);
|
||||||
|
msg.write(env_vars);
|
||||||
|
|
||||||
Key key{ resize_modifier, Codepoint(((int)m_dimensions.line << 16) |
|
Key key{ resize_modifier, Codepoint(((int)m_dimensions.line << 16) |
|
||||||
(int)m_dimensions.column) };
|
(int)m_dimensions.column) };
|
||||||
msg.write(key);
|
msg.write(key);
|
||||||
|
@ -446,6 +474,7 @@ void RemoteClient::write_next_key()
|
||||||
|
|
||||||
std::unique_ptr<RemoteClient> connect_to(const String& session,
|
std::unique_ptr<RemoteClient> connect_to(const String& session,
|
||||||
std::unique_ptr<UserInterface>&& ui,
|
std::unique_ptr<UserInterface>&& ui,
|
||||||
|
const EnvVarMap& env_vars,
|
||||||
const String& init_command)
|
const String& init_command)
|
||||||
{
|
{
|
||||||
auto filename = "/tmp/kak-" + session;
|
auto filename = "/tmp/kak-" + session;
|
||||||
|
@ -459,6 +488,7 @@ std::unique_ptr<RemoteClient> connect_to(const String& session,
|
||||||
throw connection_failed(filename);
|
throw connection_failed(filename);
|
||||||
|
|
||||||
return std::unique_ptr<RemoteClient>{new RemoteClient{sock, std::move(ui),
|
return std::unique_ptr<RemoteClient>{new RemoteClient{sock, std::move(ui),
|
||||||
|
env_vars,
|
||||||
init_command}};
|
init_command}};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -525,8 +555,10 @@ private:
|
||||||
}
|
}
|
||||||
if (c == 0) // end of initial command stream, go to interactive ui
|
if (c == 0) // end of initial command stream, go to interactive ui
|
||||||
{
|
{
|
||||||
|
EnvVarMap env_vars = read_map<String, String>(socket);
|
||||||
std::unique_ptr<UserInterface> ui{new RemoteUI{socket}};
|
std::unique_ptr<UserInterface> ui{new RemoteUI{socket}};
|
||||||
ClientManager::instance().create_client(std::move(ui),
|
ClientManager::instance().create_client(std::move(ui),
|
||||||
|
std::move(env_vars),
|
||||||
m_buffer);
|
m_buffer);
|
||||||
Server::instance().remove_accepter(this);
|
Server::instance().remove_accepter(this);
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
#include "display_buffer.hh"
|
#include "display_buffer.hh"
|
||||||
#include "event_manager.hh"
|
#include "event_manager.hh"
|
||||||
#include "user_interface.hh"
|
#include "user_interface.hh"
|
||||||
|
#include "env_vars.hh"
|
||||||
|
|
||||||
namespace Kakoune
|
namespace Kakoune
|
||||||
{
|
{
|
||||||
|
@ -23,7 +24,7 @@ class RemoteClient
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
RemoteClient(int socket, std::unique_ptr<UserInterface>&& ui,
|
RemoteClient(int socket, std::unique_ptr<UserInterface>&& ui,
|
||||||
const String& init_command);
|
const EnvVarMap& env_vars, const String& init_command);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void process_next_message();
|
void process_next_message();
|
||||||
|
@ -35,6 +36,7 @@ private:
|
||||||
};
|
};
|
||||||
std::unique_ptr<RemoteClient> connect_to(const String& session,
|
std::unique_ptr<RemoteClient> connect_to(const String& session,
|
||||||
std::unique_ptr<UserInterface>&& ui,
|
std::unique_ptr<UserInterface>&& ui,
|
||||||
|
const EnvVarMap& env_vars,
|
||||||
const String& init_command);
|
const String& init_command);
|
||||||
|
|
||||||
void send_command(const String& session, const String& command);
|
void send_command(const String& session, const String& command);
|
||||||
|
|
|
@ -3,15 +3,13 @@
|
||||||
|
|
||||||
#include "string.hh"
|
#include "string.hh"
|
||||||
#include "utils.hh"
|
#include "utils.hh"
|
||||||
|
#include "env_vars.hh"
|
||||||
#include <unordered_map>
|
|
||||||
|
|
||||||
namespace Kakoune
|
namespace Kakoune
|
||||||
{
|
{
|
||||||
|
|
||||||
class Context;
|
class Context;
|
||||||
using EnvVarRetriever = std::function<String (const String& name, const Context&)>;
|
using EnvVarRetriever = std::function<String (const String& name, const Context&)>;
|
||||||
using EnvVarMap = std::unordered_map<String, String>;
|
|
||||||
|
|
||||||
class ShellManager : public Singleton<ShellManager>
|
class ShellManager : public Singleton<ShellManager>
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in New Issue
Block a user