xref: /aosp_15_r20/external/tensorflow/tensorflow/python/debug/cli/curses_widgets.py (revision b6fb3261f9314811a0f4371741dbb8839866f948)
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