summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Userland/Libraries/LibCMake/SyntaxHighlighter.cpp106
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();