Browse Source

Merge branch 'rolling' of github.com:ChristianChiarulli/LunarVim

christianchiarulli 3 years ago
parent
commit
ed5559d259
63 changed files with 2304 additions and 1723 deletions
  1. 2 1
      .github/workflows/format.yaml
  2. 19 18
      .github/workflows/install.yaml
  3. 10 13
      .github/workflows/lint.yaml
  4. 29 0
      .pre-commit-config.yaml
  5. 12 2
      CONTRIBUTING.md
  6. 14 4
      README.md
  7. 1 0
      ftdetect/nix.lua
  8. 1 0
      ftplugin/elixir.lua
  9. 1 0
      ftplugin/fortran.lua
  10. 2 0
      ftplugin/netrw.lua
  11. 1 0
      ftplugin/nix.lua
  12. 23 40
      init.lua
  13. 229 330
      lua/config/defaults.lua
  14. 49 0
      lua/config/init.lua
  15. 1 3
      lua/config/settings.lua
  16. 7 1
      lua/core/autocmds.lua
  17. 57 42
      lua/core/autopairs.lua
  18. 5 0
      lua/core/bufferline.lua
  19. 29 0
      lua/core/builtins/init.lua
  20. 31 0
      lua/core/comment.lua
  21. 21 8
      lua/core/compe.lua
  22. 7 6
      lua/core/dap.lua
  23. 21 21
      lua/core/dashboard.lua
  24. 0 320
      lua/core/galaxyline.lua
  25. 50 46
      lua/core/gitsigns.lua
  26. 120 156
      lua/core/info.lua
  27. 48 18
      lua/core/log.lua
  28. 19 0
      lua/core/lspinstall.lua
  29. 16 0
      lua/core/lualine/colors.lua
  30. 152 0
      lua/core/lualine/components.lua
  31. 17 0
      lua/core/lualine/conditions.lua
  32. 47 0
      lua/core/lualine/init.lua
  33. 137 0
      lua/core/lualine/styles.lua
  34. 27 0
      lua/core/lualine/utils.lua
  35. 46 52
      lua/core/nvimtree.lua
  36. 51 0
      lua/core/project.lua
  37. 0 15
      lua/core/rooter.lua
  38. 0 19
      lua/core/status_colors.lua
  39. 56 12
      lua/core/telescope.lua
  40. 9 13
      lua/core/terminal.lua
  41. 6 0
      lua/core/treesitter.lua
  42. 22 14
      lua/core/which-key.lua
  43. 62 0
      lua/interface/popup.lua
  44. 79 0
      lua/interface/text.lua
  45. 11 3
      lua/keymappings.lua
  46. 23 16
      lua/lsp/init.lua
  47. 0 142
      lua/lsp/null-ls.lua
  48. 74 0
      lua/lsp/null-ls/formatters.lua
  49. 44 0
      lua/lsp/null-ls/init.lua
  50. 74 0
      lua/lsp/null-ls/linters.lua
  51. 47 0
      lua/lsp/null-ls/services.lua
  52. 28 0
      lua/lsp/utils.lua
  53. 4 7
      lua/plugin-loader.lua
  54. 22 63
      lua/plugins.lua
  55. 10 57
      lua/utils/init.lua
  56. 0 9
      utils/bin/install-latest-neovim
  57. 4 1
      utils/bin/lvim
  58. 29 1
      utils/installer/config.example-no-ts.lua
  59. 29 1
      utils/installer/config.example.lua
  60. 83 0
      utils/installer/install-neovim-from-release
  61. 244 227
      utils/installer/install.sh
  62. 38 38
      utils/installer/install_stylua.sh
  63. 4 4
      utils/installer/uninstall.sh

+ 2 - 1
.github/workflows/format.yaml

@@ -37,7 +37,8 @@ jobs:
         run: |
           GO111MODULE=on go get mvdan.cc/sh/v3/cmd/shfmt
 
+      # https://google.github.io/styleguide/shellguide.html
       - name: Check formatting
         run: |
-          shfmt -l -d .
+          shfmt -f . | grep -v jdtls | xargs shfmt -i 2 -ci -l -d
     

+ 19 - 18
.github/workflows/install.yaml

@@ -1,11 +1,11 @@
 name: install
 on:
   push:
-    branches: '**'
+    branches: "**"
   pull_request:
     branches:
-      - 'master'
-      - 'rolling'
+      - "master"
+      - "rolling"
 
 jobs:
   unixish:
@@ -22,31 +22,32 @@ jobs:
     if: github.event.pull_request.draft == false
     steps:
       - uses: actions/checkout@v2
-      
-      - name: Install dependencies for Linux
-        if: matrix.os == 'linux'
-        run: |
-          sudo add-apt-repository ppa:neovim-ppa/unstable -y
-          sudo apt-get update
-          sudo apt-get install neovim -y
 
-      - name: Install dependencies for OSX
+      # sha256sum is not available by default
+      - name: Installl dependencies for OSX
         if: matrix.os == 'osx'
         run: |
-          brew update >/dev/null
-          brew install neovim
+          echo "HOMEBREW_NO_AUTO_UPDATE=1" >> $GITHUB_ENV
+          echo "$HOME/.local/bin" >> $GITHUB_PATH
+          brew install coreutils
+
+      - name: Install neovim binary
+        run: |
+          bash ./utils/installer/install-neovim-from-release
 
       - name: Install LunarVim
         timeout-minutes: 4
         run: |
-          bash ./utils/installer/install.sh --testing
+          mkdir -p "$HOME"/.local/share/lunarvim/lvim
+          mkdir -p "$HOME"/.config/lvim
+          ln -s "$PWD"/* "$HOME"/.local/share/lunarvim/lvim/.
+          bash ./utils/installer/install.sh
 
       - name: Test LunarVim PackerCompile
-        run: if lvim --headless +PackerCompile -c ':qall' 2>&1|grep -q 'Error'; then false; fi
+        run: if "$HOME"/.local/bin/lvim --headless +PackerCompile -c ':qall' 2>&1|grep -q 'Error'; then false; fi
 
       - name: Test LunarVim Health
-        run: if lvim --headless +checkhealth -c ':qall' 2>&1|grep -q 'Error'; then false; fi
-
+        run: if "$HOME"/.local/bin/lvim --headless +checkhealth -c ':qall' 2>&1|grep -q 'Error'; then false; fi
 #   freebsd:
 #     runs-on: macos-latest
 #     if: github.event.pull_request.draft == false
@@ -54,7 +55,7 @@ jobs:
 #     name: "FreeBSD macos-latest"
 #     steps:
 #       - uses: actions/checkout@v2
-      
+
 #       - name: Install dependencies for FreeBSD
 #         uses: vmactions/freebsd-vm@v0.1.5
 #         with:

+ 10 - 13
.github/workflows/lint.yaml

@@ -22,17 +22,14 @@ jobs:
       
       - name: Run luacheck
         run: luacheck *.lua lua/
-    
-  shell-linter:
-    name: "Linting with shellcheck"
-    runs-on: ubuntu-20.04
+
+  shellcheck:
+    name: Shellcheck
+    runs-on: ubuntu-latest
     steps:
-      - uses: actions/checkout@v2
-      
-      - name: Use shellcheck
-        run: sudo apt-get install shellcheck
-      
-      - name: Run shellcheck
-        run:  |
-          pwd
-          shellcheck $(find . -name "*.sh")
+    - uses: actions/checkout@v2
+    - name: Run ShellCheck
+      uses: ludeeus/action-shellcheck@master
+      with:
+       scandir: './utils'
+       ignore: 'bin'

+ 29 - 0
.pre-commit-config.yaml

@@ -0,0 +1,29 @@
+repos:
+  - repo: local
+    hooks:
+      - id: shfmt
+        name: shfmt
+        minimum_pre_commit_version: 2.4.0
+        language: system
+        types: [shell]
+        entry: bash
+        args: [-c, "shfmt -f $(git rev-parse --show-toplevel) | grep -v jdtls | xargs shfmt -i=2 -ci -w"]
+      - id: shellcheck
+        name: shellcheck
+        language: system
+        types: [shell]
+        entry: bash
+        args:
+          [-c, "shfmt -f $(git rev-parse --show-toplevel) | grep -v jdtls | xargs shellcheck"]
+      - id: stylua
+        name: StyLua
+        language: rust
+        entry: stylua
+        types: [lua]
+        args: ['-']
+      - id: luacheck
+        name: luacheck
+        language: system
+        entry: luacheck
+        types: [lua]
+        args: [.]

+ 12 - 2
CONTRIBUTING.md

@@ -17,9 +17,19 @@ One of the best ways to begin contributing in a meaningful way is by helping fin
 
 ## Setting up development tools
 
-1. Install [stylua](https://github.com/johnnymorganz/stylua#installation)
+### For editing Lua files
 
-2. Install [shfmt](https://github.com/mvdan/sh#shfmt)
+1. Formatter: [stylua](https://github.com/johnnymorganz/stylua#installation).
+2. Linter:  [luacheck](https://github.com/luarocks/luacheck).
+
+### For editing shell scripts
+
+1. Formatter: [shfmt](https://github.com/mvdan/sh#shfmt).
+2. Linter: [shellcheck](https://github.com/koalaman/shellcheck).
+
+### (Optional)
+
+Install [pre-commit](https://github.com/pre-commit/pre-commit) which will run all linters and formatters for you as a pre-commit-hook.
 
 ## Some Guidelines
 

+ 14 - 4
README.md

@@ -28,10 +28,20 @@ Make sure you have the newest version of Neovim (0.5).
 bash <(curl -s https://raw.githubusercontent.com/lunarvim/lunarvim/master/utils/installer/install.sh)
 ```
 
-### Installing
-The following command installs LunarVim.  Change `LVBRANCH` to the branch you'd like to install.  `master` for the stable branch and `rolling` for the latest changes.
+### Customizing the installation
+
+The following options are supported by setting environment variables:
+- `"$LV_REMOTE"`            Select a different LunarVim remote [default: 'lunarvim/lunarvim.git']
+- `"$LV_BRANCH"`            Select LunarVim's branch [default: 'rolling']
+- `"$INSTALL_PREFIX"`       Select LunarVim's install prefix [default: `'$HOME/.local'`]
+- `"$LUNARVIM_RUNTIME_DIR"` Select LunarVim's runtime directory [default: `'$HOME/.local/share/lunarvim'`]
+- `"$LUNARVIM_CONFIG_DIR"`  Select LunarVim's configuration directory [default: `'$HOME/.config/lvim'`]
+
+Putting it all together
+
 ``` bash
-LVBRANCH=rolling bash <(curl -s https://raw.githubusercontent.com/lunarvim/lunarvim/rolling/utils/installer/install.sh)
+curl -LSs https://raw.githubusercontent.com/lunarvim/lunarvim/rolling/utils/installer/install.sh -O install.sh
+INSTALL_PREFIX=/tmp/t1 LUNARVIM_CONFIG_DIR=/tmp/t2 LUNARVIM_RUNTIME_DIR=/tmp/t3 bash ./install.sh
 ```
 
 ### BREAKING CHANGE on rolling and master branches
@@ -94,7 +104,7 @@ lvim.keys.normal_mode["<C-s>"] = ":w<cr>"
 -- lvim.keys.insert_mode["po"] = {'<ESC>', { noremap = true }}
 
 -- Use which-key to add extra bindings with the leader-key prefix
--- lvim.builtin.which_key.mappings["P"] = { "<cmd>lua require'telescope'.extensions.project.project{}<CR>", "Projects" }
+-- lvim.builtin.which_key.mappings["P"] = { "<cmd>Telescope projects<CR>", "Projects" }
 -- lvim.builtin.which_key.mappings["t"] = {
 --   name = "+Trouble",
 --   r = { "<cmd>Trouble lsp_references<cr>", "References" },

+ 1 - 0
ftdetect/nix.lua

@@ -0,0 +1 @@
+vim.cmd [[ au BufRead,BufNewFile *.nix set filetype=nix ]]

+ 1 - 0
ftplugin/elixir.lua

@@ -1,4 +1,5 @@
 require("lsp").setup "elixir"
+vim.api.nvim_buf_set_option(0, "commentstring", "# %s")
 
 -- TODO: do we need this?
 -- needed for the LSP to recognize elixir files (alternatively just use elixir-editors/vim-elixir)

+ 1 - 0
ftplugin/fortran.lua

@@ -0,0 +1 @@
+require("lsp").setup "fortran"

+ 2 - 0
ftplugin/netrw.lua

@@ -0,0 +1,2 @@
+vim.cmd [[nmap <buffer> h -]]
+vim.cmd [[nmap <buffer> l <cr>]]

+ 1 - 0
ftplugin/nix.lua

@@ -0,0 +1 @@
+require("lsp").setup "nix"

+ 23 - 40
init.lua

@@ -1,50 +1,33 @@
-vim.cmd [[
-  set packpath-=~/.config/nvim
-  set packpath-=~/.config/nvim/after
-  set packpath-=~/.local/share/nvim/site
-  set packpath^=~/.local/share/lunarvim/site
-  set packpath^=~/.config/lvim
+-- {{{ Bootstrap
+local home_dir = vim.loop.os_homedir()
 
-  set runtimepath-=~/.config/nvim
-  set runtimepath-=~/.config/nvim/after
-  set runtimepath+=~/.config/lvim
-  set runtimepath^=~/.local/share/lunarvim/lvim/after
-]]
--- vim.opt.rtp:append() instead of vim.cmd ?
+vim.opt.rtp:append(home_dir .. "/.local/share/lunarvim/lvim")
 
-local function file_exists(name)
-  local f = io.open(name, "r")
-  if f ~= nil then
-    io.close(f)
-    return true
-  else
-    return false
-  end
-end
+vim.opt.rtp:remove(home_dir .. "/.config/nvim")
+vim.opt.rtp:remove(home_dir .. "/.config/nvim/after")
+vim.opt.rtp:append(home_dir .. "/.config/lvim")
+vim.opt.rtp:append(home_dir .. "/.config/lvim/after")
 
-local lvim_path = os.getenv "HOME" .. "/.config/lvim/"
-USER_CONFIG_PATH = lvim_path .. "config.lua"
-local config_exist = file_exists(USER_CONFIG_PATH)
-if not config_exist then
-  USER_CONFIG_PATH = lvim_path .. "lv-config.lua"
-  print "Rename ~/.config/lvim/lv-config.lua to config.lua"
-end
+vim.opt.rtp:remove(home_dir .. "/.local/share/nvim/site")
+vim.opt.rtp:remove(home_dir .. "/.local/share/nvim/site/after")
+vim.opt.rtp:append(home_dir .. "/.local/share/lunarvim/site")
+vim.opt.rtp:append(home_dir .. "/.local/share/lunarvim/site/after")
 
-require "default-config"
-local autocmds = require "core.autocmds"
-require("settings").load_options()
+-- TODO: we need something like this: vim.opt.packpath = vim.opt.rtp
+vim.cmd [[let &packpath = &runtimepath]]
+-- }}}
 
-local status_ok, error = pcall(vim.cmd, "luafile " .. USER_CONFIG_PATH)
-if not status_ok then
-  print("something is wrong with your " .. USER_CONFIG_PATH)
-  print(error)
-end
-require("settings").load_commands()
-autocmds.define_augroups(lvim.autocommands)
+local config = require "config"
+config:init()
+config:load()
 
 local plugins = require "plugins"
 local plugin_loader = require("plugin-loader").init()
 plugin_loader:load { plugins, lvim.plugins }
+
+local Log = require "core.log"
+Log:info "Starting LunarVim"
+
 vim.g.colors_name = lvim.colorscheme -- Colorscheme must get called after plugins are loaded or it will break new installs.
 vim.cmd("colorscheme " .. lvim.colorscheme)
 
@@ -58,13 +41,13 @@ require("lsp").config()
 local null_status_ok, null_ls = pcall(require, "null-ls")
 if null_status_ok then
   null_ls.config {}
-  require("lspconfig")["null-ls"].setup {}
+  require("lspconfig")["null-ls"].setup(lvim.lsp.null_ls.setup)
 end
 
 local lsp_settings_status_ok, lsp_settings = pcall(require, "nlspsettings")
 if lsp_settings_status_ok then
   lsp_settings.setup {
-    config_home = os.getenv "HOME" .. "/.config/lvim/lsp-settings",
+    config_home = home_dir .. "/.config/lvim/lsp-settings",
   }
 end
 

File diff suppressed because it is too large
+ 229 - 330
lua/config/defaults.lua


+ 49 - 0
lua/config/init.lua

@@ -0,0 +1,49 @@
+local home_dir = vim.loop.os_homedir()
+local M = {
+  path = string.format("%s/.config/lvim/config.lua", home_dir),
+}
+
+--- Initialize lvim default configuration
+-- Define lvim global variable
+function M:init()
+  local utils = require "utils"
+
+  require "config.defaults"
+
+  local builtins = require "core.builtins"
+  builtins.config(self)
+
+  local settings = require "config.settings"
+  settings.load_options()
+
+  -- Fallback config.lua to lv-config.lua
+  if not utils.is_file(self.path) then
+    local lv_config = self.path:gsub("config.lua$", "lv-config.lua")
+    print(self.path, "not found, falling back to", lv_config)
+
+    self.path = lv_config
+  end
+end
+
+--- Override the configuration with a user provided one
+-- @param config_path The path to the configuration overrides
+function M:load(config_path)
+  local autocmds = require "core.autocmds"
+
+  config_path = config_path or self.path
+  local ok, err = pcall(vim.cmd, "luafile " .. config_path)
+  if not ok then
+    print("Invalid configuration", config_path)
+    print(err)
+    return
+  end
+
+  self.path = config_path
+
+  autocmds.define_augroups(lvim.autocommands)
+
+  local settings = require "config.settings"
+  settings.load_commands()
+end
+
+return M

+ 1 - 3
lua/settings.lua → lua/config/settings.lua

@@ -1,8 +1,6 @@
 local M = {}
 
 M.load_options = function()
-  local opt = vim.opt
-
   local default_options = {
     backup = false, -- creates a backup file
     clipboard = "unnamedplus", -- allows neovim to access the system clipboard
@@ -51,7 +49,7 @@ M.load_options = function()
 
   ---  SETTINGS  ---
 
-  opt.shortmess:append "c"
+  vim.opt.shortmess:append "c"
 
   for k, v in pairs(default_options) do
     vim.opt[k] = v

+ 7 - 1
lua/core/autocmds.lua

@@ -1,4 +1,5 @@
 local autocommands = {}
+local config = require "config"
 
 lvim.autocommands = {
   _general_settings = {
@@ -7,6 +8,11 @@ lvim.autocommands = {
       "*",
       "lua require('utils.ft').do_filetype(vim.fn.expand(\"<amatch>\"))",
     },
+    {
+      "FileType",
+      "qf",
+      "nnoremap <silent> <buffer> q :q<CR>",
+    },
     {
       "TextYankPost",
       "*",
@@ -32,7 +38,7 @@ lvim.autocommands = {
       "*",
       "setlocal formatoptions-=c formatoptions-=r formatoptions-=o",
     },
-    { "BufWritePost", USER_CONFIG_PATH, "lua require('utils').reload_lv_config()" },
+    { "BufWritePost", config.path, "lua require('utils').reload_lv_config()" },
     {
       "FileType",
       "qf",

+ 57 - 42
lua/core/autopairs.lua

@@ -1,54 +1,69 @@
--- if not package.loaded['nvim-autopairs'] then
---   return
--- end
-local Log = require "core.log"
-local status_ok, _ = pcall(require, "nvim-autopairs")
-if not status_ok then
-  Log:get_default().error "Failed to load autopairs"
-  return
+local M = {}
+
+function M.config()
+  lvim.builtin.autopairs = {
+    active = true,
+    on_config_done = nil,
+    ---@usage  map <CR> on insert mode
+    map_cr = true,
+    ---@usage auto insert after select function or method item
+    -- NOTE: This should be wrapped into a function so that it is re-evaluated when opening new files
+    map_complete = vim.bo.filetype ~= "tex",
+    ---@usage check treesitter
+    check_ts = true,
+    ts_config = {
+      lua = { "string" },
+      javascript = { "template_string" },
+      java = false,
+    },
+  }
 end
-local npairs = require "nvim-autopairs"
-local Rule = require "nvim-autopairs.rule"
 
--- skip it, if you use another global object
-_G.MUtils = {}
+M.setup = function()
+  -- skip it, if you use another global object
+  _G.MUtils = {}
+  local autopairs = require "nvim-autopairs"
+  local Rule = require "nvim-autopairs.rule"
 
-vim.g.completion_confirm_key = ""
-MUtils.completion_confirm = function()
-  if vim.fn.pumvisible() ~= 0 then
-    if vim.fn.complete_info()["selected"] ~= -1 then
-      return vim.fn["compe#confirm"](npairs.esc "<cr>")
+  vim.g.completion_confirm_key = ""
+  MUtils.completion_confirm = function()
+    if vim.fn.pumvisible() ~= 0 then
+      if vim.fn.complete_info()["selected"] ~= -1 then
+        return vim.fn["compe#confirm"](autopairs.esc "<cr>")
+      else
+        return autopairs.esc "<cr>"
+      end
     else
-      return npairs.esc "<cr>"
+      return autopairs.autopairs_cr()
     end
-  else
-    return npairs.autopairs_cr()
   end
-end
 
-if package.loaded["compe"] then
-  local map_complete_optional = vim.bo.filetype ~= "tex"
-  require("nvim-autopairs.completion.compe").setup {
-    map_cr = true, --  map <CR> on insert mode
-    map_complete = map_complete_optional, -- it will auto insert `(` after select function or method item
+  if package.loaded["compe"] then
+    require("nvim-autopairs.completion.compe").setup {
+      map_cr = lvim.builtin.autopairs.map_cr,
+      map_complete = lvim.builtin.autopairs.map_complete,
+    }
+  end
+
+  autopairs.setup {
+    check_ts = lvim.builtin.autopairs.check_ts,
+    ts_config = lvim.builtin.autopairs.ts_config,
   }
-end
 
-npairs.setup {
-  check_ts = true,
-  ts_config = {
-    lua = { "string" }, -- it will not add pair on that treesitter node
-    javascript = { "template_string" },
-    java = false, -- don't check treesitter on java
-  },
-}
+  require("nvim-treesitter.configs").setup { autopairs = { enable = true } }
+
+  local ts_conds = require "nvim-autopairs.ts-conds"
 
-require("nvim-treesitter.configs").setup { autopairs = { enable = true } }
+  -- TODO: can these rules be safely added from "config.lua" ?
+  -- press % => %% is only inside comment or string
+  autopairs.add_rules {
+    Rule("%", "%", "lua"):with_pair(ts_conds.is_ts_node { "string", "comment" }),
+    Rule("$", "$", "lua"):with_pair(ts_conds.is_not_ts_node { "function" }),
+  }
 
-local ts_conds = require "nvim-autopairs.ts-conds"
+  if lvim.builtin.autopairs.on_config_done then
+    lvim.builtin.autopairs.on_config_done(autopairs)
+  end
+end
 
--- press % => %% is only inside comment or string
-npairs.add_rules {
-  Rule("%", "%", "lua"):with_pair(ts_conds.is_ts_node { "string", "comment" }),
-  Rule("$", "$", "lua"):with_pair(ts_conds.is_not_ts_node { "function" }),
-}
+return M

+ 5 - 0
lua/core/bufferline.lua

@@ -3,6 +3,7 @@ local M = {}
 M.config = function()
   lvim.builtin.bufferline = {
     active = true,
+    on_config_done = nil,
     keymap = {
       normal_mode = {
         ["<S-l>"] = ":BufferNext<CR>",
@@ -15,6 +16,10 @@ end
 M.setup = function()
   local keymap = require "keymappings"
   keymap.append_to_defaults(lvim.builtin.bufferline.keymap)
+
+  if lvim.builtin.bufferline.on_config_done then
+    lvim.builtin.bufferline.on_config_done()
+  end
 end
 
 return M

+ 29 - 0
lua/core/builtins/init.lua

@@ -0,0 +1,29 @@
+local M = {}
+
+local builtins = {
+  "keymappings",
+  "core.which-key",
+  "core.gitsigns",
+  "core.compe",
+  "core.dashboard",
+  "core.dap",
+  "core.terminal",
+  "core.telescope",
+  "core.treesitter",
+  "core.nvimtree",
+  "core.project",
+  "core.bufferline",
+  "core.autopairs",
+  "core.comment",
+  "core.lspinstall",
+  "core.lualine",
+}
+
+function M.config(config)
+  for _, builtin_path in ipairs(builtins) do
+    local builtin = require(builtin_path)
+    builtin.config(config)
+  end
+end
+
+return M

+ 31 - 0
lua/core/comment.lua

@@ -0,0 +1,31 @@
+local M = {}
+
+function M.config()
+  lvim.builtin.comment = {
+    active = true,
+    on_config_done = nil,
+    -- Linters prefer comment and line to have a space in between markers
+    marker_padding = true,
+    -- should comment out empty or whitespace only lines
+    comment_empty = false,
+    -- Should key mappings be created
+    create_mappings = true,
+    -- Normal mode mapping left hand side
+    line_mapping = "gcc",
+    -- Visual/Operator mapping left hand side
+    operator_mapping = "gc",
+    -- Hook function to call before commenting takes place
+    hook = nil,
+  }
+end
+
+function M.setup()
+  local nvim_comment = require "nvim_comment"
+
+  nvim_comment.setup(lvim.builtin.comment)
+  if lvim.builtin.comment.on_config_done then
+    lvim.builtin.comment.on_config_done(nvim_comment)
+  end
+end
+
+return M

+ 21 - 8
lua/core/compe.lua

@@ -1,8 +1,9 @@
 local M = {}
-local Log = require "core.log"
+
 M.config = function()
   lvim.builtin.compe = {
-    enabled = true,
+    active = true,
+    on_config_done = nil,
     autocomplete = true,
     debug = false,
     min_length = 1,
@@ -61,11 +62,7 @@ end
 M.setup = function()
   vim.g.vsnip_snippet_dir = lvim.vsnip_dir
 
-  local status_ok, compe = pcall(require, "compe")
-  if not status_ok then
-    Log:get_default().error "Failed to load compe"
-    return
-  end
+  local compe = require "compe"
 
   compe.setup(lvim.builtin.compe)
 
@@ -82,6 +79,17 @@ M.setup = function()
     end
   end
 
+  local is_emmet_active = function()
+    local clients = vim.lsp.buf_get_clients()
+
+    for _, client in pairs(clients) do
+      if client.name == "emmet_ls" then
+        return true
+      end
+    end
+    return false
+  end
+
   -- Use (s-)tab to:
   --- move to prev/next item in completion menuone
   --- jump to prev/next snippet's placeholder
@@ -92,8 +100,9 @@ M.setup = function()
       return t "<Plug>(vsnip-jump-next)"
     elseif check_back_space() then
       return t "<Tab>"
+    elseif is_emmet_active() then
+      return vim.fn["compe#complete"]()
     else
-      -- return vim.fn["compe#complete"]() -- < use this if you want <tab> to always offer completion
       return t "<Tab>"
     end
   end
@@ -115,6 +124,10 @@ M.setup = function()
   vim.api.nvim_set_keymap("s", "<Tab>", "v:lua.tab_complete()", { expr = true })
   vim.api.nvim_set_keymap("i", "<S-Tab>", "v:lua.s_tab_complete()", { expr = true })
   vim.api.nvim_set_keymap("s", "<S-Tab>", "v:lua.s_tab_complete()", { expr = true })
+
+  if lvim.builtin.compe.on_config_done then
+    lvim.builtin.compe.on_config_done(compe)
+  end
 end
 
 return M

+ 7 - 6
lua/core/dap.lua

@@ -1,8 +1,9 @@
 local M = {}
-local Log = require "core.log"
+
 M.config = function()
   lvim.builtin.dap = {
     active = false,
+    on_config_done = nil,
     breakpoint = {
       text = "",
       texthl = "LspDiagnosticsSignError",
@@ -13,11 +14,7 @@ M.config = function()
 end
 
 M.setup = function()
-  local status_ok, dap = pcall(require, "dap")
-  if not status_ok then
-    Log:get_default().error "Failed to load dap"
-    return
-  end
+  local dap = require "dap"
 
   vim.fn.sign_define("DapBreakpoint", lvim.builtin.dap.breakpoint)
   dap.defaults.fallback.terminal_win_cmd = "50vsplit new"
@@ -38,6 +35,10 @@ M.setup = function()
     s = { "<cmd>lua require'dap'.continue()<cr>", "Start" },
     q = { "<cmd>lua require'dap'.close()<cr>", "Quit" },
   }
+
+  if lvim.builtin.dap.on_config_done then
+    lvim.builtin.dap.on_config_done(dap)
+  end
 end
 
 -- TODO put this up there ^^^ call in ftplugin

+ 21 - 21
lua/core/dashboard.lua

@@ -1,8 +1,13 @@
 local M = {}
-M.config = function()
+local home_dir = vim.loop.os_homedir()
+
+M.config = function(config)
   lvim.builtin.dashboard = {
     active = false,
+    on_config_done = nil,
     search_handler = "telescope",
+    disable_at_vim_enter = 0,
+    session_directory = home_dir .. "/.cache/lvim/sessions",
     custom_header = {
       "⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣀⣀⣀⣀⣀⣀⣀⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀",
       "⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣠⣤⣶⣾⠿⠿⠟⠛⠛⠛⠛⠿⠿⣿⣷⣤⣄⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀",
@@ -30,20 +35,20 @@ M.config = function()
         command = "Telescope find_files",
       },
       b = {
+        description = { "  Recent Projects    " },
+        command = "Telescope projects",
+      },
+      c = {
         description = { "  Recently Used Files" },
         command = "Telescope oldfiles",
       },
-      -- c = {
-      --   description = { "  Load Last Session  " },
-      --   command = "SessionLoad",
-      -- },
-      c = {
+      d = {
         description = { "  Find Word          " },
         command = "Telescope live_grep",
       },
-      d = {
-        description = { "  Settings           " },
-        command = ":e " .. USER_CONFIG_PATH,
+      e = {
+        description = { "  Configuration      " },
+        command = ":e " .. config.path,
       },
     },
 
@@ -52,7 +57,7 @@ M.config = function()
 end
 
 M.setup = function()
-  vim.g.dashboard_disable_at_vimenter = 0
+  vim.g.dashboard_disable_at_vimenter = lvim.builtin.dashboard.disable_at_vim_enter
 
   vim.g.dashboard_custom_header = lvim.builtin.dashboard.custom_header
 
@@ -62,12 +67,8 @@ M.setup = function()
 
   lvim.builtin.which_key.mappings[";"] = { "<cmd>Dashboard<CR>", "Dashboard" }
 
-  -- f = {
-  --   description = { "  Neovim Config Files" },
-  --   command = "Telescope find_files cwd=" .. CONFIG_PATH,
-  -- },
-  -- e = {description = {'  Marks              '}, command = 'Telescope marks'}
-  vim.cmd 'let g:dashboard_session_directory = "~/.config/lvim/.sessions"'
+  vim.g.dashboard_session_directory = lvim.builtin.dashboard.session_directory
+
   vim.cmd "let packages = len(globpath('~/.local/share/lunarvim/site/pack/packer/start', '*', 0, 1))"
 
   vim.api.nvim_exec(
@@ -77,11 +78,6 @@ M.setup = function()
     false
   )
 
-  -- file_browser = {description = {' File Browser'}, command = 'Telescope find_files'},
-
-  -- vim.g.dashboard_session_directory = CACHE_PATH..'/session'
-  -- vim.g.dashboard_custom_footer = lvim.dashboard.footer
-
   require("core.autocmds").define_augroups {
     _dashboard = {
       -- seems to be nobuflisted that makes my stuff disappear will do more testing
@@ -98,6 +94,10 @@ M.setup = function()
       { "FileType", "dashboard", "nnoremap <silent> <buffer> q :q<CR>" },
     },
   }
+
+  if lvim.builtin.dashboard.on_config_done then
+    lvim.builtin.dashboard.on_config_done()
+  end
 end
 
 return M

+ 0 - 320
lua/core/galaxyline.lua

@@ -1,320 +0,0 @@
--- if not package.loaded['galaxyline'] then
---   return
--- end
-local Log = require "core.log"
-local status_ok, gl = pcall(require, "galaxyline")
-if not status_ok then
-  Log:get_default().error "Failed to load galaxyline"
-  return
-end
-
--- NOTE: if someone defines colors but doesn't have them then this will break
-local palette_status_ok, colors = pcall(require, lvim.colorscheme .. ".palette")
-if not palette_status_ok then
-  colors = lvim.builtin.galaxyline.colors
-end
-
-local condition = require "galaxyline.condition"
-local gls = gl.section
-gl.short_line_list = { "NvimTree", "vista", "dbui", "packer" }
-
-table.insert(gls.left, {
-  ViMode = {
-    provider = function()
-      -- auto change color according the vim mode
-      local mode_color = {
-        n = colors.blue,
-        i = colors.green,
-        v = colors.purple,
-        [""] = colors.purple,
-        V = colors.purple,
-        c = colors.magenta,
-        no = colors.blue,
-        s = colors.orange,
-        S = colors.orange,
-        [""] = colors.orange,
-        ic = colors.yellow,
-        R = colors.red,
-        Rv = colors.red,
-        cv = colors.blue,
-        ce = colors.blue,
-        r = colors.cyan,
-        rm = colors.cyan,
-        ["r?"] = colors.cyan,
-        ["!"] = colors.blue,
-        t = colors.blue,
-      }
-      vim.api.nvim_command("hi GalaxyViMode guifg=" .. mode_color[vim.fn.mode()])
-      return "▊"
-    end,
-    separator_highlight = { "NONE", colors.alt_bg },
-    highlight = { "NONE", colors.alt_bg },
-  },
-})
--- print(vim.fn.getbufvar(0, 'ts'))
-vim.fn.getbufvar(0, "ts")
-
-table.insert(gls.left, {
-  GitIcon = {
-    provider = function()
-      return "  "
-    end,
-    condition = condition.check_git_workspace,
-    separator = " ",
-    separator_highlight = { "NONE", colors.alt_bg },
-    highlight = { colors.orange, colors.alt_bg },
-  },
-})
-
-table.insert(gls.left, {
-  GitBranch = {
-    provider = "GitBranch",
-    condition = condition.check_git_workspace,
-    separator = " ",
-    separator_highlight = { "NONE", colors.alt_bg },
-    highlight = { colors.grey, colors.alt_bg },
-  },
-})
-
-table.insert(gls.left, {
-  DiffAdd = {
-    provider = "DiffAdd",
-    condition = condition.hide_in_width,
-    icon = "  ",
-    highlight = { colors.green, colors.alt_bg },
-  },
-})
-
-table.insert(gls.left, {
-  DiffModified = {
-    provider = "DiffModified",
-    condition = condition.hide_in_width,
-    icon = " 柳",
-    highlight = { colors.blue, colors.alt_bg },
-  },
-})
-
-table.insert(gls.left, {
-  DiffRemove = {
-    provider = "DiffRemove",
-    condition = condition.hide_in_width,
-    icon = "  ",
-    highlight = { colors.red, colors.alt_bg },
-  },
-})
-
-table.insert(gls.left, {
-  Filler = {
-    provider = function()
-      return " "
-    end,
-    highlight = { colors.grey, colors.alt_bg },
-  },
-})
--- get output from shell command
-function os.capture(cmd, raw)
-  local f = assert(io.popen(cmd, "r"))
-  local s = assert(f:read "*a")
-  f:close()
-  if raw then
-    return s
-  end
-  s = string.gsub(s, "^%s+", "")
-  s = string.gsub(s, "%s+$", "")
-  s = string.gsub(s, "[\n\r]+", " ")
-  return s
-end
--- cleanup virtual env
-local function env_cleanup(venv)
-  if string.find(venv, "/") then
-    local final_venv = venv
-    for w in venv:gmatch "([^/]+)" do
-      final_venv = w
-    end
-    venv = final_venv
-  end
-  return venv
-end
-local PythonEnv = function()
-  if vim.bo.filetype == "python" then
-    local venv = os.getenv "CONDA_DEFAULT_ENV"
-    if venv ~= nil then
-      return "  (" .. env_cleanup(venv) .. ")"
-    end
-    venv = os.getenv "VIRTUAL_ENV"
-    if venv ~= nil then
-      return "  (" .. env_cleanup(venv) .. ")"
-    end
-    return ""
-  end
-  return ""
-end
-table.insert(gls.left, {
-  VirtualEnv = {
-    provider = PythonEnv,
-    event = "BufEnter",
-    highlight = { colors.green, colors.alt_bg },
-  },
-})
-
-table.insert(gls.right, {
-  DiagnosticError = {
-    provider = "DiagnosticError",
-    icon = "  ",
-    highlight = { colors.red, colors.alt_bg },
-  },
-})
-table.insert(gls.right, {
-  DiagnosticWarn = {
-    provider = "DiagnosticWarn",
-    icon = "  ",
-    highlight = { colors.orange, colors.alt_bg },
-  },
-})
-
-table.insert(gls.right, {
-  DiagnosticInfo = {
-    provider = "DiagnosticInfo",
-    icon = "  ",
-    highlight = { colors.yellow, colors.alt_bg },
-  },
-})
-
-table.insert(gls.right, {
-  DiagnosticHint = {
-    provider = "DiagnosticHint",
-    icon = "  ",
-    highlight = { colors.blue, colors.alt_bg },
-  },
-})
-
-table.insert(gls.right, {
-  TreesitterIcon = {
-    provider = function()
-      if next(vim.treesitter.highlighter.active) ~= nil then
-        return "  "
-      end
-      return ""
-    end,
-    separator = " ",
-    separator_highlight = { "NONE", colors.alt_bg },
-    highlight = { colors.green, colors.alt_bg },
-  },
-})
-
-local function get_attached_provider_name(msg)
-  msg = msg or "LSP Inactive"
-  local buf_clients = vim.lsp.buf_get_clients()
-  if next(buf_clients) == nil then
-    return msg
-  end
-  local buf_ft = vim.bo.filetype
-  local buf_client_names = {}
-  local null_ls_providers = require("lsp.null-ls").get_registered_providers_by_filetype(buf_ft)
-  for _, client in pairs(buf_clients) do
-    if client.name ~= "null-ls" then
-      table.insert(buf_client_names, client.name)
-    end
-  end
-  vim.list_extend(buf_client_names, null_ls_providers)
-  return table.concat(buf_client_names, ", ")
-end
-
-table.insert(gls.right, {
-  ShowLspClient = {
-    provider = get_attached_provider_name,
-    condition = function()
-      local tbl = { ["dashboard"] = true, [" "] = true }
-      if tbl[vim.bo.filetype] then
-        return false
-      end
-      return true
-    end,
-    icon = " ",
-    highlight = { colors.grey, colors.alt_bg },
-  },
-})
-
-table.insert(gls.right, {
-  LineInfo = {
-    provider = "LineColumn",
-    separator = "  ",
-    separator_highlight = { "NONE", colors.alt_bg },
-    highlight = { colors.grey, colors.alt_bg },
-  },
-})
-
-table.insert(gls.right, {
-  PerCent = {
-    provider = "LinePercent",
-    separator = " ",
-    separator_highlight = { "NONE", colors.alt_bg },
-    highlight = { colors.grey, colors.alt_bg },
-  },
-})
-
-table.insert(gls.right, {
-  Tabstop = {
-    provider = function()
-      local label = "Spaces: "
-      if not vim.api.nvim_buf_get_option(0, "expandtab") then
-        label = "Tab size: "
-      end
-      return label .. vim.api.nvim_buf_get_option(0, "shiftwidth") .. " "
-    end,
-    condition = condition.hide_in_width,
-    separator = " ",
-    separator_highlight = { "NONE", colors.alt_bg },
-    highlight = { colors.grey, colors.alt_bg },
-  },
-})
-
-table.insert(gls.right, {
-  BufferType = {
-    provider = "FileTypeName",
-    condition = condition.hide_in_width,
-    separator = " ",
-    separator_highlight = { "NONE", colors.alt_bg },
-    highlight = { colors.grey, colors.alt_bg },
-  },
-})
-
-table.insert(gls.right, {
-  FileEncode = {
-    provider = "FileEncode",
-    condition = condition.hide_in_width,
-    separator = " ",
-    separator_highlight = { "NONE", colors.alt_bg },
-    highlight = { colors.grey, colors.alt_bg },
-  },
-})
-
-table.insert(gls.right, {
-  Space = {
-    provider = function()
-      return " "
-    end,
-    separator = " ",
-    separator_highlight = { "NONE", colors.alt_bg },
-    highlight = { colors.grey, colors.alt_bg },
-  },
-})
-
-table.insert(gls.short_line_left, {
-  BufferType = {
-    provider = "FileTypeName",
-    separator = " ",
-    separator_highlight = { "NONE", colors.alt_bg },
-    highlight = { colors.alt_bg, colors.alt_bg },
-  },
-})
-
-table.insert(gls.short_line_left, {
-  SFileName = {
-    provider = "SFileName",
-    condition = condition.buffer_not_empty,
-    highlight = { colors.alt_bg, colors.alt_bg },
-  },
-})
-
---table.insert(gls.short_line_right[1] = {BufferIcon = {provider = 'BufferIcon', highlight = {colors.grey, colors.alt_bg}}})

+ 50 - 46
lua/core/gitsigns.lua

@@ -1,60 +1,64 @@
 local M = {}
-local Log = require "core.log"
+
 M.config = function()
   lvim.builtin.gitsigns = {
-    signs = {
-      add = {
-        hl = "GitSignsAdd",
-        text = "▎",
-        numhl = "GitSignsAddNr",
-        linehl = "GitSignsAddLn",
-      },
-      change = {
-        hl = "GitSignsChange",
-        text = "▎",
-        numhl = "GitSignsChangeNr",
-        linehl = "GitSignsChangeLn",
-      },
-      delete = {
-        hl = "GitSignsDelete",
-        text = "契",
-        numhl = "GitSignsDeleteNr",
-        linehl = "GitSignsDeleteLn",
+    active = true,
+    on_config_done = nil,
+    opts = {
+      signs = {
+        add = {
+          hl = "GitSignsAdd",
+          text = "▎",
+          numhl = "GitSignsAddNr",
+          linehl = "GitSignsAddLn",
+        },
+        change = {
+          hl = "GitSignsChange",
+          text = "▎",
+          numhl = "GitSignsChangeNr",
+          linehl = "GitSignsChangeLn",
+        },
+        delete = {
+          hl = "GitSignsDelete",
+          text = "契",
+          numhl = "GitSignsDeleteNr",
+          linehl = "GitSignsDeleteLn",
+        },
+        topdelete = {
+          hl = "GitSignsDelete",
+          text = "契",
+          numhl = "GitSignsDeleteNr",
+          linehl = "GitSignsDeleteLn",
+        },
+        changedelete = {
+          hl = "GitSignsChange",
+          text = "▎",
+          numhl = "GitSignsChangeNr",
+          linehl = "GitSignsChangeLn",
+        },
       },
-      topdelete = {
-        hl = "GitSignsDelete",
-        text = "契",
-        numhl = "GitSignsDeleteNr",
-        linehl = "GitSignsDeleteLn",
+      numhl = false,
+      linehl = false,
+      keymaps = {
+        -- Default keymap options
+        noremap = true,
+        buffer = true,
       },
-      changedelete = {
-        hl = "GitSignsChange",
-        text = "▎",
-        numhl = "GitSignsChangeNr",
-        linehl = "GitSignsChangeLn",
-      },
-    },
-    numhl = false,
-    linehl = false,
-    keymaps = {
-      -- Default keymap options
-      noremap = true,
-      buffer = true,
+      watch_index = { interval = 1000 },
+      sign_priority = 6,
+      update_debounce = 200,
+      status_formatter = nil, -- Use default
     },
-    watch_index = { interval = 1000 },
-    sign_priority = 6,
-    update_debounce = 200,
-    status_formatter = nil, -- Use default
   }
 end
 
 M.setup = function()
-  local status_ok, gitsigns = pcall(require, "gitsigns")
-  if not status_ok then
-    Log:get_default().error "Failed to load gitsigns"
-    return
+  local gitsigns = require "gitsigns"
+
+  gitsigns.setup(lvim.builtin.gitsigns.opts)
+  if lvim.builtin.gitsigns.on_config_done then
+    lvim.builtin.gitsigns.on_config_done(gitsigns)
   end
-  gitsigns.setup(lvim.builtin.gitsigns)
 end
 
 return M

+ 120 - 156
lua/core/info.lua

@@ -1,222 +1,186 @@
-local M = {}
-local u = require "utils"
-local null_ls_handler = require "lsp.null-ls"
-local indent = "  "
-
-M.banner = {
-  " ",
-  indent
-    .. "⠀⣿⡟⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀   ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀  ⠀⠀     ⠀⠀⠀   ⠀⠀ ⣺⡿⠀⠀⠀⠀⠀⠀⠀ ⠀⠀⠀",
-  indent
-    .. "⠀⣿⠇⠀⠀⠀⠀⠀⣤⡄⠀⠀⢠⣤⡄⠀.⣠⣤⣤⣤⡀⠀⠀⢀⣤⣤⣤⣤⡄⠀⠀⠀⣤⣄⣤⣤⣤⠀⠀ ⣿⣯  ⣿⡟⠀   ⣤⣤⠀⠀⠀⠀⣠⣤⣤⣤⣄⣤⣤",
-  indent
-    .. "⢠⣿⠀⠀⠀⠀⠀⠀⣿⠃⠀⠀⣸⣿⠁⠀⣿⣿⠉⠀⠈⣿⡇⠀⠀⠛⠋⠀⠀⢹⣿⠀⠀⠀⣿⠏⠀⠸⠿⠃⠀⣿⣿⠀⣰⡟⠀⠀⠀⠀⠀⢸⣿⠀⠀⠀⠀⣿⡟⢸⣿⡇⢀⣿",
-  indent
-    .. "⣸⡇⠀⠀⠀⠀⠀⢸⣿⠀⠀⠀⣿⡟⠀⢠⣿⡇⠀⠀⢰⣿⡇⠀⣰⣾⠟⠛⠛⣻⡇⠀⠀⢸⡿⠀⠀⠀⠀⠀⠀⢻⣿⢰⣿⠀⠀⠀⠀⠀⠀⣾⡇⠀⠀⠀⢸⣿⠇⢸⣿⠀⢸⡏",
-  indent
-    .. "⣿⣧⣤⣤⣤⡄⠀⠘⣿⣤⣤⡤⣿⠇⠀⢸⣿⠁⠀⠀⣼⣿⠀⠀⢿⣿⣤⣤⠔⣿⠃⠀⠀⣾⡇⠀⠀⠀⠀⠀⠀⢸⣿⣿⠋⠀⠀⠀⢠⣤⣤⣿⣥⣤⡄⠀⣼⣿⠀⣸⡏⠀⣿⠃",
-  indent
-    .. "⠉⠉⠉⠉⠉⠁⠀⠀⠈⠉⠉⠀⠉⠀⠀⠈⠉⠀⠀⠀⠉⠉⠀⠀⠀⠉⠉⠁⠈⠉⠀⠀⠀⠉⠀⠀⠀⠀⠀⠀⠀⠈⠉⠉⠀⠀⠀⠀⠈⠉⠉⠉⠉⠉⠁⠀⠉⠁⠀⠉⠁⠀⠉⠀",
-  "",
+local M = {
+  banner = {
+    "",
+    [[    __                          _    ___         ]],
+    [[   / /   __  ______  ____ _____| |  / (_)___ ___ ]],
+    [[  / /   / / / / __ \/ __ `/ ___/ | / / / __ `__ \]],
+    [[ / /___/ /_/ / / / / /_/ / /   | |/ / / / / / / /]],
+    [[/_____/\__,_/_/ /_/\__,_/_/    |___/_/_/ /_/ /_/ ]],
+  },
 }
 
+local fmt = string.format
+
 local function str_list(list)
-  return "[ " .. table.concat(list, ", ") .. " ]"
+  return fmt("[ %s ]", table.concat(list, ", "))
 end
 
 local function get_formatter_suggestion_msg(ft)
-  local supported_formatters = u.get_supported_formatters_by_filetype(ft)
-  return {
-    indent
-      .. "───────────────────────────────────────────────────────────────────",
-    "",
-    indent .. " HINT ",
-    "",
-    indent .. "* List of supported formatters: " .. str_list(supported_formatters),
-    indent .. "* Configured formatter needs to be installed and executable.",
-    indent .. "* Enable installed formatter(s) with following config in ~/.config/lvim/config.lua",
-    "",
-    indent .. "  lvim.lang." .. tostring(ft) .. [[.formatting = { { exe = ']] .. table.concat(
-      supported_formatters,
-      "│"
-    ) .. [[' } }]],
+  local config = require "config"
+  local null_formatters = require "lsp.null-ls.formatters"
+  local supported_formatters = null_formatters.list_available(ft)
+  local section = {
+    " HINT ",
     "",
+    fmt("* List of supported formatters: %s", str_list(supported_formatters)),
   }
+
+  if not vim.tbl_isempty(supported_formatters) then
+    vim.list_extend(section, {
+      "* Configured formatter needs to be installed and executable.",
+      fmt("* Enable installed formatter(s) with following config in %s", config.path),
+      "",
+      fmt("  lvim.lang.%s.formatters = { { exe = '%s' } }", ft, table.concat(supported_formatters, "│")),
+    })
+  end
+
+  return section
 end
 
 local function get_linter_suggestion_msg(ft)
-  local supported_linters = u.get_supported_linters_by_filetype(ft)
-  return {
-    indent
-      .. "───────────────────────────────────────────────────────────────────",
-    "",
-    indent .. " HINT ",
-    "",
-    indent .. "* List of supported linters: " .. str_list(supported_linters),
-    indent .. "* Configured linter needs to be installed and executable.",
-    indent .. "* Enable installed linter(s) with following config in ~/.config/lvim/config.lua",
-    "",
-    indent
-      .. "  lvim.lang."
-      .. tostring(ft)
-      .. [[.linters = { { exe = ']]
-      .. table.concat(supported_linters, "│")
-      .. [[' } }]],
+  local config = require "config"
+  local null_linters = require "lsp.null-ls.linters"
+  local supported_linters = null_linters.list_available(ft)
+  local section = {
+    " HINT ",
     "",
+    fmt("* List of supported linters: %s", str_list(supported_linters)),
   }
-end
 
----creates an average size popup
----@param buf_lines a list of lines to print
----@param callback could be used to set syntax highlighting rules for example
----@return bufnr buffer number of the created buffer
----@return win_id window ID of the created popup
-function M.create_simple_popup(buf_lines, callback)
-  -- runtime/lua/vim/lsp/util.lua
-  local bufnr = vim.api.nvim_create_buf(false, true)
-  local height_percentage = 0.9
-  local width_percentage = 0.8
-  local row_start_percentage = (1 - height_percentage) / 2
-  local col_start_percentage = (1 - width_percentage) / 2
-  local opts = {}
-  opts.relative = "editor"
-  opts.height = math.min(math.ceil(vim.o.lines * height_percentage), #buf_lines)
-  opts.row = math.ceil(vim.o.lines * row_start_percentage)
-  opts.col = math.floor(vim.o.columns * col_start_percentage)
-  opts.width = math.floor(vim.o.columns * width_percentage)
-  opts.style = "minimal"
-  opts.border = "rounded"
-  --[[
-  opts.border = {
-    lvim.builtin.telescope.defaults.borderchars[5], -- "┌",
-    lvim.builtin.telescope.defaults.borderchars[3], -- "-",
-    lvim.builtin.telescope.defaults.borderchars[6], -- "┐",
-    lvim.builtin.telescope.defaults.borderchars[2], -- "|",
-    lvim.builtin.telescope.defaults.borderchars[7], -- "┘",
-    lvim.builtin.telescope.defaults.borderchars[3], -- "-",
-    lvim.builtin.telescope.defaults.borderchars[8], -- "└",
-    lvim.builtin.telescope.defaults.borderchars[4], -- "|",
-  }
-  --]]
-
-  local win_id = vim.api.nvim_open_win(bufnr, true, opts)
-
-  vim.api.nvim_win_set_buf(win_id, bufnr)
-  -- this needs to be window option!
-  vim.api.nvim_win_set_option(win_id, "number", false)
-  vim.cmd "setlocal nocursorcolumn"
-  vim.cmd "setlocal wrap"
-  -- set buffer options
-  vim.api.nvim_buf_set_option(bufnr, "filetype", "lspinfo")
-  vim.lsp.util.close_preview_autocmd({ "BufHidden", "BufLeave" }, win_id)
-  buf_lines = vim.lsp.util._trim(buf_lines, {})
-  vim.api.nvim_buf_set_lines(bufnr, 0, -1, false, buf_lines)
-  vim.api.nvim_buf_set_option(bufnr, "modifiable", false)
-  if type(callback) == "function" then
-    callback()
+  if not vim.tbl_isempty(supported_linters) then
+    vim.list_extend(section, {
+      "* Configured linter needs to be installed and executable.",
+      fmt("* Enable installed linter(s) with following config in %s", config.path),
+      "",
+      fmt("  lvim.lang.%s.linters = { { exe = '%s' } }", ft, table.concat(supported_linters, "│")),
+    })
   end
-  return bufnr, win_id
+
+  return section
 end
 
 local function tbl_set_highlight(terms, highlight_group)
-  if type(terms) ~= "table" then
-    return
-  end
-
   for _, v in pairs(terms) do
-    vim.cmd('let m=matchadd("' .. highlight_group .. '", "' .. v .. '")')
+    vim.cmd('let m=matchadd("' .. highlight_group .. '", "' .. v .. "[ ,│']\")")
   end
 end
 
 function M.toggle_popup(ft)
-  local client = u.get_active_client_by_ft(ft)
+  local lsp_utils = require "lsp.utils"
+  local client = lsp_utils.get_active_client_by_ft(ft)
   local is_client_active = false
   local client_enabled_caps = {}
   local client_name = ""
   local client_id = 0
   local document_formatting = false
-  local missing_linters = {}
-  local missing_formatters = {}
-  local num_caps = 0
-  local null_ls_providers = null_ls_handler.get_registered_providers_by_filetype(ft)
   if client ~= nil then
     is_client_active = not client.is_stopped()
     client_enabled_caps = require("lsp").get_ls_capabilities(client.id)
-    num_caps = vim.tbl_count(client_enabled_caps)
     client_name = client.name
     client_id = client.id
     document_formatting = client.resolved_capabilities.document_formatting
   end
-  if lvim.lang[ft] ~= nil then
-    missing_linters = lvim.lang[ft].linters._failed_requests or {}
-    missing_formatters = lvim.lang[ft].formatters._failed_requests or {}
-  end
-
-  local buf_lines = {}
-  vim.list_extend(buf_lines, M.banner)
 
   local header = {
-    indent .. "Detected filetype:     " .. tostring(ft),
-    indent .. "Treesitter active:     " .. tostring(next(vim.treesitter.highlighter.active) ~= nil),
-    "",
+    fmt("Detected filetype:      %s", ft),
+    fmt("Treesitter active:      %s", tostring(next(vim.treesitter.highlighter.active) ~= nil)),
   }
-  vim.list_extend(buf_lines, header)
 
+  local text = require "interface.text"
   local lsp_info = {
-    indent .. "Language Server Protocol (LSP) info",
-    indent .. "* Associated server:   " .. client_name,
-    indent .. "* Active:              " .. tostring(is_client_active) .. " (id: " .. tostring(client_id) .. ")",
-    indent .. "* Supports formatting: " .. tostring(document_formatting),
-    indent .. "* Capabilities list:   " .. table.concat(vim.list_slice(client_enabled_caps, 1, num_caps / 2), ", "),
-    indent .. indent .. indent .. table.concat(vim.list_slice(client_enabled_caps, ((num_caps / 2) + 1)), ", "),
-    "",
+    "Language Server Protocol (LSP) info",
+    fmt("* Associated server:    %s", client_name),
+    fmt("* Active:               %s (id: %d)", tostring(is_client_active), client_id),
+    fmt("* Supports formatting:  %s", tostring(document_formatting)),
   }
-  vim.list_extend(buf_lines, lsp_info)
-
+  if not vim.tbl_isempty(client_enabled_caps) then
+    local caps_text = "* Capabilities list:    "
+    local caps_text_len = caps_text:len()
+    local enabled_caps = text.format_table(client_enabled_caps, 3, " | ")
+    enabled_caps = text.shift_left(enabled_caps, caps_text_len)
+    enabled_caps[1] = fmt("%s%s", caps_text, enabled_caps[1]:sub(caps_text_len + 1))
+    vim.list_extend(lsp_info, enabled_caps)
+  end
+  local null_ls = require "lsp.null-ls"
+  local registered_providers = null_ls.list_supported_provider_names(ft)
+  local registered_count = vim.tbl_count(registered_providers)
   local null_ls_info = {
-    indent .. "Formatters and linters",
-    indent .. "* Configured providers: " .. table.concat(null_ls_providers, "  , ") .. "  ",
+    "Formatters and linters",
+    fmt(
+      "* Configured providers: %s%s",
+      table.concat(registered_providers, "  , "),
+      registered_count > 0 and "  " or ""
+    ),
   }
-  vim.list_extend(buf_lines, null_ls_info)
 
-  local missing_formatters_status
-  if vim.tbl_count(missing_formatters) > 0 then
+  local null_formatters = require "lsp.null-ls.formatters"
+  local missing_formatters = null_formatters.list_unsupported_names(ft)
+  local missing_formatters_status = {}
+  if not vim.tbl_isempty(missing_formatters) then
     missing_formatters_status = {
-      indent .. "* Missing formatters:   " .. table.concat(missing_formatters, "  , ") .. "  ",
+      fmt("* Missing formatters:   %s", table.concat(missing_formatters, "  , ") .. "  "),
     }
-    vim.list_extend(buf_lines, missing_formatters_status)
   end
 
-  local missing_linters_status
-  if vim.tbl_count(missing_linters) > 0 then
+  local null_linters = require "lsp.null-ls.linters"
+  local missing_linters = null_linters.list_unsupported_names(ft)
+  local missing_linters_status = {}
+  if not vim.tbl_isempty(missing_linters) then
     missing_linters_status = {
-      indent .. "* Missing linters:      " .. table.concat(missing_linters, "  , ") .. "  ",
+      fmt("* Missing linters:      %s", table.concat(missing_linters, "  , ") .. "  "),
     }
-    vim.list_extend(buf_lines, missing_linters_status)
   end
 
-  vim.list_extend(buf_lines, { "" })
-
-  vim.list_extend(buf_lines, get_formatter_suggestion_msg(ft))
-
-  vim.list_extend(buf_lines, get_linter_suggestion_msg(ft))
+  local content_provider = function(popup)
+    local content = {}
+
+    for _, section in ipairs {
+      M.banner,
+      { "" },
+      { "" },
+      header,
+      { "" },
+      lsp_info,
+      { "" },
+      null_ls_info,
+      missing_formatters_status,
+      missing_linters_status,
+      { "" },
+      { "" },
+      get_formatter_suggestion_msg(ft),
+      { "" },
+      { "" },
+      get_linter_suggestion_msg(ft),
+    } do
+      vim.list_extend(content, section)
+    end
+
+    return text.align(popup, content, 0.5)
+  end
 
   local function set_syntax_hl()
     vim.cmd [[highlight LvimInfoIdentifier gui=bold]]
     vim.cmd [[highlight link LvimInfoHeader Type]]
-    vim.cmd [[let m=matchadd("DashboardHeader", "Language Server Protocol (LSP) info")]]
-    vim.cmd [[let m=matchadd("DashboardHeader", "Formatters and linters")]]
+    vim.cmd [[let m=matchadd("LvimInfoHeader", "Language Server Protocol (LSP) info")]]
+    vim.cmd [[let m=matchadd("LvimInfoHeader", "Formatters and linters")]]
     vim.cmd('let m=matchadd("LvimInfoIdentifier", " ' .. ft .. '$")')
     vim.cmd 'let m=matchadd("string", "true")'
     vim.cmd 'let m=matchadd("error", "false")'
-    tbl_set_highlight(null_ls_providers, "LvimInfoIdentifier")
+    tbl_set_highlight(registered_providers, "LvimInfoIdentifier")
     tbl_set_highlight(missing_formatters, "LvimInfoIdentifier")
     tbl_set_highlight(missing_linters, "LvimInfoIdentifier")
-    -- tbl_set_highlight(u.get_supported_formatters_by_filetype(ft), "LvimInfoIdentifier")
-    -- tbl_set_highlight(u.get_supported_linters_by_filetype(ft), "LvimInfoIdentifier")
+    -- tbl_set_highlight(require("lsp.null-ls.formatters").list_available(ft), "LvimInfoIdentifier")
+    -- tbl_set_highlight(require("lsp.null-ls.linters").list_available(ft), "LvimInfoIdentifier")
     vim.cmd('let m=matchadd("LvimInfoIdentifier", "' .. client_name .. '")')
   end
 
-  return M.create_simple_popup(buf_lines, set_syntax_hl)
+  local Popup = require("interface.popup"):new {
+    win_opts = { number = false },
+    buf_opts = { modifiable = false, filetype = "lspinfo" },
+  }
+  Popup:display(content_provider)
+  set_syntax_hl()
+
+  return Popup
 end
 return M

+ 48 - 18
lua/core/log.lua

@@ -1,29 +1,59 @@
 local Log = {}
 
---- Creates a log handle based on Plenary.log
----@param opts these are passed verbatim to Plenary.log
----@return log handle
-function Log:new(opts)
-  local status_ok, _ = pcall(require, "plenary.log")
-  if not status_ok then
-    return nil
+--- Adds a log entry using Plenary.log
+---@param msg any
+---@param level string [same as vim.log.log_levels]
+function Log:add_entry(msg, level)
+  assert(type(level) == "string")
+  if self.__handle then
+    -- plenary uses lower-case log levels
+    self.__handle[level:lower()](msg)
   end
+  local status_ok, plenary = pcall(require, "plenary")
+  if status_ok then
+    local default_opts = { plugin = "lunarvim", level = lvim.log.level }
+    local handle = plenary.log.new(default_opts)
+    handle[level:lower()](msg)
+    self.__handle = handle
+  end
+  -- don't do anything if plenary is not available
+end
 
-  local obj = require("plenary.log").new(opts)
-  local path = string.format("%s/%s.log", vim.api.nvim_call_function("stdpath", { "cache" }), opts.plugin)
+---Retrieves the path of the logfile
+---@return string path of the logfile
+function Log:get_path()
+  return string.format("%s/%s.log", vim.fn.stdpath "cache", "lunarvim")
+end
 
-  obj.get_path = function()
-    return path
-  end
+---Add a log entry at TRACE level
+---@param msg any
+function Log:trace(msg)
+  self:add_entry(msg, "TRACE")
+end
+
+---Add a log entry at DEBUG level
+---@param msg any
+function Log:debug(msg)
+  self:add_entry(msg, "DEBUG")
+end
+
+---Add a log entry at INFO level
+---@param msg any
+function Log:info(msg)
+  self:add_entry(msg, "INFO")
+end
 
-  return obj
+---Add a log entry at WARN level
+---@param msg any
+function Log:warn(msg)
+  self:add_entry(msg, "WARN")
 end
 
---- Creates or retrieves a log handle for the default logfile
---- based on Plenary.log
----@return log handle
-function Log:get_default()
-  return Log:new { plugin = "lunarvim", level = lvim.log.level }
+---Add a log entry at ERROR level
+---@param msg any
+function Log:error(msg)
+  self:add_entry(msg, "ERROR")
 end
 
+setmetatable({}, Log)
 return Log

+ 19 - 0
lua/core/lspinstall.lua

@@ -0,0 +1,19 @@
+local M = {}
+
+M.config = function()
+  lvim.builtin.lspinstall = {
+    active = true,
+    on_config_done = nil,
+  }
+end
+
+M.setup = function()
+  local lspinstall = require "lspinstall"
+  lspinstall.setup()
+
+  if lvim.builtin.lspinstall.on_config_done then
+    lvim.builtin.lspinstall.on_config_done(lspinstall)
+  end
+end
+
+return M

+ 16 - 0
lua/core/lualine/colors.lua

@@ -0,0 +1,16 @@
+local colors = {
+  bg = "#202328",
+  fg = "#bbc2cf",
+  yellow = "#ECBE7B",
+  cyan = "#008080",
+  darkblue = "#081633",
+  green = "#98be65",
+  orange = "#FF8800",
+  violet = "#a9a1e1",
+  magenta = "#c678dd",
+  purple = "#c678dd",
+  blue = "#51afef",
+  red = "#ec5f67",
+}
+
+return colors

+ 152 - 0
lua/core/lualine/components.lua

@@ -0,0 +1,152 @@
+local conditions = require "core.lualine.conditions"
+local colors = require "core.lualine.colors"
+
+local function diff_source()
+  local gitsigns = vim.b.gitsigns_status_dict
+  if gitsigns then
+    return {
+      added = gitsigns.added,
+      modified = gitsigns.changed,
+      removed = gitsigns.removed,
+    }
+  end
+end
+
+return {
+  mode = {
+    function()
+      return " "
+    end,
+    left_padding = 0,
+    right_padding = 0,
+    color = {},
+    condition = nil,
+  },
+  branch = {
+    "b:gitsigns_head",
+    icon = " ",
+    color = { gui = "bold" },
+    condition = conditions.hide_in_width,
+  },
+  filename = {
+    "filename",
+    color = {},
+    condition = nil,
+  },
+  diff = {
+    "diff",
+    source = diff_source,
+    symbols = { added = "  ", modified = "柳", removed = " " },
+    color_added = { fg = colors.green },
+    color_modified = { fg = colors.yellow },
+    color_removed = { fg = colors.red },
+    color = {},
+    condition = nil,
+  },
+  python_env = {
+    function()
+      local utils = require "core.lualine.utils"
+      if vim.bo.filetype == "python" then
+        local venv = os.getenv "CONDA_DEFAULT_ENV"
+        if venv then
+          return string.format("  (%s)", utils.env_cleanup(venv))
+        end
+        venv = os.getenv "VIRTUAL_ENV"
+        if venv then
+          return string.format("  (%s)", utils.env_cleanup(venv))
+        end
+        return ""
+      end
+      return ""
+    end,
+    color = { fg = colors.green },
+    condition = conditions.hide_in_width,
+  },
+  diagnostics = {
+    "diagnostics",
+    sources = { "nvim_lsp" },
+    symbols = { error = " ", warn = " ", info = " ", hint = " " },
+    color = {},
+    condition = conditions.hide_in_width,
+  },
+  treesitter = {
+    function()
+      if next(vim.treesitter.highlighter.active) then
+        return "  "
+      end
+      return ""
+    end,
+    color = { fg = colors.green },
+    condition = conditions.hide_in_width,
+  },
+  lsp = {
+    function(msg)
+      msg = msg or "LSP Inactive"
+      local buf_clients = vim.lsp.buf_get_clients()
+      if next(buf_clients) == nil then
+        return msg
+      end
+      local buf_ft = vim.bo.filetype
+      local buf_client_names = {}
+
+      -- add client
+      local utils = require "lsp.utils"
+      local active_client = utils.get_active_client_by_ft(buf_ft)
+      for _, client in pairs(buf_clients) do
+        if client.name ~= "null-ls" then
+          table.insert(buf_client_names, client.name)
+        end
+      end
+      vim.list_extend(buf_client_names, active_client or {})
+
+      -- add formatter
+      local formatters = require "lsp.null-ls.formatters"
+      local supported_formatters = formatters.list_supported_names(buf_ft)
+      vim.list_extend(buf_client_names, supported_formatters)
+
+      -- add linter
+      local linters = require "lsp.null-ls.linters"
+      local supported_linters = linters.list_supported_names(buf_ft)
+      vim.list_extend(buf_client_names, supported_linters)
+
+      return table.concat(buf_client_names, ", ")
+    end,
+    icon = " ",
+    color = { gui = "bold" },
+    condition = conditions.hide_in_width,
+  },
+  location = { "location", condition = conditions.hide_in_width, color = {} },
+  progress = { "progress", condition = conditions.hide_in_width, color = {} },
+  spaces = {
+    function()
+      local label = "Spaces: "
+      if not vim.api.nvim_buf_get_option(0, "expandtab") then
+        label = "Tab size: "
+      end
+      return label .. vim.api.nvim_buf_get_option(0, "shiftwidth") .. " "
+    end,
+    condition = conditions.hide_in_width,
+    color = {},
+  },
+  encoding = {
+    "o:encoding",
+    upper = true,
+    color = {},
+    condition = conditions.hide_in_width,
+  },
+  filetype = { "filetype", condition = conditions.hide_in_width, color = {} },
+  scrollbar = {
+    function()
+      local current_line = vim.fn.line "."
+      local total_lines = vim.fn.line "$"
+      local chars = { "__", "▁▁", "▂▂", "▃▃", "▄▄", "▅▅", "▆▆", "▇▇", "██" }
+      local line_ratio = current_line / total_lines
+      local index = math.ceil(line_ratio * #chars)
+      return chars[index]
+    end,
+    left_padding = 0,
+    right_padding = 0,
+    color = { fg = colors.yellow, bg = colors.bg },
+    condition = nil,
+  },
+}

+ 17 - 0
lua/core/lualine/conditions.lua

@@ -0,0 +1,17 @@
+local window_width_limit = 80
+
+local conditions = {
+  buffer_not_empty = function()
+    return vim.fn.empty(vim.fn.expand "%:t") ~= 1
+  end,
+  hide_in_width = function()
+    return vim.fn.winwidth(0) > window_width_limit
+  end,
+  -- check_git_workspace = function()
+  --   local filepath = vim.fn.expand "%:p:h"
+  --   local gitdir = vim.fn.finddir(".git", filepath .. ";")
+  --   return gitdir and #gitdir > 0 and #gitdir < #filepath
+  -- end,
+}
+
+return conditions

+ 47 - 0
lua/core/lualine/init.lua

@@ -0,0 +1,47 @@
+local M = {}
+M.config = function()
+  lvim.builtin.lualine = {
+    active = true,
+    style = "lvim",
+    options = {
+      icons_enabled = nil,
+      component_separators = nil,
+      section_separators = nil,
+      theme = nil,
+      disabled_filetypes = nil,
+    },
+    sections = {
+      lualine_a = nil,
+      lualine_b = nil,
+      lualine_c = nil,
+      lualine_x = nil,
+      lualine_y = nil,
+      lualine_z = nil,
+    },
+    inactive_sections = {
+      lualine_a = nil,
+      lualine_b = nil,
+      lualine_c = nil,
+      lualine_x = nil,
+      lualine_y = nil,
+      lualine_z = nil,
+    },
+    tabline = nil,
+    extensions = nil,
+    on_config_done = nil,
+  }
+end
+
+M.setup = function()
+  require("core.lualine.styles").update()
+  require("core.lualine.utils").validate_theme()
+
+  local lualine = require "lualine"
+  lualine.setup(lvim.builtin.lualine)
+
+  if lvim.builtin.lualine.on_config_done then
+    lvim.builtin.lualine.on_config_done(lualine)
+  end
+end
+
+return M

+ 137 - 0
lua/core/lualine/styles.lua

@@ -0,0 +1,137 @@
+local M = {}
+local components = require "core.lualine.components"
+
+local styles = {
+  lvim = nil,
+  default = nil,
+  none = nil,
+}
+
+styles.none = {
+  style = "none",
+  options = {
+    icons_enabled = true,
+    component_separators = "",
+    section_separators = "",
+    disabled_filetypes = {},
+  },
+  sections = {
+    lualine_a = {},
+    lualine_b = {},
+    lualine_c = {},
+    lualine_x = {},
+    lualine_y = {},
+    lualine_z = {},
+  },
+  inactive_sections = {
+    lualine_a = {},
+    lualine_b = {},
+    lualine_c = {},
+    lualine_x = {},
+    lualine_y = {},
+    lualine_z = {},
+  },
+  tabline = {},
+  extensions = {},
+}
+
+styles.default = {
+  style = "default",
+  options = {
+    icons_enabled = true,
+    component_separators = { "", "" },
+    section_separators = { "", "" },
+    disabled_filetypes = {},
+  },
+  sections = {
+    lualine_a = { "mode" },
+    lualine_b = { "branch" },
+    lualine_c = { "filename" },
+    lualine_x = { "encoding", "fileformat", "filetype" },
+    lualine_y = { "progress" },
+    lualine_z = { "location" },
+  },
+  inactive_sections = {
+    lualine_a = {},
+    lualine_b = {},
+    lualine_c = { "filename" },
+    lualine_x = { "location" },
+    lualine_y = {},
+    lualine_z = {},
+  },
+  tabline = {},
+  extensions = {},
+}
+
+styles.lvim = {
+  style = "lvim",
+  options = {
+    icons_enabled = true,
+    component_separators = "",
+    section_separators = "",
+    disabled_filetypes = { "dashboard", "NvimTree", "Outline" },
+  },
+  sections = {
+    lualine_a = {
+      components.mode,
+    },
+    lualine_b = {
+      components.branch,
+      components.filename,
+    },
+    lualine_c = {
+      components.diff,
+      components.python_env,
+    },
+    lualine_x = {
+      components.diagnostics,
+      components.treesitter,
+      components.lsp,
+      components.filetype,
+    },
+    lualine_y = {},
+    lualine_z = {
+      components.scrollbar,
+    },
+  },
+  inactive_sections = {
+    lualine_a = {
+      "filename",
+    },
+    lualine_b = {},
+    lualine_c = {},
+    lualine_x = {},
+    lualine_y = {},
+    lualine_z = {},
+  },
+  tabline = {},
+  extensions = { "nvim-tree" },
+}
+
+function M.get_style(style)
+  local style_keys = vim.tbl_keys(styles)
+  if not vim.tbl_contains(style_keys, style) then
+    local Log = require "core.log"
+    Log:error(
+      "Invalid lualine style",
+      string.format('"%s"', style),
+      "options are: ",
+      string.format('"%s"', table.concat(style_keys, '", "'))
+    )
+    Log:debug '"lvim" style is applied.'
+    style = "lvim"
+  end
+
+  return vim.deepcopy(styles[style])
+end
+
+function M.update()
+  local style = M.get_style(lvim.builtin.lualine.style)
+  if lvim.builtin.lualine.options.theme == nil then
+    lvim.builtin.lualine.options.theme = lvim.colorscheme
+  end
+
+  lvim.builtin.lualine = vim.tbl_deep_extend("keep", lvim.builtin.lualine, style)
+end
+
+return M

+ 27 - 0
lua/core/lualine/utils.lua

@@ -0,0 +1,27 @@
+local M = {}
+
+function M.validate_theme()
+  local theme = lvim.builtin.lualine.options.theme
+  if type(theme) == "table" then
+    return
+  end
+
+  local lualine_loader = require "lualine.utils.loader"
+  local ok = pcall(lualine_loader.load_theme, theme)
+  if not ok then
+    lvim.builtin.lualine.options.theme = "auto"
+  end
+end
+
+function M.env_cleanup(venv)
+  if string.find(venv, "/") then
+    local final_venv = venv
+    for w in venv:gmatch "([^/]+)" do
+      final_venv = w
+    end
+    venv = final_venv
+  end
+  return venv
+end
+
+return M

+ 46 - 52
lua/core/nvimtree.lua

@@ -1,8 +1,10 @@
 local M = {}
 local Log = require "core.log"
---
-M.config = function()
+
+function M.config()
   lvim.builtin.nvimtree = {
+    active = true,
+    on_config_done = nil,
     side = "left",
     width = 30,
     show_icons = {
@@ -13,7 +15,7 @@ M.config = function()
       tree_width = 30,
     },
     ignore = { ".git", "node_modules", ".cache" },
-    auto_open = 1,
+    auto_open = 0,
     auto_close = 1,
     quit_on_open = 0,
     follow = 1,
@@ -46,11 +48,11 @@ M.config = function()
     },
   }
 end
---
-M.setup = function()
+
+function M.setup()
   local status_ok, nvim_tree_config = pcall(require, "nvim-tree.config")
   if not status_ok then
-    Log:get_default().error "Failed to load nvim-tree.config"
+    Log:error "Failed to load nvim-tree.config"
     return
   end
   local g = vim.g
@@ -59,6 +61,15 @@ M.setup = function()
     g["nvim_tree_" .. opt] = val
   end
 
+  -- Implicitly update nvim-tree when project module is active
+  if lvim.builtin.project.active then
+    vim.g.nvim_tree_update_cwd = 1
+    vim.g.nvim_tree_respect_buf_cwd = 1
+    vim.g.nvim_tree_disable_netrw = 0
+    vim.g.nvim_tree_hijack_netrw = 0
+    vim.g.netrw_banner = 0
+  end
+
   local tree_cb = nvim_tree_config.nvim_tree_callback
 
   if not g.nvim_tree_bindings then
@@ -68,61 +79,44 @@ M.setup = function()
       { key = "v", cb = tree_cb "vsplit" },
     }
   end
-end
---
-M.focus_or_close = function()
-  local view_status_ok, view = pcall(require, "nvim-tree.view")
-  if not view_status_ok then
-    return
+
+  lvim.builtin.which_key.mappings["e"] = { "<cmd>NvimTreeToggle<CR>", "Explorer" }
+
+  local tree_view = require "nvim-tree.view"
+
+  -- Add nvim_tree open callback
+  local open = tree_view.open
+  tree_view.open = function()
+    M.on_open()
+    open()
   end
-  local a = vim.api
 
-  local curwin = a.nvim_get_current_win()
-  local curbuf = a.nvim_win_get_buf(curwin)
-  local bufnr = view.View.bufnr
-  local winnr = view.get_winnr()
+  vim.cmd "au WinClosed * lua require('core.nvimtree').on_close()"
 
-  if view.win_open() then
-    if curwin == winnr and curbuf == bufnr then
-      view.close()
-      if package.loaded["bufferline.state"] then
-        require("bufferline.state").set_offset(0)
-      end
-    else
-      view.focus()
-    end
-  else
-    view.open()
-    if package.loaded["bufferline.state"] and lvim.builtin.nvimtree.side == "left" then
-      -- require'bufferline.state'.set_offset(lvim.builtin.nvimtree.width + 1, 'File Explorer')
-      require("bufferline.state").set_offset(lvim.builtin.nvimtree.width + 1, "")
-    end
+  if lvim.builtin.nvimtree.on_config_done then
+    lvim.builtin.nvimtree.on_config_done(nvim_tree_config)
   end
 end
---
-M.toggle_tree = function()
-  local view_status_ok, view = pcall(require, "nvim-tree.view")
-  if not view_status_ok then
-    return
+
+function M.on_open()
+  if package.loaded["bufferline.state"] and lvim.builtin.nvimtree.side == "left" then
+    require("bufferline.state").set_offset(lvim.builtin.nvimtree.width + 1, "")
   end
-  if view.win_open() then
-    require("nvim-tree").close()
-    if package.loaded["bufferline.state"] then
-      require("bufferline.state").set_offset(0)
-    end
-  else
-    if package.loaded["bufferline.state"] and lvim.builtin.nvimtree.side == "left" then
-      -- require'bufferline.state'.set_offset(lvim.builtin.nvimtree.width + 1, 'File Explorer')
-      require("bufferline.state").set_offset(lvim.builtin.nvimtree.width + 1, "")
-    end
-    require("nvim-tree").toggle()
+end
+
+function M.on_close()
+  local buf = tonumber(vim.fn.expand "<abuf>")
+  local ft = vim.api.nvim_buf_get_option(buf, "filetype")
+  if ft == "NvimTree" and package.loaded["bufferline.state"] then
+    require("bufferline.state").set_offset(0)
   end
 end
---
+
 function M.change_tree_dir(dir)
-  if vim.g.loaded_tree then
-    require("nvim-tree.lib").change_dir(dir)
+  local lib_status_ok, lib = pcall(require, "nvim-tree.lib")
+  if lib_status_ok then
+    lib.change_dir(dir)
   end
 end
---
+
 return M

+ 51 - 0
lua/core/project.lua

@@ -0,0 +1,51 @@
+local M = {}
+
+function M.config()
+  lvim.builtin.project = {
+    ---@usage set to false to disable project.nvim.
+    --- This is on by default since it's currently the expected behavior.
+    active = true,
+
+    on_config_done = nil,
+
+    ---@usage set to true to disable setting the current-woriking directory
+    --- Manual mode doesn't automatically change your root directory, so you have
+    --- the option to manually do so using `:ProjectRoot` command.
+    manual_mode = false,
+
+    ---@usage Methods of detecting the root directory
+    --- Allowed values: **"lsp"** uses the native neovim lsp
+    --- **"pattern"** uses vim-rooter like glob pattern matching. Here
+    --- order matters: if one is not detected, the other is used as fallback. You
+    --- can also delete or rearangne the detection methods.
+    detection_methods = { "lsp", "pattern" },
+
+    ---@usage patterns used to detect root dir, when **"pattern"** is in detection_methods
+    patterns = { ".git", "_darcs", ".hg", ".bzr", ".svn", "Makefile", "package.json" },
+
+    ---@ Show hidden files in telescope when searching for files in a project
+    show_hidden = false,
+
+    ---@usage When set to false, you will get a message when project.nvim changes your directory.
+    -- When set to false, you will get a message when project.nvim changes your directory.
+    silent_chdir = true,
+
+    ---@usage list of lsp client names to ignore when using **lsp** detection. eg: { "efm", ... }
+    ignore_lsp = {},
+
+    ---@type string
+    ---@usage path to store the project history for use in telescope
+    datapath = CACHE_PATH,
+  }
+end
+
+function M.setup()
+  local project = require "project_nvim"
+
+  project.setup(lvim.builtin.project)
+  if lvim.builtin.project.on_config_done then
+    lvim.builtin.project.on_config_done(project)
+  end
+end
+
+return M

+ 0 - 15
lua/core/rooter.lua

@@ -1,15 +0,0 @@
-local M = {}
-function M.config()
-  lvim.builtin.rooter = {
-    --- This is on by default since it's currently the expected behavior.
-    ---@usage set to false to disable vim-rooter.
-    active = true,
-    silent_chdir = 1,
-    manual_only = 0,
-  }
-end
-function M.setup()
-  vim.g.rooter_silent_chdir = lvim.builtin.rooter.silent_chdir
-  vim.g.rooter_manual_only = lvim.builtin.rooter.manual_only
-end
-return M

+ 0 - 19
lua/core/status_colors.lua

@@ -1,19 +0,0 @@
-lvim.builtin.galaxyline = {
-  active = true,
-  colors = {
-    alt_bg = "#2E2E2E",
-    grey = "#858585",
-    blue = "#569CD6",
-    green = "#608B4E",
-    yellow = "#DCDCAA",
-    orange = "#FF8800",
-    purple = "#C586C0",
-    magenta = "#D16D9E",
-    cyan = "#4EC9B0",
-    red = "#D16969",
-    error_red = "#F44747",
-    warning_orange = "#FF8800",
-    info_yellow = "#FFCC66",
-    hint_blue = "#9CDCFE",
-  },
-}

+ 56 - 12
lua/core/telescope.lua

@@ -1,13 +1,19 @@
 local M = {}
-local Log = require "core.log"
-M.config = function()
+
+function M.config()
+  -- Define this minimal config so that it's available if telescope is not yet available.
+  lvim.builtin.telescope = {
+    ---@usage disable telescope completely [not recommeded]
+    active = true,
+    on_config_done = nil,
+  }
+
   local status_ok, actions = pcall(require, "telescope.actions")
   if not status_ok then
     return
   end
 
-  lvim.builtin.telescope = {
-    active = false,
+  lvim.builtin.telescope = vim.tbl_extend("force", lvim.builtin.telescope, {
     defaults = {
       prompt_prefix = " ",
       selection_caret = " ",
@@ -60,8 +66,8 @@ M.config = function()
           -- ["<CR>"] = actions.select_default + actions.center + my_cool_custom_action,
         },
         n = {
-          ["<C-j>"] = actions.move_selection_next,
-          ["<C-k>"] = actions.move_selection_previous,
+          ["<C-n>"] = actions.move_selection_next,
+          ["<C-p>"] = actions.move_selection_previous,
           ["<C-q>"] = actions.smart_send_to_qflist + actions.open_qflist,
           -- ["<c-t>"] = trouble.open_with_trouble,
           -- ["<C-i>"] = my_cool_custom_action,
@@ -74,16 +80,54 @@ M.config = function()
         override_file_sorter = true,
       },
     },
+  })
+end
+
+function M.find_lunarvim_files(opts)
+  opts = opts or {}
+  local themes = require "telescope.themes"
+  local theme_opts = themes.get_ivy {
+    previewer = false,
+    sorting_strategy = "ascending",
+    layout_strategy = "bottom_pane",
+    layout_config = {
+      height = 5,
+      width = 0.5,
+    },
+    prompt = ">> ",
+    prompt_title = "~ LunarVim files ~",
+    cwd = CONFIG_PATH,
+    find_command = { "git", "ls-files" },
   }
+  opts = vim.tbl_deep_extend("force", theme_opts, opts)
+  require("telescope.builtin").find_files(opts)
 end
 
-M.setup = function()
-  local status_ok, telescope = pcall(require, "telescope")
-  if not status_ok then
-    Log:get_default().error "Failed to load telescope"
-    return
-  end
+function M.grep_lunarvim_files(opts)
+  opts = opts or {}
+  local themes = require "telescope.themes"
+  local theme_opts = themes.get_ivy {
+    sorting_strategy = "ascending",
+    layout_strategy = "bottom_pane",
+    prompt = ">> ",
+    prompt_title = "~ search LunarVim ~",
+    cwd = CONFIG_PATH,
+  }
+  opts = vim.tbl_deep_extend("force", theme_opts, opts)
+  require("telescope.builtin").live_grep(opts)
+end
+
+function M.setup()
+  local telescope = require "telescope"
+
   telescope.setup(lvim.builtin.telescope)
+  if lvim.builtin.project.active then
+    telescope.load_extension "projects"
+  end
+
+  if lvim.builtin.telescope.on_config_done then
+    lvim.builtin.telescope.on_config_done(telescope)
+  end
 end
 
 return M

+ 9 - 13
lua/core/terminal.lua

@@ -1,9 +1,9 @@
 local M = {}
-local Log = require "core.log"
 local utils = require "utils"
 
 M.config = function()
   lvim.builtin["terminal"] = {
+    on_config_done = nil,
     -- size can be a number or function which is passed the current terminal
     size = 20,
     -- open_mapping = [[<c-\>]],
@@ -46,20 +46,15 @@ M.config = function()
 end
 
 M.setup = function()
-  local status_ok, terminal = pcall(require, "toggleterm")
-  if not status_ok then
-    Log:get_default().error "Failed to load toggleterm"
-    print(terminal)
-    return
-  end
+  local terminal = require "toggleterm"
   for _, exec in pairs(lvim.builtin.terminal.execs) do
     require("core.terminal").add_exec(exec[1], exec[2], exec[3])
   end
   terminal.setup(lvim.builtin.terminal)
-end
 
-local function is_installed(exe)
-  return vim.fn.executable(exe) == 1
+  if lvim.builtin.terminal.on_config_done then
+    lvim.builtin.terminal.on_config_done(terminal)
+  end
 end
 
 M.add_exec = function(exec, keymap, name)
@@ -85,8 +80,9 @@ end
 
 M._exec_toggle = function(exec)
   local binary = M._split(exec)[1]
-  if is_installed(binary) ~= true then
-    print("Please install executable " .. binary .. ". Check documentation for more information")
+  if vim.fn.executable(binary) ~= 1 then
+    local Log = require "core.log"
+    Log:error("Unable to run executable " .. binary .. ". Please make sure it is installed properly.")
     return
   end
   local Terminal = require("toggleterm.terminal").Terminal
@@ -126,7 +122,7 @@ M.toggle_log_view = function(name)
 
   local Terminal = require("toggleterm.terminal").Terminal
   local log_view = Terminal:new(term_opts)
-  -- require("core.log"):get_default().debug("term", vim.inspect(term_opts))
+  -- require("core.log"):debug("term", vim.inspect(term_opts))
   log_view:toggle()
 end
 

+ 6 - 0
lua/core/treesitter.lua

@@ -1,7 +1,9 @@
 local M = {}
 local Log = require "core.log"
+
 M.config = function()
   lvim.builtin.treesitter = {
+    on_config_done = nil,
     ensure_installed = {}, -- one of "all", "maintained" (parsers with maintainers), or a list of languages
     ignore_install = {},
     matchup = {
@@ -70,6 +72,10 @@ M.setup = function()
   end
 
   treesitter_configs.setup(lvim.builtin.treesitter)
+
+  if lvim.builtin.treesitter.on_config_done then
+    lvim.builtin.treesitter.on_config_done(treesitter_configs)
+  end
 end
 
 return M

+ 22 - 14
lua/core/which-key.lua

@@ -1,8 +1,10 @@
 local M = {}
-local Log = require "core.log"
+
 M.config = function()
   lvim.builtin.which_key = {
-    active = false,
+    ---@usage disable which-key completely [not recommeded]
+    active = true,
+    on_config_done = nil,
     setup = {
       plugins = {
         marks = true, -- shows a list of your marks on ' and `
@@ -66,7 +68,6 @@ M.config = function()
       ["q"] = { "<cmd>q!<CR>", "Quit" },
       ["/"] = { "<cmd>CommentToggle<CR>", "Comment" },
       ["c"] = { "<cmd>BufferClose!<CR>", "Close Buffer" },
-      ["e"] = { "<cmd>lua require'core.nvimtree'.toggle_tree()<CR>", "Explorer" },
       ["f"] = { "<cmd>Telescope find_files<CR>", "Find File" },
       ["h"] = { "<cmd>nohlsearch<CR>", "No Highlight" },
       b = {
@@ -169,6 +170,18 @@ M.config = function()
       },
       L = {
         name = "+LunarVim",
+        c = {
+          "<cmd>edit ~/.config/lvim/config.lua<cr>",
+          "Edit config.lua",
+        },
+        f = {
+          "<cmd>lua require('core.telescope').find_lunarvim_files()<cr>",
+          "Find LunarVim files",
+        },
+        g = {
+          "<cmd>lua require('core.telescope').grep_lunarvim_files()<cr>",
+          "Grep LunarVim files",
+        },
         k = { "<cmd>lua require('keymappings').print()<cr>", "View LunarVim's default keymappings" },
         i = {
           "<cmd>lua require('core.info').toggle_popup(vim.bo.filetype)<cr>",
@@ -218,14 +231,7 @@ M.config = function()
 end
 
 M.setup = function()
-  -- if not package.loaded['which-key'] then
-  --  return
-  -- end
-  local status_ok, which_key = pcall(require, "which-key")
-  if not status_ok then
-    Log:get_default "Failed to load whichkey"
-    return
-  end
+  local which_key = require "which-key"
 
   which_key.setup(lvim.builtin.which_key.setup)
 
@@ -235,10 +241,12 @@ M.setup = function()
   local mappings = lvim.builtin.which_key.mappings
   local vmappings = lvim.builtin.which_key.vmappings
 
-  local wk = require "which-key"
+  which_key.register(mappings, opts)
+  which_key.register(vmappings, vopts)
 
-  wk.register(mappings, opts)
-  wk.register(vmappings, vopts)
+  if lvim.builtin.which_key.on_config_done then
+    lvim.builtin.which_key.on_config_done(which_key)
+  end
 end
 
 return M

+ 62 - 0
lua/interface/popup.lua

@@ -0,0 +1,62 @@
+local Popup = {}
+
+--- Create a new floating window
+-- @param config The configuration passed to vim.api.nvim_open_win
+-- @param win_opts The options registered with vim.api.nvim_win_set_option
+-- @param buf_opts The options registered with vim.api.nvim_buf_set_option
+-- @return A new popup
+function Popup:new(opts)
+  opts = opts or {}
+  opts.layout = opts.layout or {}
+  opts.win_opts = opts.win_opts or {}
+  opts.buf_opts = opts.buf_opts or {}
+
+  Popup.__index = Popup
+
+  local editor_layout = {
+    height = vim.o.lines - vim.o.cmdheight - 2, -- Add margin for status and buffer line
+    width = vim.o.columns,
+  }
+  local popup_layout = {
+    relative = "editor",
+    height = math.floor(editor_layout.height * 0.9),
+    width = math.floor(editor_layout.width * 0.8),
+    style = "minimal",
+    border = "rounded",
+  }
+  popup_layout.row = math.floor((editor_layout.height - popup_layout.height) / 2)
+  popup_layout.col = math.floor((editor_layout.width - popup_layout.width) / 2)
+
+  local obj = {
+    buffer = vim.api.nvim_create_buf(false, true),
+    layout = vim.tbl_deep_extend("force", popup_layout, opts.layout),
+    win_opts = opts.win_opts,
+    buf_opts = opts.buf_opts,
+  }
+
+  setmetatable(obj, Popup)
+
+  return obj
+end
+
+--- Display the popup with the provided content
+-- @param content_provider A function accepting the popup's layout and returning the content to display
+function Popup:display(content_provider)
+  self.win_id = vim.api.nvim_open_win(self.buffer, true, self.layout)
+  vim.lsp.util.close_preview_autocmd({ "BufHidden", "BufLeave" }, self.win_id)
+
+  local lines = content_provider(self.layout)
+  vim.api.nvim_buf_set_lines(self.bufnr, 0, -1, false, lines)
+
+  -- window options
+  for key, value in pairs(self.win_opts) do
+    vim.api.nvim_win_set_option(self.win_id, key, value)
+  end
+
+  -- buffer options
+  for key, value in pairs(self.buf_opts) do
+    vim.api.nvim_buf_set_option(self.buffer, key, value)
+  end
+end
+
+return Popup

+ 79 - 0
lua/interface/text.lua

@@ -0,0 +1,79 @@
+local M = {}
+
+local function max_len_line(lines)
+  local max_len = 0
+
+  for _, line in ipairs(lines) do
+    local line_len = line:len()
+    if line_len > max_len then
+      max_len = line_len
+    end
+  end
+
+  return max_len
+end
+
+--- Center align lines relatively to the parent container
+-- @param container The container where lines will be displayed
+-- @param lines The text to align
+-- @param alignment The alignment value, range: [0-1]
+function M.align(container, lines, alignment)
+  local max_len = max_len_line(lines)
+  local indent_amount = math.ceil(math.max(container.width - max_len, 0) * alignment)
+  return M.shift_left(lines, indent_amount)
+end
+
+--- Shift lines by a given amount
+-- @params lines The lines the shift
+-- @param amount The amount of spaces to add
+function M.shift_left(lines, amount)
+  local output = {}
+  local padding = string.rep(" ", amount)
+
+  for _, line in ipairs(lines) do
+    table.insert(output, padding .. line)
+  end
+
+  return output
+end
+
+--- Pretty format tables
+-- @param entries The table to format
+-- @param col_count The number of column to span the table on
+-- @param col_sep The separator between each colummn, default: " "
+function M.format_table(entries, col_count, col_sep)
+  col_sep = col_sep or " "
+
+  local col_rows = math.ceil(vim.tbl_count(entries) / col_count)
+  local cols = {}
+  local count = 0
+
+  for i, entry in ipairs(entries) do
+    if ((i - 1) % col_rows) == 0 then
+      table.insert(cols, {})
+      count = count + 1
+    end
+    table.insert(cols[count], entry)
+  end
+
+  local col_max_len = {}
+  for _, col in ipairs(cols) do
+    table.insert(col_max_len, max_len_line(col))
+  end
+
+  local output = {}
+  for i, col in ipairs(cols) do
+    for j, entry in ipairs(col) do
+      if not output[j] then
+        output[j] = entry
+      else
+        local padding = string.rep(" ", col_max_len[i - 1] - cols[i - 1][j]:len())
+        output[j] = output[j] .. padding .. col_sep .. entry
+      end
+    end
+  end
+
+  return output
+end
+
+return M

+ 11 - 3
lua/keymappings.lua

@@ -8,6 +8,7 @@ local generic_opts = {
   normal_mode = generic_opts_any,
   visual_mode = generic_opts_any,
   visual_block_mode = generic_opts_any,
+  command_mode = generic_opts_any,
   term_mode = { silent = true },
 }
 
@@ -17,6 +18,7 @@ local mode_adapters = {
   term_mode = "t",
   visual_mode = "v",
   visual_block_mode = "x",
+  command_mode = "c",
 }
 
 -- Append key mappings to lunarvim's defaults for a given mode
@@ -142,6 +144,14 @@ function M.config()
       ["<A-j>"] = ":m '>+1<CR>gv-gv",
       ["<A-k>"] = ":m '<-2<CR>gv-gv",
     },
+
+    ---@usage change or add keymappings for command mode
+    command_mode = {
+      -- navigate tab completion with <c-j> and <c-k>
+      -- runs conditionally
+      ["<C-j>"] = { 'pumvisible() ? "\\<C-n>" : "\\<C-j>"', { expr = true, noremap = true } },
+      ["<C-k>"] = { 'pumvisible() ? "\\<C-p>" : "\\<C-k>"', { expr = true, noremap = true } },
+    },
   }
 
   if vim.fn.has "mac" == 1 then
@@ -149,9 +159,7 @@ function M.config()
     lvim.keys.normal_mode["<A-Down>"] = lvim.keys.normal_mode["<C-Down>"]
     lvim.keys.normal_mode["<A-Left>"] = lvim.keys.normal_mode["<C-Left>"]
     lvim.keys.normal_mode["<A-Right>"] = lvim.keys.normal_mode["<C-Right>"]
-    if Log:get_default() then
-      Log:get_default().info "Activated mac keymappings"
-    end
+    Log:debug "Activated mac keymappings"
   end
 end
 

+ 23 - 16
lua/lsp/init.lua

@@ -1,5 +1,6 @@
 local M = {}
 local Log = require "core.log"
+
 function M.config()
   vim.lsp.protocol.CompletionItemKind = lvim.lsp.completion.item_kind
 
@@ -33,13 +34,17 @@ local function lsp_highlight_document(client)
 end
 
 local function add_lsp_buffer_keybindings(bufnr)
-  local wk = require "which-key"
+  local status_ok, wk = pcall(require, "which-key")
+  if not status_ok then
+    return
+  end
+
   local keys = {
     ["K"] = { "<cmd>lua vim.lsp.buf.hover()<CR>", "Show hover" },
     ["gd"] = { "<cmd>lua vim.lsp.buf.definition()<CR>", "Goto Definition" },
     ["gD"] = { "<cmd>lua vim.lsp.buf.declaration()<CR>", "Goto declaration" },
     ["gr"] = { "<cmd>lua vim.lsp.buf.references()<CR>", "Goto references" },
-    ["gi"] = { "<cmd>lua vim.lsp.buf.implementation()<CR>", "Goto implementation" },
+    ["gI"] = { "<cmd>lua vim.lsp.buf.implementation()<CR>", "Goto Implementation" },
     ["gs"] = { "<cmd>lua vim.lsp.buf.signature_help()<CR>", "show signature help" },
     ["gp"] = { "<cmd>lua require'lsp.peek'.Peek('definition')<CR>", "Peek definition" },
     ["gl"] = {
@@ -50,14 +55,6 @@ local function add_lsp_buffer_keybindings(bufnr)
   wk.register(keys, { mode = "n", buffer = bufnr })
 end
 
-local function set_smart_cwd(client)
-  local proj_dir = client.config.root_dir
-  if lvim.lsp.smart_cwd and proj_dir ~= "/" then
-    vim.api.nvim_set_current_dir(proj_dir)
-    require("core.nvimtree").change_tree_dir(proj_dir)
-  end
-end
-
 function M.common_capabilities()
   local capabilities = vim.lsp.protocol.make_client_capabilities()
   capabilities.textDocument.completion.completionItem.snippetSupport = true
@@ -102,14 +99,14 @@ end
 function M.common_on_init(client, bufnr)
   if lvim.lsp.on_init_callback then
     lvim.lsp.on_init_callback(client, bufnr)
-    Log:get_default().info "Called lsp.on_init_callback"
+    Log:debug "Called lsp.on_init_callback"
     return
   end
 
   local formatters = lvim.lang[vim.bo.filetype].formatters
   if not vim.tbl_isempty(formatters) and formatters[1]["exe"] ~= nil and formatters[1].exe ~= "" then
     client.resolved_capabilities.document_formatting = false
-    Log:get_default().info(
+    Log:debug(
       string.format("Overriding language server [%s] with format provider [%s]", client.name, formatters[1].exe)
     )
   end
@@ -118,22 +115,21 @@ end
 function M.common_on_attach(client, bufnr)
   if lvim.lsp.on_attach_callback then
     lvim.lsp.on_attach_callback(client, bufnr)
-    Log:get_default().info "Called lsp.on_init_callback"
+    Log:debug "Called lsp.on_init_callback"
   end
   lsp_highlight_document(client)
   add_lsp_buffer_keybindings(bufnr)
-  set_smart_cwd(client)
   require("lsp.null-ls").setup(vim.bo.filetype)
 end
 
 function M.setup(lang)
+  local lsp_utils = require "lsp.utils"
   local lsp = lvim.lang[lang].lsp
-  if require("utils").check_lsp_client_active(lsp.provider) then
+  if lsp_utils.is_client_active(lsp.provider) then
     return
   end
 
   local overrides = lvim.lsp.override
-
   if type(overrides) == "table" then
     if vim.tbl_contains(overrides, lang) then
       return
@@ -142,6 +138,17 @@ function M.setup(lang)
 
   if lsp.provider ~= nil and lsp.provider ~= "" then
     local lspconfig = require "lspconfig"
+
+    if not lsp.setup.on_attach then
+      lsp.setup.on_attach = M.common_on_attach
+    end
+    if not lsp.setup.on_init then
+      lsp.setup.on_init = M.common_on_init
+    end
+    if not lsp.setup.capabilities then
+      lsp.setup.capabilities = M.common_capabilities()
+    end
+
     lspconfig[lsp.provider].setup(lsp.setup)
   end
 end

+ 0 - 142
lua/lsp/null-ls.lua

@@ -1,142 +0,0 @@
-local M = {}
-local Log = require "core.log"
-
-local null_ls = require "null-ls"
-
-local nodejs_local_providers = { "prettier", "prettierd", "prettier_d_slim", "eslint_d", "eslint" }
-
-M.requested_providers = {}
-
-function M.get_registered_providers_by_filetype(ft)
-  local matches = {}
-  for _, provider in pairs(M.requested_providers) do
-    if vim.tbl_contains(provider.filetypes, ft) then
-      local provider_name = provider.name
-      -- special case: show "eslint_d" instead of eslint
-      -- https://github.com/jose-elias-alvarez/null-ls.nvim/blob/9b8458bd1648e84169a7e8638091ba15c2f20fc0/doc/BUILTINS.md#eslint
-      if string.find(provider._opts.command, "eslint_d") then
-        provider_name = "eslint_d"
-      end
-      table.insert(matches, provider_name)
-    end
-  end
-
-  return matches
-end
-
-function M.get_missing_providers_by_filetype(ft)
-  local matches = {}
-  for _, provider in pairs(M.requested_providers) do
-    if vim.tbl_contains(provider.filetypes, ft) then
-      local provider_name = provider.name
-
-      table.insert(matches, provider_name)
-    end
-  end
-
-  return matches
-end
-
-local function register_failed_request(ft, provider, operation)
-  if not lvim.lang[ft][operation]._failed_requests then
-    lvim.lang[ft][operation]._failed_requests = {}
-  end
-  table.insert(lvim.lang[ft][operation]._failed_requests, provider)
-end
-
-local function validate_nodejs_provider(provider)
-  local command_path
-  local root_dir
-  if lvim.builtin.rooter.active then
-    --- use vim-rooter to set root_dir
-    vim.cmd "let root_dir = FindRootDirectory()"
-    root_dir = vim.api.nvim_get_var "root_dir"
-  else
-    --- use LSP to set root_dir
-    local ts_client = require("utils").get_active_client_by_ft "typescript"
-    if ts_client == nil then
-      Log:get_default().error "Unable to determine root directory since tsserver didn't start correctly"
-      return
-    end
-    root_dir = ts_client.config.root_dir
-  end
-  local local_nodejs_command = root_dir .. "/node_modules/.bin/" .. provider._opts.command
-  Log:get_default().debug("checking for local node module: ", vim.inspect(provider))
-
-  if vim.fn.executable(local_nodejs_command) == 1 then
-    command_path = local_nodejs_command
-  elseif vim.fn.executable(provider._opts.command) == 1 then
-    Log:get_default().debug("checking in global path instead for node module", provider._opts.command)
-    command_path = provider._opts.command
-  else
-    Log:get_default().debug("Unable to find node module", provider._opts.command)
-  end
-  return command_path
-end
-
-local function validate_provider_request(provider)
-  if provider == "" or provider == nil then
-    return
-  end
-  -- NOTE: we can't use provider.name because eslint_d uses eslint name
-  if vim.tbl_contains(nodejs_local_providers, provider._opts.command) then
-    return validate_nodejs_provider(provider)
-  end
-  if vim.fn.executable(provider._opts.command) ~= 1 then
-    Log:get_default().debug("Unable to find the path for", vim.inspect(provider))
-    Log:get_default().warn("Unable to find the path for ", provider._opts.command)
-    return
-  end
-  return provider._opts.command
-end
-
--- TODO: for linters and formatters with spaces and '-' replace with '_'
-function M.setup(filetype)
-  for _, formatter in pairs(lvim.lang[filetype].formatters) do
-    Log:get_default().debug("validating format provider: ", formatter.exe)
-    local builtin_formatter = null_ls.builtins.formatting[formatter.exe]
-    if not vim.tbl_contains(M.requested_providers, builtin_formatter) then
-      -- FIXME: why doesn't this work?
-      -- builtin_formatter._opts.args = formatter.args or builtin_formatter._opts.args
-      -- builtin_formatter._opts.to_stdin = formatter.stdin or builtin_formatter._opts.to_stdin
-      local resolved_path = validate_provider_request(builtin_formatter)
-      if resolved_path then
-        builtin_formatter._opts.command = resolved_path
-        table.insert(M.requested_providers, builtin_formatter)
-        Log:get_default().info("Using format provider", builtin_formatter.name)
-      else
-        -- mark it here to avoid re-doing the lookup again
-        register_failed_request(filetype, formatter.exe, "formatters")
-      end
-    end
-  end
-
-  for _, linter in pairs(lvim.lang[filetype].linters) do
-    local builtin_diagnoser = null_ls.builtins.diagnostics[linter.exe]
-    Log:get_default().debug("validating lint provider: ", linter.exe)
-    -- special case: fallback to "eslint"
-    -- https://github.com/jose-elias-alvarez/null-ls.nvim/blob/9b8458bd1648e84169a7e8638091ba15c2f20fc0/doc/BUILTINS.md#eslint
-    -- if provider.exe
-    if linter.exe == "eslint_d" then
-      builtin_diagnoser = null_ls.builtins.diagnostics.eslint.with { command = "eslint_d" }
-    end
-    if not vim.tbl_contains(M.requested_providers, builtin_diagnoser) then
-      -- FIXME: why doesn't this work?
-      -- builtin_diagnoser._opts.args = linter.args or builtin_diagnoser._opts.args
-      -- builtin_diagnoser._opts.to_stdin = linter.stdin or builtin_diagnoser._opts.to_stdin
-      local resolved_path = validate_provider_request(builtin_diagnoser)
-      if resolved_path then
-        builtin_diagnoser._opts.command = resolved_path
-        table.insert(M.requested_providers, builtin_diagnoser)
-        Log:get_default().info("Using linter provider", builtin_diagnoser.name)
-      else
-        -- mark it here to avoid re-doing the lookup again
-        register_failed_request(filetype, linter.exe, "linters")
-      end
-    end
-  end
-
-  null_ls.register { sources = M.requested_providers }
-end
-
-return M

+ 74 - 0
lua/lsp/null-ls/formatters.lua

@@ -0,0 +1,74 @@
+local M = {}
+local formatters_by_ft = {}
+
+local null_ls = require "null-ls"
+local services = require "lsp.null-ls.services"
+local Log = require "core.log"
+
+local function list_names(formatters, options)
+  options = options or {}
+  local filter = options.filter or "supported"
+
+  return vim.tbl_keys(formatters[filter])
+end
+
+function M.list_supported_names(filetype)
+  if not formatters_by_ft[filetype] then
+    return {}
+  end
+  return list_names(formatters_by_ft[filetype], { filter = "supported" })
+end
+
+function M.list_unsupported_names(filetype)
+  if not formatters_by_ft[filetype] then
+    return {}
+  end
+  return list_names(formatters_by_ft[filetype], { filter = "unsupported" })
+end
+
+function M.list_available(filetype)
+  local formatters = {}
+  for _, provider in pairs(null_ls.builtins.formatting) do
+    -- TODO: Add support for wildcard filetypes
+    if vim.tbl_contains(provider.filetypes or {}, filetype) then
+      table.insert(formatters, provider.name)
+    end
+  end
+
+  return formatters
+end
+
+function M.list_configured(formatter_configs)
+  local formatters, errors = {}, {}
+
+  for _, fmt_config in ipairs(formatter_configs) do
+    local formatter = null_ls.builtins.formatting[fmt_config.exe]
+
+    if not formatter then
+      Log:error("Not a valid formatter:", fmt_config.exe)
+      errors[fmt_config.exe] = {} -- Add data here when necessary
+    else
+      local formatter_cmd = services.find_command(formatter._opts.command)
+      if not formatter_cmd then
+        Log:warn("Not found:", formatter._opts.command)
+        errors[fmt_config.exe] = {} -- Add data here when necessary
+      else
+        Log:debug("Using formatter:", formatter_cmd)
+        formatters[fmt_config.exe] = formatter.with { command = formatter_cmd, extra_args = fmt_config.args }
+      end
+    end
+  end
+
+  return { supported = formatters, unsupported = errors }
+end
+
+function M.setup(filetype, options)
+  if not lvim.lang[filetype] or (formatters_by_ft[filetype] and not options.force_reload) then
+    return
+  end
+
+  formatters_by_ft[filetype] = M.list_configured(lvim.lang[filetype].formatters)
+  null_ls.register { sources = formatters_by_ft[filetype].supported }
+end
+
+return M

+ 44 - 0
lua/lsp/null-ls/init.lua

@@ -0,0 +1,44 @@
+local M = {}
+
+function M.list_supported_provider_names(filetype)
+  local names = {}
+
+  local formatters = require "lsp.null-ls.formatters"
+  local linters = require "lsp.null-ls.linters"
+
+  vim.list_extend(names, formatters.list_supported_names(filetype))
+  vim.list_extend(names, linters.list_supported_names(filetype))
+
+  return names
+end
+
+function M.list_unsupported_provider_names(filetype)
+  local names = {}
+
+  local formatters = require "lsp.null-ls.formatters"
+  local linters = require "lsp.null-ls.linters"
+
+  vim.list_extend(names, formatters.list_unsupported_names(filetype))
+  vim.list_extend(names, linters.list_unsupported_names(filetype))
+
+  return names
+end
+
+-- TODO: for linters and formatters with spaces and '-' replace with '_'
+function M.setup(filetype, options)
+  options = options or {}
+
+  local ok, _ = pcall(require, "null-ls")
+  if not ok then
+    require("core.log"):error "Missing null-ls dependency"
+    return
+  end
+
+  local formatters = require "lsp.null-ls.formatters"
+  local linters = require "lsp.null-ls.linters"
+
+  formatters.setup(filetype, options)
+  linters.setup(filetype, options)
+end
+
+return M

+ 74 - 0
lua/lsp/null-ls/linters.lua

@@ -0,0 +1,74 @@
+local M = {}
+local linters_by_ft = {}
+
+local null_ls = require "null-ls"
+local services = require "lsp.null-ls.services"
+local Log = require "core.log"
+
+local function list_names(linters, options)
+  options = options or {}
+  local filter = options.filter or "supported"
+
+  return vim.tbl_keys(linters[filter])
+end
+
+function M.list_supported_names(filetype)
+  if not linters_by_ft[filetype] then
+    return {}
+  end
+  return list_names(linters_by_ft[filetype], { filter = "supported" })
+end
+
+function M.list_unsupported_names(filetype)
+  if not linters_by_ft[filetype] then
+    return {}
+  end
+  return list_names(linters_by_ft[filetype], { filter = "unsupported" })
+end
+
+function M.list_available(filetype)
+  local linters = {}
+  for _, provider in pairs(null_ls.builtins.diagnostics) do
+    -- TODO: Add support for wildcard filetypes
+    if vim.tbl_contains(provider.filetypes or {}, filetype) then
+      table.insert(linters, provider.name)
+    end
+  end
+
+  return linters
+end
+
+function M.list_configured(linter_configs)
+  local linters, errors = {}, {}
+
+  for _, lnt_config in pairs(linter_configs) do
+    local linter = null_ls.builtins.diagnostics[lnt_config.exe]
+
+    if not linter then
+      Log:error("Not a valid linter:", lnt_config.exe)
+      errors[lnt_config.exe] = {} -- Add data here when necessary
+    else
+      local linter_cmd = services.find_command(linter._opts.command)
+      if not linter_cmd then
+        Log:warn("Not found:", linter._opts.command)
+        errors[lnt_config.exe] = {} -- Add data here when necessary
+      else
+        Log:debug("Using linter:", linter_cmd)
+        linters[lnt_config.exe] = linter.with { command = linter_cmd, extra_args = lnt_config.args }
+      end
+    end
+  end
+
+  return { supported = linters, unsupported = errors }
+end
+
+function M.setup(filetype, options)
+  if not lvim.lang[filetype] or (linters_by_ft[filetype] and not options.force_reload) then
+    return
+  end
+
+  linters_by_ft[filetype] = M.list_configured(lvim.lang[filetype].linters)
+  null_ls.register { sources = linters_by_ft[filetype].supported }
+end
+
+return M

+ 47 - 0
lua/lsp/null-ls/services.lua

@@ -0,0 +1,47 @@
+local M = {}
+
+local function find_root_dir()
+  local util = require "lspconfig/util"
+  local lsp_utils = require "lsp.utils"
+
+  local status_ok, ts_client = lsp_utils.is_client_active "typescript"
+  if status_ok then
+    return ts_client.config.root_dir
+  end
+  local dirname = vim.fn.expand "%:p:h"
+  return util.root_pattern "package.json"(dirname)
+end
+
+local function from_node_modules(command)
+  local root_dir = find_root_dir()
+
+  if not root_dir then
+    return nil
+  end
+
+  return root_dir .. "/node_modules/.bin/" .. command
+end
+
+local local_providers = {
+  prettier = { find = from_node_modules },
+  prettierd = { find = from_node_modules },
+  prettier_d_slim = { find = from_node_modules },
+  eslint_d = { find = from_node_modules },
+  eslint = { find = from_node_modules },
+}
+
+function M.find_command(command)
+  if local_providers[command] then
+    local local_command = local_providers[command].find(command)
+    if local_command and vim.fn.executable(local_command) == 1 then
+      return local_command
+    end
+  end
+
+  if vim.fn.executable(command) == 1 then
+    return command
+  end
+  return nil
+end
+
+return M

+ 28 - 0
lua/lsp/utils.lua

@@ -0,0 +1,28 @@
+local M = {}
+
+function M.is_client_active(name)
+  local clients = vim.lsp.get_active_clients()
+  for _, client in pairs(clients) do
+    if client.name == name then
+      return true, client
+    end
+  end
+  return false
+end
+
+-- FIXME: this should return a list instead
+function M.get_active_client_by_ft(filetype)
+  if not lvim.lang[filetype] or not lvim.lang[filetype].lsp then
+    return nil
+  end
+
+  local clients = vim.lsp.get_active_clients()
+  for _, client in pairs(clients) do
+    if client.name == lvim.lang[filetype].lsp.provider then
+      return client
+    end
+  end
+  return nil
+end
+
+return M

+ 4 - 7
lua/plugin-loader.lua

@@ -1,13 +1,10 @@
 local plugin_loader = {}
 
 function plugin_loader:init()
-  local execute = vim.api.nvim_command
-  local fn = vim.fn
-
   local install_path = "~/.local/share/lunarvim/site/pack/packer/start/packer.nvim"
-  if fn.empty(fn.glob(install_path)) > 0 then
-    execute("!git clone https://github.com/wbthomason/packer.nvim " .. install_path)
-    execute "packadd packer.nvim"
+  if vim.fn.empty(vim.fn.glob(install_path)) > 0 then
+    vim.fn.system { "git", "clone", "https://github.com/wbthomason/packer.nvim", install_path }
+    vim.cmd "packadd packer.nvim"
   end
 
   local packer_ok, packer = pcall(require, "packer")
@@ -23,7 +20,7 @@ function plugin_loader:init()
     git = { clone_timeout = 300 },
     display = {
       open_fn = function()
-        return util.float { border = "single" }
+        return util.float { border = "rounded" }
       end,
     },
   }

+ 22 - 63
lua/plugins.lua

@@ -4,15 +4,13 @@ return {
   { "neovim/nvim-lspconfig" },
   { "tamago324/nlsp-settings.nvim" },
   { "jose-elias-alvarez/null-ls.nvim" },
+  { "antoinemadec/FixCursorHold.nvim" }, -- Needed while issue https://github.com/neovim/neovim/issues/12587 is still open
   {
     "kabouzeid/nvim-lspinstall",
     event = "VimEnter",
     config = function()
-      local lspinstall = require "lspinstall"
+      local lspinstall = require "core.lspinstall"
       lspinstall.setup()
-      if lvim.builtin.lspinstall.on_config_done then
-        lvim.builtin.lspinstall.on_config_done(lspinstall)
-      end
     end,
   },
 
@@ -23,10 +21,8 @@ return {
     "nvim-telescope/telescope.nvim",
     config = function()
       require("core.telescope").setup()
-      if lvim.builtin.telescope.on_config_done then
-        lvim.builtin.telescope.on_config_done(require "telescope")
-      end
     end,
+    disable = not lvim.builtin.telescope.active,
   },
 
   -- Completion & Snippets
@@ -35,10 +31,8 @@ return {
     event = "InsertEnter",
     config = function()
       require("core.compe").setup()
-      if lvim.builtin.compe.on_config_done then
-        lvim.builtin.compe.on_config_done(require "compe")
-      end
     end,
+    disable = not lvim.builtin.compe.active,
     -- wants = "vim-vsnip",
     -- requires = {
     -- {
@@ -56,10 +50,12 @@ return {
     "hrsh7th/vim-vsnip",
     -- wants = "friendly-snippets",
     event = "InsertEnter",
+    disable = not lvim.builtin.compe.active,
   },
   {
     "rafamadriz/friendly-snippets",
     event = "InsertCharPre",
+    disable = not lvim.builtin.compe.active,
   },
 
   -- Autopairs
@@ -68,11 +64,9 @@ return {
     -- event = "InsertEnter",
     after = "nvim-compe",
     config = function()
-      require "core.autopairs"
-      if lvim.builtin.autopairs.on_config_done then
-        lvim.builtin.autopairs.on_config_done(require "nvim-autopairs")
-      end
+      require("core.autopairs").setup()
     end,
+    disable = not lvim.builtin.autopairs.active or not lvim.builtin.compe.active,
   },
 
   -- Treesitter
@@ -82,9 +76,6 @@ return {
     -- run = ":TSUpdate",
     config = function()
       require("core.treesitter").setup()
-      if lvim.builtin.treesitter.on_config_done then
-        lvim.builtin.treesitter.on_config_done(require "nvim-treesitter.configs")
-      end
     end,
   },
 
@@ -96,10 +87,8 @@ return {
     -- commit = "fd7f60e242205ea9efc9649101c81a07d5f458bb",
     config = function()
       require("core.nvimtree").setup()
-      if lvim.builtin.nvimtree.on_config_done then
-        lvim.builtin.nvimtree.on_config_done(require "nvim-tree.config")
-      end
     end,
+    disable = not lvim.builtin.nvimtree.active,
   },
 
   {
@@ -107,11 +96,9 @@ return {
 
     config = function()
       require("core.gitsigns").setup()
-      if lvim.builtin.gitsigns.on_config_done then
-        lvim.builtin.gitsigns.on_config_done(require "gitsigns")
-      end
     end,
     event = "BufRead",
+    disable = not lvim.builtin.gitsigns.active,
   },
 
   -- Whichkey
@@ -119,11 +106,9 @@ return {
     "folke/which-key.nvim",
     config = function()
       require("core.which-key").setup()
-      if lvim.builtin.which_key.on_config_done then
-        lvim.builtin.which_key.on_config_done(require "which-key")
-      end
     end,
     event = "BufWinEnter",
+    disable = not lvim.builtin.which_key.active,
   },
 
   -- Comments
@@ -131,30 +116,18 @@ return {
     "terrortylor/nvim-comment",
     event = "BufRead",
     config = function()
-      local status_ok, nvim_comment = pcall(require, "nvim_comment")
-      if not status_ok then
-        local Log = require "core.log"
-        Log:get_default().error "Failed to load nvim-comment"
-        return
-      end
-      nvim_comment.setup()
-      if lvim.builtin.comment.on_config_done then
-        lvim.builtin.comment.on_config_done(nvim_comment)
-      end
+      require("nvim_comment").setup()
     end,
+    disable = not lvim.builtin.comment.active,
   },
 
-  -- vim-rooter
+  -- project.nvim
   {
-    "airblade/vim-rooter",
-    -- event = "BufReadPre",
+    "ahmedkhalf/project.nvim",
     config = function()
-      require("core.rooter").setup()
-      if lvim.builtin.rooter.on_config_done then
-        lvim.builtin.rooter.on_config_done()
-      end
+      require("core.project").setup()
     end,
-    disable = not lvim.builtin.rooter.active,
+    disable = not lvim.builtin.project.active,
   },
 
   -- Icons
@@ -162,24 +135,19 @@ return {
 
   -- Status Line and Bufferline
   {
-    "glepnir/galaxyline.nvim",
+    -- "hoob3rt/lualine.nvim",
+    "shadmansaleh/lualine.nvim",
+    -- "Lunarvim/lualine.nvim",
     config = function()
-      require "core.galaxyline"
-      if lvim.builtin.galaxyline.on_config_done then
-        lvim.builtin.galaxyline.on_config_done(require "galaxyline")
-      end
+      require("core.lualine").setup()
     end,
-    event = "BufWinEnter",
-    disable = not lvim.builtin.galaxyline.active,
+    disable = not lvim.builtin.lualine.active,
   },
 
   {
     "romgrk/barbar.nvim",
     config = function()
       require("core.bufferline").setup()
-      if lvim.builtin.bufferline.on_config_done then
-        lvim.builtin.bufferline.on_config_done()
-      end
     end,
     event = "BufWinEnter",
     disable = not lvim.builtin.bufferline.active,
@@ -191,9 +159,6 @@ return {
     -- event = "BufWinEnter",
     config = function()
       require("core.dap").setup()
-      if lvim.builtin.dap.on_config_done then
-        lvim.builtin.dap.on_config_done(require "dap")
-      end
     end,
     disable = not lvim.builtin.dap.active,
   },
@@ -212,9 +177,6 @@ return {
     event = "BufWinEnter",
     config = function()
       require("core.dashboard").setup()
-      if lvim.builtin.dashboard.on_config_done then
-        lvim.builtin.dashboard.on_config_done(require "dashboard")
-      end
     end,
     disable = not lvim.builtin.dashboard.active,
   },
@@ -225,9 +187,6 @@ return {
     event = "BufWinEnter",
     config = function()
       require("core.terminal").setup()
-      if lvim.builtin.terminal.on_config_done then
-        lvim.builtin.terminal.on_config_done(require "toggleterm")
-      end
     end,
     disable = not lvim.builtin.terminal.active,
   },

+ 10 - 57
lua/utils/init.lua

@@ -70,9 +70,7 @@ function utils.toggle_autoformat()
         },
       },
     }
-    if Log:get_default() then
-      Log:get_default().info "Format on save active"
-    end
+    Log:debug "Format on save active"
   end
 
   if not lvim.format_on_save then
@@ -81,15 +79,16 @@ function utils.toggle_autoformat()
         :autocmd! autoformat
       endif
     ]]
-    if Log:get_default() then
-      Log:get_default().info "Format on save off"
-    end
+    Log:debug "Format on save off"
   end
 end
 
 function utils.reload_lv_config()
-  vim.cmd "source ~/.local/share/lunarvim/lvim/lua/settings.lua"
-  vim.cmd("source " .. USER_CONFIG_PATH)
+  require("core.lualine").config()
+
+  local config = require "config"
+  config:load()
+
   require("keymappings").setup() -- this should be done before loading the plugins
   vim.cmd "source ~/.local/share/lunarvim/lvim/lua/plugins.lua"
   local plugins = require "plugins"
@@ -99,55 +98,9 @@ function utils.reload_lv_config()
   vim.cmd ":PackerCompile"
   vim.cmd ":PackerInstall"
   -- vim.cmd ":PackerClean"
-  Log:get_default().info "Reloaded configuration"
-end
-
-function utils.check_lsp_client_active(name)
-  local clients = vim.lsp.get_active_clients()
-  for _, client in pairs(clients) do
-    if client.name == name then
-      return true
-    end
-  end
-  return false
-end
-
-function utils.get_active_client_by_ft(filetype)
-  local clients = vim.lsp.get_active_clients()
-  for _, client in pairs(clients) do
-    if client.name == lvim.lang[filetype].lsp.provider then
-      return client
-    end
-  end
-  return nil
-end
-
--- TODO: consider porting this logic to null-ls instead
-function utils.get_supported_linters_by_filetype(filetype)
-  local null_ls = require "null-ls"
-  local matches = {}
-  for _, provider in pairs(null_ls.builtins.diagnostics) do
-    if vim.tbl_contains(provider.filetypes, filetype) then
-      local provider_name = provider.name
-
-      table.insert(matches, provider_name)
-    end
-  end
-
-  return matches
-end
-
-function utils.get_supported_formatters_by_filetype(filetype)
-  local null_ls = require "null-ls"
-  local matches = {}
-  for _, provider in pairs(null_ls.builtins.formatting) do
-    if provider.filetypes and vim.tbl_contains(provider.filetypes, filetype) then
-      -- table.insert(matches, { provider.name, ft })
-      table.insert(matches, provider.name)
-    end
-  end
-
-  return matches
+  local null_ls = require "lsp.null-ls"
+  null_ls.setup(vim.bo.filetype, { force_reload = true })
+  Log:info "Reloaded configuration"
 end
 
 function utils.unrequire(m)

+ 0 - 9
utils/bin/install-latest-neovim

@@ -1,9 +0,0 @@
-!#/bin/bash
-cd ~
-sudo rm -r neovim
-git clone --branch master --depth 1 https://github.com/neovim/neovim
-cd neovim
-sudo make CMAKE_BUILD_TYPE=Release install
-cd ~
-sudo rm -r neovim
-

+ 4 - 1
utils/bin/lvim

@@ -1,3 +1,6 @@
 #!/bin/sh
 
-exec nvim -u ~/.local/share/lunarvim/lvim/init.lua --cmd "set runtimepath+=~/.local/share/lunarvim/lvim" "$@"
+export LUNARVIM_RUNTIME_DIR="${LUNARVIM_RUNTIME_DIR:-$HOME/.local/share/lunarvim}"
+export LUNARVIM_CONFIG_DIR="${LUNARVIM_RUNTIME_DIR:-$HOME/.config/lvim}"
+
+exec nvim -u "$LUNARVIM_RUNTIME_DIR/lvim/init.lua" "$@"

+ 29 - 1
utils/installer/config.example-no-ts.lua

@@ -14,8 +14,21 @@ lvim.keys.normal_mode["<C-s>"] = ":w<cr>"
 -- edit a default keymapping
 -- lvim.keys.normal_mode["<C-q>"] = ":q<cr>"
 
+-- Change Telescope navigation to use j and k for navigation and n and p for history in both input and normal mode.
+-- lvim.builtin.telescope.on_config_done = function()
+--   local actions = require "telescope.actions"
+--   -- for input mode
+--   lvim.builtin.telescope.defaults.mappings.i["<C-j>"] = actions.move_selection_next
+--   lvim.builtin.telescope.defaults.mappings.i["<C-k>"] = actions.move_selection_previous
+--   lvim.builtin.telescope.defaults.mappings.i["<C-n>"] = actions.cycle_history_next
+--   lvim.builtin.telescope.defaults.mappings.i["<C-p>"] = actions.cycle_history_prev
+--   -- for normal mode
+--   lvim.builtin.telescope.defaults.mappings.n["<C-j>"] = actions.move_selection_next
+--   lvim.builtin.telescope.defaults.mappings.n["<C-k>"] = actions.move_selection_previous
+-- end
+
 -- Use which-key to add extra bindings with the leader-key prefix
--- lvim.builtin.which_key.mappings["P"] = { "<cmd>lua require'telescope'.extensions.project.project{}<CR>", "Projects" }
+-- lvim.builtin.which_key.mappings["P"] = { "<cmd>Telescope projects<CR>", "Projects" }
 -- lvim.builtin.which_key.mappings["t"] = {
 --   name = "+Trouble",
 --   r = { "<cmd>Trouble lsp_references<cr>", "References" },
@@ -48,6 +61,21 @@ lvim.builtin.treesitter.highlight.enabled = true
 --   --Enable completion triggered by <c-x><c-o>
 --   buf_set_option("omnifunc", "v:lua.vim.lsp.omnifunc")
 -- end
+-- you can overwrite the null_ls setup table (useful for setting the root_dir function)
+-- lvim.lsp.null_ls.setup = {
+--   root_dir = require("lspconfig").util.root_pattern("Makefile", ".git", "node_modules"),
+-- }
+-- or if you need something more advanced
+-- lvim.lsp.null_ls.setup.root_dir = function(fname)
+--   if vim.bo.filetype == "javascript" then
+--     return require("lspconfig/util").root_pattern("Makefile", ".git", "node_modules")(fname)
+--       or require("lspconfig/util").path.dirname(fname)
+--   elseif vim.bo.filetype == "php" then
+--     return require("lspconfig/util").root_pattern("Makefile", ".git", "composer.json")(fname) or vim.fn.getcwd()
+--   else
+--     return require("lspconfig/util").root_pattern("Makefile", ".git")(fname) or require("lspconfig/util").path.dirname(fname)
+--   end
+-- end
 
 -- set a formatter if you want to override the default lsp one (if it exists)
 -- lvim.lang.python.formatters = {

+ 29 - 1
utils/installer/config.example.lua

@@ -23,8 +23,21 @@ lvim.keys.normal_mode["<C-s>"] = ":w<cr>"
 -- edit a default keymapping
 -- lvim.keys.normal_mode["<C-q>"] = ":q<cr>"
 
+-- Change Telescope navigation to use j and k for navigation and n and p for history in both input and normal mode.
+-- lvim.builtin.telescope.on_config_done = function()
+--   local actions = require "telescope.actions"
+--   -- for input mode
+--   lvim.builtin.telescope.defaults.mappings.i["<C-j>"] = actions.move_selection_next
+--   lvim.builtin.telescope.defaults.mappings.i["<C-k>"] = actions.move_selection_previous
+--   lvim.builtin.telescope.defaults.mappings.i["<C-n>"] = actions.cycle_history_next
+--   lvim.builtin.telescope.defaults.mappings.i["<C-p>"] = actions.cycle_history_prev
+--   -- for normal mode
+--   lvim.builtin.telescope.defaults.mappings.n["<C-j>"] = actions.move_selection_next
+--   lvim.builtin.telescope.defaults.mappings.n["<C-k>"] = actions.move_selection_previous
+-- end
+
 -- Use which-key to add extra bindings with the leader-key prefix
--- lvim.builtin.which_key.mappings["P"] = { "<cmd>lua require'telescope'.extensions.project.project{}<CR>", "Projects" }
+-- lvim.builtin.which_key.mappings["P"] = { "<cmd>Telescope projects<CR>", "Projects" }
 -- lvim.builtin.which_key.mappings["t"] = {
 --   name = "+Trouble",
 --   r = { "<cmd>Trouble lsp_references<cr>", "References" },
@@ -57,6 +70,21 @@ lvim.builtin.treesitter.highlight.enabled = true
 --   --Enable completion triggered by <c-x><c-o>
 --   buf_set_option("omnifunc", "v:lua.vim.lsp.omnifunc")
 -- end
+-- you can overwrite the null_ls setup table (useful for setting the root_dir function)
+-- lvim.lsp.null_ls.setup = {
+--   root_dir = require("lspconfig").util.root_pattern("Makefile", ".git", "node_modules"),
+-- }
+-- or if you need something more advanced
+-- lvim.lsp.null_ls.setup.root_dir = function(fname)
+--   if vim.bo.filetype == "javascript" then
+--     return require("lspconfig/util").root_pattern("Makefile", ".git", "node_modules")(fname)
+--       or require("lspconfig/util").path.dirname(fname)
+--   elseif vim.bo.filetype == "php" then
+--     return require("lspconfig/util").root_pattern("Makefile", ".git", "composer.json")(fname) or vim.fn.getcwd()
+--   else
+--     return require("lspconfig/util").root_pattern("Makefile", ".git")(fname) or require("lspconfig/util").path.dirname(fname)
+--   end
+-- end
 
 -- set a formatter if you want to override the default lsp one (if it exists)
 -- lvim.lang.python.formatters = {

+ 83 - 0
utils/installer/install-neovim-from-release

@@ -0,0 +1,83 @@
+#!/usr/bin/env bash
+
+set -eu pipefall
+
+declare -r LV_INSTALL_PREFIX="${INSTALL_PREFIX:-"$HOME/.local"}"
+declare -r RELEASE_VER="${RELEASE_VER:-latest}" # can be set to nightly
+
+declare ARCHIVE_NAME
+declare RELEASE_NAME
+declare OS
+
+OS="$(uname -s)"
+
+if [ "$OS" == "Linux" ]; then
+  ARCHIVE_NAME="nvim-linux64"
+  RELEASE_NAME="nvim-linux64"
+elif [ "$OS" == "Darwin" ]; then
+  ARCHIVE_NAME="nvim-macos"
+  # for some reason the archive has a different name
+  RELEASE_NAME="nvim-osx64"
+else
+  echo "$OS platform is not supported currently"
+  exit 1
+fi
+
+declare -r RELEASE_URL="https://github.com/neovim/neovim/releases/$RELEASE_VER/download/$ARCHIVE_NAME.tar.gz"
+declare -r CHECKSUM_URL="$RELEASE_URL.sha256sum"
+
+DOWNLOAD_DIR="$(mktemp -d)"
+readonly DOWNLOAD_DIR
+
+RELEASE_SHA="$(curl -Ls "$CHECKSUM_URL" | awk '{print $1}')"
+readonly RELEASE_SHA
+
+function main() {
+  if [ ! -d "$LV_INSTALL_PREFIX" ]; then
+    mkdir -p "$LV_INSTALL_PREFIX" || __invalid__prefix__handler
+  fi
+  download_neovim
+  verify_neovim
+  install_neovim
+}
+
+function download_neovim() {
+  echo "Downloading Neovim's binary from $RELEASE_VER release.."
+  if ! curl --progress-bar --fail -L "$RELEASE_URL" -o "$DOWNLOAD_DIR/$ARCHIVE_NAME.tar.gz"; then
+    echo "Download failed.  Check that the release/filename are correct."
+    exit 1
+  fi
+  echo "Download complete!"
+}
+
+function verify_neovim() {
+  echo "Verifying the installation.."
+  DOWNLOADED_SHA="$(sha256sum "$DOWNLOAD_DIR/$ARCHIVE_NAME.tar.gz" | awk '{print $1}')"
+
+  if [ "$RELEASE_SHA" != "$DOWNLOADED_SHA" ]; then
+    echo "Error! checksum mis-match."
+    echo "Expected: $RELEASE_SHA but got: $DOWNLOADED_SHA"
+    exit 1
+  fi
+  echo "Verification complete!"
+}
+
+function install_neovim() {
+
+  echo "Installing Neovim.."
+  pushd "$DOWNLOAD_DIR"
+  tar -xzf "$DOWNLOAD_DIR/$ARCHIVE_NAME.tar.gz"
+  popd
+  # https://dev.to/ackshaey/macos-vs-linux-the-cp-command-will-trip-you-up-2p00
+  cp -r "$DOWNLOAD_DIR/$RELEASE_NAME/." "$LV_INSTALL_PREFIX"
+  echo "Installation complete!"
+  echo "Now you can run $LV_INSTALL_PREFIX/bin/nvim"
+}
+
+function __invalid__prefix__handler() {
+  echo "Error! Invalid value for LV_INSTALL_PREFIX: [$INSTALL_PREFIX]"
+  echo "Please verify that the folder exists and re-run the installer!"
+  exit 1
+}
+
+main "$@"

+ 244 - 227
utils/installer/install.sh

@@ -1,272 +1,289 @@
-#!/bin/sh
-#Set Variable to master is not set differently
-LVBRANCH="${LVBRANCH:-master}"
-USER_BIN_DIR="/usr/local/bin"
-set -o nounset # error when referencing undefined variable
-set -o errexit # exit when command fails
-
-installnodemac() {
-	brew install lua
-	brew install node
-	brew install yarn
-}
+#!/usr/bin/env bash
+set -eo pipefail
 
-installnodeubuntu() {
-	sudo apt install nodejs
-	sudo apt install npm
-}
+#Set branch to master unless specified by the user
+declare -r LV_BRANCH="${LV_BRANCH:-rolling}"
+declare -r LV_REMOTE="${LV_REMOTE:-lunarvim/lunarvim.git}"
+declare -r INSTALL_PREFIX="${INSTALL_PREFIX:-"$HOME/.local"}"
+
+declare -r XDG_DATA_HOME="${XDG_DATA_HOME:-"$HOME/.local/share"}"
+declare -r XDG_CACHE_HOME="${XDG_CACHE_HOME:-"$HOME/.cache"}"
+declare -r XDG_CONFIG_HOME="${XDG_CONFIG_HOME:-"$HOME/.config"}"
 
-installnodetermux() {
-	apt install nodejs
-}
+# TODO: Use a dedicated cache directory #1256
+declare -r NEOVIM_CACHE_DIR="$XDG_CACHE_HOME/nvim"
 
-moveoldlvim() {
-	echo "Not installing LunarVim"
-	echo "Please move your ~/.local/share/lunarvim folder before installing"
-	exit
-}
+declare -r LUNARVIM_RUNTIME_DIR="${LUNARVIM_RUNTIME_DIR:-"$XDG_DATA_HOME/lunarvim"}"
+declare -r LUNARVIM_CONFIG_DIR="${LUNARVIM_CONFIG_DIR:-"$XDG_CONFIG_HOME/lvim"}"
 
-installnodearch() {
-	sudo pacman -S nodejs
-	sudo pacman -S npm
-}
+declare -a __lvim_dirs=(
+  "$LUNARVIM_CONFIG_DIR"
+  "$LUNARVIM_RUNTIME_DIR"
+  "$NEOVIM_CACHE_DIR" # for now this is shared with neovim
+)
+
+declare -a __npm_deps=(
+  "neovim"
+  "tree-sitter-cli"
+)
+
+declare -a __pip_deps=(
+  "pynvim"
+)
+
+function main() {
+  cat <<'EOF'
+
+      88\                                                   88\               
+      88 |                                                  \__|              
+      88 |88\   88\ 888888$\   888888\   888888\ 88\    88\ 88\ 888888\8888\  
+      88 |88 |  88 |88  __88\  \____88\ 88  __88\\88\  88  |88 |88  _88  _88\ 
+      88 |88 |  88 |88 |  88 | 888888$ |88 |  \__|\88\88  / 88 |88 / 88 / 88 |
+      88 |88 |  88 |88 |  88 |88  __88 |88 |       \88$  /  88 |88 | 88 | 88 |
+      88 |\888888  |88 |  88 |\888888$ |88 |        \$  /   88 |88 | 88 | 88 |
+      \__| \______/ \__|  \__| \_______|\__|         \_/    \__|\__| \__| \__|
+
+EOF
+
+  __add_separator "80"
+
+  echo "Detecting platform for managing any additional neovim dependencies"
+  detect_platform
+
+  if [ -n "$GITHUB_ACTIONS" ]; then
+    install_packer
+    setup_lvim
+    exit 0
+  fi
+
+  check_system_deps
+
+  __add_separator "80"
+
+  echo "Would you like to check lunarvim's NodeJS dependencies?"
+  read -p "[y]es or [n]o (default: no) : " -r answer
+  [ "$answer" != "${answer#[Yy]}" ] && install_nodejs_deps
+
+  echo "Would you like to check lunarvim's Python dependencies?"
+  read -p "[y]es or [n]o (default: no) : " -r answer
+  [ "$answer" != "${answer#[Yy]}" ] && install_python_deps
+
+  echo "Would you like to check lunarvim's Rust dependencies?"
+  read -p "[y]es or [n]o (default: no) : " -r answer
+  [ "$answer" != "${answer#[Yy]}" ] && install_rust_deps
+
+  __add_separator "80"
+
+  echo "Backing up old LunarVim configuration"
+  backup_old_config
+
+  __add_separator "80"
+
+  case "$@" in
+    *--overwrite*)
+      echo "!!Warning!! -> Removing all lunarvim related config \
+        because of the --overwrite flag"
+      read -p "Would you like to continue? [y]es or [n]o : " -r answer
+      [ "$answer" == "${answer#[Yy]}" ] && exit 1
+      for dir in "${__lvim_dirs[@]}"; do
+        [ -d "$dir" ] && rm -rf "$dir"
+      done
+      ;;
+  esac
+
+  if [ -e "$LUNARVIM_RUNTIME_DIR/site/pack/packer/start/packer.nvim" ]; then
+    echo "Packer already installed"
+  else
+    install_packer
+  fi
+
+  __add_separator "80"
+
+  if [ -e "$LUNARVIM_RUNTIME_DIR/lvim/init.lua" ]; then
+    echo "Updating LunarVim"
+    update_lvim
+  else
+    clone_lvim
+    setup_lvim
+  fi
+
+  __add_separator "80"
 
-installnodefedora() {
-	sudo dnf install -y nodejs
-	sudo dnf install -y npm
 }
 
-installnodegentoo() {
-	echo "Printing current node status..."
-	emerge -pqv net-libs/nodejs
-	echo "Make sure the npm USE flag is enabled for net-libs/nodejs"
-	echo "If it isn't enabled, would you like to enable it with flaggie? (Y/N)"
-	read -r answer
-	[ "$answer" != "${answer#[Yy]}" ] && sudo flaggie net-libs/nodejs +npm
-	sudo emerge -avnN net-libs/nodejs
+function detect_platform() {
+  OS="$(uname -s)"
+  case "$OS" in
+    Linux)
+      if [ -f "/etc/arch-release" ] || [ -f "/etc/artix-release" ]; then
+        RECOMMEND_INSTALL="sudo pacman -S"
+      elif [ -f "/etc/fedora-release" ] || [ -f "/etc/redhat-release" ]; then
+        RECOMMEND_INSTALL="sudo dnf install -y"
+      elif [ -f "/etc/gentoo-release" ]; then
+        RECOMMEND_INSTALL="emerge install -y"
+      else # assume debian based
+        RECOMMEND_INSTALL="sudo apt install -y"
+      fi
+      ;;
+    Darwin)
+      RECOMMEND_INSTALL="brew install"
+      ;;
+    *)
+      echo "OS $OS is not currently supported."
+      exit 1
+      ;;
+  esac
 }
 
-installnode() {
-	echo "Installing node..."
-	[ "$(uname)" = "Darwin" ] && installnodemac
-	grep -q Ubuntu /etc/os-release && installnodeubuntu
-	[ -f "/etc/arch-release" ] && installnodearch
-	[ -f "/etc/artix-release" ] && installnodearch
-	[ -f "/etc/fedora-release" ] && installnodefedora
-	[ -f "/etc/gentoo-release" ] && installnodegentoo
-	[ -d "/data/data/com.termux" ] && installnodetermux
-	[ "$(uname -s | cut -c 1-10)" = "MINGW64_NT" ] && echo "Windows not currently supported"
-	sudo npm i -g neovim
+function print_missing_dep_msg() {
+  echo "[ERROR]: Unable to find dependency [$1]"
+  echo "Please install it first and re-run the installer. Try: $RECOMMEND_INSTALL $1"
 }
 
-installpiponmac() {
-	sudo curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py
-	python3 get-pip.py
-	rm get-pip.py
+function check_dep() {
+  if ! command -v "$1" &>/dev/null; then
+    print_missing_dep_msg "$1"
+    exit 1
+  fi
 }
 
-installpiponubuntu() {
-	sudo apt install python3-pip >/dev/null
+function check_system_deps() {
+  if ! command -v git &>/dev/null; then
+    print_missing_dep_msg "git"
+    exit 1
+  fi
+  if ! command -v nvim &>/dev/null; then
+    print_missing_dep_msg "neovim"
+    exit 1
+  fi
 }
 
-installpipontermux() {
-	apt install python
+function install_nodejs_deps() {
+  check_dep "npm"
+  echo "Installing node modules with npm.."
+  for dep in "${__npm_deps[@]}"; do
+    if ! npm ls -g "$dep" &>/dev/null; then
+      printf "installing %s .." "$dep"
+      npm install -g "$dep"
+    fi
+  done
+  echo "All NodeJS dependencies are succesfully installed"
 }
 
-installpiponarch() {
-	sudo pacman -S python-pip
+function install_python_deps() {
+  echo "Verifying that pip is available.."
+  if ! python3 -m ensurepip &>/dev/null; then
+    if ! python3 -m pip --version &>/dev/null; then
+      print_missing_dep_msg "pip"
+      exit 1
+    fi
+  fi
+  echo "Installing with pip.."
+  for dep in "${__pip_deps[@]}"; do
+    python3 -m pip install --user "$dep"
+  done
+  echo "All Python dependencies are succesfully installed"
 }
 
-installpiponfedora() {
-	sudo dnf install -y pip >/dev/null
+function __attempt_to_install_with_cargo() {
+  if ! command -v cargo &>/dev/null; then
+    echo "Installing missing Rust dependency with cargo"
+    cargo install "$1"
+  else
+    echo "[WARN]: Unable to find fd. Make sure to install it to avoid any problems"
+  fi
 }
 
-installpipongentoo() {
-	sudo emerge -avn dev-python/pip
+# we try to install the missing one with cargo even though it's unlikely to be found
+function install_rust_deps() {
+  if ! command -v fd &>/dev/null; then
+    __attempt_to_install_with_cargo "fd-find"
+  fi
+  if ! command -v rg &>/dev/null; then
+    __attempt_to_install_with_cargo "ripgrep"
+  fi
+  echo "All Rust dependencies are succesfully installed"
 }
 
-installpip() {
-	echo "Installing pip..."
-	[ "$(uname)" = "Darwin" ] && installpiponmac
-	grep -q Ubuntu /etc/os-release && installpiponubuntu
-	[ -f "/etc/arch-release" ] && installpiponarch
-	[ -f "/etc/fedora-release" ] && installpiponfedora
-	[ -f "/etc/gentoo-release" ] && installpipongentoo
-	[ -d "/data/data/com.termux" ] && installpipontermux
-	[ "$(uname -s | cut -c 1-10)" = "MINGW64_NT" ] && echo "Windows not currently supported"
+function backup_old_config() {
+  for dir in "${__lvim_dirs[@]}"; do
+    # we create an empty folder for subsequent commands \
+    # that require an existing directory
+    mkdir -p "$dir" "$dir.bak"
+    if command -v rsync &>/dev/null; then
+      rsync --archive -hh --partial --progress --cvs-exclude \
+        --modify-window=1 "$dir"/ "$dir.bak"
+    else
+      cp -R "$dir/*" "$dir.bak/."
+    fi
+  done
+  echo "Backup operation complete"
 }
 
-installpynvim() {
-	echo "Installing pynvim..."
-	if [ -f "/etc/gentoo-release" ]; then
-		echo "Installing using Portage"
-		sudo emerge -avn dev-python/pynvim
-	else
-		pip3 install pynvim --user
-	fi
+function install_packer() {
+  git clone --depth 1 https://github.com/wbthomason/packer.nvim \
+    "$LUNARVIM_RUNTIME_DIR/site/pack/packer/start/packer.nvim"
 }
 
-installpacker() {
-	git clone https://github.com/wbthomason/packer.nvim ~/.local/share/lunarvim/site/pack/packer/start/packer.nvim
+function clone_lvim() {
+  echo "Cloning LunarVim configuration"
+  if ! git clone --branch "$LV_BRANCH" \
+    --depth 1 "https://github.com/${LV_REMOTE}" "$LUNARVIM_RUNTIME_DIR/lvim"; then
+    echo "Failed to clone repository. Installation failed."
+    exit 1
+  fi
 }
 
-cloneconfig() {
-	if [ -d "/data/data/com.termux" ]; then
-		sudo() {
-			eval "$@"
-		}
-		USER_BIN_DIR="$HOME/../usr/bin"
-	fi
-	echo "Cloning LunarVim configuration"
-	mkdir -p ~/.local/share/lunarvim
-	case "$@" in
-
-	*--testing*)
-		cp -r "$(pwd)" ~/.local/share/lunarvim/lvim
-		;;
-	*)
-		git clone --branch "$LVBRANCH" https://github.com/lunarvim/lunarvim.git ~/.local/share/lunarvim/lvim
-		;;
-	esac
-	mkdir -p "$HOME/.config/lvim"
-	sudo cp "$HOME/.local/share/lunarvim/lvim/utils/bin/lvim" "$USER_BIN_DIR"
-	sudo chmod a+rx "$USER_BIN_DIR"/lvim
-	cp "$HOME/.local/share/lunarvim/lvim/utils/installer/config.example-no-ts.lua" "$HOME/.config/lvim/config.lua"
-
-	nvim -u ~/.local/share/lunarvim/lvim/init.lua --cmd "set runtimepath+=~/.local/share/lunarvim/lvim" --headless \
-		+'autocmd User PackerComplete sleep 100m | qall' \
-		+PackerInstall
-
-	nvim -u ~/.local/share/lunarvim/lvim/init.lua --cmd "set runtimepath+=~/.local/share/lunarvim/lvim" --headless \
-		+'autocmd User PackerComplete sleep 100m | qall' \
-		+PackerSync
-
-	printf "\nCompile Complete\n"
-
-	if [ -e "$HOME/.local/share/lunarvim/lvim/init.lua" ]; then
-		echo 'config.lua already present'
-	else
-		cp "$HOME/.local/share/lunarvim/lvim/utils/installer/config.example.lua" "$HOME/.config/lvim/config.lua"
-	fi
+function setup_shim() {
+  if [ ! -d "$INSTALL_PREFIX/bin" ]; then
+    mkdir -p "$INSTALL_PREFIX/bin"
+  fi
+  cat >"$INSTALL_PREFIX/bin/lvim" <<EOF
+#!/bin/sh
 
-}
+export LUNARVIM_CONFIG_DIR="\${LUNARVIM_CONFIG_DIR:-$LUNARVIM_CONFIG_DIR}"
+export LUNARVIM_RUNTIME_DIR="\${LUNARVIM_RUNTIME_DIR:-$LUNARVIM_RUNTIME_DIR}"
 
-asktoinstallnode() {
-	echo "node not found"
-	printf "Would you like to install node now (y/n)? "
-	read -r answer
-	[ "$answer" != "${answer#[Yy]}" ] && installnode
+exec nvim -u "\$LUNARVIM_RUNTIME_DIR/lvim/init.lua" "\$@"
+EOF
+  chmod +x "$INSTALL_PREFIX/bin/lvim"
 }
 
-asktoinstallgit() {
-	echo "git not found, please install git"
-	exit
-}
+function setup_lvim() {
+  echo "Installing LunarVim shim"
 
-asktoinstallpip() {
-	# echo "pip not found"
-	# echo -n "Would you like to install pip now (y/n)? "
-	# read answer
-	# [ "$answer" != "${answer#[Yy]}" ] && installpip
-	echo "Please install pip3 before continuing with install"
-	exit
-}
+  setup_shim
 
-installonmac() {
-	brew install ripgrep fzf
-	npm install -g tree-sitter-cli
-}
+  echo "Preparing Packer setup"
 
-installonubuntu() {
-	sudo apt install ripgrep fzf
-	sudo apt install libjpeg8-dev zlib1g-dev python-dev python3-dev libxtst-dev
-	pip3 install neovim-remote
-	npm install -g tree-sitter-cli
-}
+  cp "$LUNARVIM_RUNTIME_DIR/lvim/utils/installer/config.example-no-ts.lua" \
+    "$LUNARVIM_CONFIG_DIR/config.lua"
 
-installtermux() {
-	apt install ripgrep fzf
-	pip install neovim-remote
-	npm install -g tree-sitter-cli
-}
+  nvim -u "$LUNARVIM_RUNTIME_DIR/lvim/init.lua" --headless \
+    -c 'autocmd User PackerComplete quitall' \
+    -c 'PackerSync'
 
-installonarch() {
-	sudo pacman -S ripgrep fzf
-	pip3 install neovim-remote
-	npm install -g tree-sitter-cli
-}
+  echo "Packer setup complete"
+
+  cp "$LUNARVIM_RUNTIME_DIR/lvim/utils/installer/config.example.lua" "$LUNARVIM_CONFIG_DIR/config.lua"
 
-installonfedora() {
-	sudo dnf groupinstall "X Software Development"
-	sudo dnf install -y fzf ripgrep
+  echo "Thank you for installing LunarVim!!"
+  echo "You can start it by running: $INSTALL_PREFIX/bin/lvim"
+  echo "Do not forget to use a font with glyphs (icons) support [https://github.com/ryanoasis/nerd-fonts]"
 }
 
-installongentoo() {
-	sudo emerge -avn sys-apps/ripgrep app-shells/fzf dev-python/neovim-remote virtual/jpeg sys-libs/zlib
-	npm install -g tree-sitter-cli
+function update_lvim() {
+  git -C "$LUNARVIM_RUNTIME_DIR/lvim" fetch --quiet
+  if ! git -C "$LUNARVIM_RUNTIME_DIR/lvim" diff --quiet "@{upstream}"; then
+    git -C "$LUNARVIM_RUNTIME_DIR/lvim" merge --ff-only --progress ||
+      echo "Unable to guarantee data integrity while updating. Please do that manually instead." && exit 1
+  fi
+  echo "Your LunarVim installation is now up to date!"
 }
 
-installextrapackages() {
-	[ "$(uname)" = "Darwin" ] && installonmac
-	grep -q Ubuntu /etc/os-release && installonubuntu
-	[ -f "/etc/arch-release" ] && installonarch
-	[ -f "/etc/artix-release" ] && installonarch
-	[ -f "/etc/fedora-release" ] && installonfedora
-	[ -f "/etc/gentoo-release" ] && installongentoo
-	[ -d "/data/data/com.termux" ] && installtermux
-	[ "$(uname -s | cut -c 1-10)" = "MINGW64_NT" ] && echo "Windows not currently supported"
+function __add_separator() {
+  local DIV_WIDTH="$1"
+  printf "%${DIV_WIDTH}s\n" ' ' | tr ' ' -
 }
 
-# Welcome
-echo 'Installing LunarVim'
-
-case "$@" in
-*--overwrite*)
-	echo '!!Warning!! -> Removing all lunarvim related config because of the --overwrite flag'
-	rm -rf "$HOME/.local/share/lunarvim"
-	rm -rf "$HOME/.cache/nvim"
-	rm -rf "$HOME/.config/lvim"
-	;;
-esac
-
-# move old lvim directory if it exists
-[ -d "$HOME/.local/share/lunarvim" ] && moveoldlvim
-
-# install node and neovim support
-(command -v git >/dev/null && echo "git installed, moving on...") || asktoinstallgit
-
-# install pip
-(command -v pip3 >/dev/null && echo "pip installed, moving on...") || asktoinstallpip
-
-# install node and neovim support
-(command -v node >/dev/null && echo "node installed, moving on...") || asktoinstallnode
-
-# install pynvim
-(pip3 list | grep pynvim >/dev/null && echo "pynvim installed, moving on...") || installpynvim
-
-if [ -e "$HOME/.local/share/lunarvim/site/pack/packer/start/packer.nvim" ]; then
-	echo 'packer already installed'
-else
-	installpacker
-fi
-
-if [ -e "$HOME/.local/share/lunarvim/lvim/init.lua" ]; then
-	echo 'LunarVim already installed'
-else
-	# clone config down
-	cloneconfig "$@"
-	# echo 'export PATH=$HOME/.config/nvim/utils/bin:$PATH' >>~/.zshrc
-	# echo 'export PATH=$HOME/.config/lunarvim/utils/bin:$PATH' >>~/.bashrc
-fi
-
-if [ "$(uname)" != "Darwin" ]; then
-	if [ -e "$HOME/.local/share/applications/lvim.desktop" ]; then
-		echo 'Desktop file already available'
-	else
-		mkdir -p "$HOME/.local/share/applications"
-		cp "$HOME/.local/share/lunarvim/lvim/utils/desktop/lvim.desktop" "$HOME/.local/share/applications/lvim.desktop"
-	fi
-fi
-
-echo "I recommend you also install and activate a font from here: https://github.com/ryanoasis/nerd-fonts"
-# echo 'export PATH=/home/$USER/.config/lunarvim/utils/bin:$PATH appending to zshrc/bashrc'
+main "$@"

+ 38 - 38
utils/installer/install_stylua.sh

@@ -11,53 +11,53 @@ declare -r FILENAME="stylua-$RELEASE-$OS"
 declare -a __deps=("curl" "unzip")
 
 function check_deps() {
-	for dep in "${__deps[@]}"; do
-		if ! command -v "$dep" >/dev/null; then
-			echo "Missing depdendecy!"
-			echo "The \"$dep\" command was not found!. Please install and try again."
-		fi
-	done
+  for dep in "${__deps[@]}"; do
+    if ! command -v "$dep" >/dev/null; then
+      echo "Missing depdendecy!"
+      echo "The \"$dep\" command was not found!. Please install and try again."
+    fi
+  done
 }
 
 function download_stylua() {
-	local DOWNLOAD_DIR
-	local URL="https://github.com/JohnnyMorganz/StyLua/releases/download/v$RELEASE/$FILENAME.zip"
-
-	DOWNLOAD_DIR="$(mktemp -d)"
-	echo "Initiating download for Stylua v$RELEASE"
-	if ! curl --progress-bar --fail -L "$URL" -o "$DOWNLOAD_DIR/$FILENAME.zip"; then
-		echo "Download failed.  Check that the release/filename are correct."
-		exit 1
-	fi
-
-	echo "Installation in progress.."
-	unzip -q "$DOWNLOAD_DIR/$FILENAME.zip" -d "$DOWNLOAD_DIR"
-
-	if [ -f "$DOWNLOAD_DIR/stylua" ]; then
-		mv "$DOWNLOAD_DIR/stylua" "$INSTALL_DIR/stylua"
-	else
-		mv "$DOWNLOAD_DIR/$FILENAME/stylua" "$INSTALL_DIR/."
-	fi
-
-	chmod u+x "$INSTALL_DIR/stylua"
+  local DOWNLOAD_DIR
+  local URL="https://github.com/JohnnyMorganz/StyLua/releases/download/v$RELEASE/$FILENAME.zip"
+
+  DOWNLOAD_DIR="$(mktemp -d)"
+  echo "Initiating download for Stylua v$RELEASE"
+  if ! curl --progress-bar --fail -L "$URL" -o "$DOWNLOAD_DIR/$FILENAME.zip"; then
+    echo "Download failed.  Check that the release/filename are correct."
+    exit 1
+  fi
+
+  echo "Installation in progress.."
+  unzip -q "$DOWNLOAD_DIR/$FILENAME.zip" -d "$DOWNLOAD_DIR"
+
+  if [ -f "$DOWNLOAD_DIR/stylua" ]; then
+    mv "$DOWNLOAD_DIR/stylua" "$INSTALL_DIR/stylua"
+  else
+    mv "$DOWNLOAD_DIR/$FILENAME/stylua" "$INSTALL_DIR/."
+  fi
+
+  chmod u+x "$INSTALL_DIR/stylua"
 }
 
 function verify_install() {
-	echo "Verifying installation.."
-	local DOWNLOADED_VER
-	DOWNLOADED_VER="$("$INSTALL_DIR/stylua" -V | awk '{ print $2 }')"
-	if [ "$DOWNLOADED_VER" != "$RELEASE" ]; then
-		echo "Mismatched version!"
-		echo "Expected: v$RELEASE but got v$DOWNLOADED_VER"
-		exit 1
-	fi
-	echo "Verification complete!"
+  echo "Verifying installation.."
+  local DOWNLOADED_VER
+  DOWNLOADED_VER="$("$INSTALL_DIR/stylua" -V | awk '{ print $2 }')"
+  if [ "$DOWNLOADED_VER" != "$RELEASE" ]; then
+    echo "Mismatched version!"
+    echo "Expected: v$RELEASE but got v$DOWNLOADED_VER"
+    exit 1
+  fi
+  echo "Verification complete!"
 }
 
 function main() {
-	check_deps
-	download_stylua
-	verify_install
+  check_deps
+  download_stylua
+  verify_install
 }
 
 main "$@"

+ 4 - 4
utils/installer/uninstall.sh

@@ -1,10 +1,10 @@
 #!/bin/sh
 USER_BIN_DIR="/usr/local/bin"
 if [ -d "/data/data/com.termux" ]; then
-	sudo() {
-		eval "$@"
-	}
-	USER_BIN_DIR="$HOME/../usr/bin"
+  sudo() {
+    eval "$@"
+  }
+  USER_BIN_DIR="$HOME/../usr/bin"
 fi
 rm -rf ~/.local/share/lunarvim
 sudo rm "$USER_BIN_DIR"/lvim

Some files were not shown because too many files changed in this diff