commit 59b70998b162c461ec03780e9ca4cabdb7411af4 Author: Damian Johnson atagar@torproject.org Date: Tue Aug 30 09:41:53 2011 -0700
Moving content and backlog tracking to interpretor
The interpretor class will be tracking state so moving this information to it from the panel. This will make a common saving function feasable. --- src/cli/interpretorPanel.py | 49 +++++++++++----------------------- src/util/torInterpretor.py | 60 +++++++++++++++++++++++++++++++++++-------- 2 files changed, 65 insertions(+), 44 deletions(-)
diff --git a/src/cli/interpretorPanel.py b/src/cli/interpretorPanel.py index 02868ce..cc8b496 100644 --- a/src/cli/interpretorPanel.py +++ b/src/cli/interpretorPanel.py @@ -10,17 +10,12 @@ from util import panel, textInput, torInterpretor, uiTools USAGE_INFO = "to use this panel press enter" PROMPT_LINE = [torInterpretor.PROMPT, (USAGE_INFO, torInterpretor.USAGE_FORMAT)]
-# limits used for cropping -BACKLOG_LIMIT = 100 -LINES_LIMIT = 2000 - -# lazy loaded curses formatting constants +# lazy loaded mapping of interpretor attributes to curses formatting constants FORMATS = {}
def getFormat(formatAttr): """ - Provides the curses drawing attributes for torInterpretor formatting - attributes. + Provides the curses drawing attributes for torInterpretor formats.
Arguments: formatAttr - list of formatting attributes @@ -45,11 +40,10 @@ def getFormat(formatAttr): class InterpretorPanel(panel.Panel): def __init__(self, stdscr): panel.Panel.__init__(self, stdscr, "interpretor", 0) + self.interpretor = torInterpretor.ControlInterpretor() + self.inputCompleter = torInterpretor.TorControlCompleter() self.isInputMode = False self.scroll = 0 - self.interpretor = torInterpretor.ControlInterpretor() - self.previousCommands = [] # user input, newest to oldest - self.contents = [PROMPT_LINE] # (msg, format enum) tuples being displayed (oldest to newest)
def prompt(self): """ @@ -64,17 +58,16 @@ class InterpretorPanel(panel.Panel): self.redraw(True)
# intercepts input so user can cycle through the history - torCommands = torInterpretor.TorControlCompleter() - validator = textInput.BasicValidator() - validator = textInput.HistoryValidator(self.previousCommands, validator) - validator = textInput.TabCompleter(torCommands.getMatches, validator) + validator = textInput.HistoryValidator(list(reversed(self.interpretor.getBacklog())), validator) + validator = textInput.TabCompleter(self.inputCompleter.getMatches, validator)
xOffset = len(torInterpretor.PROMPT[0]) - if len(self.contents) > self.maxY - 1: + displayLength = len(self.interpretor.getDisplayContents(PROMPT_LINE)) + if displayLength > self.maxY - 1: xOffset += 3 # offset for scrollbar
- inputLine = min(self.maxY - 1, len(self.contents)) + inputLine = min(self.maxY - 1, displayLength) inputFormat = getFormat(torInterpretor.INPUT_FORMAT) input = self.getstr(inputLine, xOffset, "", inputFormat, validator = validator) input, isDone = input.strip(), False @@ -83,22 +76,10 @@ class InterpretorPanel(panel.Panel): # terminate input when we get a blank line isDone = True else: - self.previousCommands.insert(0, input) - self.previousCommands = self.previousCommands[:BACKLOG_LIMIT] - try: inputEntry, outputEntry = self.interpretor.handleQuery(input) except torInterpretor.InterpretorClosed: isDone = True - - promptEntry = self.contents.pop() # removes old prompt entry - self.contents += inputEntry - self.contents += outputEntry - self.contents.append(promptEntry) - - # if too long then crop lines - cropLines = len(self.contents) - LINES_LIMIT - if cropLines > 0: self.contents = self.contents[cropLines:]
if isDone: self.isInputMode = False @@ -112,7 +93,8 @@ class InterpretorPanel(panel.Panel): self.prompt() elif uiTools.isScrollKey(key) and not self.isInputMode: pageHeight = self.getPreferredSize()[0] - 1 - newScroll = uiTools.getScrollPosition(key, self.scroll, pageHeight, len(self.contents)) + displayLength = len(self.interpretor.getDisplayContents(PROMPT_LINE)) + newScroll = uiTools.getScrollPosition(key, self.scroll, pageHeight, displayLength)
if self.scroll != newScroll: self.scroll = newScroll @@ -127,17 +109,18 @@ class InterpretorPanel(panel.Panel): self.addstr(0, 0, "Control Interpretor%s:" % usageMsg, curses.A_STANDOUT)
xOffset = 0 - if len(self.contents) > height - 1: + displayContents = self.interpretor.getDisplayContents(PROMPT_LINE) + if len(displayContents) > height - 1: # if we're in input mode then make sure the last line is visible if self.isInputMode: - self.scroll = len(self.contents) - height + 1 + self.scroll = len(displayContents) - height + 1
xOffset = 3 - self.addScrollBar(self.scroll, self.scroll + height - 1, len(self.contents), 1) + self.addScrollBar(self.scroll, self.scroll + height - 1, len(displayContents), 1)
# draws prior commands and output drawLine = 1 - for entry in self.contents[self.scroll:]: + for entry in displayContents[self.scroll:]: cursor = xOffset
for msg, formatEntry in entry: diff --git a/src/util/torInterpretor.py b/src/util/torInterpretor.py index c7bcebb..e062532 100644 --- a/src/util/torInterpretor.py +++ b/src/util/torInterpretor.py @@ -31,6 +31,10 @@ ERROR_FORMAT = (Attr.BOLD, Color.RED) CSI = "\x1B[%sm" RESET = CSI % "0"
+# limits used for cropping +BACKLOG_LIMIT = 100 +CONTENT_LIMIT = 2000 + class InterpretorClosed(Exception): """ Exception raised when the interpretor should be shut down. @@ -92,11 +96,10 @@ class TorControlCompleter: if line.startswith("config/*") or line.startswith("dir-usage"): continue
- # strips off the ending asterisk if it accepts a value infoOpt = line.split(" ", 1)[0]
- if infoOpt.endswith("*"): - infoOpt = infoOpt[:-1] + # strips off the ending asterisk if it accepts a value + if infoOpt.endswith("*"): infoOpt = infoOpt[:-1]
self.commands.append("GETINFO %s" % infoOpt) else: self.commands.append("GETINFO ") @@ -157,7 +160,7 @@ class TorControlCompleter:
def getMatches(self, text): """ - Provides all options that match the given input. + Provides all options that match the given input. This is case insensetive.
Arguments: text - user input text to be matched against @@ -171,10 +174,9 @@ class TorControlCompleter: the readlines set_completer function. """
- for cmd in self.commands: - if cmd.lower().startswith(text.lower()): - if not state: return cmd - else: state -= 1 + for cmd in self.getMatches(text): + if not state: return cmd + else: state -= 1
class ControlInterpretor: """ @@ -183,8 +185,29 @@ class ControlInterpretor: """
def __init__(self): - self.queries = [] # requests made, newest to oldest - self.contents = [] # (msg, format list) tuples of both input and output (oldest to newest) + self.backlog = [] # prior requests the user has made + self.contents = [] # (msg, format list) tuples for what's been displayed + + def getBacklog(self): + """ + Provides the backlog of prior user input. + """ + + return self.backlog + + def getDisplayContents(self, appendPrompt = None): + """ + Provides a list of lines to be displayed, each being a list of (msg, + format) tuples for the content to be displayed. This is ordered as the + oldest to newest. + + Arguments: + appendPrompt - adds the given line to the end + """ + + if appendPrompt: + return self.contents + [appendPrompt] + else: return self.contents
def handleQuery(self, input): """ @@ -199,6 +222,12 @@ class ControlInterpretor: """
input = input.strip() + + # appends new input, cropping if too long + self.backlog.append(input) + backlogCrop = len(self.backlog) - BACKLOG_LIMIT + if backlogCrop > 0: self.backlog = self.backlog[backlogCrop:] + inputEntry, outputEntry = [PROMPT], [] conn = torTools.getConn()
@@ -258,7 +287,16 @@ class ControlInterpretor: except Exception, exc: outputEntry.append((str(exc), ERROR_FORMAT))
- return (_splitOnNewlines(inputEntry), _splitOnNewlines(outputEntry)) + # converts to lists split on newlines + inputLines = _splitOnNewlines(inputEntry) + outputLines = _splitOnNewlines(outputEntry) + + # appends new contents, cropping if too long + self.contents += inputLines + outputLines + cropLines = len(self.contents) - CONTENT_LIMIT + if cropLines > 0: self.contents = self.contents[cropLines:] + + return (inputLines, outputLines)
def prompt(): prompt = format(">>> ", Color.GREEN, Attr.BOLD)