Wegwerf-Vervollständigung

Ja, der Titel ist ein wenig sperrig, aber bevor wir zum erschlagenden Thema LSPs kommen, muss ich noch zwei Plugins für neovim zeigen, die ich auch nicht missen möchte.
Das erste Plugin ist ein Feature, das ich auch in “normalen” IDEs sehr gerne nutze: Wegwerf-Dateien. Gekritzel-Dateien wären die sinnvollere Übersetzung für diese Dateien. Ich paste zum Beispiel gerne einen Teil der Debug-Informationen in eine Datei ab, wenn ich programmiere, damit ich immer mal nachschauen kann, wie denn der Zustand zum Punkt X gewesen ist. Und dafür nutze ich sogenannte Scratch-Files. Die kann man mittels scratch.nvim in einem definierten Verzeichnis erstellen und später auch wieder aufrufen. Zum einen benötigt man dafür die Konfiguration des Plugins selber:

return {
  "LintaoAmons/scratch.nvim",
  event = "VeryLazy",
}

Sowie eine Konfigurations-Datei (berichtigt mich, wenn man die auch in die Plugin-Konfiguration übernehmen kann, ich habe es leider nicht geschafft), die ich unter lua/kaffdaddy/config/scratch_config.json gespeichert habe:

{
  "filetype_details": {
  }, 
  "use_telescope": true, 
  "filetypes": ["xml", "go", "lua", "js", "py", "sh", "html", "php", "css", "scss", "xml"], 
  "localKeys": [{
    "filenameContains": ["gp"], 
    "LocalKeys": [{
      "modes": ["n", "i", "v"],
      "cmd": "GpResponse",
      "key": "k"
    }]
  }], 
  "scratch_file_dir": "~/.cache/nvim/scratch.nvim",
  "window_cmd": "edit"
}

Das zweite Plugin ist insofern recht hilfreich, wenn man sich die zigtausend Befehle, die man dann so mittlerweile in vim zur Verfügung hat, nicht merken kann. which-key listet zum Beispiel nach Drücken des Leader-Keys alle Befehlsketten auf, denen man dann folgen kann und zeigt sogar den Beschreibungstext an. Auch wieder ganz einfach eine neue Datei mit folgendem Inhalt im Plugin-Verzeichnis erstellen und schon ist man nicht mehr aufgeschmissen:

return {
  "folke/which-key.nvim",
  event = "VeryLazy",
  init = function()
    vim.o.timeout = true
    vim.o.timeoutlen = 500
  end,
  opts = {
  },
}

Der aktuelle Stand der Konfiguration bis hierin ist hier zu finden.

Noch mehr Kleinkram

Kleinkram hört sich so despektierlich an, soll es aber in dem Fall gar nicht sein. Denn all diese kleinen Plugins bringen eine wunderbare Funktion mit, die mir das Leben als Entwickler einfacher machen und von daher möchte ich sie auch gar nicht missen.

Visualiserung von Farbwerten

Wer weiß denn auch schon aus dem Stegreif, was #336bff für eine Farbe ist? Ich jedenfalls nicht. Und dafür gibt es auch für neovim ein Plugin namens colorizer.lua, das Farbwerte in hexadezimaler Schreibweise mit der entsprechenden Hintergrundfarbe versieht. Und das funktioniert auch mit rgb- und hsl-Funktionen von CSS. Normalerweise versucht das Plugin Farbwerte in allen Dateitypen zu erkennen und darzustellen, ich benötige die Funktion aber lediglich in CSS/SCSS-Dateien und vielleicht ab und zu mal in JavaScript-Dateien. Daher sieht meine angepasste Konfiguration folgendermaßen aus:

return {
  "NvChad/nvim-colorizer.lua",
  event = { "BufReadPre", "BufNewFile" },
  config = function()
   local colorizer = require('colorizer')
    colorizer.setup({
      filetypes = {
        'css',
        'scss',
        'javascript',
        html = { mode = 'foreground' }
      },
      user_default_options = {
        names = false,
        rgb_fn = true,
        hsl_fn = true,
      }
    })
  end,
}

Eingaben schöner gestalten

Wenn man zum Beispiel eine Datei erstellen oder umbennen möchte, wird man normalerweise im “Prompt” von neovim zur Eingabe des Dateinamens gebeten. Damit sich das ein wenig besser in die UI von neovim einbettet, kann man dafür dressing.nvim verwenden. Das sieht dann so aus, wie oben im Screenshot festgehalten.

return {
  "stevearc/dressing.nvim",
  event = "VeryLazy",
}

Eine Statusbar für alles

Sehr oft benötige ich für mich zur Orientierung nicht nur den Pfad und Dateinamen der Datei, in der ich mich gerade befinde, sondern auch den Modus von neovim und die Kodierung und Art der Datei, sowie Zeile und Spalte, in der man sich befindet. lualine.nvim gibt einem das. Und nicht nur das, sondern man kann sich die Statusbar auch anständig konfigurieren, wenn man mit den Informationen noch nicht zufrieden ist oder die Reihenfolge geändert haben möchte.

return {
  "nvim-lualine/lualine.nvim",
  dependencies = { "nvim-tree/nvim-web-devicons" },
  config = function()
    local lualine = require("lualine")
    local lazy_status = require("lazy.status")

    local colors = {
      blue = "#65D1FF",
      green = "#3EFFDC",
      violet = "#FF61EF",
      yellow = "#FFDA7B",
      red = "#FF4A4A",
      fg = "#c3ccdc",
      bg = "#112638",
      inactive_bg = "#2c3043",
    }

    local my_lualine_theme = {
      normal = {
        a = { bg = colors.blue, fg = colors.bg, gui = "bold" },
        b = { bg = colors.bg, fg = colors.fg },
        c = { bg = colors.bg, fg = colors.fg },
      },
      insert = {
        a = { bg = colors.green, fg = colors.bg, gui = "bold" },
        b = { bg = colors.bg, fg = colors.fg },
        c = { bg = colors.bg, fg = colors.fg },
      },
      visual = {
        a = { bg = colors.violet, fg = colors.bg, gui = "bold" },
        b = { bg = colors.bg, fg = colors.fg },
        c = { bg = colors.bg, fg = colors.fg },
      },
      command = {
        a = { bg = colors.yellow, fg = colors.bg, gui = "bold" },
        b = { bg = colors.bg, fg = colors.fg },
        c = { bg = colors.bg, fg = colors.fg },
      },
      replace = {
        a = { bg = colors.red, fg = colors.bg, gui = "bold" },
        b = { bg = colors.bg, fg = colors.fg },
        c = { bg = colors.bg, fg = colors.fg },
      },
      inactive = {
        a = { bg = colors.inactive_bg, fg = colors.semilightgray, gui = "bold" },
        b = { bg = colors.inactive_bg, fg = colors.semilightgray },
        c = { bg = colors.inactive_bg, fg = colors.semilightgray },
      },
    }

    lualine.setup({
      options = {
        theme = my_lualine_theme,
      },
      sections = {
        lualine_b = {
          'branch',
          'diff',
          'diagnostics'
        },
        lualine_c = {
          {
            'filename',
            file_status = true,
            newfile_status = true,
            path = 1,
          }
        },
        lualine_x = {
          {
            lazy_status.updates,
            cond = lazy_status.has_updates,
            color = { fg = "#ff9e64" },
          },
          { "encoding" },
          { "fileformat" },
          { "filetype" },
        },
      },
    })
  end,
}

Den aktuellen Stand der Konfiguration bis hierhin kann man sich hier anschauen.

Kleinkram macht auch Code

Bevor ich noch einen vermutlich abschließenden Blogpost über die Integration von LSPs (Language Server Protocol) schreiben werde, räume ich hier noch mit ein paar kleinen Tools vorher auf. Die Konfiguration von neovim ist wirklich eine Wissenschaft für sich mit einer steilen Lernkurve, die ich am Anfang unterschätzt habe.
Bis jetzt verstehe ich davon nur einen ganz kleinen Teil und eigentlich habe ich meine Konfiguration auch nur aus den Konfigurationen anderer Leute zusammenkopiert und stundenlang Videos von ThePrimeagen, Josean Martinez, typecraft und devaslife geschaut.

Welcome screen

Fangen wir an mit alpha-nvim an, der einem einen Welcome screen mit einigen Shortcuts beim Starten von neovim präsentiert. Ganz einfach per

return {
 'goolord/alpha-nvim',
   config = function ()
       require'alpha'.setup(require'alpha.themes.dashboard'.config)
   end
}

zu integrieren. Ich habe es jetzt auch erstmal nicht weiter konfiguriert, wer sich damit auseinandersetzen möchte, findet alles dafür in der Dokumentation von alpha-nvim.

Erweitertes Session-Management

neovim bringt zwar schon selber ein Session-Management mit, auto-session erweitertet das Ganze aber noch ein wenig, indem es das Verhalten ein wenig automatisiert und anpassbar macht. Zum Beispiel kann man angeben, in welchen Verzeichnissen gar kein Session-Management zum Tragen kommen soll.

return {
 "rmagatti/auto-session",
 config = function()
   local auto_session = require("auto-session")
   auto_session.setup({
     auto_restore_enabled = false,
     auto_session_suppress_dirs = { "~/", "~/Downloads", "~/Documents", "~/Desktop/" },
   })
   local keymap = vim.keymap
   keymap.set("n", "<leader>wr", "<cmd>SessionRestore<CR>", { desc = "Restore session for cwd" })
   keymap.set("n", "<leader>ws", "<cmd>SessionSave<CR>", { desc = "Save session for auto session root dir" })
 end,
}

Schöne Tabs braucht man auch

Und die bekommt man mit bufferline.nvim , das einen erlaubt, die Tabs zu stylen und in naher Zukunft auch Diagnose-Informationen der LSPs anzuzeigen. Und den Filetree kann man auch ein wenig im Layout anpassen, damit es stimmiger wird.

return {
"akinsho/bufferline.nvim",
dependencies = { "nvim-tree/nvim-web-devicons" },
version = "*",
opts = {
options = {
mode = "tabs",
separator_style = "slant",
offsets = {
{
filetype = "NvimTree",
text = "File Explorer",
text_align = "left",
separator = true
}
}
},
},
}

In den nächsten Artikeln gibt es dann noch ein wenig mehr Feinschliff und dann kommt auch wirklich die LSP-Integration mit Mason und auch meine Debug-Konfiguration, mit der ich auch schon produktiv arbeite.
Den aktuellen Stand bis hierhin kann man sich hier anschauen.

Ein Parser für meinen Code

Code zu Lesen ist fast genauso schwierig wie Code zu Schreiben. Das betrifft nicht nur fremden Code, sondern auch den eigenen Code von vor zwei Monaten. Den man jetzt refactoren darf. Oder möchte.
Damit das Ganze sich ein wenig einfacher gestaltet, ist ein kluger Kopf auf die Idee gekommen, die einzelnen Teile des Codes nach seiner Bestimmung einzufärben. So dass man Funktionen, Variablen und Konstrukte auf den ersten Blick voneinander unterscheiden kann.

Natürlich möchte man diese Teile nicht selber einfärben und da kommen dann Plugins wie tree-sitter bzw. nvim-treesitter ins Spiel, die den Code selbst mit Syntax-Fehlern parsen können, damit sie entsprechend gehighlighted werden. Dazu habe ich mir in meiner Konfiguration unter lua/kaffdaddy/plugins eine Datei mit folgendem Inhalt erstellt:

return {
  {
    "nvim-treesitter/nvim-treesitter",
    event = { "BufReadPre", "BufNewFile" },
    build = ":TSUpdate",
    dependencies = {
      "nvim-treesitter/nvim-treesitter-textobjects",
      "windwp/nvim-ts-autotag",
    },
    config = function()
      local treesitter = require("nvim-treesitter.configs")
      treesitter.setup({
        highlight = {
          enable = true,
        },
        indent = { enable = true },
        autotag = {
          enable = true,
        },
        ensure_installed = {
          "json",
          "javascript",
          "typescript",
          "yaml",
          "html",
          "css",
          "bash",
          "lua",
          "vim",
          "dockerfile",
          "gitignore",
          "php",
          "scss",
          "gitattributes",
          "gitignore",
          "json",
          "json5",
          "regex",
          "typoscript",
        },
        incremental_selection = {
          enable = true,
          keymaps = {
            init_selection = "",
            node_incremental = "",
            scope_incremental = false,
            node_decremental = "",
          },
        },
        context_commentstring = {
          enable = true,
          enable_autocmd = false,
        },
      })
    end,
  },
}

Wie man sieht, ist dort sogar ein Parser für TypoScript enthalten, auf den mich dankenswerterweise Dragan (melde dich, wenn ich hier etwas verlinken soll!) aufmerksam gemacht hat: Teddytrombone/tree-sitter-typoscript
Weil neovim natürlich nicht alle Dateitypen aufgrund ihrer Dateiendung erkennt, habe ich ein wenig mit nathom/filetype.nvim nachgeholfen:

return {
  "nathom/filetype.nvim",
  config = function()
    local filetype = require("filetype")
    filetype.setup({
      overrides = {
        extensions = {
            html = "html",
            tsconfig = "typoscript",
            typoscript = "typoscript",
        }
      }
    })
  end,
}

Den aktuellen Stand meiner Konfiguration kann man sich hier anschauen.

Doom

Vor genau 30 Jahren ist Doom erschienen und hat in den nächsten Monaten und eigentlich Jahren mein Computerspielen beeinflusst. Zwar haben wir auch schon ein Jahr vorher mit Wolfenstein 3D Egoshooter gespielt, aber Doom war dann doch ein Meilenstein mit richtiger 3D-Grafik, der unsere Rechner zum Glühen brachte.

Auch die hervorragende Soundkulisse und vor allem die Geräusche von sich in der Nähe befindlichen Imps lassen auch heute noch meinen Puls in die Höhe schnellen. Selbst auf einem Game Boy Advance habe ich das Spiel komplett durchgespielt. Und wahrscheinlich meine Augen ruiniert.

Ein Fuzzy-Finder für neovim

Eine der wichtigsten Funktionen in einer IDE ist für mich das Suchen nach Code. Oder Dateien. Ich habe keine Lust, auch wenn ich das Projekt vielleicht in- und auswendig kenne, mich durch die Dateiansicht zu klicken, um eine Datei zu finden.
Für sowas sind sogenannte Fuzzy-Suchen zuständig und mit telescope gibt es sowas in wunderbarer Umsetzung auch für neovim.

Im Verzeichnis lua/kaffdaddy/plugins lege ich die Datei telescope.lua mit folgendem Inhalt an:

return {
  "nvim-telescope/telescope.nvim",
  branch = "0.1.x",
  dependencies = {
    "nvim-lua/plenary.nvim",
    { "nvim-telescope/telescope-fzf-native.nvim", build = "make" },
    "nvim-tree/nvim-web-devicons",
  },
  config = function()
    local telescope = require("telescope")
    local actions = require("telescope.actions")

    telescope.setup({
      defaults = {
        path_display = { "truncate " },
        mappings = {
          i = {
            ["<C-k>"] = actions.move_selection_previous,
            ["<C-j>"] = actions.move_selection_next,
            ["<C-q>"] = actions.send_selected_to_qflist + actions.open_qflist,
          },
        },
      },
    })

    telescope.load_extension("fzf")

    -- set keymaps
    local keymap = vim.keymap

    keymap.set("n", "<leader>ff", "<cmd>Telescope find_files<cr>", { desc = "Fuzzy find files in cwd" })
    keymap.set("n", "<leader>fr", "<cmd>Telescope oldfiles<cr>", { desc = "Fuzzy find recent files" })
    keymap.set("n", "<leader>fs", "<cmd>Telescope live_grep<cr>", { desc = "Find string in cwd" })
    keymap.set("n", "<leader>fc", "<cmd>Telescope grep_string<cr>", { desc = "Find string under cursor in cwd" })
  end,
}

Mit den definierten Kommandos kann ich dann nach Dateien und sogar nach Strings im aktuellen Projektverzeichnis suchen. Und das atemberaubend schnell und mit einer Vorschau der Datei und Position, wenn man nach einem String gesucht hat.
Den aktuellen Stand der Konfiguration bis hierher findet man dann hier: https://github.com/KaffDaddy/nvim-config/tree/0.5.0

Ein Universum voller Plugins

Ich habe am Anfang meiner Reise mit neovim sehr schnell gemerkt, dass es für den Editor zigtausend Plugins und noch viel mehr Wege gibt, selbige zu integrieren. Es gibt wirklich mehr Möglichkeiten, seine eigene Editor-Konfiguration zu schreiben, als mit TYPO3 eine Website zu erstellen. Und das finde ich bemerkenswert.

Mein Ziel ist es ja, dass ich mit neovim zu 95% meine Bedürfnisse abdecke, die ich beim Programmieren von PHP/HTML/JavaScript/SCSS habe. Das ich nicht alles erschlagen werde, war mir im Vorfeld bewusst. Plugins wie Fluid- oder TypoScript-Linter gibt es einfach nicht. Aber den Rest, den ich benötige... das ist alles da. Es gibt wirklich alles, was das Herz begehrt.

Nur habe ich gemerkt, dass ich zwar ein Grundverständnis entwickelt habe, wie man mit Lazy als Paket-/Plugin-Manager eine Konfiguration erstellt, aber ich stoße bei der Konfiguration der ganzen Plugins an sich sehr schnell an Grenzen.
Ich habe mir zwar mittlerweile eine für mich funktionierende Konfiguration erstellt, in der ich sogar mittels Xdebug in einem Docker-Container debuggen kann, aber so richtig verstehen und erklären, was jetzt welches Plugin wie macht, kann ich nicht. Da brauche ich noch ein wenig Zeit und muss mich eingehender mit der Materie beschäftigen.

Daher habe ich auch noch nicht weiter in meinem Repository für meine Konfiguration weitergemacht, obwohl ich schon viel weiter bin und nächste Woche auch das erste Mal produktiv entwickeln werde. Da freue ich mich schon drauf wie Bolle.
Und ich werde bestimmt noch das ein oder andere Plugin entdecken, was das Ganze noch mehr zu einer besseren und auf mich abgestimmteren Programmierumgebung machen wird.

Gerechtes Feuer

Nachdem ich nun vor einigen Wochen endlich mal Privateer durchgespielt hatte, konnte ich es mir natürlich nicht nehmen, auch das Add-On “Righteous fire” durchzuspielen. Das fügt dem Hauptspiel einen Geschichtsstrang nach dem Hauptspiel hinzu und bringt einem noch ein paar weitere Stunden Spaß. Einschneidende Änderungen am bekannten Spiel gibt es nicht, ein paar Erweiterungen für sein Raumschiff, die aber kaum der Rede wert sind. Ansonsten findet die Handlung in den schon bekannten Weltraum-Sektoren statt, was ein wenig schade ist. Hier hätte man das Spiel vielleicht noch ein wenig erweitern können.

Aber hey, Spaß hatte ich. Mal sehen, was ich als nächstes vom Pile of shame nehme. Baldurs Gate 3 muss glaube ich noch ein wenig warten.

vim in PhpStorm

In der letzten Woche habe ich wenig Zeit gefunden, an meiner vim-Erfahrung zu schrauben. Mein kurzer Exkurs, LazyVimals Kickstarter zu verwenden, habe ich direkt wieder verworfen. Zu umständlich erscheint es mir, wieder Plugins zu deaktivieren und Konfigurationen umzuschreiben. Da kann ich meine Konfiguration auch gleich direkt schreiben. Und habe gefühlt mehr Einfluss darauf, wie es werden soll.

Aber ich habe mir das IdeaVim-Plugin in PhpStorm aktiviert. Das sorgt dafür, dass sich der Editor und auch ein wenig der Projektbaum sich wie vim verhält. Und das großartige ist, dass man seine vim-Konfiguration integrieren kann. Ich habe zwar noch nicht viel konfiguriert, aber mit dem bißchen hier

set number relativenumber
set idearefactormode=keep
set ideajoin
set surround
set easymotion

let mapleader = " "
nmap <leader>h <action>(PreviousTab) nmap <leader>l <action>(NextTab) nmap <leader>i <action>(Generate) nmap <leader>s <action>(ShowErrorDescription) nmap <leader>n <action>(GotoNextError) nmap <leader>p <action>(GoToPreviousError) nnoremap <leader><leader> <C-Tab>
set NERDTree nnoremap <Leader>nj :NERDTreeFind<CR> nnoremap <Leader>nn :NERDTreeFocus<CR> nnoremap <Leader>tn :NERDTreeToggle<CR> let g:NERDTreeMapActivateNode='l' let g:NERDTreeMapJumpParent='h'

konnte ich schon für ein wenig vim-Feeling sorgen.

Kleine Verbesserungen und Annehmlichkeiten

Da vim von Hause aus geöffnete Dateien und somit auch die Navigations-Sidebar in sogenannten Buffer hält, muss man natürlich zwischen diesen wechseln können. Das kann man selber anhand von Keymaps versuchen abzubilden oder man installiert sich tmux und das Plugin christoomey/vim-tmux-navigator, mit dem man dann per STRG/Control und HJKL-Tasten zwischen der Navigation und der geöffneten Datei wechseln kann. Es hat also doch etwas gebracht, Nethack zu spielen.

In der Datei lua/kaffdaddy/core/options.lua habe ich ein paar grundsätzliche Konfigurationen hinterlegt, die das Einrückungsverhalten und das Aussehen von nvim regeln. Nichts spannendes, kann man sich mal anschauen und sollte selbsterklärend sein.

Aktueller Stand der Konfiguration ist hier zu finden, als nächstes kommt dann auch schon die Integration von LSP dran, um dann die ersten Schritte beim täglichen Entwickeln gehen zu können. Yeah!