Home | History | Annotate | Download | only in cli
      1 # Copyright 2017 The TensorFlow Authors. All Rights Reserved.
      2 #
      3 # Licensed under the Apache License, Version 2.0 (the "License");
      4 # you may not use this file except in compliance with the License.
      5 # You may obtain a copy of the License at
      6 #
      7 #     http://www.apache.org/licenses/LICENSE-2.0
      8 #
      9 # Unless required by applicable law or agreed to in writing, software
     10 # distributed under the License is distributed on an "AS IS" BASIS,
     11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     12 # See the License for the specific language governing permissions and
     13 # limitations under the License.
     14 # ==============================================================================
     15 """Widgets for Curses-based CLI."""
     16 from __future__ import absolute_import
     17 from __future__ import division
     18 from __future__ import print_function
     19 
     20 from tensorflow.python.debug.cli import debugger_cli_common
     21 
     22 
     23 RL = debugger_cli_common.RichLine
     24 
     25 
     26 class NavigationHistoryItem(object):
     27   """Individual item in navigation history."""
     28 
     29   def __init__(self, command, screen_output, scroll_position):
     30     """Constructor of NavigationHistoryItem.
     31 
     32     Args:
     33       command: (`str`) the command line text.
     34       screen_output: the screen output of the command.
     35       scroll_position: (`int`) scroll position in the screen output.
     36     """
     37     self.command = command
     38     self.screen_output = screen_output
     39     self.scroll_position = scroll_position
     40 
     41 
     42 class CursesNavigationHistory(object):
     43   """Navigation history containing commands, outputs and scroll info."""
     44 
     45   BACK_ARROW_TEXT = "<--"
     46   FORWARD_ARROW_TEXT = "-->"
     47 
     48   def __init__(self, capacity):
     49     """Constructor of CursesNavigationHistory.
     50 
     51     Args:
     52       capacity: (`int`) How many items this object can hold. Each item consists
     53         of a command stirng, an output RichTextLines object and a scroll
     54         position.
     55 
     56     Raises:
     57       ValueError: If capacity is not a positive number.
     58     """
     59     if capacity <= 0:
     60       raise ValueError("In valid capacity value: %d" % capacity)
     61 
     62     self._capacity = capacity
     63     self._items = []
     64     self._pointer = -1
     65 
     66   def add_item(self, command, screen_output, scroll_position):
     67     """Add an item to the navigation histoyr.
     68 
     69     Args:
     70       command: command line text.
     71       screen_output: screen output produced for the command.
     72       scroll_position: (`int`) scroll position in the screen output.
     73     """
     74     if self._pointer + 1 < len(self._items):
     75       self._items = self._items[:self._pointer + 1]
     76     self._items.append(
     77         NavigationHistoryItem(command, screen_output, scroll_position))
     78     if len(self._items) > self._capacity:
     79       self._items = self._items[-self._capacity:]
     80     self._pointer = len(self._items) - 1
     81 
     82   def update_scroll_position(self, new_scroll_position):
     83     """Update the scroll position of the currently-pointed-to history item.
     84 
     85     Args:
     86       new_scroll_position: (`int`) new scroll-position value.
     87 
     88     Raises:
     89       ValueError: If the history is empty.
     90     """
     91     if not self._items:
     92       raise ValueError("Empty navigation history")
     93     self._items[self._pointer].scroll_position = new_scroll_position
     94 
     95   def size(self):
     96     return len(self._items)
     97 
     98   def pointer(self):
     99     return self._pointer
    100 
    101   def go_back(self):
    102     """Go back one place in the history, if possible.
    103 
    104     Decrease the pointer value by 1, if possible. Otherwise, the pointer value
    105     will be unchanged.
    106 
    107     Returns:
    108       The updated pointer value.
    109 
    110     Raises:
    111       ValueError: If history is empty.
    112     """
    113     if not self._items:
    114       raise ValueError("Empty navigation history")
    115 
    116     if self.can_go_back():
    117       self._pointer -= 1
    118     return self._items[self._pointer]
    119 
    120   def go_forward(self):
    121     """Go forward one place in the history, if possible.
    122 
    123     Increase the pointer value by 1, if possible. Otherwise, the pointer value
    124     will be unchanged.
    125 
    126     Returns:
    127       The updated pointer value.
    128 
    129     Raises:
    130       ValueError: If history is empty.
    131     """
    132     if not self._items:
    133       raise ValueError("Empty navigation history")
    134 
    135     if self.can_go_forward():
    136       self._pointer += 1
    137     return self._items[self._pointer]
    138 
    139   def can_go_back(self):
    140     """Test whether client can go back one place.
    141 
    142     Returns:
    143       (`bool`) Whether going back one place is possible.
    144     """
    145     return self._pointer >= 1
    146 
    147   def can_go_forward(self):
    148     """Test whether client can go forward one place.
    149 
    150     Returns:
    151       (`bool`) Whether going back one place is possible.
    152     """
    153     return self._pointer + 1 < len(self._items)
    154 
    155   def render(self,
    156              max_length,
    157              backward_command,
    158              forward_command,
    159              latest_command_attribute="black_on_white",
    160              old_command_attribute="magenta_on_white"):
    161     """Render the rich text content of the single-line navigation bar.
    162 
    163     Args:
    164       max_length: (`int`) Maximum length of the navigation bar, in characters.
    165       backward_command: (`str`) command for going backward. Used to construct
    166         the shortcut menu item.
    167       forward_command: (`str`) command for going forward. Used to construct the
    168         shortcut menu item.
    169        latest_command_attribute: font attribute for lastest command.
    170        old_command_attribute: font attribute for old (non-latest) command.
    171 
    172     Returns:
    173       (`debugger_cli_common.RichTextLines`) the navigation bar text with
    174         attributes.
    175 
    176     """
    177     output = RL("| ")
    178     output += RL(
    179         self.BACK_ARROW_TEXT,
    180         (debugger_cli_common.MenuItem(None, backward_command)
    181          if self.can_go_back() else None))
    182     output += RL(" ")
    183     output += RL(
    184         self.FORWARD_ARROW_TEXT,
    185         (debugger_cli_common.MenuItem(None, forward_command)
    186          if self.can_go_forward() else None))
    187 
    188     if self._items:
    189       command_attribute = (latest_command_attribute
    190                            if (self._pointer == (len(self._items) - 1))
    191                            else old_command_attribute)
    192       output += RL(" | ")
    193       if self._pointer != len(self._items) - 1:
    194         output += RL("(-%d) " % (len(self._items) - 1 - self._pointer),
    195                      command_attribute)
    196 
    197       if len(output) < max_length:
    198         maybe_truncated_command = self._items[self._pointer].command[
    199             :(max_length - len(output))]
    200         output += RL(maybe_truncated_command, command_attribute)
    201 
    202     return debugger_cli_common.rich_text_lines_from_rich_line_list([output])
    203