vimspector/python3/vimspector/utils.py
Ben Jackson 920db068c6 Use a prompt buffer for adding watches
This very new feature makes it kind of nice to add new watches in the
place where they will appear, keeping the eye in the same screen
location.
2018-06-09 22:33:22 +01:00

220 lines
5.6 KiB
Python

# vimspector - A multi-language debugging system for Vim
# Copyright 2018 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.
import logging
import os
import contextlib
import vim
import json
_log_handler = logging.FileHandler( os.path.expanduser( '~/.vimspector.log' ) )
_log_handler.setFormatter(
logging.Formatter( '%(asctime)s - %(levelname)s - %(message)s' ) )
def SetUpLogging( logger ):
logger.setLevel( logging.DEBUG )
if _log_handler not in logger.handlers:
logger.addHandler( _log_handler )
def SetUpScratchBuffer( buf, name ):
buf.options[ 'buftype' ] = 'nofile'
buf.options[ 'swapfile' ] = False
buf.options[ 'modifiable' ] = False
buf.options[ 'modified' ] = False
buf.options[ 'readonly' ] = True
buf.options[ 'buflisted' ] = False
buf.options[ 'bufhidden' ] = 'wipe'
buf.name = name
def SetUpHiddenBuffer( buf, name ):
buf.options[ 'buftype' ] = 'nofile'
buf.options[ 'swapfile' ] = False
buf.options[ 'modifiable' ] = False
buf.options[ 'modified' ] = False
buf.options[ 'readonly' ] = True
buf.options[ 'buflisted' ] = False
buf.options[ 'bufhidden' ] = 'hide'
buf.name = name
def SetUpPromptBuffer( buf, name, prompt, callback ):
# This feature is _super_ new, so only enable when available
if not int( vim.eval( "exists( '*prompt_setprompt' )" ) ):
return SetUpScratchBuffer( buf, name )
buf.options[ 'buftype' ] = 'prompt'
buf.options[ 'swapfile' ] = False
buf.options[ 'modifiable' ] = True
buf.options[ 'modified' ] = False
buf.options[ 'readonly' ] = False
buf.options[ 'buflisted' ] = False
buf.options[ 'bufhidden' ] = 'wipe'
buf.name = name
vim.eval( "prompt_setprompt( {0}, '{1}' )".format( buf.number,
Escape( prompt ) ) )
vim.eval( "prompt_setcallback( {0}, function( '{1}' ) )".format(
buf.number,
Escape( callback ) ) )
@contextlib.contextmanager
def ModifiableScratchBuffer( buf ):
if buf.options[ 'modifiable' ]:
yield
return
buf.options[ 'modifiable' ] = True
buf.options[ 'readonly' ] = False
try:
yield
finally:
buf.options[ 'modifiable' ] = False
buf.options[ 'readonly' ] = True
@contextlib.contextmanager
def RestoreCursorPosition():
current_pos = vim.current.window.cursor
try:
yield
finally:
vim.current.window.cursor = (
min( current_pos[ 0 ], len( vim.current.buffer ) ),
current_pos[ 1 ] )
@contextlib.contextmanager
def RestorCurrentWindow():
# TODO: Don't trigger autoccommands when shifting windows
old_window = vim.current.window
try:
yield
finally:
vim.current.window = old_window
@contextlib.contextmanager
def RestoreCurrentBuffer( window ):
# TODO: Don't trigger autoccommands when shifting buffers
old_buffer = window.buffer
try:
yield
finally:
with RestorCurrentWindow():
vim.current.window = window
vim.current.buffer = old_buffer
@contextlib.contextmanager
def TemporaryVimOptions( opts ):
old_value = {}
try:
for option, value in opts.items():
old_value[ option ] = vim.options[ option ]
vim.options[ option ] = value
yield
finally:
for option, value in old_value.items():
vim.options[ option ] = value
@contextlib.contextmanager
def TemporaryVimOption( opt, value ):
old_value = vim.options[ opt ]
vim.options[ opt ] = value
try:
yield
finally:
vim.options[ opt ] = old_value
def PathToConfigFile( file_name ):
p = os.getcwd()
while True:
candidate = os.path.join( p, file_name )
if os.path.exists( candidate ):
return candidate
parent = os.path.dirname( p )
if parent == p:
return None
p = parent
def Escape( msg ):
return msg.replace( "'", "''" )
def UserMessage( msg, persist=False ):
vim.command( 'redraw' )
cmd = 'echom' if persist else 'echo'
for line in msg.split( '\n' ):
vim.command( "{0} '{1}'".format( cmd, Escape( line ) ) )
@contextlib.contextmanager
def InputSave():
vim.eval( 'inputsave()' )
try:
yield
except:
vim.eval( 'inputrestore()' )
def SelectFromList( prompt, options ):
with InputSave():
display_options = [ prompt ]
display_options.extend( [ '{0}: {1}'.format( i + 1, v )
for i, v in enumerate( options ) ] )
try:
selection = int( vim.eval(
'inputlist( ' + json.dumps( display_options ) + ' )' ) ) - 1
if selection < 0 or selection >= len( options ):
return None
return options[ selection ]
except KeyboardInterrupt:
return None
def AskForInput( prompt ):
with InputSave():
return vim.eval( "input( '{0}' )".format( Escape( prompt ) ) )
def AppendToBuffer( buf, line_or_lines ):
# After clearing the buffer (using buf[:] = None) there is always a single
# empty line in the buffer object and no "is empty" method.
if len( buf ) > 1 or buf[ 0 ]:
line = len( buf ) + 1
buf.append( line_or_lines )
elif isinstance( line_or_lines, str ):
line = 1
buf[-1] = line_or_lines
else:
line = 1
buf[:] = line_or_lines
# Return the first Vim line number (1-based) that we just set.
return line
def ClearBuffer( buf ):
buf[:] = None