VIM Tricks mit Perl (Linux-Magazin, Juli 2005)

Im Editor vim (VI IMproved) stecken eine Unzahl von Tricks, die im Programmieralltag die Tipparbeit reduzieren. Der heutige Snapshot zeigt eine Auswahl der effektivsten Sparmaßnahmen für Perl-Hacker.

Keine Wahl, die ein Programmierer im Leben trifft, ist so entscheidend und so unwiderrufbar wie die Wahl eines Editors. Wer sich einmal für vi oder Emacs entschieden hat, bleibt üblicherweise dabei und setzt alles daran, auch noch den letzten Trick aus dem permanent genutzten Werkzeug herauszufieseln. Wer effektiver mit dem Editor arbeitet, reduziert nicht nur die Gefahr, am Carpal-Tunnel-Syndrom zu erkranken, sondern programmiert erheblich schneller und mit weniger Tippfehlern.

Der Editor vim ist seinem altersschwachen Kollegen vi um einiges voraus. vim wurde über die Jahre für Vieltipper hochgezüchtet, lässt sich bis ins Detail konfigurieren, mit Plugins erweitern und so ganz nach Geschmack an den persönlichen Arbeitsstil anpassen. In der Konfigurationsdatei .vimrc im Home-Verzeichnis lassen sich alle heute vorgestellten Tricks dann permanent abgelegen.

Da Linux-Distributionen nicht immer der neueste vim beiliegt, prüft man am Besten mit

    vim --version

nach, welche Version vorliegt. Bei weniger als 6.1 empfiehlt sich ein Upgrade.

Leuchtende Schlüssel

Farblich unterlegte Schlüsselworte erleichtern das Verstehen von komplexeren Code-Gebilden erheblich. vim beherrscht das Syntax-Highlighting von allerlei Programmiersprachen ganz hervorragend und produziert selbst für das schwer zu parsende Perl erstaunlich präzise Ergebnisse. Abbildung 1 und 2 zeigen, wie viel einfacher farblich herausgestellte Code-Konstrukte erkennbar sind. Vorraussetzung ist lediglich, dass das benutzte xterm Farben unterstützt. Falls nicht schon von Anfang an eingestellt, stellt das Kommando :syntax on das Syntax-Highlighting an. Falls vim an der Datei-Endung (.pl oder .pm) oder auch am Inhalt (Shebang-Zeile enthält etwas wie #!/usr/bin/perl) erkennt, dass es sich um Perl-Code handelt, kommen die Konstrukte schön zum Vorschein. Falls eine neue Datei editiert wird, deren Name keine spezifische Perl-Endung hat und die zum Zeitpunkt des vim-Aufrufs noch keine Shebang-Zeile aufwies, lässt sich der Dateityp nachträglich mit

    :set filetype=perl

auf Perl einstellen.

Abbildung 1: vim ohne ...

Abbildung 2: ... und mit Syntax-Highlighting.

Abkürzungen

Wer ständig in der gleichen Sprache programmiert, wiederholt laufend die gleichen Tippmuster. Als Log::Log4perl-Advokat kann ich zum Beispiel schon nicht mehr zählen, wie oft ich

    use Log::Log4perl qw(:easy);

getippt habe. Zum Glück hat das jetzt ein Ende, denn das Kommando

    :abbreviate ul4p use Log::Log4perl qw(:easy);<RETURN>

definiert die Abkürzung ul4p. Sobald im Texteingabe-Modus die Zeichenfolge ul4p getippt und mit einem Wortbegrenzer wie der Leer- oder Enter-Taste abgeschlossen wird, expandiert vim die Folge automatisch zum vordefinierten Ausdruck. Ein wörtlich eingetipptes <RETURN> am Ende der Definition simuliert das Drücken der Enter-Taste. Wer nach der Expansion nicht im Eingabemodus bleiben, sondern in den Kommandomodus wechseln möchte, hängt einfach ein (ebenfalls ausgeschriebenes) <ESC> hintenan.

Eine weitere Möglichkeit, mit einer Abkürzung ein längeres Textstück in den Text zu holen, ist, vim eine Datei hereinholen zu lassen:

    :abb ul4p <BACKSPACE><ESC>:r ~/.tmpl_l4p<RETURN>

Sobald nun ul4p im Text getippt wird und anschließend ein Wortbegrenzer, ersetzt vim die Abkürzung nahtlos mit dem Inhalt der angegebenen Datei.

Keyboard Makros

Wiederkehrende Editierschritte, die Modifikationen mehrerer, unzusammenhängender Bereiche erfordern, lassen sich elegant mit Makros duplizieren. Abbildung 3 zeigt drei Funktionsköpfe, die es jeweils in Kommentare einzuranken gilt, wie in Abbildung zwei gezeigt.

Dazu sind folgende Kommandos nötig: Erst mit /sub nach sub suchen, dann den Makro-Recorder einschalten, den ersten Funktionskopf einranken, den Makro-Recorder ausschalten. Dann mit n nach dem nächsten sub suchen und mit @a wieder das Makro abspielen lassen. Hier sind die Kommandos zusammengefasst:

       # Nach 'sub' suchen
    /sub
       # Makro-Rekorder 
       # für Makro a einschalten
    qa
       # Zeile darüber einfügen, 
       # zurück in Kommandomodus
    O<ESC>
       # 20 '#'-Zeichen einfügen
    20i#<ESC>
       # Zeile kopieren
    yy
       # Eine Zeile nach unten fahren, kopierte Zeile
       # unterhalb einfügen.
    jp
       # Makro-Rekorder beenden
    q
       # Nach dem nächsten 'sub' suchen
    /sub
       # Macro 'a' abspielen
    @a 
       # ... wiederholen.

Abbildung 3: Der Macro-Editor während der Aufnahme ...

Abbildung 4: ... und der Nutzer spielt das Macro danach einfach zweimal ab.

Wer gleich von Anfang an neue Funktionen mit Kommentaren einranken möchte, definiert sich ein neues Kommando auf die Taste ``F'':

    :map F o<ESC>43i#<ESC>yyosub {<ENTER><ESC>Pk$i

Drückt der Benutzer dann ``F'' im Kommandomodus, fügt vim den Funktionskopf ein und positioniert den Cursor gleich im Eingabemodus an die richtige Stelle:

    ###########################################
    sub {
        ^ (Cursormarkierung)
    ###########################################

Der Buchstabensalat in der map-Definition besteht auch wieder aus den vi-typischen Ein-Tasten-Kommandos im Kommandomodus, die jeder vi-Enthusiast auswendig kennt. Die Anzahl der Hashmarks ist Geschmackssache, in der Definition oben wurden 43 gewählt. Bei sich ständig wiederholenden Sequenzen wie Funktionsköpfen sparen map-Kommandos enorm Zeit und Nerven. Wer möchte, kann das Kommando noch an die privaten Bedürfnisse anpassen, oft genutzte Parameterübergaben der Art

    my(...) = @_;

bieten sich an.

Eine weitere, sich häufig wiederholende Aufgabe ist das Sichern eines gerade bearbeiteten Skripts mit :w und der Aufruf

    perl -c skript.pl

um festzustellen, ob sich in das Skript soweit Fehler eingeschlichen haben. Der folgende Befehl legt die Aktionen ``sichern'' und ``perl -c ausführen'' einfach auf die Taste ``X'' (großes X) im Kommandomodus:

    :nnoremap X :w<Enter>:!perl -c %<Enter>

Wird statt :map der Befehl :noremap verwendet, wird das ``X'' niemals auf der rechten Seite eines anderen map-Ausdrucks evaluiert. Und :nnoremap führt die Definition nur im Normal- (also im Kommando-) Modus durch. Der Platzhalter % wird durch den Namen der aktuell editierten Datei ersetzt.

Autoformat

Bei längeren Texten wie POD-Dokumentation stellt sich locker fließender Erzählstil erst beim siebten oder achten Umschreiben ein. Wer dabei fleißig einfügt und wieder ausstreicht, erzeugt zerfledderte Absätze, die schwer korrekturzulesen sind. Textverarbeiter wie Word arbeiten ständig im Hintergrund, um die Absätze umzuformatieren, echte Hacker müssen die Ecken selbst ausbügeln.

Mit vim geht das mit nur vier Tastendrücken im Kommandomodus: {-g-q-}. Zuerst fährt { an den Anfang des gegenwärtigen Absatzes, das Kommando gq (wohl abgeleitet vom englischsprachigen Magazin GQ) bricht die Zeilen im Flattersatz um, und das abschließende } bestimmt die Reichweite des Kommandos: Bis zum Ende des Absatzes.

Noch eleganter geht es mit dem Perl-Modul Text::Autoformat von Großmeister Damian Conway. Neben Flattersatz kennt es auch noch allerlei intelligente Umbrüche: So kann es mit Aufzählungen umgehen (nachfolgende Zeilen eines Aufzählungspunktes werden entsprechend eingerückt) und auch mit > oder >> oder mehr Eckchen eingerückte Email-Zitate behandelt es, wie ein Mensch das tun würde.

Wer sich das Kommando

    :map f !Gperl -MText::Autoformat -e'autoformat'<RETURN>

definiert, drückt im Kommandomodus einfach die Taste f, während der Cursor irgendwo in einem Textabsatz steht, und schon wird dieser automatisch und sachgemäß formatiert.

Abbildung 5: Eine Auflistung und ein Abschnitt aus einer Email vor ...

Abbildung 6: ... und nach dem Formatieren mit Text::Autoformat

Wer die standardmäßig eingestellte Zeilenlänge von 72 Zeichen als zu breit (oder zu schmal) empfindet, kann sie mit der option right variieren:

    :map f !Gperl -MText::Autoformat -e'autoformat {right=>65}'<RETURN>

Erfahrene vim-Nutzer merken natürlich sofort, dass f im Kommandomodus schon vorbelegt ist: Der Cursor springt bis zum nächsten eingegebenen Zeichen vor, fe springt so bis zum nächsten e im Text. Wer diese Funktion tatsächlich nutzt, sollte einen anderen Buchstaben wählen oder auch gerne eine Zweierkombination einstellen:

    :map !f ...

reagiert im Kommandomodus erst, wenn zuerst ! und dann f eingegeben wird.

Richtig eingerückt

Über die Formatierung von Programmcode ist schon viel gestritten worden. Wo kommen die geschweiften Klammern hin? Wie tief wird verschachtelter Code eingerückt? Mit Leerzeichen oder mit Tabs? Da jeder Programmierer seinen eigenen Geschmack hat, bietet vim genug Optionen.

Beim Einrücken mit Tabs scheiden sich die Geister, viele lehnen sie völlig ab. Ist die Option :set expandtab gesetzt, wandelt vim alle Tabs in Leerzeichen um. Die Anzahl der Leerzeichen pro Tab bestimmt die Option

    :set shiftwidth=4

Wer allerdings expandtab blindlings setzt, läuft böse auf, wenn ein Makefile editiert wird: Dort sind Tabs vor den Kommandos für eine Target tatsächlich notwendig. Werden sie durch Leerzeichen ersetzt, ist dies ein Syntaxfehler. Die Lösung ist, vim per autocmd den Dateityp feststellen zu lassen, und nur bei erkanntem Perl-Programm die Option expandtab zu setzen:

    :filetype on
    :autocmd FileType perl :set expandtab

Eine nützliche Option, um sonst unsichtbare Zeichen sichtbar zu machen, ist :set list. Alle Tabs erscheinen als ^I und das Zeilenende ist mit einem blauen '$' mariert. :set nolist schaltet wieder zurück in den Normalmodus.

Die oben vorgestellte Option shiftwidth hat noch eine weitere Funktion: Zusammen mit der Option cindent lässt sich so ordentlich Tipparbeit sparen, denn sobald ein Konstrukt wie

    if($really) {
        _

eingetippt und die Return-Taste gedrückt wird, rückt vim die nächste Zeile entsprechend shiftwidth und expandtabs bereits ein. Tippt der Benutzer allerdings } und ein Return, schiebt vim die schließende Klammer ebenfalls automatisch wieder an den Zeilenanfang zurück. Da dieses Verhalten ebenfalls nicht für alle Dateiarten geignet ist, empfiehlt sich gleichermaßen ein Auto-Kommando, das erst den File-Typ prüft, bevor die Option gesetzt wird:

    :autocmd FileType perl :set cindent

Manchmal stellt sich allerdings erst dann heraus, dass ein Code-Stück in einen Block muss, wenn es schon getippt wurde. Dann setzt man, wie in Abbildung 7 gezeigt, einfach geschweifte Klammern darum und tippt im Kommandomodus die Tastenfolge >i{, um den 'inneren' Block eine shiftwidth-Breite nach rechts einzurücken, wie Abbildung 8 zeigt.

Abbildung 7: Ein Block mit geschweiften Klammern, Cursor auf dem Blockstart ...

Abbildung 8: und das Kommando >i{ rückt den inneren Block ein.

Die Option :set smarttab setzt bei gesetzter expandtab-Option noch eins drauf: Ein Backspace im Eingabemodus auf dem ersten Zeichen einer eingerückten Zeile rückt die Zeile wieder aus, und ein Druck auf die Tab-Taste rückt sie wieder ein, ohne dass richtige ``Tabs'' im Spiel sind.

Noch ein Tipp: Um von einer geschweiften Klammer zur korrespondierenden zu springen, genügt es, den Cursor auf eine Klammer zu positionieren, und dann die Prozenttaste (%) im Kommandomodus zu drücken. So lässt sich leicht feststellen, wo eine geschweifte Klammer fehlt, wenn perl einen Syntaxfehler anzeigt.

Umlaute im Ausland

Wer einmal an einem Computer mit amerikanischer Tastatur sitzt, und mit vi einen Umlaut wie Ä eintippen möchte, der kann dies mit der Sequenz Ctrl-K A : im Eingabemodus tun. Das scharfe ß kommt mit Ctrl-K s s zustande. Die vollständige Tabelle aller so verfügbaren Umlaute bringt das Kommando :digraphs zum Vorschein.

Guter Start

Ein neues Perl-Skript startet man recht einfach mit dem tmpl-Tool von [4]: Der Aufruf

    $ tmpl -p cooltool

legt eine neue Datei cooltool an. Wie Abbildung 9 zeigt, besteht das Skript-Skelett aus einigen Kopfzeilen, einigen typischen Modulen für Skript-Optionen und Manualseitenanzeige. tmpl bezieht einige konfigurierbare Parameter aus der Datei .tmpl im Home-Verzeichnis des Benutzers:

    # ~/.tmpl
    AUTHOR Mike Schilli
    YEAR   2005
    EMAIL  cpan@perlmeister.com

Das Skelett cooltool kann schon zwei Dinge:

    $ cooltool -v

zeigt die gegenwärtige Skriptversion an, die in der Variablen $CVSVERSION liegt und mittels CVS automatisch aufgefrischt wird. Weiter hilft Pod::Usage, dass die Option -h eine kurze Bedienungsanleitung ausgibt:

    $ cooltool -h
    Usage:
        cooltool -xyz

Den Rest muss ein zukünftiger Skriptautor dann selbst ausfüllen, aber mit dieser Grundlage ist schon viel erreicht: Ein Grundgerüst, und auch ein Rahmen für Dokumentation, ohne die jedes Skript bekanntermaßen wertlos ist. Pod::Usage sorgt dafür, dass das Skript seine eigene Dokumentation ausgibt, falls etwas schief geht.

Abbildung 9: Ein Template für eine neues Skript 'cooltool'.

Komplettierung

vim behält geschriebene Wörter automatisch im Kopf und komplettiert später angefangene Wörter mit der Tastenkombination CTRL-n im Textmodus. Wer also irgendwo Variablen wie in

    our $GLOBAL_SUPER_VARIABLE;
    our $GLOBAL_OTHER_VARIABLE;

definiert und sie später wieder verwendet, muss nicht den ellenlangen Namen wieder abtippen, sondern schreibt lediglich die ersten paar Buchstaben und drückt dann CTRL-n:

    sub foo {
        print $GL CTRL-n

und schon betätigt sich vim als Gedankenleser:

    sub foo {
        print $GLOBAL_SUPER_VARIABLE

Falls, wie oben, mehrere Möglichkeiten zur Komplettierung führen, kann man mit weiteren CTRL-n vorwärts und mit CTRL-p rückwärts durch die Vorschläge fahren. Ein beinahe triviales Feature, das aber im Lauf der Zeit enorm viel Zeit und Tipparbeit spart.

Tags

C-Programmierer kennen das Programm ctags, das eine tags-Datei für vim erzeugt. Wird diese eingelesen, muss der Entwickler eines Programmes einfach den Cursor irgendwo auf einen Funktionsaufruf positionieren, und CTRL-] im Kommandomodus drücken, schon springt vim zur zugehörigen Funktionsdefinition.

Um vim zu veranlassen, zur Source-Datei von LWP::UserAgent zu springen, falls der Cursor irgendwo auf LWP::UserAgent in

    my $ua = LWP::UserAgent->new();

steht, müssen zwei Dinge erledigt werden: vim muss verstehen, dass in Perl ein Schlüsselwort auch Doppelpunkte enthalten kann:

    :set iskeyword+=:

und die tags-Datei, die alle installierten Packages indiziert hat, muss eingelesen werden:

    :set tags=/home/mschilli/.ptags.txt

Dann springt CTRL-] in die Modul-Source, wenn der Cursor auf einem Modulnamen steht. Alternativ kann man den Modulnamen auch im Kommando :tag LWP::UserAgent angeben. Einmal in der Bibliotheksdatei angelangt, genügt ein CTRL-T, um wieder zurück zum Ausgangspunkt zu navigieren.

Wenn der Cursor auf einem Modulnamen wie LWP::UserAgent steht, springt CTRL-] zur Zeile package LWP::UserAgent; in /usr/lib/perl5/site_perl/5.8.5/LWP/UserAgent.pm. Falls LWP::Debug::trace unter dem Cursor liegt, springt vim statt dessen die Zeile sub trace { ... in /usr/lib/perl5/site_perl/5.8.5/LWP.pm an. Die ganze Magie steckt in der Tags-Datei .ptags.txt, die das weiter unten vorgestellte Listing ppitags erzeugen wird.

Trotz Window-Manager ist es manchmal sinnvoll, zwei Dateien gleichzeitig in einem Fenster zu sehen. Welcher vim-User greift schon freiwillig auf die Maus zurück, wenn die Hände auf der Tastatur bleiben können?

Wer statt CTRL-] die Kombination CTRL-W-] tippt, während der Cursor auf einem Schlüsselwort steht, dessen Fenster teilt sich in zwei Hälften: In der unteren bleibt der Code der editierten Datei sichtbar, in der oberen erscheint der Code des referenzierten Moduls. Zwischen den Fensterhälften springt man mit CTRL-WW hin und her. Das Kommando :quit im oberen Fenster schließt letzteres und das Hauptfenster gehört wieder ganz der ursprünglich editierten Datei. Alternativ schließt der Befehl :only im unteren Fenster das Obere.

Die vim-Session in Abbildung 10 zeigt unten ein editiertes Testskript, dass das Modul LWP::UserAgent verwendet, und oben die new()-Methode im Sourcecode des Moduls.

Abbildung 10: vim mit gespaltenem Fenster: Unten ein Testskript, oben der Sourcecode des verwendeten Moduls LWP::UserAgent.

Weiss man den Namen eines gesuchten Moduls nicht genau, reicht es, einen regulären Ausdruck anzugeben. Das Kommando tselect sucht nach allen passenden Tags und bietet eine Liste zur Auswahl an:

    :tselect /^LWP

Abbildung 11: Tagsuche per regulärem Ausdruck nach C bringt ein Menü mit numerierten Treffern.

In Abbildung 11 muss der Benutzer dann nur den passenden Treffer per Nummernmenü auswählen.

Mach mir Tags

Wie kommt ~/.ptags.txt zustande? Hierzu müssen regelmäßig sämtliche Module der lokalen Perl-Installation durchforstet werden. Das Skript in Listing ppitags klappert alle @INC-Pfade ab, merkt sich in @dirs, wo es schon mal war und wird auch bei überlappenden Pfaden nicht zweimal denselben durchforsten.

Um Perl-Source zu analysieren, braucht man eigentlich perl, denn Perl ist extrem schwierig zu parsen. Allerdings hat Adam Kennedy vor kurzem das Unmögliche gewagt und einen ``Good-enough''-Parser für Perl geschrieben, der wirklich erstaunlich gut ist. Das PPI-Modul vom CPAN enthält PPI::Document, dessen Methode load() ein Perl-Modul einliest, in Tokens zerlegt und in einer Baumstruktur ablegt.

ppitags nutzt File::Find, um alle Verzeichnisse in Perls globalem Array @INC zu durchlaufen. Nur der ebenfalls in @INC enthaltene Pfad "." wird ausgelassen. Für jeden gefundenen Eintrag springt File::Find die Funktion file_wanted an. Falls der gefundene Eintrag ein Verzeichnis und keine Datei ist, frischt Zeile 28 den Hash %dirs auf, um festzustellen, ob der Pfad schon durchlaufen wurde. Falls ja, setzt Zeile 27 die Variable $File::Find::prune auf 1, um File::Find mitzuteilen, dass es sich den Rest des Verzeichnisses und alle Unterverzeichnisse sparen kann. Zeile 31 weist alles außer Perl-Modulen mit der Endung .pm zurück.

Zeile 33 parst das aktuell gefundene Perl-Modul. Falls irgendwelche Fehler auftreten, fängt Zeile 35 diese ab (PPI ist noch nicht perfekt), spuckt eine Warnung aus und lässt das problemhafte Modul sausen.

Konnte das Modul erfolgreich eingelesen werden, ruft Zeile 41 die find()-Methode des PPI::Document-Objekts auf, das die Tokens der Perl-Source durchschreitet und für jeden gefundenen die ab Zeile 45 definierte Funktion document_wanted aufruft.

Diese prüft, ob es sich beim gefundenen Token um ein Objekt vom Typ PPI::Statement::Package oder PPI::Statement::Sub handelt, also eine package oder sub Definition im Perl-Code.

Eine package-Definition besteht aus einer Zeile wie

    package LWP::UserAgent;

und das sind in der PPI-Welt vier Tokens: package, Leerzeichen, der Modulname und der abschließende Strichpunkt. Für die Belange von ppitags interessiert nur der Modulname, also das dritte Kind des Knotens, der in $_[1] übergeben wurde. Die Methode child() mit dem ab 0 gezählten Kinderindex fördert den String "LWP::UserAgent" zutage: $_[1]->child(2).

Wird eine package-Definition durchlaufen, speichert ppitags dessen Package-Namen als aktuelles Package, was zwar durch package-Definitionen in Blocks leicht ausgehebelt würde, aber das spielt in 99.9% aller Fälle keine Rolle. ``Good enough'' auch hier.

Zeile 55 stöbert Funktionsdefinitionen der Form sub func { auf und extrahiert den Funktions- oder Methodennamen, damit der Tag-Mechanismus Konstrukte wie LWP::Debug::trace erkennt und später zu der Stelle springt, an dem die Funktion trace im Modul LWP::Debug definiert ist.

Der push-Befehl in Zeile 64 schiebt einen neuen String ans Ende des Arrays @found, der aus dem gesuchten Tag (Package- oder voll qualifizierter Funktionsname), dem absoluten Source-Dateinamen und einem regulären Ausdruck besteht, der die package- oder Funktionsdefinition in der Source-Datei findet. Hierzu bildet die ab Zeile 72 definierte Funktion regex_from_node einen regulären Ausdruck, der aus allen Zeichen der Trefferzeile, vom Zeilenanfang bis zum gesuchten Token, besteht. Bei einer Subroutine liefert $node->content() sowohl Funktionskopf als auch den Rumpf. Deswegen schneidet Zeile 78 alle Zeilen außer der ersten ab und die Zeilen 80/81 gehen solange im Token-Baum zurück, bis der Zeilenanfang erreicht ist. Nach dem Ende der while-Schleife steht in $regex der Inhalt der Source-Zeile, vom Zeilenanfang bis zum Token. Daraus baut Zeile 88 einen regulären Ausdruck der Form /^.../ mit einem Anker für den Zeilenanfang. Die Ersetzung eine Zeile vorher stellt sicher, dass enthaltene Sonderzeichen nicht mit vim-spezifischen Regex-Metazeichen kollidieren, indem sie sie allesamt mit einem Backslash maskiert.

ppitags erzeugt eine Liste von dreispaltigen Einträgen in ~/.ptags.txt im Format

    Package/Subroutine [tab] Dateiname [tab] Regex

die vim mit :set tags= wie oben beschrieben einliest und damit elegant von Schlüsselworten zum entsprechenden Sourcecode springt.

ppitags sollte einmal pro Tag per Cronjob aufgerufen werden, damit ~/.ptags.txt immer auf dem neuesten Stand ist. Wer möchte, kann das Skript noch dahingehend erweitern, dass vim voll qualifizierte our-Variablen (wie zum Beispiel $Text::Wrap::columns) erkennt und zu deren Definition in der Modul-Source springt.

Listing 1: ppitags

    01 #!/usr/bin/perl -w
    02 use strict;
    03 
    04 use PPI::Document;
    05 use File::Find;
    06 use Sysadm::Install qw(:all);
    07 use Log::Log4perl qw(:easy);
    08 
    09 my $outfile = "$ENV{HOME}/.ptags.txt";
    10 my %dirs  = ();
    11 my @found = ();
    12 
    13 find \&file_wanted, grep {$_ ne "."} @INC;
    14 
    15 blurt join("\n", sort @found), $outfile;
    16 
    17 ###########################################
    18 sub file_wanted {
    19 ###########################################
    20     my $abs = $File::Find::name;
    21 
    22         # Avoid dupe dirs
    23     $File::Find::prune = 1 if -d and 
    24         $dirs{$abs}++;
    25 
    26         # Only Perl modules
    27     return unless /\.pm$/;
    28 
    29     my $d = PPI::Document->load($abs);
    30 
    31     unless($d) {
    32         WARN "Cannot load $abs ($! $@)";
    33         return;
    34     }
    35         # Find packages and 
    36         # all named subroutines
    37     $d->find(\&document_wanted);
    38 }
    39 
    40 ###########################################
    41 sub document_wanted {
    42 ###########################################
    43     our $package;
    44     my  $tag;
    45 
    46     if(ref($_[1]) eq 
    47            'PPI::Statement::Package') {
    48         $tag = $_[1]->child(2)->content();
    49         $package = $tag;
    50 
    51     } elsif(ref($_[1]) eq 
    52             'PPI::Statement::Sub' and 
    53             $_[1]->name()) {
    54         $tag = "$package\::" . 
    55                $_[1]->name();
    56     }
    57 
    58     return 1 unless defined $tag;
    59 
    60     push @found, $tag . "\t" . 
    61                  $File::Find::name . "\t" .
    62                  regex_from_node($_[1]);
    63 
    64     return 1;
    65 }
    66 
    67 ###########################################
    68 sub regex_from_node {
    69 ###########################################
    70     my($node) = @_;
    71 
    72     my $regex = $node->content();
    73    
    74     $regex =~ s/\n.*//gs;
    75 
    76     while(my $prev = 
    77              $node->previous_sibling()) {
    78         last if $prev =~ /\n/;
    79         $regex = $prev->content() . 
    80                  $regex;
    81         $node  = $prev;
    82     }
    83 
    84     $regex =~ s#[/.*[\]^\$]#\\$&#g;
    85     return "/^$regex/";
    86 }

Persistent Speichern

Die Datei .vimrc im Home-Verzeichnis wird von vim beim Programmstart eingelesen und erlaubt es, eine ganze Litanei von Kommandos auszuführen, bevor vim die eigentliche Arbeit beginnt. Wer die Standardeinstellungen interaktiv verschraubt hat, möchte diese wahrscheinlich permanent festhalten. Statt die eingegebenen Kommandos nochmals manuell in .vimrc einzutragen, genügt ein einfaches :mkvimrc, mit dem vim alle zur Zeit gültigen Einstellungen in ~/.vimrc ablegt.

Unter [1] liegt eine Beispielkonfiguration mit allen heute vorgestellten Einstellungen. Wer Tipparbeit spart, hat mehr Zeit zum Denken!

Listing 2: .vimrc

    01 version 6.0
    02 :map !L iuse Log::Log4perl qw(:easy);<RETURN>Log::Log4perl->easy_init($DEBUG);<RETURN><ESC>
    03 :map F o<ESC>43i#<ESC>yyosub {<ENTER><ESC>Pk$i
    04 map f !Gperl -MText::Autoformat -e'autoformat{right=>70}'
    05 set backspace=2
    06 set fileencodings=utf-8,latin1
    07 set formatoptions=tcql
    08 set helplang=en
    09 set history=50
    10 set hlsearch
    11 set ruler
    12 set shiftwidth=4
    13 :autocmd FileType perl :set cindent
    14 :autocmd FileType perl :set expandtab
    15 :autocmd Filetype perl :set smarttab
    16 :nnoremap X :w<Enter>:!perl -c %<Enter>
    17 :set tags=/home/mschilli/.ptags.txt
    18 :set iskeyword+=:

Infos

[1]
Listings zu diesem Artikel: ftp://www.linux-magazin.de/pub/listings/magazin/2005/07/Perl

[2]
Die Homepage des vim-Projekts: http://www.vim.org

[3]
Steve Oualline, ``vi IMproved - Vim'', New Riders, 2001

[4]
``Mike's Script Archive'', http://perlmeister.com/scripts, tmpl Skript: http://perlmeister.com/scripts/tmpl

Michael Schilli

arbeitet als Software-Engineer bei Yahoo! in Sunnyvale, Kalifornien. Er hat "Goto Perl 5" (deutsch) und "Perl Power" (englisch) für Addison-Wesley geschrieben und ist unter mschilli@perlmeister.com zu erreichen. Seine Homepage: http://perlmeister.com.