diff --git a/ets2panda/lsp/include/code_fixes/code_fix_types.h b/ets2panda/lsp/include/code_fixes/code_fix_types.h index 3f88578046b9fe17f16486bc3b9e3d7683383b72..31ce206b8ec90f70cb7b49b51dfbb7356336faaa 100644 --- a/ets2panda/lsp/include/code_fixes/code_fix_types.h +++ b/ets2panda/lsp/include/code_fixes/code_fix_types.h @@ -31,6 +31,7 @@ #include "public/es2panda_lib.h" #include "public/public.h" #include "../get_class_property_info.h" +#include "lsp/include/services/text_change/text_change_context.h" namespace ark::es2panda::lsp { diff --git a/ets2panda/lsp/include/formatting/formatting.h b/ets2panda/lsp/include/formatting/formatting.h index 32a89e7eab5c0dce253b24d8e33032fc25c9030a..4edb858971153e4c2da007ad2a51a91a535003f6 100644 --- a/ets2panda/lsp/include/formatting/formatting.h +++ b/ets2panda/lsp/include/formatting/formatting.h @@ -16,8 +16,10 @@ #ifndef FORMATTING_H #define FORMATTING_H +#include #include "formatting_settings.h" #include "rules_map.h" +#include "lsp/include/types.h" namespace ark::es2panda::lsp { @@ -44,6 +46,7 @@ private: }; FormatContext GetFormatContext(FormatCodeSettings &options); +std::vector FormatDocument(es2panda_Context *context, FormatContext formatContext); } // namespace ark::es2panda::lsp diff --git a/ets2panda/lsp/include/formatting/formatting_context.h b/ets2panda/lsp/include/formatting/formatting_context.h index 0748a440fbda948fd4d639c5573d64f3eca25ba3..49e725f0500f99aa46fc9a2b5c367eca54d5821f 100644 --- a/ets2panda/lsp/include/formatting/formatting_context.h +++ b/ets2panda/lsp/include/formatting/formatting_context.h @@ -2,9 +2,9 @@ * Copyright (c) 2025 Huawei Device Co., Ltd. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at* + * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0* + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, @@ -17,99 +17,42 @@ #define FORMATTING_CONTEXT_H #include "ir/astNode.h" -#include "formatting_settings.h" +#include "lexer/token/token.h" +#include namespace ark::es2panda::lsp { -enum class FormattingRequestKind { - FORMAT_DOCUMENT, - FORMAT_SELECTION, - FORMAT_ON_ENTER, - FORMAT_ON_SEMICOLON, - FORMAT_ON_OPENING_CURLY_BRACE, - FORMAT_ON_CLOSING_CURLY_BRACE, -}; - -struct RangeWithKind : lexer::SourceRange { -private: - ir::AstNodeType kind_; - +class FormattingContext { public: - explicit RangeWithKind(lexer::SourcePosition startPos = lexer::SourcePosition(), - lexer::SourcePosition endPos = lexer::SourcePosition(), - ir::AstNodeType nodeKind = ir::AstNodeType::DUMMYNODE) - : lexer::SourceRange(startPos, endPos), kind_(nodeKind) - { - } - - const ir::AstNodeType &GetKind() - { - return kind_; - } -}; - -struct FormattingContext { -public: - explicit FormattingContext(FormattingRequestKind requestKind, FormatCodeSettings &formatSettings); - - void UpdateContext(es2panda_Context *context, RangeWithKind ¤tToken, RangeWithKind &nextToken); + explicit FormattingContext(const std::string &sourceText); + + void SetCurrentToken(const lexer::Token &token); + void SetPreviousToken(const lexer::Token &token); + void SetNextToken(const lexer::Token &token); + void SetCurrentTokenParent(ir::AstNode *node); + void SetNextTokenParent(ir::AstNode *node); + + const lexer::Token &GetCurrentToken() const; + const lexer::Token &GetPreviousToken() const; + const lexer::Token &GetNextToken() const; + ir::AstNode *GetCurrentTokenParent() const; + ir::AstNode *GetNextTokenParent() const; + const std::string &GetSourceText() const; + const lexer::SourceRange &GetCurrentTokenSpan() const; - bool ContextNodeAllOnSameLine() const; - bool NextNodeAllOnSameLine() const; - bool TokensAreOnSameLine() const; bool ContextNodeBlockIsOnOneLine() const; - bool NextNodeBlockIsOnOneLine() const; - - const RangeWithKind &GetCurrentTokenSpan() const - { - return currentTokenSpan_; - } - const RangeWithKind &GetNextTokenSpan() const - { - return nextTokenSpan_; - } - - ir::AstNode *GetContextNode() const - { - return contextNode_; - } - ir::AstNode *GetCurrentTokenParent() const - { - return currentTokenParent_; - } - ir::AstNode *GetNextTokenParent() const - { - return nextTokenParent_; - } - - FormatCodeSettings GetFormatCodeSettings() - { - return formatCodeSettings_; - } - - FormattingRequestKind GetformattingRequestKind() - { - return formattingRequestKind_; - } + bool TokensAreOnSameLine() const; private: - bool NodeIsOnOneLine(ir::AstNode *node) const; bool BlockIsOnOneLine(ir::AstNode *node) const; - - bool contextNodeAllOnSameLine_ = false; - bool nextNodeAllOnSameLine_ = false; - bool tokensAreOnSameLine_ = false; - bool contextNodeBlockIsOnOneLine_ = false; - bool nextNodeBlockIsOnOneLine_ = false; - - RangeWithKind currentTokenSpan_; - RangeWithKind nextTokenSpan_; - ir::AstNode *contextNode_ = nullptr; - ir::AstNode *currentTokenParent_ = nullptr; - ir::AstNode *nextTokenParent_ = nullptr; - - FormatCodeSettings formatCodeSettings_; - FormattingRequestKind formattingRequestKind_; + const std::string &sourceText_; + + lexer::Token currentToken_; + lexer::Token prevToken_; + lexer::Token nextToken_; + ir::AstNode *currentTokenParent_ {nullptr}; + ir::AstNode *nextTokenParent_ {nullptr}; + lexer::SourceRange currentTokenSpan_; }; } // namespace ark::es2panda::lsp diff --git a/ets2panda/lsp/include/formatting/rule.h b/ets2panda/lsp/include/formatting/rule.h index 3e9e10a1098557e29582ea6a5326546773ab67ca..0ede2cea53ae76e2c6c70fabc176c6c06a73ff3e 100644 --- a/ets2panda/lsp/include/formatting/rule.h +++ b/ets2panda/lsp/include/formatting/rule.h @@ -18,7 +18,8 @@ #include #include -#include "ir/astNode.h" +#include "generated/tokenType.h" +#include "lexer/lexer.h" #include "formatting_context.h" namespace ark::es2panda::lsp { @@ -40,7 +41,7 @@ enum class RuleFlags { NONE, CAN_DELETE_NEWLINES }; struct Rule { public: - explicit Rule(std::vector &cb, RuleAction action, RuleFlags flag) + explicit Rule(std::vector cb, RuleAction action, RuleFlags flag) : context_(std::move(cb)), action_(action), flags_(flag) { } @@ -68,12 +69,12 @@ private: struct TokenRange { public: - explicit TokenRange(std::vector &tokens, bool isSpecific) + explicit TokenRange(std::vector tokens, bool isSpecific) : tokens_(std::move(tokens)), isSpecific_(isSpecific) { } - std::vector &GetTokens() + std::vector &GetTokens() { return tokens_; } @@ -84,7 +85,7 @@ public: } private: - std::vector tokens_; + std::vector tokens_; bool isSpecific_; }; diff --git a/ets2panda/lsp/include/formatting/rules_map.h b/ets2panda/lsp/include/formatting/rules_map.h index 402b805216be1361f28fa3b22865f84a54d7b2af..fcb3c33d2edda2daade47f528e56417eafa66fd6 100644 --- a/ets2panda/lsp/include/formatting/rules_map.h +++ b/ets2panda/lsp/include/formatting/rules_map.h @@ -39,7 +39,7 @@ private: static RulesMap rulesMap_; - static RulesMap CreateRulesMap(std::vector ruleSpec); + static RulesMap CreateRulesMap(const std::vector &ruleSpec); }; } // namespace ark::es2panda::lsp diff --git a/ets2panda/lsp/include/services/text_change/change_tracker.h b/ets2panda/lsp/include/services/text_change/change_tracker.h index d5514d57d48e778af770f623b92be4b246c1fa23..d029a37b20d2e91925a4741ac043289b68aa373d 100644 --- a/ets2panda/lsp/include/services/text_change/change_tracker.h +++ b/ets2panda/lsp/include/services/text_change/change_tracker.h @@ -26,6 +26,7 @@ #include "public/public.h" #include #include +#include "text_change_context.h" namespace ark::es2panda::lsp { diff --git a/ets2panda/lsp/include/services/text_change/text_change_context.h b/ets2panda/lsp/include/services/text_change/text_change_context.h new file mode 100644 index 0000000000000000000000000000000000000000..861d7b877c74c8af65769a9983cc9944ebfe1a8c --- /dev/null +++ b/ets2panda/lsp/include/services/text_change/text_change_context.h @@ -0,0 +1,32 @@ +/** + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at* + * + * http://www.apache.org/licenses/LICENSE-2.0* + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef TEXT_CHANGE_H +#define TEXT_CHANGE_H + +#include "lsp/include/formatting/formatting.h" +#include "lsp/include/user_preferences.h" + +struct LanguageServiceHost { + std::string name = "lsp"; +}; + +struct TextChangesContext { + LanguageServiceHost host = {}; + ark::es2panda::lsp::FormatContext formatContext; + ark::es2panda::lsp::UserPreferences preferences; +}; + +#endif \ No newline at end of file diff --git a/ets2panda/lsp/include/types.h b/ets2panda/lsp/include/types.h index 438144e733adeacb158b09c643bef25431661f35..dab625c4a96aa614b2564ccafdccd60fc86d794b 100644 --- a/ets2panda/lsp/include/types.h +++ b/ets2panda/lsp/include/types.h @@ -19,8 +19,6 @@ #include #include #include -#include "formatting/formatting.h" -#include "user_preferences.h" // NOLINTBEGIN @@ -222,16 +220,6 @@ public: } }; -struct LanguageServiceHost { - std::string name = "lsp"; -}; - -struct TextChangesContext { - LanguageServiceHost host = {}; - ark::es2panda::lsp::FormatContext formatContext; - ark::es2panda::lsp::UserPreferences preferences; -}; - struct SignatureHelpItems { private: std::vector items_; diff --git a/ets2panda/lsp/src/formatting/formatting.cpp b/ets2panda/lsp/src/formatting/formatting.cpp index f57e9eeba045f1a5250356180cba06cbd12e9dde..11973a8461a2a49edd29c57cc6edfd19570f1244 100644 --- a/ets2panda/lsp/src/formatting/formatting.cpp +++ b/ets2panda/lsp/src/formatting/formatting.cpp @@ -2,9 +2,9 @@ * Copyright (c) 2025 Huawei Device Co., Ltd. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at* + * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0* + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, @@ -14,13 +14,193 @@ */ #include "formatting/formatting.h" +#include "formatting/formatting_context.h" #include "formatting/rules_map.h" +#include "internal_api.h" +#include "public/public.h" +#include "lexer/lexer.h" +#include "lexer/token/token.h" namespace ark::es2panda::lsp { +// NOLINTNEXTLINE +bool TokenMatch(std::vector &tokenRanges, lexer::TokenType tokenType) +{ + for (auto &range : tokenRanges) { + auto &tokens = range.GetTokens(); + if (tokens.empty() || std::find(tokens.begin(), tokens.end(), tokenType) != tokens.end()) { + return true; + } + } + return false; +} + +void ApplyInsertSpace(RuleAction action, const TextSpan &span, std::vector &changes) +{ + if ((static_cast(RuleAction::INSERT_SPACE) & static_cast(action)) != 0) { + changes.emplace_back(TextChange {span, " "}); + } +} + +void ApplyDeleteSpace(RuleAction action, const TextSpan &span, std::vector &changes) +{ + if ((static_cast(RuleAction::DELETE_SPACE) & static_cast(action)) != 0) { + if (span.length > 0) { + changes.emplace_back(TextChange {span, ""}); + } + } +} + +void ApplyInsertNewline(RuleAction action, const TextSpan &span, std::vector &changes) +{ + if ((static_cast(RuleAction::INSERT_NEWLINE) & static_cast(action)) != 0) { + changes.emplace_back(TextChange {span, "\n"}); + } +} + +void ApplyDeleteToken(RuleAction action, const lexer::SourceRange &tokenLoc, std::vector &changes) +{ + if ((static_cast(RuleAction::DELETE_TOKEN) & static_cast(action)) != 0) { + TextSpan span {tokenLoc.start.index, tokenLoc.end.index - tokenLoc.start.index}; + changes.emplace_back(TextChange {span, ""}); + } +} + +void ApplyInsertSemicolon(RuleAction action, const lexer::SourceRange &tokenLoc, std::vector &changes) +{ + if ((static_cast(RuleAction::INSERT_TRAILING_SEMICOLON) & static_cast(action)) != 0) { + TextSpan span {tokenLoc.end.index, 0}; + changes.emplace_back(TextChange {span, ";"}); + } +} + +void ExecuteRuleAction(FormattingContext &context, std::vector &changes, Rule &rule) +{ + const auto &prevToken = context.GetPreviousToken(); + const auto ¤tToken = context.GetCurrentToken(); + auto action = rule.GetRuleAction(); + + auto prevLoc = prevToken.Loc(); + auto currLoc = currentToken.Loc(); + + size_t start = prevLoc.end.index; + size_t end = currLoc.start.index; + + if (start <= end) { + TextSpan whitespaceSpan {start, end - start}; + ApplyInsertSpace(action, whitespaceSpan, changes); + ApplyDeleteSpace(action, whitespaceSpan, changes); + ApplyInsertNewline(action, whitespaceSpan, changes); + } + + ApplyDeleteToken(action, currLoc, changes); + ApplyInsertSemicolon(action, currLoc, changes); +} + +void ApplyRulesOnRange(FormattingContext &context, std::vector &changes, RulesMap &rulesMap) +{ + const auto ¤tToken = context.GetCurrentToken(); + const auto &nextToken = context.GetNextToken(); + + if (currentToken.Type() == lexer::TokenType::EOS || nextToken.Type() == lexer::TokenType::EOS) { + return; + } + + auto allRules = rulesMap(context); + + for (auto &ruleSpec : allRules) { + if (!TokenMatch(ruleSpec.GetLeftTokenRange(), currentToken.Type())) { + continue; + } + + if (!TokenMatch(ruleSpec.GetRightTokenRange(), nextToken.Type())) { + continue; + } + + bool predicatesMet = true; + for (const auto &predicate : ruleSpec.GetRule().GetContext()) { + if (!predicate(&context)) { + predicatesMet = false; + break; + } + } + if (predicatesMet) { + ExecuteRuleAction(context, changes, ruleSpec.GetRule()); + if ((static_cast(ruleSpec.GetRule().GetRuleAction()) & + (static_cast(static_cast(RuleAction::STOP_PROCESSING_SPACE_ACTIONS) | + static_cast(RuleAction::STOP_PROCESSING_TOKEN_ACTIONS)))) != 0) { + break; + } + } + } +} + +std::vector FormatDocument(es2panda_Context *context, FormatContext formatContext) +{ + if (context == nullptr) { + return {}; + } + + auto *publicContext = reinterpret_cast(context); + if (publicContext == nullptr || publicContext->parserProgram == nullptr) { + return {}; + } + + RulesMap &rulesMap = formatContext.GetRulesMap(); + [[maybe_unused]] const FormatCodeSettings &options = formatContext.GetFormatCodeSettings(); + + parser::ParserContext parserCtx(publicContext->parserProgram, parser::ParserStatus::NO_OPTS); + lexer::Lexer lexer(&parserCtx, *publicContext->diagnosticEngine); + + std::string sourceText(publicContext->parserProgram->SourceCode().Utf8()); + + FormattingContext formattingContext(sourceText); + std::vector changes; + + lexer::Token prevToken; + prevToken.SetTokenType(lexer::TokenType::EOS); + + lexer.NextToken(); + lexer::Token currentToken = lexer.GetToken(); + if (currentToken.Type() == lexer::TokenType::EOS) { + return {}; + } + lexer.NextToken(); + lexer::Token nextToken = lexer.GetToken(); + + while (currentToken.Type() != lexer::TokenType::EOS) { + formattingContext.SetPreviousToken(prevToken); + formattingContext.SetCurrentToken(currentToken); + formattingContext.SetNextToken(nextToken); + + auto *currentTokenParent = GetTouchingToken(context, currentToken.Loc().start.index, false); + formattingContext.SetCurrentTokenParent(currentTokenParent); + + ir::AstNode *nextTokenParent = nullptr; + if (nextToken.Type() != lexer::TokenType::EOS) { + nextTokenParent = GetTouchingToken(context, nextToken.Loc().start.index, false); + } + formattingContext.SetNextTokenParent(nextTokenParent); + + ApplyRulesOnRange(formattingContext, changes, rulesMap); + + prevToken = currentToken; + currentToken = nextToken; + + if (currentToken.Type() != lexer::TokenType::EOS) { + lexer.NextToken(); + nextToken = lexer.GetToken(); + } else { + nextToken.SetTokenType(lexer::TokenType::EOS); + } + } + return changes; +} + FormatContext GetFormatContext(FormatCodeSettings &options) { - return FormatContext(options, RulesMapCache::Instance().GetRulesMap()); + RulesMap &rulesMap = RulesMapCache::Instance().GetRulesMap(); + return FormatContext(options, rulesMap); } } // namespace ark::es2panda::lsp \ No newline at end of file diff --git a/ets2panda/lsp/src/formatting/formatting_context.cpp b/ets2panda/lsp/src/formatting/formatting_context.cpp index 86b3627d2702715a24179bf59fa933a885fd71b1..39dacb334b685a41fffd0c21a299f963b942d4db 100644 --- a/ets2panda/lsp/src/formatting/formatting_context.cpp +++ b/ets2panda/lsp/src/formatting/formatting_context.cpp @@ -14,91 +14,96 @@ */ #include "formatting/formatting_context.h" -#include "brace_matching.h" -#include "public/public.h" +#include "ir/astNode.h" +#include "ir/statements/blockStatement.h" namespace ark::es2panda::lsp { -FormattingContext::FormattingContext(FormattingRequestKind requestKind, FormatCodeSettings &formatSettings) +FormattingContext::FormattingContext(const std::string &sourceText) : sourceText_(sourceText) {} + +void FormattingContext::SetCurrentToken(const lexer::Token &token) { - formatCodeSettings_ = formatSettings; - formattingRequestKind_ = requestKind; + currentToken_ = token; + currentTokenSpan_ = token.Loc(); } -void FormattingContext::UpdateContext(es2panda_Context *context, RangeWithKind ¤tToken, RangeWithKind &nextToken) +void FormattingContext::SetPreviousToken(const lexer::Token &token) { - currentTokenSpan_ = currentToken; - nextTokenSpan_ = nextToken; - - if (context == nullptr) { - return; - } + prevToken_ = token; +} - auto ctx = reinterpret_cast(context); - if (ctx->parserProgram == nullptr || ctx->parserProgram->Ast() == nullptr) { - return; - } +void FormattingContext::SetNextToken(const lexer::Token &token) +{ + nextToken_ = token; +} - contextNode_ = reinterpret_cast(ctx->parserProgram->Ast()); +void FormattingContext::SetCurrentTokenParent(ir::AstNode *node) +{ + currentTokenParent_ = node; +} - auto current = GetTouchingToken(context, currentTokenSpan_.start.index, false); - if (current == nullptr) { - return; - } - currentTokenParent_ = current->Parent(); +void FormattingContext::SetNextTokenParent(ir::AstNode *node) +{ + nextTokenParent_ = node; +} - nextTokenParent_ = GetTouchingToken(context, nextToken.start.index, false)->Parent(); +const lexer::Token &FormattingContext::GetCurrentToken() const +{ + return currentToken_; +} - contextNodeAllOnSameLine_ = NodeIsOnOneLine(contextNode_); - nextNodeAllOnSameLine_ = NodeIsOnOneLine(nextTokenParent_); - tokensAreOnSameLine_ = currentTokenSpan_.start.ToLocation().line == nextTokenSpan_.start.ToLocation().line; - contextNodeBlockIsOnOneLine_ = BlockIsOnOneLine(contextNode_); - nextNodeBlockIsOnOneLine_ = BlockIsOnOneLine(nextTokenParent_); +const lexer::Token &FormattingContext::GetPreviousToken() const +{ + return prevToken_; } -bool FormattingContext::ContextNodeAllOnSameLine() const +const lexer::Token &FormattingContext::GetNextToken() const { - return contextNodeAllOnSameLine_; + return nextToken_; } -bool FormattingContext::NextNodeAllOnSameLine() const +ir::AstNode *FormattingContext::GetCurrentTokenParent() const { - return nextNodeAllOnSameLine_; + return currentTokenParent_; } -bool FormattingContext::TokensAreOnSameLine() const +ir::AstNode *FormattingContext::GetNextTokenParent() const { - return tokensAreOnSameLine_; + return nextTokenParent_; } -bool FormattingContext::ContextNodeBlockIsOnOneLine() const +const std::string &FormattingContext::GetSourceText() const { - return contextNodeBlockIsOnOneLine_; + return sourceText_; } -bool FormattingContext::NextNodeBlockIsOnOneLine() const +const lexer::SourceRange &FormattingContext::GetCurrentTokenSpan() const { - return nextNodeBlockIsOnOneLine_; + return currentTokenSpan_; } -bool FormattingContext::NodeIsOnOneLine(ir::AstNode *node) const +bool FormattingContext::ContextNodeBlockIsOnOneLine() const { - if (node == nullptr) { - return false; + if (currentTokenParent_ == nullptr) { + return true; } + return BlockIsOnOneLine(currentTokenParent_); +} - return node->Start().line == node->End().line; +bool FormattingContext::TokensAreOnSameLine() const +{ + return prevToken_.Loc().end.line == currentToken_.Loc().start.line; } bool FormattingContext::BlockIsOnOneLine(ir::AstNode *node) const { - if (node == nullptr) { - return false; + if (node->IsBlockStatement()) { + auto block = node->AsBlockStatement(); + if (!block->Statements().empty()) { + return block->Start().line == block->Statements().back()->End().line; + } } - - auto nodeChild = node->FindChild([](ir::AstNode *astnode) { return CheckNodeKindForBraceMatching(astnode); }); - - return NodeIsOnOneLine(nodeChild); + return node->Start().line == node->End().line; } } // namespace ark::es2panda::lsp \ No newline at end of file diff --git a/ets2panda/lsp/src/formatting/rules.cpp b/ets2panda/lsp/src/formatting/rules.cpp index c145eaf67daa7c0f273427c1230cc8b31debd052..10a02df0577123bb39697da89e7a689d14ea364f 100644 --- a/ets2panda/lsp/src/formatting/rules.cpp +++ b/ets2panda/lsp/src/formatting/rules.cpp @@ -15,13 +15,35 @@ #include "formatting/rules.h" #include +#include "formatting/formatting_context.h" +#include "ir/astNode.h" namespace ark::es2panda::lsp { +static bool IsConditionalOperatorContext(FormattingContext *ctx) +{ + auto *parent = ctx->GetCurrentTokenParent(); + return parent != nullptr && (parent->IsConditionalExpression() || parent->IsTSConditionalType()); +} + std::vector GetAllRules() { std::vector rules; + auto createTokenRange = [](const std::vector &tokens) { + return std::vector {TokenRange(tokens, true)}; + }; + auto anyTokenRange = createTokenRange({}); + + { + std::vector p = {[](FormattingContext *ctx) { return ctx->TokensAreOnSameLine(); }, + IsConditionalOperatorContext}; + Rule rule(p, RuleAction::INSERT_SPACE, RuleFlags::NONE); + auto left = createTokenRange({lexer::TokenType::PUNCTUATOR_QUESTION_MARK}); + auto right = anyTokenRange; + rules.emplace_back(rule, left, right); + } + return rules; } diff --git a/ets2panda/lsp/src/formatting/rules_map.cpp b/ets2panda/lsp/src/formatting/rules_map.cpp index a6f9101f13dbd717cb11c94fd5804a334bbbc417..1253f4d578881ea3de9e9de98d52708844b8b351 100644 --- a/ets2panda/lsp/src/formatting/rules_map.cpp +++ b/ets2panda/lsp/src/formatting/rules_map.cpp @@ -17,8 +17,6 @@ namespace ark::es2panda::lsp { -RulesMap RulesMapCache::rulesMap_; - RulesMapCache &RulesMapCache::Instance() { static RulesMapCache cache; @@ -27,15 +25,13 @@ RulesMapCache &RulesMapCache::Instance() RulesMap &RulesMapCache::GetRulesMap() { - if (!rulesMap_) { - rulesMap_ = CreateRulesMap(GetAllRules()); - } - return rulesMap_; + static RulesMap rulesMap = CreateRulesMap(GetAllRules()); + return rulesMap; } -RulesMap RulesMapCache::CreateRulesMap(std::vector ruleSpec) +RulesMap RulesMapCache::CreateRulesMap(const std::vector &ruleSpec) { - return [&ruleSpec]([[maybe_unused]] const FormattingContext &ctx) { return ruleSpec; }; + return [ruleSpec]([[maybe_unused]] const FormattingContext &ctx) { return ruleSpec; }; } -} // namespace ark::es2panda::lsp \ No newline at end of file +} // namespace ark::es2panda::lsp diff --git a/ets2panda/test/unit/lsp/formatting_context_test.cpp b/ets2panda/test/unit/lsp/formatting_context_test.cpp index 19bb22555f524a0e191206c9b99208b3429a098b..c90d9952132fd89b9ac3e011ee5cc2b072a081b4 100644 --- a/ets2panda/test/unit/lsp/formatting_context_test.cpp +++ b/ets2panda/test/unit/lsp/formatting_context_test.cpp @@ -14,6 +14,7 @@ */ #include "lsp/include/formatting/formatting_context.h" +#include "lsp/include/formatting/formatting_settings.h" #include "lsp_api_test.h" #include #include "ir/astNode.h" @@ -24,79 +25,64 @@ class LSPFormattingContextTests : public LSPAPITests {}; TEST_F(LSPFormattingContextTests, FormattingContextConstructorTest) { - const size_t defaultIndentSize = 4U; - const std::string defaultNewLine = "\n"; + const std::string sourceCode = "let x = 10;"; - ark::es2panda::lsp::FormatCodeSettings settings; - settings.SetIndentSize(defaultIndentSize); - settings.SetNewLineCharacter(defaultNewLine); - settings.SetConvertTabsToSpaces(true); - - ark::es2panda::lsp::FormattingContext context(ark::es2panda::lsp::FormattingRequestKind::FORMAT_DOCUMENT, settings); + ark::es2panda::lsp::FormattingContext context(sourceCode); - EXPECT_EQ(context.GetformattingRequestKind(), ark::es2panda::lsp::FormattingRequestKind::FORMAT_DOCUMENT); - EXPECT_EQ(context.GetFormatCodeSettings().GetIndentSize(), defaultIndentSize); + EXPECT_EQ(context.GetSourceText(), sourceCode); } -TEST_F(LSPFormattingContextTests, FormattingContextSameLineTest) +TEST_F(LSPFormattingContextTests, FormattingContextTokenSettersTest) { - const size_t firstLine = 1U; - const size_t idStart = 4U; - const size_t numStart = 8U; - const size_t semiStart = 10U; + const std::string sourceCode = "let x = 10;"; - ark::es2panda::lsp::Initializer initializer; - auto ctx = initializer.CreateContext("sameLineTest.ets", ES2PANDA_STATE_CHECKED, "let x = 10;"); - auto contextPtr = reinterpret_cast(ctx); - auto program = contextPtr->parserProgram; + ark::es2panda::lsp::FormattingContext context(sourceCode); - ark::es2panda::lsp::FormatCodeSettings settings; - ark::es2panda::lsp::FormattingContext context(ark::es2panda::lsp::FormattingRequestKind::FORMAT_ON_ENTER, settings); + ark::es2panda::lexer::Token prevToken; + prevToken.SetTokenType(ark::es2panda::lexer::TokenType::KEYW_LET); - ark::es2panda::lexer::SourcePosition pos1(idStart, firstLine, program); - ark::es2panda::lexer::SourcePosition pos2(numStart, firstLine, program); - ark::es2panda::lexer::SourcePosition pos3(semiStart, firstLine, program); - ark::es2panda::lsp::RangeWithKind span1(pos1, pos2, ark::es2panda::ir::AstNodeType::IDENTIFIER); - ark::es2panda::lsp::RangeWithKind span2(pos2, pos3, ark::es2panda::ir::AstNodeType::NUMBER_LITERAL); + ark::es2panda::lexer::Token currentToken; + currentToken.SetTokenType(ark::es2panda::lexer::TokenType::LITERAL_IDENT); - context.UpdateContext(ctx, span1, span2); + ark::es2panda::lexer::Token nextToken; + nextToken.SetTokenType(ark::es2panda::lexer::TokenType::PUNCTUATOR_SUBSTITUTION); - EXPECT_TRUE(context.TokensAreOnSameLine()); + context.SetPreviousToken(prevToken); + context.SetCurrentToken(currentToken); + context.SetNextToken(nextToken); - initializer.DestroyContext(ctx); + EXPECT_EQ(context.GetPreviousToken().Type(), ark::es2panda::lexer::TokenType::KEYW_LET); + EXPECT_EQ(context.GetCurrentToken().Type(), ark::es2panda::lexer::TokenType::LITERAL_IDENT); + EXPECT_EQ(context.GetNextToken().Type(), ark::es2panda::lexer::TokenType::PUNCTUATOR_SUBSTITUTION); } -TEST_F(LSPFormattingContextTests, FormattingContextDifferentLinesTest) +TEST_F(LSPFormattingContextTests, FormattingContextParentNodeTest) { - const size_t firstLine = 1U; - const size_t secondLine = 2U; - const size_t idStart = 4U; - const size_t numStart = 8U; + const std::string sourceCode = "let x = 10;"; - ark::es2panda::lsp::Initializer initializer; - auto ctx = initializer.CreateContext("differentLinesTest.ets", ES2PANDA_STATE_CHECKED, "let x = 10;\nlet y = 20;"); - auto contextPtr = reinterpret_cast(ctx); - auto program = contextPtr->parserProgram; + ark::es2panda::lsp::FormattingContext context(sourceCode); - ark::es2panda::lsp::FormatCodeSettings settings; - ark::es2panda::lsp::FormattingContext context(ark::es2panda::lsp::FormattingRequestKind::FORMAT_ON_SEMICOLON, - settings); + ark::es2panda::ir::AstNode *mockParent = nullptr; + ark::es2panda::ir::AstNode *mockNextParent = nullptr; + + context.SetCurrentTokenParent(mockParent); + context.SetNextTokenParent(mockNextParent); - ark::es2panda::lexer::SourcePosition pos1(idStart, firstLine, program); - ark::es2panda::lexer::SourcePosition pos2(numStart, firstLine, program); - ark::es2panda::lexer::SourcePosition pos3(idStart, secondLine, program); - ark::es2panda::lexer::SourcePosition pos4(numStart, secondLine, program); - ark::es2panda::lsp::RangeWithKind span1(pos1, pos2, ark::es2panda::ir::AstNodeType::IDENTIFIER); - ark::es2panda::lsp::RangeWithKind span2(pos3, pos4, ark::es2panda::ir::AstNodeType::IDENTIFIER); + EXPECT_EQ(context.GetCurrentTokenParent(), mockParent); + EXPECT_EQ(context.GetNextTokenParent(), mockNextParent); +} - context.UpdateContext(ctx, span1, span2); +TEST_F(LSPFormattingContextTests, FormattingContextBlockIsOnOneLineTest) +{ + const std::string sourceCode = "{ let x = 10; }"; - EXPECT_TRUE(context.TokensAreOnSameLine()); + ark::es2panda::lsp::FormattingContext context(sourceCode); - initializer.DestroyContext(ctx); + context.SetCurrentTokenParent(nullptr); + EXPECT_TRUE(context.ContextNodeBlockIsOnOneLine()); } -TEST_F(LSPFormattingContextTests, FormattingCodeSettingsTest) +TEST_F(LSPFormattingContextTests, FormatCodeSettingsTest) { const size_t indentSize = 2U; const size_t tabSize = 4U; @@ -112,16 +98,26 @@ TEST_F(LSPFormattingContextTests, FormattingCodeSettingsTest) settings.SetInsertSpaceAfterSemicolonInForStatements(true); settings.SetInsertSpaceBeforeAndAfterBinaryOperators(true); - ark::es2panda::lsp::FormattingContext context(ark::es2panda::lsp::FormattingRequestKind::FORMAT_DOCUMENT, settings); + EXPECT_EQ(settings.GetIndentSize(), indentSize); + EXPECT_EQ(settings.GetTabSize(), tabSize); + EXPECT_EQ(settings.GetNewLineCharacter(), newlineChar); + EXPECT_TRUE(settings.GetConvertTabsToSpaces()); + EXPECT_TRUE(settings.GetInsertSpaceAfterCommaDelimiter()); + EXPECT_TRUE(settings.GetInsertSpaceAfterSemicolonInForStatements()); + EXPECT_TRUE(settings.GetInsertSpaceBeforeAndAfterBinaryOperators()); +} + +TEST_F(LSPFormattingContextTests, FormatCodeSettingsDefaultValuesTest) +{ + ark::es2panda::lsp::FormatCodeSettings settings; - auto contextSettings = context.GetFormatCodeSettings(); - EXPECT_EQ(contextSettings.GetIndentSize(), indentSize); - EXPECT_EQ(contextSettings.GetTabSize(), tabSize); - EXPECT_EQ(contextSettings.GetNewLineCharacter(), newlineChar); - EXPECT_TRUE(contextSettings.GetConvertTabsToSpaces()); - EXPECT_TRUE(contextSettings.GetInsertSpaceAfterCommaDelimiter()); - EXPECT_TRUE(contextSettings.GetInsertSpaceAfterSemicolonInForStatements()); - EXPECT_TRUE(contextSettings.GetInsertSpaceBeforeAndAfterBinaryOperators()); + EXPECT_EQ(settings.GetIndentSize(), 4U); + EXPECT_EQ(settings.GetTabSize(), 4U); + EXPECT_EQ(settings.GetNewLineCharacter(), "\n"); + EXPECT_TRUE(settings.GetConvertTabsToSpaces()); + EXPECT_TRUE(settings.GetInsertSpaceAfterCommaDelimiter()); + EXPECT_TRUE(settings.GetInsertSpaceAfterSemicolonInForStatements()); + EXPECT_TRUE(settings.GetInsertSpaceBeforeAndAfterBinaryOperators()); } } // namespace \ No newline at end of file diff --git a/ets2panda/test/unit/lsp/formatting_test.cpp b/ets2panda/test/unit/lsp/formatting_test.cpp index ab38f7950da09931f9246385c2a73bb041f1f850..9cb58aa6614941dd31aeb721ee1e8670bd63d24b 100644 --- a/ets2panda/test/unit/lsp/formatting_test.cpp +++ b/ets2panda/test/unit/lsp/formatting_test.cpp @@ -2,9 +2,9 @@ * Copyright (c) 2025 Huawei Device Co., Ltd. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at* + * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0* + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, @@ -14,6 +14,7 @@ */ #include "lsp/include/formatting/formatting.h" +#include "lsp/include/formatting/formatting_settings.h" #include "lsp_api_test.h" #include @@ -29,4 +30,27 @@ TEST_F(LSPFormattingTests, GetFormatContextTest) EXPECT_NE(&formatContext, nullptr); } +TEST_F(LSPFormattingTests, FormatDocumentQuestionMarkTest) +{ + std::string testCode = R"( + function conditionalTest(value:number):number{ + return value>0?value:-value; + } + )"; + const int index0 = 0; + auto tempFiles = CreateTempFile({"format_question_test.ets"}, {testCode}); + ASSERT_FALSE(tempFiles.empty()); + + ark::es2panda::lsp::Initializer initializer = ark::es2panda::lsp::Initializer(); + es2panda_Context *ctx = initializer.CreateContext(tempFiles.at(index0).c_str(), ES2PANDA_STATE_CHECKED); + + ark::es2panda::lsp::FormatCodeSettings settings; + auto formatContext = ark::es2panda::lsp::GetFormatContext(settings); + + auto changes = ark::es2panda::lsp::FormatDocument(ctx, formatContext); + + EXPECT_FALSE(changes.empty()); + initializer.DestroyContext(ctx); +} + } // namespace \ No newline at end of file