|
| 1 | +use emmylua_parser::{ |
| 2 | + LexerState, LuaAstNode, LuaAstToken, LuaComment, LuaLiteralExpr, LuaLiteralToken, |
| 3 | + LuaStringToken, Reader, SourceRange, |
| 4 | +}; |
| 5 | +use emmylua_parser_desc::{ |
| 6 | + CodeBlockHighlightKind, CodeBlockLang, DescItem, DescItemKind, ResultContainer, process_code, |
| 7 | +}; |
| 8 | +use lsp_types::SemanticTokenType; |
| 9 | +use rowan::{TextRange, TextSize}; |
| 10 | + |
| 11 | +use crate::handlers::semantic_token::semantic_token_builder::SemanticBuilder; |
| 12 | + |
| 13 | +pub fn inject_language( |
| 14 | + builder: &mut SemanticBuilder, |
| 15 | + lang_name: &str, |
| 16 | + comment: LuaComment, |
| 17 | +) -> Option<()> { |
| 18 | + // Implementation for injecting the language |
| 19 | + let owner = comment.get_owner()?; |
| 20 | + let lang = CodeBlockLang::try_parse(lang_name)?; |
| 21 | + for literal in owner.descendants::<LuaLiteralExpr>() { |
| 22 | + if let Some(LuaLiteralToken::String(str_token)) = literal.get_literal() { |
| 23 | + let code_block_info = divide_into_quote_and_code_block(&str_token)?; |
| 24 | + let code_block_range = code_block_info.code_block; |
| 25 | + let code_block_source = SourceRange::from_start_end( |
| 26 | + u32::from(code_block_range.start()) as usize, |
| 27 | + u32::from(code_block_range.end()) as usize, |
| 28 | + ); |
| 29 | + let code_block_str = str_token.slice(code_block_range)?; |
| 30 | + let reader = Reader::new(code_block_str); |
| 31 | + let mut result = InjectResult::new(); |
| 32 | + process_code( |
| 33 | + &mut result, |
| 34 | + code_block_source, |
| 35 | + reader, |
| 36 | + LexerState::Normal, |
| 37 | + lang, |
| 38 | + ); |
| 39 | + |
| 40 | + for desc_item in result.results() { |
| 41 | + if let DescItemKind::CodeBlockHl(highlight_kind) = desc_item.kind { |
| 42 | + let token_type = match highlight_kind { |
| 43 | + CodeBlockHighlightKind::Keyword => SemanticTokenType::KEYWORD, |
| 44 | + CodeBlockHighlightKind::String => SemanticTokenType::STRING, |
| 45 | + CodeBlockHighlightKind::Number => SemanticTokenType::NUMBER, |
| 46 | + CodeBlockHighlightKind::Comment => SemanticTokenType::COMMENT, |
| 47 | + CodeBlockHighlightKind::Function => SemanticTokenType::FUNCTION, |
| 48 | + CodeBlockHighlightKind::Class => SemanticTokenType::CLASS, |
| 49 | + CodeBlockHighlightKind::Enum => SemanticTokenType::ENUM, |
| 50 | + CodeBlockHighlightKind::Variable => SemanticTokenType::VARIABLE, |
| 51 | + CodeBlockHighlightKind::Property => SemanticTokenType::PROPERTY, |
| 52 | + CodeBlockHighlightKind::Decorator => SemanticTokenType::DECORATOR, |
| 53 | + CodeBlockHighlightKind::Operators => SemanticTokenType::OPERATOR, |
| 54 | + _ => continue, // Fallback for other kinds |
| 55 | + }; |
| 56 | + |
| 57 | + let sub_token_range = TextRange::new( |
| 58 | + desc_item.range.start() + code_block_range.start(), |
| 59 | + desc_item.range.end() + code_block_range.start(), |
| 60 | + ); |
| 61 | + if let Some(token_text) = str_token.slice(sub_token_range) { |
| 62 | + builder.push_at_range(token_text, sub_token_range, token_type, &[]); |
| 63 | + } |
| 64 | + } |
| 65 | + } |
| 66 | + |
| 67 | + for quote_range in code_block_info.quote_ranges { |
| 68 | + let len = u32::from(quote_range.end() - quote_range.start()); |
| 69 | + builder.push_at_position(quote_range.start(), len, SemanticTokenType::STRING, None); |
| 70 | + } |
| 71 | + builder.add_lang_inject_range(str_token.get_range()); |
| 72 | + } |
| 73 | + } |
| 74 | + |
| 75 | + Some(()) |
| 76 | +} |
| 77 | + |
| 78 | +struct InjectResult { |
| 79 | + result: Vec<DescItem>, |
| 80 | +} |
| 81 | + |
| 82 | +impl ResultContainer for InjectResult { |
| 83 | + fn results(&self) -> &Vec<DescItem> { |
| 84 | + &self.result |
| 85 | + } |
| 86 | + |
| 87 | + fn results_mut(&mut self) -> &mut Vec<DescItem> { |
| 88 | + &mut self.result |
| 89 | + } |
| 90 | + |
| 91 | + fn cursor_position(&self) -> Option<usize> { |
| 92 | + None |
| 93 | + } |
| 94 | +} |
| 95 | + |
| 96 | +impl InjectResult { |
| 97 | + pub fn new() -> Self { |
| 98 | + InjectResult { result: Vec::new() } |
| 99 | + } |
| 100 | +} |
| 101 | + |
| 102 | +struct CodeBlockInfo { |
| 103 | + code_block: TextRange, |
| 104 | + quote_ranges: Vec<TextRange>, |
| 105 | +} |
| 106 | + |
| 107 | +fn divide_into_quote_and_code_block(str_token: &LuaStringToken) -> Option<CodeBlockInfo> { |
| 108 | + let str_token_range = str_token.get_range(); |
| 109 | + let text = str_token.get_text(); |
| 110 | + let mut quote_ranges = Vec::new(); |
| 111 | + |
| 112 | + if text.is_empty() { |
| 113 | + return None; |
| 114 | + } |
| 115 | + |
| 116 | + let mut code_block_start = str_token_range.start(); |
| 117 | + let mut code_block_end = str_token_range.end(); |
| 118 | + let range_start = str_token_range.start(); |
| 119 | + if text.starts_with("[[") || text.starts_with("[=") { |
| 120 | + let mut equal_count = 0; |
| 121 | + let mut start_end = 2; |
| 122 | + |
| 123 | + for c in text.chars().skip(1) { |
| 124 | + if c == '=' { |
| 125 | + equal_count += 1; |
| 126 | + } else if c == '[' { |
| 127 | + start_end = 2 + equal_count; |
| 128 | + break; |
| 129 | + } else { |
| 130 | + break; |
| 131 | + } |
| 132 | + } |
| 133 | + |
| 134 | + let start_quote_range = |
| 135 | + TextRange::new(range_start, range_start + TextSize::from(start_end as u32)); |
| 136 | + quote_ranges.push(start_quote_range); |
| 137 | + code_block_start = start_quote_range.end(); |
| 138 | + |
| 139 | + let end_pattern = format!("]{}]", "=".repeat(equal_count)); |
| 140 | + if let Some(end_pos) = text.rfind(&end_pattern) { |
| 141 | + let end_quote_start = range_start + rowan::TextSize::from(end_pos as u32); |
| 142 | + let end_quote_range = TextRange::new( |
| 143 | + end_quote_start, |
| 144 | + range_start + rowan::TextSize::from(text.len() as u32), |
| 145 | + ); |
| 146 | + quote_ranges.push(end_quote_range); |
| 147 | + code_block_end = end_quote_range.start(); |
| 148 | + } |
| 149 | + } else if text.starts_with('"') || text.starts_with('\'') { |
| 150 | + let quote_char = text.chars().next().unwrap(); |
| 151 | + let start_quote_range = TextRange::new(range_start, range_start + TextSize::from(1)); |
| 152 | + quote_ranges.push(start_quote_range); |
| 153 | + code_block_start = start_quote_range.end(); |
| 154 | + |
| 155 | + if text.len() > 1 && text.ends_with(quote_char) { |
| 156 | + let end_quote_start = range_start + TextSize::from((text.len() - 1) as u32); |
| 157 | + let end_quote_range = TextRange::new( |
| 158 | + end_quote_start, |
| 159 | + range_start + TextSize::from(text.len() as u32), |
| 160 | + ); |
| 161 | + quote_ranges.push(end_quote_range); |
| 162 | + code_block_end = end_quote_range.start(); |
| 163 | + } |
| 164 | + } |
| 165 | + |
| 166 | + if code_block_start > code_block_end { |
| 167 | + return None; |
| 168 | + } |
| 169 | + |
| 170 | + Some(CodeBlockInfo { |
| 171 | + code_block: TextRange::new(code_block_start, code_block_end), |
| 172 | + quote_ranges, |
| 173 | + }) |
| 174 | +} |
0 commit comments