#!/usr/bin/perl
# vim:set ft=perl ts=4 sw=4 et fdm=marker:
#
# revim - add customized vim modeline for given files
# Copyright (c) 2010 chaoslawful

use strict;
use warnings;
use Getopt::Std;

my %opts;

getopts('hm:', \%opts);

if($opts{h} or !@ARGV) {
    die <<EOT;
Usage: revim [-m <vim modeline>] src/*

In <vim modeline>, the following placeholder(s) can be used:

    * %t - Expands to vim file type. E.g. 'c' for .c files, 'perl' for .pl files.
EOT
}

my $vim_ml = $opts{m} || "vim:set ft=%t ts=4 sw=4 et fdm=marker:";
my @files = map glob, @ARGV;
for my $file (@files) {
    next if -d $file;
    my ($ft, $ml) = detect_filetype($file, $vim_ml);
    next if !defined($ft);
    revim($file, $ml);
}

sub detect_filetype
{
    my ($f, $tmpl) = @_;
    my ($ft, $lcmt, $rcmt);
    my %phs;

    if($f =~ /.([cC]|[hH])$/) {
        $ft = "c";
        ($lcmt, $rcmt) = ("/* ", " */");
    } elsif($f =~ /.(pl|pm)$/) {
        $ft = "perl";
        ($lcmt, $rcmt) = ("# ", "");
    } elsif($f =~ /.t_?$/) {
        # assuming tests are written in perl
        $ft = "perl";
        ($lcmt, $rcmt) = ("# ", "");
    } else {
        $ft = undef;
    }

    if(defined($ft)) {
        %phs = (
            "%t" => $ft,
        );

        $tmpl =~ s/(%[a-z])/$phs{$1}/ge;
        $tmpl =~ s/^/$lcmt/;
        $tmpl =~ s/$/$rcmt/;

        return ($ft, $tmpl);
    }

    return (undef, undef);
}

sub revim
{
    my ($f, $ml) = @_;
    my @lines;

    open my $in, $f
        or die "Can't open $f for reading: $!";
    while(<$in>) {
        push(@lines, $_);
    }
    close $in;

    my @nlines = grep {!/\bvim?:/} @lines;
    warn "revim: $f:\tremoved existing vim modeline.\n"
        if(@nlines != @lines);

    if($nlines[0] =~ /^#!/) {    # has shebang line
        my $shebang = shift @nlines;
        unshift(@nlines, $shebang, "$ml\n");
    } else {
        unshift(@nlines, "$ml\n");
    }

    my $text = join '', @nlines;

    open my $out, "> $f"
        or die "Can't open $f for writing: $!";
    binmode $out;
    print $out $text;
    close $out;

    warn "revim: $f:\tdone.\n";
}