|
@@ -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();
|