Vim по повній: Snippets та шаблони файлів

Зміст
  1. Введення (vim_lib)
  2. Менеджер плагінів без фатальних вад (vim_lib, vim_plugmanager)
  3. Рівень проекту та файлова система (vim_prj, nerdtree)
  4. Snippets та шаблони файлів (UltiSnips, vim_template)
  5. Компіляція і виконання чого завгодно (vim_start)
  6. Робота з Git (vim_git)
  7. Деплой (vim_deploy)
  8. Тестування за допомогою xUnit (vim_unittest)
  9. Бібліотека, на якій все тримається (vim_lib)
  10. Інші корисні плагіни
Чи варто розповідати вам, як повторне використання коду та проектних рішень полегшує життя програміста? Але всі ми можемо використовувати повторно? Дуже часто я стикаюся в моїх проектах з завданнями, які вимагають копі-пасти коду і уникнути цього неможливо. До категорії цього «повторюваного» коду відносяться всі структури використовуваного ЯП, багато класи проекту і тест-кейси. На щастя давно винайдено рішення, що дозволяє працювати з таким кодом швидше і якісніше.

Рівні повторюваного коду
Неминуче повторюється код можна розділити за масштабом на дві групи:
  1. Структури ЯП або блоки коду — на приклад структури for, if/else, while, class, а так само готові рішення, які неможливо не копі-пастить
  2. Цілі файли — на приклад файли модульних тестів, документація, класи сутностей
Для кожної групи використовуються різні рішення, що дозволяють копі-пастить код просто і швидко. Природно ці рішення вже реалізовані в редакторі Vim і я пропоную їх спробувати.

Snippets
Сніппети це іменовані уривки коду (та чого завгодно), які можна швидко вставити ввівши ім'я фрагменту і натиснувши «гарячу клавішу». На приклад ви хочете вставити в клас метод getter, який повертає властивість login. З використанням фрагментів вам достатньо набрати слово get в місці, де буде розташовуватися метод, а потім натиснути клавішу Tab. В результаті буде вставлений шаблон getter методу, а курсор буде поміщений в тілі методу так, щоб ви могли вказати ім'я повертається властивості.
Приклад
public function get(){
return $this->_;
}


Погляньте на приклад. Курсор буде поміщений на місце символу нижнього підкреслення (_). Після введення слова login, ім'я методу буде змінено автоматично.
Приклад
public function getLogin(){
return $this->login;
}


Зручно, не чи правда? А адже плагін UltiSnips дозволяє реалізовувати шаблони для завдань будь-якої складності, будь то структури мови чи цілі класи. Я вже досить давно використовую цей плагін для реалізації фрагментів в Vim.

Приміром, в одному з моїх проектів, в якому важлива висока безпека, я використовую захищене програмування. Для цього проекту я реалізував кілька фрагментів, дозволяють швидко перевірити вхідні параметри методів. Так, набравши assertpositive я отримую шаблон виду:
assert('is_int(_) && _ > 0');

Тобто перевірка вхідного параметра на приналежність до типу int і значення більше нуля.

Іншим прикладом є мої сніппети для Хабра. Ви знали, що з допомогою плагіна для Firefox, який називається Vimperator, можна писати статті на Хабр прямо з Vim? Для цього достатньо відкрити вікно редагування статті і помістивши курсор в textarea натиснути комбінацію Ctrl+i. Після цього відкриється редактор Vim і все що ви в ньому напишіть після збереження (:wq) буде скопійовано в цей textarea. Круто? А як на рахунок використання фрагментів для вставки html-тегів? Так, для додавання habracut достатньо набрати cut і натиснути Tab, і ви отримаєте готовий тег. Скажете, що фрагменти вже реалізовані прямо в меню:

Але ж ви використовуєте Vim, а значить комп'ютерна миша для вас ворог номер один!

Ви полюбите фрагменти, якщо вам доводиться писати на різних МП. В цьому випадку вам не доведеться запам'ятовувати, як саме пишуться ті чи інші структури в конкретному мовою, а достатньо реалізувати фрагменти зі схожими іменами. На приклад, я завжди забуваю як реалізуються ті чи інші структури на Bash, тому я просто використовую такі фрагменти, як if, for, foreach і т. д.

Я не хочу описувати в цій статті як писати фрагменти під UltiSnips, так як офіційна документація зробить це набагато краще за мене, наведу лише невеликий приклад оголошення фрагменту для створення методу getter:
Приклад
snippet get "public function get ..." b
/**
* $2.
* @return ${3:mixed}
*/
public function get${1/\w+\s*/\u$0/}(){
return $this->$1;
}$0
endsnippet


Шаблони
Як-то раз я звернув увагу на те, як багато часу доводиться витрачати мені на створення документації для моїх плагінів Vim. Справа в тому, що файли документації мають певну структуру:
Приклад
имяФайла.txt Для Vim версії 7.0. Ім'я плагіна

КЕРІВНИЦТВО ПО `Ім'я плагіна` 

1. Опис имяПлагина-description
2. Залежно имяПлагина-requirements
3. Установка имяПлагина-install
4. Використання имяПлагина-use
5. Опції имяПлагина-opt
6. Команди имяПлагина-commands
7. Меню имяПлагина-menu
8. Події имяПлагина-events

================================================================================ 
1. Опис имяПлагина-description 

Опис плагіна ...

================================================================================ 
2. Залежно имяПлагина-requirements

Даний плагін працює з редактором Vim версії 7.0 або старше.

vim_lib https://github.com/Bashka/vim_lib
Плагін реалізований з використанням класу vim_lib#sys#Plugin#, а так
ж використовує деякі компоненти цієї бібліотеки.


Зазвичай документація до плагіну Vim включає близько сотні рядків, з яких близько шістдесяти — це шаблонні дані, такі як шапка, зміст і розділи. Використовувати для створення документації фрагменти вимагало б від мене повторюваних дій, чого мені не хотілося. Тоді я вирішив написати плагін vim_template. Цей плагін заповнює порожній файл деякими даними, створюючи шаблон і позбавляючи мене необхідності повторювати однотипні операції для підготовки файлу до роботи. Шаблони файлів можна дуже гнучко налаштувати за допомогою VimLanguage, що дозволяє створювати файли з дуже складною структурою (на приклад автоматично додавати namespace в початок файлу з урахуванням розташування класу у файловій системі). Іншою особливістю плагіна, є можливість визначити контекст шаблону. На приклад, можна створити шаблон лише для файлів тест-кейсів або для файлів, розміщених у каталозі ~/.vim/bundle/, а завантаження рівня проекту, про яку я вже говорив у попередніх статтях, дозволяє визначити шаблони тільки для конкретного проекту.

Плагін vim_template влаштований досить просто. При відкритті деякого файлу, він послідовно шукає для нього файл-шаблон у каталогах ./.vim/templates, ~/.vim/templates $VIMRUNTIME/templates), вміст якого буде скомпільовано і вставлене в цей файл. Логіка пошуку файлу-шаблону дозволяє не тільки відштовхуватися від імені файлу, але і враховувати його розташування у файловій системі. Ось кілька прикладів:
  • Якщо є шаблон ___.php, то він буде застосовуватися до всіх файлів з цим розширенням
  • Якщо є шаблон ___Test.php, то він буде застосовуватися до всіх тест-кейсів для PHP класів перекриваючи попередній
  • Якщо є шаблон autoload/___.vim, то він буде застосовуватися до всіх файлів з розширенням vim, які розташовані в каталозі autoload
  • Якщо шаблон розташований відноситься до проекту (розташований в каталозі ./.vim/templates), то він буде використовуватися тільки в цьому проекті
Зручно і гнучко, не чи правда? Ось кілька прикладів з реальних проектів:
  • Шаблони для документації плагінів Vim
  • Шаблони для файлів плагінів Vim, розташованих у каталозі plugin autoload (у них зазвичай схожі структури)
  • Шаблони для тест-кейсів
  • Шаблони для сутностей і Mapper'ів


Як вже було сказано, вміст шаблонів не просто копіюється в новий файл, але попередньо складений, завдяки чому можна вставити в новий файл дані, які можуть бути отримані тільки під час вставки шаблону, на приклад:
  • Інформація про автора, ліцензії, дату створення
  • Ім'я класу, що отримується з імені файлу
  • namespace класу, одержуваний з розташування файлу у файловій системі
  • Константа, значення якої обчислюється на підставі імені файлу або розташування у файловій системі
Робиться все це за допомогою спеціальних маркерів, які замінюються на значення під час вставки шаблону (вони вставляються в шаблон у вигляді наступної запису <+ім'я+>). Ці маркери можуть бути подані в словниках vim_template#keywords vim_prj#opt. Особисто я використовую такі маркери, як: author, email, license і т. д. Крім перерахованих вами маркерів, доступні так само зумовлені:
  • date — поточна дата
  • time — поточний час
  • datetime — дата і час
  • file — ім'я поточного файлу
  • ftype — розширення поточного файлу
  • fname — ім'я поточного файлу без розширення
  • dir — адреса каталогу в якому розташований файл, щодо кореня проекту
  • namespace — адреса файлу, щодо кореня проекту
Але на зумовлених маркерах далеко не заїдеш, тому можливе використання «виконуваних маркерів» (вони укладені в косі лапки). Це блоки коду на мові VimLanguage, які будуть виконані при вставці шаблону. З їх допомогою можна перетворювати маркери (на приклад перетворити маркер dir простір імен поточного класу замінивши символ слеша на точку), обчислювати нові маркери і т. д. Всі стандартні маркери тут доступні у вигляді локальних змінних Vim (l:date, l:dir, l:file тощо).

В якості прикладу наведу шаблон для класу Mapper, використовуваного в моєму поточному проекті:
Приклад
<?php
/**
* <++>
*
* @author <+author+>
*/
class `substitute(strpart(l:dir, strlen('application/')), '/', '_', 'g')`_<+fname+> extends My_Db_Mapper{
/**
* @see My_Db_Mapper::getDefaultTable
*/
public function getDefaultTable(){
$tableName = "tolower(substitute(strpart(l:dir, strlen("application/db/")), "/", "_", "g") . "_" . strpart(l:fname, 0, strlen(l:fname) - strlen("Mapper")))`;
$table = new My_Db_Table([
'name' => $tableName,
]);
$table->_linkedCacheTags = [$tableName];

return $table;
}

/**
* @see My_Db_Mapper::getStateEntity
*/
protected function getStateEntity(\My_Db_Entity $entity){
return [
"=> $entity->(),
];
}

/**
* @see My_Db_Mapper::setStateEntity
*/
protected function setStateEntity(array $state, \My_Db_Entity $entity){
$entity->($state["]);
}

/**
* @see My_Db_Mapper::getEmptyEntity
*/
protected function getEmptyEntity(){
return new `substitute(strpart(l:dir, strlen('application/')), '/', '_', 'g')`_`strpart(l:fname, 0, strlen(l:fname) - strlen('Mapper'))`;
}
}


При створенні нового файлу, на приклад ClientMapper.php, плагін заповнить його наступним чином:
Приклад
<?php
/**
* 
*
* @author Artur Sh. Mamedbekov
*/
class Db_ClientMapper extends My_Db_Mapper{
/**
* @see My_Db_Mapper::getDefaultTable
*/
public function getDefaultTable(){
$tableName = '_client';
$table = new My_Db_Table([
'name' => $tableName,
]);
$table->_linkedCacheTags = [$tableName];

return $table;
}

/**
* @see My_Db_Mapper::getStateEntity
*/
protected function getStateEntity(\My_Db_Entity $entity){
return [
"=> $entity->(),
];
}

/**
* @see My_Db_Mapper::setStateEntity
*/
protected function setStateEntity(array $state, \My_Db_Entity $entity){
$entity->($state["]);
}

/**
* @see My_Db_Mapper::getEmptyEntity
*/
protected function getEmptyEntity(){
return new Db_Client;
}
}


Зверніть увагу, що простір імен для класу обчислюється автоматично. Залишається тільки трохи доповнити клас приватними рішеннями і він готовий до роботи.

Поки всі
Якщо вам доводиться повторювати одну і ту ж структуру під час роботи з Vim, спробуйте реалізувати відповідний фрагмент або шаблонний файл, це заощадить вам багато часу. Звичайно, доведеться вивчити VimLanguage і мова написання фрагмента, але це з лишком окупиться, коли ви почнете створювати цілі проекти за кілька годин.

Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.