diff --git a/application.py b/application.py index c755b3e..6e12bf9 100755 --- a/application.py +++ b/application.py @@ -129,6 +129,7 @@ class Application(object): 'latex', 'insertmini', 'conf', 'haskell', 'erlang', 'iperl', 'iperlmini', 'ipython', 'ipythonmini', 'bds', #XYZ + 'shell', 'shellmini', ) for name in names: exec("import mode.%s; mode.%s.install(self)" % (name, name)) diff --git a/buffer.py b/buffer.py index e7435a6..c84e35c 100644 --- a/buffer.py +++ b/buffer.py @@ -395,6 +395,24 @@ class ConsoleBuffer(Buffer): def readonly(self): return True +class ConsoleBuffer(Buffer): + btype = 'shell' + modename = 'shell' + def __init__(self): + Buffer.__init__(self) + self.clear() + def clear(self): + lines = ['Shell\n'] + self.set_data(''.join(lines), force=True) + def name(self): + return '*Shell*' + def changed(self): + return False + def close(self): + pass + def readonly(self): + return True + class InterpreterPipeError(Exception): pass diff --git a/mode/sh.py b/mode/sh.py index 1eff6fe..9fc67d3 100644 --- a/mode/sh.py +++ b/mode/sh.py @@ -138,10 +138,11 @@ class Sh(mode.Fundamental): grammar = ShGrammar tabbercls = ShTabber opentokens = ('delimiter', 'sh_reserved', 'case.start') - opentags = {'(': ')', '[': ']', '{': '}', 'do': 'done', 'then': 'fi', 'case': 'esac'} + opentags = {'(': ')', '[': ']', '{': '}', 'do': 'done', 'then': 'fi', + 'case': 'esac'} closetokens = ('delimiter', 'sh_reserved', 'case.end') - closetags = {')': '(', ']': '[', '}': '{', - 'done': 'do', 'fi': 'then', 'esac': 'case'} + closetags = {')': '(', ']': '[', '}': '{', 'done': 'do', 'fi': 'then', + 'esac': 'case'} colors = { 'sh_builtin': ('cyan', 'default', 'bold'), 'sh_function': ('magenta', 'default', 'bold'), diff --git a/mode/shell.py b/mode/shell.py new file mode 100644 index 0000000..95d1262 --- /dev/null +++ b/mode/shell.py @@ -0,0 +1,18 @@ +import color, mode +from lex import Grammar, PatternRule, RegionRule +from mode.sh import ShGrammar + +class ShellGrammar(Grammar): + rules = [ + #RegionRule(r'shell_input', r'^(?:sh$|sh>)', ShGrammar, '\n$'), + #PatternRule(r'shell_mesg', r'^[A-Za-z].*$'), + ] +class Shell(mode.Fundamental): + modename = 'Shell' + #grammar = ShellGrammar() + colors = { + #'shell_mesg': ('red', 'default', 'bold'), + #'shell_input.start': ('red', 'default', 'bold'), + } + +install = Shell.install diff --git a/mode/shellmini.py b/mode/shellmini.py new file mode 100644 index 0000000..f15076e --- /dev/null +++ b/mode/shellmini.py @@ -0,0 +1,262 @@ +import code, os, re, string, StringIO, sys, traceback +import color, completer, lex, method, mode +from lex import Grammar, PatternRule +from mode.sh import ShGrammar +from point import Point +from method import Method +from subprocess import Popen, PIPE, STDOUT + +#PAD = ' ' +PAD = '' +LIMIT = 79 + +class ShellExec(Method): + def _execute(self, w, **vargs): + a = w.application + if a.completion_window_is_open(): + a.close_completion_buffer() + + s = w.buffer.make_string() + w.mode.history[-1] = s + w.mode.history.append('') + w.buffer.set_data('') + w.mode.hindex = len(w.mode.history) - 1 + + if not a.has_buffer_name('*Shell*'): + raise Exception, "No shell found!" + b = a.bufferlist.get_buffer_by_name('*Shell*') + if a.window().buffer is not b: + a.switch_buffer(b) + p = a.get_mini_buffer_prompt() + b.insert_string(b.get_buffer_end(), p + s + '\n', force=True) + + a.set_mini_buffer_prompt('sh$ ') + ok = True + + args = ['sh', '-c', s] + env = {'COLUMNS': '80', 'LINES': '24', 'TERM': os.environ['TERM']} + p = Popen(args, bufsize=1024, stderr=STDOUT, stdout=PIPE, + close_fds=True, env=env) + output = p.stdout.read() + output2 = [] + i = 0 + for c in output: + if c == '\n': + output2.append(c) + i = 0 + elif c == '\t': + j = i % 8 + output2.append(' ' * (8 - j)) + i += 8 - j + elif c == '\a': + pass + elif c == '\b': + if i > 0: + output2.pop(-1) + i -= 1 + else: + output2.append(c) + i += 1 + output = ''.join(output2) + + limit = 1000 + for w2 in b.windows: + limit = min(w.width, limit) + if limit == 1000: + limit = LIMIT + limit -= len(PAD) + + if output: + newlines = [] + for line in output.split('\n'): + i = 0 + while i + limit < len(line): + j = limit + while j > 0 and line[i + j] != ' ': + j -= 1 + if j == 0: + newlines.append(PAD + line[i:i + limit]) + i += j + else: + newlines.append(PAD + line[i:i + j]) + i += j + 1 + newlines.append(PAD + line[i:]) + assert newlines[-1] == PAD + newlines[-1] = '' + b.insert_lines(b.get_buffer_end(), newlines, force=True) + for w2 in b.windows: + w2.goto_end(force=True) + +class ShellCancel(Method): + def execute(self, w, **vargs): + w.application.close_mini_buffer() + if w.application.completion_window_is_open(): + w.application.close_completion_buffer() +class ShellClear(Method): + def execute(self, w, **vargs): + a = w.application + if not a.has_buffer_name('*Shell*'): + raise Exception, "No shell found!" + b = a.bufferlist.get_buffer_by_name('*Shell*') + b.clear() + +class ShellHistoryPrev(Method): + def execute(self, w, **vargs): + if w.mode.hindex <= 0: + w.mode.hindex = 0 + return + elif w.mode.hindex == len(w.mode.history) - 1: + w.mode.history[-1] = w.buffer.make_string() + w.mode.hindex -= 1 + w.buffer.set_data(w.mode.history[w.mode.hindex]) +class ShellHistoryNext(Method): + def execute(self, w, **vargs): + if w.mode.hindex == len(w.mode.history) - 1: + return + w.mode.hindex += 1 + w.buffer.set_data(w.mode.history[w.mode.hindex]) + +#class ShellTab(Method): +# def execute(self, w, **vargs): +# a = w.application +# s = w.buffer.make_string() +# +# x = w.logical_cursor().x +# if not s or s[:x].isspace(): +# w.insert_string_at_cursor(' ' * w.mode.tabwidth) +# return +# +# l = lex.Lexer(w.mode, ShGrammar) +# tokens = list(l.lex([s])) +# +# curr_t = None +# curr_i = None +# for i in range(0, len(tokens)): +# t = tokens[i] +# if t.x < x and t.end_x() >= x: +# curr_i = i +# curr_t = t +# if curr_t is None: +# return +# +# first_t = curr_t +# j = curr_i +# +# name_re = re.compile('^[a-zA-Z_][a-zA-Z0-9_]*$') +# if name_re.match(curr_t.string): +# names = [curr_t.string] +# elif curr_t.string == '.': +# names = [''] +# else: +# names = [] +# +# while j >= 1: +# j -= 1 +# t = tokens[j] +# if name_re.match(t.string): +# names.insert(0, t.string) +# elif t.string == '.': +# pass +# else: +# break +# +# if not names: +# return +# +# obj = None +# g = globals() +# i = 0 +# name = names[0] +# if len(names) > 1: +# while i < len(names): +# name = names[i] +# if obj is None: +# if name in w.mode.locals: +# obj = w.mode.locals[name] +# elif name in w.mode.globals: +# obj = w.mode.globals[name] +# else: +# break +# else: +# if hasattr(obj, name): +# obj = getattr(obj, name) +# else: +# break +# i += 1 +# +# if i == len(names) - 1: +# if obj is not None: +# newnames = dir(obj) +# else: +# newnames = set() +# newnames.update(__builtins__) +# newnames.update(w.mode.locals) +# newnames.update(w.mode.globals) +# candidates = [x for x in newnames if x.startswith(name)] +# +# s = completer.find_common_string(candidates)[len(name):] +# w.insert_string_at_cursor(s) +# mode.mini.use_completion_window(a, name, candidates) + +class ShellBaseMethod(Method): + subcls = Method + subbuf = '*Shell*' + def __init__(self): + Method.__init__(self) + self.submethod = self.subcls() + def _execute(self, w, **vargs): + a = w.application + if not a.has_buffer_name(self.subbuf): + raise Exception, "No shell found!" + w2 = a.bufferlist.get_buffer_by_name(self.subbuf).windows[0] + self.submethod.execute(w2, **vargs) + +class ShellPageUp(ShellBaseMethod): + subcls = method.move.PageUp +class ShellPageDown(ShellBaseMethod): + subcls = method.move.PageDown +class ShellGotoBeginning(ShellBaseMethod): + subcls = method.move.GotoBeginning +class ShellGotoEnd(ShellBaseMethod): + subcls = method.move.GotoEnd + +class OpenShell(Method): + '''Evaluate sh expressions''' + def execute(self, w, **vargs): + a = w.application + if not a.has_buffer_name('*Shell*'): + b = buffer.ShellBuffer() + a.add_buffer(b) + window.Window(b, a) + b = a.bufferlist.get_buffer_by_name('*Shell*') + if a.window().buffer is not b: + a.switch_buffer(b) + f = lambda x: None + w.application.open_mini_buffer('sh$ ', f, self, None, 'shellmini') + +class ShellMini(mode.Fundamental): + modename = 'ShellMini' + grammar = ShGrammar + actions = [ShellExec, ShellClear, ShellCancel, ShellHistoryPrev, + ShellHistoryNext, #ShellTab, + ShellPageUp, ShellPageDown, ShellGotoBeginning, ShellGotoEnd, + OpenShell] + def __init__(self, w): + mode.Fundamental.__init__(self, w) + self.globals = dict(w.application.globals()) + self.locals = dict(w.application.locals()) + self.saved_input = "" + self.history = [''] + self.hindex = 0 + self.add_bindings('shell-exec', ('RETURN',)) + self.add_bindings('shell-clear', ('C-l',)) + self.add_bindings('shell-cancel', ('C-]', 'C-g')) + self.add_bindings('shell-history-prev', ('C-p', 'UP')) + self.add_bindings('shell-history-next', ('C-n', 'DOWN')) + #self.add_bindings('shell-tab', ('TAB',)) + self.add_bindings('shell-page-up', ('M-v',)) + self.add_bindings('shell-page-down', ('C-v',)) + self.add_bindings('shell-goto-beginning', ('M-<',)) + self.add_bindings('shell-goto-end', ('M->',)) + +install = ShellMini.install