diff options
-rw-r--r-- | Userland/Libraries/LibCMake/SyntaxHighlighter.cpp | 106 |
1 files changed, 106 insertions, 0 deletions
diff --git a/Userland/Libraries/LibCMake/SyntaxHighlighter.cpp b/Userland/Libraries/LibCMake/SyntaxHighlighter.cpp index d5f85cbb41..60856847db 100644 --- a/Userland/Libraries/LibCMake/SyntaxHighlighter.cpp +++ b/Userland/Libraries/LibCMake/SyntaxHighlighter.cpp @@ -49,7 +49,15 @@ void SyntaxHighlighter::rehighlight(Gfx::Palette const& palette) { auto text = m_client->get_text(); auto tokens = Lexer::lex(text).release_value_but_fixme_should_propagate_errors(); + auto& document = m_client->get_document(); + struct OpenBlock { + Token token; + int open_paren_count { 0 }; + Optional<Token> ending_paren {}; + }; + Vector<OpenBlock> open_blocks; + Vector<GUI::TextDocumentFoldingRegion> folding_regions; Vector<GUI::TextDocumentSpan> spans; auto highlight_span = [&](Token::Type type, Position const& start, Position const& end) { GUI::TextDocumentSpan span; @@ -71,6 +79,43 @@ void SyntaxHighlighter::rehighlight(Gfx::Palette const& palette) spans.append(move(span)); }; + auto create_region_from_block_type = [&](auto control_keywords, Token const& end_token) { + if (open_blocks.is_empty()) + return; + + // Find the most recent open block with a matching keyword. + Optional<size_t> found_index; + OpenBlock open_block; + for (int i = open_blocks.size() - 1; i >= 0; i--) { + for (auto value : control_keywords) { + if (open_blocks[i].token.control_keyword == value) { + found_index = i; + open_block = open_blocks[i]; + break; + } + } + if (found_index.has_value()) + break; + } + + if (found_index.has_value()) { + // Remove the found token and all after it. + open_blocks.shrink(found_index.value()); + + // Create a region. + GUI::TextDocumentFoldingRegion region; + if (open_block.ending_paren.has_value()) { + region.range.set_start({ open_block.ending_paren->end.line, open_block.ending_paren->end.column }); + } else { + // The opening command is invalid, it does not have a closing paren. + // So, we just start the region at the end of the line where the command identifier was. (eg, `if`) + region.range.set_start({ open_block.token.end.line, document.line(open_block.token.end.line).last_non_whitespace_column().value() }); + } + region.range.set_end({ end_token.start.line, end_token.start.column }); + folding_regions.append(move(region)); + } + }; + for (auto const& token : tokens) { if (token.type == Token::Type::QuotedArgument || token.type == Token::Type::UnquotedArgument) { // Alternately highlight the regular/variable-reference parts. @@ -86,8 +131,69 @@ void SyntaxHighlighter::rehighlight(Gfx::Palette const& palette) } highlight_span(token.type, token.start, token.end); + + if (!open_blocks.is_empty() && !open_blocks.last().ending_paren.has_value()) { + auto& open_block = open_blocks.last(); + if (token.type == Token::Type::OpenParen) { + open_block.open_paren_count++; + } else if (token.type == Token::Type::CloseParen) { + open_block.open_paren_count--; + if (open_block.open_paren_count == 0) + open_block.ending_paren = token; + } + } + + // Create folding regions from control-keyword blocks. + if (token.type == Token::Type::ControlKeyword) { + switch (token.control_keyword.value()) { + case ControlKeywordType::If: + open_blocks.empend(token); + break; + case ControlKeywordType::ElseIf: + case ControlKeywordType::Else: + create_region_from_block_type(Array { ControlKeywordType::If, ControlKeywordType::ElseIf }, token); + open_blocks.empend(token); + break; + case ControlKeywordType::EndIf: + create_region_from_block_type(Array { ControlKeywordType::If, ControlKeywordType::ElseIf, ControlKeywordType::Else }, token); + break; + case ControlKeywordType::ForEach: + open_blocks.empend(token); + break; + case ControlKeywordType::EndForEach: + create_region_from_block_type(Array { ControlKeywordType::ForEach }, token); + break; + case ControlKeywordType::While: + open_blocks.empend(token); + break; + case ControlKeywordType::EndWhile: + create_region_from_block_type(Array { ControlKeywordType::While }, token); + break; + case ControlKeywordType::Macro: + open_blocks.empend(token); + break; + case ControlKeywordType::EndMacro: + create_region_from_block_type(Array { ControlKeywordType::Macro }, token); + break; + case ControlKeywordType::Function: + open_blocks.empend(token); + break; + case ControlKeywordType::EndFunction: + create_region_from_block_type(Array { ControlKeywordType::Function }, token); + break; + case ControlKeywordType::Block: + open_blocks.empend(token); + break; + case ControlKeywordType::EndBlock: + create_region_from_block_type(Array { ControlKeywordType::Block }, token); + break; + default: + break; + } + } } m_client->do_set_spans(move(spans)); + m_client->do_set_folding_regions(move(folding_regions)); m_has_brace_buddies = false; highlight_matching_token_pair(); |