• 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 #ifndef ES2PANDA_LSP_CHANGE_TRACKER_H
17 #define ES2PANDA_LSP_CHANGE_TRACKER_H
18 #include <cstddef>
19 #include <iostream>
20 #include <string>
21 #include <utility>
22 #include <vector>
23 #include "es2panda.h"
24 #include "public/public.h"
25 #include <ir/typeNode.h>
26 #include "lsp/include/api.h"
27 #include "ir/astNode.h"
28 #include "lsp/include/user_preferences.h"
29 #include "lsp/include/formatting/formatting_context.h"
30 #include "lsp/include/formatting/formatting.h"
31 #include "lsp/include/internal_api.h"
32 #include "public/es2panda_lib.h"
33 
34 namespace ark::es2panda::lsp {
35 
36 enum class LeadingTriviaOption { EXCLUDE, INCLUDEALL, STARTLINE };
37 
38 enum class TrailingTriviaOption { EXCLUDE, EXCLUDEWHITESPACE, INCLUDE };
39 
40 struct ConfigurableStart {
41     std::optional<LeadingTriviaOption> leadingTriviaOption;
42 };
43 
44 struct ConfigurableEnd {
45     std::optional<TrailingTriviaOption> trailingTriviaOption;
46 };
47 
48 struct ConfigurableStartEnd {
49     std::optional<LeadingTriviaOption> leadingTriviaOption;
50     std::optional<TrailingTriviaOption> trailingTriviaOption;
51 };
52 
53 struct InsertNodeOptions {
54     std::optional<std::string> prefix;
55     std::optional<std::string> suffix;
56     std::optional<int> indentation;
57     std::optional<int> delta;
58 };
59 
60 struct ReplaceWithMultipleNodesOptions : InsertNodeOptions {
61     std::optional<std::string> joiner;
62 };
63 
64 struct ChangeNodeOptions {
65     std::optional<ConfigurableStartEnd> configurableStartEnd;
66     std::optional<InsertNodeOptions> insertNodeOptions;
67 };
68 
69 enum class ChangeKind { REMOVE, REPLACEWITHSINGLENODE, REPLACEWITHMULTIPLENODES, TEXT };
70 
71 struct ChangeText {
72     const SourceFile *sourceFile;
73     TextRange range;
74     ChangeKind kind = ChangeKind::TEXT;
75     std::string text;
76 };
77 
78 struct ReplaceWithSingleNode {
79     const SourceFile *sourceFile;
80     TextRange range;
81     ChangeKind kind = ChangeKind::REPLACEWITHSINGLENODE;
82     const ir::AstNode *node;
83     std::optional<InsertNodeOptions> options;
84 };
85 
86 struct ReplaceWithMultipleNodes {
87     const SourceFile *sourceFile;
88     TextRange range;
89     ChangeKind kind = ChangeKind::REPLACEWITHMULTIPLENODES;
90     std::vector<ir::AstNode *> nodes;
91     std::optional<ReplaceWithMultipleNodesOptions> options;
92 };
93 struct RemoveNode {
94     const SourceFile *sourceFile;
95     TextRange range;
96     ChangeKind kind = ChangeKind::REMOVE;
97 };
98 
99 struct NewFile {
100     std::optional<SourceFile *> oldFile;
101     std::string fileName;
102     std::vector<const ir::Statement *> statements;
103 };
104 
105 using Change = std::variant<ReplaceWithSingleNode, ReplaceWithMultipleNodes, RemoveNode, ChangeText>;
106 
107 struct DeletedNode {
108     const SourceFile *sourceFile;
109     const std::variant<const ir::AstNode *, const std::vector<const ir::AstNode *>> &node;
110 };
111 
112 struct NewFileStruct {
113     std::optional<SourceFile *> oldFile;
114     std::string fileName;
115     std::vector<std::variant<ir::Statement *, int>> statements;
116 };
117 
118 using ValidateNonFormattedText = std::function<void(ir::AstNode *node, const std::string &text)>;
119 
120 struct ClassInsertInfo {
121     ir::AstNode *node;
122     SourceFile *sourceFile;
123 };
124 
125 class ChangeTracker {
126 private:
127     FormatContext formatContext_;
128     std::string newLineCharacter_;
129 
ChangeTracker(FormatContext & formatContext,std::string newLineCharacter)130     ChangeTracker(FormatContext &formatContext, std::string newLineCharacter)
131         : formatContext_(formatContext), newLineCharacter_(std::move(newLineCharacter)) {};
132 
133     ir::AstNode *GetAstFromContext(const es2panda_Context *context);
134     size_t GetStartPositionOfLine(size_t line, const es2panda_Context *context);
135     bool RangeContainsPosition(TextRange r, size_t pos);
136     void ReplaceRangeWithNodes(es2panda_Context *context, const TextRange range, std::vector<ir::AstNode *> &newNodes,
137                                ReplaceWithMultipleNodesOptions options = {});
138     ir::AstNode *NextCommaToken(es2panda_Context *context, const ir::AstNode *node);
139     void InsertNodesAt(es2panda_Context *context, size_t pos, std::vector<ir::AstNode *> &newNodes,
140                        ReplaceWithMultipleNodesOptions options = {});
141     void InsertAtTopOfFile(es2panda_Context *context,
142                            const std::variant<ir::AstNode *, std::vector<ir::AstNode *>> &insert,
143                            bool blankLineBetween);
144     InsertNodeOptions GetOptionsForInsertNodeBefore(const ir::AstNode *before, const ir::AstNode *inserted,
145                                                     bool blankLineBetween);
146     std::vector<ir::AstNode *> GetMembersOrProperties(const ir::AstNode *node);
147     InsertNodeOptions GetInsertNodeAtStartInsertOptions(const ir::AstNode *node);
148     void InsertNodeAtStartWorker(es2panda_Context *context, const ir::AstNode *node, const ir::AstNode *newElement);
149     bool NeedSemicolonBetween(const ir::AstNode *a, const ir::AstNode *b);
150     size_t InsertNodeAfterWorker(es2panda_Context *context, ir::AstNode *after, const ir::AstNode *newNode);
151     InsertNodeOptions GetInsertNodeAfterOptionsWorker(const ir::AstNode *node);
152     void InsertNodeInListAfterMultiLine(bool multilineList, es2panda_Context *context, const SourceFile *sourceFile,
153                                         size_t end, const ir::AstNode *newNode);
154     std::vector<FileTextChanges> GetTextChangesFromChanges(std::vector<Change> &changes, std::string &newLineCharacter,
155                                                            const FormatCodeSettings &formatCodeSettings);
156     std::vector<DeletedNode> deletedNodes_;
157     std::vector<Change> changes_;
158     std::vector<NewFile> newFiles_;
159     std::map<int, ClassInsertInfo> classesWithNodesInsertedAtStart_;
160 
161 public:
GetDeletedNodesList()162     std::vector<DeletedNode> GetDeletedNodesList() const
163     {
164         return deletedNodes_;
165     }
GetChangeList()166     std::vector<Change> GetChangeList() const
167     {
168         return changes_;
169     }
GetNewFilesList()170     std::vector<NewFile> GetNewFilesList() const
171     {
172         return newFiles_;
173     }
GetClassesWithNodesInsertedAtStartList()174     std::map<int, ClassInsertInfo> GetClassesWithNodesInsertedAtStartList() const
175     {
176         return classesWithNodesInsertedAtStart_;
177     }
178 
179     static ChangeTracker FromContext(TextChangesContext &context);
180     static std::vector<FileTextChanges> With(TextChangesContext &context,
181                                              const std::function<void(ChangeTracker &)> &cb);
182 
183     void PushRaw(const SourceFile *sourceFile, const FileTextChanges &change);
184     void DeleteRange(const SourceFile *sourceFile, TextRange range);
185     std::vector<FileTextChanges> GetChanges();
186     void Delete(const SourceFile *sourceFile,
187                 std::variant<const ir::AstNode *, const std::vector<const ir::AstNode *>> &node);
188     TextRange GetAdjustedRange(es2panda_Context *context, ir::AstNode *startNode, ir::AstNode *endNode);
189 
190     void FinishDeleteDeclarations();
191     void DeleteNode(es2panda_Context *context, const SourceFile *sourceFile, ir::AstNode *node);
192 
193     void DeleteNodeRange(es2panda_Context *context, ir::AstNode *startNode, ir::AstNode *endNode);
194 
195     void DeleteModifier(es2panda_Context *context, ir::AstNode *modifier);
196     void DeleteNodeRangeExcludingEnd(es2panda_Context *context, ir::AstNode *startNode, ir::AstNode *afterEndNode);
197     void ReplaceRange(es2panda_Context *context, TextRange range, const ir::AstNode *newNode,
198                       InsertNodeOptions &options);
199     void ReplaceNode(es2panda_Context *context, ir::AstNode *oldNode, ir::AstNode *newNode, ChangeNodeOptions options);
200     void ReplaceNodeRange(es2panda_Context *context, ir::AstNode *startNode, ir::AstNode *endNode,
201                           ir::AstNode *newNode);
202     void ReplaceNodeWithNodes(es2panda_Context *context, ir::AstNode *oldNode, std::vector<ir::AstNode *> &newNodes);
203     void ReplaceNodeWithText(es2panda_Context *context, ir::AstNode *oldNode, const std::string &text);
204     void ReplaceRangeWithText(const SourceFile *sourceFile, TextRange range, const std::string &text);
205     void ReplaceNodeRangeWithNodes(es2panda_Context *context, ir::AstNode *startNode, ir::AstNode *endNode,
206                                    std::vector<ir::AstNode *> &newNodes);
207     TextRange CreateRange(size_t pos, size_t end = 0);
208     void ReplacePropertyAssignment(es2panda_Context *context, ir::AstNode *oldNode, ir::AstNode *newNode);
209     void InsertNodeAt(es2panda_Context *context, size_t pos, const ir::AstNode *newNode, InsertNodeOptions &options);
210     bool IsLineBreak(char ch);
211     void FinishClassesWithNodesInsertedAtStart();
212     size_t GetInsertionPositionAtSourceFileTop(ir::AstNode *sourceFileAst);
213     void InsertNodeAtTopOfFile(es2panda_Context *context, ir::AstNode *newNode, bool blankLineBetween);
214     void InsertNodesAtTopOfFile(es2panda_Context *context, const std::vector<const ir::AstNode *> newNodes,
215                                 bool blankLineBetween);
216     void InsertNodeBefore(es2panda_Context *context, ir::AstNode *before, ir::AstNode *newNode,
217                           bool blankLineBetween = false);
218     void InsertModifierAt(es2panda_Context *context, size_t pos, const ir::AstNode *modifier,
219                           InsertNodeOptions &options);
220     void InsertModifierBefore(es2panda_Context *context, const ir::AstNode *modifier, ir::AstNode *before);
221     void InsertText(const SourceFile *sourceFile, size_t pos, const std::string &text);
222     bool TryInsertTypeAnnotation(es2panda_Context *context, ir::AstNode *node, ir::AstNode *type);
223     void TryInsertThisTypeAnnotation(es2panda_Context *context, ir::AstNode *node, ir::AstNode *type);
224     void InsertTypeParameters(es2panda_Context *context, const ir::AstNode *node,
225                               std::vector<ir::AstNode *> &typeParameters);
226     void ReplaceConstructorBody(es2panda_Context *context, ir::AstNode *ctr,
227                                 const std::vector<ir::Statement *> &statements);
228     void InsertNodeAtConstructorStart(es2panda_Context *context, ir::AstNode *ctr, ir::Statement *newStatement);
229     void InsertNodeAfter(es2panda_Context *context, ir::AstNode *after, ir::AstNode *newNode);
230     void InsertNodeAtConstructorEnd(es2panda_Context *context, ir::AstNode *ctr, ir::Statement *newStatement);
231     void InsertNodeAtEndOfScope(es2panda_Context *context, ir::AstNode *scope, ir::AstNode *newNode);
232     void InsertMemberAtStart(es2panda_Context *context, ir::AstNode *node, ir::AstNode *newElement);
233     void InsertNodeAtObjectStart(es2panda_Context *context, ir::ObjectExpression *obj, ir::AstNode *newElement);
234     void InsertNodeAfterComma(es2panda_Context *context, ir::AstNode *after, ir::AstNode *newNode);
235     void InsertNodeAtEndOfList(es2panda_Context *context, std::vector<const ir::AstNode *> &list, ir::AstNode *newNode);
236     InsertNodeOptions GetInsertNodeAfterOptions(const ir::AstNode *after);
237     void InsertNodesAfter(es2panda_Context *context, ir::AstNode *after, std::vector<ir::AstNode *> newNodes);
238     void InsertFirstParameter(es2panda_Context *context, std::vector<ir::TSTypeParameterDeclaration *> parameters,
239                               ir::TSTypeParameterDeclaration newParam);
240     void InsertExportModifier(const SourceFile *sourceFile, ir::Statement *node);
241     std::vector<ir::AstNode *> GetContainingList(ir::AstNode *node);
242     void InsertNodeInListAfter(es2panda_Context *context, ir::AstNode *after, ir::AstNode *newNode,
243                                std::vector<ir::AstNode *> &containingList);
244     void InsertImportSpecifierAtIndex(es2panda_Context *context, ir::AstNode *importSpecifier,
245                                       std::vector<ir::AstNode *> &namedImports, size_t index);
246     void CreateNewFile(SourceFile *oldFile, const std::string &fileName,
247                        std::vector<const ir::Statement *> &statements);
248 };
249 
250 }  // namespace ark::es2panda::lsp
251 
252 #endif
253