1 ##===-- sourcewin.py -----------------------------------------*- Python -*-===##
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
7 ##===----------------------------------------------------------------------===##
17 class SourceWin(cui
.TitledWin
):
18 def __init__(self
, driver
, x
, y
, w
, h
):
19 super(SourceWin
, self
).__init
__(x
, y
, w
, h
, "Source")
20 self
.sourceman
= driver
.getSourceManager()
36 from pygments
.formatters
import TerminalFormatter
38 self
.formatter
= TerminalFormatter()
40 # self.win.addstr("\nWarning: no 'pygments' library found. Syntax highlighting is disabled.")
45 # FIXME: syntax highlight broken
49 def handleEvent(self
, event
):
50 if isinstance(event
, int):
54 if isinstance(event
, lldb
.SBEvent
):
55 if lldb
.SBBreakpoint
.EventIsBreakpointEvent(event
):
56 self
.handleBPEvent(event
)
58 if lldb
.SBProcess
.EventIsProcessEvent(
60 ) and not lldb
.SBProcess
.GetRestartedFromEvent(event
):
61 process
= lldb
.SBProcess
.GetProcessFromEvent(event
)
62 if not process
.IsValid():
64 if process
.GetState() == lldb
.eStateStopped
:
65 self
.refreshSource(process
)
66 elif process
.GetState() == lldb
.eStateExited
:
67 self
.notifyExited(process
)
69 def notifyExited(self
, process
):
71 target
= lldbutil
.get_description(process
.GetTarget())
72 pid
= process
.GetProcessID()
73 ec
= process
.GetExitStatus()
75 "\nProcess %s [%d] has exited with exit-code %d" % (target
, pid
, ec
)
80 self
.viewline
= self
.viewline
- 1
84 if self
.viewline
< len(self
.content
) - self
.height
+ 1:
85 self
.viewline
= self
.viewline
+ 1
89 def handleKey(self
, key
):
90 if key
== curses
.KEY_DOWN
:
92 elif key
== curses
.KEY_UP
:
95 def updateViewline(self
):
96 half
= self
.height
/ 2
97 if self
.pc_line
< half
:
100 self
.viewline
= self
.pc_line
- half
+ 1
102 if self
.viewline
< 0:
104 "negative viewline: pc=%d viewline=%d" % (self
.pc_line
, self
.viewline
)
107 def refreshSource(self
, process
=None):
108 (self
.height
, self
.width
) = self
.win
.getmaxyx()
110 if process
is not None:
111 loc
= process
.GetSelectedThread().GetSelectedFrame().GetLineEntry()
112 f
= loc
.GetFileSpec()
113 self
.pc_line
= loc
.GetLine()
116 self
.win
.addstr(0, 0, "Invalid source file")
119 self
.filename
= f
.GetFilename()
120 path
= os
.path
.join(f
.GetDirectory(), self
.filename
)
122 self
.content
= self
.getContent(path
)
123 self
.updateViewline()
125 if self
.filename
is None:
128 if self
.formatter
is not None:
129 from pygments
.lexers
import get_lexer_for_filename
131 self
.lexer
= get_lexer_for_filename(self
.filename
)
135 if not self
.filename
in self
.breakpoints
136 else self
.breakpoints
[self
.filename
]
140 self
.formatContent(self
.content
, self
.pc_line
, bps
)
142 def getContent(self
, path
):
144 if path
in self
.sources
:
145 content
= self
.sources
[path
]
147 if os
.path
.exists(path
):
148 with
open(path
) as x
:
149 content
= x
.readlines()
150 self
.sources
[path
] = content
153 def formatContent(self
, content
, pc_line
, breakpoints
):
157 end
= min(len(content
), self
.viewline
+ self
.height
)
158 for i
in range(self
.viewline
, end
):
160 marker
= self
.markerNone
161 attr
= curses
.A_NORMAL
162 if line_num
== pc_line
:
163 attr
= curses
.A_REVERSE
164 if line_num
in breakpoints
:
165 marker
= self
.markerBP
166 line
= "%s%3d %s" % (marker
, line_num
, self
.highlight(content
[i
]))
167 if len(line
) >= self
.width
:
168 line
= line
[0 : self
.width
- 1] + "\n"
169 self
.win
.addstr(line
, attr
)
174 def highlight(self
, source
):
175 if self
.lexer
and self
.formatter
:
176 from pygments
import highlight
178 return highlight(source
, self
.lexer
, self
.formatter
)
182 def addBPLocations(self
, locations
):
183 for path
in locations
:
184 lines
= locations
[path
]
185 if path
in self
.breakpoints
:
186 self
.breakpoints
[path
].update(lines
)
188 self
.breakpoints
[path
] = lines
190 def removeBPLocations(self
, locations
):
191 for path
in locations
:
192 lines
= locations
[path
]
193 if path
in self
.breakpoints
:
194 self
.breakpoints
[path
].difference_update(lines
)
196 raise "Removing locations that were never added...no good"
198 def handleBPEvent(self
, event
):
199 def getLocations(event
):
202 bp
= lldb
.SBBreakpoint
.GetBreakpointFromEvent(event
)
205 # don't show anything for internal breakpoints
209 # hack! getting the LineEntry via SBBreakpointLocation.GetAddress.GetLineEntry does not work good for
210 # inlined frames, so we get the description (which does take
211 # into account inlined functions) and parse it.
212 desc
= lldbutil
.get_description(location
, lldb
.eDescriptionLevelFull
)
213 match
= re
.search("at\ ([^:]+):([\d]+)", desc
)
215 path
= match
.group(1)
216 line
= int(match
.group(2).strip())
217 except ValueError as e
:
224 locs
[path
] = set([line
])
227 event_type
= lldb
.SBBreakpoint
.GetBreakpointEventTypeFromEvent(event
)
229 event_type
== lldb
.eBreakpointEventTypeEnabled
230 or event_type
== lldb
.eBreakpointEventTypeAdded
231 or event_type
== lldb
.eBreakpointEventTypeLocationsResolved
232 or event_type
== lldb
.eBreakpointEventTypeLocationsAdded
234 self
.addBPLocations(getLocations(event
))
236 event_type
== lldb
.eBreakpointEventTypeRemoved
237 or event_type
== lldb
.eBreakpointEventTypeLocationsRemoved
238 or event_type
== lldb
.eBreakpointEventTypeDisabled
240 self
.removeBPLocations(getLocations(event
))
242 event_type
== lldb
.eBreakpointEventTypeCommandChanged
243 or event_type
== lldb
.eBreakpointEventTypeConditionChanged
244 or event_type
== lldb
.eBreakpointEventTypeIgnoreChanged
245 or event_type
== lldb
.eBreakpointEventTypeThreadChanged
246 or event_type
== lldb
.eBreakpointEventTypeInvalidType