diff --git a/flake.nix b/flake.nix index bb53bf5a..fc7b7ef5 100644 --- a/flake.nix +++ b/flake.nix @@ -316,6 +316,7 @@ mkJupyterlab = { jupyterlabEnvArgs ? {}, + notebookConfig ? {}, kernels ? k: [], # k: [ (k.python {}) k.bash ], # extensions ? e: [], # e: [ e.jupy-ext ] runtimePackages ? [], # runtime package available to all binaries @@ -390,14 +391,19 @@ jupyterlabEnv = jupyterlabEnvWrapped (baseArgs // jupyterlabEnvArgs); # create directories for storing jupyter configs - jupyterDir = pkgs.runCommand "jupyter-dir" {} '' - # make jupyter config and data directories - mkdir -p $out/config $out/data - echo "c.NotebookApp.use_redirect_file = False" > $out/config/jupyter_notebook_config.py - - # make jupyter lab user settings and workspaces directories - mkdir -p $out/config/lab/{user-settings,workspaces} - ''; + jupyterDir = let + mergeNotebookConfig = lib.recursiveUpdate notebookConfig { + NotebookApp.use_redirect_file = false; + KernelSpecManager.whitelist = map (x: x.name) userKernels; + }; + in + pkgs.runCommand "jupyter-dir" {} '' + # make jupyter config and data directories + mkdir -p $out/config $out/data + echo '${builtins.toJSON mergeNotebookConfig}' > $out/config/jupyter_notebook_config.json + # make jupyter lab user settings and workspaces directories + mkdir -p $out/config/lab/{user-settings,workspaces} + ''; /* Finds kernels from kernelDerivations that have the same kernel @@ -571,6 +577,7 @@ mkJupyterlab mkJupyterlabFromPath mkJupyterlabNew + mkJupyterlabEval ; }; packages = @@ -624,7 +631,7 @@ }; } )) - // rec { + // { jupyterKernels = builtins.mapAttrs mkKernelFlakeOutput kernelsConfig.available; templates.default = { path = ./template; diff --git a/modules/conf/jupyter_notebook_config.json b/modules/conf/jupyter_notebook_config.json new file mode 100644 index 00000000..c1243385 --- /dev/null +++ b/modules/conf/jupyter_notebook_config.json @@ -0,0 +1,13 @@ +{ + "LanguageServerManager": { + "language_servers": { + "haskell-language-server": { + "version": 2, + "argv": ["haskell-language-server-wrapper", "lsp"], + "languages": ["haskell"], + "display_name": "haskell-language-server", + "mimetypes": ["text/haskell", "text/x-haskell"] + } + } + } +} diff --git a/modules/default.nix b/modules/default.nix index a6acc528..3810f8b9 100644 --- a/modules/default.nix +++ b/modules/default.nix @@ -19,6 +19,69 @@ in { description = "A list of runtime packages available to all binaries"; default = []; }; + notebookConfig = lib.mkOption { + type = types.attrs; + description = "jupyter notebook config which will be written to jupyter_notebook_config.py"; + default = {}; + apply = c: lib.recursiveUpdate (lib.importJSON ./conf/jupyter_notebook_config.json) c; + }; + + extensions = { + features = lib.mkOption { + type = types.listOf types.str; + description = "A list of features to enable"; + default = []; + example = ["lsp"]; + }; + languageServers = lib.mkOption { + default = {}; + description = "Which language servers package to use"; + type = types.submodule { + options = { + python = lib.mkOption { + type = types.functionTo types.package; + description = "Python language server"; + default = ps: ps.python-lsp-server; + example = lib.literalExpression '' + if you want to use pyls-mypy or othter dependencies, you can do: + extraPackages = ps: [] ++ python-lsp-server.passthru.optional-dependencies.all; + ''; + }; + haskell = lib.mkOption { + type = types.package; + description = "Haskell language server"; + default = config.nixpkgs.haskell-language-server; + example = lib.literalExpression '' + config.nixpkgs.haskell-language-server.override { supportedGhcVersions = [ "90" "94" ]; }; + ''; + }; + }; + }; + }; + }; + + jupyterlabEnvArgs = lib.mkOption { + type = types.submodule { + options = + { + extraPackages = lib.mkOption { + type = types.functionTo (types.listOf types.package); + default = ps: []; + example = ps: [ps.jupytext]; + description = lib.mdDoc "A list of packages for extending the jupyterlab environment"; + }; + } + // (lib.recursiveUpdate (import ./types/poetry.nix { + inherit lib self; + config = config.jupyterlab.jupyterlabEnvArgs; + }) + { + projectDir.default = self.outPath; + }); + }; + default = {}; + description = "Arguments for the jupyterlab poetry's environment"; + }; }; # flakes ? [], # flakes where to detect custom kernels/extensions @@ -53,27 +116,73 @@ in { #++ map (name: ./. + "/../kernels/available/${name}/module.nix") (builtins.attrNames (builtins.readDir ./../kernels/available)); config = { - build = mkJupyterlab { - #jupyterlabEnvArgs = config.jupyterlabEnvArgs; - kernels = availableKernels: - lib.flatten - ( - builtins.map + build = let + findFeature = name: + if config.jupyterlab.extensions.features != [] + then ( - kernelTypeName: - builtins.map - ( - kernelName: - availableKernels.${kernelTypeName} - config.kernel.${kernelTypeName}.${kernelName}.kernelArgs - ) - (builtins.attrNames config.kernel.${kernelTypeName}) + if (lib.intersectLists [name] config.jupyterlab.extensions.features) != [] + then true + else false ) - (builtins.attrNames config.kernel) - ); - runtimePackages = config.jupyterlab.runtimePackages; - #flakes = config.flakes; - }; + else false; + + enabledLanguage = lang: feature: + ( + if config.kernel.${lang} != {} + then true + else false + ) + && (findFeature feature); + in + mkJupyterlab { + jupyterlabEnvArgs = { + inherit + (config.jupyterlab.jupyterlabEnvArgs) + pyproject + projectDir + editablePackageSources + preferWheels + poetrylock + poetry2nix + ; + + # all of python packages should be kept in extraPackages, instead of runtimePackages + extraPackages = ps: + (lib.optionals (enabledLanguage "python" "lsp") [ + (config.jupyterlab.extensions.languageServers.python ps) + ]) + ++ (lib.optionals (findFeature "jupytext") [ps.jupytext]) + ++ (lib.optionals (findFeature "lsp") [ps.jupyter-lsp]) + ++ (config.jupyterlab.jupyterlabEnvArgs.extraPackages ps); + }; + + notebookConfig = config.jupyterlab.notebookConfig; + + runtimePackages = + config.jupyterlab.runtimePackages + ++ (lib.optionals (enabledLanguage "haskell" "lsp") [ + config.jupyterlab.extensions.languageServers.haskell + ]); + + kernels = availableKernels: + lib.flatten + ( + builtins.map + ( + kernelTypeName: + builtins.map + ( + kernelName: + availableKernels.${kernelTypeName} + config.kernel.${kernelTypeName}.${kernelName}.kernelArgs + ) + (builtins.attrNames config.kernel.${kernelTypeName}) + ) + (builtins.attrNames config.kernel) + ); + #flakes = config.flakes; + }; _module.args.pkgs = config.nixpkgs; }; } diff --git a/template/lsp.nix b/template/lsp.nix new file mode 100644 index 00000000..b5c581c0 --- /dev/null +++ b/template/lsp.nix @@ -0,0 +1,40 @@ +{ + pkgs, + config, + ... +}: { + jupyterlab = { + extensions = { + features = ["lsp" "jupytext"]; + languageServers = { + python = ps: ps.python-lsp-server; + }; + }; + notebookConfig = { + # add your custom language server config here or other notebook config for the jupyterlab_notebook_config.json + # LanguageServerManager.language_servers.pyls.serverSettings = { + # pyls.plugins.pycodestyle.enabled = true; + # } + # LanguageServerManager.language_servers.custom-language-servers + # "argv" = ["haskell-language-server" "--lsp"]; + # "languages" = ["haskell"]; + # "display_name" = "haskell-language-server"; + # "mimetypes" = ["text/haskell" "text/x-haskell"]; + # server_settings = { + # "haskell.plugin.ghcide-completions.globalOn" = true; + # + # }; + # }; + }; + runtimePackages = []; + jupyterlabEnvArgs.extraPackages = ps: ([] ++ ps.python-lsp-server.passthru.optional-dependencies.all); + }; + kernel.python.minimal = { + enable = true; + }; + # FIXME: haskell-language-server is not yet support in jupyterlab-lsp + kernel.haskell.minimal = { + enable = true; + haskellCompiler = "ghc902"; + }; +}