diff --git a/content/posts/2026-01-09-vim.md b/content/posts/2026-01-09-vim.md
new file mode 100644
index 0000000000000000000000000000000000000000..16147db0f7072e58164c4919851a880f5b593f26
--- /dev/null
+++ b/content/posts/2026-01-09-vim.md
@@ -0,0 +1,204 @@
+---
+title: Integrated GDB Debugging in Vim with TermDebug
+url: integrated-gdb-debugging-in-vim-with-termdebug.html
+date: 2026-01-09T16:13:13+02:00
+type: post
+draft: false
+tags: []
+---
+
+## Motivation
+
+Over time I have tried making my `~/.vimrc` more universal and capable of
+serving all the different projects, and I always fail. Configuration just
+balloons, and sometimes I need different tools for different projects, that
+fight against each other.
+
+Based on that, I realized that per project `.vimrc` would be much better, and
+would better suit my development style. This way I can keep my main `~/.vimrc`
+short and to the point and have project specific configuration separate.
+
+> **Important**: GDB is amazing but sometimes you do require something with
+> better UI and better ergonomics. If you need a graphical debugger that uses
+> GDB as a backend give https://github.com/nakst/gf a try. If you use gf2 than
+> this post does not apply.
+
+## Main goals and requirements
+
+- Easy to launch `make` and/or run the application I'm working on.
+- If the compilation fails, I want `Quickfix List` to be populated with errors.
+- Start a debugger and break on `main`.
+- Start a debugger and break on `currently highlighted line` in Vim.
+
+## A quick demonstration
+
+
+
+## Main `~/.vimrc`
+
+My `~/.vimrc` is very minimal. I only use these four plugins:
+
+- https://github.com/tpope/vim-commentary - comment stuff out
+- https://github.com/ctrlpvim/ctrlp.vim - fuzzy file, buffer finder
+- https://github.com/dense-analysis/ale - LSP integration
+- https://github.com/mitjafelicijan/sniper.vim - buffer bookmark manager
+
+At the top of my configuration file I have the following.
+
+```vimrc
+set nocompatible exrc secure
+```
+
+- `exrc` - Allows Vim to read local configuration files.
+- `secure` - Restricts what local vimrc/exrc files are allowed to do.
+
+`exrc` is the important one. This allows us to have `.vimrc` in the directory
+of our project. And this will then only apply to that specific project while
+not polluting the main `~/.vimrc` file.
+
+## Project `~/project/foo/.vimrc`
+
+The project for testing showcasing this will be a simple C project using [GNU
+Make](https://www.gnu.org/software/make/) and [Clang](https://clang.llvm.org/).
+
+Project structure
+
+```
+~/Projects/cproject
+ main.c
+ Makefile
+ .vimrc
+```
+
+```Makefile
+# Makefile
+cprogram: main.c
+ clang -g -o cprogram main.c
+```
+
+```c
+// main.c
+
+#include
+#include
+
+typedef struct {
+ int q;
+ int w;
+} Bar;
+
+int main(void) {
+ const char *myenv = getenv("MYENV");
+
+ int a = 100;
+ int b = 123;
+ int c = a + b;
+
+ Bar bar = { .q = 565, .w = 949 };
+
+ printf("> MYENV: %s\n", myenv);
+ printf("> c: %d\n", c);
+ printf("> bar.q: %d\n", bar.q);
+
+ for (int i=0; i<10; i++) {
+ printf("> loop %d\n", i);
+ }
+
+ return 0;
+}
+```
+
+```vim
+" .vimrc
+let g:_executable = 'cprogram'
+let g:_arguments = ''
+let g:_envs = { 'MYENV': 'howdy' }
+let g:_make = 'make -B'
+
+set makeprg=make
+set errorformat=%f:%l:%c:\ %m
+packadd termdebug
+
+let g:termdebug_config = {}
+let g:termdebug_config['variables_window'] = v:true
+
+nnoremap x :call LocalRun()
+nnoremap c :call LocalMake()
+nnoremap v :call LocalDebugMain()
+nnoremap b :call LocalDebugLine()
+
+function! LocalRun() abort
+ let envs = join( map(items(g:_envs), { _, kv -> kv[0] . '=' . kv[1] }), ' ')
+ execute printf("term env %s ./%s %s", envs, g:_executable, g:_arguments)
+endfunction
+
+function! LocalDebugMain() abort
+ execute printf('Termdebug %s %s', g:_executable, g:_arguments)
+
+ for [k, v] in items(g:_envs)
+ call TermDebugSendCommand(printf('set env %s %s', k, v))
+ endfor
+
+ call TermDebugSendCommand('directory ' . getcwd())
+ call TermDebugSendCommand('break main')
+ call TermDebugSendCommand('run')
+endfunction
+
+function! LocalDebugLine() abort
+ let cmd = printf("break %s:%d", expand('%'), line('.'))
+ execute printf('Termdebug %s %s', g:_executable, g:_arguments)
+
+ for [k, v] in items(g:_envs)
+ call TermDebugSendCommand(printf('set env %s %s', k, v))
+ endfor
+
+ call TermDebugSendCommand(cmd)
+ call TermDebugSendCommand('run')
+endfunction
+
+function! LocalMake() abort
+ let envs = join( map(items(g:_envs), { _, kv -> kv[0] . '=' . kv[1] }), ' ')
+ execute printf('silent !env %s %s', g:_make, envs)
+
+ " Filter non valid errors out of quicklist.
+ let qfl = getqflist()
+ let filtered = filter(copy(qfl), {_, entry -> entry.valid == 1})
+ call setqflist(filtered, 'r')
+
+ redraw!
+
+ if len(filtered) > 0
+ execute exists(':CtrlPQuickfix') ? 'CtrlPQuickfix' : 'copen'
+ else
+ cclose
+ endif
+endfunction
+```
+
+I am using the CtrlP plugin, so I also use it for displaying the Quickfix List.
+But if the plugin is not found; it will default to native quicklist with
+`:copen`.
+
+Lets check these keybindings.
+
+- `Leader+x` - runs the binary in the terminal above and providing environment variables and argument
+- `Leader+c` - runs make and puts error in Quickfix List then opens with `:copen`
+- `Leader+v` - launches DebugTerm/DBG debugger with all variables and breaks on main
+- `Leader+b` - launches DebugTerm/DBG debugger with all variables and breaks on current line
+
+This setup can get even more elaborate. Depending on your needs. This example
+works really well for projects using C, but anything goes here.
+
+## Why use `:term` and `:TermDebug`
+
+It's very easy to yank and paste from internal terminal buffers even if you are
+not using `tmux` as multiplexer. Just makes the whole thing much easier. I don
+however use `tmux` as well but for compile/debug loop this proved to be a much
+better experience.
+
+`:TermDebug` is also a no brainier. The integration of GDB directly in Vim
+makes adding new breakpoints with `:Break` just seamless. This goes for all
+other commands as well. You can read more about other commands with `:h
+Termdebug` or on https://vimhelp.org/terminal.txt.html.
diff --git a/static/assets/posts/vim-gdb/demo.mp4 b/static/assets/posts/vim-gdb/demo.mp4
new file mode 100644
index 0000000000000000000000000000000000000000..33a67c57cc0d9d382dd67d34fc93ade96a4c803e
Binary files /dev/null and b/static/assets/posts/vim-gdb/demo.mp4 differ
diff --git a/templates/base.html b/templates/base.html
index abd16c10e13ca7484c60d29ad8ee8c6535ece342..6518c17cdb20b2fa08c9361515f0e4c8a4d2488b 100644
--- a/templates/base.html
+++ b/templates/base.html
@@ -32,13 +32,7 @@ header nav a { color: black; }
header nav span.title { font-weight: bold; }
section { margin-block-start: 3em; margin-block-end: 3em; }
-
- blockquote {
- border-left: 0.3em solid black;
- padding-left: 1em;
- margin-left: 0;
- }
-
+ blockquote { border-left: 0.2em solid black; padding-left: 1em; margin-left: 0; }
footer { font-size: small; }
ul.post-list { padding: 0em; }
@@ -53,7 +47,7 @@ article h2, article h3, article h4 { margin-block-start: 2.5em; }
article h1 { font-size: 130%; line-height: 110%; }
article code { background: lemonchiffon; padding: 0 0.2em; }
article pre { border: 1px solid var(--border-color); padding: 1em; line-height: 140%; text-wrap: nowrap; overflow-x: auto; }
- article pre > code { background: initial; }
+ article pre > code { background: initial; padding: 0; }
img, video, audio { max-width: 100%; }
figure { display: flex; justify-content: center; margin: 1.5em 0; }