diff --git a/ets2panda/lsp/src/register_code_fix/ui_plugin_suggest.cpp b/ets2panda/lsp/src/register_code_fix/ui_plugin_suggest.cpp index e450a3c3ecd961686459ebae171a3e13bfbbcfac..ecfecd3991a3e6d7b35bb0d5566d82a04c05d3cd 100644 --- a/ets2panda/lsp/src/register_code_fix/ui_plugin_suggest.cpp +++ b/ets2panda/lsp/src/register_code_fix/ui_plugin_suggest.cpp @@ -30,28 +30,38 @@ UIPluginSuggest::UIPluginSuggest() } std::vector GetTextChangesFromSuggestions(const ark::es2panda::util::Diagnostic *diag, size_t pos, - bool isAll) + bool isAll, es2panda_Context *context) { std::vector textChanges; if (!diag->HasSuggestions()) { return textChanges; } + auto ctx = reinterpret_cast(context); + auto index = lexer::LineIndex(ctx->parserProgram->SourceCode()); + auto offset = index.GetOffset(lexer::SourceLocation(diag->Line(), diag->Offset(), ctx->parserProgram)); + auto touchingToken = GetTouchingToken(context, offset, false); + if (touchingToken == nullptr) { + return textChanges; + } + auto start = touchingToken->Start().index; + auto end = touchingToken->End().index; for (auto suggestion : diag->Suggestion()) { auto sourceStart = suggestion->SourceRange()->start.index; auto sourceEnd = suggestion->SourceRange()->end.index; auto span = TextSpan(sourceStart, sourceEnd - sourceStart); - if (isAll) { - textChanges.emplace_back(TextChange(span, suggestion->SubstitutionCode())); - } else if (pos >= sourceStart && pos <= sourceEnd) { + if (isAll || (pos >= start && pos <= end)) { + // compare diag range instead of suggestion range + // to support rules of different ranges of diag and suggestion textChanges.emplace_back(TextChange(span, suggestion->SubstitutionCode())); } } return textChanges; } -std::vector GetUIPluginCodeFixesByDiagType(public_lib::Context *ctx, size_t pos, +std::vector GetUIPluginCodeFixesByDiagType(es2panda_Context *context, size_t pos, util::DiagnosticType type, bool isAll) { + auto ctx = reinterpret_cast(context); auto filename = ctx->sourceFileName; std::vector res; const auto &diagnostics = ctx->diagnosticEngine->GetDiagnosticStorage(type); @@ -59,7 +69,7 @@ std::vector GetUIPluginCodeFixesByDiagType(public_lib::Context // NOLINTNEXTLINE(modernize-loop-convert,-warnings-as-errors) for (size_t i = 0; i < diagnosticStorage->size(); ++i) { auto diag = reinterpret_cast(&(*(*diagnosticStorage)[i])); - auto textChanges = GetTextChangesFromSuggestions(diag, pos, isAll); + auto textChanges = GetTextChangesFromSuggestions(diag, pos, isAll, context); FileTextChanges fileTextChanges(filename, textChanges); res.emplace_back(fileTextChanges); } @@ -71,11 +81,10 @@ std::vector UIPluginSuggest::GetUIPluginCodeFixes(es2panda_Cont if (context == nullptr) { return {}; } - auto ctx = reinterpret_cast(context); std::vector res; - auto errorFixes = GetUIPluginCodeFixesByDiagType(ctx, pos, util::DiagnosticType::PLUGIN_ERROR, isAll); + auto errorFixes = GetUIPluginCodeFixesByDiagType(context, pos, util::DiagnosticType::PLUGIN_ERROR, isAll); res.insert(res.end(), errorFixes.begin(), errorFixes.end()); - auto warningFixes = GetUIPluginCodeFixesByDiagType(ctx, pos, util::DiagnosticType::PLUGIN_WARNING, isAll); + auto warningFixes = GetUIPluginCodeFixesByDiagType(context, pos, util::DiagnosticType::PLUGIN_WARNING, isAll); res.insert(res.end(), warningFixes.begin(), warningFixes.end()); return res; } diff --git a/ets2panda/test/unit/lsp/code_fix/ui_plugin_suggest.cpp b/ets2panda/test/unit/lsp/code_fix/ui_plugin_suggest.cpp index c049aa00f9bf6300a799b1f84128c66f7a69bdf3..f03f8e4ac5d5375ea5364cf24ffb4e5a8f83517b 100644 --- a/ets2panda/test/unit/lsp/code_fix/ui_plugin_suggest.cpp +++ b/ets2panda/test/unit/lsp/code_fix/ui_plugin_suggest.cpp @@ -179,4 +179,55 @@ TEST_F(LspUISuggestionTests, UIPluginsErrorTest3) initializer.DestroyContext(ctx); } +TEST_F(LspUISuggestionTests, UIPluginsErrorTest4) +{ + using ark::es2panda::ir::AstNode; + using ark::es2panda::public_lib::Context; + Initializer initializer = Initializer(); + std::vector files = {"ui_error1.ets"}; + std::vector texts = {R"delimiter(function main() { return 1 })delimiter"}; + auto filePaths = CreateTempFile(files, texts); + auto ctx = initializer.CreateContext(filePaths[0].c_str(), ES2PANDA_STATE_CHECKED); + LSPAPI const *lspApi = GetImpl(); + int const offset = 9; + // NOLINTNEXTLINE + const char *params[] = { + "a", + }; + const char *dmessage1 = "origin {}"; + const size_t argc1 = 1; + const char *substitutionCode = "replace b"; + const char *dmessage2 = "error"; + const size_t argc0 = 0; + const size_t index1 = 16; // suggestion left + const size_t line1 = 0; + const size_t index2 = 27; // suggestion right + const size_t line2 = 0; + const size_t index3 = 9; // diag pos + const size_t line3 = 0; + const size_t index4 = 9; // diag right virtual + const int code = 4000; + auto suggestionkind = initializer.CreateDiagnosticKind(ctx, dmessage1, ES2PANDA_PLUGIN_SUGGESTION); + auto diagnostikind = initializer.CreateDiagnosticKind(ctx, dmessage2, ES2PANDA_PLUGIN_ERROR); + es2panda_SourcePosition *left = initializer.CreateSourcePosition(ctx, index1, line1); + es2panda_SourcePosition *right = initializer.CreateSourcePosition(ctx, index2, line2); + es2panda_SourcePosition *diagPos = initializer.CreateSourcePosition(ctx, index3, line3); + es2panda_SourceRange *range = initializer.CreateSourceRange(ctx, left, right); + auto suggestionInfo = initializer.CreateSuggestionInfo(ctx, suggestionkind, params, argc1, substitutionCode, range); + auto diagnosticInfo = initializer.CreateDiagnosticInfo(ctx, diagnostikind, nullptr, argc0, diagPos); + initializer.LogDiagnosticWithSuggestion(ctx, diagnosticInfo, suggestionInfo); + auto suggest = lspApi->getSyntacticDiagnostics(ctx); + AssertDiagnosticContainsCodeAndMessage(suggest, code, dmessage2); + auto result = ark::es2panda::lsp::UIPluginSuggest::GetUIPluginCodeFixes(ctx, offset, false); + ASSERT_EQ(result.at(0).textChanges.at(0).newText, substitutionCode); + std::vector codes; + codes.emplace_back(code); + CodeFixOptions emptyOptions; + auto fix = ark::es2panda::lsp::GetCodeFixesAtPositionImpl(ctx, index3, index4, codes, emptyOptions); + ASSERT_EQ(fix.at(0).changes_.at(0).textChanges.at(0).newText, substitutionCode); + ASSERT_EQ(fix.at(0).changes_.at(0).textChanges.at(0).span.length, index2 - index1); + + initializer.DestroyContext(ctx); +} + } // namespace