Skip to content
Open
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions plugin/completion/lib_complete.py
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,9 @@ def info(self, tooltip_request, settings):
info_popup = Popup.info(
cursor.referenced, self.cindex, settings)
return tooltip_request, info_popup
if cursor.kind == self.cindex.CursorKind.MACRO_DEFINITION:
info_popup = Popup.info(cursor, self.cindex, settings)
return tooltip_request, info_popup
return empty_info

def update(self, view, settings):
Expand Down
59 changes: 46 additions & 13 deletions plugin/error_vis/popups.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@
{content}
"""

FULL_DOC_TEMPLATE = """### Full doxygen comment:
FULL_DOC_TEMPLATE = """### Detailed documentation:

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This right here is an aesthetic change, which you are free to keep or throw out - I think it is more consistent since it is more similar to the title of the other related section, "Brief documentation".

{content}
"""

Expand Down Expand Up @@ -105,6 +105,13 @@ def info(cursor, cindex, settings):
]
is_macro = cursor.kind == cindex.CursorKind.MACRO_DEFINITION
is_class_template = cursor.kind == cindex.CursorKind.CLASS_TEMPLATE
is_function = cursor.kind in [
cindex.CursorKind.FUNCTION_DECL,
cindex.CursorKind.CXX_METHOD,
cindex.CursorKind.CONSTRUCTOR,
cindex.CursorKind.DESTRUCTOR,
cindex.CursorKind.CONVERSION_FUNCTION,
cindex.CursorKind.FUNCTION_TEMPLATE]

# Show the return type of the function/method if applicable,
# macros just show that they are a macro.
Expand Down Expand Up @@ -156,12 +163,9 @@ def info(cursor, cindex, settings):
args.append(arg_type_decl + " " + arg.spelling)
else:
args.append(arg_type_decl)
if cursor.kind in [cindex.CursorKind.FUNCTION_DECL,
cindex.CursorKind.CXX_METHOD,
cindex.CursorKind.CONSTRUCTOR,
cindex.CursorKind.DESTRUCTOR,
cindex.CursorKind.CONVERSION_FUNCTION,
cindex.CursorKind.FUNCTION_TEMPLATE]:
if is_function:
if cursor.type is not None and cursor.type.is_function_variadic():
args.append("...")
args_string = '('
if len(args):
args_string += ', '.join(args)
Expand All @@ -183,12 +187,18 @@ def info(cursor, cindex, settings):
popup.__text += Popup.__lookup_in_sublime_index(
sublime.active_window(), cursor.spelling)

# Doxygen comments
if cursor.brief_comment:
raw_comment = None
if is_macro:
raw_comment = macro_parser.doc_string
else:
raw_comment = cursor.raw_comment
# Doxygen comment: single-line brief description
if raw_comment and cursor.brief_comment:
popup.__text += BRIEF_DOC_TEMPLATE.format(
content=CODE_TEMPLATE.format(lang="",
code=cursor.brief_comment))
if cursor.raw_comment:
# Doxygen comment: multi-line detailed description
if raw_comment and cursor.raw_comment:
clean_comment = Popup.cleanup_comment(cursor.raw_comment).strip()
print(clean_comment)
if clean_comment:
Expand All @@ -197,11 +207,34 @@ def info(cursor, cindex, settings):
content=CODE_TEMPLATE.format(lang="", code=clean_comment))
# Show macro body
if is_macro:
body = "#define "
body += cursor.spelling
body += macro_parser.args_string if len(macro_parser.args_string) else " "
body += macro_parser.body_string
popup.__text += BODY_TEMPLATE.format(
content=CODE_TEMPLATE.format(lang="c++", code=body))
# Show function declaration
elif is_function:
body = cursor.result_type.spelling
body += " "
body += cursor.spelling
args = []
for arg in cursor.get_arguments():
if arg.spelling:
args.append(arg.type.spelling + " " + arg.spelling)
else:
args.append(arg.type.spelling)
if cursor.type is not None and cursor.type.is_function_variadic():
args.append("...")
body += '('
if len(args):
body += ', '.join(args)
body += ');'
body = Popup.prettify_body(body)
popup.__text += BODY_TEMPLATE.format(
content=CODE_TEMPLATE.format(lang="c++",
code=macro_parser.body_string))
content=CODE_TEMPLATE.format(lang="c++", code=body))
# Show type declaration
if settings.show_type_body and body_cursor and body_cursor.extent:
elif settings.show_type_body and body_cursor and body_cursor.extent:
body = Popup.get_text_by_extent(body_cursor.extent)
body = Popup.prettify_body(body)
popup.__text += BODY_TEMPLATE.format(
Expand Down
66 changes: 66 additions & 0 deletions plugin/utils/macro_parser.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
"""Parse a macro from cindex."""

import re
import logging

log = logging.getLogger("ECC")

class MacroParser(object):
"""Parse info from macros.
Expand All @@ -21,6 +25,7 @@ def __init__(self, name, location):
in a macro with parenthesis, continue parsing into the next line
to find it and create a proper args string.
"""
self._raw_comment = ''
self._args_string = ''
self._name = name
self._body = ''
Expand All @@ -38,6 +43,52 @@ def _parse_macro_file_lines(self, macro_file_lines, macro_line_number):
macro_line_number (int): line number (1-based) of the macro
in macro_file_lines.
"""
# parse doxygen comment above macro definition
self._raw_comment = ""
parser_state = 0
# (parser_state == 0) -> no comment encountered yet
# (parser_state == 1) -> single-line comment encountered
# (parser_state == 2) -> multi-line comment encountered
# (parser_state == 3) -> comment found, finished
lineno = macro_line_number - 1
while (lineno > 0):
# parse the preceding line of text
lineno -= 1
if (lineno == 0):
break
prevline = macro_file_lines[lineno].lstrip()
# skip any `#if`/`#ifdef` guards before the macro `#define` line, if applicable
if re.match(r'^[ \t]*#[ \t]*(if|elif|else|ifn?def)[ \t]+', prevline):
continue
# parse single-line comments
if (parser_state != 2) and re.match(r'^\s*//', prevline):
parser_state = 1
if (re.match(r'^\s*//!', prevline) or re.match(r'^\s*///', prevline)):
self._raw_comment = prevline + "\n" + self._raw_comment
else:
parser_state = 3
log.debug("Error while parsing macro doc comment: found normal single-line comment: " + self._raw_comment)
parser_state = 0 if len(self._raw_comment) == 0 else 3
continue
# parse multi-line comments
if (parser_state == 2):
if re.match(r'^\s*/\*', prevline):
if re.match(r'^\s*/\*[\*!]', prevline):
self._raw_comment = prevline + "\n" + self._raw_comment
else:
log.debug("Error while parsing macro doc comment: found normal multi-line comment: " + self._raw_comment)
parser_state = 0 if len(self._raw_comment) == 0 else 3
else:
self._raw_comment = prevline + "\n" + self._raw_comment
continue
elif re.match(r'^\s*\*/', prevline):
self._raw_comment = prevline + "\n" + self._raw_comment
parser_state = 2
continue
if (len(prevline) > 0):
log.debug("Error while parsing macro doc comment: found " + prevline)
break

macro_line = macro_file_lines[macro_line_number - 1].strip()
# strip leading '#<whitespace>define<whitespace><macro name>'
macro_line = macro_line.lstrip('#').lstrip().lstrip('define')
Expand Down Expand Up @@ -79,3 +130,18 @@ def args_string(self):
def body_string(self):
"""Get macro body string."""
return self._body

@property
def doc_string(self):
"""Get documentation comment string.

This follows conventional doxygen syntax, so your comment can use any of these syntaxes:
- /** doc comment (Java style) */
- /// doc comment (C# style)
- //! doc comment (Qt style), single-line
- /*! doc comment (Qt style), block */
This means that the following comment syntaxes are NOT valid documentation comments:
- // normal single-line comment
- /* normal block comment */
"""
return self._raw_comment