gnu-emacs-sources
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

retags 1.1


From: Tom Tromey
Subject: retags 1.1
Date: Sun, 09 Mar 2008 10:59:08 -0600
User-agent: Gnus/5.11 (Gnus v5.11) Emacs/22.1 (gnu/linux)

Here's a new version of the 'retags' script.

This version adds the ability to ignore patterns, using a line like
this in the .retags file:

testsuite/* ignore

It also changes retags to run recursively by default.

Finally, it tries to be smarter at startup time, so that the --force
option is no longer needed.

Tom

#! /bin/bash

# retags [--verbose] [--kill] DIR
# Run in background and watch for changes in DIR.
# Re-run etags whenever something changes.
# --kill means stop a running retags in DIR.
# --verbose turns on debug output
# Reads DIR/.retags to see how to run etags.
# Each line of .retags is a glob matching a file name, followed
# by etags arguments (don't use -o or the like).
# Instead of arguments, the word "ignore" means to ignore the file.
# Comments (with '#') or blank lines are ok.
# For instance, a typical, simple one is:
# *.[chyl]
# A more complicated one:
# *.def --language=none --regex='/DEFTREECODE [(]\([A-Z_]+\)/\1/'
# testsuite/* ignore

# By Tom Tromey <address@hidden>
# Licensed under the GPL.
# Version 1.1.

kill=
verbose=

function usage ()
{
    echo "usage: retags [--verbose] [--kill] DIR" 1>&2
    exit 1
}

while [[ $# -ne 1 ]]; do
    case "$1" in
      (--kill) kill=yes ;;
        # We used to support --force, so keep doing that.
      (--force) ;;
      (--verbose) verbose=yes ;;
      (*) break
    esac
    shift
done

if test $# -ne 1; then
    usage
fi
dir=$1

if test -n "$kill"; then
    rm -f $dir/.retags.pid
    exit 0
fi

if ! test -f $dir/.retags; then
    echo "retags: no such file $dir/.retags" 1>&2
    exit 1
fi

dir=$(cd $dir && pwd)
cd $dir

if test -f .retags.pid; then
    # Kill the running retags.
    rm .retags.pid
fi

mkdir -p .retags.d

declare -a file_contents

function verbose ()
{
    if test -n "$verbose"; then
        echo "$@"
    fi
}

function run ()
{
    verbose "$@"
    "$@"
}

function read_retags ()
{
    local -i idx=0
    file_contents=()
    while read line; do
        line=$(echo "$line" | sed -e 's, *#.*$,,')
        if test "$line" != ""; then
            verbose "Adding $line"
            file_contents[$idx]=$line
            idx=idx+1
        fi
    done < .retags
}

function finish ()
{
    if test "$(cat .retags.pid 2> /dev/null)" = "$$"; then
        rm -f .retags.pid
    fi
    exit 0
}

function update_etags ()
{
    local args
    local files=$(echo .retags.d/*)
    if test "$files" != '.retags.d/*'; then
        # We could use etags -i, but having a large number of includes
        # makes emacs freak out.
        cat $files > TAGS
    fi
}

function handle_file ()
{
    local event=$1
    local file=$2
    local -i idx=0

    if test -e $file && ! test -f $file; then
        return
    fi

    case $file in
      # Backups and other uninteresting files.
      (*~ | .#* | \#* | .retags.d* | TAGS)
        return
        ;;

      # Source control stuff.
      (*.git* | *CVS* | *.svn* | *.hg*)
        return
        ;;

      (.retags.pid | */.retags.pid)
        finish
        return
        ;;

      (.retags | */.retags)
        if test $event = DELETE; then
            finish
        elif test $event = CLOSE_WRITE; then
            read_retags
        fi
        return
    esac

    # We may get these even without listening for them explicitly
    if test "$event" = DELETE_SELF || test $event = UNMOUNT; then
        finish
    fi

    # See if the file matches any pattern the user provided.
    local found pattern args
    while [[ $idx -lt address@hidden ]]; do
        pattern=$(echo "${file_contents[$idx]}" | sed -e 's, .*$,,')
        case $file in
          ($pattern)
              found=yes
              args=$(echo "${file_contents[$idx]}" | sed -e 's,^[^ ]*\( 
\|$\),,')
              break
              ;;
        esac
        idx=idx+1
    done

    if test -z "$found"; then
        return
    fi

    # A silly way to generate a variable name from the file name.  Why
    # did I write this in sh again?  Note the leading 't'.
    local md5=t$(echo "$file" | md5sum | sed 's/  *.*$//')
    local tfile=.retags.d/$md5
    local update=
    local run_etags=

    if test "$args" = ignore; then
        if test $event = MODIFY_IF && test -f $tfile; then
            # Was created, but is now ignored.
            rm -f $tfile
        fi
        return
    fi

    case $event in
      # This is a fake event used at startup only.
      (MODIFY_IF)
         # Note this does the right thing if tfile doesn't exist.
         if test $file -nt $tfile; then
             run_etags=yes
         fi
         ;;

      (MODIFY)
         eval $md5=modified
         ;;

      (CREATE)
         eval $md5=modified
         update=yes
         ;;

      (DELETE)
         if test -f $tfile; then
             rm -f $tfile
             update=yes
         fi
         ;;

      (CLOSE_WRITE)
         if test "$(eval echo \$$md5)" = modified; then
             run_etags=yes
         fi
         ;;
    esac

    if test -n "$run_etags"; then
        # Update the tags file for this source file.  We have to
        # play games here to prevent relativization, so we can
        # cat the files into TAGS.  If we ever use -i instead,
        # fix this.
        run eval "etags $args -o .retags.tmp $file"
        mv .retags.tmp $tfile
        eval $md5=
        if test $event != MODIFY_IF; then
            # See comment in update_etags.
            update=yes
        fi
    fi

    if test -n "$update"; then
        update_etags
    fi
}

# Make the TAGS file when it is missing.
function start_up ()
{
    local file
    echo -n "Updating tags files..."
    find . -type f -print | sed -e 's,^[.]/,,' | while read file; do
        handle_file MODIFY_IF $file
    done
    update_etags
    echo "done"
}

# Read the config file and make the tags file the first time, if
# needed.
read_retags
start_up

(
absdir=$(pwd)/
# FIXME: handle moved_to and moved_from
inotifywait -mqr -e modify -e close_write -e delete -e create . |
while read edir events file; do
    # Only bother with the first event.
    event=$(echo "$events" | sed -e 's/,.*$//')
    # Strip the base off the directory.
    edir=$(echo "$edir" | sed -e "s,$absdir,,")
    if test -n "$edir"; then
        file="$edir$file"
    fi
    handle_file $event $file
done
) &

echo $! > .retags.pid





reply via email to

[Prev in Thread] Current Thread [Next in Thread]