Author SHA1 Message Date
Chris a086657031 Treesitter update 2025-12-10 14:19:07 +00:00
36 changed files with 1339 additions and 2276 deletions
-391
View File
@@ -1,391 +0,0 @@
# Bash Cheat Sheet
## Conditionals
```bash
if [[ -z "$string" ]]; then
echo "String is empty"
elif [[ -n "$string" ]]; then
echo "String is not empty"
fi
```
### String conditions
┌────────────────────────┬──────────────────┐
`[[ -z STRING ]]` │ Empty string │
├────────────────────────┼──────────────────┤
`[[ -n STRING ]]` │ Not empty string │
├────────────────────────┼──────────────────┤
`[[ STRING == STRING ]]` │ Equal │
├────────────────────────┼──────────────────┤
`[[ STRING != STRING ]]` │ Not Equal │
├────────────────────────┼──────────────────┤
`[[ STRING =~ STRING ]]` │ Regex │
└────────────────────────┴──────────────────┘
### Number conditions
┌────────────────────────┬───────────────────────┐
`[[ NUM1 -eq NUM2 ]]` │ Equal │
├────────────────────────┼───────────────────────┤
`[[ NUM1 -ne NUM2 ]]` │ Not Equal │
├────────────────────────┼───────────────────────┤
`[[ NUM1 -lt NUM2 ]]` │ Less than │
├────────────────────────┼───────────────────────┤
`[[ NUM1 -le NUM2 ]]` │ Less than or equal │
├────────────────────────┼───────────────────────┤
`[[ NUM1 -gt NUM2 ]]` │ Greater than │
├────────────────────────┼───────────────────────┤
`[[ NUM1 -ge NUM2 ]]` │ Greater than or equal │
├────────────────────────┼───────────────────────┤
`(( NUM < NUM ))` │ Numeric conditions │
└────────────────────────┴───────────────────────┘
### File conditions
┌───────────────────────┬─────────────────────────┐
`[[ -e FILE ]]` │ Exists │
├───────────────────────┼─────────────────────────┤
`[[ -r FILE ]]` │ Readable │
├───────────────────────┼─────────────────────────┤
`[[ -h FILE ]]` │ Symlink │
├───────────────────────┼─────────────────────────┤
`[[ -d FILE ]]` │ Directory │
├───────────────────────┼─────────────────────────┤
`[[ -w FILE ]]` │ Writable │
├───────────────────────┼─────────────────────────┤
`[[ -s FILE ]]` │ Size is > 0 bytes │
├───────────────────────┼─────────────────────────┤
`[[ -f FILE ]]` │ File │
├───────────────────────┼─────────────────────────┤
`[[ -x FILE ]]` │ Executable │
├───────────────────────┼─────────────────────────┤
`[[ FILE1 -nt FILE2 ]]` │ 1 is more recent than 2 │
├───────────────────────┼─────────────────────────┤
`[[ FILE1 -ot FILE2 ]]` │ 2 is more recent than 1 │
├───────────────────────┼─────────────────────────┤
`[[ FILE1 -ef FILE2 ]]` │ Same files │
└───────────────────────┴─────────────────────────┘
### Other conditions
┌────────────────────┬────────────────────┐
`[[ -o noclobber ]]` │ Option enabled │
├────────────────────┼────────────────────┤
`[[ ! EXPR ]]` │ Not │
├────────────────────┼────────────────────┤
`[[ X && Y ]]` │ And │
├────────────────────┼────────────────────┤
`[[ X || Y ]]` │ Or │
└────────────────────┴────────────────────┘
## Parameter manipulation
┌─────────────────────────────────┬────────────────────────────────────────────────────────────────┐
`${foo}` │ Variable │
├─────────────────────────────────┼────────────────────────────────────────────────────────────────┤
`${foo/from/to}` │ Replace first match │
├─────────────────────────────────┼────────────────────────────────────────────────────────────────┤
`${foo/#from/to}`/`${foo/%from/to}` │ Replace first match anchored to start (default) or end │
├─────────────────────────────────┼────────────────────────────────────────────────────────────────┤
`${foo//from/to}` │ Replace all │
├─────────────────────────────────┼────────────────────────────────────────────────────────────────┤
`${foo:start:length}` │ Slice from `start` to `length` (both optional and can be negative) │
├─────────────────────────────────┼────────────────────────────────────────────────────────────────┤
`${foo%suffix}`/`${foo%%suffix}` │ Remove `suffix` (ungreedy/greedy) │
├─────────────────────────────────┼────────────────────────────────────────────────────────────────┤
`${foo#prefix}`/`${foo##prefix}` │ Remove `prefix` (ungreedy/greedy) │
├─────────────────────────────────┼────────────────────────────────────────────────────────────────┤
`${#foo}` │ Length of `foo`
├─────────────────────────────────┼────────────────────────────────────────────────────────────────┤
`${foo,}` │ Lowercase first letter │
├─────────────────────────────────┼────────────────────────────────────────────────────────────────┤
`${foo,,}` │ Lowercase all letters │
├─────────────────────────────────┼────────────────────────────────────────────────────────────────┤
`${foo^}` │ Uppercase first letter │
├─────────────────────────────────┼────────────────────────────────────────────────────────────────┤
`${foo^^}` │ Uppercase all letters │
├─────────────────────────────────┼────────────────────────────────────────────────────────────────┤
`${foo:-val}` │ Use `val` if `foo` is unset or null │
├─────────────────────────────────┼────────────────────────────────────────────────────────────────┤
`${foo:=val}` │ Set `foo` to `val` if unset or null │
├─────────────────────────────────┼────────────────────────────────────────────────────────────────┤
`${foo:+val}` │ Use `val` if `foo` **is** set and not null │
├─────────────────────────────────┼────────────────────────────────────────────────────────────────┤
`${foo:?message}` │ Print `message` and exit if `foo` is unset or null │
└─────────────────────────────────┴────────────────────────────────────────────────────────────────┘
> Omitting the `:` for default values will only check if the variable is set, not if it is null.
### Examples
```bash
name="John"
echo "${name}"
echo "${name/J/j}" #=> "john" (substitution)
echo "${name:0:2}" #=> "Jo" (slicing)
echo "${name::2}" #=> "Jo" (slicing)
echo "${name::-1}" #=> "Joh" (slicing)
echo "${name:(-1)}" #=> "n" (slicing from right)
echo "${name:(-2):1}" #=> "h" (slicing from right)
echo "${food:-Cake}" #=> $food or "Cake"
length=2
echo "${name:0:length}" #=> "Jo" (slicing with variable)
str="/path/to/foo.cpp"
echo "${str%.cpp}" # /path/to/foo
echo "${str%.cpp}.o" # /path/to/foo.o
echo "${str%/*}" # /path/to
echo "${str##*.}" # cpp (extension)
echo "${str##*/}" # foo.cpp (basepath)
echo "${str#*o}" # o/foo.cpp (first occurrence of 'o')
echo "${str##*o}" # oo.cpp (last occurrence of 'o')
echo "${str#*/}" # path/to/foo.cpp
echo "${str##*/}" # foo.cpp
echo "${str/foo/bar}" # /path/to/bar.cpp
str="HELLO WORLD!"
echo "${str,}" #=> "hELLO WORLD!" (lowercase 1st letter)
echo "${str,,}" #=> "hello world!" (all lowercase)
str="hello world!"
echo "${str^}" #=> "Hello world!" (uppercase 1st letter)
echo "${str^^}" #=> "HELLO WORLD!" (all uppercase)
```
## Comments
```bash
# This is a comment
: '
This is a multi-line comment workaround
'
```
## Loops
### Basic for loop
```bash
for i in /etc/rc.*; do
echo "$i"
done
```
### C-style for loop
```bash
for (( i = 0; i < 10; i++ )); do
echo "$i"
done
```
### Ranges
```bash
for i in {1..10}; do
echo "$i"
done
# With step size
for i in {1..10..2}; do
echo "$i"
done
```
### Reading lines
```bash
while IFS= read -r line; do
echo "$line"
done < file.txt
```
### Infinite loop
```bash
while true; do
echo "Press [CTRL+C] to stop.."
sleep 1
done
```
## History
┌───────────────┬────────────────────────────────────────────────────────────────┐
`!!` │ Expand latest command in history │
├───────────────┼────────────────────────────────────────────────────────────────┤
`!$` │ Expand last parameter of latest command │
├───────────────┼────────────────────────────────────────────────────────────────┤
`!*` │ Expand all parameters of latest command │
├───────────────┼────────────────────────────────────────────────────────────────┤
`!-n` │ Expand nth latest command in history │
├───────────────┼────────────────────────────────────────────────────────────────┤
`!n` │ Expand nth command in history │
├───────────────┼────────────────────────────────────────────────────────────────┤
`!string` │ Expand latest command starting with string │
├───────────────┼────────────────────────────────────────────────────────────────┤
`!!:s/from/to` │ Replace first occurrence of "from" with "to" in latest command │
├───────────────┼────────────────────────────────────────────────────────────────┤
`!!:gs/from/to` │ Replace all occurrences of "from" with "to" in latest command │
├───────────────┼────────────────────────────────────────────────────────────────┤
`!!:p` │ Print latest command without executing it │
├───────────────┼────────────────────────────────────────────────────────────────┤
`!$:t` │ Expand basename of last parameter of latest command │
├───────────────┼────────────────────────────────────────────────────────────────┤
`!$:h` │ Expand directory of last parameter of latest command │
├───────────────┼────────────────────────────────────────────────────────────────┤
`!!:n` │ Expand only nth parameter of latest command │
├───────────────┼────────────────────────────────────────────────────────────────┤
`!^` │ Expand first parameter of latest command │
├───────────────┼────────────────────────────────────────────────────────────────┤
`!$` │ Expand last parameter of latest command │
├───────────────┼────────────────────────────────────────────────────────────────┤
`!!:n-m` │ Expand parameters from nth to mth of latest command │
├───────────────┼────────────────────────────────────────────────────────────────┤
`!!:n-$` │ Expand parameters from nth to last of latest command │
└───────────────┴────────────────────────────────────────────────────────────────┘
## Redirection
┌────────────────────────────┬─────────────────────────────────┐
`SCRIPT > FILE` │ stdout to FILE │
├────────────────────────────┼─────────────────────────────────┤
`SCRIPT >> FILE` │ Append stdout to FILE │
├────────────────────────────┼─────────────────────────────────┤
`SCRIPT 2> FILE` │ stderr to FILE │
├────────────────────────────┼─────────────────────────────────┤
`SCRIPT 2>&1`/`SCRIPT &> FILE` │ stderr to stdout │
├────────────────────────────┼─────────────────────────────────┤
`SCRIPT 2> /dev/null` │ stderr to /dev/null │
├────────────────────────────┼─────────────────────────────────┤
`SCRIPT > FILE 2>&1` │ stdout and stderr to FILE │
├────────────────────────────┼─────────────────────────────────┤
`SCRIPT &> /dev/null` │ stdout and stderr to /dev/null │
├────────────────────────────┼─────────────────────────────────┤
`echo "$0: warning" >&2` │ Print to stderr │
├────────────────────────────┼─────────────────────────────────┤
`SCRIPT < FILE` │ stdin from FILE │
└────────────────────────────┴─────────────────────────────────┘
## Switch statement
```bash
case "$1" in
start | up)
vagrant up
;;
*)
echo "Usage: $0 {start|stop|ssh}"
;;
esac
```
### Options switch statement
```bash
while [[ "$1" =~ ^- && ! "$1" == "--" ]]; do case $1 in
-V | --version )
echo "$version"
exit
;;
-s | --string )
shift; string=$1
;;
-f | --flag )
flag=1
;;
esac; shift; done
if [[ "$1" == '--' ]]; then shift; fi
```
## Functions
```bash
my_function() {
echo "Hello, World!"
}
```
### Arguments
┌────┬───────────────────────────────────┐
`$#` │ Number of arguments │
├────┼───────────────────────────────────┤
`$*` │ All arguments │
├────┼───────────────────────────────────┤
`$@` │ All arguments as separate strings │
├────┼───────────────────────────────────┤
`$1` │ First argument │
├────┼───────────────────────────────────┤
`$_` │ Last argument │
└────┴───────────────────────────────────┘
## Arrays
```bash
# Create an array in one line
Fruits=('Apple' 'Banana' 'Orange')
# Or assign each value
Fruits[0]="Apple"
Fruits[1]="Banana"
Fruits[2]="Orange"
# Associative array
declare -A sounds
sounds[dog]="bark"
sounds[cow]="moo"
sounds[bird]="tweet"
sounds[wolf]="howl"
```
### Array element expansion
┌──────────────────────┬────────────────────────────────────────────────────┐
`${array[key]}` │ Element at `key`
├──────────────────────┼────────────────────────────────────────────────────┤
`${array[-1]}` │ Last element (not for associative) │
├──────────────────────┼────────────────────────────────────────────────────┤
`${array[@]}` │ All elements │
├──────────────────────┼────────────────────────────────────────────────────┤
`${#array[@]}` │ Number of elements │
├──────────────────────┼────────────────────────────────────────────────────┤
`${#array}` │ String length of 1st element (not for associative) │
├──────────────────────┼────────────────────────────────────────────────────┤
`${#array[key]}` │ String length of element at `key`
├──────────────────────┼────────────────────────────────────────────────────┤
`${array[@]:n:length}``length` eements from position `n`
├──────────────────────┼────────────────────────────────────────────────────┤
`${!array[@]}` │ Keys of all elements │
└──────────────────────┴────────────────────────────────────────────────────┘
### Operations
```bash
Fruits=("${Fruits[@]}" "Watermelon") # Push
Fruits+=('Watermelon') # Also Push
Fruits=( "${Fruits[@]/Ap*/}" ) # Remove by regex match
unset Fruits[2] # Remove one item
Fruits=("${Fruits[@]}") # Duplicate
Fruits=("${Fruits[@]}" "${Veggies[@]}") # Concatenate
words=($(< datafile)) # From file (split by IFS)
```
### Looping
```bash
for i in "${Fruits[@]}"; do
echo "$i"
done
for key in "${!sounds[@]}"; do
echo "$key"
done
```
## Misc
### Directory of script
```bash
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
```
### Special variables
┌──────────────────┬────────────────────────────────────────┐
`$?` │ Exit status of last task │
├──────────────────┼────────────────────────────────────────┤
`$!` │ PID of last background task │
├──────────────────┼────────────────────────────────────────┤
`$$` │ PID of shell │
├──────────────────┼────────────────────────────────────────┤
`$0` │ Filename of the shell script │
├──────────────────┼────────────────────────────────────────┤
`$_` │ Last arugment of altest command │
├──────────────────┼────────────────────────────────────────┤
`${PIPESTATUS[n]}` │ Return value of piped commands (array) │
└──────────────────┴────────────────────────────────────────┘
-205
View File
@@ -1,205 +0,0 @@
# Regex Cheat Sheet
## Anchors
┌────┬─────────────────────────────────────────────────────────┐
│ ^ │ Start of string, or start of line in multi-line pattern │
├────┼─────────────────────────────────────────────────────────┤
│ \A │ Start of string │
├────┼─────────────────────────────────────────────────────────┤
│ $ │ End of string, or end of line in multi-line pattern │
├────┼─────────────────────────────────────────────────────────┤
│ \Z │ End of string │
├────┼─────────────────────────────────────────────────────────┤
│ \b │ Word boundary │
├────┼─────────────────────────────────────────────────────────┤
│ \B │ Not word boundary │
├────┼─────────────────────────────────────────────────────────┤
│ \< │ Start of word │
├────┼─────────────────────────────────────────────────────────┤
│ \> │ End of word │
└────┴─────────────────────────────────────────────────────────┘
## Character Classes
┌────┬────────────────────┐
│ \c │ Control character │
├────┼────────────────────┤
│ \s │ White space │
├────┼────────────────────┤
│ \S │ Not white space │
├────┼────────────────────┤
│ \d │ Digit │
├────┼────────────────────┤
│ \D │ Not digit │
├────┼────────────────────┤
│ \w │ Word │
├────┼────────────────────┤
│ \W │ Not word │
├────┼────────────────────┤
│ \x │ Hexadecimal digit │
├────┼────────────────────┤
│ \O │ Octal digit │
└────┴────────────────────┘
## POSIX
┌───────────┬───────────────────────────────┐
`[:upper:]` │ Upper case letters │
├───────────┼───────────────────────────────┤
`[:lower:]` │ Lower case letters │
├───────────┼───────────────────────────────┤
`[:alpha:]` │ All letters │
├───────────┼───────────────────────────────┤
`[:alnum:]` │ Digits and letters │
├───────────┼───────────────────────────────┤
`[:digit:]` │ Digits │
├───────────┼───────────────────────────────┤
`[:xdigit:]`│ Hexadecimal digits │
├───────────┼───────────────────────────────┤
`[:punct:]` │ Punctuation │
├───────────┼───────────────────────────────┤
`[:blank:]` │ Space and tab │
├───────────┼───────────────────────────────┤
`[:space:]` │ Blank characters │
├───────────┼───────────────────────────────┤
`[:cntrl:]` │ Control characters │
├───────────┼───────────────────────────────┤
`[:graph:]` │ Printed characters │
├───────────┼───────────────────────────────┤
`[:print:]` │ Printed characters and spaces │
├───────────┼───────────────────────────────┤
`[:word:]` │ Digits, letters and underscore│
└───────────┴───────────────────────────────┘
## Assertions
┌──────┬──────────────────────────────┐
│ ?= │ Lookahead assertion │
├──────┼──────────────────────────────┤
│ ?! │ Negative lookahead │
├──────┼──────────────────────────────┤
│ ?<= │ Lookbehind assertion │
├──────┼──────────────────────────────┤
│ ?!= │ Negative lookbehind │
│ ?<! │ │
├──────┼──────────────────────────────┤
│ ?> │ Once-only subexpression │
├──────┼──────────────────────────────┤
│ ?() │ Condition `[if then]`
├──────┼──────────────────────────────┤
│ ?()| │ Condition `[if then else]`
├──────┼──────────────────────────────┤
│ ?# │ Comment │
└──────┴──────────────────────────────┘
## Quantifiers
┌───────┬─────────────┐
│ * │ 0 or more │
├───────┼─────────────┤
│ + │ 1 or more │
├───────┼─────────────┤
│ ? │ 0 or 1 │
├───────┼─────────────┤
│ {3} │ Exactly 3 │
├───────┼─────────────┤
│ {3,} │ 3 or more │
├───────┼─────────────┤
│ {3,5} │ 3, 4 or 5 │
└───────┴─────────────┘
> Add a `?` to a quantifier to make it ungreedy.
## Escape Sequences
┌────┬─────────────────────────────┐
│ \ │ Escape following character │
├────┼─────────────────────────────┤
│ \Q │ Begin literal sequence │
├────┼─────────────────────────────┤
│ \E │ End literal sequence │
└────┴─────────────────────────────┘
> "Escaping" is a way of treating characters which have a special meaning in regular expressions literally, rather than as special characters.
## Common Metacharacters
┌───┬───┬───┬───┐
│ ^ │ [ │ . │ $ │
├───┼───┼───┼───┤
│ { │ * │ ( │ \ │
├───┼───┼───┼───┤
│ + │ ) │ | │ ? │
├───┼───┼───┼───┤
│ < │ > │ │ │
└───┴───┴───┴───┘
> The escape character is usually `\`.
## Special Characters
┌──────┬────────────────────┐
│ \n │ New line │
├──────┼────────────────────┤
│ \r │ Carriage return │
├──────┼────────────────────┤
│ \t │ Tab │
├──────┼────────────────────┤
│ \v │ Vertical tab │
├──────┼────────────────────┤
│ \f │ Form feed │
├──────┼────────────────────┤
│ \xxx │ Octal character xxx│
├──────┼────────────────────┤
│ \xhh │ Hex character hh │
└──────┴────────────────────┘
## Groups and Ranges
┌─────────┬────────────────────────────────────┐
│ . │ Any character except new line (\n) │
├─────────┼────────────────────────────────────┤
│ (a|b) │ a or b │
├─────────┼────────────────────────────────────┤
│ (...) │ Group │
├─────────┼────────────────────────────────────┤
│ (?:...) │ Passive (non-capturing) group │
├─────────┼────────────────────────────────────┤
`[abc]` │ Range (a or b or c) │
├─────────┼────────────────────────────────────┤
`[^abc]` │ Not (a or b or c) │
├─────────┼────────────────────────────────────┤
`[a-q]` │ Lower case letter from a to q │
├─────────┼────────────────────────────────────┤
`[A-Q]` │ Upper case letter from A to Q │
├─────────┼────────────────────────────────────┤
`[0-7]` │ Digit from 0 to 7 │
├─────────┼────────────────────────────────────┤
│ \x │ Group/subpattern number "x" │
└─────────┴────────────────────────────────────┘
> Ranges are inclusive.
## Pattern Modifiers
┌────┬────────────────────────────────────┐
│ g │ Global match │
├────┼────────────────────────────────────┤
│ i │ Case-insensitive │
├────┼────────────────────────────────────┤
│ m │ Multiple lines │
├────┼────────────────────────────────────┤
│ s │ Treat string as single line │
├────┼────────────────────────────────────┤
│ x │ Allow comments and whitespace │
├────┼────────────────────────────────────┤
│ e │ Evaluate replacement │
├────┼────────────────────────────────────┤
│ U │ Ungreedy pattern │
└────┴────────────────────────────────────┘
> * PCRE modifier
## String Replacement
┌────┬──────────────────────────────────┐
`$n` │ nth non-passive group │
├────┼──────────────────────────────────┤
`$2` │ "xyz" in `/^(abc(xyz))$/`
├────┼──────────────────────────────────┤
`$1` │ "xyz" in `/^(?:abc)(xyz)$/`
├────┼──────────────────────────────────┤
`$`` │ Before matched string │
├────┼──────────────────────────────────┤
│ `$'` │ After matched string │
├────┼──────────────────────────────────┤
│ `$+` │ Last matched string │
├────┼──────────────────────────────────┤
│ `$&` │ Entire matched string │
└────┴──────────────────────────────────┘
> Some regex implementations use `\` instead of `$`.
-437
View File
@@ -1,437 +0,0 @@
# ZSH Cheat Sheet
## Conditionals
```zsh
if [[ -z "$string" ]]; then
echo "String is empty"
elif [[ -n "$string" ]]; then
echo "String is not empty"
fi
```
### String conditions
┌────────────────────────┬──────────────────┐
`[[ -z STRING ]]` │ Empty string │
├────────────────────────┼──────────────────┤
`[[ -n STRING ]]` │ Not empty string │
├────────────────────────┼──────────────────┤
`[[ STRING == STRING ]]` │ Equal │
├────────────────────────┼──────────────────┤
`[[ STRING != STRING ]]` │ Not Equal │
├────────────────────────┼──────────────────┤
`[[ STRING =~ STRING ]]` │ Regex │
└────────────────────────┴──────────────────┘
### Number conditions
┌────────────────────────┬───────────────────────┐
`[[ NUM1 -eq NUM2 ]]` │ Equal │
├────────────────────────┼───────────────────────┤
`[[ NUM1 -ne NUM2 ]]` │ Not Equal │
├────────────────────────┼───────────────────────┤
`[[ NUM1 -lt NUM2 ]]` │ Less than │
├────────────────────────┼───────────────────────┤
`[[ NUM1 -le NUM2 ]]` │ Less than or equal │
├────────────────────────┼───────────────────────┤
`[[ NUM1 -gt NUM2 ]]` │ Greater than │
├────────────────────────┼───────────────────────┤
`[[ NUM1 -ge NUM2 ]]` │ Greater than or equal │
├────────────────────────┼───────────────────────┤
`(( NUM < NUM ))` │ Numeric conditions │
└────────────────────────┴───────────────────────┘
### File conditions
┌───────────────────────┬─────────────────────────┐
`[[ -e FILE ]]` │ Exists │
├───────────────────────┼─────────────────────────┤
`[[ -r FILE ]]` │ Readable │
├───────────────────────┼─────────────────────────┤
`[[ -h FILE ]]` │ Symlink │
├───────────────────────┼─────────────────────────┤
`[[ -d FILE ]]` │ Directory │
├───────────────────────┼─────────────────────────┤
`[[ -w FILE ]]` │ Writable │
├───────────────────────┼─────────────────────────┤
`[[ -s FILE ]]` │ Size is > 0 bytes │
├───────────────────────┼─────────────────────────┤
`[[ -f FILE ]]` │ File │
├───────────────────────┼─────────────────────────┤
`[[ -x FILE ]]` │ Executable │
├───────────────────────┼─────────────────────────┤
`[[ FILE1 -nt FILE2 ]]` │ 1 is more recent than 2 │
├───────────────────────┼─────────────────────────┤
`[[ FILE1 -ot FILE2 ]]` │ 2 is more recent than 1 │
├───────────────────────┼─────────────────────────┤
`[[ FILE1 -ef FILE2 ]]` │ Same files │
└───────────────────────┴─────────────────────────┘
### Other conditions
┌────────────────────┬────────────────────┐
`[[ -o noclobber ]]` │ Option enabled │
├────────────────────┼────────────────────┤
`[[ ! EXPR ]]` │ Not │
├────────────────────┼────────────────────┤
`[[ X && Y ]]` │ And │
├────────────────────┼────────────────────┤
`[[ X || Y ]]` │ Or │
└────────────────────┴────────────────────┘
## Parameter manipulation
┌─────────────────────────────────┬────────────────────────────────────────────────────────────────┐
`${foo}` │ Variable │
├─────────────────────────────────┼────────────────────────────────────────────────────────────────┤
`${foo/from/to}` │ Replace first match │
├─────────────────────────────────┼────────────────────────────────────────────────────────────────┤
`${foo/#from/to}`/`${foo/%from/to}` │ Replace first match anchored to start (default) or end │
├─────────────────────────────────┼────────────────────────────────────────────────────────────────┤
`${foo//from/to}` │ Replace all │
├─────────────────────────────────┼────────────────────────────────────────────────────────────────┤
`${foo[start,length]}` │ Slice from `start` to `length` (`length` can be negative) │
├─────────────────────────────────┼────────────────────────────────────────────────────────────────┤
`${foo%suffix}`/`${foo%%suffix}` │ Remove `suffix` (ungreedy/greedy) │
├─────────────────────────────────┼────────────────────────────────────────────────────────────────┤
`${foo#prefix}`/`${foo##prefix}` │ Remove `prefix` (ungreedy/greedy) │
├─────────────────────────────────┼────────────────────────────────────────────────────────────────┤
`${#foo}` │ Length of `foo`
├─────────────────────────────────┼────────────────────────────────────────────────────────────────┤
`${(L)foo}` │ Lowercase all letters │
├─────────────────────────────────┼────────────────────────────────────────────────────────────────┤
`${(U)foo}` │ Uppercase all letters │
├─────────────────────────────────┼────────────────────────────────────────────────────────────────┤
`${(q)foo}` │ Quote safely │
├─────────────────────────────────┼────────────────────────────────────────────────────────────────┤
`${(qq)foo}` │ Quote more explicitely │
├─────────────────────────────────┼────────────────────────────────────────────────────────────────┤
`${(s.del.)foo}` │ Split on `del`
├─────────────────────────────────┼────────────────────────────────────────────────────────────────┤
`${(j.del.)foo}` │ Join with `del`
├─────────────────────────────────┼────────────────────────────────────────────────────────────────┤
`${file:h}` │ Directory of `file`
├─────────────────────────────────┼────────────────────────────────────────────────────────────────┤
`${file:t}` │ Filename of `file`
├─────────────────────────────────┼────────────────────────────────────────────────────────────────┤
`${file:e}` │ Extension of `file`
├─────────────────────────────────┼────────────────────────────────────────────────────────────────┤
`${file:r}` │ Root of `file` (without extension) │
├─────────────────────────────────┼────────────────────────────────────────────────────────────────┤
`${file:a}` │ Absolute path of `file`
├─────────────────────────────────┼────────────────────────────────────────────────────────────────┤
`${file:A}` │ Absolute path of `file` (resolves symlinks) │
├─────────────────────────────────┼────────────────────────────────────────────────────────────────┤
`${foo:-val}` │ Use `val` if `foo` is unset or null │
├─────────────────────────────────┼────────────────────────────────────────────────────────────────┤
`${foo:=val}` │ Set `foo` to `val` if unset or null │
├─────────────────────────────────┼────────────────────────────────────────────────────────────────┤
`${foo:+val}` │ Use `val` if `foo` **is** set and not null │
├─────────────────────────────────┼────────────────────────────────────────────────────────────────┤
`${foo:?message}` │ Print `message` and exit if `foo` is unset or null │
└─────────────────────────────────┴────────────────────────────────────────────────────────────────┘
> Omitting the `:` for default values will only check if the variable is set, not if it is null.
### Examples
```zsh
name="John"
echo "${name}"
echo "${name/J/j}" #=> "john" (substitution)
echo "${name[0,2]}" #=> "Jo" (slicing)
echo "${food:-Cake}" #=> $food or "Cake"
length=2
echo "${name[1,length]}" #=> "Jo" (slicing with variable)
str="/path/to/foo.cpp"
echo "${str%.cpp}" # /path/to/foo
echo "${str%.cpp}.o" # /path/to/foo.o
echo "${str%/*}" # /path/to
echo "${str##*.}" # cpp (extension)
echo "${str##*/}" # foo.cpp (basepath)
echo "${str#*o}" # o/foo.cpp (first occurrence of 'o')
echo "${str##*o}" # oo.cpp (last occurrence of 'o')
echo "${str#*/}" # path/to/foo.cpp
echo "${str##*/}" # foo.cpp
echo "${str/foo/bar}" # /path/to/bar.cpp
str="HELLO WORLD!"
echo "${(L)str}" #=> "hello world!" (all lowercase)
str="hello world!"
echo "${(U)str}" #=> "HELLO WORLD!" (all uppercase)
```
## Globs
┌───────────────┬───────────────────────┐
`ls **/*.js` │ Recursive expansion │
├───────────────┼───────────────────────┤
`ls *(.)` │ Plain files │
├───────────────┼───────────────────────┤
`ls *(/)` │ Directories │
├───────────────┼───────────────────────┤
`ls *(@)` │ Symlinks │
├───────────────┼───────────────────────┤
`ls *(*)` │ Executables │
├───────────────┼───────────────────────┤
`ls *(L+1M)` │ Files larger than 1MB │
├───────────────┼───────────────────────┤
`ls *(om[1,5])` │ 5 newest files │
├───────────────┼───────────────────────┤
`ls *(.)~*.bak` │ Files except *.bak │
└───────────────┴───────────────────────┘
## Comments
```zsh
# This is a comment
: '
This is a multi-line comment workaround
'
```
## Loops
### Basic for loop
```zsh
for i in /etc/rc.*; do
echo "$i"
done
```
### C-style for loop
```zsh
for (( i = 0; i < 10; i++ )); do
echo "$i"
done
```
### Ranges
```zsh
for i in {1..10}; do
echo "$i"
done
# With step size
for i in {1..10..2}; do
echo "$i"
done
```
### Reading lines
```zsh
while IFS= read -r line; do
print -r -- "$line"
done < file.txt
```
### Infinite loop
```zsh
while true; do
echo "Press [CTRL+C] to stop.."
sleep 1
done
```
## History
┌───────────────┬────────────────────────────────────────────────────────────────┐
`!!` │ Expand latest command in history │
├───────────────┼────────────────────────────────────────────────────────────────┤
`!$` │ Expand last parameter of latest command │
├───────────────┼────────────────────────────────────────────────────────────────┤
`!*` │ Expand all parameters of latest command │
├───────────────┼────────────────────────────────────────────────────────────────┤
`!-n` │ Expand nth latest command in history │
├───────────────┼────────────────────────────────────────────────────────────────┤
`!n` │ Expand nth command in history │
├───────────────┼────────────────────────────────────────────────────────────────┤
`!string` │ Expand latest command starting with string │
├───────────────┼────────────────────────────────────────────────────────────────┤
`!!:s/from/to` │ Replace first occurrence of "from" with "to" in latest command │
├───────────────┼────────────────────────────────────────────────────────────────┤
`!!:gs/from/to` │ Replace all occurrences of "from" with "to" in latest command │
├───────────────┼────────────────────────────────────────────────────────────────┤
`!!:p` │ Print latest command without executing it │
├───────────────┼────────────────────────────────────────────────────────────────┤
`!$:t` │ Expand basename of last parameter of latest command │
├───────────────┼────────────────────────────────────────────────────────────────┤
`!$:h` │ Expand directory of last parameter of latest command │
├───────────────┼────────────────────────────────────────────────────────────────┤
`!!:n` │ Expand only nth parameter of latest command │
├───────────────┼────────────────────────────────────────────────────────────────┤
`!^` │ Expand first parameter of latest command │
├───────────────┼────────────────────────────────────────────────────────────────┤
`!$` │ Expand last parameter of latest command │
├───────────────┼────────────────────────────────────────────────────────────────┤
`!!:n-m` │ Expand parameters from nth to mth of latest command │
├───────────────┼────────────────────────────────────────────────────────────────┤
`!!:n-$` │ Expand parameters from nth to last of latest command │
└───────────────┴────────────────────────────────────────────────────────────────┘
## Redirection
┌────────────────────────┬─────────────────────────────────┐
`SCRIPT > FILE` │ stdout to FILE │
├────────────────────────┼─────────────────────────────────┤
`SCRIPT >> FILE` │ Append stdout to FILE │
├────────────────────────┼─────────────────────────────────┤
`SCRIPT 2> FILE` │ stderr to FILE │
├────────────────────────┼─────────────────────────────────┤
`SCRIPT 2>&1` │ stderr to stdout │
├────────────────────────┼─────────────────────────────────┤
`SCRIPT &> FILE` │ stderr and stdout to FILE │
├────────────────────────┼─────────────────────────────────┤
`SCRIPT 2> /dev/null` │ stderr to /dev/null │
├────────────────────────┼─────────────────────────────────┤
`SCRIPT > FILE 2>&1` │ stdout and stderr to FILE │
├────────────────────────┼─────────────────────────────────┤
`SCRIPT &> /dev/null` │ stdout and stderr to /dev/null │
├────────────────────────┼─────────────────────────────────┤
`echo "$0: warning" >&2` │ Print to stderr │
├────────────────────────┼─────────────────────────────────┤
`SCRIPT < FILE` │ stdin from FILE │
└────────────────────────┴─────────────────────────────────┘
## Switch statement
```zsh
case "$1" in
start | up)
vagrant up
;;
*)
echo "Usage: $0 {start|stop|ssh}"
;;
esac
```
### Options switch statement
```zsh
while [[ "$1" =~ ^- && ! "$1" == "--" ]]; do case $1 in
-V | --version )
echo "$version"
exit
;;
-s | --string )
shift; string=$1
;;
-f | --flag )
flag=1
;;
esac; shift; done
if [[ "$1" == '--' ]]; then shift; fi
```
## Functions
```zsh
my_function() {
echo "Hello, World!"
}
```
### Arguments
┌────┬───────────────────────────────────┐
`$#` │ Number of arguments │
├────┼───────────────────────────────────┤
`$*` │ All arguments │
├────┼───────────────────────────────────┤
`$@` │ All arguments as separate strings │
├────┼───────────────────────────────────┤
`$1` │ First argument │
├────┼───────────────────────────────────┤
`$_` │ Last argument │
└────┴───────────────────────────────────┘
## Arrays
```zsh
# Create an array in one line
Fruits=('Apple' 'Banana' 'Orange')
# Or assign each value
Fruits[1]="Apple"
Fruits[2]="Banana"
Fruits[3]="Orange"
# Associative array
typeset -A sounds
sounds[dog]="bark"
sounds[cow]="moo"
sounds[bird]="tweet"
sounds[wolf]="howl"
```
### Array element expansion
┌──────────────────────────────────────┬────────────────────────────────────┐
`${array[key]}` │ Element at `key`
├──────────────────────────────────────┼────────────────────────────────────┤
`${array[-1]}` │ Last element (not for associative) │
├──────────────────────────────────────┼────────────────────────────────────┤
`${array[@]}` │ All elements │
├──────────────────────────────────────┼────────────────────────────────────┤
`${#array}` │ Number of elements │
├──────────────────────────────────────┼────────────────────────────────────┤
`${#array[key]}` │ String length of element at `key`
├──────────────────────────────────────┼────────────────────────────────────┤
`${array:n:length}`/`${array[n,length]}``length` eements from position `n`
├──────────────────────────────────────┼────────────────────────────────────┤
`${(k)array}` │ Keys of all elements │
└──────────────────────────────────────┴────────────────────────────────────┘
### Operations
```zsh
Fruits=("${Fruits[@]}" "Watermelon") # Push
Fruits+=('Watermelon') # Also Push
Fruits=( "${Fruits[@]/Ap*/}" ) # Remove by regex match
unset Fruits[3] # Remove one item
Fruits=("${Fruits[@]}") # Duplicate
Fruits=("${Fruits[@]}" "${Veggies[@]}") # Concatenate
words=($(< datafile)) # From file (split by IFS)
```
### Looping
```zsh
for i in "${Fruits[@]}"; do
echo "$i"
done
for val in "${(v)sounds}"; do
echo "$val"
done
for key in "${(k)sounds}"; do
echo "$key"
done
```
## Misc
### Directory of script
```zsh
DIR="$( cd "$( dirname "${ZSH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
```
### Special variables
┌──────────────────┬───────────────────────────────────────────────┐
`$?` │ Exit status of last task │
├──────────────────┼───────────────────────────────────────────────┤
`$!` │ PID of last background task │
├──────────────────┼───────────────────────────────────────────────┤
`$$` │ PID of shell │
├──────────────────┼───────────────────────────────────────────────┤
`$#` │ Number of arguments passed to script │
├──────────────────┼───────────────────────────────────────────────┤
`$0` │ Filename of the shell script │
├──────────────────┼───────────────────────────────────────────────┤
`$_` │ Last arugment of altest command │
├──────────────────┼───────────────────────────────────────────────┤
`$argv` │ Array of all arguments passed to script │
├──────────────────┼───────────────────────────────────────────────┤
`$ARGC` │ Number of arguments passed to script │
├──────────────────┼───────────────────────────────────────────────┤
`${pipestatus[n]}` │ Return value of piped commands (array) │
├──────────────────┼───────────────────────────────────────────────┤
`$RANDOM` │ Random number between 0 and 32767 │
├──────────────────┼───────────────────────────────────────────────┤
`$SECONDS` │ Number of seconds since the shell was started │
├──────────────────┼───────────────────────────────────────────────┤
`$PWD` │ Current working directory │
├──────────────────┼───────────────────────────────────────────────┤
`$OLDPWD` │ Previous working directory │
└──────────────────┴───────────────────────────────────────────────┘
+114
View File
@@ -0,0 +1,114 @@
{
"name": "Neovim Configuration",
"version": "1.0.0",
"system_prompt": "You are an expert Neovim configuration assistant. Help the user understand and modify their Neovim setup. Focus on clear explanations and suggest improvements when appropriate.",
"vars": {
"lua_dir": "lua"
},
"groups": [
{
"name": "Core Configuration",
"system_prompt": "These files define the core Neovim behavior including options, keymaps, and autocommands. When suggesting changes, ensure they align with Neovim best practices.",
"data": [
"core_opt",
"core_keymap",
"core_autocmd",
"core_helpers"
]
},
{
"name": "Plugin Management",
"system_prompt": "This file contains the plugin manager setup. Help the user understand plugin dependencies and installation patterns.",
"data": [
"lazy_init"
]
},
{
"name": "LSP Configuration",
"system_prompt": "These files define the Language Server Protocol setup. Help the user configure language servers, diagnostics, and code actions.",
"data": [
"lsp_config"
]
},
{
"name": "Frequently Used Plugins",
"system_prompt": "These are plugins the user frequently configures. Provide detailed explanations about their options and how they interact with the rest of the configuration.",
"data": [
"plugin_codecompanion",
"plugin_edgy",
"plugin_snacks",
"plugin_mini",
"plugin_telescope"
]
},
{
"name": "UI and Theme",
"system_prompt": "These files define the visual appearance of Neovim. Help the user understand how to customize colors and UI elements.",
"data": [
"plugin_colorscheme"
]
}
],
"data": {
"core_opt": {
"type": "file",
"path": "${lua_dir}/opt.lua",
"description": "Neovim options configuration"
},
"core_keymap": {
"type": "file",
"path": "${lua_dir}/keymap.lua",
"description": "Key mappings configuration"
},
"core_autocmd": {
"type": "file",
"path": "${lua_dir}/autocmd.lua",
"description": "Automatic commands configuration"
},
"core_helpers": {
"type": "file",
"path": "${lua_dir}/helpers.lua",
"description": "Helper functions for Neovim configuration"
},
"lazy_init": {
"type": "file",
"path": "${lua_dir}/lazy_init.lua",
"description": "Lazy plugin manager initialization"
},
"lsp_config": {
"type": "file",
"path": "${lua_dir}/plugins/lsp.lua",
"description": "LSP plugins and configuration"
},
"plugin_codecompanion": {
"type": "file",
"path": "${lua_dir}/plugins/codecompanion.lua",
"description": "CodeCompanion plugin configuration"
},
"plugin_edgy": {
"type": "file",
"path": "${lua_dir}/plugins/edgy.lua",
"description": "Edgy plugin configuration"
},
"plugin_snacks": {
"type": "file",
"path": "${lua_dir}/plugins/snacks.lua",
"description": "Snacks plugin configuration"
},
"plugin_mini": {
"type": "file",
"path": "${lua_dir}/plugins/mini.lua",
"description": "Mini plugins configuration"
},
"plugin_telescope": {
"type": "file",
"path": "${lua_dir}/plugins/telescope.lua",
"description": "Telescope fuzzy finder configuration"
},
"plugin_colorscheme": {
"type": "file",
"path": "${lua_dir}/plugins/color.lua",
"description": "Color scheme and UI appearance configuration"
}
}
}
-2
View File
@@ -1,7 +1,6 @@
-- Set <space> as the leader key -- Set <space> as the leader key
-- See `:help mapleader` -- See `:help mapleader`
-- NOTE: Must happen before plugins are loaded (otherwise wrong leader will be used) -- NOTE: Must happen before plugins are loaded (otherwise wrong leader will be used)
vim.lsp.log.set_level 'debug'
vim.g.mapleader = ',' vim.g.mapleader = ','
vim.g.maplocalleader = ',' vim.g.maplocalleader = ','
@@ -63,4 +62,3 @@ TODO: Neovim configurations I want to add:
-- " - To operate on multiple lines use <c-v> to select the lines, <c-i> to go to -- " - To operate on multiple lines use <c-v> to select the lines, <c-i> to go to
-- " insert mode, perform edit, press <esc>. -- " insert mode, perform edit, press <esc>.
-- " - Use the command :r to paste the output of a command to the buffer. -- " - Use the command :r to paste the output of a command to the buffer.
+25 -111
View File
@@ -18,14 +18,12 @@ vim.api.nvim_create_autocmd('FocusLost', {
group = autosave_group, group = autosave_group,
pattern = '*', pattern = '*',
command = 'silent! wa', -- Save all files when moving away from the window command = 'silent! wa', -- Save all files when moving away from the window
nested = true, -- Allow other autocommands to run after this one
}) })
vim.api.nvim_create_autocmd('BufLeave', { vim.api.nvim_create_autocmd('InsertLeave', {
group = autosave_group, group = autosave_group,
pattern = '*', pattern = '*',
command = 'silent! wa', -- Save all files when switching buffers command = 'silent! wa', -- Save all files when leaving insert mode
nested = true, -- Allow other autocommands to run after this one
}) })
vim.api.nvim_create_autocmd('SwapExists', { vim.api.nvim_create_autocmd('SwapExists', {
@@ -94,13 +92,6 @@ vim.api.nvim_create_autocmd({ 'User' }, {
-- end, -- end,
-- }) -- })
vim.api.nvim_create_autocmd({ 'BufWritePre' }, {
pattern = { '*.lua' },
callback = function(args)
require('helpers').format_buffer('stylua', args.buf)
end,
})
local modes_group = vim.api.nvim_create_augroup('modes', { clear = true }) local modes_group = vim.api.nvim_create_augroup('modes', { clear = true })
vim.api.nvim_create_autocmd('FocusLost', { vim.api.nvim_create_autocmd('FocusLost', {
group = modes_group, group = modes_group,
@@ -125,110 +116,33 @@ vim.api.nvim_create_autocmd('BufWritePost', {
end, end,
}) })
-- Run shortcuts script when saving shortcut files vim.api.nvim_create_autocmd('FileType', {
vim.api.nvim_create_autocmd('BufWritePost', {
pattern = { pattern = {
os.getenv 'XDG_CONFIG_HOME' .. '/shell/bm-files', 'bash',
os.getenv 'XDG_CONFIG_HOME' .. '/shell/bm-dirs', 'c',
'diff',
'html',
'lua',
'luadoc',
'markdown',
'markdown_inline',
'query',
'vim',
'vimdoc',
'php',
'javascript',
'typescript',
'json',
}, },
desc = 'Run shortcuts script',
callback = function() callback = function()
os.execute 'station-shortcuts' -- syntax highlighting, provided by Neovim
vim.notify('Shortcuts updated!', vim.log.levels.INFO) vim.treesitter.start()
end, -- folds, provided by Neovim
}) vim.wo.foldexpr = 'v:lua.vim.treesitter.foldexpr()'
vim.wo.foldmethod = 'expr'
local configs = { -- indentation, provided by nvim-treesitter
{ vim.bo.indentexpr = "v:lua.require'nvim-treesitter'.indentexpr()"
path = os.getenv 'HOME' .. '/.config/nixos',
worktree = nil,
git_dir = nil,
track_untracked = false,
force_add = false,
},
{
path = os.getenv 'HOME' .. '/.config/nvim',
worktree = nil,
git_dir = nil,
track_untracked = false,
force_add = false,
},
{
path = os.getenv 'HOME',
worktree = os.getenv 'HOME',
git_dir = os.getenv 'HOME' .. '/.config/dotfiles/.git',
track_untracked = true,
force_add = true, -- Handles ignored files in dotfiles
include_dirs = { '.config', '.local/bin', '.local/share' },
},
}
local function get_git_status(config)
local base = 'git'
if config.git_dir and config.worktree then
base = string.format('git --git-dir=%s --worktree=%s', config.git_dir, config.worktree)
end
-- Check modified and untracked (including ignored if force_add is true)
local cmd = base .. ' status --porcelain'
if config.force_add then
cmd = cmd .. ' --ignored'
end
local handle = io.popen(cmd)
local result = handle:read '*a'
handle:close()
local has_changes = false
for line in result:gmatch '[^\r\n]+' do
local status = line:sub(1, 2)
local file = line:sub(4)
if status:match '[MAR]' then
has_changes = true
elseif (status == '??' or status == '!!') and config.track_untracked then
if config.include_dirs then
for _, dir in ipairs(config.include_dirs) do
if file:sub(1, #dir) == dir then
has_changes = true
break
end
end
else
has_changes = true
end
end
end
return has_changes, base
end
vim.api.nvim_create_autocmd('VimLeavePre', {
callback = function()
local cwd = vim.fn.getcwd()
for _, config in ipairs(configs) do
if cwd:sub(1, #config.path) == config.path then
local has_changes, git_base = get_git_status(config)
if has_changes then
local confirm = vim.fn.confirm('Uncommitted changes in managed path. Commit?', '&Yes\n&No', 2)
if confirm == 1 then
local msg = vim.fn.input('Commit message: ', 'chore: update files')
if msg ~= '' then
msg = 'Update files'
end
local add_cmd = ' add -A'
os.execute(git_base .. add_cmd)
os.execute(git_base .. ' commit -m ' .. vim.fn.shellescape(msg))
os.execute(git_base .. ' push')
print '\nChanges committed.'
end
end
break
end
end
end, end,
}) })
require('helpers').edit_cf('a', '/lua/autocmd.lua') require('helpers').edit_cf('a', '/lua/autocmd.lua')
+9 -124
View File
@@ -16,28 +16,17 @@ end
helpers.edit_cf('h', '/lua/helpers.lua') helpers.edit_cf('h', '/lua/helpers.lua')
---@param opts? { cmd?: string, win_opts?: table, buf_opts?: table } ---@param opts? { cmd?: string }
helpers.open_term = function(opts, buf_opts) helpers.open_term = function(opts)
opts = opts or {} opts = opts or { cmd = '' }
local cmd = opts.cmd or ''
local win_opts = opts.win_opts
local buf_options = buf_opts or opts.buf_opts or {
bufhidden = 'wipe',
modifiable = false,
}
local buf = vim.api.nvim_create_buf(false, true) local buf = vim.api.nvim_create_buf(false, true)
if buf_options.name then vim.api.nvim_set_option_value('bufhidden', 'wipe', { buf = buf })
vim.api.nvim_buf_set_name(buf, buf_options.name) vim.api.nvim_set_option_value('modifiable', false, { buf = buf })
buf_options.name = nil
end
for k, v in pairs(buf_options) do
vim.api.nvim_set_option_value(k, v, { buf = buf })
end
local height = math.ceil(vim.o.lines * 0.9) local height = math.ceil(vim.o.lines * 0.9)
local width = math.ceil(vim.o.columns * 0.9) local width = math.ceil(vim.o.columns * 0.9)
local win = vim.api.nvim_open_win(buf, true, win_opts or { local win = vim.api.nvim_open_win(buf, true, {
style = 'minimal', style = 'minimal',
relative = 'editor', relative = 'editor',
width = width, width = width,
@@ -49,7 +38,7 @@ helpers.open_term = function(opts, buf_opts)
vim.api.nvim_set_current_win(win) vim.api.nvim_set_current_win(win)
vim.fn.jobstart(cmd, { vim.fn.jobstart(opts.cmd, {
term = true, term = true,
on_exit = function(_, _, _) on_exit = function(_, _, _)
if vim.api.nvim_win_is_valid(win) then if vim.api.nvim_win_is_valid(win) then
@@ -62,112 +51,8 @@ helpers.open_term = function(opts, buf_opts)
end end
helpers.has_copilot = function() helpers.has_copilot = function()
return vim.fn.getenv 'COPILOT_API_KEY' ~= vim.NIL return vim.fn.getenv('COPILOT_API_KEY') ~= vim.NIL
end
helpers.open_file_modal = function(path, title)
local width = math.floor(vim.o.columns * 0.85)
local height = math.floor(vim.o.lines * 0.85)
local row = math.floor((vim.o.lines - height) / 2)
local col = math.floor((vim.o.columns - width) / 2)
local buf = vim.api.nvim_create_buf(false, true)
vim.api.nvim_open_win(buf, true, {
relative = 'editor',
width = width,
height = height,
row = row,
col = col,
style = 'minimal',
border = 'rounded',
title = ' ' .. title .. ' ',
title_pos = 'center',
})
vim.api.nvim_buf_set_name(buf, path)
local lines = vim.fn.readfile(path)
vim.api.nvim_buf_set_lines(buf, 0, -1, false, lines)
vim.bo[buf].filetype = 'markdown'
vim.bo[buf].buftype = 'nofile'
vim.bo[buf].bufhidden = 'wipe'
vim.bo[buf].swapfile = false
vim.bo[buf].modifiable = false
vim.bo[buf].readonly = true
vim.wo.wrap = false
vim.wo.number = false
vim.wo.relativenumber = false
vim.wo.cursorline = true
vim.keymap.set('n', 'q', '<cmd>close<CR>', {
buffer = buf,
silent = true,
desc = 'Close cheatsheet',
})
vim.keymap.set('n', '<Esc>', '<cmd>close<CR>', {
buffer = buf,
silent = true,
desc = 'Close cheatsheet',
})
end
local function get_formatter_bin(formatter)
return vim.fn.stdpath 'data' .. '/mason/bin/' .. formatter
end
local formatter_args = {
pint = function(file)
return {
get_formatter_bin 'pint',
'--stdin-filename',
file,
}
end,
eslint_d = function(file)
return {
get_formatter_bin 'eslint_d',
'--stdin',
'--stdin-filename',
file,
'--fix-to-stdout',
}
end,
stylua = function(file)
return {
get_formatter_bin 'stylua',
'--stdin-filepath',
file,
'-',
}
end,
}
helpers.format_buffer = function(formatter, buf, cwd)
local file = vim.api.nvim_buf_get_name(buf)
local input = table.concat(vim.api.nvim_buf_get_lines(buf, 0, -1, false), '\n')
local result = vim.system(formatter_args[formatter](file), {
text = true,
stdin = input,
cwd = cwd,
}):wait()
if result.code ~= 0 then
vim.notify(result.stderr, vim.log.levels.ERROR)
return
end
vim.api.nvim_buf_set_lines(buf, 0, -1, false, vim.split(result.stdout, '\n', { plain = true }))
end
helpers.create_bookmark = function(key, bookmark)
helpers.map('<Leader>b' .. key, function()
vim.cmd('edit ' .. bookmark)
end, { desc = 'Navigate to ' .. bookmark })
end end
return helpers return helpers
-- nnoremap <Leader>ev :tabedit $MYVIMRC<CR>
+101 -179
View File
@@ -26,17 +26,17 @@ vim.keymap.set('v', 'p', '"zdP', { desc = 'Paste over selection without yanking
-- or just use <C-\><C-n> to exit terminal mode -- or just use <C-\><C-n> to exit terminal mode
vim.keymap.set('t', '<Esc><Esc>', '<C-\\><C-n>', { desc = 'Exit terminal mode' }) vim.keymap.set('t', '<Esc><Esc>', '<C-\\><C-n>', { desc = 'Exit terminal mode' })
vim.keymap.set('n', '<Leader>z', function() vim.keymap.set('n', '<Leader>c', function()
vim.treesitter.inspect_tree() vim.treesitter.inspect_tree()
end, { desc = 'Treesitter' }) end, { desc = 'Treesitter' })
vim.keymap.set('n', '<C-S-D>', function() -- vim.keymap.set('n', '<C-S-D>', function()
local node = vim.treesitter.get_node {} -- local node = vim.treesitter.get_node {}
local range = { vim.treesitter.get_node_range(node) } -- local range = { vim.treesitter.get_node_range(node) }
vim.api.nvim_win_set_cursor(0, { range[3] + 1, range[4] - 1 }) -- vim.api.nvim_win_set_cursor(0, { range[3] + 1, range[4] - 1 })
vim.fn.setpos("'x", { 0, range[1] + 1, range[2] + 1, 0 }) -- vim.fn.setpos("'x", { 0, range[1] + 1, range[2] + 1, 0 })
vim.cmd.normal 'v`x' -- vim.cmd.normal 'v`x'
end, { desc = 'Select surrounding treesitter node' }) -- end, { desc = 'Select surrounding treesitter node' })
vim.keymap.set('v', '<C-S-D>', function() vim.keymap.set('v', '<C-S-D>', function()
local start = vim.api.nvim_win_get_cursor(0) local start = vim.api.nvim_win_get_cursor(0)
@@ -121,11 +121,6 @@ vim.keymap.set('n', '<C-l>', '<C-w><C-l>', { desc = 'Move focus to the right win
vim.keymap.set('n', '<C-j>', (win_or_treesj)('j', 'Move focus to the lower window or treesj.toggle()')) vim.keymap.set('n', '<C-j>', (win_or_treesj)('j', 'Move focus to the lower window or treesj.toggle()'))
vim.keymap.set('n', '<C-k>', (win_or_treesj)('k', 'Move focus to the upper window or treesj.toggle()')) vim.keymap.set('n', '<C-k>', (win_or_treesj)('k', 'Move focus to the upper window or treesj.toggle()'))
vim.keymap.set('t', '<C-h>', '<C-\\><C-n><C-w>h', { desc = 'Move focus to the left window' })
vim.keymap.set('t', '<C-l>', '<C-\\><C-n><C-w>l', { desc = 'Move focus to the right window' })
vim.keymap.set('t', '<C-j>', '<C-\\><C-n><C-w>j', { desc = 'Move focus to the lower window' })
vim.keymap.set('t', '<C-k>', '<C-\\><C-n><C-w>k', { desc = 'Move focus to the upper window' })
vim.keymap.set({ 'i' }, '<C-J>', function() vim.keymap.set({ 'i' }, '<C-J>', function()
local ls = require 'luasnip' local ls = require 'luasnip'
if ls.choice_active() then if ls.choice_active() then
@@ -182,67 +177,17 @@ vim.keymap.set({ 'n', 'v', 'c', 'i' }, '<C-f>', function()
end, { desc = 'Search within the whole project' }) end, { desc = 'Search within the whole project' })
vim.keymap.set({ 'n', 'v', 'c', 'i' }, '<C-Y>', '<CMD>Telescope quickfix<CR>', { desc = 'Show quickfix list' }) vim.keymap.set({ 'n', 'v', 'c', 'i' }, '<C-Y>', '<CMD>Telescope quickfix<CR>', { desc = 'Show quickfix list' })
vim.keymap.set({ 'n', 'v', 'c', 'i' }, '<C-S-Y>', '<CMD>Telescope quickfixhistory<CR>', { desc = 'Show quickfix history' }) vim.keymap.set({ 'n', 'v', 'c', 'i' }, '<C-S-Y>', '<CMD>Telescope quickfixhistory<CR>', { desc = 'Show quickfix history' })
vim.keymap.set({ 'n', 'v', 'c', 'i', 't' }, '<C-A>', function() vim.keymap.set({ 'n', 'v', 'c', 'i' }, '<C-A>', '<CMD>CodeCompanionChat Toggle<CR>', { desc = 'Open AI Actions' })
local ai_tools = { 'codex', 'agy', 'opencode' } vim.keymap.set({ 'n', 'v', 'c', 'i' }, '<C-S-A>', '<CMD>CodeCompanionActions<CR>', { desc = 'Open AI Actions' })
local ai_win_opts = {
width = math.floor(vim.o.columns / 3),
vertical = true,
split = 'right',
}
local term_bufnr = -1
for _, bufnr in ipairs(vim.api.nvim_list_bufs()) do
if vim.bo[bufnr].buftype == 'terminal' then
local name = vim.api.nvim_buf_get_name(bufnr)
for _, tool in ipairs(ai_tools) do
if name:match(':' .. tool .. '$') or name:match('/' .. tool .. '$') then
term_bufnr = bufnr
end
end
end
end
if term_bufnr ~= -1 then
local term_winid = vim.fn.bufwinid(term_bufnr)
if term_winid ~= -1 then
vim.api.nvim_win_hide(term_winid)
return
end
vim.api.nvim_open_win(term_bufnr, true, ai_win_opts)
vim.cmd.startinsert()
return
end
for _, tool in ipairs(ai_tools) do
if vim.fn.executable(tool) == 1 then
helpers.open_term {
cmd = tool,
win_opts = ai_win_opts,
buf_opts = {
bufhidden = 'hide',
modifiable = false,
},
}
return
end
end
vim.api.nvim_echo({ { 'No AI tool found. Please install one of the following: codex, agy, opencode', 'WarningMsg' } }, false, {})
end, { desc = 'Toggle AI tool' })
-- vim.keymap.set({ 'n', 'v', 'c', 'i' }, '<C-A>', '<CMD>vsplit term://codex<CR>i', { desc = 'Open AI tool' })
vim.keymap.set({ 'n', 'v', 'c', 'i' }, '<C-S>', function() vim.keymap.set({ 'n', 'v', 'c', 'i' }, '<C-S>', function()
require('snacks').scratch() require('snacks').scratch()
end, { desc = 'Open scratchpad' }) end, { desc = 'Open scratchpad' })
vim.keymap.set({ 'n', 'v', 'c', 'i' }, '<C-S-S>', function() vim.keymap.set({ 'n', 'v', 'c', 'i' }, '<C-S-S>', function()
require('snacks').scratch.select() require('snacks').scratch.select()
end, { desc = 'Open scratchpad buffers' }) end, { desc = 'Open scratchpad buffers' })
vim.keymap.set({ 'n', 'v', 'c', 'i', 't' }, '<C-T>', function() vim.keymap.set({ 'n', 'v', 'c', 'i' }, '<C-T>', function()
require('snacks').terminal.toggle() require('snacks').terminal.toggle()
end, { desc = 'Toggle terminal' }) end, { desc = 'Open terminal' })
vim.keymap.set({ 'n', 'v', 'c', 'i' }, '<C-N>', '<Esc>', { desc = 'Enter normal mode' })
vim.keymap.set({ 't' }, '<C-N>', '<C-\\><C-N>', { desc = 'Enter normal mode' })
vim.keymap.set({ 't' }, '<C-U>', '<C-\\><C-N><C-U>', { desc = 'Scroll up in terminal' })
vim.keymap.set({ 't' }, '<C-D>', '<C-\\><C-N><C-D>', { desc = 'Scroll down in terminal' })
-- Editing helpers -- Editing helpers
vim.keymap.set('i', '<C-O>', '<Esc>o', { desc = 'Add line below' }) vim.keymap.set('i', '<C-O>', '<Esc>o', { desc = 'Add line below' })
@@ -312,113 +257,101 @@ vim.keymap.set('n', '<Leader>sGS', '<CMD>Telescope git_stash<CR>', { desc = 'Sea
vim.keymap.set('n', '<Leader>]', '<CMD>cnext<CR>', { desc = 'Next item in quickfix list' }) vim.keymap.set('n', '<Leader>]', '<CMD>cnext<CR>', { desc = 'Next item in quickfix list' })
vim.keymap.set('n', '<Leader>[', '<CMD>cprevious<CR>', { desc = 'Previous item in quickfix list' }) vim.keymap.set('n', '<Leader>[', '<CMD>cprevious<CR>', { desc = 'Previous item in quickfix list' })
vim.keymap.set('n', 'gd', '<CMD>Telescope lsp_definitions<CR>', { desc = 'Go to definition' }) vim.keymap.set('n', 'gd', '<CMD>Telescope lsp_definitions<CR>', { desc = 'Go to definition' })
vim.keymap.set('n', '<Leader>r', function()
for _, c in ipairs(vim.lsp.get_clients { bufnr = 0 }) do
if c.name == 'phpantom_lsp' then
c:stop(true)
end
end
vim.cmd 'edit'
end, { desc = 'Reload buffer' })
local function open_test() local function open_test()
require('neotest').summary.open() require('neotest').summary.open()
require('neotest').output_panel.open() require('neotest').output_panel.open()
end end
-- Testing -- Testing
local test_maps = { -- local test_maps = {
{ -- {
keys = { '<Leader>tc' }, -- keys = { '<F12>', '<Leader>tn' },
action = function() -- action = function()
require('neotest').summary.close() -- require('neotest').run.run()
require('neotest').output_panel.close() -- open_test()
end, -- end,
desc = 'Close [T]est panels', -- desc = 'Run nearest test',
}, -- },
{ -- {
keys = { '<Leader>tn' }, -- keys = { '<F9>', '<Leader>ta' },
action = function() -- action = function()
require('neotest').run.run() -- require('neotest').run.run { suite = true }
open_test() -- open_test()
end, -- end,
desc = 'Run nearest [T]est', -- desc = 'Run all tests in the project',
}, -- },
{ -- {
keys = { '<Leader>ta' }, -- keys = { '<F11>', '<Leader>tp' },
action = function() -- action = function()
require('neotest').run.run { suite = true } -- require('neotest').run.run_last()
open_test() -- open_test()
end, -- end,
desc = 'Run all [T]ests in the project', -- desc = 'Run previous test again',
}, -- },
{ -- {
keys = { '<Leader>tp' }, -- keys = { '<F10>', '<Leader>td' },
action = function() -- action = function()
require('neotest').run.run_last() -- local dap = require 'dap'
open_test() -- if dap.session() == nil then
end, -- dap.continue()
desc = 'Run previous [T]est again', -- end
}, -- require('dapui').open()
{ -- local neotest = require 'neotest'
keys = { '<Leader>td' }, -- local bufnr = vim.api.nvim_get_current_buf()
action = function() -- local row = vim.api.nvim_win_get_cursor(0)[1] - 1
local dap = require 'dap' --
if dap.session() == nil then -- local adapters = neotest.state.adapter_ids()
dap.continue() -- local found = false
end --
require('dapui').open() -- for _, adapter_id in ipairs(adapters) do
local neotest = require 'neotest' -- local tree = neotest.state.positions(adapter_id, { buffer = bufnr })
local bufnr = vim.api.nvim_get_current_buf() -- if tree then
local row = vim.api.nvim_win_get_cursor(0)[1] - 1 -- local nearest = require('neotest.lib.positions').nearest(tree, row)
-- if nearest and nearest:data().type ~= 'file' then
-- neotest.run.run()
-- found = true
-- break
-- end
-- end
-- end
--
-- if not found then
-- neotest.run.run_last()
-- end
-- end,
-- desc = 'Run last test with debugger',
-- },
-- }
--
-- for _, map_info in ipairs(test_maps) do
-- for _, key in ipairs(map_info.keys) do
-- vim.keymap.set('n', key, map_info.action, { desc = map_info.desc })
-- end
-- end
-- vim.keymap.set('n', '<Leader>tf', function()
-- require('neotest').run.run(vim.fn.expand '%')
-- open_test()
-- end, { desc = 'Run all tests in the current file' })
-- vim.keymap.set('n', '<Leader>tc', function()
-- require('neotest').summary.close()
-- require('neotest').output_panel.close()
-- end, { desc = 'Close test panels' })
local adapters = neotest.state.adapter_ids() -- vim.keymap.set('i', '<Tab>', function()
local found = false -- local copilot = require 'copilot.suggestion'
-- local ls = require 'luasnip'
vim.g.neotest_debug = true -- if copilot.is_visible() then
for _, adapter_id in ipairs(adapters) do -- vim.api.nvim_echo({{'Accepting copilot suggestion'}}, false, {})
local tree = neotest.state.positions(adapter_id, { buffer = bufnr }) -- copilot.accept()
if tree then -- copilot.dismiss()
local nearest = require('neotest.lib.positions').nearest(tree, row) -- elseif ls.jumpable(1) then
if nearest and nearest:data().type ~= 'file' then -- vim.api.nvim_echo({{'Jumping in snippet'}}, false, {})
neotest.run.run() -- ls.jump()
found = true -- else
break -- vim.api.nvim_echo({{'Inserting tab'}}, false, {})
end -- vim.api.nvim_feedkeys(vim.api.nvim_replace_termcodes('<Tab>', true, true, true), 'n', true)
end -- end
end -- end, { desc = 'Luasnip accept copilot or jump forward' })
if not found then
neotest.run.run_last()
end
vim.g.neotest_debug = nil
end,
desc = 'Run last [T]est with debugger',
},
}
for _, map_info in ipairs(test_maps) do
for _, key in ipairs(map_info.keys) do
vim.keymap.set('n', key, map_info.action, { desc = map_info.desc })
end
end
vim.keymap.set('n', '<Leader>tf', function()
require('neotest').run.run(vim.fn.expand '%')
open_test()
end, { desc = 'Run all tests in the current file' })
vim.keymap.set('n', '<Leader>tc', function()
require('neotest').summary.close()
require('neotest').output_panel.close()
end, { desc = 'Close test panels' })
vim.keymap.set('i', '<Tab>', function()
if ls.jumpable(1) then
vim.api.nvim_echo({ { 'Jumping in snippet' } }, false, {})
ls.jump()
else
vim.api.nvim_echo({ { 'Inserting tab' } }, false, {})
vim.api.nvim_feedkeys(vim.api.nvim_replace_termcodes('<Tab>', true, true, true), 'n', true)
end
end, { desc = 'Luasnip accept copilot or jump forward' })
-- Leaving this commented out, I will try the format command instead -- Leaving this commented out, I will try the format command instead
-- "A command to properly indent json code -- "A command to properly indent json code
@@ -428,7 +361,7 @@ end, { desc = 'Luasnip accept copilot or jump forward' })
-- directory if the file does not exist -- directory if the file does not exist
vim.keymap.set('n', '<Leader>es', function() vim.keymap.set('n', '<Leader>es', function()
local ft = vim.bo.filetype local ft = vim.bo.filetype
if ft == 'vue' or ft == 'typescript' then if ft == 'vue' then
ft = 'javascript' ft = 'javascript'
end end
local snippets_dir = vim.fn.stdpath 'config' .. '/lua/snippets' local snippets_dir = vim.fn.stdpath 'config' .. '/lua/snippets'
@@ -436,15 +369,4 @@ vim.keymap.set('n', '<Leader>es', function()
vim.cmd('tabedit ' .. snippets_file) vim.cmd('tabedit ' .. snippets_file)
end, { desc = 'Edit snippets file' }) end, { desc = 'Edit snippets file' })
vim.keymap.set('n', '<Leader>cz', function()
require('utils/cheatsheets').open 'zsh'
end, { desc = 'Open zsh cheat sheet' })
vim.keymap.set('n', '<Leader>cb', function()
require('utils/cheatsheets').open 'bash'
end, { desc = 'Open bash cheat sheet' })
vim.keymap.set('n', '<Leader>cr', function()
require('utils/cheatsheets').open 'regex'
end, { desc = 'Open RegExp cheat sheet' })
require('helpers').edit_cf('k', '/lua/keymap.lua') require('helpers').edit_cf('k', '/lua/keymap.lua')
-1
View File
@@ -47,4 +47,3 @@ require('lazy').setup({
}) })
require('helpers').edit_cf('l', '/lua/lazy_init.lua') require('helpers').edit_cf('l', '/lua/lazy_init.lua')
-1
View File
@@ -75,7 +75,6 @@ vim.opt.softtabstop = 4
vim.opt.expandtab = true vim.opt.expandtab = true
-- Automatically indent code -- Automatically indent code
vim.opt.smartindent = true vim.opt.smartindent = true
vim.opt.autoindent = true
-- Save the file when switching buffers -- Save the file when switching buffers
vim.opt.autowriteall = true vim.opt.autowriteall = true
+67
View File
@@ -0,0 +1,67 @@
require('helpers').edit_cf('pa', '/lua/plugins/aider.lua')
local change_model_function = function(model)
return function()
require('nvim_aider').api.send_command('/model', model)
end
end
return {
'GeorgesAlkhouri/nvim-aider',
cmd = 'Aider',
keys = {
{ '<leader>a/', '<cmd>Aider toggle<cr>', desc = 'Toggle Aider' },
{ '<leader>as', '<cmd>Aider send<cr>', desc = 'Send to Aider', mode = { 'n', 'v' } },
{ '<leader>ac', '<cmd>Aider command<cr>', desc = 'Aider Commands' },
{ '<leader>ab', '<cmd>Aider buffer<cr>', desc = 'Send Buffer' },
{ '<leader>a+', '<cmd>Aider add<cr>', desc = 'Add File' },
{ '<leader>a-', '<cmd>Aider drop<cr>', desc = 'Drop File' },
{ '<leader>ar', '<cmd>Aider add readonly<cr>', desc = 'Add Read-Only' },
{ '<leader>aR', '<cmd>Aider reset<cr>', desc = 'Reset Session' },
-- Example nvim-tree.lua integration if needed
{ '<leader>a+', '<cmd>AiderTreeAddFile<cr>', desc = 'Add File from Tree to Aider', ft = 'NvimTree' },
{ '<leader>a-', '<cmd>AiderTreeDropFile<cr>', desc = 'Drop File from Tree from Aider', ft = 'NvimTree' },
{ '<leader>am4', change_model_function 'gpt-4.1', desc = 'Switch aider model to GPT-4.1' },
{ '<leader>amo', change_model_function 'openai/o4-mini', desc = 'Switch aider model to o4-mini' },
{ '<leader>amg', change_model_function 'openai/gemini-2.5-pro', desc = 'Switch aider model to Gemini 2.5 Pro' },
{ '<leader>ams', change_model_function 'openai/claude-sonnet-4', desc = 'Switch aider model to Claude Sonnet 4' },
},
dependencies = {
'folke/snacks.nvim',
--- The below dependencies are optional
'catppuccin/nvim',
'nvim-tree/nvim-tree.lua',
--- Neo-tree integration
{
'nvim-neo-tree/neo-tree.nvim',
opts = function(_, opts)
-- Example mapping configuration (already set by default)
-- opts.window = {
-- mappings = {
-- ["+"] = { "nvim_aider_add", desc = "add to aider" },
-- ["-"] = { "nvim_aider_drop", desc = "drop from aider" }
-- ["="] = { "nvim_aider_add_read_only", desc = "add read-only to aider" }
-- }
-- }
require('nvim_aider.neo_tree').setup(opts)
end,
},
},
config = function()
require('nvim_aider').setup {
aider_cmd = 'aider',
args = {
'--config=$HOME/.config/aider/aider.yaml',
'--env-file=$(pwd)/aider.env',
'--watch',
'--architect',
},
auto_reload = true,
win = {
wo = { winbar = 'Aider' },
style = 'nvim_aider',
position = 'bottom',
},
}
end,
}
-14
View File
@@ -1,14 +0,0 @@
return {
'ricardoramirezr/blade-nav.nvim',
ft = { 'blade', 'php' }, -- optional, improves startup time
config = function()
require('blade-nav').setup {
close_tag_on_complete = false,
integrations = {
cmp = false,
coq = false,
},
}
end,
}
+3 -5
View File
@@ -56,10 +56,9 @@ return {
providers = { providers = {
lazydev = { module = 'lazydev.integrations.blink', score_offset = 100 }, lazydev = { module = 'lazydev.integrations.blink', score_offset = 100 },
-- avante = { module = 'blink-cmp-avante', name = 'Avante', opts = {} }, -- avante = { module = 'blink-cmp-avante', name = 'Avante', opts = {} },
['blade-nav'] = { },
name = 'blade-nav', per_filetype = {
module = 'blade-nav.integrations.blink', codecompanion = { 'codecompanion' },
},
}, },
}, },
@@ -78,4 +77,3 @@ return {
signature = { enabled = true }, signature = { enabled = true },
}, },
} }
+339
View File
@@ -0,0 +1,339 @@
return {
'olimorris/codecompanion.nvim',
config = function()
require('codecompanion').setup {
adapters = {
http = {
default = function()
if (require('helpers').has_copilot()) then
return require('codecompanion.adapters').extend('copilot', {
schema = {
model = {
default = vim.env.DEFAULT_AI_MODEL,
},
max_tokens = {
default = 1000000,
},
}
})
end
return require('codecompanion.adapters').extend('openai_compatible', {
env = {
url = vim.env.DEFAULT_OPENAI_API_BASE,
api_key = vim.env.DEFAULT_OPENAI_API_KEY,
chat_url = '/v1/chat/completions',
models_endpoint = '/v1/models',
},
schema = {
model = {
default = vim.env.DEFAULT_AI_MODEL,
},
max_tokens = {
default = 1000000,
},
},
})
end,
},
},
display = {
chat = {
show_settings = true,
start_in_insert_mode = false,
},
},
strategies = {
chat = {
adapter = 'default',
slash_commands = {
-- codebase = require('vectorcode.integrations').codecompanion.chat.make_slash_command(),
},
tools = {
-- vectorcode = {
-- description = 'Run VectorCode to retrieve the project context.',
-- callback = require('vectorcode.integrations').codecompanion.chat.make_tool(),
-- },
['cmd_runner'] = {
opts = {
requires_approval = false,
},
},
},
roles = {
---@type string|fun(adapter: CodeCompanion.Adapter): string
llm = function(adapter)
return 'CodeCompanion (' .. adapter.formatted_name .. ': ' .. adapter.parameters.model .. ')'
end,
},
keymaps = {
send = {
modes = { n = '<C-s>', i = '<C-s>' },
},
close = {
modes = { n = '<C-c>', i = '<C-c>' },
},
},
},
inline = {
adapter = {
name = 'default',
model = vim.env.FAST_MODEL,
},
},
cmd = {
adapter = {
name = 'default',
model = vim.env.FAST_MODEL,
},
},
},
extensions = {
mcphub = {
callback = 'mcphub.extensions.codecompanion',
opts = {
show_result_in_chat = true,
make_vars = true,
make_slash_commands = true,
},
},
},
system_prompt = function(opts)
local language = opts.language or 'English'
return string.format(
[[You are an AI programming assistant named "CodeCompanion". You are currently plugged into the Neovim text editor on a user's machine.
Your core tasks include:
- Answering general programming questions.
- Explaining how the code in a Neovim buffer works.
- Reviewing the selected code from a Neovim buffer.
- Generating unit tests for the selected code.
- Proposing fixes for problems in the selected code.
- Scaffolding code for a new workspace.
- Finding relevant code to the user's query.
- Proposing fixes for test failures.
- Answering questions about Neovim.
- Running tools.
You must:
- Follow the user's requirements carefully and to the letter.
- Keep your answers short and impersonal, especially if the user's context is outside your core tasks.
- Minimize additional prose unless clarification is needed.
- Use Markdown formatting in your answers.
- Include the programming language name at the start of each Markdown code block.
- Avoid including line numbers in code blocks.
- Avoid wrapping the whole response in triple backticks.
- Only return code that's directly relevant to the task at hand. You may omit code that isnt necessary for the solution.
- Avoid using H1, H2 or H3 headers in your responses as these are reserved for the user.
- Use actual line breaks in your responses; only use "\n" when you want a literal backslash followed by 'n'.
- All non-code text responses must be written in the %s language indicated.
- Multiple, different tools can be called as part of the same response.
- Use full path names when using tools to read and modify files.
When given a task:
1. Think step-by-step and, unless the user requests otherwise or the task is very simple, describe your plan in detailed pseudocode.
2. Output the final code in a single code block, ensuring that only relevant code is included.
3. End your response with a short suggestion for the next user turn that directly supports continuing the conversation.
4. Provide exactly one complete reply per conversation turn.
5. If necessary, execute multiple tools in a single turn.]],
language
)
end,
prompt_library = {
['Code Expert'] = {
strategy = 'chat',
description = 'Get some special advice from an LLM.',
opts = {
mapping = '<Leader>ce',
modes = { 'v' },
short_name = 'expert',
auto_submit = true,
stop_context_insertion = true,
user_prompt = true,
},
prompts = {
{
role = 'system',
content = function(context)
return 'I want you to act as a senior'
.. context.filetype
.. 'developer I will ask you specific questions and I want you to return concise explanations and codeblock examples.'
end,
},
{
role = 'user',
content = function(context)
local text = require('codecompanion.helpers.actions').get_code(context.start_line, context.end_line)
return 'I have the following code:\n\n```' .. context.filetype .. '\n' .. text .. '\n```\n\n'
end,
opts = {
contains_code = true,
},
},
},
},
['Games Master'] = {
strategy = 'chat',
description = 'A personal Games Master Assistant.',
opts = {
user_prompt = false,
},
prompts = {
{
role = 'system',
content = [[
You are a personal Games Master Assistant. You are currently plugged in to the Neovim text editor on a user's machine.
Your core tasks include:
- Crafting engaging role playing sessions
- Building immersive and authentic worlds
- Creating compelling characters for the players to interact with and drive the story
- Reviewing session summaries and building on them
- Providing advice on how to bring sessions to life
- Create exciting encounters to challenge the players
- Encounters can be combat, social, or exploration based
- There should always be a story reason for the encounter
- Combine player character backgrounds and motivations into the world
- Build tension and drama into the game
- Ensure each session provides opportunities for the players to engage with and drive the story
You must:
- Follow the user's requirements carefully and to the letter.
- Keep answers focused on the task at hand.
- Provide original ideas and suggestions.
- Use Markdown formatting in your answers.
- Use actual line breaks instead of '\n' in your response to begin new lines.
- Use '\n' only when you want a literal backslash followed by a character 'n'.
- All responses must be written in English.
- Multiple, different tools can be called as part of the same response.
- Help sessions be fast paced and fun.
- Ensure there are multiple soltuions to each problem.
- Avoid railroading the players.
When given a task:
1. Consider the existing world and characters. Use tools to gather information that may be relevant.
2. Provide exactly one complete reply per conversation turn.
3. If necessary, execute multiple tools in a single turn.
]],
},
{
role = 'user',
content = '',
},
},
},
['PHPStan Fixer'] = {
strategy = 'workflow',
description = 'Use a workflow to fix PHPStan errors until there are none left.',
opts = {
short_name = 'phpstan',
},
prompts = {
{
{
name = 'Run PHPStan',
role = 'user',
opts = { auto_submit = false },
content = function()
-- Enable turbo mode!!!
vim.g.codecompanion_auto_tool_mode = true
return [[PHPStan is a static analysis tool for PHP. It is currently reporting errors in the code. Your task is to fix these errors and run PHPStan again until there are no errors left.
First of all use the @cmd_runner tool to run the `composer type-check` command. This will run PHPStan and output type errors in the code.]]
end,
},
},
{
{
name = 'Fetch files',
role = 'user',
opts = { auto_submit = false },
content = function()
-- Enable turbo mode!!!
vim.g.codecompanion_auto_tool_mode = true
return 'PHPStan has reported errors. Look at the output and see where the files are reported. Use the @mcp tool to read all the offending files so you can implement the fixes.'
end,
},
},
{
{
name = 'Fix errors and run',
role = 'user',
opts = { auto_submit = false },
content = function()
-- Enable turbo mode!!!
vim.g.codecompanion_auto_tool_mode = true
return [[### Instructions
Now you have the errors and the appropriate context you can fix the errors.
### Steps to Follow
You are required to analyse the PHPStan errors and write code to fix them.
Reason through the errors and write out the possible causes and fixes for each.
1. Then use the @mcp tool to update the files with the fixes. When editing files use the full path name including the project name /Users/chris/Code/Sites/hylark/
2. After editing the files use the @cmd_runner tool again to run the `composer type-check` command to see if any errors remain.
We'll repeat this cycle until there are no errors. Ensure no deviations from these steps.
### Tips for fixing PHPStan errors
- Do not ignore the errors
- Use Webmozart Assert library to narrow types
- Most errors are due to missing docblocks or calling methods/parameters on types that PHPStan cannot infer
- Use fully qualified namespaces for classes in doc blocks
- The code is working so fixes should not change the behaviour of the code
- There are some custom types in this project that help define types
- closure<T> creates T|Closure(): T, you can add a second argument to specify the depth so closure<T, 2> creates T|Closure(): T|Closure(): (Closure(): T). This is useful because GraphQL queries can return closures.
- promise<T> creates T|SyncPromise<T>, as with closure, you can add a second argument for depth. You can also add a third argument if T is an array to allow adding promises to each value, so promise<array{a: int}, 1, 1> will create array{a: int}|SyncPromise<array{a: int}>|array{a: SyncPromise<int>}|SyncPromise<array{a: SyncPromise<int>}>. This is useful because GraphQL queries can return promises and arrays of promises.
- GraphQL schema types (these should only be used in the Query classes)
- GArgs<T> will look at the GraphQL schema and create an array type for the arguments of a field on one of the root types (Query or Mutation)
- GArgs<TType, TField> will create a type for the arguments of a field on a type
- GType<T> will create an type for a specific GraphQL type, input, interface, or enum
- GVal<T> will create a type for the return value of a field on one of the root types (Query or Mutation)
- GVal<TType, TField> will create a type for the return value of a field on a type
- pick<T, TKeys> will create an array from T with only the keys in TKeys (e.g. pick<array{a: int, b: int, c: int}, 'a'|'b'> will create array{a: int, b: int})
- except<T, TKeys> works like pick only it excludes the keys in TKeys (e.g. except<array{a: int, b: int, c: int}, 'a'|'b'> will create array{c: int})
- union<T, U> creates a union array of T and U (e.g. union<array{a: int}, array{b: int}> will create array{a: int, b: int})]]
end,
},
},
{
{
name = 'Repeat On Failure',
role = 'user',
opts = { auto_submit = false },
-- Scope this prompt to the cmd_runner tool
condition = function()
return _G.codecompanion_current_tool == 'cmd_runner'
end,
-- Repeat until the tests pass, as indicated by the testing flag
-- which the cmd_runner tool sets on the chat buffer
repeat_until = function(chat)
-- Check if the last message in the chat buffer contains "[ERROR] found"
local messages = chat.messages
local last_message = messages[#messages]
if last_message and last_message.role == 'assistant' then
local content = last_message.content
return not content:find '[ERROR] found'
end
return true
end,
content = 'PHPStan is still reporting errors. Edit the code to fix the errors and run PHPStan again.',
},
},
},
},
},
}
end,
dependencies = {
'nvim-lua/plenary.nvim',
'nvim-treesitter/nvim-treesitter',
-- 'Davidyz/VectorCode',
'ravitemer/mcphub.nvim',
},
}
+9 -14
View File
@@ -4,24 +4,19 @@ return {
-- change the command in the config to whatever the name of that colorscheme is. -- change the command in the config to whatever the name of that colorscheme is.
-- --
-- If you want to see what colorschemes are already installed, you can use `:Telescope colorscheme`. -- If you want to see what colorschemes are already installed, you can use `:Telescope colorscheme`.
'AlphaTechnolog/pywal.nvim', 'folke/tokyonight.nvim',
dependencies = {
'folke/tokyonight.nvim',
},
priority = 1000, -- Make sure to load this before all the other start plugins. priority = 1000, -- Make sure to load this before all the other start plugins.
config = function() config = function()
---@diagnostic disable-next-line: missing-fields ---@diagnostic disable-next-line: missing-fields
require('tokyonight').setup() require('tokyonight').setup {
styles = {
comments = { italic = false }, -- Disable italics in comments
},
}
-- Load the colorscheme here.
-- Like many other themes, this one has different styles, and you could load
-- any other, such as 'tokyonight-storm', 'tokyonight-moon', or 'tokyonight-day'.
vim.cmd.colorscheme 'tokyonight-night' vim.cmd.colorscheme 'tokyonight-night'
-- Check if wal directory exists otherwise use tokyo
local handle = io.popen 'ls -d $HOME/.cache/wal 2>/dev/null'
local result = handle:read '*a'
handle:close()
if result ~= '' then
require('pywal').setup()
end
end, end,
} }
+44
View File
@@ -0,0 +1,44 @@
return { -- Autoformat
'stevearc/conform.nvim',
event = { 'BufWritePre' },
cmd = { 'ConformInfo' },
keys = {
{
'<leader>f',
function()
require('conform').format { async = true, lsp_format = 'fallback' }
end,
mode = '',
desc = '[F]ormat buffer',
},
},
config = function()
require('conform').setup({
notify_on_error = false,
format_on_save = function(bufnr)
-- Disable "format_on_save lsp_fallback" for languages that don't
-- have a well standardized coding style. You can add additional
-- languages here or re-enable it for the disabled ones.
local disable_filetypes = { c = true, cpp = true, vue = true, js = true }
if disable_filetypes[vim.bo[bufnr].filetype] then
return nil
else
return {
timeout_ms = 500,
lsp_format = 'fallback',
}
end
end,
formatters_by_ft = {
lua = { 'stylua' },
php = { 'pint' },
-- Conform can also run multiple formatters sequentially
-- python = { "isort", "black" },
--
-- You can use 'stop_after_first' to run the first available formatter from the list
-- javascript = { "prettierd", "prettier", stop_after_first = true },
},
log_level = vim.log.levels.DEBUG
})
end
}
+34
View File
@@ -180,6 +180,40 @@ return {
vim.fn.sign_define(tp, { text = icon, texthl = hl, numhl = hl }) vim.fn.sign_define(tp, { text = icon, texthl = hl, numhl = hl })
end end
local function get_php_ini_dir()
local handle = io.popen 'php --ini 2>/dev/null'
if not handle then
return nil
end
local result = handle:read '*a'
handle:close()
local dir = result:match 'Scan for additional .ini files in:%s+([^\n]+)'
if dir and dir:find '%(none%)' == nil then
return vim.trim(dir)
end
return nil
end
local function enable_xdebug()
local ini_dir = get_php_ini_dir()
if not ini_dir then
return
end
os.execute(string.format('mv "%s/20-xdebug.ini.disabled" "%s/20-xdebug.ini" 2>/dev/null', ini_dir, ini_dir))
end
local function disable_xdebug()
local ini_dir = get_php_ini_dir()
if not ini_dir then
return
end
os.execute(string.format('mv "%s/20-xdebug.ini" "%s/20-xdebug.ini.disabled" 2>/dev/null', ini_dir, ini_dir))
end
dap.listeners.after.event_initialized['xdebug_enable'] = enable_xdebug
dap.listeners.before.event_terminated['xdebug_disable'] = disable_xdebug
dap.listeners.before.event_exited['xdebug_disable'] = disable_xdebug
-- dap.listeners.after.event_output['neotest_display'] = function(_, body) -- dap.listeners.after.event_output['neotest_display'] = function(_, body)
-- require('neotest').output.open { enter = true, last = true } -- require('neotest').output.open { enter = true, last = true }
-- end -- end
+4 -18
View File
@@ -13,10 +13,9 @@ return {
'saghen/blink.cmp', 'saghen/blink.cmp',
'folke/lazydev.nvim', 'folke/lazydev.nvim',
}, },
-- Might not be necessary for phpantom build = function()
-- build = function() vim.fn.system 'composer global require jetbrains/phpstorm-stubs friendsofphp/php-cs-fixer'
-- vim.fn.system 'composer global require jetbrains/phpstorm-stubs friendsofphp/php-cs-fixer' end,
-- end,
config = function() config = function()
-- This function gets run when an LSP attaches to a particular buffer. -- This function gets run when an LSP attaches to a particular buffer.
-- That is to say, every time a new file is opened that is associated with -- That is to say, every time a new file is opened that is associated with
@@ -116,12 +115,6 @@ return {
}, },
} }
-- vim.lsp.config['phpantom'] = {
-- filetypes = { 'php' },
-- cmd = { 'phpantom_lsp' },
-- root_markers = { 'composer.json', '.git' },
-- }
-- Enable the following language servers -- Enable the following language servers
-- --
-- Add any additional override configuration in the following tables. Available keys are: -- Add any additional override configuration in the following tables. Available keys are:
@@ -146,11 +139,7 @@ return {
}, },
}, },
}, },
phpantom_lsp = { phpactor = {},
filetypes = { 'php' },
cmd = { 'phpantom_lsp' },
root_markers = { 'composer.json', '.git' },
},
vtsls = { vtsls = {
settings = { settings = {
vtsls = { vtsls = {
@@ -206,9 +195,6 @@ return {
local ensure_installed = vim.tbl_keys(servers or {}) local ensure_installed = vim.tbl_keys(servers or {})
vim.list_extend(ensure_installed, { vim.list_extend(ensure_installed, {
'stylua', -- Used to format Lua code 'stylua', -- Used to format Lua code
'blade-formatter',
'eslint_d',
'pint',
}) })
-- LSP servers and clients are able to communicate to each other what features they support. -- LSP servers and clients are able to communicate to each other what features they support.
+1 -1
View File
@@ -1,7 +1,7 @@
-- Filesystem manager -- Filesystem manager
-- require('helpers').edit_cf('po', '/lua/plugins/oil.lua') -- require('helpers').edit_cf('po', '/lua/plugins/oil.lua')
-- --
vim.keymap.set('n', '-', '<CMD>Oil<CR>', { desc = 'Open parent directory' }) -- vim.keymap.set('n', '-', '<CMD>Oil<CR>', { desc = 'Open parent directory' })
return { return {
'stevearc/oil.nvim', 'stevearc/oil.nvim',
-3
View File
@@ -1,3 +0,0 @@
return {
'AlphaTechnolog/pywal.nvim',
}
+8 -8
View File
@@ -1,9 +1,9 @@
return { return {
-- "ThePrimeagen/refactoring.nvim", "ThePrimeagen/refactoring.nvim",
-- dependencies = { dependencies = {
-- "nvim-lua/plenary.nvim", "nvim-lua/plenary.nvim",
-- "nvim-treesitter/nvim-treesitter", "nvim-treesitter/nvim-treesitter",
-- }, },
-- lazy = false, lazy = false,
-- opts = {}, opts = {},
} }
+1 -11
View File
@@ -5,16 +5,6 @@ return {
-- dependencies = { 'nvim-treesitter/nvim-treesitter', 'nvim-tree/nvim-web-devicons' }, -- if you prefer nvim-web-devicons -- dependencies = { 'nvim-treesitter/nvim-treesitter', 'nvim-tree/nvim-web-devicons' }, -- if you prefer nvim-web-devicons
---@module 'render-markdown' ---@module 'render-markdown'
---@type render.md.UserConfig ---@type render.md.UserConfig
opts = {},
ft = { 'markdown', 'codecompanion' }, ft = { 'markdown', 'codecompanion' },
config = function()
require('render-markdown').setup {
latex = {
enabled = true,
render_modes = true,
},
anti_conceal = {
enabled = false,
},
}
end,
} }
-2
View File
@@ -13,7 +13,6 @@ return {
config = function() config = function()
local ls = require 'luasnip' local ls = require 'luasnip'
ls.filetype_extend('vue', { 'javascript' }) ls.filetype_extend('vue', { 'javascript' })
ls.filetype_extend('typescript', { 'javascript' })
local snippets_dir = vim.fn.stdpath 'config' .. '/lua/snippets' local snippets_dir = vim.fn.stdpath 'config' .. '/lua/snippets'
require('luasnip.loaders.from_lua').load { require('luasnip.loaders.from_lua').load {
paths = { snippets_dir }, paths = { snippets_dir },
@@ -25,4 +24,3 @@ return {
end, end,
opts = {}, opts = {},
} }
-27
View File
@@ -1,27 +0,0 @@
require('helpers').edit_cf('pt', '/lua/plugins/test.lua')
return {
'nvim-neotest/neotest',
dependencies = {
'V13Axel/neotest-pest',
},
config = function()
require('neotest').setup {
adapters = {
require 'neotest-pest' {
sail_enabled = function()
return false
end,
parallel = 8,
pest_cmd = function()
local cmd = vim.g.neotest_pest_cmd or { 'vendor/bin/pest' }
if vim.g.neotest_debug then
cmd = { 'sh', '-c', 'XDEBUG_MODE=debug exec ' .. table.concat(cmd, ' ') .. ' "$@"' }
end
return cmd
end,
},
},
}
end,
}
+151 -6
View File
@@ -1,8 +1,153 @@
-- Highlight, edit, and navigate code
return { return {
'nvim-treesitter/nvim-treesitter-textobjects', -- 'nvim-treesitter/nvim-treesitter-textobjects',
branch = 'main', -- lazy = true,
config = function() -- branch = 'main',
-- put your config here -- dependencies = {
end, -- 'nvim-treesitter/nvim-treesitter',
} -- },
-- config = function()
-- -- configuration
-- require('nvim-treesitter-textobjects').setup {
-- select = {
-- lookahead = true,
-- selection_modes = {
-- ['@parameter.outer'] = 'v', -- charwise
-- ['@function.outer'] = 'V', -- linewise
-- ['@class.outer'] = 'V', -- blockwise
-- },
-- include_surrounding_whitespace = false,
-- },
-- move = {
-- set_jumps = true,
-- },
-- }
-- for keys, query in
-- {
-- -- or you use the queries from supported languages with textobjects.scm
-- ['af'] = '@function.outer',
-- ['if'] = '@function.inner',
-- ['aC'] = '@class.outer',
-- ['iC'] = '@class.inner',
-- ['ay'] = '@conditional.outer',
-- ['iy'] = '@conditional.inner',
-- ['aj'] = '@loop.outer',
-- ['ij'] = '@loop.inner',
-- ['is'] = '@statement.inner',
-- ['as'] = '@statement.outer',
-- ['ac'] = '@comment.outer',
-- ['ic'] = '@comment.inner',
-- ['ap'] = '@comment.parameter',
-- ['ip'] = '@comment.parameter',
-- ['an'] = '@local.scope',
-- }
-- do
-- vim.keymap.set({ 'n', 'x', 'o' }, keys, function()
-- require('nvim-treesitter-textobjects.select').select_textobject(query, 'textobjects')
-- end)
-- end
-- for keys, query in
-- {
-- -- or you use the queries from supported languages with textobjects.scm
-- [']'] = '@function.outer',
-- ['o'] = { '@loop.inner', '@loop.outer' },
-- ['n'] = '@local.scope',
-- ['y'] = '@conditional.outer',
-- ['e'] = '@statement.outer',
-- ['/'] = '@comment.outer',
-- }
-- do
-- vim.keymap.set({ 'n', 'x', 'o' }, ']' .. keys, function()
-- require('nvim-treesitter-textobjects.move').goto_next_start(query, 'textobjects')
-- end)
-- vim.keymap.set({ 'n', 'x', 'o' }, '[' .. keys, function()
-- require('nvim-treesitter-textobjects.move').goto_previous_start(query, 'textobjects')
-- end)
-- end
-- -- require('nvim-treesitter.configs').setup {
-- -- incremental_selection = {
-- -- enable = true,
-- -- keymaps = {
-- -- -- mappings for incremental selection (visual mappings)
-- -- init_selection = '<C-S-D>', -- maps in normal mode to init the node/scope selection
-- -- node_incremental = '<C-S-D>', -- increment to the upper named parent
-- -- scope_incremental = '<C-S-U>', -- increment to the upper scope (as defined in locals.scm)
-- -- node_decremental = '<C-S-S>', -- decrement to the previous node
-- -- },
-- -- },
-- -- textobjects = {
-- -- -- syntax-aware textobjects
-- -- enable = true,
-- -- keymaps = {
-- -- ['iL'] = {
-- -- -- you can define your own textobjects directly here
-- -- go = '(function_definition) @function',
-- -- },
-- -- -- or you use the queries from supported languages with textobjects.scm
-- -- ['af'] = '@function.outer',
-- -- ['if'] = '@function.inner',
-- -- ['aC'] = '@class.outer',
-- -- ['iC'] = '@class.inner',
-- -- ['ay'] = '@conditional.outer',
-- -- ['iy'] = '@conditional.inner',
-- -- ['aj'] = '@loop.outer',
-- -- ['ij'] = '@loop.inner',
-- -- ['is'] = '@statement.inner',
-- -- ['as'] = '@statement.outer',
-- -- ['ac'] = '@comment.outer',
-- -- ['ic'] = '@comment.inner',
-- -- ['ap'] = '@comment.parameter',
-- -- ['ip'] = '@comment.parameter',
-- -- },
-- -- move = {
-- -- enable = true,
-- -- set_jumps = true, -- whether to set jumps in the jumplist
-- -- goto_next_start = {
-- -- [']m'] = '@function.outer',
-- -- [']]'] = '@class.outer',
-- -- },
-- -- goto_next_end = {
-- -- [']M'] = '@function.outer',
-- -- [']['] = '@class.outer',
-- -- },
-- -- goto_previous_start = {
-- -- ['[m'] = '@function.outer',
-- -- ['[['] = '@class.outer',
-- -- },
-- -- goto_previous_end = {
-- -- ['[M'] = '@function.outer',
-- -- ['[]'] = '@class.outer',
-- -- },
-- -- },
-- -- select = {
-- -- enable = true,
-- -- keymaps = {
-- -- -- You can use the capture groups defined in textobjects.scm
-- -- ['af'] = '@function.outer',
-- -- ['if'] = '@function.inner',
-- -- ['ac'] = '@class.outer',
-- -- ['ic'] = '@class.inner',
-- -- -- Or you can define your own textobjects like this
-- -- ['iF'] = {
-- -- python = '(function_definition) @function',
-- -- cpp = '(function_definition) @function',
-- -- c = '(function_definition) @function',
-- -- java = '(method_declaration) @function',
-- -- go = '(method_declaration) @function',
-- -- },
-- -- },
-- -- },
-- -- },
-- -- }
-- end,
-- opts = {},
-- -- There are additional nvim-treesitter modules that you can use to interact
-- -- with nvim-treesitter. You should go explore a few and see what interests you:
-- --
-- -- - Incremental selection: Included, see `:help nvim-treesitter-incremental-selection-mod`
-- -- - Show your current context: https://github.com/nvim-treesitter/nvim-treesitter-context
-- -- - Treesitter + textobjects: https://github.com/nvim-treesitter/nvim-treesitter-textobjects
}
+11 -17
View File
@@ -1,17 +1,16 @@
-- Highlight, edit, and navigate code -- Highlight, edit, and navigate code
return { return {
'nvim-treesitter/nvim-treesitter', 'nvim-treesitter/nvim-treesitter',
branch = 'main',
build = ':TSUpdate', build = ':TSUpdate',
branch = 'main',
-- [[ Configure Treesitter ]] See `:help nvim-treesitter` -- [[ Configure Treesitter ]] See `:help nvim-treesitter`
config = function() config = function()
local langs = { require('nvim-treesitter').setup {}
require('nvim-treesitter').install {
'bash', 'bash',
'zsh',
'c', 'c',
'diff', 'diff',
'html', 'html',
'latex',
'lua', 'lua',
'luadoc', 'luadoc',
'markdown', 'markdown',
@@ -20,21 +19,16 @@ return {
'vim', 'vim',
'vimdoc', 'vimdoc',
'php', 'php',
'yaml',
'vue',
'javascript', 'javascript',
'typescript', 'typescript',
'css', 'json',
'blade',
} }
require('nvim-treesitter').install(langs)
vim.api.nvim_create_autocmd('FileType', {
pattern = langs,
callback = function()
vim.treesitter.start()
vim.bo.indentexpr = "v:lua.require'nvim-treesitter'.indentexpr()"
end,
})
end, end,
opts = {},
-- There are additional nvim-treesitter modules that you can use to interact
-- with nvim-treesitter. You should go explore a few and see what interests you:
--
-- - Incremental selection: Included, see `:help nvim-treesitter-incremental-selection-mod`
-- - Show your current context: https://github.com/nvim-treesitter/nvim-treesitter-context
-- - Treesitter + textobjects: https://github.com/nvim-treesitter/nvim-treesitter-textobjects
} }
+6
View File
@@ -8,6 +8,12 @@ return {
}, },
keys = { keys = {
-- 👇 in this section, choose your own keymappings! -- 👇 in this section, choose your own keymappings!
{
'-',
mode = { 'n', 'v' },
'<cmd>Yazi<cr>',
desc = 'Open yazi at the current file',
},
{ {
-- Open in the current working directory -- Open in the current working directory
'\\', '\\',
+243 -28
View File
@@ -6,48 +6,261 @@ local M = {}
local helpers = require 'helpers' local helpers = require 'helpers'
local map = helpers.map local map = helpers.map
local function relevant_directory_from_cwd(cwd) -- Get the last folder name from a path
local basename = vim.fn.fnamemodify(cwd, ':t') local function project_name_from_cwd(cwd)
local nested_folder_names = { 'server', 'frontend', 'frontend2', 'client', 'backend' } return vim.fn.fnamemodify(cwd, ':t')
if vim.tbl_contains(nested_folder_names, basename) then
local parent = vim.fn.fnamemodify(cwd, ':h')
return relevant_directory_from_cwd(parent)
end
return cwd
end end
-- Get the last folder name from a path local function command_with_dir(dir, cmd)
local function project_name_from_dir(dir) if dir then
return vim.fn.fnamemodify(dir, ':t') return '!cd ' .. dir .. ' && ' .. cmd
end
return '!' .. cmd
end end
local function make_laravel_file(dir, cmd)
local cwd = vim.fn.getcwd()
if dir then
vim.fn.chdir(dir)
end
vim.ui.input({ prompt = 'Make: ' .. cmd }, function(input)
if input == nil then
vim.fn.chdir(cwd)
return
end
local output = vim.system({ 'vendor/bin/sail', 'artisan', 'make:' .. cmd, input }):wait().stdout
local new_file = output:match '%[([%w%./]+)%]'
if new_file ~= nil then
vim.cmd('edit ' .. new_file)
end
vim.fn.chdir(cwd)
end)
end
local function get_scope_from_file(filename)
local ext = filename:match '%.(%w+)$'
local base_name = filename:match '^(%w+)%.?%w*$'
local extensionSuffixes = {
php = { 'Controller', 'Repository', 'StoreRequest', 'UpdateRequest', 'Request', 'Resource', 'Test', 'Observer', 'Policy', 'Seeder', 'Factory' },
['[jt]s'] = { 'Service' },
}
for suffix_type, _ in pairs(extensionSuffixes) do
if ext:match(suffix_type) then
local suffixes = extensionSuffixes[suffix_type]
for _, suffix in ipairs(suffixes) do
local scope = base_name:match('^(%w+)' .. suffix .. '$')
if scope then
return scope
end
end
end
end
return base_name
end
local function navigate_using_suffix(suffix)
local scope = get_scope_from_file(vim.fn.expand '%:t')
local file_name = scope .. suffix
local file_path = vim.system({ 'git', 'ls-files', file_name, '**/' .. file_name }):wait().stdout
if file_path ~= '' then
vim.cmd('edit ' .. file_path)
else
vim.notify('File ' .. file_name .. ' not found in git ls-files', vim.log.levels.ERROR)
end
end
local function create_navigation_maps(maps)
for key, suffix_and_name in pairs(maps) do
map('<Leader>n' .. key, function()
navigate_using_suffix(suffix_and_name[1])
end, { desc = 'Navigate to relevant ' .. suffix_and_name[2] })
end
end
local function create_bookmark(key, bookmark)
map('<Leader>b' .. key, function()
vim.cmd('edit ' .. bookmark)
end, { desc = 'Navigate to ' .. bookmark })
end
local function create_bookmark_maps(maps)
for key, bookmark in pairs(maps) do
create_bookmark(key, bookmark)
end
end
local function laravel_bookmarks_with_dir(dir)
create_bookmark_maps {
['e'] = dir .. '/.env',
['l'] = dir .. '/storage/logs/laravel.log',
['w'] = dir .. '/routes/web.php',
['a'] = dir .. '/routes/api.php',
['m'] = dir .. '/database/migrations',
['dc'] = dir .. '/app/Core/',
['dd'] = dir .. '/app/Data/',
['dE'] = dir .. '/app/Enums/',
['de'] = dir .. '/app/Events/',
['dh'] = dir .. '/app/Http/',
['dj'] = dir .. '/app/Jobs/',
['dl'] = dir .. '/app/Listeners/',
['dM'] = dir .. '/app/Mail/',
['dm'] = dir .. '/app/Models/',
['dn'] = dir .. '/app/Notifications/',
['do'] = dir .. '/app/Observers/',
['dp'] = dir .. '/app/Providers/',
['pa'] = dir .. '/app/Providers/AppServiceProvider.php',
['pe'] = dir .. '/app/Providers/EventServiceProvider.php',
['cA'] = dir .. '/config/app.php',
['ca'] = dir .. '/config/auth.php',
['cb'] = dir .. '/config/broadcasting.php',
['cd'] = dir .. '/config/database.php',
['cf'] = dir .. '/config/filesystems.php',
['ch'] = dir .. '/config/filesystems.php',
['cl'] = dir .. '/config/logging.php',
['cm'] = dir .. '/config/mail.php',
['cq'] = dir .. '/config/queue.php',
['cS'] = dir .. '/config/services.php',
['cs'] = dir .. '/config/session.php',
}
end
local function laravel_keymaps(dir)
map('sl ', command_with_dir(dir, 'vendor/bin/sail '), { desc = 'Run sail command' }, 'c')
map('art ', command_with_dir(dir, 'php artisan '), { desc = 'Run artisan command' }, 'c')
map('sart ', command_with_dir(dir, 'vendor/bin/sail artisan '), { desc = 'Run artisan command with sail' }, 'c')
map('cmp ', command_with_dir(dir, 'composer '), { desc = 'Run composer script' }, 'c')
map('<Leader>pm', ':' .. command_with_dir(dir, 'vendor/bin/sail artisan migrate<CR>'))
map('<Leader>pr', ':' .. command_with_dir(dir, 'vendor/bin/sail artisan migrate:rollback<CR>'))
map('<Leader>pM', ':' .. command_with_dir(dir, 'vendor/bin/sail artisan make:'))
create_navigation_maps {
['m'] = { '.php', 'model' },
['c'] = { 'Controller.php', 'controller' },
['p'] = { 'Policy.php', 'policy' },
['R'] = { 'Resource.php', 'resource' },
['r'] = { 'Request.php', 'request' },
['t'] = { 'Test.php', 'test file' },
}
if dir == nil then
dir = ''
end
laravel_bookmarks_with_dir(dir)
end
local function laravel_makes(dir)
for key, name in pairs {
c = 'controller',
d = 'data',
e = 'event',
f = 'factory',
j = 'job',
l = 'listener',
ma = 'mail',
mi = 'migration',
mo = 'model',
mw = 'middleware',
n = 'notification',
o = 'observer',
pi = 'model --pivot',
po = 'policy',
pr = 'provider',
t = 'test --pest',
v = 'view',
x = 'exception',
} do
map('<Leader>m' .. key, function()
make_laravel_file(dir, name)
end, { desc = 'Make and navigate to relevant ' .. name })
end
end
-- Define per-project configuration here.
-- Keys are folder names (last segment of your cwd).
local PROJECTS = {
-- Example: a repo folder
['runcats'] = function(dir)
-- local dump_buf = vim.api.nvim_create_buf(false, true)
-- vim.api.nvim_set_option_value('bufhidden', 'hide', { buf = dump_buf })
-- vim.api.nvim_set_option_value('modifiable', false, { buf = dump_buf })
--
-- vim.fn.jobstart('cd server && sail artisan dump-server', {
-- term = true,
-- })
-- map('<leader>dd', function()
-- local height = math.ceil(vim.o.lines * 0.8)
-- local width = math.ceil(vim.o.columns * 0.8)
-- vim.api.nvim_open_win(dump_buf, true, {
-- style = 'minimal',
-- relative = 'editor',
-- width = width,
-- height = height,
-- row = math.ceil((vim.o.lines - height) / 2),
-- col = math.ceil((vim.o.columns - width) / 2),
-- border = 'single',
-- })
--
-- vim.cmd.startinsert()
-- end, { desc = 'Open the dump-server window' })
map(
'<leader>pl',
':cexpr system("cd server && vendor/bin/phpstan analyse --no-progress --error-format=raw --memory-limit=2G -vv") <CR>',
{ desc = 'Run lint' }
)
map('<leader>pd', function()
helpers.open_term { cmd = 'lazysql mysql://root@localhost:3306/runcats' }
end, { desc = 'Open database manager' })
map('s ', '!cd server && ', { desc = 'Run command in server directory' }, 'c')
map('c ', '!cd client && ', { desc = 'Run command in client directory' }, 'c')
laravel_keymaps 'server'
laravel_makes 'server'
map('yrn ', '!cd client && yarn ', { desc = 'Run yarn script' }, 'c')
map('<Leader>pt', ':!cd server && php artisan typescript:transform --format<CR>', { desc = 'Compile typescript' })
require('conform').formatters.pint = {
append_args = {
'--config=' .. dir .. '/server/pint.json',
},
}
create_bookmark('E', dir .. '/client/src/types/api/endpointMap.d.ts')
create_bookmark('q', dir .. '/client/quasar.config.ts')
create_bookmark('db', dir .. '/client/src/boot')
create_bookmark('ds', dir .. '/client/src/stores')
create_bookmark('dS', dir .. '/client/src/services')
end,
['hylark'] = function(dir)
map('<leader>pl', ':cexpr system("vendor/bin/phpstan analyse --no-progress --error-format=raw --memory-limit=2G -vv") <CR>', { desc = 'Run lint' })
map('<leader>pd', function()
helpers.open_term { cmd = 'lazysql pgsql://homestead:password@localhost:5432/homestead' }
end, { desc = 'Open database manager' })
laravel_keymaps()
laravel_makes()
map('yrn ', '!cd frontend && yarn ', { desc = 'Run yarn script' }, 'c')
map('<Leader>pm', ':vendor/bin/sail composer migrate<CR>')
end,
default = function() end,
}
local last_applied = nil local last_applied = nil
function M.apply(cwd) function M.apply(cwd)
local dir = relevant_directory_from_cwd(cwd or vim.fn.getcwd()) local dir = cwd or vim.fn.getcwd()
local name = project_name_from_dir(dir) local name = project_name_from_cwd(dir)
if last_applied == name then if last_applied == name then
return return
end end
last_applied = name last_applied = name
-- Check if project lua file exists in nvim config path local setup = PROJECTS[name] or PROJECTS.default
if vim.fn.filereadable(vim.fn.stdpath 'config' .. '/lua/projects/' .. name .. '.lua') == 1 then if type(setup) == 'function' then
local setup = require('projects.' .. name)
helpers.edit_cf('w', '/lua/projects/' .. name .. '.lua')
setup(dir) setup(dir)
vim.notify(('Project config loaded: %s'):format(name), vim.log.levels.INFO, { title = 'projects.lua' }) vim.notify(('Project config loaded: %s'):format(name), vim.log.levels.INFO, { title = 'projects.lua' })
-- Source the project file to apply any new settings
vim.api.nvim_create_autocmd({ 'BufWritePost' }, {
pattern = { vim.fn.stdpath 'config' .. '/lua/projects/' .. name .. '.lua' },
callback = function()
package.loaded['projects.' .. name] = nil
local setup = require('projects.' .. name)
setup(dir)
vim.notify(('Project config reloaded: %s'):format(name), vim.log.levels.INFO, { title = 'projects.lua' })
end,
})
end end
end end
@@ -77,3 +290,5 @@ vim.api.nvim_create_user_command('ProjectReload', function()
M.apply() M.apply()
end, {}) end, {})
-- Keep your helper line
require('helpers').edit_cf('w', '/lua/projects.lua')
-184
View File
@@ -1,184 +0,0 @@
local helpers = require 'helpers'
local map = helpers.map
local M = {}
local function command_with_dir(dir, cmd)
if dir then
return '!cd ' .. dir .. ' && ' .. cmd
end
return '!' .. cmd
end
local function make_laravel_file(dir, cmd)
local cwd = vim.fn.getcwd()
if dir then
vim.fn.chdir(dir)
end
vim.ui.input({ prompt = 'Make: ' .. cmd }, function(input)
if input == nil then
vim.fn.chdir(cwd)
return
end
local output = vim.system({ 'vendor/bin/sail', 'artisan', 'make:' .. cmd, input }):wait().stdout
local new_file = output:match '%[([%w%./]+)%]'
if new_file ~= nil then
vim.cmd('edit ' .. new_file)
end
vim.fn.chdir(cwd)
end)
end
local function get_scope_from_file(filename)
local ext = filename:match '%.(%w+)$'
local base_name = filename:match '^(%w+)%.?%w*$'
local extensionSuffixes = {
php = { 'Controller', 'Repository', 'StoreRequest', 'UpdateRequest', 'Request', 'Resource', 'Test', 'Observer', 'Policy', 'Seeder', 'Factory' },
['[jt]s'] = { 'Service' },
}
for suffix_type, _ in pairs(extensionSuffixes) do
if ext:match(suffix_type) then
local suffixes = extensionSuffixes[suffix_type]
for _, suffix in ipairs(suffixes) do
local scope = base_name:match('^(%w+)' .. suffix .. '$')
if scope then
return scope
end
end
end
end
return base_name
end
local function navigate_using_suffix(suffix)
local scope = get_scope_from_file(vim.fn.expand '%:t')
local file_name = scope .. suffix
local file_path = vim.system({ 'git', 'ls-files', file_name, '**/' .. file_name }):wait().stdout
if file_path ~= '' then
vim.cmd('edit ' .. file_path)
else
vim.notify('File ' .. file_name .. ' not found in git ls-files', vim.log.levels.ERROR)
end
end
local function create_navigation_maps(maps)
for key, suffix_and_name in pairs(maps) do
map('<Leader>n' .. key, function()
navigate_using_suffix(suffix_and_name[1])
end, { desc = 'Navigate to relevant ' .. suffix_and_name[2] })
end
end
local function create_bookmark_maps(maps)
for key, bookmark in pairs(maps) do
helpers.create_bookmark(key, bookmark)
end
end
local function laravel_bookmarks_with_dir(dir)
create_bookmark_maps {
['e'] = dir .. '.env',
['l'] = dir .. 'storage/logs/laravel.log',
['w'] = dir .. 'routes/web.php',
['a'] = dir .. 'routes/api.php',
['m'] = dir .. 'database/migrations',
['dc'] = dir .. 'app/Core/',
['dd'] = dir .. 'app/Data/',
['dE'] = dir .. 'app/Enums/',
['de'] = dir .. 'app/Events/',
['dh'] = dir .. 'app/Http/',
['dj'] = dir .. 'app/Jobs/',
['dl'] = dir .. 'app/Listeners/',
['dM'] = dir .. 'app/Mail/',
['dm'] = dir .. 'app/Models/',
['dn'] = dir .. 'app/Notifications/',
['do'] = dir .. 'app/Observers/',
['dp'] = dir .. 'app/Providers/',
['pa'] = dir .. 'app/Providers/AppServiceProvider.php',
['pe'] = dir .. 'app/Providers/EventServiceProvider.php',
['cA'] = dir .. 'config/app.php',
['ca'] = dir .. 'config/auth.php',
['cb'] = dir .. 'config/broadcasting.php',
['cd'] = dir .. 'config/database.php',
['cf'] = dir .. 'config/filesystems.php',
['ch'] = dir .. 'config/filesystems.php',
['cl'] = dir .. 'config/logging.php',
['cm'] = dir .. 'config/mail.php',
['cq'] = dir .. 'config/queue.php',
['cS'] = dir .. 'config/services.php',
['cs'] = dir .. 'config/session.php',
}
end
local function laravel_utils(dir)
map('<Leader>D', function()
helpers.open_term {
cmd = dir .. 'vendor/bin/var-dump-server',
buf_opts = {
bufhidden = 'hide',
modifiable = false,
},
}
end, { desc = 'Open dump-server window' })
end
local function laravel_keymaps(dir)
map('sl ', command_with_dir(dir, 'vendor/bin/sail '), { desc = 'Run sail command' }, 'c')
map('art ', command_with_dir(dir, 'php artisan '), { desc = 'Run artisan command' }, 'c')
map('sart ', command_with_dir(dir, 'vendor/bin/sail artisan '), { desc = 'Run artisan command with sail' }, 'c')
map('cmp ', command_with_dir(dir, 'composer '), { desc = 'Run composer script' }, 'c')
map('<Leader>pm', ':' .. command_with_dir(dir, 'vendor/bin/sail artisan migrate<CR>'))
map('<Leader>pr', ':' .. command_with_dir(dir, 'vendor/bin/sail artisan migrate:rollback<CR>'))
map('<Leader>pM', ':' .. command_with_dir(dir, 'vendor/bin/sail artisan make:'))
create_navigation_maps {
['m'] = { '.php', 'model' },
['c'] = { 'Controller.php', 'controller' },
['p'] = { 'Policy.php', 'policy' },
['R'] = { 'Resource.php', 'resource' },
['r'] = { 'Request.php', 'request' },
['t'] = { 'Test.php', 'test file' },
}
if dir == nil then
dir = ''
end
laravel_bookmarks_with_dir(dir)
end
local function laravel_makes(dir)
for key, name in pairs {
c = 'controller',
d = 'data',
e = 'event',
f = 'factory',
j = 'job',
l = 'listener',
ma = 'mail',
mi = 'migration',
mo = 'model',
mw = 'middleware',
n = 'notification',
o = 'observer',
pi = 'model --pivot',
po = 'policy',
pr = 'provider',
t = 'test --pest',
v = 'view',
x = 'exception',
} do
map('<Leader>m' .. key, function()
make_laravel_file(dir, name)
end, { desc = 'Make and navigate to relevant ' .. name })
end
end
M.laravel_keymaps = laravel_keymaps
M.laravel_makes = laravel_makes
M.laravel_utils = laravel_utils
M.laravel_bookmarks = laravel_bookmarks_with_dir
return M
-106
View File
@@ -1,106 +0,0 @@
local laravel_utils = require 'projects.framework_utils.laravel'
local helpers = require 'helpers'
local map = helpers.map
local create_bookmark = helpers.create_bookmark
local function switch_to(dir)
if vim.fn.getcwd() == dir then
return
end
vim.fn.chdir(dir)
vim.notify('Changed working directory to ' .. dir, vim.log.levels.INFO, { title = 'hylark.lua' })
end
return function(dir)
local function switch_to_server()
switch_to(dir .. '/server')
end
local function switch_to_frontend()
switch_to(dir .. '/frontend2')
end
local function switch_to_root()
switch_to(dir)
end
local function compile_typescript()
vim.fn.jobstart('php artisan typescript:transform', { cwd = dir .. '/server', stdout_buffered = true })
vim.notify('Compiling typescript types', vim.log.levels.INFO, { title = 'hylark.lua' })
end
map('<leader>pl', ':cexpr system("vendor/bin/phpstan analyse --no-progress --error-format=raw --memory-limit=2G -vv") <CR>', { desc = 'Run lint' })
map('<leader>T', function()
helpers.open_term { cmd = 'lazysql pgsql://homestead:password@localhost:5432/homestead' }
end, { desc = 'Open database manager' })
map('yrn ', '!cd frontend && yarn ', { desc = 'Run yarn script' }, 'c')
map('<Leader>pm', ':vendor/bin/sail composer migrate<CR>')
map('<Leader>pcs', switch_to_server, { desc = 'Change working directory to server' })
map('<Leader>pcf', switch_to_frontend, { desc = 'Change working directory to frontend2' })
map('<Leader>pcr', switch_to_root, { desc = 'Change working directory to root' })
map('<Leader>pt', compile_typescript, { desc = 'Compile typescript' })
vim.api.nvim_create_autocmd({ 'BufWritePost' }, {
pattern = { '*.php', '.env', '.graphql' },
callback = function()
if not vim.fn.expand('%:p'):match(dir .. '/server/') then
vim.fn.jobstart('vendor/bin/sail artisan octane:reload', { stdout_buffered = true })
vim.fn.jobstart('vendor/bin/sail artisan horizon:terminate', { stdout_buffered = true })
end
if vim.fn.search '#\\[TypeScript\\]' ~= 0 then
compile_typescript()
end
end,
})
vim.api.nvim_create_autocmd({ 'BufWritePre' }, {
pattern = { '*.js', '*.ts', '*.vue' },
callback = function(args)
vim.env.ESLINT_D_PPID = vim.fn.getpid()
local cwd = vim.fn.getcwd()
if vim.fn.expand('%:p'):match(dir .. '/frontend2/') then
cwd = dir .. '/frontend2/'
end
require('helpers').format_buffer('eslint_d', args.buf, cwd)
end,
})
vim.api.nvim_create_autocmd({ 'BufWritePre' }, {
pattern = { '*.php' },
callback = function(args)
local cwd = vim.fn.getcwd()
if vim.fn.expand('%:p'):match(dir .. '/server/') then
cwd = dir .. '/server/'
end
require('helpers').format_buffer('pint', args.buf, cwd)
end,
})
vim.api.nvim_create_autocmd({ 'BufWritePost' }, {
pattern = { '.graphql' },
callback = function()
vim.fn.jobstart('vendor/bin/sail artisan lighthouse:clear-cache', { stdout_buffered = true })
end,
})
vim.api.nvim_create_autocmd({ 'BufEnter' }, {
pattern = { dir .. '**' },
callback = function()
if vim.fn.expand('%:p'):match(dir .. '/server/') then
switch_to_server()
elseif vim.fn.expand('%:p'):match(dir .. '/frontend2/') then
switch_to_frontend()
else
switch_to_root()
end
end,
})
laravel_utils.laravel_keymaps 'server/'
laravel_utils.laravel_makes 'server/'
laravel_utils.laravel_utils 'server/'
laravel_utils.laravel_bookmarks(dir .. '/server/')
create_bookmark('s', dir .. '/frontend2/src/types/api/server-types.d.ts')
end
-44
View File
@@ -1,44 +0,0 @@
local laravel_utils = require 'projects.framework_utils.laravel'
local helpers = require 'helpers'
local map = helpers.map
local create_bookmark = helpers.create_bookmark
return function(dir)
map(
'<leader>pl',
':cexpr system("cd server && vendor/bin/phpstan analyse --no-progress --error-format=raw --memory-limit=2G -vv") <CR>',
{ desc = 'Run lint' }
)
map('<leader>T', function()
helpers.open_term { cmd = 'lazysql mysql://root@localhost:3306/runcats' }
end, { desc = 'Open database manager' })
map('s ', '!cd server && ', { desc = 'Run command in server directory' }, 'c')
map('c ', '!cd client && ', { desc = 'Run command in client directory' }, 'c')
map('yrn ', '!cd client && yarn ', { desc = 'Run yarn script' }, 'c')
map('<Leader>pt', ':!cd server && php artisan typescript:transform --format<CR>', { desc = 'Compile typescript' })
vim.api.nvim_create_autocmd({ 'BufWritePre' }, {
pattern = { '*.php' },
callback = function(args)
helpers.format_buffer('pint', args.buf)
end,
})
vim.api.nvim_create_autocmd({ 'BufWritePre' }, {
pattern = { '*.js', '*.ts', '*.vue' },
callback = function(args)
helpers.format_buffer('eslint_d', args.buf)
end,
})
laravel_utils.laravel_keymaps 'server/'
laravel_utils.laravel_makes 'server/'
laravel_utils.laravel_utils 'server/'
laravel_utils.laravel_bookmarks 'server/'
create_bookmark('E', dir .. '/client/src/types/api/endpointMap.d.ts')
create_bookmark('q', dir .. '/client/quasar.config.ts')
create_bookmark('db', dir .. '/client/src/boot')
create_bookmark('ds', dir .. '/client/src/stores')
create_bookmark('dS', dir .. '/client/src/services')
end
+29 -145
View File
@@ -1,161 +1,45 @@
local ls = require 'luasnip' local ls = require 'luasnip'
local s = ls.snippet local s = ls.snippet
local sn = ls.snippet_node
local fn = ls.function_node
local ms = ls.multi_snippet
local t = ls.text_node local t = ls.text_node
local c = ls.choice_node
local i = ls.insert_node local i = ls.insert_node
local f = ls.function_node local f = ls.function_node
local d = ls.dynamic_node
local fmt = require('luasnip.extras.fmt').fmt
local rep = require('luasnip.extras').rep
local extend_decorator = require 'luasnip.util.extend_decorator'
local fmta = extend_decorator.apply(fmt, { delimiters = '#~' })
local utils = require 'snippets.snip_utils'
local tr = utils.tr
local etr = utils.etr
local atr = utils.atr
local ctr = utils.ctr
local bs = utils.bs
return { return {
s(etr('du ', 'Dump a variable to the console'), fmta('console.log(#~);', { i(0) })), s('du', { t 'console.log(', i(0), t ');' }),
s(
etr('vue', 'Vue Single File Component skeleton'),
fmta(
[[
<template>
</template>
<script setup>
#~
</script>
<style scoped>
</style>
]],
{ i(0) }
)
),
-- bs(atr('t ', 'this'), fmta('this.#~', { i(0) })), s('vue', {
t { '<template>', '' },
s(etr('return ', 'Add semicolon after return'), fmta('return #~;', { i(0) })), t { '', '</template>', '', '', '<script setup>', '' },
s(etr('rt ', 'return alias'), fmta('return #~;', { i(0) })), i(0),
t { '', '</script>', '', '', '<style scoped>', '', '.o-share-page {', '}', '', '</style>' },
s(etr('const ', 'const declaration'), {
c(1, {
sn(nil, fmta('const #~ = #~;', { i(1), i(2) })),
sn(
nil,
fmta(
[[
const #~ = (#~) => {
#~
}
]],
{ i(1), i(2), i(3) }
)
),
}),
}), }),
s(etr('ref ', 'ref'), { s('fun', {
c(1, { t 'function ',
sn(nil, fmta('const #~ = ref(#~);', { i(1), i(2) })), i(1),
sn( t '(',
nil, i(2),
fmta( t ') {',
[[ t { '', ' ' },
const #~ = ref<#~>(#~); i(0),
]], t { '', '}' },
{ i(1), i(2), i(3) }
)
),
}),
}), }),
s(etr('com ', 'computed'), { s('afun', {
c(1, { t 'async function ',
sn(nil, fmta('const #~ = computed(() => #~);', { i(1), i(2) })), i(1),
sn( t '(',
nil, i(2),
fmta( t ') {',
[[ t { '', ' ' },
const #~ = computed(() => { i(0),
#~ t { '', '}' },
})
]],
{ i(1), i(2) }
)
),
}),
}), }),
s( s('()', {
etr('fn ', 'function block'), t '() => {',
fmta( t { '', ' ' },
[[ i(0),
function #~(#~) { t { '', '}' },
#~
}
]],
{ i(1), i(2), i(0) }
)
),
bs(atr('fn ', 'function block'), {
c(1, {
sn(
nil,
fmta(
[[
(#~) => {
#~
}
]],
{ i(1), i(2) }
)
),
sn(
nil,
fmta(
[[
function (#~) {
#~
}
]],
{ i(1), i(2) }
)
),
}),
}),
bs(atr('afn ', 'async function block'), {
c(1, {
sn(
nil,
fmta(
[[
async (#~) => {
#~
}
]],
{ i(1), i(2) }
)
),
sn(
nil,
fmta(
[[
async function (#~) {
#~
}
]],
{ i(1), i(2) }
)
),
}),
}), }),
} }
+140 -157
View File
@@ -16,7 +16,6 @@ local fmta = extend_decorator.apply(fmt, { delimiters = '#~' })
local utils = require 'snippets.snip_utils' local utils = require 'snippets.snip_utils'
local tr = utils.tr local tr = utils.tr
local etr = utils.etr local etr = utils.etr
local Etr = utils.Etr
local atr = utils.atr local atr = utils.atr
local ctr = utils.ctr local ctr = utils.ctr
local bs = utils.bs local bs = utils.bs
@@ -70,10 +69,10 @@ return {
--------------- ---------------
-- DEBUGGING -- -- DEBUGGING --
--------------- ---------------
s(etr('du ', 'Dump a variable to the dump server', { priority = 1001 }), fmta('dump(#~);', { i(0) })), s(etr('du ', 'Dump a variable to the dump server'), fmta('dump(#~);', { i(0) })),
bs(etr('du ', 'Dump a variable to the dump server'), fmta('dump(#~)', { i(0) })), bs(atr('du ', 'Dump a variable to the dump server'), fmta('dump(#~)', { i(0) })),
s(etr('r ', 'ray', { priority = 1001 }), fmta('ray(#~);', { i(0) })), s(etr('r ', 'ray'), fmta('ray(#~);', { i(0) })),
bs(etr('r ', 'ray'), fmta('ray(#~)', { i(0) })), bs(atr('r ', 'ray'), fmta('ray(#~)', { i(0) })),
s(etr('dt ', 'Dump PHPStan type definition'), fmta('\\PhpStan\\dumpType(#~);', { i(0) })), s(etr('dt ', 'Dump PHPStan type definition'), fmta('\\PhpStan\\dumpType(#~);', { i(0) })),
s( s(
etr('ql ', 'Log all queries'), etr('ql ', 'Log all queries'),
@@ -108,7 +107,6 @@ return {
}), }),
s(etr('@v', '@var docblock'), fmta('/** @var #~ $#~ */', { i(1), i(0) })), s(etr('@v', '@var docblock'), fmta('/** @var #~ $#~ */', { i(1), i(0) })),
s(ctr('@v', '@var docblock'), fmta('@var #~ $#~', { i(1), i(0) })), s(ctr('@v', '@var docblock'), fmta('@var #~ $#~', { i(1), i(0) })),
s(Etr('@pi', '@phpstan-ignore'), fmta('// @phpstan-ignore #~ (#~)', { i(1), i(0) })),
s(ctr('* @pr', 'Class property docblock'), fmta('* @property #~ $#~', { i(1), i(0) })), s(ctr('* @pr', 'Class property docblock'), fmta('* @property #~ $#~', { i(1), i(0) })),
s(ctr('* @pb', 'Class boolean property docblock'), fmta('* @property bool $#~', { i(0) })), s(ctr('* @pb', 'Class boolean property docblock'), fmta('* @property bool $#~', { i(0) })),
s(ctr('* @pi', 'Class int property docblock'), fmta('* @property int $#~', { i(0) })), s(ctr('* @pi', 'Class int property docblock'), fmta('* @property int $#~', { i(0) })),
@@ -170,9 +168,9 @@ return {
) )
), ),
s(etr('return ', 'Add semicolon after return'), fmta('return #~;', { i(0) })), s(etr('return ', 'Add semicolon after return'), fmta('return #~;', { i(0) })),
-- s(atr(' use ', 'Add use to function'), fmta(' use (#~)', { i(0) })), s(atr(' use ', 'Add use to function'), fmta(' use (#~)', { i(0) })),
s(etr('rt ', 'return alias'), fmta('return #~;', { i(0) })), s(etr('rt ', 'return alias'), fmta('return #~;', { i(0) })),
bs(atr('fn ', 'Shorthand function block'), { bs(etr('fn ', 'Shorthand function block'), {
c(1, { c(1, {
sn(nil, fmta('fn (#~) => #~', { i(1), i(2) })), sn(nil, fmta('fn (#~) => #~', { i(1), i(2) })),
sn( sn(
@@ -188,7 +186,7 @@ return {
), ),
}), }),
}), }),
bs(atr('fun ', 'Shorthand function block'), { bs(etr('fun ', 'Shorthand function block'), {
c(1, { c(1, {
sn( sn(
nil, nil,
@@ -214,36 +212,36 @@ return {
), ),
}), }),
}), }),
-- s( s(
-- etr('con', 'Constructor function block'), etr('con', 'Constructor function block'),
-- c(1, { c(1, {
-- sn( sn(
-- nil, nil,
-- fmta( fmta(
-- [[ [[
-- public function __construct(#~) public function __construct(#~)
-- { {
-- #~ #~
-- } }
-- ]], ]],
-- { i(1), i(2) } { i(1), i(2) }
-- ) )
-- ), ),
-- sn( sn(
-- nil, nil,
-- fmta( fmta(
-- [[ [[
-- public function __construct( public function __construct(
-- #~ #~
-- ) { ) {
-- #~ #~
-- } }
-- ]], ]],
-- { i(1), i(2) } { i(1), i(2) }
-- ) )
-- ), ),
-- }) })
-- ), ),
bs(atr('function', 'Shorthand function block'), fmta('fun', {})), bs(atr('function', 'Shorthand function block'), fmta('fun', {})),
bs(atr('s%$', 'string type parameter'), fmta('string $#~', { i(0, 'var') })), bs(atr('s%$', 'string type parameter'), fmta('string $#~', { i(0, 'var') })),
bs(atr('i%$', 'int type parameter'), fmta('int $#~', { i(0, 'var') })), bs(atr('i%$', 'int type parameter'), fmta('int $#~', { i(0, 'var') })),
@@ -360,35 +358,20 @@ return {
etr('test', 'Create a test function'), etr('test', 'Create a test function'),
fmta( fmta(
[[ [[
test('#~', function () { test(#~ function () {
#~ #~
}); })
]], ]],
{ i(1), i(0) } { i(1), i(0) }
) )
), ),
s( s(etr('nn ', 'Assert not null'), fmta('Assert::notNull(#~)', { i(0) })),
etr('it ', 'Create a test function'),
fmta(
[[
it('#~', function () {
#~
});
]],
{ i(1), i(0) }
)
),
s(etr('nn ', 'Assert not null'), fmta('Assert::notNull(#~);', { i(0) })),
s(etr('ast ', 'Assert string'), fmta('Assert::string(#~);', { i(0) })),
s(etr('ain ', 'Assert integer'), fmta('Assert::integer(#~);', { i(0) })),
s(etr('aio ', 'Assert instance of'), fmta('Assert::isInstanceOf(#~);', { i(0) })),
s(etr('at ', 'Assert true'), fmta('Assert::true(#~);', { i(0) })),
------------- -------------
-- LARAVEL -- -- LARAVEL --
------------- -------------
s( s(
etr('bt', 'belongsTo Laravel relationship method'), etr('bt', 'belongsTo Laravel relationship method'),
c(1, { c(0, {
sn( sn(
nil, nil,
fmta( fmta(
@@ -401,7 +384,7 @@ return {
return $this->belongsTo(#~::class); return $this->belongsTo(#~::class);
} }
]], ]],
{ rep(1), i(2), i(1) } { rep(1), i(0), i(1) }
) )
), ),
sn( sn(
@@ -416,14 +399,14 @@ return {
return $this->belongsTo(#~::class, #~); return $this->belongsTo(#~::class, #~);
} }
]], ]],
{ rep(1), i(2), i(1), i(3) } { rep(1), i(2), i(1), i(0) }
) )
), ),
}) })
), ),
s( s(
etr('hm', 'hasMany Laravel relationship method'), etr('hm', 'hasMany Laravel relationship method'),
c(1, { c(0, {
sn( sn(
nil, nil,
fmta( fmta(
@@ -436,7 +419,7 @@ return {
return $this->hasOne(#~::class); return $this->hasOne(#~::class);
} }
]], ]],
{ rep(1), i(2), i(1) } { rep(1), i(0), i(1) }
) )
), ),
sn( sn(
@@ -451,14 +434,14 @@ return {
return $this->hasOne(#~::class, #~); return $this->hasOne(#~::class, #~);
} }
]], ]],
{ rep(1), i(2), i(1), i(3) } { rep(1), i(2), i(1), i(0) }
) )
), ),
}) })
), ),
s( s(
etr('ho', 'hasOne Laravel relationship method'), etr('ho', 'hasOne Laravel relationship method'),
c(1, { c(0, {
sn( sn(
nil, nil,
fmta( fmta(
@@ -471,7 +454,7 @@ return {
return $this->hasOne(#~::class); return $this->hasOne(#~::class);
} }
]], ]],
{ rep(1), i(2), i(1) } { rep(1), i(0), i(1) }
) )
), ),
sn( sn(
@@ -486,14 +469,14 @@ return {
return $this->hasOne(#~::class, #~); return $this->hasOne(#~::class, #~);
} }
]], ]],
{ rep(1), i(2), i(1), i(3) } { rep(1), i(2), i(1), i(0) }
) )
), ),
}) })
), ),
s( s(
etr('bm', 'belongsToMany Laravel relationship method'), etr('bm', 'belongsToMany Laravel relationship method'),
c(1, { c(0, {
sn( sn(
nil, nil,
fmta( fmta(
@@ -506,7 +489,7 @@ return {
return $this->belongsToMany(#~::class, #~); return $this->belongsToMany(#~::class, #~);
} }
]], ]],
{ rep(1), i(2), i(1), i(3) } { rep(1), i(2), i(1), i(0) }
) )
), ),
sn( sn(
@@ -521,97 +504,97 @@ return {
return $this->belongsToMany(#~::class, #~, #~); return $this->belongsToMany(#~::class, #~, #~);
} }
]], ]],
{ rep(1), i(2), i(1), i(3), i(4) } { rep(1), i(2), i(1), i(3), i(0) }
)
),
})
),
s(
atr('->wr', 'Eloquent where method'),
c(0, {
sn(nil, fmta("->where('#~', #~)", { i(1), i(0) })),
sn(nil, fmta('->where(fn ($query) => #~)', { i(0) })),
sn(
nil,
fmta(
[[
->where(function ($query) {
#~
})
]],
{ i(0) }
)
),
sn(
nil,
fmta(
[[
->where(function ($query) use (#~) {
#~
})
]],
{ i(1), i(0) }
)
),
})
),
s(atr('->wi', 'Eloquent where in method'), fmta("->whereIn('#~', #~)", { i(1), i(0) })),
s(atr('->wn', 'Eloquent where not in method'), fmta("->whereNotIn('#~', #~)", { i(1), i(0) })),
s(
atr('->wh', 'Eloquent where has method'),
c(0, {
sn(nil, fmta("->whereHas('#~')", { i(1) })),
sn(nil, fmta("->whereHas('#~', fn ($query) => #~)", { i(1), i(0) })),
sn(
nil,
fmta(
[[
->whereHas('#~', function ($query) {
#~
})
]],
{ i(1), i(0) }
)
),
sn(
nil,
fmta(
[[
->whereHas('#~', function ($query) use (#~) {
#~
})
]],
{ i(1), i(2), i(0) }
)
),
})
),
s(
atr('->we', 'Eloquent where exists method'),
c(0, {
sn(nil, fmta('->whereExists(fn ($query) => #~)', { i(0) })),
sn(
nil,
fmta(
[[
->whereExists(function ($query) {
#~
})
]],
{ i(0) }
)
),
sn(
nil,
fmta(
[[
->whereExists(function ($query) use (#~) {
#~
})
]],
{ i(1), i(0) }
) )
), ),
}) })
), ),
-- s(
-- atr('->wr', 'Eloquent where method'),
-- c(0, {
-- sn(nil, fmta("->where('#~', #~)", { i(1), i(0) })),
-- sn(nil, fmta('->where(fn ($query) => #~)', { i(0) })),
-- sn(
-- nil,
-- fmta(
-- [[
-- ->where(function ($query) {
-- #~
-- })
-- ]],
-- { i(0) }
-- )
-- ),
-- sn(
-- nil,
-- fmta(
-- [[
-- ->where(function ($query) use (#~) {
-- #~
-- })
-- ]],
-- { i(1), i(0) }
-- )
-- ),
-- })
-- ),
-- s(atr('->wi', 'Eloquent where in method'), fmta("->whereIn('#~', #~)", { i(1), i(0) })),
-- s(atr('->wn', 'Eloquent where not in method'), fmta("->whereNotIn('#~', #~)", { i(1), i(0) })),
-- s(
-- atr('->wha', 'Eloquent where has method'),
-- c(0, {
-- sn(nil, fmta("->whereHas('#~')", { i(1) })),
-- sn(nil, fmta("->whereHas('#~', fn ($query) => #~)", { i(1), i(0) })),
-- sn(
-- nil,
-- fmta(
-- [[
-- ->whereHas('#~', function ($query) {
-- #~
-- })
-- ]],
-- { i(1), i(0) }
-- )
-- ),
-- sn(
-- nil,
-- fmta(
-- [[
-- ->whereHas('#~', function ($query) use (#~) {
-- #~
-- })
-- ]],
-- { i(1), i(2), i(0) }
-- )
-- ),
-- })
-- ),
-- s(
-- atr('->we', 'Eloquent where exists method'),
-- c(0, {
-- sn(nil, fmta('->whereExists(fn ($query) => #~)', { i(0) })),
-- sn(
-- nil,
-- fmta(
-- [[
-- ->whereExists(function ($query) {
-- #~
-- })
-- ]],
-- { i(0) }
-- )
-- ),
-- sn(
-- nil,
-- fmta(
-- [[
-- ->whereExists(function ($query) use (#~) {
-- #~
-- })
-- ]],
-- { i(1), i(0) }
-- )
-- ),
-- })
-- ),
} }
-16
View File
@@ -11,7 +11,6 @@ local d = ls.dynamic_node
local fmt = require('luasnip.extras.fmt').fmt local fmt = require('luasnip.extras.fmt').fmt
local rep = require('luasnip.extras').rep local rep = require('luasnip.extras').rep
local line_begin = require('luasnip.extras.conditions').line_begin local line_begin = require('luasnip.extras.conditions').line_begin
local line_end = require('luasnip.extras.conditions').line_end
local extend_decorator = require 'luasnip.util.extend_decorator' local extend_decorator = require 'luasnip.util.extend_decorator'
local fmta = extend_decorator.apply(fmt, { delimiters = '#~' }) local fmta = extend_decorator.apply(fmt, { delimiters = '#~' })
@@ -110,21 +109,6 @@ utils.atr = function(trigger, description, options)
) )
end end
--- Create a trigger for a snippet to expand at the end of a line
---@param trigger string
---@param description? string
---@param options? table
---@return table
utils.Etr = function(trigger, description, options)
return utils.tr(
trigger,
description,
vim.tbl_extend('force', {
condition = line_end
}, options or {})
)
end
--- Create a snippet that will expand anywhere but in the middle of a word --- Create a snippet that will expand anywhere but in the middle of a word
---@param trigger any ---@param trigger any
---@param nodes any ---@param nodes any
-16
View File
@@ -1,16 +0,0 @@
local M = {}
function M.open(name)
local titles = {
zsh = 'ZSH Cheat Sheet',
bash = 'Bash Cheat Sheet',
regex = 'RegExp Cheat Sheet',
}
local path = vim.fn.stdpath 'config' .. '/cheatsheets/' .. name .. '.md'
print('Opening cheat sheet: ' .. path)
require('../helpers').open_file_modal(path, titles[name] or name)
end
return M
-3
View File
@@ -1,3 +0,0 @@
WRN 2026-07-03T16:02:54.215 c/nvim.57084.0 server_start:197: Failed to start server: operation not permitted: /var/folders/j3/6yv08_8s1s740ggr40_kgvrr0000gn/T/nvim.chris/esnddj/nvim.57725.0
WRN 2026-07-03T16:03:18.385 c/nvim.57084.0 server_start:197: Failed to start server: operation not permitted: /var/folders/j3/6yv08_8s1s740ggr40_kgvrr0000gn/T/nvim.chris/O4WnB1/nvim.57764.0
WRN 2026-07-14T11:43:01.105 c/nvim.17401.0 server_start:197: Failed to start server: operation not permitted: /var/folders/j3/6yv08_8s1s740ggr40_kgvrr0000gn/T/nvim.chris/KkEtYu/nvim.19436.0