Last active
March 17, 2020 18:12
-
-
Save gnfisher/16ebb1c9f299b7172f27fce21886cc9a to your computer and use it in GitHub Desktop.
Dotfiles
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
=== zshrc | |
# Lines configured by zsh-newuser-install | |
HISTFILE=~/.histfile | |
HISTSIZE=10000 | |
SAVEHIST=10000 | |
setopt appendhistory autocd extendedglob | |
unsetopt beep | |
bindkey -e | |
# End of lines configured by zsh-newuser-install | |
# The following lines were added by compinstall | |
zstyle :compinstall filename '/home/greg/.zshrc' | |
zstyle ':completion:*' matcher-list '' 'm:{a-zA-Z}={A-Za-z}' | |
source ~/zprompts/zprompts.plugin.zsh | |
autoload -Uz compinit promptinit | |
compinit | |
promptinit | |
prompt scala4 | |
# End of lines added by compinstall | |
# Disable ctrl+s | |
stty -ixon | |
. $HOME/.asdf/asdf.sh | |
. $HOME/.asdf/completions/asdf.bash | |
[ -f ~/.fzf.zsh ] && source ~/.fzf.zsh | |
export PATH=$PATH:/usr/bin:/home/greg/bin:/Users/greg/bin | |
export EDITOR=nvim | |
export JAVA_HOME=/Library/Java/JavaVirtualMachines/adoptopenjdk-8.jdk/Contents/Home/ | |
export PATH="/usr/local/opt/[email protected]/bin:$PATH" | |
export PATH="/usr/local/sbin:$PATH" | |
alias ads='cd ~/Code/thoughtbot/ads-quoting-ui' | |
alias adss='cd ~/Code/thoughtbot/ads-quoting-server' | |
alias nv=nvim | |
alias g=git | |
alias c=clear | |
alias scratch='nvim ~/Scratch' | |
alias sf='~/Code/thoughtbot/ads-quoting-server/bin/salesforce' | |
alias sfq='~/Code/thoughtbot/ads-quoting-server/bin/salesforce query ' | |
alias sfd='~/Code/thoughtbot/ads-quoting-server/bin/salesforce describe ' | |
=== vimrc | |
set encoding=utf-8 | |
" Leader | |
let mapleader = " " | |
set backspace=2 " Backspace deletes like most programs in insert mode | |
set nobackup | |
set nowritebackup | |
set noswapfile " http://robots.thoughtbot.com/post/18739402579/global-gitignore#comment-458413287 | |
set undofile | |
set undodir=~/.vim/undodir | |
set history=50 | |
set ruler " show the cursor position all the time | |
set showcmd " display incomplete commands | |
set incsearch " do incremental searching | |
set laststatus=2 " Always display the status line | |
set autowrite " Automatically :write before running commands | |
set modelines=0 " Disable modelines as a security precaution | |
set nomodeline | |
set ignorecase smartcase | |
set autoread | |
set noeb vb t_vb= | |
set complete=.,w,b,u,t,i | |
:set completeopt=menu,preview | |
set nofoldenable | |
set foldmethod=manual | |
set nojoinspaces | |
set scrolloff=10 | |
set sidescrolloff=5 | |
" statusline | |
:set statusline=%<%f\ (%{&ft})\ %-4(%m%)%=%-19(%3l,%02c%03V%) | |
" Switch syntax highlighting on, when the terminal has colors | |
" Also switch on highlighting the last used search pattern. | |
if (&t_Co > 2 || has("gui_running")) && !exists("syntax_on") | |
syntax enable | |
endif | |
if filereadable(expand("~/.vimrc.bundles")) | |
source ~/.vimrc.bundles | |
endif | |
" Load matchit.vim, but only if the user hasn't installed a newer version. | |
if !exists('g:loaded_matchit') && findfile('plugin/matchit.vim', &rtp) ==# '' | |
runtime! macros/matchit.vim | |
endif | |
filetype plugin indent on | |
" Use The Silver Searcher https://github.com/ggreer/the_silver_searcher | |
if executable('ag') | |
" Use Ag over Grep | |
set grepprg=ag\ --nogroup\ --nocolor | |
" Use ag in fzf for listing files. Lightning fast and respects .gitignore | |
let $FZF_DEFAULT_COMMAND = 'ag --literal --files-with-matches --nocolor --hidden -g ""' | |
endif | |
" Tab completion | |
" will insert tab at beginning of line, | |
" will use completion if not at beginning | |
set wildmode=list:longest,list:full | |
function! InsertTabWrapper() | |
let col = col('.') - 1 | |
if !col || getline('.')[col - 1] !~ '\k' | |
return "\<Tab>" | |
else | |
return "\<C-p>" | |
endif | |
endfunction | |
inoremap <Tab> <C-r>=InsertTabWrapper()<CR> | |
inoremap <S-Tab> <C-n> | |
augroup vimrcEx | |
autocmd! | |
" When editing a file, always jump to the last known cursor position. | |
" Don't do it for commit messages, when the position is invalid, or when | |
" inside an event handler (happens when dropping a file on gvim). | |
autocmd BufReadPost * | |
\ if &ft != 'gitcommit' && line("'\"") > 0 && line("'\"") <= line("$") | | |
\ exe "normal g`\"" | | |
\ endif | |
" Set syntax highlighting for specific file types | |
autocmd BufRead,BufNewFile *.md set filetype=markdown | |
autocmd BufRead,BufNewFile .{jscs,jshint,eslint}rc set filetype=json | |
autocmd BufRead,BufNewFile aliases.local,zshrc.local,*/zsh/configs/* set filetype=sh | |
autocmd BufRead,BufNewFile gitconfig.local set filetype=gitconfig | |
autocmd BufRead,BufNewFile tmux.conf.local set filetype=tmux | |
autocmd BufRead,BufNewFile vimrc.local set filetype=vim | |
autocmd BufNewFile,BufRead * if expand('%:t') !~ '\.' | setlocal textwidth=80 | endif | |
" json | |
autocmd! FileType json set sw=2 sts=2 expandtab | |
autocmd! FileType scss set sw=2 sts=2 expandtab | |
" elm indentation | |
autocmd! FileType elm set sw=4 sts=4 expandtab autoindent smartindent nocindent | |
" Remove whitespace at end of lines | |
autocmd BufWritePre *.erb,*.scss,*.rb,*.js,*.c,*.py,*.php,*.coffee :%s/\s\+$//e | |
autocmd BufRead,BufNewFile *.sbt set filetype=scala | |
" Spell check gitcommits and Markdown | |
autocmd FileType gitcommit setlocal spell | |
autocmd BufRead,BufNewFile *.md setlocal spell | |
" Only wrap md and text | |
autocmd BufRead,BufNewFile *.md,*.txt setlocal textwidth=80 | |
augroup END | |
" When the type of shell script is /bin/sh, assume a POSIX-compatible | |
" shell for syntax highlighting purposes. | |
let g:is_posix = 1 | |
" Softtabs, 2 spaces | |
set tabstop=2 | |
set shiftwidth=2 | |
set shiftround | |
set expandtab | |
" Display extra whitespace | |
set list listchars=tab:»·,trail:·,nbsp:· | |
" Use one space, not two, after punctuation. | |
set nojoinspaces | |
" Make it obvious where 80 characters is | |
set colorcolumn=81 | |
" Numbers | |
set number | |
set numberwidth=5 | |
" Tab completion | |
" will insert tab at beginning of line, | |
" will use completion if not at beginning | |
set wildmode=list:longest,list:full | |
function! InsertTabWrapper() | |
let col = col('.') - 1 | |
if !col || getline('.')[col - 1] !~ '\k' | |
return "\<Tab>" | |
else | |
return "\<C-p>" | |
endif | |
endfunction | |
inoremap <Tab> <C-r>=InsertTabWrapper()<CR> | |
inoremap <S-Tab> <C-n> | |
" Switch between the last two files | |
nnoremap <Leader><Leader> <C-^> | |
" vim-test mappings | |
nnoremap <silent> <Leader>tf :TestFile<CR> | |
nnoremap <silent> <Leader>tn :TestNearest<CR> | |
nnoremap <silent> <Leader>tl :TestLast<CR> | |
nnoremap <silent> <Leader>ta :TestSuite<CR> | |
nnoremap <silent> <Leader>tv :TestVisit<CR> | |
let test#strategy = "dispatch" | |
" Run commands that require an interactive shell | |
nnoremap <Leader>r :RunInInteractiveShell<Space> | |
" Treat <li> and <p> tags like the block tags they are | |
let g:html_indent_tags = 'li\|p' | |
" Set tags for vim-fugitive | |
set tags^=.git/tags | |
" Open new split panes to right and bottom, which feels more natural | |
set splitbelow | |
set splitright | |
" Quicker window movement | |
nnoremap <C-j> <C-w>j | |
nnoremap <C-k> <C-w>k | |
nnoremap <C-h> <C-w>h | |
nnoremap <C-l> <C-w>l | |
" Set spellfile to location that is guaranteed to exist, can be symlinked to | |
" Dropbox or kept in Git and managed outside of thoughtbot/dotfiles using rcm. | |
set spellfile=$HOME/.vim-spell-en.utf-8.add | |
" Autocomplete with dictionary words when spell check is on | |
set complete+=kspell | |
" Always use vertical diffs | |
set diffopt+=vertical | |
" use C-Space to Esc any mode | |
nnoremap <C-Space> <Esc>:noh<CR> | |
vnoremap <C-Space> <Esc>gV | |
onoremap <C-Space> <Esc> | |
cnoremap <C-Space> <C-c> | |
inoremap <C-Space> <Esc> | |
" terminal sees <C-@> as <C-space> | |
nnoremap <C-@> <Esc>:noh<CR> | |
vnoremap <C-@> <Esc>gV | |
onoremap <C-@> <Esc> | |
cnoremap <C-@> <C-c> | |
inoremap <C-@> <Esc> | |
" convenience | |
nnoremap <leader>; : | |
" move between wrapped lines | |
nmap j gj | |
nmap k gk | |
nnoremap <leader>sub :%s///g<left><left> | |
vnoremap <leader>sub :s///g<left><left> | |
" CoC config | |
set updatetime=300 | |
set shortmess+=c | |
set signcolumn=yes | |
" Use tab for trigger completion with characters ahead and navigate. | |
" Use command ':verbose imap <tab>' to make sure tab is not mapped by other plugin. | |
inoremap <silent><expr> <TAB> | |
\ pumvisible() ? "\<C-n>" : | |
\ <SID>check_back_space() ? "\<TAB>" : | |
\ coc#refresh() | |
inoremap <expr><S-TAB> pumvisible() ? "\<C-p>" : "\<C-h>" | |
function! s:check_back_space() abort | |
let col = col('.') - 1 | |
return !col || getline('.')[col - 1] =~# '\s' | |
endfunction | |
" Use <c-tag> to trigger completion. | |
inoremap <silent><expr> <c-tab> coc#refresh() | |
" Use <cr> to confirm completion, `<C-g>u` means break undo chain at current position. | |
" Coc only does snippet and additional edit on confirm. | |
inoremap <expr> <cr> pumvisible() ? "\<C-y>" : "\<C-g>u\<CR>" | |
" Or use `complete_info` if your vim support it, like: | |
" noremap <expr> <cr> complete_info()["selected"] != "-1" ? "\<C-y>" : "\<C-g>u\<CR>" | |
nmap <silent> [g <Plug>(coc-diagnostic-prev) | |
nmap <silent> ]g <Plug>(coc-diagnostic-next) | |
nmap <silent> gd <Plug>(coc-definition) | |
nmap <silent> gr <Plug>(coc-references) | |
nnoremap <expr><C-f> coc#util#has_float() ? coc#util#float_scroll(1) : "\<C-f>" | |
nnoremap <expr><C-b> coc#util#has_float() ? coc#util#float_scroll(0) : "\<C-b>" | |
" Use K to show documentation in preview window | |
nnoremap <silent> K :call <SID>show_documentation()<CR> | |
function! s:show_documentation() | |
if (index(['vim','help'], &filetype) >= 0) | |
execute 'h '.expand('<cword>') | |
else | |
call CocAction('doHover') | |
endif | |
endfunction | |
" Remap for format selected region | |
xmap <leader>f <Plug>(coc-format-selected) | |
nmap <leader>f <Plug>(coc-format-selected) | |
nnoremap <silent> <leader>F :call CocAction('format')<CR> | |
augroup mygroup | |
autocmd! | |
" Setup formatexpr specified filetype(s). | |
autocmd FileType typescript,json setl formatexpr=CocAction('formatSelected') | |
" Update signature help on jump placeholder | |
autocmd User CocJumpPlaceholder call CocActionAsync('showSignatureHelp') | |
augroup end | |
" Use `:Format` to format current buffer | |
command! -nargs=0 Format :call CocAction('format') | |
" Use `:Fold` to fold current buffer | |
command! -nargs=? Fold :call CocAction('fold', <f-args>) | |
" use `:OR` for organize import of current buffer | |
command! -nargs=0 OR :call CocAction('runCommand', 'editor.action.organizeImport') | |
" Add status line support, for integration with other plugin, checkout `:h coc-status` | |
set statusline^=%{coc#status()}%{get(b:,'coc_current_function','')} | |
" Using CocList | |
" Show all diagnostics | |
nnoremap <silent> <space>d :<C-u>CocList diagnostics<cr> | |
" fzf | |
nnoremap <C-p> :Files<CR> | |
nnoremap <C-a> :Ag<space> | |
nnoremap <leader>fp :Files<cr> | |
nnoremap <leader>fb :Buffers<CR> | |
nnoremap <leader>ft :Tag<cr> | |
nnoremap <leader>fo :BTag<cr> | |
nnoremap <leader>fa :Files app/<cr> | |
nnoremap <leader>fm :Files app/models/<cr> | |
nnoremap <leader>fv :Files app/views/<cr> | |
nnoremap <leader>fc :Files app/controllers/<cr> | |
nnoremap <leader>fy :Files app/assets/stylesheets/<cr> | |
nnoremap <leader>fj :Files app/assets/javascripts/<cr> | |
nnoremap <leader>fs :Files spec/<cr> | |
let g:fzf_files_options = '--preview "(coderay {} || cat {}) 2> /dev/null | head -'.&lines.'"' | |
" \ '--reverse ' . | |
" shortcut to open files | |
map <Leader>e :e <C-R>=expand("%:p:h") . "/" <CR> | |
map <Leader>te :tabe <C-R>=expand("%:p:h") . "/" <CR> | |
map <Leader>se :sp <C-R>=expand("%:p:h") . "/" <CR> | |
nmap <Leader>g :silent !termite -e gitsh &> /dev/null &<CR> | |
nmap <Leader>z :silent !termite &> /dev/null &<CR> | |
" Easier than "+ | |
nmap cp "+y | |
xnoremap cp "+y | |
nmap cv "+p | |
nmap cV "+P | |
" in line, around line operators | |
xnoremap il g_o0 | |
onoremap il :normal vil<CR> | |
xnoremap al $o0 | |
onoremap al :normal val<CR> | |
function CopyToBasecamp() range | |
echo system('echo '.shellescape(join(getline(a:firstline, a:lastline), "\n")).' | pandoc -f markdown | textutil -stdin -format html -convert rtf -stdout | pbcopy') | |
endfunction | |
com -range=% -nargs=0 CopyToBasecamp :<line1>,<line2>call CopyToBasecamp() | |
xnoremap <Leader>b <esc>:'<,'>CopyToBasecamp<CR> | |
let g:lightline = { | |
\ 'colorscheme': 'onedark', | |
\ } | |
set termguicolors | |
set background=light | |
" colorscheme onedark | |
=== diffsofancy | |
#!/usr/bin/env perl | |
# This chunk of stuff was generated by App::FatPacker. To find the original | |
# file's code, look for the end of this BEGIN block or the string 'FATPACK' | |
BEGIN { | |
my %fatpacked; | |
$fatpacked{"DiffHighlight.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'DIFFHIGHLIGHT'; | |
package DiffHighlight; | |
use 5.008; | |
use warnings FATAL => 'all'; | |
use strict; | |
# Use the correct value for both UNIX and Windows (/dev/null vs nul) | |
use File::Spec; | |
my $NULL = File::Spec->devnull(); | |
# Highlight by reversing foreground and background. You could do | |
# other things like bold or underline if you prefer. | |
our @OLD_HIGHLIGHT = ( | |
color_config('color.diff-highlight.oldnormal', "\e[1;31m"), | |
color_config('color.diff-highlight.oldhighlight', "\e[1;31;48;5;52m"), | |
"\x1b[27m", | |
); | |
our @NEW_HIGHLIGHT = ( | |
color_config('color.diff-highlight.newnormal', "\e[1;32m"), | |
color_config('color.diff-highlight.newhighlight', "\e[1;32;48;5;22m"), | |
$OLD_HIGHLIGHT[2], | |
); | |
my $RESET = "\x1b[m"; | |
my $COLOR = qr/\x1b\[[0-9;]*m/; | |
my $BORING = qr/$COLOR|\s/; | |
my @removed; | |
my @added; | |
my $in_hunk; | |
my $graph_indent = 0; | |
our $line_cb = sub { print @_ }; | |
our $flush_cb = sub { local $| = 1 }; | |
# Count the visible width of a string, excluding any terminal color sequences. | |
sub visible_width { | |
local $_ = shift; | |
my $ret = 0; | |
while (length) { | |
if (s/^$COLOR//) { | |
# skip colors | |
} elsif (s/^.//) { | |
$ret++; | |
} | |
} | |
return $ret; | |
} | |
# Return a substring of $str, omitting $len visible characters from the | |
# beginning, where terminal color sequences do not count as visible. | |
sub visible_substr { | |
my ($str, $len) = @_; | |
while ($len > 0) { | |
if ($str =~ s/^$COLOR//) { | |
next | |
} | |
$str =~ s/^.//; | |
$len--; | |
} | |
return $str; | |
} | |
sub handle_line { | |
my $orig = shift; | |
local $_ = $orig; | |
# match a graph line that begins a commit | |
if (/^(?:$COLOR?\|$COLOR?[ ])* # zero or more leading "|" with space | |
$COLOR?\*$COLOR?[ ] # a "*" with its trailing space | |
(?:$COLOR?\|$COLOR?[ ])* # zero or more trailing "|" | |
[ ]* # trailing whitespace for merges | |
/x) { | |
my $graph_prefix = $&; | |
# We must flush before setting graph indent, since the | |
# new commit may be indented differently from what we | |
# queued. | |
flush(); | |
$graph_indent = visible_width($graph_prefix); | |
} elsif ($graph_indent) { | |
if (length($_) < $graph_indent) { | |
$graph_indent = 0; | |
} else { | |
$_ = visible_substr($_, $graph_indent); | |
} | |
} | |
if (!$in_hunk) { | |
$line_cb->($orig); | |
$in_hunk = /^$COLOR*\@\@ /; | |
} | |
elsif (/^$COLOR*-/) { | |
push @removed, $orig; | |
} | |
elsif (/^$COLOR*\+/) { | |
push @added, $orig; | |
} | |
else { | |
flush(); | |
$line_cb->($orig); | |
$in_hunk = /^$COLOR*[\@ ]/; | |
} | |
# Most of the time there is enough output to keep things streaming, | |
# but for something like "git log -Sfoo", you can get one early | |
# commit and then many seconds of nothing. We want to show | |
# that one commit as soon as possible. | |
# | |
# Since we can receive arbitrary input, there's no optimal | |
# place to flush. Flushing on a blank line is a heuristic that | |
# happens to match git-log output. | |
if (!length) { | |
$flush_cb->(); | |
} | |
} | |
sub flush { | |
# Flush any queued hunk (this can happen when there is no trailing | |
# context in the final diff of the input). | |
show_hunk(\@removed, \@added); | |
@removed = (); | |
@added = (); | |
} | |
sub highlight_stdin { | |
while (<STDIN>) { | |
handle_line($_); | |
} | |
flush(); | |
} | |
# Ideally we would feed the default as a human-readable color to | |
# git-config as the fallback value. But diff-highlight does | |
# not otherwise depend on git at all, and there are reports | |
# of it being used in other settings. Let's handle our own | |
# fallback, which means we will work even if git can't be run. | |
sub color_config { | |
my ($key, $default) = @_; | |
my $s = `git config --get-color $key 2>$NULL`; | |
return length($s) ? $s : $default; | |
} | |
sub show_hunk { | |
my ($a, $b) = @_; | |
# If one side is empty, then there is nothing to compare or highlight. | |
if (!@$a || !@$b) { | |
$line_cb->(@$a, @$b); | |
return; | |
} | |
# If we have mismatched numbers of lines on each side, we could try to | |
# be clever and match up similar lines. But for now we are simple and | |
# stupid, and only handle multi-line hunks that remove and add the same | |
# number of lines. | |
if (@$a != @$b) { | |
$line_cb->(@$a, @$b); | |
return; | |
} | |
my @queue; | |
for (my $i = 0; $i < @$a; $i++) { | |
my ($rm, $add) = highlight_pair($a->[$i], $b->[$i]); | |
$line_cb->($rm); | |
push @queue, $add; | |
} | |
$line_cb->(@queue); | |
} | |
sub highlight_pair { | |
my @a = split_line(shift); | |
my @b = split_line(shift); | |
# Find common prefix, taking care to skip any ansi | |
# color codes. | |
my $seen_plusminus; | |
my ($pa, $pb) = (0, 0); | |
while ($pa < @a && $pb < @b) { | |
if ($a[$pa] =~ /$COLOR/) { | |
$pa++; | |
} | |
elsif ($b[$pb] =~ /$COLOR/) { | |
$pb++; | |
} | |
elsif ($a[$pa] eq $b[$pb]) { | |
$pa++; | |
$pb++; | |
} | |
elsif (!$seen_plusminus && $a[$pa] eq '-' && $b[$pb] eq '+') { | |
$seen_plusminus = 1; | |
$pa++; | |
$pb++; | |
} | |
else { | |
last; | |
} | |
} | |
# Find common suffix, ignoring colors. | |
my ($sa, $sb) = ($#a, $#b); | |
while ($sa >= $pa && $sb >= $pb) { | |
if ($a[$sa] =~ /$COLOR/) { | |
$sa--; | |
} | |
elsif ($b[$sb] =~ /$COLOR/) { | |
$sb--; | |
} | |
elsif ($a[$sa] eq $b[$sb]) { | |
$sa--; | |
$sb--; | |
} | |
else { | |
last; | |
} | |
} | |
if (is_pair_interesting(\@a, $pa, $sa, \@b, $pb, $sb)) { | |
return highlight_line(\@a, $pa, $sa, \@OLD_HIGHLIGHT), | |
highlight_line(\@b, $pb, $sb, \@NEW_HIGHLIGHT); | |
} | |
else { | |
return join('', @a), | |
join('', @b); | |
} | |
} | |
# we split either by $COLOR or by character. This has the side effect of | |
# leaving in graph cruft. It works because the graph cruft does not contain "-" | |
# or "+" | |
sub split_line { | |
local $_ = shift; | |
return utf8::decode($_) ? | |
map { utf8::encode($_); $_ } | |
map { /$COLOR/ ? $_ : (split //) } | |
split /($COLOR+)/ : | |
map { /$COLOR/ ? $_ : (split //) } | |
split /($COLOR+)/; | |
} | |
sub highlight_line { | |
my ($line, $prefix, $suffix, $theme) = @_; | |
my $start = join('', @{$line}[0..($prefix-1)]); | |
my $mid = join('', @{$line}[$prefix..$suffix]); | |
my $end = join('', @{$line}[($suffix+1)..$#$line]); | |
# If we have a "normal" color specified, then take over the whole line. | |
# Otherwise, we try to just manipulate the highlighted bits. | |
if (defined $theme->[0]) { | |
s/$COLOR//g for ($start, $mid, $end); | |
chomp $end; | |
return join('', | |
$theme->[0], $start, $RESET, | |
$theme->[1], $mid, $RESET, | |
$theme->[0], $end, $RESET, | |
"\n" | |
); | |
} else { | |
return join('', | |
$start, | |
$theme->[1], $mid, $theme->[2], | |
$end | |
); | |
} | |
} | |
# Pairs are interesting to highlight only if we are going to end up | |
# highlighting a subset (i.e., not the whole line). Otherwise, the highlighting | |
# is just useless noise. We can detect this by finding either a matching prefix | |
# or suffix (disregarding boring bits like whitespace and colorization). | |
sub is_pair_interesting { | |
my ($a, $pa, $sa, $b, $pb, $sb) = @_; | |
my $prefix_a = join('', @$a[0..($pa-1)]); | |
my $prefix_b = join('', @$b[0..($pb-1)]); | |
my $suffix_a = join('', @$a[($sa+1)..$#$a]); | |
my $suffix_b = join('', @$b[($sb+1)..$#$b]); | |
return visible_substr($prefix_a, $graph_indent) !~ /^$COLOR*-$BORING*$/ || | |
visible_substr($prefix_b, $graph_indent) !~ /^$COLOR*\+$BORING*$/ || | |
$suffix_a !~ /^$BORING*$/ || | |
$suffix_b !~ /^$BORING*$/; | |
} | |
DIFFHIGHLIGHT | |
s/^ //mg for values %fatpacked; | |
my $class = 'FatPacked::'.(0+\%fatpacked); | |
no strict 'refs'; | |
*{"${class}::files"} = sub { keys %{$_[0]} }; | |
if ($] < 5.008) { | |
*{"${class}::INC"} = sub { | |
if (my $fat = $_[0]{$_[1]}) { | |
my $pos = 0; | |
my $last = length $fat; | |
return (sub { | |
return 0 if $pos == $last; | |
my $next = (1 + index $fat, "\n", $pos) || $last; | |
$_ .= substr $fat, $pos, $next - $pos; | |
$pos = $next; | |
return 1; | |
}); | |
} | |
}; | |
} | |
else { | |
*{"${class}::INC"} = sub { | |
if (my $fat = $_[0]{$_[1]}) { | |
open my $fh, '<', \$fat | |
or die "FatPacker error loading $_[1] (could be a perl installation issue?)"; | |
return $fh; | |
} | |
return; | |
}; | |
} | |
unshift @INC, bless \%fatpacked, $class; | |
} # END OF FATPACK CODE | |
my $VERSION = "1.2.6"; | |
################################################################################# | |
use File::Spec; # For catdir | |
use File::Basename; # For dirname | |
use Encode; # For handling UTF8 stuff | |
use Cwd qw(abs_path); # For realpath() | |
use lib dirname(abs_path(File::Spec->catdir($0))) . "/lib"; # Add the local lib/ to @INC | |
use DiffHighlight; | |
use strict; | |
use warnings FATAL => 'all'; | |
my $remove_file_add_header = 1; | |
my $remove_file_delete_header = 1; | |
my $clean_permission_changes = 1; | |
my $manually_color_lines = 0; # Usually git/hg colorizes the lines, but for raw patches we use this | |
my $change_hunk_indicators = git_config_boolean("diff-so-fancy.changeHunkIndicators","true"); | |
my $strip_leading_indicators = git_config_boolean("diff-so-fancy.stripLeadingSymbols","true"); | |
my $mark_empty_lines = git_config_boolean("diff-so-fancy.markEmptyLines","true"); | |
my $use_unicode_dash_for_ruler = git_config_boolean("diff-so-fancy.useUnicodeRuler","true"); | |
my $ruler_width = git_config("diff-so-fancy.rulerWidth", undef); | |
my $git_strip_prefix = git_config_boolean("diff.noprefix","false"); | |
my $has_stdin = has_stdin(); | |
my $ansi_color_regex = qr/(\e\[([0-9]{1,3}(;[0-9]{1,3}){0,10})[mK])?/; | |
my $reset_color = color("reset"); | |
my $bold = color("bold"); | |
my $meta_color = ""; | |
my ($file_1,$file_2); | |
my $args = argv(); # Hashref of all the ARGV stuff | |
my $last_file_seen = ""; | |
my $last_file_mode = ""; | |
my $i = 0; | |
my $in_hunk = 0; | |
my $columns_to_remove = 0; | |
my $is_mercurial = 0; | |
my $color_forced = 0; # Has the color been forced on/off | |
# We try and be smart about whether we need to do line coloring, but | |
# this is an option to force it on/off | |
if ($args->{color_on}) { | |
$manually_color_lines = 1; | |
$color_forced = 1; | |
} elsif ($args->{color_off}) { | |
$manually_color_lines = 0; | |
$color_forced = 1; | |
} | |
# We only process ARGV if we don't have STDIN | |
if (!$has_stdin) { | |
if ($args->{v} || $args->{version}) { | |
die(version()); | |
} elsif ($args->{'set-defaults'}) { | |
my $ok = set_defaults(); | |
} elsif ($args->{colors}) { | |
# We print this to STDOUT so we can redirect to bash to auto-set the colors | |
print get_default_colors(); | |
exit; | |
} elsif (!%$args || $args->{help} || $args->{h}) { | |
my $first = check_first_run(); | |
if (!$first) { | |
die(usage()); | |
} | |
} else { | |
die("Missing input on STDIN\n"); | |
} | |
} else { | |
# Check to see if were using default settings | |
check_first_run(); | |
my @lines; | |
local $DiffHighlight::line_cb = sub { | |
push(@lines,@_); | |
my $last_line = $lines[-1]; | |
# Buffer X lines before we try and output anything | |
# Also make sure we're sending enough data to d-s-f to do it's magic. | |
# Certain things require a look-ahead line or two to function so | |
# we make sure we don't break on those sections prematurely | |
if (@lines > 24 && ($last_line !~ /^${ansi_color_regex}(---|index|old mode|similarity index|rename (from|to))/)) { | |
do_dsf_stuff(\@lines); | |
@lines = (); | |
} | |
}; | |
my $line_count = 0; | |
while (my $line = <STDIN>) { | |
# If the very first line of the diff doesn't start with ANSI color we're assuming | |
# it's a raw patch file, and we have to color the added/removed lines ourself | |
if (!$color_forced && $line_count == 0 && starts_with_ansi($line)) { | |
$manually_color_lines = 1; | |
} | |
my $ok = DiffHighlight::handle_line($line); | |
$line_count++; | |
} | |
DiffHighlight::flush(); | |
do_dsf_stuff(\@lines); | |
} | |
################################################################################# | |
sub do_dsf_stuff { | |
my $input = shift(); | |
#print STDERR "START -------------------------------------------------\n"; | |
#print STDERR join("",@$input); | |
#print STDERR "END ---------------------------------------------------\n"; | |
while (my $line = shift(@$input)) { | |
###################################################### | |
# Pre-process the line before we do any other markup # | |
###################################################### | |
# If the first line of the input is a blank line, skip that | |
if ($i == 0 && $line =~ /^\s*$/) { | |
next; | |
} | |
###################### | |
# End pre-processing # | |
###################### | |
####################################################################### | |
#################################################################### | |
# Look for git index and replace it horizontal line (header later) # | |
#################################################################### | |
if ($line =~ /^${ansi_color_regex}index /) { | |
# Print the line color and then the actual line | |
$meta_color = $1 || get_config_color("meta"); | |
# Get the next line without incrementing counter while loop | |
my $next = $input->[0] || ""; | |
my ($file_1,$file_2); | |
# The line immediately after the "index" line should be the --- file line | |
# If it's not it's an empty file add/delete | |
if ($next !~ /^$ansi_color_regex(---|Binary files)/) { | |
# We fake out the file names since it's a raw add/delete | |
if ($last_file_mode eq "add") { | |
$file_1 = "/dev/null"; | |
$file_2 = $last_file_seen; | |
} elsif ($last_file_mode eq "delete") { | |
$file_1 = $last_file_seen; | |
$file_2 = "/dev/null"; | |
} | |
} | |
if ($file_1 && $file_2) { | |
print horizontal_rule($meta_color); | |
print $meta_color . file_change_string($file_1,$file_2) . "\n"; | |
print horizontal_rule($meta_color); | |
} | |
######################### | |
# Look for the filename # | |
######################### | |
# $4 $5 | |
} elsif ($line =~ /^${ansi_color_regex}diff (-r|--git|--cc) (.+?)(\s|\e|$)/) { | |
# Mercurial looks like: diff -r 82e55d328c8c hello.c | |
if ($4 eq "-r") { | |
$is_mercurial = 1; | |
$meta_color ||= get_config_color("meta"); | |
# Git looks like: diff --git a/diff-so-fancy b/diff-so-fancy | |
} else { | |
$last_file_seen = $5; | |
} | |
$last_file_seen =~ s|^\w/||; # Remove a/ (and handle diff.mnemonicPrefix). | |
$in_hunk = 0; | |
######################################## | |
# Find the first file: --- a/README.md # | |
######################################## | |
} elsif (!$in_hunk && $line =~ /^$ansi_color_regex--- (\w\/)?(.+?)(\e|\t|$)/) { | |
$meta_color ||= get_config_color("meta"); | |
if ($git_strip_prefix) { | |
my $file_dir = $4 || ""; | |
$file_1 = $file_dir . $5; | |
} else { | |
$file_1 = $5; | |
} | |
# Find the second file on the next line: +++ b/README.md | |
my $next = shift(@$input); | |
$next =~ /^$ansi_color_regex\+\+\+ (\w\/)?(.+?)(\e|\t|$)/; | |
if ($1) { | |
print $1; # Print out whatever color we're using | |
} | |
if ($git_strip_prefix) { | |
my $file_dir = $4 || ""; | |
$file_2 = $file_dir . $5; | |
} else { | |
$file_2 = $5; | |
} | |
if ($file_2 ne "/dev/null") { | |
$last_file_seen = $file_2; | |
} | |
# Print out the top horizontal line of the header | |
print $reset_color; | |
print horizontal_rule($meta_color); | |
# Mercurial coloring is slightly different so we need to hard reset colors | |
if ($is_mercurial) { | |
print $reset_color; | |
} | |
print $meta_color; | |
print file_change_string($file_1,$file_2) . "\n"; | |
# Print out the bottom horizontal line of the header | |
print horizontal_rule($meta_color); | |
######################################## | |
# Check for "@@ -3,41 +3,63 @@" syntax # | |
######################################## | |
} elsif ($change_hunk_indicators && $line =~ /^${ansi_color_regex}(@@@* .+? @@@*)(.*)/) { | |
$in_hunk = 1; | |
my $hunk_header = $4; | |
my $remain = bleach_text($5); | |
# The number of colums to remove (1 or 2) is based on how many commas in the hunk header | |
$columns_to_remove = (char_count(",",$hunk_header)) - 1; | |
# On single line removes there is NO comma in the hunk so we force one | |
if ($columns_to_remove <= 0) { | |
$columns_to_remove = 1; | |
} | |
if ($1) { | |
print $1; # Print out whatever color we're using | |
} | |
my ($orig_offset, $orig_count, $new_offset, $new_count) = parse_hunk_header($hunk_header); | |
#$last_file_seen = basename($last_file_seen); | |
# Figure out the start line | |
my $start_line = start_line_calc($new_offset,$new_count); | |
# Last function has it's own color | |
my $last_function_color = get_config_color("last_function"); | |
print "@ $last_file_seen:$start_line \@${bold}${last_function_color}${remain}${reset_color}\n"; | |
################################### | |
# Remove any new file permissions # | |
################################### | |
} elsif ($remove_file_add_header && $line =~ /^${ansi_color_regex}.*new file mode/) { | |
# Don't print the line (i.e. remove it from the output); | |
$last_file_mode = "add"; | |
###################################### | |
# Remove any delete file permissions # | |
###################################### | |
} elsif ($remove_file_delete_header && $line =~ /^${ansi_color_regex}deleted file mode/) { | |
# Don't print the line (i.e. remove it from the output); | |
$last_file_mode = "delete"; | |
################################ | |
# Look for binary file changes # | |
################################ | |
} elsif ($line =~ /^Binary files (\w\/)?(.+?) and (\w\/)?(.+?) differ/) { | |
my $change = file_change_string($2,$4); | |
print horizontal_rule($meta_color); | |
print "$meta_color$change (binary)\n"; | |
print horizontal_rule($meta_color); | |
##################################################### | |
# Check if we're changing the permissions of a file # | |
##################################################### | |
} elsif ($clean_permission_changes && $line =~ /^${ansi_color_regex}old mode (\d+)/) { | |
my ($old_mode) = $4; | |
my $next = shift(@$input); | |
if ($1) { | |
print $1; # Print out whatever color we're using | |
} | |
my ($new_mode) = $next =~ m/new mode (\d+)/; | |
print "$last_file_seen changed file mode from $old_mode to $new_mode\n"; | |
############### | |
# File rename # | |
############### | |
} elsif ($line =~ /^${ansi_color_regex}similarity index (\d+)%/) { | |
my $simil = $4; | |
# If it's a move with content change we ignore this and the next two lines | |
if ($simil != 100) { | |
shift(@$input); | |
shift(@$input); | |
next; | |
} | |
my $next = shift(@$input); | |
my ($file1) = $next =~ /rename from (.+)/; | |
$next = shift(@$input); | |
my ($file2) = $next =~ /rename to (.+)/; | |
if ($file1 && $file2) { | |
# We may not have extracted this yet, so we pull from the config if not | |
$meta_color ||= get_config_color("meta"); | |
my $change = file_change_string($file1,$file2); | |
print horizontal_rule($meta_color); | |
print $meta_color . $change . "\n"; | |
print horizontal_rule($meta_color); | |
} | |
$i += 3; # We've consumed three lines | |
next; | |
##################################### | |
# Just a regular line, print it out # | |
##################################### | |
} else { | |
# Mark empty line with a red/green box indicating addition/removal | |
if ($mark_empty_lines) { | |
$line = mark_empty_line($line); | |
} | |
# Remove the correct number of leading " " or "+" or "-" | |
if ($strip_leading_indicators) { | |
$line = strip_leading_indicators($line,$columns_to_remove); | |
} | |
print $line; | |
} | |
$i++; | |
} | |
} | |
###################################################################################################### | |
# End regular code, begin functions | |
###################################################################################################### | |
# Courtesy of github.com/git/git/blob/ab5d01a/git-add--interactive.perl#L798-L805 | |
sub parse_hunk_header { | |
my ($line) = @_; | |
my ($o_ofs, $o_cnt, $n_ofs, $n_cnt) = $line =~ /^\@\@+(?: -(\d+)(?:,(\d+))?)+ \+(\d+)(?:,(\d+))? \@\@+/; | |
$o_cnt = 1 unless defined $o_cnt; | |
$n_cnt = 1 unless defined $n_cnt; | |
return ($o_ofs, $o_cnt, $n_ofs, $n_cnt); | |
} | |
# Mark the first char of an empty line | |
sub mark_empty_line { | |
my $line = shift(); | |
my $reset_color = "\e\\[0?m"; | |
my $reset_escape = "\e\[m"; | |
my $invert_color = "\e\[7m"; | |
$line =~ s/^($ansi_color_regex)[+-]$reset_color\s*$/$invert_color$1 $reset_escape\n/; | |
return $line; | |
} | |
# String to boolean | |
sub boolean { | |
my $str = shift(); | |
$str = trim($str); | |
if ($str eq "" || $str =~ /^(no|false|0)$/i) { | |
return 0; | |
} else { | |
return 1; | |
} | |
} | |
# Memoize getting the git config | |
{ | |
my $static_config; | |
sub git_config_raw { | |
if ($static_config) { | |
# If we already have the config return that | |
return $static_config; | |
} | |
my $cmd = "git config --list"; | |
my @out = `$cmd`; | |
$static_config = \@out; | |
return \@out; | |
} | |
} | |
# Fetch a textual item from the git config | |
sub git_config { | |
my $search_key = lc($_[0] || ""); | |
my $default_value = lc($_[1] || ""); | |
my $out = git_config_raw(); | |
# If we're in a unit test, use the default (don't read the users config) | |
if (in_unit_test()) { | |
return $default_value; | |
} | |
my $raw = {}; | |
foreach my $line (@$out) { | |
if ($line =~ /=/) { | |
my ($key,$value) = split("=",$line,2); | |
$value =~ s/\s+$//; | |
$raw->{$key} = $value; | |
} | |
} | |
# If we're given a search key return that, else return the hash | |
if ($search_key) { | |
return $raw->{$search_key} || $default_value; | |
} else { | |
return $raw; | |
} | |
} | |
# Fetch a boolean item from the git config | |
sub git_config_boolean { | |
my $search_key = lc($_[0] || ""); | |
my $default_value = lc($_[1] || 0); # Default to false | |
# If we're in a unit test, use the default (don't read the users config) | |
if (in_unit_test()) { | |
return boolean($default_value); | |
} | |
my $result = git_config($search_key,$default_value); | |
my $ret = boolean($result); | |
return $ret; | |
} | |
# Check if we're inside of BATS | |
sub in_unit_test { | |
if ($ENV{BATS_CWD}) { | |
return 1; | |
} else { | |
return 0; | |
} | |
} | |
sub get_less_charset { | |
my @less_char_vars = ("LESSCHARSET", "LESSCHARDEF", "LC_ALL", "LC_CTYPE", "LANG"); | |
foreach (@less_char_vars) { | |
return $ENV{$_} if defined $ENV{$_}; | |
} | |
return ""; | |
} | |
sub should_print_unicode { | |
if (-t STDOUT) { | |
# Always print unicode chars if we're not piping stuff, e.g. to less(1) | |
return 1; | |
} | |
# Otherwise, assume we're piping to less(1) | |
my $less_charset = get_less_charset(); | |
if ($less_charset =~ /utf-?8/i) { | |
return 1; | |
} | |
return 0; | |
} | |
# Try and be smart about what line the diff hunk starts on | |
sub start_line_calc { | |
my ($line_num,$diff_context) = @_; | |
my $ret; | |
if ($line_num == 0 && $diff_context == 0) { | |
return 1; | |
} | |
# Git defaults to three lines of context | |
my $default_context_lines = 3; | |
# Three lines on either side, and the line itself = 7 | |
my $expected_context = ($default_context_lines * 2 + 1); | |
# The first three lines | |
if ($line_num == 1 && $diff_context < $expected_context) { | |
$ret = $diff_context - $default_context_lines; | |
} else { | |
$ret = $line_num + $default_context_lines; | |
} | |
if ($ret < 1) { | |
$ret = 1; | |
} | |
return $ret; | |
} | |
# Remove + or - at the beginning of the lines | |
sub strip_leading_indicators { | |
my $line = shift(); # Array passed in by reference | |
my $columns_to_remove = shift(); # Don't remove any lines by default | |
if ($columns_to_remove == 0) { | |
return $line; # Nothing to do | |
} | |
$line =~ s/^(${ansi_color_regex})([ +-]){${columns_to_remove}}/$1/; | |
if ($manually_color_lines) { | |
if (defined($5) && $5 eq "+") { | |
my $add_line_color = get_config_color("add_line"); | |
$line = $add_line_color . $line . $reset_color; | |
} elsif (defined($5) && $5 eq "-") { | |
my $remove_line_color = get_config_color("remove_line"); | |
$line = $remove_line_color . $line . $reset_color; | |
} | |
} | |
return $line; | |
} | |
# Count the number of a given char in a string | |
sub char_count { | |
my ($needle,$str) = @_; | |
my $len = length($str); | |
my $ret = 0; | |
for (my $i = 0; $i < $len; $i++) { | |
my $found = substr($str,$i,1); | |
if ($needle eq $found) { $ret++; } | |
} | |
return $ret; | |
} | |
# Remove all ANSI codes from a string | |
sub bleach_text { | |
my $str = shift(); | |
$str =~ s/\e\[\d*(;\d+)*m//mg; | |
return $str; | |
} | |
# Remove all trailing and leading spaces | |
sub trim { | |
my $s = shift(); | |
if (!$s) { return ""; } | |
$s =~ s/^\s*|\s*$//g; | |
return $s; | |
} | |
# Print a line of em-dash or line-drawing chars the full width of the screen | |
sub horizontal_rule { | |
my $color = $_[0] || ""; | |
my $width = $ruler_width || `tput cols`; | |
if (is_windows()) { | |
$width--; | |
} | |
# em-dash http://www.fileformat.info/info/unicode/char/2014/index.htm | |
#my $dash = "\x{2014}"; | |
# BOX DRAWINGS LIGHT HORIZONTAL http://www.fileformat.info/info/unicode/char/2500/index.htm | |
my $dash; | |
if ($use_unicode_dash_for_ruler && should_print_unicode()) { | |
$dash = Encode::encode('UTF-8', "\x{2500}"); | |
} else { | |
$dash = "-"; | |
} | |
# Draw the line | |
my $ret = $color . ($dash x $width) . "\n"; | |
return $ret; | |
} | |
sub file_change_string { | |
my $file_1 = shift(); | |
my $file_2 = shift(); | |
# If they're the same it's a modify | |
if ($file_1 eq $file_2) { | |
return "modified: $file_1"; | |
# If the first is /dev/null it's a new file | |
} elsif ($file_1 eq "/dev/null") { | |
my $add_color = $DiffHighlight::NEW_HIGHLIGHT[1]; | |
return "added: $add_color$file_2$reset_color"; | |
# If the second is /dev/null it's a deletion | |
} elsif ($file_2 eq "/dev/null") { | |
my $del_color = $DiffHighlight::OLD_HIGHLIGHT[1]; | |
return "deleted: $del_color$file_1$reset_color"; | |
# If the files aren't the same it's a rename | |
} elsif ($file_1 ne $file_2) { | |
my ($old, $new) = DiffHighlight::highlight_pair($file_1,$file_2,{only_diff => 1}); | |
$old = trim($old); | |
$new = trim($new); | |
# highlight_pair resets the colors, but we want it to be the meta color | |
$old =~ s/(\e0?\[m)/$1$meta_color/g; | |
$new =~ s/(\e0?\[m)/$1$meta_color/g; | |
return "renamed: $old to $new"; | |
# Something we haven't thought of yet | |
} else { | |
return "$file_1 -> $file_2"; | |
} | |
} | |
# Check to see if STDIN is connected to an interactive terminal | |
sub has_stdin { | |
my $i = -t STDIN; | |
my $ret = int(!$i); | |
return $ret; | |
} | |
# We use this instead of Getopt::Long because it's faster and we're not parsing any | |
# crazy arguments | |
# Borrowed from: https://www.perturb.org/display/1153_Perl_Quick_extract_variables_from_ARGV.html | |
sub argv { | |
my $ret = {}; | |
for (my $i = 0; $i < scalar(@ARGV); $i++) { | |
# If the item starts with "-" it's a key | |
if ((my ($key) = $ARGV[$i] =~ /^--?([a-zA-Z_-]*\w)$/) && ($ARGV[$i] !~ /^-\w\w/)) { | |
# If the next item does not start with "--" it's the value for this item | |
if (defined($ARGV[$i + 1]) && ($ARGV[$i + 1] !~ /^--?\D/)) { | |
$ret->{$key} = $ARGV[$i + 1]; | |
# Bareword like --verbose with no options | |
} else { | |
$ret->{$key}++; | |
} | |
} | |
} | |
# We're looking for a certain item | |
if ($_[0]) { return $ret->{$_[0]}; } | |
return $ret; | |
} | |
# Output the command line usage for d-s-f | |
sub usage { | |
my $out = color("white_bold") . version() . color("reset") . "\n"; | |
$out .= "Usage: | |
git diff --color | diff-so-fancy # Use d-s-f on one diff | |
diff-so-fancy --colors # View the commands to set the recommended colors | |
diff-so-fancy --set-defaults # Configure git-diff to use diff-so-fancy and suggested colors | |
# Configure git to use d-s-f for *all* diff operations | |
git config --global core.pager \"diff-so-fancy | less --tabs=4 -RFX\"\n"; | |
return $out; | |
} | |
sub get_default_colors { | |
my $out = "# Recommended default colors for diff-so-fancy\n"; | |
$out .= "# --------------------------------------------\n"; | |
$out .= 'git config --global color.ui true | |
git config --global color.diff-highlight.oldNormal "red bold" | |
git config --global color.diff-highlight.oldHighlight "red bold 52" | |
git config --global color.diff-highlight.newNormal "green bold" | |
git config --global color.diff-highlight.newHighlight "green bold 22" | |
git config --global color.diff.meta "yellow" | |
git config --global color.diff.frag "magenta bold" | |
git config --global color.diff.commit "yellow bold" | |
git config --global color.diff.old "red bold" | |
git config --global color.diff.new "green bold" | |
git config --global color.diff.whitespace "red reverse" | |
'; | |
return $out; | |
} | |
# Output the current version string | |
sub version { | |
my $ret = "Diff-so-fancy: https://github.com/so-fancy/diff-so-fancy\n"; | |
$ret .= "Version : $VERSION\n"; | |
return $ret; | |
} | |
sub is_windows { | |
if ($^O eq 'MSWin32' or $^O eq 'dos' or $^O eq 'os2' or $^O eq 'cygwin' or $^O eq 'msys') { | |
return 1; | |
} else { | |
return 0; | |
} | |
} | |
# Return value is whether this is the first time they've run d-s-f | |
sub check_first_run { | |
my $ret = 0; | |
# If first-run is not set, or it's set to "true" | |
my $first_run = git_config_boolean('diff-so-fancy.first-run'); | |
# See if they're previously set SOME diff-highlight colors | |
my $has_dh_colors = git_config_boolean('color.diff-highlight.oldnormal') || git_config_boolean('color.diff-highlight.newnormal'); | |
#$first_run = 1; $has_dh_colors = 0; | |
if (!$first_run || $has_dh_colors) { | |
return 0; | |
} else { | |
print "This appears to be the first time you've run diff-so-fancy, please note\n"; | |
print "that the default git colors are not ideal. Diff-so-fancy recommends the\n"; | |
print "following colors.\n\n"; | |
print get_default_colors(); | |
# Set the first run flag to false | |
my $cmd = 'git config --global diff-so-fancy.first-run false'; | |
system($cmd); | |
exit; | |
} | |
return 1; | |
} | |
sub set_defaults { | |
my $color_config = get_default_colors(); | |
my $git_config = 'git config --global core.pager "diff-so-fancy | less --tabs=4 -RFX"'; | |
my $first_cmd = 'git config --global diff-so-fancy.first-run false'; | |
my @cmds = split(/\n/,$color_config); | |
push(@cmds,$git_config); | |
push(@cmds,$first_cmd); | |
# Remove all comments from the commands | |
foreach my $x (@cmds) { | |
$x =~ s/#.*//g; | |
} | |
# Remove any empty commands | |
@cmds = grep($_,@cmds); | |
foreach my $cmd (@cmds) { | |
system($cmd); | |
my $exit = ($? >> 8); | |
if ($exit != 0) { | |
die("Error running: '$cmd' (error #18941)\n"); | |
} | |
} | |
return 1; | |
} | |
# Borrowed from: https://www.perturb.org/display/1167_Perl_ANSI_colors.html | |
# String format: '115', '165_bold', '10_on_140', 'reset', 'on_173', 'red', 'white_on_blue' | |
sub color { | |
my $str = shift(); | |
# No string sent in, so we just reset | |
if (!length($str) || $str eq 'reset') { return "\e[0m"; } | |
# Some predefined colors | |
my %color_map = qw(red 160 blue 21 green 34 yellow 226 orange 214 purple 93 white 15 black 0); | |
$str =~ s|([A-Za-z]+)|$color_map{$1} // $1|eg; | |
# Get foreground/background and any commands | |
my ($fc,$cmd) = $str =~ /(\d+)?_?(\w+)?/g; | |
my ($bc) = $str =~ /on_?(\d+)/g; | |
# Some predefined commands | |
my %cmd_map = qw(bold 1 italic 3 underline 4 blink 5 inverse 7); | |
my $cmd_num = $cmd_map{$cmd // 0}; | |
my $ret = ''; | |
if ($cmd_num) { $ret .= "\e[${cmd_num}m"; } | |
if (defined($fc)) { $ret .= "\e[38;5;${fc}m"; } | |
if (defined($bc)) { $ret .= "\e[48;5;${bc}m"; } | |
return $ret; | |
} | |
# Get colors used for various output sections (memoized) | |
{ | |
my $static_config; | |
sub get_config_color { | |
my $str = shift(); | |
my $ret = ""; | |
if ($static_config->{$str}) { | |
return $static_config->{$str}; | |
} | |
if ($str eq "meta") { | |
# Default ANSI yellow | |
$ret = DiffHighlight::color_config('color.diff.meta', color(11)); | |
} elsif ($str eq "reset") { | |
$ret = color("reset"); | |
} elsif ($str eq "add_line") { | |
# Default ANSI green | |
$ret = DiffHighlight::color_config('color.diff.new', color('bold') . color(2)); | |
} elsif ($str eq "remove_line") { | |
# Default ANSI red | |
$ret = DiffHighlight::color_config('color.diff.old', color('bold') . color(1)); | |
} elsif ($str eq "last_function") { | |
$ret = DiffHighlight::color_config('color.diff.func', color(146)); | |
} | |
# Cache (memoize) the entry for later | |
$static_config->{$str} = $ret; | |
return $ret; | |
} | |
} | |
sub starts_with_ansi { | |
my $str = shift(); | |
if ($str =~ /^$ansi_color_regex/) { | |
return 1; | |
} else { | |
return 0; | |
} | |
} | |
# vim: tabstop=4 shiftwidth=4 noexpandtab autoindent softtabstop=4 | |
=== tat | |
#!/bin/sh | |
# | |
# Attach or create tmux session named the same as current directory. | |
path_name="$(basename "$PWD" | tr . -)" | |
session_name=${1-$path_name} | |
not_in_tmux() { | |
[ -z "$TMUX" ] | |
} | |
session_exists() { | |
tmux has-session -t "$session_name" | |
} | |
create_detached_session() { | |
(TMUX='' tmux new-session -Ad -s "$session_name") | |
} | |
create_if_needed_and_attach() { | |
if not_in_tmux; then | |
tmux new-session -As "$session_name" | |
else | |
if ! session_exists; then | |
create_detached_session | |
fi | |
tmux switch-client -t "$session_name" | |
fi | |
} | |
create_if_needed_and_attach | |
=== vimrc.bundles | |
call plug#begin('~/.vim/bundle') | |
" Define bundles via Github repos | |
Plug 'janko-m/vim-test' | |
Plug 'pangloss/vim-javascript' | |
Plug 'pbrisbin/vim-mkdir' | |
Plug 'slim-template/vim-slim' | |
Plug 'tpope/vim-bundler' | |
Plug 'tpope/vim-eunuch' | |
Plug 'tpope/vim-fugitive' | |
Plug 'tpope/vim-projectionist' | |
Plug 'tpope/vim-rails' | |
Plug 'tpope/vim-rake' | |
Plug 'tpope/vim-repeat' | |
Plug 'tpope/vim-rhubarb' | |
Plug 'tpope/vim-surround' | |
Plug 'vim-ruby/vim-ruby' | |
Plug 'vim-scripts/tComment' | |
Plug 'ElmCast/elm-vim' | |
Plug 'tpope/vim-dispatch' | |
Plug 'tpope/vim-markdown' | |
Plug 'tpope/vim-vinegar' | |
Plug 'nelstrom/vim-textobj-rubyblock' | |
Plug 'kana/vim-textobj-user' | |
Plug 'altercation/vim-colors-solarized' | |
Plug 'neoclide/coc.nvim', {'branch': 'release'} | |
Plug 'derekwyatt/vim-scala' | |
Plug 'junegunn/fzf', { 'dir': '~/.fzf', 'do': './install --all' } | |
Plug 'junegunn/fzf.vim' | |
Plug 'KabbAmine/zeavim.vim' | |
Plug 'christoomey/vim-tmux-navigator' | |
Plug 'morhetz/gruvbox' | |
Plug 'jiangmiao/auto-pairs' | |
Plug 'tomasr/molokai' | |
Plug 'fatih/vim-go' | |
Plug 'joshdick/onedark.vim' | |
Plug 'itchyny/lightline.vim' | |
call plug#end() |
Author
gnfisher
commented
Dec 16, 2019
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment