2020-03-13 19:07:44 -03:00
|
|
|
/*
|
|
|
|
|
* Copyright (c) 2020, the SerenityOS developers.
|
2023-02-23 12:35:57 -03:00
|
|
|
* Copyright (c) 2023, Sam Atkins <atkinssj@serenityos.org>
|
2020-03-13 19:07:44 -03:00
|
|
|
*
|
2021-04-22 05:24:48 -03:00
|
|
|
* SPDX-License-Identifier: BSD-2-Clause
|
2020-03-13 19:07:44 -03:00
|
|
|
*/
|
|
|
|
|
|
2021-01-16 11:51:56 -03:00
|
|
|
#include <AK/Debug.h>
|
2026-06-06 10:38:27 -03:00
|
|
|
#include <AK/UnicodeUtils.h>
|
Libraries: Clean up UTF-16 source text paths
Store parser errors, source range filenames, source code filenames,
module source, and Rust parser errors as UTF-16 where they flow back
into JavaScript-visible strings. Keep byte-oriented source buffers
byte-backed.
Remove temporary PrimitiveString, ByteString, and UTF-8 detours from
JSON, RegExp, module debug logging, print formatting, and tests.
2026-06-21 14:03:19 -03:00
|
|
|
#include <LibCore/ImmutableBytes.h>
|
2020-03-15 20:05:06 -03:00
|
|
|
#include <LibGfx/Palette.h>
|
2026-04-02 14:34:56 -03:00
|
|
|
#include <LibJS/RustFFI.h>
|
2026-03-19 16:03:42 -03:00
|
|
|
#include <LibJS/SourceCode.h>
|
2021-02-07 12:56:02 -03:00
|
|
|
#include <LibJS/SyntaxHighlighter.h>
|
2020-03-12 19:52:03 -03:00
|
|
|
#include <LibJS/Token.h>
|
Libraries: Clean up UTF-16 source text paths
Store parser errors, source range filenames, source code filenames,
module source, and Rust parser errors as UTF-16 where they flow back
into JavaScript-visible strings. Keep byte-oriented source buffers
byte-backed.
Remove temporary PrimitiveString, ByteString, and UTF-8 detours from
JSON, RegExp, module debug logging, print formatting, and tests.
2026-06-21 14:03:19 -03:00
|
|
|
#include <LibTextCodec/Decoder.h>
|
2020-03-12 19:52:03 -03:00
|
|
|
|
2021-02-07 12:56:02 -03:00
|
|
|
namespace JS {
|
2020-03-12 19:52:03 -03:00
|
|
|
|
2026-03-19 16:03:42 -03:00
|
|
|
static Gfx::TextAttributes style_for_token_category(Gfx::Palette const& palette, TokenCategory category)
|
2020-03-12 19:52:03 -03:00
|
|
|
{
|
2026-03-19 16:03:42 -03:00
|
|
|
switch (category) {
|
2022-04-03 11:19:33 -03:00
|
|
|
case TokenCategory::Invalid:
|
2020-03-15 20:05:06 -03:00
|
|
|
return { palette.syntax_comment() };
|
2022-04-03 11:19:33 -03:00
|
|
|
case TokenCategory::Number:
|
2020-03-15 20:05:06 -03:00
|
|
|
return { palette.syntax_number() };
|
2022-04-03 11:19:33 -03:00
|
|
|
case TokenCategory::String:
|
2020-03-15 20:05:06 -03:00
|
|
|
return { palette.syntax_string() };
|
2022-04-03 11:19:33 -03:00
|
|
|
case TokenCategory::Punctuation:
|
2020-03-15 20:05:06 -03:00
|
|
|
return { palette.syntax_punctuation() };
|
2022-04-03 11:19:33 -03:00
|
|
|
case TokenCategory::Operator:
|
2020-03-15 20:05:06 -03:00
|
|
|
return { palette.syntax_operator() };
|
2022-04-03 11:19:33 -03:00
|
|
|
case TokenCategory::Keyword:
|
2023-03-15 09:49:17 -03:00
|
|
|
return { palette.syntax_keyword(), {}, true };
|
2022-04-03 11:19:33 -03:00
|
|
|
case TokenCategory::ControlKeyword:
|
2023-03-15 09:49:17 -03:00
|
|
|
return { palette.syntax_control_keyword(), {}, true };
|
2022-04-03 11:19:33 -03:00
|
|
|
case TokenCategory::Identifier:
|
2020-03-15 20:05:06 -03:00
|
|
|
return { palette.syntax_identifier() };
|
2020-03-12 19:52:03 -03:00
|
|
|
default:
|
2020-03-15 20:05:06 -03:00
|
|
|
return { palette.base_text() };
|
2020-03-12 19:52:03 -03:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-19 16:03:42 -03:00
|
|
|
struct RehighlightState {
|
|
|
|
|
Gfx::Palette const& palette;
|
|
|
|
|
Vector<Syntax::TextDocumentSpan>& spans;
|
|
|
|
|
u16 const* source;
|
2023-08-29 06:13:41 -03:00
|
|
|
Syntax::TextPosition position { 0, 0 };
|
2026-03-19 16:03:42 -03:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static void advance_position(Syntax::TextPosition& position, u16 const* source, u32 start, u32 len)
|
|
|
|
|
{
|
|
|
|
|
for (u32 i = 0; i < len; ++i) {
|
2026-06-06 10:38:27 -03:00
|
|
|
if (auto code_unit = source[start + i]; code_unit == '\n') {
|
2020-03-12 19:52:03 -03:00
|
|
|
position.set_line(position.line() + 1);
|
|
|
|
|
position.set_column(0);
|
2026-03-19 16:03:42 -03:00
|
|
|
} else {
|
2020-03-12 19:52:03 -03:00
|
|
|
position.set_column(position.column() + 1);
|
2026-06-06 10:38:27 -03:00
|
|
|
|
|
|
|
|
if (AK::UnicodeUtils::is_utf16_high_surrogate(code_unit)
|
|
|
|
|
&& i + 1 < len
|
|
|
|
|
&& AK::UnicodeUtils::is_utf16_low_surrogate(source[start + i + 1])) {
|
|
|
|
|
++i;
|
|
|
|
|
}
|
2026-03-19 16:03:42 -03:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-03-12 19:52:03 -03:00
|
|
|
|
2026-03-19 16:03:42 -03:00
|
|
|
static void on_token(void* ctx, FFI::FFIToken const* ffi_token)
|
|
|
|
|
{
|
|
|
|
|
auto& state = *static_cast<RehighlightState*>(ctx);
|
|
|
|
|
auto token_type = static_cast<TokenType>(ffi_token->token_type);
|
|
|
|
|
auto category = static_cast<TokenCategory>(ffi_token->category);
|
|
|
|
|
|
|
|
|
|
// Emit trivia span
|
|
|
|
|
if (ffi_token->trivia_length > 0) {
|
|
|
|
|
auto trivia_start = state.position;
|
|
|
|
|
advance_position(state.position, state.source, ffi_token->trivia_offset, ffi_token->trivia_length);
|
|
|
|
|
Syntax::TextDocumentSpan span;
|
|
|
|
|
span.range.set_start(trivia_start);
|
|
|
|
|
span.range.set_end({ state.position.line(), state.position.column() });
|
|
|
|
|
span.attributes = style_for_token_category(state.palette, TokenCategory::Trivia);
|
|
|
|
|
span.is_skippable = true;
|
|
|
|
|
span.data = pack_token_data(TokenType::Trivia, TokenCategory::Trivia);
|
|
|
|
|
state.spans.append(span);
|
|
|
|
|
}
|
2020-03-15 20:05:06 -03:00
|
|
|
|
2026-03-19 16:03:42 -03:00
|
|
|
// Emit token span
|
|
|
|
|
auto token_start = state.position;
|
|
|
|
|
if (ffi_token->length > 0) {
|
|
|
|
|
advance_position(state.position, state.source, ffi_token->offset, ffi_token->length);
|
2023-08-29 06:13:41 -03:00
|
|
|
Syntax::TextDocumentSpan span;
|
2026-03-19 16:03:42 -03:00
|
|
|
span.range.set_start(token_start);
|
|
|
|
|
span.range.set_end({ state.position.line(), state.position.column() });
|
|
|
|
|
span.attributes = style_for_token_category(state.palette, category);
|
|
|
|
|
span.is_skippable = false;
|
|
|
|
|
span.data = pack_token_data(token_type, category);
|
|
|
|
|
state.spans.append(span);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SyntaxHighlighter::rehighlight(Palette const& palette)
|
|
|
|
|
{
|
|
|
|
|
auto text = m_client->get_text();
|
Libraries: Clean up UTF-16 source text paths
Store parser errors, source range filenames, source code filenames,
module source, and Rust parser errors as UTF-16 where they flow back
into JavaScript-visible strings. Keep byte-oriented source buffers
byte-backed.
Remove temporary PrimitiveString, ByteString, and UTF-8 detours from
JSON, RegExp, module debug logging, print formatting, and tests.
2026-06-21 14:03:19 -03:00
|
|
|
auto source_bytes = MUST(Core::ImmutableBytes::copy(text.bytes()));
|
|
|
|
|
auto decoder = TextCodec::decoder_for("UTF-8"sv);
|
|
|
|
|
VERIFY(decoder.has_value());
|
|
|
|
|
auto source_length = TextCodec::convert_input_to_utf16_length_using_given_decoder_unless_there_is_a_byte_order_mark(
|
|
|
|
|
*decoder, StringView { source_bytes.bytes() })
|
|
|
|
|
.release_value_but_fixme_should_propagate_errors();
|
|
|
|
|
auto source_code = SourceCode::create({}, source_length, "UTF-8"sv, move(source_bytes));
|
2026-03-19 16:03:42 -03:00
|
|
|
auto const* source_data = source_code->utf16_data();
|
|
|
|
|
auto source_len = source_code->length_in_code_units();
|
|
|
|
|
|
|
|
|
|
Vector<Syntax::TextDocumentSpan> spans;
|
|
|
|
|
|
|
|
|
|
RehighlightState state {
|
|
|
|
|
.palette = palette,
|
|
|
|
|
.spans = spans,
|
|
|
|
|
.source = source_data,
|
2026-03-20 10:26:00 -03:00
|
|
|
.position = { 0, 0 },
|
2026-03-19 16:03:42 -03:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
FFI::rust_tokenize(source_data, source_len, &state,
|
|
|
|
|
[](void* ctx, FFI::FFIToken const* token) { on_token(ctx, token); });
|
2020-03-12 19:52:03 -03:00
|
|
|
|
2021-02-07 12:56:02 -03:00
|
|
|
m_client->do_set_spans(move(spans));
|
2020-03-12 19:52:03 -03:00
|
|
|
}
|
2022-04-03 11:19:33 -03:00
|
|
|
|
2020-03-12 19:52:03 -03:00
|
|
|
}
|