安裝好用的文字編輯工具 — LunarVim

安裝好用的文字編輯工具 — LunarVim

大家好,祝福大家都可以睡得很好。

今天來跟大家講講怎麼在 Ubuntu 上安裝 LunarVim 這個好工具,以及它其他好用的套件。它是一個基於 NeoVim 的 IDE,除了基本 Vim 的功能以外,它也集成了很多好用的設定及插件。

由於 APT 庫裡面的 NeoVim 並不是最新的穩定版本,所以我們需要使用別的方法來安裝 NeoVim。

我的安裝策略是這樣的:

rsvm => Rust 1.78.0 => Bob => NeoVim v0.9.5

首先,我們先打開終端機,用 curl 指令安裝 rsvm,rsvm 是 Rust 的版本管理工具,安裝後,可以任意切換 Rust 版本:

curl -L https://raw.github.com/sdepold/rsvm/master/install.sh | bash

使用 rsvm 來安裝並套用最新穩定版本的 Rust:

rsvm install 1.78.0
rsvm use 1.78.0

使用 Cargo 工具來安裝 Bob,Bob 是一個 NeoVim 的版本管理工具:

cargo install bob-nvim

使用 Bob 安裝 NeoVim v0.9.5:

bob install stable
bob use stable

執行完後,可以看看 NeoVim 的版本是否正確:

nvim --version

如果確認 NeoVim 安裝成功,而且版本是 0.9.5 以上的話,我們就可以開始安裝 LunarVim 了,在終端機中輸入以下指令:

LV_BRANCH='release-1.4/neovim-0.9' bash <(curl -s https://raw.githubusercontent.com/LunarVim/LunarVim/release-1.4/neovim-0.9/utils/installer/install.sh)

LunarVim 安裝完成後,在終端機中輸入 lvim ,即可打開 LunarVim。

LunarVim 的設定檔預設會被放在 ~/.config/lvim 中,如果想要安裝插件的話,可以編輯 config.lua 這個檔案。如果你不知道該怎麼做的話,可以參考我的設定:

-- Read the docs: https://www.lunarvim.org/docs/configuration
-- Video Tutorials: https://www.youtube.com/watch?v=sFA9kX-Ud_c&list=PLhoH5vyxr6QqGu0i7tt_XoVK9v-KvZ3m6
-- Forum: https://www.reddit.com/r/lunarvim/
-- Discord: https://discord.com/invite/Xb9B4Ny

lvim.plugins = {
  -- {
  --   "rainbow-delimiters.setup",
  --   config = function()
  --     require("rainbow-delimiters.setup").setup()
  --   end,
  -- },
  -- GitHub Copilot 
  -- {
  --   "zbirenbaum/copilot.lua",
  --   cmd = "Copilot",
  --   event = "InsertEnter",
  -- },
  -- {
  --   "zbirenbaum/copilot-cmp",
  --   after = { "copilot.lua" },
  --   config = function()
  --     require("copilot_cmp").setup()
  --   end,
  -- },
  --

  -- Codeium
  {
    'Exafunction/codeium.vim',
    event = 'BufEnter'
  },

  -- css color
  { "brenoprata10/nvim-highlight-colors" },

  -- themes
  {
    "gmr458/vscode_modern_theme.nvim",
    lazy = false,
    priority = 1000,
    config = function()
      require("vscode_modern").setup({
        cursorline = true,
        transparent_background = false,
        nvim_tree_darker = true,
      })
    end
  },
  -- { "NLKNguyen/papercolor-theme"},
  -- { "jacoborus/tender.vim" },
  -- { "rebelot/kanagawa.nvim" },
  -- { "rose-pine/neovim", name = "rose-pine"},
  -- { "navarasu/onedark.nvim",
  --   name = "onedark",
  --   config = function()
  --     require("onedark").setup {
  --       style = "darker"
  --     }
  --   end
  -- },

  -- git blame
  { "f-person/git-blame.nvim", name = "gitblame",
    event = "BufRead",
    config = function()
      vim.cmd "highlight default link gitblame SpecialComment"
      require("gitblame").setup { enabled = true }
    end,
  },

  -- todo comments
  {
    "folke/todo-comments.nvim",
    dependencies = { "nvim-lua/plenary.nvim" },
    opts = {
      signs = true,
      sign_priority = 8,
      keywords = {
        FIX = {
          icon = " ", -- icon used for the sign, and in search results
          color = "error", -- can be a hex color, or a named color (see below)
          alt = { "FIXME", "BUG", "FIXIT", "ISSUE", "ERROR" }, -- a set of other keywords that all map to this FIX keywords
          -- signs = false, -- configure signs for some keywords individually
        },
        TODO = { icon = " ", color = "info" },
        HACK = { icon = " ", color = "warning" },
        WARN = { icon = " ", color = "warning", alt = { "WARNING", "XXX" } },
        PERF = { icon = " ", alt = { "OPTIM", "PERFORMANCE", "OPTIMIZE" } },
        NOTE = { icon = " ", color = "hint", alt = { "INFO", "SEEHERE" } },
        TEST = { icon = "⏲ ", color = "test", alt = { "TESTING", "PASSED", "FAILED" } },
      },
      gui_style = {
        fg = "NONE",
        bg = "BOLD",
      },
      merge_keywords = true,
      highlight = {
        multiline = true, -- enable multine todo comments
        multiline_pattern = "^.", -- lua pattern to match the next multiline from the start of the matched keyword
        multiline_context = 10, -- extra lines that will be re-evaluated when changing a line
        before = "", -- "fg" or "bg" or empty
        keyword = "wide", -- "fg", "bg", "wide", "wide_bg", "wide_fg" or empty. (wide and wide_bg is the same as bg, but will also highlight surrounding characters, wide_fg acts accordingly but with fg)
        after = "fg", -- "fg" or "bg" or empty
        pattern = [[.*<(KEYWORDS)\s*:]], -- pattern or table of patterns, used for highlighting (vim regex)
        comments_only = true, -- uses treesitter to match keywords in comments only
        max_line_len = 400, -- ignore lines longer than this
        exclude = {}, -- list of file types to exclude highlighting
      },
      colors = {
        error = { "DiagnosticError", "ErrorMsg", "#DC2626" },
        warning = { "DiagnosticWarn", "WarningMsg", "#FBBF21" },
        info = { "DiagnosticInfo", "#2563EB" },
        hint = { "DiagnosticHint", "#10B981" },
        default = { "Identifier", "#7C3AED" },
        test = { "Identifier", "#FF00FF" }
      },
      search = {
        command = "rg",
        args = {
          "--color=never",
          "--no-heading",
          "--with-filename",
          "--line-number",
          "--column",
        },
    -- regex that will be used to match keywords.
    -- don't replace the (KEYWORDS) placeholder
        pattern = [[\b(KEYWORDS):]], -- ripgrep regex
      },
    },
    -- event = "BufRead",
    -- config = function()
    --   require("todo-comments").setup()
    -- end
  },

  -- discord
  { "vimsence/vimsence" }
}

-- basic settings ---------------------------------------------
vim.opt.wrap = true

-- hotkeys -----------------------
-- gt or gT to switch buffer
lvim.keys.normal_mode["gt"] = ":BufferLineCycleNext<CR>"
lvim.keys.normal_mode["gT"] = ":BufferLineCyclePrev<CR>"

-- alt + f to search words in files
lvim.keys.normal_mode["<A-f>"] = ":Telescope live_grep<CR>"

-- ctrl + c to close search highlight
lvim.keys.normal_mode["<C-n>"] = ":noh<CR>"
lvim.keys.normal_mode["<C-p>"] = ":Telescope find_files<CR>"

-- ctrl + t to open terminal
lvim.builtin.terminal.open_mapping = "<c-t>"
-- lvim.builtin.which_key.mappings["e"] = {"<cmd>NvimTreeFocus<CR>", "Explorer"}

---------------------------------------------------------------

-- colorscheme ------------------------------------------------
lvim.colorscheme = "vscode_modern"
---------------------------------------------------------------


-- css color --------------------------------------------------
require("nvim-highlight-colors").setup {}
---------------------------------------------------------------


-- status line ------------------------------------------------
-- local custom_jellybeans = require "lualine.themes.jellybeans"
-- local components = require "lvim.core.lualine.components"

local colors = {
  blue   = '#80a0ff',
  cyan   = '#79dac8',
  black  = '#080808',
  white  = '#c6c6c6',
  red    = '#ff5189',
  violet = '#d183e8',
  grey   = '#303030',
}

lvim.builtin.lualine.sections.lualine_c = { "filename" }
----------------------------------------------------------------


-- Setup GitHub Copilot ---------------------------------------
-- To logout copilot, remove ~/.config/github/hosts.json to delete auth information.
local ok, copilot = pcall(require, "copilot")
if not ok then
  return
end
copilot.setup {
  suggestion = {
    keymap = {
      accept = "<c-l>",
      next = "<c-j>",
      prev = "<c-k>",
      dismiss = "<c-h>",
    },
  },
}
local opts = { noremap = true, silent = true }
vim.api.nvim_set_keymap("n", "<c-s>", "<cmd>lua require('copilot.suggestion').toggle_auto_trigger()<CR>", opts)
---------------------------------------------------------------

-- discord ----------------------------------------------------
-- vim.g.vimsence_client_id = "Your Vimsence client ID"
vim.g.vimsence_small_text = "NeoVim"
vim.g.vimsence_small_image = "neovim"
vim.g.vimsence_editing_details = "Editing: {}"
vim.g.vimsence_editing_state = "Working on: {}"
vim.g.vimsence_file_explorer_text = "In NERDTree"
vim.g.vimsence_file_explorer_details = "Looking for files"
---------------------------------------------------------------

儲存後再次使用 lvim 打開 LunarVim,就會自動重載 config.lua 當中的插件了。

Read more

《逆思維》4 - 如何贏得辯論並影響他人?

《逆思維》4 - 如何贏得辯論並影響他人?

好久不見,這個網站已經好一陣子沒有更新一些東西了。 在寫這篇文的時候,我一直在思考一個問題:我是該憑著我讀完的印象來寫呢?還是我可以 open book 邊寫邊重新閱讀這本書,從作者的文字中尋找靈感呢?我想了一下覺得邊寫邊重新閱讀好像挺好的,可以幫助我補足第一次閱讀時沒有理解到、沒有掌握到的道理。 這章的主軸是一個 AI 與人類辯士之間的辯論賽,主題為政府是否該補助幼兒園。AI 為正方,人類為反方。AI 擅長理性地分析大量的資料,並且給出有利的證據來佐證自己的論點。有那麼齊全的資料,以及強大的計算能力,看起來 AI 是贏定了;然而辯論到後面,有許多評審、觀眾的想法反而被帶到人類辯士的這邊。是為什麼呢? 想要說服一個人、給人建議,數據與分析當然是必須的。但也要考量我們人類並不是完全理性的,舉再多的數據佐證,不會買單的人就是不會買單。有沒有什麼好方法可以說動一個人呢?作者研究了談判專家,和普通的談判者在談判中所使用到的技巧,發現談判專家們並沒有花特別大的篇幅提供證據、分析、數據那些東西。而是談判專家會找到雙方立場的共同點,並且以提問表達出好奇,引發雙方有更進一步的思考,對方心裡可能也

By Shiangogo
搶救網站大作戰!

搶救網站大作戰!

去年大約 12 月底,我用了 Zeabur 這個平台架設我的網站。因為有先人寫好了 Ghost 的 infra 模板,所以我架設起來非常方便快速。 原本是抱持著一個白嫖免費服務的心態使用 Zeabur 的,結果今天心血來潮想進網站看一看,才發現服務掛掉了。進到 Zeabur Dashboard 一看,發現已經頂到免費額度上限,服務被自動停用了!為了避免資料被自動刪除,話不多說,只好先課金 5 美金了! 課完金之後,我嘗試重啟主服務,似乎也遇到了問題,主服務一直崩潰重啟。這不曉得是 Zeabur 平台不夠完善,還是 Infra 的 Template 寫得有問題。我想,既然主服務有狀況,先確保 DB 資料安全應該比較實在。 還好 MySQL 服務是活著的,目前的策略是先用 Zeabur

By Shiangogo
《逆思維》3 - 想法有誤的喜悅

《逆思維》3 - 想法有誤的喜悅

承認自己的錯誤,並且拋棄成見,不再堅持「信念」,才能讓我們更接近真相。 在《逆思維》這本書的第三章,最令我印象深刻的例子,就是對於 2016 年美國總統大選前,共和黨內初選結果的預測。當時沒有人認為川普會贏得初選,甚至把川普當成是笑話。但是有一位預測專家尚皮耶・波岡斯認為川普高機率會贏。為什麼大家都預測錯,但是尚皮耶可以正確預測呢?大家都被「信念」蒙蔽了雙眼,沒辦法真的客觀、理性地探究事情的本質。 很多時候我們遇到的事情都有兩面性,或是多面性。但我們寧願只看其中的一面,並且死死抓著它,讓它成為一切推論的不可動搖的根基。這種信念有可能出現問題,與事實相去甚遠,但我們礙於面子、自尊心,沒辦法調整自己的看法,並承認自己的錯誤。 舉個近期的例子,就是去年 2025 年,在台灣由民進黨發起的大罷免行動。在罷免投票的前一兩週,罷團的聲勢相當浩大,每天搭車在捷運站外都會看到他們的宣傳。那時,就連反對罷免的我也認為國民黨和民眾黨完蛋了。然而罷免開票結果卻讓罷免支持者集體崩潰,完全沒有立委被罷免成功。在路邊、網路上,鋪天蓋地地宣傳大罷免,

By Shiangogo
手把手教你用 GitHub Pages 搭建網站!

手把手教你用 GitHub Pages 搭建網站!

我們公司內部的讀書分享會,有同事用AI vibe coding 把簡報做成網頁 HTML 的形式。我心想這也太酷了吧,我也來搞一個試試看!感謝好心的同事有留下那個簡報的原始碼,我也用 vibe coding 把那份簡報魔改成一個讀書會簡報編輯器,並且輸出了一份 HTML 簡報。 我覺得把簡報做成網頁的優點很明顯,不需要安裝任何簡報軟體,只要有瀏覽器,輸入 URL 就可以使用簡報,而且想玩什麼花樣,都可以用 CSS、JavaScript 去寫;但同樣的,缺點也很大——也就是把 HTML 做出來。把 HTML 做出來之後還會遇到問題,如果只是用瀏覽器打開這個 HTML,YouTube 的影片會沒辦法播放。 把簡報做成 HTML 的這個難點,我相信可以透過 AI 來解決。或是說簡報軟體也可以把做好的東西轉換成 HTML?這個我就沒有研究了,但我猜應該有類似功能。

By Shiangogo