Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Python bind for osh #2092

Draft
wants to merge 15 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
122 changes: 115 additions & 7 deletions builtin/readline_osh.py
Original file line number Diff line number Diff line change
@@ -1,39 +1,147 @@
#!/usr/bin/env python2
"""
builtin_lib.py - Builtins that are bindings to libraries, e.g. GNU readline.
readline_osh.py - Builtins that are dependent on GNU readline.
"""
from __future__ import print_function

from _devbuild.gen import arg_types
from _devbuild.gen.syntax_asdl import loc
from core.error import e_usage
from _devbuild.gen.value_asdl import value, value_e
from core import pyutil
from core import vm
from core.error import e_usage
from frontend import flag_util
from mycpp import mops
from mycpp import mylib

from typing import Optional, TYPE_CHECKING
from typing import Optional, Any, TYPE_CHECKING
if TYPE_CHECKING:
from _devbuild.gen.runtime_asdl import cmd_value
from frontend.py_readline import Readline
from core import sh_init
from display import ui

class ctx_Keymap(object):

def __init__(self, readline, keymap_name = None):
# type: (Readline, str) -> None
self.readline = readline
self.orig_keymap_name = keymap_name

def __enter__(self):
# type: () -> None
if self.orig_keymap_name:
self.readline.use_temp_keymap(self.orig_keymap_name)

def __exit__(self, type, value, traceback):
# type: (Any, Any, Any) -> None
if self.orig_keymap_name is not None:
self.readline.restore_orig_keymap()


class Bind(vm._Builtin):
"""For :, true, false."""
"""Interactive interface to readline bindings"""

def __init__(self, readline, errfmt):
# type: (Optional[Readline], ui.ErrorFormatter) -> None
self.readline = readline
self.errfmt = errfmt
self.exclusive_flags = ["q", "u", "r", "x", "f"]

def Run(self, cmd_val):
# type: (cmd_value.Argv) -> int
self.errfmt.Print_("warning: bind isn't implemented",
blame_loc=cmd_val.arg_locs[0])
return 1
readline = self.readline
if not readline:
e_usage("is disabled because Oils wasn't compiled with 'readline'",
loc.Missing)

attrs, arg_r = flag_util.ParseCmdVal('bind', cmd_val)

# print("attrs:\n", attrs)
# print("attrs.attrs:\n", attrs.attrs)
# print("attrs.attrs.m:\n", attrs.attrs["m"])
# print("type(attrs.attrs.m):\n", type(attrs.attrs["m"]))
# print("type(attrs.attrs[m]):\n", type(attrs.attrs["m"]))
# print("attrs.attrs[m].tag() :\n", attrs.attrs["m"].tag())
# print("attrs.attrs[m].tag() == value_e.Undef:\n", attrs.attrs["m"].tag() == value_e.Undef)
# print(arg_r)
# print("Reader argv=%s locs=%s n=%i i=%i" % (arg_r.argv, str(arg_r.locs), arg_r.n, arg_r.i))

# Check mutually-exclusive flags and non-flag args
found = False
for flag in self.exclusive_flags:
if flag in attrs.attrs and attrs.attrs[flag].tag() != value_e.Undef:
# print("\tFound flag: {0} with tag: {1}".format(flag, attrs.attrs[flag].tag()))
if found:
self.errfmt.Print_("error: can only use one of the following flags at a time: -" + ", -".join(self.exclusive_flags), blame_loc=cmd_val.arg_locs[0])
return 1
else:
found = True
if found and not arg_r.AtEnd():
self.errfmt.Print_("error: cannot mix bind commands with the following flags: -" + ", -".join(self.exclusive_flags), blame_loc=cmd_val.arg_locs[0])
return 1


arg = arg_types.bind(attrs.attrs)
# print("arg:\n", arg)
# print("dir(arg):\n", dir(arg))
# print("arg.m:\n", arg.m)


try:
with ctx_Keymap(readline, arg.m): # Replicates bind's -m behavior

# This gauntlet of ifs is meant to replicate bash behavior, in case we
# need to relax the mutual exclusion of flags like bash does

if arg.l:
readline.list_funmap_names()

if arg.p:
readline.function_dumper(True)

if arg.P:
readline.function_dumper(False)

if arg.s:
readline.macro_dumper(True)

if arg.S:
readline.macro_dumper(False)

if arg.v:
readline.variable_dumper(True)

if arg.V:
readline.variable_dumper(False)

if arg.f is not None:
readline.read_init_file(arg.f)

if arg.q is not None:
readline.query_bindings(arg.q)

if arg.u is not None:
readline.unbind_rl_function(arg.u)

if arg.r is not None:
readline.unbind_keyseq(arg.r)

if arg.x is not None:
self.errfmt.Print_("warning: bind -x isn't implemented",
blame_loc=cmd_val.arg_locs[0])
return 1

if arg.X:
readline.print_shell_cmd_map()

except ValueError as e:
# temp var to work around mycpp runtime limitation
msg = e.message # type: str
self.errfmt.Print_("bind error: %s" % msg, loc.Missing)
return 1

return 0


class History(vm._Builtin):
Expand Down
49 changes: 49 additions & 0 deletions cpp/frontend_pyreadline.cc
Original file line number Diff line number Diff line change
Expand Up @@ -324,6 +324,55 @@ void Readline::resize_terminal() {
#endif
}

// bind fns
void Readline::list_funmap_names() {
#if HAVE_READLINE
rl_list_funmap_names();
#else
assert(0); // not implemented
#endif
}

void Readline::read_init_file(BigStr* s) {
assert(0); // not implemented
}

void Readline::function_dumper(bool print_readably) {
assert(0); // not implemented
}

void Readline::macro_dumper(bool print_readably) {
assert(0); // not implemented
}

void Readline::variable_dumper(bool print_readably) {
assert(0); // not implemented
}

void Readline::query_bindings(BigStr* fn_name) {
assert(0); // not implemented
}

void Readline::unbind_rl_function(BigStr* fn_name) {
assert(0); // not implemented
}

void Readline::use_temp_keymap(BigStr* fn_name) {
assert(0); // not implemented
}

void Readline::restore_orig_keymap() {
assert(0); // not implemented
}

void Readline::print_shell_cmd_map() {
assert(0); // not implemented
}

void Readline::unbind_keyseq(BigStr* keyseq) {
assert(0); // not implemented
}

Readline* MaybeGetReadline() {
#if HAVE_READLINE
gReadline = Alloc<Readline>();
Expand Down
13 changes: 13 additions & 0 deletions cpp/frontend_pyreadline.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,19 @@ class Readline {
void remove_history_item(int pos);
int get_current_history_length();
void resize_terminal();
void list_funmap_names();

// These functions were added for the 'bind' builtin
void read_init_file(BigStr* s);
void function_dumper(bool print_readably);
void macro_dumper(bool print_readably);
void variable_dumper(bool print_readably);
void query_bindings(BigStr* fn_name);
void unbind_rl_function(BigStr* fn_name);
void use_temp_keymap(BigStr* fn_name);
void restore_orig_keymap();
void print_shell_cmd_map();
void unbind_keyseq(BigStr* keyseq);

static constexpr uint32_t field_mask() {
return maskbit(offsetof(Readline, completer_delims_)) |
Expand Down
17 changes: 17 additions & 0 deletions frontend/flag_def.py
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,23 @@
#HELP_SPEC.ShortFlag('-i') # show index
# Note: bash has help -d -m -s, which change the formatting

BIND_SPEC = FlagSpec('bind')
BIND_SPEC.ShortFlag('-m', args.String)
BIND_SPEC.ShortFlag('-q', args.String)
BIND_SPEC.ShortFlag('-u', args.String)
BIND_SPEC.ShortFlag('-r', args.String)
BIND_SPEC.ShortFlag('-f', args.String)
BIND_SPEC.ShortFlag('-x', args.String)
BIND_SPEC.ShortFlag('-l')
BIND_SPEC.ShortFlag('-p')
BIND_SPEC.ShortFlag('-s')
BIND_SPEC.ShortFlag('-v')
BIND_SPEC.ShortFlag('-P')
BIND_SPEC.ShortFlag('-S')
BIND_SPEC.ShortFlag('-V')
BIND_SPEC.ShortFlag('-X')


HISTORY_SPEC = FlagSpec('history')
HISTORY_SPEC.ShortFlag('-a')
HISTORY_SPEC.ShortFlag('-r')
Expand Down
44 changes: 44 additions & 0 deletions frontend/py_readline.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,10 @@ def parse_and_bind(self, s):
# type: (str) -> None
line_input.parse_and_bind(s)

def read_init_file(self, s):
# type: (str) -> None
line_input.read_init_file(s)

def add_history(self, line):
# type: (str) -> None
line_input.add_history(line)
Expand Down Expand Up @@ -93,6 +97,46 @@ def get_current_history_length(self):
def resize_terminal(self):
# type: () -> None
line_input.resize_terminal()

def list_funmap_names(self):
# type: () -> None
line_input.list_funmap_names()

def function_dumper(self, print_readably):
# type: (bool) -> None
line_input.function_dumper(print_readably)

def macro_dumper(self, print_readably):
# type: (bool) -> None
line_input.macro_dumper(print_readably)

def variable_dumper(self, print_readably):
# type: (bool) -> None
line_input.variable_dumper(print_readably)

def query_bindings(self, fn_name):
# type: (str) -> None
line_input.query_bindings(fn_name)

def unbind_rl_function(self, fn_name):
# type: (str) -> None
line_input.unbind_rl_function(fn_name)

def use_temp_keymap(self, fn_name):
# type: (str) -> None
line_input.use_temp_keymap(fn_name)

def restore_orig_keymap(self):
# type: () -> None
line_input.restore_orig_keymap()

def print_shell_cmd_map(self):
# type: () -> None
line_input.print_shell_cmd_map()

def unbind_keyseq(self, keyseq):
# type: (str) -> None
line_input.unbind_keyseq(keyseq)


def MaybeGetReadline():
Expand Down
Loading
Loading