diff --git a/homeManagerModules/neovim/langs/bash/default.nix b/homeManagerModules/neovim/langs/bash/default.nix
index 40339c05..0be94e45 100644
--- a/homeManagerModules/neovim/langs/bash/default.nix
+++ b/homeManagerModules/neovim/langs/bash/default.nix
@@ -42,10 +42,9 @@ in {
                 command = 'setlocal ts=4 sw=4 sts=0 expandtab',
             });
 
-            local default_capabilities = require('cmp_nvim_lsp').default_capabilities();
-
-            require('lspconfig').bashls.setup({
-                capabilities = default_capabilities,
+            vim.lsp.enable('bashls');
+            vim.lsp.config('bashls', {
+                capabilities = require('cmp_nvim_lsp').default_capabilities(),
 
                 settings = {
                     bashIde = {
diff --git a/homeManagerModules/neovim/langs/c-lang/default.nix b/homeManagerModules/neovim/langs/c-lang/default.nix
index 4c9d7bd0..f7dde381 100644
--- a/homeManagerModules/neovim/langs/c-lang/default.nix
+++ b/homeManagerModules/neovim/langs/c-lang/default.nix
@@ -7,32 +7,10 @@
   inherit (lib) mkIf;
 
   cfg = config.programs.neovim;
-  flakeEnv = config.programs.bash.sessionVariables.FLAKE;
 in {
   config = mkIf cfg.enable {
     programs = {
       neovim = {
-        extraLuaConfig =
-          # lua
-          ''
-            --
-            vim.api.nvim_create_autocmd({ 'FileType', 'BufEnter' }, {
-                pattern = { 'cpp', 'c' },
-
-                callback = function()
-                    vim.cmd[[setlocal ts=4 sw=4 sts=0 expandtab]];
-
-                    if (devShells['c-lang'] == nil) then
-                        devShells['c-lang'] = 1;
-
-                        require('nix-develop').nix_develop_extend({'${flakeEnv}#c-lang'}, function()
-                            vim.cmd[[LspStart]];
-                        end);
-                    end
-                end,
-            });
-          '';
-
         plugins = [
           {
             plugin = pkgs.vimPlugins.clangd_extensions-nvim;
@@ -41,22 +19,31 @@ in {
               # lua
               ''
                 --
-                local lsp = require('lspconfig');
                 local default_capabilities = require('cmp_nvim_lsp').default_capabilities();
 
-
-                lsp.cmake.setup({
-                    capabilities = default_capabilities,
-                    autostart = false,
-                });
-
-                lsp.clangd.setup({
-                    capabilities = default_capabilities,
-                    autostart = false,
-
-                    on_attach = function(_, bufnr)
-                        require('clangd_extensions').setup();
+                loadDevShell({
+                    name = 'c-lang',
+                    pattern = { 'cpp', 'c' },
+                    pre_shell_callback = function()
+                        vim.cmd[[setlocal ts=4 sw=4 sts=0 expandtab]];
                     end,
+                    language_servers = {
+                        cmake = function()
+                            vim.lsp.start(vim.tbl_deep_extend('force', vim.lsp.config['cmake'], {
+                                capabilities = default_capabilities,
+                            }));
+                        end,
+
+                        clangd = function()
+                            vim.lsp.start(vim.tbl_deep_extend('force', vim.lsp.config['clangd'], {
+                                capabilities = default_capabilities,
+
+                                on_attach = function(_, bufnr)
+                                    require('clangd_extensions').setup();
+                                end,
+                            }));
+                        end,
+                    },
                 });
               '';
           }
diff --git a/homeManagerModules/neovim/langs/csharp/default.nix b/homeManagerModules/neovim/langs/csharp/default.nix
index 03ee72c9..e6496886 100644
--- a/homeManagerModules/neovim/langs/csharp/default.nix
+++ b/homeManagerModules/neovim/langs/csharp/default.nix
@@ -10,7 +10,6 @@ self: {
   inherit (lib) mkIf;
 
   cfg = config.programs.neovim;
-  flakeEnv = config.programs.bash.sessionVariables.FLAKE;
 in {
   config = mkIf cfg.enable {
     programs = {
@@ -60,23 +59,17 @@ in {
 
                         exe = 'Microsoft.CodeAnalysis.LanguageServer',
                     });
+
+                    vim.cmd[[e]]; -- reload to attach on current file
                 end;
 
-                vim.api.nvim_create_autocmd({ 'FileType', 'BufEnter' }, {
+                loadDevShell({
+                    name = 'csharp',
                     pattern = { 'cs' },
-
-                    callback = function()
+                    pre_shell_callback = function()
                         vim.cmd[[setlocal ts=4 sw=4 sts=0 expandtab]];
-
-                        if (devShells['csharp'] == nil) then
-                            devShells['csharp'] = 1;
-
-                            require('nix-develop').nix_develop_extend({'${flakeEnv}#csharp'}, function()
-                                startRoslyn();
-                                vim.cmd[[e]]; -- reload to attach on current file
-                            end);
-                        end
                     end,
+                    post_shell_callback = startRoslyn,
                 });
               '';
           }
diff --git a/homeManagerModules/neovim/langs/default.nix b/homeManagerModules/neovim/langs/default.nix
index 3751003f..8c92dcce 100644
--- a/homeManagerModules/neovim/langs/default.nix
+++ b/homeManagerModules/neovim/langs/default.nix
@@ -10,6 +10,7 @@ self: {
   inherit (lib) attrValues fileContents mkBefore mkIf;
 
   cfg = config.programs.neovim;
+  flakeEnv = config.programs.bash.sessionVariables.FLAKE;
 in {
   imports = [
     ./bash
@@ -37,9 +38,51 @@ in {
         # lua
         ''
           --
+          local nix_develop = require('nix-develop');
+
           -- Init object to keep track of loaded devShells
           local devShells = {};
 
+          --- @param name string
+          --- @param pattern string|string[]
+          --- @param pre_shell_callback function
+          --- @param language_servers function?
+          --- @param post_shell_callback function?
+          local loadDevShell = function(args)
+              local name = args.name;
+              local pattern = args.pattern;
+              local pre_shell_callback = args.pre_shell_callback;
+
+              local post_shell_callback = args.post_shell_callback or function()
+                  local filetype = vim.api.nvim_buf_get_option(0, 'filetype')
+
+                  for name, func in pairs(args.language_servers) do
+                      if vim.tbl_contains(vim.lsp.config[name].filetypes, filetype) then
+                          func();
+                      end;
+                  end;
+              end;
+
+              vim.api.nvim_create_autocmd({ 'FileType', 'BufEnter' }, {
+                  pattern = pattern,
+
+                  callback = function()
+                      pre_shell_callback();
+
+                      if (devShells[name] == nil) then
+                          devShells[name] = 1;
+
+                          nix_develop.nix_develop_extend(
+                              {'${flakeEnv}#' .. name},
+                              post_shell_callback
+                          );
+                      else
+                          post_shell_callback();
+                      end
+                  end,
+              });
+          end;
+
           -- Add formatting cmd
           vim.api.nvim_create_user_command(
               'Format',
diff --git a/homeManagerModules/neovim/langs/golang/default.nix b/homeManagerModules/neovim/langs/golang/default.nix
index 0c270e86..e0d6718e 100644
--- a/homeManagerModules/neovim/langs/golang/default.nix
+++ b/homeManagerModules/neovim/langs/golang/default.nix
@@ -6,7 +6,6 @@
   inherit (lib) mkIf;
 
   cfg = config.programs.neovim;
-  flakeEnv = config.programs.bash.sessionVariables.FLAKE;
 in {
   config = mkIf cfg.enable {
     programs = {
@@ -15,28 +14,19 @@ in {
           # lua
           ''
             --
-            local lsp = require('lspconfig');
-            local default_capabilities = require('cmp_nvim_lsp').default_capabilities();
-
-            lsp.gopls.setup({
-                capabilities = default_capabilities,
-                autostart = false,
-            });
-
-            vim.api.nvim_create_autocmd({ 'FileType', 'BufEnter' }, {
+            loadDevShell({
+                name = 'golang',
                 pattern = { 'go', 'gomod', 'gowork', 'gotmpl' },
-
-                callback = function()
+                pre_shell_callback = function()
                     vim.cmd[[setlocal ts=4 sw=4 sts=0 expandtab]];
-
-                    if (devShells['golang'] == nil) then
-                        devShells['golang'] = 1;
-
-                        require('nix-develop').nix_develop_extend({'${flakeEnv}#golang'}, function()
-                            vim.cmd[[LspStart]];
-                        end);
-                    end
                 end,
+                language_servers = {
+                    gopls = function()
+                        vim.lsp.start(vim.tbl_deep_extend('force', vim.lsp.config['gopls'], {
+                            capabilities = require('cmp_nvim_lsp').default_capabilities(),
+                        }));
+                    end,
+                },
             });
           '';
       };
diff --git a/homeManagerModules/neovim/langs/json/default.nix b/homeManagerModules/neovim/langs/json/default.nix
index e94e8e5c..5c8bfe00 100644
--- a/homeManagerModules/neovim/langs/json/default.nix
+++ b/homeManagerModules/neovim/langs/json/default.nix
@@ -6,7 +6,6 @@
   inherit (lib) mkIf;
 
   cfg = config.programs.neovim;
-  flakeEnv = config.programs.bash.sessionVariables.FLAKE;
 in {
   config = mkIf cfg.enable {
     programs = {
@@ -15,46 +14,40 @@ in {
           # lua
           ''
             --
-            vim.api.nvim_create_autocmd({ 'FileType', 'BufEnter' }, {
-                pattern = { 'json', 'yaml', '.clang-.*' },
-
-                callback = function()
-                    vim.cmd[[setlocal ts=4 sw=4 sts=0 expandtab]];
-
-                    if (devShells['json'] == nil) then
-                        devShells['json'] = 1;
-
-                        require('nix-develop').nix_develop_extend({'${flakeEnv}#json'}, function()
-                            vim.cmd[[LspStart]];
-                        end);
-                    end
-                end,
-            });
-
-            local lsp = require('lspconfig');
             local default_capabilities = require('cmp_nvim_lsp').default_capabilities();
 
-            lsp.jsonls.setup({
-                capabilities = default_capabilities,
-                autostart = false,
-            });
+            loadDevShell({
+                name = 'json',
+                pattern = { 'json', 'yaml', '.clang-.*' },
+                pre_shell_callback = function()
+                    vim.cmd[[setlocal ts=4 sw=4 sts=0 expandtab]];
+                end,
+                language_servers = {
+                    jsonls = function()
+                        vim.lsp.start(vim.tbl_deep_extend('force', vim.lsp.config['jsonls'], {
+                            capabilities = default_capabilities,
+                        }));
+                    end,
 
-            lsp.yamlls.setup({
-                capabilities = default_capabilities,
-                autostart = false,
+                    yamlls = function()
+                        vim.lsp.start(vim.tbl_deep_extend('force', vim.lsp.config['yamlls'], {
+                            capabilities = default_capabilities,
 
-                settings = {
-                    yaml = {
-                        format = {
-                            enable = true,
-                            singleQuote = true,
-                        },
-                        schemas = {
-                            [
-                                "https://json.schemastore.org/github-workflow.json"
-                            ] = "/.github/workflows/*",
-                        },
-                    },
+                            settings = {
+                                yaml = {
+                                    format = {
+                                        enable = true,
+                                        singleQuote = true,
+                                    },
+                                    schemas = {
+                                        [
+                                            "https://json.schemastore.org/github-workflow.json"
+                                        ] = "/.github/workflows/*",
+                                    },
+                                },
+                            },
+                        }));
+                    end,
                 },
             });
           '';
diff --git a/homeManagerModules/neovim/langs/kotlin/default.nix b/homeManagerModules/neovim/langs/kotlin/default.nix
index 6903e642..9eec6d53 100644
--- a/homeManagerModules/neovim/langs/kotlin/default.nix
+++ b/homeManagerModules/neovim/langs/kotlin/default.nix
@@ -6,7 +6,6 @@
   inherit (lib) mkIf;
 
   cfg = config.programs.neovim;
-  flakeEnv = config.programs.bash.sessionVariables.FLAKE;
 in {
   config = mkIf cfg.enable {
     programs = {
@@ -15,28 +14,19 @@ in {
           # lua
           ''
             --
-            vim.api.nvim_create_autocmd({ 'FileType', 'BufEnter' }, {
+            loadDevShell({
+                name = 'kotlin',
                 pattern = { 'kotlin' },
-
-                callback = function()
+                pre_shell_callback = function()
                     vim.cmd[[setlocal ts=4 sw=4 sts=0 expandtab]];
-
-                    if (devShells['kotlin'] == nil) then
-                        devShells['kotlin'] = 1;
-
-                        require('nix-develop').nix_develop_extend({'${flakeEnv}#kotlin'}, function()
-                            vim.cmd[[LspStart]];
-                        end);
-                    end
                 end,
-            });
-
-            local lsp = require('lspconfig');
-            local default_capabilities = require('cmp_nvim_lsp').default_capabilities();
-
-            lsp.kotlin_language_server.setup({
-                capabilities = default_capabilities,
-                autostart = false,
+                language_servers = {
+                    rust_analyzer = function()
+                        vim.lsp.start(vim.tbl_deep_extend('force', vim.lsp.config['kotlin_language_server'], {
+                            capabilities = require('cmp_nvim_lsp').default_capabilities(),
+                        }));
+                    end,
+                },
             });
           '';
       };
diff --git a/homeManagerModules/neovim/langs/lua/default.nix b/homeManagerModules/neovim/langs/lua/default.nix
index c7698ebc..20fe4849 100644
--- a/homeManagerModules/neovim/langs/lua/default.nix
+++ b/homeManagerModules/neovim/langs/lua/default.nix
@@ -7,8 +7,6 @@
   inherit (lib) mkIf;
 
   cfg = config.programs.neovim;
-
-  flakeEnv = config.programs.bash.sessionVariables.FLAKE;
 in {
   config = mkIf cfg.enable {
     programs = {
@@ -21,35 +19,27 @@ in {
               # lua
               ''
                 --
-                local default_capabilities = require('cmp_nvim_lsp').default_capabilities();
-
-                vim.api.nvim_create_autocmd({ 'FileType', 'BufEnter' }, {
-                    pattern = 'lua',
-
-                    callback = function()
+                loadDevShell({
+                    name = 'lua',
+                    pattern = { 'lua' },
+                    pre_shell_callback = function()
                         vim.cmd[[setlocal ts=4 sw=4 sts=0 expandtab]];
-
-                        if (devShells['lua'] == nil) then
-                            devShells['lua'] = 1;
-
-                            require('nix-develop').nix_develop_extend({'${flakeEnv}#lua'}, function()
-                                vim.cmd[[LspStart]];
-                            end);
-                        end
                     end,
-                });
+                    language_servers = {
+                        lua_ls = function()
+                            require('lazydev').setup({
+                                library = {
+                                    -- Load luvit types when the `vim.uv` word is found
+                                    { path = '${pkgs.vimPlugins.luvit-meta}/library', words = { 'vim%.uv' } },
+                                },
+                            });
 
-                require('lazydev').setup({
-                    library = {
-                        -- Load luvit types when the `vim.uv` word is found
-                        { path = '${pkgs.vimPlugins.luvit-meta}/library', words = { 'vim%.uv' } },
+                            vim.lsp.start(vim.tbl_deep_extend('force', vim.lsp.config['lua_ls'], {
+                                capabilities = require('cmp_nvim_lsp').default_capabilities(),
+                            }));
+                        end,
                     },
                 });
-
-                require('lspconfig').lua_ls.setup({
-                    capabilities = default_capabilities,
-                    autostart = false,
-                });
               '';
           }
         ];
diff --git a/homeManagerModules/neovim/langs/markdown/default.nix b/homeManagerModules/neovim/langs/markdown/default.nix
index cb4be85a..b4b39063 100644
--- a/homeManagerModules/neovim/langs/markdown/default.nix
+++ b/homeManagerModules/neovim/langs/markdown/default.nix
@@ -12,7 +12,6 @@ self: {
   inherit (lib) concatStringsSep mkIf;
 
   cfg = config.programs.neovim;
-  flakeEnv = config.programs.bash.sessionVariables.FLAKE;
   isServer = osConfig.roles.server.sshd.enable or false;
   isDesktop = osConfig.roles.desktop.enable or false;
 
@@ -28,37 +27,29 @@ in {
           # lua
           ''
             --
-            vim.api.nvim_create_autocmd({ 'FileType', 'BufEnter' }, {
+            loadDevShell({
+                name = 'markdown',
                 pattern = { 'markdown', 'tex' },
-
-                callback = function()
+                pre_shell_callback = function()
                     vim.cmd[[setlocal ts=4 sw=4 sts=0 expandtab]];
-
-                    if (devShells['markdown'] == nil) then
-                        devShells['markdown'] = 1;
-
-                        require('nix-develop').nix_develop_extend({'${flakeEnv}#markdown'}, function()
-                            vim.cmd[[LspStart]];
-                        end);
-                    end
                 end,
-            });
+                language_servers = {
+                    texlab = function()
+                        vim.lsp.start(vim.tbl_deep_extend('force', vim.lsp.config['texlab'], {
+                            capabilities = require('cmp_nvim_lsp').default_capabilities(),
 
-            local lsp = require('lspconfig');
-
-            lsp.texlab.setup({
-                capabilities = require('cmp_nvim_lsp').default_capabilities(),
-                autostart = false,
-
-                settings = {
-                    texlab = {
-                        formatterLineLength = 100,
-                        latexFormatter = 'latexindent',
-                        latexindent = {
-                            modifyLineBreaks = false,
-                            ["local"] = '.indentconfig.yaml';
-                        },
-                    },
+                            settings = {
+                                texlab = {
+                                    formatterLineLength = 100,
+                                    latexFormatter = 'latexindent',
+                                    latexindent = {
+                                        modifyLineBreaks = false,
+                                        ["local"] = '.indentconfig.yaml';
+                                    },
+                                },
+                            },
+                        }));
+                    end,
                 },
             });
           '';
diff --git a/homeManagerModules/neovim/langs/nix-lang/default.nix b/homeManagerModules/neovim/langs/nix-lang/default.nix
index 214e9591..b09c5aeb 100644
--- a/homeManagerModules/neovim/langs/nix-lang/default.nix
+++ b/homeManagerModules/neovim/langs/nix-lang/default.nix
@@ -75,7 +75,8 @@ in {
           # lua
           ''
             --
-            require('lspconfig').nixd.setup({
+            vim.lsp.enable('nixd');
+            vim.lsp.config('nixd', {
                 capabilities = require('cmp_nvim_lsp').default_capabilities(),
 
                 filetypes = { 'nix', 'in.nix' },
diff --git a/homeManagerModules/neovim/langs/python/default.nix b/homeManagerModules/neovim/langs/python/default.nix
index aca6a649..8e37b6b2 100644
--- a/homeManagerModules/neovim/langs/python/default.nix
+++ b/homeManagerModules/neovim/langs/python/default.nix
@@ -6,7 +6,6 @@
   inherit (lib) mkIf;
 
   cfg = config.programs.neovim;
-  flakeEnv = config.programs.bash.sessionVariables.FLAKE;
 in {
   config = mkIf cfg.enable {
     programs = {
@@ -15,38 +14,31 @@ in {
           # lua
           ''
             --
-            local lsp = require('lspconfig');
             local default_capabilities = require('cmp_nvim_lsp').default_capabilities();
 
-            lsp.basedpyright.setup({
-                capabilities = default_capabilities,
-                autostart = false,
-                settings = {
-                    python = {
-                        pythonPath = vim.fn.exepath("python"),
-                    },
-                },
-            });
-
-            lsp.ruff.setup({
-                capabilities = default_capabilities,
-                autostart = false,
-            });
-
-            vim.api.nvim_create_autocmd({ 'FileType', 'BufEnter' }, {
+            loadDevShell({
+                name = 'python',
                 pattern = { 'python' },
-
-                callback = function()
+                pre_shell_callback = function()
                     vim.cmd[[setlocal ts=4 sw=4 sts=0 expandtab]];
-
-                    if (devShells['python'] == nil) then
-                        devShells['python'] = 1;
-
-                        require('nix-develop').nix_develop_extend({'${flakeEnv}#python'}, function()
-                            vim.cmd[[LspStart]];
-                        end);
-                    end
                 end,
+                language_servers = {
+                    basedpyright = function()
+                        vim.lsp.start(vim.tbl_deep_extend('force', vim.lsp.config['basedpyright'], {
+                            capabilities = default_capabilities,
+                            settings = {
+                                python = {
+                                    pythonPath = vim.fn.exepath("python"),
+                                },
+                            },
+                        }));
+                    end,
+                    ruff = function()
+                        vim.lsp.start(vim.tbl_deep_extend('force', vim.lsp.config['ruff'], {
+                            capabilities = default_capabilities,
+                        }));
+                    end,
+                },
             });
           '';
       };
diff --git a/homeManagerModules/neovim/langs/qml/default.nix b/homeManagerModules/neovim/langs/qml/default.nix
index 168b0003..ac743c91 100644
--- a/homeManagerModules/neovim/langs/qml/default.nix
+++ b/homeManagerModules/neovim/langs/qml/default.nix
@@ -6,7 +6,6 @@
   inherit (lib) mkIf;
 
   cfg = config.programs.neovim;
-  flakeEnv = config.programs.bash.sessionVariables.FLAKE;
 in {
   config = mkIf cfg.enable {
     programs = {
@@ -15,30 +14,24 @@ in {
           # lua
           ''
             --
-            vim.api.nvim_create_autocmd({ 'FileType', 'BufEnter' }, {
-                pattern = { 'qml' },
-
-                callback = function()
-                    vim.cmd[[setlocal ts=4 sw=4 sts=0 expandtab]];
-
-                    if (devShells['qml'] == nil) then
-                        devShells['qml'] = 1;
-
-                        require('nix-develop').nix_develop_extend({'${flakeEnv}#qml'}, function()
-                            vim.cmd[[LspStart]];
-                        end);
-                    end
-                end,
-            });
-
             local lsp = require('lspconfig');
             local default_capabilities = require('cmp_nvim_lsp').default_capabilities();
 
-            lsp.qmlls.setup({
-                cmd = { 'qmlls', '-E' },
-                root_dir = lsp.util.root_pattern('*.qml', '.git'),
-                capabilities = default_capabilities,
-                autostart = false,
+            loadDevShell({
+                name = 'qml',
+                pattern = { 'qml' },
+                pre_shell_callback = function()
+                    vim.cmd[[setlocal ts=4 sw=4 sts=0 expandtab]];
+                end,
+                language_servers = {
+                    qmlls = function()
+                        vim.lsp.start(vim.tbl_deep_extend('force', vim.lsp.config['qmlls'], {
+                            cmd = { 'qmlls', '-E' },
+                            root_dir = lsp.util.root_pattern('*.qml', '.git'),
+                            capabilities = default_capabilities,
+                        }));
+                    end,
+                },
             });
           '';
       };
diff --git a/homeManagerModules/neovim/langs/rust/default.nix b/homeManagerModules/neovim/langs/rust/default.nix
index a049b7eb..8326be38 100644
--- a/homeManagerModules/neovim/langs/rust/default.nix
+++ b/homeManagerModules/neovim/langs/rust/default.nix
@@ -6,7 +6,6 @@
   inherit (lib) mkIf;
 
   cfg = config.programs.neovim;
-  flakeEnv = config.programs.bash.sessionVariables.FLAKE;
 in {
   config = mkIf cfg.enable {
     programs = {
@@ -15,25 +14,19 @@ in {
           # lua
           ''
             --
-            vim.api.nvim_create_autocmd({ 'FileType', 'BufEnter' }, {
+            loadDevShell({
+                name = 'rust',
                 pattern = { 'rust' },
-
-                callback = function()
+                pre_shell_callback = function()
                     vim.cmd[[setlocal ts=4 sw=4 sts=0 expandtab]];
-
-                    if (devShells['rust'] == nil) then
-                        devShells['rust'] = 1;
-
-                        require('nix-develop').nix_develop_extend({'${flakeEnv}#rust'}, function()
-                            vim.cmd[[LspStart]];
-                        end);
-                    end
                 end,
-            });
-
-            require('lspconfig').rust_analyzer.setup({
-                capabilities = require('cmp_nvim_lsp').default_capabilities(),
-                autostart = false,
+                language_servers = {
+                    rust_analyzer = function()
+                        vim.lsp.start(vim.tbl_deep_extend('force', vim.lsp.config['rust_analyzer'], {
+                            capabilities = require('cmp_nvim_lsp').default_capabilities(),
+                        }));
+                    end,
+                },
             });
           '';
       };
diff --git a/homeManagerModules/neovim/langs/web/default.nix b/homeManagerModules/neovim/langs/web/default.nix
index 17941212..c1cfea8e 100644
--- a/homeManagerModules/neovim/langs/web/default.nix
+++ b/homeManagerModules/neovim/langs/web/default.nix
@@ -10,7 +10,6 @@ self: {
   inherit (lib) mkIf;
 
   cfg = config.programs.neovim;
-  flakeEnv = config.programs.bash.sessionVariables.FLAKE;
 in {
   config = mkIf cfg.enable {
     programs = {
@@ -21,21 +20,139 @@ in {
           # lua
           ''
             --
-            local lsp = require('lspconfig');
-            local tsserver = require('typescript-tools');
             local default_capabilities = require('cmp_nvim_lsp').default_capabilities();
 
-            local loadDevShell = function()
-                if (devShells['web'] == nil) then
-                    devShells['web'] = 1;
+            local eslintConfig = function()
+                local config = vim.lsp.config['eslint'];
+                config.before_init = nil;
 
-                    require('nix-develop').nix_develop_extend({'${flakeEnv}#web'}, function()
-                        vim.cmd[[LspStart]];
-                    end);
-                end
+                vim.lsp.start(vim.tbl_deep_extend('force', config, {
+                    capabilities = default_capabilities,
+
+                    -- auto-save
+                    on_attach = function(client, bufnr)
+                        vim.lsp.config['eslint'].on_attach(client, bufnr);
+
+                        vim.api.nvim_create_autocmd('BufWritePre', {
+                            buffer = bufnr,
+                            command = 'LspEslintFixAll',
+                        });
+                    end,
+
+                    settings = {
+                        validate = 'on',
+                        packageManager = 'npm',
+                        useESLintClass = true,
+                        useFlatConfig = true,
+                        experimental = {
+                            useFlatConfig = true,
+                        },
+                        codeAction = {
+                            disableRuleComment = {
+                                enable = true,
+                                location = 'separateLine'
+                            },
+                            showDocumentation = {
+                                enable = true,
+                            },
+                        },
+                        codeActionOnSave = {
+                            mode = 'all',
+                            rules = {},
+                        },
+                        format = true,
+                        quiet = false,
+                        onIgnoredFiles = 'off',
+                        rulesCustomizations = {},
+                        run = 'onType',
+                        problems = {
+                            shortenToSingleLine = false,
+                        },
+                        nodePath = "",
+                        workingDirectory = {
+                            mode = 'location',
+                        },
+                    },
+                }));
             end;
 
             vim.api.nvim_create_autocmd({ 'FileType', 'BufEnter' }, {
+                pattern = 'scss',
+                command = 'setlocal iskeyword+=@-@',
+            });
+            local cssConfig = function()
+                vim.lsp.start(vim.tbl_deep_extend('force', vim.lsp.config['cssls'], {
+                    capabilities = default_capabilities,
+
+                    settings = {
+                        css = {
+                            validate = false,
+                        },
+                        less = {
+                            validate = false,
+                        },
+                        scss = {
+                            validate = false,
+                        },
+                    },
+                }));
+
+                vim.lsp.start(vim.tbl_deep_extend('force', vim.lsp.config['somesass_ls'], {
+                    capabilities = default_capabilities,
+
+                    settings = {
+                        somesass = {
+                            scss = {
+                                completion = {
+                                    suggestFromUseOnly = true,
+                                },
+                            },
+                        },
+                    };
+                }));
+            end;
+
+            local htmlConfig = function()
+                local html_caps = default_capabilities;
+                html_caps.textDocument.completion.completionItem.snippetSupport = true;
+
+                vim.lsp.start(vim.tbl_deep_extend('force', vim.lsp.config['html'], {
+                    capabilities = html_caps,
+                    autostart = false,
+
+                    settings = {
+                        configurationSection = { "html", "css", "javascript" },
+                        embeddedLanguages = {
+                            css = true,
+                            javascript = true,
+                        },
+                        provideFormatter = true,
+                        tabSize = 4,
+                        insertSpaces = true,
+                        indentEmptyLines = false,
+                        wrapAttributes = 'auto',
+                        wrapAttributesIndentSize = 4,
+                        endWithNewline = true,
+                    },
+                }));
+            end;
+
+            local typescriptConfig = function()
+                vim.lsp.start(vim.tbl_deep_extend('force', vim.lsp.config['ts_ls'], {
+                    capabilities = default_capabilities,
+
+                    handlers = {
+                        -- format error code with better error message
+                        ['textDocument/publishDiagnostics'] = function(err, result, ctx, config)
+                            require('ts-error-translator').translate_diagnostics(err, result, ctx, config)
+                            vim.lsp.diagnostic.on_publish_diagnostics(err, result, ctx, config)
+                        end,
+                    },
+                }));
+            end;
+
+            loadDevShell({
+                name = 'web',
                 pattern = {
                     'javascript',
                     'javascriptreact',
@@ -45,148 +162,21 @@ in {
                     'typescript.tsx',
                     'css',
                     'scss',
+                    'html',
                 },
-
-                callback = function()
+                pre_shell_callback = function()
                     vim.cmd[[setlocal ts=4 sw=4 sts=0 expandtab]];
-
-                    loadDevShell();
                 end,
-            });
-
-            vim.api.nvim_create_autocmd({ 'FileType', 'BufEnter' }, {
-                pattern = 'html',
-
-                callback = function()
-                    vim.cmd[[setlocal ts=4 sw=4 sts expandtab]];
-
-                    loadDevShell();
-                end,
-            });
-
-            vim.api.nvim_create_autocmd({ 'FileType', 'BufEnter' }, {
-                pattern = 'scss',
-                command = 'setlocal iskeyword+=@-@',
-            });
-
-            tsserver.setup({
-                capabilities = default_capabilities,
-                autostart = false,
-
-                handlers = {
-                    -- format error code with better error message
-                    ['textDocument/publishDiagnostics'] = function(err, result, ctx, config)
-                        require('ts-error-translator').translate_diagnostics(err, result, ctx, config)
-                        vim.lsp.diagnostic.on_publish_diagnostics(err, result, ctx, config)
-                    end,
-                },
-            });
-
-            lsp.eslint.setup({
-                capabilities = default_capabilities,
-                autostart = false,
-
-                -- auto-save
-                on_attach = function(client, bufnr)
-                    vim.api.nvim_create_autocmd('BufWritePre', {
-                        buffer = bufnr,
-                        command = 'EslintFixAll',
-                    });
-                end,
-
-                settings = {
-                    validate = 'on',
-                    packageManager = 'npm',
-                    useESLintClass = true,
-                    useFlatConfig = true,
-                    experimental = {
-                        useFlatConfig = true,
-                    },
-                    codeAction = {
-                        disableRuleComment = {
-                            enable = true,
-                            location = 'separateLine'
-                        },
-                        showDocumentation = {
-                            enable = true,
-                        },
-                    },
-                    codeActionOnSave = {
-                        mode = 'all',
-                        rules = {},
-                    },
-                    format = true,
-                    quiet = false,
-                    onIgnoredFiles = 'off',
-                    rulesCustomizations = {},
-                    run = 'onType',
-                    problems = {
-                        shortenToSingleLine = false,
-                    },
-                    nodePath = "",
-                    workingDirectory = {
-                        mode = 'location',
-                    },
-                },
-            });
-
-            lsp.cssls.setup({
-                capabilities = default_capabilities,
-                autostart = false,
-
-                settings = {
-                    css = {
-                        validate = false,
-                    },
-                    less = {
-                        validate = false,
-                    },
-                    scss = {
-                        validate = false,
-                    },
-                },
-            });
-
-            lsp.somesass_ls.setup({
-                capabilities = default_capabilities,
-                autostart = false,
-            });
-            lsp.somesass_ls.manager.config.settings = {
-                somesass = {
-                    scss = {
-                        completion = {
-                            suggestFromUseOnly = true,
-                        },
-                    },
-                },
-            };
-
-            local html_caps = default_capabilities;
-            html_caps.textDocument.completion.completionItem.snippetSupport = true;
-
-            lsp.html.setup({
-                capabilities = html_caps,
-                autostart = false,
-
-                settings = {
-                    configurationSection = { "html", "css", "javascript" },
-                    embeddedLanguages = {
-                        css = true,
-                        javascript = true,
-                    },
-                    provideFormatter = true,
-                    tabSize = 4,
-                    insertSpaces = true,
-                    indentEmptyLines = false,
-                    wrapAttributes = 'auto',
-                    wrapAttributesIndentSize = 4,
-                    endWithNewline = true,
+                language_servers = {
+                    cssls = cssConfig,
+                    eslint = eslintConfig,
+                    html = htmlConfig,
+                    ts_ls = typescriptConfig,
                 },
             });
           '';
 
         plugins = [
-          pkgs.vimPlugins.typescript-tools-nvim
           (buildPlugin "ts-error-translator" vimplugin-ts-error-translator-src)
 
           {
diff --git a/homeManagerModules/neovim/langs/web/shell.nix b/homeManagerModules/neovim/langs/web/shell.nix
index bca918e4..9171015a 100644
--- a/homeManagerModules/neovim/langs/web/shell.nix
+++ b/homeManagerModules/neovim/langs/web/shell.nix
@@ -15,6 +15,8 @@ mkShell {
 
     nodePackages.npm
 
+    nodePackages.typescript-language-server
+
     some-sass-language-server
   ];
 }