So entwerfen Sie einen Befehl in einem Plugin, der von vimrc aufgerufen werden kann

8

Ich arbeite an einem Plugin, mit dem Benutzer benutzerdefinierte Operatoren erstellen können. Die Operatoren würden einen VimL-Ausdruck auf das Bewegungs- oder Textobjekt anwenden, über das sich der Operator bewegt.

Saubere Benutzeroberfläche

Ich denke, die sauberste Schnittstelle zum Definieren der benutzerdefinierten Operatoren wäre die Verwendung eines Befehls. Ich habe einen Befehl definiert :MapExpress, der ungefähr so ​​aufgerufen wird:

:MapExpress cd '/* ' . v:val . ' */'

Dies würde einen Operator für den Normalmodus erstellen und eine Zuordnung für den visuellen Modus cd, die die Bewegung oder Auswahl in Kommentarbegrenzern im C-Stil umgibt.

Hier liegt natürlich ein Problem. Sie können keinen Befehl, der in einem Plugin definiert ist, aus Ihrer .vimrcDatei aufrufen .

Weniger als zufriedenstellende Problemumgehungen

Ich habe ein paar Problemumgehungen gefunden, mit denen ich nicht ganz zufrieden bin.

Der Benutzer autocmd VimEnter *ruft den Befehl auf

Während dies funktionieren würde, fügt es viel "mentalen Overhead" hinzu. Ich würde vermuten, dass viele Vim-Benutzer nicht genau wissen, wie das autocmdfunktioniert.

Der Benutzer erstellt eine Datei, in ~/.vim/after/plugin/der der Befehl aufgerufen wird

Auch dies würde funktionieren, hat jedoch den Nachteil, dass diese Konfiguration in einer eigenen Datei deaktiviert ist und leicht verloren geht und vergessen wird.

Ich verschiebe die Befehlsdefinitionen in das autoload/Verzeichnis meines Plugins , und der Benutzer ruft eine Methode auf, die das Laden der Datei und damit die zu definierenden Befehle auslöst

Das würde so aussehen:

call express#init()
MapExpress cd '/* ' . v:val . ' */'

Etwas besser, aber dies würde zu Verwirrung darüber führen, ob die express#init()Methode erforderlich ist, damit das Plugin überhaupt funktioniert.

Alternativen zur Verwendung eines Befehls

Ich habe auch einige Alternativen zur Verwendung eines Befehls zum Definieren des Operators in Betracht gezogen, aber jeder hat seine Vorbehalte.

Der Benutzer ruft eine Funktion zum Definieren der Operatoren auf

Das würde ungefähr so ​​aussehen:

call express#operator('cd', '"/* ".v:val." */"')

Dies ist nicht schrecklich, hat aber den Nachteil, dass ein zitierter Ausdruck erforderlich ist. Das kann ärgerlich sein, wenn Sie Anführungszeichen in Ihrem Ausdruck verwenden möchten.

Benutzer verwendet eine <expr>Zuordnung

So was:

nmap <expr> cd express#nmap('"/* ".v:val." */"')
xmap <expr> cd express#xmap('"/* ".v:val." */"')

Dies hat die gleiche mühsame Anforderung an zitierte Ausdrücke und verstößt auch gegen DRY, sofern Sie keine Variable einführen (nicht ideal).

Okay, na und?

Hier sind alle meine Ideen und warum ich keine davon mag. Bin ich zu pingelig? Gibt es eine bessere Lösung, an die ich nicht gedacht habe?

tommcdo
quelle
2
Die Autoload-Lösung klingt für mich nicht schlecht, insbesondere wenn Sie einen Funktionsnamen verwenden, der etwas spezifischer für das ist, was er tut. Es ist schwer, ein gutes Beispiel zu finden, ohne zu wissen, für welche anderen Funktionen Ihr Plugin den Autoload-Init nicht benötigt, aber vielleicht so etwas call express#initMapCommands()? Alles, was zusätzliche Zitate erfordert, ist eine super schlechte Idee.
Rich
Das ist es, worauf ich mich neige, denke ich.
Tommcdo
@tommcdo und heute habe ich gelernt, wie es <q-args>wirklich funktioniert. Danke
D. Ben Knoble

Antworten:

5

Mir scheint, Sie suchen :runtime. Sie können Befehle, die in Plugins definiert sind, perfekt von Ihrer .vimrc ausführen, aber Sie müssen vorher den Plugin-Namen (der ihn definiert) kennen.

" 1- Prepare the plugin-suite if it's managed through VAM/Vundle/NeoBundle/...
ActivateAddons FooBar

" 2- Be sure the plugin is loaded
runtime plugin/foobar.vim

" 3- And finally run (defensively) the command
if !exists(':FooBar')
   echo "Sorry some initializations will be ignored as foobar is nowhere to be found"
else
   FooBar abc
   FooBar 135468
endif

Hinweis: Im Gegensatz zur Autoload-Lösung stürzt bei diesem Ansatz die .vimrc-Beschaffung nicht ab. Wenn ein Autoload-Plugin nicht gefunden werden kann (durch Ausführung einer Funktion), gibt vim einen Fehler aus. :runtimebleibt stumm, wenn kein Plugin geladen werden kann.

Luc Hermitte
quelle
1
Dies ist eine Menge Arbeit für den Endbenutzer. Als Plugin-Entwickler versuche ich, den Aufwand für die Verwendung meines Plugins zu minimieren.
Tommcdo
Diese Lösung ist für eine .vimrc-Version vorgesehen, die Sie möglicherweise dort bereitstellen, wo Ihr Plugin nicht installiert ist. Für eine vereinfachte Installationsanleitung ist es nur :runtime. Verwenden Sie andernfalls eine Option, wie von romainl vorgeschlagen.
Luc Hermitte
Trotzdem muss der Benutzer wissen, wo sich die Datei befindet. Wenn sie einen Plugin-Manager verwenden, ist dies möglicherweise weniger offensichtlich als plugin/.
Tommcdo
Wenn sie einen Plugin-Manager verwenden, ist dieser immer vorhanden plugin/- wenn Sie Ihr "Projekt" / Repository auf diese Weise organisieren. Plugin-Manager aktualisieren die 'runtimepath'Option automatisch .
Luc Hermitte
Oh, guter Punkt.
Tommcdo
2

Eine Liste wäre IMO die einfachste Lösung:

let g:pluginname_my_operators = [
    ['cd', '/* ' . v:val . ' */'],
    ['cm', '<em>' . v:val . '</em>']
]

Überprüfen g:pluginname_my_operatorsSie, wann Sie Ihr Plugin initialisieren und :MapExpressggf. für jedes Element ausführen, während Ihre Benutzer den Befehl für die On-the-Fly-Definition weiterhin verwenden können.

romainl
quelle
2
Dies würde tatsächlich mehr Anführungszeichen erfordern ... v:valmuss Teil der Zeichenfolge sein, da es ausgewertet wird, wenn die Map-Interna aufgerufen werden. Ich denke, jede Lösung mit Zitaten ist weniger als ideal.
Tommcdo