[clang][Interp] Implement IntegralAP::truncate() (#69912)
[llvm-project.git] / lldb / utils / lui / cui.py
blob06ffc4feadd4590bb581d307eaa38f9d2c2f49c8
1 ##===-- cui.py -----------------------------------------------*- Python -*-===##
2 ##
3 # Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 # See https://llvm.org/LICENSE.txt for license information.
5 # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 ##
7 ##===----------------------------------------------------------------------===##
9 import curses
10 import curses.ascii
11 import threading
14 class CursesWin(object):
15 def __init__(self, x, y, w, h):
16 self.win = curses.newwin(h, w, y, x)
17 self.focus = False
19 def setFocus(self, focus):
20 self.focus = focus
22 def getFocus(self):
23 return self.focus
25 def canFocus(self):
26 return True
28 def handleEvent(self, event):
29 return
31 def draw(self):
32 return
35 class TextWin(CursesWin):
36 def __init__(self, x, y, w):
37 super(TextWin, self).__init__(x, y, w, 1)
38 self.win.bkgd(curses.color_pair(1))
39 self.text = ""
40 self.reverse = False
42 def canFocus(self):
43 return False
45 def draw(self):
46 w = self.win.getmaxyx()[1]
47 text = self.text
48 if len(text) > w:
49 # trunc_length = len(text) - w
50 text = text[-w + 1 :]
51 if self.reverse:
52 self.win.addstr(0, 0, text, curses.A_REVERSE)
53 else:
54 self.win.addstr(0, 0, text)
55 self.win.noutrefresh()
57 def setReverse(self, reverse):
58 self.reverse = reverse
60 def setText(self, text):
61 self.text = text
64 class TitledWin(CursesWin):
65 def __init__(self, x, y, w, h, title):
66 super(TitledWin, self).__init__(x, y + 1, w, h - 1)
67 self.title = title
68 self.title_win = TextWin(x, y, w)
69 self.title_win.setText(title)
70 self.draw()
72 def setTitle(self, title):
73 self.title_win.setText(title)
75 def draw(self):
76 self.title_win.setReverse(self.getFocus())
77 self.title_win.draw()
78 self.win.noutrefresh()
81 class ListWin(CursesWin):
82 def __init__(self, x, y, w, h):
83 super(ListWin, self).__init__(x, y, w, h)
84 self.items = []
85 self.selected = 0
86 self.first_drawn = 0
87 self.win.leaveok(True)
89 def draw(self):
90 if len(self.items) == 0:
91 self.win.erase()
92 return
94 h, w = self.win.getmaxyx()
96 allLines = []
97 firstSelected = -1
98 lastSelected = -1
99 for i, item in enumerate(self.items):
100 lines = self.items[i].split("\n")
101 lines = lines if lines[len(lines) - 1] != "" else lines[:-1]
102 if len(lines) == 0:
103 lines = [""]
105 if i == self.getSelected():
106 firstSelected = len(allLines)
107 allLines.extend(lines)
108 if i == self.selected:
109 lastSelected = len(allLines) - 1
111 if firstSelected < self.first_drawn:
112 self.first_drawn = firstSelected
113 elif lastSelected >= self.first_drawn + h:
114 self.first_drawn = lastSelected - h + 1
116 self.win.erase()
118 begin = self.first_drawn
119 end = begin + h
121 y = 0
122 for i, line in list(enumerate(allLines))[begin:end]:
123 attr = curses.A_NORMAL
124 if i >= firstSelected and i <= lastSelected:
125 attr = curses.A_REVERSE
126 line = "{0:{width}}".format(line, width=w - 1)
128 # Ignore the error we get from drawing over the bottom-right char.
129 try:
130 self.win.addstr(y, 0, line[:w], attr)
131 except curses.error:
132 pass
133 y += 1
134 self.win.noutrefresh()
136 def getSelected(self):
137 if self.items:
138 return self.selected
139 return -1
141 def setSelected(self, selected):
142 self.selected = selected
143 if self.selected < 0:
144 self.selected = 0
145 elif self.selected >= len(self.items):
146 self.selected = len(self.items) - 1
148 def handleEvent(self, event):
149 if isinstance(event, int):
150 if len(self.items) > 0:
151 if event == curses.KEY_UP:
152 self.setSelected(self.selected - 1)
153 if event == curses.KEY_DOWN:
154 self.setSelected(self.selected + 1)
155 if event == curses.ascii.NL:
156 self.handleSelect(self.selected)
158 def addItem(self, item):
159 self.items.append(item)
161 def clearItems(self):
162 self.items = []
164 def handleSelect(self, index):
165 return
168 class InputHandler(threading.Thread):
169 def __init__(self, screen, queue):
170 super(InputHandler, self).__init__()
171 self.screen = screen
172 self.queue = queue
174 def run(self):
175 while True:
176 c = self.screen.getch()
177 self.queue.put(c)
180 class CursesUI(object):
181 """Responsible for updating the console UI with curses."""
183 def __init__(self, screen, event_queue):
184 self.screen = screen
185 self.event_queue = event_queue
187 curses.start_color()
188 curses.init_pair(1, curses.COLOR_WHITE, curses.COLOR_BLUE)
189 curses.init_pair(2, curses.COLOR_YELLOW, curses.COLOR_BLACK)
190 curses.init_pair(3, curses.COLOR_RED, curses.COLOR_BLACK)
191 self.screen.bkgd(curses.color_pair(1))
192 self.screen.clear()
194 self.input_handler = InputHandler(self.screen, self.event_queue)
195 self.input_handler.daemon = True
197 self.focus = 0
199 self.screen.refresh()
201 def focusNext(self):
202 self.wins[self.focus].setFocus(False)
203 old = self.focus
204 while True:
205 self.focus += 1
206 if self.focus >= len(self.wins):
207 self.focus = 0
208 if self.wins[self.focus].canFocus():
209 break
210 self.wins[self.focus].setFocus(True)
212 def handleEvent(self, event):
213 if isinstance(event, int):
214 if event == curses.KEY_F3:
215 self.focusNext()
217 def eventLoop(self):
218 self.input_handler.start()
219 self.wins[self.focus].setFocus(True)
221 while True:
222 self.screen.noutrefresh()
224 for i, win in enumerate(self.wins):
225 if i != self.focus:
226 win.draw()
227 # Draw the focused window last so that the cursor shows up.
228 if self.wins:
229 self.wins[self.focus].draw()
230 curses.doupdate() # redraw the physical screen
232 event = self.event_queue.get()
234 for win in self.wins:
235 if isinstance(event, int):
236 if win.getFocus() or not win.canFocus():
237 win.handleEvent(event)
238 else:
239 win.handleEvent(event)
240 self.handleEvent(event)
243 class CursesEditLine(object):
244 """Embed an 'editline'-compatible prompt inside a CursesWin."""
246 def __init__(self, win, history, enterCallback, tabCompleteCallback):
247 self.win = win
248 self.history = history
249 self.enterCallback = enterCallback
250 self.tabCompleteCallback = tabCompleteCallback
252 self.prompt = ""
253 self.content = ""
254 self.index = 0
255 self.startx = -1
256 self.starty = -1
258 def draw(self, prompt=None):
259 if not prompt:
260 prompt = self.prompt
261 (h, w) = self.win.getmaxyx()
262 if (len(prompt) + len(self.content)) / w + self.starty >= h - 1:
263 self.win.scroll(1)
264 self.starty -= 1
265 if self.starty < 0:
266 raise RuntimeError("Input too long; aborting")
267 (y, x) = (self.starty, self.startx)
269 self.win.move(y, x)
270 self.win.clrtobot()
271 self.win.addstr(y, x, prompt)
272 remain = self.content
273 self.win.addstr(remain[: w - len(prompt)])
274 remain = remain[w - len(prompt) :]
275 while remain != "":
276 y += 1
277 self.win.addstr(y, 0, remain[:w])
278 remain = remain[w:]
280 length = self.index + len(prompt)
281 self.win.move(self.starty + length / w, length % w)
283 def showPrompt(self, y, x, prompt=None):
284 self.content = ""
285 self.index = 0
286 self.startx = x
287 self.starty = y
288 self.draw(prompt)
290 def handleEvent(self, event):
291 if not isinstance(event, int):
292 return # not handled
293 key = event
295 if self.startx == -1:
296 raise RuntimeError("Trying to handle input without prompt")
298 if key == curses.ascii.NL:
299 self.enterCallback(self.content)
300 elif key == curses.ascii.TAB:
301 self.tabCompleteCallback(self.content)
302 elif curses.ascii.isprint(key):
303 self.content = (
304 self.content[: self.index] + chr(key) + self.content[self.index :]
306 self.index += 1
307 elif key == curses.KEY_BACKSPACE or key == curses.ascii.BS:
308 if self.index > 0:
309 self.index -= 1
310 self.content = (
311 self.content[: self.index] + self.content[self.index + 1 :]
313 elif key == curses.KEY_DC or key == curses.ascii.DEL or key == curses.ascii.EOT:
314 self.content = self.content[: self.index] + self.content[self.index + 1 :]
315 elif key == curses.ascii.VT: # CTRL-K
316 self.content = self.content[: self.index]
317 elif key == curses.KEY_LEFT or key == curses.ascii.STX: # left or CTRL-B
318 if self.index > 0:
319 self.index -= 1
320 elif key == curses.KEY_RIGHT or key == curses.ascii.ACK: # right or CTRL-F
321 if self.index < len(self.content):
322 self.index += 1
323 elif key == curses.ascii.SOH: # CTRL-A
324 self.index = 0
325 elif key == curses.ascii.ENQ: # CTRL-E
326 self.index = len(self.content)
327 elif key == curses.KEY_UP or key == curses.ascii.DLE: # up or CTRL-P
328 self.content = self.history.previous(self.content)
329 self.index = len(self.content)
330 elif key == curses.KEY_DOWN or key == curses.ascii.SO: # down or CTRL-N
331 self.content = self.history.next()
332 self.index = len(self.content)
333 self.draw()