diff --git a/.github/test_plan.md b/.github/test_plan.md index b22e1121efae..b542476dc24d 100644 --- a/.github/test_plan.md +++ b/.github/test_plan.md @@ -224,14 +224,6 @@ def foo():pass - [ ] yapf - [ ] `"editor.formatOnType": true` works and has expected results -#### [Refactoring](https://code.visualstudio.com/docs/python/editing#_refactoring) - -- [ ] [`Extract Variable`](https://code.visualstudio.com/docs/python/editing#_extract-variable) works - - [ ] You are prompted to install `rope` if it is not already available -- [ ] [`Extract method`](https://code.visualstudio.com/docs/python/editing#_extract-method) works - - [ ] You are prompted to install `rope` if it is not already available -- [ ] [`Sort Imports`](https://code.visualstudio.com/docs/python/editing#_sort-imports) works - ### [Debugging](https://code.visualstudio.com/docs/python/debugging) - [ ] [Configurations](https://code.visualstudio.com/docs/python/debugging#_debugging-specific-app-types) work (see [`package.json`](https://github.com/Microsoft/vscode-python/blob/main/package.json) and the `"configurationSnippets"` section for all of the possible configurations) diff --git a/build/conda-functional-requirements.txt b/build/conda-functional-requirements.txt index 4237bde6d759..276e241bbf0f 100644 --- a/build/conda-functional-requirements.txt +++ b/build/conda-functional-requirements.txt @@ -11,7 +11,6 @@ pycodestyle pydocstyle nose pytest==4.6.9 # Last version of pytest with Python 2.7 support -rope fastapi ; python_version>='3.6' uvicorn ; python_version>='3.6' flask diff --git a/build/test-requirements.txt b/build/test-requirements.txt index 4cc0fddc27b6..5b121a5b1cc4 100644 --- a/build/test-requirements.txt +++ b/build/test-requirements.txt @@ -11,7 +11,6 @@ pydocstyle prospector ; python_version > '2.7' nose pytest<6 ; python_version > '2.7' # Tests currently fail against pytest 6. -rope flask fastapi ; python_version > '2.7' uvicorn ; python_version > '2.7' diff --git a/package.json b/package.json index 086ebdf4fa95..eda7b4315972 100644 --- a/package.json +++ b/package.json @@ -219,16 +219,6 @@ "title": "%python.command.python.resetInterpreterSecurityStorage.title%", "category": "Python" }, - { - "command": "python.refactorExtractVariable", - "title": "%python.command.python.refactorExtractVariable.title%", - "category": "Python Refactor" - }, - { - "command": "python.refactorExtractMethod", - "title": "%python.command.python.refactorExtractMethod.title%", - "category": "Python Refactor" - }, { "command": "python.viewTestOutput", "title": "%python.command.python.viewTestOutput.title%", @@ -372,18 +362,6 @@ ], "menus": { "editor/context": [ - { - "command": "python.refactorExtractVariable", - "title": "Refactor: Extract Variable", - "group": "Refactor", - "when": "editorHasSelection && editorLangId == python && !notebookEditorFocused" - }, - { - "command": "python.refactorExtractMethod", - "title": "Refactor: Extract Method", - "group": "Refactor", - "when": "editorHasSelection && editorLangId == python && !notebookEditorFocused" - }, { "command": "python.sortImports", "title": "Refactor: Sort Imports", diff --git a/package.nls.de.json b/package.nls.de.json index 77ec14b3ee9c..55348a519866 100644 --- a/package.nls.de.json +++ b/package.nls.de.json @@ -7,8 +7,6 @@ "python.command.python.debugtests.title": "Alle Unittests debuggen", "python.command.python.execInTerminal.title": "Python-Datei im Terminal ausführen", "python.command.python.setInterpreter.title": "Interpreter auswählen", - "python.command.python.refactorExtractVariable.title": "Variable extrahieren", - "python.command.python.refactorExtractMethod.title": "Methode extrahieren", "python.command.python.viewTestOutput.title": "Unittest-Ausgabe anzeigen", "python.command.python.selectAndRunTestMethod.title": "Unittest-Methode ausführen ...", "python.command.python.selectAndDebugTestMethod.title": "Unittest-Debug-Methode ausführen ...", diff --git a/package.nls.es.json b/package.nls.es.json index 227e268dbc8d..e3c33137f2f9 100644 --- a/package.nls.es.json +++ b/package.nls.es.json @@ -7,8 +7,6 @@ "python.command.python.debugtests.title": "Depurar todas las pruebas unitarias", "python.command.python.execInTerminal.title": "Ejecutar archivo Python en la terminal", "python.command.python.setInterpreter.title": "Seleccionar intérprete", - "python.command.python.refactorExtractVariable.title": "Extraer variable", - "python.command.python.refactorExtractMethod.title": "Extraer método", "python.command.python.viewTestOutput.title": "Mostrar resultados de la prueba unitaria", "python.command.python.selectAndRunTestMethod.title": "Método de ejecución de pruebas unitarias ...", "python.command.python.selectAndDebugTestMethod.title": "Método de depuración de pruebas unitarias ...", diff --git a/package.nls.fr.json b/package.nls.fr.json index ad28a94d6e09..897bb7e9bb9c 100644 --- a/package.nls.fr.json +++ b/package.nls.fr.json @@ -7,8 +7,6 @@ "python.command.python.debugtests.title": "Déboguer tous les tests unitaires", "python.command.python.execInTerminal.title": "Exécuter le script Python dans un terminal", "python.command.python.setInterpreter.title": "Sélectionner l'interpreteur", - "python.command.python.refactorExtractVariable.title": "Extraire la variable", - "python.command.python.refactorExtractMethod.title": "Extraire la méthode", "python.command.python.viewTestOutput.title": "Afficher la sortie des tests unitaires", "python.command.python.selectAndRunTestMethod.title": "Exécuter la méthode de test unitaire ...", "python.command.python.selectAndDebugTestMethod.title": "Déboguer la méthode de test unitaire ...", diff --git a/package.nls.it.json b/package.nls.it.json index c16d6ce74241..ef0e0df5f1af 100644 --- a/package.nls.it.json +++ b/package.nls.it.json @@ -7,8 +7,6 @@ "python.command.python.debugtests.title": "Esegui debug di tutti i test", "python.command.python.execInTerminal.title": "Esegui file Python nel terminale", "python.command.python.setInterpreter.title": "Seleziona interprete", - "python.command.python.refactorExtractVariable.title": "Estrai variable", - "python.command.python.refactorExtractMethod.title": "Estrai metodo", "python.command.python.viewTestOutput.title": "Mostra output dei test", "python.command.python.selectAndRunTestMethod.title": "Esegui metodo di test ...", "python.command.python.selectAndDebugTestMethod.title": "Esegui debug del metodo di test ...", diff --git a/package.nls.ja.json b/package.nls.ja.json index a2db0a823636..cadd72c6e864 100644 --- a/package.nls.ja.json +++ b/package.nls.ja.json @@ -6,8 +6,6 @@ "python.command.python.debugtests.title": "すべての単体テストをデバッグ", "python.command.python.execInTerminal.title": "ターミナルで Python ファイルを実行", "python.command.python.setInterpreter.title": "インタープリターを選択", - "python.command.python.refactorExtractVariable.title": "変数を抽出", - "python.command.python.refactorExtractMethod.title": "メソッドを抽出", "python.command.python.viewTestOutput.title": "単体テストの出力を表示", "python.command.python.selectAndRunTestMethod.title": "単体テストメソッドを実行...", "python.command.python.selectAndDebugTestMethod.title": "単体テストメソッドをデバッグ...", diff --git a/package.nls.json b/package.nls.json index a88ba79a1fda..6f82c0d775d0 100644 --- a/package.nls.json +++ b/package.nls.json @@ -12,8 +12,6 @@ "python.command.python.switchToWeeklyChannel.title": "Switch to Insiders Weekly Channel", "python.command.python.clearWorkspaceInterpreter.title": "Clear Workspace Interpreter Setting", "python.command.python.resetInterpreterSecurityStorage.title": "Reset Stored Info for Untrusted Interpreters", - "python.command.python.refactorExtractVariable.title": "Extract Variable", - "python.command.python.refactorExtractMethod.title": "Extract Method", "python.command.python.viewOutput.title": "Show Output", "python.command.python.viewTestOutput.title": "Show Test Output", "python.command.python.viewLanguageServerOutput.title": "Show Language Server Output", diff --git a/package.nls.ko-kr.json b/package.nls.ko-kr.json index 5309a9b07b81..b6ffaa5a8dd7 100644 --- a/package.nls.ko-kr.json +++ b/package.nls.ko-kr.json @@ -6,8 +6,6 @@ "python.command.python.debugtests.title": "모든 단위 테스트 디버그", "python.command.python.execInTerminal.title": "터미널에서 Python 파일 실행", "python.command.python.setInterpreter.title": "인터프리터 선택", - "python.command.python.refactorExtractVariable.title": "변수 추출", - "python.command.python.refactorExtractMethod.title": "메서드 추출", "python.command.python.viewTestOutput.title": "단위 테스트 결과 보기", "python.command.python.selectAndRunTestMethod.title": "단위 테스트 메서드 실행 ...", "python.command.python.selectAndDebugTestMethod.title": "단위 테스트 메서드 디버그 ...", diff --git a/package.nls.nl.json b/package.nls.nl.json index 08c9534dc962..14c721dbde8a 100644 --- a/package.nls.nl.json +++ b/package.nls.nl.json @@ -7,8 +7,6 @@ "python.command.python.debugtests.title": "Alle unittests debuggen", "python.command.python.execInTerminal.title": "Python-bestand in terminal uitvoeren", "python.command.python.setInterpreter.title": "Interpreter selecteren", - "python.command.python.refactorExtractVariable.title": "Variabelen selecteren", - "python.command.python.refactorExtractMethod.title": "Methode selecteren", "python.command.python.viewTestOutput.title": "Unittest-resultaat laten zien", "python.command.python.selectAndRunTestMethod.title": "Unittest-methode uitvoeren ...", "python.command.python.selectAndDebugTestMethod.title": "Unittest-methode debuggen ...", diff --git a/package.nls.pl.json b/package.nls.pl.json index 00f186f2ebab..932b0a5eeba7 100644 --- a/package.nls.pl.json +++ b/package.nls.pl.json @@ -7,8 +7,6 @@ "python.command.python.debugtests.title": "Debuguj wszystkie testy jednostkowe", "python.command.python.execInTerminal.title": "Uruchom plik pythonowy w terminalu", "python.command.python.setInterpreter.title": "Wybierz wersję interpretera", - "python.command.python.refactorExtractVariable.title": "Wyodrębnij zmienną", - "python.command.python.refactorExtractMethod.title": "Wyodrębnij metodę", "python.command.python.viewOutput.title": "Pokaż wyniki", "python.command.python.viewTestOutput.title": "Pokaż wyniki testów jednostkowych", "python.command.python.selectAndRunTestMethod.title": "Uruchom metodę testów jednostkowych ...", diff --git a/package.nls.pt-br.json b/package.nls.pt-br.json index 1acc94053ca0..587c2ab344ce 100644 --- a/package.nls.pt-br.json +++ b/package.nls.pt-br.json @@ -7,8 +7,6 @@ "python.command.python.debugtests.title": "Depurar Todos os Testes Unitários", "python.command.python.execInTerminal.title": "Executar Arquivo no Terminal", "python.command.python.setInterpreter.title": "Selecionar Interpretador", - "python.command.python.refactorExtractVariable.title": "Extrair Variável", - "python.command.python.refactorExtractMethod.title": "Extrair Método", "python.command.python.viewTestOutput.title": "Exibir Resultados dos Testes Unitários", "python.command.python.selectAndRunTestMethod.title": "Executar Testes Unitários do Método ...", "python.command.python.selectAndDebugTestMethod.title": "Depurar Testes Unitários do Método ...", diff --git a/package.nls.ru.json b/package.nls.ru.json index 0a80260e6644..3eb4d0db2910 100644 --- a/package.nls.ru.json +++ b/package.nls.ru.json @@ -6,8 +6,6 @@ "python.command.python.debugtests.title": "Запустить все тесты под отладчиком", "python.command.python.execInTerminal.title": "Выполнить файл в консоли", "python.command.python.setInterpreter.title": "Выбрать интерпретатор", - "python.command.python.refactorExtractVariable.title": "Извлечь в переменную", - "python.command.python.refactorExtractMethod.title": "Извлечь в метод", "python.command.python.viewTestOutput.title": "Показать вывод теста", "python.command.python.selectAndRunTestMethod.title": "Запусть тестовый метод...", "python.command.python.selectAndDebugTestMethod.title": "Отладить тестовый метод...", diff --git a/package.nls.tr.json b/package.nls.tr.json index 0e648bb38fdf..3d8f75da110c 100644 --- a/package.nls.tr.json +++ b/package.nls.tr.json @@ -7,8 +7,6 @@ "python.command.python.debugtests.title": "Testleri Debug Et", "python.command.python.execInTerminal.title": "Terminalde Çalıştır", "python.command.python.setInterpreter.title": "Bir Interpreter Seçin", - "python.command.python.refactorExtractVariable.title": "Değişken Çıkar", - "python.command.python.refactorExtractMethod.title": "Metot Çıkar", "python.command.python.viewTestOutput.title": "Test Çıktısını Görüntüle", "python.command.python.selectAndRunTestMethod.title": "Test Metodu Çalıştır", "python.command.python.selectAndDebugTestMethod.title": "Test Metodu Debug Et", diff --git a/package.nls.zh-cn.json b/package.nls.zh-cn.json index a645a0ae600c..ef72fef47bd5 100644 --- a/package.nls.zh-cn.json +++ b/package.nls.zh-cn.json @@ -12,8 +12,6 @@ "python.command.python.switchToWeeklyChannel.title": "切换到每周预览版本", "python.command.python.clearWorkspaceInterpreter.title": "清除工作区解释器设置", "python.command.python.resetInterpreterSecurityStorage.title": "重置已存储的未受信任解释器的信息", - "python.command.python.refactorExtractVariable.title": "提取变量", - "python.command.python.refactorExtractMethod.title": "提取方法", "python.command.python.viewOutput.title": "显示输出", "python.command.python.viewTestOutput.title": "显示单元测试输出", "python.command.python.viewLanguageServerOutput.title": "显示语言服务器输出", diff --git a/package.nls.zh-tw.json b/package.nls.zh-tw.json index 373a44299d87..f965c7ac4b49 100644 --- a/package.nls.zh-tw.json +++ b/package.nls.zh-tw.json @@ -7,8 +7,6 @@ "python.command.python.debugtests.title": "偵錯所有單元測試", "python.command.python.execInTerminal.title": "在終端機中執行 Python 檔案", "python.command.python.setInterpreter.title": "選擇直譯器", - "python.command.python.refactorExtractVariable.title": "提取變數", - "python.command.python.refactorExtractMethod.title": "提取方法", "python.command.python.viewTestOutput.title": "顯示單元測試輸出", "python.command.python.selectAndRunTestMethod.title": "執行單元測試方法…", "python.command.python.selectAndDebugTestMethod.title": "偵錯單元測試方法…", diff --git a/pythonFiles/refactor.py b/pythonFiles/refactor.py deleted file mode 100644 index f9d3b29e3b19..000000000000 --- a/pythonFiles/refactor.py +++ /dev/null @@ -1,395 +0,0 @@ -# Arguments are: -# 1. Working directory. -# 2. Rope folder - -import difflib -import io -import json -import os -import sys -import traceback - -try: - import rope - from rope.base import libutils - from rope.refactor.rename import Rename - from rope.refactor.extract import ExtractMethod, ExtractVariable - import rope.base.project - import rope.base.taskhandle -except: - jsonMessage = { - "error": True, - "message": "Rope not installed", - "traceback": "", - "type": "ModuleNotFoundError", - } - sys.stderr.write(json.dumps(jsonMessage)) - sys.stderr.flush() - -WORKSPACE_ROOT = sys.argv[1] -ROPE_PROJECT_FOLDER = ".vscode/.ropeproject" - - -class RefactorProgress: - """ - Refactor progress information - """ - - def __init__(self, name="Task Name", message=None, percent=0): - self.name = name - self.message = message - self.percent = percent - - -class ChangeType: - """ - Change Type Enum - """ - - EDIT = 0 - NEW = 1 - DELETE = 2 - - -class Change: - """ """ - - EDIT = 0 - NEW = 1 - DELETE = 2 - - def __init__(self, filePath, fileMode=ChangeType.EDIT, diff=""): - self.filePath = filePath - self.diff = diff - self.fileMode = fileMode - - -def get_diff(changeset): - """This is a copy of the code form the ChangeSet.get_description method found in Rope.""" - new = changeset.new_contents - old = changeset.old_contents - if old is None: - if changeset.resource.exists(): - old = changeset.resource.read() - else: - old = "" - - # Ensure code has a trailing empty lines, before generating a diff. - # https://github.com/Microsoft/vscode-python/issues/695. - old_lines = old.splitlines(True) - if not old_lines[-1].endswith("\n"): - old_lines[-1] = old_lines[-1] + os.linesep - new = new + os.linesep - - result = difflib.unified_diff( - old_lines, - new.splitlines(True), - "a/" + changeset.resource.path, - "b/" + changeset.resource.path, - ) - return "".join(list(result)) - - -class BaseRefactoring(object): - """ - Base class for refactorings - """ - - def __init__(self, project, resource, name="Refactor", progressCallback=None): - self._progressCallback = progressCallback - self._handle = rope.base.taskhandle.TaskHandle(name) - self._handle.add_observer(self._update_progress) - self.project = project - self.resource = resource - self.changes = [] - - def _update_progress(self): - jobset = self._handle.current_jobset() - if jobset and not self._progressCallback is None: - progress = RefactorProgress() - # getting current job set name - if jobset.get_name() is not None: - progress.name = jobset.get_name() - # getting active job name - if jobset.get_active_job_name() is not None: - progress.message = jobset.get_active_job_name() - # adding done percent - percent = jobset.get_percent_done() - if percent is not None: - progress.percent = percent - if not self._progressCallback is None: - self._progressCallback(progress) - - def stop(self): - self._handle.stop() - - def refactor(self): - try: - self.onRefactor() - except rope.base.exceptions.InterruptedTaskError: - # we can ignore this exception, as user has cancelled refactoring - pass - - def onRefactor(self): - """ - To be implemented by each base class - """ - pass - - -class RenameRefactor(BaseRefactoring): - def __init__( - self, - project, - resource, - name="Rename", - progressCallback=None, - startOffset=None, - newName="new_Name", - ): - BaseRefactoring.__init__(self, project, resource, name, progressCallback) - self._newName = newName - self.startOffset = startOffset - - def onRefactor(self): - renamed = Rename(self.project, self.resource, self.startOffset) - changes = renamed.get_changes(self._newName, task_handle=self._handle) - for item in changes.changes: - if isinstance(item, rope.base.change.ChangeContents): - self.changes.append( - Change(item.resource.real_path, ChangeType.EDIT, get_diff(item)) - ) - else: - raise Exception("Unknown Change") - - -class ExtractVariableRefactor(BaseRefactoring): - def __init__( - self, - project, - resource, - name="Extract Variable", - progressCallback=None, - startOffset=None, - endOffset=None, - newName="new_Name", - similar=False, - global_=False, - ): - BaseRefactoring.__init__(self, project, resource, name, progressCallback) - self._newName = newName - self._startOffset = startOffset - self._endOffset = endOffset - self._similar = similar - self._global = global_ - - def onRefactor(self): - renamed = ExtractVariable( - self.project, self.resource, self._startOffset, self._endOffset - ) - changes = renamed.get_changes(self._newName, self._similar, self._global) - for item in changes.changes: - if isinstance(item, rope.base.change.ChangeContents): - self.changes.append( - Change(item.resource.real_path, ChangeType.EDIT, get_diff(item)) - ) - else: - raise Exception("Unknown Change") - - -class ExtractMethodRefactor(ExtractVariableRefactor): - def __init__( - self, - project, - resource, - name="Extract Method", - progressCallback=None, - startOffset=None, - endOffset=None, - newName="new_Name", - similar=False, - global_=False, - ): - ExtractVariableRefactor.__init__( - self, - project, - resource, - name, - progressCallback, - startOffset=startOffset, - endOffset=endOffset, - newName=newName, - similar=similar, - global_=global_, - ) - - def onRefactor(self): - renamed = ExtractMethod( - self.project, self.resource, self._startOffset, self._endOffset - ) - changes = renamed.get_changes(self._newName, self._similar, self._global) - for item in changes.changes: - if isinstance(item, rope.base.change.ChangeContents): - self.changes.append( - Change(item.resource.real_path, ChangeType.EDIT, get_diff(item)) - ) - else: - raise Exception("Unknown Change") - - -class RopeRefactoring(object): - def __init__(self): - self.default_sys_path = sys.path - self._input = io.open(sys.stdin.fileno(), encoding="utf-8") - - def _rename(self, filePath, start, newName, indent_size): - """ - Renames a variable - """ - project = rope.base.project.Project( - WORKSPACE_ROOT, - ropefolder=ROPE_PROJECT_FOLDER, - save_history=False, - indent_size=indent_size, - ) - resourceToRefactor = libutils.path_to_resource(project, filePath) - refactor = RenameRefactor( - project, resourceToRefactor, startOffset=start, newName=newName - ) - refactor.refactor() - changes = refactor.changes - project.close() - valueToReturn = [] - for change in changes: - valueToReturn.append({"diff": change.diff}) - return valueToReturn - - def _extractVariable(self, filePath, start, end, newName, indent_size): - """ - Extracts a variable - """ - project = rope.base.project.Project( - WORKSPACE_ROOT, - ropefolder=ROPE_PROJECT_FOLDER, - save_history=False, - indent_size=indent_size, - ) - resourceToRefactor = libutils.path_to_resource(project, filePath) - refactor = ExtractVariableRefactor( - project, - resourceToRefactor, - startOffset=start, - endOffset=end, - newName=newName, - similar=True, - ) - refactor.refactor() - changes = refactor.changes - project.close() - valueToReturn = [] - for change in changes: - valueToReturn.append({"diff": change.diff}) - return valueToReturn - - def _extractMethod(self, filePath, start, end, newName, indent_size): - """ - Extracts a method - """ - project = rope.base.project.Project( - WORKSPACE_ROOT, - ropefolder=ROPE_PROJECT_FOLDER, - save_history=False, - indent_size=indent_size, - ) - resourceToRefactor = libutils.path_to_resource(project, filePath) - refactor = ExtractMethodRefactor( - project, - resourceToRefactor, - startOffset=start, - endOffset=end, - newName=newName, - similar=True, - ) - refactor.refactor() - changes = refactor.changes - project.close() - valueToReturn = [] - for change in changes: - valueToReturn.append({"diff": change.diff}) - return valueToReturn - - def _serialize(self, identifier, results): - """ - Serializes the refactor results - """ - return json.dumps({"id": identifier, "results": results}) - - def _deserialize(self, request): - """Deserialize request from VSCode. - - Args: - request: String with raw request from VSCode. - - Returns: - Python dictionary with request data. - """ - return json.loads(request) - - def _process_request(self, request): - """Accept serialized request from VSCode and write response.""" - request = self._deserialize(request) - lookup = request.get("lookup", "") - - if lookup == "": - pass - elif lookup == "rename": - changes = self._rename( - request["file"], - int(request["start"]), - request["name"], - int(request["indent_size"]), - ) - return self._write_response(self._serialize(request["id"], changes)) - elif lookup == "extract_variable": - changes = self._extractVariable( - request["file"], - int(request["start"]), - int(request["end"]), - request["name"], - int(request["indent_size"]), - ) - return self._write_response(self._serialize(request["id"], changes)) - elif lookup == "extract_method": - changes = self._extractMethod( - request["file"], - int(request["start"]), - int(request["end"]), - request["name"], - int(request["indent_size"]), - ) - return self._write_response(self._serialize(request["id"], changes)) - - def _write_response(self, response): - sys.stdout.write(response + "\n") - sys.stdout.flush() - - def watch(self): - self._write_response("STARTED") - while True: - try: - self._process_request(self._input.readline()) - except: - exc_type, exc_value, exc_tb = sys.exc_info() - tb_info = traceback.extract_tb(exc_tb) - jsonMessage = { - "error": True, - "message": str(exc_value), - "traceback": str(tb_info), - "type": str(exc_type), - } - sys.stderr.write(json.dumps(jsonMessage)) - sys.stderr.flush() - - -if __name__ == "__main__": - RopeRefactoring().watch() diff --git a/src/client/activation/common/activatorBase.ts b/src/client/activation/common/activatorBase.ts index 3de6b41c2e98..620f017256b2 100644 --- a/src/client/activation/common/activatorBase.ts +++ b/src/client/activation/common/activatorBase.ts @@ -61,7 +61,7 @@ export abstract class LanguageServerActivatorBase implements ILanguageServerActi this.manager.dispose(); } - public abstract async ensureLanguageServerIsAvailable(resource: Resource): Promise; + public abstract ensureLanguageServerIsAvailable(resource: Resource): Promise; public activate(): void { this.manager.connect(); diff --git a/src/client/activation/jedi.ts b/src/client/activation/jedi.ts index 54390a6b1f2b..f9999c9e8a7c 100644 --- a/src/client/activation/jedi.ts +++ b/src/client/activation/jedi.ts @@ -36,7 +36,6 @@ import { PythonDefinitionProvider } from '../providers/definitionProvider'; import { PythonHoverProvider } from '../providers/hoverProvider'; import { PythonObjectDefinitionProvider } from '../providers/objectDefinitionProvider'; import { PythonReferenceProvider } from '../providers/referenceProvider'; -import { PythonRenameProvider } from '../providers/renameProvider'; import { PythonSignatureProvider } from '../providers/signatureProvider'; import { JediSymbolProvider } from '../providers/symbolProvider'; import { PythonEnvironment } from '../pythonEnvironments/info'; @@ -48,7 +47,7 @@ export class JediExtensionActivator implements ILanguageServerActivator { private readonly context: IExtensionContext; private jediFactory?: JediFactory; private readonly documentSelector: DocumentFilter[]; - private renameProvider: PythonRenameProvider | undefined; + private renameProvider: undefined; private hoverProvider: PythonHoverProvider | undefined; private definitionProvider: PythonDefinitionProvider | undefined; private referenceProvider: PythonReferenceProvider | undefined; @@ -73,7 +72,7 @@ export class JediExtensionActivator implements ILanguageServerActivator { context.subscriptions.push(jediFactory); const serviceContainer = this.serviceManager.get(IServiceContainer); - this.renameProvider = new PythonRenameProvider(this.serviceManager); + this.renameProvider = undefined; this.definitionProvider = new PythonDefinitionProvider(jediFactory); this.hoverProvider = new PythonHoverProvider(jediFactory); this.referenceProvider = new PythonReferenceProvider(jediFactory); @@ -97,7 +96,6 @@ export class JediExtensionActivator implements ILanguageServerActivator { public activate() { if ( this.registrations.length === 0 && - this.renameProvider && this.definitionProvider && this.hoverProvider && this.referenceProvider && @@ -113,7 +111,6 @@ export class JediExtensionActivator implements ILanguageServerActivator { this.objectDefinitionProvider!.goToObjectDefinition(), ), ); - this.registrations.push(languages.registerRenameProvider(this.documentSelector, this.renameProvider)); this.registrations.push( languages.registerDefinitionProvider(this.documentSelector, this.definitionProvider), ); @@ -135,14 +132,9 @@ export class JediExtensionActivator implements ILanguageServerActivator { } } - public provideRenameEdits( - document: TextDocument, - position: Position, - newName: string, - token: CancellationToken, - ): ProviderResult { + public provideRenameEdits(): ProviderResult { if (this.renameProvider) { - return this.renameProvider.provideRenameEdits(document, position, newName, token); + return Promise.resolve(undefined); } } public provideDefinition( diff --git a/src/client/common/constants.ts b/src/client/common/constants.ts index f08b5b97f9dc..a85aa6c1793c 100644 --- a/src/client/common/constants.ts +++ b/src/client/common/constants.ts @@ -43,8 +43,6 @@ export namespace Commands { export const Tests_Select_And_Debug_Method = 'python.selectAndDebugTestMethod'; export const Tests_Select_And_Run_File = 'python.selectAndRunTestFile'; export const Tests_Run_Current_File = 'python.runCurrentTestFile'; - export const Refactor_Extract_Variable = 'python.refactorExtractVariable'; - export const Refactor_Extract_Method = 'python.refactorExtractMethod'; export const Build_Workspace_Symbols = 'python.buildWorkspaceSymbols'; export const Start_REPL = 'python.startREPL'; export const Create_Terminal = 'python.createTerminal'; diff --git a/src/client/common/installer/moduleInstaller.ts b/src/client/common/installer/moduleInstaller.ts index ec0ea881294e..415947e6ba7a 100644 --- a/src/client/common/installer/moduleInstaller.ts +++ b/src/client/common/installer/moduleInstaller.ts @@ -181,8 +181,6 @@ export function translateProductToModule(product: Product, purpose: ModuleNamePu return 'flake8'; case Product.unittest: return 'unittest'; - case Product.rope: - return 'rope'; case Product.bandit: return 'bandit'; case Product.jupyter: diff --git a/src/client/common/installer/productInstaller.ts b/src/client/common/installer/productInstaller.ts index 0493818c8151..47c5ed5e2284 100644 --- a/src/client/common/installer/productInstaller.ts +++ b/src/client/common/installer/productInstaller.ts @@ -281,22 +281,6 @@ class TestFrameworkInstaller extends BaseInstaller { } } -class RefactoringLibraryInstaller extends BaseInstaller { - protected async promptToInstallImplementation( - product: Product, - resource?: Uri, - cancel?: CancellationToken, - ): Promise { - const productName = ProductNames.get(product)!; - const item = await this.appShell.showErrorMessage( - `Refactoring library ${productName} is not installed. Install?`, - 'Yes', - 'No', - ); - return item === 'Yes' ? this.install(product, resource, cancel) : InstallerResponse.Ignore; - } -} - class DataScienceInstaller extends BaseInstaller { // Override base installer to support a more DS-friendly streamlined installation. public async install( @@ -445,8 +429,6 @@ export class ProductInstaller implements IInstaller { return new FormatterInstaller(this.serviceContainer, this.outputChannel); case ProductType.TestFramework: return new TestFrameworkInstaller(this.serviceContainer, this.outputChannel); - case ProductType.RefactoringLibrary: - return new RefactoringLibraryInstaller(this.serviceContainer, this.outputChannel); case ProductType.DataScience: return new DataScienceInstaller(this.serviceContainer, this.outputChannel); default: diff --git a/src/client/common/installer/productNames.ts b/src/client/common/installer/productNames.ts index ff0fcf75b44c..94551bfd5d19 100644 --- a/src/client/common/installer/productNames.ts +++ b/src/client/common/installer/productNames.ts @@ -17,7 +17,6 @@ ProductNames.set(Product.pydocstyle, 'pydocstyle'); ProductNames.set(Product.pylint, 'pylint'); ProductNames.set(Product.pytest, 'pytest'); ProductNames.set(Product.yapf, 'yapf'); -ProductNames.set(Product.rope, 'rope'); ProductNames.set(Product.tensorboard, 'tensorboard'); ProductNames.set(Product.torchProfilerInstallName, 'torch-tb-profiler'); ProductNames.set(Product.torchProfilerImportName, 'torch_tb_profiler'); diff --git a/src/client/common/installer/productPath.ts b/src/client/common/installer/productPath.ts index 4648567c9696..95756e8c8ae5 100644 --- a/src/client/common/installer/productPath.ts +++ b/src/client/common/installer/productPath.ts @@ -78,16 +78,6 @@ export class TestFrameworkProductPathService extends BaseProductPathsService { } } -@injectable() -export class RefactoringLibraryProductPathService extends BaseProductPathsService { - constructor(@inject(IServiceContainer) serviceContainer: IServiceContainer) { - super(serviceContainer); - } - public getExecutableNameFromSettings(product: Product, _?: Uri): string { - return this.productInstaller.translateProductToModuleName(product, ModuleNamePurpose.run); - } -} - @injectable() export class DataScienceProductPathService extends BaseProductPathsService { constructor(@inject(IServiceContainer) serviceContainer: IServiceContainer) { diff --git a/src/client/common/installer/productService.ts b/src/client/common/installer/productService.ts index 969d3b440832..9a252adf33e2 100644 --- a/src/client/common/installer/productService.ts +++ b/src/client/common/installer/productService.ts @@ -26,7 +26,6 @@ export class ProductService implements IProductService { this.ProductTypes.set(Product.autopep8, ProductType.Formatter); this.ProductTypes.set(Product.black, ProductType.Formatter); this.ProductTypes.set(Product.yapf, ProductType.Formatter); - this.ProductTypes.set(Product.rope, ProductType.RefactoringLibrary); this.ProductTypes.set(Product.jupyter, ProductType.DataScience); this.ProductTypes.set(Product.notebook, ProductType.DataScience); this.ProductTypes.set(Product.ipykernel, ProductType.DataScience); diff --git a/src/client/common/installer/serviceRegistry.ts b/src/client/common/installer/serviceRegistry.ts index 7e5cba29f231..fcd247142323 100644 --- a/src/client/common/installer/serviceRegistry.ts +++ b/src/client/common/installer/serviceRegistry.ts @@ -16,7 +16,6 @@ import { DataScienceProductPathService, FormatterProductPathService, LinterProductPathService, - RefactoringLibraryProductPathService, TestFrameworkProductPathService, } from './productPath'; import { ProductService } from './productService'; @@ -59,11 +58,6 @@ export function registerTypes(serviceManager: IServiceManager) { TestFrameworkProductPathService, ProductType.TestFramework, ); - serviceManager.addSingleton( - IProductPathService, - RefactoringLibraryProductPathService, - ProductType.RefactoringLibrary, - ); serviceManager.addSingleton( IProductPathService, DataScienceProductPathService, diff --git a/src/client/common/process/internal/scripts/index.ts b/src/client/common/process/internal/scripts/index.ts index c8220c10934d..05e443fc9004 100644 --- a/src/client/common/process/internal/scripts/index.ts +++ b/src/client/common/process/internal/scripts/index.ts @@ -114,25 +114,6 @@ export function sortImports(filename: string, sortArgs?: string[]): [string[], ( return [args, parse]; } -// refactor.py - -export function refactor(root: string): [string[], (out: string) => Record[]] { - const script = path.join(SCRIPTS_DIR, 'refactor.py'); - const args = maybeIsolated([script, root]); - - // TODO: Make the return type more specific, like we did - // with completion(). - function parse(out: string): Record[] { - // TODO: Also handle "STARTED"? - return out - .split(/\r?\n/g) - .filter((line) => line.length > 0) - .map((resp) => JSON.parse(resp)); - } - - return [args, parse]; -} - // normalizeSelection.py export function normalizeSelection(): [string[], (out: string) => string] { diff --git a/src/client/common/types.ts b/src/client/common/types.ts index 1c1e44deca20..801c29f580dc 100644 --- a/src/client/common/types.ts +++ b/src/client/common/types.ts @@ -93,7 +93,6 @@ export enum Product { autopep8 = 10, mypy = 11, unittest = 12, - rope = 14, isort = 15, black = 16, bandit = 17, diff --git a/src/client/extensionActivation.ts b/src/client/extensionActivation.ts index d6da4616520d..cee27816a061 100644 --- a/src/client/extensionActivation.ts +++ b/src/client/extensionActivation.ts @@ -44,7 +44,6 @@ import { PythonCodeActionProvider } from './providers/codeActionProvider/pythonC import { PythonFormattingEditProvider } from './providers/formatProvider'; import { ReplProvider } from './providers/replProvider'; import { registerTypes as providersRegisterTypes } from './providers/serviceRegistry'; -import { activateSimplePythonRefactorProvider } from './providers/simpleRefactorProvider'; import { TerminalProvider } from './providers/terminalProvider'; import { ISortImportsEditingProvider } from './providers/types'; import { setExtensionInstallTelemetryProperties } from './telemetry/extensionInstallTelemetry'; @@ -107,8 +106,6 @@ async function activateLegacy(ext: ExtensionState): Promise { // register "services" - const standardOutputChannel = serviceManager.get(IOutputChannel, STANDARD_OUTPUT_CHANNEL); - // We need to setup this property before any telemetry is sent const fs = serviceManager.get(IFileSystem); await setExtensionInstallTelemetryProperties(fs); @@ -186,8 +183,6 @@ async function activateLegacy(ext: ExtensionState): Promise { serviceManager.get(ITerminalAutoActivation).register(); const pythonSettings = configuration.getSettings(); - activateSimplePythonRefactorProvider(context, standardOutputChannel, serviceContainer); - const sortImports = serviceContainer.get(ISortImportsEditingProvider); sortImports.registerCommands(); diff --git a/src/client/interpreter/configuration/interpreterSelector/commands/base.ts b/src/client/interpreter/configuration/interpreterSelector/commands/base.ts index e4defaadf20c..618f698df35a 100644 --- a/src/client/interpreter/configuration/interpreterSelector/commands/base.ts +++ b/src/client/interpreter/configuration/interpreterSelector/commands/base.ts @@ -28,7 +28,7 @@ export abstract class BaseInterpreterSelectorCommand implements IExtensionSingle this.disposables.forEach((disposable) => disposable.dispose()); } - public abstract async activate(): Promise; + public abstract activate(): Promise; protected async getConfigTarget(): Promise< | { diff --git a/src/client/providers/renameProvider.ts b/src/client/providers/renameProvider.ts deleted file mode 100644 index 8a23a4443ee4..000000000000 --- a/src/client/providers/renameProvider.ts +++ /dev/null @@ -1,93 +0,0 @@ -import { - CancellationToken, - OutputChannel, - Position, - ProviderResult, - RenameProvider, - TextDocument, - Uri, - window, - workspace, - WorkspaceEdit, -} from 'vscode'; -import { STANDARD_OUTPUT_CHANNEL } from '../common/constants'; -import { getWorkspaceEditsFromPatch } from '../common/editor'; -import { traceError } from '../common/logger'; -import { IFileSystem } from '../common/platform/types'; -import { IPythonExecutionFactory } from '../common/process/types'; -import { IInstaller, IOutputChannel, Product } from '../common/types'; -import { IServiceContainer } from '../ioc/types'; -import { RefactorProxy } from '../refactor/proxy'; -import { captureTelemetry } from '../telemetry'; -import { EventName } from '../telemetry/constants'; - -type RenameResponse = { - results: [{ diff: string }]; -}; - -export class PythonRenameProvider implements RenameProvider { - private readonly outputChannel: OutputChannel; - constructor(private serviceContainer: IServiceContainer) { - this.outputChannel = serviceContainer.get(IOutputChannel, STANDARD_OUTPUT_CHANNEL); - } - @captureTelemetry(EventName.REFACTOR_RENAME) - public provideRenameEdits( - document: TextDocument, - position: Position, - newName: string, - _token: CancellationToken, - ): ProviderResult { - return workspace.saveAll(false).then(() => { - return this.doRename(document, position, newName); - }); - } - - private doRename(document: TextDocument, position: Position, newName: string): ProviderResult { - if (document.lineAt(position.line).text.match(/^\s*\/\//)) { - return; - } - if (position.character <= 0) { - return; - } - - const range = document.getWordRangeAtPosition(position); - if (!range || range.isEmpty) { - return; - } - const oldName = document.getText(range); - if (oldName === newName) { - return; - } - - let workspaceFolder = workspace.getWorkspaceFolder(document.uri); - if (!workspaceFolder && Array.isArray(workspace.workspaceFolders) && workspace.workspaceFolders.length > 0) { - workspaceFolder = workspace.workspaceFolders[0]; - } - const workspaceRoot = workspaceFolder ? workspaceFolder.uri.fsPath : __dirname; - - const proxy = new RefactorProxy(workspaceRoot, async () => { - const factory = this.serviceContainer.get(IPythonExecutionFactory); - return factory.create({ resource: Uri.file(workspaceRoot) }); - }); - return proxy - .rename(document, newName, document.uri.fsPath, range) - .then((response) => { - const fileDiffs = response.results.map((fileChanges) => fileChanges.diff); - const fs = this.serviceContainer.get(IFileSystem); - return getWorkspaceEditsFromPatch(fileDiffs, workspaceRoot, fs); - }) - .catch((reason) => { - if (reason === 'Not installed') { - const installer = this.serviceContainer.get(IInstaller); - installer - .promptToInstall(Product.rope, document.uri) - .catch((ex) => traceError('Python Extension: promptToInstall', ex)); - return Promise.reject(''); - } else { - window.showErrorMessage(reason); - this.outputChannel.appendLine(reason); - } - return Promise.reject(reason); - }); - } -} diff --git a/src/client/providers/simpleRefactorProvider.ts b/src/client/providers/simpleRefactorProvider.ts deleted file mode 100644 index ed485e084137..000000000000 --- a/src/client/providers/simpleRefactorProvider.ts +++ /dev/null @@ -1,223 +0,0 @@ -import * as vscode from 'vscode'; -import { Commands } from '../common/constants'; -import { getTextEditsFromPatch } from '../common/editor'; -import { traceError } from '../common/logger'; -import { IPythonExecutionFactory } from '../common/process/types'; -import { IInstaller, Product } from '../common/types'; -import { StopWatch } from '../common/utils/stopWatch'; -import { IServiceContainer } from '../ioc/types'; -import { RefactorProxy } from '../refactor/proxy'; -import { sendTelemetryWhenDone } from '../telemetry'; -import { EventName } from '../telemetry/constants'; - -type RenameResponse = { - results: [{ diff: string }]; -}; - -let installer: IInstaller; - -export function activateSimplePythonRefactorProvider( - context: vscode.ExtensionContext, - outputChannel: vscode.OutputChannel, - serviceContainer: IServiceContainer, -) { - installer = serviceContainer.get(IInstaller); - let disposable = vscode.commands.registerCommand(Commands.Refactor_Extract_Variable, () => { - const stopWatch = new StopWatch(); - const promise = extractVariable( - vscode.window.activeTextEditor!, - vscode.window.activeTextEditor!.selection, - outputChannel, - serviceContainer, - ).catch(() => {}); - sendTelemetryWhenDone(EventName.REFACTOR_EXTRACT_VAR, promise, stopWatch); - }); - context.subscriptions.push(disposable); - - disposable = vscode.commands.registerCommand(Commands.Refactor_Extract_Method, () => { - const stopWatch = new StopWatch(); - const promise = extractMethod( - vscode.window.activeTextEditor!, - vscode.window.activeTextEditor!.selection, - outputChannel, - serviceContainer, - ).catch(() => {}); - sendTelemetryWhenDone(EventName.REFACTOR_EXTRACT_FUNCTION, promise, stopWatch); - }); - context.subscriptions.push(disposable); -} - -// Exported for unit testing -export function extractVariable( - textEditor: vscode.TextEditor, - range: vscode.Range, - outputChannel: vscode.OutputChannel, - serviceContainer: IServiceContainer, -): Promise { - let workspaceFolder = vscode.workspace.getWorkspaceFolder(textEditor.document.uri); - if ( - !workspaceFolder && - Array.isArray(vscode.workspace.workspaceFolders) && - vscode.workspace.workspaceFolders.length > 0 - ) { - workspaceFolder = vscode.workspace.workspaceFolders[0]; - } - const workspaceRoot = workspaceFolder ? workspaceFolder.uri.fsPath : __dirname; - - return validateDocumentForRefactor(textEditor).then(() => { - const newName = `newvariable${new Date().getMilliseconds().toString()}`; - const proxy = new RefactorProxy(workspaceRoot, async () => { - const factory = serviceContainer.get(IPythonExecutionFactory); - return factory.create({ resource: vscode.Uri.file(workspaceRoot) }); - }); - const rename = proxy - .extractVariable( - textEditor.document, - newName, - textEditor.document.uri.fsPath, - range, - textEditor.options, - ) - .then((response) => { - return response.results[0].diff; - }); - - return extractName(textEditor, newName, rename, outputChannel); - }); -} - -// Exported for unit testing -export function extractMethod( - textEditor: vscode.TextEditor, - range: vscode.Range, - outputChannel: vscode.OutputChannel, - serviceContainer: IServiceContainer, -): Promise { - let workspaceFolder = vscode.workspace.getWorkspaceFolder(textEditor.document.uri); - if ( - !workspaceFolder && - Array.isArray(vscode.workspace.workspaceFolders) && - vscode.workspace.workspaceFolders.length > 0 - ) { - workspaceFolder = vscode.workspace.workspaceFolders[0]; - } - const workspaceRoot = workspaceFolder ? workspaceFolder.uri.fsPath : __dirname; - - return validateDocumentForRefactor(textEditor).then(() => { - const newName = `newmethod${new Date().getMilliseconds().toString()}`; - const proxy = new RefactorProxy(workspaceRoot, async () => { - const factory = serviceContainer.get(IPythonExecutionFactory); - return factory.create({ resource: vscode.Uri.file(workspaceRoot) }); - }); - const rename = proxy - .extractMethod( - textEditor.document, - newName, - textEditor.document.uri.fsPath, - range, - textEditor.options, - ) - .then((response) => { - return response.results[0].diff; - }); - - return extractName(textEditor, newName, rename, outputChannel); - }); -} - -function validateDocumentForRefactor(textEditor: vscode.TextEditor): Promise { - if (!textEditor.document.isDirty) { - return Promise.resolve(); - } - - return new Promise((resolve, reject) => { - vscode.window.showInformationMessage('Please save changes before refactoring', 'Save').then((item) => { - if (item === 'Save') { - textEditor.document.save().then(resolve, reject); - } else { - return reject(); - } - }); - }); -} - -function extractName( - textEditor: vscode.TextEditor, - newName: string, - renameResponse: Promise, - outputChannel: vscode.OutputChannel, -): Promise { - let changeStartsAtLine = -1; - return renameResponse - .then((diff) => { - if (diff.length === 0) { - return []; - } - return getTextEditsFromPatch(textEditor.document.getText(), diff); - }) - .then((edits) => { - return textEditor.edit((editBuilder) => { - edits.forEach((edit) => { - if (changeStartsAtLine === -1 || changeStartsAtLine > edit.range.start.line) { - changeStartsAtLine = edit.range.start.line; - } - editBuilder.replace(edit.range, edit.newText); - }); - }); - }) - .then((done) => { - if (done && changeStartsAtLine >= 0) { - let newWordPosition: vscode.Position | undefined; - for (let lineNumber = changeStartsAtLine; lineNumber < textEditor.document.lineCount; lineNumber += 1) { - const line = textEditor.document.lineAt(lineNumber); - const indexOfWord = line.text.indexOf(newName); - if (indexOfWord >= 0) { - newWordPosition = new vscode.Position(line.range.start.line, indexOfWord); - break; - } - } - - if (newWordPosition) { - textEditor.selections = [ - new vscode.Selection( - newWordPosition, - new vscode.Position(newWordPosition.line, newWordPosition.character + newName.length), - ), - ]; - textEditor.revealRange( - new vscode.Range(textEditor.selection.start, textEditor.selection.end), - vscode.TextEditorRevealType.Default, - ); - } - return newWordPosition; - } - return null; - }) - .then((newWordPosition) => { - if (newWordPosition) { - return textEditor.document.save().then(() => { - // Now that we have selected the new variable, lets invoke the rename command - return vscode.commands.executeCommand('editor.action.rename'); - }); - } - }) - .catch((error) => { - if (error === 'Not installed') { - installer - .promptToInstall(Product.rope, textEditor.document.uri) - .catch((ex) => traceError('Python Extension: simpleRefactorProvider.promptToInstall', ex)); - return Promise.reject(''); - } - let errorMessage = `${error}`; - if (typeof error === 'string') { - errorMessage = error; - } - if (typeof error === 'object' && error.message) { - errorMessage = error.message; - } - outputChannel.appendLine(`${'#'.repeat(10)}Refactor Output${'#'.repeat(10)}`); - outputChannel.appendLine(`Error in refactoring:\n${errorMessage}`); - vscode.window.showErrorMessage(`Cannot perform refactoring using selected element(s). (${errorMessage})`); - return Promise.reject(error); - }); -} diff --git a/src/client/refactor/proxy.ts b/src/client/refactor/proxy.ts deleted file mode 100644 index 7f89eef54408..000000000000 --- a/src/client/refactor/proxy.ts +++ /dev/null @@ -1,206 +0,0 @@ -import { ChildProcess } from 'child_process'; -import { Disposable, Position, Range, TextDocument, TextEditorOptions, window } from 'vscode'; -import '../common/extensions'; -import { traceError } from '../common/logger'; -import { IS_WINDOWS } from '../common/platform/constants'; -import * as internalScripts from '../common/process/internal/scripts'; -import { IPythonExecutionService } from '../common/process/types'; -import { createDeferred, Deferred } from '../common/utils/async'; -import { getWindowsLineEndingCount } from '../common/utils/text'; - -export class RefactorProxy extends Disposable { - private _process?: ChildProcess; - private _previousOutData: string = ''; - private _previousStdErrData: string = ''; - private _startedSuccessfully: boolean = false; - private _commandResolve?: (value?: any | PromiseLike) => void; - private _commandReject!: (reason?: any) => void; - private initialized!: Deferred; - constructor( - private workspaceRoot: string, - private getPythonExecutionService: () => Promise, - ) { - super(() => {}); - } - - public dispose() { - try { - this._process!.kill(); - } catch (ex) {} - this._process = undefined; - } - private getOffsetAt(document: TextDocument, position: Position): number { - if (!IS_WINDOWS) { - return document.offsetAt(position); - } - - // get line count - // Rope always uses LF, instead of CRLF on windows, funny isn't it - // So for each line, reduce one characer (for CR) - // But Not all Windows users use CRLF - const offset = document.offsetAt(position); - const winEols = getWindowsLineEndingCount(document, offset); - - return offset - winEols; - } - public rename( - document: TextDocument, - name: string, - filePath: string, - range: Range, - options?: TextEditorOptions, - ): Promise { - if (!options) { - options = window.activeTextEditor!.options; - } - const command = { - lookup: 'rename', - file: filePath, - start: this.getOffsetAt(document, range.start).toString(), - id: '1', - name: name, - indent_size: options.tabSize, - }; - - return this.sendCommand(JSON.stringify(command)); - } - public extractVariable( - document: TextDocument, - name: string, - filePath: string, - range: Range, - options?: TextEditorOptions, - ): Promise { - if (!options) { - options = window.activeTextEditor!.options; - } - const command = { - lookup: 'extract_variable', - file: filePath, - start: this.getOffsetAt(document, range.start).toString(), - end: this.getOffsetAt(document, range.end).toString(), - id: '1', - name: name, - indent_size: options.tabSize, - }; - return this.sendCommand(JSON.stringify(command)); - } - public extractMethod( - document: TextDocument, - name: string, - filePath: string, - range: Range, - options?: TextEditorOptions, - ): Promise { - if (!options) { - options = window.activeTextEditor!.options; - } - // Ensure last line is an empty line - if ( - !document.lineAt(document.lineCount - 1).isEmptyOrWhitespace && - range.start.line === document.lineCount - 1 - ) { - return Promise.reject('Missing blank line at the end of document (PEP8).'); - } - const command = { - lookup: 'extract_method', - file: filePath, - start: this.getOffsetAt(document, range.start).toString(), - end: this.getOffsetAt(document, range.end).toString(), - id: '1', - name: name, - indent_size: options.tabSize, - }; - return this.sendCommand(JSON.stringify(command)); - } - private sendCommand(command: string): Promise { - return this.initialize().then(() => { - return new Promise((resolve, reject) => { - this._commandResolve = resolve; - this._commandReject = reject; - this._process!.stdin?.write(command + '\n'); - }); - }); - } - private async initialize(): Promise { - const pythonProc = await this.getPythonExecutionService(); - this.initialized = createDeferred(); - const [args, parse] = internalScripts.refactor(this.workspaceRoot); - const result = pythonProc.execObservable(args, {}); - this._process = result.proc; - result.out.subscribe( - (output) => { - if (output.source === 'stdout') { - if (!this._startedSuccessfully && output.out.startsWith('STARTED')) { - this._startedSuccessfully = true; - return this.initialized.resolve(); - } - this.onData(output.out, parse); - } else { - this.handleStdError(output.out); - } - }, - (error) => this.handleError(error), - ); - - return this.initialized.promise; - } - private handleStdError(data: string) { - // Possible there was an exception in parsing the data returned - // So append the data then parse it - let dataStr = (this._previousStdErrData = this._previousStdErrData + data + ''); - let errorResponse: { message: string; traceback: string; type: string }[]; - try { - errorResponse = dataStr - .split(/\r?\n/g) - .filter((line) => line.length > 0) - .map((resp) => JSON.parse(resp)); - this._previousStdErrData = ''; - } catch (ex) { - traceError(ex); - // Possible we've only received part of the data, hence don't clear previousData - return; - } - if (typeof errorResponse[0].message !== 'string' || errorResponse[0].message.length === 0) { - errorResponse[0].message = errorResponse[0].traceback.splitLines().pop()!; - } - let errorMessage = errorResponse[0].message + '\n' + errorResponse[0].traceback; - - if (this._startedSuccessfully) { - this._commandReject(`Refactor failed. ${errorMessage}`); - } else { - if (typeof errorResponse[0].type === 'string' && errorResponse[0].type === 'ModuleNotFoundError') { - this.initialized.reject('Not installed'); - return; - } - - this.initialized.reject(`Refactor failed. ${errorMessage}`); - } - } - private handleError(error: Error) { - if (this._startedSuccessfully) { - return this._commandReject(error); - } - this.initialized.reject(error); - } - private onData(data: string, parse: (out: string) => object[]) { - if (!this._commandResolve) { - return; - } - - // Possible there was an exception in parsing the data returned - // So append the data then parse it - let dataStr = (this._previousOutData = this._previousOutData + data + ''); - let response: any; - try { - response = parse(dataStr); - this._previousOutData = ''; - } catch (ex) { - // Possible we've only received part of the data, hence don't clear previousData - return; - } - this.dispose(); - this._commandResolve!(response[0]); - this._commandResolve = undefined; - } -} diff --git a/src/test/common/installer.test.ts b/src/test/common/installer.test.ts index a8b1dfe17e49..6ebc38290f1c 100644 --- a/src/test/common/installer.test.ts +++ b/src/test/common/installer.test.ts @@ -50,7 +50,6 @@ import { ProductInstaller } from '../../client/common/installer/productInstaller import { FormatterProductPathService, LinterProductPathService, - RefactoringLibraryProductPathService, TestFrameworkProductPathService, } from '../../client/common/installer/productPath'; import { ProductService } from '../../client/common/installer/productService'; @@ -195,11 +194,6 @@ suite('Installer', () => { TestFrameworkProductPathService, ProductType.TestFramework, ); - ioc.serviceManager.addSingleton( - IProductPathService, - RefactoringLibraryProductPathService, - ProductType.RefactoringLibrary, - ); ioc.serviceManager.addSingleton(IActiveResourceService, ActiveResourceService); ioc.serviceManager.addSingleton(IInterpreterPathService, InterpreterPathService); ioc.serviceManager.addSingleton(IExtensions, Extensions); diff --git a/src/test/common/installer/installer.invalidPath.unit.test.ts b/src/test/common/installer/installer.invalidPath.unit.test.ts index 29566fa936b1..bbbd7936091b 100644 --- a/src/test/common/installer/installer.invalidPath.unit.test.ts +++ b/src/test/common/installer/installer.invalidPath.unit.test.ts @@ -80,7 +80,6 @@ suite('Module Installer - Invalid Paths', () => { switch (product.value) { case Product.isort: - case Product.rope: case Product.unittest: { return; } diff --git a/src/test/common/installer/productPath.unit.test.ts b/src/test/common/installer/productPath.unit.test.ts index 3bab379d593b..15fea79f4a40 100644 --- a/src/test/common/installer/productPath.unit.test.ts +++ b/src/test/common/installer/productPath.unit.test.ts @@ -14,7 +14,6 @@ import { BaseProductPathsService, FormatterProductPathService, LinterProductPathService, - RefactoringLibraryProductPathService, TestFrameworkProductPathService, } from '../../../client/common/installer/productPath'; import { ProductService } from '../../../client/common/installer/productService'; @@ -165,21 +164,6 @@ suite('Product Path', () => { }); break; } - case ProductType.RefactoringLibrary: { - test(`Ensure path is returned for ${product.name} (${ - resource ? 'With a resource' : 'without a resource' - })`, async () => { - const productPathService = new RefactoringLibraryProductPathService(serviceContainer.object); - - const value = productPathService.getExecutableNameFromSettings(product.value, resource); - const moduleName = productInstaller.translateProductToModuleName( - product.value, - ModuleNamePurpose.run, - ); - expect(value).to.be.equal(moduleName); - }); - break; - } case ProductType.TestFramework: { test(`Ensure path is returned for ${product.name} (${ resource ? 'With a resource' : 'without a resource' diff --git a/src/test/common/installer/serviceRegistry.unit.test.ts b/src/test/common/installer/serviceRegistry.unit.test.ts index de4d117238e6..7cc45c916774 100644 --- a/src/test/common/installer/serviceRegistry.unit.test.ts +++ b/src/test/common/installer/serviceRegistry.unit.test.ts @@ -15,7 +15,6 @@ import { PoetryInstaller } from '../../../client/common/installer/poetryInstalle import { FormatterProductPathService, LinterProductPathService, - RefactoringLibraryProductPathService, TestFrameworkProductPathService, } from '../../../client/common/installer/productPath'; import { ProductService } from '../../../client/common/installer/productService'; @@ -89,13 +88,6 @@ suite('Common installer Service Registry', () => { ProductType.TestFramework, ), ).once(); - verify( - serviceManager.addSingleton( - IProductPathService, - RefactoringLibraryProductPathService, - ProductType.RefactoringLibrary, - ), - ).once(); verify(serviceManager.addSingleton(IWebviewPanelProvider, WebviewPanelProvider)).once(); }); }); diff --git a/src/test/linters/lint.multiroot.test.ts b/src/test/linters/lint.multiroot.test.ts index 771fbf8d0528..1e64d91ff4af 100644 --- a/src/test/linters/lint.multiroot.test.ts +++ b/src/test/linters/lint.multiroot.test.ts @@ -6,7 +6,6 @@ import { PythonSettings } from '../../client/common/configSettings'; import { FormatterProductPathService, LinterProductPathService, - RefactoringLibraryProductPathService, TestFrameworkProductPathService, } from '../../client/common/installer/productPath'; import { ProductService } from '../../client/common/installer/productService'; @@ -67,11 +66,6 @@ suite('Multiroot Linting', () => { TestFrameworkProductPathService, ProductType.TestFramework, ); - ioc.serviceManager.addSingleton( - IProductPathService, - RefactoringLibraryProductPathService, - ProductType.RefactoringLibrary, - ); } async function createLinter(product: Product): Promise { diff --git a/src/test/linters/lint.test.ts b/src/test/linters/lint.test.ts index 5e12e8b94f69..a4d655bdda98 100644 --- a/src/test/linters/lint.test.ts +++ b/src/test/linters/lint.test.ts @@ -10,7 +10,6 @@ import { Product } from '../../client/common/installer/productInstaller'; import { FormatterProductPathService, LinterProductPathService, - RefactoringLibraryProductPathService, TestFrameworkProductPathService, } from '../../client/common/installer/productPath'; import { ProductService } from '../../client/common/installer/productService'; @@ -75,11 +74,6 @@ suite('Linting Settings', () => { TestFrameworkProductPathService, ProductType.TestFramework, ); - ioc.serviceManager.addSingleton( - IProductPathService, - RefactoringLibraryProductPathService, - ProductType.RefactoringLibrary, - ); } async function resetSettings(lintingEnabled = true) { diff --git a/src/test/pythonFiles/refactoring/source folder/with empty line.py b/src/test/pythonFiles/refactoring/source folder/with empty line.py deleted file mode 100644 index 01ed75727900..000000000000 --- a/src/test/pythonFiles/refactoring/source folder/with empty line.py +++ /dev/null @@ -1,8 +0,0 @@ -import os - -def one(): - return True - -def two(): - if one(): - print("A" + one()) diff --git a/src/test/pythonFiles/refactoring/source folder/without empty line.py b/src/test/pythonFiles/refactoring/source folder/without empty line.py deleted file mode 100644 index a449eb106f5c..000000000000 --- a/src/test/pythonFiles/refactoring/source folder/without empty line.py +++ /dev/null @@ -1,8 +0,0 @@ -import os - -def one(): - return True - -def two(): - if one(): - print("A" + one()) \ No newline at end of file diff --git a/src/test/pythonFiles/refactoring/standAlone/refactor.py b/src/test/pythonFiles/refactoring/standAlone/refactor.py deleted file mode 100644 index ee941dd45ebf..000000000000 --- a/src/test/pythonFiles/refactoring/standAlone/refactor.py +++ /dev/null @@ -1,245 +0,0 @@ -# Arguments are: -# 1. Working directory. -# 2. Rope folder - -import io -import sys -import json -import traceback -import rope - -from rope.base import libutils -from rope.refactor.rename import Rename -from rope.refactor.extract import ExtractMethod, ExtractVariable -import rope.base.project -import rope.base.taskhandle - -WORKSPACE_ROOT = sys.argv[1] -ROPE_PROJECT_FOLDER = sys.argv[2] - - -class RefactorProgress(): - """ - Refactor progress information - """ - - def __init__(self, name='Task Name', message=None, percent=0): - self.name = name - self.message = message - self.percent = percent - - -class ChangeType(): - """ - Change Type Enum - """ - EDIT = 0 - NEW = 1 - DELETE = 2 - - -class Change(): - """ - """ - EDIT = 0 - NEW = 1 - DELETE = 2 - - def __init__(self, filePath, fileMode=ChangeType.EDIT, diff=""): - self.filePath = filePath - self.diff = diff - self.fileMode = fileMode - - -class BaseRefactoring(object): - """ - Base class for refactorings - """ - - def __init__(self, project, resource, name="Refactor", progressCallback=None): - self._progressCallback = progressCallback - self._handle = rope.base.taskhandle.TaskHandle(name) - self._handle.add_observer(self._update_progress) - self.project = project - self.resource = resource - self.changes = [] - - def _update_progress(self): - jobset = self._handle.current_jobset() - if jobset and not self._progressCallback is None: - progress = RefactorProgress() - # getting current job set name - if jobset.get_name() is not None: - progress.name = jobset.get_name() - # getting active job name - if jobset.get_active_job_name() is not None: - progress.message = jobset.get_active_job_name() - # adding done percent - percent = jobset.get_percent_done() - if percent is not None: - progress.percent = percent - if not self._progressCallback is None: - self._progressCallback(progress) - - def stop(self): - self._handle.stop() - - def refactor(self): - try: - self.onRefactor() - except rope.base.exceptions.InterruptedTaskError: - # we can ignore this exception, as user has cancelled refactoring - pass - - def onRefactor(self): - """ - To be implemented by each base class - """ - pass - - -class RenameRefactor(BaseRefactoring): - - def __init__(self, project, resource, name="Rename", progressCallback=None, startOffset=None, newName="new_Name"): - BaseRefactoring.__init__(self, project, resource, - name, progressCallback) - self._newName = newName - self.startOffset = startOffset - - def onRefactor(self): - renamed = Rename(self.project, self.resource, self.startOffset) - changes = renamed.get_changes(self._newName, task_handle=self._handle) - for item in changes.changes: - if isinstance(item, rope.base.change.ChangeContents): - self.changes.append( - Change(item.resource.real_path, ChangeType.EDIT, item.get_description())) - else: - raise Exception('Unknown Change') - - -class ExtractVariableRefactor(BaseRefactoring): - - def __init__(self, project, resource, name="Extract Variable", progressCallback=None, startOffset=None, endOffset=None, newName="new_Name", similar=False, global_=False): - BaseRefactoring.__init__(self, project, resource, - name, progressCallback) - self._newName = newName - self._startOffset = startOffset - self._endOffset = endOffset - self._similar = similar - self._global = global_ - - def onRefactor(self): - renamed = ExtractVariable( - self.project, self.resource, self._startOffset, self._endOffset) - changes = renamed.get_changes( - self._newName, self._similar, self._global) - for item in changes.changes: - if isinstance(item, rope.base.change.ChangeContents): - self.changes.append( - Change(item.resource.real_path, ChangeType.EDIT, item.get_description())) - else: - raise Exception('Unknown Change') - - -class ExtractMethodRefactor(ExtractVariableRefactor): - - def __init__(self, project, resource, name="Extract Method", progressCallback=None, startOffset=None, endOffset=None, newName="new_Name", similar=False, global_=False): - ExtractVariableRefactor.__init__(self, project, resource, - name, progressCallback, startOffset=startOffset, endOffset=endOffset, newName=newName, similar=similar, global_=global_) - def onRefactor(self): - renamed = ExtractMethod( - self.project, self.resource, self._startOffset, self._endOffset) - changes = renamed.get_changes( - self._newName, self._similar, self._global) - for item in changes.changes: - if isinstance(item, rope.base.change.ChangeContents): - self.changes.append( - Change(item.resource.real_path, ChangeType.EDIT, item.get_description())) - else: - raise Exception('Unknown Change') - - -class RopeRefactoring(object): - - def __init__(self): - self.default_sys_path = sys.path - self._input = io.open(sys.stdin.fileno(), encoding='utf-8') - - def _extractVariable(self, filePath, start, end, newName): - """ - Extracts a variable - """ - project = rope.base.project.Project(WORKSPACE_ROOT, ropefolder=ROPE_PROJECT_FOLDER, save_history=False) - resourceToRefactor = libutils.path_to_resource(project, filePath) - refactor = ExtractVariableRefactor(project, resourceToRefactor, startOffset=start, endOffset=end, newName=newName) - refactor.refactor() - changes = refactor.changes - project.close() - valueToReturn = [] - for change in changes: - valueToReturn.append({'diff':change.diff}) - return valueToReturn - - def _extractMethod(self, filePath, start, end, newName): - """ - Extracts a method - """ - project = rope.base.project.Project(WORKSPACE_ROOT, ropefolder=ROPE_PROJECT_FOLDER, save_history=False) - resourceToRefactor = libutils.path_to_resource(project, filePath) - refactor = ExtractMethodRefactor(project, resourceToRefactor, startOffset=start, endOffset=end, newName=newName) - refactor.refactor() - changes = refactor.changes - project.close() - valueToReturn = [] - for change in changes: - valueToReturn.append({'diff':change.diff}) - return valueToReturn - - def _serialize(self, identifier, results): - """ - Serializes the refactor results - """ - return json.dumps({'id': identifier, 'results': results}) - - def _deserialize(self, request): - """Deserialize request from VSCode. - - Args: - request: String with raw request from VSCode. - - Returns: - Python dictionary with request data. - """ - return json.loads(request) - - def _process_request(self, request): - """Accept serialized request from VSCode and write response. - """ - request = self._deserialize(request) - lookup = request.get('lookup', '') - - if lookup == '': - pass - elif lookup == 'extract_variable': - changes = self._extractVariable(request['file'], int(request['start']), int(request['end']), request['name']) - return self._write_response(self._serialize(request['id'], changes)) - elif lookup == 'extract_method': - changes = self._extractMethod(request['file'], int(request['start']), int(request['end']), request['name']) - return self._write_response(self._serialize(request['id'], changes)) - - def _write_response(self, response): - sys.stdout.write(response + '\n') - sys.stdout.flush() - - def watch(self): - self._write_response("STARTED") - while True: - try: - self._process_request(self._input.readline()) - except Exception as ex: - message = ex.message + ' \n' + traceback.format_exc() - sys.stderr.write(str(len(message)) + ':' + message) - sys.stderr.flush() - -if __name__ == '__main__': - RopeRefactoring().watch() diff --git a/src/test/refactor/extension.refactor.extract.method.test.ts b/src/test/refactor/extension.refactor.extract.method.test.ts deleted file mode 100644 index 6fe8b13c57e6..000000000000 --- a/src/test/refactor/extension.refactor.extract.method.test.ts +++ /dev/null @@ -1,201 +0,0 @@ -import * as assert from 'assert'; -import * as fs from 'fs-extra'; -import * as path from 'path'; -import { instance, mock } from 'ts-mockito'; -import { - commands, - Position, - Range, - Selection, - TextEditorCursorStyle, - TextEditorLineNumbersStyle, - TextEditorOptions, - Uri, - window, - workspace, -} from 'vscode'; -import { getTextEditsFromPatch } from '../../client/common/editor'; -import { IPythonExecutionFactory, IPythonExecutionService } from '../../client/common/process/types'; -import { ICondaService, IInterpreterService } from '../../client/interpreter/contracts'; -import { InterpreterService } from '../../client/interpreter/interpreterService'; -import { extractMethod } from '../../client/providers/simpleRefactorProvider'; -import { CondaService } from '../../client/pythonEnvironments/discovery/locators/services/condaService'; -import { RefactorProxy } from '../../client/refactor/proxy'; -import { UnitTestIocContainer } from '../testing/serviceRegistry'; -import { closeActiveWindows, initialize, initializeTest } from './../initialize'; -import { MockOutputChannel } from './../mockClasses'; - -const refactorSourceFile = path.join( - __dirname, - '..', - '..', - '..', - 'src', - 'test', - 'pythonFiles', - 'refactoring', - 'standAlone', - 'refactor.py', -); -const refactorTargetFileDir = path.join( - __dirname, - '..', - '..', - '..', - 'out', - 'test', - 'pythonFiles', - 'refactoring', - 'standAlone', -); - -interface RenameResponse { - results: [{ diff: string }]; -} - -suite('Method Extraction', () => { - // Hack hac hack - const oldExecuteCommand = commands.executeCommand; - const options: TextEditorOptions = { - cursorStyle: TextEditorCursorStyle.Line, - insertSpaces: true, - lineNumbers: TextEditorLineNumbersStyle.Off, - tabSize: 4, - }; - let refactorTargetFile = ''; - let ioc: UnitTestIocContainer; - suiteSetup(initialize); - suiteTeardown(() => { - commands.executeCommand = oldExecuteCommand; - return closeActiveWindows(); - }); - setup(async () => { - await initializeDI(); - refactorTargetFile = path.join(refactorTargetFileDir, `refactor${new Date().getTime()}.py`); - fs.copySync(refactorSourceFile, refactorTargetFile, { overwrite: true }); - await initializeTest(); - (commands as any).executeCommand = (_cmd: any) => Promise.resolve(); - }); - teardown(async () => { - commands.executeCommand = oldExecuteCommand; - try { - await fs.unlink(refactorTargetFile); - } catch {} - await closeActiveWindows(); - }); - async function initializeDI() { - ioc = new UnitTestIocContainer(); - ioc.registerCommonTypes(); - ioc.registerProcessTypes(); - ioc.registerVariableTypes(); - ioc.registerInterpreterStorageTypes(); - await ioc.registerMockInterpreterTypes(); - ioc.serviceManager.rebindInstance(ICondaService, instance(mock(CondaService))); - ioc.serviceManager.rebindInstance(IInterpreterService, instance(mock(InterpreterService))); - } - function createPythonExecGetter(workspaceRoot: string): () => Promise { - return async () => { - const factory = ioc.serviceContainer.get(IPythonExecutionFactory); - return factory.create({ resource: Uri.file(workspaceRoot) }); - }; - } - - async function testingMethodExtraction(shouldError: boolean, startPos: Position, endPos: Position): Promise { - const rangeOfTextToExtract = new Range(startPos, endPos); - const workspaceRoot = path.dirname(refactorTargetFile); - const proxy = new RefactorProxy(workspaceRoot, createPythonExecGetter(workspaceRoot)); - - const DIFF = `--- a/refactor.py\n+++ b/refactor.py\n@@ -237,9 +237,12 @@\n try:\n self._process_request(self._input.readline())\n except Exception as ex:\n- message = ex.message + ' \\n' + traceback.format_exc()\n- sys.stderr.write(str(len(message)) + ':' + message)\n- sys.stderr.flush()\n+ self.myNewMethod(ex)\n+\n+ def myNewMethod(self, ex):\n+ message = ex.message + ' \\n' + traceback.format_exc()\n+ sys.stderr.write(str(len(message)) + ':' + message)\n+ sys.stderr.flush()\n \n if __name__ == '__main__':\n RopeRefactoring().watch()\n`; - const mockTextDoc = await workspace.openTextDocument(refactorTargetFile); - const expectedTextEdits = getTextEditsFromPatch(mockTextDoc.getText(), DIFF); - try { - const response = await proxy.extractMethod( - mockTextDoc, - 'myNewMethod', - refactorTargetFile, - rangeOfTextToExtract, - options, - ); - if (shouldError) { - assert.fail('No error', 'Error', 'Extraction should fail with an error', ''); - } - const textEdits = getTextEditsFromPatch(mockTextDoc.getText(), DIFF); - assert.equal(response.results.length, 1, 'Invalid number of items in response'); - assert.equal(textEdits.length, expectedTextEdits.length, 'Invalid number of Text Edits'); - textEdits.forEach((edit) => { - const foundEdit = expectedTextEdits.filter( - (item) => item.newText === edit.newText && item.range.isEqual(edit.range), - ); - assert.equal(foundEdit.length, 1, 'Edit not found'); - }); - } catch (error) { - if (!shouldError) { - // Wait a minute this shouldn't work, what's going on - assert.equal('Error', 'No error', `${error}`); - } - } - } - - test('Extract Method', async () => { - const startPos = new Position(239, 0); - const endPos = new Position(241, 35); - await testingMethodExtraction(false, startPos, endPos); - }); - - test('Extract Method will fail if complete statements are not selected', async () => { - const startPos = new Position(239, 30); - const endPos = new Position(241, 35); - await testingMethodExtraction(true, startPos, endPos); - }); - - async function testingMethodExtractionEndToEnd( - shouldError: boolean, - startPos: Position, - endPos: Position, - ): Promise { - const ch = new MockOutputChannel('Python'); - const rangeOfTextToExtract = new Range(startPos, endPos); - - const textDocument = await workspace.openTextDocument(refactorTargetFile); - const editor = await window.showTextDocument(textDocument); - - editor.selections = [new Selection(rangeOfTextToExtract.start, rangeOfTextToExtract.end)]; - editor.selection = new Selection(rangeOfTextToExtract.start, rangeOfTextToExtract.end); - - try { - await extractMethod(editor, rangeOfTextToExtract, ch, ioc.serviceContainer); - if (shouldError) { - assert.fail('No error', 'Error', 'Extraction should fail with an error', ''); - } - - const newMethodRefLine = textDocument.lineAt(editor.selection.start); - assert.equal(ch.output.length, 0, 'Output channel is not empty'); - assert.equal( - textDocument - .lineAt(newMethodRefLine.lineNumber + 2) - .text.trim() - .indexOf('def newmethod'), - 0, - 'New Method not created', - ); - assert.equal(newMethodRefLine.text.trim().startsWith('self.newmethod'), true, 'New Method not being used'); - } catch (error) { - if (!shouldError) { - assert.equal('Error', 'No error', `${error}`); - } - } - } - - // This test fails on linux (text document not getting updated in time) - test('Extract Method (end to end)', async () => { - const startPos = new Position(239, 0); - const endPos = new Position(241, 35); - await testingMethodExtractionEndToEnd(false, startPos, endPos); - }); - - test('Extract Method will fail if complete statements are not selected', async () => { - const startPos = new Position(239, 30); - const endPos = new Position(241, 35); - await testingMethodExtractionEndToEnd(true, startPos, endPos); - }); -}); diff --git a/src/test/refactor/extension.refactor.extract.var.test.ts b/src/test/refactor/extension.refactor.extract.var.test.ts deleted file mode 100644 index 737d1eae8026..000000000000 --- a/src/test/refactor/extension.refactor.extract.var.test.ts +++ /dev/null @@ -1,202 +0,0 @@ -import * as assert from 'assert'; -import * as fs from 'fs-extra'; -import * as path from 'path'; -import { - commands, - Position, - Range, - Selection, - TextEditorCursorStyle, - TextEditorLineNumbersStyle, - TextEditorOptions, - Uri, - window, - workspace, -} from 'vscode'; -import { getTextEditsFromPatch } from '../../client/common/editor'; -import { IPythonExecutionFactory, IPythonExecutionService } from '../../client/common/process/types'; -import { extractVariable } from '../../client/providers/simpleRefactorProvider'; -import { RefactorProxy } from '../../client/refactor/proxy'; -import { isPythonVersion } from '../common'; -import { UnitTestIocContainer } from '../testing/serviceRegistry'; -import { closeActiveWindows, initialize, initializeTest, IS_CI_SERVER } from './../initialize'; -import { MockOutputChannel } from './../mockClasses'; - -const refactorSourceFile = path.join( - __dirname, - '..', - '..', - '..', - 'src', - 'test', - 'pythonFiles', - 'refactoring', - 'standAlone', - 'refactor.py', -); -const refactorTargetFileDir = path.join( - __dirname, - '..', - '..', - '..', - 'out', - 'test', - 'pythonFiles', - 'refactoring', - 'standAlone', -); - -interface RenameResponse { - results: [{ diff: string }]; -} - -suite('Variable Extraction', () => { - // Hack hac hack - const oldExecuteCommand = commands.executeCommand; - const options: TextEditorOptions = { - cursorStyle: TextEditorCursorStyle.Line, - insertSpaces: true, - lineNumbers: TextEditorLineNumbersStyle.Off, - tabSize: 4, - }; - let refactorTargetFile = ''; - let ioc: UnitTestIocContainer; - suiteSetup(initialize); - suiteTeardown(() => { - commands.executeCommand = oldExecuteCommand; - return closeActiveWindows(); - }); - setup(async () => { - await initializeDI(); - refactorTargetFile = path.join(refactorTargetFileDir, `refactor${new Date().getTime()}.py`); - fs.copySync(refactorSourceFile, refactorTargetFile, { overwrite: true }); - await initializeTest(); - (commands).executeCommand = (_cmd: any) => Promise.resolve(); - }); - teardown(async () => { - commands.executeCommand = oldExecuteCommand; - try { - await fs.unlink(refactorTargetFile); - } catch {} - await closeActiveWindows(); - }); - async function initializeDI() { - ioc = new UnitTestIocContainer(); - ioc.registerCommonTypes(); - ioc.registerProcessTypes(); - ioc.registerVariableTypes(); - ioc.registerInterpreterStorageTypes(); - await ioc.registerMockInterpreterTypes(); - } - function createPythonExecGetter(workspaceRoot: string): () => Promise { - return async () => { - const factory = ioc.serviceContainer.get(IPythonExecutionFactory); - return factory.create({ resource: Uri.file(workspaceRoot) }); - }; - } - - async function testingVariableExtraction( - shouldError: boolean, - startPos: Position, - endPos: Position, - ): Promise { - const rangeOfTextToExtract = new Range(startPos, endPos); - const workspaceRoot = path.dirname(refactorTargetFile); - const proxy = new RefactorProxy(workspaceRoot, createPythonExecGetter(workspaceRoot)); - - const DIFF = - '--- a/refactor.py\n+++ b/refactor.py\n@@ -232,7 +232,8 @@\n sys.stdout.flush()\n \n def watch(self):\n- self._write_response("STARTED")\n+ myNewVariable = "STARTED"\n+ self._write_response(myNewVariable)\n while True:\n try:\n self._process_request(self._input.readline())\n'; - const mockTextDoc = await workspace.openTextDocument(refactorTargetFile); - const expectedTextEdits = getTextEditsFromPatch(mockTextDoc.getText(), DIFF); - try { - const response = await proxy.extractVariable( - mockTextDoc, - 'myNewVariable', - refactorTargetFile, - rangeOfTextToExtract, - options, - ); - if (shouldError) { - assert.fail('No error', 'Error', 'Extraction should fail with an error', ''); - } - const textEdits = getTextEditsFromPatch(mockTextDoc.getText(), DIFF); - assert.equal(response.results.length, 1, 'Invalid number of items in response'); - assert.equal(textEdits.length, expectedTextEdits.length, 'Invalid number of Text Edits'); - textEdits.forEach((edit) => { - const foundEdit = expectedTextEdits.filter( - (item) => item.newText === edit.newText && item.range.isEqual(edit.range), - ); - assert.equal(foundEdit.length, 1, 'Edit not found'); - }); - } catch (error) { - if (!shouldError) { - assert.equal('Error', 'No error', `${error}`); - } - } - } - - test('Extract Variable', async function () { - if (isPythonVersion('3.7')) { - return this.skip(); - } else { - const startPos = new Position(234, 29); - const endPos = new Position(234, 38); - await testingVariableExtraction(false, startPos, endPos); - } - }); - - test('Extract Variable fails if whole string not selected', async () => { - const startPos = new Position(234, 20); - const endPos = new Position(234, 38); - await testingVariableExtraction(true, startPos, endPos); - }); - - async function testingVariableExtractionEndToEnd( - shouldError: boolean, - startPos: Position, - endPos: Position, - ): Promise { - const ch = new MockOutputChannel('Python'); - const rangeOfTextToExtract = new Range(startPos, endPos); - - const textDocument = await workspace.openTextDocument(refactorTargetFile); - const editor = await window.showTextDocument(textDocument); - - editor.selections = [new Selection(rangeOfTextToExtract.start, rangeOfTextToExtract.end)]; - editor.selection = new Selection(rangeOfTextToExtract.start, rangeOfTextToExtract.end); - try { - await extractVariable(editor, rangeOfTextToExtract, ch, ioc.serviceContainer); - if (shouldError) { - assert.fail('No error', 'Error', 'Extraction should fail with an error', ''); - } - assert.equal(ch.output.length, 0, 'Output channel is not empty'); - - const newVarDefLine = textDocument.lineAt(editor.selection.start); - const newVarRefLine = textDocument.lineAt(newVarDefLine.lineNumber + 1); - - assert.equal(newVarDefLine.text.trim().indexOf('newvariable'), 0, 'New Variable not created'); - assert.equal(newVarDefLine.text.trim().endsWith('= "STARTED"'), true, 'Started Text Assigned to variable'); - assert.equal(newVarRefLine.text.indexOf('(newvariable') >= 0, true, 'New Variable not being used'); - } catch (error) { - if (!shouldError) { - assert.fail('Error', 'No error', `${error}`); - } - } - } - - // This test fails on linux (text document not getting updated in time) - test('Extract Variable (end to end)', async function () { - if (!IS_CI_SERVER) { - return this.skip(); - } - const startPos = new Position(234, 29); - const endPos = new Position(234, 38); - await testingVariableExtractionEndToEnd(false, startPos, endPos); - }); - - test('Extract Variable fails if whole string not selected (end to end)', async () => { - const startPos = new Position(234, 20); - const endPos = new Position(234, 38); - await testingVariableExtractionEndToEnd(true, startPos, endPos); - }); -}); diff --git a/src/test/refactor/rename.test.ts b/src/test/refactor/rename.test.ts deleted file mode 100644 index ebd74f965c9c..000000000000 --- a/src/test/refactor/rename.test.ts +++ /dev/null @@ -1,202 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -'use strict'; - -import { expect } from 'chai'; -import { EOL } from 'os'; -import * as path from 'path'; -import { instance, mock } from 'ts-mockito'; -import * as typeMoq from 'typemoq'; -import { - Range, - TextEditorCursorStyle, - TextEditorLineNumbersStyle, - TextEditorOptions, - Uri, - window, - workspace, -} from 'vscode'; -import { EXTENSION_ROOT_DIR } from '../../client/common/constants'; -import { DiscoveryVariants } from '../../client/common/experiments/groups'; -import '../../client/common/extensions'; -import { BufferDecoder } from '../../client/common/process/decoder'; -import { ProcessService } from '../../client/common/process/proc'; -import { PythonExecutionFactory } from '../../client/common/process/pythonExecutionFactory'; -import { - IProcessLogger, - IProcessServiceFactory, - IPythonExecutionFactory, - IPythonExecutionService, -} from '../../client/common/process/types'; -import { IConfigurationService, IExperimentService, IPythonSettings } from '../../client/common/types'; -import { IEnvironmentActivationService } from '../../client/interpreter/activation/types'; -import { IComponentAdapter, ICondaService, IInterpreterService } from '../../client/interpreter/contracts'; -import { IServiceContainer } from '../../client/ioc/types'; -import { RefactorProxy } from '../../client/refactor/proxy'; -import { PYTHON_PATH } from '../common'; -import { closeActiveWindows, initialize, initializeTest } from '../initialize'; - -type RenameResponse = { - results: [{ diff: string }]; -}; - -suite('Refactor Rename', () => { - const options: TextEditorOptions = { - cursorStyle: TextEditorCursorStyle.Line, - insertSpaces: true, - lineNumbers: TextEditorLineNumbersStyle.Off, - tabSize: 4, - }; - let pythonSettings: typeMoq.IMock; - let serviceContainer: typeMoq.IMock; - suiteSetup(initialize); - setup(async () => { - pythonSettings = typeMoq.Mock.ofType(); - pythonSettings.setup((p) => p.pythonPath).returns(() => PYTHON_PATH); - const configService = typeMoq.Mock.ofType(); - configService.setup((c) => c.getSettings(typeMoq.It.isAny())).returns(() => pythonSettings.object); - const condaService = typeMoq.Mock.ofType(); - const experimentService = typeMoq.Mock.ofType(); - const processServiceFactory = typeMoq.Mock.ofType(); - processServiceFactory - .setup((p) => p.create(typeMoq.It.isAny())) - .returns(() => Promise.resolve(new ProcessService(new BufferDecoder()))); - const interpreterService = typeMoq.Mock.ofType(); - interpreterService.setup((i) => i.hasInterpreters).returns(() => Promise.resolve(true)); - const envActivationService = typeMoq.Mock.ofType(); - envActivationService - .setup((e) => e.getActivatedEnvironmentVariables(typeMoq.It.isAny())) - .returns(() => Promise.resolve(undefined)); - envActivationService - .setup((e) => e.getActivatedEnvironmentVariables(typeMoq.It.isAny(), typeMoq.It.isAny())) - .returns(() => Promise.resolve(undefined)); - envActivationService - .setup((e) => - e.getActivatedEnvironmentVariables(typeMoq.It.isAny(), typeMoq.It.isAny(), typeMoq.It.isAny()), - ) - .returns(() => Promise.resolve(undefined)); - serviceContainer = typeMoq.Mock.ofType(); - serviceContainer - .setup((s) => s.get(typeMoq.It.isValue(IConfigurationService), typeMoq.It.isAny())) - .returns(() => configService.object); - serviceContainer - .setup((s) => s.get(typeMoq.It.isValue(IProcessServiceFactory), typeMoq.It.isAny())) - .returns(() => processServiceFactory.object); - serviceContainer - .setup((s) => s.get(typeMoq.It.isValue(IInterpreterService), typeMoq.It.isAny())) - .returns(() => interpreterService.object); - serviceContainer - .setup((s) => s.get(typeMoq.It.isValue(IEnvironmentActivationService), typeMoq.It.isAny())) - .returns(() => envActivationService.object); - - const pyenvs: IComponentAdapter = mock(); - - experimentService - .setup((e) => e.inExperiment(DiscoveryVariants.discoverWithFileWatching)) - .returns(() => Promise.resolve(false)); - - serviceContainer - .setup((s) => s.get(typeMoq.It.isValue(IPythonExecutionFactory), typeMoq.It.isAny())) - .returns( - () => - new PythonExecutionFactory( - serviceContainer.object, - // eslint-disable-next-line @typescript-eslint/no-explicit-any - undefined as any, - processServiceFactory.object, - configService.object, - condaService.object, - // eslint-disable-next-line @typescript-eslint/no-explicit-any - undefined as any, - instance(pyenvs), - experimentService.object, - ), - ); - const processLogger = typeMoq.Mock.ofType(); - processLogger - .setup((p) => p.logProcess(typeMoq.It.isAny(), typeMoq.It.isAny(), typeMoq.It.isAny())) - .returns(() => { - /** No body */ - }); - serviceContainer - .setup((s) => s.get(typeMoq.It.isValue(IProcessLogger), typeMoq.It.isAny())) - .returns(() => processLogger.object); - await initializeTest(); - }); - teardown(closeActiveWindows); - suiteTeardown(closeActiveWindows); - function createPythonExecGetter(workspaceRoot: string): () => Promise { - return async () => { - const factory = serviceContainer.object.get(IPythonExecutionFactory); - return factory.create({ resource: Uri.file(workspaceRoot) }); - }; - } - - test('Rename function in source without a trailing empty line', async () => { - const sourceFile = path.join( - EXTENSION_ROOT_DIR, - 'src', - 'test', - 'pythonFiles', - 'refactoring', - 'source folder', - 'without empty line.py', - ); - const expectedDiff = `--- a/${path.basename(sourceFile)}${EOL}+++ b/${path.basename( - sourceFile, - )}${EOL}@@ -1,8 +1,8 @@${EOL} import os${EOL} ${EOL}-def one():${EOL}+def three():${EOL} return True${EOL} ${EOL} def two():${EOL}- if one():${EOL}- print(\"A\" + one())${EOL}+ if three():${EOL}+ print(\"A\" + three())${EOL}`.splitLines( - { removeEmptyEntries: false, trim: false }, - ); - const workspaceRoot = path.dirname(sourceFile); - - const proxy = new RefactorProxy(workspaceRoot, createPythonExecGetter(workspaceRoot)); - const textDocument = await workspace.openTextDocument(sourceFile); - await window.showTextDocument(textDocument); - - const response = await proxy.rename( - textDocument, - 'three', - sourceFile, - new Range(7, 20, 7, 23), - options, - ); - expect(response.results).to.be.lengthOf(1); - expect(response.results[0].diff.splitLines({ removeEmptyEntries: false, trim: false })).to.be.deep.equal( - expectedDiff, - ); - }); - test('Rename function in source with a trailing empty line', async () => { - const sourceFile = path.join( - EXTENSION_ROOT_DIR, - 'src', - 'test', - 'pythonFiles', - 'refactoring', - 'source folder', - 'with empty line.py', - ); - const expectedDiff = `--- a/${path.basename(sourceFile)}${EOL}+++ b/${path.basename( - sourceFile, - )}${EOL}@@ -1,8 +1,8 @@${EOL} import os${EOL} ${EOL}-def one():${EOL}+def three():${EOL} return True${EOL} ${EOL} def two():${EOL}- if one():${EOL}- print(\"A\" + one())${EOL}+ if three():${EOL}+ print(\"A\" + three())${EOL}`.splitLines( - { removeEmptyEntries: false, trim: false }, - ); - const workspaceRoot = path.dirname(sourceFile); - - const proxy = new RefactorProxy(workspaceRoot, createPythonExecGetter(workspaceRoot)); - const textDocument = await workspace.openTextDocument(sourceFile); - await window.showTextDocument(textDocument); - - const response = await proxy.rename( - textDocument, - 'three', - sourceFile, - new Range(7, 20, 7, 23), - options, - ); - expect(response.results).to.be.lengthOf(1); - expect(response.results[0].diff.splitLines({ removeEmptyEntries: false, trim: false })).to.be.deep.equal( - expectedDiff, - ); - }); -});