mirror of https://github.com/tstack/lnav.git
[vum/configmanager] make the clipboard commands configurable
This commit is contained in:
parent
4d16be7b6e
commit
d239bb4594
5
NEWS
5
NEWS
|
@ -5,6 +5,11 @@ lnav v0.10.1:
|
|||
* The ":write-raw-to" command now accepts a --view flag that specifies
|
||||
the source view for the data to write. For example, to write the
|
||||
results of a SQL query, you would pass "--view=db" to the command.
|
||||
* The commands used to access the clipboard are now configured through
|
||||
the "tuning" section of the configuration.
|
||||
Interface changes:
|
||||
* The xclip implementation for accessing the system clipboard now writes
|
||||
to the "clipboard" selection instead of the "primary" selection.
|
||||
Bug Fixes:
|
||||
* The text "send-input" would show up on some terminals instead of
|
||||
ignoring the escape sequence. This control sequence was only
|
||||
|
|
|
@ -133,6 +133,48 @@
|
|||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"clipboard": {
|
||||
"description": "Settings related to the clipboard",
|
||||
"title": "/tuning/clipboard",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"impls": {
|
||||
"description": "Clipboard implementations",
|
||||
"title": "/tuning/clipboard/impls",
|
||||
"type": "object",
|
||||
"patternProperties": {
|
||||
"([\\w\\-]+)": {
|
||||
"description": "Clipboard implementation",
|
||||
"title": "/tuning/clipboard/impls/<clipboard_impl_name>",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"test": {
|
||||
"title": "/tuning/clipboard/impls/<clipboard_impl_name>/test",
|
||||
"description": "The command that checks",
|
||||
"type": "string",
|
||||
"examples": [
|
||||
"command -v pbcopy"
|
||||
]
|
||||
},
|
||||
"general": {
|
||||
"description": "Commands to work with the general clipboard",
|
||||
"title": "/tuning/clipboard/impls/<clipboard_impl_name>/general",
|
||||
"$ref": "#/definitions/clip-commands"
|
||||
},
|
||||
"find": {
|
||||
"description": "Commands to work with the find clipboard",
|
||||
"title": "/tuning/clipboard/impls/<clipboard_impl_name>/find",
|
||||
"$ref": "#/definitions/clip-commands"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
|
@ -512,6 +554,30 @@
|
|||
},
|
||||
"additionalProperties": false,
|
||||
"definitions": {
|
||||
"clip-commands": {
|
||||
"title": "clip-commands",
|
||||
"type": "object",
|
||||
"$$target": "#/definitions/clip-commands",
|
||||
"properties": {
|
||||
"write": {
|
||||
"title": "/write",
|
||||
"description": "The command used to write to the clipboard",
|
||||
"type": "string",
|
||||
"examples": [
|
||||
"pbcopy"
|
||||
]
|
||||
},
|
||||
"read": {
|
||||
"title": "/read",
|
||||
"description": "The command used to read from the clipboard",
|
||||
"type": "string",
|
||||
"examples": [
|
||||
"pbpaste"
|
||||
]
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"style": {
|
||||
"title": "style",
|
||||
"type": "object",
|
||||
|
|
|
@ -444,6 +444,7 @@ add_library(diag STATIC
|
|||
strong_int.hh
|
||||
string_attr_type.hh
|
||||
sysclip.hh
|
||||
sysclip.cfg.hh
|
||||
term_extra.hh
|
||||
termios_guard.hh
|
||||
text_format.hh
|
||||
|
|
|
@ -257,6 +257,7 @@ noinst_HEADERS = \
|
|||
string_attr_type.hh \
|
||||
strong_int.hh \
|
||||
sysclip.hh \
|
||||
sysclip.cfg.hh \
|
||||
termios_guard.hh \
|
||||
term_extra.hh \
|
||||
text_format.hh \
|
||||
|
|
|
@ -930,7 +930,7 @@ static Result<string, string> com_save_to(exec_context &ec, string cmdline, vect
|
|||
}
|
||||
}
|
||||
else if (split_args[0] == "/dev/clipboard") {
|
||||
toclose = outfile = open_clipboard(CT_GENERAL);
|
||||
toclose = outfile = sysclip::open(sysclip::type_t::GENERAL);
|
||||
closer = pclose;
|
||||
if (!outfile) {
|
||||
alerter::singleton().chime();
|
||||
|
@ -1449,7 +1449,7 @@ static Result<string, string> com_redirect_to(exec_context &ec, string cmdline,
|
|||
if (split_args[0] == "-") {
|
||||
ec.clear_output();
|
||||
} else if (split_args[0] == "/dev/clipboard") {
|
||||
auto out = open_clipboard(CT_GENERAL);
|
||||
auto out = sysclip::open(sysclip::type_t::GENERAL);
|
||||
if (!out) {
|
||||
alerter::singleton().chime();
|
||||
return ec.make_error("Unable to copy to clipboard. "
|
||||
|
|
|
@ -91,6 +91,10 @@ static auto tc = injector::bind<tailer::config>::to_instance(+[]() {
|
|||
return &lnav_config.lc_tailer;
|
||||
});
|
||||
|
||||
static auto scc = injector::bind<sysclip::config>::to_instance(+[]() {
|
||||
return &lnav_config.lc_sysclip;
|
||||
});
|
||||
|
||||
bool check_experimental(const char *feature_name)
|
||||
{
|
||||
const char *env_value = getenv("LNAV_EXP");
|
||||
|
@ -973,6 +977,62 @@ static struct json_path_container remote_handlers = {
|
|||
.with_children(ssh_handlers),
|
||||
};
|
||||
|
||||
static struct json_path_container sysclip_impl_cmd_handlers = json_path_container{
|
||||
yajlpp::property_handler("write")
|
||||
.with_synopsis("<command>")
|
||||
.with_description("The command used to write to the clipboard")
|
||||
.with_example("pbcopy")
|
||||
.for_field(&sysclip::clip_commands::cc_write),
|
||||
yajlpp::property_handler("read")
|
||||
.with_synopsis("<command>")
|
||||
.with_description("The command used to read from the clipboard")
|
||||
.with_example("pbpaste")
|
||||
.for_field(&sysclip::clip_commands::cc_read),
|
||||
}
|
||||
.with_definition_id("clip-commands");
|
||||
|
||||
static struct json_path_container sysclip_impl_handlers = {
|
||||
yajlpp::property_handler("test")
|
||||
.with_synopsis("<command>")
|
||||
.with_description("The command that checks")
|
||||
.with_example("command -v pbcopy")
|
||||
.for_field(&sysclip::clipboard::c_test_command),
|
||||
yajlpp::property_handler("general")
|
||||
.with_description("Commands to work with the general clipboard")
|
||||
.with_obj_provider<sysclip::clip_commands, sysclip::clipboard>([](const yajlpp_provider_context &ypc, sysclip::clipboard *root) {
|
||||
return &root->c_general;
|
||||
})
|
||||
.with_children(sysclip_impl_cmd_handlers),
|
||||
yajlpp::property_handler("find")
|
||||
.with_description("Commands to work with the find clipboard")
|
||||
.with_obj_provider<sysclip::clip_commands, sysclip::clipboard>([](const yajlpp_provider_context &ypc, sysclip::clipboard *root) {
|
||||
return &root->c_find;
|
||||
})
|
||||
.with_children(sysclip_impl_cmd_handlers),
|
||||
};
|
||||
|
||||
static struct json_path_container sysclip_impls_handlers = {
|
||||
yajlpp::pattern_property_handler("(?<clipboard_impl_name>[\\w\\-]+)")
|
||||
.with_synopsis("<name>")
|
||||
.with_description("Clipboard implementation")
|
||||
.with_obj_provider<sysclip::clipboard, _lnav_config>([](const yajlpp_provider_context &ypc, _lnav_config *root) {
|
||||
auto &retval = root->lc_sysclip.c_clipboard_impls[ypc.ypc_extractor.get_substr("clipboard_impl_name")];
|
||||
return &retval;
|
||||
})
|
||||
.with_path_provider<_lnav_config>([](struct _lnav_config *cfg, vector<string> &paths_out) {
|
||||
for (const auto &iter : cfg->lc_sysclip.c_clipboard_impls) {
|
||||
paths_out.emplace_back(iter.first);
|
||||
}
|
||||
})
|
||||
.with_children(sysclip_impl_handlers),
|
||||
};
|
||||
|
||||
static struct json_path_container sysclip_handlers = {
|
||||
yajlpp::property_handler("impls")
|
||||
.with_description("Clipboard implementations")
|
||||
.with_children(sysclip_impls_handlers),
|
||||
};
|
||||
|
||||
static struct json_path_container tuning_handlers = {
|
||||
yajlpp::property_handler("archive-manager")
|
||||
.with_description("Settings related to opening archive files")
|
||||
|
@ -986,6 +1046,9 @@ static struct json_path_container tuning_handlers = {
|
|||
yajlpp::property_handler("remote")
|
||||
.with_description("Settings related to remote file support")
|
||||
.with_children(remote_handlers),
|
||||
yajlpp::property_handler("clipboard")
|
||||
.with_description("Settings related to the clipboard")
|
||||
.with_children(sysclip_handlers),
|
||||
};
|
||||
|
||||
static set<string> SUPPORTED_CONFIG_SCHEMAS = {
|
||||
|
|
|
@ -50,6 +50,7 @@
|
|||
#include "file_vtab.cfg.hh"
|
||||
#include "logfile.cfg.hh"
|
||||
#include "tailer/tailer.looper.cfg.hh"
|
||||
#include "sysclip.cfg.hh"
|
||||
|
||||
/**
|
||||
* Check if an experimental feature should be enabled by
|
||||
|
@ -99,6 +100,7 @@ struct _lnav_config {
|
|||
file_vtab::config lc_file_vtab;
|
||||
lnav::logfile::config lc_logfile;
|
||||
tailer::config lc_tailer;
|
||||
sysclip::config lc_sysclip;
|
||||
};
|
||||
|
||||
extern struct _lnav_config lnav_config;
|
||||
|
|
|
@ -603,7 +603,7 @@ static void rl_callback_int(readline_curses *rc, bool is_alt)
|
|||
vis_line_t vl = is_alt ? bv.prev(tc->get_top()) :
|
||||
bv.next(tc->get_top());
|
||||
|
||||
pfile = open_clipboard(CT_FIND);
|
||||
pfile = sysclip::open(sysclip::type_t::FIND);
|
||||
if (pfile.in() != nullptr) {
|
||||
fprintf(pfile, "%s", rc->get_value().c_str());
|
||||
}
|
||||
|
|
|
@ -22,6 +22,55 @@
|
|||
"start-command": "bash -c ./{0:}",
|
||||
"transfer-command": "cat > {0:} && chmod ugo+rx ./{0:}"
|
||||
}
|
||||
},
|
||||
"clipboard": {
|
||||
"impls": {
|
||||
"MacOS": {
|
||||
"test": "command -v pbcopy",
|
||||
"general": {
|
||||
"write": "pbcopy",
|
||||
"read": "pbpaste -Prefer txt"
|
||||
},
|
||||
"find": {
|
||||
"write": "pbcopy -pboard find",
|
||||
"read": "pbpaste -pboard find -Prefer txt"
|
||||
}
|
||||
},
|
||||
"Wayland": {
|
||||
"test": "test -n \"$WAYLAND_DISPLAY\"",
|
||||
"general": {
|
||||
"write": "wl-copy --foreground --type text/plain",
|
||||
"read": "wl-paste --no-newline"
|
||||
}
|
||||
},
|
||||
"X11-xclip": {
|
||||
"test": "test -n \"$DISPLAY\" && command -v xclip",
|
||||
"general": {
|
||||
"write": "xclip -i -selection clipboard",
|
||||
"read": "xclip -o -selection clipboard"
|
||||
}
|
||||
},
|
||||
"tmux": {
|
||||
"test": "test -n \"$TMUX\"",
|
||||
"general": {
|
||||
"write": "tmux load-buffer -",
|
||||
"read": "tmux save-buffer -"
|
||||
}
|
||||
},
|
||||
"NeoVim": {
|
||||
"test": "command -v win32yank.exe",
|
||||
"general": {
|
||||
"write": "win32yank.exe -i --crlf",
|
||||
"read": "win32yank.exe -o --lf"
|
||||
}
|
||||
},
|
||||
"Windows": {
|
||||
"test": "command -v clip.exe",
|
||||
"general": {
|
||||
"write": "clip.exe"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
117
src/sysclip.cc
117
src/sysclip.cc
|
@ -33,97 +33,64 @@
|
|||
|
||||
#include <stdio.h>
|
||||
|
||||
#include "base/injector.hh"
|
||||
#include "base/lnav_log.hh"
|
||||
#include "fmt/format.h"
|
||||
#include "sysclip.hh"
|
||||
#include "sysclip.cfg.hh"
|
||||
|
||||
struct clip_command {
|
||||
const char *cc_cmd[2];
|
||||
};
|
||||
namespace sysclip {
|
||||
|
||||
static clip_command *get_commands()
|
||||
static nonstd::optional<clipboard> get_commands()
|
||||
{
|
||||
static clip_command NEOVIM_CMDS[] = {
|
||||
{ { "win32yank.exe -i --crlf > /dev/null 2>&1",
|
||||
"win32yank.exe -o --lf < /dev/null 2>/dev/null" } },
|
||||
{ { nullptr, nullptr } },
|
||||
};
|
||||
static clip_command OSX_CMDS[] = {
|
||||
{ { "pbcopy > /dev/null 2>&1",
|
||||
"pbpaste -Prefer txt 2>/dev/null", } },
|
||||
{ { "pbcopy -pboard find > /dev/null 2>&1",
|
||||
"pbpaste -pboard find -Prefer txt 2>/dev/null" } },
|
||||
};
|
||||
static clip_command TMUX_CMDS[] = {
|
||||
{ { "tmux load-buffer - > /dev/null 2>&1",
|
||||
"tmux save-buffer - < /dev/null 2>/dev/null" } },
|
||||
{ { nullptr, nullptr } },
|
||||
};
|
||||
static clip_command WAYLAND_CMDS[] = {
|
||||
{ { "wl-copy --foreground --type text/plain > /dev/null 2>&1",
|
||||
"wl-paste --no-newline < /dev/null 2>/dev/null" } },
|
||||
{ { nullptr, nullptr } },
|
||||
};
|
||||
static clip_command WINDOWS_CMDS[] = {
|
||||
{ { "clip.exe > /dev/null 2>&1",
|
||||
nullptr } },
|
||||
{ { nullptr, nullptr } },
|
||||
};
|
||||
static clip_command XCLIP_CMDS[] = {
|
||||
{ { "xclip -i > /dev/null 2>&1",
|
||||
"xclip -o < /dev/null 2>/dev/null" } },
|
||||
{ { nullptr, nullptr } },
|
||||
};
|
||||
static clip_command XSEL_CMDS[] = {
|
||||
{ { "xsel --nodetach -i -b > /dev/null 2>&1",
|
||||
"xclip -o -b < /dev/null 2>/dev/null" } },
|
||||
{ { nullptr, nullptr } },
|
||||
};
|
||||
auto& cfg = injector::get<const config&>();
|
||||
|
||||
clip_command *retval = nullptr;
|
||||
if (system("command -v pbcopy > /dev/null 2>&1") == 0) {
|
||||
retval = OSX_CMDS;
|
||||
} else if (getenv("WAYLAND_DISPLAY") != nullptr) {
|
||||
retval = WAYLAND_CMDS;
|
||||
} else if (getenv("DISPLAY") != nullptr && system("command -v xclip > /dev/null 2>&1") == 0) {
|
||||
retval = XCLIP_CMDS;
|
||||
} else if (getenv("DISPLAY") != nullptr && system("command -v xsel > /dev/null 2>&1") == 0) {
|
||||
retval = XSEL_CMDS;
|
||||
} else if (getenv("TMUX") != nullptr) {
|
||||
retval = TMUX_CMDS;
|
||||
} else if (system("command -v win32yank.exe > /dev/null 2>&1") == 0) {
|
||||
/*
|
||||
* NeoVim's win32yank command is bidirectional, whereas the system-supplied
|
||||
* clip.exe is copy-only.
|
||||
* xclip and clip.exe may coexist on Windows Subsystem for Linux
|
||||
*/
|
||||
retval = NEOVIM_CMDS;
|
||||
} else if (system("command -v clip.exe > /dev/null 2>&1") == 0) {
|
||||
retval = WINDOWS_CMDS;
|
||||
} else {
|
||||
log_error("unable to detect clipboard commands");
|
||||
for (const auto& pair : cfg.c_clipboard_impls) {
|
||||
const auto full_cmd = fmt::format("{} > /dev/null 2>&1",
|
||||
pair.second.c_test_command);
|
||||
|
||||
log_debug("testing clipboard impl %s using: %s",
|
||||
pair.first.c_str(), full_cmd.c_str());
|
||||
if (system(full_cmd.c_str()) == 0) {
|
||||
log_info("detected clipboard: %s", pair.first.c_str());
|
||||
return pair.second;
|
||||
}
|
||||
}
|
||||
|
||||
if (retval != nullptr) {
|
||||
log_info("detected clipboard copy command: %s", retval[0].cc_cmd[0]);
|
||||
log_info("detected clipboard paste command: %s", retval[0].cc_cmd[1]);
|
||||
}
|
||||
|
||||
return retval;
|
||||
return nonstd::nullopt;
|
||||
}
|
||||
|
||||
/* XXX For one, this code is kinda crappy. For two, we should probably link
|
||||
* directly with X so we don't need to have xclip installed and it'll work if
|
||||
* we're ssh'd into a box.
|
||||
*/
|
||||
FILE *open_clipboard(clip_type_t type, clip_op_t op)
|
||||
FILE *open(type_t type, op_t op)
|
||||
{
|
||||
const char *mode = op == CO_WRITE ? "w" : "r";
|
||||
static clip_command *cc = get_commands();
|
||||
FILE *pfile = nullptr;
|
||||
const char *mode = op == op_t::WRITE ? "w" : "r";
|
||||
static const auto clip_opt = sysclip::get_commands();
|
||||
|
||||
if (cc != nullptr && cc[type].cc_cmd[op] != nullptr) {
|
||||
pfile = popen(cc[type].cc_cmd[op], mode);
|
||||
if (!clip_opt) {
|
||||
log_error("unable to detect clipboard implementation");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return pfile;
|
||||
auto cmd = clip_opt.value().select(type).select(op);
|
||||
|
||||
if (cmd.empty()) {
|
||||
log_error("clipboard does not support type/op");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
switch (op) {
|
||||
case op_t::WRITE:
|
||||
cmd = fmt::format("{} > /dev/null 2>&1", cmd);
|
||||
break;
|
||||
case op_t::READ:
|
||||
cmd = fmt::format("{} < /dev/null 2>/dev/null", cmd);
|
||||
break;
|
||||
}
|
||||
|
||||
return popen(cmd.c_str(), mode);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,77 @@
|
|||
/**
|
||||
* Copyright (c) 2021, Timothy Stack
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
* * Neither the name of Timothy Stack nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* @file sysclip.cfg.hh
|
||||
*/
|
||||
|
||||
#ifndef lnav_sysclip_cfg_hh
|
||||
#define lnav_sysclip_cfg_hh
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
#include "sysclip.hh"
|
||||
|
||||
namespace sysclip {
|
||||
|
||||
struct clip_commands {
|
||||
std::string cc_write;
|
||||
std::string cc_read;
|
||||
|
||||
std::string select(op_t op) const {
|
||||
switch (op) {
|
||||
case op_t::WRITE:
|
||||
return this->cc_write;
|
||||
case op_t::READ:
|
||||
return this->cc_read;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct clipboard {
|
||||
std::string c_test_command;
|
||||
clip_commands c_general;
|
||||
clip_commands c_find;
|
||||
|
||||
const clip_commands& select(type_t t) const {
|
||||
switch (t) {
|
||||
case type_t::GENERAL:
|
||||
return this->c_general;
|
||||
case type_t::FIND:
|
||||
return this->c_find;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct config {
|
||||
std::map<std::string, clipboard> c_clipboard_impls;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
|
@ -32,18 +32,22 @@
|
|||
#ifndef sysclip_hh
|
||||
#define sysclip_hh
|
||||
|
||||
#include <stdio.h>
|
||||
#include <cstdio>
|
||||
|
||||
enum clip_type_t {
|
||||
CT_GENERAL,
|
||||
CT_FIND,
|
||||
namespace sysclip {
|
||||
|
||||
enum class type_t {
|
||||
GENERAL,
|
||||
FIND,
|
||||
};
|
||||
|
||||
enum clip_op_t {
|
||||
CO_WRITE,
|
||||
CO_READ,
|
||||
enum class op_t {
|
||||
WRITE,
|
||||
READ,
|
||||
};
|
||||
|
||||
FILE *open_clipboard(clip_type_t type, clip_op_t op = CO_WRITE);
|
||||
FILE *open(type_t type, op_t op = op_t::WRITE);
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
Loading…
Reference in New Issue