manen

Fancy Lua REPL
Log | Files | Refs | README | LICENSE

parse.rs (12530B)


      1 use emmylua_parser::{
      2     LuaAst, LuaAstNode, LuaKind, LuaLanguageLevel, LuaParser, LuaSyntaxKind, LuaSyntaxNode,
      3     LuaSyntaxToken, LuaSyntaxTree, LuaTokenKind, ParserConfig,
      4 };
      5 use nu_ansi_term::{Color, Style};
      6 use reedline::StyledText;
      7 use rowan::WalkEvent;
      8 
      9 #[cfg(feature = "lua54")]
     10 pub fn config<'cache>() -> ParserConfig<'cache> {
     11     ParserConfig::with_level(LuaLanguageLevel::Lua54)
     12 }
     13 
     14 #[cfg(feature = "lua53")]
     15 pub fn config<'cache>() -> ParserConfig<'cache> {
     16     ParserConfig::with_level(LuaLanguageLevel::Lua53)
     17 }
     18 
     19 #[cfg(feature = "lua52")]
     20 pub fn config<'cache>() -> ParserConfig<'cache> {
     21     ParserConfig::with_level(LuaLanguageLevel::Lua52)
     22 }
     23 
     24 #[cfg(feature = "lua51")]
     25 pub fn config<'cache>() -> ParserConfig<'cache> {
     26     ParserConfig::with_level(LuaLanguageLevel::Lua51)
     27 }
     28 
     29 #[cfg(any(feature = "luajit", feature = "luajit52"))]
     30 pub fn config<'cache>() -> ParserConfig<'cache> {
     31     ParserConfig::with_level(LuaLanguageLevel::LuaJIT)
     32 }
     33 
     34 #[cfg(any(feature = "luajit", feature = "luajit52"))]
     35 pub fn config<'cache>() -> ParserConfig<'cache> {
     36     ParserConfig::with_level(LuaLanguageLevel::LuaJIT)
     37 }
     38 
     39 fn node_name(node: &LuaAst) -> Option<&'static str> {
     40     match node {
     41         LuaAst::LuaChunk(_) => Some("chunk"),
     42         LuaAst::LuaBlock(_) => Some("block"),
     43         LuaAst::LuaAssignStat(_) => Some("assignment"),
     44         LuaAst::LuaLocalStat(_) => Some("local"),
     45         LuaAst::LuaCallExprStat(_) => Some("call_statement"),
     46         LuaAst::LuaLabelStat(_) => Some("label"),
     47         LuaAst::LuaBreakStat(_) => Some("break"),
     48         LuaAst::LuaGotoStat(_) => Some("goto"),
     49         LuaAst::LuaDoStat(_) => Some("do"),
     50         LuaAst::LuaWhileStat(_) => Some("while"),
     51         LuaAst::LuaRepeatStat(_) => Some("repeat"),
     52         LuaAst::LuaIfStat(_) => Some("if"),
     53         LuaAst::LuaForStat(_) => Some("for_range"),
     54         LuaAst::LuaForRangeStat(_) => Some("for"),
     55         LuaAst::LuaFuncStat(_) => Some("function"),
     56         LuaAst::LuaLocalFuncStat(_) => Some("local_function"),
     57         LuaAst::LuaReturnStat(_) => Some("return"),
     58         LuaAst::LuaNameExpr(_) => Some("identifier"),
     59         LuaAst::LuaIndexExpr(_) => Some("index"),
     60         LuaAst::LuaTableExpr(_) => Some("table"),
     61         LuaAst::LuaBinaryExpr(_) => Some("binop"),
     62         LuaAst::LuaUnaryExpr(_) => Some("unop"),
     63         LuaAst::LuaParenExpr(_) => Some("parenthesis"),
     64         LuaAst::LuaCallExpr(_) => Some("call"),
     65         LuaAst::LuaLiteralExpr(_) => Some("literal"),
     66         LuaAst::LuaClosureExpr(_) => Some("closure"),
     67         LuaAst::LuaTableField(_) => Some("table_field"),
     68         LuaAst::LuaParamList(_) => Some("parameters"),
     69         LuaAst::LuaParamName(_) => Some("parameter"),
     70         LuaAst::LuaCallArgList(_) => Some("arguments"),
     71         LuaAst::LuaLocalName(_) => Some("identifier"),
     72         LuaAst::LuaLocalAttribute(_) => Some("attribute"),
     73         LuaAst::LuaElseIfClauseStat(_) => Some("elseif"),
     74         LuaAst::LuaElseClauseStat(_) => Some("else"),
     75         LuaAst::LuaComment(_) => Some("comment"),
     76         _ => None,
     77     }
     78 }
     79 
     80 fn should_print_contents(node: &LuaAst) -> bool {
     81     matches!(
     82         node,
     83         LuaAst::LuaLabelStat(_)
     84             | LuaAst::LuaGotoStat(_)
     85             | LuaAst::LuaNameExpr(_)
     86             | LuaAst::LuaIndexExpr(_)
     87             | LuaAst::LuaTableExpr(_)
     88             | LuaAst::LuaBinaryExpr(_)
     89             | LuaAst::LuaUnaryExpr(_)
     90             | LuaAst::LuaParenExpr(_)
     91             | LuaAst::LuaCallExpr(_)
     92             | LuaAst::LuaLiteralExpr(_)
     93             | LuaAst::LuaTableField(_)
     94             | LuaAst::LuaParamName(_)
     95             | LuaAst::LuaLocalName(_)
     96             | LuaAst::LuaLocalAttribute(_)
     97             | LuaAst::LuaComment(_)
     98     )
     99 }
    100 
    101 pub fn debug_tree(tree: &LuaSyntaxTree) {
    102     let chunk = tree.get_chunk_node();
    103     let mut depth = -1isize;
    104 
    105     for event in chunk.walk_descendants::<LuaAst>() {
    106         match event {
    107             WalkEvent::Enter(node) => {
    108                 if let Some(name) = node_name(&node) {
    109                     depth += 1;
    110 
    111                     let syntax = node.syntax();
    112                     let range = syntax.text_range();
    113                     let start: u32 = range.start().into();
    114                     let end: u32 = range.end().into();
    115 
    116                     let text = if should_print_contents(&node) {
    117                         format!("`{}`", syntax.text())
    118                     } else {
    119                         String::new()
    120                     };
    121 
    122                     println!(
    123                         "{}{name} [{start}-{end}] {}",
    124                         "   ".repeat(depth as usize),
    125                         text
    126                     )
    127                 }
    128             }
    129             WalkEvent::Leave(_) => {
    130                 depth -= 1;
    131             }
    132         }
    133     }
    134 }
    135 
    136 fn default_token_color(token: &LuaSyntaxToken) -> Color {
    137     let kind = match token.kind() {
    138         LuaKind::Syntax(_) => unreachable!(),
    139         LuaKind::Token(kind) => kind,
    140     };
    141 
    142     match kind {
    143         LuaTokenKind::TkWhitespace
    144         | LuaTokenKind::TkEndOfLine
    145         | LuaTokenKind::TkEof
    146         | LuaTokenKind::TkUnknown
    147         | LuaTokenKind::None => Color::Default,
    148 
    149         LuaTokenKind::TkBreak
    150         | LuaTokenKind::TkDo
    151         | LuaTokenKind::TkElse
    152         | LuaTokenKind::TkElseIf
    153         | LuaTokenKind::TkEnd
    154         | LuaTokenKind::TkFor
    155         | LuaTokenKind::TkFunction
    156         | LuaTokenKind::TkGoto
    157         | LuaTokenKind::TkIf
    158         | LuaTokenKind::TkIn
    159         | LuaTokenKind::TkLocal
    160         | LuaTokenKind::TkRepeat
    161         | LuaTokenKind::TkReturn
    162         | LuaTokenKind::TkThen
    163         | LuaTokenKind::TkUntil
    164         | LuaTokenKind::TkWhile
    165         | LuaTokenKind::TkGlobal => Color::Purple,
    166 
    167         LuaTokenKind::TkOr | LuaTokenKind::TkNot | LuaTokenKind::TkAnd => Color::Cyan,
    168 
    169         LuaTokenKind::TkFalse | LuaTokenKind::TkTrue | LuaTokenKind::TkNil => Color::Red,
    170 
    171         LuaTokenKind::TkInt | LuaTokenKind::TkFloat | LuaTokenKind::TkComplex => Color::LightYellow,
    172 
    173         LuaTokenKind::TkPlus
    174         | LuaTokenKind::TkMinus
    175         | LuaTokenKind::TkMul
    176         | LuaTokenKind::TkDiv
    177         | LuaTokenKind::TkIDiv
    178         | LuaTokenKind::TkDot
    179         | LuaTokenKind::TkConcat
    180         | LuaTokenKind::TkDots
    181         | LuaTokenKind::TkComma
    182         | LuaTokenKind::TkAssign
    183         | LuaTokenKind::TkEq
    184         | LuaTokenKind::TkGe
    185         | LuaTokenKind::TkLe
    186         | LuaTokenKind::TkNe
    187         | LuaTokenKind::TkShl
    188         | LuaTokenKind::TkShr
    189         | LuaTokenKind::TkLt
    190         | LuaTokenKind::TkGt
    191         | LuaTokenKind::TkMod
    192         | LuaTokenKind::TkPow
    193         | LuaTokenKind::TkLen
    194         | LuaTokenKind::TkBitAnd
    195         | LuaTokenKind::TkBitOr
    196         | LuaTokenKind::TkBitXor
    197         | LuaTokenKind::TkColon
    198         | LuaTokenKind::TkDbColon
    199         | LuaTokenKind::TkSemicolon
    200         | LuaTokenKind::TkLeftBracket
    201         | LuaTokenKind::TkRightBracket
    202         | LuaTokenKind::TkLeftParen
    203         | LuaTokenKind::TkRightParen
    204         | LuaTokenKind::TkLeftBrace
    205         | LuaTokenKind::TkRightBrace => Color::LightGray,
    206 
    207         LuaTokenKind::TkName => Color::LightGray,
    208 
    209         LuaTokenKind::TkString | LuaTokenKind::TkLongString => Color::Green,
    210 
    211         LuaTokenKind::TkShortComment | LuaTokenKind::TkLongComment | LuaTokenKind::TkShebang => {
    212             Color::DarkGray
    213         }
    214 
    215         // EmmyLua
    216         LuaTokenKind::TkTagClass
    217         | LuaTokenKind::TkTagEnum
    218         | LuaTokenKind::TkTagInterface
    219         | LuaTokenKind::TkTagAlias
    220         | LuaTokenKind::TkTagModule
    221         | LuaTokenKind::TkTagField
    222         | LuaTokenKind::TkTagType
    223         | LuaTokenKind::TkTagParam
    224         | LuaTokenKind::TkTagReturn
    225         | LuaTokenKind::TkTagOverload
    226         | LuaTokenKind::TkTagGeneric
    227         | LuaTokenKind::TkTagSee
    228         | LuaTokenKind::TkTagDeprecated
    229         | LuaTokenKind::TkTagAsync
    230         | LuaTokenKind::TkTagCast
    231         | LuaTokenKind::TkTagOther
    232         | LuaTokenKind::TkTagVisibility
    233         | LuaTokenKind::TkTagReadonly
    234         | LuaTokenKind::TkTagDiagnostic
    235         | LuaTokenKind::TkTagMeta
    236         | LuaTokenKind::TkTagVersion
    237         | LuaTokenKind::TkTagAs
    238         | LuaTokenKind::TkTagNodiscard
    239         | LuaTokenKind::TkTagOperator
    240         | LuaTokenKind::TkTagMapping
    241         | LuaTokenKind::TkTagNamespace
    242         | LuaTokenKind::TkTagUsing
    243         | LuaTokenKind::TkTagSource
    244         | LuaTokenKind::TkTagReturnCast => Color::LightMagenta,
    245         LuaTokenKind::TkDocVisibility => Color::Purple,
    246         _ => Color::DarkGray,
    247     }
    248 }
    249 
    250 fn modify_token_color(token: &LuaSyntaxToken, parent: &LuaSyntaxNode) -> Option<Color> {
    251     let tk_kind = match token.kind() {
    252         LuaKind::Syntax(_) => unreachable!(),
    253         LuaKind::Token(kind) => kind,
    254     };
    255 
    256     let node_kind = match parent.kind() {
    257         LuaKind::Syntax(kind) => kind,
    258         LuaKind::Token(_) => unreachable!(),
    259     };
    260 
    261     match (tk_kind, node_kind) {
    262         (LuaTokenKind::TkName, LuaSyntaxKind::TypeName) => Some(Color::Yellow),
    263         (LuaTokenKind::TkName, LuaSyntaxKind::DocTagParam) => Some(Color::Red),
    264         (LuaTokenKind::TkName, LuaSyntaxKind::ParamName) => Some(Color::Red),
    265         (LuaTokenKind::TkName, _) => {
    266             let parent_kind = if let Some(p) = parent.parent() {
    267                 match p.kind() {
    268                     LuaKind::Syntax(kind) => kind,
    269                     LuaKind::Token(_) => unreachable!(),
    270                 }
    271             } else {
    272                 return None;
    273             };
    274 
    275             match (node_kind, parent_kind) {
    276                 (_, LuaSyntaxKind::CallExpr) => Some(Color::Blue),
    277                 (_, LuaSyntaxKind::LocalFuncStat) => Some(Color::Blue),
    278                 (LuaSyntaxKind::IndexExpr, LuaSyntaxKind::FuncStat) => Some(Color::Blue),
    279                 _ => None,
    280             }
    281         }
    282         _ => None,
    283     }
    284 }
    285 
    286 // this function is rubbish but it works
    287 fn highlight_string(text: &str) -> StyledText {
    288     let mut styled = StyledText::new();
    289 
    290     let mut chars = text.chars();
    291     let mut current = String::new();
    292 
    293     while let Some(c) = chars.next() {
    294         if c != '\\' {
    295             current.push(c);
    296             continue;
    297         }
    298 
    299         styled.push((Style::new().fg(Color::Green), current.clone()));
    300         current.clear();
    301 
    302         let modifier = if let Some(c) = chars.next() {
    303             c
    304         } else {
    305             // incomplete string
    306             styled.push((Style::new().fg(Color::Cyan), String::from("\\")));
    307             break;
    308         };
    309 
    310         if modifier == 'x' || modifier == 'X' {
    311             let hex1 = chars.next().map(|c| c.to_string()).unwrap_or_default();
    312             let hex2 = chars.next().map(|c| c.to_string()).unwrap_or_default();
    313 
    314             styled.push((
    315                 Style::new().fg(Color::Cyan),
    316                 format!("\\{modifier}{hex1}{hex2}"),
    317             ));
    318         } else if modifier == 'u' || modifier == 'U' {
    319             for c in chars.by_ref() {
    320                 current.push(c);
    321                 if c == '}' {
    322                     break;
    323                 }
    324             }
    325 
    326             styled.push((Style::new().fg(Color::Cyan), format!("\\u{current}")));
    327             current.clear();
    328         } else {
    329             styled.push((Style::new().fg(Color::Cyan), format!("\\{modifier}")));
    330         }
    331     }
    332 
    333     styled.push((Style::new().fg(Color::Green), current.clone()));
    334 
    335     styled
    336 }
    337 
    338 pub struct LuaHighlighter;
    339 
    340 impl reedline::Highlighter for LuaHighlighter {
    341     fn highlight(&self, line: &str, _cursor: usize) -> StyledText {
    342         let tree = LuaParser::parse(line, config());
    343         let root = tree.get_red_root();
    344 
    345         let mut text = StyledText::new();
    346 
    347         for token in root
    348             .descendants_with_tokens()
    349             .filter_map(|d| d.into_token())
    350         {
    351             let mut color = default_token_color(&token);
    352 
    353             if let Some(parent) = token.parent() {
    354                 if let Some(new_color) = modify_token_color(&token, &parent) {
    355                     color = new_color;
    356                 }
    357             }
    358 
    359             match token.kind() {
    360                 LuaKind::Syntax(_) => unreachable!(),
    361                 LuaKind::Token(kind) => {
    362                     if let LuaTokenKind::TkString = kind {
    363                         let styled = highlight_string(token.text());
    364 
    365                         text.buffer.extend(styled.buffer);
    366                         continue;
    367                     }
    368                 }
    369             }
    370 
    371             text.push((Style::new().fg(color), token.text().to_string()));
    372         }
    373 
    374         text
    375     }
    376 }