Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

enh(cpp) Improve function declaration detection #2332

Merged
merged 5 commits into from Jan 15, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGES.md
Expand Up @@ -19,6 +19,10 @@ Language Improvements:
- (ini) support TOML arrays, clean up grammar (#2335) [Josh Goebel][]
- (vbnet) add nameof operator to the keywords (#2329) [Youssef Victor][]
- (stan) updated with improved coverage of language keywords and patterns. (#1829) [Jeffrey Arnold][]
- enh(cpp) Detect namespaced function types (`A::typeName func(...)`) (#2332) [Josh Goebel][]
- enh(cpp) Detect namespaced functions also (`A::functionName`) (#2332) [Josh Goebel][]
- enh(cpp) Properly detect decltype(auto) (#2332) [Josh Goebel][]
- enh(cpp) recognize primitive types (`int8_t`, etc.) as function types (#2332) [Josh Goebel][]

Developer Tools:

Expand Down
145 changes: 87 additions & 58 deletions src/languages/cpp.js
Expand Up @@ -7,6 +7,16 @@ Website: https://isocpp.org
*/

function(hljs) {
function optional(s) {
return '(?:' + s + ')?';
}
var DECLTYPE_AUTO_RE = 'decltype\\(auto\\)'
var NAMESPACE_RE = '[a-zA-Z_]\\w*::'
joshgoebel marked this conversation as resolved.
Show resolved Hide resolved
var TEMPLATE_ARGUMENT_RE = '<.*?>';
var FUNCTION_TYPE_RE = '(' +
DECLTYPE_AUTO_RE + '|' +
optional(NAMESPACE_RE) +'[a-zA-Z_]\\w*' + optional(TEMPLATE_ARGUMENT_RE) +
')';
var CPP_PRIMITIVE_TYPES = {
className: 'keyword',
begin: '\\b[a-z\\d_]*_t\\b'
Expand Down Expand Up @@ -64,7 +74,13 @@ function(hljs) {
]
};

var FUNCTION_TITLE = hljs.IDENT_RE + '\\s*\\(';
var TITLE_MODE = {
className: 'title',
begin: optional(NAMESPACE_RE) + hljs.IDENT_RE,
relevance: 0
};

var FUNCTION_TITLE = optional(NAMESPACE_RE) + hljs.IDENT_RE + '\\s*\\(';

var CPP_KEYWORDS = {
keyword: 'int float while private char char8_t char16_t char32_t catch import module export virtual operator sizeof ' +
Expand Down Expand Up @@ -99,86 +115,99 @@ function(hljs) {
STRINGS
];

return {
aliases: ['c', 'cc', 'h', 'c++', 'h++', 'hpp', 'hh', 'hxx', 'cxx'],
var EXPRESSION_CONTEXT = {
// This mode covers expression context where we can't expect a function
// definition and shouldn't highlight anything that looks like one:
// `return some()`, `else if()`, `(x*sum(1, 2))`
variants: [
{begin: /=/, end: /;/},
{begin: /\(/, end: /\)/},
{beginKeywords: 'new throw return else', end: /;/}
],
keywords: CPP_KEYWORDS,
illegal: '</',
contains: EXPRESSION_CONTAINS.concat([
PREPROCESSOR,
{
begin: '\\b(deque|list|queue|stack|vector|map|set|bitset|multiset|multimap|unordered_map|unordered_set|unordered_multiset|unordered_multimap|array)\\s*<', end: '>',
begin: /\(/, end: /\)/,
keywords: CPP_KEYWORDS,
contains: ['self', CPP_PRIMITIVE_TYPES]
},
{
begin: hljs.IDENT_RE + '::',
keywords: CPP_KEYWORDS
contains: EXPRESSION_CONTAINS.concat(['self']),
relevance: 0
}
]),
relevance: 0
};

var FUNCTION_DECLARATION = {
className: 'function',
begin: '(' + FUNCTION_TYPE_RE + '[\\*&\\s]+)+' + FUNCTION_TITLE,
returnBegin: true, end: /[{;=]/,
excludeEnd: true,
keywords: CPP_KEYWORDS,
illegal: /[^\w\s\*&:<>]/,
contains: [

{ // to prevent it from being confused as the function title
begin: DECLTYPE_AUTO_RE,
keywords: CPP_KEYWORDS,
relevance: 0,
},
{
// This mode covers expression context where we can't expect a function
// definition and shouldn't highlight anything that looks like one:
// `return some()`, `else if()`, `(x*sum(1, 2))`
variants: [
{begin: /=/, end: /;/},
{begin: /\(/, end: /\)/},
{beginKeywords: 'new throw return else', end: /;/}
],
keywords: CPP_KEYWORDS,
contains: EXPRESSION_CONTAINS.concat([
{
begin: /\(/, end: /\)/,
keywords: CPP_KEYWORDS,
contains: EXPRESSION_CONTAINS.concat(['self']),
relevance: 0
}
]),
begin: FUNCTION_TITLE, returnBegin: true,
contains: [TITLE_MODE],
relevance: 0
},
{
className: 'function',
begin: '(' + hljs.IDENT_RE + '[\\*&\\s]+)+' + FUNCTION_TITLE,
returnBegin: true, end: /[{;=]/,
excludeEnd: true,
className: 'params',
begin: /\(/, end: /\)/,
keywords: CPP_KEYWORDS,
illegal: /[^\w\s\*&]/,
relevance: 0,
contains: [
hljs.C_LINE_COMMENT_MODE,
hljs.C_BLOCK_COMMENT_MODE,
STRINGS,
NUMBERS,
CPP_PRIMITIVE_TYPES,
// Count matching parentheses.
{
begin: FUNCTION_TITLE, returnBegin: true,
contains: [hljs.TITLE_MODE],
relevance: 0
},
{
className: 'params',
begin: /\(/, end: /\)/,
keywords: CPP_KEYWORDS,
relevance: 0,
contains: [
'self',
hljs.C_LINE_COMMENT_MODE,
hljs.C_BLOCK_COMMENT_MODE,
STRINGS,
NUMBERS,
CPP_PRIMITIVE_TYPES,
// Count matching parentheses.
{
begin: /\(/, end: /\)/,
keywords: CPP_KEYWORDS,
relevance: 0,
contains: [
'self',
hljs.C_LINE_COMMENT_MODE,
hljs.C_BLOCK_COMMENT_MODE,
STRINGS,
NUMBERS,
CPP_PRIMITIVE_TYPES
]
}
CPP_PRIMITIVE_TYPES
]
},
hljs.C_LINE_COMMENT_MODE,
hljs.C_BLOCK_COMMENT_MODE,
PREPROCESSOR
}
]
},
CPP_PRIMITIVE_TYPES,
hljs.C_LINE_COMMENT_MODE,
hljs.C_BLOCK_COMMENT_MODE,
PREPROCESSOR
]
};

return {
aliases: ['c', 'cc', 'h', 'c++', 'h++', 'hpp', 'hh', 'hxx', 'cxx'],
keywords: CPP_KEYWORDS,
illegal: '</',
contains: [].concat(
EXPRESSION_CONTEXT,
FUNCTION_DECLARATION,
EXPRESSION_CONTAINS,
[
PREPROCESSOR,
{
begin: '\\b(deque|list|queue|stack|vector|map|set|bitset|multiset|multimap|unordered_map|unordered_set|unordered_multiset|unordered_multimap|array)\\s*<', end: '>',
keywords: CPP_KEYWORDS,
contains: ['self', CPP_PRIMITIVE_TYPES]
},
{
begin: hljs.IDENT_RE + '::',
keywords: CPP_KEYWORDS
},
{
className: 'class',
beginKeywords: 'class struct', end: /[{;:]/,
Expand Down
16 changes: 16 additions & 0 deletions test/markup/cpp/function-declarations.expect.txt
@@ -0,0 +1,16 @@
<span class="hljs-function"><span class="hljs-keyword">decltype</span>(<span class="hljs-keyword">auto</span>) <span class="hljs-title">look_up_a_string_1</span><span class="hljs-params">()</span> </span>{ <span class="hljs-keyword">return</span> lookup1(); }
<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">look_up_a_string_2</span><span class="hljs-params">()</span> </span>{ <span class="hljs-keyword">return</span> lookup2(); }
<span class="hljs-function"><span class="hljs-keyword">friend</span> <span class="hljs-keyword">void</span> <span class="hljs-title">A::showB</span><span class="hljs-params">(B x)</span> </span>{}
<span class="hljs-function"><span class="hljs-keyword">friend</span> <span class="hljs-keyword">void</span> <span class="hljs-title">showB</span><span class="hljs-params">(B x)</span> </span>{}
<span class="hljs-function"><span class="hljs-keyword">friend</span> <span class="hljs-keyword">void</span> <span class="hljs-title">showB</span><span class="hljs-params">(B::SomeType x)</span> </span>{}
<span class="hljs-function"><span class="hljs-keyword">inline</span> <span class="hljs-keyword">int</span> <span class="hljs-title">add</span><span class="hljs-params">(<span class="hljs-keyword">int</span> a, <span class="hljs-keyword">int</span> b)</span> </span>{}
<span class="hljs-function">int8t <span class="hljs-title">Get_Tile_Value</span><span class="hljs-params">()</span> </span>{}

<span class="hljs-function"><span class="hljs-keyword">int8_t</span> <span class="hljs-title">Get_Tile_Value</span><span class="hljs-params">()</span> </span>{}

<span class="hljs-function">B::type <span class="hljs-title">test</span><span class="hljs-params">()</span> </span>{};

<span class="hljs-comment">// template</span>
<span class="hljs-function">boost::optional&lt;application&gt; <span class="hljs-title">handle_key</span><span class="hljs-params">(application state, key_code key, coord size)</span></span>;

test();
16 changes: 16 additions & 0 deletions test/markup/cpp/function-declarations.txt
@@ -0,0 +1,16 @@
decltype(auto) look_up_a_string_1() { return lookup1(); }
void look_up_a_string_2() { return lookup2(); }
friend void A::showB(B x) {}
friend void showB(B x) {}
friend void showB(B::SomeType x) {}
inline int add(int a, int b) {}
int8t Get_Tile_Value() {}

int8_t Get_Tile_Value() {}

B::type test() {};

// template
boost::optional<application> handle_key(application state, key_code key, coord size);

test();