diff --git a/nvim/lua/th b/.nojekyll
similarity index 100%
rename from nvim/lua/th
rename to .nojekyll
diff --git a/LICENSE b/LICENSE
deleted file mode 100644
index 370cde6..0000000
--- a/LICENSE
+++ /dev/null
@@ -1,21 +0,0 @@
-MIT License
-
-Copyright (c) 2026 CrtlUserKnown
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
diff --git a/README.md b/README.md
index ab953b7..b35868f 100644
--- a/README.md
+++ b/README.md
@@ -161,3 +161,4 @@ Plugins will be installed automatically via `lazy.nvim` on first launch.
---
Built by [CrtlUserKnown](https://github.com/CrtlUserKnown)
+
diff --git a/about.html b/about.html
new file mode 100644
index 0000000..76dc2e8
--- /dev/null
+++ b/about.html
@@ -0,0 +1,229 @@
+
+
+
A hand-crafted Neovim configuration built for developers who want a fast, modern editor without having to stitch plugins together themselves.
+
+
+
+
+
+
+
+
The idea
+
+ CharVim started as a personal Neovim config and grew into a shareable, opinionated distribution. The goal is simple: give you a Neovim that feels like a real IDE, with LSP, completion, debugging, AI, fuzzy-finding, and multi-cursor support, while staying lean, fast, and true to Vim's modal nature.
+
+
+ Every plugin in CharVim earns its place. Nothing is included just because it's popular. The configuration is built around a daily-driver workflow: writing code, navigating large projects, debugging, and staying in flow.
+
+
+
Philosophy
+
+
Speed first. Plugins are lazy-loaded by filetype. Cold-start time stays minimal regardless of how many servers are installed.
+
Keyboard-centric. Everything has a keymap. The mouse is optional. Custom implementations like multi-cursor and Find and Replace are built with Vim motions in mind.
+
Personalize without forking. Two git-ignored files, user.lua and user-plugins.lua, let you add keymaps, options, and plugins that survive git pull updates without ever touching core files.
+
Batteries included. The install script handles system dependencies automatically on macOS, Fedora, Debian/Ubuntu, and Arch Linux.
+
+
+
Language support
+
CharVim ships with ten pre-configured LSP servers, covering the most common languages for backend, frontend, systems, and document authoring:
+
+
Lua via lua_ls, with full Neovim API awareness
+
Python via Pyright
+
TypeScript and JavaScript via ts_ls
+
Java via JDTLS (Maven, Gradle, and Ant are auto-detected)
+
Kotlin via kotlin_language_server (JVM target 17, inlay hints)
+
Go via gopls (gofumpt, full inlay hints, shadow analysis)
+
C, C++, and Obj-C via clangd
+
Swift and Obj-C via SourceKit-LSP
+
Typst via Tinymist (exports PDF on save)
+
XML via lemminx
+
+
Inlay hints are enabled by default on all servers that support them and can be toggled with leaderih.
+
+
The themes
+
CharVim ships with three themes, all switchable at runtime via :ThemeSelect or :Theme <name>. Your selection persists across restarts.
+
+
Knew-pines: A warm variant of Rose Pine with dusty purples, muted pinks, gold accents, and pine greens. This is the default.
+
Noir-cat: A custom dark theme with a Catppuccin-Mocha palette. Deep blacks, blue and purple highlights, and soft pastels. Defined in nvim/colors/noir-cat.lua.
+
Dark: A clean, minimal dark mode with cool blue accents. Low saturation, high legibility.
+
+
Additional themes available inside Neovim include Tokyo Night, Catppuccin, Gruvbox, and Habamax.
+
+
AI integration
+
+ avante.nvim connects CharVim to Anthropic Claude (claude-haiku-4-5) for AI-assisted code chat and editing. Use :AvanteAsk to discuss code in context, or :AvanteEdit to apply AI suggestions directly. Add your ANTHROPIC_API_KEY to a .env file at the repo root and it gets loaded on startup automatically.
+
+
+
Installation
+
Run the one-line install script. It detects your OS, installs missing system dependencies, and symlinks the config into ~/.config/nvim.
ripgrep for Telescope live grep and Find and Replace
+
fd for Telescope file finding
+
python3 for DAP Python support
+
make for building avante.nvim
+
node for Mason-managed LSP servers like TypeScript
+
ANTHROPIC_API_KEY (optional) for AI features via avante.nvim
+
+
+
User customization
+
Two git-ignored files let you personalize CharVim without touching core config. They are auto-created with template comments on first startup and never overwritten by updates.
+
+
nvim/lua/user.lua
+
Loaded last, after all CharVim modules. Add keymaps, options, autocommands, or override any default:
Every addition, change, fix, and removal across every release of CharVim, starting from the very beginning.
+
+
+
+
+
+
+
+
+
+
+ v1.5.2
+ Latest
+ June 26, 2026
+
+
Cross-Platform Setup
+
+ This release rewrites the setup script to support macOS (Homebrew), Debian/Ubuntu (apt), and Arch Linux (pacman) alongside the existing Fedora support. Each platform function checks for missing binaries before installing, keeping installs minimal. The README was also updated with detailed LSP and linter reference tables.
+
+
+
+ Added
+
+
macOS support via Homebrew
+
Debian and Ubuntu support via apt
+
Arch Linux support via pacman
+
Per-platform missing-binary checks before installation
+
+
+
+ Changed
+
+
Setup script refactored from a single Fedora-only function to a per-platform design
+
Dependency list expanded to include make and node
+
README now includes LSP server and linter tables for quick reference
+
README updated with per-distro package manager table and expanded prerequisites
+
+
+
+
+
+
+
+
+ v1.5.1
+ June 26, 2026
+
+
Find and Replace Panel, Theme Persistence, AI Switch to Claude
+
+ This release replaces the old inline rename with a full find and replace panel backed by ripgrep. The theme switcher was rewritten with live Telescope preview and cross-session persistence. avante.nvim was switched from a local Ollama model to Anthropic Claude.
+
+
+
+ Added
+
+
New rename-config.lua module with a full find and replace panel using nui.nvim
+
Live ripgrep search with file preview
+
Single-occurrence and project-wide regex replace
+
LSP rename integration when the cursor is on a known symbol
+
Persistent undo via undofile = true
+
Theme selection now persists across sessions
+
Telescope-powered live-preview theme picker
+
"Auto" theme (Neovim default) added as the first option
+
Fedora dependency auto-install via dnf in setup.sh
+
+
+
+ Changed
+
+
avante.nvim provider switched from Ollama (qwen2.5-coder:3b) to Anthropic Claude (claude-haiku-4-5)
+
Auto-suggestions disabled; accept key changed from Shift+Tab to Alt+Enter
+
Old project_rename function removed in favor of the new rename-config module
+
<leader>r which-key group label changed from "Refactor" to "Run"
+
c and cpp removed from sourcekit-lsp filetypes
+
.env at the repo root is now auto-loaded on startup
+
+
+
+ Fixed
+
+
.env, CLAUDE.md, and GEMINI.md added to .gitignore
+
+
+
+
+
+
+
+
+ v1.5.0
+ June 17, 2026
+
+
AI Assistance, Multi-Theme Support, and Integrated Linting
+
+ This release introduces local AI code completion, multiple theme support, and integrated linting. GitHub Copilot was removed in favor of a fully local AI solution. The completion stack was restored to nvim-cmp with LuaSnip, and the LSP setup was migrated from mason-lspconfig to direct lspconfig for greater control.
+
+
+
+ Added
+
+
avante.nvim with Ollama and Qwen 2.5 Coder for local AI code completion
+
Tokyo Night theme
+
Catppuccin (Mocha) theme
+
Gruvbox theme
+
theme-switcher module for runtime theme cycling
+
lint-config.lua with nvim-lint for configurable linting
+
Custom noir-cat colorscheme in nvim/colors/
+
make.lua ftplugin for Makefile-specific settings
+
+
+
+ Changed
+
+
Completion reverted from native LSP back to nvim-cmp with LuaSnip snippets
+
LSP replaced mason-lspconfig.nvim with direct lspconfig setup for greater control
+
Inlay hints added across all configured LSP clients
+
Statusline updated to dynamically reflect the active colorscheme
+
noice.nvim popupmenu backend disabled
+
lazy.nvim clone depth set to 1 and timeout increased to 120s
+
+
+
+ Removed
+
+
GitHub Copilot (zbirenbaum/copilot.lua) and all related configuration
+
+
+
+
+
+
+
+
+ v1.3.0
+ May 6, 2026
+
+
Neovim 0.12 Compatibility and Optimization
+
+ This release focuses on compatibility and optimization for Neovim 0.12, including the transition to native completion and internal refactors to support the latest editor APIs. File runners for HTML, Python, and Java were added alongside Mason integration in the dashboard.
+
+
+
+ Added
+
+
Mason integration in the alpha-nvim dashboard
+
Keybindings and user commands to run HTML, Python, and Java files directly
+
nvim-ts-autotag as a Treesitter dependency for HTML/XML tag management
+
+
+
+ Changed
+
+
Major internal refactoring for Neovim 0.12 compatibility
+
Switched to native LSP completion; removed nvim-cmp and related plugins
+
Statusline refactored for better performance and mode-specific colors
+
Treesitter updated to use the new main branch
+
Dashboard button layout streamlined and which-key presets updated
+
+
+
+ Fixed
+
+
Updated LSP capability checks to use client:supports_method()
+
Treesitter parser retrieval updated for the new API
+
Improved reliability of setup.sh in CI and GitHub Actions environments
+
Corrected scrolloff which was set as a global Lua variable instead of a vim option
+
+
+
+
+
+
+
+
+ v1.2.0
+ March 29, 2026
+
+
Project Management and CI
+
+ Added project.nvim for smart project detection and workspace switching, integrated with Telescope. GitHub Actions CI was set up for automated Neovim configuration testing.
+
+
+
+ Added
+
+
project.nvim for project detection and management via Telescope
+
GitHub Actions workflow for automated Neovim configuration testing
+
+
+
+ Changed
+
+
README cleaned up by removing unnecessary emoji
+
.DS_Store added to .gitignore
+
+
+
+ Removed
+
+
Old .nvim.bak version from the repository
+
+
+
+
+
+
+
+
+ v1.1.0
+ March 10, 2026
+
+
Debugger Integration and Setup Script
+
+ DAP debuggers were integrated for Java, Swift, Python, and C/C++ with customized UI layouts and session management. A setup.sh install script was added for easier environment setup, and COBOL language support was improved through specialized ftplugins.
+
+
+
+ Added
+
+
DAP debuggers for Java, Swift, Python, and C/C++ with UI auto-open/close
+
setup.sh for easier installation and environment configuration
+
COBOL ftplugin with 8-space tabs, column markers at 7 and 73
+
GitHub Actions workflow for automated setup testing
+
+
+
+ Fixed
+
+
Symlink creation and repository source detection issues during installation
+
+
+
+
+
+
+
+
+ v0.1.0
+ March 5, 2026
+
+
Initial Release
+
+ The first public release of CharVim. A complete Neovim configuration with comprehensive LSP support, GitHub Copilot AI, a startup dashboard, fuzzy finding, quick file navigation, and tmux integration. The project also launched with an official GitHub Wiki.
+
+
+
+ Added
+
+
GitHub Copilot for AI-assisted coding
+
Renamed entry point to charvim, allowing use of cvim command
+
Comprehensive LSP configuration via mason.nvim
+
Specialized handlers for Java (JDTLS), Typst (Tinymist), and Go (gopls)
+
alpha-nvim dashboard with ASCII art logo
+
noice.nvim for enhanced cmdline and popups
+
rose-pine colorscheme (Knew-pines variant)
+
telescope.nvim for fuzzy finding and file browsing
+
harpoon2 for quick file switching
+
vim-tmux-navigator for seamless pane navigation
+
nvim-surround for delimiter manipulation
+
vim-visual-multi for multiple cursors
+
Official project documentation via GitHub Wiki
+
+
+
+ Changed
+
+
Initial project structure and configuration migration from personal dotfiles
Complete reference for CharVim, a custom Neovim 0.12+ configuration managed by lazy.nvim. The entry point is nvim/init.lua and all modules live in nvim/lua/.
+
+
+
Installation
+
Run the install script. It auto-detects your OS and installs missing system dependencies before symlinking the config into ~/.config/nvim.
Back up your existing config: mv ~/.config/nvim ~/.config/nvim.bak
+
Clone the repository: git clone https://github.com/CrtlUserKnown/Charvim.git ~/.config/nvim
+
Launch Neovim: nvim
+
+
Plugins install automatically via lazy.nvim on first launch.
+
+
+
Requirements
+
+
Neovim 0.12+
+
Git
+
A Nerd Font for icons
+
ripgrep for Telescope live grep and Find and Replace
+
fd for Telescope file finding
+
python3 for DAP Python support
+
make for building avante.nvim
+
node for Mason-managed LSP servers
+
ANTHROPIC_API_KEY (optional) for AI features via avante.nvim
+
+
+
+
How Vim Works
+
Vim (and Neovim) is a modal editor. Keys do different things depending on the active mode. Press Esc to return to Normal mode from anywhere.
+
+
Mode
How to enter
Purpose
+
+
Normal
Esc
Navigation and commands. This is the default mode.
+
Insert
i / a / o
Typing text
+
Visual
v / V / Ctrl+v
Selecting blocks of text
+
Command-line
: / /
Saving, quitting, searching
+
+
+
+
+ Tip
+ Efficiency in Vim comes from staying in Normal mode and combining operators with motions: dw deletes a word, yy yanks a line, ci" changes text inside quotes.
+
+
+
+
Leader Keys
+
+
Key
Value
+
+
<leader>
Space
+
<localleader>
\
+
+
+
Leader key timeout: 500 ms.
+
+
+
General
+
+
Keys
Mode
Action
+
+
jk
Insert
Escape to Normal mode
+
leaderw
Normal
Quick save (:w)
+
leaderW
Normal
Save popup (Save / Save As / Format & Save / Save All)
+
leaderq
Normal
Save and quit (:wq)
+
leaderqq
Normal
Quit without saving (:q!)
+
;
Normal
Shell command shortcut (:!)
+
+
+
+
+
Navigation
+
+
Keys
Mode
Action
+
+
[[
Normal
Go to beginning of file (gg)
+
]]
Normal
Go to end of file (G)
+
H
Normal / Visual
Go to beginning of line (0)
+
L
Normal / Visual
Go to end of line ($)
+
Alt+[
Normal / Visual
Previous paragraph ({)
+
Alt+]
Normal / Visual
Next paragraph (})
+
Ctrl+h/j/k/l
Normal
Navigate splits (also integrates with tmux panes)
+
+
+
+
+
Line Manipulation
+
+
Keys
Mode
Action
+
+
Alt+k / Alt+j
Normal
Move line up / down
+
Alt+k / Alt+j
Visual
Move selection up / down
+
Ctrl+k / Ctrl+j
Insert
Move line up / down
+
Ctrl+Shift+k/j
Normal
Duplicate line up / down
+
Ctrl+Shift+k/j
Visual
Duplicate selection up / down
+
+
+
+
+
File Explorer
+
+
Keys
Mode
Action
+
+
leadere
Normal
Open file browser at current file's directory
+
leaderE
Normal
Open file browser at project root
+
leadert
Normal
Open new tab with file browser
+
:E / :Tree
Command
Open file browser
+
+
+
+
+
Telescope
+
+
Keys
Context
Action
+
+
leaderg
Normal
Live grep
+
leaderp
Normal
Projects picker
+
Ctrl+.
Telescope insert
Toggle hidden files
+
+
+
Hidden files are shown by default. .git/ directories are always ignored.
+
+
+
Harpoon 2
+
+
Keys
Mode
Action
+
+
leadera
Normal
Add current file to Harpoon list
+
leaderar
Normal
Remove current file from Harpoon list
+
leaderac
Normal
Clear all Harpoon marks
+
leaderh
Normal
Toggle Harpoon quick menu
+
Alt+1 – 5
Normal
Jump to Harpoon file 1–5
+
Alt+n / Alt+p
Normal
Next / previous Harpoon file
+
+
+
+
+
Multi-Cursor
+
+
Keys
Mode
Action
+
+
Ctrl+n
Normal
Enter multi-cursor mode
+
Ctrl+↑
Normal / Visual
Add cursor above
+
Ctrl+↓
Normal / Visual
Add cursor below
+
j / k
Multi-cursor active
Move down/up and extend cursors
+
Esc
Multi-cursor active
Exit multi-cursor mode
+
+
+
In multi-cursor mode, entering Insert types at all cursor positions simultaneously.
+
+
+
Find & Replace / Rename
+
+
Keys
Mode
Action
+
+
leaderS
Normal
Open find & replace panel
+
leaderS
Visual
Open panel pre-filled with selection
+
:Find
Command
Same as leaderS
+
+
+
+
Panel navigation:
+
+
Keys
Context
Action
+
+
Tab
Any panel
Cycle focus: Find → Replace → Results
+
j / k
Results list
Navigate matches
+
y
Results list
Accept replacement for highlighted match
+
Y
Any panel
Accept replacement for all matches
+
Enter
Results list
Jump to match in file
+
Esc / q
Any panel
Close
+
+
+
When the find term matches an identifier with an active LSP client, Y uses LSP rename instead of regex substitution.
+
+
+
LSP
+
+
Keys
Mode
Action
+
+
K
Normal
Hover documentation
+
gd
Normal
Go to definition
+
gD
Normal
Go to declaration
+
gi
Normal
Go to implementation
+
go
Normal
Go to type definition
+
gr
Normal
Show references
+
gs
Normal
Signature help
+
leaderlr
Normal
Rename symbol (LSP)
+
leaderca
Normal
Code action
+
leaderle
Normal
Open diagnostic float
+
[d / ]d
Normal
Previous / next diagnostic
+
leaderih
Normal
Toggle inlay hints
+
leaderig
Normal
Add word to dictionary (ltex/typos)
+
+
+
+
Configured LSP Servers
+
+
Server
Languages
Notes
+
+
lua_ls
Lua
Auto-installed via Mason
+
pyright
Python
Auto-installed via Mason
+
ts_ls
TypeScript / JavaScript / Vue
Auto-installed via Mason
+
jdtls
Java
Per-project workspace, GoogleStyle format
+
kotlin_language_server
Kotlin
JVM target 17, type & param inlay hints
+
gopls
Go
gofumpt, full inlay hints, shadow analysis
+
clangd
C / C++ / Obj-C
Auto-installed via Mason
+
tinymist
Typst
Exports PDF on save
+
sourcekit-lsp
Swift / Obj-C
System binary; lazy-enabled on filetype
+
lemminx
XML
Auto-installed via Mason
+
+
+
+
+
Debugging (DAP)
+
+
Keys
Mode
Action
+
+
leaderdb
Normal
Toggle breakpoint
+
leaderdB
Normal
Set conditional breakpoint
+
leaderdc
Normal
Continue
+
leaderdn
Normal
Step over
+
leaderdi
Normal
Step into
+
leaderdo
Normal
Step out
+
leaderdr
Normal
Open REPL
+
leaderdl
Normal
Run last configuration
+
leaderdt
Normal
Terminate session
+
leaderdu
Normal
Toggle DAP UI
+
leaderdp
Normal
Debug Python method under cursor
+
+
+
+
+
Language
Adapter
+
+
Python
dap-python
+
Java
jdtls built-in
+
C / C++
codelldb
+
Swift
codelldb
+
Kotlin
kotlin-debug-adapter
+
+
+
The DAP UI opens automatically when a session starts and closes when it ends.
+
+
+
AI (avante.nvim)
+
+
Keys
Mode
Action
+
+
Alt+Enter
Insert
Accept inline suggestion
+
Alt+]
Insert
Next suggestion
+
Ctrl+]
Insert
Dismiss suggestion
+
+
+
Requires ANTHROPIC_API_KEY set in a .env file at the repo root. The file is auto-loaded on startup.
+
+
+
Themes
+
+
Keys / Command
Action
+
+
:ThemeSelect
Open interactive theme picker with Telescope live-preview
+
:Theme <name>
Switch directly to a named theme
+
Dashboard t
Open theme picker from the dashboard
+
+
+
Available theme names: Auto, Knew-pines, Noir-cat, Tokyo Night, Catppuccin, Gruvbox, Habamax. Selection persists to ~/.local/share/nvim/charvim_theme.
+
+
+
UI Plugins
+
+
noice.nvim: Replaces the command line with a centered popup.
+
alpha-nvim: Dashboard with ASCII art logo and a two-column quick-access grid.
+
which-key.nvim: Displays available keybindings in a popup using the modern preset.
+
+
+
+
Navigation and File Management
+
+
telescope.nvim and telescope-file-browser.nvim: Fuzzy finder and file browser that replaces netrw. Shows hidden files by default.
+
project.nvim: Project detection and management via Telescope.
+
harpoon (v2): Quick file bookmarking and navigation.
+
vim-tmux-navigator: Seamless navigation between Neovim splits and tmux panes.
+
+
+
+
Editing Tools
+
+
nvim-surround: Add, change, and delete surrounding pairs.
CharVim bundles a carefully chosen set of plugins and custom modules. Opinionated enough to feel cohesive, flexible enough to make your own.
+
+
+
+ ⚡
+
Lazy-loaded plugins
+
Built on lazy.nvim with per-filetype loading. Your editor starts fast and stays fast, no matter how many plugins are installed.
+
+
+
+ 🧠
+
Full LSP suite
+
Ten language servers pre-configured for Lua, Python, TypeScript, Java, Kotlin, Go, C/C++, Swift, Typst, and XML. All installed and managed through Mason.
+
+
+
+ 🤖
+
AI assistance
+
avante.nvim powered by Anthropic Claude for inline code chat and AI-assisted editing. Just set ANTHROPIC_API_KEY and go.
+
+
+
+ 🐛
+
Debug Adapter Protocol
+
DAP configured for Python, Java, C/C++, Swift, and Kotlin. The debug UI opens automatically when a session starts and closes when it ends.
+
+
+
+ 🎨
+
Three themes
+
Knew-pines, Noir-cat, and Dark. Switch between them at runtime with a live Telescope preview, and your choice carries over between sessions.
+
+
+
+ 🔎
+
Find and Replace panel
+
A custom nui.nvim panel backed by ripgrep. Live search with file preview, project-wide regex replace, and LSP rename when available.
+
+
+
+ 📌
+
Harpoon 2
+
Quick file bookmarks with Alt+1 through Alt+5 jumps. Navigate your five most important files in a single keystroke.
+
+
+
+ ✏️
+
Multi-cursor
+
Native multi-cursor implementation with no plugin dependency. Add cursors above or below with Ctrl+↑ and Ctrl+↓, then type at all positions at once.
+
+
+
+ 🖥️
+
Cross-platform setup
+
One install script, four platforms: macOS via Homebrew, Fedora, Debian/Ubuntu, and Arch Linux. Missing dependencies get installed automatically.
+
+
+
+
+
+
+
+
+
+
+
Color themes
+
Three themes. Click to switch.
+
All themes are available inside Neovim via :ThemeSelect. The site reflects whichever theme you pick.
+
+
+
+
+
+
+
+
+
+ knew-pines
+
+
+
localM={}
+
functionM.setup(opts)
+
localtheme="knew-pines"
+
-- warm rose-pine palette
+
vim.cmd("colorscheme " .. theme)
+
end
+
returnM
+
+
+
+
+
+
+
+
+
+ noir-cat
+
+
+
localM={}
+
functionM.setup(opts)
+
localtheme="noir-cat"
+
-- dark catppuccin-inspired
+
vim.cmd("colorscheme " .. theme)
+
end
+
returnM
+
+
+
+
+
+
+
+
+
+ dark
+
+
+
localM={}
+
functionM.setup(opts)
+
localtheme="dark"
+
-- minimal github-dark palette
+
vim.cmd("colorscheme " .. theme)
+
end
+
returnM
+
+
+
+
+
+
+
+
+
+
+
+
Installation
+
One command to get started.
+
+ The install script detects your OS and installs all required dependencies before symlinking the config into place.
+
+
+
+
+
+
+
+
+
+
+
diff --git a/js/main.js b/js/main.js
new file mode 100644
index 0000000..f81cc90
--- /dev/null
+++ b/js/main.js
@@ -0,0 +1,119 @@
+/* === THEME === */
+const DEFAULT_THEME = 'knew-pines';
+const STORAGE_KEY = 'charvim-theme';
+
+function getTheme() {
+ return localStorage.getItem(STORAGE_KEY) || DEFAULT_THEME;
+}
+
+function applyTheme(theme) {
+ document.documentElement.setAttribute('data-theme', theme);
+ localStorage.setItem(STORAGE_KEY, theme);
+
+ document.querySelectorAll('.theme-btn').forEach(btn => {
+ btn.classList.toggle('active', btn.dataset.theme === theme);
+ });
+
+ document.querySelectorAll('.theme-preview').forEach(prev => {
+ prev.classList.toggle('active-preview', prev.dataset.theme === theme);
+ });
+}
+
+/* === CURSOR GRADIENT === */
+function initCursorGradient() {
+ let tx = 50, ty = 50;
+ let cx = 50, cy = 50;
+
+ document.addEventListener('mousemove', e => {
+ tx = (e.clientX / window.innerWidth) * 100;
+ ty = (e.clientY / window.innerHeight) * 100;
+ });
+
+ (function tick() {
+ cx += (tx - cx) * 0.055;
+ cy += (ty - cy) * 0.055;
+ document.documentElement.style.setProperty('--gx', cx.toFixed(2) + '%');
+ document.documentElement.style.setProperty('--gy', cy.toFixed(2) + '%');
+ requestAnimationFrame(tick);
+ })();
+}
+
+/* === COPY BUTTONS === */
+function initCopyBtns() {
+ document.querySelectorAll('.copy-btn').forEach(btn => {
+ btn.addEventListener('click', () => {
+ const text = btn.closest('.code-block').querySelector('code').textContent;
+ navigator.clipboard.writeText(text).then(() => {
+ const orig = btn.textContent;
+ btn.textContent = '✓';
+ btn.style.color = 'var(--foam)';
+ setTimeout(() => { btn.textContent = orig; btn.style.color = ''; }, 2000);
+ });
+ });
+ });
+}
+
+/* === NAVIGATION === */
+function initNav() {
+ const page = window.location.pathname.split('/').pop() || 'index.html';
+
+ document.querySelectorAll('.nav-links a').forEach(a => {
+ const href = a.getAttribute('href') || '';
+ const match = href === page
+ || (page === '' && href === 'index.html')
+ || (page === 'index.html' && href === 'index.html');
+ a.classList.toggle('active', match);
+ });
+
+ const burger = document.querySelector('.hamburger');
+ const links = document.querySelector('.nav-links');
+ if (burger && links) {
+ burger.addEventListener('click', () => links.classList.toggle('open'));
+ document.addEventListener('click', e => {
+ if (!burger.contains(e.target) && !links.contains(e.target)) {
+ links.classList.remove('open');
+ }
+ });
+ }
+}
+
+/* === DOCS SIDEBAR SCROLL HIGHLIGHT === */
+function initDocsSidebar() {
+ const headings = document.querySelectorAll('.docs-content h2[id], .docs-content h3[id]');
+ const sideLinks = document.querySelectorAll('.sidebar-links a[href^="#"]');
+ if (!headings.length) return;
+
+ const obs = new IntersectionObserver(entries => {
+ entries.forEach(e => {
+ if (e.isIntersecting) {
+ sideLinks.forEach(l => {
+ l.classList.toggle('active', l.getAttribute('href') === '#' + e.target.id);
+ });
+ }
+ });
+ }, { rootMargin: '-64px 0px -55% 0px', threshold: 0 });
+
+ headings.forEach(h => obs.observe(h));
+}
+
+/* === THEME PREVIEW CLICK === */
+function initThemePreviews() {
+ document.querySelectorAll('.theme-preview[data-theme]').forEach(el => {
+ el.addEventListener('click', () => applyTheme(el.dataset.theme));
+ });
+}
+
+/* === BOOT === */
+document.addEventListener('DOMContentLoaded', () => {
+ applyTheme(getTheme());
+
+ document.querySelectorAll('.theme-btn[data-theme]').forEach(btn => {
+ btn.addEventListener('click', () => applyTheme(btn.dataset.theme));
+ });
+
+ initCursorGradient();
+ initCopyBtns();
+ initNav();
+ initDocsSidebar();
+ initThemePreviews();
+});
diff --git a/nvim/.gitignore b/nvim/.gitignore
deleted file mode 100644
index 8d7af86..0000000
--- a/nvim/.gitignore
+++ /dev/null
@@ -1,7 +0,0 @@
-# Neovim
-lazy-lock.json
-.DS_Store
-
-# Backup files
-*.backup
-*~
diff --git a/nvim/after/ftplugin/cobol.lua b/nvim/after/ftplugin/cobol.lua
deleted file mode 100644
index 73ec5b2..0000000
--- a/nvim/after/ftplugin/cobol.lua
+++ /dev/null
@@ -1,14 +0,0 @@
--- COBOL-specific settings
-
--- Tab settings
-vim.opt_local.tabstop = 8 -- Number of spaces a tab counts for
-vim.opt_local.shiftwidth = 8 -- Number of spaces for indentation
-vim.opt_local.expandtab = true -- Convert tabs to spaces
-
-vim.opt_local.colorcolumn = "7,73" -- Mark columns 7 and 73
-
-vim.opt_local.textwidth = 72
-
--- Line numbering (helpful for debugging)
-vim.opt_local.number = true
-vim.opt_local.relativenumber = false
diff --git a/nvim/after/ftplugin/make.lua b/nvim/after/ftplugin/make.lua
deleted file mode 100644
index 15c6583..0000000
--- a/nvim/after/ftplugin/make.lua
+++ /dev/null
@@ -1,7 +0,0 @@
--- Makefile-specific settings
-
--- Tabs are required for Makefile recipes
-vim.opt_local.tabstop = 8
-vim.opt_local.shiftwidth = 8
-vim.opt_local.softtabstop = 8
-vim.opt_local.expandtab = false
diff --git a/nvim/init.lua b/nvim/init.lua
deleted file mode 100644
index 183650b..0000000
--- a/nvim/init.lua
+++ /dev/null
@@ -1,58 +0,0 @@
-vim.g.mapleader = " "
-vim.g.maplocalleader = "\\"
-
-local env_file = vim.fn.stdpath("config") .. "/../.env"
-if vim.fn.filereadable(env_file) == 1 then
- for line in io.lines(env_file) do
- local key, value = line:match("^([%w_]+)=(.*)$")
- if key and value ~= "" then
- vim.fn.setenv(key, value)
- end
- end
-end
-
-vim.api.nvim_set_hl(0, 'CursorLineNr', {
- fg = '#f6c177',
- bold = true
-})
-
-require('statusline')
-require('options')
-require('keymaps')
-require('theme-switcher')
-require('multicursor').setup()
-
-require('autoclose').setup()
-
-local lazypath = vim.fn.stdpath("data") .. "/lazy/lazy.nvim"
-if not vim.loop.fs_stat(lazypath) then
- vim.fn.system({ "git", "clone", "--filter=blob:none", "https://github.com/folke/lazy.nvim.git", "--branch=stable",
- lazypath })
-end
-vim.opt.rtp:prepend(lazypath)
-
-require('lazy').setup('plugins', {
- change_detection = { enabled = true, notify = false },
- timeout = 120000,
- git = {
- clone_params = { depth = 1 },
- },
-})
-
-require('lsp-config')
-require('treesitter-config')
-require('completion-config')
-require('rename-config')
-
-vim.api.nvim_set_hl(0, "Normal", { bg = "none" })
-vim.api.nvim_set_hl(0, "NormalFloat", { bg = "none" })
-
-vim.api.nvim_create_autocmd("FileType", {
- pattern = "crystal",
- callback = function()
- if vim.bo.syntax == 'on' or vim.bo.syntax == 'crystal' then
- vim.cmd("set syntax=ruby")
- end
- end
-})
-
diff --git a/nvim/lazy-lock.json b/nvim/lazy-lock.json
new file mode 100644
index 0000000..ce59345
--- /dev/null
+++ b/nvim/lazy-lock.json
@@ -0,0 +1,36 @@
+{
+ "LuaSnip": { "branch": "master", "commit": "0abc8f390b278c3b4aabc4c004ac8a088b65cf24" },
+ "alpha-nvim": { "branch": "main", "commit": "6c6a89d5b068b5251c8bdf0dd57bb921bcfeeb09" },
+ "avante.nvim": { "branch": "main", "commit": "dee2402ed0b7c1d3710c0c8eed3d26aa055e118e" },
+ "catppuccin": { "branch": "main", "commit": "49a926655a2f5579e9c276470fc300baaa49e524" },
+ "cmp-buffer": { "branch": "main", "commit": "b74fab3656eea9de20a9b8116afa3cfc4ec09657" },
+ "cmp-nvim-lsp": { "branch": "main", "commit": "cbc7b02bb99fae35cb42f514762b89b5126651ef" },
+ "dressing.nvim": { "branch": "master", "commit": "2d7c2db2507fa3c4956142ee607431ddb2828639" },
+ "gruvbox.nvim": { "branch": "main", "commit": "154eb5ff5b96d0641307113fa385eaf0d36d9796" },
+ "harpoon": { "branch": "harpoon2", "commit": "87b1a3506211538f460786c23f98ec63ad9af4e5" },
+ "img-clip.nvim": { "branch": "main", "commit": "b6ddfb97b5600d99afe3452d707444afda658aca" },
+ "lazy.nvim": { "branch": "main", "commit": "306a05526ada86a7b30af95c5cc81ffba93fef97" },
+ "mason-nvim-dap.nvim": { "branch": "main", "commit": "9a10e096703966335bd5c46c8c875d5b0690dade" },
+ "mason.nvim": { "branch": "main", "commit": "2a6940af80375532e5e9e7c1f2fc6319a1b7a69d" },
+ "noice.nvim": { "branch": "main", "commit": "7bfd942445fb63089b59f97ca487d605e715f155" },
+ "nui.nvim": { "branch": "main", "commit": "de740991c12411b663994b2860f1a4fd0937c130" },
+ "nvim-cmp": { "branch": "main", "commit": "a1d504892f2bc56c2e79b65c6faded2fd21f3eca" },
+ "nvim-dap": { "branch": "master", "commit": "531771530d4f82ad2d21e436e3cc052d68d7aebb" },
+ "nvim-dap-python": { "branch": "master", "commit": "1808458eba2b18f178f990e01376941a42c7f93b" },
+ "nvim-dap-ui": { "branch": "master", "commit": "1a66cabaa4a4da0be107d5eda6d57242f0fe7e49" },
+ "nvim-lint": { "branch": "master", "commit": "99cbc3ca8a76845fca50e496be7212bebf907dd3" },
+ "nvim-nio": { "branch": "master", "commit": "21f5324bfac14e22ba26553caf69ec76ae8a7662" },
+ "nvim-surround": { "branch": "main", "commit": "2e93e154de9ff326def6480a4358bfc149d5da2c" },
+ "nvim-treesitter": { "branch": "main", "commit": "4916d6592ede8c07973490d9322f187e07dfefac" },
+ "nvim-ts-autotag": { "branch": "main", "commit": "88c1453db4ba7dd24131086fe51fdf74e587d275" },
+ "nvim-web-devicons": { "branch": "master", "commit": "dfbfaa967a6f7ec50789bead7ef87e336c1fa63c" },
+ "plenary.nvim": { "branch": "master", "commit": "74b06c6c75e4eeb3108ec01852001636d85a932b" },
+ "project.nvim": { "branch": "main", "commit": "8c6bad7d22eef1b71144b401c9f74ed01526a4fb" },
+ "rose-pine": { "branch": "main", "commit": "ff483051a47e27d84bdef47703538df1ed9f4a47" },
+ "telescope-file-browser.nvim": { "branch": "master", "commit": "3610dc7dc91f06aa98b11dca5cc30dfa98626b7e" },
+ "telescope.nvim": { "branch": "master", "commit": "7d324792b7943e4aa16ad007212e6acc6f9fe335" },
+ "tokyonight.nvim": { "branch": "main", "commit": "cdc07ac78467a233fd62c493de29a17e0cf2b2b6" },
+ "typst-preview.nvim": { "branch": "master", "commit": "325036ee145ca51d9efb145c09ac16bce3bc8b7d" },
+ "vim-tmux-navigator": { "branch": "master", "commit": "e41c431a0c7b7388ae7ba341f01a0d217eb3a432" },
+ "which-key.nvim": { "branch": "main", "commit": "3aab2147e74890957785941f0c1ad87d0a44c15a" }
+}
diff --git a/nvim/lua/alpha-config.lua b/nvim/lua/alpha-config.lua
deleted file mode 100644
index 7a17034..0000000
--- a/nvim/lua/alpha-config.lua
+++ /dev/null
@@ -1,83 +0,0 @@
-local alpha = require('alpha')
-local dashboard = require('alpha.themes.dashboard')
-
--- Read ASCII art from file
-local function read_ascii_art()
- local config_path = vim.fn.stdpath('config')
- local art_path = config_path .. '/lua/img/charVim.txt'
- local file = io.open(art_path, 'r')
-
- if file then
- local lines = {}
- for line in file:lines() do
- if line:sub(1,1) == '[' and line:sub(-1,-1) == ']' then
- table.insert(lines, line:sub(2, -2))
- else
- table.insert(lines, line)
- end
- end
- file:close()
- return lines
- else
- return {
- " ",
- " \xe2\x96\x88\xe2\x96\x88\xe2\x96\x88\xe2\x96\x88\xe2\x96\x88\xe2\x96\x88\xe2\x96\x88\xe2\x96\x88\xe2\x96\x88\xe2\x96\x88\xe2\x96\x88\xe2\x96\x88\xe2\x96\x88 \xe2\x96\x88\xe2\x96\x88\xe2\x96\x88\xe2\x96\x88\xe2\x96\x88\xe2\x96\x88\xe2\x96\x88\xe2\x96\x88\xe2\x96\x88 ",
- " \xe2\x96\x88\xe2\x96\x88\xe2\x96\x88\xe2\x96\x88\xe2\x96\x88\xe2\x96\x88\xe2\x96\x88\xe2\x96\x88 \xe2\x96\x88\xe2\x96\x88\xe2\x96\x88\xe2\x96\x88\xe2\x96\x88 ",
- " \xe2\x96\x88\xe2\x96\x88\xe2\x96\x88\xe2\x96\x88\xe2\x96\x88\xe2\x96\x88\xe2\x96\x88\xe2\x96\x88 \xe2\x96\x88\xe2\x96\x88\xe2\x96\x88\xe2\x96\x88 ",
- " \xe2\x96\x88\xe2\x96\x88\xe2\x96\x88\xe2\x96\x88\xe2\x96\x88\xe2\x96\x88\xe2\x96\x88 \xe2\x96\x88\xe2\x96\x88\xe2\x96\x88 ",
- " \xe2\x96\x88\xe2\x96\x88\xe2\x96\x88\xe2\x96\x88\xe2\x96\x88\xe2\x96\x88\xe2\x96\x88 \xe2\x96\x88\xe2\x96\x88\xe2\x96\x88 ",
- " \xe2\x96\x88\xe2\x96\x88\xe2\x96\x88\xe2\x96\x88\xe2\x96\x88\xe2\x96\x88\xe2\x96\x88 \xe2\x96\x88\xe2\x96\x88\xe2\x96\x88\xe2\x96\x88 ",
- " \xe2\x96\x88\xe2\x96\x88\xe2\x96\x88\xe2\x96\x88\xe2\x96\x88\xe2\x96\x88\xe2\x96\x88 \xe2\x96\x88\xe2\x96\x88\xe2\x96\x88 ",
- " \xe2\x96\x88\xe2\x96\x88\xe2\x96\x88\xe2\x96\x88\xe2\x96\x88\xe2\x96\x88\xe2\x96\x88 \xe2\x96\x88\xe2\x96\x88\xe2\x96\x88 ",
- " \xe2\x96\x88\xe2\x96\x88\xe2\x96\x88\xe2\x96\x88\xe2\x96\x88\xe2\x96\x88\xe2\x96\x88 \xe2\x96\x88\xe2\x96\x88\xe2\x96\x88\xe2\x96\x88 ",
- " \xe2\x96\x88\xe2\x96\x88\xe2\x96\x88\xe2\x96\x88\xe2\x96\x88\xe2\x96\x88\xe2\x96\x88 \xe2\x96\x88\xe2\x96\x88\xe2\x96\x88 \xe2\x96\x88\xe2\x96\x88 ",
- " \xe2\x96\x88\xe2\x96\x88\xe2\x96\x88\xe2\x96\x88\xe2\x96\x88\xe2\x96\x88\xe2\x96\x88\xe2\x96\x88\xe2\x96\x88\xe2\x96\x88 \xe2\x96\x88\xe2\x96\x88\xe2\x96\x88\xe2\x96\x88\xe2\x96\x88\xe2\x96\x88 ",
- " \xe2\x96\x88\xe2\x96\x88\xe2\x96\x88\xe2\x96\x88\xe2\x96\x88\xe2\x96\x88\xe2\x96\x88\xe2\x96\x88\xe2\x96\x88 \xe2\x96\x88\xe2\x96\x88\xe2\x96\x88\xe2\x96\x88\xe2\x96\x88\xe2\x96\x88\xe2\x96\x88\xe2\x96\x88 ",
- " \xe2\x96\x88\xe2\x96\x88\xe2\x96\x88\xe2\x96\x88\xe2\x96\x88\xe2\x96\x88\xe2\x96\x88 \xe2\x96\x88\xe2\x96\x88\xe2\x96\x88\xe2\x96\x88\xe2\x96\x88\xe2\x96\x88\xe2\x96\x88\xe2\x96\x88\xe2\x96\x88 ",
- " \xe2\x96\x88\xe2\x96\x88\xe2\x96\x88\xe2\x96\x88\xe2\x96\x88\xe2\x96\x88 \xe2\x96\x88\xe2\x96\x88\xe2\x96\x88\xe2\x96\x88\xe2\x96\x88\xe2\x96\x88\xe2\x96\x88\xe2\x96\x88\xe2\x96\x88\xe2\x96\x88 ",
- " \xe2\x96\x88\xe2\x96\x88\xe2\x96\x88\xe2\x96\x88\xe2\x96\x88 \xe2\x96\x88\xe2\x96\x88\xe2\x96\x88\xe2\x96\x88\xe2\x96\x88\xe2\x96\x88\xe2\x96\x88\xe2\x96\x88 ",
- " \xe2\x96\x88\xe2\x96\x88\xe2\x96\x88 \xe2\x96\x88\xe2\x96\x88\xe2\x96\x88\xe2\x96\x88 ",
- " ",
- }
- end
-end
-
-dashboard.section.header.val = read_ascii_art()
-
-dashboard.section.buttons.val = {
- dashboard.button("f", " Find file", ":Telescope find_files "),
- dashboard.button("p", " Projects", ":Telescope projects "),
- dashboard.button("n", " New file", ":ene startinsert "),
- dashboard.button("r", " Recent files", ":Telescope oldfiles "),
- dashboard.button("g", " Find text", ":Telescope live_grep "),
- dashboard.button("c", " Config", ":e $MYVIMRC "),
- dashboard.button("l", " Lazy", ":Lazy"),
- dashboard.button("m", " Mason", ":Mason"),
- dashboard.button("t", " Themes", ":ThemeSelect"),
- dashboard.button("q", " Quit", ":qa"),
-}
-
-vim.keymap.set('n', 'd', ':Alpha', { desc = 'Go to Dashboard' })
-vim.keymap.set('n', 'p', ':Telescope projects', { desc = 'Projects' })
-
-local function footer()
- local datetime = os.date(" %d-%m-%Y %H:%M:%S")
- local version = vim.version()
- local nvim_version_info = " v" .. version.major .. "." .. version.minor .. "." .. version.patch
- return datetime .. " " .. nvim_version_info
-end
-
-dashboard.section.footer.val = footer()
-
-dashboard.config.layout = {
- { type = "padding", val = 2 },
- dashboard.section.header,
- { type = "padding", val = 2 },
- dashboard.section.buttons,
- { type = "padding", val = 1 },
- dashboard.section.footer,
-}
-
-dashboard.config.opts.noautocmd = true
-
-alpha.setup(dashboard.config)
diff --git a/nvim/lua/autoclose.lua b/nvim/lua/autoclose.lua
deleted file mode 100644
index 701872a..0000000
--- a/nvim/lua/autoclose.lua
+++ /dev/null
@@ -1,176 +0,0 @@
-local autoclose = {}
-
-local config = {
- keys = {
- ["("] = { escape = false, close = true, pair = "()" },
- ["["] = { escape = false, close = true, pair = "[]" },
- ["{"] = { escape = false, close = true, pair = "{}" },
-
- [">"] = { escape = true, close = false, pair = "<>" },
- [")"] = { escape = true, close = false, pair = "()" },
- ["]"] = { escape = true, close = false, pair = "[]" },
- ["}"] = { escape = true, close = false, pair = "{}" },
-
- ['"'] = { escape = true, close = true, pair = '""' },
- ["'"] = { escape = true, close = true, pair = "''" },
- ["`"] = { escape = true, close = true, pair = "``" },
-
- [" "] = { escape = false, close = true, pair = " " },
-
- [""] = {},
- [""] = {},
- [""] = {},
- [""] = { disable_command_mode = true },
- [""] = { disable_command_mode = true },
- },
- options = {
- disabled_filetypes = { "text" },
- disable_when_touch = false,
- touch_regex = "[%w(%[{]",
- pair_spaces = false,
- auto_indent = true,
- disable_command_mode = false,
- },
- disabled = false,
-}
-
-local function insert_get_pair()
- local line = "_" .. vim.api.nvim_get_current_line()
- local col = vim.api.nvim_win_get_cursor(0)[2] + 1
- return line:sub(col, col + 1)
-end
-
-local function command_get_pair()
- local line = "_" .. vim.fn.getcmdline()
- local col = vim.fn.getcmdpos()
- return line:sub(col, col + 1)
-end
-
-local function is_pair(pair)
- if pair == " " then return false end
- for _, info in pairs(config.keys) do
- if pair == info.pair then return true end
- end
- return false
-end
-
-local function is_disabled(info)
- if config.disabled then return true end
-
- -- use vim.bo instead of deprecated nvim_buf_get_option
- local current_filetype = vim.bo.filetype
-
- for _, filetype in pairs(config.options.disabled_filetypes) do
- if filetype == current_filetype then return true end
- end
-
- if info["enabled_filetypes"] ~= nil then
- for _, filetype in pairs(info.enabled_filetypes) do
- if filetype == current_filetype then return false end
- end
- return true
- end
-
- if info["disabled_filetypes"] ~= nil then
- for _, filetype in pairs(info.disabled_filetypes) do
- if filetype == current_filetype then return true end
- end
- end
- return false
-end
-
-local function handler(key, info, mode)
- if is_disabled(info) then return key end
-
- local pair = mode == "insert" and insert_get_pair() or command_get_pair()
-
- if (key == "" or key == "" or key == "") and is_pair(pair) then
- return ""
- elseif
- mode == "insert"
- and (key == "" or key == "")
- and is_pair(pair)
- then
- return "O" .. (config.options.auto_indent and "" or "")
- elseif info.escape and pair:sub(2, 2) == key then
- return mode == "insert" and "U" or ""
- elseif info.close then
- if
- config.options.disable_when_touch
- and (pair .. "_"):sub(2, 2):match(config.options.touch_regex)
- then
- return key
- end
-
- if
- key == " "
- and (
- not config.options.pair_spaces
- or (config.options.pair_spaces and not is_pair(pair))
- or pair:sub(1, 1) == pair:sub(2, 2)
- )
- then
- return key
- end
-
- return info.pair .. (mode == "insert" and "U" or "")
- else
- return key
- end
-end
-
-local function visual_handler(key, info)
- if is_disabled(info) then return key end
-
- if info.close and info.pair then
- local open_char = info.pair:sub(1, 1)
- local close_char = info.pair:sub(2, 2)
- return string.format('`>a%s`', close_char, open_char)
- else
- return key
- end
-end
-
-function autoclose.setup(user_config)
- user_config = user_config or {}
-
- if user_config.keys ~= nil then
- for key, info in pairs(user_config.keys) do
- config.keys[key] = info
- end
- end
-
- if user_config.options ~= nil then
- for key, info in pairs(user_config.options) do
- config.options[key] = info
- end
- end
-
- for key, info in pairs(config.keys) do
- vim.keymap.set("i", key, function()
- return (key == " " and "" or "") .. handler(key, info, "insert")
- end, { noremap = true, expr = true })
-
- if
- not config.options.disable_command_mode
- and not info.disable_command_mode
- then
- vim.keymap.set("c", key, function()
- return (key == " " and "" or "")
- .. handler(key, info, "command")
- end, { noremap = true, expr = true })
- end
-
- if info.close and info.pair then
- vim.keymap.set("x", key, function()
- return visual_handler(key, info)
- end, { noremap = true, expr = true })
- end
- end
-end
-
-function autoclose.toggle()
- config.disabled = not config.disabled
-end
-
-return autoclose
diff --git a/nvim/lua/completion-config.lua b/nvim/lua/completion-config.lua
deleted file mode 100644
index cade18b..0000000
--- a/nvim/lua/completion-config.lua
+++ /dev/null
@@ -1,34 +0,0 @@
-local cmp = require("cmp")
-
-cmp.setup({
- snippet = {
- expand = function(args)
- require("luasnip").lsp_expand(args.body)
- end,
- },
- mapping = cmp.mapping.preset.insert({
- [""] = cmp.mapping.confirm({ select = false }),
- [""] = cmp.mapping(function(fallback)
- if cmp.visible() then
- cmp.select_next_item()
- else
- fallback()
- end
- end, { "i", "s" }),
- [""] = cmp.mapping(function(fallback)
- if cmp.visible() then
- cmp.select_prev_item()
- else
- fallback()
- end
- end, { "i", "s" }),
- }),
- sources = cmp.config.sources({
- { name = "nvim_lsp" },
- }, {
- { name = "buffer" },
- }),
-})
-
-vim.opt.completeopt = "menu,menuone,noselect"
-vim.opt.shortmess:append("c")
diff --git a/nvim/lua/dap-config.lua b/nvim/lua/dap-config.lua
deleted file mode 100644
index d3a9e07..0000000
--- a/nvim/lua/dap-config.lua
+++ /dev/null
@@ -1,133 +0,0 @@
-local dap = require('dap')
-local dapui = require('dapui')
-
--- ui setup
-dapui.setup({
- icons = { expanded = "▾", collapsed = "▸", current_frame = "▸" },
- layouts = {
- {
- elements = {
- { id = "scopes", size = 0.35 },
- { id = "breakpoints", size = 0.15 },
- { id = "stacks", size = 0.35 },
- { id = "watches", size = 0.15 },
- },
- size = 40,
- position = "left",
- },
- {
- elements = {
- { id = "repl", size = 0.5 },
- { id = "console", size = 0.5 },
- },
- size = 10,
- position = "bottom",
- },
- },
-})
-
--- open/close ui with session
-dap.listeners.after.event_initialized["dapui_config"] = function() dapui.open() end
-dap.listeners.before.event_terminated["dapui_config"] = function() dapui.close() end
-dap.listeners.before.event_exited["dapui_config"] = function() dapui.close() end
-
--- python
-local dap_python = require('dap-python')
-dap_python.setup('python3')
-dap_python.test_runner = 'pytest'
-
--- java (jdtls built-in dap)
-dap.configurations.java = {
- {
- type = 'java',
- request = 'attach',
- name = 'Attach to process',
- processId = require('dap.utils').pick_process,
- },
- {
- type = 'java',
- request = 'launch',
- name = 'Launch Java',
- mainClass = function()
- return vim.fn.input('Main class: ', '', 'file')
- end,
- },
-}
-
--- c / c++ / swift adapter
-dap.adapters.codelldb = {
- type = 'server',
- port = '${port}',
- executable = {
- command = vim.fn.stdpath('data') .. '/mason/bin/codelldb',
- args = { '--port', '${port}' },
- },
-}
-
-dap.configurations.c = {
- {
- name = 'Launch',
- type = 'codelldb',
- request = 'launch',
- program = function()
- return vim.fn.input('Executable: ', vim.fn.getcwd() .. '/', 'file')
- end,
- cwd = '${workspaceFolder}',
- stopOnEntry = false,
- },
-}
-dap.configurations.cpp = dap.configurations.c
-
--- swift uses codelldb, points to .build/debug by default
-dap.configurations.swift = {
- {
- name = 'Launch Swift',
- type = 'codelldb',
- request = 'launch',
- program = function()
- return vim.fn.input('Executable: ', vim.fn.getcwd() .. '/.build/debug/', 'file')
- end,
- cwd = '${workspaceFolder}',
- stopOnEntry = false,
- },
-}
-
--- kotlin
-dap.adapters.kotlin = {
- type = 'executable',
- command = vim.fn.stdpath('data') .. '/mason/bin/kotlin-debug-adapter',
- args = {},
-}
-
-dap.configurations.kotlin = {
- {
- type = 'kotlin',
- request = 'launch',
- name = 'Launch Kotlin',
- mainClass = function()
- return vim.fn.input('Main class (e.g. com.example.MainKt): ')
- end,
- projectRoot = '${workspaceFolder}',
- },
- {
- type = 'kotlin',
- request = 'attach',
- name = 'Attach to Kotlin',
- port = 5005,
- projectRoot = '${workspaceFolder}',
- },
-}
-
--- keymaps
-local map = vim.keymap.set
-map('n', 'db', dap.toggle_breakpoint, { desc = 'Toggle breakpoint' })
-map('n', 'dB', function() dap.set_breakpoint(vim.fn.input('Condition: ')) end, { desc = 'Conditional breakpoint' })
-map('n', 'dc', dap.continue, { desc = 'Continue' })
-map('n', 'dn', dap.step_over, { desc = 'Step over' })
-map('n', 'di', dap.step_into, { desc = 'Step into' })
-map('n', 'do', dap.step_out, { desc = 'Step out' })
-map('n', 'dr', dap.repl.open, { desc = 'Open REPL' })
-map('n', 'dl', dap.run_last, { desc = 'Run last' })
-map('n', 'dt', dap.terminate, { desc = 'Terminate' })
-map('n', 'du', dapui.toggle, { desc = 'Toggle DAP UI' })
-map('n', 'dp', function() require('dap-python').test_method() end, { desc = 'Debug Python method' })
diff --git a/nvim/lua/harpoon-config.lua b/nvim/lua/harpoon-config.lua
deleted file mode 100644
index 662f80d..0000000
--- a/nvim/lua/harpoon-config.lua
+++ /dev/null
@@ -1,40 +0,0 @@
-local harpoon = require("harpoon")
-harpoon:setup({
- settings = {
- save_on_toggle = true,
- sync_on_ui_close = true,
- },
- -- UI customization
- menu = {
- width = vim.api.nvim_win_get_width(0) - 4,
- height = 10,
- borderchars = { "─", "│", "─", "│", "╭", "╮", "╯", "╰" }, -- Rounded corners
- -- Other border options:
- -- borderchars = { "━", "┃", "━", "┃", "┏", "┓", "┛", "┗" }, -- Thick borders
- -- borderchars = { "─", "│", "─", "│", "┌", "┐", "┘", "└" }, -- Simple corners
- -- borderchars = { " ", " ", " ", " ", " ", " ", " ", " " }, -- No border
- },
-})
-
--- Add file to harpoon
-vim.keymap.set("n", "a", function() harpoon:list():add() end, { desc = "Add file to Harpoon" })
-
--- Remove current file from harpoon
-vim.keymap.set("n", "ar", function() harpoon:list():remove() end, { desc = "Remove from Harpoon" })
-
--- Clear all harpoon marks
-vim.keymap.set("n", "ac", function() harpoon:list():clear() end, { desc = "Clear all Harpoon marks" })
-
--- Toggle harpoon menu
-vim.keymap.set("n", "h", function() harpoon.ui:toggle_quick_menu(harpoon:list()) end, { desc = "Toggle Harpoon menu" })
-
--- Navigate with Option (Alt) + number
-vim.keymap.set("n", "", function() harpoon:list():select(1) end, { desc = "Harpoon file 1" })
-vim.keymap.set("n", "", function() harpoon:list():select(2) end, { desc = "Harpoon file 2" })
-vim.keymap.set("n", "", function() harpoon:list():select(3) end, { desc = "Harpoon file 3" })
-vim.keymap.set("n", "", function() harpoon:list():select(4) end, { desc = "Harpoon file 4" })
-vim.keymap.set("n", "", function() harpoon:list():select(5) end, { desc = "Harpoon file 5" })
-
--- Navigate to next/previous harpoon file
-vim.keymap.set("n", "", function() harpoon:list():next() end, { desc = "Next Harpoon file" })
-vim.keymap.set("n", "", function() harpoon:list():prev() end, { desc = "Previous Harpoon file" })
diff --git a/nvim/lua/img/charVim.txt b/nvim/lua/img/charVim.txt
deleted file mode 100644
index 5430a4c..0000000
--- a/nvim/lua/img/charVim.txt
+++ /dev/null
@@ -1,18 +0,0 @@
-[ ]
-[ █████████████ █████████ ]
-[ ████████ █████ ]
-[ ████████ ████ ]
-[ ███████ ███ ]
-[ ███████ ███ ]
-[ ███████ ████ ]
-[ ███████ ███ ]
-[ ███████ ███ ]
-[ ███████ ████ ]
-[ ███████ ███ ██ ]
-[ ██████████ ██████ ]
-[ █████████ ████████ ]
-[ ███████ █████████ ]
-[ ██████ ██████████ ]
-[ █████ ████████ ]
-[ ███ ████ ]
-[ ]
diff --git a/nvim/lua/keymaps.lua b/nvim/lua/keymaps.lua
deleted file mode 100644
index 0ed2e7b..0000000
--- a/nvim/lua/keymaps.lua
+++ /dev/null
@@ -1,98 +0,0 @@
--- force Escape key to always work
-vim.keymap.set('i', '', '', { noremap = true, silent = true })
-vim.keymap.set('i', 'jk', '', { noremap = true, silent = true })
-
--- save and quit with leader keys
-vim.keymap.set('n', 'w', ':w', { noremap = true, silent = true, desc = 'Save file' })
-vim.keymap.set('n', 'q', ':wq', { noremap = true, silent = true, desc = 'Save and quit' })
-vim.keymap.set('n', 'qq', ':q!', { noremap = true, silent = true, desc = 'Quit without saving' })
-vim.keymap.set('n', 't', ':tabnew | Telescope file_browser path=%:p:h select_buffer=true hidden=true', { desc = 'New tab with file explorer' })
-
--- go to beginning and end of file
-vim.keymap.set('n', '[[', 'gg', { noremap = true, silent = true, desc = 'Go to beginning of file' })
-vim.keymap.set('n', ']]', 'G', { noremap = true, silent = true, desc = 'Go to end of file' })
-
--- go to beginning and end of line
-vim.keymap.set('n', 'H', '0', { noremap = true, silent = true, desc = 'Go to beginning of line' })
-vim.keymap.set('n', 'L', '$', { noremap = true, silent = true, desc = 'Go to end of line' })
-vim.keymap.set('v', 'H', '0', { noremap = true, silent = true, desc = 'Go to beginning of line' })
-vim.keymap.set('v', 'L', '$', { noremap = true, silent = true, desc = 'Go to end of line' })
-
--- paragraph navigation with Option + [ and ]
-vim.keymap.set('n', '', '{', { noremap = true, silent = true, desc = 'Previous paragraph' })
-vim.keymap.set('n', '', '}', { noremap = true, silent = true, desc = 'Next paragraph' })
-vim.keymap.set('v', '', '{', { noremap = true, silent = true, desc = 'Previous paragraph' })
-vim.keymap.set('v', '', '}', { noremap = true, silent = true, desc = 'Next paragraph' })
-
--- Move line up and down with option/alt + j/k
-vim.keymap.set('n', '', ':m .-2==', { desc = 'Move line up' })
-vim.keymap.set('n', '', ':m .+1==', { desc = 'Move line down' })
-vim.keymap.set('v', '', ":m '<-2gv=gv", { desc = 'Move selection up' })
-vim.keymap.set('v', '', ":m '>+1gv=gv", { desc = 'Move selection down' })
-vim.keymap.set('i', '', ':m .-2==gi', { desc = 'Move line up' })
-vim.keymap.set('i', '', ':m .+1==gi', { desc = 'Move line down' })
-
--- Copy line up and down with ctrl + shift + j/k
-vim.keymap.set('n', '', ':t .-1==', { desc = 'Copy line up' })
-vim.keymap.set('n', '', ':t .==', { desc = 'Copy line down' })
-vim.keymap.set('v', '', ":t '<-1gv=gv", { desc = 'Copy selection up' })
-vim.keymap.set('v', '', ":t '>gv=gv", { desc = 'Copy selection down' })
-
--- split navigation handled by vim-tmux-navigator
-vim.g.tmux_navigator_no_mappings = 1
-
--- terminal shortcut to run command without having to press :!
-vim.keymap.set('n', ';', ':!', { noremap = true })
-
--- runners
--- open current HTML file in browser
-local function open_in_browser()
- local file = vim.fn.expand('%:p')
- local ext = vim.fn.expand('%:e')
- if ext ~= 'html' and ext ~= 'htm' then
- vim.notify('Not an HTML file', vim.log.levels.WARN)
- return
- end
- local opener
- if vim.fn.has('mac') == 1 then
- opener = 'open'
- elseif vim.fn.has('win32') == 1 or vim.fn.has('win64') == 1 then
- opener = 'start'
- else
- opener = 'xdg-open'
- end
- local ok = vim.fn.jobstart({ opener, file }, { detach = true })
- if ok == -1 or ok == 0 then
- vim.notify('Failed to open file in browser', vim.log.levels.ERROR)
- end
-end
-
-vim.keymap.set('n', 'ob', open_in_browser, { desc = 'Open HTML in browser' })
-vim.api.nvim_create_user_command('OpenInBrowser', open_in_browser, { desc = 'Open current HTML file in browser' })
-
--- run current Python file in a terminal split
-local function run_python()
- if vim.fn.expand('%:e') ~= 'py' then
- vim.notify('Not a Python file', vim.log.levels.WARN)
- return
- end
- vim.cmd('split | terminal python3 ' .. vim.fn.shellescape(vim.fn.expand('%:p')))
-end
-
-vim.keymap.set('n', 'rp', run_python, { desc = 'Run Python file' })
-vim.api.nvim_create_user_command('RunPython', run_python, { desc = 'Run current Python file' })
-
--- run current Java file in a terminal split (compiles then runs)
-local function run_java()
- if vim.fn.expand('%:e') ~= 'java' then
- vim.notify('Not a Java file', vim.log.levels.WARN)
- return
- end
- local file = vim.fn.shellescape(vim.fn.expand('%:p'))
- local dir = vim.fn.shellescape(vim.fn.expand('%:p:h'))
- vim.cmd('split | terminal cd ' .. dir .. ' && javac ' .. file .. ' && java ' .. vim.fn.expand('%:t:r'))
-end
-
-vim.keymap.set('n', 'rj', run_java, { desc = 'Run Java file' })
-vim.api.nvim_create_user_command('RunJava', run_java, { desc = 'Run current Java file' })
-
diff --git a/nvim/lua/lint-config.lua b/nvim/lua/lint-config.lua
deleted file mode 100644
index 50f343a..0000000
--- a/nvim/lua/lint-config.lua
+++ /dev/null
@@ -1,19 +0,0 @@
-local lint = require("lint")
-
-lint.linters_by_ft = {
- lua = { "luacheck" },
- go = { "staticcheck" },
- python = { "ruff" },
- javascript = { "eslint_d" },
- typescript = { "eslint_d" },
- javascriptreact = { "eslint_d" },
- typescriptreact = { "eslint_d" },
- java = { "checkstyle" },
- kotlin = { "ktlint" },
-}
-
-vim.api.nvim_create_autocmd({ "BufWritePost", "BufEnter" }, {
- callback = function()
- pcall(lint.try_lint)
- end,
-})
diff --git a/nvim/lua/lsp-config.lua b/nvim/lua/lsp-config.lua
deleted file mode 100644
index 0f2583c..0000000
--- a/nvim/lua/lsp-config.lua
+++ /dev/null
@@ -1,410 +0,0 @@
-local function add_word_to_dictionary(word, client_name)
- if client_name == "ltex" then
- local params = {
- command = "ltex.addToDictionary",
- arguments = {
- {
- words = {
- ["en-US"] = { word }
- }
- }
- }
- }
- vim.lsp.buf.execute_command(params)
- end
-
- if client_name == "typos_lsp" then
- local config_path = vim.fn.expand("~/.config/typos.toml")
- vim.notify("Add custom typos config at " .. config_path, vim.log.levels.INFO)
- end
-end
-
-local on_attach = function(client, bufnr)
- local opts = { noremap = true, silent = true, buffer = bufnr }
-
- if client:supports_method('textDocument/inlayHint') then
- vim.lsp.inlay_hint.enable(true, { bufnr = bufnr })
- end
-
- vim.keymap.set('n', 'K', vim.lsp.buf.hover, opts)
- vim.keymap.set('n', 'gd', vim.lsp.buf.definition, opts)
- vim.keymap.set('n', 'gD', vim.lsp.buf.declaration, opts)
- vim.keymap.set('n', 'gi', vim.lsp.buf.implementation, opts)
- vim.keymap.set('n', 'go', vim.lsp.buf.type_definition, opts)
- vim.keymap.set('n', 'gr', vim.lsp.buf.references, opts)
- vim.keymap.set('n', 'gs', vim.lsp.buf.signature_help, opts)
- vim.keymap.set('n', 'lr', vim.lsp.buf.rename, opts)
- vim.keymap.set('n', 'ca', vim.lsp.buf.code_action, opts)
- vim.keymap.set('n', 'le', vim.diagnostic.open_float, opts)
- vim.keymap.set('n', '[d', vim.diagnostic.goto_prev, opts)
- vim.keymap.set('n', ']d', vim.diagnostic.goto_next, opts)
-
- vim.keymap.set('n', 'ih', function()
- vim.lsp.inlay_hint.enable(not vim.lsp.inlay_hint.is_enabled({ bufnr = bufnr }), { bufnr = bufnr })
- end, { buffer = bufnr, desc = 'Toggle inlay hints' })
-
- vim.keymap.set('n', 'ig', function()
- local word = vim.fn.expand('')
- add_word_to_dictionary(word, client.name)
- vim.notify("Added '" .. word .. "' to dictionary", vim.log.levels.INFO)
- end, vim.tbl_extend('force', opts, { desc = 'Ignore word (add to dictionary)' }))
-
- vim.keymap.set('n', 'di', function()
- local diag = vim.diagnostic.get(0, { lnum = vim.fn.line('.') - 1 })
- print(vim.inspect(diag))
- end, vim.tbl_extend('force', opts, { desc = 'Inspect diagnostics at cursor' }))
-end
-
-local function filter_diagnostics(diagnostics)
- return vim.tbl_filter(function(diagnostic)
- if diagnostic.message:match("sentence is %d+ words long") then return false end
- if diagnostic.message:match("paragraph is %d+ words long") then return false end
- if diagnostic.message:match("This sentence") then return false end
- return true
- end, diagnostics)
-end
-
-local original_set = vim.diagnostic.set
-vim.diagnostic.set = function(namespace, bufnr, diagnostics, opts)
- diagnostics = filter_diagnostics(diagnostics)
- original_set(namespace, bufnr, diagnostics, opts)
-end
-
-vim.diagnostic.config({
- virtual_text = { source = "if_many" },
- signs = true,
- underline = true,
- update_in_insert = true,
- severity_sort = true,
- float = {
- border = "rounded",
- source = "if_many",
- header = "",
- prefix = "",
- },
-})
-
-local capabilities = require("cmp_nvim_lsp").default_capabilities()
-
-local function mason_cmd(name)
- local path = vim.fn.stdpath('data') .. '/mason/bin/' .. name
- if vim.fn.executable(path) == 1 then
- return { path }
- end
- return { name }
-end
-
-vim.lsp.config('lua_ls', {
- cmd = mason_cmd('lua-language-server'),
- filetypes = { 'lua' },
- root_markers = { '.luarc.json', '.luarc.jsonc', '.luacheckrc', '.stylua.toml', 'selene.toml', 'selene.yml', '.git' },
- capabilities = capabilities,
- on_attach = on_attach,
- settings = {
- Lua = {
- runtime = { version = 'LuaJIT' },
- workspace = {
- checkThirdParty = false,
- library = vim.api.nvim_get_runtime_file("", true),
- },
- diagnostics = { globals = { 'vim' } },
- telemetry = { enable = false },
- },
- },
-})
-vim.lsp.enable('lua_ls')
-
-vim.lsp.config('gopls', {
- cmd = mason_cmd('gopls'),
- filetypes = { 'go', 'gomod' },
- root_markers = { 'go.mod', '.git' },
- capabilities = capabilities,
- on_attach = on_attach,
- settings = {
- gopls = {
- analyses = { unusedparams = true, shadow = true },
- staticcheck = true,
- gofumpt = true,
- hints = {
- assignVariableTypes = true,
- compositeLiteralFields = true,
- compositeLiteralTypes = true,
- constantValues = true,
- functionTypeParameters = true,
- parameterNames = true,
- rangeVariableTypes = true,
- },
- },
- },
-})
-vim.lsp.enable('gopls')
-
-vim.lsp.config('tinymist', {
- cmd = mason_cmd('tinymist'),
- filetypes = { 'typst' },
- root_markers = { '.git' },
- capabilities = capabilities,
- on_attach = on_attach,
- settings = {
- exportPdf = "onSave",
- outputPath = "$root/$dir/$name",
- },
-})
-vim.lsp.enable('tinymist')
-
-vim.lsp.config('clangd', {
- cmd = mason_cmd('clangd'),
- filetypes = { 'c', 'cpp', 'objc', 'objcpp' },
- root_markers = { '.clangd', 'compile_commands.json', '.git' },
- capabilities = capabilities,
- on_attach = on_attach,
-})
-vim.lsp.enable('clangd')
-
-vim.lsp.config('sourcekit-lsp', {
- cmd = { 'sourcekit-lsp' },
- filetypes = { 'swift', 'objective-c', 'objective-cpp' },
- root_markers = { 'Package.swift', 'compile_commands.json', '.git' },
- capabilities = capabilities,
- on_attach = on_attach,
-})
-
-vim.api.nvim_create_autocmd('FileType', {
- pattern = { 'swift', 'objective-c', 'objective-cpp' },
- callback = function()
- vim.lsp.enable('sourcekit-lsp')
- end,
-})
-
-local function jdtls_workspace(root_dir)
- local workspace = vim.fn.stdpath('cache') .. '/jdtls'
- if root_dir then
- workspace = workspace .. '/' .. vim.fn.fnamemodify(root_dir, ':t')
- else
- workspace = workspace .. '/default'
- end
- return workspace
-end
-
-vim.lsp.config('jdtls', {
- cmd = function(dispatchers, config)
- local path = vim.fn.stdpath('data') .. '/mason/bin/jdtls'
- local exe = vim.fn.executable(path) == 1 and path or 'jdtls'
- local data_dir = jdtls_workspace(config.root_dir)
- return vim.lsp.rpc.start({ exe, '-data', data_dir }, dispatchers, {
- cwd = config.cmd_cwd,
- env = config.cmd_env,
- detached = config.detached,
- })
- end,
- filetypes = { 'java' },
- root_markers = { 'build.xml', 'pom.xml', 'build.gradle', 'build.gradle.kts', 'settings.gradle', 'settings.gradle.kts', '.git' },
- capabilities = capabilities,
- on_attach = on_attach,
- settings = {
- java = {
- eclipse = { downloadSources = true },
- configuration = { updateBuildConfiguration = "interactive" },
- maven = { downloadSources = true, updateSnapshots = false },
- project = {
- referencedLibraries = {
- "lib/**/*.jar", "**/lib/*.jar",
- vim.fn.expand("~/.m2/repository/**/*.jar"),
- vim.fn.expand("~/.gradle/caches/**/*.jar"),
- "target/**/*.jar", "build/libs/**/*.jar", "dist/**/*.jar",
- },
- },
- inlayHints = { parameterNames = { enabled = "all" } },
- implementationsCodeLens = { enabled = true },
- referencesCodeLens = { enabled = true },
- sources = { organizeImports = { starThreshold = 9999, staticStarThreshold = 9999 } },
- codeGeneration = {
- toString = { template = "${object.className}{${member.name()}=${member.value}, ${otherMembers}}" },
- hashCodeEquals = { useJava7Objects = true },
- useBlocks = true,
- },
- completion = {
- favoriteStaticMembers = {
- "org.junit.Assert.*", "org.junit.Assume.*",
- "org.junit.jupiter.api.Assertions.*", "org.junit.jupiter.api.Assumptions.*",
- "org.junit.jupiter.api.DynamicContainer.*", "org.junit.jupiter.api.DynamicTest.*",
- "org.mockito.Mockito.*", "org.mockito.ArgumentMatchers.*", "org.mockito.Answers.*",
- },
- filteredTypes = { "com.sun.*", "io.micrometer.shaded.*", "java.awt.*", "jdk.*", "sun.*" },
- importOrder = { "java", "javax", "com", "org" },
- },
- format = {
- enabled = true,
- settings = {
- profile = "GoogleStyle",
- },
- },
- },
- },
-})
-vim.lsp.enable('jdtls')
-
-vim.lsp.config('kotlin_language_server', {
- cmd = mason_cmd('kotlin-language-server'),
- filetypes = { 'kotlin' },
- root_markers = { 'settings.gradle', 'settings.gradle.kts', 'build.gradle', 'build.gradle.kts', 'pom.xml', '.git' },
- capabilities = capabilities,
- on_attach = on_attach,
- settings = {
- kotlin = {
- compiler = { jvm = { target = "17" } },
- inlayHints = {
- typeHints = { enable = true },
- parameterHints = { enable = true },
- },
- },
- },
-})
-vim.lsp.enable('kotlin_language_server')
-
-vim.api.nvim_create_autocmd("FileType", {
- pattern = "kotlin",
- callback = function(args)
- local bufnr = args.buf
- local opts = { buffer = bufnr, noremap = true, silent = true }
-
- local function run_build_cmd(cmd)
- if cmd then vim.cmd('split | terminal ' .. cmd) end
- end
-
- local function get_kotlin_cmd(gradle_cmd, maven_cmd)
- local root = vim.fs.root(0, {
- 'settings.gradle', 'settings.gradle.kts', 'build.gradle', 'build.gradle.kts', 'pom.xml',
- })
- if not root then
- vim.notify("No Kotlin project root detected", vim.log.levels.WARN)
- return nil
- end
- if vim.fn.filereadable(root .. '/pom.xml') == 1 then
- return 'cd ' .. root .. ' && ' .. maven_cmd
- end
- return 'cd ' .. root .. ' && ' .. gradle_cmd
- end
-
- vim.keymap.set('n', 'kb', function()
- run_build_cmd(get_kotlin_cmd('./gradlew build', 'mvn compile'))
- end, vim.tbl_extend('force', opts, { desc = 'Build Kotlin project' }))
-
- vim.keymap.set('n', 'kt', function()
- run_build_cmd(get_kotlin_cmd('./gradlew test', 'mvn test'))
- end, vim.tbl_extend('force', opts, { desc = 'Run Kotlin tests' }))
-
- vim.keymap.set('n', 'kc', function()
- run_build_cmd(get_kotlin_cmd('./gradlew clean', 'mvn clean'))
- end, vim.tbl_extend('force', opts, { desc = 'Clean Kotlin project' }))
-
- vim.keymap.set('n', 'kr', function()
- run_build_cmd(get_kotlin_cmd('./gradlew run', 'mvn exec:java'))
- end, vim.tbl_extend('force', opts, { desc = 'Run Kotlin project' }))
- end,
-})
-
-vim.lsp.config('pyright', {
- cmd = mason_cmd('pyright'),
- filetypes = { 'python' },
- root_markers = { 'pyproject.toml', 'setup.py', 'setup.cfg', 'requirements.txt', '.git' },
- capabilities = capabilities,
- on_attach = on_attach,
-})
-vim.lsp.enable('pyright')
-
-vim.lsp.config('ts_ls', {
- cmd = mason_cmd('typescript-language-server'),
- filetypes = { 'javascript', 'typescript', 'javascriptreact', 'typescriptreact', 'vue' },
- root_markers = { 'package.json', 'tsconfig.json', 'jsconfig.json', '.git' },
- capabilities = capabilities,
- on_attach = on_attach,
-})
-vim.lsp.enable('ts_ls')
-
-vim.lsp.config('lemminx', {
- cmd = mason_cmd('lemminx'),
- filetypes = { 'xml' },
- root_markers = { '.git' },
- capabilities = capabilities,
- on_attach = on_attach,
-})
-vim.lsp.enable('lemminx')
-
-vim.api.nvim_create_autocmd("FileType", {
- pattern = "java",
- callback = function()
- local root = vim.fs.root(0, {
- 'build.xml', 'pom.xml', 'build.gradle', 'build.gradle.kts',
- 'settings.gradle', 'settings.gradle.kts',
- })
- if not root then return end
-
- if vim.fn.filereadable(root .. '/pom.xml') == 1 then
- vim.b.java_build_system = 'Maven'
- vim.b.java_build_file = 'pom.xml'
- elseif vim.fn.filereadable(root .. '/build.gradle') == 1 or
- vim.fn.filereadable(root .. '/build.gradle.kts') == 1 then
- vim.b.java_build_system = 'Gradle'
- vim.b.java_build_file = vim.fn.filereadable(root .. '/build.gradle') == 1
- and 'build.gradle' or 'build.gradle.kts'
- elseif vim.fn.filereadable(root .. '/build.xml') == 1 then
- vim.b.java_build_system = 'Ant'
- vim.b.java_build_file = 'build.xml'
- end
- vim.b.java_project_root = root
- end
-})
-
-vim.api.nvim_create_autocmd("FileType", {
- pattern = "java",
- callback = function(args)
- local bufnr = args.buf
- local opts = { buffer = bufnr, noremap = true, silent = true }
-
- local function run_build_cmd(cmd)
- if cmd then vim.cmd('split | terminal ' .. cmd) end
- end
-
- local function get_cmd(maven_cmd, gradle_cmd, ant_cmd)
- local bs = vim.b.java_build_system
- local root = vim.b.java_project_root
- if not bs or not root then
- vim.notify("No build system detected", vim.log.levels.WARN)
- return nil
- end
- if bs == 'Maven' then return 'cd ' .. root .. ' && ' .. maven_cmd end
- if bs == 'Gradle' then return 'cd ' .. root .. ' && ' .. gradle_cmd end
- if bs == 'Ant' then return 'cd ' .. root .. ' && ' .. ant_cmd end
- end
-
- vim.keymap.set('n', 'jb', function()
- run_build_cmd(get_cmd('mvn compile', './gradlew build', 'ant compile'))
- end, vim.tbl_extend('force', opts, { desc = 'Build Java project' }))
-
- vim.keymap.set('n', 'jt', function()
- run_build_cmd(get_cmd('mvn test', './gradlew test', 'ant test'))
- end, vim.tbl_extend('force', opts, { desc = 'Run Java tests' }))
-
- vim.keymap.set('n', 'jc', function()
- run_build_cmd(get_cmd('mvn clean', './gradlew clean', 'ant clean'))
- end, vim.tbl_extend('force', opts, { desc = 'Clean Java project' }))
-
- vim.keymap.set('n', 'jw', function()
- local root = vim.b.java_project_root
- if not root then
- vim.notify("No Java project detected", vim.log.levels.WARN)
- return
- end
- local workspace_dir = jdtls_workspace(root)
- vim.lsp.stop_client(vim.lsp.get_clients({ name = 'jdtls' }))
- vim.fn.system('rm -rf ' .. workspace_dir)
- vim.notify("Cleaned JDTLS workspace for " .. vim.fn.fnamemodify(root, ':t'), vim.log.levels.INFO)
- vim.notify("Restart Neovim or run :LspStart to reinitialize", vim.log.levels.INFO)
- end, vim.tbl_extend('force', opts, { desc = 'Clean JDTLS workspace cache' }))
- end
-})
-
-return {
- on_attach = on_attach,
-}
diff --git a/nvim/lua/multicursor.lua b/nvim/lua/multicursor.lua
deleted file mode 100644
index 1afb772..0000000
--- a/nvim/lua/multicursor.lua
+++ /dev/null
@@ -1,368 +0,0 @@
-local M = {}
-
-local ns = vim.api.nvim_create_namespace('multicursor')
-local on_key_ns = vim.api.nvim_create_namespace('multicursor_on_key')
-
-local cursors = nil
-local last_real_pos = nil
-local active_idx = nil
-local in_tracking = false
-local in_broadcast = false
-local insert_start = nil
-local skip_cursor_move = false
-local multi_mode = false
-
-local attached_buffers = {}
-
-local function gc()
- return vim.api.nvim_win_get_cursor(0)
-end
-
-local function sc(pos)
- vim.api.nvim_win_set_cursor(0, pos)
-end
-
-local function lc(lnum)
- local lines = vim.api.nvim_buf_get_lines(0, lnum - 1, lnum, false)
- return lines[1] or ''
-end
-
-local function blc()
- return vim.api.nvim_buf_line_count(0)
-end
-
-local function cc(lnum, col)
- if col < 0 then return 0 end
- local line = lc(lnum)
- return math.min(col, #line)
-end
-
-local function sort_cursors()
- table.sort(cursors, function(a, b)
- if a[1] == b[1] then return a[2] < b[2] end
- return a[1] < b[1]
- end)
-end
-
-local function render()
- vim.api.nvim_buf_clear_namespace(0, ns, 0, -1)
- if not cursors then return end
- for _, c in ipairs(cursors) do
- pcall(vim.api.nvim_buf_set_extmark, 0, ns, c[1] - 1, cc(c[1], c[2]), {
- sign_text = '▎',
- sign_hl_group = 'Cursor',
- priority = 250,
- })
- end
-end
-
-local function refresh_active_idx()
- if not cursors then return end
- local pos = gc()
- for i, c in ipairs(cursors) do
- if c[1] == pos[1] and c[2] == pos[2] then
- active_idx = i
- return
- end
- end
- active_idx = nil
-end
-
-function M.is_active()
- return cursors ~= nil
-end
-
-function M.enter()
- if cursors then return end
- local pos = gc()
- cursors = {{pos[1], pos[2]}}
- last_real_pos = {pos[1], pos[2]}
- active_idx = 1
- multi_mode = true
- render()
-end
-
-function M.add_above()
- local pos = gc()
- if not cursors then
- cursors = {{pos[1], pos[2]}}
- last_real_pos = {pos[1], pos[2]}
- active_idx = 1
- multi_mode = true
- end
- if pos[1] > 1 then
- local ok = true
- for _, c in ipairs(cursors) do
- if c[1] == pos[1] - 1 and c[2] == pos[2] then ok = false; break end
- end
- if ok then
- table.insert(cursors, {pos[1] - 1, pos[2]})
- sort_cursors()
- refresh_active_idx()
- render()
- end
- end
-end
-
-function M.add_below()
- local pos = gc()
- if not cursors then
- cursors = {{pos[1], pos[2]}}
- last_real_pos = {pos[1], pos[2]}
- active_idx = 1
- multi_mode = true
- end
- if pos[1] < blc() then
- local ok = true
- for _, c in ipairs(cursors) do
- if c[1] == pos[1] + 1 and c[2] == pos[2] then ok = false; break end
- end
- if ok then
- table.insert(cursors, {pos[1] + 1, pos[2]})
- sort_cursors()
- refresh_active_idx()
- render()
- end
- end
-end
-
-function M.exit()
- cursors = nil
- last_real_pos = nil
- active_idx = nil
- insert_start = nil
- multi_mode = false
- vim.api.nvim_buf_clear_namespace(0, ns, 0, -1)
-end
-
-local function do_normal_jk(key, count)
- vim.cmd('normal! ' .. (count > 0 and count or 1) .. key)
-end
-
-local function j_handler()
- if not multi_mode then
- do_normal_jk('j', vim.v.count)
- return
- end
- local count = vim.v.count > 0 and vim.v.count or 1
- for _ = 1, count do
- local pos = gc()
- if pos[1] >= blc() then break end
- M.add_below()
- last_real_pos = {pos[1] + 1, pos[2]}
- sc({pos[1] + 1, pos[2]})
- end
- refresh_active_idx()
- render()
-end
-
-local function k_handler()
- if not multi_mode then
- do_normal_jk('k', vim.v.count)
- return
- end
- local count = vim.v.count > 0 and vim.v.count or 1
- for _ = 1, count do
- local pos = gc()
- if pos[1] <= 1 then break end
- M.add_above()
- last_real_pos = {pos[1] - 1, pos[2]}
- sc({pos[1] - 1, pos[2]})
- end
- refresh_active_idx()
- render()
-end
-
-local function move_all_cursors(dlnum, dcol)
- if not cursors or in_tracking then return end
- in_tracking = true
- for _, c in ipairs(cursors) do
- c[1] = math.max(1, math.min(blc(), c[1] + dlnum))
- c[2] = cc(c[1], c[2] + dcol)
- end
- in_tracking = false
- render()
-end
-
-local function broadcast_text(inserted, start_lnum, start_col)
- if not cursors or #inserted == 0 then return end
- local buf = vim.api.nvim_get_current_buf()
- local work = {}
- for i, c in ipairs(cursors) do
- work[#work + 1] = {i, c[1], c[2]}
- end
- table.sort(work, function(a, b)
- if a[2] == b[2] then return a[3] > b[3] end
- return a[2] > b[2]
- end)
- for _, item in ipairs(work) do
- local idx, lnum, col = item[1], item[2], item[3]
- if lnum ~= start_lnum or col ~= start_col then
- local line = lc(lnum)
- local new_line = line:sub(1, col) .. inserted .. line:sub(col + 1)
- vim.api.nvim_buf_set_lines(buf, lnum - 1, lnum, false, {new_line})
- cursors[idx][2] = col + #inserted
- else
- cursors[idx][2] = cursors[idx][2] + #inserted
- end
- end
- render()
-end
-
-local function insert_handler()
- if not cursors then return end
- local pos = gc()
- insert_start = {lnum = pos[1], col = pos[2], line = lc(pos[1])}
- last_real_pos = nil
-end
-
-local function insert_leave_handler()
- local state = insert_start
- insert_start = nil
- if not cursors or not state then return end
- local end_pos = gc()
- local final_lnum, final_col = end_pos[1], end_pos[2]
- if final_lnum == state.lnum then
- local new_line = lc(final_lnum)
- local inserted = new_line:sub(state.col + 1, final_col)
- broadcast_text(inserted, state.lnum, state.col)
- last_real_pos = {gc()[1], gc()[2]}
- return
- end
- last_real_pos = {gc()[1], gc()[2]}
-end
-
-local function cursor_handler()
- if not cursors or in_tracking then return end
- local new_pos = gc()
-
- if skip_cursor_move then
- skip_cursor_move = false
- last_real_pos = {new_pos[1], new_pos[2]}
- if active_idx and cursors[active_idx] then
- cursors[active_idx][1] = new_pos[1]
- cursors[active_idx][2] = new_pos[2]
- end
- render()
- return
- end
-
- if last_real_pos then
- local dlnum = new_pos[1] - last_real_pos[1]
- local dcol = new_pos[2] - last_real_pos[2]
- if dlnum ~= 0 or dcol ~= 0 then
- move_all_cursors(dlnum, dcol)
- end
- end
- last_real_pos = {new_pos[1], new_pos[2]}
- refresh_active_idx()
-end
-
-local function text_changed_handler()
- if not cursors or in_broadcast then return end
- in_broadcast = true
- local work = {}
- for _, c in ipairs(cursors) do
- work[#work + 1] = {c[1], c[2]}
- end
- table.sort(work, function(a, b)
- if a[1] == b[1] then return a[2] > b[2] end
- return a[1] > b[1]
- end)
- local feed_dot = vim.api.nvim_replace_termcodes('.', true, false, true)
- local origin_idx = active_idx
- if not origin_idx then
- local current = gc()
- for i, c in ipairs(cursors) do
- if c[1] == current[1] and c[2] == current[2] then
- origin_idx = i
- break
- end
- end
- end
- for _, pos in ipairs(work) do
- if not (origin_idx and cursors[origin_idx]
- and cursors[origin_idx][1] == pos[1]
- and cursors[origin_idx][2] == pos[2]) then
- sc(pos)
- vim.api.nvim_feedkeys(feed_dot, 'nx', false)
- local np = gc()
- for _, c in ipairs(cursors) do
- if c[1] == pos[1] and c[2] == pos[2] then
- c[1] = np[1]
- c[2] = np[2]
- break
- end
- end
- end
- end
- sort_cursors()
- render()
- in_broadcast = false
-end
-
-local function on_buf_lines(_, _, firstline, lastline, new_lastline)
- if not cursors then return end
- if new_lastline > lastline then
- local shift = new_lastline - lastline
- for _, c in ipairs(cursors) do
- if c[1] > firstline then
- c[1] = c[1] + shift
- end
- end
- elseif new_lastline < lastline then
- local deleted = lastline - new_lastline
- for _, c in ipairs(cursors) do
- if c[1] > lastline then
- c[1] = c[1] - deleted
- elseif c[1] > firstline then
- c[1] = firstline + 1
- end
- end
- end
- skip_cursor_move = true
- render()
-end
-
-local function ensure_attached()
- local buf = vim.api.nvim_get_current_buf()
- if attached_buffers[buf] then return end
- attached_buffers[buf] = true
- vim.api.nvim_buf_attach(buf, false, { on_lines = on_buf_lines })
-end
-
-local function setup_autocmds()
- local group = vim.api.nvim_create_augroup('multicursor', { clear = true })
- vim.api.nvim_create_autocmd('CursorMoved', { group = group, callback = cursor_handler })
- vim.api.nvim_create_autocmd('InsertEnter', { group = group, callback = insert_handler })
- vim.api.nvim_create_autocmd('InsertLeave', { group = group, callback = insert_leave_handler })
- vim.api.nvim_create_autocmd('TextChanged', { group = group, callback = text_changed_handler })
- vim.api.nvim_create_autocmd('BufEnter', { group = group, callback = ensure_attached })
-end
-
-local function setup_keymaps()
- vim.keymap.set({'n', 'v'}, '', M.add_above, { desc = 'Add cursor above' })
- vim.keymap.set({'n', 'v'}, '', M.add_below, { desc = 'Add cursor below' })
- vim.keymap.set('n', '', M.enter, { desc = 'Enter multi-cursor mode' })
- vim.keymap.set('n', 'j', j_handler, { desc = 'j or add cursor below (multi-cursor)' })
- vim.keymap.set('n', 'k', k_handler, { desc = 'k or add cursor above (multi-cursor)' })
- vim.keymap.set('n', 'gj', j_handler, { desc = 'gj or add cursor below (multi-cursor)' })
- vim.keymap.set('n', 'gk', k_handler, { desc = 'gk or add cursor above (multi-cursor)' })
-end
-
-vim.on_key(function(key)
- if cursors and key == '\27' and vim.fn.mode() == 'n' then
- vim.wait(10, function() return vim.fn.getchar(0) ~= 0 end)
- if vim.fn.getchar(0) == 0 then
- M.exit()
- end
- end
-end, on_key_ns)
-
-function M.setup()
- setup_autocmds()
- setup_keymaps()
- ensure_attached()
-end
-
-return M
diff --git a/nvim/lua/nvim-tmux-navigator.lua b/nvim/lua/nvim-tmux-navigator.lua
deleted file mode 100644
index db886ce..0000000
--- a/nvim/lua/nvim-tmux-navigator.lua
+++ /dev/null
@@ -1,4 +0,0 @@
-return {
- "christoomey/vim-tmux-navigator",
- lazy = false
-}
diff --git a/nvim/lua/options.lua b/nvim/lua/options.lua
deleted file mode 100644
index 26f2f65..0000000
--- a/nvim/lua/options.lua
+++ /dev/null
@@ -1,53 +0,0 @@
-vim.wo.number = true
-vim.wo.relativenumber = true
-
-vim.opt.timeoutlen = 500
-
-vim.opt.list = true
-vim.opt.listchars = { tab = '▸ ', trail = '·', extends = '»', precedes = '«', nbsp = '␣', leadmultispace = '│ ', lead = '│'}
-
-vim.api.nvim_create_user_command('E', 'Telescope file_browser path=%:p:h select_buffer=true hidden=true', {})
-vim.api.nvim_create_user_command('Tree', 'Telescope file_browser path=%:p:h select_buffer=true hidden=true', {})
-
-vim.keymap.set('n', 'e', ':Telescope file_browser path=%:p:h select_buffer=true hidden=true')
-vim.keymap.set('n', 'E', function()
- local root = vim.fs.root(0, { '.git', 'package.json', 'Makefile', 'pom.xml', 'build.gradle', 'Cargo.toml' })
- local path = root or vim.fn.getcwd()
- require('telescope').extensions.file_browser.file_browser({ path = path, hidden = true })
-end)
-
-vim.keymap.set('n', 'g', ':Telescope live_grep', { noremap = true, silent = true, desc = 'Live grep' })
-
-vim.scriptencoding = "utf-8"
-vim.opt.encoding = "utf-8"
-vim.opt.fileencoding = "utf-8"
-
-vim.opt.backup = false
-
-vim.opt.scrolloff = 10 -- was vim.scrolloff (global Lua var, had no effect)
-
-vim.opt.expandtab = true
-vim.opt.smarttab = true
-vim.opt.smartindent = true
-vim.opt.shiftwidth = 4
-vim.opt.tabstop = 4
-vim.opt.softtabstop = 4
-
-vim.opt.swapfile = false
-
-vim.opt.clipboard = "unnamedplus"
-
-vim.opt.cursorline = true
-vim.opt.cursorlineopt = 'number'
-
-vim.opt.guicursor = "n-v-c:block,i-ci-ve:ver25,r-cr:hor20,o:hor50"
-
-vim.opt.cmdheight = 0
-vim.opt.showcmd = false
-
--- search incramental
-vim.opt.inccommand = "split"
-
--- undo dirfor nvim
-vim.opt.undodir = vim.fn.stdpath("data") .. "/undodir"
-vim.opt.undofile = true
diff --git a/nvim/lua/plugins.lua b/nvim/lua/plugins.lua
deleted file mode 100644
index 8d19b59..0000000
--- a/nvim/lua/plugins.lua
+++ /dev/null
@@ -1,368 +0,0 @@
-return
-{
- -- Nvim Surround
- {
- "kylechui/nvim-surround",
- version = "*",
- event = "VeryLazy",
- config = function()
- require("nvim-surround").setup({})
- end
- },
-
- -- Multi-cursor support (replaced by homebrew multicursor.lua)
- -- {
- -- "mg979/vim-visual-multi",
- -- branch = "master",
- -- lazy = false,
- -- },
-
- -- Vim-Tmux-Navigator
- {
- "christoomey/vim-tmux-navigator",
- cmd = {
- "TmuxNavigateLeft",
- "TmuxNavigateDown",
- "TmuxNavigateUp",
- "TmuxNavigateRight",
- "TmuxNavigatePrevious",
-
- },
- keys = {
- { "", "TmuxNavigateLeft" },
- { "", "TmuxNavigateDown" },
- { "", "TmuxNavigateUp" },
- { "", "TmuxNavigateRight" },
- { "", "TmuxNavigatePrevious" },
- },
- },
-
- -- Harpoon2
- {
- 'ThePrimeagen/harpoon',
- branch = 'harpoon2',
- dependencies = { 'nvim-lua/plenary.nvim' },
- config = function()
- require('harpoon-config')
- end
- },
-
- -- Rosé Pine
- {
- 'rose-pine/neovim',
- name = 'rose-pine',
- priority = 1000,
- config = function()
- require('rose-pine').setup({
- variant = 'main',
- disable_background = true,
- disable_float_background = true,
- disable_italics = false,
- highlight_groups = {
- CursorLineNr = { fg = 'gold', bold = true }
- }
- })
- end
- },
-
- -- Tokyo Night
- {
- 'folke/tokyonight.nvim',
- lazy = false,
- priority = 1000,
- opts = {
- style = 'night',
- transparent = true,
- },
- },
-
- -- Catppuccin
- {
- 'catppuccin/nvim',
- name = 'catppuccin',
- lazy = false,
- priority = 1000,
- opts = {
- flavour = 'mocha',
- transparent_background = true,
- },
- },
-
- -- Gruvbox
- {
- 'ellisonleao/gruvbox.nvim',
- lazy = false,
- priority = 1000,
- opts = {
- transparent_mode = true,
- },
- },
-
- -- Treesitter
- {
- 'nvim-treesitter/nvim-treesitter',
- branch = 'main',
- build = ':TSUpdate',
- dependencies = {
- {
- 'windwp/nvim-ts-autotag',
- config = function()
- require('nvim-ts-autotag').setup({
- opts = {
- enable_close = true,
- enable_rename = true,
- enable_close_on_slash = true,
- },
- })
- end,
- },
- },
- },
-
- -- Mason (LSP servers, linters, DAP installers)
- {
- 'williamboman/mason.nvim',
- config = function()
- require('mason').setup({
- ui = {
- icons = {
- package_installed = "✓",
- package_pending = "➜",
- package_uninstalled = "✗"
- }
- }
- })
- end,
- },
-
- -- Project management
- {
- "ahmedkhalf/project.nvim",
- config = function()
- require("project_nvim").setup({
- detection_methods = { "pattern", "lsp" },
- patterns = { ".git", "Makefile", "package.json", "pom.xml", "build.gradle", "Cargo.toml" },
- show_hidden = false,
- silent_chdir = true,
- })
- end,
- },
-
- -- Telescope
- {
- 'nvim-telescope/telescope.nvim',
- dependencies = {
- 'nvim-lua/plenary.nvim',
- 'nvim-telescope/telescope-file-browser.nvim',
- 'ahmedkhalf/project.nvim',
- },
- config = function()
- local telescope = require('telescope')
- local actions = require('telescope.actions')
- local action_state = require('telescope.actions.state')
-
- telescope.setup({
- defaults = {
- prompt_prefix = ' 🔎 ',
- selection_caret = '▸',
- file_ignore_patterns = { "%.git/", "%DS_Store$" },
- mappings = {
- i = {
- [""] = function(prompt_bufnr)
- local picker = action_state.get_current_picker(prompt_bufnr)
- local finder = picker.finder
- finder.hidden = not finder.hidden
- picker:refresh(require('telescope.finders').new_oneshot_job(
- vim.tbl_flatten({
- "fd", "--type", "f",
- finder.hidden and "--hidden" or "--no-hidden",
- "--color", "never"
- }),
- picker.finder.entry_maker
- ))
- end,
- },
- },
- },
- pickers = {
- find_files = { hidden = true },
- },
- extensions = {
- file_browser = { hidden = true, hijack_netrw = true },
- projects = {},
- },
- })
-
- pcall(telescope.load_extension, 'file_browser')
- pcall(telescope.load_extension, 'projects')
- end,
- },
-
- -- Which-key
- {
- "folke/which-key.nvim",
- event = "VeryLazy",
- opts = { preset = "modern" },
- config = function()
- local wk = require("which-key")
- wk.setup({ preset = "modern" })
- wk.add({
- { "r", group = "Run" },
- { "w", desc = "Save file" },
- { "q", desc = "Save and quit" },
- { "Q", desc = "Quit without saving" },
- { "e", desc = "File explorer" },
- { "v", desc = "File explorer" },
- { "p", desc = "Projects" },
- { "g", desc = "Live grep" },
- })
- end,
- },
-
- -- DAP
- {
- 'mfussenegger/nvim-dap',
- dependencies = {
- 'rcarriga/nvim-dap-ui',
- 'nvim-neotest/nvim-nio',
- 'jay-babu/mason-nvim-dap.nvim',
- 'mfussenegger/nvim-dap-python',
- },
- config = function()
- require('dap-config')
- end,
- },
-
- -- Mason DAP
- {
- 'jay-babu/mason-nvim-dap.nvim',
- dependencies = { 'williamboman/mason.nvim', 'mfussenegger/nvim-dap' },
- config = function()
- require('mason-nvim-dap').setup({
- ensure_installed = { 'python', 'javadbg', 'codelldb', 'kotlin' },
- automatic_installation = true,
- })
- end,
- },
-
- -- noice.nvim
- {
- "folke/noice.nvim",
- event = "VeryLazy",
- dependencies = { "MunifTanjim/nui.nvim" },
- opts = {
- cmdline = {
- enabled = true,
- view = "cmdline_popup",
- format = {
- cmdline = { icon = ":" },
- search_down = { icon = "🔍 ⌄" },
- search_up = { icon = "🔍 ⌃" },
- filter = { icon = "$" },
- lua = { icon = "☾" },
- help = { icon = "?" },
- },
- },
- messages = { enabled = false },
- popupmenu = { enabled = false },
- lsp = {
- override = {
- ["vim.lsp.util.convert_input_to_markdown_lines"] = true,
- ["vim.lsp.util.stylize_markdown"] = true,
- },
- },
- presets = {
- bottom_search = true,
- command_palette = true,
- long_message_to_split = true,
- },
- },
- },
-
- -- avante.nvim (AI code chat and editing with Anthropic Claude)
- {
- "yetone/avante.nvim",
- event = "VeryLazy",
- build = "make",
- dependencies = {
- "nvim-lua/plenary.nvim",
- "stevearc/dressing.nvim",
- "nvim-telescope/telescope.nvim",
- "nvim-tree/nvim-web-devicons",
- "MunifTanjim/nui.nvim",
- {
- "HakonHarnes/img-clip.nvim",
- event = "VeryLazy",
- opts = {
- default = {
- embed_image_as_base64 = false,
- },
- },
- },
- },
- config = function()
- vim.g.plenary_curl_bin_path = "/usr/bin/curl"
- require("avante").setup({
- provider = "claude",
- providers = {
- claude = {
- endpoint = "https://api.anthropic.com",
- model = "claude-haiku-4-5",
- timeout = 30000,
- extra_request_body = {
- max_tokens = 4096,
- },
- },
- },
- mappings = {
- suggestion = {
- accept = "",
- next = "",
- dismiss = "",
- },
- },
- behaviour = {
- auto_suggestions = false,
- },
- })
- end,
- },
-
- -- alpha-nvim dashboard
- {
- "goolord/alpha-nvim",
- config = function()
- require('alpha-config')
- end
- },
-
- -- Typst Preview
- {
- 'chomosuke/typst-preview.nvim',
- ft = 'typst',
- version = '1.*',
- build = function() require('typst-preview').update() end,
- opts = {},
- config = function()
- vim.api.nvim_create_user_command('TP', 'TypstPreview', { desc = 'Start Typst preview' })
- vim.api.nvim_create_user_command('TS', 'TypstPreviewStop', { desc = 'Stop Typst preview' })
- vim.api.nvim_create_user_command('TU', 'TypstPreviewUpdate', { desc = 'Update Typst preview' })
- end
- },
-
- -- Linting (nvim-lint)
- {
- "mfussenegger/nvim-lint",
- config = function()
- require('lint-config')
- end,
- },
-
- -- nvim-cmp core + sources
- "hrsh7th/nvim-cmp",
- "hrsh7th/cmp-nvim-lsp",
- "hrsh7th/cmp-buffer",
-
- -- Snippet engine (kept for DAP/LSP snippet expansion)
- 'L3MON4D3/LuaSnip',
-}
diff --git a/nvim/lua/rename-config.lua b/nvim/lua/rename-config.lua
deleted file mode 100644
index bafa69d..0000000
--- a/nvim/lua/rename-config.lua
+++ /dev/null
@@ -1,405 +0,0 @@
-local M = {}
-
-local function get_visual_selection()
- local ls, cs = vim.fn.line("'<"), vim.fn.col("'<")
- local le, ce = vim.fn.line("'>"), vim.fn.col("'>")
- if ls ~= le then return nil end
- local line = vim.api.nvim_buf_get_lines(0, ls - 1, ls, false)[1] or ''
- return line:sub(cs, ce)
-end
-
-local function is_identifier(s)
- return s ~= '' and s:match('^[%a_][%w_]*$') ~= nil
-end
-
-function M.open(from_visual)
- local orig_win = vim.api.nvim_get_current_win()
- local orig_bufnr = vim.api.nvim_get_current_buf()
- local orig_pos = vim.api.nvim_win_get_cursor(0)
- local init_word = from_visual and get_visual_selection() or nil
- init_word = (init_word and init_word ~= '') and init_word or vim.fn.expand('')
-
- -- Detect if LSP rename is available for this symbol
- local lsp_rename = false
- if is_identifier(init_word) then
- for _, c in ipairs(vim.lsp.get_clients({ bufnr = orig_bufnr })) do
- if c.server_capabilities.renameProvider then
- lsp_rename = true
- break
- end
- end
- end
-
- local Layout = require('nui.layout')
- local Popup = require('nui.popup')
-
- local results = {}
- local focused = 'find'
- local stimer = nil
- local alive = false
-
- local find_pop = Popup({
- border = { style = 'rounded', text = { top = ' 🔎 Find ', top_align = 'left' } },
- buf_options = { buftype = 'nofile', modifiable = true },
- })
- local replace_pop = Popup({
- border = { style = 'rounded', text = { top = ' Replace ', top_align = 'left' } },
- buf_options = { buftype = 'nofile', modifiable = true },
- })
- local list_pop = Popup({
- border = { style = 'rounded', text = { top = ' Results ', top_align = 'left' } },
- buf_options = { buftype = 'nofile', modifiable = false },
- win_options = { cursorline = true, wrap = false },
- })
- local hint_pop = Popup({
- border = { style = 'none' },
- buf_options = { buftype = 'nofile', modifiable = false },
- win_options = { winhighlight = 'Normal:Comment' },
- })
- local preview_pop = Popup({
- border = { style = 'rounded', text = { top = ' Preview ', top_align = 'left' } },
- buf_options = { buftype = 'nofile', modifiable = false },
- win_options = { wrap = false, cursorline = true },
- })
-
- local total_h = math.max(16, math.min(30, math.floor(vim.o.lines * 0.75)))
- local total_w = math.min(vim.o.columns - 4, 170)
- local left_w = 48
-
- local layout = Layout(
- { relative = 'editor', position = '50%', size = { width = total_w, height = total_h } },
- Layout.Box({
- Layout.Box({
- Layout.Box({
- Layout.Box(find_pop, { size = 3 }),
- Layout.Box(replace_pop, { size = 3 }),
- Layout.Box(list_pop, { grow = 1 }),
- }, { dir = 'col', size = left_w }),
- Layout.Box(preview_pop, { grow = 1 }),
- }, { dir = 'row', grow = 1 }),
- Layout.Box(hint_pop, { size = 2 }),
- }, { dir = 'col' })
- )
-
- local ns = vim.api.nvim_create_namespace('charvim_rename_preview')
-
- local function get_find()
- return vim.api.nvim_buf_get_lines(find_pop.bufnr, 0, 1, false)[1] or ''
- end
- local function get_replace()
- return vim.api.nvim_buf_get_lines(replace_pop.bufnr, 0, 1, false)[1] or ''
- end
-
- local function update_preview(r)
- if not (alive and vim.api.nvim_win_is_valid(preview_pop.winid)) then return end
- vim.api.nvim_buf_clear_namespace(preview_pop.bufnr, ns, 0, -1)
- if not r then
- vim.bo[preview_pop.bufnr].modifiable = true
- vim.api.nvim_buf_set_lines(preview_pop.bufnr, 0, -1, false, {})
- vim.bo[preview_pop.bufnr].modifiable = false
- pcall(function() vim.wo[preview_pop.winid].winbar = '' end)
- return
- end
-
- local context = 8
- local lines, hl_row
-
- local buf = vim.fn.bufnr(r.filename)
- if buf ~= -1 and vim.api.nvim_buf_is_loaded(buf) then
- local s = math.max(0, r.lnum - context - 1)
- lines = vim.api.nvim_buf_get_lines(buf, s, r.lnum + context, false)
- hl_row = r.lnum - 1 - s
- else
- local ok, all = pcall(vim.fn.readfile, r.filename)
- if not ok then
- vim.bo[preview_pop.bufnr].modifiable = true
- vim.api.nvim_buf_set_lines(preview_pop.bufnr, 0, -1, false, { ' (cannot read file)' })
- vim.bo[preview_pop.bufnr].modifiable = false
- return
- end
- local s = math.max(1, r.lnum - context)
- local e = math.min(#all, r.lnum + context)
- lines = {}
- for i = s, e do lines[#lines + 1] = all[i] end
- hl_row = r.lnum - s
- end
-
- vim.bo[preview_pop.bufnr].modifiable = true
- vim.api.nvim_buf_set_lines(preview_pop.bufnr, 0, -1, false, lines)
- vim.bo[preview_pop.bufnr].modifiable = false
-
- -- Highlight the matched line and the matched text within it
- vim.api.nvim_buf_add_highlight(preview_pop.bufnr, ns, 'CursorLine', hl_row, 0, -1)
- local pat = get_find()
- if pat ~= '' then
- local col_s = (r.col or 1) - 1
- vim.api.nvim_buf_add_highlight(
- preview_pop.bufnr, ns, 'IncSearch', hl_row, col_s, col_s + #pat)
- end
-
- pcall(vim.api.nvim_win_set_cursor, preview_pop.winid, { hl_row + 1, 0 })
- pcall(vim.fn.win_execute, preview_pop.winid, 'normal! zz')
-
- local short = vim.fn.fnamemodify(r.filename, ':~:.')
- pcall(function() vim.wo[preview_pop.winid].winbar = ' ' .. short .. ':' .. r.lnum end)
-
- local ft = vim.filetype.match({ filename = r.filename }) or ''
- if ft ~= '' and vim.bo[preview_pop.bufnr].filetype ~= ft then
- vim.bo[preview_pop.bufnr].filetype = ft
- end
- end
-
- local function set_list(lines)
- vim.bo[list_pop.bufnr].modifiable = true
- vim.api.nvim_buf_set_lines(list_pop.bufnr, 0, -1, false, lines)
- vim.bo[list_pop.bufnr].modifiable = false
- end
-
- local function update_results(new_results)
- results = new_results
- if #results == 0 then
- set_list({ ' (no matches)' })
- update_preview(nil)
- else
- local lines = {}
- for _, r in ipairs(results) do
- lines[#lines + 1] = string.format(' %s:%d %s',
- vim.fn.fnamemodify(r.filename, ':~:.'), r.lnum, r.text)
- end
- set_list(lines)
- -- Show preview for the currently highlighted row (or first result)
- local row = vim.api.nvim_win_is_valid(list_pop.winid)
- and vim.api.nvim_win_get_cursor(list_pop.winid)[1] or 1
- update_preview(results[row] or results[1])
- end
- end
-
- local function run_search(pat)
- if pat == '' then update_results({}); return end
- local cwd = vim.fn.getcwd()
- vim.fn.jobstart(
- { 'rg', '--line-number', '--column', '--no-heading', '--color=never', '--', pat, '.' },
- {
- cwd = cwd,
- stdout_buffered = true,
- on_stdout = function(_, data)
- local found = {}
- for _, line in ipairs(data or {}) do
- if line ~= '' then
- local f, ln, col, txt = line:match('^(.-):(%d+):(%d+):(.*)$')
- if f then
- found[#found + 1] = {
- filename = vim.fn.fnamemodify(cwd .. '/' .. f, ':p'),
- lnum = tonumber(ln),
- col = tonumber(col),
- text = txt:match('^%s*(.-)%s*$') or txt,
- }
- end
- end
- end
- vim.schedule(function()
- if alive and vim.api.nvim_win_is_valid(list_pop.winid) then
- update_results(found)
- end
- end)
- end,
- }
- )
- end
-
- local function debounce(pat)
- if stimer then vim.fn.timer_stop(stimer) end
- stimer = vim.fn.timer_start(200, function()
- stimer = nil
- vim.schedule(function() run_search(pat) end)
- end)
- end
-
- local function focus_find()
- focused = 'find'
- vim.api.nvim_set_current_win(find_pop.winid)
- vim.cmd('startinsert!')
- end
- local function focus_replace()
- focused = 'replace'
- vim.api.nvim_set_current_win(replace_pop.winid)
- vim.cmd('startinsert!')
- end
- local function focus_list()
- focused = 'list'
- vim.api.nvim_set_current_win(list_pop.winid)
- local row = vim.api.nvim_win_get_cursor(list_pop.winid)[1]
- update_preview(results[row])
- end
- local function cycle()
- if focused == 'find' then focus_replace()
- elseif focused == 'replace' then focus_list()
- else focus_find() end
- end
-
- local function close()
- if not alive then return end
- alive = false
- if stimer then vim.fn.timer_stop(stimer); stimer = nil end
- layout:unmount()
- -- Restore normal mode in the underlying window after the popup is gone
- vim.schedule(function()
- if vim.api.nvim_get_mode().mode:sub(1, 1) == 'i' then
- vim.cmd('stopinsert')
- end
- end)
- end
-
- -- Y: accept all occurrences (LSP rename when applicable, else regex via cfdo)
- local function accept_all()
- local pat = get_find()
- local rep = get_replace()
- if pat == '' then vim.notify('Find field is empty', vim.log.levels.WARN); return end
- if #results == 0 then vim.notify('No matches', vim.log.levels.WARN); return end
-
- -- Use LSP rename when: LSP available, find term unchanged from original symbol
- if lsp_rename and pat == init_word and is_identifier(pat) then
- local new_name = rep ~= '' and rep or nil
- local saved_win = orig_win
- local saved_pos = orig_pos
- close()
- -- Defer past the close() stopinsert callback so the window is fully settled
- vim.schedule(function()
- if vim.api.nvim_win_is_valid(saved_win) then
- vim.api.nvim_set_current_win(saved_win)
- vim.api.nvim_win_set_cursor(saved_win, saved_pos)
- end
- vim.lsp.buf.rename(new_name)
- end)
- return
- end
-
- -- Regex replace across all matched files via quickfix
- local qfl = vim.tbl_map(function(r)
- return { filename = r.filename, lnum = r.lnum, col = r.col, text = r.text }
- end, results)
- close()
- vim.fn.setqflist(qfl)
- local ep = vim.fn.escape(pat, '/\\')
- local er = vim.fn.escape(rep, '/\\&')
- vim.cmd('cfdo %s/\\V' .. ep .. '/' .. er .. '/ge | update')
- vim.cmd('cclose')
- end
-
- -- y: accept the single occurrence under the list cursor
- local function accept_one()
- local row = vim.api.nvim_win_get_cursor(list_pop.winid)[1]
- local r = results[row]
- if not r then return end
- local pat = get_find()
- local rep = get_replace()
- if pat == '' then return end
-
- local ep = '\\V' .. vim.fn.escape(pat, '\\')
- local er = vim.fn.escape(rep, '\\&')
-
- local buf = vim.fn.bufnr(r.filename)
- if buf ~= -1 and vim.api.nvim_buf_is_loaded(buf) then
- local line = vim.api.nvim_buf_get_lines(buf, r.lnum - 1, r.lnum, false)[1] or ''
- vim.api.nvim_buf_set_lines(buf, r.lnum - 1, r.lnum, false,
- { vim.fn.substitute(line, ep, er, '') })
- else
- local ok, lines = pcall(vim.fn.readfile, r.filename)
- if not ok or not lines[r.lnum] then return end
- lines[r.lnum] = vim.fn.substitute(lines[r.lnum], ep, er, '')
- vim.fn.writefile(lines, r.filename)
- end
-
- table.remove(results, row)
- update_results(results)
- end
-
- local function jump()
- local row = vim.api.nvim_win_get_cursor(list_pop.winid)[1]
- local r = results[row]
- if not r then return end
- close()
- vim.cmd('edit ' .. vim.fn.fnameescape(r.filename))
- vim.api.nvim_win_set_cursor(0, { r.lnum, (r.col or 1) - 1 })
- end
-
- local function bmap(pop, modes, key, fn)
- for _, m in ipairs(modes) do
- pop:map(m, key, fn, { noremap = true, silent = true })
- end
- end
-
- layout:mount()
- alive = true
-
- vim.api.nvim_create_autocmd({ 'CursorMoved', 'CursorMovedI' }, {
- buffer = list_pop.bufnr,
- callback = function()
- if not alive then return end
- local row = vim.api.nvim_win_get_cursor(list_pop.winid)[1]
- update_preview(results[row])
- end,
- })
-
- local mode_label = lsp_rename and ' · LSP rename' or ' · Regex'
- vim.bo[hint_pop.bufnr].modifiable = true
- vim.api.nvim_buf_set_lines(hint_pop.bufnr, 0, -1, false, {
- '',
- ' cycle · j/k list · y accept · Y accept all · jump · close' .. mode_label,
- })
- vim.bo[hint_pop.bufnr].modifiable = false
-
- vim.api.nvim_buf_set_lines(find_pop.bufnr, 0, -1, false, { init_word })
-
- vim.api.nvim_create_autocmd({ 'TextChangedI', 'TextChanged' }, {
- buffer = find_pop.bufnr,
- callback = function() debounce(get_find()) end,
- })
-
- -- Find panel: Tab cycles, Esc closes, Y (normal) accepts all
- bmap(find_pop, { 'i', 'n' }, '', cycle)
- bmap(find_pop, { 'i', 'n' }, '', close)
- bmap(find_pop, { 'n' }, 'Y', accept_all)
-
- -- Replace panel: Tab cycles, Esc closes, CR/Y accepts all
- bmap(replace_pop, { 'i', 'n' }, '', cycle)
- bmap(replace_pop, { 'i', 'n' }, '', close)
- bmap(replace_pop, { 'i', 'n' }, '', accept_all)
- bmap(replace_pop, { 'n' }, 'Y', accept_all)
-
- local function list_down()
- local row = vim.api.nvim_win_get_cursor(list_pop.winid)[1]
- if row < #results then vim.api.nvim_win_set_cursor(list_pop.winid, { row + 1, 0 }) end
- end
- local function list_up()
- local row = vim.api.nvim_win_get_cursor(list_pop.winid)[1]
- if row > 1 then vim.api.nvim_win_set_cursor(list_pop.winid, { row - 1, 0 }) end
- end
-
- -- List panel: all keys mapped for both modes (mode-switching in nui is unreliable)
- bmap(list_pop, { 'i', 'n' }, '', cycle)
- bmap(list_pop, { 'i', 'n' }, '', close)
- bmap(list_pop, { 'i', 'n' }, 'q', close)
- bmap(list_pop, { 'i', 'n' }, '', jump)
- bmap(list_pop, { 'i', 'n' }, 'y', accept_one)
- bmap(list_pop, { 'i', 'n' }, 'Y', accept_all)
- bmap(list_pop, { 'i', 'n' }, 'j', list_down)
- bmap(list_pop, { 'i', 'n' }, 'k', list_up)
- bmap(list_pop, { 'i', 'n' }, '', list_down)
- bmap(list_pop, { 'i', 'n' }, '', list_up)
-
- run_search(init_word)
- focus_find()
-end
-
-function M.setup()
- vim.api.nvim_create_user_command('Find', function(opts)
- M.open(opts.range > 0)
- end, { desc = 'Find & replace / rename', range = true })
-
- vim.keymap.set('n', 'S', function() M.open(false) end, { desc = 'Find & replace / rename' })
- vim.keymap.set('v', 'S', function() M.open(true) end, { desc = 'Find & replace / rename (selection)' })
-end
-
-M.setup()
-return M
diff --git a/nvim/lua/statusline.lua b/nvim/lua/statusline.lua
deleted file mode 100644
index 2c1ba8c..0000000
--- a/nvim/lua/statusline.lua
+++ /dev/null
@@ -1,82 +0,0 @@
-local mode_map = {
- n = { label = 'N', hl = 'StatusLineNormal' },
- i = { label = 'I', hl = 'StatusLineInsert' },
- v = { label = 'V', hl = 'StatusLineVisual' },
- V = { label = 'V', hl = 'StatusLineVisual' },
- ['\22'] = { label = 'V', hl = 'StatusLineVisual' },
- c = { label = 'C', hl = 'StatusLineCommand' },
- R = { label = 'R', hl = 'StatusLineReplace' },
- r = { label = 'R', hl = 'StatusLineReplace' },
- t = { label = 'T', hl = 'StatusLineNormal' },
-}
-
-local name_map = {
- n = 'NORMAL',
- i = 'INSERT',
- v = 'VISUAL',
- V = 'V-LINE',
- ['\22'] = 'V-BLOCK',
- c = 'COMMAND',
- s = 'SELECT',
- S = 'S-LINE',
- ['\19'] = 'S-BLOCK',
- R = 'REPLACE',
- r = 'REPLACE',
- ['!'] = 'SHELL',
- t = 'TERMINAL',
-}
-
-local function get_mode_info()
- local mode = vim.api.nvim_get_mode().mode
- local info = mode_map[mode] or { label = mode:upper(), hl = 'StatusLineNormal' }
- return info.label, info.hl, (name_map[mode] or mode:upper())
-end
-
-function _G.custom_statusline()
- local label, hl, name = get_mode_info()
-
- local filename = vim.fn.expand('%:t')
- if filename == '' then filename = '[No Name]' end
-
- local modified = vim.bo.modified and ' [+]' or ''
-
- local saved_status = ''
- if vim.g.statusline_save_msg then
- saved_status = string.format('%%#StatusLineSaved# %s %%*', vim.g.statusline_save_msg)
- vim.defer_fn(function()
- vim.g.statusline_save_msg = nil
- vim.cmd('redrawstatus')
- end, 2000)
- end
-
- local left = string.format('%%#%s# %s %%* %s%s', hl, label, filename, modified)
- local right = string.format('%s %s %%p%%%% %%c Help', saved_status, name)
-
- return left .. '%=' .. right
-end
-
--- mode highlights
-vim.api.nvim_set_hl(0, 'StatusLineNormal', { fg = '#191724', bg = '#f6c177', bold = true })
-vim.api.nvim_set_hl(0, 'StatusLineInsert', { fg = '#191724', bg = '#9ccfd8', bold = true })
-vim.api.nvim_set_hl(0, 'StatusLineVisual', { fg = '#191724', bg = '#c4a7e7', bold = true })
-vim.api.nvim_set_hl(0, 'StatusLineCommand', { fg = '#191724', bg = '#eb6f92', bold = true })
-vim.api.nvim_set_hl(0, 'StatusLineReplace', { fg = '#191724', bg = '#eb6f92', bold = true })
-vim.api.nvim_set_hl(0, 'StatusLineSaved', { fg = '#191724', bg = '#c4a7e7', bold = true })
-vim.api.nvim_set_hl(0, 'StatusLine', { fg = '#e0def4', bg = '#191724' })
-vim.api.nvim_set_hl(0, 'StatusLineNC', { fg = '#6e6a86', bg = '#191724' })
-
-vim.opt.statusline = '%!v:lua.custom_statusline()'
-vim.opt.laststatus = 2
-
-vim.api.nvim_create_autocmd('BufWritePost', {
- callback = function()
- vim.g.statusline_save_msg = 'written'
- vim.cmd('redrawstatus')
- end
-})
-
-vim.api.nvim_create_autocmd('ModeChanged', {
- callback = function()
- vim.cmd('redrawstatus')
- end
-})
diff --git a/nvim/lua/theme-switcher.lua b/nvim/lua/theme-switcher.lua
deleted file mode 100644
index b2e450e..0000000
--- a/nvim/lua/theme-switcher.lua
+++ /dev/null
@@ -1,215 +0,0 @@
-local M = {}
-
-local themes = {
- {
- name = 'Auto',
- setup = function()
- vim.cmd.colorscheme('default')
- end,
- },
- {
- name = 'Knew-pines',
- setup = function()
- require('rose-pine').setup({
- variant = 'main',
- disable_background = true,
- disable_float_background = true,
- disable_italics = false,
- highlight_groups = {
- CursorLineNr = { fg = 'gold', bold = true },
- },
- })
- vim.cmd.colorscheme('rose-pine')
- end,
- },
- {
- name = 'Noir-cat',
- setup = function()
- vim.cmd.colorscheme('noir-cat')
- end,
- },
- {
- name = 'Tokyo Night',
- setup = function()
- require('tokyonight').setup({ style = 'night', transparent = true })
- vim.cmd.colorscheme('tokyonight')
- end,
- },
- {
- name = 'Catppuccin',
- setup = function()
- require('catppuccin').setup({ flavour = 'mocha', transparent_background = true })
- vim.cmd.colorscheme('catppuccin')
- end,
- },
- {
- name = 'Gruvbox',
- setup = function()
- require('gruvbox').setup({ transparent_mode = true })
- vim.cmd.colorscheme('gruvbox')
- end,
- },
- {
- name = 'Habamax',
- setup = function()
- vim.cmd.colorscheme('habamax')
- end,
- },
-}
-
-local current_theme_name = nil
-local persist_path = vim.fn.stdpath('data') .. '/charvim_theme'
-
-local function save_theme(name)
- local f = io.open(persist_path, 'w')
- if f then
- f:write(name)
- f:close()
- end
-end
-
-local function load_saved_theme()
- local f = io.open(persist_path, 'r')
- if not f then return nil end
- local name = f:read('*l')
- f:close()
- return name
-end
-
-local function apply_transparent_background()
- vim.api.nvim_set_hl(0, 'Normal', { bg = 'none' })
- vim.api.nvim_set_hl(0, 'NormalFloat', { bg = 'none' })
-end
-
-local function apply_theme(theme, persist)
- theme.setup()
- apply_transparent_background()
- current_theme_name = theme.name
- if persist then save_theme(theme.name) end
-end
-
-local function restore_theme(name)
- for _, t in ipairs(themes) do
- if t.name == name then
- apply_theme(t)
- return
- end
- end
-end
-
-function M.select_theme()
- local ok, pickers = pcall(require, 'telescope.pickers')
- if not ok then
- -- fallback when telescope is unavailable
- local items = vim.tbl_map(function(t) return t.name end, themes)
- vim.ui.select(items, {
- prompt = 'Select theme',
- format_item = function(item)
- return (item == current_theme_name and '▸ ' or ' ') .. item
- end,
- }, function(choice)
- if not choice then return end
- for _, t in ipairs(themes) do
- if t.name == choice then apply_theme(t, true) end
- end
- end)
- return
- end
-
- local finders = require('telescope.finders')
- local conf = require('telescope.config').values
- local actions = require('telescope.actions')
- local action_state = require('telescope.actions.state')
-
- local saved = current_theme_name or 'Auto'
-
- pickers.new({
- layout_strategy = 'center',
- layout_config = { width = 0.28, height = 0.45 },
- sorting_strategy = 'ascending',
- }, {
- prompt_title = ' Themes',
- previewer = false,
- finder = finders.new_table({
- results = themes,
- entry_maker = function(theme)
- return {
- value = theme,
- display = (theme.name == saved and ' • ' or ' ') .. theme.name,
- ordinal = theme.name,
- }
- end,
- }),
- sorter = conf.generic_sorter({}),
- attach_mappings = function(prompt_bufnr, map)
- local function preview_selected()
- local entry = action_state.get_selected_entry()
- if entry then
- entry.value.setup()
- apply_transparent_background()
- end
- end
-
- local function do_cancel()
- restore_theme(saved)
- actions.close(prompt_bufnr)
- end
-
- -- wrap movement to live-preview each theme
- map('i', '', function() actions.move_selection_next(prompt_bufnr); preview_selected() end)
- map('i', '', function() actions.move_selection_previous(prompt_bufnr); preview_selected() end)
- map('i', '', function() actions.move_selection_next(prompt_bufnr); preview_selected() end)
- map('i', '', function() actions.move_selection_previous(prompt_bufnr); preview_selected() end)
- map('n', 'j', function() actions.move_selection_next(prompt_bufnr); preview_selected() end)
- map('n', 'k', function() actions.move_selection_previous(prompt_bufnr); preview_selected() end)
-
- -- confirm
- actions.select_default:replace(function()
- local entry = action_state.get_selected_entry()
- actions.close(prompt_bufnr)
- if entry then
- apply_theme(entry.value, true)
- else
- restore_theme(saved)
- end
- end)
-
- -- cancel — restore original
- map('i', '', do_cancel)
- map('n', 'q', do_cancel)
- map('n', '', do_cancel)
-
- return true
- end,
- }):find()
-end
-
-function M.set_theme(name)
- for _, t in ipairs(themes) do
- if t.name:lower() == name:lower() then
- apply_theme(t, true)
- return
- end
- end
- vim.notify('Unknown theme: ' .. name, vim.log.levels.ERROR)
-end
-
-function M.setup()
- vim.schedule(function()
- local saved = load_saved_theme()
- if saved then
- restore_theme(saved)
- else
- apply_theme(themes[1])
- end
- end)
-
- vim.api.nvim_create_user_command('ThemeSelect', M.select_theme, { desc = 'Open theme picker' })
- vim.api.nvim_create_user_command('Theme', function(opts)
- M.set_theme(opts.args)
- end, { nargs = 1, desc = 'Switch to theme by name' })
-end
-
-M.setup()
-
-return M
diff --git a/nvim/lua/treesitter-config.lua b/nvim/lua/treesitter-config.lua
deleted file mode 100644
index 9949445..0000000
--- a/nvim/lua/treesitter-config.lua
+++ /dev/null
@@ -1,47 +0,0 @@
-local status_ok, ts = pcall(require, 'nvim-treesitter')
-if not status_ok then return end
-
-vim.filetype.add({
- extension = { cr = 'crystal', plist = 'xml' },
-})
-
-ts.setup({
- ensure_installed = {
- "c", "lua", "vim", "vimdoc", "query",
- "javascript", "typescript", "python", "rust", "go",
- "html", "css", "json", "markdown", "bash", "yaml", "toml",
- "xml", "kotlin"
- },
-
- sync_install = false,
- auto_install = true,
- ignore_install = {},
-
- highlight = {
- enable = true,
- disable = function(lang, buf)
- local max_filesize = 100 * 1024
- -- get_parser() returns nil on failure in 0.12 instead of throwing
- local parser = vim.treesitter.get_parser(buf, lang)
- if not parser then return true end
- local ok, stats = pcall(vim.loop.fs_stat, vim.api.nvim_buf_get_name(buf))
- if ok and stats and stats.size > max_filesize then return true end
- end,
- additional_vim_regex_highlighting = false,
- },
-
- indent = {
- enable = true,
- disable = { "python" },
- },
-
- incremental_selection = {
- enable = true,
- keymaps = {
- init_selection = "gnn",
- node_incremental = "grn",
- scope_incremental = "grc",
- node_decremental = "grm",
- },
- },
-})
diff --git a/nvim/lua/user-plugins.lua b/nvim/lua/user-plugins.lua
new file mode 100644
index 0000000..83dc264
--- /dev/null
+++ b/nvim/lua/user-plugins.lua
@@ -0,0 +1,3 @@
+-- user-plugins.lua — personal plugins (git-ignored, never overwritten by updates)
+-- Return a list of lazy.nvim plugin specs: https://lazy.folke.io/spec
+return {}
diff --git a/nvim/lua/user.lua b/nvim/lua/user.lua
new file mode 100644
index 0000000..634141e
--- /dev/null
+++ b/nvim/lua/user.lua
@@ -0,0 +1,6 @@
+-- user.lua — personal customizations (git-ignored, never overwritten by updates)
+-- Add keymaps, options, autocommands, or anything else here.
+-- This file is loaded last, so it can override any default setting.
+--
+-- vim.opt.wrap = true
+-- vim.keymap.set('n', 'x', ':!echo hi', { desc = 'My command' })
diff --git a/roadmap.html b/roadmap.html
new file mode 100644
index 0000000..6e8bae5
--- /dev/null
+++ b/roadmap.html
@@ -0,0 +1,333 @@
+
+
+
+
+
+ Roadmap | CharVim
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Release history & plans
+
Roadmap
+
From the very first commit to what's being built next, this is the full timeline of CharVim's evolution.
+
+
+
+
+
+
+
+
+
+
+
+
v1.6.0
+
Planned for 2026
+
Extended Language Support
+
+
Expanding the LSP and DAP ecosystem with additional language servers, better adapter reliability, and quality-of-life improvements across the board.
+
+ Rust / rust-analyzer
+ Ruby / solargraph
+ Better Swift DAP
+ Snippet library
+ Save popup v2
+
+
+
+
+
+
+
+
v2.0.0
+
Planned for the future
+
CLI Tooling, Theme Authoring, and In-Editor Updates
+
+
This release focuses on making CharVim feel like a complete, polished product from the command line down. A dedicated CLI will wrap common workflows, a theme authoring guide and toolkit will make it easy to build and share custom themes, and the update system will move fully in-editor so you never have to leave Neovim to stay current.
The far-horizon goal: CharVim becomes its own self-contained program. No Neovim installation required, no plugin manager, no external dependencies to wrangle. The editor runtime, language servers, and tooling ship together as a single installable application. A native GUI interface is part of this release, making CharVim accessible as a first-class desktop editor while keeping the full modal Vim experience intact underneath.
Rewrote the setup script to support macOS (Homebrew), Debian/Ubuntu (apt), and Arch Linux (pacman) alongside the existing Fedora support. Each platform function checks for missing binaries before installing, keeping installs minimal.
+
+ macOS via brew
+ Debian / Ubuntu via apt
+ Arch Linux via pacman
+ Per-platform installer design
+ Expanded dependency list
+
+
+
+
+
+
+
+
v1.5.1
+
June 26, 2026
+
Find & Replace Panel + Theme Persistence
+
+
Replaced the old inline rename with a full find & replace panel backed by ripgrep. Rewrote the theme switcher with live Telescope preview and cross-session persistence. Switched avante from local Ollama to Anthropic Claude.
Introduced local AI code completion via avante.nvim + Ollama, added Tokyo Night, Catppuccin, and Gruvbox themes with a runtime theme-switcher module, and integrated configurable linting via nvim-lint. GitHub Copilot was removed.
Major internal refactoring and optimizations for Neovim 0.12. Switched to native LSP completion, added Mason dashboard integration, new file runners, and resolved several breaking API changes.
+
+ Mason in dashboard
+ HTML / Python / Java runners
+ nvim-ts-autotag
+ Native LSP completion
+ 0.12 API compatibility
+ Treesitter parser retrieval
+ scrolloff option
+
+
+
+
+
+
+
+
v1.2.0
+
March 29, 2026
+
Project Management & CI
+
+
Added project.nvim for smart project detection and workspace switching via Telescope. Set up GitHub Actions for Neovim configuration tests.
+
+ project.nvim
+ GitHub Actions CI
+
+
+
+
+
+
+
+
v1.1.0
+
March 10, 2026
+
Debugger Integration & Setup Script
+
+
Integrated DAP debuggers for Java, Swift, Python, and C/C++ with customized UI layouts and session management. Added setup.sh for easier installation and improved COBOL language support.
The first public release of CharVim. A full Neovim configuration with comprehensive LSP support, GitHub Copilot AI, a beautiful dashboard, telescope fuzzy finding, harpoon file navigation, and tmux integration.