Gehen Sie zum nächsten Wort, beginnend mit einem bestimmten Buchstaben in der aktuellen Zeile

7

Ich möchte den Befehl haben, der im Titel angegeben ist. Ich habe im intrinsischen Vim-Befehl danach gesucht, es aber nicht gefunden. Ich habe online ein Tutorial über Funktionen in vim ausgeführt. Die Idee wäre, so etwas in meinem zu haben .vimrc:

map <command> <argument>: call nextWordWithLetter(argument)

function nextWordWithLetter(letter)
    " for word i in line
    " if word i's first letter = letter
        " col = column of word i
        " break
    col | "The command to jump to column number 'col'
end function
Solalito
quelle

Antworten:

11

Sie können getchar()und verwenden search(), um Ihr Ziel zu erreichen.

nnoremap <key> :call search('\<' . nr2char(getchar()), 'W', line('.'))<cr>

Die Idee ist, dass wir nr2char(getchar())warten, bis ein Zeichen eingegeben wurde, und search()nach der nächsten Instanz des Zeichens am Anfang eines Wortes über suchen \<. Wir beschränken dies auf die aktuelle Zeile, indem wir die aktuelle Zeile line('.')als dritten Parameter angeben search().

Persönlich denke ich, ich würde lieber nur verwenden /oder f, aber es gibt andere Plugins, die ähnliche Funktionen bieten:

Weitere Hilfe finden Sie unter:

:h search(
:h getchar(
:h /\<
Peter Rincker
quelle
7

Bearbeiten : Die Antwort von Peter Rincker ist kürzer, leichter zu erklären und kann so oft wiederholt werden, wie Sie möchten. Meine Lösung ist zu lang und kann nicht für mehrere Wörter wiederholt werden. Ich sollte die Antwort löschen, aber vielleicht interessiert sich jemand dafür, also lasse ich es hier.


Ich bin mir nicht sicher, ob Sie dies möchten, aber versuchen Sie den folgenden Code:

function! NextWordWithLetter(type)

    normal! `]vy

    let letter = @0
    let line = getline('.')
    let list = split(line)

    for word in list
        if matchstr(word, '\%1c.') ==# letter
            let col = match(line, word) + 1
            execute "normal! " . col . "|"
            break
        endif
    endfor

endfunction

nnoremap <silent> <leader>g :set operatorfunc=NextWordWithLetter<cr>g@

Die Zuordnung zum Aufrufen der Funktion lautet <leader>g<motion>.

Wenn Sie den Cursor auf das nächste Wort bewegen möchten, das mit dem Buchstaben beginnt pund ,Ihr Leitschlüssel ist, können Sie Folgendes eingeben ,gfp.


Tor

Sie möchten zum nächsten Wort gehen, das mit einem bestimmten Buchstaben beginnt. Ich weiß nicht, ob in vim bereits eine solche Bewegung integriert ist, aber nehmen wir an, dass dies nicht der Fall ist.

Wir brauchen eine neue Bewegung definieren (wie w, b, eusw.). Normalerweise verwenden Sie den onoremapBefehl , um eine neue Bewegung oder ein neues Objekt zu definieren . Es wird hier ausführlich erklärt .

Ich weiß nicht, wie Sie Ihr Ziel auf diese Weise erreichen können, aber ich weiß, wie Sie einen neuen Operator definieren, der indirekt das tut, was Sie wollen.
Ich sage indirekt, weil der Zweck eines neuen Operators normalerweise nicht darin besteht , sich irgendwohin zu bewegen , sondern etwas an einem Textstück zu tun .

Vim erklärt in der Hilfe, wie Sie einen neuen Operator erstellen : :h map-operator. Es wird auch hier , hier und hier erklärt .

Die Syntax mag Ihnen seltsam erscheinen, aber so scheint es in Vimscript zu funktionieren:

g@ist ein spezieller Operator ( czum Ändern, dLöschen, yKopieren usw.), mit dem Sie Ihren eigenen Operator erstellen können.

Wenn Sie g@{motion}vim eingeben, suchen Sie nach der opfuncOption, deren Wert der Name einer Funktion sein soll, setzen Sie die Markierungen `[und `]um den Text, den Sie verschoben hätten, wenn Sie gerade getippt hätten {motion}(dazu später mehr), und führen Sie die Funktion aus, die dies kann Mach alles, was du willst.

Sie können verschiedene Operatoren erstellen, indem Sie verschiedene Funktionen schreiben und die opfuncOption auf den Namen der Funktion setzen, die Sie verwenden möchten.
Bevor wir uns jedoch mit der Funktionsweise unserer Funktion befassen, werfen wir einen Blick auf das Mapping, das wir zum Auslösen benötigen.


Kartierung

Am Ende des Codes befindet sich diese Zeile:

nnoremap <silent> <leader>g :set operatorfunc=NextWordWithLetter<cr>g@

Es teilt vim mit, dass es bei jedem Treffer <leader>glautlos ( <silent>) den Wert der operatorfuncOption auf setzen NextWordWithLetterund dann im normalen Modus drücken muss g@.

Der Bediener g@wartet auf eine Bewegung, um die Markierungen `[und `]den Text zu setzen, mit dem Sie sich bewegt hätten, {motion}bevor er die in gespeicherte Funktion ausführt opfunc. Hier ist die Funktion NextWordWithLetter().

Sie können dasselbe tun, indem Sie im Befehlsmodus Folgendes eingeben:

:set operatorfunc=NextWordWithLetter

Dann im normalen Modus: g@
Dann immer im normalen Modus:{motion}

Aber natürlich wäre es mühsam, also erstellen wir das Mapping <leader>g, das die operatorfuncOption automatisch auf den gewünschten Funktionsnamen setzt und g@für uns trifft .

Nehmen wir an, Ihr Führungsschlüssel ist ,und Sie suchen nach dem nächsten Wort, das mit dem Buchstaben beginnt p. Mit unserem Mapping können Sie eingeben ,gfp.
Als Ergebnis vim wird die Markierungen gesetzt `[und `]um den Text zwischen dem Cursor und dem nächsten Zeichen p, Satz opfunczu NextWordWithLetter, und wegen des Wertes dieser Option wird die Funktion ausführen NextWordWithLetter().

Schauen wir uns als nächstes an, was die Funktion tut.


Variablen einstellen

Zuerst müssen wir der Funktion mitteilen, nach welchem ​​Zeichen wir suchen:

normal! `]vy

Der normalBefehl gibt eine beliebige Folge von Zeichen ein, die Sie an ihn übergeben, als wären Sie im normalen Modus. Hier ist die Zeichenfolge `]vy.

Wenn Sie sich in einem Puffer befinden, können Sie mit dem normalen Befehl überall eine Markierung setzen m{letter}.

Wenn Sie die Markierung aan der Stelle platzieren möchten, an der sich Ihr Cursor befindet, geben Sie ein ma. Dann können Sie sich bewegen und zur gleichen Zeile wie die Markierung amit dem Befehl 'aoder zu dem genauen Zeichen zurückkehren, bei dem Sie die Markierung amit dem Befehl setzen `a.
Sie können alle Ihre Markierungen mit dem Befehl sehen :marks. Die Großbuchstaben sind global und können verwendet werden, um über Puffer zu springen. Die Kleinbuchstaben sind lokal für einen Puffer.

Es gibt zwei spezielle Zeichen, die vim automatisch für uns setzt: `[und `]. Immer wenn Sie Text ändern oder ziehen, setzt vim diese Markierungen darum ( :h '[).

Kehren wir zu unserem Beispiel zurück: Sie wollten zum nächsten Wort gehen, das mit dem Buchstaben beginnt p, also haben Sie getroffen ,gfp. fpist ein Befehl, der den Cursor zum nächsten Zeichen bewegt p.

An diesem Punkt setzt vim automatisch die Markierungen `[und `]den Text, um den wir uns bewegt hätten, wenn wir gerade getippt hätten {motion}( fpin unserem Beispiel).
Es steht in der Hilfe ( :h g@):

g @ {motion} Rufen Sie die mit der Option 'operatorfunc' festgelegte Funktion auf. Die Markierung '[befindet sich am Anfang des Textes, der von {motion} verschoben wurde, die Markierung'] am letzten Zeichen des Textes.

Wir haben noch nichts geändert oder gerissen. Warum setzt vim diese Zeichen bereits? Weil es denkt, dass die Funktion etwas am Text tun möchte und die Funktion diese Markierungen benötigt, um den zu bearbeitenden Text zu identifizieren.

Wenn der normalBefehl eingegeben wird `]vy, kann er wie folgt gelesen werden:
Gehen Sie zur Markierung `](letztes Zeichen des Textes, der verschoben wurde), wechseln Sie in den visuellen Modus ( v) und kopieren Sie die visuelle Auswahl ( y) .

Jetzt haben wir Ihren gesuchten Charakter (Charakter p) kopiert . Alles, was Sie kopieren, befindet sich in einem Register. Es gibt verschiedene Register in vim, von denen jedes für einen anderen Zweck verwendet wird. Um sie anzusehen :reg, geben Sie ein und wissen Sie, in welchem ​​Register was gespeichert ist :h registers.

Der zuletzt kopierte Text wird im Register gespeichert 0. Sie können darauf zugreifen mit @0: echo @0.

Ihr gesuchter Charakter pbefindet sich in diesem Register. Wir weisen es der Variablen zu letter:

let letter = @0

Als nächstes weisen wir der Variablen die aktuelle Zeile zu line:

let line = getline('.')

getline()ist eine integrierte Funktion, die die Zeile zurückgibt, deren Nummer als Argument übergeben wurde, und '.'die als Nummer der aktuellen Zeile erweitert wird.

Als nächstes weisen wir der Variablen listeine Liste zu, die jedes Wort der Zeile enthält:

let list = split(line)

split()ist eine integrierte Funktion, die eine Zeichenfolge aufteilt, wenn ein Leerzeichen gefunden wird (Leerzeichen, Tabulatorzeichen ...). Sie können es an anderen Stellen aufteilen, indem Sie ein zweites Argument angeben. Wenn Sie beispielsweise teilen möchten, wann immer ein Doppelpunkt vorhanden ist, würden Sie verwenden split(line, ':').


Für Schleife

Die forSchleife durchläuft die Variable listund speichert jedes ihrer Elemente in der Variablen word:

for word in list
...
endfor

Anschließend wird überprüft, ob der erste Buchstabe von wordder gesuchte Buchstabe ist:

if matchstr(word, '\%1c.') ==# letter
...
endif

matchstr() ist eine integrierte Funktion, die ein Muster zurückgibt, wenn es in einer Zeichenfolge gefunden wird.

Hier suchen wir nach dem Muster im '\%1c.'Inneren word. In einem Vim-Regex \%1cist ein Atom mit der Breite Null. Es fügt dem Muster nichts hinzu, sondern teilt der Regex-Engine, die den Regex analysiert, mit, dass unser Muster in der ersten Spalte des Textes beginnt (für weitere Informationen :h /\%c). Der Punkt am Ende des regulären Ausdrucks bedeutet ein beliebiges Zeichen.

Bedeutet '\%1c.'also: ein beliebiges Zeichen in der ersten Spalte des Textes und matchstr(word, '\%1c.')gibt das erste Zeichen von zurück word.

Um den ersten Buchstaben von wordmit zu vergleichen, verwenden letterwir den ==#Operator. Es ist ein Operator, bei dem zwischen Groß- und Kleinschreibung unterschieden wird (unabhängig vom Wert der ignorecaseOption). Sie können auch eine ==?Groß- und Kleinschreibung verwenden, bei der ==die Groß- und Kleinschreibung nicht berücksichtigt wird (bei der die Groß- und Kleinschreibung beachtet wird oder die nicht davon abhängt ignorecase; es wird nicht empfohlen, diese zu verwenden).

Wenn das erste Zeichen von wordist letter, bewirkt die Funktion Folgendes:

let col = match(line, word) + 1

Es speichert den Index des ersten Zeichens von wordplus eins in der Variablen col. match()ist eine integrierte Funktion, deren Basissyntax lautet match(string, pattern)und die den Index zurückgibt, in stringdem sie patternübereinstimmt. Wir fügen eins hinzu, weil vim mit 0 indiziert.

Schließlich führt es Folgendes aus:

execute "normal! " . col . "|"

executeist ein Befehl, der den Inhalt einer Zeichenfolge ausführt, die Sie an ihn übergeben. Hier ist die Zeichenfolge:"normal! " . col . "|"

Der Punkt - Operator verkettet die drei Saiten "normal !", col(es ist eine Zahl , aber Vims Zwang verwandelt es in einen String automatisch), und "|".

Angenommen, Ihr gesuchtes Zeichen befindet sich in der 10. Spalte der Zeile. In diesem Fall würde die vorherige Zeichenfolge wie folgt ausgewertet : "normal! 10|". executeWird also ausgeführt "normal! 10|", was dazu führt , dass der normalBefehl 10|für uns eingegeben wird.

saginaw
quelle
Danke, genau das habe ich gesucht. Ich verstehe nicht die Hälfte der Befehle, die Sie verwendet haben, um: /
Solalito
2
Es tut mir leid, dass ich den Code nicht erklärt habe. Ich werde den Beitrag bearbeiten, um ihn zu erklären, damit Sie ihn nach Ihren Wünschen anpassen können, wenn Sie möchten. Es wird nur einen Moment dauern.
Saginaw
Danke, nimm dir Zeit! Ich bin immer daran interessiert, genau zu wissen, welche Tools ich verwende.
Solalito
Ich habe versucht, den Code zu vereinfachen und mein Bestes getan, um ihn ausführlich zu erklären. Ich hoffe, dass der Code nach dem Lesen sinnvoller wird.
Saginaw
Ich hätte nie gehofft, eine so vollständige Antwort zu bekommen. Ich habe es gelesen, aber ich muss es sorgfältig durchgehen. +2 wenn ich könnte!
Solalito