diff options
44 files changed, 1948 insertions, 0 deletions
diff --git a/.bash_login b/.bash_login new file mode 100644 index 0000000..3309ee9 --- /dev/null +++ b/.bash_login @@ -0,0 +1,7 @@ +# ~/.bash_login +# +# Executed by login shells when starting + +[[ -r ~/.bashrc ]] && source "$HOME/.bashrc" + +# vim: filetype=bash diff --git a/.bash_logout b/.bash_logout new file mode 100644 index 0000000..72d3ada --- /dev/null +++ b/.bash_logout @@ -0,0 +1,6 @@ +# ~/.bash_logout +# +# Executed by login shells when exiting +# Executed by non-interactive login shells when calling the `exit` builtin + +# vim: filetype=bash @@ -0,0 +1,7 @@ +# ~/.bashrc +# +# Executed by non-login interactive shells when starting + +[ -f "$HOME/.config/bash/bashrc" ] && source "$HOME/.config/bash/bashrc" + +# vim: set filetype=bash diff --git a/.config/bash/bashrc b/.config/bash/bashrc new file mode 100644 index 0000000..bd440fc --- /dev/null +++ b/.config/bash/bashrc @@ -0,0 +1,62 @@ +# bashrc +# +# Sourced by `~/.bashrc` (which is executed by non-login interactive shells) + +[[ $- != *i* ]] && return # if not running interactively, stop + +function source_if_readable() { + local f="${1:?file argument missing}" + # shellcheck disable=1090 + if [[ -r $f ]]; then source "$f"; fi +} + +# The terminal consumes certain characters before the shell see them. This +# disables them so Readline can bind them. +# +# `stty` settings live on the terminal device, not in the shell process. +# +# -ixon Ctrl+S and Ctrl+Q (output flow control) +# werase undef Ctrl+W (erase word) +stty -ixon werase undef + +# ** expands to all files and directories recursively +# **/ expands to all directories recursively +shopt -s globstar +shopt -s extglob # extended pattern matching (like `?(a|b|c)`) + +alias r="fc -s" # See `help fc` +alias wl-copy="wl-copy --trim-newline" + +export INPUTRC="$HOME/.config/bash/inputrc" # Readline configuration +export VISUAL=nvim EDITOR=nvim # used by various programs like `git`, `fc`, `man`, … +export MANPAGER="nvim +Man!" MANWIDTH=999 # `nvim` as pager for `man` + +# Colors and prompt +# The terminal emulator sets $TERM +if [[ $TERM == +(xterm-256color|tmux-256color|foot) ]]; then + eval "$(dircolors --bourne-shell)" # Set LS_COLORS which is used by other programs + alias ls='ls --color=auto' + alias grep='grep --color=auto' + alias yay='yay --color auto' + alias pacman='pacman --color auto' + alias bat='bat --color=always' + export LESS="--RAW-CONTROL-CHARS" # `less` display colors instead of the raw escape sequences + # Prompt + PS1='\[\033[01;32m\][\u@\h\[\033[01;37m\] \W :$SHLVL :\j\[\033[01;32m\]]\$\[\033[00m\] ' +fi + +# History +shopt -s histappend # append to instead of overwriting HISTFILE +export HISTFILE="$HOME/.config/bash/bash_history" # history file +export HISTSIZE=1000 # number of commands to save in history +export HISTTIMEFORMAT='%F %T ' # history entry timesptamp format, also enables multi-line entries +export HISTFILESIZE=-1 # don't truncate history +export HISTCONTROL="ignoreboth" # don't save duplicate lines or lines starting with a space + +# Completion +source_if_readable /usr/share/bash-completion/bash_completion # see https://github.com/scop/bash-completion/#bash-completion +source_if_readable "$HOME/.config/fzf/fzf.bash" # fzf configuration + +source_if_readable "$HOME/.config/bash/localrc" # local configuration + +# vim: ft=bash diff --git a/.config/bash/inputrc b/.config/bash/inputrc new file mode 100644 index 0000000..c56068e --- /dev/null +++ b/.config/bash/inputrc @@ -0,0 +1,88 @@ +# ------------------------------------------------------------------------------ +# Readline configuration file +# ------------------------------------------------------------------------------ +# +# https://www.gnu.org/software/bash/manual/bash.html#Readline-Init-File +# +# Executed by Readline when a program starts the Readline library +# +# Options that take a string parameter cannot have a comment next to them, e.g. +# `set bell-style none # Some comment` will not work. + +# ------------------------------------------------------------------------------ +# Variable settings +# ------------------------------------------------------------------------------ + +# Completion configuration +# +# It's worth noting what's going on here since it's not clear from the +# documentation. +# +# There are three cases to cover for the behavior I want: +# - Only one match → complete +# - Multiple matches → show the list +# - Multiple matches with common prefix → complete prefix and show the list +# +# By default, `menu-complete()` will select the first completion, this covers +# the first case. `show-all-if-ambiguous` covers the second case by showing the +# list when there are multiple choices. `menu-complete-display-prefix` covers +# the third case by completing the common prefix. +set show-all-if-ambiguous on +set menu-complete-display-prefix on + +set editing-mode vi # Vi keybindings +set match-hidden-files off # Don't match hidden files, unless a leading '.' is typed explicitely +set completion-ignore-case on # Ignore case when mathching completions +set search-ignore-case on # Ignore case when searching +set skip-completed-text on # Don't insert match when when completing in the middle of a word +set completion-query-items 100 # Prompt before showing more than this number of completions +set completion-display-width 100 # 100 columns for completion results +set print-completions-horizontally off # Sort completions horizontally +set colored-stats on # Show completions using colors depending on file type +set visible-stats on # Append a character denoting file type +set colored-completion-prefix on # Color the common prefix of completions +set expand-tilde off # Don't expand tilde automatically +set show-mode-in-prompt on # Show current mode (vi-insert, vi-command) in prompt +# String to display for each mode +set vi-cmd-mode-string -CMD- +set vi-ins-mode-string -INS- +# Don't ring the terminal bell +set bell-style none + +# ------------------------------------------------------------------------------ +# Key bindings +# ------------------------------------------------------------------------------ +# +# To unbind: bind to `""` +# +# https://www.gnu.org/software/bash/manual/bash.html#Bindable-Readline-Commands-1 + +$if mode=vi + +# vi-insert ____________________________________________________________________ +set keymap vi-insert +$include ~/.config/bash/vi.inputrc + +"\e\t": shell-expand-line # Alt+Tab perform shell expansion +"\C-i": menu-complete # Tab cycle completions forward +"\e[Z": menu-complete-backward # Shift+Tab cycle completions backward +"\C-a": insert-completions # Ctrl+a insert all completions +"\e!": dynamic-complete-history # Alt+! complete from history entries (not lines!) +"\eh": backward-char # Alt+h one char backward +"\el": forward-char # Alt+l one char forward +"\C-b": beginning-of-line # Ctrl+b beginning of line +"\C-e": end-of-line # Ctrl+e end of line + +$if Bash + +"\ee": "ranger\r" # Alt+e open `ranger` + +$endif + +# vi-command ___________________________________________________________________ +set keymap vi-command +$include ~/.config/bash/vi.inputrc + +"u": vi-undo # u undo + +$endif diff --git a/.config/bash/vi.inputrc b/.config/bash/vi.inputrc new file mode 100644 index 0000000..4ec6049 --- /dev/null +++ b/.config/bash/vi.inputrc @@ -0,0 +1,8 @@ +# Readline key bindings for both the vi-insert and vi-command keymaps + +"\C-l": clear-screen # Ctrl+l clear screen +"\C-w": unix-filename-rubout # Ctrl+w erase word backward (hyphens included) +"\C-k": history-search-backward # Ctrl+k search history backward (non-incremental) +"\C-j": history-search-forward # Ctrl+j search history forward (non-incremental) + +# vim: filetype=readline diff --git a/.config/foot/foot.ini b/.config/foot/foot.ini new file mode 100644 index 0000000..66c5f44 --- /dev/null +++ b/.config/foot/foot.ini @@ -0,0 +1,37 @@ +# foot configuration file +# +# See `/etc/xdg/foot/foot.ini` and `man 5 foot.ini` +# Use `foot --check-config` to verify the configuration + +shell=/usr/bin/tmux +term=foot +login-shell=no +app-id=foot +title=foot +locked-title=no +; https://github.com/source-foundry/Hack +font=Hack Nerd Font Mono:style=Regular:size=9 +initial-color-theme=dark +initial-window-size-chars=130x40 +initial-window-mode=windowed + +[scrollback] +lines=1000 + +[key-bindings] +fullscreen=F11 +color-theme-toggle=F10 + +[colors-dark] +background=242424 +foreground=e6e6e6 +regular0=242424 +regular7=e6e6e6 + +[colors-light] +background=e6e6e6 +foreground=242424 +regular0=e6e6e6 +regular7=242424 + +# vim: ft=dosini diff --git a/.config/fzf/fzf.bash b/.config/fzf/fzf.bash new file mode 100644 index 0000000..f567a6d --- /dev/null +++ b/.config/fzf/fzf.bash @@ -0,0 +1,69 @@ +# —————————————————————————————————————————————————————————————————————————————— +# fzf configuration +# +# See https://github.com/junegunn/fzf +# —————————————————————————————————————————————————————————————————————————————— + +fzf_default_usage='Alt+Y copy to clipboard +Alt+/ move/hide preview' +fzf_ctrl_t_usage='Ctrl+Y select +Alt+Y copy to clipboard +Alt+/ move/hide preview' +fzf_ctrl_r_usage='Alt+R show surrounding entries +Ctrl+R sort by relevance/recency +Alt+Y copy to clipboard' +fzf_alt_c_usage='Alt+Y copy to clipboard +Alt+/ move/hide preview' + +# This is used by the Ctrl+T, Ctrl+R, and Alt+C bindings, unless redefined +export FZF_DEFAULT_OPTS=" +--walker file,dir,follow,hidden +--walker-skip .git,node_modules + +--bind tab:down,btab:up +--bind 'alt-y:execute-silent(wl-copy {..})+abort' +--bind 'alt-/:change-preview-window(down|hidden|)' + +--layout reverse --height 50% +--color header:italic +--footer '$fzf_default_usage' +" + +# —————————————————————————————————————————————————————————————————————————————— +# Ctrl+T, Ctrl+R, Alt+C function options +# +# See https://github.com/junegunn/fzf#key-bindings-for-command-line +# —————————————————————————————————————————————————————————————————————————————— + +# (Ctrl+T) Paste the selected files and directories onto the command-line +export FZF_CTRL_T_OPTS=" +--walker file,follow,hidden +--preview 'bat --number --color=always {}' +--bind ctrl-y:toggle +--bind 'alt-/:change-preview-window(down|hidden|)' +--footer '$fzf_ctrl_t_usage' +" + +# (Ctrl+R) Paste the selected command from history onto the command-line +export FZF_CTRL_R_OPTS=" +--bind 'alt-y:execute-silent(wl-copy {2..})+abort' +--footer '$fzf_ctrl_r_usage' +" + +# (Alt+C) `cd` into the selected directory +export FZF_ALT_C_OPTS=" +--walker dir,follow,hidden +--preview 'tree -C {}' +--bind 'alt-/:change-preview-window(down|hidden|)' +--footer '$fzf_alt_c_usage' +" + +# —————————————————————————————————————————————————————————————————————————————— +# ** fuzzy completion +# +# See https://github.com/junegunn/fzf#customizing-fuzzy-completion-for-bash-and-zsh +# —————————————————————————————————————————————————————————————————————————————— + +# —————————————————————————————————————————————————————————————————————————————— + +eval "$(fzf --bash)" diff --git a/.config/kglobalshortcutsrc b/.config/kglobalshortcutsrc new file mode 100644 index 0000000..1be6366 --- /dev/null +++ b/.config/kglobalshortcutsrc @@ -0,0 +1,293 @@ +[ActivityManager] +_k_friendly_name=Activity Manager +switch-to-activity-4ec92300-2853-4661-b146-41fc38818210=none,none,Switch to activity "Default" + +[KDE Keyboard Layout Switcher] +Switch to Last-Used Keyboard Layout=Meta+Alt+L,Meta+Alt+L,Switch to Last-Used Keyboard Layout +Switch to Next Keyboard Layout=Meta+Alt+K,Meta+Alt+K,Switch to Next Keyboard Layout +_k_friendly_name=Keyboard Layout Switcher + +[kaccess] +Toggle Screen Reader On and Off=Meta+Alt+S,Meta+Alt+S,Toggle Screen Reader On and Off +_k_friendly_name=Accessibility + +[kmix] +_k_friendly_name=Audio Volume +decrease_microphone_volume=Microphone Volume Down,Microphone Volume Down,Decrease Microphone Volume +decrease_volume=Volume Down,Volume Down,Decrease Volume +decrease_volume_small=Shift+Volume Down,Shift+Volume Down,Decrease Volume by 1% +increase_microphone_volume=Microphone Volume Up,Microphone Volume Up,Increase Microphone Volume +increase_volume=Volume Up,Volume Up,Increase Volume +increase_volume_small=Shift+Volume Up,Shift+Volume Up,Increase Volume by 1% +mic_mute=Microphone Mute\tMeta+Volume Mute,Microphone Mute\tMeta+Volume Mute,Mute Microphone +mute=Volume Mute,Volume Mute,Mute + +[ksmserver] +Halt Without Confirmation=none,,Shut Down Without Confirmation +Lock Session=Meta+Shift+L,Meta+L\tScreensaver,Lock Session +Log Out=Ctrl+Alt+Del,Ctrl+Alt+Del,Show Logout Screen +Log Out Without Confirmation=none,,Log Out Without Confirmation +LogOut=none,,Log Out +Reboot=none,,Reboot +Reboot Without Confirmation=none,,Reboot Without Confirmation +Shut Down=none,,Shut Down +_k_friendly_name=Session Management + +[kwin] +Activate Window Demanding Attention=Meta+Ctrl+A,Meta+Ctrl+A,Activate Window Demanding Attention +Cycle Overview=none,none,Cycle through Overview and Grid View +Cycle Overview Opposite=none,none,Cycle through Grid View and Overview +Decrease Opacity=none,,Decrease Opacity of Active Window by 5% +Edit Tiles=none,Meta+T,Toggle Tiles Editor +Expose=Ctrl+F9,Meta+F9\tCtrl+F9,Toggle Present Windows (Current desktop) +ExposeAll=Ctrl+F10\tLaunch (C),Meta+F10\tLaunch (C)\tCtrl+F10,Toggle Present Windows (All desktops) +ExposeClass=Ctrl+F7,Meta+F7\tCtrl+F7,Toggle Present Windows (Window class) +ExposeClassCurrentDesktop=none,none,Toggle Present Windows (Window class on current desktop) +Grid View=Meta+G,Meta+G,Toggle Grid View +Increase Opacity=none,,Increase Opacity of Active Window by 5% +Kill Window=Meta+Q\tMeta+Ctrl+Esc,Meta+Ctrl+Esc,Kill Window +Move Tablet to Next LogicalOutput=none,none,Move the tablet to the next output +Move Tablet to Next Output=none,none,Move the tablet to the next output +MoveMouseToCenter=Meta+F6,Meta+F6,Move Mouse to Center +MoveMouseToFocus=Meta+F5,Meta+F5,Move Mouse to Focus +MoveZoomDown=none,none,Move Zoomed Area Downwards +MoveZoomLeft=none,none,Move Zoomed Area to Left +MoveZoomRight=none,none,Move Zoomed Area to Right +MoveZoomUp=none,none,Move Zoomed Area Upwards +Overview=Meta+W,Meta+W,Toggle Overview +Setup Window Shortcut=none,,Setup Window Shortcut +Show Desktop=Meta+D,Meta+D,Peek at Desktop +Switch One Desktop Down=Meta+Ctrl+Down,Meta+Ctrl+Down,Switch One Desktop Down +Switch One Desktop Up=Meta+Ctrl+Up,Meta+Ctrl+Up,Switch One Desktop Up +Switch One Desktop to the Left=Meta+Ctrl+Left,Meta+Ctrl+Left,Switch One Desktop to the Left +Switch One Desktop to the Right=Meta+Ctrl+Right,Meta+Ctrl+Right,Switch One Desktop to the Right +Switch Window Down=Meta+Alt+Down,Meta+Alt+Down,Switch to Window Below +Switch Window Left=Meta+Alt+Left,Meta+Alt+Left,Switch to Window to the Left +Switch Window Right=Meta+Alt+Right,Meta+Alt+Right,Switch to Window to the Right +Switch Window Up=Meta+Alt+Up,Meta+Alt+Up,Switch to Window Above +Switch to Desktop 1=Ctrl+F1,Meta+F1\tCtrl+F1,Switch to Desktop 1 +Switch to Desktop 10=none,,Switch to Desktop 10 +Switch to Desktop 11=none,,Switch to Desktop 11 +Switch to Desktop 12=none,,Switch to Desktop 12 +Switch to Desktop 13=none,,Switch to Desktop 13 +Switch to Desktop 14=none,,Switch to Desktop 14 +Switch to Desktop 15=none,,Switch to Desktop 15 +Switch to Desktop 16=none,,Switch to Desktop 16 +Switch to Desktop 17=none,,Switch to Desktop 17 +Switch to Desktop 18=none,,Switch to Desktop 18 +Switch to Desktop 19=none,,Switch to Desktop 19 +Switch to Desktop 2=Ctrl+F2,Meta+F2\tCtrl+F2,Switch to Desktop 2 +Switch to Desktop 20=none,,Switch to Desktop 20 +Switch to Desktop 21=,,Switch to Desktop 21 +Switch to Desktop 22=,,Switch to Desktop 22 +Switch to Desktop 23=,,Switch to Desktop 23 +Switch to Desktop 24=,,Switch to Desktop 24 +Switch to Desktop 25=,,Switch to Desktop 25 +Switch to Desktop 3=Ctrl+F3,Meta+F3\tCtrl+F3,Switch to Desktop 3 +Switch to Desktop 4=Ctrl+F4,Meta+F4\tCtrl+F4,Switch to Desktop 4 +Switch to Desktop 5=none,,Switch to Desktop 5 +Switch to Desktop 6=none,,Switch to Desktop 6 +Switch to Desktop 7=none,,Switch to Desktop 7 +Switch to Desktop 8=none,,Switch to Desktop 8 +Switch to Desktop 9=none,,Switch to Desktop 9 +Switch to Next Desktop=none,,Switch to Next Desktop +Switch to Next Screen=none,,Switch to Next Screen +Switch to Previous Desktop=none,,Switch to Previous Desktop +Switch to Previous Screen=none,,Switch to Previous Screen +Switch to Screen 0=none,,Switch to Screen 0 +Switch to Screen 1=none,,Switch to Screen 1 +Switch to Screen 2=none,,Switch to Screen 2 +Switch to Screen 3=none,,Switch to Screen 3 +Switch to Screen 4=none,,Switch to Screen 4 +Switch to Screen 5=none,,Switch to Screen 5 +Switch to Screen 6=none,,Switch to Screen 6 +Switch to Screen 7=none,,Switch to Screen 7 +Switch to Screen Above=none,,Switch to Screen Above +Switch to Screen Below=none,,Switch to Screen Below +Switch to Screen to the Left=none,,Switch to Screen to the Left +Switch to Screen to the Right=none,,Switch to Screen to the Right +Toggle Night Color=none,none,Suspend/Resume Night Light +Toggle Window Raise/Lower=none,,Toggle Window Raise/Lower +Walk Through Windows=Meta+Tab,Meta+Tab\tAlt+Tab,Walk Through Windows +Walk Through Windows (Reverse)=Meta+Shift+Tab,Meta+Shift+Tab\tAlt+Shift+Tab,Walk Through Windows (Reverse) +Walk Through Windows Alternative=none,none,Walk Through Windows Alternative +Walk Through Windows Alternative (Reverse)=none,none,Walk Through Windows Alternative (Reverse) +Walk Through Windows of Current Application=Meta+`,Meta+`\tAlt+`,Walk Through Windows of Current Application +Walk Through Windows of Current Application (Reverse)=Meta+~,Meta+~\tAlt+~,Walk Through Windows of Current Application (Reverse) +Walk Through Windows of Current Application Alternative=none,none,Walk Through Windows of Current Application Alternative +Walk Through Windows of Current Application Alternative (Reverse)=none,none,Walk Through Windows of Current Application Alternative (Reverse) +Window Above Other Windows=Meta+O,,Keep Window Above Others +Window Below Other Windows=none,,Keep Window Below Others +Window Close=Meta+C,Alt+F4,Close Window +Window Custom Quick Tile Bottom=none,,Custom Quick Tile Window to the Bottom +Window Custom Quick Tile Left=none,,Custom Quick Tile Window to the Left +Window Custom Quick Tile Right=none,,Custom Quick Tile Window to the Right +Window Custom Quick Tile Top=none,,Custom Quick Tile Window to the Top +Window Fullscreen=none,,Make Window Fullscreen +Window Grow Horizontal=none,,Expand Window Horizontally +Window Grow Vertical=none,,Expand Window Vertically +Window Lower=none,,Lower Window +Window Maximize=Meta+K\tMeta+PgUp,Meta+PgUp,Maximize Window +Window Maximize Horizontal=none,,Maximize Window Horizontally +Window Maximize Vertical=none,,Maximize Window Vertically +Window Minimize=Meta+J\tMeta+PgDown,Meta+PgDown,Minimize Window +Window Move=none,,Move Window +Window Move Center=none,,Move Window to the Center +Window No Border=none,,Toggle Window Titlebar and Frame +Window On All Desktops=none,,Keep Window on All Desktops +Window One Desktop Down=Meta+Ctrl+Shift+Down,Meta+Ctrl+Shift+Down,Window One Desktop Down +Window One Desktop Up=Meta+Ctrl+Shift+Up,Meta+Ctrl+Shift+Up,Window One Desktop Up +Window One Desktop to the Left=Meta+Ctrl+Shift+Left,Meta+Ctrl+Shift+Left,Window One Desktop to the Left +Window One Desktop to the Right=Meta+Ctrl+Shift+Right,Meta+Ctrl+Shift+Right,Window One Desktop to the Right +Window One Screen Down=none,,Move Window One Screen Down +Window One Screen Up=none,,Move Window One Screen Up +Window One Screen to the Left=none,,Move Window One Screen to the Left +Window One Screen to the Right=none,,Move Window One Screen to the Right +Window Operations Menu=Alt+F3,Alt+F3,Window Menu +Window Pack Down=none,,Move Window Down +Window Pack Left=none,,Move Window Left +Window Pack Right=none,,Move Window Right +Window Pack Up=none,,Move Window Up +Window Quick Tile Bottom=Meta+Down,Meta+Down,Quick Tile Window to the Bottom +Window Quick Tile Bottom Left=none,,Quick Tile Window to the Bottom Left +Window Quick Tile Bottom Right=none,,Quick Tile Window to the Bottom Right +Window Quick Tile Left=Meta+Left\tMeta+H,Meta+Left,Quick Tile Window to the Left +Window Quick Tile Right=Meta+L\tMeta+Right,Meta+Right,Quick Tile Window to the Right +Window Quick Tile Top=Meta+Up,Meta+Up,Quick Tile Window to the Top +Window Quick Tile Top Left=none,,Quick Tile Window to the Top Left +Window Quick Tile Top Right=none,,Quick Tile Window to the Top Right +Window Raise=none,,Raise Window +Window Resize=none,,Resize Window +Window Shrink Horizontal=none,,Shrink Window Horizontally +Window Shrink Vertical=none,,Shrink Window Vertically +Window to Desktop 1=none,,Window to Desktop 1 +Window to Desktop 10=none,,Window to Desktop 10 +Window to Desktop 11=none,,Window to Desktop 11 +Window to Desktop 12=none,,Window to Desktop 12 +Window to Desktop 13=none,,Window to Desktop 13 +Window to Desktop 14=none,,Window to Desktop 14 +Window to Desktop 15=none,,Window to Desktop 15 +Window to Desktop 16=none,,Window to Desktop 16 +Window to Desktop 17=none,,Window to Desktop 17 +Window to Desktop 18=none,,Window to Desktop 18 +Window to Desktop 19=none,,Window to Desktop 19 +Window to Desktop 2=none,,Window to Desktop 2 +Window to Desktop 20=none,,Window to Desktop 20 +Window to Desktop 21=,,Window to Desktop 21 +Window to Desktop 22=,,Window to Desktop 22 +Window to Desktop 23=,,Window to Desktop 23 +Window to Desktop 24=,,Window to Desktop 24 +Window to Desktop 25=,,Window to Desktop 25 +Window to Desktop 3=none,,Window to Desktop 3 +Window to Desktop 4=none,,Window to Desktop 4 +Window to Desktop 5=none,,Window to Desktop 5 +Window to Desktop 6=none,,Window to Desktop 6 +Window to Desktop 7=none,,Window to Desktop 7 +Window to Desktop 8=none,,Window to Desktop 8 +Window to Desktop 9=none,,Window to Desktop 9 +Window to Next Desktop=none,,Window to Next Desktop +Window to Next Screen=Meta+Shift+Right,Meta+Shift+Right,Move Window to Next Screen +Window to Previous Desktop=none,,Window to Previous Desktop +Window to Previous Screen=Meta+Shift+Left,Meta+Shift+Left,Move Window to Previous Screen +Window to Screen 0=none,,Move Window to Screen 0 +Window to Screen 1=none,,Move Window to Screen 1 +Window to Screen 2=none,,Move Window to Screen 2 +Window to Screen 3=none,,Move Window to Screen 3 +Window to Screen 4=none,,Move Window to Screen 4 +Window to Screen 5=none,,Move Window to Screen 5 +Window to Screen 6=none,,Move Window to Screen 6 +Window to Screen 7=none,,Move Window to Screen 7 +_k_friendly_name=KWin +disableInputCapture=Meta+Shift+Esc,Meta+Shift+Esc,Disable Active Input Capture +view_actual_size=Meta+0,Meta+0,Zoom to Actual Size +view_zoom_in=Meta++\tMeta+=,Meta++\tMeta+=,Zoom In +view_zoom_out=Meta+-,Meta+-,Zoom Out + +[mediacontrol] +_k_friendly_name=Media Controller +mediavolumedown=none,,Media volume down +mediavolumeup=none,,Media volume up +nextmedia=Media Next,Media Next,Media playback next +pausemedia=Media Pause,Media Pause,Pause media playback +playmedia=none,,Play media playback +playpausemedia=Media Play,Media Play,Play/Pause media playback +previousmedia=Media Previous,Media Previous,Media playback previous +seekbackwardmedia=Media Rewind,Media Rewind,Media playback seek backward 5s +seekbackwardmedialong=,,Media playback seek backward 30s +seekforwardmedia=Media Fast Forward,Media Fast Forward,Media playback seek forward 5s +seekforwardmedialong=,,Media playback seek forward 30s +stopmedia=Media Stop,Media Stop,Stop media playback + +[org_kde_powerdevil] +Decrease Keyboard Brightness=Keyboard Brightness Down,Keyboard Brightness Down,Decrease Keyboard Brightness +Decrease Screen Brightness=Monitor Brightness Down,Monitor Brightness Down,Decrease Screen Brightness +Decrease Screen Brightness Small=Shift+Monitor Brightness Down,Shift+Monitor Brightness Down,Decrease Screen Brightness by 1% +Hibernate=Hibernate,Hibernate,Hibernate +Increase Keyboard Brightness=Keyboard Brightness Up,Keyboard Brightness Up,Increase Keyboard Brightness +Increase Screen Brightness=Monitor Brightness Up,Monitor Brightness Up,Increase Screen Brightness +Increase Screen Brightness Small=Shift+Monitor Brightness Up,Shift+Monitor Brightness Up,Increase Screen Brightness by 1% +PowerDown=Power Down,Power Down,Power Down +PowerOff=Power Off,Power Off,Power Off +Sleep=Sleep,Sleep,Suspend +Toggle Keyboard Backlight=Keyboard Light On/Off,Keyboard Light On/Off,Toggle Keyboard Backlight +Turn Off Screen=none,none,Turn Off Screen +_k_friendly_name=Power Management +powerProfile=none,Battery\tMeta+B,Switch Power Profile + +[plasmashell] +Slideshow Wallpaper Next Image=none,,Next Wallpaper Image +_k_friendly_name=plasmashell +activate application launcher=none,Meta\tAlt+F1,Activate Application Launcher +activate task manager entry 1=Meta+1,Meta+1,Activate Task Manager Entry 1 +activate task manager entry 10=none,,Activate Task Manager Entry 10 +activate task manager entry 2=Meta+2,Meta+2,Activate Task Manager Entry 2 +activate task manager entry 3=Meta+3,Meta+3,Activate Task Manager Entry 3 +activate task manager entry 4=Meta+4,Meta+4,Activate Task Manager Entry 4 +activate task manager entry 5=Meta+5,Meta+5,Activate Task Manager Entry 5 +activate task manager entry 6=Meta+6,Meta+6,Activate Task Manager Entry 6 +activate task manager entry 7=Meta+7,Meta+7,Activate Task Manager Entry 7 +activate task manager entry 8=Meta+8,Meta+8,Activate Task Manager Entry 8 +activate task manager entry 9=Meta+9,Meta+9,Activate Task Manager Entry 9 +activate widget 25=none,none,Activate Icons-and-Text Task Manager Widget +activate widget 26=none,none,Activate Application Menu Widget +activate widget 27=Alt+Space,none,Activate Application Menu Widget +clear-history=none,,Clear Clipboard History +clipboard_action=Meta+Ctrl+X,Meta+Ctrl+X,Automatic Action Popup Menu +cycle-panels=Meta+Alt+P,Meta+Alt+P,Move keyboard focus between panels +cycleNextAction=none,,Next History Item +cyclePrevAction=none,,Previous History Item +edit_clipboard=none,,Edit Contents… +manage activities=none,Meta+Q,Show Activity Switcher +next activity=none,none,Walk through activities +previous activity=none,none,Walk through activities (Reverse) +repeat_action=none,,Manually Invoke Action on Current Clipboard +show dashboard=Ctrl+F12,Ctrl+F12,Show Desktop +show-barcode=none,,Show Barcode… +show-on-mouse-pos=Meta+V,Meta+V,Show Clipboard Items at Mouse Position +switch to next activity=none,,Switch to Next Activity +switch to previous activity=none,,Switch to Previous Activity +toggle do not disturb=none,,Toggle do not disturb + +[services][firefox.desktop] +_launch=Meta+Shift+B + +[services][foot.desktop] +_launch=Meta+Shift+T + +[services][net.local.open_wiki_index_firefox.bash.desktop] +_launch=Meta+Ctrl+I + +[services][net.local.open_wiki_index_nvim.bash.desktop] +_launch=Meta+I + +[services][org.kde.konsole.desktop] +_launch=none + +[services][org.kde.krunner.desktop] +_launch=Search\tAlt+F2\tMeta + +[services][org.keepassxc.KeePassXC.desktop] +_launch=Meta+K + +[services][org.mozilla.Thunderbird.desktop] +_launch=Meta+Shift+M diff --git a/.config/nvim/after/ftplugin/bash.lua b/.config/nvim/after/ftplugin/bash.lua new file mode 100644 index 0000000..d46657d --- /dev/null +++ b/.config/nvim/after/ftplugin/bash.lua @@ -0,0 +1,17 @@ +vim.opt_local.textwidth = 80 -- gw wraps at this value +vim.opt_local.colorcolumn = "+1" -- Highlight one column after 'textwidth' +vim.opt_local.complete = { + "o", -- 'omnifunc' +} + +vim.b.autoformat = true +vim.b.format_func = function() + local ret = vim.system({ "shfmt" }, { stdin = vim.api.nvim_buf_get_lines(0, 0, -1, false) }):wait() + if ret.code ~= 0 then + vim.notify("shfmt failed (" .. ret.code .. "):\n" .. ret.stderr, vim.log.levels.WARN) + return + end + local view = vim.fn.winsaveview() + vim.cmd("%!shfmt") + vim.fn.winrestview(view) +end diff --git a/.config/nvim/after/ftplugin/gitcommit.lua b/.config/nvim/after/ftplugin/gitcommit.lua new file mode 100644 index 0000000..e52a2d8 --- /dev/null +++ b/.config/nvim/after/ftplugin/gitcommit.lua @@ -0,0 +1,27 @@ +vim.opt_local.colorcolumn = "+1" -- Highlight one column after 'textwidth' +vim.opt_local.tabstop = 2 -- CommonMark expects two spaces for indentation +vim.opt_local.shiftwidth = 0 +vim.opt_local.softtabstop = -1 +vim.opt_local.expandtab = true -- Change tabs to spaces +vim.opt_local.spelllang = "en_us" +vim.opt_local.complete = { + "w", -- other windows + ".", -- current buffer +} + +vim.api.nvim_create_autocmd("BufWinEnter", { + desc = "Set up gitcommit buffer", + group = vim.g.dotfiles.augroup, + buffer = 0, + callback = function() + local gitcommit_window = vim.api.nvim_get_current_win() + -- Open cached diff window + vim.cmd.DiffGitCached() + vim.cmd.wincmd("L") + -- Enable English spell check in the original window + -- If `'spell'` is set before `:DiffGitChached` it propagates to the cached diff window + vim.api.nvim_set_option_value("spell", true, { win = gitcommit_window }) + -- The current window is the diff window + vim.o.filetype = "diff" + end, +}) diff --git a/.config/nvim/after/ftplugin/lua.lua b/.config/nvim/after/ftplugin/lua.lua new file mode 100644 index 0000000..784915e --- /dev/null +++ b/.config/nvim/after/ftplugin/lua.lua @@ -0,0 +1,36 @@ +local function format() + local view = vim.fn.winsaveview() + local buf_str = "" + for _, v in ipairs(vim.api.nvim_buf_get_lines(0, 0, -1, false)) do + buf_str = buf_str .. v .. "\n" + end + local tempname = vim.fn.tempname() + local tempfile = assert(io.open(tempname, "w"), "Could not open temporary file") + tempfile:write(buf_str) + tempfile:close() + local r_check = vim.system({ "stylua", "--check", tempname }):wait() + if r_check.code == 0 then + return + elseif r_check.code ~= 1 then + vim.notify("stylua failed (" .. r_check.code .. "):\n" .. r_check.stderr) + end + local r_format = vim.system({ "stylua", tempname }):wait() + if r_format.code ~= 0 then + vim.notify("stylua failed (" .. r_format.code .. "):\n" .. r_format.stderr) + end + local formatted_lines = {} + for line in io.lines(tempname) do + formatted_lines[#formatted_lines + 1] = line + end + vim.api.nvim_buf_set_lines(0, 0, -1, false, formatted_lines) + vim.fn.winrestview(view) +end + +vim.opt_local.textwidth = 120 -- gw wraps at this value +vim.opt_local.colorcolumn = "+1" -- Highlight one column after +vim.opt_local.complete = { + "o", -- 'omnifunc' +} + +vim.b.autoformat = true +vim.b.format_func = format diff --git a/.config/nvim/after/ftplugin/markdown.lua b/.config/nvim/after/ftplugin/markdown.lua new file mode 100644 index 0000000..755b9aa --- /dev/null +++ b/.config/nvim/after/ftplugin/markdown.lua @@ -0,0 +1,80 @@ +local function format(callback) + local view = vim.fn.winsaveview() + local buf_str = "" + for _, v in ipairs(vim.api.nvim_buf_get_lines(0, 0, -1, false)) do + buf_str = buf_str .. v .. "\n" + end + local tempname = vim.fn.tempname() + local tempfile = assert(io.open(tempname, "w"), "Could not open temporary file") + tempfile:write(buf_str) + tempfile:close() + + local r_check = vim.system({ + "mdformat", + "--number", + "--extensions", + "tables", + "--extensions", + "frontmatter", + "--wrap", + tostring(vim.bo.textwidth), + "--check", + tempname, + }):wait() + if r_check.code == 0 then + return + elseif r_check.code ~= 1 then + vim.notify("mdformat failed (" .. r_check.code .. "):\n" .. r_check.stderr) + end + local r_format = vim.system({ + "mdformat", + "--number", + "--extensions", + "tables", + "--extensions", + "frontmatter", + "--wrap", + tostring(vim.bo.textwidth), + tempname, + }):wait() + if r_format.code ~= 0 then + vim.notify("mdformat failed (" .. r_format.code .. "):\n" .. r_format.stderr) + end + local formatted_lines = {} + for line in io.lines(tempname) do + formatted_lines[#formatted_lines + 1] = line + end + vim.api.nvim_buf_set_lines(0, 0, -1, false, formatted_lines) + + vim.fn.winrestview(view) + if callback then + callback() + end +end + +vim.opt_local.tabstop = 2 -- CommonMark expects two spaces for indentation +vim.opt_local.shiftwidth = 0 +vim.opt_local.softtabstop = -1 +vim.opt_local.expandtab = true -- Change tabs to spaces +-- Nothing in the gutter, except sign columns if necessary +vim.opt_local.number = false +vim.opt_local.relativenumber = false +vim.opt_local.signcolumn = "auto" +vim.opt_local.foldcolumn = "0" +-- +vim.opt_local.textwidth = 120 -- gw wraps at this value +vim.opt_local.colorcolumn = "+1" -- Highlight one column after 'textwidth' +vim.opt_local.foldlevel = 1 +-- Replace concealed text with a character from `'listchars'` +-- With `'foldmethod'` set to `expr` and `'foldlevel'` set to 2 fenced code blocks are concealed and completely invisible +-- I guess that's a TODO for later, find a way to use `'conceallevel'` = 2 but with folded fenced code blocks visible +vim.opt_local.conceallevel = 1 +vim.opt_local.complete = { + "o", -- 'omnifunc' + ".", -- current buffer +} +-- Visible link labels +vim.api.nvim_set_hl(0, "@markup.link.label.markdown_inline", { underline = true, update = true }) + +vim.b.autoformat = true +vim.b.format_func = format diff --git a/.config/nvim/after/ftplugin/qf.lua b/.config/nvim/after/ftplugin/qf.lua new file mode 100644 index 0000000..80aec64 --- /dev/null +++ b/.config/nvim/after/ftplugin/qf.lua @@ -0,0 +1 @@ +vim.opt.wrap = false diff --git a/.config/nvim/after/queries/gitcommit/injections.scm b/.config/nvim/after/queries/gitcommit/injections.scm new file mode 100644 index 0000000..5996a50 --- /dev/null +++ b/.config/nvim/after/queries/gitcommit/injections.scm @@ -0,0 +1,4 @@ +((message) @injection.content + (#set! injection.language "markdown_inline")) +((message_line) @injection.content + (#set! injection.language "markdown_inline")) diff --git a/.config/nvim/init.lua b/.config/nvim/init.lua new file mode 100644 index 0000000..0a116d5 --- /dev/null +++ b/.config/nvim/init.lua @@ -0,0 +1,178 @@ +if vim.env.DOTFILES_DIR == nil then + vim.notify("DOTFILES_DIR is not set!", vim.log.levels.WARN) +end + +vim.g.dotfiles = { augroup = vim.api.nvim_create_augroup("dotfiles", {}) } + +------------------------------------------------------------------------------------------------------------------------ +-- Local functions +------------------------------------------------------------------------------------------------------------------------ + +-- Show the diff between the current buffer and the file it was loaded from +local function show_buf_diff_orig() + vim.cmd.new({ mods = { vertical = true } }) + vim.b.buftype = "nofile" + vim.cmd.read("++edit #") + vim.api.nvim_buf_set_lines(0, 0, 2, true, {}) + vim.cmd.diffthis() + vim.cmd.wincmd("p") + vim.cmd.diffthis() +end + +local function reload_buffer() + local view = vim.fn.winsaveview() + if vim.o.filetype == "netrw" then + vim.cmd.edit(".") + else + vim.cmd.edit() + end + vim.fn.winrestview(view) +end + +local function toggle_wrap() + vim.o.wrap = not vim.o.wrap +end + +------------------------------------------------------------------------------------------------------------------------ +-- Miscellaneous +------------------------------------------------------------------------------------------------------------------------ + +vim.g.mapleader = " " -- Set before any `vim.keymap.set()` call + +-- Highlight on yank +vim.api.nvim_create_autocmd("TextYankPost", { + desc = "Highlight on yank", + group = vim.g.dotfiles.augroup, + callback = function() + vim.hl.on_yank() + end, +}) + +-- Start terminal mode automatically +vim.api.nvim_create_autocmd({ "WinEnter", "TermOpen" }, { + desc = "Start terminal mode", + group = vim.g.dotfiles.augroup, + pattern = "term://*", + callback = function() + vim.cmd.startinsert() + end, +}) + +------------------------------------------------------------------------------------------------------------------------ +-- Plugins +------------------------------------------------------------------------------------------------------------------------ + +vim.pack.add({ + "https://github.com/qadzek/link.vim", -- Tidy URLs + "https://github.com/tpope/vim-surround", -- Manipulate delimiters + "https://github.com/airblade/vim-gitgutter", -- Show git status in the gutter +}) + +------------------------------------------------------------------------------------------------------------------------ +-- Commands (there's more in the different plugins) +------------------------------------------------------------------------------------------------------------------------ + +vim.api.nvim_create_user_command("DiffOrig", show_buf_diff_orig, { desc = "Show diff between current buffer and file" }) + +------------------------------------------------------------------------------------------------------------------------ +-- Keymaps (there's more in the different plugins) +------------------------------------------------------------------------------------------------------------------------ +-- See `:help key-notation` and `:help <Cmd>` + +-- Miscellaneous +vim.keymap.set("n", "<Leader>s", "<Cmd>update<CR>", { desc = "Save buffer" }) +vim.keymap.set("n", "<Leader>be", reload_buffer, { desc = "Reload buffer" }) +vim.keymap.set("n", "<Leader>bd", "<Cmd>bdelete<CR>", { desc = "Delete current buffer" }) +vim.keymap.set("n", "<Leader>bb", "<Cmd>close<CR>", { desc = "Delete current window" }) +vim.keymap.set("n", "<Leader>bw", toggle_wrap, { desc = "Toggle text wrap" }) +vim.keymap.set("n", "<M-z>", "zA", { desc = "Toggle fold (recursively) under cursor" }) +vim.keymap.set("n", "ZR", vim.cmd.SessionRestart, { desc = "Save and load default session and restart" }) +vim.keymap.set("n", "ZZ", vim.cmd.SessionExitSave, { desc = "Save to default session and exit" }) +vim.keymap.set("n", "ZQ", vim.cmd.SessionExitNoSave, { desc = "Save to default session and exit" }) +vim.keymap.set("t", "<C-\\><C-\\>", "<C-\\><C-n>", { desc = "Exit terminal mode" }) +vim.keymap.set("n", "<Leader>mm", "<Cmd>messages<CR>", { desc = "Show all messages" }) +vim.keymap.set("n", "<Leader>mc", "<Cmd>messages clear<CR>", { desc = "Clear all messages" }) +vim.keymap.set("n", "<Leader>lh", vim.cmd.LspHintToggle, { desc = "Toggle inlay hints (LSP)" }) +-- These two mappings work well together to start command mode and insert from history in two keystrokes. +vim.keymap.set("n", "<C-p>", ":", { desc = "Enter command mode" }) +vim.keymap.set("c", "<C-p>", "<Up>", { desc = "Insert previous history entry" }) +-- Navigate files +vim.keymap.set("n", "<M-e>", vim.cmd.GotoExplorer, { desc = "Open/close/focus netrw window" }) +-- Navigate window +vim.keymap.set({ "n", "i" }, "<M-h>", "<C-w>h", { desc = "Move left a window" }) +vim.keymap.set({ "n", "i" }, "<M-j>", "<C-w>j", { desc = "Move down a window" }) +vim.keymap.set({ "n", "i" }, "<M-k>", "<C-w>k", { desc = "Move up a window" }) +vim.keymap.set({ "n", "i" }, "<M-l>", "<C-w>l", { desc = "Move right a window" }) +vim.keymap.set("t", "<M-h>", "<C-\\><C-n><C-w>h", { desc = "Move left a window" }) +vim.keymap.set("t", "<M-j>", "<C-\\><C-n><C-w>j", { desc = "Move down a windowd" }) +vim.keymap.set("t", "<M-k>", "<C-\\><C-n><C-w>k", { desc = "Move up a window" }) +vim.keymap.set("t", "<M-l>", "<C-\\><C-n><C-w>l", { desc = "Move right a window" }) +-- Navigate tab +vim.keymap.set({ "n", "i", "t" }, "<M-n>", "<Cmd>tabnext #<CR>", { desc = "Move to last accessed tab page" }) +vim.keymap.set({ "n", "i", "t" }, "<M-n>", "<Cmd>tabnext<CR>", { desc = "Move to next tab page" }) +vim.keymap.set({ "n", "i", "t" }, "<M-b>", "<Cmd>tabprevious<CR>", { desc = "Move to previous tab page" }) +vim.keymap.set("i", "<M-h>", "<Space><BS><Left>") +vim.keymap.set("i", "<M-l>", "<Space><BS><Right>") +-- Navigate in command/insert mode +vim.keymap.set({ "c", "i" }, "<M-h>", "<Space><BS><Left>") +vim.keymap.set({ "c", "i" }, "<M-l>", "<Space><BS><Right>") +vim.keymap.set("c", "<M-H>", "<C-Left>") +vim.keymap.set("c", "<M-L>", "<C-Right>") +-- Non incremental history search, works when the completion menu is active +vim.keymap.set("c", "<C-k>", function() + return vim.fn.pumvisible() == 1 and "<C-e><Up>" or "<Up>" +end, { expr = true }) +vim.keymap.set("c", "<C-j>", function() + return vim.fn.pumvisible() == 1 and "<C-e><Down>" or "<Down>" +end, { expr = true }) + +------------------------------------------------------------------------------------------------------------------------ +-- Options +------------------------------------------------------------------------------------------------------------------------ + +vim.opt.shell = "/usr/bin/bash" +vim.opt.foldenable = true -- Folds are closed by default +vim.opt.pumheight = 0 -- Popup menu height +vim.opt.confirm = true -- Confirm before losing buffer unsaved changes +vim.opt.splitbelow = false +vim.opt.splitright = false +vim.opt.clipboard = "unnamedplus" -- Sync clipboard +vim.opt.cursorline = true -- Highlight the line where the cursor is on. +vim.opt.scrolloff = 2 -- Keep this many screen lines above/below the cursor +vim.opt.ignorecase = true -- Search ignores case by default +vim.opt.smartcase = true -- Search is case-sensitive if searching for uppercase characters +vim.opt.conceallevel = 2 -- Hide text with the "conceal" syntax attribute +vim.opt.list = true -- Display <Tab> and other non-printables +vim.opt.listchars = { -- Characters used by 'list' + tab = "> ", -- Tab + trail = "-", -- Trailing space + nbsp = "+", -- Non-breakable space + extends = "→", -- Last column when 'wrap' is off + precedes = "←", -- First column when there is text before it +} +-- See `'cpoptions'` +-- Use a `\` like `CTRL_V` in mappings, abbreviations, user commands and the "to" part of menu commands +-- Useful for example for matching mappings that start with space: `:map \ ` +vim.opt.cpoptions:remove("B") +-- Backup +vim.opt.swapfile = true -- Store a swap file, see `:help swap-file` +vim.opt.undofile = true -- Save buffer undo history +vim.opt.backup = true -- Backup file before overwriting +vim.opt.backupdir = { vim.fn.stdpath("state") .. "/nvim/backup//" } +vim.opt.writebackup = true -- Keep backup after writing file +-- Gutter +vim.opt.number = true -- Show line number +vim.opt.relativenumber = true -- Show relative line number +vim.opt.numberwidth = 4 -- Minimal number of columns for line number (includes the space before the text) +vim.opt.signcolumn = "auto" -- Always show sign column +vim.opt.foldcolumn = "0" -- Fixed number of fold columns +-- Wrapping +vim.opt.wrap = false -- Don't wrap text by default +vim.opt.showbreak = "+++ " -- String to show wrapped lines +vim.opt.linebreak = true -- Wrap at characters in 'breakat' +vim.opt.breakindent = true -- Indent wrapped lines +-- Tab and backspace (See `help 30.5`) +vim.opt.tabstop = 4 -- <Tab> and <Bs> visually expand at columns multiple of 'tabstop' +vim.opt.shiftwidth = 0 -- Number of space in first indendation. If 0 use 'tabstop' value. +vim.opt.softtabstop = -1 -- <Tab> and <Bs> stop at columns multiple of 'softtabstop'. If <0 use 'shiftwidth' value. +vim.opt.expandtab = false -- Don't turn tabs into spaces diff --git a/.config/nvim/plugin/10-treesitter.lua b/.config/nvim/plugin/10-treesitter.lua new file mode 100644 index 0000000..4602519 --- /dev/null +++ b/.config/nvim/plugin/10-treesitter.lua @@ -0,0 +1,37 @@ +-- +-- Treesitter parsers +-- + +vim.pack.add({ "https://github.com/nvim-treesitter/nvim-treesitter" }) + +require("nvim-treesitter").install({ + "bash", + "diff", + "gitcommit", + "ini", + "json", + "lua", + "markdown", + "markdown_inline", + "readline", + "vim", + "vimdoc", +}) + +-- Parse the tree as early as possible, which is as soon as we know the filetype +-- +-- Event handlers for the same event are triggered in the order they were created. This must be the first FileType +-- handler created so that the tree is parsed for other FileType handlers (which often need the parsed tree). To create +-- it first put this in a file that comes before alphabetically in the `plugin/` directory. +vim.api.nvim_create_autocmd("FileType", { + desc = "Start treesitter", + group = vim.g.dotfiles.augroup, + callback = function() + local parser = vim.treesitter.get_parser(0) + if parser == nil then + return + end + parser:parse() -- Parse once asynchronously; used by logic that needs the parsed tree (like extmarks) + vim.treesitter.start() + end, +}) diff --git a/.config/nvim/plugin/50-autoformat.lua b/.config/nvim/plugin/50-autoformat.lua new file mode 100644 index 0000000..4761097 --- /dev/null +++ b/.config/nvim/plugin/50-autoformat.lua @@ -0,0 +1,24 @@ +-- +-- Autoformat plugin +-- + +local function toggle_autoformat() + vim.b.autoformat = not vim.b.autoformat +end + +-- Autoformat +vim.api.nvim_create_autocmd("BufWritePre", { + desc = "Autoformat buffer", + group = vim.g.dotfiles.augroup, + callback = function(ev) + if vim.b.autoformat then + if vim.b.format_func == nil then + vim.notify("No formatter set for " .. ev.file .. " (" .. vim.o.filetype .. ")", vim.log.levels.WARN) + return + end + vim.b.format_func() + end + end, +}) + +vim.api.nvim_create_user_command("AutoformatToggle", toggle_autoformat, { desc = "Toggle autoformatting" }) diff --git a/.config/nvim/plugin/50-autopairs.lua b/.config/nvim/plugin/50-autopairs.lua new file mode 100644 index 0000000..2ab41c8 --- /dev/null +++ b/.config/nvim/plugin/50-autopairs.lua @@ -0,0 +1,17 @@ +-- +-- Autopairs configuration (inserting matching delimiters while inserting) +-- +-- See https://github.com/windwp/nvim-autopairs + +vim.pack.add({ "https://github.com/windwp/nvim-autopairs" }) + +local Rule = require("nvim-autopairs.rule") +local npairs = require("nvim-autopairs") +local cond = require("nvim-autopairs.conds") + +npairs.setup() + +npairs.add_rules({ + Rule("*", "*", "markdown"):with_cr(cond.none()), + Rule("~", "~", "markdown"):with_cr(cond.none()), +}) diff --git a/.config/nvim/plugin/50-colors.lua b/.config/nvim/plugin/50-colors.lua new file mode 100644 index 0000000..f713a74 --- /dev/null +++ b/.config/nvim/plugin/50-colors.lua @@ -0,0 +1,99 @@ +-- +-- Color scheme plugin +-- +vim.pack.add({ "https://github.com/maxmx03/solarized.nvim" }) -- Solarized color scheme + +local solarized_colors = { + base03 = "#002b36", + base02 = "#073642", + base01 = "#586e75", + base00 = "#657b83", + base0 = "#839496", + base1 = "#93a1a1", + base2 = "#eee8d5", + base3 = "#fdf6e3", + yellow = "#b58900", + orange = "#cb4b16", + red = "#dc322f", + magenta = "#d33682", + violet = "#6c71c4", + blue = "#268bd2", + cyan = "#2aa198", + green = "#859900", +} + +local function adjust_highlight() + if vim.g.colors_name == "solarized" then + -- General highlight + vim.api.nvim_set_hl(0, "EndOfBuffer", { fg = solarized_colors.base0, update = true }) + vim.api.nvim_set_hl(0, "MatchParen", { link = "CurSearch" }) + if vim.o.background == "light" then + vim.api.nvim_set_hl(0, "TabLineSel", { bg = "#edffd5" }) + vim.api.nvim_set_hl(0, "Cursorline", { bg = "#edffd5" }) + vim.api.nvim_set_hl(0, "NormalFloat", { bg = solarized_colors.base2 }) + elseif vim.o.background == "dark" then + vim.api.nvim_set_hl(0, "TabLineSel", { bg = "#043624" }) + vim.api.nvim_set_hl(0, "Cursorline", { bg = "#043624" }) + vim.api.nvim_set_hl(0, "NormalFloat", { bg = solarized_colors.base02 }) + end + + -- Markdown highlight + if vim.o.background == "light" then + vim.api.nvim_set_hl(0, "@markup.raw", { bg = solarized_colors.base2, update = true }) + elseif vim.o.background == "dark" then + vim.api.nvim_set_hl(0, "@markup.raw", { bg = solarized_colors.base02, update = true }) + end + vim.api.nvim_set_hl(0, "@markup.raw.block", { fg = solarized_colors.cyan, update = true }) + vim.api.nvim_set_hl(0, "@markup.heading.1", { link = "@markup.heading" }) + vim.api.nvim_set_hl(0, "@markup.heading.2", { link = "@markup.heading" }) + vim.api.nvim_set_hl(0, "@markup.heading.3", { link = "@markup.heading" }) + vim.api.nvim_set_hl(0, "@markup.heading.4", { link = "@markup.heading" }) + vim.api.nvim_set_hl(0, "@markup.heading.5", { link = "@markup.heading" }) + vim.api.nvim_set_hl(0, "@markup.heading.6", { link = "@markup.heading" }) + end +end + +local function next_colorscheme() + local colorschemes = vim.fn.getcompletion("", "color") + vim.g.colors_name = vim.g.colors_name or "default" + for i = 1, #colorschemes do + if colorschemes[i] == vim.g.colors_name then + vim.cmd.colorscheme(colorschemes[(i % #colorschemes) + 1]) + vim.notify("Color scheme set to " .. vim.g.colors_name) + return + end + end +end + +local function random_colorscheme() + local colorschemes = vim.fn.getcompletion("", "color") + vim.cmd.colorscheme(colorschemes[math.random(#colorschemes)]) + vim.notify("Color scheme set to " .. vim.g.colors_name) +end + +local function toggle_theme() + vim.opt.background = vim.o.background == "light" and "dark" or "light" + vim.notify("Color theme set to " .. vim.o.background) +end + +-- Adjust colors after the color scheme has loaded +vim.api.nvim_create_autocmd("ColorScheme", { + desc = "Adjust color scheme", + group = vim.g.dotfiles.augroup, + callback = adjust_highlight, +}) + +-- Adjust colors when the theme (light/dark) changes +vim.api.nvim_create_autocmd("OptionSet", { + desc = "Adjust color scheme", + pattern = "background", + group = vim.g.dotfiles.augroup, + callback = adjust_highlight, +}) + +vim.cmd.colorscheme("solarized") -- Default colorscheme +vim.opt.background = "dark" -- Default theme + +vim.api.nvim_create_user_command("ColorsNext", next_colorscheme, { desc = "Load next color scheme" }) +vim.api.nvim_create_user_command("ColorsRandom", random_colorscheme, { desc = "Load random color scheme" }) +vim.api.nvim_create_user_command("ColorsThemeToggle", toggle_theme, { desc = "Toggle light/dark theme" }) diff --git a/.config/nvim/plugin/50-completion.lua b/.config/nvim/plugin/50-completion.lua new file mode 100644 index 0000000..94b4cbb --- /dev/null +++ b/.config/nvim/plugin/50-completion.lua @@ -0,0 +1,72 @@ +-- +-- Completion configuration plugin +-- + +------------------------------------------------------------------------------------------------------------------------ +-- Insert mode completion +------------------------------------------------------------------------------------------------------------------------ +-- See `:help ins-completion-menu` + +vim.opt.autocomplete = true -- Show completion menu automatically +vim.opt.completeopt = { + "noselect", -- No item selected initially + "fuzzy", + "menuone", -- Show matches in a menu, even if there's only one match + "popup", -- Menu items show extra ingo in the popup window + "preview", -- Show extra info in the preview window +} +-- Completion sources (in order of priority) +vim.opt.complete = { + "o", -- 'omnifunc' +} +vim.opt.pumwidth = 25 +vim.keymap.set("i", "<Tab>", function() + return vim.fn.pumvisible() == 1 and "<C-n>" or "<Tab>" +end, { expr = true }) +vim.keymap.set("i", "<S-Tab>", function() + return vim.fn.pumvisible() == 1 and "<C-p>" or "<S-Tab>" +end, { expr = true }) + +------------------------------------------------------------------------------------------------------------------------ +-- Command-line mode completion +------------------------------------------------------------------------------------------------------------------------ +-- See `:help cmdline-completion` and `:help cmdline-autocompletion` + +-- Show completion menu automatically +vim.api.nvim_create_autocmd({ "CmdlineChanged", "CmdlineEnter" }, { + desc = "Autocompletion", + group = vim.g.dotfiles.augroup, + pattern = "[:\\/\\?]", + callback = function() + vim.cmd.call("wildtrigger()") + end, +}) +vim.opt.wildmenu = true -- Show completions in a menu +vim.opt.wildchar = 9 -- Char code assigned to command line wildcard expansion +vim.opt.wildoptions = { + "exacttext", -- Discard regex artifacts when performing search pattern completion + "pum", -- Show completions in a popup menu + "tagfile", -- Show tag kind and file + "fuzzy", -- Fuzzy matching (doesn't work with files/dirs, see `:help 'wildoptions'`, but patterns do work!) +} +-- Completion modes triggered in order by `wildtrigger()` or 'wildchar' +vim.opt.wildmode = { + "noselect", -- List matches without inserting + "full", -- List matches and insert first full match +} +-- Insert unique match +vim.keymap.set("c", string.format("%c", vim.o.wildchar), function() + local complete_info = vim.fn.cmdcomplete_info() + if complete_info.pum_visible == 1 then + return #complete_info.matches == 1 and "<C-n><Space><BS>" or "<C-n>" + end + vim.fn.wildtrigger() +end, { expr = true, desc = "Command line wildcard expansion" }) +-- Show next completion choices after accepting an entry with `<C-y>` +vim.keymap.set("c", "<C-y>", function() + local complete_info = vim.fn.cmdcomplete_info() + if complete_info.pum_visible == 1 then + return complete_info.selected ~= -1 and "<Space><BS>" or "<C-e>" + end + return "<C-y>" +end, { expr = true }) diff --git a/.config/nvim/plugin/50-dap.lua b/.config/nvim/plugin/50-dap.lua new file mode 100644 index 0000000..683984c --- /dev/null +++ b/.config/nvim/plugin/50-dap.lua @@ -0,0 +1,28 @@ +-- +-- DAP client/adapter configurations +-- + +vim.pack.add({ "https://github.com/mfussenegger/nvim-dap" }) -- DAP (Debug Adapter Protocol) client +local dap = require("dap") + +-- OSV configuration +vim.pack.add({ "https://github.com/jbyuki/one-small-step-for-vimkind" }) -- DAP adapter for Neovim Lua +local osv = require("osv") + +dap.configurations.lua = { + { + type = "nlua", + request = "attach", + name = "Attach to running Neovim instance", + }, +} +dap.adapters.nlua = function(callback, config) + callback({ type = "server", host = config.host or "127.0.0.1", port = config.port or 8086 }) +end +vim.api.nvim_create_user_command("OSVLaunch", function() + osv.launch({ port = 8086 }) +end, { desc = "Launch OSV server" }) +vim.api.nvim_create_user_command("OSVStatus", function() + vim.notify("OSV server running: " .. tostring(osv.is_running())) + vim.notify("OSV client attached: " .. tostring(osv.is_attached())) +end, { desc = "Show OSV status" }) diff --git a/.config/nvim/plugin/50-dotfiles.lua b/.config/nvim/plugin/50-dotfiles.lua new file mode 100644 index 0000000..122912c --- /dev/null +++ b/.config/nvim/plugin/50-dotfiles.lua @@ -0,0 +1,36 @@ +-- +-- dotfiles plugin +-- + +-- Returns true if the current buffer is part of the Dotfiles repository +local function detect_dotfiles_file() + if vim.env.DOTFILES_DIR == nil then + return false + end + local ret = vim.system( + { "git", "--git-dir=" .. vim.env.DOTFILES_DIR, "--work-tree=" .. vim.env.HOME, "ls-files" }, + { text = true } + ):wait() + local buf_file_name = string.sub(vim.api.nvim_buf_get_name(0), #vim.env.HOME + 2) + for _, dotfiles_filename in ipairs(vim.split(ret.stdout, "\n", { trimempty = true })) do + if dotfiles_filename == buf_file_name then + return true + end + end + return false +end + +vim.api.nvim_create_autocmd("BufEnter", { + desc = "Detect dotfiles file", + group = vim.g.dotfiles.augroup, + callback = function() + vim.b.dotfiles_file = detect_dotfiles_file() + end, +}) + +vim.api.nvim_create_user_command("DotfilesOn", function() + vim.g.gitgutter_git_args = "--git-dir=" .. vim.env.DOTFILES_DIR .. " --work-tree=" .. vim.env.HOME +end, { desc = "Activate dotfiles (set git env)" }) +vim.api.nvim_create_user_command("DotfilesOff", function() + vim.g.gitgutter_git_args = "" +end, { desc = "Deactivate dotfiles (unset git env)" }) diff --git a/.config/nvim/plugin/50-fold.lua b/.config/nvim/plugin/50-fold.lua new file mode 100644 index 0000000..c7013bb --- /dev/null +++ b/.config/nvim/plugin/50-fold.lua @@ -0,0 +1,22 @@ +-- +-- fold plugin +-- + +-- Initialize folds with foldmethod = expr if a treesitter parser is available, then switch back to manual +-- This is to have the folds calculated and closed when editing the file initially, but with foldmethod set to manual +-- Without foldmethod set to manual folds are created while writing in insert mode which is annoying +-- +-- Skip floating windows, because they often are documentation (and thus should be visible by default) +vim.api.nvim_create_autocmd("FileType", { + desc = "Initialize folds", + group = vim.g.dotfiles.augroup, + callback = function(ev) + if vim.api.nvim_win_get_config(0).relative ~= "" or vim.treesitter.get_parser(0) == nil then + return + end + vim.opt_local.foldexpr = "v:lua.vim.treesitter.foldexpr()" + vim.opt_local.foldmethod = "expr" + vim.cmd.normal({ "zX", bang = true }) + vim.opt_local.foldmethod = "manual" + end, +}) diff --git a/.config/nvim/plugin/50-ftdetect.lua b/.config/nvim/plugin/50-ftdetect.lua new file mode 100644 index 0000000..49fc6d6 --- /dev/null +++ b/.config/nvim/plugin/50-ftdetect.lua @@ -0,0 +1,18 @@ +-- +-- Filetype detection plugin +-- + +-- Detect bash files +vim.api.nvim_create_autocmd({ "BufRead", "BufNewFile" }, { + desc = "Detect bash file", + group = vim.g.dotfiles.augroup, + pattern = "*", + callback = function() + if + string.find(vim.api.nvim_buf_get_name(0), "%.bash$") + or string.find(vim.api.nvim_buf_get_lines(0, 0, 1, false)[1] or "", "^#!/usr/bin/bash") + then + vim.bo.filetype = "bash" + end + end, +}) diff --git a/.config/nvim/plugin/50-highlight.lua b/.config/nvim/plugin/50-highlight.lua new file mode 100644 index 0000000..0e8fb97 --- /dev/null +++ b/.config/nvim/plugin/50-highlight.lua @@ -0,0 +1,101 @@ +-- +-- Custom highlighting plugin (in `after/` to that the autocommand in `treesitter/init.lua` can be set first in order) +-- + +-- Returns an iterator over the ascendants of `node`, starting at the root node +local function node_ascendants(node) + local root = node:tree():root() + local current = root + return function() + if current:equal(node) then + return nil + end + current = assert(current:child_with_descendant(node)) + return current + end +end + +-- Returns true if `node` or any of its parent has the type `type` +local function node_within_type(node, type) + local next_ascendant = node_ascendants(node) + local next = next_ascendant() + while next do + if next:type() == type then + return true + end + next = next_ascendant() + end + return false +end + +-- Returns an iterator over the positions (`{ col1, col2, row }`) of matches of pattern in lines +local function pmatches(lines, pattern) + local col1, col2, row = nil, nil, 1 + return function() + while row < #lines do + col1, col2 = string.find(lines[row], pattern, (col2 or 0) + 1) + if col1 then + return { col1 = col1, col2 = col2, row = row } + else + col1, col2, row = 0, 0, row + 1 + end + end + return nil + end +end + +-- Set extmarks are the positions (`{ col1, col2, row }`) given by `next_pos()` +-- `opts`: { +-- ns: highlight namespace +-- predicate: is passed row and col, if true then set the mark +-- hl_group: highlight group +-- } +local function set_extmarks(next_pos, opts) + for pos in next_pos do + local marks = vim.api.nvim_buf_get_extmarks( + 0, + opts.ns, + { pos.row - 1, pos.col1 - 1 }, + { pos.row - 1, pos.col2 - 1 }, + {} + ) + if #marks == 0 and opts.predicate(pos.row - 1, pos.col1 - 1) then + vim.api.nvim_buf_set_extmark( + 0, + opts.ns, + pos.row - 1, + pos.col1 - 1, + { end_col = pos.col2, hl_group = opts.hl_group } + ) + end + pos = next_pos() + end +end + +-- TODO a better way to do this: +-- For code, only check in "comment" node ranges +-- For non-code, do it per type. For Markdown, only check "paragraph" nodes +local function hl_todo_predicate(row, col) + if vim.o.filetype == "markdown" or vim.treesitter.get_parser() == nil then + return true + end + local node = assert(vim.treesitter.get_node({ pos = { row, col } })) + return node_within_type(node, "comment") +end + +local patterns = { todo = "TODO" } +local hl_ns = vim.api.nvim_create_namespace("extmarks") + +vim.api.nvim_set_hl(hl_ns, "dotfiles.Todo", { bg = "Yellow", fg = "Black", bold = true }) +local extmarks_todo_opts = { ns = hl_ns, hl_group = "dotfiles.Todo", predicate = hl_todo_predicate } + +-- Initialize extmarks +vim.api.nvim_create_autocmd("FileType", { + desc = "Initialize extmarks", + group = vim.g.dotfiles.augroup, + callback = function() + vim.api.nvim_win_set_hl_ns(vim.api.nvim_get_current_win(), hl_ns) + local lines = vim.api.nvim_buf_get_lines(0, 0, -1, false) + set_extmarks(pmatches(lines, patterns.todo), extmarks_todo_opts) + end, +}) diff --git a/.config/nvim/plugin/50-local.lua b/.config/nvim/plugin/50-local.lua new file mode 100644 index 0000000..88c63c5 --- /dev/null +++ b/.config/nvim/plugin/50-local.lua @@ -0,0 +1,8 @@ +-- +-- local include plugin +-- + +local local_config_file = vim.fn.stdpath("config") .. "/local.lua" +if vim.uv.fs_stat(local_config_file) then + dofile(local_config_file) +end diff --git a/.config/nvim/plugin/50-lorem.lua b/.config/nvim/plugin/50-lorem.lua new file mode 100644 index 0000000..8970574 --- /dev/null +++ b/.config/nvim/plugin/50-lorem.lua @@ -0,0 +1,35 @@ +-- +-- Lorem plugin (command to insert "lorem ipsum" words) +-- + +-- Returns lorem ipsum words +local function iter_lorem() + local filename = vim.env.HOME .. "/.local/share/dotfiles/lorem.txt" + local file = assert(io.open(filename, "r")) + local string = file:read("*all") + io.close(file) + local lorem_words = vim.split(vim.trim(string), " ", { trimempty = true }) + local i = 0 + return function() + i = i % #lorem_words + 1 + return lorem_words[i] + end +end + +-- Returns a n words long "lorem ipsum" string +local function get_lorem(n) + local iter = iter_lorem() + local words = {} + for i = 1, n do + words[i] = iter() + end + return table.concat(words, " ") +end + +local function put_lorem(n) + vim.api.nvim_put({ get_lorem(n) }, "c", true, true) +end + +vim.api.nvim_create_user_command("Lorem", function(opts) + put_lorem(tonumber(opts.args)) +end, { desc = "Put lorem ipsum string", nargs = 1 }) diff --git a/.config/nvim/plugin/50-lsp.lua b/.config/nvim/plugin/50-lsp.lua new file mode 100644 index 0000000..de86ecd --- /dev/null +++ b/.config/nvim/plugin/50-lsp.lua @@ -0,0 +1,90 @@ +-- +-- LSP client configurations +-- + +local function inspect_lsp() + local client = vim.lsp.get_clients()[1] + if client == nil then + vim.notify("No LSP client loaded") + return + end + vim.notify(vim.inspect(client.server_capabilities), vim.log.levels.INFO) +end + +vim.pack.add({ "https://github.com/neovim/nvim-lspconfig" }) + +vim.api.nvim_create_user_command("LspInspect", inspect_lsp, { desc = "Inpsect LSP client" }) + +-- Enable LSP server capabilities if available when attaching, see `:help lsp-attach` +vim.api.nvim_create_autocmd("LspAttach", { + desc = "Enable LSP capabilities", + group = vim.g.dotfiles.augroup, + callback = function(ev) + local client = assert(vim.lsp.get_client_by_id(ev.data.client_id)) + if client:supports_method("textDocument/completion") then + vim.lsp.completion.enable(true, client.id, ev.buf, { autotrigger = true }) + else + vim.notify("Client " .. client.name .. " does not support completion", vim.log.levels.WARN) + end + if client:supports_method("textDocument/inlayHint") then + vim.lsp.inlay_hint.enable(true, { bufnr = 0 }) + else + vim.notify("Client " .. client.name .. " does not support inlay hints", vim.log.levels.WARN) + end + end, +}) + +vim.api.nvim_create_user_command("LspHintToggle", function() + vim.lsp.inlay_hint.enable(not vim.lsp.inlay_hint.is_enabled({ bufnr = 0 }), { bufnr = 0 }) +end, { desc = "Toggle LSP inlay hints" }) + +------------------------------------------------------------------------------------------------------------------------ +-- Lua +------------------------------------------------------------------------------------------------------------------------ + +vim.lsp.config("lua_ls", { + on_init = function(client) + -- Use `.luarc.json` or `.luarc.jsonc` if available, then exit + -- See `:help vim.lsp.ClientConfig` for `workspace_folders` + if client.workspace_folders then + local path = client.workspace_folders[1].name + if + path ~= vim.fn.stdpath("config") + and (vim.uv.fs_stat(path .. "/.luarc.json") or vim.uv.fs_stat(path .. "/.luarc.jsonc")) + then + return + end + end + -- See Lua language server configuration settings at https://luals.github.io/wiki/settings + client.config.settings.Lua = vim.tbl_deep_extend("force", client.config.settings.Lua, { + runtime = { + version = "Lua 5.1", + path = { "?.lua", "?/init.lua" }, -- See `:h lua-module-load` + }, + workspace = { + checkThirdParty = false, + library = vim.api.nvim_get_runtime_file("", true), -- Load all folders in 'runtimepath' + }, + }) + end, +}) +vim.lsp.enable("lua_ls") + +------------------------------------------------------------------------------------------------------------------------ +-- Bash +------------------------------------------------------------------------------------------------------------------------ + +vim.lsp.enable("bashls") + +------------------------------------------------------------------------------------------------------------------------ +-- Markdown +------------------------------------------------------------------------------------------------------------------------ + +vim.lsp.config("marksman", { filetypes = { "markdown" } }) +vim.lsp.enable("marksman") + +------------------------------------------------------------------------------------------------------------------------ +-- Vimscript +------------------------------------------------------------------------------------------------------------------------ + +vim.lsp.enable("vimls") diff --git a/.config/nvim/plugin/50-netrw.lua b/.config/nvim/plugin/50-netrw.lua new file mode 100644 index 0000000..4fdbfa6 --- /dev/null +++ b/.config/nvim/plugin/50-netrw.lua @@ -0,0 +1,25 @@ +-- +-- netrw plugin +-- + +local function explore() + local windows = vim.api.nvim_tabpage_list_wins(0) + for _, win in ipairs(windows) do + local buf = vim.api.nvim_win_get_buf(win) + local ft = vim.api.nvim_get_option_value("filetype", { buf = buf }) + if ft == "netrw" then + if win ~= vim.api.nvim_get_current_win() then + vim.api.nvim_set_current_win(win) + return + end + break + end + end + vim.cmd.Lexplore() +end + +vim.g.netrw_liststyle = 3 -- 3: list style +vim.g.netrw_winsize = -30 -- 30 colums wide +vim.g.netrw_banner = 0 -- No banner + +vim.api.nvim_create_user_command("GotoExplorer", explore, { desc = "Open/close/focus netrw window" }) diff --git a/.config/nvim/plugin/50-session.lua b/.config/nvim/plugin/50-session.lua new file mode 100644 index 0000000..b4aadda --- /dev/null +++ b/.config/nvim/plugin/50-session.lua @@ -0,0 +1,69 @@ +-- +-- Session plugin +-- + +local session_dir = vim.fn.stdpath("state") .. "/sessions" +local session_default = session_dir .. "/default.vim" +if not vim.uv.fs_stat(session_dir) then + vim.uv.fs_mkdir(session_dir, tonumber("755", 8)) + vim.notify("Sessions save directory created at " .. session_dir) +end + +local function save_session(path) + vim.cmd.mksession({ path, bang = true }) +end + +local function load_session(path) + vim.cmd.source(path) +end + +local function reload_session(path) + save_session(path) + vim.cmd.restart({ args = { "+qall", "SessionLoad", path } }) +end + +local function delete_session(path) + vim.fs.rm(path) +end + +local function session_completefunc(arg_lead, _, _) + local completions = {} + for path in vim.fs.dir(session_dir) do + if string.match(path, "^" .. arg_lead) and string.match(path, ".vim$") then + completions[#completions + 1] = path:sub(1, -5) + end + end + return completions +end + +local function session_op(base, op) + local path = #base > 0 and base or session_default + if not string.match(path, "^" .. session_dir) then + path = session_dir .. "/" .. path + end + if not string.match(path, "%.vim$") then + path = path .. ".vim" + end + op(path) +end + +vim.api.nvim_create_user_command("SessionSave", function(ev) + session_op(ev.args, save_session) +end, { desc = "Save session", nargs = "?", complete = session_completefunc }) +vim.api.nvim_create_user_command("SessionLoad", function(ev) + session_op(ev.args, load_session) +end, { desc = "Load session", nargs = "?", complete = session_completefunc }) +vim.api.nvim_create_user_command("SessionDelete", function(ev) + session_op(ev.args, delete_session) +end, { desc = "Delete session", nargs = "?", complete = session_completefunc }) +vim.api.nvim_create_user_command("SessionRestart", function(ev) + session_op(ev.args, reload_session) +end, { desc = "Reload session", nargs = "?", complete = session_completefunc }) +vim.api.nvim_create_user_command("SessionExitSave", function(ev) + session_op(ev.args, save_session) + vim.cmd.qall() +end, { desc = "Save and exit session", nargs = "?", complete = session_completefunc }) +vim.api.nvim_create_user_command("SessionExitNoSave", function(ev) + session_op(ev.args, save_session) + vim.cmd.qall({ bang = true }) +end, { desc = "Save and exit session", nargs = "?", complete = session_completefunc }) diff --git a/.config/nvim/plugin/50-spell.lua b/.config/nvim/plugin/50-spell.lua new file mode 100644 index 0000000..56d4c5f --- /dev/null +++ b/.config/nvim/plugin/50-spell.lua @@ -0,0 +1,34 @@ +-- +-- Spell check plugin +-- + +local function toggle_spell() + vim.o.spell = not vim.o.spell +end + +local function set_spell_en() + vim.opt.spelllang = { "en_us" } +end + +local function set_spell_fr() + vim.opt.spelllang = { "fr" } +end + +local function set_spell_en_fr() + vim.opt.spelllang = { "en_us", "fr" } +end + +vim.opt.spelllang = "en_us" -- Initial spell language +vim.opt.spellfile = { -- Spellfiles for zg, zw, z=, etc + "/home/tvanbesi/.config/nvim/spell/en.utf-8.add", + "/home/tvanbesi/.config/nvim/spell/fr.utf-8.add", +} + +vim.api.nvim_create_user_command("SpellToggle", toggle_spell, { desc = "Toggle spell checking" }) +vim.api.nvim_create_user_command("SpellSetEn", set_spell_en, { desc = "Set spell language to English" }) +vim.api.nvim_create_user_command("SpellSetFr", set_spell_fr, { desc = "Set spell language to French" }) +vim.api.nvim_create_user_command( + "SpellSetEnFr", + set_spell_en_fr, + { desc = "Set spell languages to English and French" } +) diff --git a/.config/nvim/plugin/50-statusline.lua b/.config/nvim/plugin/50-statusline.lua new file mode 100644 index 0000000..092f08f --- /dev/null +++ b/.config/nvim/plugin/50-statusline.lua @@ -0,0 +1,48 @@ +-- +-- Status line configuration plugin +-- +-- I use lualine, see https://github.com/nvim-lualine/lualine.nvim#configuring-lualine-in-initvim + +vim.pack.add({ + "https://github.com/nvim-tree/nvim-web-devicons", + "https://github.com/nvim-lualine/lualine.nvim", +}) +local lualine = require("lualine") + +local function spell_status() + if vim.o.spelllang == "fr" then + return "+S(fr)" + elseif vim.o.spelllang == "en_us" then + return "+S(en)" + elseif vim.o.spelllang == "en_us,fr" then + return "+S(en,fr)" + end +end + +local function statuses() + return (vim.b.autoformat and "+F" or "") .. (vim.o.spell and spell_status() or "") +end + +local lualine_sections = { + lualine_c = { statuses, "filename" }, + lualine_x = { "encoding", "fileformat", "filetype", "lsp_status" }, +} + +-- Adjust colors when the theme (light/dark) changes +vim.api.nvim_create_autocmd("OptionSet", { + desc = "Adjust color scheme", + pattern = "background", + group = vim.g.dotfiles.augroup, + callback = function() + lualine.setup({ + options = { theme = vim.o.background == "light" and "solarized_light" or "solarized_dark" }, + sections = lualine_sections, + }) + end, +}) + +lualine.setup({ options = { theme = "solarized_light" }, sections = lualine_sections }) + +vim.api.nvim_create_user_command("LualineConfig", function() + vim.notify(vim.inspect(lualine.get_config())) +end, { desc = "Show lualine configuration" }) diff --git a/.config/nvim/queries/markdown/injections.scm b/.config/nvim/queries/markdown/injections.scm new file mode 100644 index 0000000..c24cbd8 --- /dev/null +++ b/.config/nvim/queries/markdown/injections.scm @@ -0,0 +1,26 @@ +; See /home/tvanbesi/.local/share/nvim/site/queries/markdown/injections.scm +; It's the same but with language-specific highlighting disabled in fenced code blocks + +(fenced_code_block + (code_fence_content) @injection.content) + +((html_block) @injection.content + (#set! injection.language "html") + (#set! injection.combined) + (#set! injection.include-children)) + +((minus_metadata) @injection.content + (#set! injection.language "yaml") + (#offset! @injection.content 1 0 -1 0) + (#set! injection.include-children)) + +((plus_metadata) @injection.content + (#set! injection.language "toml") + (#offset! @injection.content 1 0 -1 0) + (#set! injection.include-children)) + +([ + (inline) + (pipe_table_cell) +] @injection.content + (#set! injection.language "markdown_inline")) diff --git a/.config/nvim/spell/en.utf-8.add b/.config/nvim/spell/en.utf-8.add new file mode 100644 index 0000000..3f63cf8 --- /dev/null +++ b/.config/nvim/spell/en.utf-8.add @@ -0,0 +1,27 @@ +self-consciousness +self-aware +MDMA +stimming +hyperfixations +lol +fuck-ups +Christelle +ChatGPT +dataset +whitelist +Konsole +nvim +prepends +cheatsheets +TLS +Neovim +keybinds +README +gitignore +autocommand +Dotfiles +dotfiles +Cheatsheet +tmux +fzf +submodule diff --git a/.config/nvim/spell/fr.utf-8.add b/.config/nvim/spell/fr.utf-8.add new file mode 100644 index 0000000..16cfe93 --- /dev/null +++ b/.config/nvim/spell/fr.utf-8.add @@ -0,0 +1,19 @@ +j'apprenne +histrionique +psy +kétamine +addictologue +t'allais +conso +lol +seum +super-héros +niqués +qu'intéragir +hyperfixation +Repens-toi +Jean-Philippe +Christelle +dermatophagie +dit-il +mdr diff --git a/.config/ranger/rc.conf b/.config/ranger/rc.conf new file mode 100644 index 0000000..b0ab044 --- /dev/null +++ b/.config/ranger/rc.conf @@ -0,0 +1,7 @@ +# ranger configuration file +# +# ranger first load a system-wide set of defaults before reading this file +# You can see it with `ranger --copy-config` or at `/usr/share/doc/ranger/config` + +# `gio` is provided by `glib2` +map dT shell gio trash %s diff --git a/.config/tmux/tmux.conf b/.config/tmux/tmux.conf new file mode 100644 index 0000000..967f5c0 --- /dev/null +++ b/.config/tmux/tmux.conf @@ -0,0 +1,31 @@ +# tmux configuration file + +# See `man 1 tmux` +# Ran when tmux's server start (not the client) +# To reload: `source-file ~/.config/tmux/tmux.conf` + +set-option -g default-shell /usr/bin/bash +set-option -g mode-keys vi +set-option -g status-keys vi +set-option -g mouse on +set-option -g status-right-length 50 +set-option -g status-left-length 20 + +bind-key -T root M-: command-prompt +bind-key -T root M-M choose-tree + +bind-key -T root M-T new-window +bind-key -T root M-N next-window +bind-key -T root M-B previous-window +bind-key -T root M-P last-window +bind-key -T prefix < swap-window -d -t -1 +bind-key -T prefix > swap-window -d -t +1 +bind-key -T prefix r command-prompt -I "#W" { rename-window "%%" } +bind-key -T prefix R command-prompt -I "#S" { rename-session "%%" } + +bind-key -T root M-H select-pane -L +bind-key -T root M-J select-pane -D +bind-key -T root M-K select-pane -U +bind-key -T root M-L select-pane -R +bind-key -T root M-S split-window +bind-key -T root M-V split-window -h diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..3136ce6 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule ".local/share/dotfiles/deps/neovim"] + path = .local/share/dotfiles/deps/neovim + url = https://github.com/neovim/neovim diff --git a/.local/bin/dotfiles b/.local/bin/dotfiles new file mode 100755 index 0000000..faf2c1f --- /dev/null +++ b/.local/bin/dotfiles @@ -0,0 +1,104 @@ +#!/usr/bin/bash +set -euo pipefail + +prompt_for_install() { + local label=${1:?label argument missing} + echo "$label" + read -rn 1 -p "Install? (y/n)" input + echo + if [[ $input == y ]]; then return 0; else return 1; fi +} + +clipboard_hint() { + local cmd=${1:?cmd argument missing} + wl-copy "$cmd" + echo -e "Copied to clipboard:\n\n$cmd\n" +} + +sync_deps() { + local missing_deps=() + for dep; do + if ! pacman --query "$dep" >/dev/null 2>&1; then missing_deps+=("$dep"); fi + done + if [[ ${#missing_deps[@]} -eq 0 ]]; then return; fi + if prompt_for_install "Missing dependencies: ${missing_deps[*]}"; then + sudo pacman --sync "${missing_deps[@]}" + fi +} + +sync_mdformat() { + local pipx_json + pipx_json="$(pipx list --json)" + if ! jq --exit-status '.venvs | has("mdformat")' <<<"$pipx_json" >/dev/null 2>&1; then + if prompt_for_install "Missing mdformat"; then pipx install mdformat; else return; fi + fi + local missing_injected_packages=() + for pack in mdformat-gfm mdformat-frontmatter; do + if ! jq --exit-status ".venvs.mdformat.metadata.injected_packages | has(\"$pack\")" <<<"$pipx_json" >/dev/null 2>&1; then + missing_injected_packages+=("$pack") + fi + done + if [[ ${#missing_injected_packages[@]} -eq 0 ]]; then return; fi + if prompt_for_install "Missing mdformat extensions: ${missing_injected_packages[*]}"; then + pipx inject mdformat "${missing_injected_packages[@]}" + fi +} + +build_and_install_nvim() { + local src_dir="$HOME/.local/share/dotfiles/deps/neovim" + local build_dir="$HOME/.local/share/nvim" + rm -rf "$build_dir/build/" + make \ + --directory="$src_dir" \ + CMAKE_BUILD_TYPE=RelWithDebInfo \ + CMAKE_INSTALL_PREFIX="$build_dir" + make --directory="$src_dir" install + ln --symbolic --force "$build_dir/bin/nvim" "$HOME/.local/bin/nvim" +} + +sync_nvim() { + local nvim_dir="$HOME/.local/share/dotfiles/deps/neovim" + local git_args=(--git-dir "$nvim_dir/.git" --work-tree "$nvim_dir") + git "${git_args[@]}" fetch --tags --force >/dev/null + local current_version latest_version + if ! command -v nvim >/dev/null; then + if prompt_for_install "Missing nvim"; then build_and_install_nvim; fi + return + fi + current_version="$(command nvim --version | head -1 | grep -E --only-matching "v[0-9]+\.[0-9]+\.[0-9]+")" + latest_version="$(git "${git_args[@]}" tag --list "v*" --sort=-version:refname | head -1)" + if [[ $current_version == "$latest_version" ]]; then return; fi + if prompt_for_install "New nvim version $current_version → $latest_version"; then + git "${git_args[@]}" checkout "$latest_version" + build_and_install_nvim + fi +} + +case ${1:-} in +sync) + declare deps=( + foot # terminal emulator + tmux # terminal multiplexer + bash # shell + bash-completion # collection of bash completions + fzf # fuzzy finder + ranger # file explorer + bat # syntax highlighter + python-pipx # Python apps environment + wl-clipboard # Wayland clipboard + jq # JSON processor + shfmt # shell program formatter + stylua # Lua formatter + base-devel cmake ninja git # nvim build dependencies + ) + sync_deps "${deps[@]}" + sync_nvim + sync_mdformat + ;; +on) clipboard_hint "source $HOME/.local/share/dotfiles/dotfiles_on.env" ;; +off) clipboard_hint "source $HOME/.local/share/dotfiles/dotfiles_off.env" ;; +*) + echo "What?" + exit 1 + ;; +esac diff --git a/.local/share/dotfiles/README.md b/.local/share/dotfiles/README.md new file mode 100644 index 0000000..8382007 --- /dev/null +++ b/.local/share/dotfiles/README.md @@ -0,0 +1,41 @@ +# dotfiles + +The repository for my configurations, my tools, my setup. Each branch is for a specific environment (OS, distribution, +DE, whatever). It is inspired by [this][0]. + +A dotfiles repository index can be anywhere, but the working tree must be at `HOME`. This is how to start from scratch: + +```bash +git init --bare "$DOTFILES_DIR" +export GIT_DIR="$DOTFILES_DIR" GIT_WORK_TREE="$HOME" +git config --local status.showUntrackedFiles no +``` + +Add any file to the repository: `git add ~/.bashrc`. + +## Setup + +There is one thing to configure not included in the repo (because you can choose `DOTFILES_DIR`): + +```bash +echo "DOTFILES_DIR=$DOTFILES_DIR" >>"$HOME/.config/environment.d/50-dotfiles.conf" +``` + +This variable must be available for other programs to function. See `man 5 environment.d`. Now reboot. That's it! + +## Overview + +- **Terminal emulator**: [foot][1] +- **Terminal multiplexer**: [tmux][2] +- **Shell**: [bash][3] +- **File browser**: [ranger][4] +- **Text editor**: [nvim][5] + +## Links + +[0]: https://www.atlassian.com/git/tutorials/dotfiles +[1]: https://codeberg.org/dnkl/foot#index +[2]: https://github.com/tmux/tmux/wiki +[3]: https://www.gnu.org/software/bash/ +[4]: https://github.com/ranger/ranger +[5]: https://neovim.io/ diff --git a/.local/share/dotfiles/deps/neovim b/.local/share/dotfiles/deps/neovim new file mode 160000 +Subproject 4b35336f6f850ce68a230716401cdaa21bdb6a2 diff --git a/.local/share/dotfiles/dotfiles_off.env b/.local/share/dotfiles/dotfiles_off.env new file mode 100644 index 0000000..949494d --- /dev/null +++ b/.local/share/dotfiles/dotfiles_off.env @@ -0,0 +1 @@ +unset GIT_DIR GIT_WORK_TREE diff --git a/.local/share/dotfiles/dotfiles_on.env b/.local/share/dotfiles/dotfiles_on.env new file mode 100644 index 0000000..f02b9f4 --- /dev/null +++ b/.local/share/dotfiles/dotfiles_on.env @@ -0,0 +1,6 @@ +export GIT_DIR="$DOTFILES_DIR" GIT_WORK_TREE="$HOME" + +function _comp_dotfiles() { + COMPREPLY=(sync on off) +} +complete -F _comp_dotfiles dotfiles |
