Vim: создание родительских каталогов при сохранении

122

Если я вызываю, vim foo/bar/somefileно foo/barеще не существует, Vim отказывается сохранять.

Я знаю, что могу переключиться на оболочку или сделать это :!mkdir foo/barиз Vim, но я ленив :) Есть ли способ заставить Vim делать это автоматически при сохранении буфера?

Дэмиен Полле
источник
13
mkdir -p %:hлучше, потому что он работает для вложенных несуществующих путей, не вызывает ошибки, если путь уже существует, и %:hявляется полным путем к текущему файлу. Однако я не знаю, как вызвать это автоматически. Обычно это делается с помощью автоматических команд, но BufWritePre, похоже, событие здесь не работает.
Конрад Рудольф
Определите функцию , которая проверяет , является ли файл существует и вызывает встроенную команду , writeи призывает систему mkdir -pна dirnameиначе, сопоставьте его W... Я слишком ленив , чтобы искать синтаксис и разместить его в ответ ... Извините
Хачик
1
Думаю, я мог бы объединить ваши предложения и псевдоним :wс mkdir -p %:hпоследующим строительством:write
Дэмиен Поллет

Ответы:

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

Обратите внимание на условия: expand("<afile>")!~#'^\w\+:/'запретит vim создавать каталоги для файлов, таких как ftp://*и !isdirectoryпредотвратит дорогостоящий вызов mkdir.

Обновление : немного лучшее решение, которое также проверяет непустой тип буфера и использует 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
источник
1
Спасибо, кажется намного чище, чем то, что я думаю, взломанное :)
Дэмиен Поллет
11
call mkdir(expand('%:h'), 'p')может быть более портативным.
Мариус Гедминас
1
@MariusGedminas Я хотел бы увидеть полный код с этим изменением. Не могли бы вы выложить его как ответ / загрузить где-нибудь?
kikito
@kikito Смотри мой ответ. Он был отредактирован через несколько часов после этого комментария.
ZyX
@Zyx спасибо! Я закончил тем, что сделал что-то более короткое (мой ответ в настоящее время последний), но, похоже, это хорошо помогает.
kikito
20

Основываясь на предложениях по моему вопросу, вот что у меня получилось:

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

Это определяет :Wкоманду. В идеале я хотел бы иметь все :w!, :wq, :wq!, и :wallт.д. работаю так же, но я не уверен , если это возможно без в основном реализовав их все пользовательские функции.

Дэмиен Полле
источник
2
Я пробовал эту же команду, и каждый раз, когда я ее использую :W, мой экран становится почти пустым. Я попробую удалить свои предыдущие варианты и оставлю отзыв.
moebius_eye 03
6

Я добавил это в свой ~ / .vimrc

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

Если мне нужно создать каталог, в котором я нахожусь, я :mk.набираю текст, и он заменяет его на «! Mkdir -p / path / to / my / file /» и позволяет мне просмотреть команду перед ее вызовом.

Аса Айерс
источник
3

Этот код предложит вам создать каталог :wили просто сделать это с помощью :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
Том Хейл
источник
1

Думаю, мне удалось сделать это в трех строках, объединив то, что другие говорят по этому поводу.

Кажется, это помогает:

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

Он пытается автоматически создать папку при сохранении буфера. Если случится что-нибудь плохое (например, проблемы с правами доступа), он просто отключится и даст возможность записать файл не удастся.

Если кто-то видит очевидные недостатки, оставьте комментарий. Я не очень разбираюсь в vimscript.

РЕДАКТИРОВАТЬ: Заметки благодаря ZyX

  • Это не сработает, если в ваших папках есть пробелы (по-видимому, они неправильно экранированы или что-то в этом роде)
  • Или если вы делаете псевдофайлы.
  • Или если вы используете vimrc.
  • Но сынок, это недолго.
kikito
источник
1
Никогда не используйте %в таких скриптах. Vim не собирается бежать какие - либо специальные символы, например, если вы редактируете файл с именем /mnt/windows/Documents and Settings/User/_vimrcвы будете в конечном итоге, четыре новых каталогов: /mnt/windows/Documents, ./and, ./Settingsи ./Settings/User. И, кстати, вам :executeздесь не нужно .
ZyX
1
Есть system()функция для полностью бесшумных вызовов оболочки, но вам не нужны оба :executeи %:p:h: :silent !mkdir -p %:p:hработает точно так, как вы написали (хотя вам может понадобиться :redraw!в конце, в этом случае :executeпригодится), но лучше использовать call system('mkdir -p '.shellescape(expand('%:p:h'))). Используйте :execute '!command' shellescape(arg, 1)(со вторым аргументом shellescape), если вам нужно использовать челку вместо system(). Используйте челку, если экранированный аргумент содержит символы новой строки.
ZyX
И вы не избежите других проблем, которых я избегаю в моем первом фрагменте кода: запуск оболочки еще раз после каждого источника vimrc (предположим, что вы выполняете обновления vimrc, выполняя :source ~/.vimrc) (это то , для чего augroupи autocmd!для), отмененный вид после запуска оболочки команды (это то redraw!, для чего), создание каталогов мусора в случае использования псевдофайлов (в первом фрагменте кода он проверяется только сопоставлением имени файла с шаблоном, но во втором я также проверяю &buftype) и бесполезный вызов оболочки в каталоге case существует ( isdirectory()условие).
ZyX
1
@ZyX Спасибо за отзыв. Я не хочу решать проблемы, которых у меня нет. Я никогда не использую специальные символы (например, пробелы) в своих папках, поэтому%: p: h мне подходит. Я никогда не использую vimrc (вместо этого я убиваю и снова открываю vim) и даже не знаю, что такое псевдофайлы. перерисовывать! мне кажется, он вообще ничего не делает. Но мне нравится ваше предложение об удалении выполнения, чтобы все было короче. Ура!
kikito
1
Неважно, есть ли у вас специальные символы или нет, это то, о чем вы должны заботиться. С %расширением слишком много проблем, чтобы когда-либо предлагать его кому-либо использовать. Псевдо файлы используются в большом количестве плагинов (например, fugitive или my aurum), поэтому о них стоит позаботиться. Ресурсы vimrc также являются распространенной практикой. Вы можете иметь все, что захотите, в vimrc, только не предлагайте это в качестве ответа. Использование :silent! call mkdir(expand('%:p:h'), 'p')варианта решает два пункта, о которых я упомянул, а третий, который я не упомянул: !mkdirне будет работать с окнами.
ZyX