├── .gitignore ├── .stylua.toml ├── LICENSE ├── README.md ├── README_zh.md ├── init.lua ├── lua ├── core │ ├── basic.lua │ ├── commands.lua │ ├── ft.lua │ ├── health.lua │ ├── init.lua │ ├── keymap.lua │ ├── miscellaneous.lua │ ├── symbols.lua │ └── utils.lua ├── lsp │ ├── completion.lua │ ├── extra.lua │ ├── format.lua │ ├── init.lua │ ├── lsp.lua │ └── plugins.lua └── plugins │ ├── colorscheme.lua │ ├── config.lua │ ├── init.lua │ ├── keymap.lua │ ├── lazy.lua │ └── utils.lua └── screenshots ├── 1.jpg ├── 2.jpg ├── 3.jpg └── 4.jpg /.gitignore: -------------------------------------------------------------------------------- 1 | lazy-lock.json 2 | lua/custom/ 3 | bin/ 4 | -------------------------------------------------------------------------------- /.stylua.toml: -------------------------------------------------------------------------------- 1 | column_width = 120 2 | line_endings = "Unix" 3 | indent_type = "Spaces" 4 | indent_width = 4 5 | quote_style = "AutoPreferDouble" 6 | call_parentheses = "None" 7 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023-2025 Shaobin Jiang 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

IceNvim

2 | 3 |
4 | 5 | *Please consider starring the project ✨✨✨. Your support is much appreciated.* 6 | 7 | [![Minimum Neovim Version](https://img.shields.io/badge/Minimum_Neovim_Version-0.11.0-blueviolet.svg?style=for-the-badge&color=90E59A&logoColor=white)](https://github.com/neovim/neovim) 8 | [![Supported OS](https://img.shields.io/badge/Supported_OS-MacOS_/_Windows_/_Linux-blueviolet.svg?style=for-the-badge&color=violet&logoColor=white)](https://github.com/neovim/neovim) 9 | [![GitHub License](https://img.shields.io/github/license/Shaobin-Jiang/IceNvim?style=for-the-badge&color=EE999F&logoColor=white)](https://github.com/Shaobin-Jiang/IceNvim/blob/master/LICENSE) 10 | 11 |
12 | 13 | Read this in other languages: [English](README.md) / [中文](README_zh.md) 14 | 15 | IceNvim is a beautiful, powerful and customizable neovim config. Powerful, yet blazing fast. 16 | 17 | For a detailed introduction on the various features of and on how to use IceNvim, refer to the [wiki](https://github.com/Shaobin-Jiang/IceNvim/wiki/Introduction). 18 | 19 | ## Showcase 20 | 21 | ![](./screenshots/1.jpg) 22 | 23 | ![](./screenshots/2.jpg) 24 | 25 | ![](./screenshots/3.jpg) 26 | 27 | ![](./screenshots/4.jpg) 28 | 29 | ## Features 30 | 31 | - Ideal for development: 32 | - Set up for C# / Flutter / Lua / Python / Rust / Web development and markdown writing 33 | - Git integration 34 | - Enhanced editing experience: 35 | - Plugins such as `hop.nvim`, `undotree` and `nvim-surround` 36 | - For Chinese users, automatic IME switching when changing modes (needs [additional setup](#mark-im-select)) 37 | - Nice looks: 38 | - Multiple colorschemes made ready 39 | - A custom colorschemes picker 40 | - User friendly: 41 | - Uses which-key.nvim for new comers to check out keymaps 42 | - Provides a health check for new comers to locate missing dependencies 43 | - Well equiped: 44 | - An icon viewer to check whether your font works well with icons 45 | - A configuration file selector 46 | - Modern: uses `Lazy` and `Mason` 47 | - Customizable: 48 | - Easily override defaults with your own [config file](#mark-custom-configuration) 49 | 50 | ## Requirements 51 | 52 | - IceNvim requires neovim **0.10.0+** or newer 53 | - Additionally, you need to install these also: 54 | - A [nerd font](https://www.nerdfonts.com/font-downloads): this is optional, but things may look funny without one installed 55 | - git: almost all the plugin and lsp installations depend on it 56 | - Required by Mason: 57 | - curl 58 | - gzip / 7zip 59 | - wget 60 | - Required by telescope: 61 | - fd 62 | - ripgrep (also required by grug-far.nvim) 63 | - Required by nvim treesitter: 64 | - gcc 65 | - cmake 66 | - node 67 | - npm 68 | - Required by markdown-preview.nvim: 69 | - yarn 70 | - Required by rustaceanvim: 71 | - rust-analyzer (NOT the rust-analyzer provided by Mason!!!) 72 | - python3 and pip3 73 | - Additional dependencies on Linux / WSL: 74 | - unzip 75 | - python virtual environment 76 | - xclip (for accessing system clipboard) 77 | - zip 78 | 79 | Note that some of the packages might have different names with different package managers! 80 | 81 | Installing dependencies on MacOS: 82 | 83 | ```bash 84 | brew install wget fd ripgrep node yarn cmake 85 | ``` 86 | 87 | Installing dependencies on Arch: 88 | 89 | ```bash 90 | sudo pacman -S --needed curl gzip wget fd ripgrep gcc nodejs npm python python-pip unzip zip xclip python-virtualenv 91 | ``` 92 | 93 | Installing dependencies on Windows (via scoop): 94 | 95 | ```bash 96 | scoop install curl gzip wget fd ripgrep mingw nodejs-lts python 97 | ``` 98 | 99 | To verify if these are installed, try opening neovim with `nvim --noplugin` and then running `IceHealth` after following the installation instruction in the next section. 100 | 101 | ## Installation 102 | 103 | On Windows: 104 | 105 | ```bash 106 | git clone https://github.com/Shaobin-Jiang/IceNvim "$env:LOCALAPPDATA\nvim" 107 | ``` 108 | 109 | On Linux / MacOS: 110 | 111 | ```bash 112 | git clone https://github.com/Shaobin-Jiang/IceNvim ~/.config/nvim 113 | ``` 114 | 115 |

Download `im-select.exe` (recommended for windows / wsl users)

116 | 117 | For automatic IME switching when inputing Chinese, im-select.exe is needed. 118 | 119 | Download it from [https://github.com/daipeihust/im-select/raw/master/win/out/x86/im-select.exe](https://github.com/daipeihust/im-select/raw/master/win/out/x86/im-select.exe) and place to the `bin` directory in the configuration directory. 120 | 121 | Additionally, if you are using wsl, you might have to do this: 122 | 123 | ```bash 124 | chmod +x ~/.config/nvim/bin/im-select.exe 125 | ``` 126 | 127 | ### Install `macism` (recommended for MacOS users) 128 | 129 | Macism can be installed via the command below: 130 | 131 | ```bash 132 | brew tap laishulu/homebrew 133 | brew install macism 134 | ``` 135 | 136 | Note that: 137 | 138 | - The first time you use this function, MacOS will popup a window asking you to grant permission of Accessibility 139 | - You need to enable the MacOS keyboard shortcut for "Select the previous input source", which can be found in "Preference -> Keyboard -> Shortcuts -> Input Source" 140 | 141 | ### Download `uclip.exe` (recommended for windows / wsl users) 142 | 143 | Although text yanked from within IceNvim is already available from outside, one might find that unicode characters are not copied properly on Windows and WSL. This is because the functionality is dealt with by Windows' `CLIP` command which does a poor job when used with utf-8 characters. 144 | 145 | To solve this, one might need to download [uclip.exe](https://github.com/suzusime/uclip/releases/download/v0.1.0/uclip.exe) and place it in the `bin` directory in the configuration directory. 146 | 147 | Additionally, if you are using wsl, you might have to do this: 148 | 149 | ```bash 150 | chmod +x ~/.config/nvim/bin/uclip.exe 151 | ``` 152 | 153 |

Custom Configuration

154 | 155 | This neovim configuration allows users to override the default configuration by creating a `custom` dir under `lua/`. 156 | 157 | IceNvim will try to detect and load `custom/init.lua`. Since `custom/` is git-ignored, it will be easy for you to make your own configurations without messing up the original git repo and missing follow-up updates. 158 | 159 | Most IceNvim config options can be found under a global variable `Ice`. The entire setup follows this routine: 160 | 161 | - IceNvim sets its default options and store some of them, e.g., plugin config and keymaps, in `Ice` 162 | - IceNvim loads `custom/init.lua` 163 | - IceNvim uses `Ice` to set up plugins and create keymaps 164 | 165 | Therefore, almost everything IceNvim defines can be re-configured by you. 166 | 167 | An example `custom/init.lua`: 168 | 169 | ```lua 170 | Ice.plugins["nvim-transparent"].enabled = false 171 | 172 | Ice.keymap.general.open_terminal = { "n", "terminal", ":split term://bash" } 173 | 174 | local autogroup = vim.api.nvim_create_augroup("OverrideFtplugin", { clear = true }) 175 | vim.api.nvim_create_autocmd("BufEnter", { 176 | group = autogroup, 177 | callback = function() 178 | if vim.bo.filetype == "lua" then 179 | vim.cmd "setlocal colorcolumn=120" 180 | end 181 | end, 182 | }) 183 | ``` 184 | 185 | ## Troubleshooting 186 | 187 | ### Alt-Combination Keys Not Working in Kitty for MacOS 188 | 189 | Add `macos_option_as_alt yes` to your `kitty.conf`. 190 | 191 | ### Installing Omnisharp / Csharpier 192 | 193 | When installing omnisharp, make sure that dotnet sdk is installed. 194 | 195 | When receiving nuget-related errors when installing csharpier, you might have to configure nuget source (see [https://learn.microsoft.com/zh-cn/nuget/reference/errors-and-warnings/nu1100#solution-2](https://learn.microsoft.com/zh-cn/nuget/reference/errors-and-warnings/nu1100#solution-2)): 196 | 197 | ```shell 198 | dotnet nuget add source https://api.nuget.org/v3/index.json -n nuget.org 199 | ``` 200 | 201 | ### Rust Not Working Properly 202 | 203 | You need to check how you installed rust. I have not been able to set up rust-analyzer when installing rust only (e.g., via `scoop install rust` or `sudo zypper in rust`) either, but with the officially recommended way, i.e., by installing rustup, everything works properly. 204 | 205 | Also, you might find that completion does not work when first opening a rust project. That is because some time needs to be taken to index the code, and completion would only work after indexing is done. 206 | 207 | ### Installation Failure for typst-preview.nvim 208 | 209 | When installing [typst-preview.nvim](https://github.com/chomosuke/typst-preview.nvim), you might have this error: `Downloading typst-preview binary failed, exit code: 35`. This might be due to the use of proxies. Shut down softwares of such kind and run this command again: 210 | 211 | ```vim 212 | lua require("typst-preview").update() 213 | ``` 214 | 215 | ## Star History 216 | 217 | 218 | 219 | 220 | 221 | Star History Chart 222 | 223 | 224 | -------------------------------------------------------------------------------- /README_zh.md: -------------------------------------------------------------------------------- 1 |

IceNvim

2 | 3 |
4 | 5 | *如果可以的话,可以点一个 star✨✨✨。谢谢您的支持~* 6 | 7 | [![最低neovim版本](https://img.shields.io/badge/Minimum_Neovim_Version-0.11.0-blueviolet.svg?style=flat-square&color=90E59A&logoColor=white)](https://github.com/neovim/neovim) 8 | [![支持的最新版本](https://img.shields.io/badge/Latest_Supported_Version-0.10.0-blueviolet.svg?style=flat-square&color=90E59A&logoColor=white)](https://github.com/neovim/neovim) 9 | [![GitHub License](https://img.shields.io/github/license/Shaobin-Jiang/IceNvim?style=flat-square&color=EE999F&logoColor=white)](https://github.com/Shaobin-Jiang/IceNvim/blob/master/LICENSE) 10 | 11 |
12 | 13 | 通过其他语言查看:[English](README.md) / [中文](README_zh.md) 14 | 15 | IceNvim 是一个美观、功能强大、支持高度自定义的 neovim 配置,且流畅迅速。 16 | 17 | 如果想要更详细地了解 IceNvim 的一些特性和使用方法,可以参考 [wiki](https://github.com/Shaobin-Jiang/IceNvim/wiki/Introduction)(目前仅有英文版本,但之后计划重写了)。 18 | 19 | ## 截图 20 | 21 | ![](./screenshots/1.jpg) 22 | 23 | ![](./screenshots/2.jpg) 24 | 25 | ![](./screenshots/3.jpg) 26 | 27 | ![](./screenshots/4.jpg) 28 | 29 | ## 特性 30 | 31 | - 适合开发工作: 32 | - 支持 C# / Flutter / Lua / Python / Rust / Web 开发以及 markdown 编辑 33 | - 结合了 Git 34 | - 更好的编辑体验 35 | - 使用了 `hop.nvim`、`undotree`、`nvim-surround` 等插件 36 | - 针对中文用户,添加了切换模式时自动切换输入法的功能(需要[额外配置](#mark-im-select)) 37 | - 美观: 38 | - 预装了多种主题 39 | - 主题切换工具 40 | - 用户友好: 41 | - 使用 which-key.nvim 显示快捷键 42 | - 预装了 health check,可以帮助新用户检查是否缺少某些依赖 43 | - 功能齐全 44 | - 提供了查看图标的工具,用来检查字体是否和图标兼容 45 | - 一键打开配置文件的工具 46 | - 现代:使用 `Lazy` 和 `Mason` 47 | - 自定义: 48 | - 轻松使用自己的 [配置文件](#mark-custom-configuration) 覆盖默认设置 49 | 50 | ## 依赖 51 | 52 | - IceNvim 需要 neovim **0.9.0+**,不过 **0.10.0+** 更好 53 | - 此外,你需要安装以下内容: 54 | - 一款 [nerd font](https://www.nerdfonts.com/font-downloads):可选,但是如果不安装看起来会比较奇怪 55 | - git:几乎所有的插件和 lsp 安装都需要它 56 | - Mason 的依赖项: 57 | - curl 58 | - gzip / 7zip 59 | - wget 60 | - telescope 的依赖项 61 | - fd 62 | - ripgrep (也是 grug-far.nvim 的依赖项) 63 | - nvim treesitter 的依赖项 64 | - gcc 65 | - cmake 66 | - node 67 | - npm 68 | - markdown-preview.nvim 的依赖项 69 | - yarn 70 | - rustaceanvim 的依赖项 71 | - rust-analyzer (不是 Mason 提供的那个!!!) 72 | - python3 和 pip3 73 | - Linux / WSL 上的额外依赖 74 | - unzip 75 | - python 虚拟环境 76 | - xclip(用来访问系统剪贴板) 77 | - zip 78 | 79 | 注意,不同包管理器里这些包的名称可能有所不同! 80 | 81 | 在 MacOS 上安装依赖: 82 | 83 | ```bash 84 | brew install wget fd ripgrep node yarn cmake 85 | ``` 86 | 87 | 在 Arch 上安装依赖: 88 | 89 | ```bash 90 | sudo pacman -S --needed curl gzip wget fd ripgrep gcc nodejs npm python python-pip unzip zip xclip python-virtualenv 91 | ``` 92 | 93 | 在 Windows 上安装依赖(使用 scoop): 94 | 95 | ```bash 96 | scoop install curl gzip wget fd ripgrep mingw nodejs-lts python 97 | ``` 98 | 99 | 如果需要确认依赖项是否被正确安装,可以在按照下一节内容安装完毕后,通过 `nvim --noplugin` 启动 neovim 并运行 `IceHealth`。 100 | 101 | ## 安装 102 | 103 | Windows: 104 | 105 | ```bash 106 | git clone https://github.com/Shaobin-Jiang/IceNvim "$env:LOCALAPPDATA\nvim" 107 | ``` 108 | 109 | Linux / MacOS: 110 | 111 | ```bash 112 | git clone https://github.com/Shaobin-Jiang/IceNvim ~/.config/nvim 113 | ``` 114 | 115 |

下载 `im-select.exe` (推荐 windows / wsl 用户安装)

116 | 117 | 如果想要在使用中文输入的时候进行自动化的输入法切换则需要 im-select.exe。 118 | 119 | 从 [https://github.com/daipeihust/im-select/raw/master/win/out/x86/im-select.exe](https://github.com/daipeihust/im-select/raw/master/win/out/x86/im-select.exe) 下载并放到配置文件夹下的 `bin` 文件夹内部。 120 | 121 | 此外,如果你使用的是 wsl,还需要运行这行命令: 122 | 123 | ```bash 124 | chmod +x ~/.config/nvim/bin/im-select.exe 125 | ``` 126 | 127 | ### 安装 `macism` (推荐 MacOS 用户安装) 128 | 129 | 你可以通过下面的命令安装 macism: 130 | 131 | ```bash 132 | brew tap laishulu/homebrew 133 | brew install macism 134 | ``` 135 | 136 | 请注意: 137 | 138 | - 第一次使用这个功能的时候,MacOS 会弹出窗口请求相应权限 139 | - 你需要启用“选择上一个输入法”快捷键,启用选项在“系统设置 -> 键盘 -> 键盘快捷键 -> 输入法” 中可以找到 140 | 141 | ### 下载 `uclip.exe` (推荐 windows / wsl 用户安装) 142 | 143 | 尽管外部程序可以使用 IceNvim 内部复制的文字,你可能会发现在 Windows 和 WSL 上复制的 unicode 无法被正确粘贴,这是因为这一功能是由 Windows 的 `CLIP` 命令处理的,这个命令处理 utf-8 字符很差。 144 | 145 | 如果要解决这个问题,你需要下载 [uclip.exe](https://github.com/suzusime/uclip/releases/download/v0.1.0/uclip.exe) 并放到配置文件夹下的 `bin` 文件夹内部。 146 | 147 | 此外,如果你使用的是 wsl,还需要运行这行命令: 148 | 149 | ```bash 150 | chmod +x ~/.config/nvim/bin/uclip.exe 151 | ``` 152 | 153 |

自定义配置

154 | 155 | 我们允许你通过在 `lua/` 下创建一个 `custom` 文件夹来覆盖默认的配置。 156 | 157 | IceNvim 会尝试检测并加载 `custom/init.lua`。`custom/` 不会被 git 追踪,所以你可以放心地编写自己的配置,不用担心弄乱原本的 git 仓库以及错过后续更新。 158 | 159 | IceNvim 多数的配置内容可以在全局变量 `Ice` 下找到。IceNvim 遵循以下流程进行配置: 160 | 161 | - IceNvim 进行一些默认项的设置,并把其中一些内容存储到 `Ice` 中,比如插件配置和快捷键(此时尚未生效) 162 | - IceNvim 加载 `custom/init.lua` 163 | - IceNvim 使用 `Ice` 里的内容加载插件、创建快捷键 164 | 165 | 因此,IceNvim 定义的所有内容几乎都可以由你进行重新配置。 166 | 167 | 一份示例的 `custom/init.lua`: 168 | 169 | ```lua 170 | Ice.plugins["nvim-transparent"].enabled = false 171 | 172 | Ice.keymap.general.open_terminal = { "n","terminal", ":split term://bash" } 173 | 174 | local autogroup = vim.api.nvim_create_augroup("OverrideFtplugin",{ clear = true }) 175 | vim.api.nvim_create_autocmd("BufEnter",{ 176 | group = autogroup, 177 | callback = function() 178 | if vim.bo.filetype == "lua" then 179 | vim.cmd "setlocal colorcolumn=120" 180 | end 181 | end, 182 | }) 183 | ``` 184 | 185 | ## 可能的问题 186 | 187 | ### Alt 相关的快捷键在 MacOS 的 Kitty 上不生效 188 | 189 | 在 `kitty.conf` 中添加 `macos_option_as_alt yes`。 190 | 191 | ### 安装 Omnisharp / Csharpier 192 | 193 | 安装 omnisharp 的时候需要确保你已经安装了 dotnet sdk。 194 | 195 | 安装 csharpier 的时候如果遇到了 nuget 相关的错误,可能需要配置 nuget 源(见 [https://learn.microsoft.com/zh-cn/nuget/reference/errors-and-warnings/nu1100#solution-2](https://learn.microsoft.com/zh-cn/nuget/reference/errors-and-warnings/nu1100#solution-2)): 196 | 197 | ```shell 198 | dotnet nuget add source https://api.nuget.org/v3/index.json -n nuget.org 199 | ``` 200 | 201 | ### Rust 支持有问题 202 | 203 | 你可能需要检查一下你是怎么安装 rust 的。我在仅安装了 rust (例如,通过 `scoop install rust` 或者 `sudo zypper in rust`)的时候也没法正常使用 rust-analyzer,但是如果通过官方推荐的方式——即安装 rustup——就可以了。 204 | 205 | 此外,你可能会发现第一次打开 rust 项目的时候补全功能不能正常工作。这是因为索引代码需要时间,索引结束后才能开始补全。 206 | 207 | ### 安装 typst-preview.nvim 失败 208 | 209 | 安装 [typst-preview.nvim](https://github.com/chomosuke/typst-preview.nvim) 的时候,你可能会遇到这样的报错:`Downloading typst-preview binary failed, exit code: 35`。这可能是因为你使用了代理,你可以把代理软件关掉然后运行下面的命令: 210 | 211 | ```vim 212 | lua require("typst-preview").update() 213 | ``` 214 | 215 | ## Star 走势 216 | 217 | 218 | 219 | 220 | 221 | Star History Chart 222 | 223 | 224 | -------------------------------------------------------------------------------- /init.lua: -------------------------------------------------------------------------------- 1 | Ice = {} 2 | 3 | require "core.init" 4 | require "plugins.init" 5 | 6 | -- Load user configuration files 7 | local config_root = string.gsub(vim.fn.stdpath "config", "\\", "/") 8 | if not vim.api.nvim_get_runtime_file("lua/custom/", false)[1] then 9 | os.execute('mkdir "' .. config_root .. '/lua/custom"') 10 | end 11 | 12 | local custom_path = config_root .. "/lua/custom/" 13 | if require("core.utils").file_exists(custom_path .. "init.lua") then 14 | require "custom.init" 15 | end 16 | 17 | -- Define keymap 18 | local keymap = Ice.keymap.general 19 | require("core.utils").group_map(keymap) 20 | 21 | for filetype, config in pairs(Ice.ft) do 22 | require("core.utils").ft(filetype, config) 23 | end 24 | 25 | -- Only load plugins and colorscheme when --noplugin arg is not present 26 | if not require("core.utils").noplugin then 27 | -- Load plugins 28 | require("lazy").setup(vim.tbl_values(Ice.plugins), Ice.lazy) 29 | 30 | vim.api.nvim_create_autocmd("User", { 31 | once = true, 32 | pattern = "VeryLazy", 33 | callback = function() 34 | local rtp_plugin_path = vim.opt.packpath:get()[1] .. "/plugin" 35 | local dir = vim.uv.fs_scandir(rtp_plugin_path) 36 | if dir ~= nil then 37 | while true do 38 | local plugin = vim.uv.fs_scandir_next(dir) 39 | if plugin == nil then 40 | break 41 | else 42 | vim.cmd(string.format("source %s/%s", rtp_plugin_path, plugin)) 43 | end 44 | end 45 | end 46 | 47 | require("core.utils").group_map(Ice.keymap.plugins) 48 | 49 | -- Define colorscheme 50 | if not Ice.colorscheme then 51 | local colorscheme_cache = vim.fn.stdpath "data" .. "/colorscheme" 52 | if require("core.utils").file_exists(colorscheme_cache) then 53 | local colorscheme_cache_file = io.open(colorscheme_cache, "r") 54 | ---@diagnostic disable: need-check-nil 55 | local colorscheme = colorscheme_cache_file:read "*a" 56 | colorscheme_cache_file:close() 57 | Ice.colorscheme = colorscheme 58 | else 59 | Ice.colorscheme = "tokyonight" 60 | end 61 | end 62 | 63 | require("plugins.utils").colorscheme(Ice.colorscheme) 64 | end, 65 | }) 66 | end 67 | 68 | -- Prepend this to runtimepath last as it would be overridden by lazy otherwise 69 | vim.opt.rtp:prepend(custom_path) 70 | -------------------------------------------------------------------------------- /lua/core/basic.lua: -------------------------------------------------------------------------------- 1 | local g = vim.g 2 | local opt = vim.opt 3 | 4 | -- This MUST NOT be en_US, but en_US.UTF-8 5 | -- I originally set it to en_US without UTF-8 and `yGp` ceased to work 6 | -- It just threw an 'E353: Nothing in register "' error at me 7 | vim.cmd "language en_US.UTF-8" 8 | 9 | vim.cmd "syntax off" 10 | g.encoding = "UTF-8" 11 | opt.fileencoding = "utf-8" 12 | 13 | local win_height = vim.fn.winheight(0) 14 | opt.scrolloff = math.floor((win_height - 1) / 2) 15 | opt.sidescrolloff = math.floor((win_height - 1) / 2) 16 | 17 | opt.number = true 18 | opt.relativenumber = true 19 | 20 | opt.cursorline = true 21 | 22 | opt.signcolumn = "yes" 23 | 24 | opt.colorcolumn = "80" 25 | 26 | opt.tabstop = 4 27 | opt.shiftwidth = 0 28 | opt.expandtab = true 29 | opt.shiftround = true 30 | 31 | -- Case insensitive searching when no upper case character is present 32 | opt.ignorecase = true 33 | opt.smartcase = true 34 | 35 | -- Disable the ugly highlight during searches 36 | opt.hlsearch = false 37 | 38 | opt.cmdheight = 1 39 | opt.cmdwinheight = 1 40 | 41 | -- Auto load the file when modified externally 42 | opt.autoread = true 43 | 44 | -- Use left / right arrow to move to the previous / next line when at the start 45 | -- or end of a line. 46 | -- See doc (:help 'whichwrap') 47 | opt.whichwrap = "<,>,[,]" 48 | 49 | -- Allow hiding modified buffer 50 | opt.hidden = true 51 | 52 | -- Add mouse support for all modes 53 | opt.mouse = "a" 54 | opt.mousemodel = "extend" 55 | 56 | opt.backup = false 57 | opt.writebackup = false 58 | opt.swapfile = false 59 | 60 | -- Time to wait for a sequence of key combination 61 | opt.timeoutlen = 500 62 | 63 | -- Split window from below and right 64 | opt.splitbelow = true 65 | opt.splitright = true 66 | 67 | opt.termguicolors = true 68 | 69 | -- Do not display the character "W" before search count 70 | opt.shortmess = vim.o.shortmess .. "s" 71 | 72 | -- Maximum of 16 lines of prompt 73 | -- This affects both neovim's native completion and that of nvim-cmp 74 | opt.pumheight = 16 75 | 76 | -- Always show tab line 77 | -- Otherwise, when bufferline is loaded, it will "flash" a bit initially 78 | opt.showtabline = 2 79 | opt.tabline = "%!''" 80 | 81 | opt.showmode = false 82 | 83 | opt.nrformats = "bin,hex,alpha" 84 | 85 | opt.foldlevel = 99 86 | opt.foldlevelstart = 99 87 | opt.foldenable = false 88 | 89 | if require("core.utils").is_windows then 90 | opt.shellslash = true 91 | end 92 | 93 | vim.api.nvim_create_autocmd("TermOpen", { 94 | callback = function() 95 | vim.wo.number = false 96 | vim.wo.relativenumber = false 97 | end, 98 | }) 99 | 100 | opt.shadafile = "NONE" 101 | vim.api.nvim_create_autocmd({ "CmdlineEnter", "CmdwinEnter" }, { 102 | once = true, 103 | callback = function() 104 | local shada = vim.fn.stdpath "state" .. "/shada/main.shada" 105 | vim.o.shadafile = shada 106 | vim.cmd("rshada! " .. shada) 107 | end, 108 | }) 109 | 110 | vim.api.nvim_create_autocmd("CmdwinEnter", { 111 | callback = function() 112 | vim.cmd "startinsert" 113 | vim.wo.number = false 114 | vim.wo.relativenumber = false 115 | end, 116 | }) 117 | 118 | vim.api.nvim_create_autocmd("WinNew", { 119 | callback = function() 120 | vim.wo.wrap = false 121 | end 122 | }) 123 | 124 | -- WinNew is not triggered for the first window 125 | vim.wo.wrap = false 126 | -------------------------------------------------------------------------------- /lua/core/commands.lua: -------------------------------------------------------------------------------- 1 | vim.api.nvim_create_user_command("IceAbout", function() 2 | local buf = vim.api.nvim_create_buf(false, true) 3 | vim.keymap.set("n", "q", "c", { buffer = buf }) 4 | 5 | local win_width = vim.fn.winwidth(0) 6 | local win_height = vim.fn.winheight(0) 7 | local width = 80 8 | local height = math.floor(win_height * 0.3) 9 | local left = math.floor((win_width - width) / 2) 10 | local top = math.floor((win_height - height) / 2) 11 | 12 | vim.api.nvim_buf_set_lines(buf, 0, -1, false, { 13 | "", 14 | "A beautiful, powerful and highly customizable neovim config.", 15 | "", 16 | "Author: Shaobin Jiang", 17 | "", 18 | "Url: https://github.com/Shaobin-Jiang/IceNvim", 19 | "", 20 | string.format("Copyright © 2022-%s Shaobin Jiang", os.date "%Y"), 21 | }) 22 | 23 | local win = vim.api.nvim_open_win(buf, true, { 24 | relative = "win", 25 | width = width, 26 | height = height, 27 | row = top, 28 | col = left, 29 | border = "rounded", 30 | title = "About IceNvim", 31 | title_pos = "center", 32 | footer = "Press q to close window", 33 | footer_pos = "center", 34 | }) 35 | 36 | vim.api.nvim_set_option_value("number", false, { win = win }) 37 | vim.api.nvim_set_option_value("relativenumber", false, { win = win }) 38 | vim.api.nvim_set_option_value("modifiable", false, { buf = buf }) 39 | end, { nargs = 0 }) 40 | 41 | vim.api.nvim_create_user_command("IceCheckIcons", function() 42 | local buf = vim.api.nvim_create_buf(false, true) 43 | vim.keymap.set("n", "q", "c", { buffer = buf }) 44 | 45 | local item_width = 24 46 | local item_name_width = 18 47 | local win_width = vim.fn.winwidth(0) 48 | local win_height = vim.fn.winheight(0) 49 | local columns = math.floor(win_width / item_width) - 1 50 | 51 | local content = {} 52 | local items_in_row = 0 53 | local line = "" 54 | local item_number = 0 55 | for name, icon in require("core.utils").ordered_pair(Ice.symbols) do 56 | item_number = item_number + 1 57 | line = string.format( 58 | "%s%s%s%s%s", 59 | line, 60 | name, 61 | string.rep(" ", item_name_width - #name), 62 | icon, 63 | string.rep(" ", item_width - item_name_width - vim.fn.strdisplaywidth(icon)) 64 | ) 65 | 66 | items_in_row = items_in_row + 1 67 | 68 | if items_in_row == columns then 69 | content[#content + 1] = line 70 | items_in_row = 0 71 | line = "" 72 | end 73 | end 74 | 75 | vim.api.nvim_buf_set_lines(buf, 0, -1, false, content) 76 | 77 | local width = columns * item_width 78 | local height = math.ceil(item_number / columns) 79 | local left = math.floor((win_width - width) / 2) 80 | local top = math.floor((win_height - height) / 2) 81 | 82 | local win = vim.api.nvim_open_win(buf, true, { 83 | relative = "win", 84 | width = width, 85 | height = height, 86 | row = top, 87 | col = left, 88 | border = "rounded", 89 | title = "Check Nerd Font Icons", 90 | title_pos = "center", 91 | footer = "Press q to close window", 92 | footer_pos = "center", 93 | }) 94 | 95 | vim.api.nvim_set_option_value("number", false, { win = win }) 96 | vim.api.nvim_set_option_value("relativenumber", false, { win = win }) 97 | vim.api.nvim_set_option_value("wrap", false, { win = win }) 98 | vim.api.nvim_set_option_value("modifiable", false, { buf = buf }) 99 | end, { nargs = 0 }) 100 | 101 | vim.api.nvim_create_user_command("IceCheckPlugins", function() 102 | local plugins_path = vim.fn.stdpath "data" .. "/lazy/" 103 | local dir = vim.uv.fs_scandir(plugins_path) 104 | 105 | local stale_plugins = {} 106 | 107 | local plugin_count = 0 108 | 109 | if dir ~= nil then 110 | local co = coroutine.create(function() 111 | local checked_plugin_count = 1 112 | while plugin_count > checked_plugin_count do 113 | coroutine.yield() 114 | checked_plugin_count = checked_plugin_count + 1 115 | end 116 | 117 | table.sort(stale_plugins, function(a, b) 118 | return a[2] > b[2] 119 | end) 120 | 121 | local really_stale_plugin_count = 0 122 | local report = vim.tbl_map(function(entry) 123 | if entry[2] > 365 then 124 | really_stale_plugin_count = really_stale_plugin_count + 1 125 | end 126 | return string.format("%s: %d days", entry[1], entry[2]) 127 | end, stale_plugins) 128 | 129 | -- Calling the api related to buffer / window directly in a coroutine seems to cause problems 130 | -- We have to wrap it with a `vim.schedule` call 131 | vim.schedule(function() 132 | local report_buf = vim.api.nvim_create_buf(false, true) 133 | vim.keymap.set("n", "q", "c", { buffer = report_buf }) 134 | 135 | local win_width = vim.fn.winwidth(0) 136 | local win_height = vim.fn.winheight(0) 137 | local report_width = math.floor(win_width * 0.5) 138 | local report_height = math.min(math.floor(win_height * 0.7), #stale_plugins) 139 | local row = math.floor((win_height - report_height) / 2) 140 | local col = math.floor((win_width - report_width) / 2) 141 | 142 | -- If setting end to 0 instead of -1, there would be an empty line at the end 143 | vim.api.nvim_buf_set_lines(report_buf, 0, -1, false, report) 144 | 145 | local ns_id = vim.api.nvim_create_namespace "out-of-date-plugins" 146 | for line = 0, really_stale_plugin_count - 1 do 147 | vim.hl.range(report_buf, ns_id, "ErrorMsg", {line, 0}, {line, -1}, {}) 148 | end 149 | for line = really_stale_plugin_count, #stale_plugins - 1 do 150 | vim.hl.range(report_buf, ns_id, "WarningMsg", {line, 0}, {line, -1}, {}) 151 | end 152 | 153 | local win = vim.api.nvim_open_win(report_buf, true, { 154 | relative = "win", 155 | width = report_width, 156 | height = report_height, 157 | row = row, 158 | col = col, 159 | border = "rounded", 160 | title = string.format("%d plugins possibly out of date", #stale_plugins), 161 | title_pos = "center", 162 | }) 163 | 164 | vim.api.nvim_set_option_value("number", false, { win = win }) 165 | vim.api.nvim_set_option_value("relativenumber", false, { win = win }) 166 | vim.api.nvim_set_option_value("modifiable", false, { buf = report_buf }) 167 | end) 168 | end) 169 | 170 | while true do 171 | local item, item_type = vim.uv.fs_scandir_next(dir) 172 | 173 | if not item then 174 | break 175 | end 176 | 177 | if item_type == "directory" and item ~= "readme" then 178 | plugin_count = plugin_count + 1 179 | vim.system( 180 | { "git", "log", "-1", "--format=%cd", "--date=short" }, 181 | { cwd = plugins_path .. item }, 182 | function(obj) 183 | local date = string.gsub(obj.stdout, "\n", "") -- e.g., "2000-06-15" 184 | local year = string.sub(date, 1, 4) 185 | local month = string.sub(date, 6, 7) 186 | local day = string.sub(date, 9, 10) 187 | local last_update_timestamp = os.time { year = year, month = month, day = day } 188 | local current_timestamp = os.time() 189 | local stale_days = math.floor((current_timestamp - last_update_timestamp) / 86400) 190 | if stale_days > 30 then 191 | stale_plugins[#stale_plugins + 1] = { item, stale_days } 192 | end 193 | coroutine.resume(co) 194 | end 195 | ) 196 | end 197 | end 198 | end 199 | end, { nargs = 0 }) 200 | 201 | vim.api.nvim_create_user_command("IceUpdate", "lua require('core.utils').update()", { nargs = 0 }) 202 | 203 | vim.api.nvim_create_user_command("IceHealth", "checkhealth core", { nargs = 0 }) 204 | 205 | -- Allow a command to be repeated based on v:count1 206 | vim.api.nvim_create_user_command("IceRepeat", function(args) 207 | for _ = 1, vim.v.count1 do 208 | vim.cmd(args.args) 209 | end 210 | end, { nargs = "+", complete = "command" }) 211 | 212 | -- View the output of a command in an external buffer 213 | vim.api.nvim_create_user_command("IceView", function(args) 214 | local path = vim.fn.stdpath "data" .. "/ice-view.txt" 215 | if args.args == "" then 216 | vim.cmd("edit " .. path) 217 | else 218 | vim.cmd(string.format( 219 | [[ 220 | redir! > %s 221 | silent %s 222 | redir END 223 | edit %s 224 | ]], 225 | path, 226 | args.args, 227 | path 228 | )) 229 | end 230 | end, { nargs = "*", complete = "command" }) 231 | -------------------------------------------------------------------------------- /lua/core/ft.lua: -------------------------------------------------------------------------------- 1 | Ice.ft = { 2 | c = function () 3 | vim.bo.tabstop = 2 4 | end, 5 | cs = function() 6 | vim.wo.colorcolumn = "100" 7 | end, 8 | css = function() 9 | vim.bo.comments = "s1:/*,ex:*/" 10 | end, 11 | dart = function() 12 | vim.bo.tabstop = 2 13 | end, 14 | html = function() 15 | vim.wo.wrap = true 16 | vim.wo.linebreak = true 17 | vim.wo.breakindent = true 18 | 19 | if vim.bo.filetype == "html" then 20 | vim.wo.colorcolumn = "120" 21 | end 22 | end, 23 | javascript = function() 24 | vim.wo.colorcolumn = "120" 25 | vim.bo.commentstring = "// %s" 26 | end, 27 | markdown = function() 28 | vim.wo.wrap = true 29 | vim.wo.linebreak = true 30 | vim.wo.breakindent = true 31 | end, 32 | org = function() 33 | vim.bo.tabstop = 2 34 | end, 35 | python = function() 36 | vim.bo.formatoptions = "tcqjor" 37 | vim.wo.colorcolumn = "88" -- specified by black 38 | end, 39 | typescript = function() 40 | vim.wo.colorcolumn = "120" 41 | vim.bo.commentstring = "// %s" 42 | end, 43 | typst = function() 44 | vim.wo.wrap = true 45 | vim.wo.linebreak = true 46 | vim.wo.breakindent = true 47 | vim.bo.tabstop = 2 48 | vim.bo.commentstring = "// %s" 49 | end, 50 | -- Convenience method for setting FileType callback 51 | -- Extends default callback if already set 52 | ---@param self table 53 | ---@param ft string 54 | ---@param callback function 55 | set = function(self, ft, callback) 56 | local default_callback = self[ft] 57 | if default_callback ~= nil then 58 | self[ft] = function() 59 | default_callback() 60 | callback() 61 | end 62 | else 63 | self[ft] = callback 64 | end 65 | end, 66 | } 67 | -------------------------------------------------------------------------------- /lua/core/health.lua: -------------------------------------------------------------------------------- 1 | local M = {} 2 | 3 | -- Checks whether the cmd is executable and determines how to behave if it is not 4 | -- 5 | ---@param cmd string the command to check 6 | ---@param behavior function | nil what to do if cmd is not executable; throws an error message by default 7 | local function check(cmd, behavior) 8 | if vim.fn.executable(cmd) == 1 then 9 | vim.health.ok(cmd .. " is installed.") 10 | else 11 | if not behavior then 12 | vim.health.error(cmd .. " is missing.") 13 | else 14 | behavior() 15 | end 16 | end 17 | end 18 | 19 | M.check = function() 20 | vim.health.start "IceNvim Prerequisites" 21 | vim.health.info "IceNvim does not check this for you, but at least one [nerd font] should be installed." 22 | 23 | for _, cmd in ipairs { "curl", "wget", "fd", "rg", "gcc", "cmake", "node", "npm", "yarn", "python3", "pip3" } do 24 | check(cmd) 25 | end 26 | 27 | if vim.fn.executable "gzip" == 1 or vim.fn.executable "7z" == 1 then 28 | vim.health.ok "One of gzip / 7zip is installed." 29 | else 30 | vim.health.error "You must install one of gzip or 7zip." 31 | end 32 | 33 | check("rust-analyzer", function() 34 | vim.health.warn "For best experience with rust development, you should install rust-analyzer." 35 | end) 36 | 37 | if require("core.utils").is_linux then 38 | vim.health.start "IceNvim Prerequisites for Linux" 39 | vim.health.info "IceNvim does not check this for you, but you need a [python virtualenv]." 40 | 41 | for _, cmd in ipairs { "unzip", "xclip", "zip" } do 42 | check(cmd) 43 | end 44 | end 45 | 46 | if require("core.utils").is_windows or require("core.utils").is_wsl then 47 | vim.health.start "IceNvim Optional Dependencies for Windows and WSL" 48 | 49 | check(vim.fn.stdpath "config" .. "/bin/im-select.exe", function() 50 | vim.health.warn "You need im-select.exe to enable automatic IME switching for Chinese. Consider downloading it at https://github.com/daipeihust/im-select/raw/master/win/out/x86/im-select.exe" 51 | end) 52 | 53 | check(vim.fn.stdpath "config" .. "/bin/uclip.exe", function() 54 | vim.health.warn "You need uclip.exe for correct unicode copy / paste. Consider downloading it at https://github.com/suzusime/uclip/releases/download/v0.1.0/uclip.exe" 55 | end) 56 | end 57 | 58 | if require("core.utils").is_mac then 59 | vim.health.start "IceNvim Optional Dependencies for MacOS" 60 | 61 | check("im-select", function() 62 | vim.health.warn "You need im-select to enable automatic IME switching for Chinese. Please refer to the wiki for instruction on how to install it." 63 | end) 64 | end 65 | end 66 | 67 | return M 68 | -------------------------------------------------------------------------------- /lua/core/init.lua: -------------------------------------------------------------------------------- 1 | require "core.basic" 2 | require "core.commands" 3 | require "core.ft" 4 | require "core.keymap" 5 | require "core.miscellaneous" 6 | require "core.symbols" 7 | -------------------------------------------------------------------------------- /lua/core/keymap.lua: -------------------------------------------------------------------------------- 1 | vim.g.mapleader = " " 2 | vim.g.maplocalleader = "," 3 | 4 | -- Generates a comment function that can be used in a keymap 5 | -- 6 | -- Uses the buffer's local commentstring to add a comment; the "%s" in the commentstring is removed 7 | -- Upon the addition of a comment, the user ends up in insert mode, with the cursor at the exact same position as the %s 8 | -- Adding a comment at the end of a line will have a blank before it only if the line is non-blank. 9 | -- 10 | ---@param pos string Can be one of "above" / "below" / "end"; indicates where the comment is to be inserted 11 | local function comment(pos) 12 | return function() 13 | local row = vim.api.nvim_win_get_cursor(0)[1] 14 | local total_lines = vim.api.nvim_buf_line_count(0) 15 | local commentstring = vim.bo.commentstring 16 | local cmt = string.gsub(commentstring, "%%s", "") 17 | local index = string.find(commentstring, "%%s") 18 | 19 | local target_line 20 | if pos == "below" then 21 | -- Uses the same indentation as the next line if we are adding a comment below 22 | -- We have to consider whether the current line is the last one in the buffer 23 | if row == total_lines then 24 | target_line = vim.api.nvim_buf_get_lines(0, row - 1, row, true)[1] 25 | else 26 | target_line = vim.api.nvim_buf_get_lines(0, row, row + 1, true)[1] 27 | end 28 | else 29 | target_line = vim.api.nvim_get_current_line() 30 | end 31 | 32 | if pos == "end" then 33 | -- Only insert a blank space before the comment if the current line is non-blank 34 | if string.find(target_line, "%S") then 35 | cmt = " " .. cmt 36 | index = index + 1 37 | end 38 | vim.api.nvim_buf_set_lines(0, row - 1, row, false, { target_line .. cmt }) 39 | vim.api.nvim_win_set_cursor(0, { row, #target_line + index - 2 }) 40 | else 41 | -- Get the index of the first non blank character 42 | local line_start = string.find(target_line, "%S") or (#target_line + 1) 43 | local blank = string.sub(target_line, 1, line_start - 1) 44 | 45 | if pos == "above" then 46 | vim.api.nvim_buf_set_lines(0, row - 1, row - 1, true, { blank .. cmt }) 47 | vim.api.nvim_win_set_cursor(0, { row, #blank + index - 2 }) 48 | end 49 | 50 | if pos == "below" then 51 | vim.api.nvim_buf_set_lines(0, row, row, true, { blank .. cmt }) 52 | vim.api.nvim_win_set_cursor(0, { row + 1, #blank + index - 2 }) 53 | end 54 | end 55 | 56 | vim.api.nvim_feedkeys("a", "n", false) 57 | end 58 | end 59 | 60 | -- If the line is not empty, apply gcc. Otherwise, add a comment to it 61 | -- This is necessary as the default gcc does not work on blank lines 62 | local function comment_line() 63 | local line = vim.api.nvim_get_current_line() 64 | 65 | local row = vim.api.nvim_win_get_cursor(0)[1] 66 | local commentstring = vim.bo.commentstring 67 | local cmt = string.gsub(commentstring, "%%s", "") 68 | local index = string.find(commentstring, "%%s") 69 | 70 | if not string.find(line, "%S") then 71 | vim.api.nvim_buf_set_lines(0, row - 1, row, false, { line .. cmt }) 72 | vim.api.nvim_win_set_cursor(0, { row, #line + index - 1 }) 73 | else 74 | -- WARN: I have no clue as to why neovim is not including this in its official documentations 75 | -- It is possible that the api will be renamed in future releases 76 | require("vim._comment").toggle_lines(row, row, { row, 0 }) 77 | end 78 | end 79 | 80 | -- J joins + 1 lines 81 | local function join_lines() 82 | local v_count = vim.v.count1 + 1 83 | local mode = vim.api.nvim_get_mode().mode 84 | local keys 85 | if mode == "n" then 86 | keys = v_count .. "J" 87 | else 88 | keys = "J" 89 | end 90 | vim.api.nvim_feedkeys(vim.api.nvim_replace_termcodes(keys, true, false, true), "n", false) 91 | end 92 | 93 | -- Open the current html file with the default browser. 94 | local function open_html_file() 95 | if vim.bo.filetype == "html" then 96 | local utils = require "core.utils" 97 | local command 98 | if utils.is_linux or utils.is_wsl then 99 | command = "xdg-open" 100 | elseif utils.is_windows then 101 | command = "explorer" 102 | else 103 | command = "open" 104 | end 105 | if require("core.utils").is_windows then 106 | local old_shellslash = vim.opt.shellslash 107 | vim.opt.shellslash = false 108 | vim.cmd(string.format('silent exec "!%s %%:p"', command)) 109 | vim.opt.shellslash = old_shellslash 110 | else 111 | vim.cmd(string.format('silent exec "!%s %%:p"', command)) 112 | end 113 | end 114 | end 115 | 116 | -- Save when the buffer is modified 117 | local function save_file() 118 | local buffer_is_modified = vim.api.nvim_get_option_value("modified", { buf = 0 }) 119 | if buffer_is_modified then 120 | vim.cmd "write" 121 | else 122 | print "Buffer not modified. No writing is done." 123 | end 124 | vim.api.nvim_feedkeys(vim.api.nvim_replace_termcodes("", true, false, true), "n", false) 125 | end 126 | 127 | -- When evoked under normal / insert / visual mode, call vim's `undo` command and then go to normal mode. 128 | local function undo() 129 | local mode = vim.api.nvim_get_mode().mode 130 | 131 | -- Only undo in normal / insert / visual mode 132 | if mode == "n" or mode == "i" or mode == "v" then 133 | vim.cmd "undo" 134 | -- Back to normal mode 135 | vim.api.nvim_feedkeys(vim.api.nvim_replace_termcodes("", true, false, true), "n", false) 136 | end 137 | end 138 | 139 | -- Determine in advance what shell to use for the keymap 140 | local terminal_command 141 | if not require("core.utils").is_windows then 142 | terminal_command = "split | terminal" -- let $SHELL decide the default shell 143 | else 144 | local executables = { "pwsh", "powershell", "bash", "cmd" } 145 | for _, executable in require("core.utils").ordered_pair(executables) do 146 | if vim.fn.executable(executable) == 1 then 147 | terminal_command = "split term://" .. executable .. "" 148 | break 149 | end 150 | end 151 | end 152 | 153 | Ice.keymap = {} 154 | Ice.keymap.general = { 155 | -- See `:h quote_` 156 | black_hole_register = { { "n", "v" }, "\\", '"_' }, 157 | clear_cmd_line = { { "n", "i", "v", "t" }, "", "mode" }, 158 | cmd_forward = { "c", "", "", { silent = false } }, 159 | cmd_backward = { "c", "", "", { silent = false } }, 160 | cmd_home = { "c", "", "", { silent = false } }, 161 | cmd_end = { "c", "", "", { silent = false } }, 162 | cmd_word_forward = { "c", "", "", { silent = false } }, 163 | cmd_word_backward = { "c", "", "", { silent = false } }, 164 | 165 | comment_line = { "n", "gcc", comment_line }, 166 | comment_above = { "n", "gcO", comment "above" }, 167 | comment_below = { "n", "gco", comment "below" }, 168 | comment_end = { "n", "gcA", comment "end" }, 169 | 170 | disable_ctrl_left_mouse = { "n", "", "" }, 171 | disable_right_mouse = { { "n", "i", "v", "t" }, "", "" }, 172 | 173 | join_lines = { { "n", "v" }, "J", join_lines }, 174 | 175 | -- Move the cursor through wrapped lines with j and k 176 | -- https://github.com/NvChad/NvChad/blob/b9963e29b21a672325af5b51f1d32a9191abcdaa/lua/core/mappings.lua#L40C5-L41C99 177 | move_down = { "n", "j", 'v:count || mode(1)[0:1] == "no" ? "j" : "gj"', { expr = true } }, 178 | move_up = { "n", "k", 'v:count || mode(1)[0:1] == "no" ? "k" : "gk"', { expr = true } }, 179 | 180 | new_line_below_normal = { "n", "", "o" }, 181 | new_line_above_normal = { "n", "", "O" }, 182 | 183 | open_html_file = { "n", "", open_html_file }, 184 | open_terminal = { "n", "", terminal_command }, 185 | normal_mode_in_terminal = { "t", "", "" }, 186 | save_file = { { "n", "i", "v" }, "", save_file }, 187 | undo = { { "n", "i", "v", "t", "c" }, "", undo }, 188 | visual_line = { "n", "V", "0v$" }, 189 | } 190 | -------------------------------------------------------------------------------- /lua/core/miscellaneous.lua: -------------------------------------------------------------------------------- 1 | local utils = require "core.utils" 2 | 3 | local config_path = string.gsub(vim.fn.stdpath "config", "\\", "/") 4 | 5 | -- Yanking on windows / wsl 6 | local clip_path = config_path .. "/bin/uclip.exe" 7 | if not require("core.utils").file_exists(clip_path) then 8 | local root 9 | if utils.is_windows then 10 | root = "C:" 11 | else 12 | root = "/mnt/c" 13 | end 14 | clip_path = root .. "/Windows/System32/clip.exe" 15 | end 16 | 17 | if utils.is_windows or utils.is_wsl then 18 | vim.api.nvim_create_autocmd("TextYankPost", { 19 | callback = function() 20 | if vim.v.event.operator == "y" then 21 | vim.fn.system(clip_path, vim.fn.getreg "0") 22 | end 23 | end, 24 | }) 25 | else 26 | vim.cmd "set clipboard+=unnamedplus" 27 | end 28 | 29 | -- IME switching on windows / wsl 30 | if utils.is_windows or utils.is_wsl then 31 | local im_select_path = config_path .. "/bin/im-select.exe" 32 | 33 | if require("core.utils").file_exists(im_select_path) then 34 | local ime_autogroup = vim.api.nvim_create_augroup("ImeAutoGroup", { clear = true }) 35 | 36 | local function autocmd(event, code) 37 | vim.api.nvim_create_autocmd(event, { 38 | group = ime_autogroup, 39 | callback = function() 40 | vim.cmd(":silent :!" .. im_select_path .. " " .. code) 41 | end, 42 | }) 43 | end 44 | 45 | autocmd("InsertLeave", 1033) 46 | autocmd("InsertEnter", 2052) 47 | autocmd("VimLeavePre", 2052) 48 | end 49 | elseif utils.is_mac then 50 | if vim.fn.executable "macism" == 1 then 51 | local ime_autogroup = vim.api.nvim_create_augroup("ImeAutoGroup", { clear = true }) 52 | 53 | vim.api.nvim_create_autocmd("InsertLeave", { 54 | group = ime_autogroup, 55 | callback = function() 56 | vim.system({ "macism" }, { text = true }, function(out) 57 | Ice.__PREVIOUS_IM_CODE_MAC = string.gsub(out.stdout, "\n", "") 58 | end) 59 | vim.cmd ":silent :!macism com.apple.keylayout.ABC" 60 | end, 61 | }) 62 | 63 | vim.api.nvim_create_autocmd("InsertEnter", { 64 | group = ime_autogroup, 65 | callback = function() 66 | if Ice.__PREVIOUS_IM_CODE_MAC then 67 | vim.cmd(":silent :!macism " .. Ice.__PREVIOUS_IM_CODE_MAC) 68 | end 69 | Ice.__PREVIOUS_IM_CODE_MAC = nil 70 | end, 71 | }) 72 | end 73 | elseif utils.is_linux then 74 | vim.cmd [[ 75 | let fcitx5state=system("fcitx5-remote") 76 | autocmd InsertLeave * :silent let fcitx5state=system("fcitx5-remote")[0] | silent !fcitx5-remote -c 77 | autocmd InsertEnter * :silent if fcitx5state == 2 | call system("fcitx5-remote -o") | endif 78 | ]] 79 | end 80 | 81 | -- Automatic switch to root directory 82 | vim.api.nvim_create_autocmd("BufEnter", { 83 | group = vim.api.nvim_create_augroup("AutoChdir", { clear = true }), 84 | callback = function() 85 | if not (Ice.auto_chdir or Ice.auto_chdir == nil) then 86 | return 87 | end 88 | 89 | local default_exclude_filetype = { "NvimTree", "help" } 90 | local default_exclude_buftype = { "terminal", "nofile" } 91 | 92 | local exclude_filetype = Ice.chdir_exclude_filetype 93 | if exclude_filetype == nil or type(exclude_filetype) ~= "table" then 94 | exclude_filetype = default_exclude_filetype 95 | end 96 | 97 | local exclude_buftype = Ice.chdir_exclude_buftype 98 | if exclude_buftype == nil or type(exclude_buftype) ~= "table" then 99 | exclude_buftype = default_exclude_buftype 100 | end 101 | 102 | if table.find(exclude_filetype, vim.bo.filetype) or table.find(exclude_buftype, vim.bo.buftype) then 103 | return 104 | end 105 | 106 | vim.api.nvim_set_current_dir(require("core.utils").get_root()) 107 | end, 108 | }) 109 | 110 | -- Clears redundant shada.tmp.X files (for windows only) 111 | if utils.is_windows then 112 | local remove_shada_tmp_group = vim.api.nvim_create_augroup("RemoveShadaTmp", { clear = true }) 113 | vim.api.nvim_create_autocmd("VimLeavePre", { 114 | group = remove_shada_tmp_group, 115 | callback = function() 116 | local dir = vim.fn.stdpath "data" .. "/shada/" 117 | local shada_dir = vim.uv.fs_scandir(dir) 118 | 119 | local shada_temp = "" 120 | while shada_temp ~= nil do 121 | if string.find(shada_temp, ".tmp.") then 122 | local full_path = dir .. shada_temp 123 | os.remove(full_path) 124 | end 125 | shada_temp = vim.uv.fs_scandir_next(shada_dir) 126 | end 127 | end, 128 | }) 129 | end 130 | -------------------------------------------------------------------------------- /lua/core/symbols.lua: -------------------------------------------------------------------------------- 1 | Ice.symbols = { 2 | Affirmative = "✓ ", 3 | Array = " ", 4 | Boolean = " ", 5 | Class = "󰠱 ", 6 | CodeAction = " ", 7 | Color = "󰏘 ", 8 | Component = "󰡀 ", 9 | Constant = "󰏿 ", 10 | Constructor = " ", 11 | Definition = " ", 12 | Diagnostic = " ", 13 | Dos = " ", 14 | Enum = " ", 15 | EnumMember = " ", 16 | Error = " ", 17 | Event = " ", 18 | Field = "󰜢 ", 19 | File = "󰈙 ", 20 | FittenCode = "", 21 | Folder = "󰉋 ", 22 | Fragment = " ", 23 | Function = "󰊕 ", 24 | Hint = " ", 25 | Info = " ", 26 | Interface = " ", 27 | Key = " ", 28 | Keyword = "󰌋 ", 29 | LspSagaFinder = " ", 30 | Mac = " ", 31 | Method = "󰆧 ", 32 | Module = "󰆧 ", 33 | Namespace = " ", 34 | Negative = "✗ ", 35 | Null = "󰟢 ", 36 | Number = " ", 37 | Object = " ", 38 | Operator = "󰆕 ", 39 | Package = " ", 40 | Pending = "➜ ", 41 | Property = "󰜢 ", 42 | Reference = "󰈇 ", 43 | RenamePrompt = "➤ ", 44 | Snippet = " ", 45 | String = " ", 46 | Struct = "󰙅 ", 47 | Text = "󰉿 ", 48 | TypeParameter = "󰉺 ", 49 | Unit = "󰑭 ", 50 | Unix = " ", 51 | Value = "󰎠 ", 52 | Variable = "󰀫 ", 53 | Warn = " ", 54 | } 55 | -------------------------------------------------------------------------------- /lua/core/utils.lua: -------------------------------------------------------------------------------- 1 | -- Do not use vim.version as it loads the vim.version module 2 | -- Using vim.fn.api_info().version is no good either as api_info also consumes much time 3 | local version = vim.fn.matchstr(vim.fn.execute('version'), 'NVIM v\\zs[^\\n]*') 4 | 5 | local argv = vim.api.nvim_get_vvar "argv" 6 | local noplugin = vim.list_contains(argv, "--noplugin") or vim.list_contains(argv, "--noplugins") 7 | 8 | local utils = { 9 | is_linux = vim.uv.os_uname().sysname == "Linux", 10 | is_mac = vim.uv.os_uname().sysname == "Darwin", 11 | is_windows = vim.uv.os_uname().sysname == "Windows_NT", 12 | is_wsl = string.find(vim.uv.os_uname().release, "WSL") ~= nil, 13 | noplugin = noplugin, 14 | version = version, 15 | } 16 | 17 | local ft_group = vim.api.nvim_create_augroup("IceFt", { clear = true }) 18 | 19 | -- Checks if a file exists 20 | ---@param file string 21 | ---@return boolean 22 | utils.file_exists = function(file) 23 | local fid = io.open(file, "r") 24 | if fid ~= nil then 25 | io.close(fid) 26 | return true 27 | else 28 | return false 29 | end 30 | end 31 | 32 | -- Add callback to filetype 33 | ---@param filetype string 34 | ---@param config function 35 | utils.ft = function(filetype, config) 36 | vim.api.nvim_create_autocmd("FileType", { 37 | pattern = filetype, 38 | group = ft_group, 39 | callback = config, 40 | }) 41 | end 42 | 43 | -- Get the parent directory of target. If target is nil, the parent directory of the current file will be looked for, 44 | -- suffixed with a "/" (which is because this function is intended to be used together with fs_scandir, where errors 45 | -- would occur sometimes should a path without an ending "/" be passed to it, such as "C:" instead of "C:/"). 46 | -- 47 | -- If the target has no parent directory, such as "/" on Linux or "C:" on Windows, nil will be returned. 48 | ---@param target string? 49 | ---@return string? 50 | utils.get_parent = function(target) 51 | if target == nil then 52 | local parent = vim.fn.expand("%:p:h", true) 53 | 54 | if utils.is_windows then 55 | parent = string.gsub(parent, "\\", "/") 56 | end 57 | 58 | return parent 59 | end 60 | 61 | if utils.is_windows then 62 | target = string.gsub(target, "\\", "/") 63 | end 64 | 65 | -- removes trailing slash 66 | if string.sub(target, #target, #target) == "/" then 67 | target = string.sub(target, 1, #target - 1) 68 | end 69 | 70 | if string.find(target, "/") == nil then 71 | return nil 72 | end 73 | 74 | return string.sub(target, 1, string.findlast(target, "/")) 75 | end 76 | 77 | utils.get_root = function() 78 | local uv = vim.uv 79 | 80 | local default_pattern = { 81 | ".git", 82 | "package.json", 83 | ".prettierrc", 84 | "tsconfig.json", 85 | "pubspec.yaml", 86 | ".gitignore", 87 | "stylua.toml", 88 | "README.md", 89 | } 90 | 91 | local pattern = Ice.chdir_root_pattern 92 | if pattern == nil or type(pattern) ~= "table" then 93 | pattern = default_pattern 94 | end 95 | 96 | local parent = utils.get_parent() 97 | local root = parent 98 | local has_found_root = false 99 | 100 | while not (has_found_root or parent == nil) do 101 | local dir = uv.fs_scandir(parent) 102 | 103 | if dir == nil then 104 | break 105 | end 106 | 107 | local file = "" 108 | 109 | while file ~= nil do 110 | file = uv.fs_scandir_next(dir) 111 | if table.find(pattern, file) then 112 | root = parent 113 | has_found_root = true 114 | break 115 | end 116 | end 117 | 118 | parent = utils.get_parent(parent) 119 | end 120 | 121 | return root 122 | end 123 | 124 | -- Maps a group of keymaps with the same opt; if no opt is provided, the default opt is used. 125 | -- The keymaps should be in the format like below: 126 | -- desc = { mode, lhs, rhs, [opt] } 127 | -- For example: 128 | -- black_hole_register = { { "n", "v" }, "\\", '"_' }, 129 | -- The desc part will automatically merged into the keymap's opt, unless one is already provided there, with the slight 130 | -- modification of replacing "_" with a blank space. 131 | ---@param group table list of keymaps 132 | ---@param opt table | nil default opt 133 | utils.group_map = function(group, opt) 134 | if not opt then 135 | opt = {} 136 | end 137 | 138 | for desc, keymap in pairs(group) do 139 | desc = string.gsub(desc, "_", " ") 140 | local default_option = vim.tbl_extend("force", { desc = desc, nowait = true, silent = true }, opt) 141 | local map = vim.tbl_deep_extend("force", { nil, nil, nil, default_option }, keymap) 142 | vim.keymap.set(map[1], map[2], map[3], map[4]) 143 | end 144 | end 145 | 146 | -- Allow ordered iteration through a table 147 | ---@param t table 148 | ---@return function 149 | utils.ordered_pair = function(t) 150 | local a = {} 151 | 152 | for n in pairs(t) do 153 | a[#a + 1] = n 154 | end 155 | 156 | table.sort(a) 157 | 158 | local i = 0 159 | 160 | return function() 161 | i = i + 1 162 | return a[i], t[a[i]] 163 | end 164 | end 165 | 166 | -- Updates IceNvim 167 | utils.update = function() 168 | vim.system({ "git", "pull" }, { cwd = vim.fn.stdpath "config", text = true }, function(out) 169 | if out.code == 0 then 170 | vim.notify "IceNvim up to date" 171 | else 172 | vim.notify("IceNvim update failed: " .. out.stderr, vim.log.levels.WARN) 173 | end 174 | end) 175 | end 176 | 177 | -- Looks for the last match of `pattern` 178 | -- WARN: this function does poorly with unicode characters! 179 | ---@param s string | number 180 | ---@param pattern string | number 181 | ---@param last integer? 182 | ---@param plain boolean? 183 | ---@return integer | nil, integer | nil, ... | any 184 | string.findlast = function(s, pattern, last, plain) 185 | local reverse = string.reverse(s) 186 | 187 | if last == nil then 188 | last = #s 189 | end 190 | 191 | local start, finish = string.find(reverse, string.reverse(pattern), #s + 1 - last, plain) 192 | if start == nil then 193 | return nil 194 | else 195 | return #s + 1 - finish, #s + 1 - start 196 | end 197 | end 198 | 199 | -- Finds the first occurence of the target in table and returns the key / index. 200 | -- If the target is not in the table, nil is returned. 201 | ---@param t table 202 | ---@param target ... | any 203 | ---@return ... | any 204 | table.find = function(t, target) 205 | for key, value in pairs(t) do 206 | if value == target then 207 | return key 208 | end 209 | end 210 | 211 | return nil 212 | end 213 | 214 | return utils 215 | -------------------------------------------------------------------------------- /lua/lsp/completion.lua: -------------------------------------------------------------------------------- 1 | Ice.plugins["blink-cmp"] = { 2 | "saghen/blink.cmp", 3 | dependencies = { "rafamadriz/friendly-snippets" }, 4 | event = { "InsertEnter", "CmdlineEnter", "User IceLoad" }, 5 | version = "*", 6 | opts = { 7 | appearance = { 8 | kind_icons = Ice.symbols, 9 | }, 10 | cmdline = { 11 | completion = { 12 | menu = { 13 | auto_show = true, 14 | } 15 | }, 16 | keymap = { 17 | preset = "none", 18 | [""] = {"accept"}, 19 | [""] = { "select_prev", "fallback" }, 20 | [""] = { "select_next", "fallback" }, 21 | } 22 | }, 23 | completion = { 24 | accept = { 25 | auto_brackets = { enabled = true }, 26 | }, 27 | documentation = { 28 | auto_show = true, 29 | auto_show_delay_ms = 200, 30 | }, 31 | ghost_text = { 32 | enabled = true, 33 | show_without_selection = true, 34 | }, 35 | list = { 36 | selection = { 37 | preselect = false, 38 | auto_insert = true, 39 | }, 40 | }, 41 | menu = { 42 | draw = { 43 | columns = { 44 | { "label", "label_description", gap = 1 }, 45 | { "kind_icon" }, 46 | }, 47 | treesitter = { "lsp" }, 48 | }, 49 | }, 50 | }, 51 | enabled = function() 52 | local filetype_is_allowed = not vim.tbl_contains({ "grug-far", "TelescopePrompt" }, vim.bo.filetype) 53 | 54 | local ok, stats = pcall(vim.uv.fs_stat, vim.api.nvim_buf_get_name(0)) 55 | local filesize_is_allowed = true 56 | if ok and stats then 57 | ---@diagnostic disable-next-line: need-check-nil 58 | filesize_is_allowed = stats.size < 100 * 1024 59 | end 60 | return filetype_is_allowed and filesize_is_allowed 61 | end, 62 | keymap = { 63 | preset = "none", 64 | [""] = { 65 | function(cmp) 66 | if not cmp.is_menu_visible() then 67 | return 68 | end 69 | 70 | local completion_list = require "blink.cmp.completion.list" 71 | local selected_id = completion_list.selected_item_idx or 1 72 | local item = completion_list.items[selected_id] 73 | local source = item.source_name 74 | return cmp.select_and_accept { 75 | callback = function() 76 | if source == "fittencode" then 77 | -- Do not just feed a or keys of such sort 78 | -- Should the previous line be a comment, the new line might be a comment as well 79 | local line_number = 1 80 | local insert_text = item.insertText 81 | for _ in string.gmatch(insert_text, "\n") do 82 | line_number = line_number + 1 83 | end 84 | local row = vim.api.nvim_win_get_cursor(0)[1] + line_number - 1 85 | local line = vim.api.nvim_get_current_line() 86 | local line_start = string.find(line, "%S") or 1 87 | local blank = string.sub(line, 1, line_start - 1) 88 | vim.api.nvim_buf_set_lines(0, row, row, true, { blank }) 89 | vim.api.nvim_win_set_cursor(0, { row + 1, #blank + 1 }) 90 | 91 | -- It seems that I have to defer the next call to fittencode 92 | -- Otherwise the completion menu would not show up 93 | vim.defer_fn(function() 94 | require("blink.cmp").show { providers = { "fittencode" } } 95 | end, 20) 96 | end 97 | end, 98 | } 99 | end, 100 | "snippet_forward", 101 | "fallback", 102 | }, 103 | [""] = { "snippet_backward", "fallback" }, 104 | [""] = { "select_prev", "fallback" }, 105 | [""] = { "select_next", "fallback" }, 106 | [""] = { 107 | -- DO NOT add "fallback" here!!! 108 | -- It would cause the letter "c" to be inserted as well 109 | function(cmp) 110 | if cmp.is_menu_visible() then 111 | cmp.cancel() 112 | else 113 | cmp.show() 114 | end 115 | end, 116 | }, 117 | [""] = { "scroll_documentation_down", "fallback" }, 118 | [""] = { "scroll_documentation_up", "fallback" }, 119 | }, 120 | sources = { 121 | default = function() 122 | local cmdwin_type = vim.fn.getcmdwintype() 123 | if cmdwin_type == "/" or cmdwin_type == "?" then 124 | return { "buffer" } 125 | end 126 | if cmdwin_type == ":" or cmdwin_type == "@" then 127 | return { "cmdline" } 128 | end 129 | 130 | local source = { "lsp", "path", "snippets", "buffer" } 131 | if Ice.__FITTENCODE_SOURCE_ADDED then 132 | source[#source + 1] = "fittencode" 133 | end 134 | if vim.bo.filetype == "org" and Ice.__ORGMODE_SOURCE_ADDED then 135 | source[#source + 1] = "orgmode" 136 | end 137 | return source 138 | end, 139 | providers = { 140 | snippets = { 141 | opts = { 142 | search_paths = { vim.fn.stdpath "config" .. "/lua/custom/snippets" }, 143 | }, 144 | }, 145 | }, 146 | }, 147 | }, 148 | } 149 | -------------------------------------------------------------------------------- /lua/lsp/extra.lua: -------------------------------------------------------------------------------- 1 | Ice.plugins.lspsaga = { 2 | "nvimdev/lspsaga.nvim", 3 | cmd = "Lspsaga", 4 | opts = { 5 | finder = { 6 | keys = { 7 | toggle_or_open = "", 8 | }, 9 | }, 10 | symbol_in_winbar = { 11 | enable = false, 12 | }, 13 | }, 14 | keys = { 15 | { "lr", "Lspsaga rename", desc = "rename", silent = true }, 16 | { "lc", "Lspsaga code_action", desc = "code action", silent = true }, 17 | { "ld", "Lspsaga goto_definition", desc = "go to definition", silent = true }, 18 | { 19 | "lh", 20 | function() 21 | local win = require "lspsaga.window" 22 | local old_new_float = win.new_float 23 | win.new_float = function(self, float_opt, enter, force) 24 | local window = old_new_float(self, float_opt, enter, force) 25 | local _, winid = window:wininfo() 26 | vim.api.nvim_set_current_win(winid) 27 | 28 | win.new_float = old_new_float 29 | return window 30 | end 31 | 32 | vim.cmd "Lspsaga hover_doc" 33 | end, 34 | desc = "hover doc", 35 | silent = true, 36 | }, 37 | { "lR", "Lspsaga finder", desc = "references", silent = true }, 38 | { "li", "Lspsaga finder", desc = "go_to_implementation", silent = true }, 39 | { "lP", "Lspsaga show_line_diagnostics", desc = "show_line_diagnostic", silent = true }, 40 | { "ln", "Lspsaga diagnostic_jump_next", desc = "next_diagnostic", silent = true }, 41 | { "lp", "Lspsaga diagnostic_jump_prev", desc = "prev_diagnostic", silent = true }, 42 | }, 43 | } 44 | 45 | Ice.plugins.trouble = { 46 | "folke/trouble.nvim", 47 | opts = {}, 48 | cmd = "Trouble", 49 | keys = { 50 | { "lt", "Trouble diagnostics toggle focus=true", desc = "trouble toggle", silent = true }, 51 | }, 52 | } 53 | -------------------------------------------------------------------------------- /lua/lsp/format.lua: -------------------------------------------------------------------------------- 1 | -- While null-ls can do a lot more than just formatting, I am leaving the rest to LspSaga 2 | -- See extra.lua 3 | Ice.plugins["null-ls"] = { 4 | "nvimtools/none-ls.nvim", 5 | dependencies = { "nvim-lua/plenary.nvim" }, 6 | event = "User IceLoad", 7 | opts = { 8 | debug = false, 9 | }, 10 | config = function(_, opts) 11 | local null_ls = require "null-ls" 12 | local formatting = null_ls.builtins.formatting 13 | 14 | local sources = {} 15 | for _, config in pairs(Ice.lsp) do 16 | if config.formatter then 17 | local source = formatting[config.formatter] 18 | sources[#sources + 1] = source 19 | end 20 | end 21 | 22 | null_ls.setup(vim.tbl_deep_extend("keep", opts, { sources = sources })) 23 | end, 24 | keys = { 25 | { 26 | "lf", 27 | function() 28 | local active_client = vim.lsp.get_clients { bufnr = 0, name = "null-ls" } 29 | 30 | local format_option = { async = true } 31 | if #active_client > 0 then 32 | format_option.name = "null-ls" 33 | end 34 | vim.lsp.buf.format(format_option) 35 | end, 36 | mode = { "n", "v" }, 37 | desc = "format code", 38 | }, 39 | }, 40 | } 41 | -------------------------------------------------------------------------------- /lua/lsp/init.lua: -------------------------------------------------------------------------------- 1 | require "lsp.lsp" 2 | require "lsp.plugins" 3 | require "lsp.completion" 4 | require "lsp.format" 5 | require "lsp.extra" 6 | -------------------------------------------------------------------------------- /lua/lsp/lsp.lua: -------------------------------------------------------------------------------- 1 | local lsp = {} 2 | 3 | -- For instructions on configuration, see official wiki: 4 | -- https://github.com/neovim/nvim-lspconfig/blob/master/doc/configs.md 5 | lsp = { 6 | ["bash-language-server"] = { 7 | formatter = "shfmt", 8 | }, 9 | clangd = {}, 10 | ["css-lsp"] = { 11 | formatter = "prettier", 12 | setup = { 13 | settings = { 14 | css = { 15 | validate = true, 16 | lint = { 17 | unknownAtRules = "ignore", 18 | }, 19 | }, 20 | less = { 21 | validate = true, 22 | lint = { 23 | unknownAtRules = "ignore", 24 | }, 25 | }, 26 | scss = { 27 | validate = true, 28 | lint = { 29 | unknownAtRules = "ignore", 30 | }, 31 | }, 32 | }, 33 | }, 34 | }, 35 | ["emmet-ls"] = { 36 | setup = { 37 | filetypes = { "html", "typescriptreact", "javascriptreact", "css", "sass", "scss", "less" }, 38 | }, 39 | }, 40 | flutter = { 41 | managed_by_plugin = true, 42 | }, 43 | gopls = { 44 | formatter = "gofumpt", 45 | setup = { 46 | settings = { 47 | gopls = { 48 | analyses = { 49 | unusedparams = true, 50 | }, 51 | }, 52 | }, 53 | }, 54 | }, 55 | ["html-lsp"] = { 56 | formatter = "prettier", 57 | }, 58 | ["json-lsp"] = { 59 | formatter = "prettier", 60 | }, 61 | ["lua-language-server"] = { 62 | formatter = "stylua", 63 | setup = { 64 | settings = { 65 | Lua = { 66 | runtime = { 67 | version = "LuaJIT", 68 | path = (function() 69 | local runtime_path = vim.split(package.path, ";") 70 | table.insert(runtime_path, "lua/?.lua") 71 | table.insert(runtime_path, "lua/?/init.lua") 72 | return runtime_path 73 | end)(), 74 | }, 75 | diagnostics = { 76 | globals = { "vim" }, 77 | }, 78 | hint = { 79 | enable = true, 80 | }, 81 | workspace = { 82 | library = { 83 | vim.env.VIMRUNTIME, 84 | "${3rd}/luv/library", 85 | }, 86 | checkThirdParty = false, 87 | }, 88 | telemetry = { 89 | enable = false, 90 | }, 91 | }, 92 | }, 93 | }, 94 | enabled = true, 95 | }, 96 | omnisharp = { 97 | formatter = "csharpier", 98 | setup = { 99 | cmd = { 100 | "dotnet", 101 | vim.fn.stdpath "data" .. "/mason/packages/omnisharp/libexec/Omnisharp.dll", 102 | }, 103 | on_attach = function(client, _) 104 | client.server_capabilities.semanticTokensProvider = nil 105 | end, 106 | }, 107 | }, 108 | pyright = { 109 | formatter = "black", 110 | }, 111 | rust = { 112 | managed_by_plugin = true, 113 | }, 114 | tinymist = { 115 | formatter = "typstfmt", 116 | setup = { 117 | single_file_support = true, 118 | }, 119 | }, 120 | ["typescript-language-server"] = { 121 | formatter = "prettier", 122 | setup = { 123 | single_file_support = true, 124 | flags = lsp.flags, 125 | on_attach = function(client) 126 | if #vim.lsp.get_clients { name = "denols" } > 0 then 127 | client.stop() 128 | end 129 | end, 130 | }, 131 | }, 132 | } 133 | 134 | Ice.lsp = lsp 135 | -------------------------------------------------------------------------------- /lua/lsp/plugins.lua: -------------------------------------------------------------------------------- 1 | local symbols = Ice.symbols 2 | 3 | Ice.plugins["flutter-tools"] = { 4 | "akinsho/flutter-tools.nvim", 5 | ft = "dart", 6 | dependencies = { 7 | "nvim-lua/plenary.nvim", 8 | "stevearc/dressing.nvim", 9 | }, 10 | main = "flutter-tools", 11 | opts = { 12 | ui = { 13 | border = "rounded", 14 | }, 15 | decorations = { 16 | statusline = { 17 | app_version = true, 18 | device = true, 19 | }, 20 | }, 21 | }, 22 | enabled = function() 23 | return Ice.lsp.flutter.enabled == true 24 | end, 25 | } 26 | 27 | Ice.plugins.rustaceanvim = { 28 | "mrcjkb/rustaceanvim", 29 | ft = "rust", 30 | enabled = function() 31 | return Ice.lsp.rust.enabled == true 32 | end, 33 | } 34 | 35 | Ice.plugins["typst-preview"] = { 36 | "chomosuke/typst-preview.nvim", 37 | ft = "typst", 38 | build = function() 39 | require("typst-preview").update() 40 | end, 41 | opts = {}, 42 | keys = { 43 | { "", "TypstPreviewToggle", desc = "typst preview toggle", ft = "typst", silent = true }, 44 | }, 45 | enabled = function() 46 | return Ice.lsp.tinymist.enabled == true 47 | end, 48 | } 49 | 50 | Ice.plugins.mason = { 51 | "mason-org/mason.nvim", 52 | dependencies = { 53 | "neovim/nvim-lspconfig", 54 | "mason-org/mason-lspconfig.nvim", 55 | }, 56 | event = "User IceLoad", 57 | cmd = "Mason", 58 | opts = { 59 | ui = { 60 | icons = { 61 | package_installed = symbols.Affirmative, 62 | package_pending = symbols.Pending, 63 | package_uninstalled = symbols.Negative, 64 | }, 65 | }, 66 | }, 67 | config = function(_, opts) 68 | require("mason").setup(opts) 69 | 70 | local registry = require "mason-registry" 71 | local function install(package) 72 | local s, p = pcall(registry.get_package, package) 73 | if s and not p:is_installed() then 74 | p:install() 75 | end 76 | end 77 | 78 | local lspconfig = require "lspconfig" 79 | local mason_lspconfig_mapping = require("mason-lspconfig").get_mappings().package_to_lspconfig 80 | 81 | local installed_packages = registry.get_installed_package_names() 82 | 83 | for lsp, config in pairs(Ice.lsp) do 84 | if not config.enabled then 85 | goto continue 86 | end 87 | 88 | local formatter = config.formatter 89 | install(lsp) 90 | install(formatter) 91 | 92 | if not vim.tbl_contains(installed_packages, lsp) then 93 | goto continue 94 | end 95 | 96 | lsp = mason_lspconfig_mapping[lsp] 97 | if not config.managed_by_plugin and lspconfig[lsp] ~= nil then 98 | local setup = config.setup 99 | if type(setup) == "function" then 100 | setup = setup() 101 | elseif setup == nil then 102 | setup = {} 103 | end 104 | 105 | local blink_capabilities = require("blink.cmp").get_lsp_capabilities() 106 | blink_capabilities.textDocument.foldingRange = { 107 | dynamicRegistration = false, 108 | lineFoldingOnly = true, 109 | } 110 | setup = vim.tbl_deep_extend("force", setup, { 111 | capabilities = blink_capabilities, 112 | }) 113 | 114 | lspconfig[lsp].setup(setup) 115 | end 116 | ::continue:: 117 | end 118 | 119 | vim.diagnostic.config { 120 | update_in_insert = true, 121 | severity_sort = true, -- necessary for lspsaga's show_line_diagnostics to work 122 | virtual_text = true, 123 | } 124 | local signs = { 125 | Error = symbols.Error, 126 | Warn = symbols.Warn, 127 | Hint = symbols.Hint, 128 | Info = symbols.Info, 129 | } 130 | for type, icon in pairs(signs) do 131 | local hl = "DiagnosticSign" .. type 132 | vim.fn.sign_define(hl, { text = icon, texthl = hl, numhl = hl }) 133 | end 134 | 135 | vim.lsp.inlay_hint.enable() 136 | 137 | vim.cmd "LspStart" 138 | end, 139 | } 140 | -------------------------------------------------------------------------------- /lua/plugins/colorscheme.lua: -------------------------------------------------------------------------------- 1 | -- Predefined colorschemes 2 | Ice.colorschemes = { 3 | ["github-dark"] = { 4 | name = "github_dark", 5 | background = "dark", 6 | }, 7 | ["github-light"] = { 8 | name = "github_light", 9 | background = "light", 10 | }, 11 | ["github-dark-dimmed"] = { 12 | name = "github_dark_dimmed", 13 | background = "dark", 14 | }, 15 | ["github-dark-high-contrast"] = { 16 | name = "github_dark_high_contrast", 17 | background = "dark", 18 | }, 19 | ["github-light-high-contrast"] = { 20 | name = "github_light_high_contrast", 21 | background = "light", 22 | }, 23 | ["gruvbox-dark"] = { 24 | name = "gruvbox", 25 | setup = { 26 | italic = { 27 | strings = true, 28 | operators = false, 29 | comments = true, 30 | }, 31 | contrast = "hard", 32 | }, 33 | background = "dark", 34 | }, 35 | ["gruvbox-light"] = { 36 | name = "gruvbox", 37 | setup = { 38 | italic = { 39 | strings = true, 40 | operators = false, 41 | comments = true, 42 | }, 43 | contrast = "hard", 44 | }, 45 | background = "light", 46 | }, 47 | ["kanagawa-wave"] = { 48 | name = "kanagawa-wave", 49 | background = "dark", 50 | }, 51 | ["kanagawa-dragon"] = { 52 | name = "kanagawa-dragon", 53 | background = "dark", 54 | }, 55 | ["kanagawa-lotus"] = { 56 | name = "kanagawa-lotus", 57 | background = "light", 58 | }, 59 | miasma = { 60 | name = "miasma", 61 | background = "dark", 62 | }, 63 | nightfox = { 64 | name = "nightfox", 65 | background = "dark", 66 | }, 67 | ["nightfox-carbon"] = { 68 | name = "carbonfox", 69 | background = "dark", 70 | }, 71 | ["nightfox-day"] = { 72 | name = "dayfox", 73 | background = "light", 74 | }, 75 | ["nightfox-dawn"] = { 76 | name = "dawnfox", 77 | background = "light", 78 | }, 79 | ["nightfox-dusk"] = { 80 | name = "duskfox", 81 | background = "dark", 82 | }, 83 | ["nightfox-nord"] = { 84 | name = "nordfox", 85 | background = "dark", 86 | }, 87 | ["nightfox-tera"] = { 88 | name = "terafox", 89 | background = "dark", 90 | }, 91 | tokyonight = { 92 | name = "tokyonight", 93 | setup = { 94 | style = "moon", 95 | styles = { 96 | comments = { italic = true }, 97 | keywords = { italic = false }, 98 | }, 99 | }, 100 | background = "dark", 101 | }, 102 | } 103 | -------------------------------------------------------------------------------- /lua/plugins/config.lua: -------------------------------------------------------------------------------- 1 | -- Configuration for each individual plugin 2 | ---@diagnostic disable: need-check-nil 3 | local config = {} 4 | local symbols = Ice.symbols 5 | local config_root = string.gsub(vim.fn.stdpath "config" --[[@as string]], "\\", "/") 6 | 7 | -- Add IceLoad event 8 | vim.api.nvim_create_autocmd("User", { 9 | pattern = "IceAfter colorscheme", 10 | callback = function() 11 | local function should_trigger() 12 | return vim.bo.filetype ~= "dashboard" and vim.api.nvim_buf_get_name(0) ~= "" 13 | end 14 | 15 | local function trigger() 16 | vim.api.nvim_exec_autocmds("User", { pattern = "IceLoad" }) 17 | end 18 | 19 | if should_trigger() then 20 | trigger() 21 | return 22 | end 23 | 24 | local ice_load 25 | ice_load = vim.api.nvim_create_autocmd("BufEnter", { 26 | callback = function() 27 | if should_trigger() then 28 | trigger() 29 | vim.api.nvim_del_autocmd(ice_load) 30 | end 31 | end, 32 | }) 33 | end, 34 | }) 35 | 36 | config.bufferline = { 37 | "akinsho/bufferline.nvim", 38 | dependencies = { "nvim-tree/nvim-web-devicons" }, 39 | event = "User IceLoad", 40 | opts = { 41 | options = { 42 | close_command = ":BufferLineClose %d", 43 | right_mouse_command = ":BufferLineClose %d", 44 | separator_style = "thin", 45 | offsets = { 46 | { 47 | filetype = "NvimTree", 48 | text = "File Explorer", 49 | highlight = "Directory", 50 | text_align = "left", 51 | }, 52 | }, 53 | diagnostics = "nvim_lsp", 54 | diagnostics_indicator = function(_, _, diagnostics_dict, _) 55 | local s = " " 56 | for e, n in pairs(diagnostics_dict) do 57 | local sym = e == "error" and symbols.Error or (e == "warning" and symbols.Warn or symbols.Info) 58 | s = s .. n .. sym 59 | end 60 | return s 61 | end, 62 | }, 63 | }, 64 | config = function(_, opts) 65 | vim.api.nvim_create_user_command("BufferLineClose", function(buffer_line_opts) 66 | local bufnr = 1 * buffer_line_opts.args 67 | local buf_is_modified = vim.api.nvim_get_option_value("modified", { buf = bufnr }) 68 | 69 | local bdelete_arg 70 | if bufnr == 0 then 71 | bdelete_arg = "" 72 | else 73 | bdelete_arg = " " .. bufnr 74 | end 75 | local command = "bdelete!" .. bdelete_arg 76 | if buf_is_modified then 77 | local option = vim.fn.confirm("File is not saved. Close anyway?", "&Yes\n&No", 2) 78 | if option == 1 then 79 | vim.cmd(command) 80 | end 81 | else 82 | vim.cmd(command) 83 | end 84 | end, { nargs = 1 }) 85 | 86 | require("bufferline").setup(opts) 87 | 88 | require("nvim-web-devicons").setup { 89 | override = { 90 | typ = { icon = "󰰥", color = "#239dad", name = "typst" }, 91 | }, 92 | } 93 | end, 94 | keys = { 95 | { "bc", "BufferLinePickClose", desc = "pick close", silent = true }, 96 | { "bd", "BufferLineClose 0", desc = "close current buffer", silent = true }, 97 | { "bh", "BufferLineCyclePrev", desc = "prev buffer", silent = true }, 98 | { "bl", "BufferLineCycleNext", desc = "next buffer", silent = true }, 99 | { "bo", "BufferLineCloseOthers", desc = "close others", silent = true }, 100 | { "bp", "BufferLinePick", desc = "pick buffer", silent = true }, 101 | { "bm", "IceRepeat BufferLineMoveNext", desc = "move right", silent = true }, 102 | { "bM", "IceRepeat BufferLineMovePrev", desc = "move left", silent = true }, 103 | }, 104 | } 105 | 106 | config.colorizer = { 107 | "NvChad/nvim-colorizer.lua", 108 | main = "colorizer", 109 | event = "User IceLoad", 110 | opts = { 111 | filetypes = { 112 | "*", 113 | css = { 114 | names = true, 115 | }, 116 | }, 117 | user_default_options = { 118 | css = true, 119 | css_fn = true, 120 | names = false, 121 | always_update = true, 122 | }, 123 | }, 124 | config = function(_, opts) 125 | require("colorizer").setup(opts) 126 | vim.cmd "ColorizerToggle" 127 | end, 128 | } 129 | 130 | config.dashboard = { 131 | "nvimdev/dashboard-nvim", 132 | event = "User IceAfter colorscheme", 133 | opts = { 134 | theme = "doom", 135 | config = { 136 | -- https://patorjk.com/software/taag/#p=display&f=ANSI%20Shadow&t=icenvim 137 | header = { 138 | " ", 139 | "██╗ ██████╗███████╗███╗ ██╗██╗ ██╗██╗███╗ ███╗", 140 | "██║██╔════╝██╔════╝████╗ ██║██║ ██║██║████╗ ████║", 141 | "██║██║ █████╗ ██╔██╗ ██║██║ ██║██║██╔████╔██║", 142 | "██║██║ ██╔══╝ ██║╚██╗██║╚██╗ ██╔╝██║██║╚██╔╝██║", 143 | "██║╚██████╗███████╗██║ ╚████║ ╚████╔╝ ██║██║ ╚═╝ ██║", 144 | "╚═╝ ╚═════╝╚══════╝╚═╝ ╚═══╝ ╚═══╝ ╚═╝╚═╝ ╚═", 145 | " ", 146 | string.format(" %s ", require("core.utils").version), 147 | " ", 148 | }, 149 | center = { 150 | { 151 | icon = " ", 152 | desc = "Lazy Profile", 153 | action = "Lazy profile", 154 | }, 155 | { 156 | icon = " ", 157 | desc = "Edit preferences ", 158 | action = string.format("edit %s/lua/custom/init.lua", config_root), 159 | }, 160 | { 161 | icon = " ", 162 | desc = "Mason", 163 | action = "Mason", 164 | }, 165 | { 166 | icon = " ", 167 | desc = "About IceNvim", 168 | action = "IceAbout", 169 | }, 170 | }, 171 | footer = { "🧊 Hope that you enjoy using IceNvim 😀😀😀" }, 172 | }, 173 | }, 174 | config = function(_, opts) 175 | require("dashboard").setup(opts) 176 | 177 | if vim.api.nvim_buf_get_name(0) == "" then 178 | vim.cmd "Dashboard" 179 | end 180 | 181 | -- Use the highlight command to replace instead of overriding the original highlight group 182 | -- Much more convenient than using vim.api.nvim_set_hl() 183 | vim.cmd "highlight DashboardFooter cterm=NONE gui=NONE" 184 | end, 185 | } 186 | 187 | config.fidget = { 188 | "j-hui/fidget.nvim", 189 | event = "VeryLazy", 190 | opts = { 191 | notification = { 192 | override_vim_notify = true, 193 | window = { 194 | x_padding = 2, 195 | align = "top", 196 | }, 197 | }, 198 | integration = { 199 | ["nvim-tree"] = { 200 | enable = false, 201 | }, 202 | }, 203 | }, 204 | } 205 | 206 | config.fittencode = { 207 | "luozhiya/fittencode.nvim", 208 | opts = { 209 | inline_completion = { 210 | enable = false, 211 | }, 212 | chat = { 213 | style = "floating", 214 | floating = { 215 | size = { width = 0.6, height = 0.6 }, 216 | }, 217 | }, 218 | keymaps = { 219 | chat = { 220 | ["q"] = "close", 221 | [""] = "goto_previous_conversation", 222 | [""] = "goto_next_conversation", 223 | ["y"] = "copy_conversation", 224 | ["Y"] = "copy_all_conversations", 225 | ["d"] = "delete_conversation", 226 | ["D"] = "delete_all_conversations", 227 | }, 228 | }, 229 | source_completion = { 230 | enable = true, 231 | engine = "blink", 232 | }, 233 | completion_mode = "source", 234 | log = { 235 | level = vim.log.levels.WARN, 236 | max_size = 1, 237 | }, 238 | }, 239 | config = function(_, opts) 240 | require("blink.cmp").add_source_provider("fittencode", { 241 | name = "fittencode", 242 | module = "fittencode.sources.blink", 243 | fallbacks = { "buffer" }, 244 | }) 245 | 246 | Ice.__FITTENCODE_SOURCE_ADDED = true 247 | require("fittencode").setup(opts) 248 | end, 249 | keys = { 250 | { "fb", "Fitten find_bugs", mode = "v", desc = "find bugs" }, 251 | { "fc", "Fitten show_chat", mode = "v", desc = "show chat" }, 252 | { "fd", "Fitten document_code", mode = "v", desc = "document code" }, 253 | { "fe", "Fitten explain_code", mode = "v", desc = "explain code" }, 254 | { "fE", "Fitten edit_code", mode = "v", desc = "edit code" }, 255 | { "fo", "Fitten optimize_code", mode = "v", desc = "optimize code" }, 256 | { "fr", "Fitten refactor_code", mode = "v", desc = "refactor code" }, 257 | { 258 | "fs", 259 | "Fitten enable_completionlua vim.notify('FittenCode enabled')", 260 | desc = "start fittencode", 261 | }, 262 | { 263 | "", 264 | function() 265 | require("blink.cmp").show { providers = { "fittencode" } } 266 | end, 267 | mode = "i", 268 | desc = "show fittencode completion", 269 | }, 270 | }, 271 | enabled = false, 272 | } 273 | 274 | config.gitsigns = { 275 | "lewis6991/gitsigns.nvim", 276 | event = "User IceLoad", 277 | main = "gitsigns", 278 | opts = {}, 279 | keys = { 280 | { "gn", "Gitsigns next_hunk", desc = "next hunk", silent = true }, 281 | { "gp", "Gitsigns prev_hunk", desc = "prev hunk", silent = true }, 282 | { "gP", "Gitsigns preview_hunk", desc = "preview hunk", silent = true }, 283 | { "gs", "Gitsigns stage_hunk", desc = "stage hunk", silent = true }, 284 | { "gu", "Gitsigns undo_stage_hunk", desc = "undo stage", silent = true }, 285 | { "gr", "Gitsigns reset_hunk", desc = "reset hunk", silent = true }, 286 | { "gB", "Gitsigns stage_buffer", desc = "stage buffer", silent = true }, 287 | { "gb", "Gitsigns blame", desc = "git blame", silent = true }, 288 | { "gl", "Gitsigns blame_line", desc = "git blame line", silent = true }, 289 | }, 290 | } 291 | 292 | config["grug-far"] = { 293 | "MagicDuck/grug-far.nvim", 294 | opts = { 295 | disableBufferLineNumbers = true, 296 | startInInsertMode = true, 297 | windowCreationCommand = "tabnew %", 298 | }, 299 | keys = { 300 | { "ug", "GrugFar", desc = "find and replace", silent = true }, 301 | }, 302 | } 303 | 304 | config.hop = { 305 | "smoka7/hop.nvim", 306 | main = "hop", 307 | opts = { 308 | -- This is actually equal to: 309 | -- require("hop.hint").HintPosition.END 310 | hint_position = 3, 311 | keys = "fjghdksltyrueiwoqpvbcnxmza", 312 | }, 313 | keys = { 314 | { "hp", "HopWord", desc = "hop word", silent = true }, 315 | }, 316 | } 317 | 318 | config["indent-blankline"] = { 319 | "lukas-reineke/indent-blankline.nvim", 320 | event = "User IceAfter nvim-treesitter", 321 | main = "ibl", 322 | opts = { 323 | exclude = { 324 | filetypes = { "dashboard", "terminal", "help", "log", "markdown", "TelescopePrompt" }, 325 | }, 326 | indent = { 327 | highlight = { 328 | "IblIndent", 329 | "RainbowDelimiterRed", 330 | "RainbowDelimiterYellow", 331 | "RainbowDelimiterBlue", 332 | "RainbowDelimiterOrange", 333 | "RainbowDelimiterGreen", 334 | "RainbowDelimiterViolet", 335 | "RainbowDelimiterCyan", 336 | }, 337 | }, 338 | }, 339 | } 340 | 341 | config.lualine = { 342 | "nvim-lualine/lualine.nvim", 343 | dependencies = { "nvim-tree/nvim-web-devicons" }, 344 | event = "User IceLoad", 345 | main = "lualine", 346 | opts = { 347 | options = { 348 | theme = "auto", 349 | component_separators = { left = "", right = "" }, 350 | section_separators = { left = "", right = "" }, 351 | disabled_filetypes = { "undotree", "diff" }, 352 | }, 353 | extensions = { "nvim-tree" }, 354 | sections = { 355 | lualine_b = { "branch", "diff" }, 356 | lualine_c = { 357 | "filename", 358 | }, 359 | lualine_x = { 360 | "filesize", 361 | { 362 | "fileformat", 363 | symbols = { unix = symbols.Unix, dos = symbols.Dos, mac = symbols.Mac }, 364 | }, 365 | "encoding", 366 | "filetype", 367 | }, 368 | }, 369 | }, 370 | } 371 | 372 | config["markdown-preview"] = { 373 | "iamcco/markdown-preview.nvim", 374 | ft = "markdown", 375 | config = function() 376 | vim.g.mkdp_filetypes = { "markdown" } 377 | vim.g.mkdp_auto_close = 0 378 | end, 379 | build = "cd app && yarn install", 380 | keys = { 381 | { 382 | "", 383 | "MarkdownPreviewToggle", 384 | desc = "markdown preview", 385 | ft = "markdown", 386 | silent = true, 387 | }, 388 | }, 389 | } 390 | 391 | config.neogit = { 392 | "NeogitOrg/neogit", 393 | dependencies = { "nvim-lua/plenary.nvim" }, 394 | main = "neogit", 395 | opts = { 396 | disable_hint = true, 397 | status = { 398 | recent_commit_count = 30, 399 | }, 400 | commit_editor = { 401 | kind = "auto", 402 | show_staged_diff = false, 403 | }, 404 | }, 405 | keys = { 406 | { "gt", "Neogit", desc = "neogit", silent = true }, 407 | }, 408 | config = function(_, opts) 409 | require("neogit").setup(opts) 410 | Ice.ft.NeogitCommitMessage = function() 411 | vim.api.nvim_win_set_cursor(0, { 1, 0 }) 412 | end 413 | end, 414 | } 415 | 416 | config.nui = { 417 | "MunifTanjim/nui.nvim", 418 | lazy = true, 419 | } 420 | 421 | config["nvim-autopairs"] = { 422 | "windwp/nvim-autopairs", 423 | event = "InsertEnter", 424 | main = "nvim-autopairs", 425 | opts = {}, 426 | } 427 | 428 | config["nvim-scrollview"] = { 429 | "dstein64/nvim-scrollview", 430 | event = "User IceLoad", 431 | main = "scrollview", 432 | opts = { 433 | excluded_filetypes = { "nvimtree" }, 434 | current_only = true, 435 | winblend = 75, 436 | base = "right", 437 | column = 1, 438 | }, 439 | } 440 | 441 | config["nvim-transparent"] = { 442 | "xiyaowong/nvim-transparent", 443 | opts = { 444 | extra_groups = { 445 | "NvimTreeNormal", 446 | "NvimTreeNormalNC", 447 | }, 448 | }, 449 | config = function(_, opts) 450 | local autogroup = vim.api.nvim_create_augroup("transparent", { clear = true }) 451 | vim.api.nvim_create_autocmd("ColorScheme", { 452 | group = autogroup, 453 | callback = function() 454 | local normal_hl = vim.api.nvim_get_hl(0, { name = "Normal" }) 455 | local foreground = string.format("#%06x", normal_hl.fg) 456 | local background = string.format("#%06x", normal_hl.bg) 457 | vim.cmd("highlight default IceNormal guifg=" .. foreground .. " guibg=" .. background) 458 | 459 | require("transparent").clear() 460 | end, 461 | }) 462 | -- Enable transparent by default 463 | local transparent_cache = vim.fn.stdpath "data" .. "/transparent_cache" 464 | if not require("core.utils").file_exists(transparent_cache) then 465 | local f = io.open(transparent_cache, "w") 466 | f:write "true" 467 | f:close() 468 | end 469 | 470 | require("transparent").setup(opts) 471 | 472 | local old_get_hl = vim.api.nvim_get_hl 473 | vim.api.nvim_get_hl = function(ns_id, opt) 474 | if opt.name == "Normal" then 475 | local attempt = old_get_hl(0, { name = "IceNormal" }) 476 | if next(attempt) ~= nil then 477 | opt.name = "IceNormal" 478 | end 479 | end 480 | return old_get_hl(ns_id, opt) 481 | end 482 | end, 483 | } 484 | 485 | config["nvim-tree"] = { 486 | "nvim-tree/nvim-tree.lua", 487 | dependencies = { "nvim-tree/nvim-web-devicons" }, 488 | opts = { 489 | on_attach = function(bufnr) 490 | local api = require "nvim-tree.api" 491 | local opt = { buffer = bufnr, silent = true } 492 | 493 | api.config.mappings.default_on_attach(bufnr) 494 | 495 | require("core.utils").group_map({ 496 | edit = { 497 | "n", 498 | "", 499 | function() 500 | local node = api.tree.get_node_under_cursor() 501 | if node.name ~= ".." and node.fs_stat.type == "file" then 502 | -- Taken partially from: 503 | -- https://support.microsoft.com/en-us/windows/common-file-name-extensions-in-windows-da4a4430-8e76-89c5-59f7-1cdbbc75cb01 504 | -- 505 | -- Not all are included for speed's sake 506 | -- stylua: ignore start 507 | local extensions_opened_externally = { 508 | "avi", "bmp", "doc", "docx", "exe", "flv", "gif", "jpg", "jpeg", "m4a", "mov", "mp3", 509 | "mp4", "mpeg", "mpg", "pdf", "png", "ppt", "pptx", "psd", "pub", "rar", "rtf", "tif", 510 | "tiff", "wav", "xls", "xlsx", "zip", 511 | } 512 | -- stylua: ignore end 513 | if table.find(extensions_opened_externally, node.extension) then 514 | api.node.run.system() 515 | return 516 | end 517 | end 518 | 519 | api.node.open.edit() 520 | end, 521 | }, 522 | vertical_split = { "n", "V", api.node.open.vertical }, 523 | horizontal_split = { "n", "H", api.node.open.horizontal }, 524 | toggle_hidden_file = { "n", ".", api.tree.toggle_hidden_filter }, 525 | reload = { "n", "", api.tree.reload }, 526 | create = { "n", "a", api.fs.create }, 527 | remove = { "n", "d", api.fs.remove }, 528 | rename = { "n", "r", api.fs.rename }, 529 | cut = { "n", "x", api.fs.cut }, 530 | copy = { "n", "y", api.fs.copy.node }, 531 | paste = { "n", "p", api.fs.paste }, 532 | system_run = { "n", "s", api.node.run.system }, 533 | show_info = { "n", "i", api.node.show_info_popup }, 534 | }, opt) 535 | end, 536 | git = { 537 | enable = false, 538 | }, 539 | update_focused_file = { 540 | enable = true, 541 | }, 542 | filters = { 543 | dotfiles = false, 544 | custom = { "node_modules", "^.git$" }, 545 | exclude = { ".gitignore" }, 546 | }, 547 | respect_buf_cwd = true, 548 | view = { 549 | width = 30, 550 | side = "left", 551 | number = false, 552 | relativenumber = false, 553 | signcolumn = "yes", 554 | }, 555 | actions = { 556 | open_file = { 557 | resize_window = true, 558 | quit_on_open = true, 559 | }, 560 | }, 561 | }, 562 | keys = { 563 | { "uf", "NvimTreeToggle", desc = "toggle nvim tree", silent = true }, 564 | }, 565 | } 566 | 567 | config["nvim-treesitter"] = { 568 | "nvim-treesitter/nvim-treesitter", 569 | build = ":TSUpdate", 570 | dependencies = { "hiphish/rainbow-delimiters.nvim" }, 571 | event = "User IceAfter colorscheme", 572 | branch = "main", 573 | opts = { 574 | -- Preserved for compatibility concerns 575 | -- stylua: ignore start 576 | ensure_installed = { 577 | "bash", "c", "c_sharp", "cpp", "css", "go", "html", "javascript", "json", "lua", "markdown", 578 | "markdown_inline", "python", "query", "rust", "toml", "typescript", "typst", "tsx", "vim", "vimdoc", 579 | }, 580 | -- stylua: ignore end 581 | }, 582 | config = function(_, opts) 583 | local nvim_treesitter = require "nvim-treesitter" 584 | nvim_treesitter.setup() 585 | 586 | local pattern = {} 587 | for _, parser in ipairs(opts.ensure_installed) do 588 | local has_parser, _ = pcall(vim.treesitter.language.inspect, parser) 589 | 590 | if not has_parser then 591 | -- Needs restart to take effect 592 | nvim_treesitter.install(parser) 593 | else 594 | vim.list_extend(pattern, vim.treesitter.language.get_filetypes(parser)) 595 | end 596 | end 597 | 598 | local group = vim.api.nvim_create_augroup("NvimTreesitterFt", { clear = true }) 599 | vim.api.nvim_create_autocmd("FileType", { 600 | group = group, 601 | pattern = pattern, 602 | callback = function(ev) 603 | local max_filesize = 100 * 1024 604 | local ok, stats = pcall(vim.uv.fs_stat, vim.api.nvim_buf_get_name(ev.buf)) 605 | if not (ok and stats and stats.size > max_filesize) then 606 | vim.treesitter.start() 607 | if vim.bo.filetype ~= "dart" then 608 | -- Conflicts with flutter-tools.nvim, causing performance issues 609 | vim.bo.indentexpr = "v:lua.require'nvim-treesitter'.indentexpr()" 610 | end 611 | end 612 | end, 613 | }) 614 | 615 | local rainbow_delimiters = require "rainbow-delimiters" 616 | 617 | vim.g.rainbow_delimiters = { 618 | strategy = { 619 | [""] = rainbow_delimiters.strategy["global"], 620 | vim = rainbow_delimiters.strategy["local"], 621 | }, 622 | query = { 623 | [""] = "rainbow-delimiters", 624 | lua = "rainbow-blocks", 625 | }, 626 | highlight = { 627 | "RainbowDelimiterRed", 628 | "RainbowDelimiterYellow", 629 | "RainbowDelimiterBlue", 630 | "RainbowDelimiterOrange", 631 | "RainbowDelimiterGreen", 632 | "RainbowDelimiterViolet", 633 | "RainbowDelimiterCyan", 634 | }, 635 | } 636 | rainbow_delimiters.enable() 637 | 638 | -- In markdown files, the rendered output would only display the correct highlight if the code is set to scheme 639 | -- However, this would result in incorrect highlight in neovim 640 | -- Therefore, the scheme language should be linked to query 641 | vim.treesitter.language.register("query", "scheme") 642 | 643 | vim.api.nvim_exec_autocmds("User", { pattern = "IceAfter nvim-treesitter" }) 644 | vim.api.nvim_exec_autocmds("FileType", { group = "NvimTreesitterFt" }) 645 | end, 646 | } 647 | 648 | config.orgmode = { 649 | "nvim-orgmode/orgmode", 650 | ft = { "org" }, 651 | -- See https://github.com/nvim-orgmode/orgmode/blob/master/DOCS.md 652 | opts = { 653 | org_agenda_files = "~/Documents/orgfiles/**/*", 654 | org_default_notes_file = "~/Documents/orgfiles/refile.org", 655 | org_todo_keywords = { "TODO(t)", "URGENT(u)", "PENDING(p)", "|", "DONE(d)" }, 656 | win_split_mode = { "float", 0.6 }, 657 | org_startup_folded = "inherit", 658 | org_todo_keyword_faces = { 659 | URGENT = ":foreground white :background red :weight bold", 660 | PENDING = ":foreground white :background gray", 661 | }, 662 | org_hide_leading_stars = true, 663 | org_hide_emphasis_markers = true, 664 | mappings = { 665 | org = { 666 | org_toggle_checkbox = "oT", 667 | }, 668 | }, 669 | }, 670 | config = function(_, opts) 671 | require("blink.cmp").add_source_provider("orgmode", { 672 | name = "Orgmode", 673 | module = "orgmode.org.autocompletion.blink", 674 | fallbacks = { "buffer" }, 675 | }) 676 | Ice.__ORGMODE_SOURCE_ADDED = true 677 | require("orgmode").setup(opts) 678 | end, 679 | keys = { 680 | { "lf", "gggqG", mode = "n", desc = "format file", ft = "org", silent = true }, 681 | { "lf", "gq", mode = "v", desc = "format selected", ft = "org", silent = true }, 682 | { "oa", "Org agenda", desc = "orgmode agenda" }, 683 | { "oc", "Org capture", desc = "orgmode capture" }, 684 | }, 685 | } 686 | 687 | config.surround = { 688 | "kylechui/nvim-surround", 689 | version = "*", 690 | opts = {}, 691 | event = "User IceLoad", 692 | } 693 | 694 | config.telescope = { 695 | "nvim-telescope/telescope.nvim", 696 | dependencies = { 697 | "nvim-lua/plenary.nvim", 698 | { 699 | "nvim-telescope/telescope-fzf-native.nvim", 700 | build = "cmake -S. -Bbuild -DCMAKE_BUILD_TYPE=Release && " 701 | .. "cmake --build build --config Release && " 702 | .. "cmake --install build --prefix build", 703 | }, 704 | }, 705 | -- ensure that other plugins that use telescope can function properly 706 | cmd = "Telescope", 707 | opts = { 708 | defaults = { 709 | initial_mode = "insert", 710 | mappings = { 711 | i = { 712 | [""] = "move_selection_next", 713 | [""] = "move_selection_previous", 714 | [""] = "cycle_history_next", 715 | [""] = "cycle_history_prev", 716 | [""] = "close", 717 | [""] = "preview_scrolling_up", 718 | [""] = "preview_scrolling_down", 719 | }, 720 | }, 721 | }, 722 | pickers = { 723 | find_files = { 724 | winblend = 20, 725 | }, 726 | }, 727 | extensions = { 728 | fzf = { 729 | fuzzy = true, 730 | override_generic_sorter = true, 731 | override_file_sorter = true, 732 | case_mode = "smart_case", 733 | }, 734 | }, 735 | }, 736 | config = function(_, opts) 737 | local telescope = require "telescope" 738 | telescope.setup(opts) 739 | telescope.load_extension "fzf" 740 | end, 741 | keys = { 742 | { "tf", "Telescope find_files", desc = "find file", silent = true }, 743 | { "t", "Telescope live_grep", desc = "live grep", silent = true }, 744 | }, 745 | } 746 | 747 | config["todo-comments"] = { 748 | "folke/todo-comments.nvim", 749 | dependencies = { "nvim-lua/plenary.nvim" }, 750 | event = "User IceLoad", 751 | main = "todo-comments", 752 | opts = {}, 753 | keys = { 754 | { "ut", "TodoTelescope", desc = "todo list", silent = true }, 755 | }, 756 | } 757 | 758 | config.ufo = { 759 | "kevinhwang91/nvim-ufo", 760 | dependencies = { 761 | "kevinhwang91/promise-async", 762 | }, 763 | event = "VeryLazy", 764 | opts = { 765 | preview = { 766 | win_config = { 767 | border = "rounded", 768 | winhighlight = "Normal:Folded", 769 | winblend = 0, 770 | }, 771 | }, 772 | }, 773 | config = function(_, opts) 774 | vim.opt.foldenable = true 775 | 776 | require("ufo").setup(opts) 777 | end, 778 | keys = { 779 | { 780 | "zR", 781 | function() 782 | require("ufo").openAllFolds() 783 | end, 784 | desc = "Open all folds", 785 | }, 786 | { 787 | "zM", 788 | function() 789 | require("ufo").closeAllFolds() 790 | end, 791 | desc = "Close all folds", 792 | }, 793 | { 794 | "zp", 795 | function() 796 | require("ufo").peekFoldedLinesUnderCursor() 797 | end, 798 | desc = "Preview folded content", 799 | }, 800 | }, 801 | } 802 | 803 | config.undotree = { 804 | "mbbill/undotree", 805 | config = function() 806 | vim.g.undotree_WindowLayout = 2 807 | vim.g.undotree_TreeNodeShape = "-" 808 | end, 809 | keys = { 810 | { "uu", "UndotreeToggle", desc = "undo tree toggle", silent = true }, 811 | }, 812 | } 813 | 814 | config["which-key"] = { 815 | "folke/which-key.nvim", 816 | event = "VeryLazy", 817 | opts = { 818 | icons = { 819 | mappings = false, 820 | }, 821 | plugins = { 822 | marks = true, 823 | registers = true, 824 | spelling = { 825 | enabled = false, 826 | }, 827 | presets = { 828 | operators = false, 829 | motions = true, 830 | text_objects = true, 831 | windows = true, 832 | nav = true, 833 | z = true, 834 | g = true, 835 | }, 836 | }, 837 | spec = { 838 | { "b", group = "+buffer" }, 839 | { "c", group = "+comment" }, 840 | { "f", group = "+fittencode" }, 841 | { "g", group = "+git" }, 842 | { "h", group = "+hop" }, 843 | { "l", group = "+lsp" }, 844 | { "o", group = "+orgmode" }, 845 | { "t", group = "+telescope" }, 846 | { "u", group = "+utils" }, 847 | }, 848 | win = { 849 | border = "none", 850 | padding = { 1, 0, 1, 0 }, 851 | wo = { 852 | winblend = 0, 853 | }, 854 | zindex = 1000, 855 | }, 856 | }, 857 | } 858 | 859 | -- Colorschemes 860 | config["github"] = { "projekt0n/github-nvim-theme", lazy = true } 861 | config["gruvbox"] = { "ellisonleao/gruvbox.nvim", lazy = true } 862 | config["kanagawa"] = { "rebelot/kanagawa.nvim", lazy = true } 863 | config["miasma"] = { "xero/miasma.nvim", lazy = true } 864 | config["nightfox"] = { "EdenEast/nightfox.nvim", lazy = true } 865 | config["tokyonight"] = { "folke/tokyonight.nvim", lazy = true } 866 | 867 | Ice.plugins = config 868 | -------------------------------------------------------------------------------- /lua/plugins/init.lua: -------------------------------------------------------------------------------- 1 | require "plugins.lazy" 2 | require "plugins.config" 3 | require "plugins.colorscheme" 4 | require "plugins.keymap" 5 | 6 | require "lsp" 7 | -------------------------------------------------------------------------------- /lua/plugins/keymap.lua: -------------------------------------------------------------------------------- 1 | local utils = require "plugins.utils" 2 | 3 | Ice.keymap.plugins = { 4 | lazy_profile = { "n", "ul", "Lazy profile" }, 5 | select_colorscheme = { "n", "", utils.select_colorscheme }, 6 | view_configuration = { "n", "uc", utils.view_configuration }, 7 | } 8 | -------------------------------------------------------------------------------- /lua/plugins/lazy.lua: -------------------------------------------------------------------------------- 1 | -- Set up lazy.nvim 2 | local lazypath = vim.fn.stdpath "data" .. "/lazy/lazy.nvim" 3 | 4 | if not require("core.utils").noplugin then 5 | if not vim.uv.fs_stat(lazypath) then 6 | vim.fn.system { 7 | "git", 8 | "clone", 9 | "--filter=blob:none", 10 | "https://github.com/folke/lazy.nvim.git", 11 | "--branch=stable", 12 | lazypath, 13 | } 14 | end 15 | vim.opt.rtp:prepend(lazypath) 16 | end 17 | 18 | Ice.lazy = { 19 | performance = { 20 | rtp = { 21 | disabled_plugins = { 22 | "editorconfig", 23 | "gzip", 24 | "man", 25 | "matchit", 26 | "matchparen", 27 | "netrwPlugin", 28 | "osc52", 29 | "rplugin", 30 | "shada", 31 | "spellfile", 32 | "tarPlugin", 33 | "tohtml", 34 | "tutor", 35 | "zipPlugin", 36 | }, 37 | }, 38 | }, 39 | ui = { 40 | backdrop = 100, 41 | }, 42 | } 43 | -------------------------------------------------------------------------------- /lua/plugins/utils.lua: -------------------------------------------------------------------------------- 1 | ---@diagnostic disable: need-check-nil 2 | local utils = {} 3 | 4 | -- Set up colorscheme and Ice.colorscheme, but does not take care of lualine 5 | -- The colorscheme is a table with: 6 | -- - name: to be called with the `colorscheme` command 7 | -- - setup: optional; can either be: 8 | -- - a function called alongside `colorscheme` 9 | -- - a table for plugin setup 10 | -- - background: "light" / "dark" 11 | -- - lualine_theme: optional 12 | ---@param colorscheme_name string 13 | utils.colorscheme = function(colorscheme_name) 14 | Ice.colorscheme = colorscheme_name 15 | 16 | local colorscheme = Ice.colorschemes[colorscheme_name] 17 | if not colorscheme then 18 | vim.notify(colorscheme_name .. " is not a valid color scheme!", vim.log.levels.ERROR) 19 | return 20 | end 21 | 22 | if type(colorscheme.setup) == "table" then 23 | require(colorscheme.name).setup(colorscheme.setup) 24 | elseif type(colorscheme.setup) == "function" then 25 | colorscheme.setup() 26 | end 27 | require("lazy.core.loader").colorscheme(colorscheme.name) 28 | vim.cmd("colorscheme " .. colorscheme.name) 29 | vim.o.background = colorscheme.background 30 | 31 | vim.api.nvim_set_hl(0, "Visual", { reverse = true }) 32 | 33 | vim.api.nvim_exec_autocmds("User", { pattern = "IceAfter colorscheme" }) 34 | end 35 | 36 | -- Switch colorscheme 37 | utils.select_colorscheme = function() 38 | local status, _ = pcall(require, "telescope") 39 | if not status then 40 | return 41 | end 42 | 43 | local pickers = require "telescope.pickers" 44 | local finders = require "telescope.finders" 45 | local conf = require("telescope.config").values 46 | local actions = require "telescope.actions" 47 | local action_state = require "telescope.actions.state" 48 | 49 | local function picker(opts) 50 | opts = opts or {} 51 | 52 | local colorschemes = Ice.colorschemes 53 | local suffix_current = " (current)" 54 | local results = { Ice.colorscheme .. suffix_current } 55 | for name, _ in require("core.utils").ordered_pair(colorschemes) do 56 | if name ~= Ice.colorscheme then 57 | results[#results + 1] = name 58 | end 59 | end 60 | 61 | pickers 62 | .new(opts, { 63 | prompt_title = "Colorschemes", 64 | finder = finders.new_table { 65 | entry_maker = function(entry) 66 | local pattern = string.gsub(suffix_current, "%(", "%%%(") 67 | pattern = string.gsub(pattern, "%)", "%%%)") 68 | local colorscheme, _ = string.gsub(entry, pattern, "") 69 | 70 | return { 71 | value = colorscheme, 72 | display = entry, 73 | ordinal = entry, 74 | } 75 | end, 76 | results = results, 77 | }, 78 | sorter = conf.generic_sorter(opts), 79 | attach_mappings = function(prompt_bufnr, _) 80 | actions.select_default:replace(function() 81 | actions.close(prompt_bufnr) 82 | 83 | local selection = action_state.get_selected_entry() 84 | if selection == nil then 85 | return 86 | end 87 | 88 | local colorscheme = selection.value 89 | local config = colorschemes[colorscheme] 90 | 91 | if Ice.plugins["nvim-transparent"].enabled ~= false then 92 | if config.background == "light" then 93 | ---@diagnostic disable-next-line: param-type-mismatch 94 | pcall(vim.cmd, "TransparentDisable") 95 | else 96 | ---@diagnostic disable-next-line: param-type-mismatch 97 | pcall(vim.cmd, "TransparentEnable") 98 | end 99 | end 100 | 101 | utils.colorscheme(selection.value) 102 | 103 | local colorscheme_cache = vim.fn.stdpath "data" .. "/colorscheme" 104 | local f = io.open(colorscheme_cache, "w") 105 | f:write(colorscheme) 106 | f:close() 107 | end) 108 | return true 109 | end, 110 | }) 111 | :find() 112 | end 113 | 114 | picker() 115 | end 116 | 117 | -- Quickly look through configuration files using telescope 118 | utils.view_configuration = function() 119 | local status, _ = pcall(require, "telescope") 120 | if not status then 121 | return 122 | end 123 | 124 | local pickers = require "telescope.pickers" 125 | local finders = require "telescope.finders" 126 | local conf = require("telescope.config").values 127 | local actions = require "telescope.actions" 128 | local action_state = require "telescope.actions.state" 129 | local previewers = require "telescope.previewers.buffer_previewer" 130 | local from_entry = require "telescope.from_entry" 131 | 132 | local function picker(opts) 133 | opts = opts or {} 134 | 135 | local config_root = vim.fn.stdpath "config" 136 | local files = require("plenary.scandir").scan_dir(config_root, { hidden = true }) 137 | local sep = require("plenary.path").path.sep 138 | local picker_sep = "/" -- sep that is displayed in the picker 139 | local results = {} 140 | 141 | local make_entry = require("telescope.make_entry").gen_from_file 142 | 143 | for _, item in pairs(files) do 144 | item = string.gsub(item, config_root, "") 145 | item = string.gsub(item, sep, picker_sep) 146 | item = string.sub(item, 2) 147 | if not (string.find(item, "bin/") or string.find(item, ".git/") or string.find(item, "screenshots/")) then 148 | results[#results + 1] = item 149 | end 150 | end 151 | 152 | pickers 153 | .new(opts, { 154 | prompt_title = "Configuration Files", 155 | finder = finders.new_table { 156 | entry_maker = make_entry(opts), 157 | results = results, 158 | }, 159 | previewer = (function(_opts) 160 | _opts = _opts or {} 161 | return previewers.new_buffer_previewer { 162 | title = "Configuration", 163 | get_buffer_by_name = function(_, entry) 164 | return from_entry.path(entry, false) 165 | end, 166 | define_preview = function(self, entry) 167 | local p = config_root .. "/" .. entry.filename 168 | if p == nil or p == "" then 169 | return 170 | end 171 | conf.buffer_previewer_maker(p, self.state.bufnr, { 172 | bufname = self.state.bufname, 173 | winid = self.state.winid, 174 | preview = _opts.preview, 175 | file_encoding = _opts.file_encoding, 176 | }) 177 | end, 178 | } 179 | end)(opts), 180 | sorter = conf.generic_sorter(opts), 181 | attach_mappings = function(prompt_bufnr, _) 182 | actions.select_default:replace(function() 183 | actions.close(prompt_bufnr) 184 | 185 | local selected_entry = action_state.get_selected_entry() 186 | if selected_entry ~= nil then 187 | local selection = selected_entry[1] 188 | selection = string.gsub(selection, picker_sep, sep) 189 | local full_path = config_root .. sep .. selection 190 | 191 | vim.cmd("edit " .. full_path) 192 | end 193 | end) 194 | return true 195 | end, 196 | }) 197 | :find() 198 | end 199 | 200 | picker() 201 | end 202 | 203 | return utils 204 | -------------------------------------------------------------------------------- /screenshots/1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Shaobin-Jiang/IceNvim/1a1c595f0a03c50f6bc10f7e496db286a507d825/screenshots/1.jpg -------------------------------------------------------------------------------- /screenshots/2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Shaobin-Jiang/IceNvim/1a1c595f0a03c50f6bc10f7e496db286a507d825/screenshots/2.jpg -------------------------------------------------------------------------------- /screenshots/3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Shaobin-Jiang/IceNvim/1a1c595f0a03c50f6bc10f7e496db286a507d825/screenshots/3.jpg -------------------------------------------------------------------------------- /screenshots/4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Shaobin-Jiang/IceNvim/1a1c595f0a03c50f6bc10f7e496db286a507d825/screenshots/4.jpg --------------------------------------------------------------------------------