updating all of my config, specifically doing this to fix the nvim configurations

This commit is contained in:
klein panic
2025-01-29 15:26:21 -05:00
parent c15409e848
commit 47a02e4479
949 changed files with 127542 additions and 6069 deletions

1
nvim/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
pack/

View File

@@ -1,6 +0,0 @@
function c()
nn('<c-n>', ":w<cr>:!gcc '%' -o '%:r' -lm -O3 && ./'%:r'<cr>")
nn('<c-p>', ":w<cr>:make<cr>")
end
c()

View File

@@ -1,324 +1,14 @@
-- Enable line numbers
vim.o.number = true
-- Load settings
require('settings')
-- Enable true color support
if vim.fn.has("termguicolors") then
vim.o.termguicolors = true
end
-- Load plugins
require('plugins')
-- Set the indentation rules
vim.o.tabstop = 4
vim.o.shiftwidth = 4
vim.o.expandtab = true
-- Load keybindings
require('keybindings')
-- Enable syntax highlighting
vim.cmd [[packadd packer.nvim]]
-- Load Vimwiki and LaTeX integration
require('vimwiki')
-- Use a protected call to avoid errors on first use
local status_ok, packer = pcall(require, "packer")
if not status_ok then
print("Packer not found!")
return
end
-- Initialize the plugins
packer.init({
display = {
open_fn = function()
return require('packer.util').float({ border = 'single' })
end,
},
})
-- Specify the plugins
packer.startup(function(use)
-- Packer can manage itself
use 'wbthomason/packer.nvim'
-- File icons
use 'kyazdani42/nvim-web-devicons'
-- Gruvbox theme
use {
'morhetz/gruvbox',
config = function()
vim.cmd 'colorscheme gruvbox'
end
}
-- Commenting
use {
'numToStr/Comment.nvim',
config = function()
require('Comment').setup()
end
}
-- Status line
use {
'nvim-lualine/lualine.nvim',
requires = {'kyazdani42/nvim-web-devicons', opt = true},
config = function()
require('lualine').setup {
options = {
icons_enabled = true,
theme = 'gruvbox',
component_separators = '|',
section_separators = '',
},
sections = {
lualine_a = {'mode'},
lualine_b = {'branch', 'diff', {'diagnostics', sources={'nvim_lsp', 'coc'}}},
lualine_c = {'filename'},
lualine_x = {'encoding', 'fileformat', 'filetype'},
lualine_y = {'progress'},
lualine_z = {'location'}
},
inactive_sections = {
lualine_a = {},
lualine_b = {},
lualine_c = {'filename'},
lualine_x = {'location'},
},
tabline = {},
extensions = {}
}
end
}
-- Markdown preview
use {'iamcco/markdown-preview.nvim'}
-- Vimwiki
use {
'vimwiki/vimwiki',
config = function()
vim.g.vimwiki_list = {{
path = '~/vimwiki/',
syntax = 'markdown',
ext = '.md',
diary_rel_path = 'diary/'
}}
end
}
-- Gitsigns
use {
'lewis6991/gitsigns.nvim',
requires = {
'nvim-lua/plenary.nvim'
},
config = function()
require('gitsigns').setup()
end
}
-- Which-key
use {
'folke/which-key.nvim',
config = function()
require('which-key').setup()
end
}
-- nvim-cmp for autocompletion
use 'hrsh7th/nvim-cmp' -- Completion framework
use 'hrsh7th/cmp-nvim-lsp' -- LSP completion source
use 'hrsh7th/cmp-buffer' -- Buffer completion source
use 'hrsh7th/cmp-path' -- Path completion source
use 'hrsh7th/cmp-cmdline' -- Command line completion source
use 'saadparwaiz1/cmp_luasnip' -- Snippet completion source
use 'L3MON4D3/LuaSnip' -- Snippet engine
-- LSP support
use 'neovim/nvim-lspconfig' -- Collection of configurations for built-in LSP client
-- File explorer
use {
'kyazdani42/nvim-tree.lua',
requires = 'kyazdani42/nvim-web-devicons',
config = function()
require'nvim-tree'.setup {}
end
}
-- Fuzzy finder
use {
'nvim-telescope/telescope.nvim',
requires = { {'nvim-lua/plenary.nvim'} },
config = function()
require'telescope'.setup {}
end
}
-- Treesitter
use {
'nvim-treesitter/nvim-treesitter',
run = ':TSUpdate',
config = function()
require'nvim-treesitter.configs'.setup {
ensure_installed = { "go", "lua", "python", "rust" }, -- add Go and any other languages you need
highlight = {
enable = true,
},
incremental_selection = {
enable = true,
},
textobjects = {
select = {
enable = true,
},
},
}
end
}
-- Auto-pairs
use {
'windwp/nvim-autopairs',
config = function()
require'nvim-autopairs'.setup {}
end
}
end)
-- LSP settings
local lspconfig = require('lspconfig')
-- Set up 'gopls' and other language servers
lspconfig.gopls.setup {
on_attach = on_attach,
settings = {
gopls = {
analyses = {
unusedparams = true,
shadow = true,
},
staticcheck = true,
},
},
init_options = {
verboseOutput = true,
}
}
local on_attach = function(client, bufnr)
local function buf_set_keymap(...) vim.api.nvim_buf_set_keymap(bufnr, ...) end
local function buf_set_option(...) vim.api.nvim_buf_set_option(bufnr, ...) end
buf_set_option('omnifunc', 'v:lua.vim.lsp.omnifunc')
local opts = { noremap=true, silent=true }
buf_set_keymap('n', 'gD', '<Cmd>lua vim.lsp.buf.declaration()<CR>', opts)
buf_set_keymap('n', 'gd', '<Cmd>lua vim.lsp.buf.definition()<CR>', opts)
buf_set_keymap('n', 'K', '<Cmd>lua vim.lsp.buf.hover()<CR>', opts)
buf_set_keymap('n', 'gi', '<cmd>lua vim.lsp.buf.implementation()<CR>', opts)
buf_set_keymap('n', '<C-k>', '<cmd>lua vim.lsp.buf.signature_help()<CR>', opts)
buf_set_keymap('n', '<space>wa', '<cmd>lua vim.lsp.buf.add_workspace_folder()<CR>', opts)
buf_set_keymap('n', '<space>wr', '<cmd>lua vim.lsp.buf.remove_workspace_folder()<CR>', opts)
buf_set_keymap('n', '<space>wl', '<cmd>lua print(vim.inspect(vim.lsp.buf.list_workspace_folders()))<CR>', opts)
buf_set_keymap('n', '<space>D', '<cmd>lua vim.lsp.buf.type_definition()<CR>', opts)
buf_set_keymap('n', '<space>rn', '<cmd>lua vim.lsp.buf.rename()<CR>', opts)
buf_set_keymap('n', '<space>ca', '<cmd>lua vim.lsp.buf.code_action()<CR>', opts)
buf_set_keymap('n', 'gr', '<cmd>lua vim.lsp.buf.references()<CR>', opts)
buf_set_keymap('n', '<space>e', '<cmd>lua vim.lsp.diagnostic.show_line_diagnostics()<CR>', opts)
buf_set_keymap('n', '[d', '<cmd>lua vim.lsp.diagnostic.goto_prev()<CR>', opts)
buf_set_keymap('n', ']d', '<cmd>lua vim.lsp.diagnostic.goto_next()<CR>', opts)
buf_set_keymap('n', '<space>q', '<cmd>lua vim.lsp.diagnostic.set_loclist()<CR>', opts)
buf_set_keymap('n', '<space>f', '<cmd>lua vim.lsp.buf.formatting()<CR>', opts)
end
-- Enable the following language servers
local servers = { 'pyright', 'ts_ls', 'gopls', 'rust_analyzer' }
for _, lsp in ipairs(servers) do
lspconfig[lsp].setup {
on_attach = on_attach,
}
end
-- nvim-cmp setup
local cmp = require'cmp'
cmp.setup({
snippet = {
expand = function(args)
require'luasnip'.lsp_expand(args.body)
end,
},
mapping = {
['<C-b>'] = cmp.mapping.scroll_docs(-4),
['<C-f>'] = cmp.mapping.scroll_docs(4),
['<C-Space>'] = cmp.mapping.complete(),
['<C-e>'] = cmp.mapping.close(),
['<CR>'] = cmp.mapping.confirm({
behavior = cmp.ConfirmBehavior.Replace,
select = true,
}),
},
sources = {
{ name = 'nvim_lsp' },
{ name = 'luasnip' },
{ name = 'buffer' },
{ name = 'path' },
}
})
-- Keybindings for custom functions
function SearchWikipedia()
local query = vim.fn.input('Wikipedia Search: ')
query = vim.fn.shellescape(query)
local search_url = "https://en.wikipedia.org/wiki/Special:Search?search=" .. query
vim.cmd('silent !xdg-open ' .. search_url)
end
vim.api.nvim_set_keymap('n', '<leader>w', ':lua SearchWikipedia()<CR>', {noremap = true, silent = true})
require('arduino')
local group = vim.api.nvim_create_augroup('FileTypeSpecific', {clear = true})
local associations = {
-- extension functions to call
{ '*.ino' , { arduino } },
}
-- Create an augroup for Rust-specific settings
vim.api.nvim_create_augroup('RustGroup', { clear = true })
-- Define autocmds that apply only for Rust files
vim.api.nvim_create_autocmd('FileType', {
group = 'RustGroup',
pattern = 'rust',
callback = function()
-- Keybinding for running rustfmt (formatting)
vim.api.nvim_buf_set_keymap(0, 'n', '<leader>rf', ':lua vim.lsp.buf.format()<CR>', { noremap = true, silent = true })
-- Keybinding for running Clippy (linting)
vim.api.nvim_buf_set_keymap(0, 'n', '<leader>rc', ':!cargo clippy<CR>', { noremap = true, silent = true })
-- Keybinding to build Rust project
vim.api.nvim_buf_set_keymap(0, 'n', '<leader>rb', ':!cargo build<CR>', { noremap = true, silent = true })
-- Keybinding to run Rust project
vim.api.nvim_buf_set_keymap(0, 'n', '<leader>rr', ':!cargo run<CR>', { noremap = true, silent = true })
-- Show diagnostics (errors/warnings) in Rust
vim.api.nvim_buf_set_keymap(0, 'n', '<leader>rd', ':lua vim.diagnostic.open_float()<CR>', { noremap = true, silent = true })
print("Rust-specific keybindings loaded!")
end
})
for _, assoc in ipairs(associations) do
local pattern = assoc[1]
local callbacks = assoc[2]
vim.api.nvim_create_autocmd({'BufNewFile', 'BufRead', 'BufEnter'}, {
pattern = pattern,
group = group,
callback =
function()
for _, callback in ipairs(callbacks) do
callback()
end
end
})
end
-- Load Compilation Integrations:
require('compilation').setup()

View File

@@ -1,256 +1,73 @@
-- Enable line numbers
-- General
vim.o.number = true
-- Enable true color support
if vim.fn.has("termguicolors") then
vim.o.termguicolors = true
end
-- gruvbox
vim.o.background = "dark"
vim.cmd([[colorscheme gruvbox]])
-- Set the indentation rules
vim.o.tabstop = 4
vim.o.shiftwidth = 4
vim.o.expandtab = true
-- Enable syntax highlighting
vim.cmd [[packadd packer.nvim]]
-- treesitter
require'nvim-treesitter.configs'.setup{
highlight = {
enable = true,
},
indent = {
enable = true,
}
}
-- Use a protected call to avoid errors on first use
local status_ok, packer = pcall(require, "packer")
if not status_ok then
print("Packer not found!")
return
end
-- Initialize the plugins
packer.init({
display = {
open_fn = function()
return require('packer.util').float({ border = 'single' })
end,
},
})
-- Specify the plugins
packer.startup(function(use)
-- Packer can manage itself
use 'wbthomason/packer.nvim'
-- File icons
use 'kyazdani42/nvim-web-devicons'
-- Gruvbox theme
use {
'morhetz/gruvbox',
config = function()
vim.cmd 'colorscheme gruvbox'
end
}
-- Commenting
use {
'numToStr/Comment.nvim',
config = function()
require('Comment').setup()
end
}
-- Status line
use {
'nvim-lualine/lualine.nvim',
requires = {'kyazdani42/nvim-web-devicons', opt = true},
config = function()
require('lualine').setup {
options = {
icons_enabled = true,
theme = 'gruvbox',
component_separators = '|',
section_separators = '',
},
sections = {
lualine_a = {'mode'},
lualine_b = {'branch', 'diff', {'diagnostics', sources={'nvim_lsp', 'coc'}}},
lualine_c = {'filename'},
lualine_x = {'encoding', 'fileformat', 'filetype'},
lualine_y = {'progress'},
lualine_z = {'location'}
},
inactive_sections = {
lualine_a = {},
lualine_b = {},
lualine_c = {'filename'},
lualine_x = {'location'},
},
tabline = {},
extensions = {}
}
end
}
-- Markdown preview
use {'iamcco/markdown-preview.nvim'}
-- Vimwiki
use {
'vimwiki/vimwiki',
config = function()
vim.g.vimwiki_list = {{
-- vimwiki
vim.cmd([[
set nocompatible
filetype plugin on
syntax on
]])
vim.g.vimwiki_list = {
{
path = '~/vimwiki/',
syntax = 'markdown',
ext = '.md',
diary_rel_path = 'diary/'
}}
end
}
ext = '.md'
}
}
-- Gitsigns
use {
'lewis6991/gitsigns.nvim',
requires = {
'nvim-lua/plenary.nvim'
},
config = function()
require('gitsigns').setup()
end
}
-- Which-key
use {
'folke/which-key.nvim',
config = function()
require('which-key').setup()
end
}
-- telescope
local builtin = require('telescope.builtin')
vim.keymap.set('n', '<leader>ff', builtin.find_files, { desc = 'Telescope find files' })
vim.keymap.set('n', '<leader>fg', builtin.live_grep, { desc = 'Telescope live grep' })
vim.keymap.set('n', '<leader>fb', builtin.buffers, { desc = 'Telescope buffers' })
vim.keymap.set('n', '<leader>fh', builtin.help_tags, { desc = 'Telescope help tags' })
-- nvim-cmp for autocompletion
use 'hrsh7th/nvim-cmp' -- Completion framework
use 'hrsh7th/cmp-nvim-lsp' -- LSP completion source
use 'hrsh7th/cmp-buffer' -- Buffer completion source
use 'hrsh7th/cmp-path' -- Path completion source
use 'hrsh7th/cmp-cmdline' -- Command line completion source
use 'saadparwaiz1/cmp_luasnip' -- Snippet completion source
use 'L3MON4D3/LuaSnip' -- Snippet engine
-- LSP support
use 'neovim/nvim-lspconfig' -- Collection of configurations for built-in LSP client
-- lualine
require('lualine').setup()
-- File explorer
use {
'kyazdani42/nvim-tree.lua',
requires = 'kyazdani42/nvim-web-devicons',
config = function()
require'nvim-tree'.setup {}
end
}
-- Fuzzy finder
use {
'nvim-telescope/telescope.nvim',
requires = { {'nvim-lua/plenary.nvim'} },
config = function()
require'telescope'.setup {}
end
}
-- comment
require('Comment').setup()
-- Treesitter
use {
'nvim-treesitter/nvim-treesitter',
run = ':TSUpdate',
config = function()
require'nvim-treesitter.configs'.setup {
highlight = {
enable = true,
},
incremental_selection = {
enable = true,
},
textobjects = {
select = {
enable = true,
},
},
}
end
}
-- Auto-pairs
use {
'windwp/nvim-autopairs',
config = function()
require'nvim-autopairs'.setup {}
end
}
-- autopairs
require('nvim-autopairs').setup()
end)
-- LSP settings
local lspconfig = require('lspconfig')
local on_attach = function(client, bufnr)
local function buf_set_keymap(...) vim.api.nvim_buf_set_keymap(bufnr, ...) end
local function buf_set_option(...) vim.api.nvim_buf_set_option(bufnr, ...) end
buf_set_option('omnifunc', 'v:lua.vim.lsp.omnifunc')
local opts = { noremap=true, silent=true }
buf_set_keymap('n', 'gD', '<Cmd>lua vim.lsp.buf.declaration()<CR>', opts)
buf_set_keymap('n', 'gd', '<Cmd>lua vim.lsp.buf.definition()<CR>', opts)
buf_set_keymap('n', 'K', '<Cmd>lua vim.lsp.buf.hover()<CR>', opts)
buf_set_keymap('n', 'gi', '<cmd>lua vim.lsp.buf.implementation()<CR>', opts)
buf_set_keymap('n', '<C-k>', '<cmd>lua vim.lsp.buf.signature_help()<CR>', opts)
buf_set_keymap('n', '<space>wa', '<cmd>lua vim.lsp.buf.add_workspace_folder()<CR>', opts)
buf_set_keymap('n', '<space>wr', '<cmd>lua vim.lsp.buf.remove_workspace_folder()<CR>', opts)
buf_set_keymap('n', '<space>wl', '<cmd>lua print(vim.inspect(vim.lsp.buf.list_workspace_folders()))<CR>', opts)
buf_set_keymap('n', '<space>D', '<cmd>lua vim.lsp.buf.type_definition()<CR>', opts)
buf_set_keymap('n', '<space>rn', '<cmd>lua vim.lsp.buf.rename()<CR>', opts)
buf_set_keymap('n', '<space>ca', '<cmd>lua vim.lsp.buf.code_action()<CR>', opts)
buf_set_keymap('n', 'gr', '<cmd>lua vim.lsp.buf.references()<CR>', opts)
buf_set_keymap('n', '<space>e', '<cmd>lua vim.lsp.diagnostic.show_line_diagnostics()<CR>', opts)
buf_set_keymap('n', '[d', '<cmd>lua vim.lsp.diagnostic.goto_prev()<CR>', opts)
buf_set_keymap('n', ']d', '<cmd>lua vim.lsp.diagnostic.goto_next()<CR>', opts)
buf_set_keymap('n', '<space>q', '<cmd>lua vim.lsp.diagnostic.set_loclist()<CR>', opts)
buf_set_keymap('n', '<space>f', '<cmd>lua vim.lsp.buf.formatting()<CR>', opts)
end
-- Enable the following language servers
local servers = { 'pyright', 'tsserver', 'gopls', 'rust_analyzer' }
for _, lsp in ipairs(servers) do
lspconfig[lsp].setup {
on_attach = on_attach,
}
end
-- nvim-cmp setup
-- cmp
local cmp = require'cmp'
cmp.setup({
snippet = {
expand = function(args)
require'luasnip'.lsp_expand(args.body)
end,
},
mapping = {
['<C-b>'] = cmp.mapping.scroll_docs(-4),
['<C-f>'] = cmp.mapping.scroll_docs(4),
['<C-Space>'] = cmp.mapping.complete(),
['<C-e>'] = cmp.mapping.close(),
['<CR>'] = cmp.mapping.confirm({
behavior = cmp.ConfirmBehavior.Replace,
select = true,
}),
},
sources = {
{ name = 'nvim_lsp' },
{ name = 'luasnip' },
{ name = 'buffer' },
{ name = 'path' },
}
snippet = {
expand = function(args)
require('luasnip').lsp_expand(args.body)
end
}
})
-- Keybindings for custom functions
function SearchWikipedia()
local query = vim.fn.input('Wikipedia Search: ')
query = vim.fn.shellescape(query)
local search_url = "https://en.wikipedia.org/wiki/Special:Search?search=" .. query
vim.cmd('silent !xdg-open ' .. search_url)
end
vim.api.nvim_set_keymap('n', '<leader>w', ':lua SearchWikipedia()<CR>', {noremap = true, silent = true})
cmp.setup.cmdline(':', {
mapping = cmp.mapping.preset.cmdline(),
sources = cmp.config.sources({
{ name = 'path' }
}, {
{ name = 'cmdline' }
}),
matching = { disallow_symbol_nonprefix_matching = false }
})
-- TODO complete https://github.com/hrsh7th/nvim-cmp

View File

@@ -1,21 +0,0 @@
function map(mode, shortcut, command)
vim.api.nvim_set_keymap(mode, shortcut, command, {noremap=true})
end
function nn(shortcut, command)
map('n', shortcut, command)
end
function ino(shortcut, command)
map('i', shortcut, command)
end
function arduino()
baudrate = 9600
-- compile and upload sketch
nn('<c-n>', ":w | !$HOME/.config/nvim/scripts/arduino -u '%'<cr>")
-- show information about connected board
nn('+', ":w | !$HOME/.config/nvim/scripts/arduino -i<cr>")
-- monitor serial data
nn('-', ":w | tabe | ter $HOME/.config/nvim/scripts/arduino -m " .. baudrate .. " <cr>i")
end

102
nvim/lua/compilation.lua Normal file
View File

@@ -0,0 +1,102 @@
-- File: ~/.config/nvim/lua/compilation.lua
local M = {}
-- Function to compile and run the current file
function M.compile_and_run()
-- Get the current file path
local file = vim.fn.expand('%')
-- Check if the current buffer is a directory
if vim.fn.isdirectory(file) == 1 then
vim.notify("Cannot compile a directory. Please open a source file.", vim.log.levels.ERROR)
return
end
-- Determine the filetype (c, cpp, etc.)
local filetype = vim.bo.filetype
local compiler = ''
local flags = ''
-- Set compiler and flags based on filetype
if filetype == 'c' then
compiler = 'gcc'
flags = '-lm -O3'
elseif filetype == 'cpp' then
compiler = 'g++'
flags = '-lm -O3'
else
vim.notify("Unsupported file type: " .. filetype, vim.log.levels.ERROR)
return
end
-- Get the base name of the file without extension
local output_name = vim.fn.expand('%:t:r') -- %:t:r extracts the filename without extension
-- Construct the compile command
local compile_cmd = string.format('%s %s "%s" -o "%s"', compiler, flags, file, output_name)
-- Notify the user about the compilation process
vim.notify("Compiling...", vim.log.levels.INFO)
-- Execute the compile command
local compile_output = vim.fn.system(compile_cmd)
local compile_exit = vim.v.shell_error
-- Check if compilation was successful
if compile_exit ~= 0 then
vim.notify("Compilation failed:\n" .. compile_output, vim.log.levels.ERROR)
-- Populate quickfix list with errors
vim.fn.setqflist({}, ' ')
for line in compile_output:gmatch("[^\r\n]+") do
if line:match("error") or line:match("warning") then
vim.fn.setqflist({}, 'a', { lines = { line } })
end
end
vim.cmd('copen') -- Open the quickfix list to show errors
return
end
-- Construct the run command
local run_cmd = string.format('./%s', output_name)
-- Notify the user about the execution process
vim.notify("Running executable...", vim.log.levels.INFO)
-- Function to check if a terminal is already open
local function is_terminal_open()
for _, win in ipairs(vim.api.nvim_list_wins()) do
local buf = vim.api.nvim_win_get_buf(win)
if vim.api.nvim_buf_get_option(buf, 'buftype') == 'terminal' then
return true
end
end
return false
end
-- Run the executable in a terminal split at the bottom
if not is_terminal_open() then
vim.cmd('botright split') -- Open split at the bottom
vim.cmd('resize 15') -- Optional: Adjust the size of the split
vim.cmd('terminal ' .. run_cmd) -- Run the executable
else
-- If a terminal is already open, send the run command to it
for _, win in ipairs(vim.api.nvim_list_wins()) do
local buf = vim.api.nvim_win_get_buf(win)
if vim.api.nvim_buf_get_option(buf, 'buftype') == 'terminal' then
vim.api.nvim_set_current_win(win)
vim.api.nvim_feedkeys(run_cmd .. '\n', 'n', true)
break
end
end
end
end
-- Function to set up keybindings
function M.setup()
-- Bind <leader>c to compile_and_run in normal mode
vim.keymap.set('n', '<leader>c', M.compile_and_run, { noremap = true, silent = true })
end
return M

90
nvim/lua/keybindings.lua Normal file
View File

@@ -0,0 +1,90 @@
--local map = vim.api.nvim_set_keymap
local map = vim.keymap.set
local opts = { noremap = true, silent = true }
-- Telescope Keybindings
map('n', '<leader>ff', "<cmd>lua require('telescope.builtin').find_files()<CR>", opts)
map('n', '<leader>fg', "<cmd>lua require('telescope.builtin').live_grep()<CR>", opts)
map('n', '<leader>fb', "<cmd>lua require('telescope.builtin').buffers()<CR>", opts)
map('n', '<leader>fh', "<cmd>lua require('telescope.builtin').help_tags()<CR>", opts)
-- Language Server Protocol (LSP) Keybindings
-- gD: Go to the declaration of the symbol under the cursor.
-- gd: Go to the definition of the symbol under the cursor.
-- K: Show documentation for the symbol under the cursor.
-- gi: Go to the implementation of a symbol (e.g., the implementation of an interface or function).
-- <C-k>: Show the function signature while coding.\
-- <space>rn: Rename a symbol (e.g., a variable or function).
-- <space>ca: Trigger a code action (e.g., auto-fix issues, refactor code).
-- gr: List all references to the symbol under the cursor.
-- <space>f: Format the current buffer using the language server.
local function lsp_keybindings(bufnr)
local opts = { noremap = true, silent = true, buffer = bufnr }
vim.keymap.set('n', 'gD', vim.lsp.buf.declaration, opts)
vim.keymap.set('n', 'gd', vim.lsp.buf.definition, opts)
vim.keymap.set('n', 'K', vim.lsp.buf.hover, opts)
vim.keymap.set('n', 'gi', vim.lsp.buf.implementation, opts)
vim.keymap.set('n', '<C-k>', vim.lsp.buf.signature_help, opts)
vim.keymap.set('n', '<space>rn', vim.lsp.buf.rename, opts)
vim.keymap.set('n', '<space>ca', vim.lsp.buf.code_action, opts)
vim.keymap.set('n', 'gr', vim.lsp.buf.references, opts)
vim.keymap.set('n', '<space>f', vim.lsp.buf.format, opts)
end
-- Wikipedia search
function SearchWikipedia()
local query = vim.fn.input('Wikipedia Search: ')
query = vim.fn.shellescape(query)
local search_url = "https://en.wikipedia.org/wiki/Special:Search?search=" .. query
vim.cmd('silent !xdg-open ' .. search_url)
end
vim.api.nvim_set_keymap('n', '<leader>w', ':lua SearchWikipedia()<CR>', { noremap = true, silent = true })
-- Vimwiki Markdown Preview
function VimwikiMarkdownPreview()
-- Path to the script
local script_path = vim.fn.expand('~/.config/nvim/scripts/vimwiki-markdown-preview.sh')
-- Check if the script exists
if vim.fn.filereadable(script_path) == 0 then
vim.notify("Script not found at " .. script_path, vim.log.levels.ERROR)
return
end
-- Run the script with --index-wiki flag
local command = string.format('bash %s --index-wiki', vim.fn.shellescape(script_path))
vim.cmd('silent !' .. command)
end
map('n', '<leader>mip', VimwikiMarkdownPreview, opts)
-- Vimwiki Convert Current File to HTML, move it, and open with qutebrowser
function VimwikiConvertCurrent()
-- Path to the script
local script_path = vim.fn.expand('~/.config/nvim/scripts/vimwiki-markdown-preview.sh')
-- Check if the script exists
if vim.fn.filereadable(script_path) == 0 then
vim.notify("Script not found at " .. script_path, vim.log.levels.ERROR)
return
end
-- Get the current file path
local current_file = vim.api.nvim_buf_get_name(0)
-- vim.notify("Retrieved file path: " .. current_file, vim.log.levels.INFO) -- Debugging: Print the file path
-- Check if it's a markdown file
if not current_file:match('%.md$') then
vim.notify('Current file is not a Markdown file.', vim.log.levels.ERROR)
return
end
-- Check if the source file exists
if vim.fn.filereadable(current_file) == 0 then
vim.notify('Current Markdown file does not exist.', vim.log.levels.ERROR)
return
end
-- Explicitly construct the command string
local command = "bash " .. vim.fn.shellescape(script_path) .. " --convert " .. current_file
vim.notify("Running command: " .. command, vim.log.levels.INFO) -- Debugging: Print the command being run
-- Run the command
vim.cmd('silent !' .. command)
vim.notify('Conversion and opening in qutebrowser completed.', vim.log.levels.INFO)
end
map('n', '<leader>mp', VimwikiConvertCurrent, opts)

44
nvim/lua/plugins.lua Normal file
View File

@@ -0,0 +1,44 @@
-- treesitter
require'nvim-treesitter.configs'.setup{
highlight = {
enable = true,
},
indent = {
enable = true,
}
}
-- telescope
local builtin = require('telescope.builtin')
-- lualine
require('lualine').setup()
-- comment
require('Comment').setup()
-- autopairs
require('nvim-autopairs').setup()
-- cmp
local cmp = require'cmp'
cmp.setup({
snippet = {
expand = function(args)
require('luasnip').lsp_expand(args.body)
end
}
})
cmp.setup.cmdline(':', {
mapping = cmp.mapping.preset.cmdline(),
sources = cmp.config.sources({
{ name = 'path' }
}, {
{ name = 'cmdline' }
}),
matching = { disallow_symbol_nonprefix_matching = false }
})
-- TODO complete https://github.com/hrsh7th/nvim-cmp

4
nvim/lua/settings.lua Normal file
View File

@@ -0,0 +1,4 @@
-- General Settings
vim.o.number = true
vim.o.background = "dark"
vim.cmd([[colorscheme gruvbox]])

118
nvim/lua/settings.lua.all Normal file
View File

@@ -0,0 +1,118 @@
-- SETTINGS.LUA: Comprehensive Neovim Configuration
-- Enable line numbers
vim.o.number = true -- Show absolute line numbers
vim.o.relativenumber = true -- Show relative line numbers (useful for jump motions)
-- Cursor behavior
vim.o.cursorline = true -- Highlight the line where the cursor is
vim.o.cursorcolumn = true -- Highlight the column where the cursor is
vim.o.scrolloff = 8 -- Keep 8 lines visible above/below the cursor
vim.o.sidescrolloff = 8 -- Keep 8 columns visible to the left/right of the cursor
-- UI customization
vim.o.termguicolors = true -- Enable 24-bit RGB color in the terminal
vim.o.signcolumn = "yes" -- Always show the sign column (useful for LSP, Git)
vim.o.colorcolumn = "80" -- Highlight the 80th column (good for coding style guides)
vim.o.laststatus = 3 -- Global status line (shared between windows)
vim.o.showmode = false -- Disable the default mode display (e.g., -- INSERT --)
vim.o.wrap = false -- Disable line wrapping (horizontal scrolling for long lines)
vim.o.showcmd = true -- Display incomplete commands at the bottom
vim.o.ruler = true -- Show the cursor position at the bottom right
-- Tabs and indentation
vim.o.tabstop = 4 -- Number of spaces for a tab
vim.o.shiftwidth = 4 -- Number of spaces for indentation
vim.o.softtabstop = 4 -- Treat <Tab> as 4 spaces while editing
vim.o.expandtab = true -- Convert tabs to spaces
vim.o.autoindent = true -- Copy indentation from the previous line
vim.o.smartindent = true -- Automatically add extra indentation for new lines
-- Search settings
vim.o.ignorecase = true -- Ignore case when searching
vim.o.smartcase = true -- Override ignorecase if the search query contains uppercase
vim.o.incsearch = true -- Show incremental search results as you type
vim.o.hlsearch = true -- Highlight all matches from the search
vim.o.wildmenu = true -- Show a menu for command-line tab completion
-- File handling
vim.o.undofile = true -- Enable persistent undo (saves undo history in a file)
vim.o.swapfile = false -- Disable swap files (reduces disk writes)
vim.o.backup = false -- Disable file backups
vim.o.writebackup = false -- Disable backup before overwriting a file
vim.o.autoread = true -- Automatically reload files changed outside of Neovim
vim.o.hidden = true -- Allow switching buffers without saving
-- Split behavior
vim.o.splitright = true -- Open vertical splits to the right
vim.o.splitbelow = true -- Open horizontal splits below
vim.o.equalalways = false -- Prevent automatic resizing of splits
-- Clipboard
vim.o.clipboard = "unnamedplus" -- Use the system clipboard for all operations
-- Performance
vim.o.lazyredraw = true -- Redraw only when needed (improves performance)
vim.o.updatetime = 300 -- Time (ms) to trigger CursorHold events (lower = faster)
vim.o.timeoutlen = 500 -- Time (ms) to wait for a key sequence to complete
-- Completion
vim.o.completeopt = "menu,menuone,noselect" -- Customize the completion menu
vim.o.pumheight = 10 -- Limit the number of items in the popup menu
-- Folding
vim.o.foldmethod = "indent" -- Fold based on indentation levels
vim.o.foldlevel = 99 -- Start unfolded
vim.o.foldenable = true -- Enable folding by default
-- Backup directory (if enabled)
vim.o.backupdir = vim.fn.stdpath("data") .. "/backup//" -- Store backups in a custom directory
-- Mouse support
vim.o.mouse = "a" -- Enable mouse in all modes
-- Command-line
vim.o.cmdheight = 1 -- Height of the command-line area
vim.o.shortmess = vim.o.shortmess .. "c" -- Avoid showing extra messages during completion
-- Spell checking
vim.o.spell = false -- Disable spell checking by default
vim.o.spelllang = "en" -- Set spell check language to English
-- Wildmenu (command-line completion)
vim.o.wildignore = "*.o,*.obj,*.bak,*.exe,*.pyc,*.class" -- Ignore specific file types during file navigation
vim.o.wildmode = "longest:full,full" -- Customize command-line completion behavior
-- Terminal integration
vim.o.shell = "bash" -- Set the default shell
vim.o.shellcmdflag = "-c" -- Set shell command flag
-- Key mappings display
vim.o.showcmdloc = "statusline" -- Display key mappings in the status line
-- Diagnostics (LSP)
vim.diagnostic.config({
virtual_text = true, -- Show diagnostics as virtual text
signs = true, -- Show signs in the sign column
underline = true, -- Underline problematic code
update_in_insert = false, -- Don't update diagnostics while typing
})
-- Performance optimizations for large files
vim.cmd([[
augroup LargeFiles
autocmd!
autocmd BufReadPre * if getfsize(expand("<afile>")) > 1048576 | setlocal syntax=off | endif
augroup END
]])
-- Custom filetype settings
vim.cmd([[
augroup FileTypeSpecific
autocmd!
autocmd FileType markdown setlocal wrap spell
autocmd FileType python setlocal expandtab shiftwidth=4 tabstop=4
autocmd FileType go setlocal noexpandtab tabstop=8 shiftwidth=8
augroup END
]])

314
nvim/lua/vimwiki.lua Normal file
View File

@@ -0,0 +1,314 @@
-- Vimwiki Configuration
vim.cmd([[
set nocompatible
filetype plugin on
syntax on
]])
vim.g.vimwiki_list = {
{
path = '~/vimwiki/',
syntax = 'markdown',
ext = '.md',
diary_rel_path = 'Diary/'
}
}
local M = {}
-- Create a LaTeX file with a more advanced template
function M.create_latex()
local markdown_path = vim.fn.expand('%:p') -- Full path of the current markdown file
local tex_name = vim.fn.expand('%:t:r') .. ".tex" -- Base name with .tex extension
local tex_path = markdown_path:gsub('/[^/]*$', '') .. "/" .. tex_name -- Path to the new .tex file
-- Check if the .tex file already exists
if vim.fn.filereadable(tex_path) == 0 then
-- Define the LaTeX template as a table of lines
local template = {
"\\documentclass[12pt]{article}",
"\\usepackage{amsmath, amssymb, amsthm}",
"\\usepackage{graphicx}", -- For including images
"\\usepackage{hyperref}", -- For clickable links
"\\usepackage[margin=1in]{geometry}", -- Page margins
"\\usepackage{enumitem}", -- Better lists
"",
"\\title{Document Title}",
"\\author{Author Name}",
"\\date{\\today}",
"",
"\\begin{document}",
"",
"\\maketitle",
"",
"\\tableofcontents",
"\\newpage",
"",
"\\section{Introduction}",
"This is the introduction section.",
"",
"\\section{Main Content}",
"This is where the main content goes.",
"",
"\\section{Conclusion}",
"This is the conclusion.",
"",
"\\end{document}"
}
-- Write the template to the .tex file
local success, err = pcall(function()
vim.fn.writefile(template, tex_path)
end)
if not success then
print("Error writing LaTeX template to file: " .. err)
return
end
end
-- Open the newly created .tex file
vim.cmd('edit ' .. tex_path)
end
-- Delete key: Save the .tex file and return to the markdown file
function M.delete_latex()
local tex_path = vim.fn.expand('%:p') -- Get the current LaTeX file path
local markdown_path = tex_path:gsub('%.tex$', '.md') -- Replace .tex with .md
vim.cmd('write') -- Save the LaTeX file
vim.cmd('edit ' .. markdown_path) -- Open the corresponding markdown file
end
local zathura_pid = nil -- Global variable to store Zathura's actual PID
-- Helper function to get the PID of Zathura
local function get_zathura_pid(pdf_path)
local cmd = string.format("pgrep -f 'zathura %s'", pdf_path)
local pid = vim.fn.system(cmd):gsub("%s+", "") -- Get PID and trim whitespace
if pid == "" then
return nil
end
return tonumber(pid)
end
-- Stop monitoring and kill Zathura process
function M.stop_monitoring()
vim.cmd([[
augroup LatexLiveMonitor
autocmd!
augroup END
]])
if zathura_pid ~= nil then
print("Stopping Zathura (PID: " .. zathura_pid .. ")...")
local result = vim.fn.system({'kill', '-9', tostring(zathura_pid)})
if result ~= "" then
print("Zathura process stopped.")
else
print("Failed to kill Zathura process.")
end
zathura_pid = nil
else
print("No Zathura process to stop.")
end
end
-- Compile LaTeX and handle PDF
function M.compile_latex()
local tex_path = vim.fn.expand('%:p') -- Full path of the current .tex file
-- Ensure the current file is a .tex file
if not tex_path:match("%.tex$") then
print("This command can only be run inside a .tex file!")
return
end
-- Save the .tex file
vim.cmd('write')
-- Define paths
local pdf_name = vim.fn.expand('%:t:r') .. ".pdf" -- PDF file name
local root_vimwiki = vim.fn.expand(vim.g.vimwiki_list[1].path) -- Expand ~ to home directory
local supporting_files_path = root_vimwiki .. "/SupportingFiles/" -- SupportingFiles directory
local pdf_dest = supporting_files_path .. pdf_name -- Full destination path for the PDF
-- Ensure SupportingFiles directory exists
if vim.fn.isdirectory(supporting_files_path) == 0 then
vim.fn.mkdir(supporting_files_path, "p")
end
-- Compile LaTeX
local compile_cmd = 'pdflatex -output-directory=' .. vim.fn.shellescape(vim.fn.expand('%:p:h')) .. ' ' .. vim.fn.shellescape(tex_path)
vim.fn.system(compile_cmd)
-- Move the generated PDF to SupportingFiles
local compiled_pdf_path = vim.fn.expand('%:p:h') .. '/' .. pdf_name
if vim.fn.filereadable(compiled_pdf_path) == 1 then
local move_cmd = 'mv ' .. vim.fn.shellescape(compiled_pdf_path) .. ' ' .. vim.fn.shellescape(pdf_dest)
vim.fn.system(move_cmd)
else
print("Error: PDF file was not generated.")
return
end
-- Stop existing Zathura process if running
if zathura_pid ~= nil then
print("Stopping existing Zathura (PID: " .. zathura_pid .. ")...")
vim.fn.system({'kill', '-9', tostring(zathura_pid)})
zathura_pid = nil
end
-- Start Zathura and store its actual PID
vim.fn.jobstart({'zathura', pdf_dest}, {detach = true})
zathura_pid = get_zathura_pid(pdf_dest) -- Get the actual PID of Zathura
if zathura_pid == nil then
print("Failed to retrieve Zathura PID.")
return
end
print("Zathura started (PID: " .. zathura_pid .. ").")
-- Cleanup auxiliary files
M.cleanup_auxiliary_files()
-- Provide feedback to the user
print("LaTeX compiled successfully. PDF moved to SupportingFiles.")
end
-- Stop Zathura when exiting Neovim or closing the buffer
vim.cmd([[
augroup CloseZathuraOnExit
autocmd!
autocmd VimLeavePre * lua require('vimwiki').stop_monitoring()
autocmd BufWinLeave,BufDelete *.tex lua require('vimwiki').stop_monitoring()
augroup END
]])
-- Monitor file and recompile on Enter or `.`
function M.monitor_latex()
local tex_path = vim.fn.expand('%:p') -- Full path of the current .tex file
-- Ensure the current file is a .tex file
if not tex_path:match("%.tex$") then
print("This command can only be run inside a .tex file!")
return
end
-- Run the initial compile (no Zathura is opened here)
M.compile_latex_no_zathura()
-- Define paths
local pdf_name = vim.fn.expand('%:t:r') .. ".pdf" -- PDF file name
local root_vimwiki = vim.fn.expand(vim.g.vimwiki_list[1].path) -- Expand ~ to home directory
local supporting_files_path = root_vimwiki .. "/SupportingFiles/" -- SupportingFiles directory
local pdf_dest = supporting_files_path .. pdf_name -- Full destination path for the PDF
-- Ensure the PDF exists before starting monitoring
if vim.fn.filereadable(pdf_dest) == 0 then
print("Error: PDF file was not generated. Cannot start monitoring.")
return
end
-- Start Zathura and store its actual PID
if zathura_pid ~= nil then
vim.fn.system({'kill', '-9', tostring(zathura_pid)}) -- Kill the old Zathura process
end
vim.fn.jobstart({'zathura', pdf_dest}, {detach = true})
zathura_pid = get_zathura_pid(pdf_dest) -- Get the actual PID of Zathura
if zathura_pid == nil then
print("Failed to retrieve Zathura PID.")
return
end
print("Zathura started (PID: " .. zathura_pid .. ").")
-- Enter live monitoring mode
vim.cmd([[
augroup LatexLiveMonitor
autocmd!
" Trigger compilation on save
autocmd BufWritePost <buffer> lua require('vimwiki').compile_latex_no_zathura()
" Stop monitoring when the file is closed or buffer is unloaded
autocmd BufWinLeave,BufDelete <buffer> lua require('vimwiki').stop_monitoring()
augroup END
]])
-- Map Enter and . to save the file
vim.api.nvim_buf_set_keymap(0, 'i', '<CR>', '<CR><Esc>:w<CR>a', { noremap = true, silent = true })
vim.api.nvim_buf_set_keymap(0, 'i', '.', '.<Esc>:w<CR>a', { noremap = true, silent = true })
print("Live monitoring started. PDF will update on save.")
end
-- Compile LaTeX without opening Zathura
function M.compile_latex_no_zathura()
local tex_path = vim.fn.expand('%:p') -- Full path of the current .tex file
-- Ensure the current file is a .tex file
if not tex_path:match("%.tex$") then
return
end
-- Save the .tex file
vim.cmd('write')
-- Define paths
local pdf_name = vim.fn.expand('%:t:r') .. ".pdf" -- PDF file name
local root_vimwiki = vim.fn.expand(vim.g.vimwiki_list[1].path) -- Expand ~ to home directory
local supporting_files_path = root_vimwiki .. "/SupportingFiles/" -- SupportingFiles directory
local pdf_dest = supporting_files_path .. pdf_name -- Full destination path for the PDF
-- Compile LaTeX
local compile_cmd = 'pdflatex -output-directory=' .. vim.fn.shellescape(vim.fn.expand('%:p:h')) .. ' ' .. vim.fn.shellescape(tex_path)
vim.fn.system(compile_cmd)
-- Move the generated PDF to SupportingFiles
local compiled_pdf_path = vim.fn.expand('%:p:h') .. '/' .. pdf_name
if vim.fn.filereadable(compiled_pdf_path) == 1 then
vim.fn.system('mv ' .. vim.fn.shellescape(compiled_pdf_path) .. ' ' .. vim.fn.shellescape(pdf_dest))
else
print("Error: PDF file was not generated.")
return
end
-- Cleanup auxiliary files
M.cleanup_auxiliary_files()
end
-- Cleanup auxiliary files
function M.cleanup_auxiliary_files()
local tex_path = vim.fn.expand('%:p') -- Full path of the current .tex file
local tex_dir = vim.fn.expand('%:p:h') -- Directory containing the .tex file
local base_name = vim.fn.expand('%:t:r') -- Base name of the .tex file
-- Auxiliary files to clean
local aux_files = {
tex_dir .. "/" .. base_name .. ".log",
tex_dir .. "/" .. base_name .. ".aux",
tex_dir .. "/texput.log"
}
for _, file in ipairs(aux_files) do
if vim.fn.filereadable(file) == 1 then
vim.fn.delete(file)
end
end
end
-- Link PDF in the markdown file
function M.link_pdf()
local pdf_path = vim.fn.getreg('a') -- Get the PDF path from register "a"
local link = "[" .. vim.fn.expand('%:t:r') .. "](" .. pdf_path .. ")"
vim.fn.append(vim.fn.line('.'), link) -- Insert the link below the current line
print("PDF linked in markdown!")
end
-- Keybindings
vim.api.nvim_set_keymap('n', '<leader>lt', [[:lua require('vimwiki').create_latex()<CR>]], { noremap = true, silent = true })
vim.api.nvim_set_keymap('n', '<leader>ld', [[:lua require('vimwiki').stop_monitoring()<CR>:lua require('vimwiki').delete_latex()<CR>]], { noremap = true, silent = true })
vim.api.nvim_set_keymap('n', '<leader>lc', [[:lua require('vimwiki').compile_latex()<CR>]], { noremap = true, silent = true })
vim.api.nvim_set_keymap('n', '<leader>lm', [[:lua require('vimwiki').monitor_latex()<CR>]], { noremap = true, silent = true })
vim.api.nvim_set_keymap('n', '<leader>ll', [[:lua require('vimwiki').link_pdf()<CR>]], { noremap = true, silent = true })
return M

Submodule nvim/pack/nvim/start/cmp-bufffer added at 3022dbc916

Submodule nvim/pack/nvim/start/cmp-cmdline added at d250c63aa1

Submodule nvim/pack/nvim/start/cmp-nvim-lsp added at 99290b3ec1

Submodule nvim/pack/nvim/start/cmp-path added at 91ff86cd9c

Submodule nvim/pack/nvim/start/cmp_luasnip added at 98d9cb5c2c

Submodule nvim/pack/nvim/start/comment added at e30b7f2008

Submodule nvim/pack/nvim/start/gitsigns added at 3ec5fbd920

Submodule nvim/pack/nvim/start/gruvbox added at 68c3460a5d

Submodule nvim/pack/nvim/start/lualine added at 2a5bae9254

Submodule nvim/pack/nvim/start/luasnip added at c9b9a22904

Submodule nvim/pack/nvim/start/markdown-preview.nvim added at a923f5fc5b

Submodule nvim/pack/nvim/start/nvim-autopairs added at 3d02855468

Submodule nvim/pack/nvim/start/nvim-cmp added at 12509903a5

Submodule nvim/pack/nvim/start/nvim-lspconfig added at b4d65bce97

Submodule nvim/pack/nvim/start/nvim-treesitter added at 6587a58868

Submodule nvim/pack/nvim/start/nvim-web-devicons added at aafa5c187a

Submodule nvim/pack/nvim/start/plenary added at 3707cdb1e4

Submodule nvim/pack/nvim/start/telescope added at 415af52339

Submodule nvim/pack/nvim/start/vimwiki added at 72792615e7

Submodule nvim/pack/nvim/start/vimwiki-markdown added at 1b6007d528

Submodule nvim/pack/nvim/start/which-key added at 6cebd86917

View File

@@ -1,254 +0,0 @@
-- Automatically generated packer.nvim plugin loader code
if vim.api.nvim_call_function('has', {'nvim-0.5'}) ~= 1 then
vim.api.nvim_command('echohl WarningMsg | echom "Invalid Neovim version for packer.nvim! | echohl None"')
return
end
vim.api.nvim_command('packadd packer.nvim')
local no_errors, error_msg = pcall(function()
_G._packer = _G._packer or {}
_G._packer.inside_compile = true
local time
local profile_info
local should_profile = false
if should_profile then
local hrtime = vim.loop.hrtime
profile_info = {}
time = function(chunk, start)
if start then
profile_info[chunk] = hrtime()
else
profile_info[chunk] = (hrtime() - profile_info[chunk]) / 1e6
end
end
else
time = function(chunk, start) end
end
local function save_profiles(threshold)
local sorted_times = {}
for chunk_name, time_taken in pairs(profile_info) do
sorted_times[#sorted_times + 1] = {chunk_name, time_taken}
end
table.sort(sorted_times, function(a, b) return a[2] > b[2] end)
local results = {}
for i, elem in ipairs(sorted_times) do
if not threshold or threshold and elem[2] > threshold then
results[i] = elem[1] .. ' took ' .. elem[2] .. 'ms'
end
end
if threshold then
table.insert(results, '(Only showing plugins that took longer than ' .. threshold .. ' ms ' .. 'to load)')
end
_G._packer.profile_output = results
end
time([[Luarocks path setup]], true)
local package_path_str = "/home/klein/.cache/nvim/packer_hererocks/2.1.1716656478/share/lua/5.1/?.lua;/home/klein/.cache/nvim/packer_hererocks/2.1.1716656478/share/lua/5.1/?/init.lua;/home/klein/.cache/nvim/packer_hererocks/2.1.1716656478/lib/luarocks/rocks-5.1/?.lua;/home/klein/.cache/nvim/packer_hererocks/2.1.1716656478/lib/luarocks/rocks-5.1/?/init.lua"
local install_cpath_pattern = "/home/klein/.cache/nvim/packer_hererocks/2.1.1716656478/lib/lua/5.1/?.so"
if not string.find(package.path, package_path_str, 1, true) then
package.path = package.path .. ';' .. package_path_str
end
if not string.find(package.cpath, install_cpath_pattern, 1, true) then
package.cpath = package.cpath .. ';' .. install_cpath_pattern
end
time([[Luarocks path setup]], false)
time([[try_loadstring definition]], true)
local function try_loadstring(s, component, name)
local success, result = pcall(loadstring(s), name, _G.packer_plugins[name])
if not success then
vim.schedule(function()
vim.api.nvim_notify('packer.nvim: Error running ' .. component .. ' for ' .. name .. ': ' .. result, vim.log.levels.ERROR, {})
end)
end
return result
end
time([[try_loadstring definition]], false)
time([[Defining packer_plugins]], true)
_G.packer_plugins = {
["Comment.nvim"] = {
config = { "\27LJ\2\n5\0\0\3\0\3\0\0066\0\0\0'\2\1\0B\0\2\0029\0\2\0B\0\1\1K\0\1\0\nsetup\fComment\frequire\0" },
loaded = true,
path = "/home/klein/.local/share/nvim/site/pack/packer/start/Comment.nvim",
url = "https://github.com/numToStr/Comment.nvim"
},
LuaSnip = {
loaded = true,
path = "/home/klein/.local/share/nvim/site/pack/packer/start/LuaSnip",
url = "https://github.com/L3MON4D3/LuaSnip"
},
["cmp-buffer"] = {
loaded = true,
path = "/home/klein/.local/share/nvim/site/pack/packer/start/cmp-buffer",
url = "https://github.com/hrsh7th/cmp-buffer"
},
["cmp-cmdline"] = {
loaded = true,
path = "/home/klein/.local/share/nvim/site/pack/packer/start/cmp-cmdline",
url = "https://github.com/hrsh7th/cmp-cmdline"
},
["cmp-nvim-lsp"] = {
loaded = true,
path = "/home/klein/.local/share/nvim/site/pack/packer/start/cmp-nvim-lsp",
url = "https://github.com/hrsh7th/cmp-nvim-lsp"
},
["cmp-path"] = {
loaded = true,
path = "/home/klein/.local/share/nvim/site/pack/packer/start/cmp-path",
url = "https://github.com/hrsh7th/cmp-path"
},
cmp_luasnip = {
loaded = true,
path = "/home/klein/.local/share/nvim/site/pack/packer/start/cmp_luasnip",
url = "https://github.com/saadparwaiz1/cmp_luasnip"
},
["gitsigns.nvim"] = {
config = { "\27LJ\2\n6\0\0\3\0\3\0\0066\0\0\0'\2\1\0B\0\2\0029\0\2\0B\0\1\1K\0\1\0\nsetup\rgitsigns\frequire\0" },
loaded = true,
path = "/home/klein/.local/share/nvim/site/pack/packer/start/gitsigns.nvim",
url = "https://github.com/lewis6991/gitsigns.nvim"
},
gruvbox = {
config = { "\27LJ\2\n7\0\0\3\0\3\0\0056\0\0\0009\0\1\0'\2\2\0B\0\2\1K\0\1\0\24colorscheme gruvbox\bcmd\bvim\0" },
loaded = true,
path = "/home/klein/.local/share/nvim/site/pack/packer/start/gruvbox",
url = "https://github.com/morhetz/gruvbox"
},
["lualine.nvim"] = {
config = { "\27LJ\2\n<EFBFBD>\5\0\0\a\0\29\0)6\0\0\0'\2\1\0B\0\2\0029\0\2\0005\2\4\0005\3\3\0=\3\5\0025\3\a\0005\4\6\0=\4\b\0035\4\t\0005\5\n\0005\6\v\0=\6\f\5>\5\3\4=\4\r\0035\4\14\0=\4\15\0035\4\16\0=\4\17\0035\4\18\0=\4\19\0035\4\20\0=\4\21\3=\3\22\0025\3\23\0004\4\0\0=\4\b\0034\4\0\0=\4\r\0035\4\24\0=\4\15\0035\4\25\0=\4\17\3=\3\26\0024\3\0\0=\3\27\0024\3\0\0=\3\28\2B\0\2\1K\0\1\0\15extensions\ftabline\22inactive_sections\1\2\0\0\rlocation\1\2\0\0\rfilename\1\0\4\14lualine_c\0\14lualine_x\0\14lualine_b\0\14lualine_a\0\rsections\14lualine_z\1\2\0\0\rlocation\14lualine_y\1\2\0\0\rprogress\14lualine_x\1\4\0\0\rencoding\15fileformat\rfiletype\14lualine_c\1\2\0\0\rfilename\14lualine_b\fsources\1\3\0\0\rnvim_lsp\bcoc\1\2\1\0\16diagnostics\fsources\0\1\3\0\0\vbranch\tdiff\14lualine_a\1\0\6\14lualine_c\0\14lualine_z\0\14lualine_x\0\14lualine_y\0\14lualine_b\0\14lualine_a\0\1\2\0\0\tmode\foptions\1\0\5\foptions\0\rsections\0\15extensions\0\ftabline\0\22inactive_sections\0\1\0\4\25component_separators\6|\23section_separators\5\ntheme\fgruvbox\18icons_enabled\2\nsetup\flualine\frequire\0" },
loaded = true,
path = "/home/klein/.local/share/nvim/site/pack/packer/start/lualine.nvim",
url = "https://github.com/nvim-lualine/lualine.nvim"
},
["markdown-preview.nvim"] = {
loaded = true,
path = "/home/klein/.local/share/nvim/site/pack/packer/start/markdown-preview.nvim",
url = "https://github.com/iamcco/markdown-preview.nvim"
},
["nvim-autopairs"] = {
config = { "\27LJ\2\n@\0\0\3\0\3\0\a6\0\0\0'\2\1\0B\0\2\0029\0\2\0004\2\0\0B\0\2\1K\0\1\0\nsetup\19nvim-autopairs\frequire\0" },
loaded = true,
path = "/home/klein/.local/share/nvim/site/pack/packer/start/nvim-autopairs",
url = "https://github.com/windwp/nvim-autopairs"
},
["nvim-cmp"] = {
loaded = true,
path = "/home/klein/.local/share/nvim/site/pack/packer/start/nvim-cmp",
url = "https://github.com/hrsh7th/nvim-cmp"
},
["nvim-lspconfig"] = {
loaded = true,
path = "/home/klein/.local/share/nvim/site/pack/packer/start/nvim-lspconfig",
url = "https://github.com/neovim/nvim-lspconfig"
},
["nvim-tree.lua"] = {
config = { "\27LJ\2\n;\0\0\3\0\3\0\a6\0\0\0'\2\1\0B\0\2\0029\0\2\0004\2\0\0B\0\2\1K\0\1\0\nsetup\14nvim-tree\frequire\0" },
loaded = true,
path = "/home/klein/.local/share/nvim/site/pack/packer/start/nvim-tree.lua",
url = "https://github.com/kyazdani42/nvim-tree.lua"
},
["nvim-treesitter"] = {
config = { "\27LJ\2\n<EFBFBD>\2\0\0\5\0\14\0\0176\0\0\0'\2\1\0B\0\2\0029\0\2\0005\2\4\0005\3\3\0=\3\5\0025\3\6\0=\3\a\0025\3\b\0=\3\t\0025\3\v\0005\4\n\0=\4\f\3=\3\r\2B\0\2\1K\0\1\0\16textobjects\vselect\1\0\1\vselect\0\1\0\1\venable\2\26incremental_selection\1\0\1\venable\2\14highlight\1\0\1\venable\2\21ensure_installed\1\0\4\26incremental_selection\0\16textobjects\0\21ensure_installed\0\14highlight\0\1\5\0\0\ago\blua\vpython\trust\nsetup\28nvim-treesitter.configs\frequire\0" },
loaded = true,
path = "/home/klein/.local/share/nvim/site/pack/packer/start/nvim-treesitter",
url = "https://github.com/nvim-treesitter/nvim-treesitter"
},
["nvim-web-devicons"] = {
loaded = true,
path = "/home/klein/.local/share/nvim/site/pack/packer/start/nvim-web-devicons",
url = "https://github.com/kyazdani42/nvim-web-devicons"
},
["packer.nvim"] = {
loaded = true,
path = "/home/klein/.local/share/nvim/site/pack/packer/start/packer.nvim",
url = "https://github.com/wbthomason/packer.nvim"
},
["plenary.nvim"] = {
loaded = true,
path = "/home/klein/.local/share/nvim/site/pack/packer/start/plenary.nvim",
url = "https://github.com/nvim-lua/plenary.nvim"
},
["telescope.nvim"] = {
config = { "\27LJ\2\n;\0\0\3\0\3\0\a6\0\0\0'\2\1\0B\0\2\0029\0\2\0004\2\0\0B\0\2\1K\0\1\0\nsetup\14telescope\frequire\0" },
loaded = true,
path = "/home/klein/.local/share/nvim/site/pack/packer/start/telescope.nvim",
url = "https://github.com/nvim-telescope/telescope.nvim"
},
vimwiki = {
config = { "\27LJ\2\nw\0\0\3\0\4\0\a6\0\0\0009\0\1\0004\1\3\0005\2\3\0>\2\1\1=\1\2\0K\0\1\0\1\0\4\vsyntax\rmarkdown\19diary_rel_path\vdiary/\bext\b.md\tpath\15~/vimwiki/\17vimwiki_list\6g\bvim\0" },
loaded = true,
path = "/home/klein/.local/share/nvim/site/pack/packer/start/vimwiki",
url = "https://github.com/vimwiki/vimwiki"
},
["which-key.nvim"] = {
config = { "\27LJ\2\n7\0\0\3\0\3\0\0066\0\0\0'\2\1\0B\0\2\0029\0\2\0B\0\1\1K\0\1\0\nsetup\14which-key\frequire\0" },
loaded = true,
path = "/home/klein/.local/share/nvim/site/pack/packer/start/which-key.nvim",
url = "https://github.com/folke/which-key.nvim"
}
}
time([[Defining packer_plugins]], false)
-- Config for: Comment.nvim
time([[Config for Comment.nvim]], true)
try_loadstring("\27LJ\2\n5\0\0\3\0\3\0\0066\0\0\0'\2\1\0B\0\2\0029\0\2\0B\0\1\1K\0\1\0\nsetup\fComment\frequire\0", "config", "Comment.nvim")
time([[Config for Comment.nvim]], false)
-- Config for: telescope.nvim
time([[Config for telescope.nvim]], true)
try_loadstring("\27LJ\2\n;\0\0\3\0\3\0\a6\0\0\0'\2\1\0B\0\2\0029\0\2\0004\2\0\0B\0\2\1K\0\1\0\nsetup\14telescope\frequire\0", "config", "telescope.nvim")
time([[Config for telescope.nvim]], false)
-- Config for: gitsigns.nvim
time([[Config for gitsigns.nvim]], true)
try_loadstring("\27LJ\2\n6\0\0\3\0\3\0\0066\0\0\0'\2\1\0B\0\2\0029\0\2\0B\0\1\1K\0\1\0\nsetup\rgitsigns\frequire\0", "config", "gitsigns.nvim")
time([[Config for gitsigns.nvim]], false)
-- Config for: nvim-autopairs
time([[Config for nvim-autopairs]], true)
try_loadstring("\27LJ\2\n@\0\0\3\0\3\0\a6\0\0\0'\2\1\0B\0\2\0029\0\2\0004\2\0\0B\0\2\1K\0\1\0\nsetup\19nvim-autopairs\frequire\0", "config", "nvim-autopairs")
time([[Config for nvim-autopairs]], false)
-- Config for: nvim-treesitter
time([[Config for nvim-treesitter]], true)
try_loadstring("\27LJ\2\n<EFBFBD>\2\0\0\5\0\14\0\0176\0\0\0'\2\1\0B\0\2\0029\0\2\0005\2\4\0005\3\3\0=\3\5\0025\3\6\0=\3\a\0025\3\b\0=\3\t\0025\3\v\0005\4\n\0=\4\f\3=\3\r\2B\0\2\1K\0\1\0\16textobjects\vselect\1\0\1\vselect\0\1\0\1\venable\2\26incremental_selection\1\0\1\venable\2\14highlight\1\0\1\venable\2\21ensure_installed\1\0\4\26incremental_selection\0\16textobjects\0\21ensure_installed\0\14highlight\0\1\5\0\0\ago\blua\vpython\trust\nsetup\28nvim-treesitter.configs\frequire\0", "config", "nvim-treesitter")
time([[Config for nvim-treesitter]], false)
-- Config for: which-key.nvim
time([[Config for which-key.nvim]], true)
try_loadstring("\27LJ\2\n7\0\0\3\0\3\0\0066\0\0\0'\2\1\0B\0\2\0029\0\2\0B\0\1\1K\0\1\0\nsetup\14which-key\frequire\0", "config", "which-key.nvim")
time([[Config for which-key.nvim]], false)
-- Config for: nvim-tree.lua
time([[Config for nvim-tree.lua]], true)
try_loadstring("\27LJ\2\n;\0\0\3\0\3\0\a6\0\0\0'\2\1\0B\0\2\0029\0\2\0004\2\0\0B\0\2\1K\0\1\0\nsetup\14nvim-tree\frequire\0", "config", "nvim-tree.lua")
time([[Config for nvim-tree.lua]], false)
-- Config for: vimwiki
time([[Config for vimwiki]], true)
try_loadstring("\27LJ\2\nw\0\0\3\0\4\0\a6\0\0\0009\0\1\0004\1\3\0005\2\3\0>\2\1\1=\1\2\0K\0\1\0\1\0\4\vsyntax\rmarkdown\19diary_rel_path\vdiary/\bext\b.md\tpath\15~/vimwiki/\17vimwiki_list\6g\bvim\0", "config", "vimwiki")
time([[Config for vimwiki]], false)
-- Config for: lualine.nvim
time([[Config for lualine.nvim]], true)
try_loadstring("\27LJ\2\n<EFBFBD>\5\0\0\a\0\29\0)6\0\0\0'\2\1\0B\0\2\0029\0\2\0005\2\4\0005\3\3\0=\3\5\0025\3\a\0005\4\6\0=\4\b\0035\4\t\0005\5\n\0005\6\v\0=\6\f\5>\5\3\4=\4\r\0035\4\14\0=\4\15\0035\4\16\0=\4\17\0035\4\18\0=\4\19\0035\4\20\0=\4\21\3=\3\22\0025\3\23\0004\4\0\0=\4\b\0034\4\0\0=\4\r\0035\4\24\0=\4\15\0035\4\25\0=\4\17\3=\3\26\0024\3\0\0=\3\27\0024\3\0\0=\3\28\2B\0\2\1K\0\1\0\15extensions\ftabline\22inactive_sections\1\2\0\0\rlocation\1\2\0\0\rfilename\1\0\4\14lualine_c\0\14lualine_x\0\14lualine_b\0\14lualine_a\0\rsections\14lualine_z\1\2\0\0\rlocation\14lualine_y\1\2\0\0\rprogress\14lualine_x\1\4\0\0\rencoding\15fileformat\rfiletype\14lualine_c\1\2\0\0\rfilename\14lualine_b\fsources\1\3\0\0\rnvim_lsp\bcoc\1\2\1\0\16diagnostics\fsources\0\1\3\0\0\vbranch\tdiff\14lualine_a\1\0\6\14lualine_c\0\14lualine_z\0\14lualine_x\0\14lualine_y\0\14lualine_b\0\14lualine_a\0\1\2\0\0\tmode\foptions\1\0\5\foptions\0\rsections\0\15extensions\0\ftabline\0\22inactive_sections\0\1\0\4\25component_separators\6|\23section_separators\5\ntheme\fgruvbox\18icons_enabled\2\nsetup\flualine\frequire\0", "config", "lualine.nvim")
time([[Config for lualine.nvim]], false)
-- Config for: gruvbox
time([[Config for gruvbox]], true)
try_loadstring("\27LJ\2\n7\0\0\3\0\3\0\0056\0\0\0009\0\1\0'\2\2\0B\0\2\1K\0\1\0\24colorscheme gruvbox\bcmd\bvim\0", "config", "gruvbox")
time([[Config for gruvbox]], false)
_G._packer.inside_compile = false
if _G._packer.needs_bufread == true then
vim.cmd("doautocmd BufRead")
end
_G._packer.needs_bufread = false
if should_profile then save_profiles() end
end)
if not no_errors then
error_msg = error_msg:gsub('"', '\\"')
vim.api.nvim_command('echohl ErrorMsg | echom "Error in packer_compiled: '..error_msg..'" | echom "Please check your config for correctness" | echohl None')
end

287
nvim/scripts/new.sh Executable file
View File

@@ -0,0 +1,287 @@
#!/usr/bin/env bash
# Exit immediately if a command exits with a non-zero status,
# Treat unset variables as an error,
# Prevent errors in a pipeline from being masked
set -euo pipefail
# ============================
# Configuration Constants
# ============================
SOURCE_DIR="$HOME/vimwiki" # Source Vimwiki directory
TEMP_DIR="/tmp/vimwikihtml" # Temporary HTML output directory
CSS_FILE="$HOME/.local/share/nvim/style.css" # Path to the CSS file for styling
CONCURRENT_JOBS=4 # Number of concurrent pandoc processes
LOG_FILE="$TEMP_DIR/conversion.log" # Log file to track conversions
ERROR_LOG_FILE="$TEMP_DIR/error.log" # Log file to track errors
# ============================
# Dependencies
# ============================
DEPENDENCIES=("pandoc" "sed" "qutebrowser" "find" "rsync" "grep" "mkdir" "basename" "diff")
# ============================
# Function Definitions
# ============================
# Function to check if all dependencies are installed
check_dependencies() {
echo "Checking for required dependencies..."
for cmd in "${DEPENDENCIES[@]}"; do
if ! command -v "$cmd" &>/dev/null; then
echo "Error: '$cmd' is not installed. Please install it and retry."
exit 1
fi
done
echo "All dependencies are satisfied."
}
# Function to extract the title from Markdown using YAML frontmatter or fallback to filename
extract_title() {
local md_file="$1"
# Attempt to extract title from YAML frontmatter
local title
title=$(grep -m1 '^title:' "$md_file" | sed 's/title: //') || true
if [[ -z "$title" ]]; then
# If no title found, use the filename without extension
title=$(basename "$md_file" .md.old)
fi
echo "$title"
}
# Function to convert a single Markdown file to HTML
convert_md_to_html() {
local md_old_file="$1" # Path to the .md.old file
# Determine the relative path from TEMP_DIR
local relative_path="${md_old_file#$TEMP_DIR/}"
# Determine the output HTML file path
local html_file="$TEMP_DIR/${relative_path%.md.old}.html"
# Create the necessary directories for the HTML file
mkdir -p "$(dirname "$html_file")"
# Extract the title for the HTML document
local title
title=$(extract_title "$md_old_file")
echo "Converting '$md_old_file' to '$html_file'..."
# Use pandoc to convert Markdown to HTML with CSS and metadata
if [[ -f "$CSS_FILE" ]]; then
pandoc -f markdown -s --css="$CSS_FILE" --metadata title="$title" "$md_old_file" -o "$html_file" || {
echo "Error: Failed to convert '$md_old_file' to HTML." | tee -a "$ERROR_LOG_FILE"
return 1
}
else
echo "Warning: CSS file '$CSS_FILE' not found. Skipping CSS for '$md_old_file'."
pandoc -f markdown -s --metadata title="$title" "$md_old_file" -o "$html_file" || {
echo "Error: Failed to convert '$md_old_file' to HTML." | tee -a "$ERROR_LOG_FILE"
return 1
}
fi
# Debug: Print a snippet of the HTML file before running sed
echo "Snippet before sed in '$html_file':"
head -n 5 "$html_file"
echo "..."
# Adjust internal href links:
# 1. Replace href="path/to/file.md.old" with href="path/to/file.html"
# 2. Replace href="path/to/file" with href="path/to/file.html" only if 'file' has no extension
echo "Adjusting links in '$html_file'..."
# First, replace links ending with .md.old
if ! sed -i -E 's|(href=")([^"#:/]+(/[^"#:/]+)*)\.md\.old(")|\1\2.html\4|g' "$html_file"; then
echo "Error: Failed to adjust '.md.old' links in '$html_file'." | tee -a "$ERROR_LOG_FILE"
echo "Snippet around problematic area in '$html_file':" | tee -a "$ERROR_LOG_FILE"
grep -E 'href="[^"#:/]+\.md\.old"' "$html_file" | head -n 5 | tee -a "$ERROR_LOG_FILE" || true
return 1
fi
# Then, replace links without any extension
if ! sed -i -E 's|(href=")([^"#:/.]+(/[^"#:/.]+)*)(")|\1\2.html\4|g' "$html_file"; then
echo "Error: Failed to adjust extensionless links in '$html_file'." | tee -a "$ERROR_LOG_FILE"
echo "Snippet around problematic area in '$html_file':" | tee -a "$ERROR_LOG_FILE"
grep -E 'href="[^"#:/.]+"' "$html_file" | head -n 5 | tee -a "$ERROR_LOG_FILE" || true
return 1
fi
# Debug: Print a snippet of the HTML file after running sed
echo "Snippet after sed in '$html_file':"
head -n 5 "$html_file"
echo "..."
# Log the successful conversion
echo "$(date +"%Y-%m-%d %H:%M:%S") - Converted '$md_old_file' to '$html_file'." >> "$LOG_FILE"
echo "Successfully converted '$md_old_file' to '$html_file'."
}
# Function to synchronize Markdown files and relevant assets to TEMP_DIR
synchronize_markdown() {
echo "Synchronizing Markdown files and assets to '$TEMP_DIR'..."
# Use rsync to copy only .md, .pdf, and image files, excluding unwanted directories and files
rsync -av --delete \
--exclude='*.html' \
--exclude='*.sh' \
--exclude='.git/' \
--exclude='.gitignore' \
--exclude='*.bak' \
--include='*/' \
--include='*.md' \
--include='*.pdf' \
--include='*.png' \
--include='*.jpg' \
--include='*.jpeg' \
--include='*.gif' \
--exclude='*' \
"$SOURCE_DIR/" "$TEMP_DIR/" | grep '\.md$' || true
echo "Synchronization completed."
}
# Function to rename .md files to .md.old in TEMP_DIR
rename_md_files() {
echo "Renaming new or modified .md files to .md.old in '$TEMP_DIR'..."
# Find all .md files in TEMP_DIR
find "$TEMP_DIR" -type f -name '*.md' | while IFS= read -r md_file; do
# Determine the corresponding .md.old file
md_old_file="${md_file}.old"
# Determine the source .md file
source_md="$SOURCE_DIR/${md_file#$TEMP_DIR/}"
# Check if the .md.old file exists
if [[ ! -f "$md_old_file" ]]; then
# New file detected, copy to .md.old
cp "$source_md" "$md_old_file"
echo "New file detected. Copied '$source_md' to '$md_old_file'."
# Convert to HTML
convert_md_to_html "$md_old_file" &
else
# Compare the source .md with the existing .md.old
if ! diff -q "$source_md" "$md_old_file" &>/dev/null; then
# Files differ, update .md.old and reconvert
cp "$source_md" "$md_old_file"
echo "Modified file detected. Updated '$md_old_file' with changes from '$source_md'."
# Convert to HTML
convert_md_to_html "$md_old_file" &
else
echo "No changes detected for '$source_md'. Skipping conversion."
fi
fi
done
# Wait for all background conversions to finish
wait
echo "Renaming and conversion of new or modified .md files completed."
}
# Function to handle deletions: Remove .html files corresponding to deleted .md files
handle_deletions() {
echo "Handling deletions of Markdown files..."
# Find all .md.old files in TEMP_DIR
find "$TEMP_DIR" -type f -name '*.md.old' | while IFS= read -r md_old_file; do
# Determine the corresponding .md file in SOURCE_DIR
source_md="$SOURCE_DIR/${md_old_file#$TEMP_DIR/}"
source_md="${source_md%.md.old}.md"
# Check if the source .md file exists
if [[ ! -f "$source_md" ]]; then
# Corresponding .md file has been deleted, remove the .html file
html_file="${md_old_file%.md.old}.html"
if [[ -f "$html_file" ]]; then
rm "$html_file"
echo "Deleted '$html_file' as the source Markdown file no longer exists."
# Log the deletion
echo "$(date +"%Y-%m-%d %H:%M:%S") - Deleted '$html_file' due to source removal." >> "$LOG_FILE"
fi
# Optionally, remove the .md.old file itself
rm "$md_old_file"
echo "Removed obsolete '$md_old_file'."
fi
done
echo "Deletion handling completed."
}
# Function to generate index.html specifically
generate_index() {
local index_md_old="$TEMP_DIR/index.md.old"
local index_html="$TEMP_DIR/index.html"
if [[ ! -f "$index_md_old" ]]; then
echo "Error: 'index.md.old' not found in '$TEMP_DIR'."
exit 1
fi
echo "Generating 'index.html' from 'index.md.old'..."
# Convert the index.md.old file to HTML
convert_md_to_html "$index_md_old"
# Ensure index.html exists
if [[ ! -f "$index_html" ]]; then
echo "Error: Failed to generate 'index.html'."
exit 1
fi
echo "'index.html' generation completed."
}
# Function to open index.html in qutebrowser
open_browser() {
local index_file="$TEMP_DIR/index.html"
if [[ -f "$index_file" ]]; then
echo "Opening '$index_file' in qutebrowser..."
qutebrowser "$index_file" &
echo "Opened '$index_file' in qutebrowser."
else
echo "Error: '$index_file' does not exist. Please ensure it is generated correctly."
exit 1
fi
}
# Function to update synchronization and conversion based on differences
update_if_needed() {
# Synchronize new and updated files
synchronize_markdown
# Rename and convert new or modified .md files
rename_md_files
# Handle deletions
handle_deletions
# Generate index.html
generate_index
}
# ============================
# Main Script Execution
# ============================
main() {
check_dependencies
# Create TEMP_DIR if it doesn't exist
if [[ ! -d "$TEMP_DIR" ]]; then
echo "Temporary directory '$TEMP_DIR' does not exist. Creating and performing full synchronization."
mkdir -p "$TEMP_DIR"
synchronize_markdown
rename_md_files
handle_deletions
generate_index
else
echo "Temporary directory '$TEMP_DIR' already exists. Checking for updates."
update_if_needed
fi
open_browser
echo "All tasks completed successfully."
}
main

27
nvim/scripts/setup-plugins.sh Executable file
View File

@@ -0,0 +1,27 @@
#!/usr/bin/env bash
PREFIX="$HOME/.config/nvim/pack/nvim/start"
mkdir -p "$PREFIX"
git clone https://github.com/numToStr/Comment.nvim "$PREFIX/comment"
git clone https://github.com/ellisonleao/gruvbox.nvim "$PREFIX/gruvbox"
git clone https://github.com/nvim-lualine/lualine.nvim "$PREFIX/lualine"
git clone https://github.com/vimwiki/vimwiki "$PREFIX/vimwiki"
git clone https://github.com/lewis6991/gitsigns.nvim "$PREFIX/gitsigns"
git clone https://github.com/hrsh7th/nvim-cmp "$PREFIX/nvim-cmp"
git clone https://github.com/hrsh7th/cmp-nvim-lsp "$PREFIX/cmp-nvim-lsp"
git clone https://github.com/hrsh7th/cmp-buffer "$PREFIX/cmp-bufffer"
git clone https://github.com/hrsh7th/cmp-path "$PREFIX/cmp-path"
git clone https://github.com/hrsh7th/cmp-cmdline "$PREFIX/cmp-cmdline"
git clone https://github.com/saadparwaiz1/cmp_luasnip "$PREFIX/cmp_luasnip"
git clone https://github.com/L3MON4D3/LuaSnip "$PREFIX/luasnip"
git clone https://github.com/nvim-treesitter/nvim-treesitter "$PREFIX/nvim-treesitter"
git clone https://github.com/windwp/nvim-autopairs "$PREFIX/nvim-autopairs"
git clone https://github.com/folke/which-key.nvim "$PREFIX/which-key"
git clone https://github.com/nvim-tree/nvim-web-devicons "$PREFIX/nvim-web-devicons"
git clone https://github.com/nvim-lua/plenary.nvim "$PREFIX/plenary"
git clone https://github.com/nvim-treesitter/nvim-treesitter "$PREFIX/nvim-treesitter"
git clone https://github.com/neovim/nvim-lspconfig "$PREFIX/nvim-lspconfig"
git clone https://github.com/iamcco/markdown-preview.nvim "$PREFIX/markdown-preview"
git clone https://github.com/nvim-telescope/telescope.nvim "$PREFIX/telescope"
sudo apt install ripgrep

654
nvim/scripts/test.readme.md Normal file
View File

@@ -0,0 +1,654 @@
## **6. Improving the `extract_title` Function**
### **a. Handling Complex YAML Frontmatter**
**Issue:** YAML frontmatter can contain more complex structures, including quotes, multiple spaces, or special characters, which the current `extract_title` function might not handle correctly.
**Solution:** Enhance the title extraction to handle various YAML frontmatter formats more robustly.
**Implementation:**
```bash
extract_title() {
local md_file="$1"
# Extract title from YAML frontmatter, handling quotes and spaces
local title
title=$(awk '/^title:/ {
sub(/^title:\s*/, "");
gsub(/['"'"']/,"", $0);
print
exit
}' "$md_file") || true
if [[ -z "$title" ]]; then
# Fallback to filename without extension
title=$(basename "$md_file" .md.old)
fi
echo "$title"
}
```
**Explanation:**
- **`awk` Usage:**
- Searches for a line starting with `title:`.
- Removes the `title:` prefix and any leading whitespace.
- Removes surrounding quotes if present.
- Prints the title and exits to prevent processing the entire file.
---
## **10. Incorporating Configuration Flexibility**
### **Issue:**
Hardcoding paths and settings reduces the script's flexibility and adaptability to different environments.
### **Solution:**
Allow configuration through external files or command-line arguments, making the script more versatile.
**Implementation:**
1. **Using a Configuration File:**
- **Create a Config File (`config.sh`):**
```bash
#!/usr/bin/env bash
SOURCE_DIR="$HOME/vimwiki"
TEMP_DIR="/tmp/vimwikihtml"
CSS_FILE="$HOME/.local/share/nvim/style.css"
CONCURRENT_JOBS=4
LOG_FILE="$TEMP_DIR/conversion.log"
ERROR_LOG_FILE="$TEMP_DIR/error.log"
```
- **Source the Config File in the Script:**
```bash
# At the beginning of test.sh
if [[ -f "$HOME/vimwiki/config.sh" ]]; then
source "$HOME/vimwiki/config.sh"
else
echo "Error: Configuration file 'config.sh' not found in '$HOME/vimwiki/'."
exit 1
fi
```
2. **Using Command-Line Arguments:**
- **Parse Arguments Using `getopts`:**
```bash
while getopts "s:t:c:j:l:e:" opt; do
case $opt in
s) SOURCE_DIR="$OPTARG" ;;
t) TEMP_DIR="$OPTARG" ;;
c) CSS_FILE="$OPTARG" ;;
j) CONCURRENT_JOBS="$OPTARG" ;;
l) LOG_FILE="$OPTARG" ;;
e) ERROR_LOG_FILE="$OPTARG" ;;
*) echo "Invalid option"; exit 1 ;;
esac
done
```
- **Usage Example:**
```bash
./test.sh -s "/path/to/source" -t "/path/to/temp" -c "/path/to/style.css" -j 8
```
**Benefits:**
- **Flexibility:** Easily adapt to different directories, styles, and settings without modifying the script.
- **Reusability:** Share the script across different projects with varying configurations.
---
## **11. Adding a Dry-Run Mode for Safe Testing**
### **Issue:**
Making changes to files (like deletions) without a preview can be risky, especially during testing.
### **Solution:**
Implement a dry-run mode that simulates actions without performing them, allowing you to verify behavior before actual execution.
**Implementation:**
1. **Introduce a `DRY_RUN` Variable:**
```bash
DRY_RUN=false
```
2. **Parse a Command-Line Argument for Dry-Run:**
```bash
while getopts "s:t:c:j:l:e:dr" opt; do
case $opt in
# ... [existing options]
d) DRY_RUN=true ;;
r) # Any other options
;;
*) echo "Invalid option"; exit 1 ;;
esac
done
```
3. **Modify File Operations to Respect Dry-Run:**
```bash
handle_deletions() {
echo "Handling deletions of Markdown files..."
find "$TEMP_DIR" -type f -name '*.md.old' -print0 | while IFS= read -r -d '' md_old_file; do
source_md="$SOURCE_DIR/${md_old_file#$TEMP_DIR/}"
source_md="${source_md%.md.old}.md"
if [[ ! -f "$source_md" ]]; then
html_file="${md_old_file%.md.old}.html"
if [[ "$DRY_RUN" = true ]]; then
echo "Dry-Run: Would delete '$html_file' as the source Markdown file no longer exists."
else
if [[ -f "$html_file" ]]; then
rm "$html_file"
echo "Deleted '$html_file' as the source Markdown file no longer exists."
echo "$(date +"%Y-%m-%d %H:%M:%S") - Deleted '$html_file' due to source removal." >> "$LOG_FILE"
fi
rm "$md_old_file"
echo "Removed obsolete '$md_old_file'."
fi
fi
done
echo "Deletion handling completed."
}
```
4. **Inform Users When in Dry-Run Mode:**
```bash
if [[ "$DRY_RUN" = true ]]; then
echo "Running in Dry-Run mode. No changes will be made."
fi
```
**Benefits:**
- **Safety:** Allows you to verify what actions the script would take without making actual changes.
- **Testing:** Facilitates safe testing and debugging of the script's logic.
---
**Explanation:**
- **`trap cleanup SIGINT SIGTERM`:** Catches interrupt (`Ctrl+C`) and termination signals, invoking the `cleanup` function.
- **`cleanup` Function:**
- Echoes a message indicating interruption.
- Terminates all background jobs to prevent orphan processes.
- Exits the script with a non-zero status.
---
## **13. Providing User Feedback and Progress Indicators**
### **Issue:**
With large numbers of files, users might find it hard to gauge progress or know if the script is still running.
### **Solution:**
Implement progress indicators or verbose outputs to inform users about the script's status.
**Implementation:**
1. **Using Progress Bars with `pv`:**
**Install `pv`:**
```bash
sudo apt-get install pv
```
2. **Integrate `pv` into File Processing:**
```bash
convert_all_md_to_html() {
echo "Starting conversion of updated Markdown files to HTML..."
find "$TEMP_DIR" -type f -name '*.md.old' -print0 | pv -0 -l -s "$(find "$TEMP_DIR" -type f -name '*.md.old' | wc -l)" | parallel -0 -j "$CONCURRENT_JOBS" convert_md_to_html {}
echo "Markdown to HTML conversion completed."
}
```
3. **Simple Progress Indicators:**
Alternatively, use echo statements to indicate progress.
```bash
convert_md_to_html() {
local md_old_file="$1"
echo "Processing file: $md_old_file"
# ... [rest of the function]
}
```
**Benefits:**
- **User Awareness:** Users are informed about the script's progress, enhancing usability.
- **Debugging:** Progress indicators can help identify if the script is stuck or processing unusually.
---
## **14. Adding a Help Message and Usage Instructions**
### **Issue:**
Users might not be aware of available options or how to use the script effectively.
### **Solution:**
Implement a help message that outlines usage instructions and available options.
**Implementation:**
```bash
print_help() {
echo "Usage: $0 [options]"
echo ""
echo "Options:"
echo " -s DIR Source Vimwiki directory (default: \$HOME/vimwiki)"
echo " -t DIR Temporary HTML output directory (default: /tmp/vimwikihtml)"
echo " -c FILE Path to the CSS file for styling (default: \$HOME/.local/share/nvim/style.css)"
echo " -j NUM Number of concurrent pandoc processes (default: 4)"
echo " -l FILE Log file to track conversions (default: /tmp/vimwikihtml/conversion.log)"
echo " -e FILE Log file to track errors (default: /tmp/vimwikihtml/error.log)"
echo " -d Enable Dry-Run mode"
echo " -h Display this help message"
echo ""
echo "Example:"
echo " $0 -s ~/myvimwiki -t ~/htmloutput -c ~/styles/style.css -j 8"
}
```
Add argument parsing to handle the `-h` option:
```bash
# Parse command-line arguments
while getopts "s:t:c:j:l:e:dh" opt; do
case $opt in
s) SOURCE_DIR="$OPTARG" ;;
t) TEMP_DIR="$OPTARG" ;;
c) CSS_FILE="$OPTARG" ;;
j) CONCURRENT_JOBS="$OPTARG" ;;
l) LOG_FILE="$OPTARG" ;;
e) ERROR_LOG_FILE="$OPTARG" ;;
d) DRY_RUN=true ;;
h)
print_help
exit 0
;;
*)
echo "Invalid option."
print_help
exit 1
;;
esac
done
```
**Benefits:**
- **Usability:** Makes the script more user-friendly and accessible.
- **Documentation:** Provides immediate reference without external documentation.
---
## **17. Adding Verbose and Quiet Modes**
### **Issue:**
Sometimes users might prefer minimal output, especially when running the script in automated environments, while other times they might want detailed logs.
### **Solution:**
Implement verbose (`-v`) and quiet (`-q`) modes to control the script's output.
**Implementation:**
1. **Introduce Variables:**
```bash
VERBOSE=false
QUIET=false
```
2. **Update Argument Parsing:**
```bash
while getopts "s:t:c:j:l:e:dhvq" opt; do
case $opt in
# ... [existing options]
v) VERBOSE=true ;;
q) QUIET=true ;;
# ... [other options]
esac
done
```
3. **Modify Echo Statements:**
```bash
log() {
if [[ "$VERBOSE" = true && "$QUIET" = false ]]; then
echo "$@"
fi
}
# Replace echo with log where appropriate
log "Starting conversion of '$md_old_file'..."
```
4. **Suppress Non-Essential Output in Quiet Mode:**
```bash
if [[ "$QUIET" = true ]]; then
exec >/dev/null 2>&1
fi
```
**Benefits:**
- **Flexibility:** Users can choose the level of output based on their needs.
- **Usability:** Enhances the script's adaptability in different contexts.
---
## **18. Documentation and In-Line Comments**
### **Issue:**
As scripts grow in complexity, maintaining clear documentation and comments becomes essential for future maintenance and collaboration.
### **Solution:**
Add comprehensive in-line comments explaining the purpose and functionality of code sections and functions.
**Implementation:**
```bash
# Function to convert a single Markdown file to HTML
# Arguments:
# $1 - Path to the .md.old file
convert_md_to_html() {
# ... [function code]
}
```
**Recommendation:** Regularly update comments to reflect any changes in the script's logic or functionality.
---
## **19. Testing and Validation**
### **Issue:**
Ensuring that the script behaves as expected in various scenarios is crucial for reliability.
### **Solution:**
Implement automated tests or conduct thorough manual testing covering different cases like:
- Adding new files.
- Modifying existing files.
- Deleting files.
- Handling files with special characters.
- Running in dry-run mode.
- Handling permission issues.
**Implementation:**
1. **Automated Testing:**
- **Use Bash Unit Testing Frameworks:** Tools like [Bats](https://github.com/bats-core/bats-core) can help write automated tests for your script.
- **Example Test Case:**
```bash
@test "Convert new Markdown file to HTML" {
# Setup: Create a new Markdown file in SOURCE_DIR
echo "# Test Page" > "$SOURCE_DIR/test.md"
# Run the script
~/vimwiki/test.sh
# Assert: Check if HTML file exists
[ -f "$TEMP_DIR/test.html" ]
# Cleanup
rm "$SOURCE_DIR/test.md" "$TEMP_DIR/test.html" "$TEMP_DIR/test.md.old"
}
```
2. **Manual Testing:**
- **Create Test Files:** Set up various Markdown files with different content and filenames.
- **Run the Script:** Execute the script and verify the HTML output.
- **Check Logs:** Ensure that logs accurately reflect the actions taken.
- **Edge Cases:** Test with empty files, very large files, and files with complex YAML frontmatter.
**Benefits:**
- **Reliability:** Ensures that the script performs correctly under various conditions.
- **Confidence:** Builds trust in the script's functionality before deploying it in critical environments.
---
## **20. Additional Feature Suggestions**
### **a. Watch Mode for Real-Time Updates**
**Issue:**
Manually running the script after every change can be tedious.
**Solution:**
Implement a watch mode that monitors the source directory for changes and triggers the script automatically.
**Implementation:**
1. **Use `inotifywait`:**
**Install `inotify-tools`:**
```bash
sudo apt-get install inotify-tools
```
2. **Add a Watch Mode Option:**
```bash
WATCH_MODE=false
while getopts "s:t:c:j:l:e:dhvqw" opt; do
case $opt in
# ... [existing options]
w) WATCH_MODE=true ;;
# ... [other options]
esac
done
```
3. **Implement Watch Mode in the Script:**
```bash
watch_mode() {
echo "Entering Watch Mode. Monitoring '$SOURCE_DIR' for changes..."
while inotifywait -r -e modify,create,delete,move "$SOURCE_DIR"; do
echo "Change detected. Updating Vimwiki HTML..."
update_if_needed
open_browser
done
}
main() {
check_dependencies
if [[ "$WATCH_MODE" = true ]]; then
# Initial synchronization
if [[ ! -d "$TEMP_DIR" ]]; then
mkdir -p "$TEMP_DIR"
synchronize_markdown
rename_md_files
handle_deletions
generate_index
else
update_if_needed
fi
# Enter watch mode
watch_mode
else
# Regular execution
if [[ ! -d "$TEMP_DIR" ]]; then
echo "Temporary directory '$TEMP_DIR' does not exist. Creating and performing full synchronization."
mkdir -p "$TEMP_DIR"
synchronize_markdown
rename_md_files
handle_deletions
generate_index
else
echo "Temporary directory '$TEMP_DIR' already exists. Checking for updates."
update_if_needed
fi
open_browser
echo "All tasks completed successfully."
fi
}
```
**Benefits:**
- **Convenience:** Automatically keeps the HTML output up-to-date in real-time.
- **Efficiency:** Eliminates the need for manual script execution after each change.
**Note:** Watch mode can consume more resources. Use it judiciously based on your system's capabilities.
---
## **22. Implementing a Configuration Validation Step**
### **Issue:**
Incorrect configurations (like wrong directory paths) can lead to script failures or unintended behavior.
### **Solution:**
Validate configuration parameters at the beginning of the script to ensure they are correct.
**Implementation:**
```bash
validate_config() {
# Validate SOURCE_DIR
if [[ ! -d "$SOURCE_DIR" ]]; then
handle_error "Source directory '$SOURCE_DIR' does not exist."
exit "$EXIT_FAILURE"
fi
# Validate CSS_FILE
if [[ ! -f "$CSS_FILE" ]]; then
echo "Warning: CSS file '$CSS_FILE' does not exist. HTML files will be generated without styling."
fi
# Validate TEMP_DIR (already handled in main)
}
main() {
check_dependencies
validate_config
# ... [rest of the main function]
}
```
**Benefits:**
- **Prevention:** Catches configuration issues early, preventing the script from running with invalid settings.
- **User Guidance:** Provides clear error messages to help users correct configurations.
---
## **23. Utilizing `set -x` for Debugging**
### **Issue:**
Debugging complex scripts can be challenging without visibility into their execution flow.
### **Solution:**
Use Bash's debugging options to trace the script's execution when needed.
**Implementation:**
1. **Add a Verbose Flag (`-x`):**
```bash
set -euo pipefail
[[ "$VERBOSE" = true ]] && set -x
```
2. **Enable Verbose Mode via Command-Line Argument:**
```bash
while getopts "s:t:c:j:l:e:dhvqx" opt; do
case $opt in
# ... [existing options]
x) VERBOSE=true ;;
# ... [other options]
esac
done
```
**Benefits:**
- **Transparency:** Provides detailed execution logs for debugging purposes.
- **Control:** Users can enable or disable debugging as needed without modifying the script.
**Caution:** Ensure that verbose mode does not expose sensitive information, especially when handling logs.
---
## **24. Preventing Infinite Loops or Excessive Resource Consumption**
### **Issue:**
If the script triggers file changes (like converting `.md.old` to `.html`), it might inadvertently cause itself to detect changes, leading to infinite loops or excessive resource usage.
### **Solution:**
Ensure that the script only processes intended file types and excludes its own output from triggering further actions.
**Implementation:**
1. **Exclude `.html` and `.md.old` Files from Synchronization:**
- Already handled via `rsync` excludes.
2. **Ensure `handle_deletions` and Other Functions Do Not Modify Source Directory:**
- All file operations target the temporary directory, ensuring the source remains untouched.
3. **Double-Check `rsync` Include/Exclude Patterns:**
```bash
rsync -av --delete \
--exclude='*.html' \
--exclude='*.sh' \
--exclude='.git/' \
--exclude='.gitignore' \
--exclude='*.bak' \
--include='*/' \
--include='*.md' \
--include='*.pdf' \
--include='*.png' \
--include='*.jpg' \
--include='*.jpeg' \
--include='*.gif' \
--exclude='*' \
"$SOURCE_DIR/" "$TEMP_DIR/"
```
**Recommendation:** Regularly review and test the script to ensure it doesn't inadvertently process or modify unintended files.
---

435
nvim/scripts/test.sh.bak Executable file
View File

@@ -0,0 +1,435 @@
#!/usr/bin/env bash
# ============================
# Exit Status Codes
# ============================
EXIT_SUCCESS=0
EXIT_FAILURE=1
EXIT_DEPENDENCY=2
EXIT_CONVERSION=3
# ============================
# Shell Options
# ============================
# Exit immediately if a command exits with a non-zero status,
# Treat unset variables as an error,
# Prevent errors in a pipeline from being masked
set -euo pipefail
# ============================
# Color Definitions
# ============================
RESET='\033[0m' # No Color
BOLD='\033[1m'
# Regular Colors
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[0;33m'
BLUE='\033[0;34m'
CYAN='\033[0;36m'
# ============================
# Configuration Constants
# ============================
SOURCE_DIR="$HOME/vimwiki" # Source Vimwiki directory
TEMP_DIR="/tmp/vimwikihtml" # Temporary HTML output directory
CSS_FILE="$HOME/.local/share/nvim/style.css" # Path to the CSS file for styling
CONCURRENT_JOBS=4 # Number of concurrent pandoc processes
LOG_FILE="$TEMP_DIR/conversion.log" # Log file to track conversions
ERROR_LOG_FILE="$TEMP_DIR/error.log" # Log file to track errors
# ============================
# Dependencies
# ============================
DEPENDENCIES=("pandoc" "sed" "qutebrowser" "find" "rsync" "grep" "mkdir" "basename" "diff")
# ============================
# Logging Functions
# ============================
# Function to log INFO messages
log_info() {
local message="$1"
echo -e "${BLUE}[INFO]${RESET} $message"
echo "[INFO] $(date +"%Y-%m-%d %H:%M:%S") - $message" >> "$LOG_FILE"
}
# Function to log SUCCESS messages
log_success() {
local message="$1"
echo -e "${GREEN}[SUCCESS]${RESET} $message"
echo "[SUCCESS] $(date +"%Y-%m-%d %H:%M:%S") - $message" >> "$LOG_FILE"
}
# Function to log WARNING messages
log_warning() {
local message="$1"
echo -e "${YELLOW}[WARNING]${RESET} $message"
echo "[WARNING] $(date +"%Y-%m-%d %H:%M:%S") - $message" >> "$LOG_FILE"
}
# Function to log ERROR messages
log_error() {
local message="$1"
echo -e "${RED}[ERROR]${RESET} $message" | tee -a "$ERROR_LOG_FILE"
echo "[ERROR] $(date +"%Y-%m-%d %H:%M:%S") - $message" >> "$ERROR_LOG_FILE"
}
# Function to handle errors with context and exit with specific code
handle_error() {
local message="$1"
local exit_code="${2:-$EXIT_FAILURE}"
local func="${FUNCNAME[1]}"
local line="${BASH_LINENO[0]}"
log_error "In function '$func' at line $line: $message"
exit "$exit_code"
}
# ============================
# Filename Validation Function
# ============================
# Function to check if a filename contains only allowed characters
is_valid_filename() {
local filename="$1"
if [[ "$filename" =~ ^[A-Za-z0-9._-]+$ ]]; then
return 0
else
return 1
fi
}
# ============================
# Bash Availability Check
# ============================
# Function to ensure that Bash is available
check_bash() {
if ! command -v bash &>/dev/null; then
echo -e "${RED}[ERROR]${RESET} Bash is not installed. Please install Bash to run this script."
exit "$EXIT_FAILURE"
fi
}
# ============================
# Path Validation Function
# ============================
# Function to validate and sanitize input paths
validate_paths() {
log_info "Validating input paths..."
# Check that SOURCE_DIR is an absolute path
if [[ "$SOURCE_DIR" != /* ]]; then
handle_error "SOURCE_DIR ('$SOURCE_DIR') is not an absolute path." "$EXIT_FAILURE"
fi
# Check that TEMP_DIR is an absolute path and under /tmp
if [[ "$TEMP_DIR" != /tmp/* ]]; then
handle_error "TEMP_DIR ('$TEMP_DIR') must be under /tmp." "$EXIT_FAILURE"
fi
# Check that SOURCE_DIR exists and is a directory
if [[ ! -d "$SOURCE_DIR" ]]; then
handle_error "SOURCE_DIR ('$SOURCE_DIR') does not exist or is not a directory." "$EXIT_FAILURE"
fi
# Check if TEMP_DIR exists; if not, it will be created later
# If it exists, ensure it's a directory
if [[ -e "$TEMP_DIR" && ! -d "$TEMP_DIR" ]]; then
handle_error "TEMP_DIR ('$TEMP_DIR') exists but is not a directory." "$EXIT_FAILURE"
fi
log_success "Input paths are valid."
}
# ============================
# Function Definitions
# ============================
# Function to check if all dependencies are installed
check_dependencies() {
log_info "Checking for required dependencies..."
local missing_dependencies=()
for cmd in "${DEPENDENCIES[@]}"; do
if ! command -v "$cmd" &>/dev/null; then
missing_dependencies+=("$cmd")
fi
done
if [[ ${#missing_dependencies[@]} -ne 0 ]]; then
for cmd in "${missing_dependencies[@]}"; do
handle_error "Dependency '$cmd' is not installed. Please install it and retry." "$EXIT_DEPENDENCY"
done
fi
log_success "All dependencies are satisfied."
}
# Function to extract the title from Markdown using YAML frontmatter or fallback to filename
extract_title() {
local md_file="$1"
# Attempt to extract title from YAML frontmatter
local title
title=$(grep -m1 '^title:' "$md_file" | sed 's/title: //') || true
if [[ -z "$title" ]]; then
# If no title found, use the filename without extension
title=$(basename "$md_file" .md.old)
fi
echo "$title"
}
# Function to convert a single Markdown file to HTML atomically
convert_md_to_html() {
local md_old_file="$1" # Path to the .md.old file
# Determine the relative path from TEMP_DIR
local relative_path="${md_old_file#$TEMP_DIR/}"
# Remove .md.old extension
relative_path="${relative_path%.md.old}"
# Determine the output HTML file path
local html_file="$TEMP_DIR/${relative_path}.html"
# Determine the temporary HTML file path
local temp_html_file="${html_file}.tmp"
# Create the necessary directories for the HTML file
mkdir -p "$(dirname "$html_file")"
# Extract the title for the HTML document
local title
title=$(extract_title "$md_old_file")
log_info "Converting '$md_old_file' to '$html_file'..."
# Use pandoc to convert Markdown to HTML with CSS and metadata
if [[ -f "$CSS_FILE" ]]; then
if ! pandoc -f markdown -s --css="$CSS_FILE" --metadata title="$title" "$md_old_file" -o "$temp_html_file"; then
handle_error "Failed to convert '$md_old_file' to HTML." "$EXIT_CONVERSION"
fi
else
log_warning "CSS file '$CSS_FILE' not found. Skipping CSS for '$md_old_file'."
if ! pandoc -f markdown -s --metadata title="$title" "$md_old_file" -o "$temp_html_file"; then
handle_error "Failed to convert '$md_old_file' to HTML." "$EXIT_CONVERSION"
fi
fi
# Debug: Print a snippet of the HTML file before running sed
log_info "Snippet before sed in '$temp_html_file':"
head -n 5 "$temp_html_file" || true
echo "..."
# Adjust internal href links:
# 1. Replace href="path/to/file.md.old" with href="path/to/file.html"
# 2. Replace href="path/to/file" with href="path/to/file.html" only if 'file' has no extension
log_info "Adjusting links in '$html_file'..."
# First, replace links ending with .md.old
if ! sed -i -E 's|(href=")([^"#:/]+(/[^"#:/]+)*)\.md\.old(")|\1\2.html\4|g' "$temp_html_file"; then
handle_error "Failed to adjust '.md.old' links in '$temp_html_file'." "$EXIT_CONVERSION"
fi
# Then, replace links without any extension
if ! sed -i -E 's|(href=")([^"#:/.]+(/[^"#:/.]+)*)(")|\1\2.html\4|g' "$temp_html_file"; then
handle_error "Failed to adjust extensionless links in '$temp_html_file'." "$EXIT_CONVERSION"
fi
# Move the temporary HTML file to the final destination atomically
mv "$temp_html_file" "$html_file"
# Debug: Print a snippet of the HTML file after running sed
log_info "Snippet after sed in '$html_file':"
head -n 5 "$html_file" || true
echo "..."
# Log the successful conversion
echo "$(date +"%Y-%m-%d %H:%M:%S") - Converted '$md_old_file' to '$html_file'." >> "$LOG_FILE"
log_success "Successfully converted '$md_old_file' to '$html_file'."
}
# Function to synchronize Markdown files and relevant assets to TEMP_DIR
synchronize_markdown() {
log_info "Synchronizing Markdown files and assets to '$TEMP_DIR'..."
# Use rsync to copy only .md, .pdf, and image files, excluding unwanted directories and files
rsync -av --delete \
--exclude='*.html' \
--exclude='*.sh' \
--exclude='.git/' \
--exclude='.gitignore' \
--exclude='*.bak' \
--include='*/' \
--include='*.md' \
--include='*.pdf' \
--include='*.png' \
--include='*.jpg' \
--include='*.jpeg' \
--include='*.gif' \
--exclude='*' \
"$SOURCE_DIR/" "$TEMP_DIR/" | grep '\.md$' || true
log_success "Synchronization completed."
}
# Function to rename .md files to .md.old in TEMP_DIR
rename_md_files() {
log_info "Renaming new or modified .md files to .md.old in '$TEMP_DIR'..."
# Find all .md files in TEMP_DIR
find "$TEMP_DIR" -type f -name '*.md' | while IFS= read -r md_file; do
# Determine the corresponding .md.old file
md_old_file="${md_file}.old"
# Determine the source .md file
source_md="$SOURCE_DIR/${md_file#$TEMP_DIR/}"
# Check if the .md.old file exists
if [[ ! -f "$md_old_file" ]]; then
# New file detected, copy to .md.old
cp "$source_md" "$md_old_file"
log_info "New file detected. Copied '$source_md' to '$md_old_file'."
# Convert to HTML
convert_md_to_html "$md_old_file" &
else
# Compare the source .md with the existing .md.old
if ! diff -q "$source_md" "$md_old_file" &>/dev/null; then
# Files differ, update .md.old and reconvert
cp "$source_md" "$md_old_file"
log_info "Modified file detected. Updated '$md_old_file' with changes from '$source_md'."
# Convert to HTML
convert_md_to_html "$md_old_file" &
else
log_info "No changes detected for '$source_md'. Skipping conversion."
fi
fi
done
# Wait for all background conversions to finish
wait
log_success "Renaming and conversion of new or modified .md files completed."
}
# Function to handle deletions: Remove .html files corresponding to deleted .md files
handle_deletions() {
log_info "Handling deletions of Markdown files..."
# Find all .md.old files in TEMP_DIR
find "$TEMP_DIR" -type f -name '*.md.old' | while IFS= read -r md_old_file; do
# Determine the corresponding .md file in SOURCE_DIR
source_md="$SOURCE_DIR/${md_old_file#$TEMP_DIR/}"
source_md="${source_md%.md.old}.md"
# Check if the source .md file exists
if [[ ! -f "$source_md" ]]; then
# Corresponding .md file has been deleted, remove the .html file
html_file="${md_old_file%.md.old}.html"
if [[ -f "$html_file" ]]; then
rm "$html_file"
log_success "Deleted '$html_file' as the source Markdown file no longer exists."
# Log the deletion
echo "$(date +"%Y-%m-%d %H:%M:%S") - Deleted '$html_file' due to source removal." >> "$LOG_FILE"
fi
# Optionally, remove the .md.old file itself
rm "$md_old_file"
log_info "Removed obsolete '$md_old_file'."
fi
done
log_success "Deletion handling completed."
}
# Function to generate index.html specifically
generate_index() {
local index_md_old="$TEMP_DIR/index.md.old"
local index_html="$TEMP_DIR/index.html"
if [[ ! -f "$index_md_old" ]]; then
handle_error "'index.md.old' not found in '$TEMP_DIR'." "$EXIT_FAILURE"
fi
log_info "Generating 'index.html' from 'index.md.old'..."
# Convert the index.md.old file to HTML
convert_md_to_html "$index_md_old"
# Ensure index.html exists
if [[ ! -f "$index_html" ]]; then
handle_error "Failed to generate 'index.html'." "$EXIT_CONVERSION"
fi
log_success "'index.html' generation completed."
}
# Function to open index.html in qutebrowser
open_browser() {
local index_file="$TEMP_DIR/index.html"
if [[ -f "$index_file" ]]; then
log_info "Opening '$index_file' in qutebrowser..."
qutebrowser "$index_file" &
log_success "Opened '$index_file' in qutebrowser."
else
handle_error "'$index_file' does not exist. Please ensure it is generated correctly." "$EXIT_FAILURE"
fi
}
# Function to update synchronization and conversion based on differences
update_if_needed() {
# Synchronize new and updated files
synchronize_markdown
# Rename and convert new or modified .md files
rename_md_files
# Handle deletions
handle_deletions
# Generate index.html
generate_index
}
# ============================
# Signal Handling Functions
# ============================
# Function to handle script termination gracefully
cleanup() {
log_warning "Script interrupted. Cleaning up..."
# Terminate all background jobs
jobs -rp | xargs -r kill -TERM 2>/dev/null || true
exit "$EXIT_FAILURE"
}
# Trap SIGINT and SIGTERM signals
trap cleanup SIGINT SIGTERM
# ============================
# Main Script Execution
# ============================
main() {
# Ensure that Bash is available
check_bash
check_dependencies
validate_paths
# Create TEMP_DIR if it doesn't exist
if [[ ! -d "$TEMP_DIR" ]]; then
log_info "Temporary directory '$TEMP_DIR' does not exist. Creating and performing full synchronization."
mkdir -p "$TEMP_DIR"
synchronize_markdown
rename_md_files
handle_deletions
generate_index
else
log_info "Temporary directory '$TEMP_DIR' already exists. Checking for updates."
update_if_needed
fi
open_browser
log_success "All tasks completed successfully."
exit "$EXIT_SUCCESS"
}
# Invoke the main function
main

View File

@@ -0,0 +1,575 @@
#!/usr/bin/env bash
# ============================
# Exit Status Codes
# ============================
EXIT_SUCCESS=0
EXIT_FAILURE=1
EXIT_DEPENDENCY=2
EXIT_CONVERSION=3
# ============================
# Shell Options
# ============================
# Exit immediately if a command exits with a non-zero status,
# Treat unset variables as an error,
# Prevent errors in a pipeline from being masked
set -euo pipefail
# ============================
# Color Definitions
# ============================
RESET='\033[0m' # No Color
BOLD='\033[1m'
# Regular Colors
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[0;33m'
BLUE='\033[0;34m'
CYAN='\033[0;36m'
# ============================
# Configuration Constants
# ============================
# Absolute paths ensure the script can be run from any directory
SOURCE_DIR="$HOME/vimwiki" # Source Vimwiki directory
TEMP_DIR="/tmp/vimwikihtml" # Temporary HTML output directory
mkdir -p $TEMP_DIR
CSS_FILE="$HOME/.local/share/nvim/style.css" # Path to the CSS file for styling
CONCURRENT_JOBS=4 # Number of concurrent pandoc processes
LOG_FILE="$TEMP_DIR/conversion.log" # Log file to track conversions
touch $LOG_FILE
ERROR_LOG_FILE="$TEMP_DIR/error.log" # Log file to track errors
VERSION="1.0.0" # Script version
# ============================
# Dependencies
# ============================
DEPENDENCIES=("pandoc" "sed" "qutebrowser" "find" "rsync" "grep" "mkdir" "basename" "diff")
# ============================
# Logging Functions
# ============================
# Function to log INFO messages
log_info() {
local message="$1"
echo -e "${BLUE}[INFO]${RESET} $message"
echo "[INFO] $(date +"%Y-%m-%d %H:%M:%S") - $message" >> "$LOG_FILE"
}
# Function to log SUCCESS messages
log_success() {
local message="$1"
echo -e "${GREEN}[SUCCESS]${RESET} $message"
echo "[SUCCESS] $(date +"%Y-%m-%d %H:%M:%S") - $message" >> "$LOG_FILE"
}
# Function to log WARNING messages
log_warning() {
local message="$1"
echo -e "${YELLOW}[WARNING]${RESET} $message"
echo "[WARNING] $(date +"%Y-%m-%d %H:%M:%S") - $message" >> "$LOG_FILE"
}
# Function to log ERROR messages
log_error() {
local message="$1"
echo -e "${RED}[ERROR]${RESET} $message" | tee -a "$ERROR_LOG_FILE"
echo "[ERROR] $(date +"%Y-%m-%d %H:%M:%S") - $message" >> "$ERROR_LOG_FILE"
}
# Function to handle errors with context and exit with specific code
handle_error() {
local message="$1"
local exit_code="${2:-$EXIT_FAILURE}"
local func="${FUNCNAME[1]}"
local line="${BASH_LINENO[0]}"
log_error "In function '$func' at line $line: $message"
exit "$exit_code"
}
# ============================
# Filename Validation Function
# ============================
# Function to check if a filename contains only allowed characters
is_valid_filename() {
local filename="$1"
if [[ "$filename" =~ ^[A-Za-z0-9._-]+$ ]]; then
return 0
else
return 1
fi
}
# ============================
# Bash Availability Check
# ============================
# Function to ensure that Bash is available
check_bash() {
if ! command -v bash &>/dev/null; then
echo -e "${RED}[ERROR]${RESET} Bash is not installed. Please install Bash to run this script."
exit "$EXIT_FAILURE"
fi
}
# ============================
# Path Validation Function
# ============================
# Function to validate and sanitize input paths
validate_paths() {
log_info "Validating input paths..."
# Check that SOURCE_DIR is an absolute path
if [[ "$SOURCE_DIR" != /* ]]; then
handle_error "SOURCE_DIR ('$SOURCE_DIR') is not an absolute path." "$EXIT_FAILURE"
fi
# Check that TEMP_DIR is an absolute path and under /tmp
if [[ "$TEMP_DIR" != /tmp/* ]]; then
handle_error "TEMP_DIR ('$TEMP_DIR') must be under /tmp." "$EXIT_FAILURE"
fi
# Check that SOURCE_DIR exists and is a directory
if [[ ! -d "$SOURCE_DIR" ]]; then
handle_error "SOURCE_DIR ('$SOURCE_DIR') does not exist or is not a directory." "$EXIT_FAILURE"
fi
# Check if TEMP_DIR exists; if not, it will be created later
# If it exists, ensure it's a directory
if [[ -e "$TEMP_DIR" && ! -d "$TEMP_DIR" ]]; then
handle_error "TEMP_DIR ('$TEMP_DIR') exists but is not a directory." "$EXIT_FAILURE"
fi
log_success "Input paths are valid."
}
# ============================
# Function Definitions
# ============================
# Function to check if all dependencies are installed
check_dependencies() {
log_info "Checking for required dependencies..."
local missing_dependencies=()
for cmd in "${DEPENDENCIES[@]}"; do
if ! command -v "$cmd" &>/dev/null; then
missing_dependencies+=("$cmd")
fi
done
if [[ ${#missing_dependencies[@]} -ne 0 ]]; then
for cmd in "${missing_dependencies[@]}"; do
handle_error "Dependency '$cmd' is not installed. Please install it and retry." "$EXIT_DEPENDENCY"
done
fi
log_success "All dependencies are satisfied."
}
# Function to extract the title from Markdown using YAML frontmatter or fallback to filename
extract_title() {
local md_file="$1"
# Attempt to extract title from YAML frontmatter
local title
title=$(grep -m1 '^title:' "$md_file" | sed 's/title: //') || true
if [[ -z "$title" ]]; then
# If no title found, use the filename without extension
title=$(basename "$md_file" .md.old)
fi
echo "$title"
}
# Function to convert a single Markdown file to HTML atomically
convert_md_to_html() {
local md_old_file="$1" # Path to the .md.old file
# Determine the relative path from TEMP_DIR
local relative_path="${md_old_file#$TEMP_DIR/}"
# Remove .md.old extension
relative_path="${relative_path%.md.old}"
# Determine the output HTML file path
local html_file="$TEMP_DIR/${relative_path}.html"
# Determine the temporary HTML file path
local temp_html_file="${html_file}.tmp"
# Create the necessary directories for the HTML file
mkdir -p "$(dirname "$html_file")"
# Extract the title for the HTML document
local title
title=$(extract_title "$md_old_file")
log_info "Converting '$md_old_file' to '$html_file'..."
# Use pandoc to convert Markdown to HTML with CSS and metadata
if [[ -f "$CSS_FILE" ]]; then
if ! pandoc -f markdown -s --css="$CSS_FILE" --metadata title="$title" "$md_old_file" -o "$temp_html_file"; then
handle_error "Failed to convert '$md_old_file' to HTML." "$EXIT_CONVERSION"
fi
else
log_warning "CSS file '$CSS_FILE' not found. Skipping CSS for '$md_old_file'."
if ! pandoc -f markdown -s --metadata title="$title" "$md_old_file" -o "$temp_html_file"; then
handle_error "Failed to convert '$md_old_file' to HTML." "$EXIT_CONVERSION"
fi
fi
# Debug: Print a snippet of the HTML file before running sed
log_info "Snippet before sed in '$temp_html_file':"
head -n 5 "$temp_html_file" || true
echo "..."
# Adjust internal href links:
# 1. Replace href="path/to/file.md.old" with href="path/to/file.html"
# 2. Replace href="path/to/file" with href="path/to/file.html" only if 'file' has no extension
log_info "Adjusting links in '$html_file'..."
# First, replace links ending with .md.old
if ! sed -i -E 's|(href=")([^"#:/]+(/[^"#:/]+)*)\.md\.old(")|\1\2.html\4|g' "$temp_html_file"; then
handle_error "Failed to adjust '.md.old' links in '$temp_html_file'." "$EXIT_CONVERSION"
fi
# Then, replace links without any extension
if ! sed -i -E 's|(href=")([^"#:/.]+(/[^"#:/.]+)*)(")|\1\2.html\4|g' "$temp_html_file"; then
handle_error "Failed to adjust extensionless links in '$temp_html_file'." "$EXIT_CONVERSION"
fi
# Adjust src attributes for images to prepend /tmp/vimwikihtml
log_info "Adjusting image paths in '$temp_html_file'..."
if ! sed -i -E 's|(<img[^>]*src=")(/[^"]*)(")|\1/tmp/vimwikihtml\2\3|g' "$temp_html_file"; then
handle_error "Failed to adjust image paths in '$temp_html_file'." "$EXIT_CONVERSION"
fi
# Move the temporary HTML file to the final destination atomically
mv "$temp_html_file" "$html_file"
# Debug: Print a snippet of the HTML file after running sed
log_info "Snippet after sed in '$html_file':"
head -n 5 "$html_file" || true
echo "..."
# Log the successful conversion
echo "$(date +"%Y-%m-%d %H:%M:%S") - Converted '$md_old_file' to '$html_file'." >> "$LOG_FILE"
log_success "Successfully converted '$md_old_file' to '$html_file'."
}
# Function to synchronize Markdown files and relevant assets to TEMP_DIR
synchronize_markdown() {
log_info "Synchronizing Markdown files and assets to '$TEMP_DIR'..."
# Use rsync to copy only .md, .pdf, and image files, excluding unwanted directories and files
rsync -av --delete \
--exclude='*.html' \
--exclude='*.sh' \
--exclude='.git/' \
--exclude='.gitignore' \
--exclude='*.bak' \
--exclude='*.tex' \
--exclude='*.toc' \
--exclude='*.out' \
--include='*/' \
--include='*.md' \
--include='*.pdf' \
--include='*.png' \
--include='*.jpg' \
--include='*.jpeg' \
--include='*.gif' \
--exclude='*' \
"$SOURCE_DIR/" "$TEMP_DIR/" | grep '\.md$' || true
log_success "Synchronization completed."
}
# Function to rename .md files to .md.old in TEMP_DIR
rename_md_files() {
log_info "Renaming new or modified .md files to .md.old in '$TEMP_DIR'..."
# Find all .md files in TEMP_DIR
find "$TEMP_DIR" -type f -name '*.md' | while IFS= read -r md_file; do
# Determine the corresponding .md.old file
md_old_file="${md_file}.old"
# Determine the source .md file
source_md="$SOURCE_DIR/${md_file#$TEMP_DIR/}"
# Check if the .md.old file exists
if [[ ! -f "$md_old_file" ]]; then
# New file detected, copy to .md.old
cp "$source_md" "$md_old_file"
log_info "New file detected. Copied '$source_md' to '$md_old_file'."
# Convert to HTML
convert_md_to_html "$md_old_file" &
else
# Compare the source .md with the existing .md.old
if ! diff -q "$source_md" "$md_old_file" &>/dev/null; then
# Files differ, update .md.old and reconvert
cp "$source_md" "$md_old_file"
log_info "Modified file detected. Updated '$md_old_file' with changes from '$source_md'."
# Convert to HTML
convert_md_to_html "$md_old_file" &
else
log_info "No changes detected for '$source_md'. Skipping conversion."
fi
fi
done
# Wait for all background conversions to finish
wait
log_success "Renaming and conversion of new or modified .md files completed."
}
# Function to handle deletions: Remove .html files corresponding to deleted .md files
handle_deletions() {
log_info "Handling deletions of Markdown files..."
# Find all .md.old files in TEMP_DIR
find "$TEMP_DIR" -type f -name '*.md.old' | while IFS= read -r md_old_file; do
# Determine the corresponding .md file in SOURCE_DIR
source_md="$SOURCE_DIR/${md_old_file#$TEMP_DIR/}"
source_md="${source_md%.md.old}.md"
# Check if the source .md file exists
if [[ ! -f "$source_md" ]]; then
# Corresponding .md file has been deleted, remove the .html file
html_file="${md_old_file%.md.old}.html"
if [[ -f "$html_file" ]]; then
rm "$html_file"
log_success "Deleted '$html_file' as the source Markdown file no longer exists."
# Log the deletion
echo "$(date +"%Y-%m-%d %H:%M:%S") - Deleted '$html_file' due to source removal." >> "$LOG_FILE"
fi
# Remove the .md.old file itself
rm "$md_old_file"
log_info "Removed obsolete '$md_old_file'."
fi
done
log_success "Deletion handling completed."
}
# Function to generate index.html specifically
generate_index() {
local index_md_old="$TEMP_DIR/index.md.old"
local index_html="$TEMP_DIR/index.html"
if [[ ! -f "$index_md_old" ]]; then
handle_error "'index.md.old' not found in '$TEMP_DIR'." "$EXIT_FAILURE"
fi
log_info "Generating 'index.html' from 'index.md.old'..."
# Convert the index.md.old file to HTML
convert_md_to_html "$index_md_old"
# Ensure index.html exists
if [[ ! -f "$index_html" ]]; then
handle_error "Failed to generate 'index.html'." "$EXIT_CONVERSION"
fi
log_success "'index.html' generation completed."
}
# Function to open index.html in qutebrowser
open_browser() {
local index_file="$TEMP_DIR/index.html"
if [[ -f "$index_file" ]]; then
log_info "Opening '$index_file' in qutebrowser..."
qutebrowser "$index_file" &
log_success "Opened '$index_file' in qutebrowser."
else
handle_error "'$index_file' does not exist. Please ensure it is generated correctly." "$EXIT_FAILURE"
fi
}
# Function to display usage information
usage() {
echo -e "${BOLD}Usage:${RESET} $0 [OPTIONS]"
echo ""
echo "Options:"
echo " --index-wiki, -iw Synchronize and convert Vimwiki to HTML, then open index.html in qutebrowser."
echo " --help, -h Display this help message."
echo " --version, -V Display the script's version."
echo ""
echo "Examples:"
echo " $0 --index-wiki"
echo " $0 -iw"
echo " $0 --help"
echo " $0 -h"
echo " $0 --version"
echo " $0 -V"
}
# Function to display version information
version_info() {
echo -e "${BOLD}Vimwiki HTML Converter${RESET} version ${GREEN}$VERSION${RESET}"
}
# Function to update synchronization and conversion based on differences
update_if_needed() {
# Synchronize new and updated files
synchronize_markdown
# Rename and convert new or modified .md files
rename_md_files
# Handle deletions
handle_deletions
# Generate index.html
generate_index
}
# ============================
# Signal Handling Functions
# ============================
# Function to handle script termination gracefully
cleanup() {
log_warning "Script interrupted. Cleaning up..."
# Terminate all background jobs
jobs -rp | xargs -r kill -TERM 2>/dev/null || true
exit "$EXIT_FAILURE"
}
# Trap SIGINT and SIGTERM signals
trap cleanup SIGINT SIGTERM
# ============================
# convert_single_file Function
# ============================
convert_single_file() {
local md_file="$1"
# Validate filename
local filename
filename=$(basename "$md_file")
if ! is_valid_filename "$filename"; then
log_warning "Skipping file with invalid filename: '$md_file'"
return
fi
# Check if the file exists
if [[ ! -f "$md_file" ]]; then
handle_error "File '$md_file' does not exist." "$EXIT_FAILURE"
fi
# Copy the Markdown file to the temporary directory
local dest_dir="/tmp/markdowndump/"
mkdir -p "$dest_dir"
local dest_md_file="$dest_dir$filename"
cp "$md_file" "$dest_md_file"
log_info "Copied '$md_file' to '$dest_md_file'."
# Prepare the paths for HTML conversion
local html_file="${dest_md_file%.md}.html"
local temp_html_file="${html_file}.tmp"
# Extract the title for the HTML document
local title
title=$(extract_title "$dest_md_file")
# Convert Markdown to HTML using pandoc with CSS and metadata
if [[ -f "$CSS_FILE" ]]; then
if ! pandoc -f markdown -s --css="$CSS_FILE" --metadata title="$title" "$dest_md_file" -o "$temp_html_file"; then
handle_error "Failed to convert '$dest_md_file' to HTML." "$EXIT_CONVERSION"
fi
else
log_warning "CSS file '$CSS_FILE' not found. Skipping CSS for '$dest_md_file'."
if ! pandoc -f markdown -s --metadata title="$title" "$dest_md_file" -o "$temp_html_file"; then
handle_error "Failed to convert '$dest_md_file' to HTML." "$EXIT_CONVERSION"
fi
fi
# Move the temporary HTML file to the final destination
mv "$temp_html_file" "$html_file"
log_info "Converted Markdown file '$dest_md_file' to HTML '$html_file'."
# Open the HTML file in qutebrowser
qutebrowser "$html_file" &
log_success "Opened '$html_file' in qutebrowser."
}
# ============================
# Main Script Execution
# ============================
main() {
# Parse command-line arguments
if [[ $# -eq 0 ]]; then
log_error "No arguments provided. Use --help for usage information."
exit "$EXIT_FAILURE"
fi
while [[ $# -gt 0 ]]; do
case "$1" in
--index-wiki|-iw)
action="index_wiki"
shift
;;
--convert|-c)
if [[ -z "${2:-}" ]]; then
echo -e "${RED}[ERROR]${RESET} --convert flag requires a filename argument."
exit "$EXIT_FAILURE"
fi
convert_single_file "$2"
exit "$EXIT_SUCCESS"
;;
--help)
usage
exit "$EXIT_SUCCESS"
;;
-h)
usage
exit "$EXIT_SUCCESS"
;;
--version)
version_info
exit "$EXIT_SUCCESS"
;;
-V)
version_info
exit "$EXIT_SUCCESS"
;;
*)
log_error "Unknown option: $1. Use --help for usage information."
exit "$EXIT_FAILURE"
;;
esac
done
# Execute based on the action
case "$action" in
index_wiki)
check_bash
check_dependencies
validate_paths
# Create TEMP_DIR if it doesn't exist
if [[ ! -d "$TEMP_DIR" ]]; then
log_info "Temporary directory '$TEMP_DIR' does not exist. Creating and performing full synchronization."
mkdir -p "$TEMP_DIR"
synchronize_markdown
rename_md_files
handle_deletions
generate_index
else
log_info "Temporary directory '$TEMP_DIR' already exists. Checking for updates."
update_if_needed
fi
open_browser
log_success "All tasks completed successfully."
exit "$EXIT_SUCCESS"
;;
*)
# This should not happen due to earlier checks
log_error "Invalid action." "$EXIT_FAILURE"
;;
esac
}
# Invoke the main function
main "$@"

View File

@@ -0,0 +1,501 @@
#!/usr/bin/env bash
# ============================
# Exit Status Codes
# ============================
EXIT_SUCCESS=0
EXIT_FAILURE=1
EXIT_DEPENDENCY=2
EXIT_CONVERSION=3
# ============================
# Shell Options
# ============================
# Exit immediately if a command exits with a non-zero status,
# Treat unset variables as an error,
# Prevent errors in a pipeline from being masked
set -euo pipefail
# ============================
# Color Definitions
# ============================
RESET='\033[0m' # No Color
BOLD='\033[1m'
# Regular Colors
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[0;33m'
BLUE='\033[0;34m'
CYAN='\033[0;36m'
# ============================
# Configuration Constants
# ============================
# Absolute paths ensure the script can be run from any directory
SOURCE_DIR="$HOME/vimwiki" # Source Vimwiki directory
TEMP_DIR="/tmp/vimwikihtml" # Temporary HTML output directory
CSS_FILE="$HOME/.local/share/nvim/style.css" # Path to the CSS file for styling
CONCURRENT_JOBS=4 # Number of concurrent pandoc processes
LOG_FILE="$TEMP_DIR/conversion.log" # Log file to track conversions
ERROR_LOG_FILE="$TEMP_DIR/error.log" # Log file to track errors
VERSION="1.0.0" # Script version
# ============================
# Dependencies
# ============================
DEPENDENCIES=("pandoc" "sed" "qutebrowser" "find" "rsync" "grep" "mkdir" "basename" "diff")
# ============================
# Logging Functions
# ============================
# Function to log INFO messages
log_info() {
local message="$1"
echo -e "${BLUE}[INFO]${RESET} $message"
echo "[INFO] $(date +"%Y-%m-%d %H:%M:%S") - $message" >> "$LOG_FILE"
}
# Function to log SUCCESS messages
log_success() {
local message="$1"
echo -e "${GREEN}[SUCCESS]${RESET} $message"
echo "[SUCCESS] $(date +"%Y-%m-%d %H:%M:%S") - $message" >> "$LOG_FILE"
}
# Function to log WARNING messages
log_warning() {
local message="$1"
echo -e "${YELLOW}[WARNING]${RESET} $message"
echo "[WARNING] $(date +"%Y-%m-%d %H:%M:%S") - $message" >> "$LOG_FILE"
}
# Function to log ERROR messages
log_error() {
local message="$1"
echo -e "${RED}[ERROR]${RESET} $message" | tee -a "$ERROR_LOG_FILE"
echo "[ERROR] $(date +"%Y-%m-%d %H:%M:%S") - $message" >> "$ERROR_LOG_FILE"
}
# Function to handle errors with context and exit with specific code
handle_error() {
local message="$1"
local exit_code="${2:-$EXIT_FAILURE}"
local func="${FUNCNAME[1]}"
local line="${BASH_LINENO[0]}"
log_error "In function '$func' at line $line: $message"
exit "$exit_code"
}
# ============================
# Filename Validation Function
# ============================
# Function to check if a filename contains only allowed characters
is_valid_filename() {
local filename="$1"
if [[ "$filename" =~ ^[A-Za-z0-9._-]+$ ]]; then
return 0
else
return 1
fi
}
# ============================
# Bash Availability Check
# ============================
# Function to ensure that Bash is available
check_bash() {
if ! command -v bash &>/dev/null; then
echo -e "${RED}[ERROR]${RESET} Bash is not installed. Please install Bash to run this script."
exit "$EXIT_FAILURE"
fi
}
# ============================
# Path Validation Function
# ============================
# Function to validate and sanitize input paths
validate_paths() {
log_info "Validating input paths..."
# Check that SOURCE_DIR is an absolute path
if [[ "$SOURCE_DIR" != /* ]]; then
handle_error "SOURCE_DIR ('$SOURCE_DIR') is not an absolute path." "$EXIT_FAILURE"
fi
# Check that TEMP_DIR is an absolute path and under /tmp
if [[ "$TEMP_DIR" != /tmp/* ]]; then
handle_error "TEMP_DIR ('$TEMP_DIR') must be under /tmp." "$EXIT_FAILURE"
fi
# Check that SOURCE_DIR exists and is a directory
if [[ ! -d "$SOURCE_DIR" ]]; then
handle_error "SOURCE_DIR ('$SOURCE_DIR') does not exist or is not a directory." "$EXIT_FAILURE"
fi
# Check if TEMP_DIR exists; if not, it will be created later
# If it exists, ensure it's a directory
if [[ -e "$TEMP_DIR" && ! -d "$TEMP_DIR" ]]; then
handle_error "TEMP_DIR ('$TEMP_DIR') exists but is not a directory." "$EXIT_FAILURE"
fi
log_success "Input paths are valid."
}
# ============================
# Function Definitions
# ============================
# Function to check if all dependencies are installed
check_dependencies() {
log_info "Checking for required dependencies..."
local missing_dependencies=()
for cmd in "${DEPENDENCIES[@]}"; do
if ! command -v "$cmd" &>/dev/null; then
missing_dependencies+=("$cmd")
fi
done
if [[ ${#missing_dependencies[@]} -ne 0 ]]; then
for cmd in "${missing_dependencies[@]}"; do
handle_error "Dependency '$cmd' is not installed. Please install it and retry." "$EXIT_DEPENDENCY"
done
fi
log_success "All dependencies are satisfied."
}
# Function to extract the title from Markdown using YAML frontmatter or fallback to filename
extract_title() {
local md_file="$1"
# Attempt to extract title from YAML frontmatter
local title
title=$(grep -m1 '^title:' "$md_file" | sed 's/title: //') || true
if [[ -z "$title" ]]; then
# If no title found, use the filename without extension
title=$(basename "$md_file" .md.old)
fi
echo "$title"
}
# Function to convert a single Markdown file to HTML atomically
convert_md_to_html() {
local md_old_file="$1" # Path to the .md.old file
# Determine the relative path from TEMP_DIR
local relative_path="${md_old_file#$TEMP_DIR/}"
# Remove .md.old extension
relative_path="${relative_path%.md.old}"
# Determine the output HTML file path
local html_file="$TEMP_DIR/${relative_path}.html"
# Determine the temporary HTML file path
local temp_html_file="${html_file}.tmp"
# Create the necessary directories for the HTML file
mkdir -p "$(dirname "$html_file")"
# Extract the title for the HTML document
local title
title=$(extract_title "$md_old_file")
log_info "Converting '$md_old_file' to '$html_file'..."
# Use pandoc to convert Markdown to HTML with CSS and metadata
if [[ -f "$CSS_FILE" ]]; then
if ! pandoc -f markdown -s --css="$CSS_FILE" --metadata title="$title" "$md_old_file" -o "$temp_html_file"; then
handle_error "Failed to convert '$md_old_file' to HTML." "$EXIT_CONVERSION"
fi
else
log_warning "CSS file '$CSS_FILE' not found. Skipping CSS for '$md_old_file'."
if ! pandoc -f markdown -s --metadata title="$title" "$md_old_file" -o "$temp_html_file"; then
handle_error "Failed to convert '$md_old_file' to HTML." "$EXIT_CONVERSION"
fi
fi
# Debug: Print a snippet of the HTML file before running sed
log_info "Snippet before sed in '$temp_html_file':"
head -n 5 "$temp_html_file" || true
echo "..."
# Adjust internal href links:
# 1. Replace href="path/to/file.md.old" with href="path/to/file.html"
# 2. Replace href="path/to/file" with href="path/to/file.html" only if 'file' has no extension
log_info "Adjusting links in '$html_file'..."
# First, replace links ending with .md.old
if ! sed -i -E 's|(href=")([^"#:/]+(/[^"#:/]+)*)\.md\.old(")|\1\2.html\4|g' "$temp_html_file"; then
handle_error "Failed to adjust '.md.old' links in '$temp_html_file'." "$EXIT_CONVERSION"
fi
# Then, replace links without any extension
if ! sed -i -E 's|(href=")([^"#:/.]+(/[^"#:/.]+)*)(")|\1\2.html\4|g' "$temp_html_file"; then
handle_error "Failed to adjust extensionless links in '$temp_html_file'." "$EXIT_CONVERSION"
fi
# Move the temporary HTML file to the final destination atomically
mv "$temp_html_file" "$html_file"
# Debug: Print a snippet of the HTML file after running sed
log_info "Snippet after sed in '$html_file':"
head -n 5 "$html_file" || true
echo "..."
# Log the successful conversion
echo "$(date +"%Y-%m-%d %H:%M:%S") - Converted '$md_old_file' to '$html_file'." >> "$LOG_FILE"
log_success "Successfully converted '$md_old_file' to '$html_file'."
}
# Function to synchronize Markdown files and relevant assets to TEMP_DIR
synchronize_markdown() {
log_info "Synchronizing Markdown files and assets to '$TEMP_DIR'..."
# Use rsync to copy only .md, .pdf, and image files, excluding unwanted directories and files
rsync -av --delete \
--exclude='*.html' \
--exclude='*.sh' \
--exclude='.git/' \
--exclude='.gitignore' \
--exclude='*.bak' \
--include='*/' \
--include='*.md' \
--include='*.pdf' \
--include='*.png' \
--include='*.jpg' \
--include='*.jpeg' \
--include='*.gif' \
--exclude='*' \
"$SOURCE_DIR/" "$TEMP_DIR/" | grep '\.md$' || true
log_success "Synchronization completed."
}
# Function to rename .md files to .md.old in TEMP_DIR
rename_md_files() {
log_info "Renaming new or modified .md files to .md.old in '$TEMP_DIR'..."
# Find all .md files in TEMP_DIR
find "$TEMP_DIR" -type f -name '*.md' | while IFS= read -r md_file; do
# Determine the corresponding .md.old file
md_old_file="${md_file}.old"
# Determine the source .md file
source_md="$SOURCE_DIR/${md_file#$TEMP_DIR/}"
# Check if the .md.old file exists
if [[ ! -f "$md_old_file" ]]; then
# New file detected, copy to .md.old
cp "$source_md" "$md_old_file"
log_info "New file detected. Copied '$source_md' to '$md_old_file'."
# Convert to HTML
convert_md_to_html "$md_old_file" &
else
# Compare the source .md with the existing .md.old
if ! diff -q "$source_md" "$md_old_file" &>/dev/null; then
# Files differ, update .md.old and reconvert
cp "$source_md" "$md_old_file"
log_info "Modified file detected. Updated '$md_old_file' with changes from '$source_md'."
# Convert to HTML
convert_md_to_html "$md_old_file" &
else
log_info "No changes detected for '$source_md'. Skipping conversion."
fi
fi
done
# Wait for all background conversions to finish
wait
log_success "Renaming and conversion of new or modified .md files completed."
}
# Function to handle deletions: Remove .html files corresponding to deleted .md files
handle_deletions() {
log_info "Handling deletions of Markdown files..."
# Find all .md.old files in TEMP_DIR
find "$TEMP_DIR" -type f -name '*.md.old' | while IFS= read -r md_old_file; do
# Determine the corresponding .md file in SOURCE_DIR
source_md="$SOURCE_DIR/${md_old_file#$TEMP_DIR/}"
source_md="${source_md%.md.old}.md"
# Check if the source .md file exists
if [[ ! -f "$source_md" ]]; then
# Corresponding .md file has been deleted, remove the .html file
html_file="${md_old_file%.md.old}.html"
if [[ -f "$html_file" ]]; then
rm "$html_file"
log_success "Deleted '$html_file' as the source Markdown file no longer exists."
# Log the deletion
echo "$(date +"%Y-%m-%d %H:%M:%S") - Deleted '$html_file' due to source removal." >> "$LOG_FILE"
fi
# Remove the .md.old file itself
rm "$md_old_file"
log_info "Removed obsolete '$md_old_file'."
fi
done
log_success "Deletion handling completed."
}
# Function to generate index.html specifically
generate_index() {
local index_md_old="$TEMP_DIR/index.md.old"
local index_html="$TEMP_DIR/index.html"
if [[ ! -f "$index_md_old" ]]; then
handle_error "'index.md.old' not found in '$TEMP_DIR'." "$EXIT_FAILURE"
fi
log_info "Generating 'index.html' from 'index.md.old'..."
# Convert the index.md.old file to HTML
convert_md_to_html "$index_md_old"
# Ensure index.html exists
if [[ ! -f "$index_html" ]]; then
handle_error "Failed to generate 'index.html'." "$EXIT_CONVERSION"
fi
log_success "'index.html' generation completed."
}
# Function to open index.html in qutebrowser
open_browser() {
local index_file="$TEMP_DIR/index.html"
if [[ -f "$index_file" ]]; then
log_info "Opening '$index_file' in qutebrowser..."
qutebrowser "$index_file" &
log_success "Opened '$index_file' in qutebrowser."
else
handle_error "'$index_file' does not exist. Please ensure it is generated correctly." "$EXIT_FAILURE"
fi
}
# Function to display usage information
usage() {
echo -e "${BOLD}Usage:${RESET} $0 [OPTIONS]"
echo ""
echo "Options:"
echo " --index-wiki, -iw Synchronize and convert Vimwiki to HTML, then open index.html in qutebrowser."
echo " --help, -h Display this help message."
echo " --version, -V Display the script's version."
echo ""
echo "Examples:"
echo " $0 --index-wiki"
echo " $0 -iw"
echo " $0 --help"
echo " $0 -h"
echo " $0 --version"
echo " $0 -V"
}
# Function to display version information
version_info() {
echo -e "${BOLD}Vimwiki HTML Converter${RESET} version ${GREEN}$VERSION${RESET}"
}
# Function to update synchronization and conversion based on differences
update_if_needed() {
# Synchronize new and updated files
synchronize_markdown
# Rename and convert new or modified .md files
rename_md_files
# Handle deletions
handle_deletions
# Generate index.html
generate_index
}
# ============================
# Signal Handling Functions
# ============================
# Function to handle script termination gracefully
cleanup() {
log_warning "Script interrupted. Cleaning up..."
# Terminate all background jobs
jobs -rp | xargs -r kill -TERM 2>/dev/null || true
exit "$EXIT_FAILURE"
}
# Trap SIGINT and SIGTERM signals
trap cleanup SIGINT SIGTERM
# ============================
# Main Script Execution
# ============================
main() {
# Parse command-line arguments
if [[ $# -eq 0 ]]; then
log_error "No arguments provided. Use --help for usage information."
exit "$EXIT_FAILURE"
fi
while [[ $# -gt 0 ]]; do
case "$1" in
--index-wiki|-iw)
action="index_wiki"
shift
;;
--help)
usage
exit "$EXIT_SUCCESS"
;;
-h)
usage
exit "$EXIT_SUCCESS"
;;
--version)
version_info
exit "$EXIT_SUCCESS"
;;
-V)
version_info
exit "$EXIT_SUCCESS"
;;
*)
log_error "Unknown option: $1. Use --help for usage information."
exit "$EXIT_FAILURE"
;;
esac
done
# Execute based on the action
case "$action" in
index_wiki)
check_bash
check_dependencies
validate_paths
# Create TEMP_DIR if it doesn't exist
if [[ ! -d "$TEMP_DIR" ]]; then
log_info "Temporary directory '$TEMP_DIR' does not exist. Creating and performing full synchronization."
mkdir -p "$TEMP_DIR"
synchronize_markdown
rename_md_files
handle_deletions
generate_index
else
log_info "Temporary directory '$TEMP_DIR' already exists. Checking for updates."
update_if_needed
fi
open_browser
log_success "All tasks completed successfully."
exit "$EXIT_SUCCESS"
;;
*)
# This should not happen due to earlier checks
log_error "Invalid action." "$EXIT_FAILURE"
;;
esac
}
# Invoke the main function
main "$@"