├── extensions.rb ├── generate_btags_functions.rb └── btags /extensions.rb: -------------------------------------------------------------------------------- 1 | def etags_file_extensions 2 | lines = `etags --list-maps`.split("\n").map { |line| line.split } 3 | lines.each { |line| line.shift } 4 | lines.each do |types| 5 | types.map! do |type| 6 | `exrex.py '#{type.gsub("+", "\\+").gsub(".", "\\.").gsub(/^\*/, '')}'`.split("\n") 7 | end 8 | end 9 | extensions = lines.flatten 10 | extensions.each do |extension| 11 | extension.insert(0, "%") if extension[0, 1] == "." 12 | end 13 | extensions 14 | end 15 | 16 | def rtags_file_extensions 17 | ["%.rb", "%.rake", "%.ru", "Gemfile", "Guardfile", "Procfile", "Rakefile"] 18 | end 19 | 20 | #Add extensions, starting with least specific and moving to more specific 21 | add_extensions("ctags", "-u -n -f '-'", etags_file_extensions, "sudo apt-get install ctags or similar; see also http://ctags.sourceforge.net.") 22 | add_extensions("ripper-tags", "-f '-'", rtags_file_extensions, "gem install ripper-tags; see also https://github.com/bloopletech/ripper-tags.") 23 | 24 | -------------------------------------------------------------------------------- /generate_btags_functions.rb: -------------------------------------------------------------------------------- 1 | 2 | COMMANDS = {} 3 | EXTENSIONS = {} 4 | def add_extensions(command, arguments, extensions, source) 5 | COMMANDS[command] = { :arguments => arguments, :extensions => [], :source => source } 6 | extensions.each do |extension| 7 | EXTENSIONS[extension] = command 8 | end 9 | end 10 | 11 | require_relative 'extensions.rb' 12 | 13 | EXTENSIONS.each_pair do |extension, command| 14 | COMMANDS[command][:extensions] << extension 15 | end 16 | 17 | 18 | f = <<-EOF 19 | function btags_check_commands() { 20 | FAILED=0 21 | 22 | EOF 23 | 24 | COMMANDS.each_pair do |command, options| 25 | f << <<-EOF 26 | if [ "$(which "#{command}")" = "" ]; then 27 | echo "Cannot find '#{command}'. Either the command is not in your PATH, or the command isn't installed. To install '#{command}': #{options[:source]}" 28 | FAILED=1 29 | fi 30 | 31 | EOF 32 | end 33 | 34 | COMMANDS.delete_if { |command, options| options[:extensions].empty? } 35 | 36 | f << <<-EOF 37 | if [ "$FAILED" = "1" ]; then 38 | exit 1 39 | fi 40 | } 41 | 42 | function btags_generate() { 43 | cd "$SRCDIR" 44 | 45 | grep -r -l -I "^" | grep -v -E '(^\\.|/\\.)' > "$TAGSDIR/files" 46 | 47 | EOF 48 | 49 | COMMANDS.each_pair do |command, options| 50 | regex = "'^(#{options[:extensions].map { |e| e.gsub(".", "\\.").gsub("%", ".*") }.join("|")})$'" 51 | f << <<-EOF 52 | grep -E --color=never #{regex} "$TAGSDIR/files" > "$TAGSDIR/#{command}.files" 53 | while read file; do 54 | if [ "$file" -nt "$TAGSDIR/$file.tags" ]; then 55 | echo "$file" 56 | fi 57 | done < "$TAGSDIR/#{command}.files" > "$TAGSDIR/#{command}.changed.files" 58 | 59 | EOF 60 | end 61 | 62 | f << <<-EOF 63 | if [ #{COMMANDS.keys.map { |c| "! -s \"$TAGSDIR/#{c}.changed.files\"" }.join(" -a ")} ]; then 64 | return 0 65 | fi 66 | 67 | ESCAPEDTAGSDIR="$(printf '%q' "$TAGSDIR")" 68 | 69 | EOF 70 | 71 | COMMANDS.each_pair do |command, options| 72 | f << <<-EOF 73 | parallel --gnu mkdir -p "$ESCAPEDTAGSDIR"/{//} "2>/dev/null" "&&" echo -e "!_TAG_COLLECTION_NAME\\\\\\t{} Tags" ">" "$ESCAPEDTAGSDIR"/{}.tags "&&" #{command} #{options[:arguments]} {} ">>" "$ESCAPEDTAGSDIR"/{}.tags "2>/dev/null" "&&" echo -n "." < "$TAGSDIR/#{command}.changed.files" 74 | 75 | EOF 76 | end 77 | 78 | f << <<-EOF 79 | echo -e "!_TAG_COLLECTION_NAME\\tProject Wide Tags" > "$TAGSDIR/tags.tags" 80 | 81 | while read file; do 82 | cat "$TAGSDIR/$file.tags" 83 | done < <(cat "$TAGSDIR"/{#{COMMANDS.keys.join(",")}}.files) >> "$TAGSDIR/tags.tags" 84 | 85 | sed -e 's/^.*$/&\\t&\\t1;"\\tF/g' < "$TAGSDIR/files" >> "$TAGSDIR/tags.tags" 86 | 87 | sed -i -e '/^$/d' "$TAGSDIR/tags.tags" 88 | 89 | rm "$TAGSDIR/files" "$TAGSDIR"/{#{COMMANDS.keys.join(",")}}.files "$TAGSDIR"/{#{COMMANDS.keys.join(",")}}.changed.files 90 | } 91 | 92 | EOF 93 | 94 | btags = File.read("btags") 95 | btags.gsub!(/\n#Start btags dynamic functions\n.*#End btags dynamic functions\n/m, "\n#Start btags dynamic functions\n#{f.gsub('\\', '\\\\\\')}#End btags dynamic functions\n") 96 | File.open("btags", "w") { |w| w << btags } 97 | 98 | -------------------------------------------------------------------------------- /btags: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | #Start btags dynamic functions 4 | function btags_check_commands() { 5 | FAILED=0 6 | 7 | if [ "$(which "ctags")" = "" ]; then 8 | echo "Cannot find 'ctags'. Either the command is not in your PATH, or the command isn't installed. To install 'ctags': sudo apt-get install ctags or similar; see also http://ctags.sourceforge.net." 9 | FAILED=1 10 | fi 11 | 12 | if [ "$(which "ripper-tags")" = "" ]; then 13 | echo "Cannot find 'ripper-tags'. Either the command is not in your PATH, or the command isn't installed. To install 'ripper-tags': gem install ripper-tags; see also https://github.com/bloopletech/ripper-tags." 14 | FAILED=1 15 | fi 16 | 17 | if [ "$FAILED" = "1" ]; then 18 | exit 1 19 | fi 20 | } 21 | 22 | function btags_generate() { 23 | cd "$SRCDIR" 24 | 25 | grep -r -l -I "^" | grep -v -E '(^\.|/\.)' > "$TAGSDIR/files" 26 | 27 | grep -E --color=never '^(.*\.build\.xml|.*\.asm|.*\.ASM|.*\.s|.*\.S|.*\.A51|.*\.29k|.*\.29K|.*\.66k|.*\.66K|.*\.66s|.*\.66S|.*\.66x|.*\.66X|.*\.68k|.*\.68K|.*\.68s|.*\.68S|.*\.68x|.*\.68X|.*\.86k|.*\.86K|.*\.86s|.*\.86S|.*\.86x|.*\.86X|.*\.88k|.*\.88K|.*\.88s|.*\.88S|.*\.88x|.*\.88X|.*\.x66|.*\.x68|.*\.x86|.*\.x88|.*\.X66|.*\.X68|.*\.X86|.*\.X88|.*\.asp|.*\.asa|.*\.awk|.*\.gawk|.*\.mawk|.*\.bas|.*\.bi|.*\.bb|.*\.pb|.*\.bet|.*\.c|.*\.cc|.*\.cp|.*\.cpp|.*\.cxx|.*\.h|.*\.hh|.*\.hp|.*\.hpp|.*\.hxx|.*\.C|.*\.H|.*\.cs|.*\.cbl|.*\.cob|.*\.CBL|.*\.COB|.*\.bat|.*\.cmd|.*\.e|.*\.erl|.*\.ERL|.*\.hrl|.*\.HRL|.*\.as|.*\.mxml|.*\.f|.*\.for|.*\.ftn|.*\.f77|.*\.f90|.*\.f95|.*\.F|.*\.FOR|.*\.FTN|.*\.F77|.*\.F90|.*\.F95|.*\.go|.*\.htm|.*\.html|.*\.java|.*\.js|.*\.cl|.*\.clisp|.*\.el|.*\.l|.*\.lisp|.*\.lsp|.*\.lua|.*\.mak|.*\.mk|Makefile|makefile|GNUmakefile|.*\.m|.*\.ml|.*\.mli|.*\.p|.*\.pas|.*\.pl|.*\.pm|.*\.plx|.*\.perl|.*\.php|.*\.php3|.*\.phtml|.*\.py|.*\.pyx|.*\.pxd|.*\.pxi|.*\.scons|.*\.rexx|.*\.rx|.*\.ruby|.*\.SCM|.*\.SM|.*\.sch|.*\.scheme|.*\.scm|.*\.sm|.*\.sh|.*\.SH|.*\.bsh|.*\.bash|.*\.ksh|.*\.zsh|.*\.sl|.*\.sml|.*\.sig|.*\.sql|.*\.tcl|.*\.tk|.*\.wish|.*\.itcl|.*\.tex|.*\.vr|.*\.vri|.*\.vrh|.*\.v|.*\.vhdl|.*\.vhd|.*\.vim|.*\.y)$' "$TAGSDIR/files" > "$TAGSDIR/ctags.files" 28 | while read file; do 29 | if [ "$file" -nt "$TAGSDIR/$file.tags" ]; then 30 | echo "$file" 31 | fi 32 | done < "$TAGSDIR/ctags.files" > "$TAGSDIR/ctags.changed.files" 33 | 34 | grep -E --color=never '^(.*\.rb|.*\.rake|.*\.ru|Gemfile|Guardfile|Procfile|Rakefile)$' "$TAGSDIR/files" > "$TAGSDIR/ripper-tags.files" 35 | while read file; do 36 | if [ "$file" -nt "$TAGSDIR/$file.tags" ]; then 37 | echo "$file" 38 | fi 39 | done < "$TAGSDIR/ripper-tags.files" > "$TAGSDIR/ripper-tags.changed.files" 40 | 41 | if [ ! -s "$TAGSDIR/ctags.changed.files" -a ! -s "$TAGSDIR/ripper-tags.changed.files" ]; then 42 | return 0 43 | fi 44 | 45 | ESCAPEDTAGSDIR="$(printf '%q' "$TAGSDIR")" 46 | 47 | parallel --gnu mkdir -p "$ESCAPEDTAGSDIR"/{//} "2>/dev/null" "&&" echo -e "!_TAG_COLLECTION_NAME\\\t{} Tags" ">" "$ESCAPEDTAGSDIR"/{}.tags "&&" ctags -u -n -f '-' {} ">>" "$ESCAPEDTAGSDIR"/{}.tags "2>/dev/null" "&&" echo -n "." < "$TAGSDIR/ctags.changed.files" 48 | 49 | parallel --gnu mkdir -p "$ESCAPEDTAGSDIR"/{//} "2>/dev/null" "&&" echo -e "!_TAG_COLLECTION_NAME\\\t{} Tags" ">" "$ESCAPEDTAGSDIR"/{}.tags "&&" ripper-tags -f '-' {} ">>" "$ESCAPEDTAGSDIR"/{}.tags "2>/dev/null" "&&" echo -n "." < "$TAGSDIR/ripper-tags.changed.files" 50 | 51 | echo -e "!_TAG_COLLECTION_NAME\tProject Wide Tags" > "$TAGSDIR/tags.tags" 52 | 53 | while read file; do 54 | cat "$TAGSDIR/$file.tags" 55 | done < <(cat "$TAGSDIR"/{ctags,ripper-tags}.files) >> "$TAGSDIR/tags.tags" 56 | 57 | sed -e 's/^.*$/&\t&\t1;"\tF/g' < "$TAGSDIR/files" >> "$TAGSDIR/tags.tags" 58 | 59 | sed -i -e '/^$/d' "$TAGSDIR/tags.tags" 60 | 61 | rm "$TAGSDIR/files" "$TAGSDIR"/{ctags,ripper-tags}.files "$TAGSDIR"/{ctags,ripper-tags}.changed.files 62 | } 63 | 64 | #End btags dynamic functions 65 | 66 | function btags_clean() { 67 | rm -r "$TAGSDIR"/* 68 | } 69 | 70 | function btags_git_branch() { 71 | if [ ! -e .git ]; then 72 | GITBRANCH="" 73 | return 74 | fi 75 | 76 | GITBRANCH="@$(git branch --no-color | sed -e "/^\s/d" -e "s/^\*\s//")" 77 | } 78 | 79 | case "$1" in 80 | path) 81 | ACTION="path" 82 | SRCPATH="$2" 83 | ;; 84 | clean) ACTION="clean";; 85 | debug) ACTION="debug";; 86 | stats) 87 | ACTION="stats" 88 | SRCPATH="$2" 89 | ;; 90 | *) ACTION="btags" 91 | SRCPATH="$1" 92 | ;; 93 | esac 94 | 95 | SRCDIR=$(realpath "$PWD") 96 | btags_git_branch 97 | TAGSDIR="$HOME/.btags$SRCDIR$GITBRANCH" 98 | OVERALLTAGSFILEPATH="$TAGSDIR/tags.tags" 99 | 100 | if [[ "$ACTION" == "btags" || "$ACTION" == "debug" || "$ACTION" == "path" || "$ACTION" == "stats" ]]; then 101 | if [[ "$SRCPATH" == "" ]]; then 102 | SRCPATH="$PWD" 103 | fi 104 | SRCPATH=$(realpath "$SRCPATH") 105 | 106 | SRCDIRLENGTH="${#SRCDIR}" 107 | if [[ "${SRCPATH:0:$SRCDIRLENGTH}" == "$SRCDIR" ]]; then 108 | SRCPATH="${SRCPATH:$SRCDIRLENGTH}" 109 | fi 110 | 111 | if [[ "$SRCPATH" == "" || -d "$SRCPATH" ]]; then 112 | TAGSFILEPATH="$TAGSDIR$SRCPATH/tags.tags" 113 | else 114 | TAGSFILEPATH="$TAGSDIR$SRCPATH.tags" 115 | fi 116 | fi 117 | 118 | mkdir -p "$TAGSDIR" 119 | 120 | ( 121 | flock -n 9 || { echo "An instance of btags is already updating this project."; exit 1; } 122 | 123 | if [[ "$ACTION" == "btags" ]]; then 124 | btags_check_commands 125 | btags_generate 126 | 127 | echo "" 128 | echo "$TAGSFILEPATH" 129 | elif [[ "$ACTION" == "debug" ]]; then 130 | echo "Starting in $PWD" 131 | echo "Srcdir is $SRCDIR" 132 | echo "Checking commands..." 133 | btags_check_commands 134 | echo "Generating updates..." 135 | btags_generate 136 | 137 | echo "" 138 | echo "Generating updates finished" 139 | echo "Tags file path:" 140 | echo "$TAGSFILEPATH" 141 | elif [[ "$ACTION" == "clean" ]]; then 142 | btags_clean 143 | echo "" 144 | elif [[ "$ACTION" == "path" ]]; then 145 | if ! [ -e "$TAGSFILEPATH" ]; then 146 | echo "Not a btags project" 147 | else 148 | echo -n "$TAGSFILEPATH" 149 | fi 150 | elif [[ "$ACTION" == "stats" ]]; then 151 | function human_readable_filesize() { 152 | LSOUTPUT=$(ls -1Ssh "$1") 153 | LSPARTS=($LSOUTPUT) 154 | echo ${LSPARTS[0]} 155 | } 156 | 157 | function human_readable_dirsize() { 158 | DUOUTPUT=$(du -csh "$1") 159 | DUPARTS=($DUOUTPUT) 160 | echo ${DUPARTS[0]} 161 | } 162 | 163 | function human_readable_word_count() { 164 | WCOUTPUT=$(wc -l "$1") 165 | WCPARTS=($WCOUTPUT) 166 | echo ${WCPARTS[0]} 167 | } 168 | 169 | function stats_for_file() { 170 | echo "File path: $1" 171 | FILESIZE=$(human_readable_filesize "$1") 172 | echo "File size: $FILESIZE" 173 | TAGSCOUNT=$(human_readable_word_count "$1") 174 | echo "Tags count: $TAGSCOUNT" 175 | echo 176 | } 177 | 178 | echo 179 | 180 | if [[ "$SRCPATH" != "" && ! -d "$SRCPATH" ]]; then 181 | echo "Specific tags file:" 182 | stats_for_file "$TAGSFILEPATH" 183 | fi 184 | 185 | echo "Overall tags file:" 186 | stats_for_file "$OVERALLTAGSFILEPATH" 187 | 188 | echo "This project:" 189 | TOTALSIZE=$(human_readable_dirsize "$TAGSDIR") 190 | echo "Total filesystem usage: $TOTALSIZE" 191 | TAGFILECOUNT=$(cd "$TAGSDIR"; find . -type f \( ! -path '*/.*' \) | wc -l) 192 | echo "Number of tag files: $TAGFILECOUNT" 193 | echo 194 | # TOTALTAGSCOUNT=$(find "$TAGSDIR" -type f -print0 | xargs -0 cat | wc -l) 195 | # echo "Total tags count: $TOTALTAGSCOUNT" 196 | 197 | TOTALSIZE=$(human_readable_dirsize "$HOME/.btags/") 198 | echo "Total filesystem usage for all btags projects: $TOTALSIZE" 199 | echo 200 | fi 201 | ) 9>"$TAGSDIR/.btags.lock" 202 | --------------------------------------------------------------------------------