Skip to content

Commit e3fe904

Browse files
C++: Improved class name detection (#2348)
1 parent b609333 commit e3fe904

File tree

5 files changed

+286
-22
lines changed

5 files changed

+286
-22
lines changed

components/prism-cpp.js

+58-20
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,59 @@
1-
Prism.languages.cpp = Prism.languages.extend('c', {
2-
'class-name': {
3-
pattern: /(\b(?:class|enum|struct)\s+)(?!class|enum|struct)\w+/,
4-
lookbehind: true
5-
},
6-
'keyword': /\b(?:alignas|alignof|asm|auto|bool|break|case|catch|char|char8_t|char16_t|char32_t|class|compl|concept|const|consteval|constexpr|constinit|const_cast|continue|co_await|co_return|co_yield|decltype|default|delete|do|double|dynamic_cast|else|enum|explicit|export|extern|float|for|friend|goto|if|inline|int|int8_t|int16_t|int32_t|int64_t|uint8_t|uint16_t|uint32_t|uint64_t|long|mutable|namespace|new|noexcept|nullptr|operator|private|protected|public|register|reinterpret_cast|requires|return|short|signed|sizeof|static|static_assert|static_cast|struct|switch|template|this|thread_local|throw|try|typedef|typeid|typename|union|unsigned|using|virtual|void|volatile|wchar_t|while)\b/,
7-
'number': {
8-
pattern: /(?:\b0b[01']+|\b0x(?:[\da-f']+\.?[\da-f']*|\.[\da-f']+)(?:p[+-]?[\d']+)?|(?:\b[\d']+\.?[\d']*|\B\.[\d']+)(?:e[+-]?[\d']+)?)[ful]*/i,
9-
greedy: true
10-
},
11-
'operator': />>=?|<<=?|->|([-+&|:])\1|[?:~]|[-+*/%&|^!=<>]=?|\b(?:and|and_eq|bitand|bitor|not|not_eq|or|or_eq|xor|xor_eq)\b/,
12-
'boolean': /\b(?:true|false)\b/
13-
});
1+
(function (Prism) {
142

15-
Prism.languages.insertBefore('cpp', 'string', {
16-
'raw-string': {
17-
pattern: /R"([^()\\ ]{0,16})\([\s\S]*?\)\1"/,
18-
alias: 'string',
19-
greedy: true
20-
}
21-
});
3+
var keyword = /\b(?:alignas|alignof|asm|auto|bool|break|case|catch|char|char8_t|char16_t|char32_t|class|compl|concept|const|consteval|constexpr|constinit|const_cast|continue|co_await|co_return|co_yield|decltype|default|delete|do|double|dynamic_cast|else|enum|explicit|export|extern|float|for|friend|goto|if|inline|int|int8_t|int16_t|int32_t|int64_t|uint8_t|uint16_t|uint32_t|uint64_t|long|mutable|namespace|new|noexcept|nullptr|operator|private|protected|public|register|reinterpret_cast|requires|return|short|signed|sizeof|static|static_assert|static_cast|struct|switch|template|this|thread_local|throw|try|typedef|typeid|typename|union|unsigned|using|virtual|void|volatile|wchar_t|while)\b/;
4+
5+
Prism.languages.cpp = Prism.languages.extend('c', {
6+
'class-name': [
7+
{
8+
pattern: RegExp(/(\b(?:class|enum|struct|typename)\s+)(?!<keyword>)\w+/.source
9+
.replace(/<keyword>/g, function () { return keyword.source; })),
10+
lookbehind: true
11+
},
12+
// This is intended to capture the class name of method implementations like:
13+
// void foo::bar() const {}
14+
// However! The `foo` in the above example could also be a namespace, so we only capture the class name if
15+
// it starts with an uppercase letter. This approximation should give decent results.
16+
/\b[A-Z]\w*(?=\s*::\s*\w+\s*\()/,
17+
// This will capture the class name before destructors like:
18+
// Foo::~Foo() {}
19+
/\b[A-Z_]\w*(?=\s*::\s*~\w+\s*\()/i,
20+
{
21+
// This also intends to capture the class name of method implementations but here the class has template
22+
// parameters, so it can't be a namespace (until C++ adds generic namespaces).
23+
pattern: /\w+(?=\s*<(?:[^<>]|<(?:[^<>]|<[^<>]*>)*>)*>\s*::\s*\w+\s*\()/,
24+
inside: null // see below
25+
}
26+
],
27+
'keyword': keyword,
28+
'number': {
29+
pattern: /(?:\b0b[01']+|\b0x(?:[\da-f']+\.?[\da-f']*|\.[\da-f']+)(?:p[+-]?[\d']+)?|(?:\b[\d']+\.?[\d']*|\B\.[\d']+)(?:e[+-]?[\d']+)?)[ful]*/i,
30+
greedy: true
31+
},
32+
'operator': />>=?|<<=?|->|([-+&|:])\1|[?:~]|[-+*/%&|^!=<>]=?|\b(?:and|and_eq|bitand|bitor|not|not_eq|or|or_eq|xor|xor_eq)\b/,
33+
'boolean': /\b(?:true|false)\b/
34+
});
35+
36+
Prism.languages.insertBefore('cpp', 'string', {
37+
'raw-string': {
38+
pattern: /R"([^()\\ ]{0,16})\([\s\S]*?\)\1"/,
39+
alias: 'string',
40+
greedy: true
41+
}
42+
});
43+
44+
Prism.languages.insertBefore('cpp', 'class-name', {
45+
// the base clause is an optional list of parent classes
46+
// https://en.cppreference.com/w/cpp/language/class
47+
'base-clause': {
48+
pattern: /(\b(?:class|struct)\s+\w+\s*:\s*)(?:[^;{}"'])+?(?=\s*[;{])/,
49+
lookbehind: true,
50+
greedy: true,
51+
inside: Prism.languages.extend('cpp', {})
52+
}
53+
});
54+
Prism.languages.insertBefore('inside', 'operator', {
55+
// All untokenized words that are not namespaces should be class names
56+
'class-name': /\b[a-z_]\w*\b(?!\s*::)/i
57+
}, Prism.languages.cpp['base-clause']);
58+
59+
}(Prism));

components/prism-cpp.min.js

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
struct Base {};
2+
struct Derived : Base {};
3+
struct Derived : private Base;
4+
class X : public virtual B {};
5+
class Y : virtual public B {};
6+
class Y : virtual baz::B {};
7+
class Z : public B<foo::T>;
8+
struct AA : X, Y, foo::bar::Z {};
9+
10+
class service : private Transport // comment
11+
{};
12+
13+
----------------------------------------------------
14+
15+
[
16+
["keyword", "struct"],
17+
["class-name", "Base"],
18+
["punctuation", "{"],
19+
["punctuation", "}"],
20+
["punctuation", ";"],
21+
["keyword", "struct"],
22+
["class-name", "Derived"],
23+
["operator", ":"],
24+
["base-clause", [
25+
["class-name", "Base"]
26+
]],
27+
["punctuation", "{"],
28+
["punctuation", "}"],
29+
["punctuation", ";"],
30+
["keyword", "struct"],
31+
["class-name", "Derived"],
32+
["operator", ":"],
33+
["base-clause", [
34+
["keyword", "private"],
35+
["class-name", "Base"]
36+
]],
37+
["punctuation", ";"],
38+
["keyword", "class"],
39+
["class-name", "X"],
40+
["operator", ":"],
41+
["base-clause", [
42+
["keyword", "public"],
43+
["keyword", "virtual"],
44+
["class-name", "B"]
45+
]],
46+
["punctuation", "{"],
47+
["punctuation", "}"],
48+
["punctuation", ";"],
49+
["keyword", "class"],
50+
["class-name", "Y"],
51+
["operator", ":"],
52+
["base-clause", [
53+
["keyword", "virtual"],
54+
["keyword", "public"],
55+
["class-name", "B"]
56+
]],
57+
["punctuation", "{"],
58+
["punctuation", "}"],
59+
["punctuation", ";"],
60+
["keyword", "class"],
61+
["class-name", "Y"],
62+
["operator", ":"],
63+
["base-clause", [
64+
["keyword", "virtual"],
65+
" baz",
66+
["operator", "::"],
67+
["class-name", "B"]
68+
]],
69+
["punctuation", "{"],
70+
["punctuation", "}"],
71+
["punctuation", ";"],
72+
["keyword", "class"],
73+
["class-name", "Z"],
74+
["operator", ":"],
75+
["base-clause", [
76+
["keyword", "public"],
77+
["class-name", "B"],
78+
["operator", "<"],
79+
"foo",
80+
["operator", "::"],
81+
["class-name", "T"],
82+
["operator", ">"]
83+
]],
84+
["punctuation", ";"],
85+
["keyword", "struct"],
86+
["class-name", "AA"],
87+
["operator", ":"],
88+
["base-clause", [
89+
["class-name", "X"],
90+
["punctuation", ","],
91+
["class-name", "Y"],
92+
["punctuation", ","],
93+
" foo",
94+
["operator", "::"],
95+
"bar",
96+
["operator", "::"],
97+
["class-name", "Z"]
98+
]],
99+
["punctuation", "{"],
100+
["punctuation", "}"],
101+
["punctuation", ";"],
102+
["keyword", "class"],
103+
["class-name", "service"],
104+
["operator", ":"],
105+
["base-clause", [
106+
["keyword", "private"],
107+
["class-name", "Transport"],
108+
["comment", "// comment"]
109+
]],
110+
["punctuation", "{"],
111+
["punctuation", "}"],
112+
["punctuation", ";"]
113+
]
114+
115+
----------------------------------------------------
116+
117+
Checks for the base clauses of classes and structs.

tests/languages/cpp/class-name_feature.test

+38-1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,11 @@ class Foo_bar
33
struct foo
44
enum bar
55
enum class FooBar
6+
template<typename FooBar>
7+
8+
void Foo::bar() {}
9+
Foo::~Foo() {}
10+
void Foo<int>::bar() {}
611

712
----------------------------------------------------
813

@@ -11,7 +16,39 @@ enum class FooBar
1116
["keyword", "class"], ["class-name", "Foo_bar"],
1217
["keyword", "struct"], ["class-name", "foo"],
1318
["keyword", "enum"], ["class-name", "bar"],
14-
["keyword", "enum"], ["keyword", "class"], ["class-name", "FooBar"]
19+
["keyword", "enum"], ["keyword", "class"], ["class-name", "FooBar"],
20+
["keyword", "template"], ["operator", "<"], ["keyword", "typename"], ["class-name", "FooBar"], ["operator", ">"],
21+
22+
23+
["keyword", "void"],
24+
["class-name", "Foo"],
25+
["operator", "::"],
26+
["function", "bar"],
27+
["punctuation", "("],
28+
["punctuation", ")"],
29+
["punctuation", "{"],
30+
["punctuation", "}"],
31+
32+
["class-name", "Foo"],
33+
["operator", "::"],
34+
["operator", "~"],
35+
["function", "Foo"],
36+
["punctuation", "("],
37+
["punctuation", ")"],
38+
["punctuation", "{"],
39+
["punctuation", "}"],
40+
41+
["keyword", "void"],
42+
["class-name", "Foo"],
43+
["operator", "<"],
44+
["keyword", "int"],
45+
["operator", ">"],
46+
["operator", "::"],
47+
["function", "bar"],
48+
["punctuation", "("],
49+
["punctuation", ")"],
50+
["punctuation", "{"],
51+
["punctuation", "}"]
1552
]
1653

1754
----------------------------------------------------

tests/languages/cpp/issue2347.test

+72
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
class MainWindow : public QMainWindow
2+
{
3+
Q_OBJECT
4+
5+
private slots:
6+
void changeWindowTitle();
7+
};
8+
void MainWindow::changeWindowTitle()
9+
{
10+
setWindowTitle(plainTextEdit->currentFile.split("/").last() + " - Notepanda");
11+
}
12+
13+
----------------------------------------------------
14+
15+
[
16+
["keyword", "class"],
17+
["class-name", "MainWindow"],
18+
["operator", ":"],
19+
["base-clause", [
20+
["keyword", "public"],
21+
["class-name", "QMainWindow"]
22+
]],
23+
24+
["punctuation", "{"],
25+
26+
"\n Q_OBJECT\n\n ",
27+
28+
["keyword", "private"],
29+
" slots",
30+
["operator", ":"],
31+
32+
["keyword", "void"],
33+
["function", "changeWindowTitle"],
34+
["punctuation", "("],
35+
["punctuation", ")"],
36+
["punctuation", ";"],
37+
38+
["punctuation", "}"],
39+
["punctuation", ";"],
40+
41+
["keyword", "void"],
42+
["class-name", "MainWindow"],
43+
["operator", "::"],
44+
["function", "changeWindowTitle"],
45+
["punctuation", "("],
46+
["punctuation", ")"],
47+
48+
["punctuation", "{"],
49+
50+
["function", "setWindowTitle"],
51+
["punctuation", "("],
52+
"plainTextEdit",
53+
["operator", "->"],
54+
"currentFile",
55+
["punctuation", "."],
56+
["function", "split"],
57+
["punctuation", "("],
58+
["string", "\"/\""],
59+
["punctuation", ")"],
60+
["punctuation", "."],
61+
["function", "last"],
62+
["punctuation", "("],
63+
["punctuation", ")"],
64+
["operator", "+"],
65+
["string", "\" - Notepanda\""],
66+
["punctuation", ")"],
67+
["punctuation", ";"],
68+
69+
["punctuation", "}"]
70+
]
71+
72+
----------------------------------------------------

0 commit comments

Comments
 (0)