ladybird/Userland/Libraries/LibGUI/INISyntaxHighlighter.cpp
Sam Atkins 1c0c59eabe LibGUI: Add folding regions to INI syntax highlighter
This lets you fold sections into a single line. We attempt to keep 1
blank line after the folded section if possible.
2023-03-03 21:56:42 +01:00

115 lines
4.2 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* Copyright (c) 2020, Hüseyin Aslıtürk <asliturk@hotmail.com>
* Copyright (c) 2022, the SerenityOS developers.
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <LibGUI/INILexer.h>
#include <LibGUI/INISyntaxHighlighter.h>
#include <LibGfx/Palette.h>
namespace GUI {
static Syntax::TextStyle style_for_token_type(Gfx::Palette const& palette, IniToken::Type type)
{
switch (type) {
case IniToken::Type::LeftBracket:
case IniToken::Type::RightBracket:
case IniToken::Type::Section:
return { palette.syntax_keyword(), true };
case IniToken::Type::Name:
return { palette.syntax_identifier() };
case IniToken::Type::Value:
return { palette.syntax_string() };
case IniToken::Type::Comment:
return { palette.syntax_comment() };
case IniToken::Type::Equal:
return { palette.syntax_operator(), true };
default:
return { palette.base_text() };
}
}
bool IniSyntaxHighlighter::is_identifier(u64 token) const
{
auto ini_token = static_cast<GUI::IniToken::Type>(token);
return ini_token == GUI::IniToken::Type::Name;
}
void IniSyntaxHighlighter::rehighlight(Palette const& palette)
{
auto text = m_client->get_text();
IniLexer lexer(text);
auto tokens = lexer.lex();
Optional<IniToken> previous_section_token;
IniToken previous_token;
Vector<TextDocumentFoldingRegion> folding_regions;
Vector<GUI::TextDocumentSpan> spans;
for (auto& token : tokens) {
GUI::TextDocumentSpan span;
span.range.set_start({ token.m_start.line, token.m_start.column });
span.range.set_end({ token.m_end.line, token.m_end.column });
auto style = style_for_token_type(palette, token.m_type);
span.attributes.color = style.color;
span.attributes.bold = style.bold;
span.is_skippable = token.m_type == IniToken::Type::Whitespace;
span.data = static_cast<u64>(token.m_type);
spans.append(span);
if (token.m_type == IniToken::Type::RightBracket && previous_token.m_type == IniToken::Type::Section) {
previous_section_token = token;
} else if (token.m_type == IniToken::Type::LeftBracket) {
if (previous_section_token.has_value()) {
TextDocumentFoldingRegion region;
region.range.set_start({ previous_section_token->m_end.line, previous_section_token->m_end.column });
// If possible, leave a blank line between sections.
// `end_line - start_line > 1` means the whitespace contains at least 1 blank line,
// so we can end the region 1 line before the end of that whitespace token.
// (Otherwise, we'd end the region at the start of the line that begins the next section.)
auto end_line = token.m_start.line;
if (previous_token.m_type == IniToken::Type::Whitespace
&& (previous_token.m_end.line - previous_token.m_start.line > 1))
end_line--;
region.range.set_end({ end_line, token.m_start.column });
folding_regions.append(move(region));
}
previous_section_token = token;
}
previous_token = token;
}
if (previous_section_token.has_value()) {
TextDocumentFoldingRegion region;
auto& end_token = tokens.last();
region.range.set_start({ previous_section_token->m_end.line, previous_section_token->m_end.column });
region.range.set_end({ end_token.m_end.line, end_token.m_end.column });
folding_regions.append(move(region));
}
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();
m_client->do_update();
}
Vector<IniSyntaxHighlighter::MatchingTokenPair> IniSyntaxHighlighter::matching_token_pairs_impl() const
{
static Vector<MatchingTokenPair> pairs;
if (pairs.is_empty()) {
pairs.append({ static_cast<u64>(IniToken::Type::LeftBracket), static_cast<u64>(IniToken::Type::RightBracket) });
}
return pairs;
}
bool IniSyntaxHighlighter::token_types_equal(u64 token1, u64 token2) const
{
return static_cast<GUI::IniToken::Type>(token1) == static_cast<GUI::IniToken::Type>(token2);
}
}