Compare commits

..

6 Commits

7 changed files with 166 additions and 282 deletions

View File

@@ -17,13 +17,53 @@ DEBUG="${DEBUG:-0}"
# Enable debugging with DEBUG=1
[ "${DEBUG:-0}" -eq 1 ] && set -x
# Detect the current shell
CURRENT_SHELL=$(ps -p $$ -ocomm= | awk -F/ '{print $NF}')
# Function to prepend a path to PATH based on the shell
x-path-prepend()
{
local dir=$1
case "$CURRENT_SHELL" in
fish)
set -U fish_user_paths "$dir" $fish_user_paths
;;
sh | bash | zsh)
PATH="$dir:$PATH"
;;
*)
echo "Unsupported shell: $CURRENT_SHELL"
exit 1
;;
esac
}
# Function to set environment variables based on the shell
x-set-env()
{
local var=$1
local value=$2
case "$CURRENT_SHELL" in
fish)
set -x "$var" "$value"
;;
sh | bash | zsh)
export "$var=$value"
;;
*)
echo "Unsupported shell: $CURRENT_SHELL"
exit 1
;;
esac
}
# Explicitly set XDG folders, if not already set
# https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html
[ -z "$XDG_CONFIG_HOME" ] && export XDG_CONFIG_HOME="$HOME/.config"
[ -z "$XDG_DATA_HOME" ] && export XDG_DATA_HOME="$HOME/.local/share"
[ -z "$XDG_CACHE_HOME" ] && export XDG_CACHE_HOME="$HOME/.cache"
[ -z "$XDG_STATE_HOME" ] && export XDG_STATE_HOME="$HOME/.local/state"
[ -z "$XDG_BIN_HOME" ] && export XDG_BIN_HOME="$HOME/.local/bin"
x-set-env XDG_CONFIG_HOME "$HOME/.config"
x-set-env XDG_DATA_HOME "$HOME/.local/share"
x-set-env XDG_CACHE_HOME "$HOME/.cache"
x-set-env XDG_STATE_HOME "$HOME/.local/state"
x-set-env XDG_BIN_HOME "$HOME/.local/bin"
# Paths
x-path-prepend "/usr/local/bin"

View File

@@ -17,7 +17,7 @@ cnoreabbrev Qall qall " quit all
"" Mappings
"*****************************************************************************
noremap <C-S> :w<CR> " save buffer
noremap <C-s> :w<CR> " save buffer
" Split
noremap <Leader>h :<C-u>split<CR> " horizontal split
@@ -57,7 +57,7 @@ noremap <Leader>r :tabe <C-R>=expand("%:p:h") . "/" <CR>
" fzf.vim
let $FZF_DEFAULT_COMMAND = "find * -path '*/\.*' -prune -o -path 'node_modules/**' -prune -o -path 'target/**' -prune -o -path 'vendor/**' -prune -o -path 'dist/**' -prune -o -type f -print -o -type l -print 2> /dev/null"
cnoremap <C-P> <C-R>=expand("%:p:h") . "/" <CR>
cnoremap <C-p> <C-R>=expand("%:p:h") . "/" <CR>
nnoremap <silent> <leader>b :Buffers<CR>
nnoremap <silent> <leader>e :FZF -m<CR>
" Recovery commands from history through FZF

View File

@@ -10,6 +10,11 @@
"version": "2"
},
"languages": {
"Python": {
"enable_language_server": true,
"allow_rewrap": "anywhere",
"auto_indent_on_paste": true
},
"Shell Script": {
"enable_language_server": true
},
@@ -49,8 +54,8 @@
"vim_mode": true,
"theme": {
"mode": "system",
"light": "Iceberg",
"dark": "Iceberg"
"light": "Tokyo Night Light",
"dark": "Tokyo Night Storm"
},
"inlay_hints": {
"enabled": true,

View File

@@ -15,9 +15,35 @@
SCRIPT=$(basename "$0")
# Loads configs for better installation experience
source "$DOTFILES/config/shared.sh"
source "${DOTFILES}/local/bin/msgr"
# Detect the current shell
CURRENT_SHELL=$(ps -p $$ -ocomm= | awk -F/ '{print $NF}')
# Function to source files based on the shell
source_file()
{
local file=$1
case "$CURRENT_SHELL" in
fish)
if [[ -f "$file.fish" ]]; then
source "$file.fish"
else
echo "Fish shell file not found: $file.fish"
exit 1
fi
;;
sh | bash | zsh)
source "$file"
;;
*)
echo "Unsupported shell: $CURRENT_SHELL"
exit 1
;;
esac
}
# Modify the source commands to use the new function
source_file "$DOTFILES/config/shared.sh"
source_file "${DOTFILES}/local/bin/msgr"
# Menu builder
menu_builder()
@@ -267,7 +293,7 @@ section_helpers()
{
USAGE_PREFIX="$SCRIPT helpers <command>"
MENU=(
"aliases:<shell> (bash, zsh) Show aliases"
"aliases:<shell> (bash, zsh, fish) Show aliases"
"colors:Show colors"
"env:Show environment variables"
"functions:Show functions"
@@ -297,8 +323,11 @@ section_helpers()
"bash")
bash -ixc : 2>&1 | grep -E '> alias' | sed "s|$HOME|~|" | grep -v "(eval)"
;;
"fish")
fish -ic "alias" | sed "s|$HOME|~|"
;;
*)
echo "$SCRIPT helpers aliases <shell> (bash, zsh)"
echo "$SCRIPT helpers aliases <shell> (bash, zsh, fish)"
;;
esac
;;

View File

@@ -11,10 +11,10 @@ set -euo pipefail
VERBOSE=0
CHECK_PATTERN="text: auto"
EXIT_ON_MISSING=0
SUGGEST_RULES=1 # Suggestions enabled by default
WRITE_RULES=0 # Writing to file is opt-in
FORMAT_WIDTH=0 # Auto-width by default (0 means auto)
MIN_FORMAT_WIDTH=20 # Minimum format width
SUGGEST_RULES=1 # Suggestions enabled by default
WRITE_RULES=0 # Writing to file is opt-in
FORMAT_WIDTH=0 # Auto-width by default (0 means auto)
MIN_FORMAT_WIDTH=20 # Minimum format width
DEBUG="${DEBUG:-0}"
@@ -23,27 +23,33 @@ if [ "$DEBUG" -eq 1 ]; then
fi
# Output functions
msg_err() {
msg_err()
{
echo -e "\e[31m$@\e[0m" >&2
}
msg_success() {
msg_success()
{
echo -e "\e[32m$@\e[0m"
}
msg_warn() {
msg_warn()
{
echo -e "\e[33m$@\e[0m" >&2
}
msg_info() {
msg_info()
{
echo -e "\e[36m$@\e[0m"
}
msg_debug() {
msg_debug()
{
[[ $VERBOSE -eq 1 ]] && echo -e "\e[35m$@\e[0m"
}
show_help() {
show_help()
{
cat << EOF
Usage: $(basename "$0") [OPTIONS]
@@ -64,31 +70,31 @@ EOF
# Parse arguments
while [[ $# -gt 0 ]]; do
case "$1" in
-h|--help)
-h | --help)
show_help
exit 0
;;
-v|--verbose)
-v | --verbose)
VERBOSE=1
shift
;;
-e|--exit)
-e | --exit)
EXIT_ON_MISSING=1
shift
;;
-p|--pattern)
-p | --pattern)
CHECK_PATTERN="$2"
shift 2
;;
-n|--no-suggest)
-n | --no-suggest)
SUGGEST_RULES=0
shift
;;
-w|--write)
-w | --write)
WRITE_RULES=1
shift
;;
-f|--format-width)
-f | --format-width)
if [[ $2 =~ ^[0-9]+$ ]]; then
FORMAT_WIDTH=$2
shift 2
@@ -106,7 +112,8 @@ while [[ $# -gt 0 ]]; do
done
# Function to check if git is installed
check_git_installed() {
check_git_installed()
{
if ! command -v git &> /dev/null; then
msg_err "git could not be found, please install it first"
exit 1
@@ -114,15 +121,17 @@ check_git_installed() {
}
# Check if we're in a git repository
check_git_repo() {
if ! git rev-parse --is-inside-work-tree &>/dev/null; then
check_git_repo()
{
if ! git rev-parse --is-inside-work-tree &> /dev/null; then
msg_err "Not inside a git repository"
exit 1
fi
}
# Check if we're in the git root directory
check_git_root() {
check_git_root()
{
local git_root
git_root=$(git rev-parse --show-toplevel)
local current_dir
@@ -136,7 +145,8 @@ check_git_root() {
}
# Check if .gitattributes exists
check_gitattributes_exists() {
check_gitattributes_exists()
{
if [[ ! -f ".gitattributes" ]]; then
msg_err ".gitattributes file not found in the repository root"
msg_warn "Create a .gitattributes file before running this command"
@@ -145,7 +155,8 @@ check_gitattributes_exists() {
}
# Format rule with proper alignment
format_rule() {
format_rule()
{
local pattern="$1"
local attributes="$2"
local width="$3"
@@ -166,7 +177,8 @@ format_rule() {
}
# Get the file extension properly, handling special cases
get_file_extension() {
get_file_extension()
{
local file="$1"
local basename=$(basename "$file")
local extension=""
@@ -199,7 +211,8 @@ get_file_extension() {
}
# Suggest appropriate gitattributes rules based on file extension
suggest_rule() {
suggest_rule()
{
local file="$1"
local extension=""
local pattern=""
@@ -237,15 +250,15 @@ suggest_rule() {
# Common text files
case "$extension" in
# Shell scripts
sh|bash|zsh|fish)
sh | bash | zsh | fish)
attributes="text eol=lf diff=shell"
;;
# Web development
html|htm|xhtml|css|scss|sass|less)
html | htm | xhtml | css | scss | sass | less)
attributes="text eol=lf diff=html"
;;
js|jsx|ts|tsx|json|json5)
js | jsx | ts | tsx | json | json5)
attributes="text eol=lf diff=javascript"
;;
@@ -262,20 +275,20 @@ suggest_rule() {
go)
attributes="text eol=lf diff=golang"
;;
java|kt|scala)
java | kt | scala)
attributes="text eol=lf diff=java"
;;
c|cpp|h|hpp)
c | cpp | h | hpp)
attributes="text eol=lf diff=cpp"
;;
# Documentation
md|markdown|txt)
md | markdown | txt)
attributes="text eol=lf"
;;
# Configuration files
yml|yaml|toml|ini|cfg|conf)
yml | yaml | toml | ini | cfg | conf)
attributes="text eol=lf"
;;
@@ -283,24 +296,24 @@ suggest_rule() {
git)
attributes="text eol=lf"
;;
gitignore|gitattributes)
gitignore | gitattributes)
attributes="text eol=lf"
;;
# Binary files
png|jpg|jpeg|gif|ico|svg|webp|avif)
png | jpg | jpeg | gif | ico | svg | webp | avif)
attributes="binary"
;;
pdf|doc|docx|xls|xlsx|ppt|pptx)
pdf | doc | docx | xls | xlsx | ppt | pptx)
attributes="binary"
;;
zip|tar|gz|7z|rar)
zip | tar | gz | 7z | rar)
attributes="binary"
;;
mp3|mp4|avi|mov|wav|ogg)
mp3 | mp4 | avi | mov | wav | ogg)
attributes="binary"
;;
ttf|otf|woff|woff2|eot)
ttf | otf | woff | woff2 | eot)
attributes="binary"
;;
@@ -321,7 +334,8 @@ suggest_rule() {
}
# Function to check for missing .gitattributes
check_gitattributes() {
check_gitattributes()
{
local missing_attributes
msg_info "Checking for pattern: $CHECK_PATTERN"
@@ -362,7 +376,8 @@ check_gitattributes() {
}
# Parse rule string and extract pattern and attributes
parse_rule() {
parse_rule()
{
local rule="$1"
if [[ "$rule" == "#"* ]]; then
@@ -379,7 +394,8 @@ parse_rule() {
}
# Check shell scripts by name regardless of extension
detect_shell_scripts() {
detect_shell_scripts()
{
msg_debug "Detecting shell scripts by name regardless of extension..."
local shell_scripts_rules=""
@@ -510,13 +526,14 @@ detect_shell_scripts() {
# Return the formatted arrays
local rules_count=${#patterns[@]}
for ((i=0; i<rules_count; i++)); do
for ((i = 0; i < rules_count; i++)); do
echo "${patterns[$i]}:${attributes[$i]}"
done
}
# Function to suggest gitattributes rules
suggest_gitattributes() {
suggest_gitattributes()
{
local missing_attributes="$1"
local files
local extension_suggestions=()
@@ -613,7 +630,7 @@ suggest_gitattributes() {
# Format and output all suggestions with proper alignment
local rule_count=${#all_patterns[@]}
for ((i=0; i<rule_count; i++)); do
for ((i = 0; i < rule_count; i++)); do
local pattern="${all_patterns[$i]}"
local attributes="${all_attributes[$i]}"
@@ -637,7 +654,8 @@ suggest_gitattributes() {
}
# Write suggestions to .gitattributes file
write_to_gitattributes() {
write_to_gitattributes()
{
local suggestions="$1"
local gitattributes=".gitattributes"
@@ -664,7 +682,8 @@ write_to_gitattributes() {
}
# Main function
main() {
main()
{
check_git_installed
check_git_repo
check_git_root

View File

@@ -201,7 +201,7 @@ process_args()
# Initialize log file if specified
if [[ -n "$LOG_FILE" ]]; then
# Create log directory if it doesn't exist
mkdir -p "$(dirname "$LOG_FILE")" 2>/dev/null || true
mkdir -p "$(dirname "$LOG_FILE")" 2> /dev/null || true
# Initialize log file
echo "[$(date +"%Y-%m-%d %H:%M:%S")] [INFO] Started git-update-dirs version $VERSION" > "$LOG_FILE"
fi
@@ -375,7 +375,7 @@ cleanup_branches()
local cleaned=0
local current_branch output
current_branch=$(git symbolic-ref --short HEAD 2>/dev/null)
current_branch=$(git symbolic-ref --short HEAD 2> /dev/null)
# Skip branch cleanup if we're not on a main branch
if [[ ! "$current_branch" =~ ^(master|main|develop)$ ]]; then
@@ -397,7 +397,7 @@ cleanup_branches()
# Delete branches
for branch in $output; do
if [[ -n "$branch" ]]; then
if git branch -d "$branch" &>/dev/null; then
if git branch -d "$branch" &> /dev/null; then
((cleaned++))
log "INFO" "Deleted merged branch $branch in $(pwd)"
else
@@ -426,7 +426,7 @@ update_repo()
# Show progress before starting the operation
show_progress "$PROCESSED" "$TOTAL" "${dir%/}"
cd "$dir" 2>/dev/null || {
cd "$dir" 2> /dev/null || {
log "ERROR" "Could not enter directory $dir"
echo -e "\n${RED}Error: Could not enter directory $dir${NC}" >&2
((FAILED++))
@@ -438,37 +438,37 @@ update_repo()
log "INFO" "Skipping directory with no remotes: $dir"
msg "Skipping directory with no remotes: $dir"
((SKIPPED++))
cd - >/dev/null || true
cd - > /dev/null || true
return 1
fi
# Get current branch name
current_branch=$(git symbolic-ref --short HEAD 2>/dev/null)
current_branch=$(git symbolic-ref --short HEAD 2> /dev/null)
if [[ -z "$current_branch" ]]; then
log "INFO" "Skipping repository in detached HEAD state: $dir"
msg "Skipping repository in detached HEAD state: $dir"
((SKIPPED++))
cd - >/dev/null || true
cd - > /dev/null || true
return 1
fi
# Check if current branch has tracking information
eval "git rev-parse --abbrev-ref --symbolic-full-name @{u} 2>/dev/null" &>/dev/null || {
eval "git rev-parse --abbrev-ref --symbolic-full-name @{u} 2>/dev/null" &> /dev/null || {
log "INFO" "Skipping branch '$current_branch' without tracking info in $dir"
msg "Skipping branch '$current_branch' without tracking info in $dir"
((SKIPPED++))
cd - >/dev/null || true
cd - > /dev/null || true
return 1
}
# Check if remote is accessible
remote_name=$(git config --get branch."$current_branch".remote)
if [[ -n "$remote_name" ]]; then
if ! git ls-remote --exit-code "$remote_name" &>/dev/null; then
if ! git ls-remote --exit-code "$remote_name" &> /dev/null; then
log "WARNING" "Skipping repository with inaccessible remote '$remote_name': $dir"
msg "Skipping repository with inaccessible remote: $dir"
((SKIPPED++))
cd - >/dev/null || true
cd - > /dev/null || true
return 1
fi
fi
@@ -478,7 +478,7 @@ update_repo()
log "WARNING" "Skipping repository with unmerged files: $dir"
msg "Skipping repository with unmerged files: $dir"
((UNMERGED++))
cd - >/dev/null || true
cd - > /dev/null || true
return 1
fi
@@ -579,7 +579,7 @@ update_repo()
fi
# Return to original directory
cd - >/dev/null || true
cd - > /dev/null || true
# Show progress after completion
show_progress "$PROCESSED" "$TOTAL" "${dir%/} - Done"

View File

@@ -1,209 +0,0 @@
#!/usr/bin/env perl
=head1 NAME
dupes - Report on files with duplicate contents, via SHA1 hash.
=cut
=head1 SYNOPSIS
dupes [options] directory
General Options:
--help Show the help information for this script.
--verbose Show useful debugging information.
=cut
=head1 ABOUT
dupes is a simple script to report upon files that are identical,
recursively.
The process involves calculating the SHA1 hash of the file contents
and reporting on anything collisions we see.
Note that a collision might be caused by a symbolic link, or hardlink,
so blindly deleting duplicates without investigation is almost certainly
a mistake.
=cut
=head1 AUTHOR
Steve
--
http://www.steve.org.uk/
=cut
=head1 LICENSE
Copyright (c) 2013 by Steve Kemp. All rights reserved.
This script is free software;you can redistribute it and/or modify it under
the same terms as Perl itself.
The LICENSE file contains the full text of the license.
=cut
use strict;
use warnings;
use File::Find;
use Getopt::Long;
use Pod::Usage;
#
# Parse the arguments
#
my %config = parsedOptions();
#
# The path to examine.
#
my $path = $ARGV[0] || '.';
#
# Get the hashing object, dynamically.
#
my $ctx = getHashObject();
my %digest;
#
# Find files and store the hash of their contents.
#
find( {
'wanted' => sub {
if ( -f $_ )
{
lstat;
if ( ( -r _ ) && ( !-l _ ) )
{
$ctx->reset;
$ctx->addfile($_);
my $md5 = $ctx->hexdigest;
if ( exists $digest{ $md5 } )
{
push @{ $digest{ $md5 }->{ 'dupes' } }, $_;
}
else
{
$digest{ $md5 } = { 'file' => $_,
'dupes' => [] };
}
}
}
else
{
$config{ 'verbose' } && print "Entering $_\n";
}
},
'no_chdir' => 1
},
$path
);
#
# Report upon collisions.
#
foreach my $hash ( keys %digest )
{
my $dupes = $digest{ $hash }->{ 'dupes' };
my $src = $digest{ $hash }->{ 'file' };
if (@$dupes)
{
print $src . "\n";
foreach my $dupe (@$dupes)
{
print "\t$dupe\n";
}
}
}
#
# All done.
#
exit(0);
=begin doc
Load one of M<Digest::SHA> and M<Digest::SHA1>, depending on what is available.
=end doc
=cut
sub getHashObject
{
my $hash = undef;
foreach my $module (qw! Digest::SHA Digest::SHA1 !)
{
# If we succeeded in calculating the hash we're done.
next if ( defined($hash) );
# Attempt to load the module
my $eval = "use $module;";
## no critic (Eval)
eval($eval);
## use critic
if ( !$@ )
{
$hash = $module->new;
}
}
if ($hash)
{
return ($hash);
}
else
{
print "Failed to load either DIgest::SHA or Digest::SHA1\n";
exit(1);
}
}
=begin doc
Parse the options and return suitable values.
=end doc
=cut
sub parsedOptions
{
my %vars;
exit
if (
!GetOptions( "help" => \$vars{ 'help' },
"verbose" => \$vars{ 'verbose' } ) );
pod2usage(1) if ( $vars{ 'help' } );
return (%vars);
}