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 "generate_constructor.h"
17 #include "internal_api.h"
18 #include "compiler/lowering/util.h"
19 #include "public/public.h"
20 #include "class_hierarchy.h"
21 #include "completions.h"
22 #include "quick_info.h"
23
24 namespace ark::es2panda::lsp {
25
HasConstructorNode(ir::AstNode * classNode)26 bool HasConstructorNode(ir::AstNode *classNode)
27 {
28 size_t start = classNode->Start().index;
29 size_t end = classNode->End().index;
30 ir::AstNode *constructorNode = classNode->FindChild([start, end](ir::AstNode *node) {
31 if (node == nullptr) {
32 return false;
33 }
34 return node->Start().index >= start && node->End().index <= end && node->IsConstructor();
35 });
36
37 return constructorNode != nullptr;
38 }
39
GetClassProperties(ir::AstNode * classNode,const std::vector<std::string> & properties)40 std::vector<ir::AstNode *> GetClassProperties(ir::AstNode *classNode, const std::vector<std::string> &properties)
41 {
42 std::vector<ir::AstNode *> classProperties = {};
43 auto bodyNodes = classNode->AsClassDeclaration()->Definition()->Body();
44 for (const auto &triggerWord : properties) {
45 auto property = ark::es2panda::lsp::FilterFromBody(bodyNodes, triggerWord);
46 for (const auto &node : property) {
47 if (node->IsStatic() || !node->IsClassProperty()) {
48 continue;
49 }
50 classProperties.emplace_back(node);
51 }
52 }
53
54 return classProperties;
55 }
56
GetExtendedClassProperties(ir::AstNode * classNode)57 std::vector<ir::AstNode *> GetExtendedClassProperties(ir::AstNode *classNode)
58 {
59 auto baseNode = ark::es2panda::lsp::GetEffectiveBaseTypeNode(classNode);
60 if (baseNode == nullptr) {
61 return {};
62 }
63 std::vector<ir::AstNode *> extendedClassProperties = {};
64 auto baseNodeBody = baseNode->AsClassDeclaration()->Definition()->Body();
65 for (auto node : baseNodeBody) {
66 if (!node->IsClassProperty()) {
67 continue;
68 }
69 auto tmp = node->AsClassProperty();
70 if (tmp->IsStatic()) {
71 continue;
72 }
73
74 extendedClassProperties.push_back(node);
75 }
76 return extendedClassProperties;
77 }
78
RemoveTrailingChar(std::string & str,const std::string & lastChar)79 void RemoveTrailingChar(std::string &str, const std::string &lastChar)
80 {
81 if (!str.empty()) {
82 size_t lastPos = str.find_last_of(lastChar);
83 if (lastPos != std::string::npos) {
84 str.erase(lastPos);
85 }
86 }
87 }
88
GetParameterListAndFunctionBody(std::string & parameterList,std::string & functionBody,const std::vector<ir::AstNode * > & nodeList,bool isSuper)89 void GetParameterListAndFunctionBody(std::string ¶meterList, std::string &functionBody,
90 const std::vector<ir::AstNode *> &nodeList, bool isSuper)
91 {
92 if (nodeList.empty()) {
93 return;
94 }
95
96 std::vector<std::string> strList1 = {};
97 std::vector<std::string> strList2 = {};
98 for (auto propertyNode : nodeList) {
99 auto nodeName = GetIdentifierName(propertyNode);
100 auto typeAnnotation = propertyNode->AsClassProperty()->TypeAnnotation();
101 if (typeAnnotation->IsETSTypeReference()) {
102 auto propertyType = GetNameForTypeReference(typeAnnotation);
103 auto str = nodeName;
104 str += ": ";
105 str += propertyType;
106 str += ", ";
107 strList1.push_back(str);
108 strList2.push_back(nodeName);
109 }
110 }
111
112 for (const auto &str : strList1) {
113 parameterList += str;
114 }
115
116 if (isSuper) {
117 functionBody += " super(";
118 for (const auto &str : strList2) {
119 functionBody += str;
120 functionBody += ", ";
121 }
122 RemoveTrailingChar(functionBody, ",");
123 functionBody += ");\n";
124 } else {
125 for (const auto &str : strList2) {
126 functionBody += " this.";
127 functionBody += str;
128 functionBody += " = ";
129 functionBody += str;
130 functionBody += ";\n";
131 }
132 }
133 }
134
CollectConstructorInfo(const std::vector<ir::AstNode * > & classProperties,const std::vector<ir::AstNode * > & extendedClassProperties)135 std::string CollectConstructorInfo(const std::vector<ir::AstNode *> &classProperties,
136 const std::vector<ir::AstNode *> &extendedClassProperties)
137 {
138 std::string constructorInfoText = "constructor(";
139 std::string parameterList;
140 std::string functionBody;
141
142 if (!extendedClassProperties.empty()) {
143 GetParameterListAndFunctionBody(parameterList, functionBody, extendedClassProperties, true);
144 }
145 GetParameterListAndFunctionBody(parameterList, functionBody, classProperties, false);
146 RemoveTrailingChar(parameterList, ",");
147 constructorInfoText += parameterList;
148 constructorInfoText += ") {\n";
149 constructorInfoText += functionBody;
150 constructorInfoText += "}";
151 return constructorInfoText;
152 }
153
GetInsertNodePosition(ir::AstNode * classNode,size_t & insertPosition)154 void GetInsertNodePosition(ir::AstNode *classNode, size_t &insertPosition)
155 {
156 if (classNode == nullptr || !classNode->IsClassDeclaration()) {
157 return;
158 }
159
160 bool isExitProperty = false;
161 auto classBody = classNode->AsClassDeclaration()->Definition()->Body();
162 for (auto node : classBody) {
163 if (node->IsClassProperty() && !isExitProperty) {
164 insertPosition = node->AsClassProperty()->Start().index;
165 isExitProperty = true;
166 break;
167 }
168 }
169
170 if (!isExitProperty) {
171 const int offset = 2;
172 insertPosition = classNode->End().index - offset;
173 }
174 }
175
GetRefactorActionsToGenerateConstructor(es2panda_Context * context,size_t position,const std::vector<std::string> & properties)176 std::vector<FileTextChanges> GetRefactorActionsToGenerateConstructor(es2panda_Context *context, size_t position,
177 const std::vector<std::string> &properties)
178 {
179 if (context == nullptr) {
180 return {};
181 }
182 auto ctx = reinterpret_cast<public_lib::Context *>(context);
183 if (ctx->parserProgram == nullptr || ctx->parserProgram->Ast() == nullptr) {
184 return {};
185 }
186
187 ir::AstNode *classDeclaration = ark::es2panda::lsp::GetTargetDeclarationNodeByPosition(context, position);
188 if (!IsDefinedClassOrStruct(classDeclaration)) {
189 return {};
190 }
191
192 if (HasConstructorNode(classDeclaration)) {
193 return {};
194 }
195
196 std::vector<ir::AstNode *> classProperties = GetClassProperties(classDeclaration, properties);
197 std::vector<ir::AstNode *> extendedClassProperties = GetExtendedClassProperties(classDeclaration);
198
199 std::string text = CollectConstructorInfo(classProperties, extendedClassProperties);
200 size_t insertPosition = 0;
201 GetInsertNodePosition(classDeclaration, insertPosition);
202
203 std::vector<FileTextChanges> fileTextChanges;
204 TextSpan span(insertPosition, text.size());
205 std::vector<TextChange> textChanges;
206 textChanges.emplace_back(TextChange(span, text));
207 auto fileName = ctx->sourceFileName;
208 FileTextChanges textChange(fileName, textChanges);
209 fileTextChanges.push_back(textChange);
210
211 return fileTextChanges;
212 }
213
214 } // namespace ark::es2panda::lsp