diff --git a/lua/custom/plugins/00-init.lua b/lua/custom/plugins/00-init.lua new file mode 100644 index 00000000..2c47f053 --- /dev/null +++ b/lua/custom/plugins/00-init.lua @@ -0,0 +1,435 @@ +vim.deprecate = function() end + +-- You can add your own plugins here or in other files in this directory! +-- I promise not to create any merge conflicts in this directory :) +-- +-- See the kickstart.nvim README for more information +return { + { + 'HakonHarnes/img-clip.nvim', + event = 'VeryLazy', + opts = {}, + keys = { + { 'p', 'PasteImage', desc = 'Paste image from system clipboard' }, + }, + }, + { + 'nvim-neo-tree/neo-tree.nvim', + version = '*', + dependencies = { + 'nvim-lua/plenary.nvim', + 'nvim-tree/nvim-web-devicons', -- not strictly required, but recommended + 'MunifTanjim/nui.nvim', + }, + cmd = 'Neotree', + keys = { + { '\\', ':Neotree reveal', desc = 'NeoTree reveal', silent = true }, + }, + opts = { + filesystem = { + window = { + mappings = { + [''] = 'close_window', + }, + }, + }, + }, + }, + { + 'nvim-treesitter/nvim-treesitter-context', + event = 'VeryLazy', + config = function() + require('treesitter-context').setup { + enable = true, + max_lines = 3, + trim_scope = 'outer', + } + end, + }, + { + 'folke/flash.nvim', + event = 'VeryLazy', + opts = {}, + keys = { + { + 's', + mode = { 'n', 'x', 'o' }, + function() + require('flash').jump() + end, + desc = 'Flash', + }, + { + 'S', + mode = { 'n', 'x', 'o' }, + function() + require('flash').treesitter() + end, + desc = 'Flash Treesitter', + }, + { + 'r', + mode = 'o', + function() + require('flash').remote() + end, + desc = 'Remote Flash', + }, + { + 'R', + mode = { 'o', 'x' }, + function() + require('flash').treesitter_search() + end, + desc = 'Treesitter Search', + }, + { + '', + mode = { 'c' }, + function() + require('flash').toggle() + end, + desc = 'Toggle Flash Search', + }, + }, + }, + { + 'echasnovski/mini.statusline', + event = 'VeryLazy', + config = function() + require('mini.statusline').setup { use_icons = vim.g.have_nerd_font } + end, + }, + { + 'yokowasis/emmet-vim', + init = function() + vim.g.user_emmet_settings = { + typescriptreact = { + attribute_name = { + ['class'] = 'class', -- override className -> class + }, + }, + } + end, + }, + { + 'jake-stewart/multicursor.nvim', + branch = '1.0', + config = function() + local mc = require 'multicursor-nvim' + mc.setup() + + local set = vim.keymap.set + + -- Add or skip cursor above/below the main cursor. + -- set({"n", "x"}, "", function() mc.lineAddCursor(-1) end) + -- set({ 'n', 'x' }, '', function() + -- mc.lineAddCursor(1) + -- end) + -- set({"n", "x"}, "", function() mc.lineSkipCursor(-1) end) + -- set({"n", "x"}, "", function() mc.lineSkipCursor(1) end) + + -- Add or skip adding a new cursor by matching word/selection + set({ 'n', 'x' }, '', function() + mc.matchAddCursor(1) + end) + -- set({ 'n', 'x' }, 's', function() + -- mc.matchSkipCursor(1) + -- end) + -- set({ 'n', 'x' }, 'N', function() + -- mc.matchAddCursor(-1) + -- end) + -- set({ 'n', 'x' }, 'S', function() + -- mc.matchSkipCursor(-1) + -- end) + + -- Add and remove cursors with control + left click. + set('n', '', mc.handleMouse) + set('n', '', mc.handleMouseDrag) + set('n', '', mc.handleMouseRelease) + + -- Disable and enable cursors. + set({ 'n', 'x' }, '', mc.toggleCursor) + + -- Mappings defined in a keymap layer only apply when there are + -- multiple cursors. This lets you have overlapping mappings. + mc.addKeymapLayer(function(layerSet) + -- Select a different cursor as the main one. + -- layerSet({ 'n', 'x' }, '', mc.prevCursor) + -- layerSet({ 'n', 'x' }, '', mc.nextCursor) + + -- Delete the main cursor. + layerSet({ 'n', 'x' }, 'x', mc.deleteCursor) + + -- Enable and clear cursors using escape. + layerSet('n', '', function() + if not mc.cursorsEnabled() then + mc.enableCursors() + else + mc.clearCursors() + end + end) + end) + + -- Customize how cursors look. + local hl = vim.api.nvim_set_hl + hl(0, 'MultiCursorCursor', { reverse = true }) + hl(0, 'MultiCursorVisual', { link = 'Visual' }) + hl(0, 'MultiCursorSign', { link = 'SignColumn' }) + hl(0, 'MultiCursorMatchPreview', { link = 'Search' }) + hl(0, 'MultiCursorDisabledCursor', { reverse = true }) + hl(0, 'MultiCursorDisabledVisual', { link = 'Visual' }) + hl(0, 'MultiCursorDisabledSign', { link = 'SignColumn' }) + end, + }, + { + 'rachartier/tiny-inline-diagnostic.nvim', + event = 'VeryLazy', -- Or `LspAttach` + priority = 1000, -- needs to be loaded in first + config = function() + require('tiny-inline-diagnostic').setup() + vim.diagnostic.config { virtual_text = false } -- Only if needed in your configuration, if you already have native LSP diagnostics + end, + }, + { + 'echasnovski/mini.indentscope', + event = 'VeryLazy', + config = function() + require('mini.indentscope').setup { + symbol = '│', + options = { try_as_border = true }, + } + end, + }, + + { + 'NickvanDyke/opencode.nvim', + dependencies = { 'folke/snacks.nvim' }, + ---@type opencode.Config + opts = { + -- Your configuration, if any + }, + -- stylua: ignore + keys = { + { 'ot', function() require('opencode').toggle() end, desc = 'Toggle embedded opencode', }, + { 'oa', function() require('opencode').ask() end, desc = 'Ask opencode', mode = 'n', }, + { 'oa', function() require('opencode').ask('@selection: ') end, desc = 'Ask opencode about selection', mode = 'v', }, + { 'op', function() require('opencode').select_prompt() end, desc = 'Select prompt', mode = { 'n', 'v', }, }, + { 'on', function() require('opencode').command('session_new') end, desc = 'New session', }, + { 'oy', function() require('opencode').command('messages_copy') end, desc = 'Copy last message', }, + { '', function() require('opencode').command('messages_half_page_up') end, desc = 'Scroll messages up', }, + { '', function() require('opencode').command('messages_half_page_down') end, desc = 'Scroll messages down', }, + }, + }, + { + 'olimorris/codecompanion.nvim', + tag = 'v17.33.0', -- Pin to stable version to avoid breaking changes + dependencies = { + 'ravitemer/mcphub.nvim', + 'nvim-lua/plenary.nvim', + 'nvim-treesitter/nvim-treesitter', + }, + }, + -- { + -- 'MeanderingProgrammer/render-markdown.nvim', + -- dependencies = { 'nvim-treesitter/nvim-treesitter', 'nvim-tree/nvim-web-devicons' }, + -- ft = { 'markdown', 'codecompanion' }, + -- }, + { + 'echasnovski/mini.diff', + config = function() + local diff = require 'mini.diff' + diff.setup { + -- Enable git source + source = diff.gen_source.git(), + view = { + style = 'sign', -- Show git changes in sign column + signs = { add = '+', change = '~', delete = '-' }, + }, + } + end, + }, + { + 'MagicDuck/grug-far.nvim', + config = function() + require('grug-far').setup { + headerMaxWidth = 80, + } + end, + cmd = 'GrugFar', + keys = { + { 'S', 'GrugFar', desc = 'Search and Replace (grug-far)' }, + { + 'sw', + function() + require('grug-far').grug_far { prefills = { search = vim.fn.expand '' } } + end, + desc = 'Search current word', + }, + { + 'sf', + function() + require('grug-far').grug_far { prefills = { paths = vim.fn.expand '%' } } + end, + desc = 'Search in current file', + }, + }, + }, + { + 'folke/noice.nvim', + event = 'VeryLazy', + opts = { + lsp = { + progress = { + enabled = false, + }, + hover = { + enabled = false, + }, + signature = { + enabled = false, + }, + -- override markdown rendering so that **cmp** and other plugins use **Treesitter** + override = { + -- ['vim.lsp.util.convert_input_to_markdown_lines'] = true, + -- ['vim.lsp.util.stylize_markdown'] = true, + -- ['cmp.entry.get_documentation'] = true, -- requires hrsh7th/nvim-cmp + }, + }, + -- you can enable a preset for easier configuration + presets = { + bottom_search = true, -- use a classic bottom cmdline for search + command_palette = true, -- position the cmdline and popupmenu together + long_message_to_split = true, -- long messages will be sent to a split + inc_rename = false, -- enables an input dialog for inc-rename.nvim + lsp_doc_border = false, -- add a border to hover docs and signature help + }, + views = { + mini = { + size = { + width = 'auto', + height = 'auto', + }, + border = { + style = 'rounded', + }, + win_options = { + winblend = 10, + }, + }, + }, + }, + dependencies = { + 'MunifTanjim/nui.nvim', + }, + }, + { + 'luckasRanarison/tailwind-tools.nvim', + name = 'tailwind-tools', + build = ':UpdateRemotePlugins', + dependencies = { + 'nvim-treesitter/nvim-treesitter', + 'nvim-telescope/telescope.nvim', -- optional + 'neovim/nvim-lspconfig', -- optional + }, + opts = {}, -- your configuration + }, + { + 'supermaven-inc/supermaven-nvim', + config = function() + require('supermaven-nvim').setup { + keymaps = { + accept_suggestion = '', + accept_word = '<>', + }, + } + end, + }, + { + 'kristijanhusak/vim-dadbod-ui', + dependencies = { + { 'tpope/vim-dadbod', lazy = true }, + { 'kristijanhusak/vim-dadbod-completion', ft = { 'sql', 'mysql', 'plsql' }, lazy = true }, -- Optional + }, + cmd = { + 'DBUI', + 'DBUIToggle', + 'DBUIAddConnection', + 'DBUIFindBuffer', + }, + init = function() + -- Your DBUI configuration + vim.g.db_ui_use_nerd_fonts = 1 + end, + }, + -- colorschemes + -- { + -- 'ribru17/bamboo.nvim', + -- lazy = false, + -- priority = 1000, + -- config = function() + -- require('bamboo').setup { + -- -- optional configuration here + -- code_style = { + -- comments = { + -- italic = false, + -- }, + -- conditionals = { + -- italic = false, + -- }, + -- keywords = { + -- italic = false, + -- }, + -- functions = { + -- italic = false, + -- }, + -- namespaces = { + -- italic = false, + -- }, + -- parameters = { + -- italic = false, + -- }, + -- strings = { + -- italic = false, + -- }, + -- variables = { + -- italic = false, + -- }, + -- }, + -- highlights = { + -- -- Remove underlines from tag-related groups + -- ['htmlLink'] = { fg = '$blue' }, + -- ['@markup.link.label.html'] = { fg = '$blue' }, + -- ['@string.special.url.html'] = { fg = '$orange' }, + -- -- TSX/JSX related + -- ['@_jsx_element.tsx'] = { fg = '$blue' }, + -- ['@_jsx_element'] = { fg = '$blue' }, + -- ['@none.tsx'] = { fg = '$blue' }, + -- ['@spell.tsx'] = { fg = '$blue' }, + -- ['@markup.link.label.tsx'] = { fg = '$blue' }, + -- -- JavaScript/JSX related + -- ['@_jsx_element.javascript'] = { fg = '$blue' }, + -- ['@none.javascript'] = { fg = '$blue' }, + -- ['@spell.javascript'] = { fg = '$blue' }, + -- ['@markup.link.label.javascript'] = { fg = '$blue' }, + -- }, + -- } + -- require('bamboo').load() + -- end, + -- }, + -- { 'Mofiqul/dracula.nvim' }, + { + 'thesimonho/kanagawa-paper.nvim', + lazy = false, + priority = 1000, + opts = {}, + config = function() + vim.cmd 'colorscheme kanagawa-paper-ink' + end, + }, + -- { + -- 'rose-pine/neovim', + -- name = 'rose-pine', + -- config = function() end, + -- }, +} 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..b862f7cc --- /dev/null +++ b/lua/custom/plugins/01-opts.lua @@ -0,0 +1,133 @@ +-- noswap +vim.o.swapfile = false +vim.o.guifont = 'JetBrainsMono Nerd Font Mono:h15: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/56-jupyter-plugin.lua b/lua/custom/plugins/56-jupyter-plugin.lua new file mode 100644 index 00000000..09de8519 --- /dev/null +++ b/lua/custom/plugins/56-jupyter-plugin.lua @@ -0,0 +1,4 @@ +return { + 'Vigemus/iron.nvim', +} + diff --git a/lua/custom/plugins/57-jupyter-setup.lua b/lua/custom/plugins/57-jupyter-setup.lua new file mode 100644 index 00000000..f5f1ce67 --- /dev/null +++ b/lua/custom/plugins/57-jupyter-setup.lua @@ -0,0 +1,150 @@ +-- setup iron nvim + +local iron = require 'iron.core' +local view = require 'iron.view' +local common = require 'iron.fts.common' + +iron.setup { + config = { + -- Whether a repl should be discarded or not + scratch_repl = true, + -- Your repl definitions come here + repl_definition = { + sh = { + -- Can be a table or a function that + -- returns a table (see below) + command = { 'zsh' }, + }, + python = { + command = { 'python3' }, -- or { "ipython", "--no-autoindent" } + format = common.bracketed_paste_python, + block_dividers = { '# %%', '#%%' }, + env = { PYTHON_BASIC_REPL = '1' }, --this is needed for python3.13 and up. + }, + }, + -- set the file type of the newly created repl to ft + -- bufnr is the buffer id of the REPL and ft is the filetype of the + -- language being used for the REPL. + repl_filetype = function(bufnr, ft) + return ft + -- or return a string name such as the following + -- return "iron" + end, + -- Send selections to the DAP repl if an nvim-dap session is running. + dap_integration = true, + -- How the repl window will be displayed + -- See below for more information + repl_open_cmd = view.bottom(40), + + -- repl_open_cmd can also be an array-style table so that multiple + -- repl_open_commands can be given. + -- When repl_open_cmd is given as a table, the first command given will + -- be the command that `IronRepl` initially toggles. + -- Moreover, when repl_open_cmd is a table, each key will automatically + -- be available as a keymap (see `keymaps` below) with the names + -- toggle_repl_with_cmd_1, ..., toggle_repl_with_cmd_k + -- For example, + -- + -- repl_open_cmd = { + -- view.split.vertical.rightbelow("%40"), -- cmd_1: open a repl to the right + -- view.split.rightbelow("%25") -- cmd_2: open a repl below + -- } + }, + -- Iron doesn't set keymaps by default anymore. + -- You can set them here or manually add keymaps to the functions in iron.core + keymaps = { + toggle_repl = 'rr', -- toggles the repl open and closed. + -- If repl_open_command is a table as above, then the following keymaps are + -- available + -- toggle_repl_with_cmd_1 = "rv", + -- toggle_repl_with_cmd_2 = "rh", + restart_repl = 'jr', -- calls `IronRestart` to restart the repl + send_motion = 'sc', + visual_send = 'sc', + send_file = 'ja', + send_line = 'sl', + send_paragraph = 'sp', + send_until_cursor = 'su', + send_mark = 'sm', + send_code_block = 'sb', + send_code_block_and_move = 'sn', + mark_motion = 'mc', + mark_visual = 'mc', + remove_mark = 'md', + cr = 's', + interrupt = 's', + exit = 'jq', + clear = 'jl', + }, + -- If the highlight is on, you can change how it looks + -- For the available options, check nvim_set_hl + highlight = { + italic = true, + }, + ignore_blank_lines = true, -- ignore blank lines when sending visual select lines +} + +-- iron also has a list of commands, see :h iron-commands for all available commands +vim.keymap.set('n', 'rf', 'IronFocus') +vim.keymap.set('n', 'rh', 'IronHide') + +local function send_cell() + local iron = require 'iron.core' + + local buf = vim.api.nvim_get_current_buf() + local cursor = vim.api.nvim_win_get_cursor(0)[1] -- 1-based + local lines = vim.api.nvim_buf_get_lines(buf, 0, -1, false) + + local start_line = 1 + local end_line = #lines + + -- search upward for previous "# %%" + for i = cursor, 1, -1 do + if lines[i]:match '^# %%%%' then + start_line = i + 1 + break + end + end + + -- search downward for next "# %%" + for i = cursor + 1, #lines do + if lines[i]:match '^# %%%%' then + end_line = i - 1 + break + end + end + + if start_line > end_line then + return + end + + -- collect lines of the cell + local cell_lines = vim.api.nvim_buf_get_lines(buf, start_line - 1, end_line, false) + + -- send to REPL + iron.send(nil, cell_lines) +end + +-- keymaps + +vim.keymap.set('n', 'jm', 'i# %%', { + noremap = true, + silent = false, + desc = '[J]upyter Create [M]ark', +}) + +vim.keymap.set('n', 'ji', ':vsplit:IronReplHere', { + noremap = true, + silent = false, + desc = '[J]upyter [I]nit', +}) + +vim.keymap.set('v', 'jv', ":lua require('iron.core').visual_send()", { + noremap = true, + silent = false, + desc = '[J]upyter [V]isual Send', +}) + +vim.keymap.set('n', 'jj', send_cell, { desc = 'Iron: send current # %% cell' }) + +return {} diff --git a/lua/custom/plugins/97-setup-plugins.lua b/lua/custom/plugins/97-setup-plugins.lua new file mode 100644 index 00000000..9dd10d83 --- /dev/null +++ b/lua/custom/plugins/97-setup-plugins.lua @@ -0,0 +1,127 @@ +local ls = require 'luasnip' +ls.config.set_config { + store_selection_keys = '', +} +ls.filetype_extend('svelte', { 'typescript', 'html' }) + +require('luasnip.loaders.from_vscode').lazy_load { + paths = { '~/git/friendly-snippets' }, +} + +vim.keymap.set({ 'i', 's' }, '', function() + if require('luasnip').choice_active() then + require('luasnip').change_choice(1) + end +end, { desc = 'LuaSnip: next choice' }) + +vim.keymap.set({ 'i', 's' }, '', function() + if require('luasnip').choice_active() then + require('luasnip').change_choice(-1) + end +end, { desc = 'LuaSnip: previous choice' }) + +-- close window with x if it's not a main window +vim.api.nvim_create_autocmd('FileType', { + pattern = { 'checkhealth', 'fugitive*', 'git', 'help', 'lspinfo', 'netrw', 'notify', 'qf', 'query' }, + callback = function() + vim.keymap.set('n', 'x', vim.cmd.close, { + desc = 'Close the current buffer', + buffer = true, + }) + end, +}) + +-- Folding settings +vim.opt.foldmethod = 'indent' + +require('nvim-treesitter.install').compilers = { 'clang', 'gcc', 'zig' } + +-- save folded state +local viewdir = vim.fn.stdpath 'state' .. '/view' + +vim.o.viewdir = viewdir +vim.o.viewoptions = 'cursor,folds' -- only save cursor + folds (not options, etc.) + +-- Create the view directory if it doesn't exist +vim.fn.mkdir(viewdir, 'p') + +vim.api.nvim_create_augroup('remember_folds', { clear = true }) +vim.api.nvim_create_autocmd('BufWinLeave', { + group = 'remember_folds', + pattern = '*', + callback = function() + vim.cmd 'silent! mkview' + end, +}) +vim.api.nvim_create_autocmd('BufWinEnter', { + group = 'remember_folds', + pattern = '*', + callback = function() + vim.cmd 'silent! loadview' + end, +}) + +require('guess-indent').setup {} +require('conform').setup { + formatters = { + ['pretty-php'] = { + prepend_args = { '-s2' }, + }, + }, +} + +-- Setup clangd with system preference (if system clangd is available and preferred) +-- This will override the main lspconfig setup with additional arguments +local has_system_clangd = vim.fn.executable('clangd') == 1 +local prefer_system_clangd = true -- Set to false if you want to use Mason's clangd + +if has_system_clangd and prefer_system_clangd then + require('lspconfig').clangd.setup { + cmd = { 'clangd', '--background-index' }, + } +end + +require('codecompanion').setup { + adapters = { + http = { + my_custom_openai = function() + return require('codecompanion.adapters').extend('openai_compatible', { + name = 'my_custom_openai', + formatted_name = 'Custom OpenAI', + env = { + api_key = 'OPENAI_API_KEY', + url = 'https://chatgpt-api.sg.app.web.id', + chat_url = '/v1/chat/completions', + models_endpoint = '/v1/models', + }, + schema = { + model = { + order = 1, + mapping = 'parameters', + type = 'enum', + desc = 'ID of the model to use', + default = 'gpt-4o', + choices = { 'gpt-4o', 'gpt-4o-mini', 'gpt-3.5-turbo' }, + }, + }, + }) + end, + }, + acp = { + gemini_cli = function() + local adapter = require 'codecompanion.adapters.acp.gemini_cli' + return require('codecompanion.adapters.acp').new(adapter) + end, + }, + }, + strategies = { + chat = { + adapter = 'my_custom_openai', + }, + inline = { + adapter = 'my_custom_openai', + }, + }, +} + +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..b6716888 --- /dev/null +++ b/lua/custom/plugins/99-keymaps.lua @@ -0,0 +1,489 @@ +-- 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', +}) + +vim.keymap.set('n', 'snf', ':%s/"/\\\\"/gggVG10>gg0v_y:%s/0/\\":%s/\\n/\\",\\r:%s/\\n\\",/\\r\\"\\",', { + noremap = true, + silent = false, + desc = '[Sn]ippets [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, + 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 {} diff --git a/lua/custom/plugins/blank.lua b/lua/custom/plugins/blank.lua new file mode 100644 index 00000000..a5647075 --- /dev/null +++ b/lua/custom/plugins/blank.lua @@ -0,0 +1 @@ +return {} diff --git a/lua/custom/plugins/readme.md b/lua/custom/plugins/readme.md new file mode 100644 index 00000000..6a342389 --- /dev/null +++ b/lua/custom/plugins/readme.md @@ -0,0 +1,96 @@ +# Neovim Setup and Notes + +## TODO + +The very first thing you should do is to run the command `:Tutor` in Neovim. + +## Notes + +### SNIPPET/LSP + +- Trigger with ``. +- For `TM_SELECTED_TEXT`, block the text and then ``. +- Move between choices with `` and ``. +- Move between placeholder with `` and ``. + +### Copilot + +- Trigger with ``. + +--- + +## Setup Instructions + +### General + +- Install required Python packages: + ```bash + pip install pynvim neovim + ``` + +### Paste Image + +- Install Pillow: + ```bash + pip install pillow + ``` + +### Jupyter Setup + +1. Set up a Conda environment. +2. Install the following packages: + ```bash + pip install ipykernel jupytext pynvim jupyter_client cairosvg plotly kaleido pyperclip nbformat + ``` + +### Install All Dependencies + +- Run the following command: + ```bash + pip install pynvim neovim pillow ipykernel jupytext pynvim jupyter_client cairosvg plotly kaleido pyperclip nbformat + :UpdateRemotePlugins + ``` + +### Windows Specific + +- Copy the `rplugin.vim` file: + ```bash + cp ./rplugin.vim C:\Users\yokow\AppData\Local\nvim-data/rplugin.vim + ``` + +### Initialization + +1. Add environment to Molten (e.g., `labs`): + ```bash + python -m ipykernel install --user --name labs --display-name labs + ``` +2. Initialize Jupyter Labs: + ```bash + jupyter kernel --kernel=labs + ``` + +--- + +## iTerm2 Setup + +- Keybindings: + - `#s` -> `` + - `!Up` -> `` + - `#Up` -> `` + - `!Down` -> `` + - `#Down` -> `` + - `#Left` -> `` + - `#Right` -> `` + +--- + +## Lemonade Setup + +- Install Lemonade: + ```bash + go install github.com/lemonade-command/lemonade@latest + ``` +- Start the Lemonade server: + ```bash + ~/go/bin/lemonade server + ```