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 __future__ import absolute_import 17from __future__ import division 18from __future__ import print_function 19 20from tensorflow.python.debug.cli import debugger_cli_common 21 22 23RL = debugger_cli_common.RichLine 24 25 26class 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 42class 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