aboutsummaryrefslogtreecommitdiff
Bash-completion is written with FHS in mind where completion scripts
all get added to /usr/share/bash-completion/completions and are picked
up by the dynamic completion loader from there---whether they are
part of bash-completion or installed by another package.

On Guix systems, we want not only to search within bash-completion's own
directory, but also in the user's profile and guix home profile, as well
as in the system profile.
This is what this patch does.


--- a/bash_completion
+++ b/bash_completion
@@ -2173,7 +2173,15 @@ complete -F _minimal ''
 
 __load_completion()
 {
-    local -a dirs=(${BASH_COMPLETION_USER_DIR:-${XDG_DATA_HOME:-$HOME/.local/share}/bash-completion}/completions)
+    local -a dirs=(
+        ${BASH_COMPLETION_USER_DIR:-${XDG_DATA_HOME:-$HOME/.local/share}/bash-completion}/completions
+        "$HOME/.guix-profile/share/bash-completion/completions"
+        "$HOME/.guix-profile/etc/bash_completion.d"
+        "$HOME/.guix-home/profile/share/bash-completion/completions"
+        "$HOME/.guix-home/profile/etc/bash_completion.d"
+        "/run/current-system/profile/share/bash-completion/completions"
+        "/run/current-system/profile/etc/bash_completion.d" )
+
     local ifs=$IFS IFS=: dir cmd="${1##*/}" compfile
     [[ -n $cmd ]] || return 1
     for dir in ${XDG_DATA_DIRS:-/usr/local/share:/usr/share}; do
         dirs+=( $dir/bash-completion/completions )
='n12' href='#n12'>12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150
/**
* Myext serving of page actions to content scripts
*
* Copyright (C) 2021 Wojtek Kosior
*
* Dual-licensed under:
*   - 0BSD license
*   - GPLv3 or (at your option) any later version
*/

"use strict";

(() => {
    const get_storage = window.get_storage;
    const TYPE_PREFIX = window.TYPE_PREFIX;
    const CONNECTION_TYPE = window.CONNECTION_TYPE;
    const browser = window.browser;
    const listen_for_connection = window.listen_for_connection;
    const url_item = window.url_item;
    const sha256 = window.sha256;

    var storage;
    var handler;

    function send_scripts(url, port)
    {
	let settings = storage.get(TYPE_PREFIX.PAGE, url_item(url));
	if (settings === undefined)
	    return;

	let components = settings.components;
	let processed_bags = new Set();

	if (components !== undefined)
	    send_scripts_rec([components], port, processed_bags);
    }

    // TODO: parallelize script fetching
    async function send_scripts_rec(components, port, processed_bags)
    {
	for (let [prefix, name] of components) {
	    if (prefix === TYPE_PREFIX.BAG) {
		if (processed_bags.has(name)) {
		    console.log(`preventing recursive inclusion of bag ${name}`);
		    continue;
		}

		var bag = storage.get(TYPE_PREFIX.BAG, name);

		if (bag === undefined) {
		    console.log(`no bag in storage for key ${name}`);
		    continue;
		}

		processed_bags.add(name);
		await send_scripts_rec(bag, port, processed_bags);
		processed_bags.delete(name);
	    } else {
		let script_text = await get_script_text(name);
		if (script_text === undefined)
		    continue;

		port.postMessage({inject : [script_text]});
	    }
	}
    }

    async function get_script_text(script_name)
    {
	try {
	    let script_data = storage.get(TYPE_PREFIX.SCRIPT, script_name);
	    if (script_data === undefined) {
		console.log(`missing data for ${script_name}`);
		return;
	    }
	    let script_text = script_data.text;
	    if (!script_text)
		script_text = await fetch_remote_script(script_data);
	    return script_text;
	} catch (e) {
	    console.log(e);
	}
    }

    function ajax_callback()
    {
	if (this.readyState == 4)
	    this.resolve_callback(this);
    }

    function initiate_ajax_request(resolve, method, url)
    {
	var xhttp = new XMLHttpRequest();
	xhttp.resolve_callback = resolve;
	xhttp.onreadystatechange = ajax_callback;
	xhttp.open(method, url, true);
	xhttp.send();
    }

    function make_ajax_request(method, url)
    {
	return new Promise((resolve, reject) =>
			   initiate_ajax_request(resolve, method, url));
    }

    async function fetch_remote_script(script_data)
    {
	try {
	    let xhttp = await make_ajax_request("GET", script_data.url);
	    if (xhttp.status === 200) {
		let computed_hash = sha256(xhttp.responseText);
		if (computed_hash !== script_data.hash) {
		    console.log(`Bad hash for ${script_data.url}\n    got ${computed_hash} instead of ${script_data.hash}`);
		    return;
		}
		return xhttp.responseText;
	    } else {
		console.log("script not fetched: " + script_data.url);
		return;
	    }
	} catch (e) {
	    console.log(e);
	}
    }

    function handle_message(port, message, handler)
    {
	port.onMessage.removeListener(handler[0]);
	let url = message.url;
	console.log({url});
	send_scripts(url, port);
    }

    function new_connection(port)
    {
	console.log("new page actions connection!");
	let handler = [];
	handler.push(m => handle_message(port, m, handler));
	port.onMessage.addListener(handler[0]);
    }

    async function start()
    {
	storage = await get_storage();

	listen_for_connection(CONNECTION_TYPE.PAGE_ACTIONS, new_connection);
    }

    window.start_page_actions_server = start;
})();