mirror of
https://github.com/d0zingcat/dotfiles.git
synced 2026-05-14 07:26:44 +00:00
refactor(nvim): 完整重写配置,支持 Go/Rust/Python/TypeScript 开发
- 架构重构:新增 plugins/lang/ 子目录,按语言拆分配置 - 补全引擎:nvim-cmp → blink.cmp + LuaSnip - 文件浏览:新增 neo-tree(<Space>e) - 语言支持: - Go: ray-x/go.nvim + dap-go + neotest-go - Rust: rustaceanvim + crates.nvim - Python: venv-selector + dap-python + neotest-python - TypeScript: typescript-tools.nvim(替换 ts_ls) - LSP: lazydev + mason + mason-lspconfig + fidget + inc-rename - 格式化: conform.nvim(lsp_format fallback,保存时自动格式化) - Lint: nvim-lint(selene 替换 luacheck,Mason 可直接安装) - UI: snacks.nvim(dashboard+notifier+picker)+ noice + lualine + bufferline - 编辑增强: mini.ai + mini.surround + grug-far + flash + ufo + trouble v3 - 删除废弃文件: cmp/coding/null-ls/mason/lspconfig/go/python 等旧文件 - 修复: Neovim 0.12 treesitter query 校验报错(noice routes 过滤) - 新增: NVIM_GUIDE.md 快捷键使用手册 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
@@ -1,6 +1,93 @@
|
||||
-- ui.lua - UI 增强插件配置
|
||||
-- 包含:lualine, bufferline, neo-tree, noice, snacks(dashboard+notifier), indent-blankline
|
||||
|
||||
return {
|
||||
-- snacks.nvim - 现代通知 + Dashboard + 其他实用功能
|
||||
{
|
||||
"folke/snacks.nvim",
|
||||
priority = 1000,
|
||||
lazy = false,
|
||||
opts = {
|
||||
bigfile = { enabled = true },
|
||||
dashboard = {
|
||||
enabled = true,
|
||||
sections = {
|
||||
{ section = "header" },
|
||||
{ section = "keys", gap = 1, padding = 1 },
|
||||
{ section = "startup" },
|
||||
},
|
||||
preset = {
|
||||
header = [[
|
||||
███╗ ██╗███████╗ ██████╗ ██╗ ██╗██╗███╗ ███╗
|
||||
████╗ ██║██╔════╝██╔═══██╗██║ ██║██║████╗ ████║
|
||||
██╔██╗ ██║█████╗ ██║ ██║██║ ██║██║██╔████╔██║
|
||||
██║╚██╗██║██╔══╝ ██║ ██║╚██╗ ██╔╝██║██║╚██╔╝██║
|
||||
██║ ╚████║███████╗╚██████╔╝ ╚████╔╝ ██║██║ ╚═╝ ██║
|
||||
╚═╝ ╚═══╝╚══════╝ ╚═════╝ ╚═══╝ ╚═╝╚═╝ ╚═╝]],
|
||||
keys = {
|
||||
{ icon = " ", key = "f", desc = "查找文件", action = ":lua Snacks.picker.files()" },
|
||||
{ icon = " ", key = "g", desc = "全局搜索", action = ":lua Snacks.picker.grep()" },
|
||||
{ icon = " ", key = "r", desc = "最近文件", action = ":lua Snacks.picker.recent()" },
|
||||
{ icon = " ", key = "c", desc = "编辑配置", action = ":e $MYVIMRC" },
|
||||
{ icon = " ", key = "l", desc = "Lazy", action = ":Lazy" },
|
||||
{ icon = " ", key = "q", desc = "退出", action = ":qa" },
|
||||
},
|
||||
},
|
||||
},
|
||||
notifier = {
|
||||
enabled = true,
|
||||
timeout = 3000,
|
||||
render = "compact",
|
||||
style = "compact",
|
||||
level = vim.log.levels.INFO,
|
||||
},
|
||||
quickfile = { enabled = true },
|
||||
statuscolumn = { enabled = false },
|
||||
words = {
|
||||
enabled = true,
|
||||
debounce = 100,
|
||||
notify_end = false,
|
||||
jumplist = true,
|
||||
},
|
||||
indent = {
|
||||
enabled = true,
|
||||
animate = { enabled = false },
|
||||
},
|
||||
picker = { enabled = true },
|
||||
input = { enabled = true },
|
||||
},
|
||||
keys = {
|
||||
{ "<leader>un", function() Snacks.notifier.hide() end, desc = "清除通知" },
|
||||
{ "<leader>nh", function() Snacks.notifier.show_history() end, desc = "通知历史" },
|
||||
{ "<leader>nd", function() Snacks.notifier.hide() end, desc = "关闭所有通知" },
|
||||
{ "]w", function() Snacks.words.jump(1) end, desc = "下一个引用词" },
|
||||
{ "[w", function() Snacks.words.jump(-1) end, desc = "上一个引用词" },
|
||||
{ "<leader>gg", function() Snacks.lazygit() end, desc = "LazyGit" },
|
||||
{ "<leader>gf", function() Snacks.lazygit.log_file() end, desc = "当前文件 Git 历史" },
|
||||
{ "<leader>gl", function() Snacks.lazygit.log() end, desc = "Git 日志" },
|
||||
-- Snacks picker
|
||||
{ "<leader>ff", function() Snacks.picker.files() end, desc = "查找文件" },
|
||||
{ "<leader>fr", function() Snacks.picker.recent() end, desc = "最近文件" },
|
||||
{ "<leader>fb", function() Snacks.picker.buffers() end, desc = "查找缓冲区" },
|
||||
{ "<leader>fg", function() Snacks.picker.grep() end, desc = "全局搜索" },
|
||||
{ "<leader>fw", function() Snacks.picker.grep_word() end, desc = "搜索当前单词", mode = { "n", "v" } },
|
||||
{ "<leader>fh", function() Snacks.picker.help() end, desc = "帮助页面" },
|
||||
{ "<leader>fk", function() Snacks.picker.keymaps() end, desc = "快捷键" },
|
||||
{ "<leader>fs", function() Snacks.picker.lsp_symbols() end, desc = "文档符号" },
|
||||
{ "<leader>fS", function() Snacks.picker.lsp_workspace_symbols() end, desc = "工作区符号" },
|
||||
{ "<leader>fd", function() Snacks.picker.diagnostics() end, desc = "诊断" },
|
||||
},
|
||||
init = function()
|
||||
vim.notify = function(msg, level, opts)
|
||||
if Snacks and Snacks.notifier then
|
||||
Snacks.notifier.notify(msg, level, opts)
|
||||
else
|
||||
vim.api.nvim_notify(msg or "", level or 0, opts or {})
|
||||
end
|
||||
end
|
||||
end,
|
||||
},
|
||||
|
||||
-- 状态栏
|
||||
{
|
||||
"nvim-lualine/lualine.nvim",
|
||||
@@ -8,53 +95,34 @@ return {
|
||||
dependencies = { "nvim-tree/nvim-web-devicons" },
|
||||
opts = {
|
||||
options = {
|
||||
theme = "auto", -- 自动匹配当前主题
|
||||
globalstatus = true, -- 全局状态栏
|
||||
theme = "tokyonight",
|
||||
globalstatus = true,
|
||||
disabled_filetypes = { statusline = { "dashboard", "snacks_dashboard" } },
|
||||
component_separators = { left = "", right = "" },
|
||||
section_separators = { left = "", right = "" },
|
||||
disabled_filetypes = {
|
||||
statusline = { "dashboard", "alpha" },
|
||||
winbar = { "dashboard", "alpha" },
|
||||
},
|
||||
},
|
||||
sections = {
|
||||
lualine_a = { { "mode", icon = "" } },
|
||||
lualine_b = {
|
||||
lualine_b = {
|
||||
{ "branch", icon = "" },
|
||||
{
|
||||
"diff",
|
||||
symbols = { added = " ", modified = " ", removed = " " },
|
||||
colored = true
|
||||
}
|
||||
{ "diff", symbols = { added = " ", modified = " ", removed = " " } },
|
||||
},
|
||||
lualine_c = {
|
||||
{
|
||||
"diagnostics",
|
||||
sources = { "nvim_diagnostic" },
|
||||
symbols = {
|
||||
error = " ",
|
||||
warn = " ",
|
||||
info = " ",
|
||||
hint = " ",
|
||||
},
|
||||
symbols = { error = " ", warn = " ", info = " ", hint = " " },
|
||||
},
|
||||
{ "filetype", icon_only = true, separator = "", padding = { left = 1, right = 0 } },
|
||||
{ "filename", path = 1, symbols = { modified = " ", readonly = " ", unnamed = " " } },
|
||||
},
|
||||
lualine_x = {
|
||||
-- Git 文件状态
|
||||
{
|
||||
function()
|
||||
local status = ""
|
||||
local ft = vim.bo.filetype
|
||||
|
||||
-- 检查 LSP 是否连接
|
||||
local clients = vim.lsp.get_active_clients({ bufnr = 0 })
|
||||
if #clients > 0 then
|
||||
status = status .. " LSP"
|
||||
end
|
||||
|
||||
return status
|
||||
local clients = vim.lsp.get_clients({ bufnr = 0 })
|
||||
if #clients == 0 then return "" end
|
||||
local names = vim.tbl_map(function(c) return c.name end, clients)
|
||||
return " " .. table.concat(names, ", ")
|
||||
end,
|
||||
},
|
||||
{ "encoding" },
|
||||
@@ -62,49 +130,47 @@ return {
|
||||
{ "filetype" },
|
||||
},
|
||||
lualine_y = {
|
||||
{ "progress", separator = " ", padding = { left = 1, right = 1 } },
|
||||
{ "location", padding = { left = 1, right = 1 } },
|
||||
},
|
||||
lualine_z = {
|
||||
function()
|
||||
return " " .. os.date("%R")
|
||||
end,
|
||||
{ "progress", padding = { left = 1, right = 1 } },
|
||||
{ "location", padding = { left = 0, right = 1 } },
|
||||
},
|
||||
lualine_z = { function() return " " .. os.date("%R") end },
|
||||
},
|
||||
tabline = {},
|
||||
extensions = { "neo-tree", "lazy" },
|
||||
extensions = { "neo-tree", "lazy", "toggleterm", "mason" },
|
||||
},
|
||||
},
|
||||
|
||||
-- 缩进线
|
||||
|
||||
-- 缓冲区标签页
|
||||
{
|
||||
"lukas-reineke/indent-blankline.nvim",
|
||||
main = "ibl",
|
||||
event = "BufReadPost",
|
||||
"akinsho/bufferline.nvim",
|
||||
event = "VeryLazy",
|
||||
keys = {
|
||||
{ "<leader>bp", "<Cmd>BufferLineTogglePin<CR>", desc = "标记缓冲区" },
|
||||
{ "<leader>bP", "<Cmd>BufferLineGroupClose ungrouped<CR>", desc = "关闭未标记缓冲区" },
|
||||
{ "<leader>bo", "<Cmd>BufferLineCloseOthers<CR>", desc = "关闭其他缓冲区" },
|
||||
{ "<leader>br", "<Cmd>BufferLineCloseRight<CR>", desc = "关闭右侧缓冲区" },
|
||||
{ "<leader>bl", "<Cmd>BufferLineCloseLeft<CR>", desc = "关闭左侧缓冲区" },
|
||||
{ "<S-h>", "<cmd>BufferLineCyclePrev<cr>", desc = "上一个缓冲区" },
|
||||
{ "<S-l>", "<cmd>BufferLineCycleNext<cr>", desc = "下一个缓冲区" },
|
||||
},
|
||||
opts = {
|
||||
indent = {
|
||||
char = "│", -- 缩进字符
|
||||
tab_char = "│", -- Tab 缩进字符
|
||||
},
|
||||
scope = { enabled = false }, -- 禁用范围高亮
|
||||
exclude = {
|
||||
filetypes = {
|
||||
"help",
|
||||
"alpha",
|
||||
"dashboard",
|
||||
"neo-tree",
|
||||
"Trouble",
|
||||
"lazy",
|
||||
"mason",
|
||||
"notify",
|
||||
"toggleterm",
|
||||
"lazyterm",
|
||||
options = {
|
||||
close_command = function(n) require("mini.bufremove").delete(n, false) end,
|
||||
right_mouse_command = function(n) require("mini.bufremove").delete(n, false) end,
|
||||
diagnostics = "nvim_lsp",
|
||||
always_show_bufferline = false,
|
||||
diagnostics_indicator = function(_, _, diag)
|
||||
local ret = (diag.error and " " .. diag.error .. " " or "")
|
||||
.. (diag.warning and " " .. diag.warning or "")
|
||||
return vim.trim(ret)
|
||||
end,
|
||||
offsets = {
|
||||
{ filetype = "neo-tree", text = "文件浏览器", highlight = "Directory", text_align = "left" },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
-- 文件树
|
||||
|
||||
-- 文件树(侧边栏)
|
||||
{
|
||||
"nvim-neo-tree/neo-tree.nvim",
|
||||
branch = "v3.x",
|
||||
@@ -115,20 +181,21 @@ return {
|
||||
"MunifTanjim/nui.nvim",
|
||||
},
|
||||
keys = {
|
||||
{ "<leader>e", "<cmd>Neotree toggle<cr>", desc = "切换文件浏览器" },
|
||||
{ "<leader>e", "<cmd>Neotree toggle<cr>", desc = "文件浏览器" },
|
||||
{ "<leader>o", "<cmd>Neotree focus<cr>", desc = "聚焦文件浏览器" },
|
||||
{ "<leader>ge", "<cmd>Neotree float git_status<cr>", desc = "Git 状态" },
|
||||
},
|
||||
opts = {
|
||||
sources = { "filesystem", "buffers", "git_status", "document_symbols" },
|
||||
open_files_do_not_replace_types = { "terminal", "Trouble", "qf", "edgy" },
|
||||
open_files_do_not_replace_types = { "terminal", "Trouble", "trouble", "qf" },
|
||||
filesystem = {
|
||||
bind_to_cwd = false,
|
||||
follow_current_file = { enabled = true },
|
||||
use_libuv_file_watcher = true,
|
||||
filtered_items = {
|
||||
visible = true, -- 显示被过滤的项目
|
||||
hide_dotfiles = false, -- 不隐藏点文件
|
||||
hide_gitignored = false, -- 不隐藏被 gitignore 的文件
|
||||
visible = true,
|
||||
hide_dotfiles = false,
|
||||
hide_gitignored = false,
|
||||
},
|
||||
},
|
||||
window = {
|
||||
@@ -143,188 +210,90 @@ return {
|
||||
},
|
||||
default_component_configs = {
|
||||
indent = {
|
||||
with_expanders = true, -- 启用展开图标
|
||||
with_expanders = true,
|
||||
expander_collapsed = "",
|
||||
expander_expanded = "",
|
||||
expander_highlight = "NeoTreeExpander",
|
||||
},
|
||||
git_status = {
|
||||
symbols = {
|
||||
-- 状态图标
|
||||
added = "✚", -- 或 "✚"
|
||||
modified = "", -- 或 ""
|
||||
deleted = "✖", -- 或 "✖"
|
||||
renamed = "", -- 或 ""
|
||||
untracked = "",
|
||||
ignored = "",
|
||||
unstaged = "",
|
||||
staged = "",
|
||||
conflict = "",
|
||||
},
|
||||
},
|
||||
},
|
||||
commands = {
|
||||
-- 添加自定义命令,如创建文件时创建路径
|
||||
parent_or_close = function(state)
|
||||
local node = state.tree:get_node()
|
||||
if (node.type == "directory" or node:has_children()) and node:is_expanded() then
|
||||
state.commands.toggle_node(state)
|
||||
else
|
||||
require("neo-tree.ui.renderer").focus_node(state, node:get_parent_id())
|
||||
end
|
||||
end,
|
||||
child_or_open = function(state)
|
||||
local node = state.tree:get_node()
|
||||
if node.type == "directory" or node:has_children() then
|
||||
if not node:is_expanded() then
|
||||
state.commands.toggle_node(state)
|
||||
else
|
||||
require("neo-tree.ui.renderer").focus_node(state, node:get_child_ids()[1])
|
||||
end
|
||||
else
|
||||
state.commands.open(state)
|
||||
end
|
||||
end,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
-- 顶部标签页
|
||||
{
|
||||
"akinsho/bufferline.nvim",
|
||||
event = "VeryLazy",
|
||||
keys = {
|
||||
{ "<leader>bp", "<Cmd>BufferLineTogglePin<CR>", desc = "标记/取消标记缓冲区" },
|
||||
{ "<leader>bP", "<Cmd>BufferLineGroupClose ungrouped<CR>", desc = "关闭未标记的缓冲区" },
|
||||
{ "<leader>bo", "<Cmd>BufferLineCloseOthers<CR>", desc = "关闭其他缓冲区" },
|
||||
{ "<leader>br", "<Cmd>BufferLineCloseRight<CR>", desc = "关闭右侧缓冲区" },
|
||||
{ "<leader>bl", "<Cmd>BufferLineCloseLeft<CR>", desc = "关闭左侧缓冲区" },
|
||||
{ "<S-h>", "<cmd>BufferLineCyclePrev<cr>", desc = "上一个缓冲区" },
|
||||
{ "<S-l>", "<cmd>BufferLineCycleNext<cr>", desc = "下一个缓冲区" },
|
||||
},
|
||||
opts = {
|
||||
options = {
|
||||
close_command = function(n) require("mini.bufremove").delete(n, false) end,
|
||||
right_mouse_command = function(n) require("mini.bufremove").delete(n, false) end,
|
||||
diagnostics = "nvim_lsp", -- 显示诊断
|
||||
always_show_bufferline = false,
|
||||
diagnostics_indicator = function(_, _, diag)
|
||||
local icons = {
|
||||
Error = " ",
|
||||
Warn = " ",
|
||||
Hint = " ",
|
||||
Info = " ",
|
||||
}
|
||||
local ret = (diag.error and icons.Error .. diag.error .. " " or "")
|
||||
.. (diag.warning and icons.Warn .. diag.warning or "")
|
||||
return vim.trim(ret)
|
||||
end,
|
||||
offsets = {
|
||||
{
|
||||
filetype = "neo-tree",
|
||||
text = "文件浏览器",
|
||||
highlight = "Directory",
|
||||
text_align = "left",
|
||||
added = "✚", modified = "", deleted = "✖",
|
||||
renamed = "", untracked = "", ignored = "",
|
||||
unstaged = "", staged = "", conflict = "",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
-- 通知系统
|
||||
|
||||
-- noice.nvim - 命令行/通知/LSP UI 增强
|
||||
{
|
||||
"rcarriga/nvim-notify",
|
||||
keys = {
|
||||
{
|
||||
"<leader>un",
|
||||
function()
|
||||
require("notify").dismiss({ silent = true, pending = true })
|
||||
end,
|
||||
desc = "清除所有通知",
|
||||
},
|
||||
},
|
||||
opts = {
|
||||
timeout = 3000,
|
||||
max_height = function()
|
||||
return math.floor(vim.o.lines * 0.75)
|
||||
end,
|
||||
max_width = function()
|
||||
return math.floor(vim.o.columns * 0.75)
|
||||
end,
|
||||
on_open = function(win)
|
||||
vim.api.nvim_win_set_config(win, { zindex = 100 })
|
||||
end,
|
||||
},
|
||||
init = function()
|
||||
-- 当 "notify" 可用时,将其替换为 vim.notify
|
||||
vim.notify = function(...)
|
||||
local loaded, notify = pcall(require, "notify")
|
||||
if loaded then
|
||||
vim.notify = notify
|
||||
return notify(...)
|
||||
else
|
||||
return vim.api.nvim_notify(...)
|
||||
end
|
||||
end
|
||||
end,
|
||||
},
|
||||
-- 快捷键提示
|
||||
{
|
||||
"folke/which-key.nvim",
|
||||
"folke/noice.nvim",
|
||||
event = "VeryLazy",
|
||||
dependencies = { "MunifTanjim/nui.nvim" },
|
||||
opts = {
|
||||
plugins = {
|
||||
marks = true, -- 显示标记
|
||||
registers = true, -- 显示寄存器
|
||||
spelling = {
|
||||
enabled = true, -- 启用拼写建议
|
||||
suggestions = 20,
|
||||
lsp = {
|
||||
override = {
|
||||
["vim.lsp.util.convert_input_to_markdown_lines"] = true,
|
||||
["vim.lsp.util.stylize_markdown"] = true,
|
||||
},
|
||||
presets = {
|
||||
operators = true, -- 添加操作符帮助
|
||||
motions = true, -- 添加动作帮助
|
||||
text_objects = true, -- 添加文本对象帮助
|
||||
windows = true, -- 添加窗口帮助 (meta-w)
|
||||
nav = true, -- 添加导航帮助
|
||||
z = true, -- 添加折叠帮助
|
||||
g = true, -- 添加 g 命令帮助
|
||||
hover = { enabled = true },
|
||||
signature = { enabled = false }, -- blink.cmp 处理签名
|
||||
progress = { enabled = true, format_done = "" },
|
||||
},
|
||||
routes = {
|
||||
{
|
||||
-- 屏蔽 treesitter query 字段兼容性警告(Neovim 0.12 校验更严格,功能不受影响)
|
||||
filter = {
|
||||
event = "msg_show",
|
||||
any = {
|
||||
{ find = "Invalid field name" },
|
||||
{ find = "Invalid node type" },
|
||||
{ find = "Query error" },
|
||||
},
|
||||
},
|
||||
opts = { skip = true },
|
||||
},
|
||||
{
|
||||
filter = {
|
||||
event = "msg_show",
|
||||
any = {
|
||||
{ find = "%d+L, %d+B" },
|
||||
{ find = "; after #%d+" },
|
||||
{ find = "; before #%d+" },
|
||||
},
|
||||
},
|
||||
view = "mini",
|
||||
},
|
||||
},
|
||||
icons = {
|
||||
breadcrumb = "»", -- 面包屑分隔符
|
||||
separator = "➜", -- 键映射前缀和命令之间的分隔符
|
||||
group = "+", -- 组图标
|
||||
},
|
||||
window = {
|
||||
border = "rounded", -- 边框样式
|
||||
position = "bottom", -- 位置
|
||||
margin = { 1, 0, 1, 0 }, -- 边距
|
||||
padding = { 1, 1, 1, 1 }, -- 内边距
|
||||
},
|
||||
layout = {
|
||||
height = { min = 3, max = 25 }, -- 最小和最大高度
|
||||
width = { min = 20, max = 50 }, -- 最小和最大宽度
|
||||
spacing = 3, -- 间距
|
||||
align = "center", -- 对齐方式
|
||||
},
|
||||
ignore_missing = false, -- 不忽略缺少的键映射
|
||||
hidden = { "<silent>", "<cmd>", "<Cmd>", "<CR>", "^:", "^ ", "^call ", "^lua " }, -- 隐藏的命令前缀
|
||||
show_help = true, -- 显示帮助信息
|
||||
triggers = "auto", -- 触发自动显示
|
||||
triggers_nowait = { -- 不等待这些前缀的键映射
|
||||
-- 字符表示操作符等待模式
|
||||
"`",
|
||||
"'",
|
||||
"g`",
|
||||
"g'",
|
||||
'"',
|
||||
"<c-r>",
|
||||
"z=",
|
||||
presets = {
|
||||
bottom_search = true,
|
||||
command_palette = true,
|
||||
long_message_to_split = true,
|
||||
inc_rename = true,
|
||||
},
|
||||
},
|
||||
config = function(_, opts)
|
||||
local wk = require("which-key")
|
||||
wk.setup(opts)
|
||||
end,
|
||||
keys = {
|
||||
{ "<leader>snl", function() require("noice").cmd("last") end, desc = "最后消息" },
|
||||
{ "<leader>snh", function() require("noice").cmd("history") end, desc = "Noice 历史" },
|
||||
{ "<leader>sna", function() require("noice").cmd("all") end, desc = "所有消息" },
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
-- 缩进线
|
||||
{
|
||||
"lukas-reineke/indent-blankline.nvim",
|
||||
main = "ibl",
|
||||
event = "BufReadPost",
|
||||
opts = {
|
||||
indent = { char = "│", tab_char = "│" },
|
||||
scope = { enabled = false },
|
||||
exclude = {
|
||||
filetypes = {
|
||||
"help", "snacks_dashboard", "neo-tree", "Trouble", "trouble",
|
||||
"lazy", "mason", "toggleterm",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user