Serenity Operating System
at master 183 lines 8.2 kB view raw
1/* 2 * Copyright (c) 2022, Itamar S. <itamar8910@gmail.com> 3 * Copyright (c) 2022, the SerenityOS developers. 4 * 5 * SPDX-License-Identifier: BSD-2-Clause 6 */ 7 8#include "SemanticSyntaxHighlighter.h" 9#include "Lexer.h" 10#include <LibDiff/Generator.h> 11#include <LibGUI/AutocompleteProvider.h> 12#include <LibGfx/Palette.h> 13 14namespace Cpp { 15 16void SemanticSyntaxHighlighter::rehighlight(Palette const& palette) 17{ 18 Vector<CodeComprehension::TokenInfo> new_tokens_info; 19 auto text = m_client->get_text(); 20 { 21 Threading::MutexLocker locker(m_lock); 22 Cpp::Lexer lexer(text); 23 lexer.set_ignore_whitespace(true); 24 auto current_tokens = lexer.lex(); 25 26 // Identify folding regions 27 Vector<Token> folding_region_start_tokens; 28 Vector<GUI::TextDocumentFoldingRegion> folding_regions; 29 for (auto& token : current_tokens) { 30 if (token.type() == Token::Type::LeftCurly) { 31 folding_region_start_tokens.append(token); 32 } else if (token.type() == Token::Type::RightCurly) { 33 if (!folding_region_start_tokens.is_empty()) { 34 auto start_token = folding_region_start_tokens.take_last(); 35 GUI::TextDocumentFoldingRegion folding_region; 36 folding_region.range.set_start({ start_token.end().line, start_token.end().column }); 37 folding_region.range.set_end({ token.start().line, token.start().column }); 38 folding_regions.append(move(folding_region)); 39 } 40 } 41 } 42 m_client->do_set_folding_regions(move(folding_regions)); 43 44 StringBuilder current_tokens_as_lines; 45 StringBuilder previous_tokens_as_lines; 46 47 for (auto& token : current_tokens) 48 current_tokens_as_lines.appendff("{}\n", token.type_as_deprecated_string()); 49 50 for (Cpp::Token const& token : m_saved_tokens) 51 previous_tokens_as_lines.appendff("{}\n", token.type_as_deprecated_string()); 52 53 auto previous = previous_tokens_as_lines.to_deprecated_string(); 54 auto current = current_tokens_as_lines.to_deprecated_string(); 55 56 // FIXME: Computing the diff on the entire document's tokens is quite inefficient. 57 // An improvement over this could be only including the tokens that are in edited text ranges in the diff. 58 auto diff_hunks = Diff::from_text(previous.view(), current.view()); 59 for (auto& token : current_tokens) { 60 new_tokens_info.append(CodeComprehension::TokenInfo { CodeComprehension::TokenInfo::SemanticType::Unknown, 61 token.start().line, token.start().column, token.end().line, token.end().column }); 62 } 63 size_t previous_token_index = 0; 64 size_t current_token_index = 0; 65 for (auto const& hunk : diff_hunks) { 66 for (; previous_token_index < hunk.original_start_line; ++previous_token_index) { 67 new_tokens_info[current_token_index].type = m_tokens_info[previous_token_index].type; 68 ++current_token_index; 69 } 70 for (size_t i = 0; i < hunk.added_lines.size(); ++i) { 71 ++current_token_index; 72 } 73 for (size_t i = 0; i < hunk.removed_lines.size(); ++i) { 74 ++previous_token_index; 75 } 76 } 77 while (current_token_index < new_tokens_info.size() && previous_token_index < m_tokens_info.size()) { 78 new_tokens_info[current_token_index].type = m_tokens_info[previous_token_index].type; 79 80 ++previous_token_index; 81 ++current_token_index; 82 } 83 } 84 85 update_spans(new_tokens_info, palette); 86} 87 88static Syntax::TextStyle style_for_token_type(Gfx::Palette const& palette, CodeComprehension::TokenInfo::SemanticType type) 89{ 90 switch (type) { 91 case CodeComprehension::TokenInfo::SemanticType::Unknown: 92 return { palette.base_text(), false }; 93 case CodeComprehension::TokenInfo::SemanticType::Keyword: 94 return { palette.syntax_keyword(), true }; 95 case CodeComprehension::TokenInfo::SemanticType::Type: 96 return { palette.syntax_type(), true }; 97 case CodeComprehension::TokenInfo::SemanticType::Identifier: 98 return { palette.syntax_identifier(), false }; 99 case CodeComprehension::TokenInfo::SemanticType::String: 100 return { palette.syntax_string(), false }; 101 case CodeComprehension::TokenInfo::SemanticType::Number: 102 return { palette.syntax_number(), false }; 103 case CodeComprehension::TokenInfo::SemanticType::IncludePath: 104 return { palette.syntax_preprocessor_value(), false }; 105 case CodeComprehension::TokenInfo::SemanticType::PreprocessorStatement: 106 return { palette.syntax_preprocessor_statement(), false }; 107 case CodeComprehension::TokenInfo::SemanticType::Comment: 108 return { palette.syntax_comment(), false }; 109 case CodeComprehension::TokenInfo::SemanticType::Function: 110 return { palette.syntax_function(), false }; 111 case CodeComprehension::TokenInfo::SemanticType::Variable: 112 return { palette.syntax_variable(), false }; 113 case CodeComprehension::TokenInfo::SemanticType::CustomType: 114 return { palette.syntax_custom_type(), false }; 115 case CodeComprehension::TokenInfo::SemanticType::Namespace: 116 return { palette.syntax_namespace(), false }; 117 case CodeComprehension::TokenInfo::SemanticType::Member: 118 return { palette.syntax_member(), false }; 119 case CodeComprehension::TokenInfo::SemanticType::Parameter: 120 return { palette.syntax_parameter(), false }; 121 case CodeComprehension::TokenInfo::SemanticType::PreprocessorMacro: 122 return { palette.syntax_preprocessor_value(), false }; 123 default: 124 VERIFY_NOT_REACHED(); 125 return { palette.base_text(), false }; 126 } 127} 128void SemanticSyntaxHighlighter::update_spans(Vector<CodeComprehension::TokenInfo> const& tokens_info, Gfx::Palette const& palette) 129{ 130 Vector<GUI::TextDocumentSpan> spans; 131 for (auto& token : tokens_info) { 132 // FIXME: The +1 for the token end column is a quick hack due to not wanting to modify the lexer (which is also used by the parser). Maybe there's a better way to do this. 133 GUI::TextDocumentSpan span; 134 span.range.set_start({ token.start_line, token.start_column }); 135 span.range.set_end({ token.end_line, token.end_column + 1 }); 136 auto style = style_for_token_type(palette, token.type); 137 span.attributes.color = style.color; 138 span.attributes.bold = style.bold; 139 span.is_skippable = token.type == CodeComprehension::TokenInfo::SemanticType::Whitespace; 140 span.data = static_cast<u64>(token.type); 141 spans.append(span); 142 } 143 m_client->do_set_spans(move(spans)); 144 145 m_has_brace_buddies = false; 146 highlight_matching_token_pair(); 147 148 m_client->do_update(); 149} 150 151void SemanticSyntaxHighlighter::update_tokens_info(Vector<CodeComprehension::TokenInfo> tokens_info) 152{ 153 { 154 Threading::MutexLocker locker(m_lock); 155 m_tokens_info = move(tokens_info); 156 157 m_saved_tokens_text = m_client->get_text(); 158 Cpp::Lexer lexer(m_saved_tokens_text); 159 lexer.set_ignore_whitespace(true); 160 m_saved_tokens = lexer.lex(); 161 } 162} 163 164bool SemanticSyntaxHighlighter::is_identifier(u64 token_type) const 165{ 166 auto type = static_cast<CodeComprehension::TokenInfo::SemanticType>(token_type); 167 168 return type == CodeComprehension::TokenInfo::SemanticType::Identifier 169 || type == CodeComprehension::TokenInfo::SemanticType::Function 170 || type == CodeComprehension::TokenInfo::SemanticType::Variable 171 || type == CodeComprehension::TokenInfo::SemanticType::CustomType 172 || type == CodeComprehension::TokenInfo::SemanticType::Namespace 173 || type == CodeComprehension::TokenInfo::SemanticType::Member 174 || type == CodeComprehension::TokenInfo::SemanticType::Parameter 175 || type == CodeComprehension::TokenInfo::SemanticType::PreprocessorMacro; 176} 177 178bool SemanticSyntaxHighlighter::is_navigatable(u64 token_type) const 179{ 180 return static_cast<CodeComprehension::TokenInfo::SemanticType>(token_type) == CodeComprehension::TokenInfo::SemanticType::IncludePath; 181} 182 183}