diff --git a/init.lua b/init.lua index aff5250e..392445e3 100644 --- a/init.lua +++ b/init.lua @@ -966,17 +966,17 @@ do -- Here are some example plugins that I've included in the Kickstart repository. -- Uncomment any of the lines below to enable them (you will need to restart nvim). -- - -- require 'kickstart.plugins.debug' - -- require 'kickstart.plugins.indent_line' - -- require 'kickstart.plugins.lint' - -- require 'kickstart.plugins.autopairs' - -- require 'kickstart.plugins.neo-tree' + require 'kickstart.plugins.debug' + require 'kickstart.plugins.indent_line' + require 'kickstart.plugins.lint' + require 'kickstart.plugins.autopairs' + require 'kickstart.plugins.neo-tree' -- require 'kickstart.plugins.gitsigns' -- adds gitsigns recommended keymaps -- NOTE: You can add your own plugins, configuration, etc from `lua/custom/plugins/*.lua` -- -- Uncomment the following line and add your plugins to `lua/custom/plugins/*.lua` to get going. - -- require 'custom.plugins' + require 'custom.plugins' end -- The line beneath this is called `modeline`. See `:help modeline` diff --git a/lua/custom/plugins/01-functions.lua b/lua/custom/plugins/01-functions.lua new file mode 100644 index 00000000..491d4a13 --- /dev/null +++ b/lua/custom/plugins/01-functions.lua @@ -0,0 +1,375 @@ +-- Notification function for job output +function notif(jobid, data, event, timeout, notifid) + if type(data) == 'number' then + return + end + + local output = table.concat(data, '\n') + if output ~= '' then + vim.notify(output, vim.log.levels.WARN, { + title = 'Notification', + timeout = timeout or 5000, + }) + end +end + +function CompileAndRun() + local filetype = GetFileType() + + -- Get full folder path of the current buffer + local folder_path = vim.fn.expand '%:p:h' + + -- get full path up to "labs" folder + local labs_fullfolder = folder_path:match '(.+labs)' + + -- Get the filename (with extension) of the current buffer + local filename_with_extension = vim.fn.expand '%:t' + + -- Get the filename without the extension + local filename_without_extension = vim.fn.expand '%:t:r' + + -- Get the file extension of the current buffer + local file_extension = vim.fn.fnamemodify(filename_with_extension, ':e') + + if file_extension == 'cjs' or file_extension == 'mjs' then + filetype = 'javascript' + elseif file_extension == 'md' then + filetype = 'markdown' + end + + local iste = true + + if filetype == 'cpp' then + vim.cmd( + ':tabnew | te g++ --std=c++17 ' + .. folder_path + .. '/' + .. filename_with_extension + .. ' -o ' + .. folder_path + .. '/' + .. filename_without_extension + .. '.out' + .. ' && ' + .. folder_path + .. '/' + .. filename_without_extension + .. '.out' + ) + elseif filetype == 'javascript' then + vim.cmd(':tabnew | te node ' .. folder_path .. '/' .. filename_with_extension) + elseif file_extension == 'py' then + vim.cmd(':tabnew | te python ' .. folder_path .. '/' .. filename_with_extension) + elseif filetype == 'typescript' then + vim.cmd(':tabnew | te ts-node ' .. folder_path .. '/' .. filename_with_extension) + elseif filetype == 'shell' then + vim.cmd(':tabnew | te bash ' .. folder_path .. '/' .. filename_with_extension) + else + vim.notify('Filetype ' .. filetype .. ' not supported for compile and run') + return + end + + if iste then + vim.api.nvim_feedkeys('i', 'n', true) + end +end + +-- livegrep search +function customSearchGrep() + local extension = vim.fn.input 'Enter File Extension (*): ' + local dirs = vim.fn.input 'Enter Search Directories (.): ' + + -- if escape is pressed, return + if extension == '' and dirs == '' then + return + end + + if extension == '' then + extension = '*' + end + if dirs == '' then + dirs = '.' + end + + vim.cmd('Telescope live_grep glob_pattern=*.{' .. extension .. '} search_dirs=' .. dirs) +end + +function SearchAndReplace(search, replace) + -- make sure search is not empty + if search == '' then + return + end + + local command = ':%s/' .. search .. '/' .. replace .. '/g' + local termcodes = vim.api.nvim_replace_termcodes(command, true, true, true) + vim.api.nvim_feedkeys(termcodes, 'n', true) +end + +function NextJSNewPage(pagename) + local page_path = 'src/app/pages/' .. pagename .. '/page.tsx' + local page_content = {} + page_content[#page_content + 1] = [["use client"]] + page_content[#page_content + 1] = [[import type { NextPage } from "next";]] + page_content[#page_content + 1] = [[]] + page_content[#page_content + 1] = [[const Test: NextPage<{ someProps: string }> = (props) => {]] + page_content[#page_content + 1] = [[ return <>Hello Test;]] + page_content[#page_content + 1] = [[};]] + page_content[#page_content + 1] = [[]] + page_content[#page_content + 1] = [[export default Test;]] + vim.fn.mkdir('src/app/pages/' .. pagename, 'p') + vim.fn.writefile(page_content, page_path) + vim.cmd('e ' .. page_path) +end + +function NextJSNewApiGet(pagename) + local page_path = 'src/app/api/' .. pagename .. '/route.ts' + local page_content = {} + page_content[#page_content + 1] = [[import { NextRequest, NextResponse } from "next/server";]] + page_content[#page_content + 1] = [[// export const runtime = "edge";]] + page_content[#page_content + 1] = [[]] + page_content[#page_content + 1] = [[export const OPTIONS = async () => {]] + page_content[#page_content + 1] = [[ return NextResponse.json({]] + page_content[#page_content + 1] = [[ status: "ok",]] + page_content[#page_content + 1] = [[ });]] + page_content[#page_content + 1] = [[};]] + page_content[#page_content + 1] = [[]] + page_content[#page_content + 1] = [[export const GET = async (]] + page_content[#page_content + 1] = [[ req: NextRequest,]] + page_content[#page_content + 1] = [[ { params }: { params: { [s: string]: string } }]] + page_content[#page_content + 1] = [[) => {]] + page_content[#page_content + 1] = [[ try {]] + page_content[#page_content + 1] = [[ const url = new URL(req.url);]] + page_content[#page_content + 1] = [[ const {} = params ? params : {};]] + page_content[#page_content + 1] = [[ const searchParams = new URLSearchParams(url.search);]] + page_content[#page_content + 1] = [[ const test = searchParams.get("test");]] + page_content[#page_content + 1] = [[]] + page_content[#page_content + 1] = [[ return NextResponse.json({ status: "ok", test });]] + page_content[#page_content + 1] = [[ } catch (error) {]] + page_content[#page_content + 1] = [[ return NextResponse.json({ err: (error as any).toString() });]] + page_content[#page_content + 1] = [[ }]] + page_content[#page_content + 1] = [[};]] + vim.fn.mkdir('src/app/api/' .. pagename, 'p') + vim.fn.writefile(page_content, page_path) + vim.cmd('e ' .. page_path) +end + +function NextJSNewApiPost(pagename) + local page_path = 'src/app/api/' .. pagename .. '/route.ts' + local page_content = {} + page_content[#page_content + 1] = [[import { NextRequest, NextResponse } from "next/server";]] + page_content[#page_content + 1] = [[// export const runtime = "edge";]] + page_content[#page_content + 1] = [[]] + page_content[#page_content + 1] = [[export const OPTIONS = async () => {]] + page_content[#page_content + 1] = [[ return NextResponse.json({]] + page_content[#page_content + 1] = [[ status: "ok",]] + page_content[#page_content + 1] = [[ });]] + page_content[#page_content + 1] = [[};]] + page_content[#page_content + 1] = [[]] + page_content[#page_content + 1] = [[export const POST = async (]] + page_content[#page_content + 1] = [[ req: NextRequest,]] + page_content[#page_content + 1] = [[ { params }: { params: { [s: string]: string } }]] + page_content[#page_content + 1] = [[) => {]] + page_content[#page_content + 1] = [[ try {]] + page_content[#page_content + 1] = [[ const body = await req.json();]] + page_content[#page_content + 1] = [[ const {}: { [key: string]: string } = body;]] + page_content[#page_content + 1] = [[ const {} = params;]] + page_content[#page_content + 1] = [[]] + page_content[#page_content + 1] = [[ return NextResponse.json({ status: "ok", body });]] + page_content[#page_content + 1] = [[ } catch (error) {]] + page_content[#page_content + 1] = [[ return NextResponse.json({ err: (error as any).toString() });]] + page_content[#page_content + 1] = [[ }]] + page_content[#page_content + 1] = [[};]] + vim.fn.mkdir('src/app/api/' .. pagename, 'p') + vim.fn.writefile(page_content, page_path) + vim.cmd('e ' .. page_path) +end + +function SvelteKitNewPage(pagename) + local page_path = 'src/routes/' .. pagename .. '/+page.svelte' + local page_content = {} + vim.fn.mkdir('src/routes/' .. pagename, 'p') + vim.fn.writefile(page_content, page_path) + vim.cmd('e ' .. page_path) +end + +function SvelteKitNewAPIPost(pagename) + local page_path = 'src/routes/api/' .. pagename .. '/+server.ts' + local page_content = {} + page_content[#page_content + 1] = [[import { json } from "@sveltejs/kit";]] + page_content[#page_content + 1] = [[import type { RequestHandler } from "./$types";]] + page_content[#page_content + 1] = [[]] + page_content[#page_content + 1] = [[const headers = {]] + page_content[#page_content + 1] = [[ "Access-Control-Allow-Origin": "*",]] + page_content[#page_content + 1] = [[ "Access-Control-Allow-Methods": "GET,HEAD,PUT,PATCH,POST,DELETE",]] + page_content[#page_content + 1] = [[ "Access-Control-Allow-Headers": "Content-Type,authorization",]] + page_content[#page_content + 1] = [[};]] + page_content[#page_content + 1] = [[]] + page_content[#page_content + 1] = [[export const OPTIONS: RequestHandler = () => {]] + page_content[#page_content + 1] = [[ return new Response("", {]] + page_content[#page_content + 1] = [[ status: 200,]] + page_content[#page_content + 1] = [[ headers,]] + page_content[#page_content + 1] = [[ });]] + page_content[#page_content + 1] = [[};]] + page_content[#page_content + 1] = [[export const POST: RequestHandler = async ({ request, cookies }) => {]] + page_content[#page_content + 1] = [[ const { something } = await request.json();]] + page_content[#page_content + 1] = [[]] + page_content[#page_content + 1] = [[ return json({ something }, { status: 200, headers });]] + page_content[#page_content + 1] = [[};]] + vim.fn.mkdir('src/routes/api/' .. pagename, 'p') + vim.fn.writefile(page_content, page_path) + vim.cmd('e ' .. page_path) +end + +function SvelteKitNewAPIGet(pagename) + local page_path = 'src/routes/api/' .. pagename .. '/+server.ts' + local page_content = {} + page_content[#page_content + 1] = [[import { json } from "@sveltejs/kit";]] + page_content[#page_content + 1] = [[import type { RequestHandler } from "./$types";]] + page_content[#page_content + 1] = [[]] + page_content[#page_content + 1] = [[const headers = {]] + page_content[#page_content + 1] = [[ "Access-Control-Allow-Origin": "*",]] + page_content[#page_content + 1] = [[ "Access-Control-Allow-Methods": "GET,HEAD,PUT,PATCH,POST,DELETE",]] + page_content[#page_content + 1] = [[ "Access-Control-Allow-Headers": "Content-Type,authorization",]] + page_content[#page_content + 1] = [[};]] + page_content[#page_content + 1] = [[]] + page_content[#page_content + 1] = [[export const OPTIONS: RequestHandler = () => {]] + page_content[#page_content + 1] = [[ return new Response("", {]] + page_content[#page_content + 1] = [[ status: 200,]] + page_content[#page_content + 1] = [[ headers,]] + page_content[#page_content + 1] = [[ });]] + page_content[#page_content + 1] = [[};]] + page_content[#page_content + 1] = [[]] + page_content[#page_content + 1] = [[export const GET: RequestHandler = ({ url, params }) => {]] + page_content[#page_content + 1] = [[ const q = url.searchParams.get("q");]] + page_content[#page_content + 1] = [[ // const slug = params.slug;]] + page_content[#page_content + 1] = [[ const number = Math.floor(Math.random() * 6) + 1;]] + page_content[#page_content + 1] = [[]] + page_content[#page_content + 1] = [[ return json({ number }, {status:200, headers});]] + page_content[#page_content + 1] = [[};]] + vim.fn.mkdir('src/routes/api/' .. pagename, 'p') + vim.fn.writefile(page_content, page_path) + vim.cmd('e ' .. page_path) +end + +-- NPM +function Npm_install() + local package_lock_exists = vim.fn.filereadable 'package-lock.json' == 1 + local yarn_lock_exists = vim.fn.filereadable 'yarn.lock' == 1 + local pnpm_lock_exists = vim.fn.filereadable 'pnpm-lock.yaml' == 1 + if package_lock_exists then + RunCommandInNewTab 'npm install --legacy-peer-deps' + elseif yarn_lock_exists then + RunCommandInNewTab 'yarn' + elseif pnpm_lock_exists then + RunCommandInNewTab 'pnpm install' + else + RunCommandInNewTab 'npm install --legacy-peer-deps' + end +end + +function GetFileType() + local filename = vim.fn.expand '%:t' + local extension = vim.fn.fnamemodify(filename, ':e') + + if extension == 'js' or extension == 'jsx' then + return 'javascript' + elseif extension == 'ts' or extension == 'tsx' then + return 'typescript' + elseif extension == 'cpp' or extension == 'c' then + return 'cpp' + elseif extension == 'sh' then + return 'shell' + else + return 'unknown' + end +end + +function fileExists(fileName) + local file = io.open(fileName, 'r') + if file then + file:close() + return true + else + return false + end +end + +function BuildAndNotify() + vim.notify('Building Project...', vim.log.levels.INFO, { + title = 'NPM', + timeout = 36000000, + }) + + vim.fn.jobstart('npm run build', { + on_stdout = function(id, data, e) + notif(id, data, e, 4000) + end, + on_stderr = function(id, data, e) + notif(id, data, e, 4000) + end, + on_exit = function(id, data, e) + notif(id, data, e, 4000) + end, + }) +end + +function RunCommandInNewTab(command) + vim.cmd(':-1tabnew | te ' .. command) +end + +function RunCommandAndNotify(command, timeout, title) + if timeout == nil then + timeout = 5000 -- 5 seconds instead of 10 hours! + end + if title == nil then + title = 'Run Command' + end + vim.notify(title, vim.log.levels.INFO, { + title = title, + timeout = timeout, + }) + + vim.fn.jobstart(command, { + on_stdout = function(id, data, e) + notif(id, data, e, 4000) + end, + on_stderr = function(id, data, e) + notif(id, data, e, 4000) + end, + on_exit = function(id, data, e) + notif(id, data, e, 4000) + end, + }) +end + +function CloseHiddenBuffers() + local all_buffers = vim.api.nvim_list_bufs() + local visible_buffers = {} + local closed_count = 0 + + -- Get all visible buffers from all tabs and windows + for _, tabpage in ipairs(vim.api.nvim_list_tabpages()) do + for _, win in ipairs(vim.api.nvim_tabpage_list_wins(tabpage)) do + local buf = vim.api.nvim_win_get_buf(win) + visible_buffers[buf] = true + end + end + + -- Close buffers that are not visible in any window + for _, buf in ipairs(all_buffers) do + if not visible_buffers[buf] and vim.api.nvim_buf_is_loaded(buf) then + local buf_name = vim.api.nvim_buf_get_name(buf) + if buf_name ~= '' and not vim.api.nvim_buf_get_option(buf, 'modified') then + vim.api.nvim_buf_delete(buf, { force = false }) + closed_count = closed_count + 1 + end + end + end + + vim.notify('Closed ' .. closed_count .. ' hidden buffers', vim.log.levels.INFO, { + title = 'Buffer Cleanup', + timeout = 2000, + }) +end + +return {} diff --git a/lua/custom/plugins/01-opts.lua b/lua/custom/plugins/01-opts.lua new file mode 100644 index 00000000..ab544f6a --- /dev/null +++ b/lua/custom/plugins/01-opts.lua @@ -0,0 +1,129 @@ +-- noswap +vim.o.swapfile = false +vim.o.guifont = 'JetBrainsMono Nerd Font Mono:h14:sb' +vim.o.list = false + +-- disable spell for pandoc +vim.api.nvim_set_var('pandoc#spell#enabled', 0) + +-- markdown multi table format +vim.g.table_mode_corner_corner = '+' +vim.g.table_mode_header_fillchar = '=' + +-- markdown bullet and numbering indent +vim.o.breakindentopt = 'list:-1' + +-- lsp border +vim.o.winborder = 'rounded' + +sysname = vim.loop.os_uname().sysname +isWindows = sysname == 'Windows_NT' +isMac = sysname == 'Darwin' +isLinux = sysname == 'Linux' + +-- neovide auto focus +if vim.g.neovide then + vim.defer_fn(function() vim.cmd 'NeovideFocus' end, 200) + + if isWindows then + -- new neovide window in Windows + vim.keymap.set('n', '', ':silent !neovide', { + desc = 'Python', + noremap = true, + silent = true, + }) + vim.keymap.set('i', '', ':silent !neovide', { + desc = 'Python', + noremap = true, + silent = true, + }) + else + -- new neovide window in MACOS + vim.keymap.set('n', '', ':silent !neovide', { + desc = 'Python', + noremap = true, + silent = true, + }) + vim.keymap.set('i', '', ':silent !neovide', { + desc = 'Python', + noremap = true, + silent = true, + }) + end +else + -- new nvim-qt window in Android + vim.keymap.set('n', '', ':silent !nvim-qt', { + desc = 'Python', + noremap = true, + silent = true, + }) +end + +if isWindows then vim.g.python3_host_prog = 'C:\\Users\\yokow\\miniforge3\\envs\\labs\\python.exe' end + +-- independent clipboard +vim.opt.clipboard = '' + +-- set autoindent expandtab tabstop=2 shiftwidth=2 +vim.o.autoindent = true +vim.o.expandtab = true +vim.o.tabstop = 2 +vim.o.shiftwidth = 2 +vim.o.smartindent = true + +vim.o.foldlevel = 20 + +vim.opt.confirm = true +vim.g.have_nerd_font = true + +-- Enable line numbers (both absolute and relative) +vim.wo.relativenumber = true + +-- "Keep it Centered +-- Create nnoremap mappings +vim.api.nvim_set_keymap('n', 'n', 'nzzzv', { + noremap = true, + silent = true, +}) +vim.api.nvim_set_keymap('n', 'N', 'Nzzzv', { + noremap = true, + silent = true, +}) +vim.o.hlsearch = true + +vim.api.nvim_set_keymap('n', 'cpr', ':CphReceive', { + noremap = true, + silent = false, + desc = '[C]ompetitive [P]rogramming [R]eceive', +}) + +vim.api.nvim_set_keymap('n', 'cpt', ':CphTest', { + noremap = true, + silent = false, + desc = '[C]ompetitive [P]rogramming [T]est', +}) + +-- save Cursor +vim.cmd [[ + autocmd BufReadPost * + \ if @% !~# '\.git[\/\\]COMMIT_EDITMSG$' && + \ line("'\"") > 1 && + \ line("'\"") <= line("$") | + \ exe "normal! g`\"" | + \ endif +]] + +vim.o.encoding = 'utf-8' + +vim.g.markdown_fenced_languages = { 'html', 'python', 'lua', 'vim', 'typescript', 'javascript' } + +-- session options +vim.opt.sessionoptions = { + 'buffers', -- save all loaded buffers + 'curdir', -- save current directory + 'tabpages', -- save tab pages + 'winsize', -- save window sizes + 'terminal', -- save terminal state +} + +return {} diff --git a/lua/custom/plugins/50-git.lua b/lua/custom/plugins/50-git.lua new file mode 100644 index 00000000..ed8e71af --- /dev/null +++ b/lua/custom/plugins/50-git.lua @@ -0,0 +1,236 @@ +-- "Git Mapping +function GitPullAndNotify() + vim.notify('Pull Processing...', vim.log.levels.INFO, { + title = 'Git', + timeout = 5000, -- 5 seconds instead of 10 hours + }) + + vim.fn.jobstart('git fetch --all && git pull --rebase', { + on_stdout = function(id, data, e) + notif(id, data, e, 4000) + end, + on_stderr = function(id, data, e) + notif(id, data, e, 4000) + end, + on_exit = function(id, data, e) + notif(id, data, e, 4000) + end, + }) +end + +function GitPushAndNotify() + vim.notify('Push Processing...', vim.log.levels.INFO, { + title = 'Git', + timeout = 5000, -- 5 seconds instead of 10 hours + }) + + vim.fn.jobstart('git pull --rebase && git push', { + on_stdout = function(id, data, e) + notif(id, data, e, 4000) + end, + on_stderr = function(id, data, e) + notif(id, data, e, 4000) + end, + on_exit = function(id, data, e) + notif(id, data, e, 4000) + end, + }) +end + +function OpenGitStatus() + -- Check if neogit is already open + local neogit_open = false + for _, buf in ipairs(vim.api.nvim_list_bufs()) do + local name = vim.api.nvim_buf_get_name(buf) + if name:match 'NeogitStatus' then + neogit_open = true + vim.api.nvim_buf_delete(buf, { force = true }) + break + end + end + + if not neogit_open then + vim.cmd [[Neotree close]] + vim.cmd [[Neogit]] + end +end + +function CreateBranchAndPush(branchName) + RunCommandAndNotify('git checkout -b ' .. branchName .. ' && git push -u origin ' .. branchName) +end + +vim.keymap.set('n', '', OpenGitStatus, { + desc = '[G]it [S]tatus', +}) + +function GitCommit(commitMessage) + RunCommandAndNotify('git add . && git commit -m "' .. commitMessage .. '"') +end + +function RevertToCommitUnderCursor() + local commit = vim.fn.expand '' + + if commit == nil or commit == '' then + print 'No word under cursor.' + return + end + + local answer = vim.fn.input("Reset to commit '" .. commit .. "' and force push? (yes/no): ") + + if answer ~= 'yes' then + print 'Cancelled.' + return + end + + local cmd = string.format('git reset --hard %s && git push --force', commit) + RunCommandInNewTab(cmd) +end + +function UndoCommit() + local n = vim.fn.input 'Enter number of commits to undo: ' + RunCommandAndNotify('git reset --soft HEAD~' .. n) +end + +vim.keymap.set('n', 'gx', UndoCommit, { desc = 'Discard changes in last commit (Undo Commit)' }) +vim.keymap.set('n', 'gr', RevertToCommitUnderCursor, { desc = 'Reset HEAD to commit under cursor + force push' }) + +vim.keymap.set('n', 'ga', function() + local branchName = vim.fn.input 'Enter commit message: ' + if branchName == '' then + return + end + + CreateBranchAndPush(branchName) +end, { + desc = '[G]it [A]dd Branch', + noremap = true, + silent = false, +}) + +vim.keymap.set('n', 'gc', function() + local commitMessage = vim.fn.input 'Enter commit message: ' + if commitMessage == '' then + return + end + + GitCommit(commitMessage) +end, { + desc = '[G]it [C]ommit', + noremap = true, + silent = false, +}) + +vim.keymap.set('n', 'gh', ':Telescope git_bcommits', { + desc = '[G]it File [H]istory', + noremap = true, + silent = true, +}) + +vim.keymap.set('n', 'gp', GitPushAndNotify, { + desc = '[G]it [P]ush', + noremap = true, + silent = true, +}) + +vim.keymap.set('n', 'gu', GitPullAndNotify, { + desc = '[G]it P[u]ll', + noremap = true, + silent = true, +}) + +vim.keymap.set('n', 'gd', 'Neogit diff', { + desc = '[G]it [D]iff', + noremap = true, + silent = true, +}) + +vim.keymap.set('n', 'gb', 'Neogit branch', { + desc = '[G]it [B]ranch', + noremap = true, + silent = true, +}) + +vim.keymap.set('n', 'gv', 'DiffviewOpen', { + desc = '[G]it [V]iew Diff (current file)', + noremap = true, + silent = true, +}) + +vim.keymap.set('n', 'gl', ':Telescope git_commits', { + desc = '[G]it [L]og', + noremap = true, + silent = true, +}) + +vim.api.nvim_create_user_command('GitInitPush', function() + local username = 'yokowasis' + local repo = vim.fn.fnamemodify(vim.loop.cwd(), ':t') + if repo == '' then + print 'Could not determine repository name from current directory' + return + end + local remote = 'https://github.com/' .. username .. '/' .. repo .. '.git' + local cmd = 'git init && git add . && git commit -m "Initial commit" && git branch -M main && gh repo create ' + .. repo + .. ' --private --source=. --remote=origin --push' + vim.cmd('terminal ' .. cmd) +end, {}) + +vim.keymap.set('n', 'gi', ':GitInitPush', { + desc = '[G]it [I]nit and Push', + noremap = true, + silent = true, +}) + +vim.keymap.set('n', 'coo', function() + -- Get current line and extract branch name, then checkout with neogit + local line = vim.api.nvim_get_current_line() + local branch = line:match 'origin/(.+)' + if branch then + vim.cmd('Neogit branch checkout ' .. branch) + else + vim.notify('No origin branch found on current line', vim.log.levels.WARN) + end +end, { + desc = 'Checkout Origin Branch', + noremap = true, + silent = true, +}) + +return { + { + 'NeogitOrg/neogit', + dependencies = { + 'nvim-lua/plenary.nvim', -- required + 'sindrets/diffview.nvim', -- optional - for diff view + 'nvim-telescope/telescope.nvim', -- optional - for telescope integration + }, + config = function() + require('neogit').setup { + -- Neogit configuration + integrations = { + telescope = true, + diffview = true, + }, + sections = { + untracked = { + folded = false, + }, + unstaged = { + folded = false, + }, + staged = { + folded = false, + }, + }, + } + + -- Branch-specific keymaps + vim.keymap.set('n', 'gb', 'Neogit branch', { + desc = '[G]it [B]ranch menu', + noremap = true, + silent = true, + }) + end, + }, +} diff --git a/lua/custom/plugins/51-navigation.lua b/lua/custom/plugins/51-navigation.lua new file mode 100644 index 00000000..a6fca5dc --- /dev/null +++ b/lua/custom/plugins/51-navigation.lua @@ -0,0 +1,77 @@ +-- Smart buffer delete function (for ) +local function smart_buffer_delete() + local buffers = vim.fn.getbufinfo { buflisted = 1 } + local listed_buffers = {} + + -- Count only listed buffers (exclude help, quickfix, etc.) + for _, buf in ipairs(buffers) do + if buf.listed == 1 then + table.insert(listed_buffers, buf) + end + end + + -- If this is the last buffer, quit Neovim + if #listed_buffers <= 1 then + vim.cmd 'quit' + else + vim.cmd 'bdelete' + end +end + +-- "Tab Navigation +vim.keymap.set('n', '', ':tabnew', { + desc = 'New Tab', + noremap = true, + silent = true, +}) +vim.keymap.set('n', 'tn', ':-1tabnew', { + desc = 'New Tab To The Left', + noremap = true, + silent = true, +}) +vim.keymap.set('n', '', ':tabnext', { + desc = 'Next Tab', + noremap = true, + silent = true, +}) +vim.keymap.set('n', '', ':tabprevious', { + desc = 'Previous Tab', + noremap = true, + silent = true, +}) +vim.keymap.set('n', '', ':tabclose', { + desc = 'Close Tab', + noremap = true, + silent = true, +}) + +-- Window splitting +vim.keymap.set('n', 'sv', ':vsplit', { + desc = '[S]plit [V]ertical', + noremap = true, + silent = true, +}) +vim.keymap.set('n', 'sh', ':split', { + desc = '[S]plit [H]orizontal', + noremap = true, + silent = true, +}) +vim.keymap.set('n', 'sq', 'q', { + desc = '[S]plit [Q]uit Window', + noremap = true, + silent = true, +}) + +-- nerdtree +vim.keymap.set('n', '', ':Neotree reveal', { + desc = 'Show File Explorer', + noremap = true, + silent = true, +}) +vim.keymap.set('i', '', ':Neotree reveal', { + desc = 'Show File Explorer', + noremap = true, + silent = true, +}) + +return {} diff --git a/lua/custom/plugins/52-terminal.lua b/lua/custom/plugins/52-terminal.lua new file mode 100644 index 00000000..a5d28637 --- /dev/null +++ b/lua/custom/plugins/52-terminal.lua @@ -0,0 +1,56 @@ +TerminalShell = '' + +if isWindows then + vim.cmd [[command! SaveInitVim :tabnew | exe ':te git -C '. stdpath("config") .' add . & git -C ' . stdpath("config") . ' commit -m save & git -C ' . stdpath("config") . ' push']] + vim.cmd [[command! SaveGlobalSnippets :tabnew | exe ':te git -C '. stdpath("config") .'/../../../git/friendly-snippets add . & git -C '. stdpath("config") .'/../../../git/friendly-snippets commit -m save & git -C '. stdpath("config") .'/../../../git/friendly-snippets push']] + vim.cmd [[command! LoadGlobalSnippets :tabnew | exe ':te git -C '. stdpath("config") .'/../../../git/friendly-snippets pull']] + + TerminalShell = 'pwsh' +else + vim.cmd [[command! SaveInitVim :tabnew | exe ':te git -C '. stdpath("config") .' add . && git -C ' . stdpath("config") . ' commit -m save && git -C ' . stdpath("config") . ' push']] + vim.cmd [[command! SaveGlobalSnippets :tabnew | exe ':te git -C ~/git/friendly-snippets add . && git -C ~/git/friendly-snippets commit -m save && git -C ~/git/friendly-snippets push']] + vim.cmd [[command! LoadGlobalSnippets :tabnew | exe ':te git -C ~/git/friendly-snippets pull']] + + TerminalShell = 'bash' +end + +vim.cmd [[command! LoadInitVim :tabnew | exe ':te git -C '. stdpath("config") .' pull' ]] +vim.cmd [[command! EditInitVim :tabnew | exe 'edit '. stdpath('config').'/lua/custom/plugins/00-init.lua']] +vim.cmd [[command! EditSnippets :lua require("luasnip.loaders").edit_snippet_files()]] + +-- open terminal +vim.keymap.set('n', '``', ':horizontal terminal ' .. TerminalShell .. 'J-----', { + desc = 'Open Terminal', + noremap = false, + silent = true, +}) + +-- vertical terminal +vim.keymap.set('n', '`v', 'v:terminal ' .. TerminalShell .. '', { + desc = 'Open Terminal [V]ertical', + noremap = false, + silent = true, +}) + +-- close terminal +vim.keymap.set('t', '`', ':q', { + desc = '', + noremap = true, + silent = true, +}) + +-- switch window +vim.keymap.set('t', '', '', { + desc = '', + noremap = true, + silent = true, +}) + +-- esc to normal mode in terminal +vim.keymap.set('t', '', '', { + desc = '', + noremap = true, + silent = true, +}) + +return {} diff --git a/lua/custom/plugins/53-window.lua b/lua/custom/plugins/53-window.lua new file mode 100644 index 00000000..7126e687 --- /dev/null +++ b/lua/custom/plugins/53-window.lua @@ -0,0 +1,23 @@ +-- "RESIZE WINDOW +vim.keymap.set('n', '.', ':vertical resize -10', { + desc = '', + noremap = true, + silent = true, +}) +vim.keymap.set('n', ',', ':vertical resize +5', { + desc = '', + noremap = true, + silent = true, +}) +vim.keymap.set('n', "'", ':horizontal resize -5', { + desc = '', + noremap = true, + silent = true, +}) +vim.keymap.set('n', ';', ':horizontal resize +2', { + desc = '', + noremap = true, + silent = true, +}) + +return {} diff --git a/lua/custom/plugins/55-startup.lua b/lua/custom/plugins/55-startup.lua new file mode 100644 index 00000000..17894e94 --- /dev/null +++ b/lua/custom/plugins/55-startup.lua @@ -0,0 +1,13 @@ +if vim.fn.argc() == 0 then + vim.defer_fn(function() + local session_name = vim.fn.fnamemodify(vim.fn.getcwd(), ':t') .. '.vim' + local session_path = vim.fn.expand('~/' .. session_name) + if vim.fn.filereadable(session_path) == 1 then + vim.cmd('silent! source ' .. vim.fn.fnameescape(session_path)) + vim.notify('Session loaded: ' .. session_name, vim.log.levels.INFO) + vim.cmd 'silent! GuessIndent' + end + end, 300) -- Delay the session load by 100ms +end + +return {} diff --git a/lua/custom/plugins/98-bookmarks.lua b/lua/custom/plugins/98-bookmarks.lua new file mode 100644 index 00000000..a5647075 --- /dev/null +++ b/lua/custom/plugins/98-bookmarks.lua @@ -0,0 +1 @@ +return {} diff --git a/lua/custom/plugins/99-keymaps.lua b/lua/custom/plugins/99-keymaps.lua new file mode 100644 index 00000000..2e0f922d --- /dev/null +++ b/lua/custom/plugins/99-keymaps.lua @@ -0,0 +1,477 @@ +-- Create a mapping for compiling and running code +vim.keymap.set('n', 'cr', CompileAndRun, { + noremap = true, + silent = false, + desc = '[C]ompile and [R]un', +}) + +-- emmet +vim.api.nvim_set_keymap('i', '', '(emmet-expand-abbr)', { + noremap = true, + silent = true, +}) + +-- "Copy Paste from OS Clipboard +vim.keymap.set('v', '', '"+p:silent! %s/\\r//g', { + desc = '[P]aste from system clipboard', + noremap = true, + silent = true, +}) +vim.keymap.set('n', '', '"+p:silent! %s/\\r//g', { + desc = '[P]aste from system clipboard', + noremap = true, + silent = true, +}) +vim.keymap.set('i', '', '"+p:silent! %s/\\r//ga', { + desc = '[P]aste from system clipboard', + noremap = true, + silent = true, +}) + +vim.keymap.set('n', '', '"+p:silent! %s/\\r//g', { + desc = '[P]aste from system clipboard', + noremap = true, + silent = true, +}) +vim.keymap.set('i', '', '*:silent! %s/\\r//g', { + desc = '[P]aste from system clipboard', + noremap = true, + silent = true, +}) + +vim.keymap.set('x', '', '"+y', { + desc = '[Y]ank to system clipboard', + noremap = true, + silent = true, +}) +vim.keymap.set('x', '', '"+y', { + desc = '[Y]ank to system clipboard', + noremap = true, + silent = true, +}) + +-- Map Ctrl+S to save +vim.keymap.set('n', '', ':w', { + desc = '[^s] Save Current File', + noremap = true, + silent = true, +}) +vim.keymap.set('i', '', ':wa', { + desc = '[^s] Save Current File', + noremap = true, + silent = true, +}) + +-- "Toggle Wrap +vim.keymap.set('n', 'ww', ':set wrap!', { + desc = '[W]ord [W]rap', + noremap = true, + silent = true, +}) + +-- +-- "keep visual mode after indent +vim.keymap.set('v', '>', '>gv', { + desc = '', + noremap = true, + silent = true, +}) + +vim.keymap.set('v', '<', '', '"_d', { + desc = 'Black Hole Delete', + noremap = true, + silent = true, +}) +vim.keymap.set('x', '', '"_d', { + desc = 'Black Hole Delete', + noremap = true, + silent = true, +}) + +-- telescope keymap +vim.keymap.set('n', '?', ':Telescope keymaps', { + desc = 'Show Keymaps', + noremap = true, + silent = true, +}) + +-- macos arrow navigation +vim.api.nvim_set_keymap('', '', '', { + noremap = true, +}) +vim.api.nvim_set_keymap('', '', '', { + noremap = true, +}) + +vim.api.nvim_set_keymap('i', '', 'b', { + noremap = true, + silent = false, +}) +vim.api.nvim_set_keymap('i', '', 'w', { + noremap = true, + silent = false, +}) +vim.api.nvim_set_keymap('x', '', 'b', { + noremap = true, + silent = false, +}) +vim.api.nvim_set_keymap('x', '', 'w', { + noremap = true, + silent = false, +}) +vim.api.nvim_set_keymap('n', '', 'b', { + noremap = true, + silent = false, +}) +vim.api.nvim_set_keymap('n', '', 'w', { + noremap = true, + silent = false, +}) + +-- macos, neovide, alacritty terminal +vim.api.nvim_set_keymap('i', '', '"*pa', { + noremap = true, + silent = false, +}) +vim.api.nvim_set_keymap('n', '', '"*p', { + noremap = true, + silent = false, +}) + +vim.api.nvim_set_keymap('n', '', '', { + noremap = true, + silent = false, +}) +vim.api.nvim_set_keymap('n', '', '', { + noremap = true, + silent = false, +}) +vim.api.nvim_set_keymap('i', '', '', { + noremap = true, + silent = false, +}) +vim.api.nvim_set_keymap('i', '', '', { + noremap = true, + silent = false, +}) +vim.api.nvim_set_keymap('x', '', '', { + noremap = true, + silent = false, +}) +vim.api.nvim_set_keymap('x', '', '', { + noremap = true, + silent = false, +}) +vim.api.nvim_set_keymap('n', '', 'gg', { + noremap = true, + silent = false, +}) +vim.api.nvim_set_keymap('n', '', 'G', { + noremap = true, + silent = false, +}) +vim.api.nvim_set_keymap('i', '', 'gg', { + noremap = true, + silent = false, +}) +vim.api.nvim_set_keymap('i', '', 'G', { + noremap = true, + silent = false, +}) +vim.api.nvim_set_keymap('x', '', 'gg', { + noremap = true, + silent = false, +}) +vim.api.nvim_set_keymap('x', '', 'G', { + noremap = true, + silent = false, +}) + +vim.keymap.set('n', '', ':w', { + desc = '[^s] Save Current File', + noremap = true, + silent = true, +}) +vim.keymap.set('i', '', ':wa', { + desc = '[^s] Save Current File', + noremap = true, + silent = true, +}) + +-- npm / yarn operation +vim.keymap.set('n', 'rb', function() RunCommandAndNotify 'npm run build' end, { + noremap = true, + silent = false, + desc = '[R]un [B]uild', +}) +vim.keymap.set('n', 'rd', function() RunCommandInNewTab 'npm run dev' end, { + noremap = true, + silent = false, + desc = '[R]un [D]ev', +}) +vim.keymap.set('n', 'ri', Npm_install, { + noremap = true, + silent = false, + desc = '[R]un npm [I]nstall', +}) +vim.keymap.set('n', 'rl', function() RunCommandInNewTab 'five-server' end, { + noremap = true, + silent = false, + desc = '[R]un npm [L]ive Server', +}) + +-- clear highlight after search by pressing return +vim.api.nvim_set_keymap('n', 'cl', ':noh:%s/\r//g', { + noremap = true, + silent = true, + desc = '[C][l]ear Search and NewLine Character', +}) + +-- cd to current directory +vim.api.nvim_set_keymap('n', 'cd', ':cd %:p:h:pwd', { + noremap = true, + silent = false, + desc = '[C]hange [D]irectory to current file', +}) + +-- Session +vim.api.nvim_set_keymap('n', 'sel', ":execute 'source ' . fnameescape(expand('~/' . fnamemodify(getcwd(), ':t') . '.vim'))", { + noremap = true, + silent = false, + desc = '[Se]ssion [L]oad', +}) + +vim.api.nvim_set_keymap('n', 'nq', ":execute 'mksession! ' . fnameescape(expand('~/' . fnamemodify(getcwd(), ':t') . '.vim')):qa!", { + noremap = true, + silent = false, + desc = '[N]vim [Q]uit', +}) + +-- vim signature help +vim.keymap.set('n', 'k', vim.lsp.buf.signature_help, { + noremap = true, + silent = true, +}) + +vim.keymap.set('n', 'sne', ':EditSnippets', { + noremap = true, + silent = false, + desc = '[Sn]ippets [E]dit', +}) + +-- "Snippet Format" +vim.keymap.set('n', 'snf', function() + local lines = vim.api.nvim_buf_get_lines(0, 0, -1, false) + + for i, line in ipairs(lines) do + line = line:gsub('\\', '\\\\') -- escape backslashes + line = line:gsub('"', '\\"') -- escape quotes + line = line:gsub("%$", "\\\\$") + line = line:gsub("\\\\\\\\", "\\\\\\\\\\\\") + + lines[i] = '"' .. line .. '",' + end + + vim.api.nvim_buf_set_lines(0, 0, -1, false, lines) +end, { + desc = '[Sn]ippet [F]ormat', +}) + +-- Noice Notification +vim.keymap.set('n', 'snn', ':NoiceAll', { + noremap = true, + silent = false, + desc = '[S]how [N]oice [N]otification', +}) + +-- auto format +vim.keymap.set('n', 'fmf', 'gg=G', { + noremap = true, + silent = false, + desc = '[F]ormat [M]anual [F]ormat', +}) + +-- Search and Replace +vim.keymap.set('n', 'sar', ':%s///g', { + noremap = true, + silent = false, + desc = '[S]earch [A]nd [R]eplace', +}) + +-- Search and Replace +vim.keymap.set('v', 'sar', '"zy:%s/z//g', { + noremap = true, + silent = false, + desc = '[S]earch [A]nd [R]eplace', +}) + +-- Search and Replace with Number of rows +vim.keymap.set('v', 'san', function() + -- Get the selected text + vim.cmd 'normal! "zy' + local selected = vim.fn.getreg 'z' + + -- Ask for number of rows + local rows = vim.fn.input 'Number of rows to affect: ' + if rows == '' then + print 'Operation cancelled' + return + end + + -- Get current line number + local current_line = vim.fn.line '.' + local end_line = current_line + tonumber(rows) - 1 + + -- Generate the command but don't execute it + local cmd = ':' .. current_line .. ',' .. end_line .. 's/' .. vim.fn.escape(selected, '/') .. '//g' + vim.fn.feedkeys(cmd .. vim.api.nvim_replace_termcodes('', true, false, true)) +end, { + noremap = true, + silent = false, + desc = '[S]earch [A]nd Replace with [N]umber of rows starting from current line', +}) + +-- Search and Visual Replace +vim.keymap.set('v', 'svr', ':s/"//g', { + noremap = true, + silent = false, + desc = '[S]earch And [V]isual [R]eplace', +}) + +-- Search and Visual Replace +vim.keymap.set('v', 'sk', ':s/\\(.*\\)/\\1/g', { + noremap = true, + silent = false, + desc = '[S]earch [K]irby', +}) + +vim.keymap.set('n', 'fnp', function() NextJSNewPage(vim.fn.input 'Enter Page Name: ') end, { + noremap = true, + silent = false, + desc = 'New [P]age', +}) +vim.keymap.set('n', 'fnr', function() NextJSNewApiPost(vim.fn.input 'Enter Route Name: ') end, { + noremap = true, + silent = false, + desc = 'New API [R]oute POST', +}) +vim.keymap.set('n', 'fng', function() NextJSNewApiGet(vim.fn.input 'Enter Route Name: ') end, { + noremap = true, + silent = false, + desc = 'New [G]et Route', +}) +vim.keymap.set('n', 'fsp', function() SvelteKitNewPage(vim.fn.input 'Enter Page Name: ') end, { + noremap = true, + silent = false, + desc = 'New [P]age', +}) +vim.keymap.set('n', 'fsr', function() SvelteKitNewAPIPost(vim.fn.input 'Enter Route Name: ') end, { + noremap = true, + silent = false, + desc = 'New Post [R]oute', +}) +vim.keymap.set('n', 'fsg', function() SvelteKitNewAPIGet(vim.fn.input 'Enter Route Name: ') end, { + noremap = true, + silent = false, + desc = 'New [G]et Route', +}) + +vim.keymap.set( + 'n', + '', + function() SearchAndReplace(vim.fn.input 'Enter Search Term: ', vim.fn.input 'Enter Replace Term: ') end, + -- ":%s/vim.fn.input('Enter Search Term: ')/vim.fn.input('Enter Replace Term: ')/g", + { + noremap = true, + silent = false, + desc = '[S]earch and [R]eplace', + } +) + +vim.keymap.set('n', 'sg', ':Telescope live_grep', { + desc = '[S]earch by [G]rep', + noremap = true, + silent = false, +}) + +vim.keymap.set('n', 'sc', customSearchGrep, { + desc = '[S]earch by [G]rep [C]ustom', + noremap = true, + silent = false, +}) + +-- close other windows except this one +vim.keymap.set('n', 'x', ':on', { + desc = 'Close all other windows', + noremap = true, + silent = true, +}) + +vim.keymap.set('n', 'rr', ':e!', { + noremap = true, + silent = false, + desc = '[R]eload [R]esource', +}) + +vim.keymap.set('n', 'rp', ':LspRestart', { + noremap = true, + silent = false, + desc = '[R]eload LS[P]', +}) + +-- code companion +vim.keymap.set('n', 'ct', ':CodeCompanionChat', { + noremap = true, + silent = false, + desc = '[C]odeCompanion [T]alk', +}) +vim.keymap.set('n', 'cc', ':CodeCompanion #{buffer} ', { + noremap = true, + silent = false, + desc = '[C]ode [C]ompanion', +}) +vim.keymap.set('v', 'cc', ':CodeCompanion ', { + noremap = true, + silent = false, + desc = '[C]ode [C]ompanion', +}) + +vim.keymap.set('n', 'fc', function() + vim.fn.setreg('+', vim.fn.expand '%:p') + print('Copied filename to clipboard: ' .. vim.fn.expand '%:p') +end, { + noremap = true, + silent = true, + desc = '[F]ile [C]opy current file path to clipboard', +}) + +vim.keymap.set('n', 'fe', function() os.execute 'explorer.exe .' end, { + noremap = true, + silent = true, + desc = '[F]ile [E]xplorer on current file folder', +}) + +vim.keymap.set('n', 'gd', require('telescope.builtin').lsp_definitions, { noremap = true, silent = true }) + +-- Close all hidden/background buffers +vim.keymap.set('n', 'bo', CloseHiddenBuffers, { + noremap = true, + silent = false, + desc = '[B]uffer [O]nly - Close all hidden buffers', +}) + +-- "Database Toggle UI" +vim.keymap.set('n', 'db', ':DBUIToggle', { + desc = '[D]ata[b]ase', + noremap = true, + silent = true, +}) + +return {}