cpp.rs

  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}