Files
2025-05-18 13:04:45 +08:00

176 lines
5.3 KiB
Python

# Copyright Epic Games, Inc. All Rights Reserved.
import os
import re
import shlex
#-------------------------------------------------------------------------------
class Shells(object):
def register_shells(self, registrar):
registrar.add("bash", _Bash)
registrar.add("zsh", _Zsh)
return super().register_shells(registrar)
#-------------------------------------------------------------------------------
class _Base(object):
def __init__(self, system):
self._system = system
def get_system(self):
return self._system
def boot_shell(self, env, cookie, user_script):
try: os.makedirs(os.path.dirname(cookie))
except: pass
with open(cookie, "wt") as out:
def printer(*args, **kwargs):
print(*args, **kwargs, file=out)
self.write_cookie(env, printer)
def write_cookie(self, env, out):
out("cd", shlex.quote(os.getcwd()))
if os.name == "nt":
pieces = []
for x in env["PATH"].split(";"):
x = x.replace("\\", "/")
if x[1] == ":":
x = "/" + x[0] + x[2:]
pieces.append(x)
env["PATH"] = ":".join(pieces)
for key, value in env.read_changes():
if "-" not in key:
value = str(value).replace("\n", "\\n")
out(f"export {key}=$'{value}'")
out(r"\$tip")
out("echo")
#-------------------------------------------------------------------------------
class _Bash(_Base):
def boot_shell(self, env, cookie, user_script):
# Add '\[...\]' around ANSI codes so Bash knows what's displayable
if prompt := env.get("FLOW_PROMPT", None):
out_prompt = ""
prev = 0
for m in re.finditer("\x1b[^m]+m", prompt):
out_prompt += prompt[prev:m.start()]
out_prompt += rf"\[\\033{m.group()[1:]}\]"
prev = m.end()
out_prompt += prompt[prev:]
env["FLOW_PROMPT"] = out_prompt
return super().boot_shell(env, cookie, user_script)
def write_cookie(self, env, out):
super().write_cookie(env, out)
out("source", shlex.quote(self._add_complete_sh()))
def _add_complete_sh(self):
system = self.get_system()
working_dir = system.get_working_dir()
script_path = working_dir + "complete.bash"
out_sh = _get_complete_bash()
cmd_tree = system.get_command_tree()
tree_root = cmd_tree.get_root_node()
for name,_ in tree_root.read_children():
out_sh += " " + shlex.quote(name)
with open(script_path, "w") as out:
out.write(out_sh)
return script_path
def _get_complete_bash():
return r"""
function _flow_prompt() {
if [[ ! -z "$FLOW_PROMPT" ]]; then
local prompt=${FLOW_PROMPT/:FLOW_CWD:/$(dirs +0)}
PS1=$(\$complete \$ "$prompt")
fi
}
if [[ ! -z "$FLOW_PROMPT" ]]; then
PROMPT_COMMAND=_flow_prompt
fi
function _flow_complete() {
local choices=$(\$complete ${COMP_WORDS[@]}...)
COMPREPLY=($(compgen -W "$choices" -- ${COMP_WORDS[$COMP_CWORD]}))
}
complete -F _flow_complete""" # purposely left no trailing CRLF
#-------------------------------------------------------------------------------
class _Zsh(_Base):
def boot_shell(self, env, cookie, user_script):
if prompt := env.get("FLOW_PROMPT", None):
# Add '%{...%}' around ANSI codes so Zsh knows what's displayable
out_prompt = ""
prev = 0
for m in re.finditer("\x1b[^m]+m", prompt):
out_prompt += prompt[prev:m.start()]
out_prompt += "%{" + m.group() + "%}"
prev = m.end()
prompt = out_prompt + prompt[prev:]
# Convert :TAG: to $TAG
out_prompt = ""
prev = 0
for m in re.finditer(":[A-Z_]+:", prompt):
out_prompt += prompt[prev:m.start()]
out_prompt += "$" + m.group()[1:-1]
prev = m.end()
prompt = out_prompt + prompt[prev:]
env["FLOW_PROMPT"] = prompt
return super().boot_shell(env, cookie, user_script)
def write_cookie(self, env, out):
super().write_cookie(env, out)
out("source", shlex.quote(self._add_complete_sh()))
def _add_complete_sh(self):
system = self.get_system()
working_dir = system.get_working_dir()
script_path = working_dir + "complete.zsh"
cmd_tree = system.get_command_tree()
tree_root = cmd_tree.get_root_node()
flow_cmds = "' '".join(name for name,_ in tree_root.read_children())
out_sh = _get_complete_zsh()
out_sh = out_sh.replace("$$$FLOW_CMDS$$$", flow_cmds)
with open(script_path, "w") as out:
out.write(out_sh)
return script_path
def _get_complete_zsh():
return r"""
_flow_prompt() {
eval $(\$prompt --format=sh)
FLOW_CWD=$(dirs)
}
setopt promptsubst
if [[ $FLOW_PROMPT ]]; then
PROMPT=$FLOW_PROMPT
fi
if [[ -z "$FLOW_CMDS" ]]; then
precmd_functions+=(_flow_prompt)
fi
function _flow_complete() {
# setopt local_options xtrace
compadd - $('$complete' ${^^words[@]:0:-1} $PREFIX...)
}
compdef _flow_complete '$$$FLOW_CMDS$$$'
"""