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.""" 16from tensorflow.python.debug.cli import debugger_cli_common 17 18 19RL = debugger_cli_common.RichLine 20 21 22class NavigationHistoryItem(object): 23 """Individual item in navigation history.""" 24 25 def __init__(self, command, screen_output, scroll_position): 26 """Constructor of NavigationHistoryItem. 27 28 Args: 29 command: (`str`) the command line text. 30 screen_output: the screen output of the command. 31 scroll_position: (`int`) scroll position in the screen output. 32 """ 33 self.command = command 34 self.screen_output = screen_output 35 self.scroll_position = scroll_position 36 37 38class CursesNavigationHistory(object): 39 """Navigation history containing commands, outputs and scroll info.""" 40 41 BACK_ARROW_TEXT = "<--" 42 FORWARD_ARROW_TEXT = "-->" 43 44 def __init__(self, capacity): 45 """Constructor of CursesNavigationHistory. 46 47 Args: 48 capacity: (`int`) How many items this object can hold. Each item consists 49 of a command stirng, an output RichTextLines object and a scroll 50 position. 51 52 Raises: 53 ValueError: If capacity is not a positive number. 54 """ 55 if capacity <= 0: 56 raise ValueError("In valid capacity value: %d" % capacity) 57 58 self._capacity = capacity 59 self._items = [] 60 self._pointer = -1 61 62 def add_item(self, command, screen_output, scroll_position): 63 """Add an item to the navigation histoyr. 64 65 Args: 66 command: command line text. 67 screen_output: screen output produced for the command. 68 scroll_position: (`int`) scroll position in the screen output. 69 """ 70 if self._pointer + 1 < len(self._items): 71 self._items = self._items[:self._pointer + 1] 72 self._items.append( 73 NavigationHistoryItem(command, screen_output, scroll_position)) 74 if len(self._items) > self._capacity: 75 self._items = self._items[-self._capacity:] 76 self._pointer = len(self._items) - 1 77 78 def update_scroll_position(self, new_scroll_position): 79 """Update the scroll position of the currently-pointed-to history item. 80 81 Args: 82 new_scroll_position: (`int`) new scroll-position value. 83 84 Raises: 85 ValueError: If the history is empty. 86 """ 87 if not self._items: 88 raise ValueError("Empty navigation history") 89 self._items[self._pointer].scroll_position = new_scroll_position 90 91 def size(self): 92 return len(self._items) 93 94 def pointer(self): 95 return self._pointer 96 97 def go_back(self): 98 """Go back one place in the history, if possible. 99 100 Decrease the pointer value by 1, if possible. Otherwise, the pointer value 101 will be unchanged. 102 103 Returns: 104 The updated pointer value. 105 106 Raises: 107 ValueError: If history is empty. 108 """ 109 if not self._items: 110 raise ValueError("Empty navigation history") 111 112 if self.can_go_back(): 113 self._pointer -= 1 114 return self._items[self._pointer] 115 116 def go_forward(self): 117 """Go forward one place in the history, if possible. 118 119 Increase the pointer value by 1, if possible. Otherwise, the pointer value 120 will be unchanged. 121 122 Returns: 123 The updated pointer value. 124 125 Raises: 126 ValueError: If history is empty. 127 """ 128 if not self._items: 129 raise ValueError("Empty navigation history") 130 131 if self.can_go_forward(): 132 self._pointer += 1 133 return self._items[self._pointer] 134 135 def can_go_back(self): 136 """Test whether client can go back one place. 137 138 Returns: 139 (`bool`) Whether going back one place is possible. 140 """ 141 return self._pointer >= 1 142 143 def can_go_forward(self): 144 """Test whether client can go forward one place. 145 146 Returns: 147 (`bool`) Whether going back one place is possible. 148 """ 149 return self._pointer + 1 < len(self._items) 150 151 def render(self, 152 max_length, 153 backward_command, 154 forward_command, 155 latest_command_attribute="black_on_white", 156 old_command_attribute="magenta_on_white"): 157 """Render the rich text content of the single-line navigation bar. 158 159 Args: 160 max_length: (`int`) Maximum length of the navigation bar, in characters. 161 backward_command: (`str`) command for going backward. Used to construct 162 the shortcut menu item. 163 forward_command: (`str`) command for going forward. Used to construct the 164 shortcut menu item. 165 latest_command_attribute: font attribute for lastest command. 166 old_command_attribute: font attribute for old (non-latest) command. 167 168 Returns: 169 (`debugger_cli_common.RichTextLines`) the navigation bar text with 170 attributes. 171 172 """ 173 output = RL("| ") 174 output += RL( 175 self.BACK_ARROW_TEXT, 176 (debugger_cli_common.MenuItem(None, backward_command) 177 if self.can_go_back() else None)) 178 output += RL(" ") 179 output += RL( 180 self.FORWARD_ARROW_TEXT, 181 (debugger_cli_common.MenuItem(None, forward_command) 182 if self.can_go_forward() else None)) 183 184 if self._items: 185 command_attribute = (latest_command_attribute 186 if (self._pointer == (len(self._items) - 1)) 187 else old_command_attribute) 188 output += RL(" | ") 189 if self._pointer != len(self._items) - 1: 190 output += RL("(-%d) " % (len(self._items) - 1 - self._pointer), 191 command_attribute) 192 193 if len(output) < max_length: 194 maybe_truncated_command = self._items[self._pointer].command[ 195 :(max_length - len(output))] 196 output += RL(maybe_truncated_command, command_attribute) 197 198 return debugger_cli_common.rich_text_lines_from_rich_line_list([output]) 199