aboutsummaryrefslogtreecommitdiff
path: root/.config/fish/functions
diff options
context:
space:
mode:
Diffstat (limited to '.config/fish/functions')
-rw-r--r--.config/fish/functions/__bass.py140
-rw-r--r--.config/fish/functions/bass.fish29
2 files changed, 169 insertions, 0 deletions
diff --git a/.config/fish/functions/__bass.py b/.config/fish/functions/__bass.py
new file mode 100644
index 0000000..3f02bd4
--- /dev/null
+++ b/.config/fish/functions/__bass.py
@@ -0,0 +1,140 @@
+"""
+To be used with a companion fish function like this:
+
+ function refish
+ set -l _x (python /tmp/bass.py source ~/.nvm/nvim.sh ';' nvm use iojs); source $_x; and rm -f $_x
+ end
+
+"""
+
+from __future__ import print_function
+
+import json
+import os
+import signal
+import subprocess
+import sys
+import traceback
+
+
+BASH = 'bash'
+
+FISH_READONLY = [
+ 'PWD', 'SHLVL', 'history', 'pipestatus', 'status', 'version',
+ 'FISH_VERSION', 'fish_pid', 'hostname', '_', 'fish_private_mode'
+]
+
+IGNORED = [
+ 'PS1', 'XPC_SERVICE_NAME'
+]
+
+def ignored(name):
+ if name == 'PWD': # this is read only, but has special handling
+ return False
+ # ignore other read only variables
+ if name in FISH_READONLY:
+ return True
+ if name in IGNORED or name.startswith("BASH_FUNC"):
+ return True
+ if name.startswith('%'):
+ return True
+ return False
+
+def escape(string):
+ # use json.dumps to reliably escape quotes and backslashes
+ return json.dumps(string).replace(r'$', r'\$')
+
+def escape_identifier(word):
+ return escape(word.replace('?', '\\?'))
+
+def comment(string):
+ return '\n'.join(['# ' + line for line in string.split('\n')])
+
+def gen_script():
+ # Use the following instead of /usr/bin/env to read environment so we can
+ # deal with multi-line environment variables (and other odd cases).
+ env_reader = "%s -c 'import os,json; print(json.dumps({k:v for k,v in os.environ.items()}))'" % (sys.executable)
+ args = [BASH, '-c', env_reader]
+ output = subprocess.check_output(args, universal_newlines=True)
+ old_env = output.strip()
+
+ pipe_r, pipe_w = os.pipe()
+ if sys.version_info >= (3, 4):
+ os.set_inheritable(pipe_w, True)
+ command = 'eval $1 && ({}; alias) >&{}'.format(
+ env_reader,
+ pipe_w
+ )
+ args = [BASH, '-c', command, 'bass', ' '.join(sys.argv[1:])]
+ p = subprocess.Popen(args, universal_newlines=True, close_fds=False)
+ os.close(pipe_w)
+ with os.fdopen(pipe_r) as f:
+ new_env = f.readline()
+ alias_str = f.read()
+ if p.wait() != 0:
+ raise subprocess.CalledProcessError(
+ returncode=p.returncode,
+ cmd=' '.join(sys.argv[1:]),
+ output=new_env + alias_str
+ )
+ new_env = new_env.strip()
+
+ old_env = json.loads(old_env)
+ new_env = json.loads(new_env)
+
+ script_lines = []
+
+ for k, v in new_env.items():
+ if ignored(k):
+ continue
+ v1 = old_env.get(k)
+ if not v1:
+ script_lines.append(comment('adding %s=%s' % (k, v)))
+ elif v1 != v:
+ script_lines.append(comment('updating %s=%s -> %s' % (k, v1, v)))
+ # process special variables
+ if k == 'PWD':
+ script_lines.append('cd %s' % escape(v))
+ continue
+ else:
+ continue
+ if k == 'PATH':
+ value = ' '.join([escape(directory)
+ for directory in v.split(':')])
+ else:
+ value = escape(v)
+ script_lines.append('set -g -x %s %s' % (k, value))
+
+ for var in set(old_env.keys()) - set(new_env.keys()):
+ script_lines.append(comment('removing %s' % var))
+ script_lines.append('set -e %s' % var)
+
+ script = '\n'.join(script_lines)
+
+ alias_lines = []
+ for line in alias_str.splitlines():
+ _, rest = line.split(None, 1)
+ k, v = rest.split("=", 1)
+ alias_lines.append("alias " + escape_identifier(k) + "=" + v)
+ alias = '\n'.join(alias_lines)
+
+ return script + '\n' + alias
+
+script_file = os.fdopen(3, 'w')
+
+if not sys.argv[1:]:
+ print('__bass_usage', file=script_file, end='')
+ sys.exit(0)
+
+try:
+ script = gen_script()
+except subprocess.CalledProcessError as e:
+ sys.exit(e.returncode)
+except Exception:
+ print('Bass internal error!', file=sys.stderr)
+ raise # traceback will output to stderr
+except KeyboardInterrupt:
+ signal.signal(signal.SIGINT, signal.SIG_DFL)
+ os.kill(os.getpid(), signal.SIGINT)
+else:
+ script_file.write(script)
diff --git a/.config/fish/functions/bass.fish b/.config/fish/functions/bass.fish
new file mode 100644
index 0000000..2b3af16
--- /dev/null
+++ b/.config/fish/functions/bass.fish
@@ -0,0 +1,29 @@
+function bass
+ set -l bash_args $argv
+ set -l bass_debug
+ if test "$bash_args[1]_" = '-d_'
+ set bass_debug true
+ set -e bash_args[1]
+ end
+
+ set -l script_file (mktemp)
+ if command -v python3 >/dev/null 2>&1
+ command python3 -sS (dirname (status -f))/__bass.py $bash_args 3>$script_file
+ else
+ command python -sS (dirname (status -f))/__bass.py $bash_args 3>$script_file
+ end
+ set -l bass_status $status
+ if test $bass_status -ne 0
+ return $bass_status
+ end
+
+ if test -n "$bass_debug"
+ cat $script_file
+ end
+ source $script_file
+ command rm $script_file
+end
+
+function __bass_usage
+ echo "Usage: bass [-d] <bash-command>"
+end