1use settings::SemanticTokenRules;
2
3pub(crate) fn semantic_token_rules() -> SemanticTokenRules {
4 let content = grammars::get_file("cpp/semantic_token_rules.json")
5 .expect("missing cpp/semantic_token_rules.json");
6 let json = std::str::from_utf8(&content.data).expect("invalid utf-8 in semantic_token_rules");
7 settings::parse_json_with_comments::<SemanticTokenRules>(json)
8 .expect("failed to parse cpp semantic_token_rules.json")
9}
10
11#[cfg(test)]
12mod tests {
13 use gpui::{AppContext as _, BorrowAppContext, TestAppContext};
14 use language::{AutoindentMode, Buffer};
15 use settings::SettingsStore;
16 use std::num::NonZeroU32;
17 use unindent::Unindent;
18
19 #[gpui::test]
20 async fn test_cpp_autoindent_access_specifier(cx: &mut TestAppContext) {
21 cx.update(|cx| {
22 let test_settings = SettingsStore::test(cx);
23 cx.set_global(test_settings);
24 cx.update_global::<SettingsStore, _>(|store, cx| {
25 store.update_user_settings(cx, |s| {
26 s.project.all_languages.defaults.tab_size = NonZeroU32::new(2);
27 });
28 });
29 });
30 let language = crate::language("cpp", tree_sitter_cpp::LANGUAGE.into());
31
32 cx.new(|cx| {
33 let mut buffer = Buffer::local("", cx).with_language(language, cx);
34
35 buffer.edit(
36 [(
37 0..0,
38 r#"
39 class Foo {
40 public:
41 void bar();
42 private:
43 int x;
44 };
45 "#
46 .unindent(),
47 )],
48 Some(AutoindentMode::EachLine),
49 cx,
50 );
51 assert_eq!(
52 buffer.text(),
53 r#"
54 class Foo {
55 public:
56 void bar();
57 private:
58 int x;
59 };
60 "#
61 .unindent(),
62 "members after access specifiers should be indented one level deeper than the specifier"
63 );
64
65 buffer
66 });
67 }
68
69 #[gpui::test]
70 async fn test_cpp_autoindent_access_specifier_next_line(cx: &mut TestAppContext) {
71 cx.update(|cx| {
72 let test_settings = SettingsStore::test(cx);
73 cx.set_global(test_settings);
74 cx.update_global::<SettingsStore, _>(|store, cx| {
75 store.update_user_settings(cx, |s| {
76 s.project.all_languages.defaults.tab_size = NonZeroU32::new(2);
77 });
78 });
79 });
80 let language = crate::language("cpp", tree_sitter_cpp::LANGUAGE.into());
81
82 cx.new(|cx| {
83 let mut buffer = Buffer::local("", cx).with_language(language, cx);
84
85 buffer.edit(
86 [(
87 0..0,
88 r#"
89 class Foo {
90 public:
91 void bar();
92 void baz();
93 private:
94 int x;
95 };
96 "#
97 .unindent(),
98 )],
99 Some(AutoindentMode::EachLine),
100 cx,
101 );
102 assert_eq!(
103 buffer.text(),
104 r#"
105 class Foo {
106 public:
107 void bar();
108 void baz();
109 private:
110 int x;
111 };
112 "#
113 .unindent(),
114 "members after access specifiers should be indented one level deeper than the specifier"
115 );
116
117 buffer
118 });
119 }
120
121 #[gpui::test]
122 async fn test_cpp_autoindent_nested_class_access_specifiers(cx: &mut TestAppContext) {
123 cx.update(|cx| {
124 let test_settings = SettingsStore::test(cx);
125 cx.set_global(test_settings);
126 cx.update_global::<SettingsStore, _>(|store, cx| {
127 store.update_user_settings(cx, |s| {
128 s.project.all_languages.defaults.tab_size = NonZeroU32::new(2);
129 });
130 });
131 });
132 let language = crate::language("cpp", tree_sitter_cpp::LANGUAGE.into());
133
134 cx.new(|cx| {
135 let mut buffer = Buffer::local("", cx).with_language(language, cx);
136
137 buffer.edit(
138 [(
139 0..0,
140 r#"
141 class Outer {
142 public:
143 class Inner {
144 public:
145 void inner_pub();
146 private:
147 int inner_priv;
148 };
149 private:
150 int outer_priv;
151 };
152 "#
153 .unindent(),
154 )],
155 Some(AutoindentMode::EachLine),
156 cx,
157 );
158 assert_eq!(
159 buffer.text(),
160 r#"
161 class Outer {
162 public:
163 class Inner {
164 public:
165 void inner_pub();
166 private:
167 int inner_priv;
168 };
169 private:
170 int outer_priv;
171 };
172 "#
173 .unindent(),
174 "nested class access specifiers should indent independently at each nesting level"
175 );
176
177 buffer
178 });
179 }
180
181 #[gpui::test]
182 async fn test_cpp_autoindent_consecutive_access_specifiers(cx: &mut TestAppContext) {
183 cx.update(|cx| {
184 let test_settings = SettingsStore::test(cx);
185 cx.set_global(test_settings);
186 cx.update_global::<SettingsStore, _>(|store, cx| {
187 store.update_user_settings(cx, |s| {
188 s.project.all_languages.defaults.tab_size = NonZeroU32::new(2);
189 });
190 });
191 });
192 let language = crate::language("cpp", tree_sitter_cpp::LANGUAGE.into());
193
194 cx.new(|cx| {
195 let mut buffer = Buffer::local("", cx).with_language(language, cx);
196
197 buffer.edit(
198 [(
199 0..0,
200 r#"
201 class Foo {
202 public:
203 protected:
204 private:
205 int x;
206 };
207 "#
208 .unindent(),
209 )],
210 Some(AutoindentMode::EachLine),
211 cx,
212 );
213 assert_eq!(
214 buffer.text(),
215 r#"
216 class Foo {
217 public:
218 protected:
219 private:
220 int x;
221 };
222 "#
223 .unindent(),
224 "consecutive access specifiers with no members between them should all align at class level"
225 );
226
227 buffer
228 });
229 }
230
231 #[gpui::test]
232 async fn test_cpp_autoindent_indented_access_specifiers(cx: &mut TestAppContext) {
233 cx.update(|cx| {
234 let test_settings = SettingsStore::test(cx);
235 cx.set_global(test_settings);
236 cx.update_global::<SettingsStore, _>(|store, cx| {
237 store.update_user_settings(cx, |s| {
238 s.project.all_languages.defaults.tab_size = NonZeroU32::new(2);
239 });
240 });
241 });
242 let language = crate::language("cpp", tree_sitter_cpp::LANGUAGE.into());
243
244 cx.new(|cx| {
245 let mut buffer = Buffer::local("", cx).with_language(language, cx);
246
247 buffer.edit(
248 [(
249 0..0,
250 r#"
251 class Foo {
252 int default_member;
253 public:
254 void pub_method();
255 private:
256 int priv_member;
257 };
258 "#
259 .unindent(),
260 )],
261 Some(AutoindentMode::EachLine),
262 cx,
263 );
264 assert_eq!(
265 buffer.text(),
266 r#"
267 class Foo {
268 int default_member;
269 public:
270 void pub_method();
271 private:
272 int priv_member;
273 };
274 "#
275 .unindent(),
276 "access specifiers should be indented one level inside class braces"
277 );
278
279 buffer
280 });
281 }
282
283 #[gpui::test]
284 async fn test_cpp_autoindent_access_specifier_with_method_bodies(cx: &mut TestAppContext) {
285 cx.update(|cx| {
286 let test_settings = SettingsStore::test(cx);
287 cx.set_global(test_settings);
288 cx.update_global::<SettingsStore, _>(|store, cx| {
289 store.update_user_settings(cx, |s| {
290 s.project.all_languages.defaults.tab_size = NonZeroU32::new(2);
291 });
292 });
293 });
294 let language = crate::language("cpp", tree_sitter_cpp::LANGUAGE.into());
295
296 cx.new(|cx| {
297 let mut buffer = Buffer::local("", cx).with_language(language, cx);
298
299 buffer.edit(
300 [(
301 0..0,
302 r#"
303 class Foo {
304 public:
305 void bar() {
306 if (x)
307 y++;
308 }
309 private:
310 int get_x() {
311 return x;
312 }
313 int x;
314 };
315 "#
316 .unindent(),
317 )],
318 Some(AutoindentMode::EachLine),
319 cx,
320 );
321 assert_eq!(
322 buffer.text(),
323 r#"
324 class Foo {
325 public:
326 void bar() {
327 if (x)
328 y++;
329 }
330 private:
331 int get_x() {
332 return x;
333 }
334 int x;
335 };
336 "#
337 .unindent(),
338 "method bodies inside access specifier sections should compose brace and specifier indent"
339 );
340
341 buffer
342 });
343 }
344
345 #[gpui::test]
346 async fn test_cpp_autoindent_basic(cx: &mut TestAppContext) {
347 cx.update(|cx| {
348 let test_settings = SettingsStore::test(cx);
349 cx.set_global(test_settings);
350 cx.update_global::<SettingsStore, _>(|store, cx| {
351 store.update_user_settings(cx, |s| {
352 s.project.all_languages.defaults.tab_size = NonZeroU32::new(2);
353 });
354 });
355 });
356 let language = crate::language("cpp", tree_sitter_cpp::LANGUAGE.into());
357
358 cx.new(|cx| {
359 let mut buffer = Buffer::local("", cx).with_language(language, cx);
360
361 buffer.edit([(0..0, "int main() {}")], None, cx);
362
363 let ix = buffer.len() - 1;
364 buffer.edit([(ix..ix, "\n\n")], Some(AutoindentMode::EachLine), cx);
365 assert_eq!(
366 buffer.text(),
367 "int main() {\n \n}",
368 "content inside braces should be indented"
369 );
370
371 buffer
372 });
373 }
374
375 #[gpui::test]
376 async fn test_cpp_autoindent_if_else(cx: &mut TestAppContext) {
377 cx.update(|cx| {
378 let test_settings = SettingsStore::test(cx);
379 cx.set_global(test_settings);
380 cx.update_global::<SettingsStore, _>(|store, cx| {
381 store.update_user_settings(cx, |s| {
382 s.project.all_languages.defaults.tab_size = NonZeroU32::new(2);
383 });
384 });
385 });
386 let language = crate::language("cpp", tree_sitter_cpp::LANGUAGE.into());
387
388 cx.new(|cx| {
389 let mut buffer = Buffer::local("", cx).with_language(language, cx);
390
391 buffer.edit(
392 [(
393 0..0,
394 r#"
395 int main() {
396 if (a)
397 b;
398 }
399 "#
400 .unindent(),
401 )],
402 Some(AutoindentMode::EachLine),
403 cx,
404 );
405 assert_eq!(
406 buffer.text(),
407 r#"
408 int main() {
409 if (a)
410 b;
411 }
412 "#
413 .unindent(),
414 "body of if-statement without braces should be indented"
415 );
416
417 let ix = buffer.len() - 4;
418 buffer.edit([(ix..ix, "\n.c")], Some(AutoindentMode::EachLine), cx);
419 assert_eq!(
420 buffer.text(),
421 r#"
422 int main() {
423 if (a)
424 b
425 .c;
426 }
427 "#
428 .unindent(),
429 "field expression (.c) should be indented further than the statement body"
430 );
431
432 buffer.edit([(0..buffer.len(), "")], Some(AutoindentMode::EachLine), cx);
433 buffer.edit(
434 [(
435 0..0,
436 r#"
437 int main() {
438 if (a) a++;
439 else b++;
440 }
441 "#
442 .unindent(),
443 )],
444 Some(AutoindentMode::EachLine),
445 cx,
446 );
447 assert_eq!(
448 buffer.text(),
449 r#"
450 int main() {
451 if (a) a++;
452 else b++;
453 }
454 "#
455 .unindent(),
456 "single-line if/else without braces should align at the same level"
457 );
458
459 buffer.edit([(0..buffer.len(), "")], Some(AutoindentMode::EachLine), cx);
460 buffer.edit(
461 [(
462 0..0,
463 r#"
464 int main() {
465 if (a)
466 b++;
467 else
468 c++;
469 }
470 "#
471 .unindent(),
472 )],
473 Some(AutoindentMode::EachLine),
474 cx,
475 );
476 assert_eq!(
477 buffer.text(),
478 r#"
479 int main() {
480 if (a)
481 b++;
482 else
483 c++;
484 }
485 "#
486 .unindent(),
487 "multi-line if/else without braces should indent statement bodies"
488 );
489
490 buffer.edit([(0..buffer.len(), "")], Some(AutoindentMode::EachLine), cx);
491 buffer.edit(
492 [(
493 0..0,
494 r#"
495 int main() {
496 if (a)
497 if (b)
498 c++;
499 }
500 "#
501 .unindent(),
502 )],
503 Some(AutoindentMode::EachLine),
504 cx,
505 );
506 assert_eq!(
507 buffer.text(),
508 r#"
509 int main() {
510 if (a)
511 if (b)
512 c++;
513 }
514 "#
515 .unindent(),
516 "nested if statements without braces should indent properly"
517 );
518
519 buffer.edit([(0..buffer.len(), "")], Some(AutoindentMode::EachLine), cx);
520 buffer.edit(
521 [(
522 0..0,
523 r#"
524 int main() {
525 if (a)
526 b++;
527 else if (c)
528 d++;
529 else
530 f++;
531 }
532 "#
533 .unindent(),
534 )],
535 Some(AutoindentMode::EachLine),
536 cx,
537 );
538 assert_eq!(
539 buffer.text(),
540 r#"
541 int main() {
542 if (a)
543 b++;
544 else if (c)
545 d++;
546 else
547 f++;
548 }
549 "#
550 .unindent(),
551 "else-if chains should align all conditions at same level with indented bodies"
552 );
553
554 buffer.edit([(0..buffer.len(), "")], Some(AutoindentMode::EachLine), cx);
555 buffer.edit(
556 [(
557 0..0,
558 r#"
559 int main() {
560 if (a) {
561 b++;
562 } else
563 c++;
564 }
565 "#
566 .unindent(),
567 )],
568 Some(AutoindentMode::EachLine),
569 cx,
570 );
571 assert_eq!(
572 buffer.text(),
573 r#"
574 int main() {
575 if (a) {
576 b++;
577 } else
578 c++;
579 }
580 "#
581 .unindent(),
582 "mixed braces should indent properly"
583 );
584
585 buffer
586 });
587 }
588}