@zzzen/pyright-internal 1.2.0-dev.20230219 → 1.2.0-dev.20230305

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (166) hide show
  1. package/dist/analyzer/backgroundAnalysisProgram.d.ts +1 -1
  2. package/dist/analyzer/backgroundAnalysisProgram.js +3 -3
  3. package/dist/analyzer/backgroundAnalysisProgram.js.map +1 -1
  4. package/dist/analyzer/binder.d.ts +0 -1
  5. package/dist/analyzer/binder.js +18 -93
  6. package/dist/analyzer/binder.js.map +1 -1
  7. package/dist/analyzer/checker.js +50 -30
  8. package/dist/analyzer/checker.js.map +1 -1
  9. package/dist/analyzer/codeFlowEngine.js +5 -5
  10. package/dist/analyzer/codeFlowEngine.js.map +1 -1
  11. package/dist/analyzer/constraintSolver.js +0 -10
  12. package/dist/analyzer/constraintSolver.js.map +1 -1
  13. package/dist/analyzer/declaration.d.ts +3 -4
  14. package/dist/analyzer/declaration.js +6 -1
  15. package/dist/analyzer/declaration.js.map +1 -1
  16. package/dist/analyzer/declarationUtils.d.ts +1 -5
  17. package/dist/analyzer/declarationUtils.js +2 -47
  18. package/dist/analyzer/declarationUtils.js.map +1 -1
  19. package/dist/analyzer/docStringConversion.js +9 -3
  20. package/dist/analyzer/docStringConversion.js.map +1 -1
  21. package/dist/analyzer/importResolver.d.ts +3 -2
  22. package/dist/analyzer/importResolver.js +28 -19
  23. package/dist/analyzer/importResolver.js.map +1 -1
  24. package/dist/analyzer/parseTreeUtils.d.ts +4 -2
  25. package/dist/analyzer/parseTreeUtils.js +77 -2
  26. package/dist/analyzer/parseTreeUtils.js.map +1 -1
  27. package/dist/analyzer/patternMatching.d.ts +1 -0
  28. package/dist/analyzer/patternMatching.js +47 -2
  29. package/dist/analyzer/patternMatching.js.map +1 -1
  30. package/dist/analyzer/program.d.ts +5 -4
  31. package/dist/analyzer/program.js +182 -43
  32. package/dist/analyzer/program.js.map +1 -1
  33. package/dist/analyzer/properties.js +8 -3
  34. package/dist/analyzer/properties.js.map +1 -1
  35. package/dist/analyzer/protocols.js +8 -5
  36. package/dist/analyzer/protocols.js.map +1 -1
  37. package/dist/analyzer/pythonPathUtils.js +27 -13
  38. package/dist/analyzer/pythonPathUtils.js.map +1 -1
  39. package/dist/analyzer/service.d.ts +7 -3
  40. package/dist/analyzer/service.js +24 -7
  41. package/dist/analyzer/service.js.map +1 -1
  42. package/dist/analyzer/sourceFile.js +3 -0
  43. package/dist/analyzer/sourceFile.js.map +1 -1
  44. package/dist/analyzer/symbol.js +1 -4
  45. package/dist/analyzer/symbol.js.map +1 -1
  46. package/dist/analyzer/symbolUtils.d.ts +0 -3
  47. package/dist/analyzer/symbolUtils.js +1 -14
  48. package/dist/analyzer/symbolUtils.js.map +1 -1
  49. package/dist/analyzer/typeDocStringUtils.js +5 -3
  50. package/dist/analyzer/typeDocStringUtils.js.map +1 -1
  51. package/dist/analyzer/typeEvaluator.js +586 -294
  52. package/dist/analyzer/typeEvaluator.js.map +1 -1
  53. package/dist/analyzer/typeEvaluatorTypes.d.ts +10 -2
  54. package/dist/analyzer/typeGuards.js +2 -2
  55. package/dist/analyzer/typeGuards.js.map +1 -1
  56. package/dist/analyzer/typeUtils.d.ts +1 -0
  57. package/dist/analyzer/typeUtils.js +14 -1
  58. package/dist/analyzer/typeUtils.js.map +1 -1
  59. package/dist/analyzer/typedDicts.js +26 -17
  60. package/dist/analyzer/typedDicts.js.map +1 -1
  61. package/dist/analyzer/types.d.ts +2 -1
  62. package/dist/analyzer/types.js.map +1 -1
  63. package/dist/backgroundAnalysisBase.d.ts +2 -3
  64. package/dist/backgroundAnalysisBase.js +12 -13
  65. package/dist/backgroundAnalysisBase.js.map +1 -1
  66. package/dist/commands/commandController.js +1 -0
  67. package/dist/commands/commandController.js.map +1 -1
  68. package/dist/commands/quickActionCommand.js +2 -2
  69. package/dist/commands/quickActionCommand.js.map +1 -1
  70. package/dist/common/cancellationUtils.d.ts +18 -2
  71. package/dist/common/cancellationUtils.js +80 -3
  72. package/dist/common/cancellationUtils.js.map +1 -1
  73. package/dist/common/collectionUtils.d.ts +1 -0
  74. package/dist/common/collectionUtils.js +8 -1
  75. package/dist/common/collectionUtils.js.map +1 -1
  76. package/dist/common/extensibility.d.ts +4 -3
  77. package/dist/common/extensibility.js.map +1 -1
  78. package/dist/common/fileBasedCancellationUtils.d.ts +0 -1
  79. package/dist/common/fileBasedCancellationUtils.js +7 -81
  80. package/dist/common/fileBasedCancellationUtils.js.map +1 -1
  81. package/dist/common/positionUtils.d.ts +2 -0
  82. package/dist/common/positionUtils.js +15 -1
  83. package/dist/common/positionUtils.js.map +1 -1
  84. package/dist/common/{textEditUtils.d.ts → textEditTracker.d.ts} +2 -6
  85. package/dist/common/{textEditUtils.js → textEditTracker.js} +6 -49
  86. package/dist/common/textEditTracker.js.map +1 -0
  87. package/dist/common/workspaceEditUtils.d.ts +14 -8
  88. package/dist/common/workspaceEditUtils.js +115 -59
  89. package/dist/common/workspaceEditUtils.js.map +1 -1
  90. package/dist/languageServerBase.js +9 -3
  91. package/dist/languageServerBase.js.map +1 -1
  92. package/dist/languageService/callHierarchyProvider.js +4 -2
  93. package/dist/languageService/callHierarchyProvider.js.map +1 -1
  94. package/dist/languageService/codeActionProvider.js +1 -1
  95. package/dist/languageService/codeActionProvider.js.map +1 -1
  96. package/dist/languageService/completionProvider.js +49 -43
  97. package/dist/languageService/completionProvider.js.map +1 -1
  98. package/dist/languageService/completionProviderUtils.d.ts +1 -1
  99. package/dist/languageService/completionProviderUtils.js +3 -3
  100. package/dist/languageService/completionProviderUtils.js.map +1 -1
  101. package/dist/languageService/definitionProvider.js +2 -1
  102. package/dist/languageService/definitionProvider.js.map +1 -1
  103. package/dist/languageService/documentSymbolProvider.js +2 -1
  104. package/dist/languageService/documentSymbolProvider.js.map +1 -1
  105. package/dist/languageService/hoverProvider.js +14 -3
  106. package/dist/languageService/hoverProvider.js.map +1 -1
  107. package/dist/languageService/importAdder.d.ts +2 -2
  108. package/dist/languageService/importAdder.js +5 -3
  109. package/dist/languageService/importAdder.js.map +1 -1
  110. package/dist/languageService/indentationUtils.d.ts +6 -2
  111. package/dist/languageService/indentationUtils.js +33 -13
  112. package/dist/languageService/indentationUtils.js.map +1 -1
  113. package/dist/languageService/renameModuleProvider.d.ts +10 -2
  114. package/dist/languageService/renameModuleProvider.js +87 -14
  115. package/dist/languageService/renameModuleProvider.js.map +1 -1
  116. package/dist/localization/localize.d.ts +12 -2
  117. package/dist/localization/localize.js +5 -1
  118. package/dist/localization/localize.js.map +1 -1
  119. package/dist/localization/package.nls.en-us.json +6 -2
  120. package/dist/parser/parser.js +18 -18
  121. package/dist/parser/parser.js.map +1 -1
  122. package/dist/pyright.js +1 -1
  123. package/dist/pyright.js.map +1 -1
  124. package/dist/tests/docStringConversion.test.js +23 -0
  125. package/dist/tests/docStringConversion.test.js.map +1 -1
  126. package/dist/tests/fourslash/completions.errorNodes.fourslash.d.ts +1 -0
  127. package/dist/tests/fourslash/completions.errorNodes.fourslash.js +24 -0
  128. package/dist/tests/fourslash/completions.errorNodes.fourslash.js.map +1 -0
  129. package/dist/tests/fourslash/hover.docstring.parameter.fourslash.d.ts +1 -0
  130. package/dist/tests/fourslash/hover.docstring.parameter.fourslash.js +57 -0
  131. package/dist/tests/fourslash/hover.docstring.parameter.fourslash.js.map +1 -0
  132. package/dist/tests/fourslash/hover.slots.fourslash.d.ts +1 -0
  133. package/dist/tests/fourslash/hover.slots.fourslash.js +16 -0
  134. package/dist/tests/fourslash/hover.slots.fourslash.js.map +1 -0
  135. package/dist/tests/harness/fourslash/testState.js +9 -2
  136. package/dist/tests/harness/fourslash/testState.js.map +1 -1
  137. package/dist/tests/importAdder.test.js +16 -3
  138. package/dist/tests/importAdder.test.js.map +1 -1
  139. package/dist/tests/indentationUtils.reindent.test.js +22 -1
  140. package/dist/tests/indentationUtils.reindent.test.js.map +1 -1
  141. package/dist/tests/moveSymbol.importAdder.test.js +183 -24
  142. package/dist/tests/moveSymbol.importAdder.test.js.map +1 -1
  143. package/dist/tests/moveSymbol.insertion.test.js +137 -2
  144. package/dist/tests/moveSymbol.insertion.test.js.map +1 -1
  145. package/dist/tests/moveSymbol.misc.test.js +109 -4
  146. package/dist/tests/moveSymbol.misc.test.js.map +1 -1
  147. package/dist/tests/renameModuleTestUtils.d.ts +1 -1
  148. package/dist/tests/renameModuleTestUtils.js +9 -5
  149. package/dist/tests/renameModuleTestUtils.js.map +1 -1
  150. package/dist/tests/testStateUtils.js +2 -2
  151. package/dist/tests/testStateUtils.js.map +1 -1
  152. package/dist/tests/textEditUtil.test.js +2 -2
  153. package/dist/tests/textEditUtil.test.js.map +1 -1
  154. package/dist/tests/typeEvaluator1.test.js +8 -0
  155. package/dist/tests/typeEvaluator1.test.js.map +1 -1
  156. package/dist/tests/typeEvaluator2.test.js +13 -1
  157. package/dist/tests/typeEvaluator2.test.js.map +1 -1
  158. package/dist/tests/typeEvaluator3.test.js +29 -4
  159. package/dist/tests/typeEvaluator3.test.js.map +1 -1
  160. package/dist/tests/typeEvaluator4.test.js +1 -1
  161. package/dist/tests/typeEvaluator5.test.js +6 -0
  162. package/dist/tests/typeEvaluator5.test.js.map +1 -1
  163. package/dist/tests/workspaceEditUtils.test.js +7 -7
  164. package/dist/tests/workspaceEditUtils.test.js.map +1 -1
  165. package/package.json +3 -3
  166. package/dist/common/textEditUtils.js.map +0 -1
@@ -86,6 +86,7 @@ export declare class Program {
86
86
  setPreCheckCallback(preCheckCallback: PreCheckCallback): void;
87
87
  setAllowedThirdPartyImports(importNames: string[]): void;
88
88
  addTrackedFiles(filePaths: string[], isThirdPartyImport?: boolean, isInPyTypedPackage?: boolean): void;
89
+ addInterimFile(filePath: string): SourceFileInfo;
89
90
  addTrackedFile(filePath: string, isThirdPartyImport?: boolean, isInPyTypedPackage?: boolean): SourceFile;
90
91
  setFileOpened(filePath: string, version: number | null, contents: TextDocumentContentChangeEvent[], options?: OpenFileOptions): void;
91
92
  getChainedFilePath(filePath: string): string | undefined;
@@ -95,7 +96,7 @@ export declare class Program {
95
96
  markFilesDirty(filePaths: string[], evenIfContentsAreSame: boolean, indexingNeeded?: boolean): void;
96
97
  getFileCount(userFileOnly?: boolean): number;
97
98
  getUserFileCount(): number;
98
- getTracked(): SourceFileInfo[];
99
+ getUserFiles(): SourceFileInfo[];
99
100
  getOpened(): SourceFileInfo[];
100
101
  getFilesToAnalyzeCount(): number;
101
102
  isCheckingOnlyOpenFiles(): boolean;
@@ -105,7 +106,7 @@ export declare class Program {
105
106
  getSourceFile(filePath: string): SourceFile | undefined;
106
107
  getBoundSourceFile(filePath: string): SourceFile | undefined;
107
108
  getSourceFileInfo(filePath: string): SourceFileInfo | undefined;
108
- getBoundSourceFileInfo(filePath: string): SourceFileInfo | undefined;
109
+ getBoundSourceFileInfo(filePath: string, content?: string, force?: boolean): SourceFileInfo | undefined;
109
110
  analyze(maxTime?: MaxAnalysisTime, token?: CancellationToken): boolean;
110
111
  analyzeFile(filePath: string, token?: CancellationToken): boolean;
111
112
  indexWorkspace(callback: (path: string, results: IndexResults) => void, token: CancellationToken): number;
@@ -150,8 +151,8 @@ export declare class Program {
150
151
  renameModule(path: string, newPath: string, token: CancellationToken): FileEditActions | undefined;
151
152
  moveSymbolAtPosition(filePath: string, newFilePath: string, position: Position, options: {
152
153
  importFormat: ImportFormat;
153
- }, token?: CancellationToken): FileEditActions | undefined;
154
- private _moveSymbolAtPosition;
154
+ }, token: CancellationToken): FileEditActions | undefined;
155
+ clone(): Program;
155
156
  canRenameSymbolAtPosition(filePath: string, position: Position, isDefaultWorkspace: boolean, allowModuleRename: boolean, token: CancellationToken): {
156
157
  range: Range;
157
158
  declarations: Declaration[];
@@ -36,15 +36,16 @@ const collectionUtils_1 = require("../common/collectionUtils");
36
36
  const configOptions_1 = require("../common/configOptions");
37
37
  const console_1 = require("../common/console");
38
38
  const debug_1 = require("../common/debug");
39
- const diagnosticSink_1 = require("../common/diagnosticSink");
40
39
  const extensibility_1 = require("../common/extensibility");
41
40
  const logTracker_1 = require("../common/logTracker");
42
41
  const pathUtils_1 = require("../common/pathUtils");
43
42
  const positionUtils_1 = require("../common/positionUtils");
44
43
  const stringUtils_1 = require("../common/stringUtils");
45
- const textEditUtils_1 = require("../common/textEditUtils");
44
+ const textEditTracker_1 = require("../common/textEditTracker");
46
45
  const textRange_1 = require("../common/textRange");
46
+ const textRangeCollection_1 = require("../common/textRangeCollection");
47
47
  const timing_1 = require("../common/timing");
48
+ const workspaceEditUtils_1 = require("../common/workspaceEditUtils");
48
49
  const autoImporter_1 = require("../languageService/autoImporter");
49
50
  const callHierarchyProvider_1 = require("../languageService/callHierarchyProvider");
50
51
  const completionProvider_1 = require("../languageService/completionProvider");
@@ -219,6 +220,15 @@ class Program {
219
220
  this.addTrackedFile(filePath, isThirdPartyImport, isInPyTypedPackage);
220
221
  });
221
222
  }
223
+ addInterimFile(filePath) {
224
+ // Double check not already there.
225
+ let fileInfo = this.getSourceFileInfo(filePath);
226
+ if (!fileInfo) {
227
+ fileInfo = this._createInterimFileInfo(filePath);
228
+ this._addToSourceFileListAndMap(fileInfo);
229
+ }
230
+ return fileInfo;
231
+ }
222
232
  addTrackedFile(filePath, isThirdPartyImport = false, isInPyTypedPackage = false) {
223
233
  let sourceFileInfo = this.getSourceFileInfo(filePath);
224
234
  const importName = this._getImportNameForFile(filePath);
@@ -280,8 +290,6 @@ class Program {
280
290
  sourceFileInfo.diagnosticsVersion = 0;
281
291
  }
282
292
  sourceFileInfo.sourceFile.setClientVersion(version, contents);
283
- // Tell any extensions that this source file changed.
284
- extensibility_1.Extensions.getProgramExtensions(filePath).forEach((e) => e.sourceFileChanged ? e.sourceFileChanged(sourceFileInfo) : undefined);
285
293
  }
286
294
  getChainedFilePath(filePath) {
287
295
  var _a;
@@ -369,8 +377,8 @@ class Program {
369
377
  getUserFileCount() {
370
378
  return this._sourceFileList.filter((s) => (0, sourceFileInfoUtils_1.isUserCode)(s)).length;
371
379
  }
372
- getTracked() {
373
- return this._sourceFileList.filter((s) => s.isTracked);
380
+ getUserFiles() {
381
+ return this._sourceFileList.filter((s) => (0, sourceFileInfoUtils_1.isUserCode)(s));
374
382
  }
375
383
  getOpened() {
376
384
  return this._sourceFileList.filter((s) => s.isOpenByClient);
@@ -422,12 +430,12 @@ class Program {
422
430
  getSourceFileInfo(filePath) {
423
431
  return this._sourceFileMap.get((0, pathUtils_1.normalizePathCase)(this._fs, filePath));
424
432
  }
425
- getBoundSourceFileInfo(filePath) {
433
+ getBoundSourceFileInfo(filePath, content, force) {
426
434
  const sourceFileInfo = this.getSourceFileInfo(filePath);
427
435
  if (!sourceFileInfo) {
428
436
  return undefined;
429
437
  }
430
- this._bindFile(sourceFileInfo);
438
+ this._bindFile(sourceFileInfo, content, force);
431
439
  return sourceFileInfo;
432
440
  }
433
441
  // Performs parsing and analysis of any source files in the program
@@ -678,8 +686,7 @@ class Program {
678
686
  _addShadowedFile(stubFile, shadowImplPath) {
679
687
  let shadowFileInfo = this.getSourceFileInfo(shadowImplPath);
680
688
  if (!shadowFileInfo) {
681
- shadowFileInfo = this._createInterimFileInfo(shadowImplPath);
682
- this._addToSourceFileListAndMap(shadowFileInfo);
689
+ shadowFileInfo = this.addInterimFile(shadowImplPath);
683
690
  }
684
691
  if (!shadowFileInfo.shadows.includes(stubFile)) {
685
692
  shadowFileInfo.shadows.push(stubFile);
@@ -727,8 +734,8 @@ class Program {
727
734
  : undefined);
728
735
  return this._evaluator;
729
736
  }
730
- _parseFile(fileToParse, content) {
731
- if (!this._isFileNeeded(fileToParse) || !fileToParse.sourceFile.isParseRequired()) {
737
+ _parseFile(fileToParse, content, force) {
738
+ if (!force && (!this._isFileNeeded(fileToParse) || !fileToParse.sourceFile.isParseRequired())) {
732
739
  return;
733
740
  }
734
741
  if (fileToParse.sourceFile.parse(this._configOptions, this._importResolver, content)) {
@@ -747,12 +754,12 @@ class Program {
747
754
  }
748
755
  // Binds the specified file and all of its dependencies, recursively. If
749
756
  // it runs out of time, it returns true. If it completes, it returns false.
750
- _bindFile(fileToAnalyze, content) {
757
+ _bindFile(fileToAnalyze, content, force) {
751
758
  var _a, _b;
752
- if (!this._isFileNeeded(fileToAnalyze) || !fileToAnalyze.sourceFile.isBindingRequired()) {
759
+ if (!force && (!this._isFileNeeded(fileToAnalyze) || !fileToAnalyze.sourceFile.isBindingRequired())) {
753
760
  return;
754
761
  }
755
- this._parseFile(fileToAnalyze, content);
762
+ this._parseFile(fileToAnalyze, content, force);
756
763
  const getScopeIfAvailable = (fileInfo) => {
757
764
  if (!fileInfo || fileInfo === fileToAnalyze) {
758
765
  return undefined;
@@ -1376,13 +1383,8 @@ class Program {
1376
1383
  });
1377
1384
  }
1378
1385
  moveSymbolAtPosition(filePath, newFilePath, position, options, token) {
1379
- if (vscode_languageserver_1.CancellationToken.is(options)) {
1380
- return this._moveSymbolAtPosition(filePath, newFilePath, position, { importFormat: "absolute" /* Absolute */ }, options);
1381
- }
1382
- return this._moveSymbolAtPosition(filePath, newFilePath, position, options, token);
1383
- }
1384
- _moveSymbolAtPosition(filePath, newFilePath, position, options, token) {
1385
1386
  return this._runEvaluatorWithCancellationToken(token, () => {
1387
+ var _a, _b;
1386
1388
  const sourceFileExt = (0, pathUtils_1.getFileExtension)(filePath);
1387
1389
  const destFileExt = (0, pathUtils_1.getFileExtension)(newFilePath);
1388
1390
  if (sourceFileExt.toLowerCase() !== destFileExt.toLowerCase()) {
@@ -1412,9 +1414,21 @@ class Program {
1412
1414
  return undefined;
1413
1415
  }
1414
1416
  // If this isn't a name node, there are no references to be found.
1415
- if (node.nodeType !== 38 /* Name */) {
1417
+ if (node.nodeType !== 38 /* Name */ || !renameModuleProvider_1.RenameModuleProvider.canMoveSymbol(this._evaluator, node)) {
1416
1418
  return undefined;
1417
1419
  }
1420
+ // We will try to
1421
+ // 1. Find symbol to move.
1422
+ // 2. Update all references to the symbol to new location.
1423
+ // 3. Remove the existing symbol.
1424
+ // 4. Insert the symbol to the destination module.
1425
+ // 5. Insert imports required for the symbol moved to the destination module.
1426
+ // 6. Remove import no longer needed from the original module.
1427
+ //
1428
+ // Here all changes are done to edits, no features in LS will apply changes to
1429
+ // program directly. All modification is done through LSP by a edit request so
1430
+ // things like undo or edit stacks UI works.
1431
+ // 1. Find symbol to move.
1418
1432
  const execEnv = this._configOptions.findExecEnvironment(filePath);
1419
1433
  const declarations = documentSymbolCollector_1.DocumentSymbolCollector.getDeclarationsForNode(node, this._evaluator,
1420
1434
  /* resolveLocalNames */ false, documentSymbolCollector_1.DocumentSymbolCollectorUseCase.Rename, token, this._createSourceMapper(execEnv, token, fileInfo));
@@ -1422,21 +1436,21 @@ class Program {
1422
1436
  if (!renameModuleProvider) {
1423
1437
  return undefined;
1424
1438
  }
1439
+ // 2. Update affected references.
1425
1440
  this._processModuleReferences(renameModuleProvider, node.value, filePath);
1441
+ // 3. Remove existing symbols.
1426
1442
  const sourceDecl = renameModuleProvider.declarations.find((d) => d.node && (0, pathUtils_1.getFileExtension)(d.path) === sourceFileExt);
1427
1443
  if (!sourceDecl) {
1428
1444
  // Can't find symbol we can move.
1429
1445
  return undefined;
1430
1446
  }
1447
+ const symbolRange = renameModuleProvider_1.RenameModuleProvider.getSymbolTextRange(parseResults, sourceDecl);
1431
1448
  const importAdder = new importAdder_1.ImportAdder(this._configOptions, this._importResolver, this._evaluator);
1432
- const collectedimports = importAdder.collectImportsForSymbolsUsed(parseResults, sourceDecl.node, token);
1449
+ const collectedimports = importAdder.collectImportsForSymbolsUsed(parseResults, symbolRange, token);
1433
1450
  let insertionPoint = 0;
1434
1451
  let insertionIndentation = 0;
1435
1452
  const newFileParseResults = newFileInfo === null || newFileInfo === void 0 ? void 0 : newFileInfo.sourceFile.getParseResults();
1436
1453
  if (newFileParseResults) {
1437
- // TODO: Add "insertAfter" option to make sure we insert symbol after that point.
1438
- // For example, if collectedImports has symbols from the destination file, we should
1439
- // insert after those symbols are defined.
1440
1454
  const insertBefore = renameModuleProvider.tryGetFirstSymbolUsage(newFileParseResults);
1441
1455
  insertionPoint = (0, insertionPointUtils_1.getInsertionPointForSymbolUnderModule)(this._evaluator, newFileParseResults, node.value, {
1442
1456
  symbolDeclToIgnore: sourceDecl.path,
@@ -1446,16 +1460,15 @@ class Program {
1446
1460
  // No place to insert the symbol.
1447
1461
  return undefined;
1448
1462
  }
1449
- insertionIndentation = (0, indentationUtils_1.getNewlineIndentation)(newFileParseResults, insertionPoint);
1463
+ insertionIndentation = (0, indentationUtils_1.getModuleStatementIndentation)(newFileParseResults);
1450
1464
  }
1465
+ const reindentResult = (0, indentationUtils_1.reindentSpan)(parseResults, symbolRange, insertionIndentation);
1466
+ const fullRange = renameModuleProvider_1.RenameModuleProvider.getSymbolFullStatementTextRange(parseResults, sourceDecl);
1467
+ renameModuleProvider.textEditTracker.addEdit(filePath, (0, positionUtils_1.convertTextRangeToRange)(textRange_1.TextRange.combine([reindentResult.originalSpan, fullRange]), parseResults.tokenizerOutput.lines), '');
1468
+ // 4. Add the symbol to the destination file.
1451
1469
  const fileOperations = [];
1452
- renameModuleProvider.textEditTracker.addEdit(filePath, (0, parseTreeUtils_1.getFullStatementRange)(sourceDecl.node, parseResults, { includeTrailingBlankLines: true }), '');
1453
- let codeSnippetToInsert = (0, indentationUtils_1.reindentSpan)(parseResults, sourceDecl.node, insertionIndentation);
1470
+ let codeSnippetToInsert = reindentResult.text;
1454
1471
  if (newFileParseResults) {
1455
- // TODO: We need to "add import" statement for symbols defined in the destination file
1456
- // if we couldn't find insertion point where all constraints are met.
1457
- // For example, if the symbol we are moving is used before other symbols it references are declared.
1458
- importAdder.applyImportsTo(collectedimports, newFileParseResults, options.importFormat, renameModuleProvider.textEditTracker, token);
1459
1472
  const range = (0, positionUtils_1.convertTextRangeToRange)({ start: insertionPoint, length: 0 }, newFileParseResults.tokenizerOutput.lines);
1460
1473
  // If we are adding at the end of line (ex, end of a file),
1461
1474
  // add new lines.
@@ -1465,16 +1478,121 @@ class Program {
1465
1478
  }
1466
1479
  else {
1467
1480
  fileOperations.push({ kind: 'create', filePath: newFilePath });
1468
- const tempParseResults = (0, sourceFile_1.parseFile)(this._configOptions, newFilePath, codeSnippetToInsert, sourceFile_1.IPythonMode.None, new diagnosticSink_1.DiagnosticSink());
1469
- const insertAddEdits = importAdder.applyImports(collectedimports, tempParseResults, insertionPoint, options.importFormat, token);
1470
- const updateContent = (0, textEditUtils_1.applyTextEditActions)(codeSnippetToInsert, insertAddEdits, tempParseResults.tokenizerOutput.lines);
1471
- renameModuleProvider.textEditTracker.addEdit(newFilePath, (0, textRange_1.getEmptyRange)(), updateContent);
1472
- }
1481
+ renameModuleProvider.textEditTracker.addEdit(newFilePath, (0, textRange_1.getEmptyRange)(), codeSnippetToInsert);
1482
+ }
1483
+ // 5. Insert imports required for the symbol moved to the destination module.
1484
+ //
1485
+ // Since step 5 and 6 can create nested edits, we clone the program and apply all changes to re-calculate
1486
+ // edits we need to apply to the destination file. The same workflow as `fix all` but done in program level
1487
+ // not service level.
1488
+ const cloned = this.clone();
1489
+ let edits = renameModuleProvider.getEdits();
1490
+ const textAfterSymbolAdded = (0, workspaceEditUtils_1.applyTextEditsToString)(edits.filter((v) => v.filePath === newFilePath), (_a = newFileParseResults === null || newFileParseResults === void 0 ? void 0 : newFileParseResults.tokenizerOutput.lines) !== null && _a !== void 0 ? _a : new textRangeCollection_1.TextRangeCollection([]), (_b = newFileInfo === null || newFileInfo === void 0 ? void 0 : newFileInfo.sourceFile.getFileContent()) !== null && _b !== void 0 ? _b : '');
1491
+ _updateFileContent(cloned, newFilePath, textAfterSymbolAdded);
1492
+ const textAfterImportsAdded = _tryGetTextAfterImportsAdded(cloned, newFilePath, collectedimports, insertionPoint, token);
1493
+ edits = _updateFileEditActions(edits, newFilePath, newFileParseResults, textAfterSymbolAdded, textAfterImportsAdded);
1494
+ // 6. Remove imports no longer required from original module.
1495
+ const textAfterSymbolRemoved = (0, workspaceEditUtils_1.applyTextEditsToString)(edits.filter((v) => v.filePath === filePath), parseResults.tokenizerOutput.lines, fileInfo.sourceFile.getFileContent());
1496
+ _updateFileContent(cloned, filePath, textAfterSymbolRemoved);
1497
+ const textAfterUnusedImportsRemoved = _tryGetTextAfterUnusedImportsRemoved(cloned, filePath, collectedimports, 0, token);
1498
+ edits = _updateFileEditActions(edits, filePath, parseResults, textAfterSymbolRemoved, textAfterUnusedImportsRemoved);
1499
+ cloned.dispose();
1473
1500
  return {
1474
- edits: renameModuleProvider.getEdits(),
1501
+ edits,
1475
1502
  fileOperations,
1476
1503
  };
1504
+ function _updateFileEditActions(edits, filePath, parseResults, oldText, newText) {
1505
+ if (newText === undefined || oldText === newText) {
1506
+ return edits;
1507
+ }
1508
+ // There were nested edits. Replace whole file.
1509
+ edits = edits.filter((v) => v.filePath !== filePath);
1510
+ edits.push({
1511
+ filePath,
1512
+ range: parseResults
1513
+ ? (0, positionUtils_1.convertTextRangeToRange)(parseResults.parseTree, parseResults.tokenizerOutput.lines)
1514
+ : (0, textRange_1.getEmptyRange)(),
1515
+ replacementText: newText,
1516
+ });
1517
+ return edits;
1518
+ }
1519
+ function _tryGetTextAfterImportsAdded(cloned, filePath, importData, insertionPoint, token) {
1520
+ const sourceFile = cloned.getBoundSourceFile(filePath);
1521
+ const parseResults = sourceFile === null || sourceFile === void 0 ? void 0 : sourceFile.getParseResults();
1522
+ if (!parseResults) {
1523
+ return undefined;
1524
+ }
1525
+ const insertAddEdits = importAdder.applyImports(importData, filePath, parseResults, insertionPoint, options.importFormat, token);
1526
+ return (0, workspaceEditUtils_1.applyTextEditsToString)(insertAddEdits, parseResults.tokenizerOutput.lines, sourceFile.getFileContent());
1527
+ }
1528
+ function _tryGetTextAfterUnusedImportsRemoved(cloned, filePath, importData, attempt, token) {
1529
+ var _a;
1530
+ (0, cancellationUtils_1.throwIfCancellationRequested)(token);
1531
+ cloned.analyzeFile(filePath, token);
1532
+ const sourceFile = cloned.getBoundSourceFile(filePath);
1533
+ const parseResults = sourceFile === null || sourceFile === void 0 ? void 0 : sourceFile.getParseResults();
1534
+ if (!parseResults) {
1535
+ return undefined;
1536
+ }
1537
+ const tracker = new textEditTracker_1.TextEditTracker();
1538
+ for (const diagnostic of cloned
1539
+ .getDiagnosticsForRange(filePath, (0, positionUtils_1.convertTextRangeToRange)(parseResults.parseTree, parseResults.tokenizerOutput.lines))
1540
+ .filter((d) => {
1541
+ var _a;
1542
+ return d.category === 3 /* UnusedCode */ &&
1543
+ ((_a = d.getActions()) === null || _a === void 0 ? void 0 : _a.some((a) => a.action === "pyright.unusedImport" /* unusedImport */));
1544
+ })) {
1545
+ const nameNode = (0, parseTreeUtils_1.findNodeByPosition)(parseResults.parseTree, diagnostic.range.start, parseResults.tokenizerOutput.lines);
1546
+ if ((nameNode === null || nameNode === void 0 ? void 0 : nameNode.nodeType) !== 38 /* Name */) {
1547
+ continue;
1548
+ }
1549
+ // decl is synthesized. there is no node associated with the decl.
1550
+ // ex) import a or import a.b
1551
+ const dottedName1 = ((_a = nameNode.parent) === null || _a === void 0 ? void 0 : _a.nodeType) === 37 /* ModuleName */ ? nameNode.parent.nameParts : [nameNode];
1552
+ for (const [decl, names] of importData.declarations) {
1553
+ if (decl.node) {
1554
+ if (textRange_1.TextRange.containsRange(decl.node, nameNode)) {
1555
+ tracker.removeNodes({ node: nameNode, parseResults: parseResults });
1556
+ break;
1557
+ }
1558
+ }
1559
+ const dottedName2 = (0, parseTreeUtils_1.getDottedName)((0, parseTreeUtils_1.getDottedNameWithGivenNodeAsLastName)(names[0]));
1560
+ if (dottedName2 && (0, collectionUtils_1.arrayEquals)(dottedName1, dottedName2, (e1, e2) => e1.value === e2.value)) {
1561
+ tracker.removeNodes({ node: nameNode, parseResults: parseResults });
1562
+ break;
1563
+ }
1564
+ }
1565
+ }
1566
+ const oldText = sourceFile.getFileContent();
1567
+ const newText = (0, workspaceEditUtils_1.applyTextEditsToString)(tracker.getEdits(token).filter((v) => v.filePath === filePath), parseResults.tokenizerOutput.lines, oldText);
1568
+ // We will attempt to remove unused imports multiple times since removing 1 unused import
1569
+ // could make another import unused. This is due to how we calculate which import is not used.
1570
+ // ex) import os, os.path, os.path.xxx
1571
+ // `os.path` and `os.path.xxx` will be marked as used due to `import os`.
1572
+ // once `os` is removed `os.path` will be marked as unused and so on.
1573
+ // We will attempt to remove those chained unused imports up to 10 chain.
1574
+ if (attempt > 10 || oldText === newText) {
1575
+ return newText;
1576
+ }
1577
+ _updateFileContent(cloned, filePath, newText);
1578
+ return _tryGetTextAfterUnusedImportsRemoved(cloned, filePath, importData, attempt + 1, token);
1579
+ }
1477
1580
  });
1581
+ function _updateFileContent(cloned, filePath, text) {
1582
+ var _a, _b;
1583
+ const info = cloned.getSourceFileInfo(filePath);
1584
+ const version = info ? ((_a = info.sourceFile.getClientVersion()) !== null && _a !== void 0 ? _a : 0) + 1 : 0;
1585
+ const chainedFilePath = info ? (_b = info.chainedSourceFile) === null || _b === void 0 ? void 0 : _b.sourceFile.getFilePath() : undefined;
1586
+ const ipythonMode = info ? info.sourceFile.getIPythonMode() : sourceFile_1.IPythonMode.None;
1587
+ const isTracked = info ? info.isTracked : true;
1588
+ const realFilePath = info ? info.sourceFile.getRealFilePath() : filePath;
1589
+ cloned.setFileOpened(filePath, version, [{ text }], {
1590
+ chainedFilePath,
1591
+ ipythonMode,
1592
+ isTracked,
1593
+ realFilePath,
1594
+ });
1595
+ }
1478
1596
  function _getNumberOfBlankLinesToInsert(parseResults, position) {
1479
1597
  // This basically try to add 2 blanks lines before previous line with text.
1480
1598
  if (position.line === 0 && position.character === 0) {
@@ -1489,6 +1607,28 @@ class Program {
1489
1607
  return position.character !== 0 ? linesToAdd + 1 : linesToAdd;
1490
1608
  }
1491
1609
  }
1610
+ clone() {
1611
+ var _a;
1612
+ const program = new Program(this._importResolver, this._configOptions, this._console, new logTracker_1.LogTracker(this._console, 'Cloned'));
1613
+ // Cloned program will use whatever user files the program currently has.
1614
+ const userFiles = this.getUserFiles();
1615
+ program.setTrackedFiles(userFiles.map((i) => i.sourceFile.getFilePath()));
1616
+ program.markAllFilesDirty(true);
1617
+ // Make sure we keep editor content (open file) which could be different than one in the file system.
1618
+ for (const fileInfo of this.getOpened()) {
1619
+ const version = fileInfo.sourceFile.getClientVersion();
1620
+ if (version === undefined) {
1621
+ continue;
1622
+ }
1623
+ program.setFileOpened(fileInfo.sourceFile.getFilePath(), version, [{ text: fileInfo.sourceFile.getOpenFileContents() }], {
1624
+ chainedFilePath: (_a = fileInfo.chainedSourceFile) === null || _a === void 0 ? void 0 : _a.sourceFile.getFilePath(),
1625
+ ipythonMode: fileInfo.sourceFile.getIPythonMode(),
1626
+ isTracked: fileInfo.isTracked,
1627
+ realFilePath: fileInfo.sourceFile.getRealFilePath(),
1628
+ });
1629
+ }
1630
+ return program;
1631
+ }
1492
1632
  canRenameSymbolAtPosition(filePath, position, isDefaultWorkspace, allowModuleRename, token) {
1493
1633
  return this._runEvaluatorWithCancellationToken(token, () => {
1494
1634
  const sourceFileInfo = this.getSourceFileInfo(filePath);
@@ -1681,6 +1821,7 @@ class Program {
1681
1821
  this._createNewEvaluator();
1682
1822
  this._discardCachedParseResults();
1683
1823
  this._parsedFileCount = 0;
1824
+ extensibility_1.Extensions.getProgramExtensions(this.rootPath).forEach((e) => (e.clearCache ? e.clearCache() : null));
1684
1825
  }
1685
1826
  test_createSourceMapper(execEnv, from) {
1686
1827
  return this._createSourceMapper(execEnv, vscode_languageserver_1.CancellationToken.None, /*from*/ from, /* mapCompiled */ false);
@@ -1911,8 +2052,7 @@ class Program {
1911
2052
  // Special case for import statement.
1912
2053
  // ex) import X.Y
1913
2054
  // SourceFile for X might not be in memory since import `X.Y` only brings in Y
1914
- stubFileInfo = this._createInterimFileInfo(stubFilePath);
1915
- this._addToSourceFileListAndMap(stubFileInfo);
2055
+ stubFileInfo = this.addInterimFile(stubFilePath);
1916
2056
  }
1917
2057
  this._addShadowedFile(stubFileInfo, implFilePath);
1918
2058
  return this.getBoundSourceFile(implFilePath);
@@ -1922,8 +2062,7 @@ class Program {
1922
2062
  // Special case for import statement.
1923
2063
  // ex) import X.Y
1924
2064
  // SourceFile for X might not be in memory since import `X.Y` only brings in Y
1925
- fileInfo = this._createInterimFileInfo(f);
1926
- this._addToSourceFileListAndMap(fileInfo);
2065
+ fileInfo = this.addInterimFile(f);
1927
2066
  // Even though this file is not referenced by anything, make sure
1928
2067
  // we have parse tree for doc string.
1929
2068
  fileInfo.sourceFile.parse(this._configOptions, this._importResolver);