/vim_runtime/sources_non_forked/jedi-vim/pythonx/jedi_vim.py
Python | 1159 lines | 955 code | 106 blank | 98 comment | 121 complexity | ceb541bd30a7c1e36b936c542ea2e8ed MD5 | raw file
1# -*- coding: utf-8 -*- 2""" 3The Python parts of the Jedi library for VIM. It is mostly about communicating 4with VIM. 5""" 6 7import traceback # for exception output 8import re 9import os 10import sys 11from shlex import split as shsplit 12from contextlib import contextmanager 13try: 14 from itertools import zip_longest 15except ImportError: 16 from itertools import izip_longest as zip_longest # Python 2 17 18import vim 19 20is_py3 = sys.version_info[0] >= 3 21if is_py3: 22 ELLIPSIS = "…" 23 unicode = str 24else: 25 ELLIPSIS = u"…" 26 27 28try: 29 # Somehow sys.prefix is set in combination with VIM and virtualenvs. 30 # However the sys path is not affected. Just reset it to the normal value. 31 sys.prefix = sys.base_prefix 32 sys.exec_prefix = sys.base_exec_prefix 33except AttributeError: 34 # If we're not in a virtualenv we don't care. Everything is fine. 35 pass 36 37 38class PythonToVimStr(unicode): 39 """ Vim has a different string implementation of single quotes """ 40 __slots__ = [] 41 42 def __new__(cls, obj, encoding='UTF-8'): 43 if not (is_py3 or isinstance(obj, unicode)): 44 obj = unicode.__new__(cls, obj, encoding) 45 46 # Vim cannot deal with zero bytes: 47 obj = obj.replace('\0', '\\0') 48 return unicode.__new__(cls, obj) 49 50 def __repr__(self): 51 # this is totally stupid and makes no sense but vim/python unicode 52 # support is pretty bad. don't ask how I came up with this... It just 53 # works... 54 # It seems to be related to that bug: http://bugs.python.org/issue5876 55 if unicode is str: 56 s = self 57 else: 58 s = self.encode('UTF-8') 59 return '"%s"' % s.replace('\\', '\\\\').replace('"', r'\"') 60 61 62class VimError(Exception): 63 def __init__(self, message, throwpoint, executing): 64 super(type(self), self).__init__(message) 65 self.message = message 66 self.throwpoint = throwpoint 67 self.executing = executing 68 69 def __str__(self): 70 return "{}; created by {!r} (in {})".format( 71 self.message, self.executing, self.throwpoint 72 ) 73 74 75def _catch_exception(string, is_eval): 76 """ 77 Interface between vim and python calls back to it. 78 Necessary, because the exact error message is not given by `vim.error`. 79 """ 80 result = vim.eval('jedi#_vim_exceptions({0}, {1})'.format( 81 repr(PythonToVimStr(string, 'UTF-8')), int(is_eval))) 82 if 'exception' in result: 83 raise VimError(result['exception'], result['throwpoint'], string) 84 return result['result'] 85 86 87def vim_command(string): 88 _catch_exception(string, is_eval=False) 89 90 91def vim_eval(string): 92 return _catch_exception(string, is_eval=True) 93 94 95def no_jedi_warning(error=None): 96 vim.command('echohl WarningMsg') 97 vim.command('echom "Please install Jedi if you want to use jedi-vim."') 98 if error: 99 vim.command('echom "The error was: {0}"'.format(error)) 100 vim.command('echohl None') 101 102 103def echo_highlight(msg): 104 vim_command('echohl WarningMsg | echom "jedi-vim: {0}" | echohl None'.format( 105 str(msg).replace('"', '\\"'))) 106 107 108jedi_path = os.path.join(os.path.dirname(__file__), 'jedi') 109sys.path.insert(0, jedi_path) 110parso_path = os.path.join(os.path.dirname(__file__), 'parso') 111sys.path.insert(0, parso_path) 112 113try: 114 import jedi 115except ImportError: 116 jedi = None 117 jedi_import_error = sys.exc_info() 118else: 119 try: 120 version = jedi.__version__ 121 except Exception as e: # e.g. AttributeError 122 echo_highlight( 123 "Error when loading the jedi python module ({0}). " 124 "Please ensure that Jedi is installed correctly (see Installation " 125 "in the README.".format(e)) 126 jedi = None 127 else: 128 if isinstance(version, str): 129 # the normal use case, now. 130 from jedi import utils 131 version = utils.version_info() 132 if version < (0, 7): 133 echo_highlight('Please update your Jedi version, it is too old.') 134finally: 135 sys.path.remove(jedi_path) 136 sys.path.remove(parso_path) 137 138 139class VimCompat: 140 _eval_cache = {} 141 _func_cache = {} 142 143 @classmethod 144 def has(cls, what): 145 try: 146 return cls._eval_cache[what] 147 except KeyError: 148 ret = cls._eval_cache[what] = cls.call('has', what) 149 return ret 150 151 @classmethod 152 def call(cls, func, *args): 153 try: 154 f = cls._func_cache[func] 155 except KeyError: 156 if IS_NVIM: 157 f = cls._func_cache[func] = getattr(vim.funcs, func) 158 else: 159 f = cls._func_cache[func] = vim.Function(func) 160 return f(*args) 161 162 @classmethod 163 def setqflist(cls, items, title, context): 164 if cls.has('patch-7.4.2200'): # can set qf title. 165 what = {'title': title} 166 if cls.has('patch-8.0.0590'): # can set qf context 167 what['context'] = {'jedi_usages': context} 168 if cls.has('patch-8.0.0657'): # can set items via "what". 169 what['items'] = items 170 cls.call('setqflist', [], ' ', what) 171 else: 172 # Can set title (and maybe context), but needs two calls. 173 cls.call('setqflist', items) 174 cls.call('setqflist', items, 'a', what) 175 else: 176 cls.call('setqflist', items) 177 178 @classmethod 179 def setqflist_title(cls, title): 180 if cls.has('patch-7.4.2200'): 181 cls.call('setqflist', [], 'a', {'title': title}) 182 183 @classmethod 184 def can_update_current_qflist_for_context(cls, context): 185 if cls.has('patch-8.0.0590'): # can set qf context 186 return cls.call('getqflist', {'context': 1})['context'] == { 187 'jedi_usages': context, 188 } 189 190 191def catch_and_print_exceptions(func): 192 def wrapper(*args, **kwargs): 193 try: 194 return func(*args, **kwargs) 195 except (Exception, vim.error): 196 print(traceback.format_exc()) 197 return None 198 return wrapper 199 200 201def _check_jedi_availability(show_error=False): 202 def func_receiver(func): 203 def wrapper(*args, **kwargs): 204 if jedi is None: 205 if show_error: 206 no_jedi_warning() 207 return 208 else: 209 return func(*args, **kwargs) 210 return wrapper 211 return func_receiver 212 213 214current_environment = (None, None) 215 216 217def get_environment(use_cache=True): 218 global current_environment 219 220 vim_force_python_version = vim_eval("g:jedi#force_py_version") 221 if use_cache and vim_force_python_version == current_environment[0]: 222 return current_environment[1] 223 224 environment = None 225 if vim_force_python_version == "auto": 226 environment = jedi.api.environment.get_cached_default_environment() 227 else: 228 force_python_version = vim_force_python_version 229 if '0000' in force_python_version or '9999' in force_python_version: 230 # It's probably a float that wasn't shortened. 231 try: 232 force_python_version = "{:.1f}".format(float(force_python_version)) 233 except ValueError: 234 pass 235 elif isinstance(force_python_version, float): 236 force_python_version = "{:.1f}".format(force_python_version) 237 238 try: 239 environment = jedi.get_system_environment(force_python_version) 240 except jedi.InvalidPythonEnvironment as exc: 241 environment = jedi.api.environment.get_cached_default_environment() 242 echo_highlight( 243 "force_python_version=%s is not supported: %s - using %s." % ( 244 vim_force_python_version, str(exc), str(environment))) 245 246 current_environment = (vim_force_python_version, environment) 247 return environment 248 249 250def get_known_environments(): 251 """Get known Jedi environments.""" 252 envs = list(jedi.api.environment.find_virtualenvs()) 253 envs.extend(jedi.api.environment.find_system_environments()) 254 return envs 255 256 257@catch_and_print_exceptions 258def get_script(source=None, column=None): 259 jedi.settings.additional_dynamic_modules = [ 260 b.name for b in vim.buffers if ( 261 b.name is not None and 262 b.name.endswith('.py') and 263 b.options['buflisted'])] 264 if source is None: 265 source = '\n'.join(vim.current.buffer) 266 row = vim.current.window.cursor[0] 267 if column is None: 268 column = vim.current.window.cursor[1] 269 buf_path = vim.current.buffer.name 270 271 return jedi.Script( 272 source, row, column, buf_path, 273 encoding=vim_eval('&encoding') or 'latin1', 274 environment=get_environment(), 275 ) 276 277 278@_check_jedi_availability(show_error=False) 279@catch_and_print_exceptions 280def completions(): 281 row, column = vim.current.window.cursor 282 # Clear call signatures in the buffer so they aren't seen by the completer. 283 # Call signatures in the command line can stay. 284 if int(vim_eval("g:jedi#show_call_signatures")) == 1: 285 clear_call_signatures() 286 if vim.eval('a:findstart') == '1': 287 count = 0 288 for char in reversed(vim.current.line[:column]): 289 if not re.match(r'[\w\d]', char): 290 break 291 count += 1 292 vim.command('return %i' % (column - count)) 293 else: 294 base = vim.eval('a:base') 295 source = '' 296 for i, line in enumerate(vim.current.buffer): 297 # enter this path again, otherwise source would be incomplete 298 if i == row - 1: 299 source += line[:column] + base + line[column:] 300 else: 301 source += line 302 source += '\n' 303 # here again hacks, because jedi has a different interface than vim 304 column += len(base) 305 try: 306 script = get_script(source=source, column=column) 307 completions = script.completions() 308 signatures = script.call_signatures() 309 310 add_info = "preview" in vim.eval("&completeopt").split(",") 311 out = [] 312 for c in completions: 313 d = dict(word=PythonToVimStr(c.name[:len(base)] + c.complete), 314 abbr=PythonToVimStr(c.name_with_symbols), 315 # stuff directly behind the completion 316 menu=PythonToVimStr(c.description), 317 icase=1, # case insensitive 318 dup=1 # allow duplicates (maybe later remove this) 319 ) 320 if add_info: 321 try: 322 d["info"] = PythonToVimStr(c.docstring()) 323 except Exception: 324 print("jedi-vim: error with docstring for %r: %s" % ( 325 c, traceback.format_exc())) 326 out.append(d) 327 328 strout = str(out) 329 except Exception: 330 # print to stdout, will be in :messages 331 print(traceback.format_exc()) 332 strout = '' 333 completions = [] 334 signatures = [] 335 336 show_call_signatures(signatures) 337 vim.command('return ' + strout) 338 339 340@contextmanager 341def tempfile(content): 342 # Using this instead of the tempfile module because Windows won't read 343 # from a file not yet written to disk 344 with open(vim_eval('tempname()'), 'w') as f: 345 f.write(content) 346 try: 347 yield f 348 finally: 349 os.unlink(f.name) 350 351 352@_check_jedi_availability(show_error=True) 353@catch_and_print_exceptions 354def goto(mode="goto"): 355 """ 356 :param str mode: "definition", "assignment", "goto" 357 :return: list of definitions/assignments 358 :rtype: list 359 """ 360 script = get_script() 361 if mode == "goto": 362 definitions = script.goto_assignments(follow_imports=True) 363 elif mode == "definition": 364 definitions = script.goto_definitions() 365 elif mode == "assignment": 366 definitions = script.goto_assignments() 367 elif mode == "stubs": 368 definitions = script.goto_assignments(follow_imports=True, only_stubs=True) 369 370 if not definitions: 371 echo_highlight("Couldn't find any definitions for this.") 372 elif len(definitions) == 1 and mode != "related_name": 373 d = list(definitions)[0] 374 if d.column is None: 375 if d.is_keyword: 376 echo_highlight("Cannot get the definition of Python keywords.") 377 else: 378 echo_highlight("Builtin modules cannot be displayed (%s)." 379 % d.desc_with_module) 380 else: 381 using_tagstack = int(vim_eval('g:jedi#use_tag_stack')) == 1 382 if (d.module_path or '') != vim.current.buffer.name: 383 result = new_buffer(d.module_path, 384 using_tagstack=using_tagstack) 385 if not result: 386 return [] 387 if (using_tagstack and d.module_path and 388 os.path.exists(d.module_path)): 389 tagname = d.name 390 with tempfile('{0}\t{1}\t{2}'.format( 391 tagname, d.module_path, 'call cursor({0}, {1})'.format( 392 d.line, d.column + 1))) as f: 393 old_tags = vim.eval('&tags') 394 old_wildignore = vim.eval('&wildignore') 395 try: 396 # Clear wildignore to ensure tag file isn't ignored 397 vim.command('set wildignore=') 398 vim.command('let &tags = %s' % 399 repr(PythonToVimStr(f.name))) 400 vim.command('tjump %s' % tagname) 401 finally: 402 vim.command('let &tags = %s' % 403 repr(PythonToVimStr(old_tags))) 404 vim.command('let &wildignore = %s' % 405 repr(PythonToVimStr(old_wildignore))) 406 vim.current.window.cursor = d.line, d.column 407 else: 408 show_goto_multi_results(definitions, mode) 409 return definitions 410 411 412def relpath(path): 413 """Make path relative to cwd if it is below.""" 414 abspath = os.path.abspath(path) 415 if abspath.startswith(os.getcwd()): 416 return os.path.relpath(path) 417 return path 418 419 420def annotate_description(d): 421 code = d.get_line_code().strip() 422 if d.type == 'statement': 423 return code 424 if d.type == 'function': 425 if code.startswith('def'): 426 return code 427 typ = 'def' 428 else: 429 typ = d.type 430 return '[%s] %s' % (typ, code) 431 432 433def show_goto_multi_results(definitions, mode): 434 """Create (or reuse) a quickfix list for multiple definitions.""" 435 global _current_definitions 436 437 lst = [] 438 (row, col) = vim.current.window.cursor 439 current_idx = None 440 current_def = None 441 for d in definitions: 442 if d.column is None: 443 # Typically a namespace, in the future maybe other things as 444 # well. 445 lst.append(dict(text=PythonToVimStr(d.description))) 446 else: 447 text = annotate_description(d) 448 lst.append(dict(filename=PythonToVimStr(relpath(d.module_path)), 449 lnum=d.line, col=d.column + 1, 450 text=PythonToVimStr(text))) 451 452 # Select current/nearest entry via :cc later. 453 if d.line == row and d.column <= col: 454 if (current_idx is None 455 or (abs(lst[current_idx]["col"] - col) 456 > abs(d.column - col))): 457 current_idx = len(lst) 458 current_def = d 459 460 # Build qflist title. 461 qf_title = mode 462 if current_def is not None: 463 if current_def.full_name: 464 qf_title += ": " + current_def.full_name 465 else: 466 qf_title += ": " + str(current_def) 467 select_entry = current_idx 468 else: 469 select_entry = 0 470 471 qf_context = id(definitions) 472 if (_current_definitions 473 and VimCompat.can_update_current_qflist_for_context(qf_context)): 474 # Same list, only adjust title/selected entry. 475 VimCompat.setqflist_title(qf_title) 476 vim_command('%dcc' % select_entry) 477 else: 478 VimCompat.setqflist(lst, title=qf_title, context=qf_context) 479 for_usages = mode == "usages" 480 vim_eval('jedi#add_goto_window(%d, %d)' % (for_usages, len(lst))) 481 vim_command('%d' % select_entry) 482 483 484def _same_definitions(a, b): 485 """Compare without _inference_state. 486 487 Ref: https://github.com/davidhalter/jedi-vim/issues/952) 488 """ 489 return all( 490 x._name.start_pos == y._name.start_pos 491 and x.module_path == y.module_path 492 and x.name == y.name 493 for x, y in zip(a, b) 494 ) 495 496 497@catch_and_print_exceptions 498def usages(visuals=True): 499 script = get_script() 500 definitions = script.usages() 501 if not definitions: 502 echo_highlight("No usages found here.") 503 return definitions 504 505 if visuals: 506 global _current_definitions 507 508 if _current_definitions: 509 if _same_definitions(_current_definitions, definitions): 510 definitions = _current_definitions 511 else: 512 clear_usages() 513 assert not _current_definitions 514 515 show_goto_multi_results(definitions, "usages") 516 if not _current_definitions: 517 _current_definitions = definitions 518 highlight_usages() 519 else: 520 assert definitions is _current_definitions # updated above 521 return definitions 522 523 524_current_definitions = None 525"""Current definitions to use for highlighting.""" 526_pending_definitions = {} 527"""Pending definitions for unloaded buffers.""" 528_placed_definitions_in_buffers = set() 529"""Set of buffers for faster cleanup.""" 530 531 532IS_NVIM = hasattr(vim, 'from_nvim') 533if IS_NVIM: 534 vim_prop_add = None 535else: 536 vim_prop_type_added = False 537 try: 538 vim_prop_add = vim.Function("prop_add") 539 except ValueError: 540 vim_prop_add = None 541 else: 542 vim_prop_remove = vim.Function("prop_remove") 543 544 545def clear_usages(): 546 """Clear existing highlights.""" 547 global _current_definitions 548 if _current_definitions is None: 549 return 550 _current_definitions = None 551 552 if IS_NVIM: 553 for buf in _placed_definitions_in_buffers: 554 src_ids = buf.vars.get('_jedi_usages_src_ids') 555 if src_ids is not None: 556 for src_id in src_ids: 557 buf.clear_highlight(src_id) 558 elif vim_prop_add: 559 for buf in _placed_definitions_in_buffers: 560 vim_prop_remove({ 561 'type': 'jediUsage', 562 'all': 1, 563 'bufnr': buf.number, 564 }) 565 else: 566 # Unset current window only. 567 assert _current_definitions is None 568 highlight_usages_for_vim_win() 569 570 _placed_definitions_in_buffers.clear() 571 572 573def highlight_usages(): 574 """Set definitions to be highlighted. 575 576 With Neovim it will use the nvim_buf_add_highlight API to highlight all 577 buffers already. 578 579 With Vim without support for text-properties only the current window is 580 highlighted via matchaddpos, and autocommands are setup to highlight other 581 windows on demand. Otherwise Vim's text-properties are used. 582 """ 583 global _current_definitions, _pending_definitions 584 585 definitions = _current_definitions 586 _pending_definitions = {} 587 588 if IS_NVIM or vim_prop_add: 589 bufs = {x.name: x for x in vim.buffers} 590 defs_per_buf = {} 591 for definition in definitions: 592 try: 593 buf = bufs[definition.module_path] 594 except KeyError: 595 continue 596 defs_per_buf.setdefault(buf, []).append(definition) 597 598 if IS_NVIM: 599 # We need to remember highlight ids with Neovim's API. 600 buf_src_ids = {} 601 for buf, definitions in defs_per_buf.items(): 602 buf_src_ids[buf] = [] 603 for definition in definitions: 604 src_id = _add_highlight_definition(buf, definition) 605 buf_src_ids[buf].append(src_id) 606 for buf, src_ids in buf_src_ids.items(): 607 buf.vars['_jedi_usages_src_ids'] = src_ids 608 else: 609 for buf, definitions in defs_per_buf.items(): 610 try: 611 for definition in definitions: 612 _add_highlight_definition(buf, definition) 613 except vim.error as exc: 614 if exc.args[0].startswith('Vim:E275:'): 615 # "Cannot add text property to unloaded buffer" 616 _pending_definitions.setdefault(buf.name, []).extend( 617 definitions) 618 else: 619 highlight_usages_for_vim_win() 620 621 622def _handle_pending_usages_for_buf(): 623 """Add (pending) highlights for the current buffer (Vim with textprops).""" 624 buf = vim.current.buffer 625 bufname = buf.name 626 try: 627 buf_defs = _pending_definitions[bufname] 628 except KeyError: 629 return 630 for definition in buf_defs: 631 _add_highlight_definition(buf, definition) 632 del _pending_definitions[bufname] 633 634 635def _add_highlight_definition(buf, definition): 636 lnum = definition.line 637 start_col = definition.column 638 639 # Skip highlighting of module definitions that point to the start 640 # of the file. 641 if definition.type == 'module' and lnum == 1 and start_col == 0: 642 return 643 644 _placed_definitions_in_buffers.add(buf) 645 646 # TODO: validate that definition.name is at this position? 647 # Would skip the module definitions from above already. 648 649 length = len(definition.name) 650 if vim_prop_add: 651 # XXX: needs jediUsage highlight (via after/syntax/python.vim). 652 global vim_prop_type_added 653 if not vim_prop_type_added: 654 vim.eval("prop_type_add('jediUsage', {'highlight': 'jediUsage'})") 655 vim_prop_type_added = True 656 vim_prop_add(lnum, start_col+1, { 657 'type': 'jediUsage', 658 'bufnr': buf.number, 659 'length': length, 660 }) 661 return 662 663 assert IS_NVIM 664 end_col = definition.column + length 665 src_id = buf.add_highlight('jediUsage', lnum-1, start_col, end_col, 666 src_id=0) 667 return src_id 668 669 670def highlight_usages_for_vim_win(): 671 """Highlight usages in the current window. 672 673 It stores the matchids in a window-local variable. 674 675 (matchaddpos() only works for the current window.) 676 """ 677 global _current_definitions 678 definitions = _current_definitions 679 680 win = vim.current.window 681 682 cur_matchids = win.vars.get('_jedi_usages_vim_matchids') 683 if cur_matchids: 684 if cur_matchids[0] == vim.current.buffer.number: 685 return 686 687 # Need to clear non-matching highlights. 688 for matchid in cur_matchids[1:]: 689 expr = 'matchdelete(%d)' % int(matchid) 690 vim.eval(expr) 691 692 matchids = [] 693 if definitions: 694 buffer_path = vim.current.buffer.name 695 for definition in definitions: 696 if (definition.module_path or '') == buffer_path: 697 positions = [ 698 [definition.line, 699 definition.column + 1, 700 len(definition.name)] 701 ] 702 expr = "matchaddpos('jediUsage', %s)" % repr(positions) 703 matchids.append(int(vim_eval(expr))) 704 705 if matchids: 706 vim.current.window.vars['_jedi_usages_vim_matchids'] = [ 707 vim.current.buffer.number] + matchids 708 elif cur_matchids is not None: 709 # Always set it (uses an empty list for "unset", which is not possible 710 # using del). 711 vim.current.window.vars['_jedi_usages_vim_matchids'] = [] 712 713 # Remember if clearing is needed for later buffer autocommands. 714 vim.current.buffer.vars['_jedi_usages_needs_clear'] = bool(matchids) 715 716 717@_check_jedi_availability(show_error=True) 718@catch_and_print_exceptions 719def show_documentation(): 720 script = get_script() 721 try: 722 definitions = script.goto_definitions() 723 except Exception: 724 # print to stdout, will be in :messages 725 definitions = [] 726 print("Exception, this shouldn't happen.") 727 print(traceback.format_exc()) 728 729 if not definitions: 730 echo_highlight('No documentation found for that.') 731 vim.command('return') 732 return 733 734 docs = [] 735 for d in definitions: 736 doc = d.docstring() 737 if doc: 738 title = 'Docstring for %s' % d.desc_with_module 739 underline = '=' * len(title) 740 docs.append('%s\n%s\n%s' % (title, underline, doc)) 741 else: 742 docs.append('|No Docstring for %s|' % d) 743 text = ('\n' + '-' * 79 + '\n').join(docs) 744 vim.command('let l:doc = %s' % repr(PythonToVimStr(text))) 745 vim.command('let l:doc_lines = %s' % len(text.split('\n'))) 746 return True 747 748 749@catch_and_print_exceptions 750def clear_call_signatures(): 751 # Check if using command line call signatures 752 if int(vim_eval("g:jedi#show_call_signatures")) == 2: 753 vim_command('echo ""') 754 return 755 cursor = vim.current.window.cursor 756 e = vim_eval('g:jedi#call_signature_escape') 757 # We need two turns here to search and replace certain lines: 758 # 1. Search for a line with a call signature and save the appended 759 # characters 760 # 2. Actually replace the line and redo the status quo. 761 py_regex = r'%sjedi=([0-9]+), (.*?)%s.*?%sjedi%s'.replace( 762 '%s', re.escape(e)) 763 for i, line in enumerate(vim.current.buffer): 764 match = re.search(py_regex, line) 765 if match is not None: 766 # Some signs were added to minimize syntax changes due to call 767 # signatures. We have to remove them again. The number of them is 768 # specified in `match.group(1)`. 769 after = line[match.end() + int(match.group(1)):] 770 line = line[:match.start()] + match.group(2) + after 771 vim.current.buffer[i] = line 772 vim.current.window.cursor = cursor 773 774 775@_check_jedi_availability(show_error=False) 776@catch_and_print_exceptions 777def show_call_signatures(signatures=()): 778 if int(vim_eval("has('conceal') && g:jedi#show_call_signatures")) == 0: 779 return 780 781 # We need to clear the signatures before we calculate them again. The 782 # reason for this is that call signatures are unfortunately written to the 783 # buffer. 784 clear_call_signatures() 785 if signatures == (): 786 signatures = get_script().call_signatures() 787 788 if not signatures: 789 return 790 791 if int(vim_eval("g:jedi#show_call_signatures")) == 2: 792 return cmdline_call_signatures(signatures) 793 794 seen_sigs = [] 795 for i, signature in enumerate(signatures): 796 line, column = signature.bracket_start 797 # signatures are listed above each other 798 line_to_replace = line - i - 1 799 # because there's a space before the bracket 800 insert_column = column - 1 801 if insert_column < 0 or line_to_replace <= 0: 802 # Edge cases, when the call signature has no space on the screen. 803 break 804 805 # TODO check if completion menu is above or below 806 line = vim_eval("getline(%s)" % line_to_replace) 807 808 # Descriptions are usually looking like `param name`, remove the param. 809 params = [p.description.replace('\n', '').replace('param ', '', 1) 810 for p in signature.params] 811 try: 812 # *_*PLACEHOLDER*_* makes something fat. See after/syntax file. 813 params[signature.index] = '*_*%s*_*' % params[signature.index] 814 except (IndexError, TypeError): 815 pass 816 817 # Skip duplicates. 818 if params in seen_sigs: 819 continue 820 seen_sigs.append(params) 821 822 # This stuff is reaaaaally a hack! I cannot stress enough, that 823 # this is a stupid solution. But there is really no other yet. 824 # There is no possibility in VIM to draw on the screen, but there 825 # will be one (see :help todo Patch to access screen under Python. 826 # (Marko Mahni, 2010 Jul 18)) 827 text = " (%s) " % ', '.join(params) 828 text = ' ' * (insert_column - len(line)) + text 829 end_column = insert_column + len(text) - 2 # -2 due to bold symbols 830 831 # Need to decode it with utf8, because vim returns always a python 2 832 # string even if it is unicode. 833 e = vim_eval('g:jedi#call_signature_escape') 834 if hasattr(e, 'decode'): 835 e = e.decode('UTF-8') 836 # replace line before with cursor 837 regex = "xjedi=%sx%sxjedix".replace('x', e) 838 839 prefix, replace = line[:insert_column], line[insert_column:end_column] 840 841 # Check the replace stuff for strings, to append them 842 # (don't want to break the syntax) 843 regex_quotes = r'''\\*["']+''' 844 # `add` are all the quotation marks. 845 # join them with a space to avoid producing ''' 846 add = ' '.join(re.findall(regex_quotes, replace)) 847 # search backwards 848 if add and replace[0] in ['"', "'"]: 849 a = re.search(regex_quotes + '$', prefix) 850 add = ('' if a is None else a.group(0)) + add 851 852 tup = '%s, %s' % (len(add), replace) 853 repl = prefix + (regex % (tup, text)) + add + line[end_column:] 854 855 vim_eval('setline(%s, %s)' % (line_to_replace, repr(PythonToVimStr(repl)))) 856 857 858@catch_and_print_exceptions 859def cmdline_call_signatures(signatures): 860 def get_params(s): 861 return [p.description.replace('\n', '').replace('param ', '', 1) for p in s.params] 862 863 def escape(string): 864 return string.replace('"', '\\"').replace(r'\n', r'\\n') 865 866 def join(): 867 return ', '.join(filter(None, (left, center, right))) 868 869 def too_long(): 870 return len(join()) > max_msg_len 871 872 if len(signatures) > 1: 873 params = zip_longest(*map(get_params, signatures), fillvalue='_') 874 params = ['(' + ', '.join(p) + ')' for p in params] 875 else: 876 params = get_params(signatures[0]) 877 878 index = next(iter(s.index for s in signatures if s.index is not None), None) 879 880 # Allow 12 characters for showcmd plus 18 for ruler - setting 881 # noruler/noshowcmd here causes incorrect undo history 882 max_msg_len = int(vim_eval('&columns')) - 12 883 if int(vim_eval('&ruler')): 884 max_msg_len -= 18 885 max_msg_len -= len(signatures[0].name) + 2 # call name + parentheses 886 887 if max_msg_len < (1 if params else 0): 888 return 889 elif index is None: 890 text = escape(', '.join(params)) 891 if params and len(text) > max_msg_len: 892 text = ELLIPSIS 893 elif max_msg_len < len(ELLIPSIS): 894 return 895 else: 896 left = escape(', '.join(params[:index])) 897 center = escape(params[index]) 898 right = escape(', '.join(params[index + 1:])) 899 while too_long(): 900 if left and left != ELLIPSIS: 901 left = ELLIPSIS 902 continue 903 if right and right != ELLIPSIS: 904 right = ELLIPSIS 905 continue 906 if (left or right) and center != ELLIPSIS: 907 left = right = None 908 center = ELLIPSIS 909 continue 910 if too_long(): 911 # Should never reach here 912 return 913 914 max_num_spaces = max_msg_len 915 if index is not None: 916 max_num_spaces -= len(join()) 917 _, column = signatures[0].bracket_start 918 spaces = min(int(vim_eval('g:jedi#first_col +' 919 'wincol() - col(".")')) + 920 column - len(signatures[0].name), 921 max_num_spaces) * ' ' 922 923 if index is not None: 924 vim_command(' echon "%s" | ' 925 'echohl Function | echon "%s" | ' 926 'echohl None | echon "(" | ' 927 'echohl jediFunction | echon "%s" | ' 928 'echohl jediFat | echon "%s" | ' 929 'echohl jediFunction | echon "%s" | ' 930 'echohl None | echon ")"' 931 % (spaces, signatures[0].name, 932 left + ', ' if left else '', 933 center, ', ' + right if right else '')) 934 else: 935 vim_command(' echon "%s" | ' 936 'echohl Function | echon "%s" | ' 937 'echohl None | echon "(%s)"' 938 % (spaces, signatures[0].name, text)) 939 940 941@_check_jedi_availability(show_error=True) 942@catch_and_print_exceptions 943def rename(): 944 if not int(vim.eval('a:0')): 945 # Need to save the cursor position before insert mode 946 cursor = vim.current.window.cursor 947 changenr = vim.eval('changenr()') # track undo tree 948 vim_command('augroup jedi_rename') 949 vim_command('autocmd InsertLeave <buffer> call jedi#rename' 950 '({}, {}, {})'.format(cursor[0], cursor[1], changenr)) 951 vim_command('augroup END') 952 953 vim_command("let s:jedi_replace_orig = expand('<cword>')") 954 line = vim_eval('getline(".")') 955 vim_command('normal! diw') 956 if re.match(r'\w+$', line[cursor[1]:]): 957 # In case the deleted word is at the end of the line we need to 958 # move the cursor to the end. 959 vim_command('startinsert!') 960 else: 961 vim_command('startinsert') 962 963 else: 964 # Remove autocommand. 965 vim_command('autocmd! jedi_rename InsertLeave') 966 967 args = vim.eval('a:000') 968 cursor = tuple(int(x) for x in args[:2]) 969 changenr = args[2] 970 971 # Get replacement, if there is something on the cursor. 972 # This won't be the case when the user ends insert mode right away, 973 # and `<cword>` would pick up the nearest word instead. 974 if vim_eval('getline(".")[getpos(".")[2]-1]') != ' ': 975 replace = vim_eval("expand('<cword>')") 976 else: 977 replace = None 978 979 vim_command('undo {}'.format(changenr)) 980 981 vim.current.window.cursor = cursor 982 983 if replace: 984 return do_rename(replace) 985 986 987def rename_visual(): 988 replace = vim.eval('input("Rename to: ")') 989 orig = vim.eval('getline(".")[(getpos("\'<")[2]-1):getpos("\'>")[2]]') 990 do_rename(replace, orig) 991 992 993def do_rename(replace, orig=None): 994 if not len(replace): 995 echo_highlight('No rename possible without name.') 996 return 997 998 if orig is None: 999 orig = vim_eval('s:jedi_replace_orig') 1000 1001 # Save original window / tab. 1002 saved_tab = int(vim_eval('tabpagenr()')) 1003 saved_win = int(vim_eval('winnr()')) 1004 1005 temp_rename = usages(visuals=False) 1006 # Sort the whole thing reverse (positions at the end of the line 1007 # must be first, because they move the stuff before the position). 1008 temp_rename = sorted(temp_rename, reverse=True, 1009 key=lambda x: (x.module_path, x.line, x.column)) 1010 buffers = set() 1011 for r in temp_rename: 1012 if r.in_builtin_module(): 1013 continue 1014 1015 if os.path.abspath(vim.current.buffer.name) != r.module_path: 1016 assert r.module_path is not None 1017 result = new_buffer(r.module_path) 1018 if not result: 1019 echo_highlight('Failed to create buffer window for %s!' % ( 1020 r.module_path)) 1021 continue 1022 1023 buffers.add(vim.current.buffer.name) 1024 1025 # Replace original word. 1026 r_line = vim.current.buffer[r.line - 1] 1027 vim.current.buffer[r.line - 1] = (r_line[:r.column] + replace + 1028 r_line[r.column + len(orig):]) 1029 1030 # Restore previous tab and window. 1031 vim_command('tabnext {0:d}'.format(saved_tab)) 1032 vim_command('{0:d}wincmd w'.format(saved_win)) 1033 1034 if len(buffers) > 1: 1035 echo_highlight('Jedi did {0:d} renames in {1:d} buffers!'.format( 1036 len(temp_rename), len(buffers))) 1037 else: 1038 echo_highlight('Jedi did {0:d} renames!'.format(len(temp_rename))) 1039 1040 1041@_check_jedi_availability(show_error=True) 1042@catch_and_print_exceptions 1043def py_import(): 1044 # args are the same as for the :edit command 1045 args = shsplit(vim.eval('a:args')) 1046 import_path = args.pop() 1047 text = 'import %s' % import_path 1048 scr = jedi.Script(text, 1, len(text), '', environment=get_environment()) 1049 try: 1050 completion = scr.goto_assignments()[0] 1051 except IndexError: 1052 echo_highlight('Cannot find %s in sys.path!' % import_path) 1053 else: 1054 if completion.column is None: # Python modules always have a line number. 1055 echo_highlight('%s is a builtin module.' % import_path) 1056 else: 1057 cmd_args = ' '.join([a.replace(' ', '\\ ') for a in args]) 1058 new_buffer(completion.module_path, cmd_args) 1059 1060 1061@catch_and_print_exceptions 1062def py_import_completions(): 1063 argl = vim.eval('a:argl') 1064 try: 1065 import jedi 1066 except ImportError: 1067 print('Pyimport completion requires jedi module: https://github.com/davidhalter/jedi') 1068 comps = [] 1069 else: 1070 text = 'import %s' % argl 1071 script = jedi.Script(text, 1, len(text), '', environment=get_environment()) 1072 comps = ['%s%s' % (argl, c.complete) for c in script.completions()] 1073 vim.command("return '%s'" % '\n'.join(comps)) 1074 1075 1076@catch_and_print_exceptions 1077def new_buffer(path, options='', using_tagstack=False): 1078 # options are what you can to edit the edit options 1079 if int(vim_eval('g:jedi#use_tabs_not_buffers')) == 1: 1080 _tabnew(path, options) 1081 elif not vim_eval('g:jedi#use_splits_not_buffers') in [1, '1']: 1082 user_split_option = vim_eval('g:jedi#use_splits_not_buffers') 1083 split_options = { 1084 'top': 'topleft split', 1085 'left': 'topleft vsplit', 1086 'right': 'botright vsplit', 1087 'bottom': 'botright split', 1088 'winwidth': 'vs' 1089 } 1090 if (user_split_option == 'winwidth' and 1091 vim.current.window.width <= 2 * int(vim_eval( 1092 "&textwidth ? &textwidth : 80"))): 1093 split_options['winwidth'] = 'sp' 1094 if user_split_option not in split_options: 1095 print('Unsupported value for g:jedi#use_splits_not_buffers: {0}. ' 1096 'Valid options are: {1}.'.format( 1097 user_split_option, ', '.join(split_options.keys()))) 1098 else: 1099 vim_command(split_options[user_split_option] + " %s" % escape_file_path(path)) 1100 else: 1101 if int(vim_eval("!&hidden && &modified")) == 1: 1102 if not vim_eval("bufname('%')"): 1103 echo_highlight('Cannot open a new buffer, use `:set hidden` or save your buffer') 1104 return False 1105 else: 1106 vim_command('w') 1107 if using_tagstack: 1108 return True 1109 vim_command('edit %s %s' % (options, escape_file_path(path))) 1110 # sometimes syntax is being disabled and the filetype not set. 1111 if int(vim_eval('!exists("g:syntax_on")')) == 1: 1112 vim_command('syntax enable') 1113 if int(vim_eval("&filetype != 'python'")) == 1: 1114 vim_command('set filetype=python') 1115 return True 1116 1117 1118@catch_and_print_exceptions 1119def _tabnew(path, options=''): 1120 """ 1121 Open a file in a new tab or switch to an existing one. 1122 1123 :param options: `:tabnew` options, read vim help. 1124 """ 1125 path = os.path.abspath(path) 1126 if int(vim_eval('has("gui")')) == 1: 1127 vim_command('tab drop %s %s' % (options, escape_file_path(path))) 1128 return 1129 1130 for tab_nr in range(int(vim_eval("tabpagenr('$')"))): 1131 for buf_nr in vim_eval("tabpagebuflist(%i + 1)" % tab_nr): 1132 buf_nr = int(buf_nr) - 1 1133 try: 1134 buf_path = vim.buffers[buf_nr].name 1135 except (LookupError, ValueError): 1136 # Just do good old asking for forgiveness. 1137 # don't know why this happens :-) 1138 pass 1139 else: 1140 if buf_path == path: 1141 # tab exists, just switch to that tab 1142 vim_command('tabfirst | tabnext %i' % (tab_nr + 1)) 1143 # Goto the buffer's window. 1144 vim_command('exec bufwinnr(%i) . " wincmd w"' % (buf_nr + 1)) 1145 break 1146 else: 1147 continue 1148 break 1149 else: 1150 # tab doesn't exist, add a new one. 1151 vim_command('tabnew %s' % escape_file_path(path)) 1152 1153 1154def escape_file_path(path): 1155 return path.replace(' ', r'\ ') 1156 1157 1158def print_to_stdout(level, str_out): 1159 print(str_out)