diff --git a/modules/kernels/cpp/default.nix b/modules/kernels/cpp/default.nix index 8da24fa..2a42e4b 100644 --- a/modules/kernels/cpp/default.nix +++ b/modules/kernels/cpp/default.nix @@ -4,6 +4,7 @@ , cling , clang , xeus-cling +, llvmPackages , settings , settingsSchema @@ -17,8 +18,13 @@ with lib; let attrs = [flavor] ++ settings.interface.attrs; + kernelName = "cpp"; + common = callPackage ../common.nix {}; + languageServers = lib.optionals settings.lsp.clangd.enable + [(callPackage ./language_server_clangd { inherit kernelName llvmPackages; })]; + displaySuffix = { "c++17" = " 17"; "c++20" = " 20"; @@ -80,10 +86,12 @@ symlinkJoin { (callPackage ./kernel_xeus.nix { inherit attrs displayName extensions; std = flavor; - kernelName = "cpp"; + inherit kernelName; }) cling - ]; + ] + ++ languageServers + ; passthru = { meta = clang.meta // { @@ -100,6 +108,7 @@ symlinkJoin { clang = clang.version; cling = cling.unwrapped.version; xeus-cling = xeus-cling.version; + clangd = llvmPackages.clang-tools.version; std = flavor; }; inherit settings settingsSchema; @@ -117,6 +126,6 @@ symlinkJoin { code_mirror_mode = "clike"; code_mirror_mime_type = "text/x-c++src"; }; - languageServerNames = []; + languageServerNames = map (x: x.languageServerName) languageServers; }; } diff --git a/modules/kernels/cpp/language_server_clangd/default.nix b/modules/kernels/cpp/language_server_clangd/default.nix new file mode 100644 index 0000000..d428a43 --- /dev/null +++ b/modules/kernels/cpp/language_server_clangd/default.nix @@ -0,0 +1,32 @@ +{ lib +, callPackage +, llvmPackages + +, kernelName +}: + +let + common = callPackage ../../common.nix {}; + + clangd = llvmPackages.clang-tools; + + languageServerName = "clangd"; + + passthru = { + inherit languageServerName; + }; + +in + +common.writeTextDirWithMetaAndPassthru clangd.meta passthru "lib/codedown/language-servers/cpp-${kernelName}-clangd.yaml" (lib.generators.toYAML {} [{ + name = languageServerName; + version = clangd.version; + extensions = ["cpp" "hpp" "cxx" "hxx" "c" "h"]; + notebook_suffix = ".cpp"; + attrs = ["cpp"]; + type = "stream"; + primary = true; + args = [ + "${clangd}/bin/clangd" + ]; +}]) diff --git a/modules/kernels/cpp/module.nix b/modules/kernels/cpp/module.nix index dd447f5..fc9943a 100644 --- a/modules/kernels/cpp/module.nix +++ b/modules/kernels/cpp/module.nix @@ -57,6 +57,12 @@ in type = types.listOf types.str; default = ["cpp" "hpp" "cxx" "hxx" "c" "h"]; }; + + lsp.clangd.enable = mkOption { + example = "Enable clangd language server"; + type = types.bool; + default = true; + }; }; }; diff --git a/tests/app/Spec/Tests/Cpp.hs b/tests/app/Spec/Tests/Cpp.hs index 298dded..6d6022b 100644 --- a/tests/app/Spec/Tests/Cpp.hs +++ b/tests/app/Spec/Tests/Cpp.hs @@ -1,12 +1,15 @@ {-# LANGUAGE RankNTypes #-} {-# OPTIONS_GHC -fno-warn-unused-top-binds #-} +{-# OPTIONS_GHC -fno-warn-incomplete-uni-patterns #-} module Spec.Tests.Cpp (tests) where import Data.String.Interpolate import Data.Text +import Language.LSP.Protocol.Types import Test.Sandwich as Sandwich import TestLib.JupyterRunnerContext +import TestLib.LSP import TestLib.NixEnvironmentContext import TestLib.NixTypes import TestLib.TestSearchers @@ -26,24 +29,47 @@ tests = describe "C++" $ parallel $ do tests' "c++23" tests' "c++2c" + testsWithLsp "c++23" + tests' :: Text -> LanguageSpec tests' flavor = describe [i|C++ (#{flavor})|] $ introduceNixEnvironment [kernelSpec flavor] [] "C++" $ introduceJupyterRunner $ do testKernelStdout "cpp" [__i|\#include using namespace std; cout << "hi" << endl;|] "hi\n" +testsWithLsp :: Text -> LanguageSpec +testsWithLsp flavor = describe [i|C++ (#{flavor}) with LSP|] $ introduceNixEnvironment [kernelSpecWithLsp flavor] [] "C++" $ do + describe "LSP" $ do + testDiagnostics'' "simple" lsName "test.cpp" (Just "cpp") + [__i|int main() { + undefined_function(); + return 0; + }|] [] $ \diags -> do + info [i|Got diags: #{diags}|] + info [i|Got ranges: #{getDiagnosticRanges' diags}|] + getDiagnosticRanges' diags `shouldBe` [(Range (Position 1 2) (Position 1 20), Just (InR "undeclared_var_use"), "Use of undeclared identifier 'undefined_function'")] + +lsName :: Text +lsName = "clangd" kernelSpec :: Text -> NixKernelSpec -kernelSpec flavor = NixKernelSpec { +kernelSpec flavor = kernelSpec' [[i|flavor = "#{flavor}"|]] + +kernelSpecWithLsp :: Text -> NixKernelSpec +kernelSpecWithLsp flavor = kernelSpec' [ + [i|flavor = "#{flavor}"|] + , "lsp.clangd.enable = true" + ] + +kernelSpec' :: [Text] -> NixKernelSpec +kernelSpec' extraConfig = NixKernelSpec { nixKernelName = "cpp" , nixKernelChannel = "codedown" , nixKernelDisplayName = Just "CPP" , nixKernelPackages = [] , nixKernelMeta = Nothing , nixKernelIcon = Nothing - , nixKernelExtraConfig = Just [ - [i|flavor = "#{flavor}"|] - ] + , nixKernelExtraConfig = Just extraConfig } main :: IO ()