From 7bb66cff3530b19df84c2b1570a2613cb1cd4ff4 Mon Sep 17 00:00:00 2001 From: Karthik Nadig Date: Mon, 7 Nov 2022 13:28:01 -0800 Subject: [PATCH] Fix for unable to read `map` issue with CodeActions --- bundled/tool/server.py | 50 ++++--- src/test/python_tests/test_sort.py | 205 +++++++++++++++++++++++++++++ 2 files changed, 237 insertions(+), 18 deletions(-) diff --git a/bundled/tool/server.py b/bundled/tool/server.py index 821d419..f3e8973 100644 --- a/bundled/tool/server.py +++ b/bundled/tool/server.py @@ -220,24 +220,38 @@ def code_action_organize_imports(params: lsp.CodeActionParams): ) ] - diagnostics = [ - d for d in params.context.diagnostics if d.source == "isort" and d.code == "E" - ] - return [ - lsp.CodeAction( - title="isort: Organize Imports", - kind=lsp.CodeActionKind.SourceOrganizeImports, - data=params.text_document.uri, - edit=None, - ), - lsp.CodeAction( - title="isort: Fix import sorting and/or formatting", - kind=lsp.CodeActionKind.QuickFix, - data=params.text_document.uri, - edit=None, - diagnostics=diagnostics if diagnostics else None, - ), - ] + actions = [] + if ( + not params.context.only + or lsp.CodeActionKind.SourceOrganizeImports in params.context.only + ): + actions.append( + lsp.CodeAction( + title="isort: Organize Imports", + kind=lsp.CodeActionKind.SourceOrganizeImports, + data=params.text_document.uri, + edit=None, + diagnostics=[], + ), + ) + + if not params.context.only or lsp.CodeActionKind.QuickFix in params.context.only: + diagnostics = [ + d + for d in params.context.diagnostics + if d.source == "isort" and d.code == "E" + ] + actions.append( + lsp.CodeAction( + title="isort: Fix import sorting and/or formatting", + kind=lsp.CodeActionKind.QuickFix, + data=params.text_document.uri, + edit=None, + diagnostics=diagnostics, + ), + ) + + return actions if actions else None @LSP_SERVER.feature(lsp.CODE_ACTION_RESOLVE) diff --git a/src/test/python_tests/test_sort.py b/src/test/python_tests/test_sort.py index 6730faf..8d67a8a 100644 --- a/src/test/python_tests/test_sort.py +++ b/src/test/python_tests/test_sort.py @@ -16,6 +16,211 @@ TIMEOUT = 10 # 10 seconds +@pytest.mark.parametrize( + "action_type", + [ + "quickfix", + "source.organizeImports", + ("quickfix", "source.organizeImports"), + None, + "", + ], +) +def test_code_actions(action_type): + """Test code actions.""" + init_params = copy.deepcopy(defaults.VSCODE_DEFAULT_INITIALIZE) + init_params["initializationOptions"]["settings"][0]["check"] = True + + FORMATTED_TEST_FILE_PATH = constants.TEST_DATA / "sample1" / "sample.py" + UNFORMATTED_TEST_FILE_PATH = constants.TEST_DATA / "sample1" / "sample.unformatted" + + contents = UNFORMATTED_TEST_FILE_PATH.read_text() + expected = FORMATTED_TEST_FILE_PATH.read_text() + + actual_diagnostics = [] + + with utils.python_file(contents, UNFORMATTED_TEST_FILE_PATH.parent) as pf: + uri = utils.as_uri(str(pf)) + + with session.LspSession() as ls_session: + ls_session.initialize(init_params) + + done = Event() + + def _handler(params): + nonlocal actual_diagnostics + actual_diagnostics = params + done.set() + + ls_session.set_notification_callback(session.PUBLISH_DIAGNOSTICS, _handler) + + ls_session.notify_did_open( + { + "textDocument": { + "uri": uri, + "languageId": "python", + "version": 1, + "text": contents, + } + } + ) + + # wait for some time to receive all notifications + done.wait(TIMEOUT) + + assert_that( + actual_diagnostics, + is_( + { + "uri": uri, + "diagnostics": [ + { + "range": { + "start": {"line": 0, "character": 0}, + "end": {"line": 1, "character": 0}, + }, + "message": "Imports are incorrectly sorted and/or formatted.", + "severity": 1, + "code": "E", + "source": "isort", + } + ], + } + ), + ) + + only = None + if action_type in ("quickfix", "source.organizeImports", ""): + only = [action_type] + else: + only = action_type + actual_code_actions = ls_session.text_document_code_action( + { + "textDocument": {"uri": uri}, + "range": { + "start": {"line": 0, "character": 0}, + "end": {"line": 0, "character": 0}, + }, + "context": { + "diagnostics": [ + { + "range": { + "start": {"line": 0, "character": 0}, + "end": {"line": 1, "character": 0}, + }, + "message": "Imports are incorrectly sorted and/or formatted.", + "severity": 1, + "code": "E", + "source": "isort", + } + ], + "only": only, + }, + }, + ) + + if action_type is None or action_type == ( + "quickfix", + "source.organizeImports", + ): + assert_that( + actual_code_actions, + is_( + [ + { + "title": "isort: Organize Imports", + "kind": "source.organizeImports", + "edit": None, + "data": uri, + }, + { + "title": "isort: Fix import sorting and/or formatting", + "kind": "quickfix", + "diagnostics": [ + { + "range": { + "start": {"line": 0, "character": 0}, + "end": {"line": 1, "character": 0}, + }, + "message": "Imports are incorrectly sorted and/or formatted.", + "severity": 1, + "code": "E", + "source": "isort", + } + ], + "edit": None, + "data": uri, + }, + ] + ), + ) + elif action_type == "": + assert_that(actual_code_actions, is_(None)) + elif action_type == "quickfix": + assert_that( + actual_code_actions, + is_( + [ + { + "title": "isort: Fix import sorting and/or formatting", + "kind": "quickfix", + "diagnostics": [ + { + "range": { + "start": {"line": 0, "character": 0}, + "end": {"line": 1, "character": 0}, + }, + "message": "Imports are incorrectly sorted and/or formatted.", + "severity": 1, + "code": "E", + "source": "isort", + } + ], + "edit": None, + "data": uri, + }, + ] + ), + ) + elif action_type == "source.organizeImports": + assert_that( + actual_code_actions, + is_( + [ + { + "title": "isort: Organize Imports", + "kind": "source.organizeImports", + "edit": { + "documentChanges": [ + { + "textDocument": {"uri": uri, "version": 1}, + "edits": [ + { + "range": { + "start": { + "line": 0, + "character": 0, + }, + "end": { + "line": 3, + "character": 0, + }, + }, + "newText": expected, + } + ], + } + ] + }, + "data": uri, + } + ] + ), + ) + else: + assert False, "Invalid action type" + + @pytest.mark.parametrize("line_ending", ["\n", "\r\n"]) def test_organize_import(line_ending): """Test formatting a python file."""