diff --git a/plugins/lsp/language-servers/pylsp.nix b/plugins/lsp/language-servers/pylsp.nix index da6f401f..50067230 100644 --- a/plugins/lsp/language-servers/pylsp.nix +++ b/plugins/lsp/language-servers/pylsp.nix @@ -24,7 +24,7 @@ in { ); }; - plugins = helpers.mkCompositeOption "pylsp plugins" { + plugins = { autopep8 = helpers.mkCompositeOption "autopep8 settings" { enabled = helpers.defaultNullOpts.mkBool true '' Enable or disable the plugin. @@ -391,6 +391,98 @@ in { ''; }; + isort = { + enabled = helpers.defaultNullOpts.mkBool false '' + Enable or disable the plugin. + Setting this explicitely to `true` will install the dependency for this plugin + (pyls-isort). + ''; + }; + + black = { + enabled = helpers.defaultNullOpts.mkBool false '' + Enable or disable the plugin. + Setting this explicitely to `true` will install the dependency for this plugin + (python-lsp-black). + ''; + + cache_config = helpers.defaultNullOpts.mkBool false '' + Whether to enable black configuration caching. + ''; + + line_length = helpers.defaultNullOpts.mkInt 88 '' + An integer that maps to black's max-line-length setting. + Defaults to 88 (same as black's default). + This can also be set through black's configuration files, which should be preferred for + multi-user projects. + ''; + + preview = helpers.defaultNullOpts.mkBool false '' + Enable or disable black's --preview setting. + ''; + }; + + memestra = { + enabled = helpers.defaultNullOpts.mkBool false '' + Enable or disable the plugin. + Setting this explicitely to `true` will install the dependency for this plugin + (pyls-memestra). + ''; + }; + + rope = { + enabled = helpers.defaultNullOpts.mkBool false '' + Enable or disable the plugin. + Setting this explicitely to `true` will install the dependency for this plugin + (pylsp-rope). + ''; + }; + + ruff = { + enabled = helpers.defaultNullOpts.mkBool false '' + Enable or disable the plugin. + Setting this explicitely to `true` will install the dependency for this plugin + (python-lsp-ruff). + ''; + + config = helpers.mkNullOrOption types.str "Path to optional pyproject.toml file."; + + exclude = helpers.defaultNullOpts.mkNullable (with types; listOf str) "[]" '' + Exclude files from being checked by ruff. + ''; + + executable = helpers.mkNullOrOption types.str '' + Path to the ruff executable. Assumed to be in PATH by default. + ''; + + ignore = helpers.defaultNullOpts.mkNullable (with types; listOf str) "[]" '' + Error codes to ignore. + ''; + + extendIgnore = helpers.defaultNullOpts.mkNullable (with types; listOf str) "[]" '' + Same as ignore, but append to existing ignores. + ''; + + lineLength = helpers.mkNullOrOption types.int "Set the line-length for length checks."; + + perFileIgnores = helpers.mkNullOrOption (with types; attrsOf (listOf str)) '' + File-specific error codes to be ignored. + ''; + + select = helpers.defaultNullOpts.mkNullable (with types; listOf str) "[]" '' + List of error codes to enable. + ''; + + extendSelect = helpers.defaultNullOpts.mkNullable (with types; listOf str) "[]" '' + Same as select, but append to existing error codes. + ''; + + format = helpers.defaultNullOpts.mkNullable (with types; listOf str) "[]" '' + List of error codes to fix during formatting. + The default is ["I"], any additional codes are appended to this list. + ''; + }; + ### END OF THIRD-PARTY PLUGINS }; @@ -409,14 +501,15 @@ in { config = mkIf cfg.enable { - extraPackages = with cfg.settings; let - isNotNullAndEnabled = x: (!isNull x) && x.enabled; + extraPackages = let + isEnabled = x: (!isNull x) && (x.enabled == true); + plugins = cfg.settings.plugins; in - optionals (!isNull cfg.settings.plugins) ( - lists.flatten (map + lists.flatten ( + (map ( pluginName: ( - optionals (isNotNullAndEnabled plugins.${pluginName}) + optionals (isEnabled plugins.${pluginName}) cfg.package.optional-dependencies.${pluginName} ) ) @@ -433,14 +526,25 @@ in { ++ ( optionals ( - (isNotNullAndEnabled plugins.rope_autoimport) - || (isNotNullAndEnabled plugins.rope_completion) + (isEnabled plugins.rope_autoimport) + || (isEnabled plugins.rope_completion) ) cfg.package.optional-dependencies.rope ) + # Third party plugins ++ ( - optional (isNotNullAndEnabled plugins.pylsp_mypy) - pkgs.python3Packages.pylsp-mypy + mapAttrsToList + ( + pluginName: nixPackage: (optional (isEnabled plugins.${pluginName}) nixPackage) + ) + (with pkgs.python3Packages; { + pylsp_mypy = pylsp-mypy; + isort = pyls-isort; + black = python-lsp-black; + memestra = pyls-memestra; + rope = pylsp-rope; + ruff = python-lsp-ruff; + }) ) ); }; diff --git a/tests/test-sources/plugins/lsp/pylsp.nix b/tests/test-sources/plugins/lsp/pylsp.nix new file mode 100644 index 00000000..03f46df8 --- /dev/null +++ b/tests/test-sources/plugins/lsp/pylsp.nix @@ -0,0 +1,161 @@ +{ + # Example pylsp config + default = { + plugins.lsp = { + enable = true; + + servers.pylsp = { + enable = true; + + settings = { + configurationSources = "pycodestyle"; + + plugins = { + autopep8 = { + enabled = true; + }; + flake8 = { + config = null; + enabled = true; + exclude = []; + executable = "flake8"; + filename = null; + hangClosing = null; + ignore = []; + maxComplexity = null; + maxLineLength = null; + indentSize = null; + perFileIgnores = []; + select = null; + }; + jedi = { + auto_import_modules = ["numpy"]; + extra_paths = []; + environment = null; + }; + jedi_completion = { + enabled = true; + include_params = true; + include_class_objects = false; + include_function_objects = false; + fuzzy = false; + eager = false; + resolve_at_most = 25; + cache_for = ["pandas" "numpy" "tensorflow" "matplotlib"]; + }; + jedi_definition = { + enabled = true; + follow_imports = true; + follow_builtin_imports = true; + follow_builtin_definitions = true; + }; + jedi_hover = { + enabled = true; + }; + jedi_references = { + enabled = true; + }; + jedi_signature_help = { + enabled = true; + }; + jedi_symbols = { + enabled = true; + all_scopes = true; + include_import_symbols = true; + }; + mccabe = { + enabled = true; + threshold = 15; + }; + preload = { + enabled = true; + modules = []; + }; + pycodestyle = { + enabled = true; + exclude = []; + filename = []; + ropeFolder = null; + ignore = []; + hangClosing = true; + maxLineLength = 80; + indentSize = 4; + }; + pydocstyle = { + enabled = false; + convention = null; + addIgnore = []; + addSelect = []; + ignore = []; + select = null; + match = "(?!test_).*\\.py"; + matchDir = "[^\\.].*"; + }; + pyflakes = { + enabled = true; + }; + pylint = { + enabled = true; + args = []; + executable = null; + }; + rope_autoimport = { + enabled = true; + memory = false; + }; + rope_completion = { + enabled = true; + eager = false; + }; + yapf = { + enabled = true; + }; + # Third party plugins + pylsp_mypy = { + enabled = true; + live_mode = true; + dmypy = false; + strict = false; + overrides = [true]; + dmypy_status_file = ".dmypy.json"; + config_sub_paths = []; + report_progress = false; + }; + black = { + enabled = true; + cache_config = true; + line_length = 100; + preview = true; + }; + memestra = { + enabled = true; + }; + rope = { + enabled = true; + }; + ruff = { + enabled = true; + config = "/foo/bar/pyproject.toml"; + exclude = ["foo" "bar"]; + executable = "/foo/bar/ruff"; + ignore = ["E42" "E720"]; + extendIgnore = ["E12"]; + lineLength = 123; + perFileIgnores = { + "__init__.py" = ["E402"]; + "path/to/file.py" = ["E402"]; + }; + select = ["E01" "E56"]; + extendSelect = ["E68"]; + format = ["E90"]; + }; + }; + rope = { + extensionModules = null; + ropeFolder = null; + }; + }; + }; + }; + }; +}