diff --git a/assets/highlighting-tests/c.c b/assets/highlighting-tests/c.c new file mode 100644 index 00000000000..a70791bfae6 --- /dev/null +++ b/assets/highlighting-tests/c.c @@ -0,0 +1,109 @@ +// Single-line comment +/* + * Multi-line + * comment + */ + +// Preprocessor directives +#include +#include "foo.h" +#ifndef HEADER_GUARD +# define HEADER_GUARD +#endif +#define MAX_BUFFER 1024 +#define MIN(a, b) \ + ((a) < (b) ? (a) : (b)) +#pragma once +#warning "unmaintained" + +// Numbers +42; +0777; +3.14; +.5; +1e10; +1.5e-3; +0xff; +0b1010; +0x1p-3; +-12L; +15lU; +6.283F; +1'000'000; + +// Constants and literals +true; +false; +NULL; +nullptr; +'a'; +'\''; +"escapes: \" \n \t \\"; + +// Control flow +int main(int argc, char *argv[]) +{ + for (int i = 0; i < 10; i++) { + if (i == 5) { + continue; + } else if (i == 8) { + break; + } else { + goto end; + } + } + + do { + printf("Hello %s!\n", argv[0]); + } while (false); + + switch (argc) { + case 1: + break; + default: + break; + } + +end: + return 0; +} + +// Types and storage classes +extern int global_var; +static const char *text = "Hello World!"; +volatile unsigned short hardware_register; +register signed long long fast_loop_counter; +_Atomic int atomic_counter; +_Bool is_active; +thread_local double tau; +typeof(tau) tau_copy; + +// Aggregates +struct Point { + int x; + int y; +}; + +typedef union { + char bytes[4]; + float value; +} Pixel; + +enum EntityKind { + Bear, + Bee, + Dog, +}; + +// Designated initializers, where `.x` must not be mistaken for a number. +struct Point origin = { .x = 0, .y = 0 }; + +// Declarations and calls +_Noreturn void die(void); +alignas(16) char buffer[64]; +static_assert(sizeof(struct Point) == 8, "unexpected layout"); + +static inline int add(int a, int b) +{ + return a + b; +} diff --git a/assets/highlighting-tests/cpp.cpp b/assets/highlighting-tests/cpp.cpp new file mode 100644 index 00000000000..2eb26f858cd --- /dev/null +++ b/assets/highlighting-tests/cpp.cpp @@ -0,0 +1,88 @@ +// Covers what C++ adds on top of C. See c.c for the constructs shared with it. + +#include + +// Numbers and literals +0b1010'1100; +42ull; +1z; +nullptr; +u8"utf-8"; +wchar_t wide = L'w'; +char16_t utf16 = u'x'; +char8_t utf8 = u8'y'; + +// Namespaces, classes, and access specifiers +namespace my_space { + +enum class State { START, STOP }; + +class Animal { +public: + Animal() : age_(0), alive_(true) {} + virtual ~Animal() = default; + virtual void speak() const = 0; + +protected: + bool alive_; + +private: + int age_; +}; + +class Dog final : public Animal { +public: + void speak() const override { + std::cout << "Woof!" << std::endl; + } +}; + +struct Config { + explicit Config(int v) : value_(v) {} + friend bool operator==(const Config &, const Config &) = default; + Config &self() { return *this; } + + mutable int cache_; + int value_; +}; + +} // namespace my_space + +using namespace my_space; + +// Templates and concepts +template +concept Addable = requires(T a, T b) { + a + b; +}; + +template +constexpr auto twice(T value) noexcept -> decltype(value + value) { + return value + value; +} + +consteval int magic() { return 42; } +constinit int counter = 0; + +// Casts, allocation, and exceptions +void run() try { + auto *dog = new Dog(); + delete dog; + + auto n = static_cast(0); + auto *p = reinterpret_cast(&n); + const_cast(p); + dynamic_cast(dog); + typeid(n); + + throw std::runtime_error("oops"); +} catch (const std::exception &e) { + // handle error +} + +// Coroutines +std::future compute() { + int result = co_await async_work(); + co_yield result; + co_return result; +} diff --git a/crates/lsh/definitions/c.lsh b/crates/lsh/definitions/c.lsh new file mode 100644 index 00000000000..40b075e40b9 --- /dev/null +++ b/crates/lsh/definitions/c.lsh @@ -0,0 +1,49 @@ +#[display_name = "C"] +#[path = "**/*.c"] +#[path = "**/*.i"] +pub fn c() { + until /$/ { + yield other; + + if /\/\/.*/ { + yield comment; + } else if /\/\*/ { + loop { + yield comment; + await input; + if /\*\// { + yield comment; + break; + } + } + } else if /#\s*(?:define|elifdef|elifndef|elif|else|embed|endif|error|ifdef|ifndef|if|include|line|pragma|undef|warning)\>/ { + // Only the directive itself is highlighted. The remainder of the + // line is regular C, more or less, and lexed as such. + yield keyword.preprocessor; + } else if /'/ { + single_quote_string(); + } else if /"/ { + double_quote_string(); + } else if /(?:break|case|continue|default|do|else|for|goto|if|return|switch|while)\>/ { + yield keyword.control; + } else if /(?:alignas|alignof|auto|constexpr|const|enum|extern|inline|register|restrict|sizeof|static_assert|static|struct|thread_local|typedef|typeof_unqual|typeof|union|volatile|_Alignas|_Alignof|_Atomic|_Generic|_Noreturn|_Static_assert|_Thread_local)\>/ { + yield keyword.other; + } else if /(?:true|false|nullptr|NULL)\>/ { + yield constant.language; + } else if /(?:bool|char|double|float|int|long|short|signed|unsigned|void|_BitInt|_Bool|_Complex)\>/ { + yield storage.type; + } else if /(?i:-?(?:0x[\da-f']+|0b[01']+|[\d']+\.?[\d']*|\.[\d']+)(?:[ep][+-]?[\d']+)?[flu]*)/ { + if /\w+/ { + // Invalid numeric literal + } else { + yield constant.numeric; + } + } else if /(\w+)\s*\(/ { + yield $1 as method; + } else if /\w+/ { + // Gobble word chars to align the next iteration on a word boundary. + } + + yield other; + } +} diff --git a/crates/lsh/definitions/cpp.lsh b/crates/lsh/definitions/cpp.lsh new file mode 100644 index 00000000000..d6f1231004d --- /dev/null +++ b/crates/lsh/definitions/cpp.lsh @@ -0,0 +1,52 @@ +#[display_name = "C++"] +#[path = "**/*.cpp"] +#[path = "**/*.cc"] +#[path = "**/*.cxx"] +#[path = "**/*.hpp"] +#[path = "**/*.h"] +pub fn cpp() { + until /$/ { + yield other; + + if /\/\/.*/ { + yield comment; + } else if /\/\*/ { + loop { + yield comment; + await input; + if /\*\// { + yield comment; + break; + } + } + } else if /#\s*(?:define|elifdef|elifndef|elif|else|embed|endif|error|ifdef|ifndef|if|include|line|pragma|undef|warning)\>/ { + // Only the directive itself is highlighted. The remainder of the + // line is regular C++, more or less, and lexed as such. + yield keyword.preprocessor; + } else if /'/ { + single_quote_string(); + } else if /"/ { + double_quote_string(); + } else if /(?:break|case|catch|co_await|co_return|co_yield|continue|default|do|else|for|goto|if|return|switch|throw|try|while)\>/ { + yield keyword.control; + } else if /(?:alignas|alignof|class|concept|consteval|constexpr|constinit|const_cast|const|decltype|delete|dynamic_cast|enum|explicit|export|extern|final|friend|inline|mutable|namespace|new|noexcept|operator|override|private|protected|public|register|reinterpret_cast|requires|sizeof|static_assert|static_cast|static|struct|template|this|thread_local|typedef|typeid|typename|union|using|virtual|volatile)\>/ { + yield keyword.other; + } else if /(?:true|false|nullptr|NULL)\>/ { + yield constant.language; + } else if /(?:auto|bool|char8_t|char16_t|char32_t|char|double|float|int|long|short|signed|unsigned|void|wchar_t)\>/ { + yield storage.type; + } else if /(?i:-?(?:0x[\da-f']+|0b[01']+|[\d']+\.?[\d']*|\.[\d']+)(?:[ep][+-]?[\d']+)?[fluz]*)/ { + if /\w+/ { + // Invalid numeric literal + } else { + yield constant.numeric; + } + } else if /(\w+)\s*\(/ { + yield $1 as method; + } else if /\w+/ { + // Gobble word chars to align the next iteration on a word boundary. + } + + yield other; + } +}