diff --git a/bundled/tool/server.py b/bundled/tool/server.py index 685b15b..7cd5c35 100644 --- a/bundled/tool/server.py +++ b/bundled/tool/server.py @@ -217,27 +217,42 @@ def code_action_organize_imports(params: lsp.CodeActionParams): kind=lsp.CodeActionKind.SourceOrganizeImports, data=params.text_document.uri, edit=_create_workspace_edits(text_document, results), + diagnostics=[], ) ] - 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 b22f6ed..e27f498 100644 --- a/src/test/python_tests/test_sort.py +++ b/src/test/python_tests/test_sort.py @@ -16,6 +16,213 @@ 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", + "diagnostics": [], + "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", + "diagnostics": [], + "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.""" @@ -116,6 +323,7 @@ def _handler(params): { "title": "isort: Organize Imports", "kind": "source.organizeImports", + "diagnostics": [], "edit": None, "data": uri, }, @@ -150,6 +358,7 @@ def _handler(params): { "title": "isort: Organize Imports", "kind": "source.organizeImports", + "diagnostics": [], "edit": { "documentChanges": [ { @@ -274,6 +483,7 @@ def _handler(params): { "title": "isort: Organize Imports", "kind": "source.organizeImports", + "diagnostics": [], "edit": None, "data": uri, }, @@ -308,6 +518,7 @@ def _handler(params): { "title": "isort: Organize Imports", "kind": "source.organizeImports", + "diagnostics": [], "edit": { "documentChanges": [ {