Merge branch 'master' into master
This commit is contained in:
commit
91e0426e88
13 changed files with 363 additions and 61 deletions
51
python3/vimspector/custom/java.py
Normal file
51
python3/vimspector/custom/java.py
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
# vimspector - A multi-language debugging system for Vim
|
||||
# Copyright 2021 Ben Jackson
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from vimspector.debug_session import DebugSession
|
||||
from vimspector import utils, settings
|
||||
|
||||
|
||||
class JavaDebugAdapter( object ):
|
||||
def __init__( self, debug_session: DebugSession ):
|
||||
self.debug_session = debug_session
|
||||
|
||||
def OnEvent_hotcodereplace( self, message ):
|
||||
# Hack for java debug server hot-code-replace
|
||||
body = message.get( 'body' ) or {}
|
||||
|
||||
if body.get( 'type' ) != 'hotcodereplace':
|
||||
return
|
||||
|
||||
if body.get( 'changeType' ) == 'BUILD_COMPLETE':
|
||||
def handler( result ):
|
||||
if result == 1:
|
||||
self.debug_session._connection.DoRequest( None, {
|
||||
'command': 'redefineClasses',
|
||||
'arguments': {},
|
||||
} )
|
||||
|
||||
mode = settings.Get( 'java_hotcodereplace_mode' )
|
||||
if mode == 'ask':
|
||||
utils.Confirm( self.debug_session._api_prefix,
|
||||
'Code has changed, hot reload?',
|
||||
handler,
|
||||
default_value = 1 )
|
||||
elif mode == 'always':
|
||||
self.debug_session._connection.DoRequest( None, {
|
||||
'command': 'redefineClasses',
|
||||
'arguments': {},
|
||||
} )
|
||||
elif body.get( 'message' ):
|
||||
utils.UserMessage( 'Hot code replace: ' + body[ 'message' ] )
|
||||
|
|
@ -29,14 +29,14 @@ class PendingRequest( object ):
|
|||
|
||||
|
||||
class DebugAdapterConnection( object ):
|
||||
def __init__( self, handler, send_func ):
|
||||
def __init__( self, handlers, send_func ):
|
||||
self._logger = logging.getLogger( __name__ )
|
||||
utils.SetUpLogging( self._logger )
|
||||
|
||||
self._Write = send_func
|
||||
self._SetState( 'READ_HEADER' )
|
||||
self._buffer = bytes()
|
||||
self._handler = handler
|
||||
self._handlers = handlers
|
||||
self._next_message_id = 0
|
||||
self._outstanding_requests = {}
|
||||
|
||||
|
|
@ -124,7 +124,7 @@ class DebugAdapterConnection( object ):
|
|||
|
||||
def Reset( self ):
|
||||
self._Write = None
|
||||
self._handler = None
|
||||
self._handlers = None
|
||||
|
||||
while self._outstanding_requests:
|
||||
_, request = self._outstanding_requests.popitem()
|
||||
|
|
@ -237,7 +237,7 @@ class DebugAdapterConnection( object ):
|
|||
|
||||
|
||||
def _OnMessageReceived( self, message ):
|
||||
if not self._handler:
|
||||
if not self._handlers:
|
||||
return
|
||||
|
||||
if message[ 'type' ] == 'response':
|
||||
|
|
@ -270,25 +270,21 @@ class DebugAdapterConnection( object ):
|
|||
self._logger.error( 'Request failed: {0}'.format( reason ) )
|
||||
if request.failure_handler:
|
||||
request.failure_handler( reason, message )
|
||||
elif 'OnFailure' in dir( self._handler ):
|
||||
self._handler.OnFailure( reason, request.msg, message )
|
||||
else:
|
||||
utils.UserMessage( 'Request failed: {0}'.format( reason ) )
|
||||
for h in self._handlers:
|
||||
if 'OnFailure' in dir( h ):
|
||||
h.OnFailure( reason, request.msg, message )
|
||||
|
||||
elif message[ 'type' ] == 'event':
|
||||
method = 'OnEvent_' + message[ 'event' ]
|
||||
if method in dir( self._handler ):
|
||||
getattr( self._handler, method )( message )
|
||||
else:
|
||||
utils.UserMessage( 'Unhandled event: {0}'.format( message[ 'event' ] ),
|
||||
persist = True )
|
||||
for h in self._handlers:
|
||||
if method in dir( h ):
|
||||
getattr( h, method )( message )
|
||||
elif message[ 'type' ] == 'request':
|
||||
method = 'OnRequest_' + message[ 'command' ]
|
||||
if method in dir( self._handler ):
|
||||
getattr( self._handler, method )( message )
|
||||
else:
|
||||
utils.UserMessage(
|
||||
'Unhandled request: {0}'.format( message[ 'command' ] ),
|
||||
persist = True )
|
||||
for h in self._handlers:
|
||||
if method in dir( h ):
|
||||
getattr( h, method )( message )
|
||||
|
||||
|
||||
def _KillTimer( request ):
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ import shlex
|
|||
import subprocess
|
||||
import functools
|
||||
import vim
|
||||
import importlib
|
||||
|
||||
from vimspector import ( breakpoints,
|
||||
code,
|
||||
|
|
@ -99,7 +100,7 @@ class DebugSession( object ):
|
|||
|
||||
return launch_config_file, configurations
|
||||
|
||||
def Start( self, launch_variables = None ):
|
||||
def Start( self, force_choose=False, launch_variables = None ):
|
||||
# We mutate launch_variables, so don't mutate the default argument.
|
||||
# https://docs.python-guide.org/writing/gotchas/#mutable-default-arguments
|
||||
if launch_variables is None:
|
||||
|
|
@ -134,6 +135,11 @@ class DebugSession( object ):
|
|||
|
||||
if 'configuration' in launch_variables:
|
||||
configuration_name = launch_variables.pop( 'configuration' )
|
||||
elif force_choose:
|
||||
# Always display the menu
|
||||
configuration_name = utils.SelectFromList(
|
||||
'Which launch configuration?',
|
||||
sorted( configurations.keys() ) )
|
||||
elif ( len( configurations ) == 1 and
|
||||
next( iter( configurations.values() ) ).get( "autoselect", True ) ):
|
||||
configuration_name = next( iter( configurations.keys() ) )
|
||||
|
|
@ -888,8 +894,21 @@ class DebugSession( object ):
|
|||
self._splash_screen,
|
||||
"Unable to start adapter" )
|
||||
else:
|
||||
if 'custom_handler' in self._adapter:
|
||||
spec = self._adapter[ 'custom_handler' ]
|
||||
if isinstance( spec, dict ):
|
||||
module = spec[ 'module' ]
|
||||
cls = spec[ 'class' ]
|
||||
else:
|
||||
module, cls = spec.rsplit( '.', 1 )
|
||||
|
||||
CustomHandler = getattr( importlib.import_module( module ), cls )
|
||||
handlers = [ CustomHandler( self ), self ]
|
||||
else:
|
||||
handlers = [ self ]
|
||||
|
||||
self._connection = debug_adapter_connection.DebugAdapterConnection(
|
||||
self,
|
||||
handlers,
|
||||
lambda msg: utils.Call(
|
||||
"vimspector#internal#{}#Send".format( self._connection_type ),
|
||||
msg ) )
|
||||
|
|
@ -897,39 +916,58 @@ class DebugSession( object ):
|
|||
self._logger.info( 'Debug Adapter Started' )
|
||||
|
||||
def _StopDebugAdapter( self, interactive = False, callback = None ):
|
||||
self._splash_screen = utils.DisplaySplash(
|
||||
self._api_prefix,
|
||||
self._splash_screen,
|
||||
"Shutting down debug adapter..." )
|
||||
|
||||
def handler( *args ):
|
||||
self._splash_screen = utils.HideSplash( self._api_prefix,
|
||||
self._splash_screen )
|
||||
|
||||
if callback:
|
||||
self._logger.debug( "Setting server exit handler before disconnect" )
|
||||
assert not self._run_on_server_exit
|
||||
self._run_on_server_exit = callback
|
||||
|
||||
vim.eval( 'vimspector#internal#{}#StopDebugSession()'.format(
|
||||
self._connection_type ) )
|
||||
|
||||
arguments = {}
|
||||
if ( interactive and
|
||||
self._server_capabilities.get( 'supportTerminateDebuggee' ) ):
|
||||
if self._stackTraceView.AnyThreadsRunning():
|
||||
choice = utils.AskForInput( "Terminate debuggee [Y/N/default]? ", "" )
|
||||
if choice == "Y" or choice == "y":
|
||||
|
||||
def disconnect():
|
||||
self._splash_screen = utils.DisplaySplash(
|
||||
self._api_prefix,
|
||||
self._splash_screen,
|
||||
"Shutting down debug adapter..." )
|
||||
|
||||
def handler( *args ):
|
||||
self._splash_screen = utils.HideSplash( self._api_prefix,
|
||||
self._splash_screen )
|
||||
|
||||
if callback:
|
||||
self._logger.debug( "Setting server exit handler before disconnect" )
|
||||
assert not self._run_on_server_exit
|
||||
self._run_on_server_exit = callback
|
||||
|
||||
vim.eval( 'vimspector#internal#{}#StopDebugSession()'.format(
|
||||
self._connection_type ) )
|
||||
|
||||
self._connection.DoRequest( handler, {
|
||||
'command': 'disconnect',
|
||||
'arguments': arguments,
|
||||
}, failure_handler = handler, timeout = 5000 )
|
||||
|
||||
if not interactive:
|
||||
disconnect()
|
||||
elif not self._server_capabilities.get( 'supportTerminateDebuggee' ):
|
||||
disconnect()
|
||||
elif not self._stackTraceView.AnyThreadsRunning():
|
||||
disconnect()
|
||||
else:
|
||||
def handle_choice( choice ):
|
||||
if choice == 1:
|
||||
# yes
|
||||
arguments[ 'terminateDebuggee' ] = True
|
||||
elif choice == "N" or choice == 'n':
|
||||
elif choice == 2:
|
||||
# no
|
||||
arguments[ 'terminateDebuggee' ] = False
|
||||
elif choice <= 0:
|
||||
# Abort
|
||||
return
|
||||
# Else, use server default
|
||||
|
||||
self._connection.DoRequest( handler, {
|
||||
'command': 'disconnect',
|
||||
'arguments': arguments,
|
||||
}, failure_handler = handler, timeout = 5000 )
|
||||
disconnect()
|
||||
|
||||
# TODO: Use the 'tarminate' request if supportsTerminateRequest set
|
||||
utils.Confirm( self._api_prefix,
|
||||
"Terminate debuggee?",
|
||||
handle_choice,
|
||||
default_value = 3,
|
||||
options = [ '(Y)es', '(N)o', '(D)efault' ],
|
||||
keys = [ 'y', 'n', 'd' ] )
|
||||
|
||||
|
||||
def _PrepareAttach( self, adapter_config, launch_config ):
|
||||
|
|
|
|||
|
|
@ -159,7 +159,8 @@ GADGETS = {
|
|||
"port": "${DAPPort}",
|
||||
"configuration": {
|
||||
"cwd": "${workspaceRoot}"
|
||||
}
|
||||
},
|
||||
'custom_handler': 'vimspector.custom.java.JavaDebugAdapter'
|
||||
}
|
||||
},
|
||||
},
|
||||
|
|
|
|||
|
|
@ -59,7 +59,10 @@ DEFAULTS = {
|
|||
'expand_or_jump': [ '<CR>', '<2-LeftMouse>' ],
|
||||
'focus_thread': [ '<leader><CR>' ],
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
# Custom
|
||||
'java_hotcodereplace_mode': 'ask',
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -375,6 +375,44 @@ def AskForInput( prompt, default_value = None, completion = None ):
|
|||
return None
|
||||
|
||||
|
||||
CONFIRM = {}
|
||||
CONFIRM_ID = 0
|
||||
|
||||
|
||||
def ConfirmCallback( confirm_id, result ):
|
||||
try:
|
||||
handler = CONFIRM.pop( confirm_id )
|
||||
except KeyError:
|
||||
UserMessage( f"Internal error: unexpected callback id { confirm_id }",
|
||||
persist = True,
|
||||
error = True )
|
||||
return
|
||||
|
||||
handler( result )
|
||||
|
||||
|
||||
def Confirm( api_prefix,
|
||||
prompt,
|
||||
handler,
|
||||
default_value = 2,
|
||||
options: list = None,
|
||||
keys: list = None ):
|
||||
if not options:
|
||||
options = [ '(Y)es', '(N)o' ]
|
||||
if not keys:
|
||||
keys = [ 'y', 'n' ]
|
||||
|
||||
global CONFIRM_ID
|
||||
CONFIRM_ID += 1
|
||||
CONFIRM[ CONFIRM_ID ] = handler
|
||||
Call( f'vimspector#internal#{ api_prefix }popup#Confirm',
|
||||
CONFIRM_ID,
|
||||
prompt,
|
||||
options,
|
||||
default_value,
|
||||
keys )
|
||||
|
||||
|
||||
def AppendToBuffer( buf, line_or_lines, modified=False ):
|
||||
line = 1
|
||||
try:
|
||||
|
|
@ -403,8 +441,10 @@ def AppendToBuffer( buf, line_or_lines, modified=False ):
|
|||
|
||||
|
||||
|
||||
def ClearBuffer( buf ):
|
||||
def ClearBuffer( buf, modified = False ):
|
||||
buf[ : ] = None
|
||||
if not modified:
|
||||
buf.options[ 'modified' ] = False
|
||||
|
||||
|
||||
def SetBufferContents( buf, lines, modified=False ):
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue