From cc02b63af70d4f2afce3c1d9614d94f9bbcd1457 Mon Sep 17 00:00:00 2001 From: Tom McLaughlin Date: Wed, 12 Nov 2025 18:07:04 -0800 Subject: [PATCH 1/2] Working on clangd language server noci --- modules/kernels/cpp/default.nix | 15 +++++++-- .../cpp/language_server_clangd/default.nix | 32 +++++++++++++++++++ modules/kernels/cpp/module.nix | 6 ++++ tests/app/Spec/Tests/Cpp.hs | 31 ++++++++++++++++++ 4 files changed, 81 insertions(+), 3 deletions(-) create mode 100644 modules/kernels/cpp/language_server_clangd/default.nix diff --git a/modules/kernels/cpp/default.nix b/modules/kernels/cpp/default.nix index 8da24fa2..2a42e4bf 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 00000000..d428a430 --- /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 dd447f5a..fc9943a5 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 298dded1..4f19409e 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,12 +29,26 @@ 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 + testDiagnosticsLabelDesired "simple" lsName "test.cpp" (Just "cpp") + [__i|int main() { + undefined_function(); + return 0; + }|] + ((== [(Range (Position 1 2) (Position 1 20), Nothing, "use of undeclared identifier 'undefined_function'")]) . getDiagnosticRanges') + +lsName :: Text +lsName = "clangd" kernelSpec :: Text -> NixKernelSpec kernelSpec flavor = NixKernelSpec { @@ -46,5 +63,19 @@ kernelSpec flavor = NixKernelSpec { ] } +kernelSpecWithLsp :: Text -> NixKernelSpec +kernelSpecWithLsp flavor = NixKernelSpec { + nixKernelName = "cpp" + , nixKernelChannel = "codedown" + , nixKernelDisplayName = Just "CPP" + , nixKernelPackages = [] + , nixKernelMeta = Nothing + , nixKernelIcon = Nothing + , nixKernelExtraConfig = Just [ + [i|flavor = "#{flavor}"|] + , "lsp.clangd.enable = true;" + ] + } + main :: IO () main = jupyterMain tests From e6acc15aa0c0bf0e0d6306f42cef575ae7e212bc Mon Sep 17 00:00:00 2001 From: Tom McLaughlin Date: Wed, 12 Nov 2025 18:34:15 -0800 Subject: [PATCH 2/2] More on Cpp.hs test noci --- tests/app/Spec/Tests/Cpp.hs | 33 ++++++++++++++------------------- 1 file changed, 14 insertions(+), 19 deletions(-) diff --git a/tests/app/Spec/Tests/Cpp.hs b/tests/app/Spec/Tests/Cpp.hs index 4f19409e..6d6022bc 100644 --- a/tests/app/Spec/Tests/Cpp.hs +++ b/tests/app/Spec/Tests/Cpp.hs @@ -40,41 +40,36 @@ tests' flavor = describe [i|C++ (#{flavor})|] $ introduceNixEnvironment [kernelS testsWithLsp :: Text -> LanguageSpec testsWithLsp flavor = describe [i|C++ (#{flavor}) with LSP|] $ introduceNixEnvironment [kernelSpecWithLsp flavor] [] "C++" $ do describe "LSP" $ do - testDiagnosticsLabelDesired "simple" lsName "test.cpp" (Just "cpp") + testDiagnostics'' "simple" lsName "test.cpp" (Just "cpp") [__i|int main() { undefined_function(); return 0; - }|] - ((== [(Range (Position 1 2) (Position 1 20), Nothing, "use of undeclared identifier 'undefined_function'")]) . getDiagnosticRanges') + }|] [] $ \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 { - nixKernelName = "cpp" - , nixKernelChannel = "codedown" - , nixKernelDisplayName = Just "CPP" - , nixKernelPackages = [] - , nixKernelMeta = Nothing - , nixKernelIcon = Nothing - , nixKernelExtraConfig = Just [ - [i|flavor = "#{flavor}"|] - ] - } +kernelSpec flavor = kernelSpec' [[i|flavor = "#{flavor}"|]] kernelSpecWithLsp :: Text -> NixKernelSpec -kernelSpecWithLsp flavor = 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}"|] - , "lsp.clangd.enable = true;" - ] + , nixKernelExtraConfig = Just extraConfig } main :: IO ()