Skip to content

Commit

Permalink
More and improved Objective-C popup info (#430)
Browse files Browse the repository at this point in the history
* Fix cindex's Cursor.result_type calling wrong libclang functions

Should call clang_getCursorResultType() instead of clang_getResultType().
https://clang.llvm.org/doxygen/group__CINDEX__TYPES.html#ga6995a2d6352e7136868574b299005a63

* ObjC message expression declarations look more ObjC-ish

Instead of "Foo* methodThatReturnsFoo", use that actual notation of
message definitions with + or - for instance/class methods and
parentheses, e.g. "-(Foo *)methodThatReturnsFoo".

* Show ObjC popup info for category/protocol/interface method declarations

Hovering over the declarations of methods, not just message
expressions that invoke the methodss, will show popup info.

* Show ObjC popup info for more types

Popups for Protocol, Category, and Interface declarations, references,
and implementations. Show type body (like is done for C++ class/struct)
for these if possible.

* Revert "Fix cindex's Cursor.result_type calling wrong libclang functions"

This reverts commit 89d46e8.

Also disable unittests that will now fail.
  • Loading branch information
kjteske authored and niosus committed Mar 23, 2018
1 parent 068b4c6 commit 01c4729
Show file tree
Hide file tree
Showing 4 changed files with 383 additions and 91 deletions.
16 changes: 14 additions & 2 deletions plugin/completion/lib_complete.py
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,18 @@ def info(self, tooltip_request, settings):
info details read from the translation unit.
"""
objc_types = [
self.cindex.CursorKind.OBJC_MESSAGE_EXPR,
self.cindex.CursorKind.OBJC_CLASS_METHOD_DECL,
self.cindex.CursorKind.OBJC_INSTANCE_METHOD_DECL,
self.cindex.CursorKind.OBJC_CATEGORY_DECL,
self.cindex.CursorKind.OBJC_INTERFACE_DECL,
self.cindex.CursorKind.OBJC_PROTOCOL_DECL,
self.cindex.CursorKind.OBJC_CATEGORY_IMPL_DECL,
self.cindex.CursorKind.OBJC_IMPLEMENTATION_DECL,
self.cindex.CursorKind.OBJC_CLASS_REF,
self.cindex.CursorKind.OBJC_PROTOCOL_REF,
]
empty_info = (tooltip_request, None)
with Completer.rlock:
if not self.tu:
Expand All @@ -269,8 +281,8 @@ def info(self, tooltip_request, settings):
self.tu, self.tu.get_location(view.file_name(), (row, col)))
if not cursor:
return empty_info
if cursor.kind == self.cindex.CursorKind.OBJC_MESSAGE_EXPR:
info_popup = Popup.info_objc(cursor)
if cursor.kind in objc_types:
info_popup = Popup.info_objc(cursor, self.cindex, settings)
return tooltip_request, info_popup
if cursor.referenced:
info_popup = Popup.info(
Expand Down
180 changes: 112 additions & 68 deletions plugin/popups/popups.py
Original file line number Diff line number Diff line change
Expand Up @@ -295,82 +295,126 @@ def prettify_body(body):

return body

@staticmethod
def info_objc(cursor):
"""Provide information about cursor to Objective C message expression.
Builds detailed information about cursor when cursor is
a CursorKind.OBJC_MESSAGE_EXPR. OBJC_MESSAGE_EXPR cursors
behave very differently from other C/C++ cursors in that:
- The return type we want to show in the tooltip
is stored in the original 'cursor.type' from the cursor the user is
hovering over; in C/C++ we only used 'cursor.referenced' but nothing
else from the original cursor.
- 'cursor.referenced' is still important, as it holds the name and args
of the method being called in the message. But
'cursor.referenced.spelling' comes in a different format then what
For example, if we have this method declaration for 'bar':
@interface Foo
-(void)bar:(BOOL)b1 boolParam2:(BOOL):b2
@end
And later, we hover over the text calling bar():
Foo* foo = [[Foo alloc] init];
[foo bar:YES boolParam2:NO]; // <- Hover over 'bar' here
Then we would see:
cursor.kind = CursorKind.OBJC_INSTANCE_METHOD_DECL
cursor.type.spelling = 'void'
cursor.referenced.kind: CursorKind.OBJC_INSTANCE_METHOD_DECL
cursor.referenced.spelling = 'bar:boolParam2:'
cursor.referenced.arguments[0].type.spelling = 'BOOL'
cursor.referenced.arguments[0].spelling = 'b1'
cursor.referenced.arguments[1].spelling = 'BOOL'
cursor.referenced.arguments[1].spelling = 'b2'
Our goal is to make the tooltip match the method declaration:
'void bar:(BOOL)b1 boolParam2:(BOOL):b2'
- Objective C methods also don't need to worry about static/const
Args:
cursor (Cursor): Current cursor.
"""
def info_objc(cursor, cindex, settings):
"""Provide information about Objective C cursors."""
popup = Popup()
popup.__popup_type = 'panel-info "ECC: Info"'
# Type declaration.
is_message = cursor.kind in [
cindex.CursorKind.OBJC_MESSAGE_EXPR,
]
is_method_decl = cursor.kind in [
cindex.CursorKind.OBJC_CLASS_METHOD_DECL,
cindex.CursorKind.OBJC_INSTANCE_METHOD_DECL,
]
is_type_decl = cursor.kind in [
cindex.CursorKind.OBJC_CATEGORY_DECL,
cindex.CursorKind.OBJC_INTERFACE_DECL,
cindex.CursorKind.OBJC_PROTOCOL_DECL,
]
is_type_impl = cursor.kind in [
cindex.CursorKind.OBJC_CATEGORY_IMPL_DECL,
cindex.CursorKind.OBJC_IMPLEMENTATION_DECL,
]
is_type_ref = cursor.kind in [
cindex.CursorKind.OBJC_CLASS_REF,
cindex.CursorKind.OBJC_PROTOCOL_REF,
]
comment_cursor = None
type_body_cursor = None
method_cursor = None
return_type = None
location_cursor = None
if is_message:
location_cursor = cursor
comment_cursor = cursor.referenced
method_cursor = cursor.referenced
return_type = cursor.type
elif is_method_decl:
location_cursor = cursor
comment_cursor = cursor.referenced
method_cursor = cursor.referenced
return_type = cursor.result_type
elif is_type_decl:
location_cursor = cursor
comment_cursor = cursor
type_body_cursor = cursor
elif is_type_impl:
location_cursor = cursor.canonical
comment_cursor = cursor.canonical
type_body_cursor = cursor.canonical
elif is_type_ref:
location_cursor = cursor
comment_cursor = cursor.referenced
type_body_cursor = cursor.referenced
location_cursor = cursor.referenced
else:
assert False, "Unexpected type"

declaration_text = ""
return_type = cursor.type
declaration_text += Popup.link_from_location(
Popup.location_from_type(return_type),
return_type.spelling)
declaration_text += ' '
# Method declaration.
method_cursor = cursor.referenced
method_and_params = method_cursor.spelling.split(':')
method_name = method_and_params[0]
if method_cursor.location:
if method_cursor:
# <+ or ->(<return type>)
method_kind = method_cursor.kind
if method_kind == cindex.CursorKind.OBJC_INSTANCE_METHOD_DECL:
declaration_text += "-("
elif method_kind == cindex.CursorKind.OBJC_CLASS_METHOD_DECL:
declaration_text += "+("
declaration_text += Popup.link_from_location(
method_cursor.location,
method_name,
Popup.location_from_type(return_type),
return_type.spelling or "",
trailing_space=False)
declaration_text += ')'

# <method name>
method_and_params = method_cursor.spelling.split(':')
method_name = method_and_params[0]
if method_cursor.location:
declaration_text += Popup.link_from_location(
method_cursor.location,
method_name,
trailing_space=False)
else:
declaration_text += method_cursor.spelling

# <args if they exist>
method_params_index = 1
for arg in method_cursor.get_arguments():
arg_type_location = Popup.location_from_type(arg.type)
arg_type_link = Popup.link_from_location(arg_type_location,
arg.type.spelling,
trailing_space=False)
declaration_text += ":(" + arg_type_link + ")"
if arg.spelling:
declaration_text += arg.spelling + " "
declaration_text += method_and_params[method_params_index]
method_params_index += 1
else:
declaration_text += method_cursor.spelling
# Params declaration.
method_params_index = 1
for arg in method_cursor.get_arguments():
arg_type_location = Popup.location_from_type(arg.type)
arg_type_link = Popup.link_from_location(arg_type_location,
arg.type.spelling,
trailing_space=False)

declaration_text += ":(" + arg_type_link + ")"
if arg.spelling:
declaration_text += arg.spelling + " "
declaration_text += method_and_params[method_params_index]
method_params_index += 1
# Set the popup text from declaration.
if location_cursor.location:
declaration_text += Popup.link_from_location(
location_cursor.location,
location_cursor.spelling)
else:
declaration_text += location_cursor.spelling
popup.__text = DECLARATION_TEMPLATE.format(
type_declaration=declaration_text)
# Brief comment.
if method_cursor.brief_comment:

if comment_cursor and comment_cursor.brief_comment:
popup.__text += BRIEF_DOC_TEMPLATE.format(
content=CODE_TEMPLATE.format(lang="",
code=method_cursor.brief_comment))
code=comment_cursor.brief_comment))
if comment_cursor and comment_cursor.raw_comment:
clean_comment = Popup.cleanup_comment(comment_cursor.raw_comment)
clean_comment = clean_comment.strip()
if clean_comment:
# Only add this if there is a Doxygen comment.
popup.__text += FULL_DOC_TEMPLATE.format(
content=CODE_TEMPLATE.format(lang="", code=clean_comment))

# Show type declaration
if type_body_cursor:
if settings.show_type_body and type_body_cursor.extent:
body = Popup.get_text_by_extent(type_body_cursor.extent)
popup.__text += BODY_TEMPLATE.format(
content=CODE_TEMPLATE.format(
lang="objective-c++",
code=body))
return popup
Loading

0 comments on commit 01c4729

Please sign in to comment.