xref: /freebsd/contrib/llvm-project/lldb/source/Core/Statusline.cpp (revision 700637cbb5e582861067a11aaca4d053546871d2)
1 //===-- Statusline.cpp ---------------------------------------------------===//
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 //===----------------------------------------------------------------------===//
8 
9 #include "lldb/Core/Statusline.h"
10 #include "lldb/Core/Debugger.h"
11 #include "lldb/Core/FormatEntity.h"
12 #include "lldb/Host/StreamFile.h"
13 #include "lldb/Interpreter/CommandInterpreter.h"
14 #include "lldb/Symbol/SymbolContext.h"
15 #include "lldb/Target/Process.h"
16 #include "lldb/Target/StackFrame.h"
17 #include "lldb/Utility/AnsiTerminal.h"
18 #include "lldb/Utility/StreamString.h"
19 #include "llvm/ADT/StringRef.h"
20 #include "llvm/Support/Locale.h"
21 
22 #define ESCAPE "\x1b"
23 #define ANSI_NORMAL ESCAPE "[0m"
24 #define ANSI_SAVE_CURSOR ESCAPE "7"
25 #define ANSI_RESTORE_CURSOR ESCAPE "8"
26 #define ANSI_CLEAR_BELOW ESCAPE "[J"
27 #define ANSI_CLEAR_SCREEN ESCAPE "[2J"
28 #define ANSI_SET_SCROLL_ROWS ESCAPE "[1;%ur"
29 #define ANSI_TO_START_OF_ROW ESCAPE "[%u;1f"
30 #define ANSI_REVERSE_VIDEO ESCAPE "[7m"
31 #define ANSI_UP_ROWS ESCAPE "[%dA"
32 
33 using namespace lldb;
34 using namespace lldb_private;
35 
Statusline(Debugger & debugger)36 Statusline::Statusline(Debugger &debugger)
37     : m_debugger(debugger), m_terminal_width(m_debugger.GetTerminalWidth()),
38       m_terminal_height(m_debugger.GetTerminalHeight()) {
39   Enable();
40 }
41 
~Statusline()42 Statusline::~Statusline() { Disable(); }
43 
TerminalSizeChanged()44 void Statusline::TerminalSizeChanged() {
45   m_terminal_width = m_debugger.GetTerminalWidth();
46   m_terminal_height = m_debugger.GetTerminalHeight();
47 
48   UpdateScrollWindow(ResizeStatusline);
49 
50   // Draw the old statusline.
51   Redraw(/*update=*/false);
52 }
53 
Enable()54 void Statusline::Enable() {
55   // Reduce the scroll window to make space for the status bar below.
56   UpdateScrollWindow(EnableStatusline);
57 
58   // Draw the statusline.
59   Redraw(/*update=*/true);
60 }
61 
Disable()62 void Statusline::Disable() {
63   // Extend the scroll window to cover the status bar.
64   UpdateScrollWindow(DisableStatusline);
65 }
66 
Draw(std::string str)67 void Statusline::Draw(std::string str) {
68   lldb::LockableStreamFileSP stream_sp = m_debugger.GetOutputStreamSP();
69   if (!stream_sp)
70     return;
71 
72   m_last_str = str;
73 
74   str = ansi::TrimAndPad(str, m_terminal_width);
75 
76   LockedStreamFile locked_stream = stream_sp->Lock();
77   locked_stream << ANSI_SAVE_CURSOR;
78   locked_stream.Printf(ANSI_TO_START_OF_ROW,
79                        static_cast<unsigned>(m_terminal_height));
80 
81   // Use "reverse video" to make sure the statusline has a background. Only do
82   // this when colors are disabled, and rely on the statusline format otherwise.
83   if (!m_debugger.GetUseColor())
84     locked_stream << ANSI_REVERSE_VIDEO;
85 
86   locked_stream << str;
87   locked_stream << ANSI_NORMAL;
88   locked_stream << ANSI_RESTORE_CURSOR;
89 }
90 
UpdateScrollWindow(ScrollWindowMode mode)91 void Statusline::UpdateScrollWindow(ScrollWindowMode mode) {
92   assert(m_terminal_width != 0 && m_terminal_height != 0);
93 
94   lldb::LockableStreamFileSP stream_sp = m_debugger.GetOutputStreamSP();
95   if (!stream_sp)
96     return;
97 
98   const unsigned reduced_scroll_window = m_terminal_height - 1;
99   LockedStreamFile locked_stream = stream_sp->Lock();
100 
101   switch (mode) {
102   case EnableStatusline:
103     // Move everything on the screen up.
104     locked_stream << '\n';
105     locked_stream.Printf(ANSI_UP_ROWS, 1);
106     // Reduce the scroll window.
107     locked_stream << ANSI_SAVE_CURSOR;
108     locked_stream.Printf(ANSI_SET_SCROLL_ROWS, reduced_scroll_window);
109     locked_stream << ANSI_RESTORE_CURSOR;
110     break;
111   case DisableStatusline:
112     // Reset the scroll window.
113     locked_stream << ANSI_SAVE_CURSOR;
114     locked_stream.Printf(ANSI_SET_SCROLL_ROWS, 0);
115     locked_stream << ANSI_RESTORE_CURSOR;
116     // Clear the screen below to hide the old statusline.
117     locked_stream << ANSI_CLEAR_BELOW;
118     break;
119   case ResizeStatusline:
120     // Clear the screen and update the scroll window.
121     // FIXME: Find a better solution (#146919).
122     locked_stream << ANSI_CLEAR_SCREEN;
123     locked_stream.Printf(ANSI_SET_SCROLL_ROWS, reduced_scroll_window);
124     break;
125   }
126 
127   m_debugger.RefreshIOHandler();
128 }
129 
Redraw(bool update)130 void Statusline::Redraw(bool update) {
131   if (!update) {
132     Draw(m_last_str);
133     return;
134   }
135 
136   ExecutionContext exe_ctx = m_debugger.GetSelectedExecutionContext();
137 
138   // For colors and progress events, the format entity needs access to the
139   // debugger, which requires a target in the execution context.
140   if (!exe_ctx.HasTargetScope())
141     exe_ctx.SetTargetPtr(&m_debugger.GetSelectedOrDummyTarget());
142 
143   SymbolContext symbol_ctx;
144   if (ProcessSP process_sp = exe_ctx.GetProcessSP()) {
145     // Check if the process is stopped, and if it is, make sure it remains
146     // stopped until we've computed the symbol context.
147     Process::StopLocker stop_locker;
148     if (stop_locker.TryLock(&process_sp->GetRunLock())) {
149       if (auto frame_sp = exe_ctx.GetFrameSP())
150         symbol_ctx = frame_sp->GetSymbolContext(eSymbolContextEverything);
151     }
152   }
153 
154   StreamString stream;
155   FormatEntity::Entry format = m_debugger.GetStatuslineFormat();
156   FormatEntity::Format(format, stream, &symbol_ctx, &exe_ctx, nullptr, nullptr,
157                        false, false);
158 
159   Draw(stream.GetString().str());
160 }
161