Soll ich für eine automatische Aktualisierung in einem ftplugin den Mustervergleich oder <buffer> verwenden?

14

Ich habe eine AutocMD für TeX- und Markdown-Dateien, um die Datei automatisch zu speichern. Nichts Ungewöhnliches:

autocmd CursorHold *.tex,*.md w

Da jedoch die benutzerdefinierten Einstellungen für diese Dateien zunahmen, habe ich sie aufgeteilt in ftplugin/tex.vimund ftplugin/markdown.vim:

" ftplugin/tex.vim
autocmd CursorHold *.tex w
" ftplugin/markdown.vim
autocmd CursorHold *.md w

Jetzt werden diese Dateien nur für die entsprechenden Dateien bereitgestellt, sodass der Mustervergleich überflüssig ist. Anscheinend kann autocmds pufferlokal sein. Von :h autocmd-buffer-local:

Buffer-local autocommands are attached to a specific buffer.  They are useful
if the buffer does not have a name and when the name does not match a specific
pattern.  But it also means they must be explicitly added to each buffer.

Instead of a pattern buffer-local autocommands use one of these forms:
        <buffer>        current buffer
        <buffer=99>     buffer number 99
        <buffer=abuf>   using <abuf> (only when executing autocommands)
                        <abuf>

Das scheint für eine solche Verwendung gedacht zu sein. Jetzt können beide ftplugin/tex.vimund ftplugin/markdown.vimhaben:

autocmd CursorHold <buffer> w

Ich bin nicht wirklich besorgt über die tatsächliche Erweiterung, solange der Dateityp korrekt ist. Dies erspart mir die Sorge über *.mdund *.markdownund alle anderen Erweiterungen, die für Markdown gültig sind.

Ist diese Verwendung <buffer>korrekt? Gibt es irgendwelche Fallstricke, auf die ich achten sollte? Wird es chaotisch, wenn ich einen Puffer lösche und einen anderen öffne (hoffentlich werden die Zahlen nicht kollidieren, aber ...)?

muru
quelle
1
Ich bin mir ziemlich sicher, dass beim Löschen eines Puffers auch alle pufferlokalen AutoCMDS gelöscht werden.
Tumbler41
@ Tumbler41 in der Tat. Es heißt in der Hilfe, ein paar Absätze weiter unten.
muru
1
Nicht genau das, was Sie gefragt haben, aber up(kurz für :update) wäre besser als win Ihrem AutocMD (vermeiden Sie unnötige Schreibvorgänge).
MMontu
@mMontu Schön. Es löst sogar ein Problem, das ich hatte, als die AutocMD für eine Datei aktiviert wurde, die ich aus dem Git-Verlauf untersuchte. Der Puffer war schreibgeschützt und wfehlgeschlagen. Wiederholt. :uptut in diesem Fall nichts. :)
muru
Froh, dass es Ihnen gefallen hat :) Übrigens, Sie könnten auch die Option 'autowrite' nützlich finden (je nach Motivation könnten Sie die autocmds fallen lassen).
MMontu

Antworten:

11

Ist diese Verwendung von <buffer> korrekt?

Ich denke, es ist richtig, aber Sie müssen es nur in eine Augroup einschließen und diese löschen, um sicherzustellen, dass die autocmd nicht jedes Mal dupliziert wird, wenn Sie einen Befehl ausführen, der denselben Puffer neu lädt.

Wie Sie erklärt haben, <buffer>können Sie sich mit dem speziellen Muster auf den in der Datei implementierten Mechanismus zur Erkennung von Dateitypen verlassen $VIMRUNTIME/filetype.vim.

In dieser Datei finden Sie die in Vim integrierten Autocmds, die für die Einstellung des richtigen Dateityps für einen bestimmten Puffer verantwortlich sind. Zum Beispiel für Abschriften:

" Markdown
au BufNewFile,BufRead *.markdown,*.mdown,*.mkd,*.mkdn,*.mdwn,*.md  setf markdown

In Ihrem Dateityp-Plugin können Sie für jede von Ihnen installierte Autocmd die gleichen Muster kopieren. So speichern Sie beispielsweise den Puffer automatisch, wenn sich der Cursor einige Sekunden lang nicht bewegt hat:

au CursorHold *.markdown,*.mdown,*.mkd,*.mkdn,*.mdwn,*.md  update

Aber <buffer>ist viel weniger ausführlich:

au CursorHold <buffer> update

Außerdem, wenn eines Tages eine andere Erweiterung gültig ist und $VIMRUNTIME/filetype.vimaktualisiert wird, um sie einzuschließen, werden Ihre AutocMDS nicht informiert. Und Sie müssen alle Muster in Ihren Dateityp-Plugins aktualisieren.


Wird es chaotisch, wenn ich einen Puffer lösche und einen anderen öffne (hoffentlich werden die Zahlen nicht kollidieren, aber ...)?

Ich bin nicht sicher, aber ich glaube nicht, dass Vim die Puffernummer eines gelöschten Puffers wiederverwenden kann. Ich konnte den relevanten Abschnitt in der Hilfe nicht finden, aber ich habe diesen Absatz von vim.wikia.com gefunden :

Nein. Vim verwendet die Puffernummer eines gelöschten Puffers nicht für einen neuen Puffer. Vim weist immer die nächste fortlaufende Nummer für einen neuen Puffer zu.

Außerdem werden, wie @ Tumbler41 erklärte, beim Löschen eines Puffers dessen Autocmds entfernt. Von :h autocmd-buflocal:

Wenn ein Puffer gelöscht wird, gehen natürlich auch seine pufferlokalen Autobefehle verloren.

Wenn Sie sich selbst überprüfen möchten, können Sie dies tun, indem Sie die Ausführlichkeitsstufe von Vim auf 6 erhöhen. Sie können dies vorübergehend für nur einen Befehl tun, indem Sie den :verboseModifikator verwenden. So können Sie in Ihrem Abschriftenpuffer Folgendes ausführen:

:6verbose bwipe

Wenn Sie dann die Nachrichten von Vim überprüfen:

:messages

Sie sollten eine Zeile sehen, die so aussieht:

auto-removing autocommand: CursorHold <buffer=42>

Wo 42war die Nummer Ihres Abschriftenpuffers?


Gibt es irgendwelche Fallstricke, auf die ich achten sollte?

Es gibt drei Situationen, die ich als Fallstricke betrachten würde und die das spezielle Muster betreffen <buffer>. In zwei Fällen <buffer>kann es sich um ein Problem handeln, in den anderen um eine Lösung.

Fallstricke 1

Zunächst sollten Sie vorsichtig sein, wie Sie die Augroups Ihrer pufferlokalen Autocmds löschen. Sie müssen mit diesem Snippet vertraut sein:

augroup your_group_name
    autocmd!
    autocmd Event pattern command
augroup END

Sie könnten also versucht sein, es für Ihre pufferlokalen autocmds zu verwenden, die nicht modifiziert wurden, wie folgt:

augroup my_markdown
    autocmd!
    autocmd CursorHold <buffer> update
augroup END

Dies hat jedoch einen unerwünschten Effekt. AWenn Sie einen Abschriftenpuffer zum ersten Mal laden, rufen wir ihn auf , seine Autocmd wird korrekt installiert. Wenn Sie dann neu laden A, wird die autocmd (wegen autocmd!) gelöscht und neu installiert. Die Augroup verhindert also korrekterweise das Duplizieren der Autocmd.

Angenommen, Sie laden einen zweiten Abschriftenpuffer, nennen wir ihn Bin einem zweiten Fenster. ALLE autocmds der Augroup werden gelöscht: das ist die autocmd von Aund die von B. Dann wird eine SINGLE-Autocmd für installiert B.

Wenn Sie also eine Änderung vornehmen Bund einige Sekunden warten, CursorHoldbis sie ausgelöst wird, wird sie automatisch gespeichert. Wenn Sie jedoch zurückkehren Aund dasselbe tun, wird der Puffer nicht gespeichert. Dies liegt daran, dass beim letzten Laden eines Markdown-Puffers ein Ungleichgewicht zwischen dem Entfernten und dem Hinzugefügten aufgetreten ist. Sie haben mehr entfernt, als Sie hinzugefügt haben.

Die Lösung besteht darin, nicht ALLE AutocMDS zu entfernen, sondern nur die des aktuellen Puffers, indem das spezielle Muster <buffer>an :autocmd!folgende Adresse übergeben wird :

augroup my_markdown
    autocmd! CursorHold <buffer>
    autocmd CursorHold <buffer> update
augroup END

Beachten Sie, dass Sie CursorHoldjedes Ereignis in der Zeile, in der autocmds entfernt werden , durch einen Stern ersetzen können:

augroup my_markdown
    autocmd! * <buffer>
    autocmd CursorHold <buffer> update
augroup END

Auf diese Weise müssen Sie nicht alle Ereignisse angeben, die Ihre Autocmds abhören, wenn Sie die Augroup löschen möchten.


Fallstricke 2

Es gibt noch eine weitere Falle, aber diesmal <buffer>ist es nicht das Problem, sondern die Lösung.

Wenn Sie eine lokale Option in ein Dateityp-Plugin einfügen, geschieht dies wahrscheinlich folgendermaßen:

setlocal option1=value
setlocal option2

Dies funktioniert erwartungsgemäß für pufferlokale Optionen, jedoch nicht immer für fensterlokale Optionen. Um das Problem zu veranschaulichen, können Sie das folgende Experiment versuchen. Erstellen Sie die Datei ~/.vim/after/ftdetect/potion.vimund schreiben Sie darin:

autocmd BufNewFile,BufRead *.pn setfiletype potion

Diese Datei legt automatisch den Dateityp potionfür jede Datei mit der Erweiterung fest .pn. Sie müssen es nicht in eine Augroup einschließen, da Vim es für diese bestimmte Art von Datei automatisch ausführt (siehe :h ftdetect).

Wenn die Zwischenverzeichnisse auf Ihrem System nicht vorhanden sind, können Sie sie erstellen.

Als nächstes erstellen Sie das Dateityp-Plugin ~/.vim/after/ftplugin/potion.vimund schreiben darin:

setlocal list

Standardmäßig potionbewirkt diese Einstellung in einer Datei, dass die Tabulatorzeichen als ^Iund das Zeilenende als angezeigt werden $.

Erstellen Sie nun ein Minimum vimrc. innen /tmp/vimrcschreiben:

filetype plugin on

... um die Dateityp-Plugins zu aktivieren.

Erstellen Sie außerdem eine Trankdatei /tmp/pn.pnund eine zufällige Datei /tmp/file. Schreiben Sie in die Trankdatei Folgendes:

foo
bar
baz

Schreiben Sie in die Zufallsdatei den Pfad zur Trankdatei /tmp/pn.pn:

/tmp/pn.pn

Starten Sie nun Vim mit einem Minimum an Initialisierungen, indem Sie nur die Quelle auswählen vimrcund beide Dateien in vertikalen Ansichtsfenstern öffnen:

$ vim -Nu /tmp/vimrc -O /tmp/pn.pn /tmp/file

Sie sollten 2 vertikale Ansichtsfenster sehen. Die Zaubertrank-Datei auf der linken Seite zeigt das Ende von Zeilen mit Dollarzeichen an, die Zufallsdatei auf der rechten Seite zeigt sie überhaupt nicht an.

Setzen Sie den Fokus auf die Zufallsdatei und drücken Sie gf, um die Trankdatei anzuzeigen, deren Pfad sich unter dem Cursor befindet. Sie sehen jetzt den gleichen Trankpuffer im rechten Ansichtsfenster, aber dieses Mal wird das Zeilenende nicht mit Dollarzeichen angezeigt. Und wenn Sie tippen :setlocal list?, sollte Vim antworten mit nolist:

Bildbeschreibung hier eingeben

Die ganze Kette von Ereignissen:

BufRead event → set 'filetype' option → load filetype plugins

... ist nicht aufgetreten, weil der erste von ihnen, BufReadnicht aufgetreten ist, als Sie gedrückt haben gf. Der Puffer wurde bereits geladen.

Es mag unerwartet erscheinen, weil Sie beim Hinzufügen setlocal listin Ihrem Potion-Dateityp-Plugin möglicherweise gedacht haben, dass es die 'list'Option in jedem Fenster aktivieren würde, in dem ein Potion-Puffer angezeigt wird .

Das Problem ist nicht spezifisch für diesen neuen potionDateityp. Sie können es auch mit einer markdownDatei erleben .

Es ist auch nicht spezifisch für die 'list'Option. Sie können es mit anderen Fenstern-lokalen Einstellungen erfahren, wie 'conceallevel', 'foldmethod', 'foldexpr', 'foldtitle', ...

Es ist auch nicht spezifisch für den gfBefehl. Sie können es mit anderen Befehlen erleben, die den im aktuellen Fenster angezeigten Puffer ändern können: globale Markierung, C-o(in der fensterlokalen Sprungliste rückwärts bewegen),, :b {buffer_number}...

Zusammenfassend werden die fensterlokalen Optionen nur dann korrekt festgelegt, wenn:

  • Die Datei wurde während der aktuellen Vim-Sitzung nicht gelesen (weil BufReadsie gefeuert werden muss)
  • Die Datei wird in einem Fenster angezeigt, in dem die fensterlokalen Optionen bereits korrekt festgelegt wurden
  • Das neue Fenster wird mit folgendem Befehl erstellt: :split(In diesem Fall sollten die fensterlokalen Optionen von dem Fenster übernommen werden, in dem der Befehl ausgeführt wurde.)

Andernfalls sind die fensterlokalen Optionen möglicherweise nicht richtig eingestellt.

Eine mögliche Lösung wäre, sie nicht direkt über ein Dateityp-Plugin festzulegen, sondern über eine in diesem installierte AutocMD, die sie abhört BufWinEnter. Dieses Ereignis sollte jedes Mal ausgelöst werden, wenn ein Puffer in einem Fenster angezeigt wird.

Also zum Beispiel, anstatt dies zu schreiben:

setlocal list

Du würdest folgendes schreiben:

augroup my_potion
    au! * <buffer>
    au BufWinEnter <buffer> setlocal list
augroup END

Und hier finden Sie wieder das spezielle Muster <buffer>.

Bildbeschreibung hier eingeben


Fallstricke 3

Wenn Sie den Dateityp Ihres Puffers ändern, bleiben die AutocMDS erhalten. Wenn Sie sie entfernen möchten, müssen Sie Folgendes konfigurieren b:undo_ftplugin(siehe :h undo_ftplugin) und den folgenden Befehl einfügen:

exe 'au! my_markdown * <buffer>'

Versuchen Sie jedoch nicht, die Augroup selbst zu entfernen, da es noch einige Abschriftenpuffer geben kann, in denen sich Autocmds befinden.

FWIW, dies ist das UltiSnips-Snippet, das ich verwende, um Folgendes festzulegen b:undo_ftplugin:

snippet undo "undo ftplugin settings" bm
" teardown {{{1

let b:undo_ftplugin =         get(b:, 'undo_ftplugin', '')
\                     .(empty(get(b:, 'undo_ftplugin', '')) ? '' : '|')
\                     ."${1:
\                          setl ${2:option}<}${3:
\                        | exe '${4:n}unmap <buffer> ${5:lhs}'}${6:
\                        | exe 'au! ${7:group_name} * <buffer>'}${8:
\                        | unlet! b:${9:variable}}${10:
\                        | delcommand ${11:Cmd}}
\                      "
$0
endsnippet

Und hier ist ein Beispiel für den Wert, den ich habe ~/.vim/after/ftplugin/awk.vim:

let b:undo_ftplugin =         get(b:, 'undo_ftplugin', '')
                    \ .(empty(get(b:, 'undo_ftplugin', '')) ? '' : '|')
                    \ ."
                    \   setl cms< cocu< cole< fdm< fdt< tw<
                    \|  exe 'nunmap <buffer> K'
                    \|  exe 'au! my_awk * <buffer>'
                    \|  exe 'au! my_awk_format * <buffer>'
                    \  "

Als Randnotiz verstehe ich, warum Sie die Frage gestellt haben, denn als ich nach allen Zeilen gesucht habe, in denen das spezielle Muster <buffer>in den Standarddateien von Vim verwendet wurde:

:vim /au\%[tocmd!].\{-}<buffer>/ $VIMRUNTIME/**/*

Ich habe nur 9 Übereinstimmungen gefunden (möglicherweise finden Sie mehr oder weniger Übereinstimmungen, ich verwende Vim Version 8.0 mit Patches bis zu 134). Und unter den 9 Übereinstimmungen sind 7 in der Dokumentation, nur 2 wurden tatsächlich beschafft. Sie sollten sie in $ VIMRUNTIME / syntax / dircolors.vim finden :

autocmd CursorMoved,CursorMovedI <buffer> call s:preview_color('.')
autocmd CursorHold,CursorHoldI   <buffer> call s:reset_colors()

Ich weiß nicht , ob es ein Problem verursachen kann, aber sie sind nicht in einem augroup, die jedes Mal , bedeutet , dass Sie einen Puffer , dessen Dateityp neu zu laden ist dircolors(es passiert , wenn Sie eine Datei mit dem Namen bearbeiten .dircolors, .dir_colorsoder dessen Pfad endet mit /etc/DIR_COLORS), Das Syntax-Plugin fügt eine neue pufferlokale Autocmd hinzu.

Sie können dies folgendermaßen überprüfen:

$ vim ~/.dir_colors
:au * <buffer>

Der letzte Befehl sollte dies anzeigen:

CursorHold
    <buffer=1>
              call s:reset_colors()
CursorHoldI
    <buffer=1>
              call s:reset_colors()
CursorMoved
    <buffer=1>
              call s:preview_color('.')
CursorMovedI
    <buffer=1>
              call s:preview_color('.')

Laden Sie nun den Puffer neu und fragen Sie erneut nach den pufferlokalen Autocmds für den aktuellen Puffer:

:e
:au * <buffer>

Dieses Mal werden Sie sehen:

CursorHold
    <buffer=1>
              call s:reset_colors()
              call s:reset_colors()
CursorHoldI
    <buffer=1>
              call s:reset_colors()
              call s:reset_colors()
CursorMoved
    <buffer=1>
              call s:preview_color('.')
              call s:preview_color('.')
CursorMovedI
    <buffer=1>
              call s:preview_color('.')
              call s:preview_color('.')

Nach jedem der Datei neu zu laden, s:reset_colors()und s:preview_color('.')wird ein weiteres Mal aufgerufen werden, jedes Mal , wenn einer der Veranstaltung CursorHold, CursorHoldI, CursorMoved, CursorMovedIwird gefeuert.

Dies ist wahrscheinlich kein großes Problem, da dircolorsich auch nach mehrmaligem Neuladen einer Datei keine merkliche Verlangsamung oder ein unerwartetes Verhalten von Vim feststellen konnte.

Wenn es ein Problem für Sie ist, können Sie sich an den Betreuer des Syntax-Plugins wenden. Wenn Sie jedoch in der Zwischenzeit die Duplizierung von autocmds verhindern möchten, können Sie dircolorsmithilfe der Datei ein eigenes Syntax-Plugin für Dateien erstellen ~/.vim/syntax/dircolors.vim. Darin würden Sie den Inhalt des ursprünglichen Syntax-Plugins importieren:

$ vim ~/.vim/syntax/dircolors.vim
:r $VIMRUNTIME/syntax/dircolors.vim

In letzterem Fall würden Sie die autocmds einfach in eine Augroup einschließen, die Sie löschen würden. Also würden Sie diese Zeilen ersetzen:

autocmd CursorMoved,CursorMovedI <buffer> call s:preview_color('.')
autocmd CursorHold,CursorHoldI   <buffer> call s:reset_colors()

... mit diesen:

augroup my_dircolors_syntax
    autocmd! * <buffer>
    autocmd CursorMoved,CursorMovedI <buffer> call s:preview_color('.')
    autocmd CursorHold,CursorHoldI   <buffer> call s:reset_colors()
augroup END

Beachten Sie, dass, wenn Sie Ihr dircolorsSyntax-Plugin mit der Datei erstellt haben ~/.vim/after/syntax/dircolors.vim, dies nicht funktioniert, da das Standardsyntax-Plugin zuvor verwendet wurde. Bei Verwendung von ~/.vim/syntax/dircolors.vimwird Ihr Syntax-Plug-In vor dem Standard-Plug-In bereitgestellt und es wird die pufferlokale Variable festgelegt b:current_syntax, die verhindert, dass das Standardsyntax-Plug-In bereitgestellt wird, da es diesen Schutz enthält:

if exists("b:current_syntax")
    finish
endif

Die allgemeine Regel scheint zu lauten: Verwenden Sie die Verzeichnisse ~/.vim/ftpluginund ~/.vim/syntax, um ein benutzerdefiniertes Dateityp- / Syntax-Plugin zu erstellen und zu verhindern, dass das nächste Plugin (für denselben Dateityp) im Laufzeitpfad (einschließlich der Standard-Plugins) bezogen wird. Und Gebrauch ~/.vim/after/ftplugin, ~/.vim/after/syntaxnicht zu verhindern , dass andere Plugins von Quellen zu werden, aber nur das letzte Wort haben , auf dem Wert von einigen Einstellungen.

user852573
quelle
1
Ich wünschte, ich könnte das härter tun.
Rich
3
@Rich Ich habe das härter für dich abgestimmt. Meine einzige Beschwerde ist das Fehlen einer Zusammenfassung "tl; dr". Seiten mit winzigen Texten, die für das Verständnis von entscheidender Bedeutung sind, tun meiner alternden Seele weh, durchzuwaten. Das Ersetzen von autocmd!mit autocmd! CursorHold <buffer>in augroupBlöcken ist ein besonders kritischer Punkt - und sollte im Vorfeld hervorgehoben werden. Trotzdem ... das ist eine erstaunliche Investition von Zeit, Mühe und blutigen Tränen.
Cecil Curry