Skip to content

Commit

Permalink
[demo/pyreadline] Change PS2 for Oil, and code cleanup.
Browse files Browse the repository at this point in the history
  • Loading branch information
Andy Chu committed Jan 8, 2019
1 parent 77687bc commit ec8e2ca
Show file tree
Hide file tree
Showing 4 changed files with 53 additions and 68 deletions.
2 changes: 1 addition & 1 deletion core/completion.py
Expand Up @@ -293,7 +293,7 @@ def Matches(self, comp):
to_list = '.'
base = ''
elif i == 0: # it's an absolute path to_complete like / or /b
to_list ='/'
to_list = '/'
base = '/'
else:
to_list = to_complete[:i]
Expand Down
107 changes: 43 additions & 64 deletions demo/pyreadline.py
Expand Up @@ -51,7 +51,7 @@
completions (which implies that it cannot remove any duplicate completions).
The default value is 1, which means that Readline will sort the completions
and, depending on the value of rl_ignore_completion_duplicates, will attempt
to remove duplicate matches.
to remove duplicate matches.
"""
from __future__ import print_function

Expand Down Expand Up @@ -86,6 +86,7 @@

# Prompt style
_RIGHT = '_RIGHT'
_OSH = '_OSH'


# ANSI escape codes affect the prompt!
Expand Down Expand Up @@ -159,7 +160,7 @@ class FileSystemAction(object):
"""Complete paths from the file system.
Directories will have a / suffix.
Copied from core/completion.py in Oil.
"""

Expand All @@ -178,7 +179,7 @@ def Matches(self, to_complete):
to_list = '.'
base = ''
elif i == 0: # it's an absolute path to_complete like / or /b
to_list ='/'
to_list = '/'
base = '/'
else:
to_list = to_complete[:i]
Expand All @@ -193,16 +194,6 @@ def Matches(self, to_complete):
for name in names:
path = os.path.join(base, name)
if path.startswith(to_complete):
if self.dirs_only: # add_slash not used here
# NOTE: There is a duplicate isdir() check later to add a trailing
# slash. Consolidate the checks for fewer stat() ops. This is hard
# because all the completion actions must obey the same interface.
# We could have another type like candidate = File | Dir |
# OtherString ?
if os_path.isdir(path):
yield path
continue

if self.exec_only:
# TODO: Handle exception if file gets deleted in between listing and
# check?
Expand All @@ -220,7 +211,7 @@ def Matches(self, to_complete):

class FlagsHelpAction(object):
"""Yield flags and their help.
Return a list of TODO: This API can't be expressed in shell itself. How do
zsh and fish do it?
"""
Expand Down Expand Up @@ -278,7 +269,7 @@ def MakeCompletionRequest(lines):
Cases we CAN'T complete:
ec\
h<TAB> # complete 'o' ?
echo f\
o<TAB> # complete 'o' ?
"""
Expand All @@ -290,8 +281,9 @@ def MakeCompletionRequest(lines):

partial_cmd, last_line_pos = JoinLinesOfCommand(lines)

first = None # the first word, or None if we're completing the first word
# itself (and the candidate is in
# the first word if we're completing an arg, or None if we're completing the
# first word itself
first = None

cmd_last_space_pos = partial_cmd.rfind(' ')
if cmd_last_space_pos == -1: # FIRST WORD state, no prefix
Expand Down Expand Up @@ -611,7 +603,7 @@ def _PrintCandidates(self, subst, matches, unused_max_match_len):
# ls <TAB>
# ls --<TAB>
#
# This is because there is a common prefix.
# This is because there is a common prefix.
# So instead use the hash of all matches as the identity.

# This could be more accurate but I think it's good enough.
Expand Down Expand Up @@ -736,15 +728,6 @@ def OnWindowChange(self):
# display to be shown with different widths.
self.width_is_dirty = True


# What does the Oil prompt look like, vs OSH?
# We have reverse and underline styles. But we need a default.
# We also have bold. We might not want the command line to be bold, because
# later it could be syntax-highlighted.

_PS1 = '\u@\h \w'
_PS2 = '> ' # A different length to test Display


def DoNothing(unused1, unused2):
pass
Expand All @@ -754,6 +737,10 @@ class PromptEvaluator(object):
"""Evaluate the prompt and give it a certain style."""

def __init__(self, style, display):
"""
Args:
style: _RIGHT, _BOLD, _UNDERLINE, _REVERSE or _OSH
"""
self.style = style
self.display = display

Expand Down Expand Up @@ -784,10 +771,13 @@ def Eval(self, template):
p2 = _PROMPT_REVERSE + ' ' + p + ' ' + _PROMPT_RESET + ' '
prompt_len += 3

else:
elif self.style == _OSH:
p2 = p + '$ ' # emulate bash style
prompt_len += 2

else:
raise AssertionError

return p2, prompt_len


Expand All @@ -796,7 +786,9 @@ class InteractiveLineReader(object):
Holds PS1 / PS2 state.
"""
def __init__(self, prompt_eval, display, bold_line=False):
def __init__(self, ps1, ps2, prompt_eval, display, bold_line=False):
self.ps1 = ps1
self.ps2 = ps2
self.prompt_eval = prompt_eval
self.display = display
self.bold_line = bold_line
Expand All @@ -806,31 +798,19 @@ def __init__(self, prompt_eval, display, bold_line=False):
self.Reset() # initialize self.prompt_str

# https://stackoverflow.com/questions/22916783/reset-python-sigint-to-default-signal-handler
self.orig_handler = signal.getsignal(signal.SIGINT)
self.orig_handler = signal.getsignal(signal.SIGINT)
self.last_prompt_len = 0
#log('%s', self.orig_handler)

def GetLine(self):
signal.signal(signal.SIGINT, self.orig_handler) # raise KeyboardInterrupt
if self.prompt_str != _PS2:
p2, prompt_len = self.prompt_eval.Eval(self.prompt_str)

else:
p2 = self.prompt_str
prompt_len = len(self.prompt_str)

# Tell the display how wide the prompt now is, so it can _ReturnToPrompt()!
self.display.SetPromptLength(prompt_len)

#p2 = _BOLD + p + _RESET
p = self.prompt_str
if self.display.bold_line:
p2 += _PROMPT_BOLD

# Maybe Oil prompt should use reverse video, so you can tell them apart?
#p2 = _REVERSE + ' ' + p + _RESET + ' '
p += _PROMPT_BOLD

try:
line = raw_input(p2) + '\n' # newline required
line = raw_input(p) + '\n' # newline required
except KeyboardInterrupt:
print('^C')
line = -1
Expand All @@ -850,11 +830,12 @@ def GetLine(self):
sys.stdout.write('\x1b[1A\x1b[2K\n')
sys.stdout.flush()

self.prompt_str = _PS2 # TODO: Do we need $PS2? Would be easy.
self.prompt_str = self.ps2
return line

def Reset(self):
self.prompt_str = _PS1
self.prompt_str, prompt_len = self.prompt_eval.Eval(self.ps1)
self.display.SetPromptLength(prompt_len)
del self.pending_lines[:]


Expand Down Expand Up @@ -900,7 +881,7 @@ def MainLoop(reader, display):
reader.Reset()


_COMMANDS = [
_MORE_COMMANDS = [
'cd', 'echo', 'sleep', 'clear', 'slowc', 'many', 'toomany'
]

Expand Down Expand Up @@ -929,6 +910,9 @@ def LoadFlags(path):
return flags


_PS1 = '\u@\h \w'


def main(argv):
_, term_width = GetTerminalSize()
fmt = '%' + str(term_width) + 's'
Expand All @@ -941,21 +925,17 @@ def main(argv):
# Used to store the original line, flag descriptions, etc.
comp_state = {}

# TODO:
# - in the case of the right-hand prompt, the display should render it?
# - i.e. you should have a PromptEvaluator, and it should be passed
# potentially to both of these based on the style?
# - reader renders it if it's a normal prompt, and display renders it if it's
# a RHS prompt.

# OSH looks normal?
#prompt = PromptEvaluator(None)

# Oil has reverse video on the right. It's also bold, and may be syntax
# highlighted later.
display = Display(comp_state, bold_line=True)
prompt = PromptEvaluator(_RIGHT, display)
reader = InteractiveLineReader(prompt, display) # updates the display

osh = False
if osh:
prompt = PromptEvaluator(_OSH, display)
reader = InteractiveLineReader(_PS1, '> ', prompt, display)
else:
# Oil has reverse video on the right. It's also bold, and may be syntax
# highlighted later.
prompt = PromptEvaluator(_RIGHT, display)
reader = InteractiveLineReader(_PS1, '| ', prompt, display)

# Register a callback to receive terminal width changes.
signal.signal(signal.SIGWINCH, lambda x, y: display.OnWindowChange())
Expand All @@ -980,7 +960,7 @@ def main(argv):
comp_lookup[cmd] = FlagsAndFileSystemAction(fl, _FS_ACTION)
commands.append(cmd)

comp_lookup['__first'] = WordsAction(commands + _COMMANDS)
comp_lookup['__first'] = WordsAction(commands + _MORE_COMMANDS)

# Register a callback to generate completion candidates.
root_comp = RootCompleter(reader, display, comp_lookup, comp_state)
Expand All @@ -996,7 +976,6 @@ def main(argv):
readline.set_completion_display_matches_hook(
lambda *args: display.PrintCandidates(*args)
)

readline.parse_and_bind('tab: complete')

MainLoop(reader, display)
Expand Down
7 changes: 4 additions & 3 deletions demo/pyreadline_test.py
Expand Up @@ -35,11 +35,12 @@ def testRootCompleter(self):
comp_lookup = {
'echo': pyreadline.WordsAction(['foo', 'bar']),
}
reader = pyreadline.InteractiveLineReader()
display = pyreadline.Display(comp_state, bold_line=True)
prompt = pyreadline.PromptEvaluator(pyreadline._RIGHT, display)
reader = pyreadline.InteractiveLineReader('$ ', '> ', prompt, display)
reader.pending_lines.extend([
'echo \\\n', # first line
'echo \\\n', # first line
])
display = pyreadline.Display(comp_state, reader)

r = pyreadline.RootCompleter(reader, display, comp_lookup, comp_state)
# second line
Expand Down
5 changes: 5 additions & 0 deletions test/lint.sh
Expand Up @@ -100,6 +100,11 @@ bin-flake8() {
fi
}

# Just do a single file
flake8-one() {
bin-flake8 --ignore 'E111,E114,E226,E265' "$@"
}

flake8-all() {
local -a dirs=(asdl bin core oil_lang osh opy ovm2)

Expand Down

0 comments on commit ec8e2ca

Please sign in to comment.