Merge branch 'x_selection' of https://github.com/glacambre/tridactyl into glacambre-x_selection

This commit is contained in:
Oliver Blanthorn 2018-07-12 18:13:49 +01:00
commit 2a43d0b910
No known key found for this signature in database
GPG key ID: 2BB8C36BB504BFF3
3 changed files with 139 additions and 48 deletions

View file

@ -296,6 +296,9 @@ const DEFAULTS = o({
// (but we are probably happy to add your terminal to the list if it isn't already there).
editorcmd: "auto",
browser: "firefox",
yankto: "clipboard", // "clipboard", "selection", "both"
putfrom: "clipboard", // "clipboard", "selection"
externalclipboardcmd: "auto",
nativeinstallcmd:
"curl -fsSl https://raw.githubusercontent.com/cmcaine/tridactyl/master/native/install.sh | bash",
win_nativeinstallcmd:

View file

@ -2118,7 +2118,45 @@ export async function get_current_url() {
*/
//#background
export async function yank(...content: string[]) {
await messageActiveTab("commandline_frame", "setClipboard", content)
await setclip(content.join(" "))
}
/** Copies a string to the clipboard/selection buffer depending on the user's preferences
*
* @hidden
*/
//#background_helper
async function setclip(str) {
// Functions to avoid retyping everything everywhere
let s = () => Native.clipboard("set", str)
let c = () => messageActiveTab("commandline_frame", "setClipboard", [str])
let promises = []
switch (await config.getAsync("yankto")) {
case "selection":
promises = [s()]
break
case "clipboard":
promises = [c()]
break
case "both":
promises = [s(), c()]
break
}
return Promise.all(promises)
}
/** Fetches the content of the clipboard/selection buffer depending on user's preferences
*
* @hidden
*/
//#background_helper
async function getclip() {
if (await config.getAsync("putfrom") == "selection") {
return messageActiveTab("commandline_frame", "getClipboard")
} else {
return Native.clipboard("get", "")
}
}
/** Use the system clipboard.
@ -2135,7 +2173,11 @@ export async function yank(...content: string[]) {
If `excmd == "yankmd"`, copy the title and url of the open page formatted in Markdown for easy use on sites such as reddit.
Unfortunately, javascript can only give us the `clipboard` clipboard, not e.g. the X selection clipboard.
If you're on Linux and the native messenger is installed, Tridactyl will call an external binary (either xclip or xsel) to read or write to your X selection buffer. If you want another program to be used, set "externalclipboardcmd" to its name and make sure it has the same interface as xsel/xclip ("-i"/"-o" and reading from stdin).
When doing a read operation (i.e. open or tabopen), if "putfrom" is set to "selection", the X selection buffer will be read instead of the clipboard. Set "putfrom" to "clipboard" to use the clipboard.
When doing a write operation, if "yankto" is set to "selection", only the X selection buffer will be written to. If "yankto" is set to "both", both the X selection and the clipboard will be written to. If "yankto" is set to "clipboard", only the clipboard will be written to.
*/
//#background
@ -2143,55 +2185,57 @@ export async function clipboard(excmd: "open" | "yank" | "yankshort" | "yankcano
let content = toYank.join(" ")
let url = ""
let urls = []
switch (excmd) {
case "yankshort":
urls = await geturlsforlinks("rel", "shortlink")
if (urls.length == 0) {
urls = await geturlsforlinks("rev", "canonical")
}
if (urls.length > 0) {
await yank(urls[0])
fillcmdline_tmp("3000", "# " + urls[0] + " copied to clipboard.")
try {
switch (excmd) {
case "yankshort":
urls = await geturlsforlinks("rel", "shortlink")
if (urls.length == 0) {
urls = await geturlsforlinks("rev", "canonical")
}
if (urls.length > 0) {
await yank(urls[0])
fillcmdline_tmp("3000", "# " + urls[0] + " copied to clipboard.")
break
}
// Trying yankcanon if yankshort failed...
case "yankcanon":
urls = await geturlsforlinks("rel", "canonical")
if (urls.length > 0) {
await yank(urls[0])
fillcmdline_tmp("3000", "# " + urls[0] + " copied to clipboard.")
break
}
// Trying yank if yankcanon failed...
case "yank":
content = content == "" ? (await activeTab()).url : content
await yank(content)
fillcmdline_tmp("3000", "# " + content + " copied to clipboard.")
break
}
case "yankcanon":
urls = await geturlsforlinks("rel", "canonical")
if (urls.length > 0) {
await yank(urls[0])
fillcmdline_tmp("3000", "# " + urls[0] + " copied to clipboard.")
case "yanktitle":
content = (await activeTab()).title
await yank(content)
fillcmdline_tmp("3000", "# " + content + " copied to clipboard.")
break
}
case "yank":
await messageActiveTab("commandline_content", "focus")
content = content == "" ? (await activeTab()).url : content
await yank(content)
fillcmdline_tmp("3000", "# " + content + " copied to clipboard.")
break
case "yanktitle":
content = (await activeTab()).title
await yank(content)
fillcmdline_tmp("3000", "# " + content + " copied to clipboard.")
break
case "yankmd":
content = "[" + (await activeTab()).title + "](" + (await activeTab()).url + ")"
await yank(content)
fillcmdline_tmp("3000", "# " + content + " copied to clipboard.")
break
case "open":
await messageActiveTab("commandline_content", "focus")
url = await messageActiveTab("commandline_frame", "getClipboard")
url && open(url)
break
case "tabopen":
await messageActiveTab("commandline_content", "focus")
url = await messageActiveTab("commandline_frame", "getClipboard")
url && tabopen(url)
break
default:
// todo: maybe we should have some common error and error handler
throw new Error(`[clipboard] unknown excmd: ${excmd}`)
case "yankmd":
content = "[" + (await activeTab()).title + "](" + (await activeTab()).url + ")"
await yank(content)
fillcmdline_tmp("3000", "# " + content + " copied to clipboard.")
break
case "open":
url = await getclip()
url && open(url)
break
case "tabopen":
url = await getclip()
url && tabopen(url)
break
default:
// todo: maybe we should have some common error and error handler
throw new Error(`[clipboard] unknown excmd: ${excmd}`)
}
} catch (e) {
logger.error(e)
}
CommandLineBackground.hide()
}
/** Change active tab.

View file

@ -282,6 +282,50 @@ export async function getenv(variable: string) {
return (await sendNativeMsg("env", { var: variable })).content
}
/** Calls an external program, to either set or get the content of the X selection.
* When setting the selection or if getting it failed, will return an empty string.
**/
export async function clipboard(
action: "set" | "get",
str: string,
): Promise<string> {
let clipcmd = await config.get("externalclipboardcmd")
if (clipcmd == "auto") clipcmd = await firstinpath(["xsel", "xclip"])
if (clipcmd === undefined) {
throw new Error("Couldn't find an external clipboard executable")
}
if (action == "get") {
let result = await run(clipcmd + " -o")
if (result.code != 0) {
throw new Error(
`External command failed with code ${result.code}: ${clipcmd}`,
)
}
return result.content
} else if (action == "set") {
// We're going to need to insert str, which we can't trust, in the clipcmd
// In order to do this safely we'll use here documents:
// http://pubs.opengroup.org/onlinepubs/009695399/utilities/xcu_chap02.html#tag_02_07_04
// Find a delimiter that isn't in str
let heredoc = "TRIDACTYL"
while (str.search(heredoc) != -1)
heredoc += Math.round(Math.random() * 10)
// Use delimiter to insert str into clipcmd's stdin
clipcmd = `${clipcmd} -i <<'${heredoc}'\n${str}\n${heredoc}\n`
let result = await run(clipcmd)
if (result.code != 0)
throw new Error(
`External command failed with code ${result.code}: ${clipcmd}`,
)
return ""
}
throw new Error("Unknown action!")
}
/** This returns the commandline that was used to start firefox.
You'll get both firefox binary (not necessarily an absolute path) and flags */
export async function ffargs(): Promise<string[]> {