*usr_41.txt* Für Vim version 7.0. Letzte Änderung: 2007-Apr-26 VIM BENUTZERHANDBUCH - von Bram Moolenaar Ein Vim-Skript schreiben Vims Skriptsprache wird für die Startup-Datei vimrc, Syntax-Dateien und viele andere Dinge verwendet. Dieses Kapitel erklärt die Elemente, die in einem Vim-Skript benutzt werden können. Davon gibt es eine Menge, daher ist dies ein langes Kapitel. |41.1| Einführung |41.2| Variablen |41.3| Ausdrücke |41.4| Bedingungen |41.5| Einen Ausdruck ausführen |41.6| Funktionen benutzen |41.7| Eine Funktion definieren |41.8| Listen und Wörterbücher |41.9| Ausnahmen |41.10| Verschiedene Anmerkungen |41.11| Ein Plugin schreiben |41.12| Ein Dateityp-Plugin schreiben |41.13| Ein Kompiler-Plugin schreiben |41.14| Ein Plugin so schreiben, dass es schnell lädt |41.15| Bibliotheksskripte schreiben |41.16| Vim-Skripte verteilen Nächstes Kapitel: |usr_42.txt| Neue Menüs hinzufügen Voriges Kapitel: |usr_40.txt| Neue Befehle machen Inhaltsübersicht: |usr_toc.txt| ============================================================================== *41.1* Einführung *vim-script-intro* *script* Ihre erste Erfahrung mit Vim-Skripten ist die Datei vimrc. Vim liest sie beim Start und führt die Befehle aus. Sie können Optionen auf Werte setzen, die Sie bevorzugen. Und Sie können jeden Doppelpunkt-Befehl in ihr benutzen (Befehle, die mit »:« starten, diese werden manchmal Ex-Befehle oder Befehlszeilen-Befehle genannt. Syntax-Dateien sind ebenfalls Vim-Skripte. So wie Dateien, die für einen bestimmten Dateityp Optionen setzen. Ein kompliziertes Makro kann in einer eigenen Vim-Skript-Datei definiert werden. Sie können sich nun weitere Möglichkeiten ausmalen. Lassen Sie uns mit einem einfachen Beispiel beginnen: > :let i = 1 :while i < 5 : echo "Zähler ist" i : let i += 1 :endwhile < Anmerkung: Die »:« werden hier nicht wirklich gebraucht. Sie brauchen sie nur, wenn Sie einen Befehl in Vim tippen. In einer Vim-Skript-Datei können sie ausgelassen werden. Wir benutzen Sie hier dennoch, um klar zu machen, dass dies Doppelpunkt-Befehle sind, und um sie sich von den Befehlen des Normalmodus abheben zu lassen. Anmerkung: Sie können die Beispiele ausprobieren, indem Sie die Zeilen von hier in ein Register kopieren und sie mit :@" ausführen. Die Ausgabe des Beispielcodes ist: Zähler ist 1 ~ Zähler ist 2 ~ Zähler ist 3 ~ Zähler ist 4 ~ In der ersten Zeile verknüpft der Befehl »:let« einen Wert mit einer Variablen. Die generische Form ist: > :let {variable} = {ausdruck} In diesem Falle ist der Variablenname »i« und der Ausdruck ist ein einfacher Wert, die Zahl eins. Der Befehl »:while« beginnt eine Schleife. Die generische Form ist: > :while {bedingung} : {anweisungen} :endwhile Die Anweisungen bis zum entsprechenden »:endwhile« werden so lange ausgeführt, wie die Bedingung wahr ist. Die hier benutzte Bedingung ist der Ausdruck »i < 5«. Dies ist wahr, wenn die Variable i kleiner als fünf ist. Anmerkung: Falls Sie versehentlich eine While-Schleife schreiben, die nicht abbricht, können Sie sie durch Drücken von CTRL-C (CTRL-Pause unter MS-Windows) unterbrechen. Der Befehl »:echo« gibt seine Argumente aus. In diesem Falle die Zeichenkette »Zähler ist« und den Wert der Variablen i. Weil i eins ist, wird folgendes ausgegeben: Zähler ist 1 ~ Dann kommt der Befehl »:let i += 1«. Dies macht dasselbe wie »:let i = i +1«. Dies addiert eins zu der Variablen i und verknüpft den neuen Wert mit derselben Variablen. Das Beispiel wurde aufgeführt, um die Befehle zu erläutern, aber wenn Sie wirklich solch eine Schleife bauen wollen, kann dies viel kompakter geschrieben werden: > :for i in range(1, 4) : echo "Zähler ist" i :endfor Wir erklären |:for| und |range()| zunächst nicht. Folgen Sie den Verweisen, wenn Sie ungeduldig sind. DREI ARTEN VON ZAHLEN Zahlen können dezimal, hexadezimal oder oktal sein. Eine hexadezimale Zahl beginnt mir »0x« oder »0X«. »0x1f« ist zum Beispiel dezimal 31. Eine oktale Zahl beginnt mit einer Null. »017« ist dezimal 15. Achtung: setzen Sie keine Null vor eine dezimale Zahl, sie wird dann als oktale Zahl interprätiert! Der Befehl »:echo« gibt immer dezimale Zahlen aus. Beispiel: > :echo 0x7f 036 < 127 30 ~ Eine Zahl wird mit einem Minus-Zeichen negativ gemacht. Dies funktioniert auch für hexadezimale und oktale Zahlen. Das Minus-Zeichen wird auch für die Substraktion verwandt. Vergleichen Sie dies mit dem vorigen Beispiel: > :echo 0x7f -036 < 97 ~ Whitespace in einem Ausdruck wird ignoriert. Dennoch empfiehlt es sich, ihn zu benutzen, um Elemente zu trennen, um den Ausdruck leichter lesbar zu machen. Um zum Beispiel oben die Verwechslung mit einer negativen Zahl zu vermeiden, setzen Sie ein Leerzeichen zwischen das Minus-Zeichen und die darauf folgende Zahl: > :echo 0x7f - 036 ============================================================================== *41.2* Variablen Ein Variablenname besteht aus ASCII-Buchstaben, Ziffern und dem Unterstrich. Er kann nicht mit einer Ziffer beginnen. Gültige Variablennamen sind: counter _aap3 very_long_variable_name_with_underscores FuncLength LENGTH Ungültige Namen sind »foo+bar« und »6var«. Diese Variablen sind global. Um eine Liste aktuell definierter Variablen zu sehen, benutzen Sie diesen Befehl: > :let Sie können überall globale Variablen verwenden. Dies bedeutet auch, dass, wenn eine Variable »zaehler« in einer Skript-Datei benutzt wird, sie auch in einer andereren Datei benutzt werden könnte. Dies führt bestenfalls zu Verwirrung, schlimmstenfalls zu wirklichen Problemen. Um dies zu vermeiden, können Sie eine Variable lokal zu einem Skript verwenden, indem Sie »s:« voranstellen. Ein Skript enthält zum Beispiel diesen Code: > :let s:count = 1 :while s:count < 5 : source other.vim : let s:count += 1 :endwhile Weil »s:count« lokal zu diesem Skript ist, können Sie sicher sein, dass das Einlesen von »other.vim« diese Variable nicht verändert. Falls »other.vim« ebenfalls eine Variable »s:count« enthält, ist dies eine andere Einheit, lokal zu diesem Skript. Mehr über Variablen, die lokal zu einem Skript sind, unter: |script-variable| Es gibt noch mehr Arten von Variablen, siehe |internal-variables|. Die am häufigsten gebrauchten sind: b:name Variable lokal zu einem Puffer w:name Variable lokal zu einem Fenster g:name globale Variable (auch in einer Funktion) v:name von Vim vordefinierte Variable VARIABLEN LöSCHEN Variablen belegen Speicher und erscheinen in der Ausgabe des Befehls »:let«. Um eine Variable zu löschen, benutzen Sie den Befehl »:unlet«. Beispiel: > :unlet s:count Dies löscht die skript-lokale Variable »s:count« um den Speicher freizugeben, den sie belegt. Wenn Sie nicht sicher sind, ob die Variable existiert und Sie keine Fehlermeldung wollen, wenn sie es nicht tut, hängen Sie »!« an: > :unlet! s:count Wenn ein Skript terminiert, werden die in ihm benutzten lokalen Variablen nicht automatisch freigegeben. Das nächste Mal, wenn das Skript ausgeführt wird, kann es immernoch den alten Wert benutzen. Beispiel: > :if !exists("s:aufruf_zaehler") : let s:aufruf_zaehler = 0 :endif :let s:aufruf_zaehler = s:aufruf_zaehler + 1 :echo s:aufruf_zaehler "Male aufgerufen" Die Funktion »exists()« prüft, ob eine Variable bereits definiert wurde. Ihr Argument ist der Name der Variablen, die man überprüfen will. Nicht die Variable selbst! Falls Sie dies tun würden: > :if !exists(s:aufruf_zaehler) Dann wird der Wert von s:aufruf_zaehler als der Name der Variablen, die exists() überprüft, genommen. Das ist nicht, was Sie wollen. Das Ausrufezeichen ! negiert einen Wert. Wenn der Wert wahr war, wird er falsch. Wenn er falsch war, wird er wahr. Sie können es als »nicht« lesen. Also kann »if !exists()« als »if not exists()« gelesen werden. Was Vim als wahr betrachtet, ist alles, was nicht Null ist. Null ist falsch. Anmerkung: Vim konvertiert eine Zeichenkette automatisch zu einer Zahl, wenn er nach einer Zahl sucht. Bei einer Zeichenkette, die nicht mit einer Ziffer beginnt, ist die resultierende Zahl Null. Also geben Sie hierauf acht: > :if "true" > Das »true« wird als Null interprätiert, also als falsch! ZEICHENKETTENVARIABLEN UND -KONSTANTEN So weit wurden nur Zahlen für den Variablenwert benutzt. Zeichenketten können auch benutzt werden. Zahlen und Zeichenketten sind die Grundtypen von Variablen, die Vim unterstützt. Der Typ ist dynamisch, er wird jedes Mal gesetzt, wenn der Variablen mit »:let« ein Wert zugewiesen wird. Mehr zu Typen in |41.8|. Um einer Variablen einen Zeichenkettenwert zuzuweisen, müssen Sie eine Zeichenkettenkonstante benutzen. Davon gibt es zwei Typen. Zuerst die Zeichenkette in doppelten Zitatzeichen: > :let name = "peter" :echo name < peter ~ Falls Sie ein doppeltes Zitatzeichen in die Zeichenkette einbeziehen wollen, stellen Sie ihm einen Rückwärtsschrägstrich voran: > :let name = "\"peter\"" :echo name < "peter" ~ Um den Rückwärtsschrägstrich zu vermeiden, können Sie eine Zeichenkette in einfachen Zitatzeichen verwenden: > :let name = '"peter"' :echo name < "peter" ~ In einer Zeichenkette mit einfachen Zitatzeichen sind alle Zeichen wie sie sind. Nur das einzelne Zitatzeichen selbst ist besonders: Sie müssen zwei verwenden, um eins zu bekommen. Ein Rückwärtsschrägstrich wird wörtlich genommen, also können Sie ihn nicht benutzen, um die Bedeutung des Zeichens danach zu verändern. In Zeichenketten mit doppelten Zitatzeichen ist es möglich, besondere Zeichen zu verwenden. Hier sind ein Paar nützliche: \t \n , Zeilenumbruch \r , \e \b , Backspace \" " \\ \. Rückwärtsschrägstrich \ \ CTRL-W Die letzten zwei sind nur Beispiele. Die Form »\« kann benutzt werden, um die Sondertaste »name« einzubeziehen. Siehe |expr-quote| für die volle Liste besonderer Elemente in einer Zeichenkette. ============================================================================== *41.3* Ausdrücke Vim hat eine reichhaltige, aber dennoch einfache Weise, um Ausdrücke zu behandeln. Sie können die Definition hier lesen: |expression-syntax|. Hier werden wir die gebräuchlichsten zeigen. Die oben erwähnten Zahlen, Zeichenketten und Variablen sind selbst Ausdrücke. Also können Sie überall, wo ein Ausdruck erwartet wird, eine Zahl, Zeichenkette oder Variable verwenden. Andere Grundelemente in einem Ausdruck sind: $NAME Umgebungsvariable &name Option @r Register Beispiele: > :echo "Der Wert von 'tabstop' ist" &ts :echo "Ihr Heimverzeichnis ist" $HOME :if @a > 5 Die Form &name kann benutzt werden, um den Wert einer Option zwischen zu speichern, sie auf einen neuen Wert zu setzen, etwas zu tun und den alten Wert zu restaurieren. Beispiel: > :let save_ic = &ic :set noic :/Der Beginn/,$delete :let &ic = save_ic Dies stellt sicher, dass das Muster »Der Beginn« mit deaktivierter Option 'ignorecase' benutzt wird. Jedoch behält es den Wert, den der Benutzer gesetzt hat. (Eine andere Möglichkeit, dies zu tun, wäre dem Suchmuster »\C« hinzuzufügen, siehe |/\C|.) MATHEMATIK Es wird interessanter, wenn wir diese Grundelemente kombinieren. Lassen Sie uns mit Mathematik auf Zahlen beginnen a + b Addition a - b Subtraktion a * b Multiplikation a / b Division a % b Modulo Die gewöhnliche Präzedenz wird benutzt. Beispiel: > :echo 10 + 5 * 2 < 20 ~ Gruppierung mit Klammern. Keine Überraschungen hier. Beispiel: > :echo (10 + 5) * 2 < 30 ~ Zeichenketten können mit ».« verkettet werden. Beispiel: > :echo "foo" . "bar" < foobar ~ Wenn der Befehl »:echo« mehrere Argumente erhält, trennt er sie mit einem Leerzeichen. In diesem Beispiel ist das Argument ein einzelner Ausdruck, also wird kein Leerzeichen eingefügt. Aus der Sprache C entliehen ist der konditionale Ausdruck: a ? b : c Falls »a« wahr ergibt, wird »b« verwandt, ansonsten wird »c« benutzt. Beispiel: > :let i = 4 :echo i > 5 ? "i is big" : "i is small" < i is small ~ Die drei Teile des Konstrukts werden immer zuerst berechnet, also könnten Sie als als das Folgende funktionieren sehen: (a) ? (b) : (c) ============================================================================== *41.4* Bedingungen Der Befehl »:if« führt die folgenden Anweisungen aus, bis zum übereinstimmenden »:endif«, nur wenn eine Bedingung erfüllt ist. Die generische Form ist: :if {bedingung} {anweisungen} :endif Nur wenn der Ausdruck {bedingung} wahr (nicht-null) ergibt, werden die {anweisungen} ausgeführt. Dies müssen immer noch gültige Befehle sein. Falls sie Müll enthalten, ist es Vim nicht möglich, das »:endif« zu finden. Sie können auch »:else« verwenden. Die generische Form hierfür ist: :if {bedingung} {anweisungen} :else {anweisungen} :endif Der zweite Block {anweisungen} wird nur ausgeführt, wenn der erste nicht wird. Schließlich gibt es »:elseif«: :if {bedingung} {anweisungen} :elseif {bedingung} {anweisungen} :endif Dies funktioniert genauso, wie »:else« und dann »if« zu verwenden, aber ohne ein extra »:endif« zu benutzen. Ein nützliches Beispiel für Ihre vimrc ist, die Option 'term' zu prüfen, und abhängig von ihrem Wert etwas zu tun: > :if &term == "xterm" : " Mache etwas für xterm :elseif &term == "vt100" : " Mache etwas für ein VT100-Terminal :else : " Mache etwas für andere Terminals :endif LOGISCHE OPERATIONEN Wir haben bereits einige in den Beispielen benutzt. Dies sind die am häufigsten verwandten: a == b gleich a != b ungleich a > b größer als a >= b größer als oder gleich a < b kleiner als a <= b kleiner als oder gleich Das Ergebnis ist eins, falls die Bedingung erfüllt ist, sonst null. Ein Beispiel: > :if v:version >= 700 : echo "Gratulation" :else : echo "Sie benutzen eine alte Version. Aktualisieren Sie!" :endif Hier ist »v:version« eine von Vim definierte Variable, die den Wert der Vim-Version hat. 600 ist für Version 6.0. Version 6.1 hat den Wert 601. Dies ist sehr nützlich beim Schreiben eines Skript, das mit verschiedenen Versionen von Vim funktionieren soll. |v:version| Die logischen Operatoren funktionieren sowohl für Zahlen wie auch für Zeichenketten. Beim Vergleich zweier Zeichenketten wird der mathematische Unterschied benutzt. Dies vergleicht Byte-Werte, was für manche Sprachen eventuell nicht richtig ist. Beim Vergleich einer Zeichenkette mit einer Zahl wird die Zeichenkette zuerst in eine Zahl verwandelt. Dies ist ein wenig verzwickt, wenn eine Zeichenkette nicht wie eine Zahl aussieht, wird die Zahl Null verwandt. Beispiel: > ;if 0 == "eins" : echo "ja" :endif Dies gibt »ja« aus, weil »eins« nicht wie eine Zahl aussieht, es also zur Zahl Null konvertiert wird. Für Zeichenketten gibt es zwei weitere Elemente: a =~ b stimmt mit überein a !~ b stimmt nicht mit überein Das linke Element »a« wird als Zeichenkette verwandt. Das rechte Element »b« wird als Muster benutzt, wie beim Suchen. Beispiel: > :if str =~ " " : echo "str enthält ein Leerzeichen" :endif :if str !~ '\.$' : echo "str endet nicht mit einem Punkt" :endif Beachten Sie den Gebrauch der einfach zitierten Zeichenkette. Dies ist nützlich, weil Rückwärtsschrägstriche in doppelt zitierten Zeichenketten verdoppelt werden müssten, und Muster dazu neigen, viele Rückwärtsschrägstriche zu enthalten. Die Option 'ignorecase' wird beim Vergleich von Zeichenketten benutzt. Wenn Sie dies nicht wollen, hängen Sie »#« an, für Übereinstimmung von Groß-/Kleinschreibung, und »?«, um dies zu ignorieren. Also vergleicht »==?« zwei Zeichenketten auf Gleichheit, während Groß-/Kleinschreibung ignoriert wird. Und »!~#« prüft, ob ein Muster nicht passt, auch Groß-/Kleinschreibung überprüfend. Für die volle Tabelle siehe |expr-==|. MEHR ÜBER SCHLEIFEN Der Befehl »:while« wurde bereits erwähnt. Zwei weitere Anweisungen können zwischen dem »:while« und dem »:endwhile« benutzt werden: :continue Springe zurück zum Beginn der While-Schleife; die Schleife wird fortgesetzt. :break Springe vorwärts zum »:endwhile«; die Schleife wird abgebrochen. Beispiel: > :while zaehler < 40 : call tue_etwas() : if flag_ueberspringen : continue : endif : if flag_beendet : break : endif : sleep 50m :endwhile Der Befehl »:sleep« lässt Vim ein Nickerchen machen. »50m« gibt fünzig Millisekunden an. Ein weiteres Beispiel ist »:sleep 4«, was für vier Sekunden schläft. Sogar noch mehr Schleifen können mit dem Befehl »:for« gemacht werden, siehe unten in |41.8|. ============================================================================== *41.5* Einen Ausdruck ausführen So weit wurden die Befehle in dem Skript von Vim direkt ausgeführt. Der Befehl »:execute« erlaubt das Ausführen des Ergebnisses eines Ausdrucks. Dies ist eine sehr mächtige Möglichkeit, Befehle aufzubauen und sie ausführen zu lassen. Ein Beispiel ist ein Tag anzuspringen, das in einer Variablen enthalten ist: > :execute "tag " . tag_name Der ».« wird verwandt, um die Zeichenkette »tag « mit dem Wert der Variablen »tag_name« zu verketten. Angenommen, »tag_name« hat den Wert »get_cmd«, dann ist der ausgeführte Befehl: > :tag get_cmd Der Befehl »:execute« kann nur Doppelpunkt-Befehle ausführen. Der Befehl »:normal« führt Befehl des Normalmodus aus. Jedoch ist sein Argument kein Ausdruck, sondern die wörtlichen Befehlszeichen. Beispiel: > :normal gg=G Dies springt in die erste Zeile und formatiert alle Zeilen mit dem Operator »=«. Damit »:normal« mit einem Ausdruck funktioniert, kombinieren Sie »:execute« mit ihm. Beispiel: > :execute "normal " . normal_befehle Die Variable »normal_befehle« muss die Normal-Modus-Befehle enthalten. Stellen Sie sicher, dass das Argument für »:normal« ein vollständiger Befehl ist. Ansonsten läuft Vim in das Ende des Arguments und führt den Befehl nicht aus. Falls Sie zum Beispiel den Einfüge-Modus starten, müssen Sie auch den Einfüge-Modus verlassen. Dies funktioniert: > :execute "normal INeuer Text \" Dies fügt »Neuer Text « in der aktuellen Zeile ein. Beachten Sie den Gebrauch der Sondersequenz »\«. Dies vermeidet, dass sie ein wirkliches -Zeichen in Ihr Skript einfügen müssen. Falls Sie eine Zeichenkette nicht ausführen, sondern auswerten möchten, um ihren Ausdruckswert zu bekommen, können Sie die Funktion eval() benutzen: > :let optname = "path" :let optval = eval('&' . optname) Ein »&«-Zeichen wird »path« vorangestellt, also ist das Argument für eval() »&path«. Das Ergebnis ist dann der Wert der Option 'path'. Dasselbe kann mit dem folgenden gemacht werden: > :exe 'let optval = &' . optname ============================================================================== *41.6* Funktionen benutzen Vim definiert viele Funktionen und bietet so einen großen Betrag an Funktionaltität. Einige wenige Beispiele werden in diesem Abschnitt gegeben. Sie können die ganze Liste hier finden: |functions|. Eine Funktion wird mit dem Befehl »:call« aufgerufen. Die Parameter werden in Klammern übergeben, getrennt durch Kommata. Beispiel: > :call search("Date: ", "W") Dies ruft die Funktion search() auf, mit den Argumenten »Date: « und »W«. Die Funktion search() benutzt ihr erstes Argument als Suchmuster und das zweite als Flags. Das Kennzeichen »W« bedeutet, dass die Suche am Ende der Datei nicht an ihrem Beginn fortgesetzt werden soll (wrap around). Eine Funktion kann in einem Ausdruck aufgerufen werden. Beispiel: > :let zeile = getline(".") :let ersetzt = substitute(zeile, '\a', "*", "g") :call setline(".", ersetzt) Die Funktion getline() holt eine Zeile aus dem aktuellen Puffer. Ihr Argument ist eine Spezifikation der Zeilennummer. In diesem Falle wird ».« benutzt, was die Zeile meint, wo sich der Cursor befindet. Die Funktion substitute() tut etwas ähnliches wie der Befehl »:substitute«. Das erste Argument ist die Zeichenkette, auf der die Ersetzung stattfinden soll. Das zweite Argument ist das Muster, das dritte die Ersetzungszeichenkette. Schließlich sind die letzten Argumente die Flags. Die Funktion setline() setzt die Zeile, die durch das erste Argument spezifiziert wird, auf eine neue Zeichenkette, das zweite Argument. In diesem Beispiel wird die Zeile unter dem Cursor ersetzt durch das Ergebnis von substitute(). Also ist der Effekt der drei Anweisungen gleich: > :substitute/\a/*/g Die Funktionen benutzen wird interessanter, wenn Sie mehr Arbeit vor und nach dem Aufruf von substitute() erledigen. FUNKTIONEN *function-list* Es gibt viele Funktionen. Wir werden Sie hier erwähnen, gruppiert nach ihrem Verwendungszweck. Sie können hier eine alphabetische Liste finden: |functions|. Benutzen Sie CTRL-] auf dem Funktionsnamen, um zu einer detailierten Hilfe zu ihr zu springen. nr2char() gibt ein Zeichen nach seinem ASCII-Wert char2nr() gibt den ASCII-Wert eines Zeichens str2nr() konvertiert eine Zeichenkette in eine Zahl printf() formatiert eine Zeichenkette nach %-Elementen escape() schützt Zeichen in einer Zeichenkette mit einem »\« tr() übersetzt Zeichen von einer Menge in eine andere strtrans() übersetzt eine Zeichenkette, um sie druckbar zu machen tolower() konvertiert eine Zeichenkette in Kleinbuchstaben toupper() konvertiert eine Zeichenkette in Großbuchstaben match() Position, wo ein Muster in einer Zeichenkette übereinstimmt matchend() Postion, wo eine Musterübereinstimmung in einer Zeichenkette endet matchstr() Übereinstimmung eines Musters in einer Zeichenkette matchlist() wie matchstr(), gibt auch Unterübereinstimmungen zurück stridx() erster Index einer kurzen in einer langen Zeichenkette strridx() letzter Index einer kurzen in einer langen Zeichenkette strlen() Länge einer Zeichenkette substitute() ersetze eine Musterübereinstimmung durch eine Zeichenkette submatch liefert eine bestimmte Übereinstimmung in einem »:substitute« strpart() liefert Teil einer Zeichenkette expand() expandiert besondere Schlüsselwörter iconv() konvertiert Text von einer Kodierung in eine andere byteidx() Byte-Index eines Zeichens in einer Zeichenkette repeat() wiederholt eine Zeichenkette mehrere Male eval() wertet einen Zeichenkettenausdruck aus Listen-Manipulation: get() liefert ein Element ohne Fehler für falschen Index len() Anzahl Elemente in einer Liste empty() prüft, ob eine Liste leer ist insert() fügt ein Element irgendwo in einer Liste ein add() hängt ein Element an eine Liste an extend() hängt eine Liste an eine Liste an remove() entfernt ein oder mehrere Elemente aus einer Liste copy() macht eine oberflächliche Kopie einer Liste deepcopy() macht eine volle Kopie einer Liste filter() entfernt ausgewählte Elemente einer Liste map() ändert jedes Listenelement sort() sortiert eine Liste reverse() kehrt die Reihenfolge einer Liste um split() spaltet eine Zeichenkette in eine Liste join() fügt Listenelemente zu einer Zeichenkette zusammen range() gibt eine Liste mit einer Folge von Zahlen zurück string() Zeichenkettenrepräsentation einer Liste call() ruft eine Funktion auf mit Liste als Argumente index() Index eines Werts in einer Liste max() maximaler Wert in einer Liste min() minimaler Wert in einer Liste count() zählt Anzahl, wie oft ein Wert in einer Liste erscheint repeat() wiederholt eine Liste mehrere Male Wörterbuch-Manipulation: get() liefert einen Eintrag ohne einen Fehler für einen falschen Schlüssel len() Anzahl von Einträgen in einem Wörterbuch has_key() prüft, ob ein Schlüssel in einem Wörterbuch erscheint empty() prüft, ob ein Wörterbuch leer ist remove() entfernt einen Eintrag aus einem Wörterbuch extend() kopiert Einträge von einem Wörterbuch in ein anderes filter() entfernt ausgewählte Einträge aus einem Wörterbuch map() ändert jeden Wörterbuch-Eintrag keys() liefert Liste von Wörterbuch-Schlüsseln values() liefert Liste von Wörterbuch-Werten items() liefert Liste von Wörterbuch-Schlüssel-Wert-Paaren copy() macht eine oberflächliche Kopie eines Wörterbuchs deepcopy macht eine volle Kopie eines Wörterbuchs string() Zeichenkettenrepräsentation eines Wörterbuchs max() maximaler Wert in einem Wörterbuch min() minimaler Wert in einem Wörterbuch count() Anzahl, wie oft ein Wert erscheint Variablen: type() Typ einer Variablen islocked() prüft, ob eine Variable gelockt ist function() liefert eine Funktionsreferenz für einen Funktionsnamen getbufvar() liefert einen Variablenwert aus einem bestimmten Puffer setbufvar() setzt eine Variable in einem bestimmten Puffer getwinvar() liefert eine Variable aus einem bestimmten Fenster gettabwinvar() liefert eine Variable für bestimmtes Fenster und Reiterseite setwinvar() setzt eine Variable in einem bestimmten Fenster settabwinvar() setzt eine Variable für bestimmtes Fenster und Reiterseite garbagecollect() gibt möglicherweise Speicher frei Cursor- und Markierungs-Position: col() Spaltennummer von Cursor oder einer Markierung virtcol() Bildschirmspalte von Cursor oder einer Markierung line() Zeilennummer von Cursor oder einer Markierung wincol() Fensterspaltennummer des Cursors winline() Fensterzeilennummer des Cursors cursor() positioniert den Cursor auf einer Zeile/Spalte getpos() liefert Position von Cursor, Markierung usw. setpos() setzt Position von Cursor, Markierung usw. byte2line() liefert Zeilennummer bei einer bestimmten Byte-Anzahl line2byte() Byte-Anzahl bei einer bestimmten Zeile diff_filler() liefert die Anzahl von Füllzeilen über einer Zeile Arbeiten mit dem Text im aktuellen Puffer: getline() liefert eine Zeile oder Liste von Zeilen aus dem aktuellen Puffer setline() ersetzt eine Zeile im Puffer append() fügt eine Zeile oder Liste von Zeilen im Puffer an indent() Einrückung einer bestimmten Zeile cindent() rückt eine Zeile nach Einrückung für C ein lispindent() rückt eine Zeile nach Einrückung für Lisp ein nextnonblank() findet nächste nicht-leere Zeile prevnonblank() findet vorige nicht-leere Zeile search() findet eine Übereinstimmung für ein Muster searchpos() findet eine Übereinstimmung für ein Muster searchpair() findet das andere Ende eines Start/Skip/End searchpairpos() findet das andere Ende eines Start/Skip/End searchdecl() sucht nach der Deklaration eines Namens Systemfunktionen und Manipulation von Dateien: glob() expandiere Wildcards globpath() expandiere Wildcards in einer Reihe von Verzeichnissen findfile() findet eine Datei in einer Liste von Verzeichnissen finddir() findet ein Verzeichnis in einer Liste von Verzeichnissen resolve() findet heraus, wohin ein Shortcut zeigt fnamemodify() modifiziert einen Dateinamen pathshorten() verkürzt Verzeichnisnamen in einem Pfad simplify() vereinfacht einen Pfad, ohne seine Bedeutung zu verändern executable() prüft, ob ein ausführbares Programm existiert filereadable() prüft, ob eine Datei gelesen werden kann filewritable() prüft, ob in eine Datei geschrieben werden kann getfperm() liefert die Zugriffsrechte einer Datei getftype() liefert die Art einer Datei isdirectory() prüft, ob ein Verzeichnis existiert getfsize() liefert die Größe einer Datei getcwd() liefert das aktuelle Arbeitsverzeichnis haslocaldir() prüft, ob das aktuelle Fenster |:lcd| benutzt hat tempname() liefert den Namen einer temporären Datei mkdir() erzeugt ein neues Verzeichnis delete() löscht eine Datei rename() benennt eine Datei um system() liefert das Ergebnis eines Shell-Befehls hostname() Rechnername readfile() lese eine Datei in eine Liste von Zeilen writefile() schreibe eine Liste von Zeilen in eine Datei Datum und Zeit: getftime() liefert Zeit der letzten Modifikation einer Datei localtime() liefert aktuelle Zeit in Sekunden strftime() konvertiert Zeit in eine Zeichenkette reltime() liefert die aktuelle oder vergangene Zeit akkurat reltimestr() konvertiert Ergebnis von reltime() in eine Zeichenkette Puffer, Fenster und die Argumentenliste: argc() Anzahl von Einträgen in der Argumentenliste argidx() aktuelle Position in der Argumentenliste argv() liefert einen Eintrag aus der Argumentenliste bufexists() prüft, ob ein Puffer existiert buflisted() prüft, ob ein Puffer existiert und gelistet ist bufloaded() prüft, ob ein Puffer existiert und geladen ist bufname() liefert den Namen eines bestimmten Puffers bufnr() liefert die Puffernummer eines bestimmten Puffers tabpagebuflist() gibt eine Liste der Puffer in einer Reiterseite zurück tabpagenr() liefert die Nummer einer Reiterseite tabpagewinnr() wie winnr() für eine bestimmte Reiterseite winnr() liefert die Fensternummer für das aktuelle Fenster bufwinnr() liefert die Fensternummer eines bestimmten Puffers winbufnr() liefert die Puffernummer eines bestimmten Fensters getbufline() liefert eine Liste von Zeilen aus dem bestimmten Puffer Befehlszeile: getcmdline() liefert die aktuelle Befehlszeile getcmdpos() liefert Position des Cursors in der Befehlszeile setcmdpos() setzt Position des Cursors in der Befehlszeile getcmdtype() gibt den Typ der aktuellen Befehlszeile zurück Quickfix und Lokationslisten: getqflist() Liste von Quickfix-Fehlern setqflist() modifiziere eine Quickfix-Liste getloclist() Liste von Lokationslisten-Elementen setloclist() modifiziere eine Lokationsliste Vervollständigung im Einfügemodus: complete() setzt gefundene Übereinstimmungen complete_add() fügt zu gefundenen Übereinstimmungen hinzu complete_check() prüft, ob Vervollständigung abgebrochen werden sollte pumvisible() prüft, ob das Aufklapp-Menü angezeigt wird Falten: foldclosed() prüft auf eine geschlossene Faltung in einer bestimmten Zeile foldclosedend() wie foldclosed(), aber gibt die letzte Zeile zurück foldlevel() prüft auf die Faltungsebene in einer bestimmten Zeile foldtext() erzeugt die für eine geschlossene Faltung angezeigte Zeile foldtextresult() liefert den für eine geschlossene Faltung angezeigten Text Syntax und Hervorhebung: hlexists() prüft, ob eine Hervorhebungsgruppe existiert hlID() liefert ID einer Hervorhebungsgruppe synID() liefert Syntax-ID an einer bestimmten Position synIDattr() liefert ein bestimmtes Attribut einer Syntax-ID synIDtrans() liefert übersetzte Syntax-ID diff_hlID() liefert Hervorhebungs-ID für den Diff-Modus an einer Position matcharg() liefert Informationen über Argumente von |:match| Rechtschreibkontrolle: spellbadword() lokalisiert falsch geschriebenes Wort am oder nach dem Cursor spellsuggest() liefert vorgeschlagene Schreibungskorrekturen zurück soundfold() liefert die ähnlich klingende Entsprechung eines Wortes zurück Verläufe: histadd() fügt ein Element einem Verlauf hinzu histdel() löscht ein Element aus einem Verlauf histget() liefert ein Element aus einem Verlauf histnr() liefert den höchsten Index einer Verlaufsliste Interaktiv: browse() stellt eine Datei-Anfrage zur Verfügung browsedir() stellt eine Verzeichnis-Anfrage zur Verfügung confirm() lässt den Benutzer eine Auswahl machen getchar() holt ein Zeichen vom Benutzer getcharmod() liefert die Modifikatoren für das letzte eingegebene Zeichen input() holt eine Zeile vom Benutzer inputlist() lässt den Benutzer einen Eintrag aus einer Liste auswählen inputsecret() holt eine Zeile vom Benutzer ohne sie anzuzeigen inputdialog() holt eine Zeile vom Benutzer in einem Dialog inputsave() speichert und löscht Vorauseingabe inputrestore() stellt die Vorauseingabe wieder her GUI: getfontname() liefert den Namen des aktuell benutzten Schriftstils getwinposx() X-Position des GUI-Vim-Fensters getwinposy() Y-Position des GUI-Vim-Fensters Vim-Server: serverlist() gibt eine Liste von Server-Namen zurück remote_send() sendet Befehlszeichen an einen Vim-Server remote_expr() wertet einen Ausdruck in einem Vim-Server aus server2client() sendet eine Antwort an einen Client eines Vim-Servers remote_peek() prüft, ob es eine Antwort von einem Vim-Server gibt remote_read() liest eine Antwort von einem Vim-Server foreground() bewegt das Vim-Fenster in den Vordergrund remote_foreground() bewegt das Fenster des Vim-Servers in den Vordergrund Fenster-Größe und -Position: winheight() liefert die Höhe eines bestimmten Fensters winwidth() liefert die Breite eines bestimmten Fensters winrestcmd() gibt Befehl zurück, um Fenstergrößen wiederherzustellen winsaveview() liefert Ansicht des aktuellen Fensters winrestview() stellt gespeicherte Ansicht des aktuellen Fensters wieder her Verschiedenes: mode() liefert aktuelle Editiermodus visualmode() der letzte benutzte visuelle Modus hasmapto() prüft, ob eine Belegung existiert mapcheck() prüft, ob eine passende Belegung existiert maparg() liefert die rechte Seite einer Belegung exists() prüft, ob eine Variable, Funktion usw. existiert has() prüft, ob ein Feature von Vim unterstützt wird changenr() gibt die Nummer der letzten Änderung zurück cscope_connection() prüft, ob eine CScope-Anbindung existiert did_filetype() prüft, ob ein automatischer Befehl eines Dateityps benutzt wurde eventhandler() prüft, ob von einem Ereignis-Behandler aufgerufen wurde libcall() ruft eine Funktion in einer externen Bibliothek auf libcallnr() ebenso, gibt eine Zahl zurück getreg() liefert Inhalt eines Registers getregtype() liefert Typ eines Registers setreg() setzt Inhalt und Typ eines Registers taglist() liefert Liste passender Tags tagfiles() liefert Liste von Tag-Dateien ============================================================================== *41.7* Eine Funktion definieren Vim ermöglicht es Ihnen, eigene Funktionen zu definieren. Die grundlegende Funktions-Deklaration beginnt wie folgt: > :function {name}({var1}, {var2}, ...) : {body} :endfunction < Anmerkung: Funktions-Namen müssen mit einem Großbuchstaben beginnen. Lassen Sie uns eine kurze Funktion definieren, die die kleinere von zwei Zahlen zurückgibt. Sie beginnt mit dieser Zeile: > :function Min(num1, num2) Dies sagt Vim, dass die Funktion »Min« heißt und zwei Argumente nimmt, »num1« und »num2«. Als erstes müssen wir überprüfen, welche Zahl kleiner ist: > : if a:num1 < a:num2 Das besondere Präfix »a:« sagt Vim, dass die Variable ein Funktions-Argument ist. Weisen wir nun der Variablen »smaller« den Wert der kleineren Zahl zu: > : if a:num1 < a:num2 : let smaller = a:num1 : else : let smaller = a:num2 : endif Die Variable »smaller« ist eine lokale Variable. In einer Funktion benutzte Variablen sind lokal, solange ihnen nicht etwas wie »g:«, »a:« oder »s:« vorangestellt wird. Anmerkung: Um innerhalb einer Funktion auf eine globale Variable zuzugreifen, müssen Sie ihr »g:« voranstellen. Also wird innerhalb einer Funktion »g:count« für die globale Funktion »count« verwandt, und ein einfaches »count« ist eine andere Variable, die zur Funktion lokal ist. Nun benutzen wir die Anweisung »:return« um die kleinere Zahl an den Aufrufer zurückzugeben. Die Funktion endet schließlich: > : return smaller :endfunction Die vollständige Funktions-Definition ist wie folgt: > :function Min(num1, num2) : if a:num1 < a:num2 : let smaller = a:num1 : else : let smaller = a:num2 : endif : return smaller :endfunction Für Leute, die kurze Funktionen mögen, leistet dies dasselbe: > :function Min(num1, num2) : if a:num1 < a:num2 : return a:num1 : endif : return a:num2 :endfunction Eine benutzerdefinierte Funktion wird genauso aufgerufen, wie eine eingebaute. Nur der Name ist verschieden. Die Funktion Min kann so benutzt werden: > :echo Min(5, 8) Erst jetzt wird die Funktion ausgeführt, und die Zeilen werden von Vim interpretiert. Falls es Fehler gibt, wie das Nutzen einer undefinierten Variablen oder Funktion, erhalten Sie nun eine Fehlermeldung. Beim Definieren der Funktion werden diese Fehler nicht erkannt. Wenn eine Funktion »:endfunction« erreicht, oder »:return« wird ohne Argument benutzt, gibt die Funktion Null zurück. Um eine bereits existierende Funktion erneut zu definieren, benutzen Sie »:function« mit dem !-Modifikator: > :function! Min(num1, num2, num3) EINEN BEREICH BENUTZEN Dem Befehl »:call« kann ein Zeilenbereich gegeben werden. Dies kann eine von zwei Bedeutungen haben. Wenn eine Funktion mit dem Schlüsselwort »range« definiert wurde, kümmert sie sich selbst um den Zeilenbereich. Der Funktion werden die Variablen »a:firstline« und »a:lastline« übergeben. Diese haben die Zeilennummern des Bereiches, mit dem die Funktion aufgerufen wurde. Beispiel: > :function Count_words() range : let n = a:firstline : let count = 0 : while n <= a:lastline : let count = count + Wordcount(getline(n)) : let n = n + 1 : endwhile : echo "found " . count . " words" :endfunction Sie können diese Funktion aufrufen mit: > :10,30call Count_words() Sie wird einmal ausgeführt und gibt die Anzahl an Worten aus. Die andere Möglichkeit einen Zeilenbereich zu benutzen ist, eine Funktion ohne das Schlüsselwort »range« zu definieren. Die Funktion wird einmal für jede Zeile in dem Bereich aufgerufen, mit dem Cursor in dieser Zeile. Beispiel: > :function Number() : echo "line " . line(".") . " contains: " . getline(".") :endfunction Falls Sie diese Funktion aufrufen mit: > :10,15call Number() wird die Funktion sechs Mal aufgerufen. VARIABLE ANZAHL VON ARGUMENTEN Vim ermöglicht Ihnen, Funktionen zu definieren, die eine variable Anzahl von Argumenten haben. Der folgende Befehl zum Beispiel definiert eine Funktion, die ein Argument haben muss (start), und die bis zu 20 zusätzliche Argumente haben kann: > :function Show(start, ...) Die Variable »a:1« enthält das erste optionale Argument, »a:2« das zweite und so weiter. Die Variable »a:0« enthält die Anzahl von zusätzlichen Argumenten Zum Beispiel: > :function Show(start, ...) : echohl Title : echo "Show is " . a:start : echohl None : let index = 1 : while index <= a:0 : echo " Arg " . index . " is " . a:{index} : let index = index + 1 : endwhile : echo "" :endfunction Dies benutzt den Befehl »:echohl« um die für den nächsten »:echo«-Befehl benutzte Hervorhebung anzugeben. »:echohl None« deaktiviert dies. Der Befehl »:echon« funktioniert wie »:echo«, gibt aber keinen Zeilenumbruch aus. Sie können auch die Variable a:000 benutzen. Dies ist eine Liste aller optionalen Argumente. Siehe |a:000|. FUNKTIONEN AUFLISTEN Der Befehl »:function« listet die Namen und Argumente aller benutzerdefinierten Funktionen auf: > :function < function Show(start, ...) ~ function GetVimIndent() ~ function SetSyn(name) ~ Um zu sehen, was eine Funktion tut, benutzen Sie ihren Namen als Argument für »:function«: > :function SetSyn < 1 if &syntax == '' ~ 2 let &syntax = a:name ~ 3 endif ~ endfunction ~ FEHLERSUCHE Die Zeilennummer ist nützlich, wenn Sie eine Fehlermeldung erhalten, oder bei der Fehlersuche. Siehe |debug-scripts| für den Fehlersuche-Modus. Sie können auch die Option 'verbose' auf 12 oder höher setzen, um alle Funktionsaufrufe zu sehen. Setzen Sie sie auf 15 oder höher um jede ausgeführte Zeile zu sehen. EINE FUNKTION LÖSCHEN Um die Funktion Show() zu löschen: > :delfunction Show Sie erhalten eine Fehlermeldung, wenn die Funktion nicht existiert. FUNKTIONSREFERENZEN Manchmal kann es nützlich sein, eine Variable auf die eine oder andere Funktion zeigen zu lassen. Dies können Sie mit der Funktion function() machen. Sie verwandelt den Namen einer Funktion in eine Referenz: > :let result = 0 " or 1 :function! Right() : return 'Right!' :endfunc :function! Wrong() : return 'Wrong!' :endfunc : :if result == 1 : let Afunc = function('Right') :else : let Afunc = function('Wrong') :endif :echo call(Afunc, []) < Wrong! ~ Beachten Sie, dass der Name einer Variablen, die eine Funktionsreferenz enthält, mit einem Großbuchstaben beginnen muss. Andernfalls könnte er mit dem Namen einer eingebauten Funktion verwechselt werden. Der Weg, eine Funktion aufzurufen, die eine Variable referenziert, ist es, die Funktion call() zu benutzen. Ihr erstes Argument ist die Funktionsreferenz, das zweite Argument ist eine Liste von Argumenten. Funktionsreferenzen sind am nützlichsten in Kombination mit einem Wörterbuch, was im nächsten Abschnitt erklärt wird. ============================================================================== *41.8* Listen und Wörterbücher Bis jetzt haben wir die Basistypen String (Zeichenkette) und Number (Zahl) benutzt. Vim unterstützt auch zwei zusammengesetzte Typen: Liste und Dictionary (Wörterbuch). Eine Liste ist eine geordnete Folge von Dinge. Die Dinge können jeglicher Art von Wert sein, also können Sie eine Liste von Zahlen machen, eine Liste von Listen, und sogar eine Liste von gemischten Elementen. Um eine Liste mit drei Zeichenketten zu erzeugen: > :let alist = ['aap', 'mies', 'noot'] Die Listenelemente sind in eckige Klammern eingeschlossen und durch Kommata getrennt. Um eine leere Liste zu erzeugen: > :let alist = [] Mit der Funktion add() können Sie einer Liste Elemente hinzufügen: > :let alist = [] :call add(alist, 'foo') :call add(alist, 'bar') :echo alist < ['foo', 'bar'] ~ Listen-Konkatenation wird mit + erledigt: > :echo alist + ['foo', 'bar'] < ['foo', 'bar', 'foo', 'bar'] ~ Oder falls Sie eine Liste direkt erweitern wollen: > :let alist = ['one'] :call extend(alist, ['two', 'three']) :echo alist < ['one', 'two', 'three'] ~ Beachten Sie, dass das Benutzen von add() einen anderen Effekt hat: > :let alist = ['one'] :call add(alist, ['two', 'three']) :echo alist < ['one', ['two', 'three']] ~ Das zweite Argument von add() wird als einzelnes Element hinzugefügt. FOR-SCHLEIFE Eines der netten Dinge, die Sie mit einer Liste machen können, ist über sie zu iterieren: > :let alist = ['one', 'two', 'three'] :for n in alist : echo n :endfor < one ~ two ~ three ~ Dies läuft über jedes Element in der Liste »alist«, wobei es den Wert der Variablen »n« zuweist. Die generische Form einer For-Schleife ist: > :for {varname} in {listexpression} : {commands} :endfor Um über eine bestimmte Anzahl von Malen zu laufen, brauchen Sie eine Liste einer bestimmten Länge. Die Funktion range() erzeugt einem eine: > :for a in range(3) : echo a :endfor < 0 ~ 1 ~ 2 ~ Beachten Sie, dass das erste Element der Liste, die range() produziert, Null ist, also ist das letzte Element eins weniger als die Länge der Liste. Sie können auch den Maximalwert angeben, den Inkrement, und sogar rückwärts laufen: > :for a in range(8, 4, -2) : echo a :endfor < 8 ~ 6 ~ 4 ~ Ein nützlicheres Beispiel, das über Zeilen im Puffer läuft: > :for line in getline(1, 20) : if line =~ "Date: " : echo matchstr(line, 'Date: \zs.*') : endif :endfor Dies schaut sich Zeilen 1 bis 20 (einschließlich) an, und gibt jedes dort gefundene Datum aus. WÖRTERBÜCHER Ein Wörterbuch speichert Schlüssel-Wert-Paare. Man kann schnell einen Wert nachschlagen, wenn man den Schlüssel kennt. Ein Wörterbuch wird mit geschwungenen Klammern erzeugt: > :let uk2nl = {'one': 'een', 'two': 'twee', 'three': 'drie'} Nun kann man Wörter nachschlagen, indem man den Schlüssel in eckige Klammern setzt: > :echo uk2nl['two'] < twee ~ Die generische Form für das Definieren eines Wörterbuchs ist: > { : , ...} Ein leeres Wörterbuch ist eines ohne Schlüssel: > {} Die Möglichkeiten mit Wörterbüchern sind zahlreich. Ebenso gibt es für sie verschiedene Funktionen. Zum Beispiel kann man eine Liste der Schlüssel beziehen und sie durchlaufen: > :for key in keys(uk2nl) : echo key :endfor < three ~ one ~ two ~ Sie werden bemerken, dass die Schlüssel nicht geordnet sind. Man kann die Liste sortieren, um eine bestimmte Ordnung zu erhalten: > :for key in sort(keys(uk2nl)) : echo key :endfor < one ~ three ~ two ~ Aber man kann nie die Reihenfolge zurückbekommen, in der die Elemente definiert wurden. Dafür muss man eine Liste benutzen, sie speichert Elemente in einer geordneten Reihenfolge. WÖRTERBUCHFUNKTIONEN Die Elemente in einem Wörterbuch können normalerweise mit einem Index in eckigen Klammern bezogen werden: > :echo uk2nl['one'] < een ~ Eine Methode, die dasselbe tut, aber ohne so viele Zeichen: > :echo uk2nl.one < een ~ Dies funktioniert nur für einen Schlüssel, der aus ASCII-Buchstaben, Ziffern und dem Unterstrich besteht. Auf diese Weise kann man auch einen neuen Wert zuweisen: > :let uk2nl.four = 'vier' :echo uk2nl < {'three': 'drie', 'four': 'vier', 'one': 'een', 'two': 'twee'} ~ Und nun zu etwas Besonderem: man kann direkt eine Funktion definieren und eine Referenz auf diese in dem Wörterbuch speichern: > :function uk2nl.translate(line) dict : return join(map(split(a:line), 'get(self, v:val, "???")')) :endfunction Probieren wir dies zunächst einmal aus: > :echo uk2nl.translate('three two five one') < drie twee ??? een ~ Das erste Besondere, das Sie bemerken, ist das »dict« am Ende der Zeile mit »:function«. Dies markiert die Funktion als aus einem Wörterbuch zu benutzen. Die Variable »self« referenziert dann dieses Wörterbuch. Schauen wir uns den komplizierten Rückgabebefehl nun Stück für Stück an: > split(a:line) Die Funktion split() nimmt eine Zeichenkette, schneidet sie in leerzeichen-getrennte Wörter, und gibt eine Liste mit diesen Wörtern zurück. Im Beispiel also: > :echo split('three two five one') < ['three', 'two', 'five', 'one'] ~ Diese Liste ist das erste Argument an die Funktion map(). Diese geht durch die Liste, und wertet dabei ihr zweite Argument mit »v:val« auf den Wert jedes Elements gesetzt. Dies ist eine Abkürzung zum Benutzen einer For-Schleife. Dieser Befehl: > :let alist = map(split(a:line), 'get(self, v:val, "???")') ist gleichbedeutend mit: > :let alist = split(a:line) :for idx in range(len(alist)) : let alist[idx] = get(self, alist[idx], "???") :endfor Die Funktion get() prüft, ob ein Schlüssel in einem Wörterbuch vorhanden ist. Falls er es ist, wird der Wert zurückgegeben. Falls nicht, wird ein Standardwert zurückgegeben, im Beispiel '???'. Dies ist eine bequeme Art Situationen zu behandeln, in denen ein Schlüssel möglicherweise nicht vorhanden ist und man keine Fehlermeldung möchte. Die Funktion join() tut das Gegenteil von split(): sie fügt eine Liste von Wörtern zusammen, wobei sie ein Leerzeichen zwischen sie setzt. Diese Kombination von split(), map() und join() ist eine schöne Möglichkeit, eine Zeile von Wörtern auf eine sehr kompakte Weise zu filtern. OBJEKT-ORIENTIERTE PROGRAMMIERUNG Jetzt, da Sie sowohl Werte wie Funktionen in ein Wörterbuch stecken können, können Sie ein Wörterbuch tatsächlich wie ein Objekt benutzen. Oben haben wir ein Wörterbuch benutzt, um Holländisch in Englisch zu übersetzen. Wir könnten dasselbe für andere Sprachen tun wollen. Machen wir zunächst ein Objekt (aka Wörterbuch), das die Übersetzungsfunktion hat, aber keine Wörter zum Übersetzen: > :let transdict = {} :function transdict.translate(line) dict : return join(map(split(a:line), 'get(self.words, v:val, "???")')) :endfunction Es ist ein wenig verschieden von der Funktion oben, indem wir 'self.words' benutzen, um Wort-Übersetzungen nachzuschlagen. Aber wir haben keine self.words. Also könnte man dies eine abstrakte Klasse nennen. Nun können wir ein Übersetzungsobjekt für Holländisch instanziieren: > :let uk2nl = copy(transdict) :let uk2nl.words = {'one': 'een', 'two': 'twee', 'three': 'drie'} :echo uk2nl.translate('three one') < drie een ~ Und einen Übersetzer für Deutsch: > :let uk2de = copy(transdict) :let uk2de.words = {'one': 'ein', 'two': 'zwei', 'three': 'drei'} :echo uk2de.translate('three one') < drei ein ~ Sie sehen, dass die Funktion copy() benutzt wird, um eine Kopie des Wörterbuchs »transdict« zu machen, und dann diese Kopie verändert wird, um die Worte aufzunehmen. Das Original bleibt natürlich dasselbe. Nun kann man einen Schritt weiter gehen, und den bevorzugten Übersetzer verwenden: > :if $LANG =~ "de" : let trans = uk2de :else : let trans = uk2nl :endif :echo trans.translate('one two three') < een twee drie ~ Hier referenziert »trans« eines der beiden Objekte (Wörterbücher). Es wird keine Kopie gemacht. Mehr über Listen- und Wörterbuch-Identität kann unter |list-identity| und |dict-identity| gefunden werden. Nun könnte man eine Sprache benutzen, die nicht unterstützt wird. Man kann die Funktion translate() überstimmen, nichts zu machen: > :let uk2uk = copy(transdict) :function! uk2uk.translate(line) : return a:line :endfunction :echo uk2uk.translate('three one wladiwostok') < three one wladiwostok ~ Beachten Sie, dass ein ! benutzt wurde, um die bestehende Funktionsreferenz zu überschreiben. Nun benutze »uk2uk«, falls keine erkannte Sprache gefunden wird: > :if $LANG =~ "de" : let trans = uk2de :elseif $LANG =~ "nl" : let trans = uk2nl :else : let trans = uk2uk :endif :echo trans.translate('one two three') < one two three ~ Zum Weiterlesen siehe |Lists| und |Dictionaries|. ============================================================================== *41.9* Ausnahmen Beginnen wir mit einem Beispiel: > :try : read ~/templates/pascal.tmpl :catch /E484:/ : echo "Sorry, the Pascal template file cannot be found." :endtry Der Befehl »:read« scheitert, falls die Datei nicht existiert. Statt eine Fehlermeldung zu generieren, fängt dieser Code den Fehler auf und gibt dem Benutzer stattdessen eine nette Meldung. Für die Befehle zwischen »:try« und »:endtry« werden Fehler in Ausnahmen verwandelt. Eine Ausnahme ist eine Zeichenkette. Im Falle eines Fehlers enthält die Zeichenkette die Fehlernummer. Und jede Fehlermeldung hat eine Nummer. In diesem Fall enthält der Fehler, den wir auffangen, »E484:«. Diese Nummer bleibt garantiert dieselbe (der Text kann sich ändern, z.B. übersetzt werden). Wenn der Befehl »:read« einen anderen Fehler verursacht, passt das Muster »E484:« nicht. Also wird diese Ausnahme nicht aufgefangen und resultiert in der üblichen Fehlermeldung. Sie sind vielleicht versucht, dies zu tun: > :try : read ~/templates/pascal.tmpl :catch : echo "Sorry, the Pascal template file cannot be found." :endtry Dies bedeutet, dass alle Fehler aufgefangen werden. Dann aber sehen Sie keine nützlichen Fehler, so wie »E21: Kann keine Änderungen machen, 'modifiable' ist aus«. Ein weiterer nützlicher Mechanismus ist der Befehl »:finally«: > :let tmp = tempname() :try : exe ".,$write " . tmp : exe "!filter " . tmp : .,$delete : exe "$read " . tmp :finally : call delete(tmp) :endtry Dies filtert die Zeilen vom Cursor bis zum Dateiende durch das Programm »filter«, das einen Dateinamen als Argument nimmt. Egal, ob das Filtern funktioniert, etwas geht zwischen »:try« und »:finally« schief, oder der Benutzer bricht das Filtern ab, indem er CTRL-C drückt, »call delete(tmp)« wird jederzeit ausgeführt. Dies stellt sicher, dass man nicht die temporäre Datei zurücklässt. Weiter Informationen über Ausnahme-Behandlung können im Referenzhandbuch gefunden werden: |exception-handling|. ============================================================================== *41.10* Verschiedene Anmerkungen Hier ist eine Sammlung von Elementen, die auf Vim-Skripte zutreffen. Sie werden auch anderswo erwähnt, aber formen eine nette Prüfliste. Das Zeichen für das Zeilenende hängt vom System ab. Unix benutzt ein einzelnes -Zeichen. Unter MS-DOS, Windows, OS/2 und so wird benutzt. Dies ist wichtig, wenn Belegungen verwandt werden, die in einem enden. Siehe |:source_crnl|. WHITE SPACE Leere Zeilen sind erlaubt und werden ignoriert. Führende Leerzeichen und Tabulatoren werden immer ignoriert. Der Whitespace zwischen Parametern (z.B zwischen dem 'set' und dem 'cpoptions' im Beispiel unten) wird reduziert auf ein Leerzeichen und spielt die Rolle eines Trenners, der Whitespace nach dem letzten (sichtbaren) Zeichen kann situationsabhängig ignoriert werden oder auch nicht, siehe unten. Für einen »:set«-Befehl, der das Zeichen »=« (gleich) enthält, so wie in: > :set cpoptions =aABceFst wird der Whitespace unmittelbar vor dem Zeichen »=« ignoriert. Aber es darf kein Whitespace nach dem »=«-Zeichen sein! Um ein Whitespace-Zeichen im Wert einer Option zu verwenden, muss es durch einen »\« (Backslash) geschützt werden, wie in dem folgenden Beispiel: > :set tags=my\ nice\ file Dasselbe Bespiel geschrieben als > :set tags=my nice file resultiert in einem Fehler, weil es interpretiert wird als: > :set tags=my :set nice :set file KOMMENTARE Das Zeichen " (das doppelte Anführungszeichen) leitet einen Kommentar ein. Alles nach und einschließlich dieses Zeichens bis zum Zeilenende wird als Kommentar betrachtet und ignoriert, außer bei Befehlen, die keine Kommentare beachten, wie in den Beispielen unten gezeigt. Ein Kommentar kann an jeder Zeichen-Position auf der Zeile beginnen. Bei einigen Befehlen gibt es »Fallen« mit Kommentaren. Beispiele: > :abbrev dev development " shorthand :map o#include " insert include :execute cmd " do it :!ls *.c " list C files Die Abkürzung 'dev' wird expandiert zu 'development " shorthand'. Die Belegung von ist tatsächlich die ganze Zeile nach dem 'o# ....', einschließlich dem '" insert include'. Der »execute«-Befehl ergibt einen Fehler. Der »!«-Befehl schickt alles nach ihm an die Shell, was einen Fehler wegen eines unbalancierten '"'-Zeichens verursacht. Nach den Befehlen »:map«, »:abbreviate«, »:execute« und »!« kann kein Kommentar stehen (es gibt ein paar Befehle mehr mit dieser Einschränkung). Für die Befehle »:map«, »:abbreviate« und »:execute« gibt es einen Trick: > :abbrev dev development|" shorthand :map o#include|" insert include :execute cmd |" do it Mit dem Zeichen '|' wird ein Befehl vom nächsten getrennt. Und dieses nächste Zeichen ist nur ein Kommentar. Man beachte, das hier kein Leerzeichen vor dem '|' in der Abkürzung und der Belegung ist. Bei diesen Befehlen wird jedes Zeichen bis zum Zeilenende oder dem '|' einbezogen. Als Konsequenz dieses Verhaltens sieht man nicht immer, das nachstehende Leerzeichen einbezogen werden: > :map o#include Um diese Probleme zu vermeiden, können Sie die Option 'list' setzen, wenn Sie vimrc-Dateien editieren. FALLEN Ein noch größeres Problem ensteht im folgenden Beispiel: > :map ,ab o#include :unmap ,ab Hier funktioniert der unmap-Befehl nich, weil er versucht »,ab « freizugeben. Dies existiert nicht als Belegungsfolge. Ein Fehler wird ausgegeben, der sehr schwer zu identifizieren ist, weil das nachstehende Leerzeichen in »:unmap ,ab « nicht sichtbar ist. Und es ist dasselbe wie wenn man einen Kommentar nach einem »unmap«-Befehl benutzt: > :unmap ,ab " comment Hier wird der Teil, der Kommentar, ist ignoriert. Vim versucht dennoch »,ab «, freizugeben, was nicht existiert. Wir schreiben es um als: > :unmap ,ab| " comment DIE ANSICHT WIEDERHERSTELLEN Manchmal will man eine Änderung machen und dahin zurück gehen, wo der Cursor war. Die relative Position wiederherstellen wäre auch nett, so dass dieselbe Zeile die oberste im Fenster ist. Dieses Beispiel kopiert die aktuelle Zeile, setzt sie über die erste Zeile der Datei, und stellt dann die Ansicht wieder her: > map ,p ma"aYHmbgg"aP`bzt`a Was dies tut: > ma"aYHmbgg"aP`bzt`a < ma setze Markierung a bei Cursor-Position "aY kopiere aktuelle Zeile in Register a Hmb gehe zur obersten Zeile im Fenster und setze dort Markierung b gg gehe zur ersten Zeile der Datei "aP setze kopierte Zeile über sie `b gehe zurück zur obersten Zeile der Anzeige zt positioniere den Text im Fenster wie vorher `a gehe zurück zur gespeicherten Cursor-Position PAKETIERUNG Um zu vermeiden, dass Ihre Funktionsnamen Funktionen beeinflussen, die Sie von anderen erhalten, benutzen Sie dieses Schema: - Stellen Sie jedem Funktionsnamen eine einzigartige Zeichenkette voran. Ich benutze oft eine Abkürzung. Zum Beispiel wird »OW_« für Funktionen des Optionsfensters verwandt. - Packen Sie die Definitionen Ihrer Funktionen in eine Datei. Setzen Sie eine globale Variable, die anzeigt, dass die Funktionen geladen wurden. Wenn die Datei erneut eingelesen wird, geben Sie erst die Funktionen frei. Beispiel: > " Dies ist das Paket XXX if exists("XXX_loaded") delfun XXX_one delfun XXX_two endif function XXX_one(a) ... body of function ... endfun function XXX_two(b) ... body of function ... endfun let XXX_loaded = 1 ============================================================================== *41.11* Ein Plugin schreiben *write-plugin* Sie können ein Vim-Skript so schreiben, dass viele Leute es benutzen können. Dies nennt sich Plugin. Vim-Benutzer können Ihr Skript in ihr Plugin-Verzeichnis legen und seine Features auf der Stelle nutzen |add-plugin|. Tatsächlich gibt es zwei Typen von Plugins: globale Plugins: Für alle Typen von Dateien. Dateityp-Plugins: Nur für Dateien eines bestimmten Typs. In diesem Abschnitt wird der erste Typ erklärt. Die meisten Elemente sind auch relevant für das Schreiben von Dateityp-Plugins. Die Spezifika für Dateityp-Plugins folgen im nächsten Abschnitt |write-filetype-plugin|. NAME Zuallererst müssen Sie einen Namen für Ihr Plugin wählen. Die Features, die von dem Plugin bereitgestellt werden werden, sollten anhand seines Namens klar sein. Und es sollte unwahrscheinlich sein, dass jemand anderes ein Plugin mit demselben Namen schreibt, das etwas anderes tut. Und bitte begrenzen Sie den Namen auf acht Zeichen, um Probleme auf alten Windows-Systemen zu vermeiden. Ein Skript, welches Tipp-Fehler korrigiert, könnte »typecorr.vim« genannt werden. Wir benutzen es hier als Beispiel. Damit das Plugin für jeden funktioniert, sollte es einigen wenigen Richtlinien folgen. Diese werden Schritt-für-Schritt erklärt. Das komplette Beispiel-Plugin steht am Ende. KÖRPER Lassen Sie uns mit dem Körper des Plugins beginnen, den Zeilen, die tatsächlich die Arbeit erledigen: > 14 iabbrev teh the 15 iabbrev otehr other 16 iabbrev wnat want 17 iabbrev synchronisation 18 \ synchronization 19 let s:count = 4 Die tatsächliche Liste sollte selbstverständlich viel länger sein. Die Zeilennummern wurden nur hinzugefügt, um ein Paar Dinge zu erläutern, übernehmen Sie sie nicht in Ihre Plugin-Datei! KOPF Sie werden dem Plugin vermutlich neue Korrekturen hinzufügen, und bald haben Sie mehrere Versionen herumliegen. Und wenn Sie die Datei verteilen, werden die Leute wissen wollen, wer dieses wundervolle Plugin schrieb und wohin sie Anmerkungen schicken können. Deshalb stellen Sie Kopfzeilen an den Beginn Ihres Plugins: > 1 " Vim global plugin for correcting typing mistakes 2 " Last Change: 2000 Oct 15 3 " Maintainer: Bram Moolenaar Über Copyright und Lizensierung: Da Plugins sehr nützlich sind, und es kaum wert ist, ihre Verteilung zu begrenzen, erwägen Sie bitte, Ihr Plugin entweder in die Public-Domain oder unter die Vim-Lizenz |license| zu stellen. Eine kurze Zeile hierüber nahe des Beginn des Plugins sollte ausreichend sein. Beispiel: > 4 " License: This file is placed in the public domain. ZEILENFORTSETZUNG, SEITENEFFEKTE VERMEIDEN *use-cpo-save* Oben in Zeile 18 wird der Mechanismus der Zeilen-Fortsetzung benutzt |line-continuation|. Benutzer, bei denen 'compatible' gesetzt ist, werden hier Probleme, eine Fehlermeldung bekommen. Wir können nicht einfach 'compatible' neu setzen, weil dies viele Seiteneffekte hat. Um dies zu vermeiden, setzen wir die Option 'cpoptions' auf ihren Vim-Standardwert und stellen sie später wieder her. Das erlaubt die Benutzung von Zeilen-Fortsetzung und lässt das Skript für die meisten Leute funktionieren. Es wird so gemacht: > 11 let s:save_cpo = &cpo 12 set cpo&vim .. 42 let &cpo = s:save_cpo Wir speichern zunächst den alten Wert von 'cpoptions' in der Variablen s:save_cpo. Am Ende des Plugins wird dieser Wert wiederhergestellt. Beachten Sie, dass eine skript-lokale Variable benutzt wird |s:var|. Eine globale Variable könnte bereits für etwas anderes in Gebrauch sein. Benutzen Sie immer skript-lokale Variablen für Dinge, die nur in dem Skript benutzt werden. NICHT LADEN Es ist möglich, dass ein Nutzer nicht immer dieses Plugin laden möchte. Oder der System-Administrator hat es in das system-weite Plugin-Verzeichnis gelegt, aber ein Nutzer hat sein eigenes Plugin, das er nutzen möchte. Dann muss der Nutzer eine Möglichkeit haben, das Laden dieses bestimmten Plugins zu deaktivieren. Dies macht es möglich: > 6 if exists("loaded_typecorr") 7 finish 8 endif 9 let loaded_typecorr = 1 Dies vermeidet es auch, dass wenn das Skript zweimal geladen wird, dass es Fehlermeldungen für das Neudefinieren von Funktionen und Schwierigkeiten bei automatischen Befehlen, die zweimal hinzugefügt werden, verursacht. BELEGUNGEN Nun lassen Sie uns das Plugin interessanter machen: Wir fügen eine Belegung hinzu, die eine Korrektur für das Wort unter dem Cursor hinzufügt. Um dem Nutzer zu erlauben, zu definieren, welche Tasten eine Belegung in einem Plugin benutzt, kann das Element benutzt werden: > 22 map a TypecorrAdd Das »TypecorrAdd« macht die Arbeit, mehr darüber weiter unten. Der Nutzer kann die Variable »mapleader« auf die Tastenfolge setzen, mit der er diese Belegung starten lassen möchte. Falls der Nutzer also dies macht: > let mapleader = "_" definiert die Belegung »_a«. Falls der Nutzer dies nicht tat, wird der Standardwert benutzt, was ein Backslash ist. Dann wird eine Belegung für »\a« definiert. Beachten Sie, dass benutzt wird, dies verursacht eine Fehlermeldung, falls die Belegung der Tastenfolge bereits existiert. |:map-| Aber was, falls der Nutzer seine eigene Tastenfolge definieren möchte? Wir können dies mit folgendem Mechanismus erlauben: > 21 if !hasmapto('TypecorrAdd') 22 map a TypecorrAdd 23 endif Dies prüft, ob eine Belegung von »TypecorrAdd« bereits existiert, und definiert die Belegung von »a« nur dann, falls nicht. Der Nutzer hat nun die Möglichkeit, dies in seine vimrc zu packen: > map ,c TypecorrAdd Dann ist die belegte Tastenfolge »,c« statt »_a« oder »\a«. STÜCKE Wenn ein Skript länger wird, möchte man oft die Arbeit in Portionen aufteilen. Man kann hierfür Funktionen oder Belegungen benutzen. Aber man will nicht, dass diese Funktionen oder Belegungen andere Skripte beeinflussen. Man könnte zum Beispiel eine Funktion Add() definieren, aber ein anderes Skript könnte versuchen, dieselbe Funktion zu definieren. Um dies zu vermeiden, definieren wir die Funktion lokal zum Skript, indem wir ihr »s:« voranstellen. Wir definieren eine Funktion, die eine neue Tipp-Korrektur hinzufügt: > 30 function s:Add(from, correct) 31 let to = input("type the correction for " . a:from . ": ") 32 exe ":iabbrev " . a:from . " " . to .. 36 endfunction Jetzt können wir innerhalb des Skripts die Funktion s:Add() aufrufen. Falls ein anderes Skript ebenfalls s:Add() definiert, ist sie lokal zu jenem Skript und kann nur von dem Skript aufgerufen werden, in dem sie definiert wurde. Es kann auch eine globale Funktion Add() geben (ohne »s:«, die wieder eine andere Funktion ist. kann mit Belegungen verwandt werden. Es generiert eine Skript-ID, die das aktuelle Skript identifiziert. In unserem Tipp-Korrektur-Plugin verwenden wir es folgendermaßen: > 24 noremap