Vim: Création de répertoires parents lors de l'enregistrement

122

Si j'invoque vim foo/bar/somefilemais foo/barn'existe pas déjà, Vim refuse de sauvegarder.

Je sais que je pourrais passer à un shell ou faire à :!mkdir foo/barpartir de Vim mais je suis paresseux :) Y a-t-il un moyen de faire en sorte que Vim fasse cela automatiquement quand il enregistre le tampon?

Damien Pollet
la source
13
mkdir -p %:hest mieux car il fonctionne pour les chemins imbriqués non existants, ne génère pas d'erreur lorsque le chemin existe déjà et %:hest le chemin complet du fichier actuel. Cependant, je ne sais pas comment l'invoquer automatiquement. Normalement, cela se fait avec des commandes automatiques, mais l' BufWritePreévénement ne semble pas fonctionner ici.
Konrad Rudolph
Définissez une fonction qui vérifie si le fichier existe et appelle la fonction intégrée writeet appelle le système mkdir -psur dirnamesinon, mappez -la à W... Je suis trop paresseux pour rechercher la syntaxe et la poster comme réponse ... Désolé
khachik
1
Je suppose que je pourrais combiner à la fois vos suggestions et votre alias :wpour mkdir -p %:hsuivre la construction:write
Damien Pollet

Réponses:

91
augroup BWCCreateDir
    autocmd!
    autocmd BufWritePre * if expand("<afile>")!~#'^\w\+:/' && !isdirectory(expand("%:h")) | execute "silent! !mkdir -p ".shellescape(expand('%:h'), 1) | redraw! | endif
augroup END

Notez les conditions: expand("<afile>")!~#'^\w\+:/'empêchera vim de créer des répertoires pour des fichiers comme ftp://*et !isdirectoryévitera un appel coûteux à mkdir.

Mise à jour : une solution légèrement meilleure qui vérifie également les buftype non vides et utilise mkdir():

function s:MkNonExDir(file, buf)
    if empty(getbufvar(a:buf, '&buftype')) && a:file!~#'\v^\w+\:\/'
        let dir=fnamemodify(a:file, ':h')
        if !isdirectory(dir)
            call mkdir(dir, 'p')
        endif
    endif
endfunction
augroup BWCCreateDir
    autocmd!
    autocmd BufWritePre * :call s:MkNonExDir(expand('<afile>'), +expand('<abuf>'))
augroup END
ZyX
la source
1
Merci, semble beaucoup plus propre que ce que je suppose - piraté :)
Damien Pollet
11
call mkdir(expand('%:h'), 'p')pourrait être plus portable.
Marius Gedminas
1
@MariusGedminas J'aimerais voir le code complet avec ce changement. Pourriez-vous s'il vous plaît l'afficher en tant que réponse / le télécharger quelque part?
kikito le
@kikito Voir ma réponse. Il a été édité quelques heures après ce commentaire.
ZyX du
@Zyx merci! J'ai fini par faire quelque chose d'un peu plus court (ma réponse est actuellement la dernière) mais cela semble bien faire l'affaire.
kikito le
20

Sur la base des suggestions de ma question, voici ce que j'ai obtenu:

function WriteCreatingDirs()
    execute ':silent !mkdir -p %:h'
    write
endfunction
command W call WriteCreatingDirs()

Ceci définit la :Wcommande. Idéalement, je voudrais avoir tous :w!, :wq, :wq!, :walletc. fonctionnent de la même, mais je ne sais pas s'il est possible sans fondamentalement les réimplémentant toutes avec des fonctions personnalisées.

Damien Pollet
la source
2
J'ai essayé cette même commande et à chaque fois que j'utilise :W, mon écran devient presque vide. J'essaierai de supprimer mes options précédentes et je donnerai des commentaires.
moebius_eye
6

J'ai ajouté ceci à mon ~ / .vimrc

cnoremap mk. !mkdir -p <c-r>=expand("%:h")<cr>/

Si j'ai besoin de créer le répertoire dans :mk.lequel je suis, je tape et il le remplace par "! Mkdir -p / chemin / vers / mon / fichier /" et me permet de revoir la commande avant de l'invoquer.

Asa Ayers
la source
3

Ce code vous demandera de créer le répertoire avec :w, ou simplement de le faire avec :w!:

augroup vimrc-auto-mkdir
  autocmd!
  autocmd BufWritePre * call s:auto_mkdir(expand('<afile>:p:h'), v:cmdbang)
  function! s:auto_mkdir(dir, force)
    if !isdirectory(a:dir)
          \   && (a:force
          \       || input("'" . a:dir . "' does not exist. Create? [y/N]") =~? '^y\%[es]$')
      call mkdir(iconv(a:dir, &encoding, &termencoding), 'p')
    endif
  endfunction
augroup END
Tom Hale
la source
1

Je pense que j'ai réussi à le faire en trois lignes, combinant ce que les autres disent sur cette réponse.

Cela semble faire l'affaire:

if has("autocmd")
  autocmd BufWritePre * :silent !mkdir -p %:p:h
end

Il tente de créer le dossier automatiquement lors de l'enregistrement d'un tampon. Si quelque chose de mauvais se produit (c'est-à-dire des problèmes d'autorisation), il se fermera et laissera l'écriture du fichier échouer.

Si quelqu'un voit des défauts évidents, veuillez poster un commentaire. Je ne connais pas très bien le vimscript.

EDIT: Notes grâce à ZyX

  • Cela ne fonctionnera pas si vos dossiers ont des espaces dessus (apparemment, ils ne sont pas correctement échappés ou quelque chose du genre)
  • Ou si vous créez des pseudo fichiers.
  • Ou si vous achetez votre vimrc.
  • Mais fils, c'est court.
kikito
la source
1
Ne jamais utiliser %dans de tels scripts. Vim ne va pas échapper à tous les symboles spéciaux: par exemple, si vous modifiez un fichier nommé , /mnt/windows/Documents and Settings/User/_vimrcvous finirez par avoir quatre nouveaux répertoires: /mnt/windows/Documents, ./and, ./Settingset ./Settings/User. Et, au fait, vous n'avez pas besoin d' :executeici.
ZyX
1
Il existe une system()fonction pour les appels shell complètement silencieux, mais vous n'avez pas besoin des deux :executeet %:p:h: :silent !mkdir -p %:p:hfonctionne exactement comme ce que vous avez écrit (bien que vous en ayez peut-être besoin :redraw!à la fin, dans ce cas :executeest pratique), mais il est préférable de l'utiliser call system('mkdir -p '.shellescape(expand('%:p:h'))). Utilisez :execute '!command' shellescape(arg, 1)(avec le deuxième argument de shellescape) si vous devez utiliser une frange au lieu de system(). Utilisez des franges si l'argument échappé contient des retours à la ligne.
ZyX
Et vous n'évitez pas d'autres problèmes que j'évite dans mon premier extrait de code: lancer le shell une fois de plus après chaque sourcing de vimrc (en supposant que vous tiriez les mises à jour de vimrc en faisant :source ~/.vimrc) (c'est ce à quoi augroupet autocmd!sont pour), vue supprimée après le lancement du shell commandes (c'est ce à quoi ça sert redraw!), la création de répertoires d'ordures en cas d'utilisation de pseudo-fichiers (dans le premier code extrait, il est vérifié en faisant correspondre uniquement le nom de fichier à un modèle, mais dans le second, je vérifie également &buftype) et un appel de shell inutile dans le répertoire de cas existe ( isdirectory()condition).
ZyX
1
@ZyX Merci pour vos commentaires. Je ne veux pas résoudre des problèmes que je n'ai pas. Je n'utilise jamais de caractères spéciaux (c'est-à-dire des espaces) sur mes dossiers, donc%: p: h me convient parfaitement. Je ne source jamais vimrc (je tue et rouvre vim à la place) et je ne sais même pas ce que sont les pseudofiles. redessiner! ne semble rien me faire du tout. Mais j'aime votre suggestion de supprimer execute pour tout raccourcir. À votre santé!
kikito du
1
Peu importe que vous ayez ou non des caractères spéciaux, c'est ce dont vous devriez vous soucier. Il y a trop de problèmes avec l' %expansion pour ne jamais suggérer de l'utiliser à personne. Les pseudo fichiers sont utilisés dans un grand nombre de plugins (par exemple fugitive ou my aurum), ils valent donc la peine d'être pris en charge. Le ressourcement de vimrc est également une pratique courante. Vous pouvez avoir tout ce que vous voulez dans le vimrc, mais ne le suggérez pas comme réponse. L'utilisation de la :silent! call mkdir(expand('%:p:h'), 'p')variante résout deux des points que j'ai mentionnés et le troisième, je n'ai pas mentionné: !mkdirne fonctionnera pas sur Windows.
ZyX