Repository: typst/typst
File: crates/typst-syntax/src/lexer.rs (Rust)

Code before:
                    '&' if no_hash && self.s.eat_if('&') => {
                        let error = self.error("`&&` is not valid in code");
                        self.hint("in Typst, `and` is used for logical AND");
                        error
                    }
                    '|' if no_hash && self.s.eat_if('|') => {
                        let error = self.error("`||` is not valid in code");
                        self.hint("in Typst, `or` is used for logical OR");
                        error
                    }
                    '!' if no_hash => {
                        let error = self.error("the character `!` is not valid in code");
                        self.hint("in Typst, `not` is used for negation");
                        self.hint("or did you mean to write `!=` for not-equal?");
                        error
                    }
                    '~' if no_hash && self.s.eat_if('=') => {
                        let error = self.error("`~=` is not valid in code");
                        self.hint("in Typst, `!=` is used for not-equal");
                        error
                    }
                    '~' if no_hash => {
                        let error = self.error("the character `~` is not valid in code");
                        self.hint("in Typst, `not` is used for negation");
                        error
                    }
                    // Our default hint for invalid characters.
                    c => {
                        let error = self.error(eco_format!(
                            "the character `{c}` is not valid in code"
                        ));
                        if c == '#' {
                            self.hint("you are already in code mode");
                        }
                        self.hint(eco_format!("try removing the `{c}`"));
                        error
                    }
                }
            }
        }
    }

    fn ident(&mut self, start: usize) -> SyntaxKind {
        self.s.eat_while(is_id_continue);
        let ident = self.s.from(start);

        let prev = self.s.get(0..start);
        if (!prev.ends_with(['.', '@']) || prev.ends_with(".."))
            && let Some(keyword) = keyword(ident)
        {
            return keyword;

Review comment (suggestion):
AFAIK, `~` is usually for bitwise negation, so suggesting using `not` instead might be a bit misleading.

Code after:
    fn invalid_char_in_code(&mut self, c: char) -> SyntaxKind {
        let invalid_char = || eco_format!("the character `{c}` is not valid in code");
        let invalid_str = |s: &str| eco_format!("`{s}` is not valid in code");
        match c {
            // Give a custom hint if we immediately follow a hash.
            _ if self.s.scout(-2) == Some('#') => {
                self.error(invalid_char());
                // This is only an accurate hint if we just came from markup or
                // math, but `#!` or `##` in code should be rare enough that
                // it's fine (and the first hash will produce its own error).
                self.hint("the preceding hash is causing this to parse in code mode");
                self.hint("try escaping the preceding hash: `\\#`");
                // The span for these hints isn't great, but it's hard to fix.
            }
            '#' => {
                self.error(invalid_char());
                self.hint("you are already in code mode");
                self.hint("try removing the `#`");
            }
            '&' if self.s.eat_if('&') => {
                self.error(invalid_str("&&"));
                self.hint("in Typst, `and` is used for logical AND");
            }
            '|' if self.s.eat_if('|') => {
                self.error(invalid_str("||"));
                self.hint("in Typst, `or` is used for logical OR");
            }
            '!' => {
                self.error(invalid_char());
                self.hint("in Typst, `not` is used for negation");
                self.hint("or did you mean to write `!=` for not-equal?");
            }
            '~' if self.s.eat_if('=') => {
                self.error(invalid_str("~="));
                self.hint("in Typst, `!=` is used for not-equal");
            }
            _ => {
                self.error(invalid_char());
            }
        }
        SyntaxKind::Error
    }

    fn ident(&mut self, start: usize) -> SyntaxKind {
        self.s.eat_while(is_id_continue);
        let ident = self.s.from(start);

        let prev = self.s.get(0..start);
        if (!prev.ends_with(['.', '@']) || prev.ends_with(".."))
            && let Some(keyword) = keyword(ident)
        {