{"id":198,"date":"2026-01-28T03:00:16","date_gmt":"2026-01-27T22:00:16","guid":{"rendered":"https:\/\/blog.vicnunes.com\/?p=198"},"modified":"2026-01-28T03:02:29","modified_gmt":"2026-01-27T22:02:29","slug":"as-text-editors-go-vim-is-the-goat","status":"publish","type":"post","link":"https:\/\/blog.vicnunes.com\/?p=198","title":{"rendered":"As text editors go, Vim is the GOAT"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\"><\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter\"><img loading=\"lazy\" decoding=\"async\" width=\"954\" height=\"714\" src=\"https:\/\/blog.vicnunes.com\/wp-content\/uploads\/2026\/01\/vim-goat.png\" alt=\"\" class=\"wp-image-197\" srcset=\"https:\/\/blog.vicnunes.com\/wp-content\/uploads\/2026\/01\/vim-goat.png 954w, https:\/\/blog.vicnunes.com\/wp-content\/uploads\/2026\/01\/vim-goat-300x225.png 300w, https:\/\/blog.vicnunes.com\/wp-content\/uploads\/2026\/01\/vim-goat-768x575.png 768w, https:\/\/blog.vicnunes.com\/wp-content\/uploads\/2026\/01\/vim-goat-361x270.png 361w\" sizes=\"auto, (max-width: 954px) 100vw, 954px\" \/><\/figure>\n<\/div>\n\n\n<p class=\"wp-block-paragraph\">My first encounter with <a href=\"https:\/\/en.wikipedia.org\/wiki\/Vim_(text_editor)\">Vim<\/a> was in 2000 when my then employer decided to port our main product (<a href=\"https:\/\/patents.google.com\/patent\/US6954429B2\/en\">Bandwidth Control System<\/a>) from Windows NT to Linux.  <\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Linux was a fresh experience for me, and it opened the door to diving into Emacs and Vim for text editing. I recall reading an <a href=\"https:\/\/en.wikipedia.org\/wiki\/Emacs\">Emacs<\/a> vs. Vim article in a magazine or trade-rag a while back that really piqued my interest. Even now, Emacs still feels a bit out of reach for my simple mind. Vim, however, felt intuitive and almost magical after some learning and experimenting. <\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Primarily due to discovering and appreciating <a href=\"https:\/\/en.wikipedia.org\/wiki\/Markdown\">John Grubber\u2019s Markdown<\/a> for my professional writing requirements, Vim remained dormant for nearly 25 years, although it was never forgotten. <\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Fast-forward to 2026 where I am now retired and <a href=\"https:\/\/en.wikipedia.org\/wiki\/Self-hosting_(network)\">self-hosting<\/a> several services for fun and practical purposes. As a macOS user who operates a Proxmox lab and has a preference for running Debian LXC or virtual machines over Docker containers, the need for a fast and powerful multi-platform text editor became evident. So, was Vim still the clear choice for me?<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">In 25 Years the Vim-adjacent landscape changed a lot. Enter <a href=\"https:\/\/en.wikipedia.org\/wiki\/Neovim\">Neovim<\/a>, the newer shinier and more extensible Vim. For roughly two months, I experimented with Neovim, <a href=\"https:\/\/github.com\/nvim-lua\/kickstart.nvim\">Kickstart.nvim<\/a>, <a href=\"https:\/\/www.lazyvim.org\">LazyVim<\/a>, <a href=\"https:\/\/lunarvim.org\/\">LunarVim<\/a>, <a href=\"https:\/\/nvchad.com\/\">NvChad<\/a> and endless variations of plugins. This time-sink forced me to re-evaluate what my actual needs are. I\u2019m not a programmer, the target audience for most of the above Neovim derivitives. Instead my needs are closer to that of a systems or network operator (Sysops or Netops).<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">In the end, I reverted to plain Vim with minimal plugins to easily replicate my setup across Macs and Linux systems. Sure, I could also use it on MS-Windows (<a href=\"https:\/\/en.wikipedia.org\/wiki\/Windows_Subsystem_for_Linux\">WSL<\/a>), but avoid Windows like the plague that it is.<br><br>Vim is a truly magical tool that keeps giving, and it\u2019s incredibly challenging in a good way. So, dive in, become proficient, and thank me later!<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">A custom <a href=\"https:\/\/docs.ansible.com\/projects\/ansible\/latest\/playbook_guide\/playbooks_intro.html\">Ansible playbook<\/a> simplifies the replication of my Vim configuration.  What follows are my Ansible configuration files that install and configure Vim on target host(s) in the inventory.ini file.<br><\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Ansible files<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">inventory.ini<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Defines the target host(s) executed by the Ansible Playbook (install_vim.yaml)<em>\n<\/em><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code># Ansible Inventory\n\n&#91;all]\n192.168.5.12\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">install_vim.yaml (Ansible playbook)<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Runs the vim Ansible role<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>---\n- name: Deploy my vim configuration\n  hosts: all\n  roles:\n      - role: vim\n  become: yes\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">vim (Ansible role) directory structure<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>.\n\u251c\u2500\u2500 defaults\n\u2502&nbsp;&nbsp; \u2514\u2500\u2500 main.yml\n\u251c\u2500\u2500 files\n\u2502&nbsp;&nbsp; \u251c\u2500\u2500 config\n\u2502&nbsp;&nbsp; \u2502&nbsp;&nbsp; \u2514\u2500\u2500 vim\n\u2502&nbsp;&nbsp; \u2502&nbsp;&nbsp;     \u251c\u2500\u2500 colors.vim\n\u2502&nbsp;&nbsp; \u2502&nbsp;&nbsp;     \u251c\u2500\u2500 fzf.vim\n\u2502&nbsp;&nbsp; \u2502&nbsp;&nbsp;     \u251c\u2500\u2500 keybinds.vim\n\u2502&nbsp;&nbsp; \u2502&nbsp;&nbsp;     \u251c\u2500\u2500 lightline.vim\n\u2502&nbsp;&nbsp; \u2502&nbsp;&nbsp;     \u251c\u2500\u2500 options.vim\n\u2502&nbsp;&nbsp; \u2502&nbsp;&nbsp;     \u251c\u2500\u2500 plugins.vim\n\u2502&nbsp;&nbsp; \u2502&nbsp;&nbsp;     \u2514\u2500\u2500 vimrc\n\u2502&nbsp;&nbsp; \u2514\u2500\u2500 vim-env.bash\n\u2514\u2500\u2500 tasks\n    \u2514\u2500\u2500 main.yml\n<\/code><\/pre>\n\n\n\n<h4 class=\"wp-block-heading\">defaults\/main.yml<\/h4>\n\n\n\n<pre class=\"wp-block-code\"><code>---\n# defaults file for vim role\n\n# Vim configuration directory\nvim_config_dir: \"~\/.config\/vim\"\n\n# Vim configuration file location\nvim_config_file: \"~\/.config\/vim\/vimrc\"\n\n# Viminfo file location\nvim_viminfo_file: \"~\/.config\/vim\/viminfo\"\n\n# Undo directory\nvim_undo_dir: \"~\/.config\/vim\/undo\"\n<\/code><\/pre>\n\n\n\n<h4 class=\"wp-block-heading\">files\/config\/vim\/colors.vim<\/h4>\n\n\n\n<pre class=\"wp-block-code\"><code>set termguicolors\nlet g:tokyonight_enable_italic = 1\ncolorscheme tokyonight\n<\/code><\/pre>\n\n\n\n<h4 class=\"wp-block-heading\">files\/config\/vim\/fzf.vim<\/h4>\n\n\n\n<pre class=\"wp-block-code\"><code>\" FZF keymaps (requires Plug 'junegunn\/fzf.vim')\n\n\" Files\nnnoremap &lt;leader&gt;ff :Files&lt;CR&gt;\nnnoremap &lt;leader&gt;fo :History&lt;CR&gt;\nnnoremap &lt;leader&gt;fb :Buffers&lt;CR&gt;\nnnoremap &lt;leader&gt;fq :clist&lt;CR&gt;    \" For quickfix list\nnnoremap &lt;leader&gt;fh :Helptags&lt;CR&gt;\n\n\" Grep current string\nnnoremap &lt;leader&gt;fs :Rg &lt;C-r&gt;&lt;C-w&gt;&lt;CR&gt;\n\n\" Grep input string (fzf prompt)\nnnoremap &lt;leader&gt;fg :Rg&lt;Space&gt;\n\n\" Grep for current file name (without extension)\nnnoremap &lt;leader&gt;fc :execute 'Rg ' . expand('%:t:r')&lt;CR&gt;\n\n\" Find files in your Vim config\nnnoremap &lt;leader&gt;fi :Files ~\/.config\/vim&lt;CR&gt;\n<\/code><\/pre>\n\n\n\n<h4 class=\"wp-block-heading\">files\/config\/vim\/keybindings.vim<\/h4>\n\n\n\n<pre class=\"wp-block-code\"><code>imap jj &lt;Esc&gt;\n\n\" Set leader key\nlet mapleader = \" \"\n\n\" Open netrw with &lt;leader&gt;cd\nnnoremap &lt;leader&gt;e :Ex&lt;CR&gt;\n<\/code><\/pre>\n\n\n\n<h4 class=\"wp-block-heading\">files\/config\/vim\/lightline.vim<\/h4>\n\n\n\n<pre class=\"wp-block-code\"><code>set laststatus=2\nlet g:lightline = {\n      \\ 'colorscheme' : 'tokyonight',\n      \\ 'active': {\n      \\   'left': &#91; &#91; 'mode', 'paste' ],\n      \\             &#91; 'gitbranch', 'readonly', 'filename', 'modified' ] ],\n      \\   'right': &#91; &#91; 'lineinfo' ], &#91; 'fileformat', 'fileencoding', 'filetype' ] ]\n      \\ },\n      \\ 'component_function': {\n      \\   'gitbranch': 'FugitiveHead',\n      \\   'filename': 'LightlineFilename'\n      \\ }\n      \\ }\n\nfunction! LightlineFilename()\n  return expand('%:t') !=# '' ? expand('%:t') : '&#91;No Name]'\nendfunction\n<\/code><\/pre>\n\n\n\n<h4 class=\"wp-block-heading\">files\/config\/vim\/options.vim<\/h4>\n\n\n\n<pre class=\"wp-block-code\"><code>\" Add line numbers\nset number\nset nrformats= \"treat all numerals as decimals\n\nset clipboard=unnamedplus\n\nset relativenumber\nset tabstop=4\nset shiftwidth=4\nset autoindent\nset mouse=a\n\nset guifont=Monaco:h16\n\nsyntax on\n\nset rtp+=\/opt\/homebrew\/opt\/fzf\n\n\" remove startup screen\nset shortmess+=I\n\n\" Set all vim directories to ~\/.config\/vim\nset viminfofile=~\/.config\/vim\/viminfo\n\n\" Enable persistent undo\nset undofile\nset undodir=~\/.config\/vim\/undo\/\/\n<\/code><\/pre>\n\n\n\n<h4 class=\"wp-block-heading\">files\/config\/vim\/plugins.vim<\/h4>\n\n\n\n<pre class=\"wp-block-code\"><code>let s:plugin_dir = expand('~\/.config\/vim\/plugged')\n\nfunction! s:ensure(repo)\n  let name = split(a:repo, '\/')&#91;-1]\n  let path = s:plugin_dir . '\/' . name\n\n  if !isdirectory(path)\n    if !isdirectory(s:plugin_dir)\n      call mkdir(s:plugin_dir, 'p')\n    endif\n    execute '!git clone --depth=1 https:\/\/github.com\/' . a:repo . ' ' . shellescape(path)\n  endif\n\n  execute 'set runtimepath+=' . fnameescape(path)\nendfunction\n\ncall s:ensure('ghifarit53\/tokyonight-vim')\ncall s:ensure('junegunn\/fzf')\ncall s:ensure('junegunn\/fzf.vim')\ncall s:ensure('itchyny\/lightline.vim')\n<\/code><\/pre>\n\n\n\n<h4 class=\"wp-block-heading\">files\/config\/vim\/vimrc<\/h4>\n\n\n\n<pre class=\"wp-block-code\"><code>\" Vim Configuration File\n\" Managed by Ansible - vim role\n\nsource ~\/.config\/vim\/options.vim\nsource ~\/.config\/vim\/keybinds.vim\nsource ~\/.config\/vim\/plugins.vim\nsource ~\/.config\/vim\/fzf.vim\nsource ~\/.config\/vim\/lightline.vim\nsource ~\/.config\/vim\/colors.vim\n<\/code><\/pre>\n\n\n\n<h4 class=\"wp-block-heading\">files\/vim-env.bash<\/h4>\n\n\n\n<pre class=\"wp-block-code\"><code># ===========================================\n# VIM CONFIGURATION\n# Managed by Ansible - vim role\n# ===========================================\n\n# Set VIMINIT to use custom vim configuration location\nexport VIMINIT=\"source ~\/.config\/vim\/vimrc\"\n\n# FZF integration for vim\nexport FZF_DEFAULT_COMMAND='fd --type f --hidden --follow'\n\n<\/code><\/pre>\n\n\n\n<h4 class=\"wp-block-heading\">tasks\/main.yml<\/h4>\n\n\n\n<pre class=\"wp-block-code\"><code>---\n# tasks file for vim role\n\n- name: Check if vim is installed\n  ansible.builtin.command: which vim\n  register: vim_check\n  changed_when: false\n  failed_when: false\n  become: no\n\n- name: Install vim (macOS)\n  ansible.builtin.shell: brew install vim\n  when:\n      - vim_check.rc != 0\n      - ansible_os_family == 'Darwin'\n  become: no\n\n- name: Install vim (Debian\/Ubuntu)\n  ansible.builtin.apt:\n      name: vim\n      state: present\n      update_cache: yes\n  when:\n      - vim_check.rc != 0\n      - ansible_os_family == 'Debian'\n  become: yes\n\n- name: Install vim (RedHat\/CentOS\/Fedora)\n  ansible.builtin.yum:\n      name: vim\n      state: present\n  when:\n      - vim_check.rc != 0\n      - ansible_os_family == 'RedHat'\n  become: yes\n\n- name: Install vim (Arch Linux)\n  ansible.builtin.pacman:\n      name: vim\n      state: present\n  when:\n      - vim_check.rc != 0\n      - ansible_os_family == 'Archlinux'\n  become: yes\n\n- name: Ensure vim config directory exists\n  ansible.builtin.file:\n      path: \"{{ vim_config_dir }}\"\n      state: directory\n      mode: \"0755\"\n  become: no\n\n- name: Ensure vim subdirectories exist\n  ansible.builtin.file:\n      path: \"{{ item }}\"\n      state: directory\n      mode: \"0755\"\n  become: no\n  loop:\n      - \"{{ vim_undo_dir }}\"\n\n- name: Deploy vim configuration files\n  ansible.builtin.copy:\n      src: \"config\/vim\/\"\n      dest: \"{{ vim_config_dir }}\/\"\n      mode: \"0644\"\n  become: no\n\n- name: Deploy .fdignore to home directory\n  ansible.builtin.copy:\n      src: fdignore\n      dest: \"~\/.fdignore\"\n      mode: \"0644\"\n  become: no\n\n- name: Ensure ~\/.config\/bash directory exists\n  ansible.builtin.file:\n      path: \"~\/.config\/bash\"\n      state: directory\n      mode: \"0755\"\n  become: no\n\n- name: Deploy vim environment configuration to modular bash\n  ansible.builtin.copy:\n      src: vim-env.bash\n      dest: \"~\/.config\/bash\/vim-env.bash\"\n      mode: \"0644\"\n  become: no\n\n- name: Display vim configuration status\n  ansible.builtin.debug:\n      msg:\n          - \"Vim configuration completed!\"\n          - \"Config directory: {{ vim_config_dir }}\"\n          - \"Config file: {{ vim_config_file }}\"\n          - \"Viminfo location: {{ vim_viminfo_file }}\"\n          - \"Environment variables configured in ~\/.config\/bash\/vim-env.bash\"\n          - \"Note: Ensure your .bashrc sources ~\/.config\/bash\/*.bash files\"\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Recommended Vim Resources<\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li><a href=\"https:\/\/danielmiessler.com\/blog\/vim\">Learn Vim For the Last Time<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/vimsheet.com\">A Great Vim Cheat Sheet<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/www.youtube.com\/watch?v=z4eA2eC28qg\">Give Me 20 Minutes and I\u2019ll Make You a Vim Motions Expert<\/a><\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>My first encounter with Vim was in 2000 when my then employer decided to port our main product (Bandwidth Control System) from Windows NT to Linux. Linux was a fresh experience for me, and it opened the door to diving into Emacs and Vim for text editing. I recall reading an Emacs vs. Vim article in a magazine or trade-rag a while back that really piqued my interest. Even now, Emacs still feels a bit out of reach for my&#8230;<\/p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https:\/\/blog.vicnunes.com\/?p=198\"> Read More<span class=\"screen-reader-text\">  Read More<\/span><\/a><\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[5],"tags":[10],"class_list":["post-198","post","type-post","status-publish","format-standard","hentry","category-lab","tag-software"],"_links":{"self":[{"href":"https:\/\/blog.vicnunes.com\/index.php?rest_route=\/wp\/v2\/posts\/198","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/blog.vicnunes.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blog.vicnunes.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blog.vicnunes.com\/index.php?rest_route=\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.vicnunes.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=198"}],"version-history":[{"count":2,"href":"https:\/\/blog.vicnunes.com\/index.php?rest_route=\/wp\/v2\/posts\/198\/revisions"}],"predecessor-version":[{"id":201,"href":"https:\/\/blog.vicnunes.com\/index.php?rest_route=\/wp\/v2\/posts\/198\/revisions\/201"}],"wp:attachment":[{"href":"https:\/\/blog.vicnunes.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=198"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.vicnunes.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=198"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.vicnunes.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=198"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}