• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2025 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  * http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "lsp/include/code_fix_provider.h"
17 #include <cstddef>
18 #include <iostream>
19 #include <memory>
20 #include <string>
21 #include <utility>
22 #include <vector>
23 #include <regex>
24 #include <stdexcept>
25 #include "lsp/include/internal_api.h"
26 
27 namespace ark::es2panda::lsp {
28 
RegisterCodeFix(const std::string & aliasName,std::unique_ptr<CodeFixRegistration> registration)29 void CodeFixProvider::RegisterCodeFix(const std::string &aliasName, std::unique_ptr<CodeFixRegistration> registration)
30 {
31     (void)aliasName;
32     ASSERT(!aliasName.empty());
33     auto shared = std::shared_ptr<CodeFixRegistration>(std::move(registration));
34 
35     for (auto error : shared->GetErrorCodes()) {
36         errorCodeToFixes_.emplace(std::to_string(error), shared);
37     }
38     if (!shared->GetFixIds().empty()) {
39         for (const auto &fixId : shared->GetFixIds()) {
40             fixIdToRegistration_.emplace(fixId, shared);
41         }
42     }
43 }
44 
Instance()45 CodeFixProvider &CodeFixProvider::Instance()
46 {
47     static CodeFixProvider instance;
48     return instance;
49 }
50 
FormatWithArgs(const std::string & text)51 std::string CodeFixProvider::FormatWithArgs(const std::string &text)
52 {
53     std::string result = text;
54     const std::string regExp = R"(\{(\d+)\})";
55     std::regex pattern(regExp);
56     std::smatch match;
57     return result;
58 }
59 
DiagnosticToString(const DiagnosticAndArguments & diag)60 std::string CodeFixProvider::DiagnosticToString(const DiagnosticAndArguments &diag)
61 {
62     std::string message;
63     if (diag.arguments.empty()) {
64         message = diag.message.message;
65     } else {
66         message = FormatWithArgs(diag.message.message);
67     }
68     return message;
69 }
70 
CreateCodeFixActionWorker(std::string & fixName,std::string & description,std::vector<FileTextChanges> & changes,std::string & fixId,std::string & fixAllDescription,std::vector<CodeActionCommand> command={})71 CodeFixAction CodeFixProvider::CreateCodeFixActionWorker(std::string &fixName, std::string &description,
72                                                          std::vector<FileTextChanges> &changes, std::string &fixId,
73                                                          std::string &fixAllDescription,
74                                                          std::vector<CodeActionCommand> command = {})
75 {
76     CodeAction codeAction;
77     codeAction.description = description;
78     codeAction.changes = changes;
79     codeAction.commands = std::move(command);
80     return {codeAction, fixName, fixId, fixAllDescription};
81 }
82 
CreateCodeFixActionWithoutFixAll(std::string & fixName,std::vector<FileTextChanges> & changes,DiagnosticAndArguments & description)83 CodeFixAction CodeFixProvider::CreateCodeFixActionWithoutFixAll(std::string &fixName,
84                                                                 std::vector<FileTextChanges> &changes,
85                                                                 DiagnosticAndArguments &description)
86 {
87     std::string fixId;
88     std::string descriptionMessage = DiagnosticToString(description);
89     std::string fixAllDescription;
90     return CreateCodeFixActionWorker(fixName, descriptionMessage, changes, fixId, fixAllDescription);
91 }
92 
CreateCodeFixAction(std::string fixName,std::vector<FileTextChanges> changes,DiagnosticAndArguments & description,std::string fixId,DiagnosticAndArguments & fixAllDescription,std::vector<CodeActionCommand> & command)93 CodeFixAction CodeFixProvider::CreateCodeFixAction(std::string fixName, std::vector<FileTextChanges> changes,
94                                                    DiagnosticAndArguments &description, std::string fixId,
95                                                    DiagnosticAndArguments &fixAllDescription,
96                                                    std::vector<CodeActionCommand> &command)
97 {
98     std::string descriptionMessage = DiagnosticToString(description);
99     std::string fixAllDescriptionMessage = DiagnosticToString(fixAllDescription);
100     return CreateCodeFixActionWorker(fixName, descriptionMessage, changes, fixId, fixAllDescriptionMessage,
101                                      std::move(command));
102 }
103 
GetFileName(const std::string & filePath)104 std::string CodeFixProvider::GetFileName(const std::string &filePath)
105 {
106     if (filePath.empty()) {
107         return "";
108     }
109 
110     std::size_t pos = filePath.find_last_of('/');
111     if (pos != std::string::npos) {
112         return filePath.substr(pos + 1);
113     }
114 
115     pos = filePath.find_last_of('\\');
116     if (pos != std::string::npos) {
117         return filePath.substr(pos + 1);
118     }
119 
120     return filePath;
121 }
122 
GetSupportedErrorCodes()123 std::vector<std::string> CodeFixProvider::GetSupportedErrorCodes()
124 {
125     std::vector<std::string> result;
126     for (const auto &kv : errorCodeToFixes_) {
127         result.push_back(kv.first);
128     }
129     return result;
130 }
131 
GetDiagnostics(const CodeFixContextBase & context)132 DiagnosticReferences *CodeFixProvider::GetDiagnostics(const CodeFixContextBase &context)
133 {
134     DiagnosticReferences *result = nullptr;
135     LSPAPI const *lspApi = GetImpl();
136     Initializer initializer = Initializer();
137     auto it = reinterpret_cast<public_lib::Context *>(context.context);
138     std::string fileNameStr(GetFileName(std::string(it->sourceFile->filePath)));
139     std::string sourceStr(it->sourceFile->source);
140     const auto ctx = initializer.CreateContext(fileNameStr.c_str(), ES2PANDA_STATE_CHECKED, sourceStr.c_str());
141     DiagnosticReferences semantic = lspApi->getSemanticDiagnostics(ctx);
142     DiagnosticReferences syntactic = lspApi->getSyntacticDiagnostics(ctx);
143     DiagnosticReferences suggestions = lspApi->getSuggestionDiagnostics(ctx);
144 
145     for (const auto &d : semantic.diagnostic) {
146         result->diagnostic.push_back(d);
147     }
148     for (const auto &d : syntactic.diagnostic) {
149         result->diagnostic.push_back(d);
150     }
151     for (const auto &d : suggestions.diagnostic) {
152         result->diagnostic.push_back(d);
153     }
154     initializer.DestroyContext(ctx);
155     return result;
156 }
157 
ShouldIncludeFixAll(const CodeFixRegistration & registration,const std::vector<Diagnostic> & diagnostics)158 bool CodeFixProvider::ShouldIncludeFixAll(const CodeFixRegistration &registration,
159                                           const std::vector<Diagnostic> &diagnostics)
160 {
161     int maybeFixableDiagnostics = 0;
162     const int minFixableDiagnostics = 1;
163     for (size_t i = 0; i <= diagnostics.size(); i++) {
164         if (std::find(registration.GetErrorCodes().begin(), registration.GetErrorCodes().end(), i) !=
165             registration.GetErrorCodes().end()) {
166             ++maybeFixableDiagnostics;
167             if (maybeFixableDiagnostics > minFixableDiagnostics) {
168                 break;
169             }
170         }
171     }
172     return maybeFixableDiagnostics > minFixableDiagnostics;
173 }
174 
GetAllFixes(const CodeFixAllContext & context)175 CombinedCodeActions CodeFixProvider::GetAllFixes(const CodeFixAllContext &context)
176 {
177     auto it = fixIdToRegistration_.find(context.fixId);
178     if (it == fixIdToRegistration_.end() || !it->second) {
179         return CombinedCodeActions();
180     }
181     const std::shared_ptr<CodeFixRegistration> &registration = it->second;
182     return registration->GetAllCodeActions(context);
183 }
184 
EachDiagnostic(const CodeFixAllContext & context,const std::vector<int> & errorCodes,const std::function<void (const DiagnosticWithLocation &)> & cb)185 void CodeFixProvider::EachDiagnostic(const CodeFixAllContext &context, const std::vector<int> &errorCodes,
186                                      const std::function<void(const DiagnosticWithLocation &)> &cb)
187 {
188     if (errorCodes.empty()) {
189     }
190     if (cb) {
191     }
192     auto diagnostics = GetDiagnostics(context);
193     if (diagnostics != nullptr) {
194         for (size_t i = 0; i <= diagnostics->diagnostic.size(); i++) {
195         }
196     }
197 }
198 
GetFixes(const CodeFixContext & context)199 std::vector<CodeFixAction> CodeFixProvider::GetFixes(const CodeFixContext &context)
200 {
201     std::vector<CodeFixAction> result;
202     auto it = errorCodeToFixes_.find(std::to_string(context.errorCode));
203     if (it != errorCodeToFixes_.end()) {
204         const auto &registrations = it->second;
205         if (registrations) {
206             auto actions = registrations->GetCodeActions(context);
207             for (auto &action : actions) {
208                 result.push_back(action);
209             }
210         }
211     }
212     return result;
213 }
214 
CodeFixAll(const CodeFixAllContext & context,const std::vector<int> & errorCodes,std::function<void (ChangeTracker &,const DiagnosticWithLocation &)> use)215 CombinedCodeActions CodeFixProvider::CodeFixAll(
216     const CodeFixAllContext &context, const std::vector<int> &errorCodes,
217     std::function<void(ChangeTracker &, const DiagnosticWithLocation &)> use)
218 {
219     std::vector<CodeActionCommand> commands;
220     TextChangesContext textChangesContext {context.host, context.formatContext, context.preferences};
221     auto changes = ChangeTracker::With(textChangesContext, [&](ChangeTracker &tracker) {
222         EachDiagnostic(context, errorCodes, [&](const DiagnosticWithLocation &diag) { use(tracker, diag); });
223     });
224     return {changes, commands};
225 }
226 
CreateFileTextChanges(const std::string & fileName,const std::vector<TextChange> & textChanges)227 FileTextChanges CodeFixProvider::CreateFileTextChanges(const std::string &fileName,
228                                                        const std::vector<TextChange> &textChanges)
229 {
230     return {fileName, textChanges};
231 }
232 
233 }  // namespace ark::es2panda::lsp
234