From c64da0589028ab1fc7fa82644b2d2303b33d002e Mon Sep 17 00:00:00 2001 From: maralla Date: Thu, 22 Aug 2024 19:36:38 +0800 Subject: [PATCH] Support call hierarchy --- autoload/completor/action.vim | 11 ++++ autoload/completor/popup.vim | 14 +++++ pythonx/completers/lsp/__init__.py | 89 +++++++++++++++++++++++++++++- pythonx/completers/lsp/models.py | 20 +++++++ pythonx/completers/lsp/utils.py | 9 +++ 5 files changed, 140 insertions(+), 3 deletions(-) diff --git a/autoload/completor/action.vim b/autoload/completor/action.vim index 7d2c841..14c5b38 100644 --- a/autoload/completor/action.vim +++ b/autoload/completor/action.vim @@ -324,11 +324,13 @@ function! completor#action#trigger(items) let items = a:items let action = s:action let ft = 'markdown' + let do_method = '' if type(a:items) == v:t_dict let items = a:items.data let action = get(a:items, 'action', action) let ft = get(a:items, 'ft', ft) + let do_method = get(a:items, 'method', '') endif if action ==# 'complete' @@ -361,6 +363,15 @@ function! completor#action#trigger(items) echo "popup window not supported" endif endif + elseif action ==# 'do' + if empty(do_method) + return + endif + let arg = '' + if !empty(items) + let arg = items[0] + endif + call completor#do(do_method, arg) elseif action ==# 'hover' if !empty(items) if completor#support_popup() diff --git a/autoload/completor/popup.vim b/autoload/completor/popup.vim index c90fc64..1ec4356 100644 --- a/autoload/completor/popup.vim +++ b/autoload/completor/popup.vim @@ -461,6 +461,19 @@ func s:select_filter(id, key) endfunc +func s:select_preview_filter(id, key) + if a:key == "j" + call win_execute(a:id, "normal! \") + return 1 + elseif a:key == "k" + call win_execute(a:id, "normal! \") + return 1 + endif + + return 0 +endfunc + + let s:is_selector_content_shown = v:false let s:selector_content = -1 func s:select_preview(id, type, item) @@ -473,6 +486,7 @@ func s:select_preview(id, type, item) \ mapping: 0, \ scrollbar: 0, \ line: 1, + \ filter: function('s:select_preview_filter'), \ maxheight: height, \ minheight: height, \ minwidth: &columns - 10, diff --git a/pythonx/completers/lsp/__init__.py b/pythonx/completers/lsp/__init__.py index 0197559..d4ccd5d 100644 --- a/pythonx/completers/lsp/__init__.py +++ b/pythonx/completers/lsp/__init__.py @@ -6,17 +6,18 @@ import os import json import io -from completor import Completor, vim, get +from completor import Completor, vim from completor.compat import to_unicode from . import ext from .models import Initialize, DidOpen, Completion, DidChange, DidSave, \ Definition, Format, Rename, Hover, Initialized, Implementation, \ References, DidChangeConfiguration, Symbol, Signature, DocumentSymbol, \ - CodeAction, CodeResolve + CodeAction, CodeResolve, PrepareCallHierarchy, IncomingCalls, \ + OutgoingCalls from .action import gen_jump_list, get_completion_word, gen_hover_doc, \ filter_items, parse_symbols, rename -from .utils import gen_uri +from .utils import gen_uri, to_filename logger = logging.getLogger('completor') @@ -30,6 +31,7 @@ def __init__(self, *args, **kwargs): self.server_cmd = None self.initialized = False self.current_id = None + self.current_request_args = None self.current_options = None self.is_ext = False self.open_file_map = {} @@ -134,6 +136,25 @@ def code_action(self, action): self.current_id = req_id return req + def prepare_call_hierarchy(self, args): + if not args: + return '' + req = self.position_request(PrepareCallHierarchy) + self.current_request_args = args + return req + + def call_hierarchy(self, data, call): + try: + item = json.loads(data) + except Exception as e: + logger.exception(e) + return '' + + c = call(item) + req_id, req = c.to_request() + self.current_id = req_id + return req + def code_action_callback(self, data): if not data: return '' @@ -220,6 +241,19 @@ def _gen_action_args(self, action, args): if action == b'hover': return self.position_request(Hover) + if action == b'prepare_call_hierarchy': + return self.prepare_call_hierarchy(args) + + if action == b'incoming_calls': + if not args: + return '' + return self.call_hierarchy(args[0], IncomingCalls) + + if action == b'outgoing_calls': + if not args: + return '' + return self.call_hierarchy(args[0], OutgoingCalls) + handler = ext.get_action_handler(action, self.ft_orig) if handler: self.is_ext = True @@ -229,6 +263,7 @@ def _gen_action_args(self, action, args): return '' def gen_request(self, action, args): + self.current_request_args = None self.current_options = {} if args: last = args[-1] @@ -453,6 +488,54 @@ def on_hover(self, data): return [gen_hover_doc(self.ft_orig, '\n\n'.join(values))] + def on_prepare_call_hierarchy(self, data): + logger.info("prepare_call_hierarchy -> %r", data) + if not data: + return [] + + if not self.current_request_args: + return [] + + method = self.current_request_args[0] + + items = data[0] + if not items: + return [] + + item = items[0] + + try: + data = json.dumps(item) + except Exception as e: + logger.exception(e) + return [] + + return vim.Dictionary(data=[data], action='do', method=method) + + def on_incoming_calls(self, data, direction='from'): + logger.info("incoming_calls -> %r", data) + if not data: + return [] + + res = [] + for item in data[0]: + data = item[direction] + if direction == 'from': + start = item['fromRanges'][0]['start'] + else: + start = data['selectionRange']['start'] + res.append({ + 'filename': to_filename(data['uri']), + 'lnum': start['line'] + 1, + 'col': start['character'] + 1, + 'name': data['name'], + }) + + return vim.Dictionary(data=res, action='references') + + def on_outgoing_calls(self, data): + return self.on_incoming_calls(data, direction='to') + def on_lsp_ext(self, data): if not data or len(data) < 3: return [] diff --git a/pythonx/completers/lsp/models.py b/pythonx/completers/lsp/models.py index 1ca72c3..349929c 100644 --- a/pythonx/completers/lsp/models.py +++ b/pythonx/completers/lsp/models.py @@ -314,3 +314,23 @@ def to_dict(self): return { "query": self.query, } + + +class PrepareCallHierarchy(Completion): + method = "textDocument/prepareCallHierarchy" + + +class IncomingCalls(Base): + method = "callHierarchy/incomingCalls" + + def __init__(self, item): + self.item = item + + def to_dict(self): + return { + "item": self.item + } + + +class OutgoingCalls(IncomingCalls): + method = "callHierarchy/outgoingCalls" diff --git a/pythonx/completers/lsp/utils.py b/pythonx/completers/lsp/utils.py index c83c1ff..a7c3f84 100644 --- a/pythonx/completers/lsp/utils.py +++ b/pythonx/completers/lsp/utils.py @@ -1,5 +1,10 @@ # -*- coding: utf-8 -*- +try: + from urlparse import unquote +except ImportError: + from urllib.parse import unquote + def gen_uri(path): return 'file://' + path @@ -9,3 +14,7 @@ def parse_uri(uri): if uri.startswith('file://'): return uri[7:] return uri + + +def to_filename(uri): + return unquote(parse_uri(uri))