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 }