def _ShowBreakpoints( self ): for file_name, line_breakpoints in self._line_breakpoints.items(): for bp in line_breakpoints: self._SignToLine( file_name, bp ) if 'sign_id' in bp: signs.UnplaceSign( bp[ 'sign_id' ], 'VimspectorBP' ) else: bp[ 'sign_id' ] = self._next_sign_id self._next_sign_id += 1 line = bp[ 'line' ] if 'server_bp' in bp: server_bp = bp[ 'server_bp' ] line = server_bp.get( 'line', line ) verified = server_bp[ 'verified' ] else: verified = self._connection is None sign = ( 'vimspectorBPDisabled' if bp[ 'state' ] != 'ENABLED' or not verified else 'vimspectorBPLog' if 'logMessage' in bp[ 'options' ] else 'vimspectorBPCond' if 'condition' in bp[ 'options' ] or 'hitCondition' in bp[ 'options' ] else 'vimspectorBP' ) if utils.BufferExists( file_name ): signs.PlaceSign( bp[ 'sign_id' ], 'VimspectorBP', sign, file_name, line )
def _DrawThreads( self ): self._line_to_frame.clear() self._line_to_thread.clear() if self._current_thread_sign_id: signs.UnplaceSign( self._current_thread_sign_id, 'VimspectorStackTrace' ) else: self._current_thread_sign_id = 1 with utils.ModifiableScratchBuffer( self._buf ): with utils.RestoreCursorPosition(): utils.ClearBuffer( self._buf ) for thread in self._threads: icon = '+' if not thread.IsExpanded() else '-' line = utils.AppendToBuffer( self._buf, f'{icon} Thread {thread.id}: {thread.thread["name"]} ' f'({thread.State()})' ) if self._current_thread == thread.id: # TODO - Scroll the window such that this line is visible (e.g. at # the top) signs.PlaceSign( self._current_thread_sign_id, 'VimspectorStackTrace', 'vimspectorCurrentThread', self._buf.name, line ) self._line_to_thread[ line ] = thread self._DrawStackTrace( thread )
def Clear(self): if self._signs['vimspectorPC']: signs.UnplaceSign(self._signs['vimspectorPC'], 'VimspectorCode') self._signs['vimspectorPC'] = None self._UndisplayPC() self.current_syntax = None
def Clear( self ): self._current_frame = None self._current_thread = None self._current_syntax = "" self._threads.clear() self._sources = {} self._requesting_threads = StackTraceView.ThreadRequestState.NO self._pending_thread_request = None if self._current_thread_sign_id: signs.UnplaceSign( self._current_thread_sign_id, 'VimspectorStackTrace' ) self._current_thread_sign_id = 0 if self._current_frame_sign_id: signs.UnplaceSign( self._current_frame_sign_id, 'VimspectorStackTrace' ) self._current_frame_sign_id = 0 with utils.ModifiableScratchBuffer( self._buf ): utils.ClearBuffer( self._buf )
def SetCurrentFrame( self, frame ): """Returns True if the code window was updated with the frame, False otherwise. False means either the frame is junk, we couldn't find the file (or don't have the data) or the code window no longer exits.""" if self._signs[ 'vimspectorPC' ]: signs.UnplaceSign( self._signs[ 'vimspectorPC' ], 'VimspectorCode' ) self._signs[ 'vimspectorPC' ] = None if not frame or not frame.get( 'source' ): return False if 'path' not in frame[ 'source' ]: return False self._signs[ 'vimspectorPC' ] = self._next_sign_id self._next_sign_id += 1 try: signs.PlaceSign( self._signs[ 'vimspectorPC' ], 'VimspectorCode', 'vimspectorPC', frame[ 'source' ][ 'path' ], frame[ 'line' ] ) except vim.error as e: # Ignore 'invalid buffer name' if 'E158' not in str( e ): raise if not self._window.valid: return False utils.JumpToWindow( self._window ) try: utils.OpenFileInCurrentWindow( frame[ 'source' ][ 'path' ] ) except vim.error: self._logger.exception( 'Unexpected vim error opening file {}'.format( frame[ 'source' ][ 'path' ] ) ) return False # SIC: column is 0-based, line is 1-based in vim. Why? Nobody knows. # Note: max() with 0 because some debug adapters (go) return 0 for the # column. try: self._window.cursor = ( frame[ 'line' ], max( frame[ 'column' ] - 1, 0 ) ) except vim.error: self._logger.exception( "Unable to jump to %s:%s in %s, maybe the file " "doesn't exist", frame[ 'line' ], frame[ 'column' ], frame[ 'source' ][ 'path' ] ) return False self.current_syntax = utils.ToUnicode( vim.current.buffer.options[ 'syntax' ] ) return True
def ClearBreakpoints( self ): # These are the user-entered breakpoints. for file_name, breakpoints in self._line_breakpoints.items(): for bp in breakpoints: self._SignToLine( file_name, bp ) if 'sign_id' in bp: signs.UnplaceSign( bp[ 'sign_id' ], 'VimspectorBP' ) self._line_breakpoints = defaultdict( list ) self._func_breakpoints = [] self._exception_breakpoints = None self.UpdateUI()
def _ClearServerBreakpointData( self ): for _, breakpoints in self._line_breakpoints.items(): for bp in breakpoints: if 'server_bp' in bp: # Unplace the sign. If the sign was moved by the server, then we don't # want a subsequent call to _SignToLine to override the user's # breakpoint location with the server one. This is not what users # typicaly expect, and we may (soon) call something that eagerly calls # _SignToLine, such as _ShowBreakpoints, if 'sign_id' in bp: signs.UnplaceSign( bp[ 'sign_id' ], 'VimspectorBP' ) del bp[ 'sign_id' ] del bp[ 'server_bp' ]
def _ShowBreakpoints(self): for file_name, line_breakpoints in self._line_breakpoints.items(): for bp in line_breakpoints: self._SignToLine(file_name, bp) if 'sign_id' in bp: signs.UnplaceSign(bp['sign_id'], 'VimspectorBP') else: bp['sign_id'] = self._next_sign_id self._next_sign_id += 1 sign = ('vimspectorBPDisabled' if bp['state'] != 'ENABLED' else 'vimspectorBPCond' if 'condition' in bp['options'] else 'vimspectorBP') signs.PlaceSign(bp['sign_id'], 'VimspectorBP', sign, file_name, bp['line'])
def _DrawStackTrace( self, thread: Thread ): if not thread.IsExpanded(): return if self._current_frame_sign_id: signs.UnplaceSign( self._current_frame_sign_id, 'VimspectorStackTrace' ) else: self._current_frame_sign_id = 2 for frame in thread.stacktrace: if frame.get( 'source' ): source = frame[ 'source' ] else: source = { 'name': '<unknown>' } if 'name' not in source: source[ 'name' ] = os.path.basename( source.get( 'path', 'unknwon' ) ) if frame.get( 'presentationHint' ) == 'label': # Sigh. FOr some reason, it's OK for debug adapters to completely ignore # the protocol; it seems that the chrome adapter sets 'label' and # doesn't set 'line' line = utils.AppendToBuffer( self._buf, ' {0}: {1}'.format( frame[ 'id' ], frame[ 'name' ] ) ) else: line = utils.AppendToBuffer( self._buf, ' {0}: {1}@{2}:{3}'.format( frame[ 'id' ], frame[ 'name' ], source[ 'name' ], frame[ 'line' ] ) ) if ( self._current_frame is not None and self._current_frame[ 'id' ] == frame[ 'id' ] ): signs.PlaceSign( self._current_frame_sign_id, 'VimspectorStackTrace', 'vimspectorCurrentFrame', self._buf.name, line ) self._line_to_frame[ line ] = ( thread, frame )
def ToggleBreakpoint(self, options): line, column = vim.current.window.cursor file_name = vim.current.buffer.name if not file_name: return found_bp = False action = 'New' for index, bp in enumerate(self._line_breakpoints[file_name]): self._SignToLine(file_name, bp) if bp['line'] == line: found_bp = True if bp['state'] == 'ENABLED' and not self._connection: bp['state'] = 'DISABLED' action = 'Disable' else: if 'sign_id' in bp: signs.UnplaceSign(bp['sign_id'], 'VimspectorBP') del self._line_breakpoints[file_name][index] action = 'Delete' break self._logger.debug("Toggle found bp at {}:{} ? {} ({})".format( file_name, line, found_bp, action)) if not found_bp: self._line_breakpoints[file_name].append({ 'state': 'ENABLED', 'line': line, 'options': options, # 'sign_id': <filled in when placed>, # # Used by other breakpoint types (specified in options): # 'condition': ..., # 'hitCondition': ..., # 'logMessage': ... }) self.UpdateUI()
def _UndisplayPC( self, clear_pc = True ): if clear_pc: self._current_frame = None if self._signs[ 'vimspectorPC' ]: signs.UnplaceSign( self._signs[ 'vimspectorPC' ], 'VimspectorCode' ) self._signs[ 'vimspectorPC' ] = None
def _UndisplaySigns( self ): for sign_id in self._signs[ 'breakpoints' ]: signs.UnplaceSign( sign_id, 'VimspectorCode' ) self._signs[ 'breakpoints' ] = []
def SendBreakpoints( self, doneHandler = None ): if self._awaiting_bp_responses > 0: self._pending_send_breakpoints = ( doneHandler, ) return self._awaiting_bp_responses = 0 def response_received( *failure_args ): self._awaiting_bp_responses -= 1 if failure_args and self._connection: reason, msg = failure_args utils.UserMessage( 'Unable to set breakpoint: {0}'.format( reason ), persist = True, error = True ) if self._awaiting_bp_responses > 0: return if doneHandler: doneHandler() if bool( self._pending_send_breakpoints ): args = self._pending_send_breakpoints self._pending_send_breakpoints = None self.SendBreakpoints( *args ) def response_handler( msg, bp_idxs = [] ): server_bps = ( msg.get( 'body' ) or {} ).get( 'breakpoints' ) or [] self._UpdateServerBreakpoints( server_bps, bp_idxs ) response_received() # NOTE: Must do this _first_ otherwise we might send requests and get # replies before we finished sending all the requests. if self._exception_breakpoints is None: self._SetUpExceptionBreakpoints( self._configured_breakpoints ) # TODO: add the _configured_breakpoints to line_breakpoints for file_name, line_breakpoints in self._line_breakpoints.items(): bp_idxs = [] breakpoints = [] for bp in line_breakpoints: bp.pop( 'server_bp', None ) self._SignToLine( file_name, bp ) if 'sign_id' in bp: signs.UnplaceSign( bp[ 'sign_id' ], 'VimspectorBP' ) if bp[ 'state' ] != 'ENABLED': continue dap_bp = {} dap_bp.update( bp[ 'options' ] ) dap_bp.update( { 'line': bp[ 'line' ] } ) dap_bp.pop( 'temporary', None ) bp_idxs.append( [ len( breakpoints ), bp ] ) breakpoints.append( dap_bp ) source = { 'name': os.path.basename( file_name ), 'path': file_name, } self._awaiting_bp_responses += 1 self._connection.DoRequest( # The source=source here is critical to ensure that we capture each # source in the iteration, rather than ending up passing the same source # to each callback. lambda msg, bp_idxs=bp_idxs: response_handler( msg, bp_idxs ), { 'command': 'setBreakpoints', 'arguments': { 'source': source, 'breakpoints': breakpoints, 'sourceModified': False, # TODO: We can actually check this }, }, failure_handler = response_received ) # TODO: Add the _configured_breakpoints to function breakpoints if self._server_capabilities.get( 'supportsFunctionBreakpoints' ): self._awaiting_bp_responses += 1 breakpoints = [] for bp in self._func_breakpoints: bp.pop( 'server_bp', None ) if bp[ 'state' ] != 'ENABLED': continue dap_bp = {} dap_bp.update( bp[ 'options' ] ) dap_bp.update( { 'name': bp[ 'function' ] } ) breakpoints.append( dap_bp ) # FIXME(Ben): The function breakpoints response actually returns # 'Breakpoint' objects. The point is that there is a server_bp for each # function breakpoint as well as every line breakpoint. We need to # implement that: # - pass the indices in here # - make _FindPostedBreakpoint also search function breakpionts # - make sure that ConnectionClosed also cleares the server_bp data for # function breakpionts # - make sure that we have tests for this, because i'm sure we don't! self._connection.DoRequest( lambda msg: response_handler( msg ), { 'command': 'setFunctionBreakpoints', 'arguments': { 'breakpoints': breakpoints, } }, failure_handler = response_received ) if self._exception_breakpoints: self._awaiting_bp_responses += 1 self._connection.DoRequest( lambda msg: response_received(), { 'command': 'setExceptionBreakpoints', 'arguments': self._exception_breakpoints }, failure_handler = response_received ) if self._awaiting_bp_responses == 0 and doneHandler: doneHandler()
def _DeleteLineBreakpoint( self, bp, file_name, index ): if 'sign_id' in bp: signs.UnplaceSign( bp[ 'sign_id' ], 'VimspectorBP' ) del self._line_breakpoints[ utils.NormalizePath( file_name ) ][ index ]
def SendBreakpoints(self, doneHandler=None): assert self._breakpoints_handler is not None # Clear any existing breakpoints prior to sending new ones self._breakpoints_handler.ClearBreakpoints() awaiting = 0 def response_received(): nonlocal awaiting awaiting = awaiting - 1 if awaiting == 0 and doneHandler: doneHandler() def response_handler(source, msg): if msg: self._breakpoints_handler.AddBreakpoints(source, msg) response_received() # NOTE: Must do this _first_ otherwise we might send requests and get # replies before we finished sending all the requests. if self._exception_breakpoints is None: self._SetUpExceptionBreakpoints(self._configured_breakpoints) # TODO: add the _configured_breakpoints to line_breakpoints # TODO: the line numbers might have changed since pressing the F9 key! for file_name, line_breakpoints in self._line_breakpoints.items(): breakpoints = [] for bp in line_breakpoints: self._SignToLine(file_name, bp) if 'sign_id' in bp: signs.UnplaceSign(bp['sign_id'], 'VimspectorBP') del bp['sign_id'] if bp['state'] != 'ENABLED': continue dap_bp = {} dap_bp.update(bp['options']) dap_bp.update({'line': bp['line']}) breakpoints.append(dap_bp) source = { 'name': os.path.basename(file_name), 'path': file_name, } awaiting = awaiting + 1 self._connection.DoRequest( # The source=source here is critical to ensure that we capture each # source in the iteration, rather than ending up passing the same source # to each callback. lambda msg, source=source: response_handler(source, msg), { 'command': 'setBreakpoints', 'arguments': { 'source': source, 'breakpoints': breakpoints, }, 'sourceModified': False, # TODO: We can actually check this }, failure_handler=lambda *_: response_received()) # TODO: Add the _configured_breakpoints to function breakpoints if self._server_capabilities.get('supportsFunctionBreakpoints'): awaiting = awaiting + 1 breakpoints = [] for bp in self._func_breakpoints: if bp['state'] != 'ENABLED': continue dap_bp = {} dap_bp.update(bp['options']) dap_bp.update({'name': bp['function']}) breakpoints.append(dap_bp) self._connection.DoRequest( lambda msg: response_handler(None, msg), { 'command': 'setFunctionBreakpoints', 'arguments': { 'breakpoints': breakpoints, } }, failure_handler=lambda *_: response_received()) if self._exception_breakpoints: awaiting = awaiting + 1 self._connection.DoRequest( lambda msg: response_handler(None, None), { 'command': 'setExceptionBreakpoints', 'arguments': self._exception_breakpoints }, failure_handler=lambda *_: response_received()) if awaiting == 0 and doneHandler: doneHandler()
def SendBreakpoints(self, doneHandler=None): assert self._breakpoints_handler is not None # Clear any existing breakpoints prior to sending new ones self._breakpoints_handler.ClearBreakpoints() awaiting = 0 def response_received(*failure_args): nonlocal awaiting awaiting = awaiting - 1 if failure_args and self._connection: reason, msg = failure_args utils.UserMessage( 'Unable to set breakpoint: {0}'.format(reason), persist=True, error=True) if awaiting == 0 and doneHandler: doneHandler() def response_handler(source, msg, temp_idxs=[]): if msg: self._breakpoints_handler.AddBreakpoints(source, msg) breakpoints = (msg.get('body') or {}).get('breakpoints') or [] self._UpdateTemporaryBreakpoints(breakpoints, temp_idxs) response_received() # NOTE: Must do this _first_ otherwise we might send requests and get # replies before we finished sending all the requests. if self._exception_breakpoints is None: self._SetUpExceptionBreakpoints(self._configured_breakpoints) # TODO: add the _configured_breakpoints to line_breakpoints for file_name, line_breakpoints in self._line_breakpoints.items(): temp_idxs = [] breakpoints = [] for bp in line_breakpoints: self._SignToLine(file_name, bp) if 'sign_id' in bp: signs.UnplaceSign(bp['sign_id'], 'VimspectorBP') del bp['sign_id'] if bp['state'] != 'ENABLED': continue dap_bp = {} dap_bp.update(bp['options']) dap_bp.update({'line': bp['line']}) dap_bp.pop('temporary', None) if bp['options'].get('temporary'): temp_idxs.append([len(breakpoints), bp]) breakpoints.append(dap_bp) source = { 'name': os.path.basename(file_name), 'path': file_name, } awaiting = awaiting + 1 self._connection.DoRequest( # The source=source here is critical to ensure that we capture each # source in the iteration, rather than ending up passing the same source # to each callback. lambda msg, source=source, temp_idxs=temp_idxs: response_handler(source, msg, temp_idxs=temp_idxs), { 'command': 'setBreakpoints', 'arguments': { 'source': source, 'breakpoints': breakpoints, }, 'sourceModified': False, # TODO: We can actually check this }, failure_handler=response_received) # TODO: Add the _configured_breakpoints to function breakpoints if self._server_capabilities.get('supportsFunctionBreakpoints'): awaiting = awaiting + 1 breakpoints = [] for bp in self._func_breakpoints: if bp['state'] != 'ENABLED': continue dap_bp = {} dap_bp.update(bp['options']) dap_bp.update({'name': bp['function']}) breakpoints.append(dap_bp) self._connection.DoRequest(lambda msg: response_handler(None, msg), { 'command': 'setFunctionBreakpoints', 'arguments': { 'breakpoints': breakpoints, } }, failure_handler=response_received) if self._exception_breakpoints: awaiting = awaiting + 1 self._connection.DoRequest( lambda msg: response_handler(None, None), { 'command': 'setExceptionBreakpoints', 'arguments': self._exception_breakpoints }, failure_handler=response_received) if awaiting == 0 and doneHandler: doneHandler()