mirror of
https://github.com/kristoferssolo/SoloVim.git
synced 2026-02-04 06:42:05 +00:00
Added LaTeX templates and more
This commit is contained in:
@@ -1,112 +0,0 @@
|
||||
-- Use 'q' to quit from common plugins
|
||||
vim.api.nvim_create_autocmd({ "FileType" }, {
|
||||
pattern = { "qf", "help", "man", "lspinfo", "spectre_panel", "lir", "git" },
|
||||
callback = function()
|
||||
vim.cmd([[ nnoremap <silent> <buffer> q :close<cr>
|
||||
set nobuflisted
|
||||
]])
|
||||
end,
|
||||
})
|
||||
|
||||
-- Set wrap and spell in markdown and gitcommit
|
||||
vim.api.nvim_create_autocmd({ "FileType" }, {
|
||||
pattern = { "gitcommit", "markdown", "vimwiki" },
|
||||
callback = function()
|
||||
vim.opt_local.wrap = true
|
||||
-- vim.opt_local.spell = true
|
||||
end,
|
||||
})
|
||||
|
||||
vim.cmd("autocmd BufEnter * ++nested if winnr('$') == 1 && bufname() == 'NvimTree_' . tabpagenr() | quit | endif")
|
||||
|
||||
-- Fixes Autocomment
|
||||
vim.api.nvim_create_autocmd({ "BufWinEnter" }, {
|
||||
callback = function()
|
||||
vim.cmd("set formatoptions-=cro")
|
||||
end,
|
||||
})
|
||||
|
||||
-- Highlight Yanked Text
|
||||
vim.api.nvim_create_autocmd({ "TextYankPost" }, {
|
||||
callback = function()
|
||||
vim.highlight.on_yank({ higroup = "Visual", timeout = 200 })
|
||||
end,
|
||||
})
|
||||
|
||||
-- Format File on Save
|
||||
vim.api.nvim_create_autocmd({ "BufWritePre" }, {
|
||||
callback = function()
|
||||
vim.lsp.buf.format()
|
||||
end,
|
||||
})
|
||||
|
||||
-- Center on InsertEnter
|
||||
vim.api.nvim_create_autocmd({ "InsertEnter" }, {
|
||||
callback = function()
|
||||
vim.cmd("norm zz")
|
||||
end,
|
||||
})
|
||||
|
||||
-- Disable `expandtab` (don't replace tab with spaces)
|
||||
vim.api.nvim_create_autocmd({ "FileType" }, {
|
||||
pattern = { "lua" },
|
||||
callback = function()
|
||||
vim.opt_local.expandtab = false
|
||||
end,
|
||||
})
|
||||
|
||||
-- Set tab size for the following file types to 2
|
||||
vim.api.nvim_create_autocmd({ "FileType" }, {
|
||||
pattern = { "vimwiki", "sql" },
|
||||
callback = function()
|
||||
vim.opt_local.ts = 2
|
||||
vim.opt_local.sw = 2
|
||||
vim.opt_local.sts = 2
|
||||
end,
|
||||
})
|
||||
|
||||
-- Autocommand that reloads waybar whenever you save the ~/.config/waybar/config file
|
||||
vim.api.nvim_create_autocmd({ "BufWritePost" }, {
|
||||
group = vim.api.nvim_create_augroup("AutoReloadWaybar", { clear = true }),
|
||||
pattern = { "**/waybar/config", "**/waybar/style.css" },
|
||||
callback = function()
|
||||
vim.cmd("!pkill waybar && waybar & disown")
|
||||
end,
|
||||
})
|
||||
|
||||
-- Autocommand that sources neovim files on save
|
||||
vim.api.nvim_create_autocmd({ "BufWritePost" }, {
|
||||
group = vim.api.nvim_create_augroup("AutoReloadConfig", { clear = true }),
|
||||
pattern = { "**/nvim/**/*.lua", "**/SoloVim/**/*.lua" },
|
||||
callback = function()
|
||||
local file_path = vim.api.nvim_buf_get_name(vim.api.nvim_get_current_buf())
|
||||
vim.cmd.source(file_path)
|
||||
end,
|
||||
})
|
||||
|
||||
-- Remove trailing whitespaces on save
|
||||
-- vim.api.nvim_create_autocmd({ "BufWritePre" }, {
|
||||
-- group = vim.api.nvim_create_augroup("RemoveTrailingWhitespaces", { clear = true }),
|
||||
-- pattern = "*",
|
||||
-- callback = function()
|
||||
-- vim.cmd([[%s/\s\+$//e]])
|
||||
-- end,
|
||||
-- })
|
||||
|
||||
-- Run lazy on file save
|
||||
-- vim.api.nvim_create_autocmd({ "BufWritePost" }, {
|
||||
-- group = vim.api.nvim_create_augroup("AutoPackerSync", { clear = true }),
|
||||
-- pattern = { "**/lua/plugins/*" },
|
||||
-- callback = function()
|
||||
-- require("lazy").sync()
|
||||
-- end,
|
||||
-- })
|
||||
|
||||
-- Set vertical column for all files
|
||||
-- vim.api.nvim_create_autocmd({ "FileType" }, {
|
||||
-- group = vim.api.nvim_create_augroup("SetColorColumn", { clear = true }),
|
||||
-- pattern = { "" },
|
||||
-- callback = function()
|
||||
-- vim.cmd.setlocal("colorcolumn=120")
|
||||
-- end,
|
||||
-- })
|
||||
@@ -1,33 +0,0 @@
|
||||
local attach_on_buffer = function(output_bufnr, pattern, command)
|
||||
vim.api.nvim_create_autocmd("BufWritePost", {
|
||||
group = vim.api.nvim_create_augroup("autosave", { clear = true }),
|
||||
pattern = pattern,
|
||||
callback = function()
|
||||
local append_data = function(_, data)
|
||||
if data then
|
||||
vim.api.nvim_buf_set_lines(output_bufnr, -1, -1, false, data)
|
||||
end
|
||||
end
|
||||
|
||||
local file_path = vim.api.nvim_buf_get_name(vim.api.nvim_get_current_buf())
|
||||
vim.api.nvim_buf_set_lines(output_bufnr, 0, -1, false, { file_path })
|
||||
vim.fn.jobstart(command, {
|
||||
stdout_buffered = true,
|
||||
on_stdout = append_data,
|
||||
on_stderr = append_data,
|
||||
})
|
||||
end,
|
||||
})
|
||||
end
|
||||
|
||||
vim.api.nvim_create_user_command("AutoRun", function()
|
||||
print("AutoRun starts now...")
|
||||
local bufnr = vim.api.nvim_get_current_buf()
|
||||
local command = vim.fn.input("Command: ")
|
||||
local pattern = vim.split(vim.fn.input("Pattern: "), " ")
|
||||
attach_on_buffer(tonumber(bufnr), pattern, command)
|
||||
end, {})
|
||||
|
||||
vim.api.nvim_create_user_command("AutoStop", function()
|
||||
vim.api.nvim_create_augroup("autosave", { clear = true })
|
||||
end, {})
|
||||
@@ -1,6 +0,0 @@
|
||||
require("solo.options")
|
||||
require("solo.keymaps")
|
||||
require("solo.vimwiki")
|
||||
require("solo.plugins")
|
||||
require("solo.autocommands")
|
||||
require("solo.autosave")
|
||||
@@ -1,71 +0,0 @@
|
||||
--Remap space as leader key
|
||||
vim.g.mapleader = " "
|
||||
vim.keymap.set("", "<Space>", "<nop>")
|
||||
vim.keymap.set("n", "Q", "<nop>")
|
||||
vim.keymap.set("n", "q", "<nop>")
|
||||
|
||||
-- Modes
|
||||
-- normal_mode = "n",
|
||||
-- insert_mode = "i",
|
||||
-- visual_mode = "v",
|
||||
-- visual_block_mode = "x",
|
||||
-- term_mode = "t",
|
||||
-- command_mode = "c",
|
||||
|
||||
vim.keymap.set("n", "<leader>n", vim.cmd.Ex)
|
||||
|
||||
vim.keymap.set("v", "<A-j>", ":m '>+1<cr>gv=gv")
|
||||
vim.keymap.set("v", "<A-k>", ":m '<-2<cr>gv=gv")
|
||||
|
||||
vim.keymap.set("n", "J", "mzJ`z")
|
||||
vim.keymap.set("n", "<C-d>", "<C-d>zz")
|
||||
vim.keymap.set("n", "<C-u>", "<C-u>zz")
|
||||
vim.keymap.set("n", "n", "nzzzv")
|
||||
vim.keymap.set("n", "N", "Nzzzv")
|
||||
|
||||
vim.keymap.set("n", "<C-h>", "<cmd>TmuxNavigateLeft<cr>")
|
||||
vim.keymap.set("n", "<C-j>", "<cmd>TmuxNavigateDown<cr>")
|
||||
vim.keymap.set("n", "<C-k>", "<cmd>TmuxNavigateUp<cr>")
|
||||
vim.keymap.set("n", "<C-l>", "<cmd>TmuxNavigateRight<cr>")
|
||||
|
||||
vim.keymap.set("n", "<C-Up>", "<cmd>resize -2<cr>")
|
||||
vim.keymap.set("n", "<C-Down>", "<cmd>resize +2<cr>")
|
||||
vim.keymap.set("n", "<C-Left>", "<cmd>vertical resize -2<cr>")
|
||||
vim.keymap.set("n", "<C-Right>", "<cmd>vertical resize +2<cr>")
|
||||
|
||||
vim.keymap.set("x", "<leader>p", '"_dP')
|
||||
|
||||
vim.keymap.set("n", "<C-f>", "<cmd>!tmux neww tmux-sessionizer<cr>")
|
||||
|
||||
vim.keymap.set("n", "<A-j>", "<cmd>m .+1<cr>==")
|
||||
vim.keymap.set("n", "<A-k>", "<cmd>m .-2<cr>==")
|
||||
|
||||
vim.keymap.set("n", "<leader>j", "<cmd>lua vim.diagnostic.goto_next()<cr>zz")
|
||||
vim.keymap.set("n", "<leader>k", "<cmd>lua vim.diagnostic.goto_prev()<cr>zz")
|
||||
vim.keymap.set("n", "<leader>r", function()
|
||||
vim.lsp.buf.rename()
|
||||
end)
|
||||
|
||||
vim.keymap.set("n", "<leader>s", ":%s/\\<<C-r><C-w>\\>/<C-r><C-w>/gI<Left><Left><Left>")
|
||||
vim.keymap.set("n", "<leader>x", "<cmd>!chmod +x %<cr>", { silent = true })
|
||||
|
||||
vim.keymap.set("i", "<A-Up>", "<C-\\><C-N><C-w>k")
|
||||
vim.keymap.set("i", "<A-Down>", "<C-\\><C-N><C-w>j")
|
||||
vim.keymap.set("i", "<A-Left>", "<C-\\><C-N><C-w>h")
|
||||
vim.keymap.set("i", "<A-Right>", "<C-\\><C-N><C-w>l")
|
||||
|
||||
vim.keymap.set("v", ">", ">gv")
|
||||
vim.keymap.set("v", "<", "<gv")
|
||||
|
||||
vim.keymap.set("c", "<C-j>", 'pumvisible() ? "\\<C-n>" : "\\<C-j>"', { expr = true, noremap = true })
|
||||
vim.keymap.set("c", "<C-k>", 'pumvisible() ? "\\<C-p>" : "\\<C-k>"', { expr = true, noremap = true })
|
||||
|
||||
vim.keymap.set("t", "<C-h>", "<C-\\><C-N><C-w>h")
|
||||
vim.keymap.set("t", "<C-j>", "<C-\\><C-N><C-w>j")
|
||||
vim.keymap.set("t", "<C-k>", "<C-\\><C-N><C-w>k")
|
||||
vim.keymap.set("t", "<C-l>", "<C-\\><C-N><C-w>l")
|
||||
|
||||
vim.keymap.set("n", "<C-b>", "<cmd>w!<cr><cmd>!compiler '%:p'<cr>")
|
||||
vim.keymap.set("n", "<C-o>", "<cmd>w!<cr><cmd>!opout '%:p'<cr>")
|
||||
|
||||
vim.keymap.set("n", "<leader>mr", "<cmd>CellularAutomaton make_it_rain<cr>")
|
||||
@@ -1,66 +0,0 @@
|
||||
local options = {
|
||||
backup = false, -- creates a backup file
|
||||
clipboard = "unnamedplus", -- allows neovim to access the system clipboard
|
||||
cmdheight = 1, -- more space in the neovim command line for displaying messages
|
||||
completeopt = { "menuone", "noselect" }, -- mostly just for cmp
|
||||
conceallevel = 0, -- so that `` is visible in markdown files
|
||||
fileencoding = "utf-8", -- the encoding written to a file
|
||||
hlsearch = false, -- highlight all matches on previous search pattern
|
||||
incsearch = true,
|
||||
ignorecase = true, -- ignore case in search patterns
|
||||
mouse = "a", -- allow the mouse to be used in neovim
|
||||
pumheight = 10, -- pop up menu height,
|
||||
showmode = false, -- we don't need to see things like -- INSERT -- anymore
|
||||
showtabline = 0, -- disable tabs
|
||||
smartcase = true, -- smart case
|
||||
smartindent = true, -- make indenting smarter again
|
||||
splitbelow = true, -- force all horizontal splits to go below current window
|
||||
splitright = true, -- force all vertical splits to go to the right of current window
|
||||
swapfile = false, -- creates a swapfile
|
||||
termguicolors = true, -- set term gui colors (most terminals support this)
|
||||
timeoutlen = 250, -- time to wait for a mapped sequence to complete (in milliseconds)
|
||||
undofile = true, -- enable persistent undo
|
||||
undodir = vim.fn.stdpath("data") .. "/nvim/undodir",
|
||||
updatetime = 50, -- faster completion (4000ms default)
|
||||
writebackup = false, -- if a file is being edited by another program (or was written to file while editing with another program), it is not allowed to be edited
|
||||
expandtab = true, -- convert tabs to spaces
|
||||
shiftwidth = 4, -- the number of spaces inserted for each indentation
|
||||
tabstop = 4, -- insert 4 spaces for a tab
|
||||
cursorcolumn = true, -- highlight the current column
|
||||
cursorline = true, -- highlight the current line
|
||||
number = true, -- set numbered lines
|
||||
relativenumber = true, -- set relative numbered lines
|
||||
laststatus = 3,
|
||||
showcmd = false,
|
||||
ruler = false,
|
||||
numberwidth = 4, -- set number column width to 4 {default 4}
|
||||
signcolumn = "yes", -- always show the sign column, otherwise it would shift the text each time
|
||||
wrap = false, -- display lines as one long line
|
||||
scrolloff = 10, -- is one of my fav
|
||||
sidescrolloff = 10,
|
||||
guifont = "JetBrainsMono NF:h11", -- the font used in graphical neovim applications
|
||||
spell = false,
|
||||
foldmethod = "manual",
|
||||
breakindent = true, -- Enable break indent
|
||||
}
|
||||
|
||||
vim.opt.fillchars.eob = " "
|
||||
vim.opt.shortmess:append("c")
|
||||
vim.opt.whichwrap:append("<,>,[,],h,l")
|
||||
vim.opt.iskeyword:append("-")
|
||||
vim.opt.isfname:append("@-@")
|
||||
|
||||
for k, v in pairs(options) do
|
||||
vim.opt[k] = v
|
||||
end
|
||||
vim.opt.spelloptions:append("camel")
|
||||
|
||||
vim.g.mapleader = " "
|
||||
vim.g.maplocalleader = " "
|
||||
vim.g.netrw_browse_split = 0
|
||||
vim.g.netrw_banner = 0
|
||||
vim.g.netrw_winsize = 25
|
||||
|
||||
vim.opt_local.suffixesadd:prepend(".lua")
|
||||
vim.opt_local.suffixesadd:prepend("init.lua")
|
||||
vim.opt_local.path:prepend(vim.fn.stdpath("config") .. "/lua")
|
||||
@@ -1,209 +0,0 @@
|
||||
local lazypath = vim.fn.stdpath("data") .. "/lazy/lazy.nvim"
|
||||
if not vim.loop.fs_stat(lazypath) then
|
||||
vim.fn.system({
|
||||
"git",
|
||||
"clone",
|
||||
"--filter=blob:none",
|
||||
"https://github.com/folke/lazy.nvim.git",
|
||||
"--branch=stable", -- latest stable release
|
||||
lazypath,
|
||||
})
|
||||
end
|
||||
vim.opt.rtp:prepend(lazypath)
|
||||
|
||||
require("lazy").setup({
|
||||
-- core
|
||||
{ "folke/lazy.nvim" },
|
||||
{ "nvim-lua/plenary.nvim" }, -- Useful lua functions used by lots of plugins
|
||||
|
||||
-- useful
|
||||
{ "ThePrimeagen/harpoon" },
|
||||
{ "preservim/tagbar" },
|
||||
{ "alvan/vim-closetag" },
|
||||
{ "mbbill/undotree" },
|
||||
{ "christoomey/vim-tmux-navigator" },
|
||||
{ "folke/todo-comments.nvim", opts = true },
|
||||
{ "ahmedkhalf/project.nvim" },
|
||||
{
|
||||
"chipsenkbeil/distant.nvim",
|
||||
branch = "v0.3",
|
||||
config = function()
|
||||
require("distant"):setup()
|
||||
end,
|
||||
},
|
||||
{ "laytan/cloak.nvim" },
|
||||
"numToStr/Comment.nvim",
|
||||
|
||||
-- lsp
|
||||
{
|
||||
"VonHeikemen/lsp-zero.nvim",
|
||||
branch = "v2.x",
|
||||
dependencies = {
|
||||
-- LSP Support
|
||||
{ "neovim/nvim-lspconfig" },
|
||||
{ "williamboman/mason.nvim" },
|
||||
{ "williamboman/mason-lspconfig.nvim" },
|
||||
|
||||
-- Autocompletion
|
||||
{
|
||||
"hrsh7th/nvim-cmp",
|
||||
event = "InsertEnter",
|
||||
dependencies = {
|
||||
"hrsh7th/cmp-buffer", -- buffer completions
|
||||
"hrsh7th/cmp-path", -- path completionsplu
|
||||
"hrsh7th/cmp-nvim-lua",
|
||||
"hrsh7th/cmp-nvim-lsp",
|
||||
},
|
||||
},
|
||||
{ "hrsh7th/cmp-nvim-lsp" },
|
||||
{
|
||||
"L3MON4D3/LuaSnip",
|
||||
build = "make install_jsregexp",
|
||||
dependencies = {
|
||||
"rafamadriz/friendly-snippets", -- a bunch of snippets to use
|
||||
},
|
||||
},
|
||||
{ "saadparwaiz1/cmp_luasnip" },
|
||||
},
|
||||
},
|
||||
{ "nanotee/sqls.nvim", lazy = true },
|
||||
{ "p00f/clangd_extensions.nvim", ft = { "cpp", "c" } },
|
||||
{ "Civitasv/cmake-tools.nvim", ft = { "cpp", "c" }, dependencies = { "nvim-lua/plenary.nvim" } },
|
||||
{
|
||||
"simrat39/rust-tools.nvim",
|
||||
dependencies = {
|
||||
"neovim/nvim-lspconfig",
|
||||
},
|
||||
ft = "rust",
|
||||
},
|
||||
{
|
||||
|
||||
"Saecki/crates.nvim",
|
||||
ft = { "rust", "toml" },
|
||||
dependencies = {
|
||||
"nvim-lua/plenary.nvim",
|
||||
},
|
||||
},
|
||||
{
|
||||
"windwp/nvim-autopairs", -- Autopairs, integrates with both cmp and treesitter
|
||||
event = "InsertEnter",
|
||||
opts = {
|
||||
check_ts = true, -- treesitter integration
|
||||
disable_filetype = {
|
||||
"NvimTree",
|
||||
"TelescopePrompt",
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
-- formatters and linters
|
||||
{
|
||||
"jay-babu/mason-null-ls.nvim",
|
||||
event = { "BufReadPre", "BufNewFile" },
|
||||
dependencies = {
|
||||
"williamboman/mason.nvim",
|
||||
"jose-elias-alvarez/null-ls.nvim",
|
||||
},
|
||||
},
|
||||
|
||||
-- debuggers
|
||||
{
|
||||
"rcarriga/nvim-dap-ui",
|
||||
dependencies = { "jayp0521/mason-nvim-dap.nvim" },
|
||||
},
|
||||
{
|
||||
"jayp0521/mason-nvim-dap.nvim",
|
||||
dependencies = {
|
||||
{
|
||||
"williamboman/mason.nvim",
|
||||
opts = {
|
||||
automatic_installation = true,
|
||||
automatic_setup = true,
|
||||
},
|
||||
},
|
||||
"mfussenegger/nvim-dap",
|
||||
},
|
||||
},
|
||||
{ "ravenxrz/DAPInstall.nvim", lazy = true },
|
||||
|
||||
-- treesitter
|
||||
{
|
||||
"nvim-treesitter/nvim-treesitter",
|
||||
build = ":TSUpdate",
|
||||
dependencies = {
|
||||
"nvim-treesitter/playground",
|
||||
"p00f/nvim-ts-rainbow",
|
||||
"mechatroner/rainbow_csv",
|
||||
"JoosepAlviste/nvim-ts-context-commentstring",
|
||||
},
|
||||
},
|
||||
|
||||
-- telescope
|
||||
{
|
||||
"nvim-telescope/telescope.nvim",
|
||||
dependencies = {
|
||||
{ "nvim-lua/plenary.nvim" },
|
||||
{ "nvim-telescope/telescope-fzf-native.nvim", build = "make" },
|
||||
{ "nvim-telescope/telescope-media-files.nvim" },
|
||||
{ "xiyaowong/telescope-emoji.nvim" },
|
||||
{ "nvim-telescope/telescope-frecency.nvim" },
|
||||
{ "nvim-telescope/telescope-ui-select.nvim" },
|
||||
{ "nvim-telescope/telescope-fzf-native.nvim", build = "make" },
|
||||
{ "nat-418/telescope-color-names.nvim" },
|
||||
},
|
||||
},
|
||||
|
||||
-- pretty
|
||||
{ "uga-rosa/ccc.nvim" },
|
||||
{ "lukas-reineke/indent-blankline.nvim" },
|
||||
{ "NvChad/nvim-colorizer.lua" },
|
||||
{ "mtdl9/vim-log-highlighting", lazy = true },
|
||||
{ "RRethy/vim-illuminate" },
|
||||
{
|
||||
"danymat/neogen",
|
||||
dependencies = { "nvim-treesitter/nvim-treesitter" },
|
||||
config = true,
|
||||
version = "*",
|
||||
},
|
||||
{ "nvim-lualine/lualine.nvim" },
|
||||
{
|
||||
"folke/tokyonight.nvim",
|
||||
lazy = false,
|
||||
priority = 1000,
|
||||
config = function()
|
||||
require("tokyonight").setup({
|
||||
style = "night",
|
||||
transparent = true,
|
||||
styles = {
|
||||
keywords = { italic = false },
|
||||
sidebars = "transparent",
|
||||
floats = "transparent",
|
||||
},
|
||||
lualine_bold = true,
|
||||
})
|
||||
vim.cmd.colorscheme("tokyonight")
|
||||
end,
|
||||
},
|
||||
|
||||
-- git
|
||||
{ "kdheepak/lazygit.nvim" },
|
||||
{ "tpope/vim-fugitive" },
|
||||
{ "lewis6991/gitsigns.nvim" },
|
||||
{ "ThePrimeagen/git-worktree.nvim" },
|
||||
|
||||
-- text
|
||||
{ "vimwiki/vimwiki" },
|
||||
{
|
||||
"epwalsh/obsidian.nvim",
|
||||
lazy = true,
|
||||
event = {
|
||||
"BufReadPre " .. vim.fn.expand("~") .. "/vimwiki/**/*.md",
|
||||
"BufReadPre " .. vim.fn.expand("~") .. "/obsidian/**/*.md",
|
||||
},
|
||||
},
|
||||
|
||||
-- fun
|
||||
{ "andweeb/presence.nvim" },
|
||||
{ "ThePrimeagen/vim-be-good", lazy = true },
|
||||
{ "eandrju/cellular-automaton.nvim" },
|
||||
})
|
||||
@@ -1,13 +0,0 @@
|
||||
vim.g.vimwiki_list = {
|
||||
{
|
||||
path = "~/vimwiki",
|
||||
syntax = "markdown",
|
||||
ext = ".md",
|
||||
},
|
||||
}
|
||||
vim.g.vimwiki_ext2syntax = {
|
||||
[".md"] = "markdown",
|
||||
[".markdown"] = "markdown",
|
||||
[".mdown"] = "markdown",
|
||||
}
|
||||
vim.g.vimwiki_global_ext = 1
|
||||
Reference in New Issue
Block a user