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