├── autoload └── vim_lib │ ├── base │ ├── tests │ │ ├── File │ │ │ ├── dir │ │ │ │ └── file.txt │ │ │ └── file.txt │ │ ├── Object │ │ │ ├── Subclass.vim │ │ │ ├── Mix.vim │ │ │ ├── Child.vim │ │ │ └── Parent.vim │ │ ├── EventHandle │ │ │ └── Mock.vim │ │ ├── TestStack.vim │ │ ├── TestDict.vim │ │ ├── TestList.vim │ │ ├── TestEventHandle.vim │ │ ├── TestFile.vim │ │ └── TestObject.vim │ ├── Stack.vim │ ├── EventHandle.vim │ ├── Dict.vim │ ├── List.vim │ ├── Object.vim │ ├── File.vim │ └── Test.vim │ ├── sys │ ├── NullPlugin.vim │ ├── Publisher.vim │ ├── Autoload.vim │ ├── tests │ │ ├── TestPublisher.vim │ │ ├── TestPlugin.vim │ │ ├── TestContent.vim │ │ └── TestBuffer.vim │ ├── Content.vim │ ├── Plugin.vim │ ├── System.vim │ └── Buffer.vim │ └── view │ └── BufferStack.vim ├── syntax ├── vim_lib-tmp.vim └── vim_lib-base.vim └── doc ├── tags-ru └── vim_lib.rux /autoload/vim_lib/base/tests/File/dir/file.txt: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /autoload/vim_lib/base/tests/File/file.txt: -------------------------------------------------------------------------------- 1 | Hello world 2 | Test 3 | -------------------------------------------------------------------------------- /syntax/vim_lib-tmp.vim: -------------------------------------------------------------------------------- 1 | set syntax=vim_lib-base 2 | 3 | syntax match Title /^".*"$/ 4 | syntax match Comment /^".*[^"]$/ 5 | -------------------------------------------------------------------------------- /autoload/vim_lib/base/tests/Object/Subclass.vim: -------------------------------------------------------------------------------- 1 | " Date Create: 2015-01-06 18:56:11 2 | " Last Change: 2015-01-06 20:26:45 3 | " Author: Artur Sh. Mamedbekov (Artur-Mamedbekov@yandex.ru) 4 | " License: GNU GPL v3 (http://www.gnu.org/copyleft/gpl.html) 5 | 6 | let s:Object = g:vim_lib#base#Object# 7 | 8 | let s:Subclass = s:Object.expand() 9 | 10 | let g:vim_lib#base#tests#Object#Subclass# = s:Subclass 11 | -------------------------------------------------------------------------------- /autoload/vim_lib/base/tests/Object/Mix.vim: -------------------------------------------------------------------------------- 1 | " Date Create: 2015-02-02 14:29:45 2 | " Last Change: 2015-02-02 14:32:15 3 | " Author: Artur Sh. Mamedbekov (Artur-Mamedbekov@yandex.ru) 4 | " License: GNU GPL v3 (http://www.gnu.org/copyleft/gpl.html) 5 | 6 | let s:Object = g:vim_lib#base#Object# 7 | 8 | let s:Class = s:Object.expand() 9 | let s:Class.properties = {'mixProperty': 1} 10 | 11 | function! s:Class.mixMethod() " {{{ 12 | return self.mixProperty 13 | endfunction " }}} 14 | 15 | let g:vim_lib#base#tests#Object#Mix# = s:Class 16 | -------------------------------------------------------------------------------- /autoload/vim_lib/base/tests/Object/Child.vim: -------------------------------------------------------------------------------- 1 | " Date Create: 2015-01-06 13:24:32 2 | " Last Change: 2015-01-07 11:45:23 3 | " Author: Artur Sh. Mamedbekov (Artur-Mamedbekov@yandex.ru) 4 | " License: GNU GPL v3 (http://www.gnu.org/copyleft/gpl.html) 5 | 6 | let s:Parent = g:vim_lib#base#tests#Object#Parent# 7 | 8 | let s:Child = s:Parent.expand() 9 | 10 | function! s:Child.new(x, y) " {{{ 11 | let l:obj = self.bless(self.parent.new(a:x)) 12 | let l:obj.y = a:y 13 | return l:obj 14 | endfunction " }}} 15 | 16 | let g:vim_lib#base#tests#Object#Child# = s:Child 17 | -------------------------------------------------------------------------------- /autoload/vim_lib/base/tests/EventHandle/Mock.vim: -------------------------------------------------------------------------------- 1 | " Date Create: 2015-02-02 17:09:15 2 | " Last Change: 2015-02-02 17:11:02 3 | " Author: Artur Sh. Mamedbekov (Artur-Mamedbekov@yandex.ru) 4 | " License: GNU GPL v3 (http://www.gnu.org/copyleft/gpl.html) 5 | 6 | let s:Object = g:vim_lib#base#Object# 7 | let s:EventHandle = g:vim_lib#base#EventHandle# 8 | 9 | let s:Class = s:Object.expand() 10 | call s:Class.mix(s:EventHandle) 11 | 12 | function! s:Class.new() " {{{ 13 | let l:obj = self.bless() 14 | return l:obj 15 | endfunction " }}} 16 | 17 | let g:vim_lib#base#tests#EventHandle#Mock# = s:Class 18 | -------------------------------------------------------------------------------- /autoload/vim_lib/base/tests/Object/Parent.vim: -------------------------------------------------------------------------------- 1 | " Date Create: 2015-01-06 13:22:19 2 | " Last Change: 2015-02-02 14:41:50 3 | " Author: Artur Sh. Mamedbekov (Artur-Mamedbekov@yandex.ru) 4 | " License: GNU GPL v3 (http://www.gnu.org/copyleft/gpl.html) 5 | 6 | let s:Object = g:vim_lib#base#Object# 7 | let s:Mix = g:vim_lib#base#tests#Object#Mix# 8 | 9 | let s:Parent = s:Object.expand() 10 | let s:Parent.properties = {'property': 1} 11 | call s:Parent.mix(s:Mix) 12 | 13 | function! s:Parent.new(x) " {{{ 14 | let l:obj = self.bless() 15 | let l:obj.x = a:x 16 | let l:obj.array = [[1, 2, 3], 2, 3] 17 | return l:obj 18 | endfunction " }}} 19 | 20 | function s:Parent.modificArray(value) " {{{ 21 | let self.array[0][0] = a:value 22 | endfunction " }}} 23 | 24 | function! s:Parent.__staticMethod() " {{{ 25 | endfunction " }}} 26 | 27 | let g:vim_lib#base#tests#Object#Parent# = s:Parent 28 | -------------------------------------------------------------------------------- /autoload/vim_lib/sys/NullPlugin.vim: -------------------------------------------------------------------------------- 1 | " Date Create: 2015-01-09 14:35:46 2 | " Last Change: 2015-02-16 13:59:56 3 | " Author: Artur Sh. Mamedbekov (Artur-Mamedbekov@yandex.ru) 4 | " License: GNU GPL v3 (http://www.gnu.org/copyleft/gpl.html) 5 | 6 | let s:Object = g:vim_lib#base#Object# 7 | 8 | let s:Class = s:Object.expand() 9 | 10 | function! s:Class.new(name) " {{{ 11 | let l:obj = self.bless() 12 | let l:obj.name = a:name 13 | return l:obj 14 | endfunction " }}} 15 | 16 | function! s:Class.getName() " {{{ 17 | return self.name 18 | endfunction " }}} 19 | 20 | function! s:Class.comm(command, method) " {{{ 21 | endfunction " }}} 22 | 23 | function! s:Class.map(sequence, method) " {{{ 24 | endfunction " }}} 25 | 26 | function! s:Class.au(event, template, method) " {{{ 27 | endfunction " }}} 28 | 29 | function! s:Class.menu(point, method) " {{{ 30 | endfunction " }}} 31 | 32 | function! s:Class.reg() " {{{ 33 | let g:[self.name . '#'] = self 34 | endfunction " }}} 35 | 36 | function! s:Class.run() " {{{ 37 | endfunction " }}} 38 | 39 | let g:vim_lib#sys#NullPlugin# = s:Class 40 | -------------------------------------------------------------------------------- /doc/tags-ru: -------------------------------------------------------------------------------- 1 | !_TAG_FILE_ENCODING utf-8 // 2 | vim_lib vim_lib.rux /*vim_lib* 3 | vim_lib-base vim_lib.rux /*vim_lib-base* 4 | vim_lib-base-dict vim_lib.rux /*vim_lib-base-dict* 5 | vim_lib-base-eventhandle vim_lib.rux /*vim_lib-base-eventhandle* 6 | vim_lib-base-file vim_lib.rux /*vim_lib-base-file* 7 | vim_lib-base-list vim_lib.rux /*vim_lib-base-list* 8 | vim_lib-base-object vim_lib.rux /*vim_lib-base-object* 9 | vim_lib-base-stack vim_lib.rux /*vim_lib-base-stack* 10 | vim_lib-base-test vim_lib.rux /*vim_lib-base-test* 11 | vim_lib-description vim_lib.rux /*vim_lib-description* 12 | vim_lib-sys vim_lib.rux /*vim_lib-sys* 13 | vim_lib-sys-buffer vim_lib.rux /*vim_lib-sys-buffer* 14 | vim_lib-sys-content vim_lib.rux /*vim_lib-sys-content* 15 | vim_lib-sys-nullplugin vim_lib.rux /*vim_lib-sys-nullplugin* 16 | vim_lib-sys-plugin vim_lib.rux /*vim_lib-sys-plugin* 17 | vim_lib-sys-publisher vim_lib.rux /*vim_lib-sys-publisher* 18 | vim_lib-sys-system vim_lib.rux /*vim_lib-sys-system* 19 | vim_lib-view vim_lib.rux /*vim_lib-view* 20 | vim_lib-view-bufferstack vim_lib.rux /*vim_lib-view-bufferstack* 21 | vim_lib.txt vim_lib.rux /*vim_lib.txt* 22 | -------------------------------------------------------------------------------- /syntax/vim_lib-base.vim: -------------------------------------------------------------------------------- 1 | " Colors. {{{ 2 | highlight Black guifg=Black ctermfg=Black 3 | highlight Gray guifg=Gray ctermfg=Gray 4 | highlight Blue guifg=DarkBlue ctermfg=DarkBlue 5 | highlight Cyan guifg=DarkCyan ctermfg=DarkCyan 6 | highlight Green guifg=DarkGreen ctermfg=DarkGreen 7 | highlight Red guifg=DarkRed ctermfg=DarkRed 8 | highlight Yellow guifg=DarkYellow ctermfg=DarkYellow 9 | highlight Magenta guifg=DarkMagenta ctermfg=DarkMagenta 10 | " }}} 11 | " Bold colors. {{{ 12 | highlight BoldBlack guifg=Black gui=bold ctermfg=DarkGray term=bold 13 | highlight BoldBlue guifg=DarkBlue gui=bold ctermfg=Blue term=bold 14 | highlight BoldCyan guifg=DarkCyan gui=bold ctermfg=Cyan term=bold 15 | highlight BoldGreen guifg=DarkGreen gui=bold ctermfg=Green term=bold 16 | highlight BoldRed guifg=DarkRed gui=bold ctermfg=Red term=bold 17 | highlight BoldYellow guifg=DarkYellow gui=bold ctermfg=Yellow term=bold 18 | highlight BoldMagenta guifg=DarkMagenta gui=bold ctermfg=Magenta term=bold 19 | " }}} 20 | " Background colors. {{{ 21 | highlight BgBlack guibg=Black guifg=White ctermbg=Black 22 | highlight BgGray guibg=Gray ctermbg=Gray ctermfg=Black 23 | highlight BgBlue guibg=DarkBlue guifg=White ctermbg=DarkBlue 24 | highlight BgCyan guibg=DarkCyan ctermbg=DarkCyan 25 | highlight BgGreen guibg=DarkGreen guifg=White ctermbg=DarkGreen 26 | highlight BgRed guibg=DarkRed guifg=White ctermbg=DarkRed 27 | highlight BgYellow guibg=DarkYellow ctermbg=DarkYellow 28 | highlight BgMagenta guibg=DarkMagenta guifg=White ctermbg=DarkMagenta 29 | " }}} 30 | -------------------------------------------------------------------------------- /autoload/vim_lib/base/Stack.vim: -------------------------------------------------------------------------------- 1 | " Date Create: 2015-01-08 23:04:18 2 | " Last Change: 2015-01-08 23:25:42 3 | " Author: Artur Sh. Mamedbekov (Artur-Mamedbekov@yandex.ru) 4 | " License: GNU GPL v3 (http://www.gnu.org/copyleft/gpl.html) 5 | 6 | let s:Object = g:vim_lib#base#Object# 7 | 8 | "" {{{ 9 | " Класс представляет список с доступом типа "стек". 10 | "" }}} 11 | let s:Class = s:Object.expand() 12 | 13 | "" {{{ 14 | " Конструктор, формирующий пустой стек. 15 | " @return vim_lib#base#Stack# Целевой стек. 16 | "" }}} 17 | function! s:Class.new() " {{{ 18 | let l:obj = self.bless() 19 | "" {{{ 20 | " @var array Массив значений стека. 21 | "" }}} 22 | let l:obj.values = [] 23 | return l:obj 24 | endfunction " }}} 25 | 26 | "" {{{ 27 | " Метод добавляет значение на вершину стека. 28 | " @param mixed value Добавляемое значение. 29 | "" }}} 30 | function! s:Class.push(value) " {{{ 31 | call add(self.values, a:value) 32 | endfunction " }}} 33 | 34 | "" {{{ 35 | " Метод изымает и возвращает значение с вершины стека. 36 | " @return mixed Значение с вершины стека. 37 | "" }}} 38 | function! s:Class.pop() " {{{ 39 | let l:len = self.length() 40 | if l:len == 0 41 | throw 'IndexOutOfRangeException: Stack is empty.' 42 | endif 43 | return remove(self.values, self.length() - 1) 44 | endfunction " }}} 45 | 46 | "" {{{ 47 | " Метод возвращает значение с вершины стека не изымая его. 48 | " @return mixed Значение с вершины стека. 49 | "" }}} 50 | function! s:Class.current() " {{{ 51 | let l:len = self.length() 52 | if l:len == 0 53 | throw 'IndexOutOfRangeException: Stack is empty.' 54 | endif 55 | return self.values[l:len - 1] 56 | endfunction " }}} 57 | 58 | "" {{{ 59 | " Метод вычисляет длину стека. 60 | " @return integer Длина стека. 61 | "" }}} 62 | function! s:Class.length() " {{{ 63 | return len(self.values) 64 | endfunction " }}} 65 | 66 | let g:vim_lib#base#Stack# = s:Class 67 | -------------------------------------------------------------------------------- /autoload/vim_lib/base/EventHandle.vim: -------------------------------------------------------------------------------- 1 | " Date Create: 2015-02-02 17:00:19 2 | " Last Change: 2015-02-06 23:36:03 3 | " Author: Artur Sh. Mamedbekov (Artur-Mamedbekov@yandex.ru) 4 | " License: GNU GPL v3 (http://www.gnu.org/copyleft/gpl.html) 5 | 6 | let s:Object = g:vim_lib#base#Object# 7 | 8 | "" {{{ 9 | " Данная примесь добавляет классу механизм обработки событий. 10 | "" }}} 11 | let s:Class = s:Object.expand() 12 | 13 | let s:Class.properties = {} 14 | "" {{{ 15 | " @var hash Словарь обработчиков событий, имеющий следующую структуру: {событие: [обработчик, ...], ...}. 16 | "" }}} 17 | let s:Class.properties.listeners = {} 18 | 19 | "" {{{ 20 | " Метод добавляет обработчик события. 21 | " В качестве обработчика события должен выступать метод вызываемого объекта или ссылка на глобальную функцию. 22 | " Позволяется устанавливать несколько обработчиков одного события. В случае наступления события, они будут вызваны в порядке их добавления. 23 | " @param string event Событие. 24 | " @param string|function listener Имя метода-обработчика события или ссылка на функцию-обработчик события. 25 | "" }}} 26 | function! s:Class.listen(event, listener) " {{{ 27 | if !has_key(self.listeners, a:event) 28 | let self.listeners[a:event] = [] 29 | endif 30 | call add(self.listeners[a:event], a:listener) 31 | endfunction " }}} 32 | 33 | "" {{{ 34 | " Метод исполняет обработчики данного события. 35 | " @param string event Имя эмулируемого события. 36 | "" }}} 37 | function! s:Class.fire(event) " {{{ 38 | if has_key(self.listeners, a:event) 39 | for l:Listener in self.listeners[a:event] 40 | if type(l:Listener) == 2 41 | call call(l:Listener, []) 42 | else 43 | call call(self[l:Listener], [], self) 44 | endif 45 | endfor 46 | endif 47 | endfunction " }}} 48 | 49 | "" {{{ 50 | " Метод удаляет обработчик события. 51 | " @param string event Имя целевого события. 52 | " @param string|function listener [optional] Имя метода-обработчика события или ссылка на функцию-обработчик. Если параметр не задан, удаляются все обработчики данного события. 53 | "" }}} 54 | function! s:Class.ignore(event, ...) " {{{ 55 | if !exists('a:1') 56 | let self.listeners[a:event] = [] 57 | else 58 | let l:pos = index(self.listeners[a:event], a:1) 59 | while l:pos != -1 60 | call remove(self.listeners[a:event], l:pos) 61 | let l:pos = index(self.listeners[a:event], a:1) 62 | endwhile 63 | endif 64 | endfunction " }}} 65 | 66 | let g:vim_lib#base#EventHandle# = s:Class 67 | -------------------------------------------------------------------------------- /autoload/vim_lib/sys/Publisher.vim: -------------------------------------------------------------------------------- 1 | " Date Create: 2015-01-16 19:26:08 2 | " Last Change: 2015-01-22 11:15:28 3 | " Author: Artur Sh. Mamedbekov (Artur-Mamedbekov@yandex.ru) 4 | " License: GNU GPL v3 (http://www.gnu.org/copyleft/gpl.html) 5 | 6 | let s:Object = g:vim_lib#base#Object# 7 | 8 | "" {{{ 9 | " Данный класс представляет модель событий редактора. Класс позволяет компонентам редактора и плагинам подписываться на определенные события с целью последующего реагирования на их возникновение. 10 | "" }}} 11 | let s:Class = s:Object.expand() 12 | 13 | "" {{{ 14 | " Конструктор всегда возвращает единственный экземпляр данного класса. 15 | " @return vim_lib#sys#Publisher# Модель событий. 16 | "" }}} 17 | function! s:Class.new() " {{{ 18 | return self.singleton 19 | endfunction " }}} 20 | 21 | "" {{{ 22 | " Метод вызывает все обработчики данного события. 23 | " @param string eventName Имя целевого события. 24 | " @param hash event [optional] Объект события, передаваемый обработчику в качестве единственного параметра. Данный объект дополняется свойством name, хранящим имя события. 25 | "" }}} 26 | function! s:Class.fire(eventName, ...) " {{{ 27 | let l:event = (exists('a:1'))? a:1 : {} 28 | let l:event.name = a:eventName 29 | if has_key(self.listeners, a:eventName) 30 | for l:Listener in self.listeners[a:eventName] 31 | call call(l:Listener, [l:event]) 32 | endfor 33 | endif 34 | endfunction " }}} 35 | 36 | "" {{{ 37 | " Метод добавляет слушателя события. 38 | " @param string eventName Имя целевого события. 39 | " @param function listener Функция-обработчик события. 40 | "" }}} 41 | function! s:Class.listen(eventName, listener) " {{{ 42 | if !has_key(self.listeners, a:eventName) 43 | let self.listeners[a:eventName] = [] 44 | endif 45 | call add(self.listeners[a:eventName], a:listener) 46 | endfunction " }}} 47 | 48 | "" {{{ 49 | " Метод удаляет слушателя события. 50 | " @param string eventName Имя целевого события. 51 | " @param function listener [optional] Удаляемая функция-обработчик. Если параметр не передан, удаляются все обработчики целевого события. 52 | "" }}} 53 | function! s:Class.ignore(eventName, ...) " {{{ 54 | if exists('a:1') 55 | if has_key(self.listeners, a:eventName) 56 | let l:i = index(self.listeners[a:eventName], a:1) 57 | if l:i != -1 58 | call remove(self.listeners[a:eventName], l:i) 59 | endif 60 | endif 61 | else 62 | if has_key(self.listeners, a:eventName) 63 | call remove(self.listeners, a:eventName) 64 | endif 65 | endif 66 | endfunction " }}} 67 | 68 | "" {{{ 69 | " @var vim_lib#sys#Publisher# Единственный экземпляр класса. 70 | "" }}} 71 | let s:Class.singleton = s:Class.bless() 72 | "" {{{ 73 | " @var hash Словарь слушателей, имеющий следующую структуру: {имяСобытия: [слушатель, ...], ...} 74 | "" }}} 75 | let s:Class.singleton.listeners = {} 76 | 77 | let g:vim_lib#sys#Publisher# = s:Class 78 | -------------------------------------------------------------------------------- /autoload/vim_lib/base/Dict.vim: -------------------------------------------------------------------------------- 1 | " Date Create: 2015-01-07 11:55:51 2 | " Last Change: 2015-01-08 23:07:25 3 | " Author: Artur Sh. Mamedbekov (Artur-Mamedbekov@yandex.ru) 4 | " License: GNU GPL v3 (http://www.gnu.org/copyleft/gpl.html) 5 | 6 | let s:Object = g:vim_lib#base#Object# 7 | 8 | "" {{{ 9 | " Класс представляет словарь с доступом к элементам по ключу. 10 | "" }}} 11 | let s:Class = s:Object.expand() 12 | 13 | "" {{{ 14 | " Конструктор, формирующий словарь на основании заданного массива или хэша данных. 15 | " @param hash|array data [optional] Исходный массив данных, имеющий следующую структуру: [[key, value], ...] или хэш. Если параметр не задан, создается пустой словарь. 16 | " @return vim_lib#base#Dict# Целевой словарь. 17 | "" }}} 18 | function! s:Class.new(...) " {{{ 19 | let l:obj = self.bless() 20 | if exists('a:1') 21 | if type(a:1) == 3 22 | " Обработка массива. {{{ 23 | let l:obj.values = {} 24 | for [l:k, l:v] in a:1 25 | let l:obj.values[l:k] = l:v 26 | endfor 27 | " }}} 28 | elseif type(a:1) == 4 29 | " Обработка хэша. 30 | let l:obj.values = a:1 31 | else 32 | let l:obj.values = {} 33 | endif 34 | else 35 | let l:obj.values = {} 36 | endif 37 | return l:obj 38 | endfunction " }}} 39 | 40 | "" {{{ 41 | " Метод вычисляет длину словаря. 42 | " @return integer Длина словаря. 43 | "" }}} 44 | function! s:Class.length() " {{{ 45 | return len(self.values) 46 | endfunction " }}} 47 | 48 | "" {{{ 49 | " Метод устанавливает или возвращает значение заданного элемента словаря. 50 | " @param string|integer key Ключ целевого элемента. 51 | " @param mixed value [optional] Устанавливаемое значение. 52 | " @throws IndexOutOfRangeException Выбрасывается при обращении к отсутствующему элементу словаря. 53 | " @return mixed Запрашиваемое значение элемента. 54 | "" }}} 55 | function! s:Class.item(key, ...) " {{{ 56 | if !exists('a:1') 57 | " Возврат значения. {{{ 58 | if !has_key(self.values, a:key) 59 | throw 'IndexOutOfRangeException: Key <' . a:key . '> not found.' 60 | endif 61 | return self.values[a:key] 62 | " }}} 63 | else 64 | " Установка значения {{{ 65 | let self.values[a:key] = a:1 66 | " }}} 67 | endif 68 | endfunction " }}} 69 | 70 | "" {{{ 71 | " Метод возвращает массив ключей, используемый в словаре. 72 | " @return array Массив ключей в словаре. 73 | "" }}} 74 | function! s:Class.keys() " {{{ 75 | return keys(self.values) 76 | endfunction " }}} 77 | 78 | "" {{{ 79 | " Метод возвращает массив значений элементов словаря. 80 | " @return array Массив значений элементов словаря. 81 | "" }}} 82 | function! s:Class.vals() " {{{ 83 | return values(self.values) 84 | endfunction " }}} 85 | 86 | "" {{{ 87 | " Метод возвращает двумерный массив ключей и значений элементов словаря. 88 | " @return array Двумерный массив следующей структуры: [[key, val], ...]. 89 | "" }}} 90 | function! s:Class.items() " {{{ 91 | return items(self.values) 92 | endfunction " }}} 93 | 94 | let g:vim_lib#base#Dict# = s:Class 95 | -------------------------------------------------------------------------------- /autoload/vim_lib/base/List.vim: -------------------------------------------------------------------------------- 1 | " Date Create: 2015-01-06 22:23:22 2 | " Last Change: 2015-01-08 23:06:56 3 | " Author: Artur Sh. Mamedbekov (Artur-Mamedbekov@yandex.ru) 4 | " License: GNU GPL v3 (http://www.gnu.org/copyleft/gpl.html) 5 | 6 | let s:Object = g:vim_lib#base#Object# 7 | 8 | "" {{{ 9 | " Класс представляет упорядоченный список с доступом к элементам по индексу. 10 | "" }}} 11 | let s:Class = s:Object.expand() 12 | 13 | "" {{{ 14 | " Конструктор, формирующий список на основании заданного массива данных. 15 | " @param array data [optional] Исходный массив данных. Если параметр не задан, создается пустой список. 16 | " @return vim_lib#base#List# Целевой список. 17 | "" }}} 18 | function! s:Class.new(...) " {{{ 19 | let l:obj = self.bless() 20 | "" {{{ 21 | " @var array Массив значений списка. 22 | "" }}} 23 | let l:obj.values = (exists('a:1') && type(a:1) == 3)? a:1 : [] 24 | return l:obj 25 | endfunction " }}} 26 | 27 | "" {{{ 28 | " Метод вычисляет длину списка. 29 | " @return integer Длина списка. 30 | "" }}} 31 | function! s:Class.length() " {{{ 32 | return len(self.values) 33 | endfunction " }}} 34 | 35 | "" {{{ 36 | " Метод устанавливает или возвращает значение заданного элемента списка. 37 | " @param integer index Индекс целевого элемента. 38 | " @param mixed value [optional] Устанавливаемое значение. 39 | " @throws IndexOutOfRangeException Выбрасывается при обращении к отсутствующему элементу списка (чтение) или попытке выхода за границы списка (запись). 40 | " @return mixed Запрашиваемое значение элемента. 41 | "" }}} 42 | function! s:Class.item(index, ...) " {{{ 43 | let s:len = self.length() 44 | if !exists('a:1') 45 | " Возврат значения. {{{ 46 | if s:len - 1 < a:index 47 | throw 'IndexOutOfRangeException: Index <' . a:index . '> not found.' 48 | endif 49 | return self.values[a:index] 50 | " }}} 51 | else 52 | " Установка значения {{{ 53 | if s:len < a:index 54 | throw 'IndexOutOfRangeException: Index <' . a:index . '> out of range.' 55 | endif 56 | call insert(self.values, a:1, a:index) 57 | " }}} 58 | endif 59 | endfunction " }}} 60 | 61 | "" {{{ 62 | " Метод преобразует список в массив VimScript. 63 | " @return array Массив элементов списка. 64 | "" }}} 65 | function! s:Class.list() " {{{ 66 | return deepcopy(self.values) 67 | endfunction " }}} 68 | 69 | "" {{{ 70 | " Метод формирует и возвращает срез списка в виде нового списка. 71 | " @param integer start Индекс элемента начала диапазона. Использование отрицательного значения эквивалентно: len() - значение. 72 | " @param integer end [optional] Индекс элемента конца диапазона включительно. Если параметр не задан, выполняется выборка всех элементов до конца списка. Использование отрицательного значения эквивалентно: len() - значение. 73 | " @return vim_lib#base#List# Результирующий список, содержащий срез исходного списка. 74 | "" }}} 75 | function! s:Class.sec(start, ...) " {{{ 76 | if !exists('a:1') 77 | return self.class.new(self.values[a:start :]) 78 | else 79 | return self.class.new(self.values[a:start : a:1]) 80 | endif 81 | endfunction " }}} 82 | 83 | let g:vim_lib#base#List# = s:Class 84 | -------------------------------------------------------------------------------- /autoload/vim_lib/base/tests/TestStack.vim: -------------------------------------------------------------------------------- 1 | " Date Create: 2015-01-08 23:10:42 2 | " Last Change: 2015-01-11 15:55:04 3 | " Author: Artur Sh. Mamedbekov (Artur-Mamedbekov@yandex.ru) 4 | " License: GNU GPL v3 (http://www.gnu.org/copyleft/gpl.html) 5 | 6 | let s:Stack = vim_lib#base#Stack# 7 | 8 | let s:Test = vim_lib#base#Test#.expand() 9 | 10 | " new {{{ 11 | "" {{{ 12 | " Должен создавать пустой стек. 13 | " @covers vim_lib#base#Stack#.new 14 | "" }}} 15 | function! s:Test.testNew() " {{{ 16 | let l:obj = s:Stack.new() 17 | call self.assertEquals(l:obj.length(), 0) 18 | endfunction " }}} 19 | " }}} 20 | " length {{{ 21 | "" {{{ 22 | " Должен возвращать размер стека. 23 | " @covers vim_lib#base#Stack#.length 24 | "" }}} 25 | function! s:Test.testLength() " {{{ 26 | let l:obj = s:Stack.new() 27 | call self.assertEquals(l:obj.length(), 0) 28 | call l:obj.push(1) 29 | call l:obj.push(2) 30 | call self.assertEquals(l:obj.length(), 2) 31 | endfunction " }}} 32 | " }}} 33 | " push, pop, current {{{ 34 | "" {{{ 35 | " Должен добавлять элемент в вершину стека. 36 | " @covers vim_lib#base#Stack#.push 37 | "" }}} 38 | function! s:Test.testPush() " {{{ 39 | let l:obj = s:Stack.new() 40 | call l:obj.push(1) 41 | call self.assertEquals(l:obj.values, [1]) 42 | call l:obj.push(2) 43 | call self.assertEquals(l:obj.values, [1, 2]) 44 | call self.assertEquals(l:obj.length(), 2) 45 | endfunction " }}} 46 | 47 | "" {{{ 48 | " Должен изымать и возвращать элемент из вершины стека. 49 | " @covers vim_lib#base#Stack#.pop 50 | "" }}} 51 | function! s:Test.testPop() " {{{ 52 | let l:obj = s:Stack.new() 53 | call l:obj.push(1) 54 | call l:obj.push(2) 55 | call self.assertEquals(l:obj.pop(), 2) 56 | call self.assertEquals(l:obj.pop(), 1) 57 | call self.assertEquals(l:obj.length(), 0) 58 | endfunction " }}} 59 | 60 | "" {{{ 61 | " Должен выбрасывать исключение, если стек пуст. 62 | " @covers vim_lib#base#Stack#.pop 63 | "" }}} 64 | function! s:Test.testPop_throwExceptionIfEmpty() " {{{ 65 | let l:obj = s:Stack.new() 66 | try 67 | call l:obj.pop() 68 | call self.fail('testPop_throwExceptionIfEmpty', 'Expected exception is not thrown.') 69 | catch /IndexOutOfRangeException:.*/ 70 | endtry 71 | endfunction " }}} 72 | 73 | "" {{{ 74 | " Должен возвращать элемент из вершины стека не изымая его. 75 | " @covers vim_lib#base#Stack#.current 76 | "" }}} 77 | function! s:Test.testCurrent() " {{{ 78 | let l:obj = s:Stack.new() 79 | call l:obj.push(1) 80 | call l:obj.push(2) 81 | call self.assertEquals(l:obj.current(), 2) 82 | call self.assertEquals(l:obj.current(), 2) 83 | call self.assertEquals(l:obj.length(), 2) 84 | endfunction " }}} 85 | 86 | "" {{{ 87 | " Должен выбрасывать исключение, если стек пуст. 88 | " @covers vim_lib#base#Stack#.current 89 | "" }}} 90 | function! s:Test.testCurrent_throwExceptionIfEmpty() " {{{ 91 | let l:obj = s:Stack.new() 92 | try 93 | call l:obj.current() 94 | call self.fail('testPop_throwExceptionIfEmpty', 'Expected exception is not thrown.') 95 | catch /IndexOutOfRangeException:.*/ 96 | endtry 97 | endfunction " }}} 98 | " }}} 99 | 100 | let g:vim_lib#base#tests#TestStack# = s:Test 101 | call s:Test.run() 102 | -------------------------------------------------------------------------------- /autoload/vim_lib/base/tests/TestDict.vim: -------------------------------------------------------------------------------- 1 | " Date Create: 2015-01-07 12:03:58 2 | " Last Change: 2015-01-11 15:56:04 3 | " Author: Artur Sh. Mamedbekov (Artur-Mamedbekov@yandex.ru) 4 | " License: GNU GPL v3 (http://www.gnu.org/copyleft/gpl.html) 5 | 6 | let s:Dict = vim_lib#base#Dict# 7 | 8 | let s:Test = vim_lib#base#Test#.expand() 9 | 10 | " new {{{ 11 | "" {{{ 12 | " Должен создавать пустой словарь. 13 | " @covers vim_lib#base#Dict#.new 14 | "" }}} 15 | function s:Test.testNew_createEmptyDict() " {{{ 16 | let l:obj = s:Dict.new() 17 | call self.assertEquals(l:obj.length(), 0) 18 | endfunction " }}} 19 | 20 | "" {{{ 21 | " Должен использовать хэш в качестве начальных данных. 22 | " @covers vim_lib#base#Dict#.new 23 | "" }}} 24 | function s:Test.testNew_wrapHash() " {{{ 25 | let l:obj = s:Dict.new({'a': 1, 'b': 2, 'c': 3}) 26 | call self.assertEquals(l:obj.length(), 3) 27 | call self.assertEquals(l:obj.item('a'), 1) 28 | endfunction " }}} 29 | 30 | "" {{{ 31 | " Должен использовать массив в качестве начальных данных. 32 | " @covers vim_lib#base#Dict#.new 33 | "" }}} 34 | function s:Test.testNew_wrapArray() " {{{ 35 | let l:obj = s:Dict.new([['a', 1], ['b', 2], ['c', 3]]) 36 | call self.assertEquals(l:obj.length(), 3) 37 | call self.assertEquals(l:obj.item('a'), 1) 38 | endfunction " }}} 39 | " }}} 40 | " item {{{ 41 | "" {{{ 42 | " Должен возвращать значение элемента словаря по ключу. 43 | " @covers vim_lib#base#Dict#.item 44 | "" }}} 45 | function s:Test.testItem_getValue() " {{{ 46 | let l:obj = s:Dict.new() 47 | call l:obj.item('a', 1) 48 | call l:obj.item('b', 2) 49 | call self.assertEquals(l:obj.item('a'), 1) 50 | call self.assertEquals(l:obj.item('b'), 2) 51 | endfunction " }}} 52 | 53 | "" {{{ 54 | " Должен выбрасывать исключение, если элемент с заданым ключем отсутствует. 55 | " @covers vim_lib#base#Dict#.item 56 | "" }}} 57 | function s:Test.testItem_throwExceptionGet() " {{{ 58 | let l:obj = s:Dict.new() 59 | try 60 | call l:obj.item('a') 61 | call self.fail('testItem_throwException', 'Expected exception is not thrown.') 62 | catch /IndexOutOfRangeException:.*/ 63 | endtry 64 | endfunction " }}} 65 | 66 | "" {{{ 67 | " Должен устанавливать значение элементу словаря. 68 | " @covers vim_lib#base#Dict#.item 69 | "" }}} 70 | function s:Test.testItem_setValue() " {{{ 71 | let l:obj = s:Dict.new() 72 | call l:obj.item('a', 1) 73 | call self.assertEquals(l:obj.item('a'), 1) 74 | endfunction " }}} 75 | " }}} 76 | " keys, vals, items {{{ 77 | "" {{{ 78 | " Должен возвращать массив ключей словаря. 79 | " @covers vim_lib#base#Dict#.keys 80 | "" }}} 81 | function s:Test.testKeys() " {{{ 82 | let l:obj = s:Dict.new({'a': 1, 'b': 2, 'c': 3}) 83 | call self.assertEquals(l:obj.keys(), ['a', 'b', 'c']) 84 | endfunction " }}} 85 | 86 | "" {{{ 87 | " Должен возвращать массив значений словаря. 88 | " @covers vim_lib#base#Dict#.vals 89 | "" }}} 90 | function s:Test.testValues() " {{{ 91 | let l:obj = s:Dict.new({'a': 1, 'b': 2, 'c': 3}) 92 | call self.assertEquals(l:obj.vals(), [1, 2, 3]) 93 | endfunction " }}} 94 | 95 | "" {{{ 96 | " Должен возвращать массив элементов словаря. 97 | " @covers vim_lib#base#Dict#.items 98 | "" }}} 99 | function s:Test.testItems() " {{{ 100 | let l:obj = s:Dict.new({'a': 1, 'b': 2, 'c': 3}) 101 | call self.assertEquals(l:obj.items(), [['a', 1], ['b', 2], ['c', 3]]) 102 | endfunction " }}} 103 | " }}} 104 | 105 | let g:vim_lib#base#tests#TestDict# = s:Test 106 | call s:Test.run() 107 | -------------------------------------------------------------------------------- /autoload/vim_lib/sys/Autoload.vim: -------------------------------------------------------------------------------- 1 | " Date Create: 2015-03-04 11:34:58 2 | " Last Change: 2015-06-13 16:46:58 3 | " Author: Artur Sh. Mamedbekov (Artur-Mamedbekov@yandex.ru) 4 | " License: GNU GPL v3 (http://www.gnu.org/copyleft/gpl.html) 5 | 6 | let s:File = vim_lib#base#File# 7 | 8 | "" {{{ 9 | " @var string Текущий уровень загрузки. 10 | "" }}} 11 | let vim_lib#sys#Autoload#currentLevel = '' 12 | "" {{{ 13 | " @var hash Словарь используемых уровней загрузки. Словарь имеет следующую структуру: {уровень: {plugdir: каталогПлагинов, plugins: [плагин, ...]}, ...}. 14 | "" }}} 15 | let vim_lib#sys#Autoload#levels = {} 16 | 17 | " Установка rtp. {{{ 18 | set nocompatible 19 | set exrc 20 | let s:rtp = [$VIMRUNTIME, $VIM . s:File.slash . 'vimfiles'] 21 | if has('win16') || has('win32') || has('win64') || has('win95') 22 | call add(s:rtp, $HOME . '\vimfiles') 23 | else 24 | call add(s:rtp, $HOME . '/.vim') 25 | endif 26 | call add(s:rtp, '.vim') 27 | let &rtp = join(s:rtp, ',') 28 | " }}} 29 | 30 | "" {{{ 31 | " Команда подключает указанный плагин для текущего уровня загрузки. 32 | "" }}} 33 | com! -nargs=+ Plugin call vim_lib#sys#Autoload#plugin() 34 | 35 | "" {{{ 36 | " Метод инициализирует очередь загрузки для данного уровня. 37 | " @param string level Адрес каталога, содержадего загружаемые файлы (на уровне системы это $VIMRUNTIME, на уровне пользователя ~/.vim или ~/vimfiles, а на уровне проекта это .vim). 38 | " @param string plugindir Имя каталога, содержащего плагины редактора (обычно это bundle). Данный каталог должен располагаться в каталоге, определенном аргументом level. 39 | "" }}} 40 | function! vim_lib#sys#Autoload#init(level, plugindir) " {{{ 41 | let g:vim_lib#sys#Autoload#levels[a:level] = {'plugdir': a:plugindir, 'plugins': []} 42 | let g:vim_lib#sys#Autoload#currentLevel = a:level 43 | endfunction " }}} 44 | 45 | "" {{{ 46 | " Метод добавляет указанный плагин в очередь загрузки. 47 | " @param string name Имя целевого плагина. 48 | " @param hash options Словарь глобальных опций, которые будут установлены перед загрузкой плагина. Опции устанавливаются в виде переменной: g:имяПлагина#ключ = значение. 49 | "" }}} 50 | function! vim_lib#sys#Autoload#plugin(name, ...) " {{{ 51 | let l:level = g:vim_lib#sys#Autoload#currentLevel 52 | let l:plugdir = g:vim_lib#sys#Autoload#levels[l:level]['plugdir'] 53 | let l:plug = s:File.absolute(l:level . s:File.slash . l:plugdir . s:File.slash . a:name) 54 | if l:plug.isDir() 55 | call add(g:vim_lib#sys#Autoload#levels[l:level]['plugins'], a:name) 56 | let &rtp = &rtp . ',' . l:plug.getAddress() 57 | if exists('a:1') 58 | for l:prop in keys(a:1) 59 | let g:[a:name. '#' . l:prop] = a:1[l:prop] 60 | endfor 61 | endif 62 | endif 63 | endfunction " }}} 64 | 65 | "" {{{ 66 | " Метод возвращает информацию об используемых уровнях загрузки. 67 | " @return hash Свойство vim_lib#sys#Autoload#levels. 68 | "" }}} 69 | function! vim_lib#sys#Autoload#getLevels() " {{{ 70 | return g:vim_lib#sys#Autoload#levels 71 | endfunction " }}} 72 | 73 | "" {{{ 74 | " Метод определяет, используется ли заданный плагин. 75 | " Метод вернет true даже в том случае, если плагин физически не установлен, но подключен командой Plugin. 76 | " @param string name Имя целевого плагина. 77 | " @return bool true - если плагин используется, иначе - false. 78 | "" }}} 79 | function! vim_lib#sys#Autoload#isPlug(name) " {{{ 80 | for l:body in values(g:vim_lib#sys#Autoload#levels) 81 | if index(l:body.plugins, a:name) != -1 82 | return 1 83 | endif 84 | endfor 85 | return 0 86 | endfunction " }}} 87 | -------------------------------------------------------------------------------- /autoload/vim_lib/view/BufferStack.vim: -------------------------------------------------------------------------------- 1 | " Date Create: 2015-01-22 19:07:10 2 | " Last Change: 2015-02-10 16:33:47 3 | " Author: Artur Sh. Mamedbekov (Artur-Mamedbekov@yandex.ru) 4 | " License: GNU GPL v3 (http://www.gnu.org/copyleft/gpl.html) 5 | 6 | let s:Object = g:vim_lib#base#Object# 7 | let s:Stack = g:vim_lib#base#Stack# 8 | let s:Buffer = g:vim_lib#sys#Buffer# 9 | 10 | "" {{{ 11 | " Класс представляет стек буферов, накладываемых друг на друга так, чтобы закрытие одного автоматически активировала следующий. 12 | "" }}} 13 | let s:Class = s:Object.expand() 14 | 15 | "" {{{ 16 | " Конструктор создает пустой стек буферов. 17 | "" }}} 18 | function! s:Class.new() " {{{ 19 | let l:obj = self.bless() 20 | let l:obj.stack = s:Stack.new() 21 | return l:obj 22 | endfunction " }}} 23 | 24 | "" {{{ 25 | " Метод добавляет буфер в стек, но не активирует его. 26 | " Метод добавляет привязку к клавише "^y" для добавляемого буфера так, чтобы по нажатии этой клавиши, текущий буфер закрывался и автоматически активировался предыдущий. 27 | " @param vim_lib#sys#Buffer# buffer Добавляемый буфер. 28 | "" }}} 29 | function! s:Class.push(buffer) " {{{ 30 | let a:buffer.stack = self 31 | function! a:buffer.quit() " {{{ 32 | call self.stack.delete() 33 | endfunction " }}} 34 | call a:buffer.map('n', '', 'quit') 35 | call self.stack.push(a:buffer) 36 | endfunction " }}} 37 | 38 | "" {{{ 39 | " Метод возвращает текущий буфер стека. 40 | " @return vim_lib#sys#Buffer# Текущий буфер стека. 41 | "" }}} 42 | function! s:Class.current() " {{{ 43 | return self.stack.current() 44 | endfunction " }}} 45 | 46 | "" {{{ 47 | " Метод удаляет текущий буфер, заменяя его следующим. Если стек буферов содержит только один буфер, окно, содержащее его, закрывается. 48 | "" }}} 49 | function! s:Class.delete() " {{{ 50 | let s:currBuffer = self.stack.pop() 51 | if self.stack.length() > 0 52 | call self.active() 53 | endif 54 | call s:currBuffer.delete() 55 | endfunction " }}} 56 | 57 | "" {{{ 58 | " Метод удаляет все буферы в стеке до указанного. 59 | " @param integer num Номер буфера, до которого будут сохранен стек. 60 | "" }}} 61 | function! s:Class.clear(num) " {{{ 62 | while self.stack.length() > a:num 63 | call self.delete() 64 | endwhile 65 | endfunction " }}} 66 | 67 | "" {{{ 68 | " Метод делает текущий буфер стека (буфер на вершине стека) текущим. 69 | "" }}} 70 | function! s:Class.active() " {{{ 71 | call self.stack.current().active() 72 | endfunction " }}} 73 | 74 | "" {{{ 75 | " Метод открывает новое окно, разделяя текущее по горизонтали, и делает в нем текущий буфер активным. 76 | " @param string pos Позиция нового окна (t - выше текущего окна, b - ниже текущего окна). 77 | " @param integer gsize [optional] Высота нового окна. 78 | " @see vim_lib#sys#Buffer#.active 79 | "" }}} 80 | function! s:Class.gactive(pos, ...) " {{{ 81 | if exists('a:1') 82 | call self.stack.current().gactive(a:pos, a:1) 83 | else 84 | call self.stack.current().gactive(a:pos) 85 | endif 86 | endfunction " }}} 87 | 88 | "" {{{ 89 | " Метод открывает новое окно, разделяя текущее по вертикали, и делает в нем текущий буфер активным. 90 | " @param string pos Позиция нового окна (l - слева от текущего окна, r - справа от текущего окна). 91 | " @param integer vsize [optional] Ширина нового окна. 92 | " @see vim_lib#sys#Buffer#.active 93 | "" }}} 94 | function! s:Class.vactive(pos, ...) " {{{ 95 | if exists('a:1') 96 | call self.stack.current().vactive(a:pos, a:1) 97 | else 98 | call self.stack.current().vactive(a:pos) 99 | endif 100 | endfunction " }}} 101 | 102 | let g:vim_lib#view#BufferStack# = s:Class 103 | -------------------------------------------------------------------------------- /autoload/vim_lib/sys/tests/TestPublisher.vim: -------------------------------------------------------------------------------- 1 | " Date Create: 2015-01-16 19:34:14 2 | " Last Change: 2015-01-17 11:54:23 3 | " Author: Artur Sh. Mamedbekov (Artur-Mamedbekov@yandex.ru) 4 | " License: GNU GPL v3 (http://www.gnu.org/copyleft/gpl.html) 5 | 6 | let s:Publisher = vim_lib#sys#Publisher# 7 | 8 | let s:Test = vim_lib#base#Test#.expand() 9 | 10 | function! s:Test.beforeTest() " {{{ 11 | let self.var = 0 12 | endfunction " }}} 13 | 14 | function! s:Test.afterTest() " {{{ 15 | call s:Publisher.new().ignore('TestEvent') 16 | endfunction " }}} 17 | 18 | " listeners {{{ 19 | function! g:ListenerInc(event) " {{{ 20 | let g:vim_lib#base#tests#TestPublisher#.var += 1 21 | endfunction " }}} 22 | function! g:ListenerDec(event) " {{{ 23 | let g:vim_lib#base#tests#TestPublisher#.var -= 1 24 | endfunction " }}} 25 | function! g:ListenerPlus(event) " {{{ 26 | let g:vim_lib#base#tests#TestPublisher#.var += a:event.var 27 | endfunction " }}} 28 | " }}} 29 | 30 | " listen {{{ 31 | "" {{{ 32 | " Должен добавлять слушателя. 33 | " @covers vim_lib#base#Publisher#.listen 34 | "" }}} 35 | function! s:Test.testListen_addListener() " {{{ 36 | let s:obj = s:Publisher.new() 37 | call s:obj.listen('TestEvent', function('g:ListenerInc')) 38 | call s:obj.listen('TestEvent', function('g:ListenerDec')) 39 | call self.assertEquals(s:obj.listeners, {'TestEvent': [function('g:ListenerInc'), function('g:ListenerDec')]}) 40 | endfunction " }}} 41 | 42 | "" {{{ 43 | " Разрешено дублирование. 44 | " @covers vim_lib#base#Publisher#.listen 45 | "" }}} 46 | function! s:Test.testListen_repeat() " {{{ 47 | let s:obj = s:Publisher.new() 48 | call s:obj.listen('TestEvent', function('g:ListenerInc')) 49 | call s:obj.listen('TestEvent', function('g:ListenerInc')) 50 | call self.assertEquals(s:obj.listeners, {'TestEvent': [function('g:ListenerInc'), function('g:ListenerInc')]}) 51 | endfunction " }}} 52 | " }}} 53 | " ignore {{{ 54 | "" {{{ 55 | " Должен удалять данного слушателя. 56 | " @covers vim_lib#base#Publisher#.ignore 57 | "" }}} 58 | function! s:Test.testIgnore_deleteListener() " {{{ 59 | let s:obj = s:Publisher.new() 60 | call s:obj.listen('TestEvent', function('g:ListenerInc')) 61 | call s:obj.listen('TestEvent', function('g:ListenerDec')) 62 | call s:obj.ignore('TestEvent', function('g:ListenerInc')) 63 | call self.assertEquals(s:obj.listeners, {'TestEvent': [function('g:ListenerDec')]}) 64 | endfunction " }}} 65 | 66 | "" {{{ 67 | " Должен удалять всех слушателей данного события. 68 | " @covers vim_lib#base#Publisher#.ignore 69 | "" }}} 70 | function! s:Test.testIgnore_deleteEvent() " {{{ 71 | let s:obj = s:Publisher.new() 72 | call s:obj.listen('TestEvent', function('g:ListenerInc')) 73 | call s:obj.listen('TestEvent', function('g:ListenerDec')) 74 | call s:obj.ignore('TestEvent') 75 | call self.assertEquals(s:obj.listeners, {}) 76 | endfunction " }}} 77 | " }}} 78 | " fire {{{ 79 | "" {{{ 80 | " Должен информировать слушателей о наступлении события. 81 | " @covers vim_lib#base#Publisher#.fire 82 | "" }}} 83 | function! s:Test.testFire_callListeners() " {{{ 84 | let s:obj = s:Publisher.new() 85 | call s:obj.listen('TestEvent', function('g:ListenerInc')) 86 | call s:obj.fire('TestEvent') 87 | call self.assertEquals(self.var, 1) 88 | call s:obj.listen('TestEvent', function('g:ListenerDec')) 89 | call s:obj.listen('TestEvent', function('g:ListenerDec')) 90 | call s:obj.listen('TestEvent', function('g:ListenerDec')) 91 | call s:obj.fire('TestEvent') 92 | call self.assertEquals(self.var, -1) 93 | endfunction " }}} 94 | 95 | "" {{{ 96 | " Должен передавать слушателю объект события. 97 | " @covers vim_lib#base#Publisher#.fire 98 | "" }}} 99 | function! s:Test.testFire_giveEvent() " {{{ 100 | let s:obj = s:Publisher.new() 101 | call s:obj.listen('TestEvent', function('g:ListenerPlus')) 102 | call s:obj.fire('TestEvent', {'var': 5}) 103 | call self.assertEquals(self.var, 5) 104 | endfunction " }}} 105 | " }}} 106 | 107 | let g:vim_lib#base#tests#TestPublisher# = s:Test 108 | call s:Test.run() 109 | -------------------------------------------------------------------------------- /autoload/vim_lib/base/tests/TestList.vim: -------------------------------------------------------------------------------- 1 | " Date Create: 2015-01-06 22:20:12 2 | " Last Change: 2015-01-11 15:55:58 3 | " Author: Artur Sh. Mamedbekov (Artur-Mamedbekov@yandex.ru) 4 | " License: GNU GPL v3 (http://www.gnu.org/copyleft/gpl.html) 5 | 6 | let s:List = vim_lib#base#List# 7 | 8 | let s:Test = vim_lib#base#Test#.expand() 9 | 10 | " new {{{ 11 | "" {{{ 12 | " Должен создавать пустой список. 13 | " @covers vim_lib#base#List#.new 14 | "" }}} 15 | function s:Test.testNew_createEmptyList() " {{{ 16 | let l:obj = s:List.new() 17 | call self.assertEquals(l:obj.length(), 0) 18 | endfunction " }}} 19 | 20 | "" {{{ 21 | " Должен использовать массив в качестве начальных данных. 22 | " @covers vim_lib#base#List#.new 23 | "" }}} 24 | function s:Test.testNew_wrapArray() " {{{ 25 | let l:obj = s:List.new([1, 2, 3]) 26 | call self.assertEquals(l:obj.length(), 3) 27 | call self.assertEquals(l:obj.item(1), 2) 28 | endfunction " }}} 29 | " }}} 30 | " item {{{ 31 | "" {{{ 32 | " Должен возвращать значение элемента списка по индексу. 33 | " @covers vim_lib#base#List#.item 34 | "" }}} 35 | function s:Test.testItem_getValue() " {{{ 36 | let l:obj = s:List.new() 37 | call l:obj.item(0, 1) 38 | call l:obj.item(1, 2) 39 | call self.assertEquals(l:obj.item(0), 1) 40 | call self.assertEquals(l:obj.item(1), 2) 41 | endfunction " }}} 42 | 43 | "" {{{ 44 | " Должен выбрасывать исключение, если элемент с заданым индексом отсутствует. 45 | " @covers vim_lib#base#List#.item 46 | "" }}} 47 | function s:Test.testItem_throwExceptionGet() " {{{ 48 | let l:obj = s:List.new() 49 | try 50 | call l:obj.item(0) 51 | call self.fail('testItem_throwException', 'Expected exception is not thrown.') 52 | catch /IndexOutOfRangeException:.*/ 53 | endtry 54 | endfunction " }}} 55 | 56 | "" {{{ 57 | " Должен устанавливать значение элементу списка. 58 | " @covers vim_lib#base#List#.item 59 | "" }}} 60 | function s:Test.testItem_setValue() " {{{ 61 | let l:obj = s:List.new() 62 | call l:obj.item(0, 1) 63 | call self.assertEquals(l:obj.item(0), 1) 64 | endfunction " }}} 65 | 66 | "" {{{ 67 | " Должен выбрасывать исключение, если произошел выход за границы списка. 68 | " @covers vim_lib#base#List#.item 69 | "" }}} 70 | function s:Test.testItem_throwExceptionSet() " {{{ 71 | let l:obj = s:List.new() 72 | try 73 | call l:obj.item(1, 1) 74 | call self.fail('testItem_throwException', 'Expected exception is not thrown.') 75 | catch /IndexOutOfRangeException:.*/ 76 | endtry 77 | endfunction " }}} 78 | " }}} 79 | " list {{{ 80 | "" {{{ 81 | " Должен формировать рекурсивную копию списка и возвращать ее в виде массива. 82 | " @covers vim_lib#base#List#.list 83 | "" }}} 84 | function s:Test.testList_returnArray() " {{{ 85 | let l:obj = s:List.new() 86 | call l:obj.item(0, 1) 87 | call l:obj.item(1, 2) 88 | call l:obj.item(2, 3) 89 | let l:list = l:obj.list() 90 | call l:obj.item(0, 0) 91 | call self.assertEquals(l:list, [1, 2, 3]) 92 | endfunction " }}} 93 | " }}} 94 | " sec {{{ 95 | "" {{{ 96 | " Должен позволять выдилить хвост списка. 97 | " @covers vim_lib#base#List#.sec 98 | "" }}} 99 | function s:Test.testSec_getTail() " {{{ 100 | let l:obj = s:List.new([1, 2, 3, 4, 5]) 101 | call self.assertEquals(l:obj.sec(2).list(), [3, 4, 5]) 102 | endfunction " }}} 103 | 104 | "" {{{ 105 | " Должен позволять выдилить голову списка. 106 | " @covers vim_lib#base#List#.sec 107 | "" }}} 108 | function s:Test.testSec_getHead() " {{{ 109 | let l:obj = s:List.new([1, 2, 3, 4, 5]) 110 | call self.assertEquals(l:obj.sec(0, 3).list(), [1, 2, 3, 4]) 111 | endfunction " }}} 112 | 113 | "" {{{ 114 | " Должен позволять выдилить тело списка. 115 | " @covers vim_lib#base#List#.sec 116 | "" }}} 117 | function s:Test.testSec_getBody() " {{{ 118 | let l:obj = s:List.new([1, 2, 3, 4, 5]) 119 | call self.assertEquals(l:obj.sec(1, 3).list(), [2, 3, 4]) 120 | endfunction " }}} 121 | 122 | "" {{{ 123 | " Можно использовать отрицательные значения параметров. 124 | " @covers vim_lib#base#List#.sec 125 | "" }}} 126 | function s:Test.testSec_negativeParams() " {{{ 127 | let l:obj = s:List.new([1, 2, 3, 4, 5]) 128 | call self.assertEquals(l:obj.sec(0, -1).list(), [1, 2, 3, 4, 5]) 129 | call self.assertEquals(l:obj.sec(0, -2).list(), [1, 2, 3, 4]) 130 | call self.assertEquals(l:obj.sec(-2).list(), [4, 5]) 131 | call self.assertEquals(l:obj.sec(-3, -2).list(), [3, 4]) 132 | endfunction " }}} 133 | " }}} 134 | 135 | let g:vim_lib#base#tests#TestList# = s:Test 136 | call s:Test.run() 137 | -------------------------------------------------------------------------------- /autoload/vim_lib/base/tests/TestEventHandle.vim: -------------------------------------------------------------------------------- 1 | " Date Create: 2015-02-02 17:06:46 2 | " Last Change: 2015-02-04 10:04:17 3 | " Author: Artur Sh. Mamedbekov (Artur-Mamedbekov@yandex.ru) 4 | " License: GNU GPL v3 (http://www.gnu.org/copyleft/gpl.html) 5 | 6 | let s:Test = vim_lib#base#Test#.expand() 7 | let s:Mock = vim_lib#base#tests#EventHandle#Mock# 8 | 9 | let vim_lib#base#tests#TestEventHandle#var = 0 10 | function! vim_lib#base#tests#TestEventHandle#funListener(...) " {{{ 11 | let g:vim_lib#base#tests#TestEventHandle#var += 1 12 | endfunction " }}} 13 | 14 | " listen {{{ 15 | "" {{{ 16 | " Должен сохранять слушателя в словарь обработчиков событий. 17 | " @covers vim_lib#base#EventHandle#.listen 18 | "" }}} 19 | function! s:Test.testListen_saveMethodListener() " {{{ 20 | let l:obj = s:Mock.new() 21 | call l:obj.listen('event', 'listener') 22 | call self.assertEquals(l:obj.listeners['event'][0], 'listener') 23 | endfunction " }}} 24 | 25 | "" {{{ 26 | " Должен сохранять функцию-обработчик в словарь обработчиков событий. 27 | " @covers vim_lib#base#EventHandle#.listen 28 | "" }}} 29 | function! s:Test.testListen_saveFunListener() " {{{ 30 | let l:obj = s:Mock.new() 31 | function! s:Listener() " {{{ 32 | endfunction " }}} 33 | call l:obj.listen('event', function('s:Listener')) 34 | call self.assertEquals(l:obj.listeners['event'][0], function('s:Listener')) 35 | endfunction " }}} 36 | 37 | "" {{{ 38 | " Должен сохранять множество слушателей одного события. 39 | " @covers vim_lib#base#EventHandle#.listen 40 | "" }}} 41 | function! s:Test.testListen_saveListeners() " {{{ 42 | let l:obj = s:Mock.new() 43 | call l:obj.listen('event', 'listener1') 44 | call l:obj.listen('event', 'listener2') 45 | call l:obj.listen('event', 'listener3') 46 | call self.assertEquals(l:obj.listeners['event'][0], 'listener1') 47 | call self.assertEquals(l:obj.listeners['event'][1], 'listener2') 48 | call self.assertEquals(l:obj.listeners['event'][2], 'listener3') 49 | endfunction " }}} 50 | " }}} 51 | " fire {{{ 52 | "" {{{ 53 | " Должен запускать методы-обработчики события. 54 | " @covers vim_lib#base#EventHandle#.fire 55 | "" }}} 56 | function! s:Test.testFire_runMethodListeners() " {{{ 57 | let l:obj = s:Mock.new() 58 | let l:obj.x = 0 59 | function! l:obj.listener(...) " {{{ 60 | let self.x += 1 61 | endfunction " }}} 62 | call l:obj.listen('event', 'listener') 63 | call l:obj.listen('event', 'listener') 64 | call l:obj.fire('event') 65 | call self.assertEquals(l:obj.x, 2) 66 | endfunction " }}} 67 | 68 | "" {{{ 69 | " Должен запускать функции-обработчики события. 70 | " @covers vim_lib#base#EventHandle#.fire 71 | "" }}} 72 | function! s:Test.testFire_runFunListeners() " {{{ 73 | let l:obj = s:Mock.new() 74 | call l:obj.listen('event', function('vim_lib#base#tests#TestEventHandle#funListener')) 75 | call l:obj.listen('event', function('vim_lib#base#tests#TestEventHandle#funListener')) 76 | call l:obj.fire('event') 77 | call self.assertEquals(g:vim_lib#base#tests#TestEventHandle#var, 2) 78 | let g:vim_lib#base#tests#TestEventHandle#var = 0 79 | endfunction " }}} 80 | " }}} 81 | " ignore {{{ 82 | "" {{{ 83 | " Должен удалять все обработчики данного события. 84 | " @covers vim_lib#base#EventHandle#.ignore 85 | "" }}} 86 | function! s:Test.testIgnore_ignoreEvent() " {{{ 87 | let l:obj = s:Mock.new() 88 | call l:obj.listen('event', 'listener') 89 | call l:obj.listen('event', 'listener') 90 | call l:obj.listen('otherEvent', 'listener') 91 | call l:obj.ignore('event') 92 | call self.assertEquals(l:obj.listeners.event, []) 93 | call self.assertEquals(l:obj.listeners.otherEvent, ['listener']) 94 | endfunction " }}} 95 | 96 | "" {{{ 97 | " Должен удалять метод-обработчики с заданым именем. 98 | " @covers vim_lib#base#EventHandle#.ignore 99 | "" }}} 100 | function! s:Test.testIgnore_ignoreMethodListener() " {{{ 101 | let l:obj = s:Mock.new() 102 | call l:obj.listen('event', 'listenerA') 103 | call l:obj.listen('event', 'listenerA') 104 | call l:obj.listen('event', 'listenerB') 105 | call l:obj.listen('event', 'listenerB') 106 | call l:obj.ignore('event', 'listenerA') 107 | call self.assertEquals(l:obj.listeners.event, ['listenerB', 'listenerB']) 108 | endfunction " }}} 109 | 110 | "" {{{ 111 | " Должен удалять функцию-обработчики с заданым именем. 112 | " @covers vim_lib#base#EventHandle#.ignore 113 | "" }}} 114 | function! s:Test.testIgnore_ignoreFunListener() " {{{ 115 | let l:obj = s:Mock.new() 116 | function! s:ListenerA() " {{{ 117 | endfunction " }}} 118 | function! s:ListenerB() " {{{ 119 | endfunction " }}} 120 | call l:obj.listen('event', function('s:ListenerA')) 121 | call l:obj.listen('event', function('s:ListenerA')) 122 | call l:obj.listen('event', function('s:ListenerB')) 123 | call l:obj.listen('event', function('s:ListenerB')) 124 | call l:obj.ignore('event', function('s:ListenerA')) 125 | call self.assertEquals(l:obj.listeners.event, [function('s:ListenerB'), function('s:ListenerB')]) 126 | endfunction " }}} 127 | " }}} 128 | 129 | let g:vim_lib#base#tests#TestEventHandle# = s:Test 130 | call s:Test.run() 131 | -------------------------------------------------------------------------------- /autoload/vim_lib/sys/Content.vim: -------------------------------------------------------------------------------- 1 | " Date Create: 2015-01-11 21:01:41 2 | " Last Change: 2015-02-09 23:02:22 3 | " Author: Artur Sh. Mamedbekov (Artur-Mamedbekov@yandex.ru) 4 | " License: GNU GPL v3 (http://www.gnu.org/copyleft/gpl.html) 5 | 6 | let s:Object = g:vim_lib#base#Object# 7 | 8 | "" {{{ 9 | " Класс представляет содержимое текущего буфера. 10 | "" }}} 11 | let s:Class = s:Object.expand() 12 | 13 | "" {{{ 14 | " Конструктор всегда возвращает единственный экземпляр данного класса. 15 | " @return vim_lib#sys#Content# Содержимое текущего буфера. 16 | "" }}} 17 | function! s:Class.new() " {{{ 18 | return self.singleton 19 | endfunction " }}} 20 | 21 | "" {{{ 22 | " Метод возвращает информацию о текущем положении курсора, или устанавливает его. 23 | " @param hash pos [optional] Информация о положении курсора. Словарь имеет следующую структуру: {'l': номерСтроки, 'c': номерСимвола}. Любой элемент словаря является необязательным. 24 | " @return hash Информация о положении курсора. Структура соответствует аргументу pos. 25 | "" }}} 26 | function! s:Class.pos(...) " {{{ 27 | if exists('a:1') 28 | let l:pos = [bufnr('%'), (has_key(a:1, 'l'))? a:1['l'] : line('.'), (has_key(a:1, 'c'))? a:1['c'] : col('.'), 0] 29 | call setpos('.', l:pos) 30 | else 31 | return {'l': line('.'), 'c': col('.')} 32 | endif 33 | endfunction " }}} 34 | 35 | "" {{{ 36 | " Метод добавляет строку в указанную позицию, сдвигая остальные строки вниз. 37 | " @param integer num Номер целевой строки. 38 | " @param string|array str Вставляемая строка или массив строк. 39 | "" }}} 40 | function! s:Class.add(num, str) " {{{ 41 | call append(a:num - 1, a:str) 42 | endfunction " }}} 43 | 44 | "" {{{ 45 | " Метод возвращает заданную строку или заменяет ее на указанную. 46 | " @param integer num Номер целевой строки. 47 | " @param string str [optional] Заменяющая строка. Если параметр не задан, метод возвращает целевую строку. 48 | "" }}} 49 | function! s:Class.line(num, ...) " {{{ 50 | if exists('a:1') 51 | call setline(a:num, a:1) 52 | else 53 | return getline(a:num) 54 | endif 55 | endfunction " }}} 56 | 57 | "" {{{ 58 | " Метод возвращает строку, выделенную в режиме Visual. 59 | " @return string Строка (или группа строк с завершающим символом переноса строки), выделенная пользователем в режиме Visual или пустая строка, если пользователь ничего не выделил. 60 | "" }}} 61 | function! s:Class.select() " {{{ 62 | let [l:lineStart, l:colStart] = getpos("'<")[1:2] 63 | let [l:lineEnd, l:colEnd] = getpos("'>")[1:2] 64 | let l:lines = getline(l:lineStart, l:lineEnd) 65 | let l:countLines = len(l:lines) 66 | let l:lines[l:countLines - 1] = l:lines[l:countLines - 1][ : l:colEnd - 1] " Удаление не выделенных символов с начала 67 | let l:lines[0] = l:lines[0][l:colStart - 1 : ] " Удаление не выделенных символов с конца 68 | return join(l:lines, "\n") 69 | endfunction " }}} 70 | 71 | "" {{{ 72 | " Метод возвращает слово, расположенное под курсором, или заменяет его на указанное. 73 | " @param string str [optional] Заменяющее слово. Если параметр не задан, метод возвращает текущее слово. 74 | " @return string Слово, расположенное под курсором. 75 | "" }}} 76 | function! s:Class.word(...) " {{{ 77 | if exists('a:1') 78 | let l:col = col('.') 79 | if l:col == 1 " Курсор в начале строки 80 | exe 'normal de' 81 | elseif getline('.')[l:col - 2] !~ '\w' " Курсор в начале слова 82 | exe 'normal de' 83 | else " Курсор в середине слова 84 | exe 'normal bde' 85 | endif 86 | exe 'normal i' . a:1 87 | else 88 | return expand('') 89 | endif 90 | endfunction " }}} 91 | 92 | "" {{{ 93 | " Метод возвращает слово (от пробела, до пробела), расположенное под курсором, или заменяет его на указанное. 94 | " @param string str [optional] Заменяющее слово. Если параметр не задан, метод возвращает текущее слово. 95 | " @return string Слово, расположенное под курсором. 96 | "" }}} 97 | function! s:Class.WORD(...) " {{{ 98 | if exists('a:1') 99 | let l:col = col('.') 100 | if l:col == 1 " Курсор в начале строки 101 | exe 'normal dE' 102 | elseif getline('.')[l:col - 2] =~ ' ' " Курсор в начале слова 103 | exe 'normal dE' 104 | else " Курсор в середине слова 105 | exe 'normal bdE' 106 | endif 107 | exe 'normal i' . a:1 108 | else 109 | return expand('') 110 | endif 111 | endfunction " }}} 112 | 113 | "" {{{ 114 | " Метод записывает в качестве содержимого текущего буфера данную строку. 115 | " @param string str Новое содержимое. 116 | "" }}} 117 | function! s:Class.rewrite(str) " {{{ 118 | normal ggVGd 119 | exe "silent put = '" . a:str . "'" 120 | keepjumps 0d 121 | endfunction " }}} 122 | 123 | "" {{{ 124 | " Метод определяет, пуст ли текущий буфер. 125 | " @return bool 1 - если буфер пуст, иначе - 0. 126 | "" }}} 127 | function! s:Class.isEmpty() " {{{ 128 | return (self.countLine() == 1 && self.line(1) == '') 129 | endfunction " }}} 130 | 131 | "" {{{ 132 | " Метод возвращает количество строк в текущем буфере. 133 | " @return integer Количество строк в текущем буфере. 134 | "" }}} 135 | function! s:Class.countLine() " {{{ 136 | return line('$') 137 | endfunction " }}} 138 | 139 | "" {{{ 140 | " @var vim_lib#sys#Content# Единственный экземпляр класса. 141 | "" }}} 142 | let s:Class.singleton = s:Class.bless() 143 | 144 | let g:vim_lib#sys#Content# = s:Class 145 | -------------------------------------------------------------------------------- /autoload/vim_lib/base/Object.vim: -------------------------------------------------------------------------------- 1 | " Date Create: 2015-01-06 13:26:24 2 | " Last Change: 2015-02-02 14:46:56 3 | " Author: Artur Sh. Mamedbekov (Artur-Mamedbekov@yandex.ru) 4 | " License: GNU GPL v3 (http://www.gnu.org/copyleft/gpl.html) 5 | 6 | "" {{{ 7 | " Объект представляет базовый класс для всех классов библиотеки. 8 | " Для инстанциации данного класса или его подклассов используется метод new, который создает и инициализирует объект класса. 9 | " Отличительной особенностью объекта является наличие свойства class, которое ссылается на класс, экземпляром которого является объект. Классы этого свойства не имеют. 10 | "" }}} 11 | let s:Class = {} 12 | 13 | "" {{{ 14 | " @var hash [optional] Свойства класса, которые будут скопированы в объект данного класса. Конкретные значения свойств могут быть установлены в конструкторе после вызова метода bless. 15 | "" }}} 16 | let s:Class.properties = {} 17 | 18 | "" {{{ 19 | " Метод создает дочерний по отношению к вызываемому классу класс. 20 | " Для создаваемого дочернего класса определено статичное свойство parent, ссылающееся на вызываемый (родительский класс). 21 | " Все методы вызываемого (родительского) класса копируются в создаваемый дочерний класс. 22 | " @return hash Класс, являющийся дочерним по отношению к вызываемому классу. 23 | "" }}} 24 | function! s:Class.expand() " {{{ 25 | let l:child = {'parent': self} 26 | " Перенос ссылок на методы класса в подкласс. {{{ 27 | let l:child.expand = self.expand 28 | let l:child.mix = self.mix 29 | let l:child.new = self.new 30 | let l:child.bless = self.bless 31 | let l:child.typeof = self.typeof 32 | " }}} 33 | return l:child 34 | endfunction " }}} 35 | 36 | "" {{{ 37 | " Метод добавляет логику примеси в вызываемый класс, копируя все методы и свойства примеси в него. 38 | " @param vim_lib#base#Object# class Примесь. 39 | "" }}} 40 | function! s:Class.mix(class) " {{{ 41 | " Примесь методов. {{{ 42 | for l:p in keys(a:class) 43 | if type(a:class[l:p]) == 2 && index(['expand', 'mix', 'bless', 'new', 'typeof'], l:p) == -1 44 | let self[l:p] = a:class[l:p] 45 | endif 46 | endfor 47 | " }}} 48 | " Примесь свойств. {{{ 49 | if has_key(a:class, 'properties') 50 | if !has_key(self, 'properties') 51 | let self.properties = {} 52 | endif 53 | for [l:k, l:v] in items(a:class.properties) 54 | let self.properties[l:k] = deepcopy(l:v) 55 | endfor 56 | endif 57 | " }}} 58 | endfunction " }}} 59 | 60 | "" {{{ 61 | " Метод создает неинициализированный экземпляр вызываемого класса. 62 | " Метод устанавливает свойства class и parent объекта в соответствии с требованиями экземпляра класса. 63 | " Метод копирует не статичные методы класса в объект. Статичными методами класса являются методы, начинающиеся на два знака подчеркивания (__). 64 | " @param hash parent [optional] Инициализированный экземпляр родительского класса. Если параметр не задан, используется конструктор по умолчанию для родительского класса. 65 | " @return hash Неинизиализированный экземпляр класса. 66 | "" }}} 67 | function! s:Class.bless(...) " {{{ 68 | let l:obj = {'class': self, 'parent': (exists('a:1'))? a:1 : self.parent.new()} 69 | " Перенос частных методов из класса в объект. {{{ 70 | for l:p in keys(self) 71 | if type(self[l:p]) == 2 && index(['expand', 'mix', 'bless', 'new', 'typeof'], l:p) == -1 && l:p[0:1] != '__' 72 | let l:obj[l:p] = self[l:p] 73 | endif 74 | endfor 75 | " }}} 76 | " Перенос свойств из класса в объект. {{{ 77 | if has_key(self, 'properties') 78 | for [l:k, l:v] in items(self.properties) 79 | let l:obj[l:k] = deepcopy(l:v) 80 | endfor 81 | endif 82 | " }}} 83 | return l:obj 84 | endfunction " }}} 85 | 86 | "" {{{ 87 | " Метод создает экземпляр вызываемого класса. 88 | " Метод сохраняет ссылку на инстанциируемый (вызываемый) класс в свойстве class объекта. 89 | " Если инстанциируемый (вызываемый) класс является дочерним, метод создает ссылку на объект родительского класса в свойстве parent объекта. 90 | " Метод не копирует методы в экземпляры класса, потому следует сделать это в самостоятельно. 91 | " Данный метод может быть переопределен в дочерних классах с определением аргументов, используемых для инициализации объекта. Делается это следующим образом: 92 | " function! s:Child.new(a, b) 93 | " let s:obj = self.bless(self.parent.new(a:a)) " Создание объекта через вызов конструктора родительского класса. 94 | " let s:obj.b = a:b " Инициализируемое свойство объекта. 95 | " let s:obj.c = 1 " Не инициализируемое свойство объекта. 96 | " return s:obj 97 | " endfunction 98 | " @return hash экземпляр вызываемого класса. 99 | "" }}} 100 | function! s:Class.new() " {{{ 101 | if has_key(self, 'parent') 102 | let l:obj = self.bless() 103 | else 104 | let l:obj = {'class': self} 105 | endif 106 | return l:obj 107 | endfunction " }}} 108 | 109 | "" {{{ 110 | " Метод определяет, является ли вызываемый класс дочерним по отношению к данному. 111 | " @param hash type Целевой класс. 112 | " @return bool Если вызываемый класс является целевым или его потомком, метод возвращает 1, иначе 0. 113 | "" }}} 114 | function! s:Class.typeof(type) " {{{ 115 | let l:currentClass = self 116 | while l:currentClass != a:type 117 | if !has_key(l:currentClass, 'parent') 118 | return 0 119 | endif 120 | let l:currentClass = l:currentClass.parent 121 | endwhile 122 | return 1 123 | endfunction " }}} 124 | 125 | let g:vim_lib#base#Object# = s:Class 126 | -------------------------------------------------------------------------------- /autoload/vim_lib/base/File.vim: -------------------------------------------------------------------------------- 1 | " Date Create: 2015-01-12 23:45:01 2 | " Last Change: 2015-03-05 11:49:48 3 | " Author: Artur Sh. Mamedbekov (Artur-Mamedbekov@yandex.ru) 4 | " License: GNU GPL v3 (http://www.gnu.org/copyleft/gpl.html) 5 | 6 | let s:Object = g:vim_lib#base#Object# 7 | 8 | let s:Class = s:Object.expand() 9 | 10 | "" {{{ 11 | " @var string Переменная хранит слеш, используемый в данной операционной системе. 12 | "" }}} 13 | let s:Class.slash = (has('win16') || has('win32') || has('win64') || has('win95'))? '\' : '/' 14 | 15 | "" {{{ 16 | " Конструктор создает объектное представление файла. 17 | " @param string address Относительный адрес файла. Основой адреса является текущий файл редактора. 18 | " @return vim_lib#base#File# Целевой файл. 19 | "" }}} 20 | function! s:Class.relative(address) " {{{ 21 | let l:obj = self.bless() 22 | let l:obj.address = fnamemodify(expand('%'), ':p:h') . '/' . a:address 23 | return l:obj 24 | endfunction " }}} 25 | 26 | "" {{{ 27 | " Конструктор создает объектное представление файла. 28 | " @param string Абсолютный адрес файла. 29 | " @return vim_lib#base#File# Целевой файл. 30 | "" }}} 31 | function! s:Class.absolute(address) " {{{ 32 | let l:obj = self.bless() 33 | let l:obj.address = expand(a:address) 34 | return l:obj 35 | endfunction " }}} 36 | 37 | "" {{{ 38 | " Метод возвращает адрес файла. Адрес каталогов не завершается слешем. 39 | " @return string Адрес файла. 40 | "" }}} 41 | function! s:Class.getAddress() " {{{ 42 | return self.address 43 | endfunction " }}} 44 | 45 | "" {{{ 46 | " Метод возвращает имя файла. 47 | " @return string Имя файла. 48 | "" }}} 49 | function! s:Class.getName() " {{{ 50 | return fnamemodify(self.getAddress(), ':t') 51 | endfunction " }}} 52 | 53 | "" {{{ 54 | " Метод возвращает каталог, в котором расположен файл. 55 | " @return vim_lib#base#File# Родительский каталог файла. 56 | "" }}} 57 | function! s:Class.getDir() " {{{ 58 | return self.class.absolute(fnamemodify(self.getAddress(), ':h')) 59 | endfunction " }}} 60 | 61 | "" {{{ 62 | " Метод возвращает файл, предположительно содержащийся в данном каталоге. 63 | " @param string name Имя целевого файла. 64 | " @return vim_lib#base#File# Целевой файл. 65 | "" }}} 66 | function! s:Class.getChild(name) " {{{ 67 | return self.class.absolute(self.getAddress() . self.class.slash . a:name) 68 | endfunction " }}} 69 | 70 | "" {{{ 71 | " Метод возвращает массив имен файлов, содержащихся в данном каталоге, за исключением файлов . и .. 72 | " @return array Массив имен файлов, содержащихся в данном каталоге. 73 | "" }}} 74 | function! s:Class.getChildren() " {{{ 75 | let l:address = self.getAddress() 76 | let l:childAddress = split(globpath(l:address, '*') . "\n" . globpath(l:address, '.*'), "\n") 77 | let l:childNames = [] 78 | for l:file in l:childAddress 79 | if l:file !~# '\/\.\.\/\?$' && l:file !~# '\/\.\/\?$' 80 | call add(l:childNames, fnamemodify(l:file, ':t')) 81 | endif 82 | endfor 83 | return l:childNames 84 | endfunction " }}} 85 | 86 | "" {{{ 87 | " Метод создает файл, если он еще не был создан. 88 | "" }}} 89 | function! s:Class.createFile() " {{{ 90 | call writefile([''], self.getAddress()) 91 | endfunction " }}} 92 | 93 | "" {{{ 94 | " Метод создает каталог, если он еще не был создан. 95 | "" }}} 96 | function! s:Class.createDir() " {{{ 97 | if !self.isExists() 98 | call mkdir(self.getAddress(), '') 99 | endif 100 | endfunction " }}} 101 | 102 | "" {{{ 103 | " Метод удаляет файл. 104 | "" }}} 105 | function! s:Class.deleteFile() " {{{ 106 | call delete(self.getAddress()) 107 | endfunction " }}} 108 | 109 | "" {{{ 110 | " Метод удаляет каталог. 111 | "" }}} 112 | function! s:Class.deleteDir() " {{{ 113 | if has("win16") || has("win32") || has("win64") 114 | call system('rmdir /s /q ' . self.getAddress()) 115 | else 116 | call system('rm -rf ' . self.getAddress()) 117 | endif 118 | endfunction " }}} 119 | 120 | "" {{{ 121 | " Метод считывает содержимое файла в массив. 122 | " @return array Массив строк, содержащихся в файле. 123 | "" }}} 124 | function! s:Class.read() " {{{ 125 | return readfile(self.getAddress()) 126 | endfunction " }}} 127 | 128 | "" {{{ 129 | " Метод записывает строку в конец файла. 130 | " @param string str Записываемая строка. 131 | "" }}} 132 | function! s:Class.write(str) " {{{ 133 | call self.rewrite(add(self.read(), a:str)) 134 | endfunction " }}} 135 | 136 | "" {{{ 137 | " Метод перезаписывает содержимое файла. 138 | " @param array strs Массив строк, записываемых в файл. 139 | "" }}} 140 | function! s:Class.rewrite(strs) " {{{ 141 | call writefile(a:strs, self.getAddress()) 142 | endfunction " }}} 143 | 144 | "" {{{ 145 | " Метод определяет, является ли данный компонент файловой системы файлом. 146 | " @return bool true - если это файл, иначе - false. 147 | "" }}} 148 | function! s:Class.isFile() " {{{ 149 | return !isdirectory(self.getAddress()) 150 | endfunction " }}} 151 | 152 | "" {{{ 153 | " Метод определяет, является ли данный компонент файловой системы каталогом. 154 | " @return bool true - если это каталог, иначе - false. 155 | "" }}} 156 | function! s:Class.isDir() " {{{ 157 | return isdirectory(self.getAddress()) 158 | endfunction " }}} 159 | 160 | "" {{{ 161 | " Метод определяет, существует ли данный файл, или файл с данным именем в каталоге. 162 | " @param string name [optional] Искомый в данном каталоге файл. Если параметр не задан, метод оценивает существование файла вызываемого объекта. 163 | " @return bool true - если файл существует, иначе - false. 164 | "" }}} 165 | function! s:Class.isExists(...) " {{{ 166 | if exists('a:1') 167 | return empty(glob(self.getAddress() . '/' . a:1))? 0 : 1 168 | else 169 | return empty(glob(self.getAddress()))? 0 : 1 170 | endif 171 | endfunction " }}} 172 | 173 | let g:vim_lib#base#File# = s:Class 174 | -------------------------------------------------------------------------------- /autoload/vim_lib/sys/tests/TestPlugin.vim: -------------------------------------------------------------------------------- 1 | " Date Create: 2015-01-09 15:13:39 2 | " Last Change: 2015-02-09 00:48:13 3 | " Author: Artur Sh. Mamedbekov (Artur-Mamedbekov@yandex.ru) 4 | " License: GNU GPL v3 (http://www.gnu.org/copyleft/gpl.html) 5 | 6 | let s:Plugin = vim_lib#sys#Plugin# 7 | let s:NullPlugin = vim_lib#sys#NullPlugin# 8 | 9 | let s:Test = vim_lib#base#Test#.expand() 10 | 11 | "" {{{ 12 | " Удаление всех зарегистрированных плагинов после выполнения теста. 13 | "" }}} 14 | function! s:Test.afterTest() " {{{ 15 | let s:Plugin.plugins = {} 16 | endfunction " }}} 17 | 18 | " new {{{ 19 | "" {{{ 20 | " Должен создавать объект плагина. 21 | " @covers vim_lib#base#Plugin#.new 22 | "" }}} 23 | function! s:Test.testNew_createPlugin() " {{{ 24 | let l:obj = s:Plugin.new('vim_lib_testPlugin', '1') 25 | call self.assertTrue(l:obj.class.typeof(s:Plugin)) 26 | endfunction " }}} 27 | 28 | "" {{{ 29 | " Должен возвращать объект класса vim_lib#sys#NullPlugin#, если плагин отключен. 30 | " @covers vim_lib#base#Plugin#.new 31 | "" }}} 32 | function! s:Test.testNew_offPlugin() " {{{ 33 | let g:vim_lib_testPlugin# = 0 34 | let l:obj = s:Plugin.new('vim_lib_testPlugin', '1') 35 | call self.assertTrue(l:obj.class.typeof(s:NullPlugin)) 36 | unlet g:vim_lib_testPlugin# 37 | endfunction " }}} 38 | 39 | "" {{{ 40 | " Должен возвращать объект класса vim_lib#sys#NullPlugin#, если версия редактора отличается от требуемой. 41 | " @covers vim_lib#base#Plugin#.new 42 | "" }}} 43 | function! s:Test.testNew_verifyVersion() " {{{ 44 | let l:obj = s:Plugin.new('vim_lib_testPlugin', '1', {'version': v:version + 1}) 45 | call self.assertTrue(l:obj.class.typeof(s:NullPlugin)) 46 | endfunction " }}} 47 | 48 | "" {{{ 49 | " Должен возвращать объект класса vim_lib#sys#NullPlugin#, если отсутствует требуемый модуль окружения. 50 | " @covers vim_lib#base#Plugin#.new 51 | "" }}} 52 | function! s:Test.testNew_verifyEnv() " {{{ 53 | let l:obj = s:Plugin.new('vim_lib_testPlugin', '1', {'has': ['vim_lib-testModule']}) 54 | call self.assertTrue(l:obj.class.typeof(s:NullPlugin)) 55 | endfunction " }}} 56 | 57 | "" {{{ 58 | " Должен возвращать объект класса vim_lib#sys#NullPlugin#, если отсутствует требуемый плагин. 59 | " @covers vim_lib#base#Plugin#.new 60 | "" }}} 61 | function! s:Test.testNew_verifyPlugins() " {{{ 62 | let l:obj = s:Plugin.new('vim_lib_testPlugin', '1', {'plugins': ['vim_lib-testPlugin']}) 63 | call self.assertTrue(l:obj.class.typeof(s:NullPlugin)) 64 | endfunction " }}} 65 | 66 | "" {{{ 67 | " Должен сохранять имя плагина. 68 | " @covers vim_lib#base#Plugin#.new 69 | "" }}} 70 | function! s:Test.testNew_saveName() " {{{ 71 | let l:obj = s:Plugin.new('vim_lib_testPlugin', '1') 72 | call self.assertEquals(l:obj.getName(), 'vim_lib_testPlugin') 73 | endfunction " }}} 74 | 75 | "" {{{ 76 | " Должен сохранять версию плагина. 77 | " @covers vim_lib#base#Plugin#.new 78 | "" }}} 79 | function! s:Test.testNew_saveVersion() " {{{ 80 | let l:obj = s:Plugin.new('vim_lib_testPlugin', '1') 81 | call self.assertEquals(l:obj.getVersion(), '1') 82 | endfunction " }}} 83 | 84 | "" {{{ 85 | " Должен сохранять и восстанавливать объекты из пула. 86 | " @covers vim_lib#sys#Plugin#.new 87 | "" }}} 88 | function s:Test.testNew_usePool() " {{{ 89 | let l:obj = s:Plugin.new('vim_lib_testPlugin', '1') 90 | let l:obj.test = 1 91 | let l:obj2 = s:Plugin.new(l:obj.getName(), '2') 92 | call self.assertEquals(l:obj.test, l:obj2.test) 93 | call self.assertEquals(l:obj2.getVersion(), '1') 94 | endfunction " }}} 95 | " }}} 96 | " getName, getVersion {{{ 97 | "" {{{ 98 | " Должен возвращать имя плагина. 99 | " @covers vim_lib#base#Plugin#.getName 100 | "" }}} 101 | function! s:Test.testGetName() " {{{ 102 | let l:obj = s:Plugin.new('vim_lib_testPlugin', '1') 103 | call self.assertEquals(l:obj.getName(), 'vim_lib_testPlugin') 104 | endfunction " }}} 105 | 106 | "" {{{ 107 | " Должен возвращать версию плагина. 108 | " @covers vim_lib#base#Plugin#.getVersion 109 | "" }}} 110 | function! s:Test.testGetVersion() " {{{ 111 | let l:obj = s:Plugin.new('vim_lib_testPlugin', '1') 112 | call self.assertEquals(l:obj.getVersion(), '1') 113 | endfunction " }}} 114 | " }}} 115 | " comm, map {{{ 116 | "" {{{ 117 | " Должен устанавливать команду плагина. 118 | " @covers vim_lib#base#Plugin#.comm 119 | "" }}} 120 | function! s:Test.testComm() " {{{ 121 | let l:obj = s:Plugin.new('vim_lib_testPlugin', '1') 122 | call l:obj.comm('testComm', 'testMethod') 123 | call self.assertEquals(l:obj.commands, {'testComm': 'testMethod'}) 124 | endfunction " }}} 125 | 126 | "" {{{ 127 | " Должен устанавливать привязку плагина. 128 | " @covers vim_lib#base#Plugin#.map 129 | "" }}} 130 | function! s:Test.testMap() " {{{ 131 | let l:obj = s:Plugin.new('vim_lib_testPlugin', '1') 132 | call l:obj.map('q', 'testMethod') 133 | call self.assertEquals(l:obj.keyListeners, {'testMethod': 'q'}) 134 | endfunction " }}} 135 | " }}} 136 | " reg {{{ 137 | "" {{{ 138 | " Должен переопределять опции плагина. 139 | " @covers vim_lib#base#Plugin#.reg 140 | "" }}} 141 | function! s:Test.testReg_resetOptions() " {{{ 142 | let g:vim_lib_testPlugin#options = {'a': 0} 143 | let l:obj = s:Plugin.new('vim_lib_testPlugin', '1') 144 | let l:obj.a = 1 145 | call l:obj.reg() 146 | call self.assertEquals(l:obj.a, 0) 147 | unlet g:vim_lib_testPlugin# 148 | endfunction " }}} 149 | 150 | "" {{{ 151 | " Должен переопределять привязки плагина. 152 | " @covers vim_lib#base#Plugin#.reg 153 | "" }}} 154 | function! s:Test.testReg_resetKeyListeners() " {{{ 155 | let g:vim_lib_testPlugin#map = {'testMethodA': 'a'} 156 | let l:obj = s:Plugin.new('vim_lib_testPlugin', '1') 157 | call l:obj.map('q', 'testMethodA') 158 | call l:obj.reg() 159 | call self.assertEquals(l:obj.keyListeners, {'testMethodA': 'a'}) 160 | unlet g:vim_lib_testPlugin# 161 | endfunction " }}} 162 | " }}} 163 | 164 | let g:vim_lib#base#tests#TestPlugin# = s:Test 165 | call s:Test.run() 166 | -------------------------------------------------------------------------------- /autoload/vim_lib/sys/tests/TestContent.vim: -------------------------------------------------------------------------------- 1 | " Date Create: 2015-01-11 21:34:18 2 | " Last Change: 2015-02-08 12:14:22 3 | " Author: Artur Sh. Mamedbekov (Artur-Mamedbekov@yandex.ru) 4 | " License: GNU GPL v3 (http://www.gnu.org/copyleft/gpl.html) 5 | 6 | let s:Content = vim_lib#sys#Content# 7 | 8 | let s:Test = vim_lib#base#Test#.expand() 9 | 10 | function! s:Test.beforeRun() " {{{ 11 | new 12 | endfunction " }}} 13 | 14 | function! s:Test.beforeTest() " {{{ 15 | call setline(1, 'Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod') 16 | call setline(2, 'tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.') 17 | endfunction " }}} 18 | 19 | function! s:Test.afterTest() " {{{ 20 | call setpos('.', [bufnr('%'), 1, 1, 0]) 21 | endfunction " }}} 22 | 23 | function! s:Test.afterRun() " {{{ 24 | q! 25 | endfunction " }}} 26 | 27 | " new {{{ 28 | "" {{{ 29 | " Класс является Singleton. 30 | " @covers vim_lib#sys#Content#.new 31 | "" }}} 32 | function! s:Test.testNew() " {{{ 33 | let l:obj = s:Content.new() 34 | call self.assertEquals(l:obj, s:Content.new()) 35 | endfunction " }}} 36 | " }}} 37 | " pos {{{ 38 | "" {{{ 39 | " Должен устанавливать позицию курсора. 40 | " @covers vim_lib#sys#Content#.pos 41 | "" }}} 42 | function! s:Test.testPos_setAll() " {{{ 43 | let l:obj = s:Content.new() 44 | call l:obj.pos({'l': 2, 'c': 5}) 45 | call self.assertEquals([line('.'), col('.')], [2, 5]) 46 | endfunction " }}} 47 | 48 | "" {{{ 49 | " Должен устанавливать текущую строку. 50 | " @covers vim_lib#sys#Content#.pos 51 | "" }}} 52 | function! s:Test.testPos_setLine() " {{{ 53 | let l:obj = s:Content.new() 54 | call l:obj.pos({'l': 2}) 55 | call self.assertEquals([line('.'), col('.')], [2, 1]) 56 | endfunction " }}} 57 | 58 | "" {{{ 59 | " Должен устанавливать текущую столбец. 60 | " @covers vim_lib#sys#Content#.pos 61 | "" }}} 62 | function! s:Test.testPos_setCol() " {{{ 63 | let l:obj = s:Content.new() 64 | call l:obj.pos({'c': 5}) 65 | call self.assertEquals([line('.'), col('.')], [1, 5]) 66 | endfunction " }}} 67 | 68 | "" {{{ 69 | " Должен возвращать позицию курсора. 70 | " @covers vim_lib#sys#Content#.pos 71 | "" }}} 72 | function! s:Test.testPos_getPos() " {{{ 73 | let l:obj = s:Content.new() 74 | call self.assertEquals(l:obj.pos(), {'l': 1, 'c': 1}) 75 | endfunction " }}} 76 | " }}} 77 | " add, line {{{ 78 | "" {{{ 79 | " Должен добавлять строку в указанную позицию, сдвигая остальные строки вниз. 80 | " @covers vim_lib#sys#Content#.add 81 | "" }}} 82 | function! s:Test.testAdd() " {{{ 83 | let l:obj = s:Content.new() 84 | call l:obj.add(2, 'Test') 85 | call self.assertEquals(getline(2), 'Test') 86 | call self.assertEquals(getline(3), 'tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.') 87 | endfunction " }}} 88 | 89 | "" {{{ 90 | " Должен возвращать заданную строку. 91 | " @covers vim_lib#sys#Content#.line 92 | "" }}} 93 | function! s:Test.testLine_getLine() " {{{ 94 | let l:obj = s:Content.new() 95 | call self.assertEquals(l:obj.line(2), 'tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.') 96 | endfunction " }}} 97 | 98 | "" {{{ 99 | " Должен задавать строку. 100 | " @covers vim_lib#sys#Content#.line 101 | "" }}} 102 | function! s:Test.testLine_setLine() " {{{ 103 | let l:obj = s:Content.new() 104 | call l:obj.line(2, 'Test') 105 | call self.assertEquals(getline(2), 'Test') 106 | endfunction " }}} 107 | " }}} 108 | " word, WORD {{{ 109 | "" {{{ 110 | " Должен возвращать текущее слово. 111 | " @covers vim_lib#sys#Content#.word 112 | "" }}} 113 | function! s:Test.testWord_getWord() " {{{ 114 | let l:obj = s:Content.new() 115 | call self.assertEquals(l:obj.word(), 'Lorem') 116 | call setpos('.', [bufnr('%'), 1, 2, 0]) 117 | call self.assertEquals(l:obj.word(), 'Lorem') 118 | call setpos('.', [bufnr('%'), 1, 5, 0]) 119 | call self.assertEquals(l:obj.word(), 'Lorem') 120 | endfunction " }}} 121 | 122 | "" {{{ 123 | " Должен устанавливать текущее слово. 124 | " @covers vim_lib#sys#Content#.word 125 | "" }}} 126 | function! s:Test.testWord_setWord() " {{{ 127 | let l:obj = s:Content.new() 128 | call l:obj.word('Test') " Курсор на начале слова 129 | call setpos('.', [bufnr('%'), 1, 1, 0]) 130 | call self.assertEquals(expand(''), 'Test') 131 | call setpos('.', [bufnr('%'), 1, 2, 0]) 132 | call l:obj.word('Lorem') " Курсор в слове 133 | call self.assertEquals(expand(''), 'Lorem') 134 | call setpos('.', [bufnr('%'), 1, 5, 0]) 135 | call l:obj.word('Test') " Курсор в конце слова 136 | call self.assertEquals(expand(''), 'Test') 137 | endfunction " }}} 138 | 139 | "" {{{ 140 | " Должен возвращать текущее СЛОВО. 141 | " @covers vim_lib#sys#Content#.WORD 142 | "" }}} 143 | function! s:Test.testWORD_getWord() " {{{ 144 | let l:obj = s:Content.new() 145 | call self.assertEquals(l:obj.WORD(), 'Lorem') 146 | call setpos('.', [bufnr('%'), 1, 2, 0]) 147 | call self.assertEquals(l:obj.WORD(), 'Lorem') 148 | call setpos('.', [bufnr('%'), 1, 5, 0]) 149 | call self.assertEquals(l:obj.WORD(), 'Lorem') 150 | endfunction " }}} 151 | 152 | "" {{{ 153 | " Должен устанавливать текущее СЛОВО. 154 | " @covers vim_lib#sys#Content#.WORD 155 | "" }}} 156 | function! s:Test.testWORD_setWord() " {{{ 157 | let l:obj = s:Content.new() 158 | call l:obj.WORD('Test') " Курсор на начале слова 159 | call self.assertEquals(expand(''), 'Test') 160 | call setpos('.', [bufnr('%'), 1, 2, 0]) 161 | call l:obj.WORD('Lorem') " Курсор в слове 162 | call self.assertEquals(expand(''), 'Lorem') 163 | call setpos('.', [bufnr('%'), 1, 5, 0]) 164 | call l:obj.WORD('Test') " Курсор в конце слова 165 | call self.assertEquals(expand(''), 'Test') 166 | endfunction " }}} 167 | " }}} 168 | " rewrite {{{ 169 | "" {{{ 170 | " Должен перезаписывать содержимое буфера. 171 | " @covers vim_lib#sys#Content#.rewrite 172 | "" }}} 173 | function! s:Test.testRewrite() " {{{ 174 | let l:obj = s:Content.new() 175 | call l:obj.rewrite('Hello') 176 | call self.assertEquals(l:obj.line(1), 'Hello') 177 | endfunction " }}} 178 | " }}} 179 | " isEmpty {{{ 180 | "" {{{ 181 | " Должен возвращать 1, когда буфер пуст, иначе - 0. 182 | " @covers vim_lib#sys#Content#.isEmpty 183 | "" }}} 184 | function! s:Test.testIsEmpty() " {{{ 185 | let l:obj = s:Content.new() 186 | call self.assertFalse(l:obj.isEmpty()) 187 | call l:obj.rewrite('') 188 | call self.assertTrue(l:obj.isEmpty()) 189 | endfunction " }}} 190 | " }}} 191 | " countLine {{{ 192 | "" {{{ 193 | " Должен определять число строк в текущем буфере. 194 | " @covers vim_lib#sys#Content#.countLine 195 | "" }}} 196 | function! s:Test.testCountLine() " {{{ 197 | let l:obj = s:Content.new() 198 | call self.assertEquals(l:obj.countLine(), 2) 199 | endfunction " }}} 200 | " }}} 201 | 202 | let g:vim_lib#base#tests#TestContent# = s:Test 203 | call s:Test.run() 204 | -------------------------------------------------------------------------------- /autoload/vim_lib/base/tests/TestFile.vim: -------------------------------------------------------------------------------- 1 | " Date Create: 2015-01-13 09:00:17 2 | " Last Change: 2015-02-22 13:57:55 3 | " Author: Artur Sh. Mamedbekov (Artur-Mamedbekov@yandex.ru) 4 | " License: GNU GPL v3 (http://www.gnu.org/copyleft/gpl.html) 5 | 6 | let s:File = vim_lib#base#File# 7 | 8 | let s:Test = vim_lib#base#Test#.expand() 9 | 10 | " relative, absolute {{{ 11 | "" {{{ 12 | " Должен сохранять адрес файла относительно файла текущего буфера. 13 | " @covers vim_lib#base#File#.relative 14 | "" }}} 15 | function! s:Test.testRelative_saveAddress() " {{{ 16 | let l:obj = s:File.relative('File/file.txt') 17 | call self.assertEquals(l:obj.getAddress(), fnamemodify(expand('%'), ':p:h') . '/' . 'File/file.txt') 18 | endfunction " }}} 19 | 20 | "" {{{ 21 | " Должен сохранять абсолютный адрес файла. 22 | " @covers vim_lib#base#File#.absolute 23 | "" }}} 24 | function! s:Test.testAbsolute_saveAddress() " {{{ 25 | let l:obj = s:File.absolute('/vim_lib/base/tests/File/file.txt') 26 | call self.assertEquals(l:obj.getAddress(), '/vim_lib/base/tests/File/file.txt') 27 | endfunction " }}} 28 | " }}} 29 | " getAddress, getName, getDir {{{ 30 | "" {{{ 31 | " Должен возвращать адрес файла. 32 | " @covers vim_lib#base#File#.getAddress 33 | "" }}} 34 | function! s:Test.testGetAddress() " {{{ 35 | let l:obj = s:File.relative('File/file.txt') 36 | call self.assertEquals(l:obj.getAddress(), fnamemodify(expand('%'), ':p:h') . '/' . 'File/file.txt') 37 | endfunction " }}} 38 | 39 | "" {{{ 40 | " Должен возвращать имя файла. 41 | " @covers vim_lib#base#File#.name 42 | "" }}} 43 | function! s:Test.testName_returnFileName() " {{{ 44 | let l:obj = s:File.relative('File/dir') 45 | call self.assertEquals(l:obj.getName(), 'dir') 46 | let l:obj = s:File.relative('File/dir/dir/file.txt') 47 | call self.assertEquals(l:obj.getName(), 'file.txt') 48 | endfunction " }}} 49 | 50 | "" {{{ 51 | " Должен возвращать родительский каталог. 52 | " @covers vim_lib#base#File#.dir 53 | "" }}} 54 | function! s:Test.testGetDir_returnParentDir() " {{{ 55 | let l:obj = s:File.relative('File/dir') 56 | call self.assertEquals(l:obj.getDir().getAddress(), fnamemodify(expand('%'), ':p:h') . '/' . 'File') 57 | call self.assertTrue(l:obj.getDir().class.typeof(s:File)) 58 | let l:obj = s:File.relative('File/dir/dir/file.txt') 59 | call self.assertEquals(l:obj.getDir().getAddress(), fnamemodify(expand('%'), ':p:h') . '/' . 'File/dir/dir') 60 | endfunction " }}} 61 | " }}} 62 | " getChild, getChildren {{{ 63 | "" {{{ 64 | " Должен возвращать дочерний файл. 65 | " @covers vim_lib#base#File#.getChild 66 | "" }}} 67 | function! s:Test.testGetChild() " {{{ 68 | let l:obj = s:File.relative('File/dir').getChild('file.txt') 69 | call self.assertEquals(l:obj.getAddress(), fnamemodify(expand('%'), ':p:h') . '/' . 'File/dir/file.txt') 70 | endfunction " }}} 71 | 72 | "" {{{ 73 | " Должен возвращать список имен дочерних файлов. 74 | " @covers vim_lib#base#File#.getChildren 75 | "" }}} 76 | function! s:Test.testGetChildren() " {{{ 77 | let l:obj = s:File.relative('File/dir') 78 | call self.assertEquals(l:obj.getChildren(), ['dir', 'file.txt']) 79 | endfunction " }}} 80 | " }}} 81 | " isExists, isFile, isDir {{{ 82 | "" {{{ 83 | " Должен определять, существует ли файл вызываемого объекта. 84 | " @covers vim_lib#base#File#.isExists 85 | "" }}} 86 | function! s:Test.testIsExists_currentFileExists() " {{{ 87 | let l:obj = s:File.relative('File/file.txt') 88 | call self.assertTrue(l:obj.isExists()) 89 | let l:obj = s:File.relative('File/dir') 90 | call self.assertTrue(l:obj.isExists()) 91 | let l:obj = s:File.relative('File/void') 92 | call self.assertFalse(l:obj.isExists()) 93 | endfunction " }}} 94 | 95 | "" {{{ 96 | " Должен определять, существует ли заданный файл. 97 | " @covers vim_lib#base#File#.isExists 98 | "" }}} 99 | function! s:Test.testIsExists_thisFileExists() " {{{ 100 | let l:obj = s:File.relative('File/dir') 101 | call self.assertTrue(l:obj.isExists('file.txt')) 102 | call self.assertTrue(l:obj.isExists('dir')) 103 | call self.assertFalse(l:obj.isExists('void')) 104 | endfunction " }}} 105 | 106 | "" {{{ 107 | " Должен определять, является ли данный компонент файлом. 108 | " @covers vim_lib#base#File#.isFile 109 | "" }}} 110 | function! s:Test.testIsFile() " {{{ 111 | let l:obj = s:File.relative('File/file.txt') 112 | call self.assertTrue(l:obj.isFile()) 113 | let l:obj = s:File.relative('File/dir') 114 | call self.assertFalse(l:obj.isFile()) 115 | endfunction " }}} 116 | 117 | "" {{{ 118 | " Должен определять, является ли данный компонент каталогом. 119 | " @covers vim_lib#base#File#.isDir 120 | "" }}} 121 | function! s:Test.testIsDir() " {{{ 122 | let l:obj = s:File.relative('File/dir') 123 | call self.assertTrue(l:obj.isDir()) 124 | let l:obj = s:File.relative('File/file.txt') 125 | call self.assertFalse(l:obj.isDir()) 126 | endfunction " }}} 127 | " }}} 128 | " createDir, createFile, deleteFile, deleteDir {{{ 129 | "" {{{ 130 | " Должен создавать новый каталог. 131 | " @covers vim_lib#base#File#.createDir 132 | "" }}} 133 | function! s:Test.testCreateDir() " {{{ 134 | let l:obj = s:File.relative('File/newDir') 135 | call self.assertFalse(l:obj.isExists()) 136 | call l:obj.createDir() 137 | call self.assertTrue(l:obj.isExists()) 138 | call self.assertTrue(l:obj.isDir()) 139 | call system('rmdir ' . l:obj.getAddress()) 140 | endfunction " }}} 141 | 142 | "" {{{ 143 | " Должен создавать новый файл. 144 | " @covers vim_lib#base#File#.createFile 145 | "" }}} 146 | function! s:Test.testCreateFile() " {{{ 147 | let l:obj = s:File.relative('File/newFile.txt') 148 | call self.assertFalse(l:obj.isExists()) 149 | call l:obj.createFile() 150 | call self.assertTrue(l:obj.isExists()) 151 | call self.assertTrue(l:obj.isFile()) 152 | call delete(l:obj.getAddress()) 153 | endfunction " }}} 154 | 155 | "" {{{ 156 | " Должен удалять файл. 157 | " @covers vim_lib#base#File#.deleteFile 158 | "" }}} 159 | function! s:Test.testDeleteFile() " {{{ 160 | call writefile([''], fnamemodify(expand('%'), ':p:h') . '/File/newfile.txt') 161 | let l:obj = s:File.relative('File/newfile.txt') 162 | call self.assertTrue(l:obj.isExists()) 163 | call l:obj.deleteFile() 164 | call self.assertFalse(l:obj.isExists()) 165 | endfunction " }}} 166 | 167 | "" {{{ 168 | " Должен удалять каталог. 169 | " @covers vim_lib#base#File#.deleteDir 170 | "" }}} 171 | function! s:Test.testDeleteDir() " {{{ 172 | call mkdir(fnamemodify(expand('%'), ':p:h') . '/File/newdir') 173 | let l:obj = s:File.relative('File/newdir') 174 | call self.assertTrue(l:obj.isExists()) 175 | call l:obj.deleteDir() 176 | call self.assertFalse(l:obj.isExists()) 177 | endfunction " }}} 178 | " }}} 179 | " read, write, rewrite {{{ 180 | "" {{{ 181 | " Должен считывает содержимое строки в массив. 182 | " @covers vim_lib#base#File#.read 183 | "" }}} 184 | function! s:Test.testRead() " {{{ 185 | let l:obj = s:File.relative('File/file.txt') 186 | call self.assertEquals(l:obj.read(), ['Hello world', 'Test']) 187 | endfunction " }}} 188 | 189 | "" {{{ 190 | " Должен перезаписывать файл. 191 | " @covers vim_lib#base#File#.rewrite 192 | "" }}} 193 | function! s:Test.testRewrite() " {{{ 194 | let l:obj = s:File.relative('File/file.txt') 195 | let l:content = l:obj.read() 196 | let l:content[1] = 'Rewrite' 197 | call l:obj.rewrite(l:content) 198 | call self.assertEquals(l:obj.read(), ['Hello world', 'Rewrite']) 199 | call l:obj.rewrite(['Hello world', 'Test']) 200 | endfunction " }}} 201 | 202 | "" {{{ 203 | " Должен добавлять запись в конец файла. 204 | " @covers vim_lib#base#File#.write 205 | "" }}} 206 | function! s:Test.testWrite() " {{{ 207 | let l:obj = s:File.relative('File/file.txt') 208 | call l:obj.write('New string') 209 | call self.assertEquals(l:obj.read(), ['Hello world', 'Test', 'New string']) 210 | call l:obj.rewrite(['Hello world', 'Test']) 211 | endfunction " }}} 212 | " }}} 213 | 214 | let g:vim_lib#base#tests#TestDict# = s:Test 215 | call s:Test.run() 216 | -------------------------------------------------------------------------------- /autoload/vim_lib/base/tests/TestObject.vim: -------------------------------------------------------------------------------- 1 | " Date Create: 2015-01-06 13:34:25 2 | " Last Change: 2015-02-02 14:59:15 3 | " Author: Artur Sh. Mamedbekov (Artur-Mamedbekov@yandex.ru) 4 | " License: GNU GPL v3 (http://www.gnu.org/copyleft/gpl.html) 5 | 6 | let s:Object = vim_lib#base#Object# 7 | let s:Subclass = vim_lib#base#tests#Object#Subclass# 8 | let s:Parent = vim_lib#base#tests#Object#Parent# 9 | let s:Child = vim_lib#base#tests#Object#Child# 10 | 11 | let s:Test = vim_lib#base#Test#.expand() 12 | 13 | " expand {{{ 14 | "" {{{ 15 | " Должен создавать новый класс сохраняя ссылку на родителя в свойстве parent. 16 | " @covers vim_lib#base#Object#.expand 17 | "" }}} 18 | function s:Test.testExpand_saveParentLink() " {{{ 19 | call self.assertEquals(s:Parent.parent, s:Object) 20 | call self.assertEquals(s:Child.parent, s:Parent) 21 | call self.assertEquals(s:Child.parent.parent, s:Object) 22 | endfunction " }}} 23 | 24 | "" {{{ 25 | " Должен создавать новый класс копируя в него ссылки на все методы родителя. 26 | " @covers vim_lib#base#Object#.expand 27 | "" }}} 28 | function s:Test.testExpand_copyMethods() " {{{ 29 | call self.assertEquals(s:Parent.expand, s:Object.expand) 30 | call self.assertEquals(s:Child.expand, s:Object.expand) 31 | endfunction " }}} 32 | 33 | "" {{{ 34 | " Может использоваться подклассом без переопределения. 35 | " @covers vim_lib#base#Object#.expand 36 | "" }}} 37 | function s:Test.testExpand_definedSubclass() " {{{ 38 | let s:Subsubclass = s:Subclass.expand() 39 | call self.assertEquals(s:Subsubclass.parent, s:Subclass) 40 | call self.assertEquals(s:Subsubclass.new, s:Subclass.new) 41 | call self.assertEquals(s:Subsubclass.expand, s:Subclass.expand) 42 | call self.assertEquals(s:Subsubclass.bless, s:Subclass.bless) 43 | endfunction " }}} 44 | " }}} 45 | " mix {{{ 46 | "" {{{ 47 | " Должен добавлять методы примести в класс. 48 | " @covers vim_lib#base#Object#.mix 49 | "" }}} 50 | function! s:Test.testMix_mixMethods() " {{{ 51 | call self.assertDictHasKey(s:Parent, 'mixMethod') 52 | let s:par = s:Parent.new(0) 53 | call self.assertDictHasKey(s:par, 'mixMethod') 54 | call self.assertEquals(s:par.mixMethod(), 1) 55 | endfunction " }}} 56 | 57 | "" {{{ 58 | " Должен добавлять свойства примести в класс. 59 | " @covers vim_lib#base#Object#.mix 60 | "" }}} 61 | function! s:Test.testMix_mixMethods() " {{{ 62 | call self.assertDictHasKey(s:Parent.properties, 'mixProperty') 63 | let s:par = s:Parent.new(0) 64 | call self.assertDictHasKey(s:par, 'mixProperty') 65 | call self.assertEquals(s:par.mixProperty, 1) 66 | endfunction " }}} 67 | " }}} 68 | " bless {{{ 69 | "" {{{ 70 | " Должен создавать неинициализированный экземпляр класса со свойствами parent и class. 71 | " @covers vim_lib#base#Object#.bless 72 | "" }}} 73 | function s:Test.testBless_createObject() " {{{ 74 | let s:par = s:Parent.bless() 75 | call self.assertEquals(s:par.parent.class, s:Object) 76 | call self.assertEquals(s:par.class, s:Parent) 77 | endfunction " }}} 78 | 79 | "" {{{ 80 | " Должен позволять задать объекту родителя. 81 | " @covers vim_lib#base#Object#.bless 82 | "" }}} 83 | function s:Test.testBless_setParentObject() " {{{ 84 | let s:obj = s:Object.new() 85 | let s:par = s:Parent.bless(s:obj) 86 | call self.assertEquals(s:par.parent, s:obj) 87 | call self.assertEquals(s:par.class, s:Parent) 88 | endfunction " }}} 89 | 90 | "" {{{ 91 | " Должен копировать частные методы класса в объект. 92 | " @covers vim_lib#base#Object#.bless 93 | "" }}} 94 | function s:Test.testBless_copyMethods() " {{{ 95 | let s:par = s:Parent.bless() 96 | call self.assertDictHasKey(s:par, 'modificArray') 97 | endfunction " }}} 98 | 99 | "" {{{ 100 | " Должен копировать свойства класса из properties в объект. 101 | " @covers vim_lib#base#Object#.bless 102 | "" }}} 103 | function! s:Test.testBless_copyProperties() " {{{ 104 | let s:par = s:Parent.bless() 105 | call self.assertEquals(s:par.property, 1) 106 | endfunction " }}} 107 | 108 | "" {{{ 109 | " Не должен копировать статичные методы класса в объект. 110 | " @covers vim_lib#base#Object#.bless 111 | "" }}} 112 | function s:Test.testBless_staticMethod() " {{{ 113 | let s:par = s:Parent.bless() 114 | call self.assertDictNotHasKey(s:par, '__staticMethod') 115 | endfunction " }}} 116 | " }}} 117 | " new {{{ 118 | "" {{{ 119 | " Должен создавать новый объект на основании данного класса сохраняя ссылку на класс в свойстве class объекта. 120 | " @covers vim_lib#base#Object#.new 121 | "" }}} 122 | function s:Test.testNew_saveClassLink() " {{{ 123 | let l:obj = s:Object.new() 124 | call self.assertEquals(l:obj.class, s:Object) 125 | let l:obj = s:Parent.new(1) 126 | call self.assertEquals(l:obj.class, s:Parent) 127 | let l:obj = s:Child.new(1, 2) 128 | call self.assertEquals(l:obj.class, s:Child) 129 | endfunction " }}} 130 | 131 | "" {{{ 132 | " Для подкласса должен сохранять ссылку на объект родительского класса в свойстве parent объекта дочернего класса. 133 | " @covers vim_lib#base#Object#.new 134 | "" }}} 135 | function s:Test.testNew_saveParentObject() " {{{ 136 | let l:p = s:Parent.new(1) 137 | call self.assertEquals(l:p.parent, s:Object.new()) 138 | let l:c = s:Child.new(1, 2) 139 | call self.assertEquals(l:c.parent, l:p) 140 | endfunction " }}} 141 | 142 | "" {{{ 143 | " Должен правильно инициализировать объекты для подклассов. 144 | " @covers vim_lib#base#Object#.new 145 | "" }}} 146 | function s:Test.testNew_initialization() " {{{ 147 | let l:obj = s:Child.new(1, 2) 148 | call self.assertEquals(1, l:obj.parent.x) 149 | call self.assertEquals(2, l:obj.y) 150 | endfunction " }}} 151 | 152 | "" {{{ 153 | " Для подкласса должен создавать новый объект не копируя все свойства родителя в объект. 154 | " @covers vim_lib#base#Object#.new 155 | "" }}} 156 | function s:Test.testNew_createObject() " {{{ 157 | let l:obj = s:Child.new(1, 2) 158 | call self.assertDictNotHasKey(l:obj, 'x') 159 | call self.assertDictHasKey(l:obj, 'y') 160 | endfunction " }}} 161 | 162 | "" {{{ 163 | " Для подклассов должен правильно инициализировать (рекурсивно копировать) сложные структуры. 164 | " @covers vim_lib#base#Object#.new 165 | "" }}} 166 | function s:Test.testNew_initializationComplexStructure() " {{{ 167 | let l:a = s:Child.new(1, 2) 168 | let l:b = s:Child.new(1, 2) 169 | let l:a.parent.array[0][0] = 0 170 | call self.assertEquals(l:a.parent.array[0][0], 0) 171 | call self.assertEquals(l:b.parent.array[0][0], 1) 172 | endfunction " }}} 173 | 174 | "" {{{ 175 | " Должен копировать ссылки на конкретные методы класса в экземпляр этого класса. 176 | " @covers vim_lib#base#Object#.new 177 | "" }}} 178 | function s:Test.testNew_copyMethodLinks() " {{{ 179 | let l:obj = s:Parent.new(1) 180 | call l:obj.modificArray(0) 181 | call self.assertEquals(l:obj.array[0][0], 0) 182 | endfunction " }}} 183 | 184 | "" {{{ 185 | " Для подклассов интерфейс родительского класса сохраняется в экземпляря родительского класса. 186 | " @covers vim_lib#base#Object#.new 187 | "" }}} 188 | function s:Test.testNew_noSaveMethodsLink() " {{{ 189 | let l:a = s:Child.new(1, 2) 190 | call l:a.parent.modificArray(0) 191 | call self.assertEquals(l:a.parent.array[0][0], 0) 192 | endfunction " }}} 193 | 194 | "" {{{ 195 | " Может использоваться подклассом без переопределения. 196 | " @covers vim_lib#base#Object#.new 197 | "" }}} 198 | function s:Test.testNew_definedSubclass() " {{{ 199 | let l:obj = s:Subclass.new() 200 | call self.assertEquals(l:obj.parent, s:Object.new()) 201 | call self.assertEquals(l:obj.class, s:Subclass) 202 | endfunction " }}} 203 | " }}} 204 | " typeof {{{ 205 | "" {{{ 206 | " Должен определять, является ли вызываемый класс почерним по отношению к заданному. 207 | " @covers vim_lib#base#Object#.typeof 208 | "" }}} 209 | function s:Test.testTypeof() " {{{ 210 | let l:obj = s:Child.new(1, 2) 211 | call self.assertTrue(l:obj.class.typeof(s:Child)) 212 | call self.assertTrue(l:obj.class.typeof(s:Parent)) 213 | call self.assertTrue(l:obj.class.typeof(s:Object)) 214 | call self.assertFalse(l:obj.class.typeof(s:Subclass)) 215 | endfunction " }}} 216 | " }}} 217 | 218 | let g:vim_lib#base#tests#TestObject# = s:Test 219 | call s:Test.run() 220 | -------------------------------------------------------------------------------- /autoload/vim_lib/sys/Plugin.vim: -------------------------------------------------------------------------------- 1 | " Date Create: 2015-01-09 13:58:18 2 | " Last Change: 2015-02-16 15:54:06 3 | " Author: Artur Sh. Mamedbekov (Artur-Mamedbekov@yandex.ru) 4 | " License: GNU GPL v3 (http://www.gnu.org/copyleft/gpl.html) 5 | 6 | let s:Object = g:vim_lib#base#Object# 7 | let s:NullPlugin = g:vim_lib#sys#NullPlugin# 8 | let s:System = g:vim_lib#sys#System#.new() 9 | 10 | "" {{{ 11 | " Объекты данного класса представляют каждый конкретный, подключаемый редактором плагин. 12 | " Такой плагин можно отключить, определив переменную: let имяПлагина# = 0 - в файле .vimrc или скриптах каталога 'plugin'. При этом все опции, команды и привязки плагина, определенные в нем по умолчанию, не будут применены. 13 | " Плагин, использующий данный класс, инициализируется в каталоге 'plugin' редактора и может выглядить следующим образом: 14 | " let s:Plugin = vim_lib#sys#Plugin# 15 | " let s:p = s:Plugin.new('myPlugin', '1.0') 16 | " ... " Значения свойств, а так же команды и привязки, используемые по умолчанию для данного плагина. 17 | " let s:p.reg() 18 | " Интерфейс плагина описывается в каталоге 'autoload' редактора и может выглядить следующим образом: 19 | " function! myPlugin#method() 20 | " echo g:myPlugin#optionA 21 | " endfunction 22 | "" }}} 23 | let s:Class = s:Object.expand() 24 | "" {{{ 25 | " @var hash Словарь инициализированных плагинов. Словарь имеет следующую структуру: {имя: объектПлагина, ...} 26 | "" }}} 27 | let s:Class.plugins = {} 28 | 29 | function! s:Class.__verifyDep(dep) " {{{ 30 | if has_key(a:dep, 'version') && !self.__verifyVersion(a:dep['version']) 31 | return 0 32 | endif 33 | if has_key(a:dep, 'has') && !self.__verifyEnv(a:dep['has']) 34 | return 0 35 | endif 36 | if has_key(a:dep, 'plugins') && !self.__verifyPlugs(a:dep['plugins']) 37 | return 0 38 | endif 39 | return 1 40 | endfunction " }}} 41 | 42 | function! s:Class.__verifyVersion(assertVersion) " {{{ 43 | if v:version < a:assertVersion 44 | echohl Error | echo 'Module ' . self.currentModule . ': You need Vim v' . a:assertVersion . ' or higher.' | echohl None 45 | return 0 46 | endif 47 | return 1 48 | endfunction " }}} 49 | 50 | function! s:Class.__verifyEnv(assertEnv) " {{{ 51 | for l:module in a:assertEnv 52 | if !has(l:module) 53 | echohl Error | echo 'Module ' . self.currentModule . ': You need module "' . l:module . '".' | echohl None 54 | return 0 55 | endif 56 | endfor 57 | return 1 58 | endfunction " }}} 59 | 60 | function! s:Class.__verifyPlugs(assertPlugins) " {{{ 61 | for l:plugin in a:assertPlugins 62 | if !has_key(self.plugins, l:plugin) 63 | echohl Error | echo 'Module ' . self.currentModule . ': You need plugin "' . l:plugin . '".' | echohl None 64 | return 0 65 | endif 66 | endfor 67 | return 1 68 | endfunction " }}} 69 | 70 | "" {{{ 71 | " Конструктор, формирующий объектное представление плагина. 72 | " После инициализации плагина необходимо вызвать метод reg. 73 | " Если плагин с заданным именем уже зарегистрирован, метод возвращает его объектное представление, созданное ранее. При этом версия, задаваемая вторым параметром, не применяется. 74 | " @param string name Имя плагина. 75 | " @param string version Версия плагина. 76 | " @param hash dependency [optional] Зависимости плагина. Словарь может иметь следующую структуру: {'version': версияРедактора, 'has': [модулиОкружения], 'plugins': [плагины]} 77 | " @return vim_lib#sys#Plugin# Целевой плагин. 78 | "" }}} 79 | function! s:Class.new(name, version, ...) " {{{ 80 | " Получение объекта из пула. {{{ 81 | if has_key(self.plugins, a:name) 82 | return self.plugins[a:name] 83 | endif 84 | " }}} 85 | let self.currentModule = a:name " Свойство используется методом __verifyDep для определения имени верифицируемого плагина до его создания 86 | if exists('g:' . a:name . '#') && type(g:[a:name . '#']) == 0 87 | return s:NullPlugin.new(a:name) 88 | endif 89 | if exists('a:1') && !self.__verifyDep(a:1) 90 | return s:NullPlugin.new(a:name) 91 | endif 92 | let l:obj = self.bless() 93 | "" {{{ 94 | " @var stinrg Имя плагина. 95 | "" }}} 96 | let l:obj.name = a:name 97 | "" {{{ 98 | " @var string Версия плагина. 99 | "" }}} 100 | let l:obj.version = a:version 101 | "" {{{ 102 | " @var hash Словарь команд, определенных плагином. Словарь имеет следующую структуру: {команда: метод, ...}. 103 | "" }}} 104 | let l:obj.commands = {} 105 | "" {{{ 106 | " @var hash Словарь привязок горячих клавишь, определенных плагином. Словарь имеет следующую структуру: {метод: комбинация, ...}. 107 | "" }}} 108 | let l:obj.keyListeners = {} 109 | "" {{{ 110 | " @var hash Словарь пунктов меню, определенных плагином. Словарь имеет следующую структуру: {пункт: метод, ...}. 111 | "" }}} 112 | let l:obj.menuPoints = {} 113 | let l:obj.savecpo = &l:cpo 114 | let self.plugins[a:name] = l:obj 115 | set cpo&vim 116 | return l:obj 117 | endfunction " }}} 118 | 119 | "" {{{ 120 | " Метод возвращает имя плагина. 121 | " @return string Имя плагина. 122 | "" }}} 123 | function! s:Class.getName() " {{{ 124 | return self.name 125 | endfunction " }}} 126 | 127 | "" {{{ 128 | " Метод возвращает версию модуля. 129 | " @return string Версия модуля. 130 | "" }}} 131 | function! s:Class.getVersion() " {{{ 132 | return self.version 133 | endfunction " }}} 134 | 135 | "" {{{ 136 | " Метод определяет команды редактора, создаваемые плагином. 137 | " При выполнении этих команд будут вызываться методы плагина, определенные в его интерфейсе. Так, команда вида: 138 | " call s:p.comm('MyPlugComm', 'method()') 139 | " выполнит метод 'MyPlugin#method'. 140 | " Важно помнить, что в качестве имени метода необходимо указывать имя целевого метода с завершающими круглыми скобками. Это позволяет указать параметры метода при его вызове. 141 | " Команды не будут созданы, если плагин отключен. 142 | " @param string command Команда. 143 | " @param string method Имя метода, являющегося частью интерфейса плагина. 144 | "" }}} 145 | function! s:Class.comm(command, method) " {{{ 146 | let self.commands[a:command] = a:method 147 | endfunction " }}} 148 | 149 | "" {{{ 150 | " Метод определяет горячие клавиши, создаваемые плагином. 151 | " При использовании этих привязок будут вызываться методы плагина, определенные в его интерфейсе. Так, привязка вида: 152 | " call s:p.map('q', 'quit') 153 | " выполнит метод 'MyPlugin#quit'. 154 | " Для переопределения привязок плагина можно использовать словарь: имяПлагина#map, который имеет следующую структуру: {метод: комбинация, ...}. 155 | " Привязки не будут созданы, если плагин отключен. 156 | " @param string sequence Комбинация клавишь, для которой создается привязка. 157 | " @param string method Имя метода, являющегося частью интерфейса плагина. 158 | "" }}} 159 | function! s:Class.map(sequence, method) " {{{ 160 | let self.keyListeners[a:method] = a:sequence 161 | endfunction " }}} 162 | 163 | "" {{{ 164 | " Метод определяет пункт меню для данного плагина. 165 | " Пункт добавляется по адресу Plugins.имяПлагина.point 166 | " При выборе данного пункта меню, вызывается метод плагина, определенный в его интерфейсе. Так, событие вида: 167 | " call s:p.menu('test', 'mehod') 168 | " выполнит метод 'myPlugin#method'. 169 | " Пункты меню не будут созданы, если плагин отключен. 170 | " @param string point Наименование создаваемого пункта меню. 171 | " @param string method Имя метода, являющегося частью интерфейса плагина. 172 | " @param integer priority [optional] Приоритет создаваемого пункта меню. Чем данное значение ниже, тем выше приоритет. 173 | "" }}} 174 | function! s:Class.menu(point, method, ...) " {{{ 175 | if exists('a:1') 176 | let l:priority = '1.' . len(self.class.plugins) . '.' . a:1 177 | else 178 | let l:priority = '' 179 | endif 180 | let self.menuPoints[a:point] = [a:method, l:priority] 181 | endfunction " }}} 182 | 183 | "" {{{ 184 | " Метод регистрирует плагин в системе и восстанавливает систему в начальное состояние. 185 | " Данный метод необходимо вызвать в конце файла инициализации плагина. 186 | "" }}} 187 | function! s:Class.reg() " {{{ 188 | " Переопределение свойств плагина. {{{ 189 | let g:[self.name . '#'] = extend(self, (exists('g:' . self.name . '#options'))? g:[self.name . '#options'] : {}) 190 | if exists('g:' . self.name . '#options') 191 | unlet g:[self.name . '#options'] 192 | endif 193 | " }}} 194 | " Установка команд плагина. {{{ 195 | for [l:comm, l:method] in items(self.commands) 196 | exe 'command! -nargs=* ' . l:comm . ' call ' . self.name . '#' . l:method 197 | endfor 198 | " }}} 199 | " Установка пунктов меню. {{{ 200 | for [l:point, l:menuListener] in items(self.menuPoints) 201 | call s:System.menu('n', 'Plugins.' . self.name . '.' . l:point, function(self.name . '#' . l:menuListener[0]), l:menuListener[1]) 202 | endfor 203 | " }}} 204 | " Переопределение и установка привязок плагина. {{{ 205 | let self.keyListeners = extend(self.keyListeners, (exists('g:' . self.name . '#map'))? g:[self.name . '#map'] : {}) 206 | for [l:method, l:sequence] in items(self.keyListeners) 207 | if l:method != '' 208 | call s:System.map('n', l:sequence, function(self.name . '#' . l:method)) 209 | endif 210 | endfor 211 | if exists('g:' . self.name . '#map') 212 | unlet g:[self.name . '#map'] 213 | endif 214 | " }}} 215 | call self.run() 216 | let &l:cpo = self.savecpo 217 | endfunction " }}} 218 | 219 | "" {{{ 220 | " Данный метод может быть переопределен конкретным плагином с целью реализации логики. 221 | "" }}} 222 | function! s:Class.run() " {{{ 223 | endfunction " }}} 224 | 225 | let g:vim_lib#sys#Plugin# = s:Class 226 | -------------------------------------------------------------------------------- /autoload/vim_lib/sys/System.vim: -------------------------------------------------------------------------------- 1 | " Date Create: 2015-02-02 10:05:45 2 | " Last Change: 2015-07-12 15:23:40 3 | " Author: Artur Sh. Mamedbekov (Artur-Mamedbekov@yandex.ru) 4 | " License: GNU GPL v3 (http://www.gnu.org/copyleft/gpl.html) 5 | 6 | let s:Object = g:vim_lib#base#Object# 7 | let s:EventHandle = g:vim_lib#base#EventHandle# 8 | 9 | "" {{{ 10 | " Класс представляет интерфейс для взаимодействия с операционной системой и редактором Vim. 11 | "" }}} 12 | let s:Class = s:Object.expand() 13 | call s:Class.mix(s:EventHandle) 14 | 15 | "" {{{ 16 | " Конструктор всегда возвращает единственный экземпляр данного класса. 17 | " @return vim_lib#sys#System# Интерфейс системы. 18 | "" }}} 19 | function! s:Class.new() " {{{ 20 | return self.singleton 21 | endfunction " }}} 22 | 23 | "" {{{ 24 | " Метод выполняет команду в командной оболочке и возвращает результат. 25 | " @param string command Выполняемая команда. 26 | " @throws ShellException Выбрасывается в случае ошибки при выполнении команды в командной оболочке. 27 | " @return string Результат работы команды. 28 | "" }}} 29 | function! s:Class.run(command) " {{{ 30 | " Вызов для MacVim, которые не наследуют переменные окружения 31 | let l:response = system(((has('mac') && &shell =~ 'sh$')? 'EDITOR="" ' : '') . a:command) 32 | if v:shell_error 33 | echohl Error | echo l:response | echohl None 34 | throw 'ShellException: command fails.' 35 | else 36 | return l:response 37 | endif 38 | endfunction " }}} 39 | 40 | "" {{{ 41 | " Метод выполняет команду в командной оболочке. В отличии от метода run, данный метод исполняет команду с переходом в командную оболочку. 42 | " @param string command Выполняемая команда. 43 | "" }}} 44 | function! s:Class.exe(command) " {{{ 45 | " Вызов для MacVim, которые не наследуют переменные окружения 46 | exe '!' . ((has('mac') && &shell =~ 'sh$')? 'EDITOR="" ' : '') . a:command 47 | endfunction " }}} 48 | 49 | "" {{{ 50 | " Метод выполняет команду в командной оболочке аналогично методу exe, но в "тихом режиме". 51 | " @param string command Выполняемая команда. 52 | "" }}} 53 | function! s:Class.silentExe(command) " {{{ 54 | " Вызов для MacVim, которые не наследуют переменные окружения 55 | exe 'silent !' . ((has('mac') && &shell =~ 'sh$')? 'EDITOR="" ' : '') . a:command 56 | redraw! 57 | endfunction " }}} 58 | 59 | "" {{{ 60 | " Метод печатает сообщение в консоли редактора. 61 | " @param string msg Сообщение. 62 | " @param string color [optional] Цвет сообщения. 63 | "" }}} 64 | function! s:Class.echo(msg, ...) " {{{ 65 | let l:color = (exists('a:1'))? a:1 : 'None' 66 | exe 'echohl ' . l:color 67 | exe 'echo "' . a:msg . '"' 68 | echohl None 69 | endfunction " }}} 70 | 71 | "" {{{ 72 | " Метод печатает устойчивое сообщение в консоли редактора. 73 | " @param string msg Сообщение. 74 | " @param string color [optional] Цвет сообщения. 75 | "" }}} 76 | function! s:Class.echom(msg, ...) " {{{ 77 | let l:color = (exists('a:1'))? a:1 : 'None' 78 | exe 'echohl ' . l:color 79 | exe 'echom "' . a:msg . '"' 80 | echohl None 81 | endfunction " }}} 82 | 83 | "" {{{ 84 | " Метод печатает сообщение в консоли редактора, ожидая реакции пользователя. 85 | " @param string msg Сообщение. 86 | " @param string color [optional] Цвет сообщения. 87 | "" }}} 88 | function! s:Class.print(msg, ...) " {{{ 89 | let l:color = (exists('a:1'))? a:1 : 'None' 90 | call inputsave() 91 | exe 'echohl ' . l:color 92 | call input(a:msg) 93 | echohl None 94 | call inputrestore() 95 | endfunction " }}} 96 | 97 | "" {{{ 98 | " Метод запрашивает строку у пользователя. 99 | " @param string msg Заголовок запроса. 100 | " @param string color [optional] Цвет заголовка. 101 | " @param string text [optional] Начальное значение строки. 102 | " @return string Строка, введеная пользователем. 103 | "" }}} 104 | function! s:Class.read(msg, ...) " {{{ 105 | let l:color = (exists('a:1'))? a:1 : 'None' 106 | let l:text = (exists('a:2'))? a:2 : '' 107 | call inputsave() 108 | exe 'echohl ' . l:color 109 | let l:result = input(a:msg, l:text) 110 | echohl None 111 | call inputrestore() 112 | return l:result 113 | endfunction " }}} 114 | 115 | "" {{{ 116 | " Метод запрашивает подтверждение у пользователя. 117 | " Подтверждением запроса является ввод: Y или пустая строка. 118 | " @param string msg Заголовок запроса. 119 | " @return bool true - если пользователь подтвердил запрос, иначе - false. 120 | "" }}} 121 | function! s:Class.confirm(msg) " {{{ 122 | let l:result = confirm(a:msg, "&Yes\n&no") 123 | return !(l:result == 0 || l:result == 2) 124 | endfunction " }}} 125 | 126 | " Метод listen примеси EventHandle выносится в закрытую область класса. 127 | let s:Class._listen = s:Class.listen 128 | unlet s:Class.listen 129 | "" {{{ 130 | " Метод определяет функцию-обработчик (слушатель) для глобального события клавиатуры. 131 | " Слушатель должен быть методом вызываемого класса или ссылкой на глобальную функцию. 132 | " @param string mode Режим привязки. Возможно одно из следующих значений: n, v, o, i, l, c. 133 | " @param string sequence Комбинация клавишь, для которой создается привязка. 134 | " @param string listener Имя метода класса или ссылка на глобальную функцию, используемую в качестве функции-обработчика. 135 | "" }}} 136 | function! s:Class.map(mode, sequence, listener) " {{{ 137 | " Исключаем автоматический перевод комбинаций вида в ^... при вызове noremap. 138 | let l:modSeq = substitute(a:sequence, '<', '\\<', '') 139 | let l:modSeq = substitute(l:modSeq, '>', '\\>', '') 140 | call self._listen('keyPress_' . a:mode . ':' . a:sequence, a:listener) 141 | if a:mode == 'i' 142 | exe a:mode . 'noremap ' . a:sequence . ' :call vim_lib#sys#System#.new().fire("' . a:mode . '", "' . l:modSeq . '")' 143 | else 144 | exe a:mode . 'noremap ' . a:sequence . ' :call vim_lib#sys#System#.new().fire("' . a:mode . '", "' . l:modSeq . '")' 145 | endif 146 | endfunction " }}} 147 | 148 | "" {{{ 149 | " Метод определяет функцию-обработчик (слушатель) для события редактора. 150 | " Слушатель должен быть методом данного буфера или ссылкой на глобальную функцию. 151 | " @param string events Имена событий, перечисленных через запятую, к которым выполняется привязка. Доступно одной из приведенных в разделе |autocommand-events| значений. 152 | " @param string listener Имя метода класса или ссылка на глобальную функцию, используемую в качестве функции-обработчика. 153 | "" }}} 154 | function! s:Class.au(events, listener) " {{{ 155 | if !has_key(self.listeners, 'autocmd_' . a:events) 156 | exe 'au ' . a:events . ' * :call vim_lib#sys#System#.new().doau("' . a:events . '")' 157 | endif 158 | call self._listen('autocmd_' . a:events, a:listener) 159 | endfunction " }}} 160 | 161 | "" {{{ 162 | " Метод определяет функцию-обработчик (слушатель) для пункта меню. 163 | " Слушатель должен быть методом вызываемого класса или ссылкой на глобальную функцию. 164 | " @param string mode Режим привязки. Возможно одно из следующих значений: n, v, o, i, c. 165 | " @param string point Наименование создаваемого пункта меню. 166 | " @param string listener Имя метода класса или ссылка на глобальную функцию, используемую в качестве функции-обработчика. 167 | " @param integer priority [optional] Приоритет создаваемого пункта меню. Чем данное значение ниже, тем выше приоритет. 168 | "" }}} 169 | function! s:Class.menu(mode, point, listener, ...) " {{{ 170 | let l:priority = (exists('a:1'))? a:1 : '' 171 | call self._listen('menu_' . a:mode . ':' . a:point, a:listener) 172 | exe a:mode . 'menu ' . l:priority . ' ' . a:point . ' :call vim_lib#sys#System#.new().domenu("' . a:mode . '", "' . a:point . '")' 173 | endfunction " }}} 174 | 175 | " Метод ignore примеси EventHandle выносится в закрытую область класса. 176 | let s:Class._ignore = s:Class.ignore 177 | "" {{{ 178 | " Метод удаляет функции-обработчики (слушатели) для глобального события клавиатуры. 179 | " @param string mode Режим привязки. Возможно одно из следующих значений: n, v, o, i, l, c. 180 | " @param string sequence Комбинация клавишь, для которой удаляется привязка. 181 | " @param string listener [optional] Имя удаляемой функции-слушателя или ссылка на глобальную функцию. Если параметр не задан, удаляются все слушатели данной комбинации клавишь. 182 | "" }}} 183 | function! s:Class.ignoreMap(mode, sequence, ...) " {{{ 184 | if exists('a:1') 185 | call self._ignore('keyPress_' . a:mode . ':' . a:sequence, a:1) 186 | else 187 | call self._ignore('keyPress_' . a:mode . ':' . a:sequence) 188 | endif 189 | if len(self.listeners['keyPress_' . a:mode . ':' . a:sequence]) == 0 190 | exe a:mode . 'unmap ' . a:sequence 191 | endif 192 | endfunction " }}} 193 | 194 | "" {{{ 195 | " Метод удаляет функции-обработчики (слушатели) для события редактора. 196 | " @param string events Имена событий, перечисленных через запятую, для которым отменяется привязка. Доступно одной из приведенных в разделе |autocommand-events| значений. 197 | " @param string listener [optional] Имя удаляемой функции-слушателя или ссылка на глобальную функцию. Если параметр не задан, удаляются все слушатели данного события. 198 | "" }}} 199 | function! s:Class.ignoreAu(events, ...) " {{{ 200 | if exists('a:1') 201 | call self._ignore('autocmd_' . a:events, a:1) 202 | else 203 | call self._ignore('autocmd_' . a:events) 204 | endif 205 | if len(self.listeners['autocmd_' . a:events]) == 0 206 | exe 'au! ' . a:events . ' *' 207 | endif 208 | endfunction " }}} 209 | 210 | "" {{{ 211 | " Метод удаляет функции-обработчики (слушатели) для пункта меню. 212 | " @param string mode Режим привязки. Возможно одно из следующих значений: n, v, o, i, l, c. 213 | " @param string point Наименование целевого пункта меню. 214 | " @param string listener [optional] Имя удаляемой функции-слушателя или ссылка на глобальную функцию. Если параметр не задан, удаляются все слушатели данного пункта меню. 215 | "" }}} 216 | function! s:Class.ignoreMenu(mode, point, ...) " {{{ 217 | if exists('a:1') 218 | call self._ignore('menu_' . a:mode . ':' . a:point, a:1) 219 | else 220 | call self._ignore('menu_' . a:mode . ':' . a:point) 221 | endif 222 | if len(self.listeners['menu_' . a:mode . ':' . a:point]) == 0 223 | exe a:mode . 'unmenu ' . a:point 224 | endif 225 | endfunction " }}} 226 | 227 | " Метод fire примеси EventHandle выносится в закрытую область класса. 228 | let s:Class._fire = s:Class.fire 229 | "" {{{ 230 | " Метод генерирует глобальное событие клавиатуры. 231 | " @param string mode Режим привязки. Возможно одно из следующих значений: n, v, o, i, c. 232 | " @param string sequence Комбинация клавишь, для которой генерируется событие нажатия. 233 | "" }}} 234 | function! s:Class.fire(mode, event) " {{{ 235 | call self._fire('keyPress_' . a:mode . ':' . a:event) 236 | endfunction " }}} 237 | 238 | "" {{{ 239 | " Метод генерирует событие редактора. 240 | " @param string events Имена событий, перечисленных через запятую, для которых выполняется генерация. Доступно одной из приведенных в разделе |autocommand-events| значений. 241 | "" }}} 242 | function! s:Class.doau(events) " {{{ 243 | call self._fire('autocmd_' . a:events) 244 | endfunction " }}} 245 | 246 | "" {{{ 247 | " Метод имитирует активацию пункта меню. 248 | " @param string mode Режим привязки. Возможно одно из следующих значений: n, v, o, i, c. 249 | " @param string sequence Пункт меню, для которого генерируется событие активации. 250 | "" }}} 251 | function! s:Class.domenu(mode, point) " {{{ 252 | call self._fire('menu_' . a:mode . ':' . a:point) 253 | endfunction " }}} 254 | 255 | "" {{{ 256 | " @var vim_lib#sys#System# Единственный экземпляр класса. 257 | "" }}} 258 | let s:Class.singleton = s:Class.bless() 259 | 260 | let g:vim_lib#sys#System# = s:Class 261 | -------------------------------------------------------------------------------- /autoload/vim_lib/base/Test.vim: -------------------------------------------------------------------------------- 1 | " Date Create: 2015-01-06 13:15:51 2 | " Last Change: 2015-02-19 19:51:49 3 | " Author: Artur Sh. Mamedbekov (Artur-Mamedbekov@yandex.ru) 4 | " License: GNU GPL v3 (http://www.gnu.org/copyleft/gpl.html) 5 | 6 | let s:Test = {} 7 | 8 | "" {{{ 9 | " @var integer Время работы теста в секундах. 10 | "" }}} 11 | let s:runtime = 0 12 | "" {{{ 13 | " @var integer Число запущенных тестов. 14 | "" }}} 15 | let s:Test.countTests = 0 16 | "" {{{ 17 | " @var integer Число допущенных предположений (проверок). 18 | "" }}} 19 | let s:Test.countAsserting = 0 20 | "" {{{ 21 | " @var integer Число нарушений. 22 | "" }}} 23 | let s:Test.countFail = 0 24 | "" {{{ 25 | " @var integer Порядковые номера допущенных предположений (проверок). 26 | "" }}} 27 | let s:Test.countTestAsserting = 0 28 | 29 | "" {{{ 30 | " Метод создает новый модульный тест. 31 | " @return vim_lib#base#Test# Модульный тест. 32 | "" }}} 33 | function! s:Test.expand() " {{{ 34 | return deepcopy(self) 35 | endfunction " }}} 36 | 37 | "" {{{ 38 | " Запуск метода вызываемого объекта для тестирования. 39 | " @param string methodName Имя запускаемого метода. 40 | "" }}} 41 | function! s:Test._runMethod(methodName) " {{{ 42 | let self.countTestAsserting = 0 43 | let self.countTests += 1 44 | call self.beforeTest() 45 | echo '---' . a:methodName . '---' 46 | let l:runtime = localtime() 47 | call self[a:methodName]() 48 | let self.runtime += localtime() - l:runtime 49 | call self.afterTest() 50 | endfunction " }}} 51 | 52 | "" {{{ 53 | " Метод выполняет сверку данных и сообщает о несоответствии. 54 | " @param string funName Имя метода, запросившего сверку. Данное имя используется в тексте о несоответствии. 55 | " @param mixed assertValue Ожидаемые данные. 56 | " @param mixed actualValue Настоящие данные. 57 | "" }}} 58 | function! s:Test._compare(funName, assertValue, actualValue) " {{{ 59 | let l:assertType = type(a:assertValue) 60 | let l:actualType = type(a:actualValue) 61 | if l:assertType != l:actualType || a:assertValue != a:actualValue 62 | call self.fail(a:funName, 'Failed asserting that <' . l:assertType . ':' . string(a:assertValue) . '> matches expected value <' . l:actualType . ':' . string(a:actualValue) . '>.') 63 | endif 64 | endfunction " }}} 65 | 66 | "" {{{ 67 | " Метод вызывается для провальных тестов. 68 | " @param string funName Имя провального теста. 69 | " @param string message Сообщение о провале. 70 | "" }}} 71 | function! s:Test.fail(funName, message) " {{{ 72 | let self.countFail += 1 73 | echo a:funName . '[' . self.countTestAsserting . ']: ' . a:message 74 | endfunction " }}} 75 | 76 | "" {{{ 77 | " Предположение истинности. 78 | " @param bool actual Проверяемое значение. 79 | "" }}} 80 | function! s:Test.assertTrue(actual) " {{{ 81 | let self.countAsserting += 1 82 | let self.countTestAsserting += 1 83 | call self._compare('assertTrue', 1, a:actual) 84 | endfunction " }}} 85 | 86 | "" {{{ 87 | " Предположение ложности. 88 | " @param bool actual Проверяемое значение. 89 | "" }}} 90 | function! s:Test.assertFalse(actual) " {{{ 91 | let self.countAsserting += 1 92 | let self.countTestAsserting += 1 93 | call self._compare('assertFalse', 0, a:actual) 94 | endfunction " }}} 95 | 96 | "" {{{ 97 | " Предположение равенства целых чисел. 98 | " @param integer assert Ожидаемое значение. 99 | " @param integer actual Проверяемое значение. 100 | "" }}} 101 | function! s:Test.assertInteger(assert, actual) " {{{ 102 | let self.countAsserting += 1 103 | let self.countTestAsserting += 1 104 | call self._compare('assertInteger', a:assert, a:actual) 105 | endfunction " }}} 106 | 107 | "" {{{ 108 | " Предположение равенства дробных чисел. 109 | " @param float assert Ожидаемое значение. 110 | " @param float actual Проверяемое значение. 111 | "" }}} 112 | function! s:Test.assertFloat(assert, actual) " {{{ 113 | let self.countAsserting += 1 114 | let self.countTestAsserting += 1 115 | call self._compare('assertFloat', a:assert, a:actual) 116 | endfunction " }}} 117 | 118 | "" {{{ 119 | " Предположение равенства строк. 120 | " @param string assert Ожидаемое значение. 121 | " @param string actual Проверяемое значение. 122 | "" }}} 123 | function! s:Test.assertString(assert, actual) " {{{ 124 | let self.countAsserting += 1 125 | let self.countTestAsserting += 1 126 | call self._compare('assertString', a:assert, a:actual) 127 | endfunction " }}} 128 | 129 | "" {{{ 130 | " Предположение отсутствия данных (пустая строка или пустой массив). 131 | " @param string|array actual Проверяемое значение. 132 | "" }}} 133 | function! s:Test.assertEmpty(actual) " {{{ 134 | let self.countAsserting += 1 135 | let self.countTestAsserting += 1 136 | if type(a:actual) == 1 137 | call self._compare('assertEmpty', '', a:actual) 138 | elseif type(a:actual) == 3 139 | call self._compare('assertEmpty', [], a:actual) 140 | endif 141 | endfunction " }}} 142 | 143 | "" {{{ 144 | " Предположение наличия данных (не пустая строка или не пустой массив). 145 | " @param string|array actual Проверяемое значение. 146 | "" }}} 147 | function! s:Test.assertNotEmpty(actual) " {{{ 148 | let self.countAsserting += 1 149 | let self.countTestAsserting += 1 150 | if (type(a:actual) == 1 && strlen(a:actual) == 0) || (type(a:actual) == 3 && len(a:actual) == 0) 151 | call self.fail('assertNotEmpty', 'Failed assert that <' . type(a:actual) . ':' . string(a:actual) . '> is the empty value.') 152 | endif 153 | endfunction " }}} 154 | 155 | "" {{{ 156 | " Предположение равенства массивов. 157 | " @param array assert Ожидаемое значение. 158 | " @param array actual Проверяемое значение. 159 | "" }}} 160 | function! s:Test.assertArray(assert, actual) " {{{ 161 | let self.countAsserting += 1 162 | let self.countTestAsserting += 1 163 | call self._compare('assertArray', a:assert, a:actual) 164 | endfunction " }}} 165 | 166 | "" {{{ 167 | " Предположения наличия значения в массиве. 168 | " @param array array Проверяемый массив. 169 | " @param mixed Искомое значение. 170 | "" }}} 171 | function! s:Test.assertArrayContains(array, element) " {{{ 172 | let self.countAsserting += 1 173 | let self.countTestAsserting += 1 174 | if index(a:array, a:element) == -1 175 | call self.fail('assertArrayContains', 'Failed assert that <' . type(a:array) . ':' . string(a:array) . '> conrains value <' . type(a:element) . ':' . string(a:element) . '>.') 176 | endif 177 | endfunction " }}} 178 | 179 | "" {{{ 180 | " Предположения отсутствия значения в массиве. 181 | " @param array array Проверяемый массив. 182 | " @param mixed Искомое значение. 183 | "" }}} 184 | function! s:Test.assertArrayNotContains() " {{{ 185 | let self.countAsserting += 1 186 | let self.countTestAsserting += 1 187 | if index(a:array, a:element) != -1 188 | call self.fail('assertArrayNotContains', 'Failed assert that <' . type(a:array) . ':' . string(a:array) . '> not conrains value <' . type(a:element) . ':' . string(a:element) . '>.') 189 | endif 190 | endfunction " }}} 191 | 192 | "" {{{ 193 | " Предположение равенства хэш-таблиц. 194 | " @param hash assert Ожидаемое значение. 195 | " @param hash actual Проверяемое значение. 196 | "" }}} 197 | function! s:Test.assertDict(assert, actual) " {{{ 198 | let self.countAsserting += 1 199 | let self.countTestAsserting += 1 200 | call self._compare('assertDict', a:assert, a:actual) 201 | endfunction " }}} 202 | 203 | "" {{{ 204 | " Предположения наличия ключа в хэш-таблице. 205 | " @param hash dict Проверяемая хэш-таблица. 206 | " @param string key Искомый ключ. 207 | "" }}} 208 | function! s:Test.assertDictHasKey(dict, key) " {{{ 209 | let self.countAsserting += 1 210 | let self.countTestAsserting += 1 211 | if has_key(a:dict, a:key) == 0 212 | call self.fail('assertDictHasKey', 'Failed assert that <' . type(a:dict) . ':' . string(a:dict) . '> contains key <' . type(a:key) . ':' . string(a:key) . '>.') 213 | endif 214 | endfunction " }}} 215 | 216 | "" {{{ 217 | " Предположения отсутствия ключа в хэш-таблице. 218 | " @param hash dict Проверяемая хэш-таблица. 219 | " @param string key Искомый ключ. 220 | "" }}} 221 | function! s:Test.assertDictNotHasKey(dict, key) " {{{ 222 | let self.countAsserting += 1 223 | let self.countTestAsserting += 1 224 | if has_key(a:dict, a:key) != 0 225 | call self.fail('assertDictNotHasKey', 'Failed assert that <' . type(a:dict) . ':' . string(a:dict) . '> not contains key <' . type(a:key) . ':' . string(a:key) . '>.') 226 | endif 227 | endfunction " }}} 228 | 229 | "" {{{ 230 | " Предположение равенства функций. 231 | " @param function assert Ожидаемое значение. 232 | " @param function actual Проверяемое значение. 233 | "" }}} 234 | function! s:Test.assertFun(assert, actual) " {{{ 235 | let self.countAsserting += 1 236 | let self.countTestAsserting += 1 237 | call self._compare('assertFun', a:assert, a:actual) 238 | endfunction " }}} 239 | 240 | "" {{{ 241 | " Предположение эквивалентности значений. 242 | " @param mixed assert Ожидаемое значение. 243 | " @param mixed actual Проверяемое значение. 244 | "" }}} 245 | function! s:Test.assertEquals(assert, actual) " {{{ 246 | let self.countAsserting += 1 247 | let self.countTestAsserting += 1 248 | call self._compare('assertEquals', a:assert, a:actual) 249 | endfunction " }}} 250 | 251 | "" {{{ 252 | " Предположение не эквивалентности значений. 253 | " @param mixed assert Недопустимое значение. 254 | " @param mixed actual Проверяемое значение. 255 | "" }}} 256 | function! s:Test.assertNotEquals(assert, actual) " {{{ 257 | let self.countAsserting += 1 258 | let self.countTestAsserting += 1 259 | let l:assertType = type(a:assertValue) 260 | let l:actualType = type(a:actualValue) 261 | if l:assertType == l:actualType && a:assertValue == a:actualValue 262 | call self.fail('assertNotEquals', 'Failed asserting that <' . l:assertType . ':' . string(a:assertValue) . '> not equals value <' . l:actualType . ':' . string(a:actualValue) . '>.') 263 | endif 264 | endfunction " }}} 265 | 266 | "" {{{ 267 | " Предположение указанного ответа команды редактора. 268 | " @param string command Целевая команда. 269 | " @param string assert Ожидаемый ответ. 270 | " @author Luc Hermitte } 271 | "" }}} 272 | function! s:Test.assertExec(command, assert) " {{{ 273 | let self.countAsserting += 1 274 | let self.countTestAsserting += 1 275 | let l:save_a = @a 276 | try 277 | silent! redir @a 278 | silent! exe a:command 279 | redir END 280 | finally 281 | let l:actual = @a 282 | let @a = l:save_a 283 | endtry 284 | call self._compare('assertExec', a:assert, l:actual) 285 | endfunction " }}} 286 | 287 | "" {{{ 288 | " Метод выполняет тестирование. 289 | " @param string methodName [optional] Имя запускаемого метода для тестирования. Если параметр не передан, запускаются все методы класса, название которых начинается на "test". 290 | " @return bool true - если тестирование пройдено успешно, иначе - false. 291 | "" }}} 292 | function! s:Test.run(...) " {{{ 293 | let self.countTests = 0 294 | let self.countAsserting = 0 295 | let self.countFail = 0 296 | let self.runtime = 0 297 | call self.beforeRun() 298 | if exists('a:1') 299 | call self._runMethod(a:1) 300 | else 301 | for l:i in keys(self) 302 | if type(self[l:i]) == 2 && strpart(l:i, 0, 4) == 'test' 303 | call self._runMethod(l:i) 304 | endif 305 | endfor 306 | endif 307 | call self.afterRun() 308 | echo '' 309 | echo 'Time: ' . self.runtime . 's' 310 | if self.countFail == 0 311 | echo 'OK' 312 | return 1 313 | else 314 | echo 'FAILURES' 315 | return 0 316 | endif 317 | echo '(Tests: ' . self.countTests . ', Assertions: ' . self.countAsserting . ', Failures: ' . self.countFail . ')' 318 | endfunction " }}} 319 | 320 | "" {{{ 321 | " Данный метод вызывается перед запуском тестов. 322 | "" }}} 323 | function! s:Test.beforeRun() " {{{ 324 | endfunction " }}} 325 | 326 | "" {{{ 327 | " Данный метод вызывается перед запуском каждого тестового метода. 328 | "" }}} 329 | function! s:Test.beforeTest() " {{{ 330 | endfunction " }}} 331 | 332 | "" {{{ 333 | " Данный метод вызывается после выполнения каждого тестового метода. 334 | "" }}} 335 | function! s:Test.afterTest() " {{{ 336 | endfunction " }}} 337 | 338 | "" {{{ 339 | " Данный метод вызывается после завершения тестирования. 340 | "" }}} 341 | function! s:Test.afterRun() " {{{ 342 | endfunction " }}} 343 | 344 | let g:vim_lib#base#Test# = s:Test 345 | -------------------------------------------------------------------------------- /autoload/vim_lib/sys/Buffer.vim: -------------------------------------------------------------------------------- 1 | " Date Create: 2015-01-07 16:18:33 2 | " Last Change: 2015-06-28 17:25:54 3 | " Author: Artur Sh. Mamedbekov (Artur-Mamedbekov@yandex.ru) 4 | " License: GNU GPL v3 (http://www.gnu.org/copyleft/gpl.html) 5 | 6 | let s:Object = g:vim_lib#base#Object# 7 | let s:EventHandle = g:vim_lib#base#EventHandle# 8 | let s:Content = g:vim_lib#sys#Content#.new() 9 | 10 | "" {{{ 11 | " Класс представляет буфер редактора. 12 | "" }}} 13 | let s:Class = s:Object.expand() 14 | "" {{{ 15 | " @var hash Объектный пул, хранящий все экземпляры данного класса. Используется для исключения возможности создания двух объектов с одним номером буфера. 16 | "" }}} 17 | let s:Class.buffers = {} 18 | call s:Class.mix(s:EventHandle) 19 | 20 | "" {{{ 21 | " Конструктор создает объектное представление буфера. 22 | " @param integer|string name Номер целевого буфера или имя файла, используемого для редактирования в буфере. Если параметр не задан, создается новый буфер. 23 | " @throws IndexOutOfRangeException Выбрасывается при обращении к отсутствующему буферу. 24 | " @return vim_lib#sys#Buffer# Целевой буфер. 25 | "" }}} 26 | function! s:Class.new(...) " {{{ 27 | " Получение объекта из пула. {{{ 28 | if exists('a:1') 29 | let l:bufnr = (type(a:1) == 1)? bufnr(a:1) : a:1 30 | if has_key(self.buffers, l:bufnr) 31 | return self.buffers[l:bufnr] 32 | endif 33 | endif 34 | " }}} 35 | let l:obj = self.bless() 36 | "" {{{ 37 | " @var integer Номер буфера. 38 | "" }}} 39 | let l:obj.number = 0 40 | if exists('a:1') 41 | if type(a:1) == 0 42 | " Обращение к существующему буферу. {{{ 43 | if !bufexists(a:1) 44 | throw 'IndexOutOfRangeException: Buffer <' . a:1 . '> not found.' 45 | endif 46 | let l:obj.number = a:1 47 | " }}} 48 | else 49 | " Создание нового, именованного буфера. {{{ 50 | let l:obj.number = bufnr(a:1, 1) 51 | " }}} 52 | endif 53 | else 54 | " Создание нового, анонимного буфера. {{{ 55 | let l:obj.number = bufnr(bufnr('$') + 1, 1) 56 | " }}} 57 | endif 58 | "" {{{ 59 | " @var hash Словарь опций, применяемых к буферу при его активации. Словарь имеет следующую структуру: [опция: значение, ...]. 60 | "" }}} 61 | let l:obj.options = {} 62 | "" {{{ 63 | " @var hash Словарь команд, применяемых к буферу при его активации, генерирующих события привязок. Словарь имеет следующую структуру: {режимКомбинация: командаГенерацииСобытия, ...}. 64 | "" }}} 65 | let l:obj.listenerMap = {} 66 | "" {{{ 67 | " @var hash Словарь событий, применяемых к буферу при его активации, генерирующих события привязок. Словарь имеет следующую структуру: {событие: командаГенерацииСобытия, ...}. 68 | "" }}} 69 | let l:obj.listenerAu = {} 70 | let self.buffers[l:obj.getNum()] = l:obj 71 | return l:obj 72 | endfunction " }}} 73 | 74 | "" {{{ 75 | " Конструктор создает объектное представление текущего буфера. 76 | " @throws IndexOutOfRangeException Выбрасывается при обращении к отсутствующему буферу. 77 | " @return vim_lib#sys#Buffer# Целевой буфер. 78 | "" }}} 79 | function! s:Class.current() " {{{ 80 | return self.new(bufnr('%')) 81 | endfunction " }}} 82 | 83 | "" {{{ 84 | " Метод возвращает номер вызываемого буфера. 85 | " @return integer Номер буфера. 86 | "" }}} 87 | function! s:Class.getNum() " {{{ 88 | return self.number 89 | endfunction " }}} 90 | 91 | "" {{{ 92 | " Метод возвращает номер окна, в котором активирован буфер. 93 | " Если буфер активен в более чем одном окне, метод возвращает номер верхнего левого окна. 94 | " @return integer Номер окна, в котором активирован буфер или -1, если буфер не активен. 95 | "" }}} 96 | function! s:Class.getWinNum() " {{{ 97 | return bufwinnr(self.getNum()) 98 | endfunction " }}} 99 | 100 | "" {{{ 101 | " Метод закрывает все окна, в котором данный буфер является активным, а так же выгружает его из памяти. 102 | " При вызове метода, все несохраненные в буфере данные будут потеряны. 103 | "" }}} 104 | function! s:Class.unload() " {{{ 105 | exe 'bunload! ' . self.getNum() 106 | endfunction " }}} 107 | 108 | "" {{{ 109 | " Метод удаляет вызываемый буфер. 110 | " При вызове метода, все несохраненные в буфере данные будут потеряны. 111 | "" }}} 112 | function! s:Class.delete() " {{{ 113 | exe 'bw! ' . self.getNum() 114 | call remove(self.class.buffers, self.getNum()) 115 | endfunction " }}} 116 | 117 | "" {{{ 118 | " Данный метод отвечает за рендеринг содержимого буфера, установку опций и обработчиков событий при активации буфера. 119 | "" }}} 120 | function! s:Class._setOptions() " {{{ 121 | " render {{{ 122 | if has_key(self, 'render') 123 | normal ggVGd 124 | if type(self.render) == 2 125 | silent put = self.render() 126 | else 127 | exe 'silent put = ' . self.render 128 | endif 129 | keepjumps 0d 130 | endif 131 | " }}} 132 | " Слушатели. {{{ 133 | for l:listenerMap in values(self.listenerMap) 134 | exe l:listenerMap 135 | endfor 136 | for l:listenerAu in values(self.listenerAu) 137 | exe l:listenerAu 138 | endfor 139 | " }}} 140 | " Опции. {{{ 141 | for [l:option, l:value] in items(self.options) 142 | exe 'let &l:' . l:option . ' = "' . l:value . '"' 143 | endfor 144 | " }}} 145 | endfunction " }}} 146 | 147 | "" {{{ 148 | " Метод делает окно буфера текущим. 149 | " Если буфер активен в более чем одном окне, метод делает текущим верхнее левое окно. 150 | " @throws RuntimeException Выбрасывается в случае, если на момент вызова метода, буфер не был активен. 151 | "" }}} 152 | function! s:Class.select() " {{{ 153 | let l:winNum = bufwinnr(self.getNum()) 154 | if l:winNum == -1 155 | throw 'RuntimeException: Buffer <' . self.getNum() . '> not active.' 156 | endif 157 | exe l:winNum . 'wincmd w' 158 | endfunction " }}} 159 | 160 | "" {{{ 161 | " Метод деталет вызываемый буфер активным в текущем окне. 162 | " При активации буфера используется свойство или метод render, который отвечает за создание содержимого этого буфера. В качестве этого свойства можно указать вызов любой функции редактора, которая возвращает строку: 163 | " let buffer.render = 'myModule#run()' 164 | " конкретное значение в виде строки, которое будет установлено в буфер: 165 | " let buffer.render = "'Hello world'" 166 | " либо определить метод, который будет возвращать строку: 167 | " function! buffer.render() 168 | " return self.myData 169 | " endfunction 170 | " в этом случае метод будет вызываться от имени объекта, представляющего буфер. 171 | "" }}} 172 | function! s:Class.active() " {{{ 173 | exe 'buffer ' . self.number 174 | cal self._setOptions() 175 | endfunction " }}} 176 | 177 | "" {{{ 178 | " Метод перерисовывает вызываемый буфер. 179 | "" }}} 180 | function! s:Class.redraw() " {{{ 181 | let l:pos = s:Content.pos() 182 | call self.active() 183 | call s:Content.pos(l:pos) 184 | endfunction " }}} 185 | 186 | "" {{{ 187 | " Метод открывает новое окно по горизонтали и делает вызываемый буфер активным в нем. 188 | " @param string pos Позиция нового окна (t - выше текущего окна, b - ниже текущего окна, T - выше всех окон, B - ниже всех окон). 189 | " @param integer gsize [optional] Высота нового окна. Возможно указать процентное значение (n%) относительно текущего окна. 190 | " @see vim_lib#sys#Buffer#.active 191 | "" }}} 192 | function! s:Class.gactive(pos, ...) " {{{ 193 | let l:winheight = winheight(winnr()) 194 | " Определение позиции нового окна. {{{ 195 | if a:pos == 't' 196 | let l:pos = 'leftabove' 197 | elseif a:pos == 'T' 198 | let l:pos = 'topleft' 199 | elseif a:pos == 'b' 200 | let l:pos = 'rightbelow' 201 | elseif a:pos == 'B' 202 | let l:pos = 'botright' 203 | else 204 | let l:pos = '' 205 | endif 206 | " }}} 207 | exe 'silent! ' . l:pos . ' new' 208 | if exists('a:1') 209 | if a:1[-1 : ] == '%' 210 | exe 'resize ' . (l:winheight * a:1[0 : -2] / 100) 211 | else 212 | exe 'resize ' . a:1 213 | endif 214 | endif 215 | let l:newBufNum = bufnr('%') 216 | call self.active() 217 | exe 'bw! ' . l:newBufNum 218 | endfunction " }}} 219 | 220 | "" {{{ 221 | " Метод открывает новое окно по вертикали и делает вызываемый буфер активным в нем. 222 | " @param string pos Позиция нового окна (l - слева от текущего окна, r - справа от текущего окна, L - левее всех окон, R - правее всех окон). 223 | " @param integer vsize [optional] Ширина нового окна. Возможно указать процентное значение (n%) относительно текущего окна. 224 | " @see vim_lib#sys#Buffer#.active 225 | "" }}} 226 | function! s:Class.vactive(pos, ...) " {{{ 227 | let l:winwidth = winwidth(winnr()) 228 | " Определение позиции нового окна. {{{ 229 | if a:pos == 'l' 230 | let l:pos = 'leftabove' 231 | elseif a:pos == 'L' 232 | let l:pos = 'topleft' 233 | elseif a:pos == 'r' 234 | let l:pos = 'rightbelow' 235 | elseif a:pos == 'R' 236 | let l:pos = 'botright' 237 | else 238 | let l:pos = '' 239 | endif 240 | " }}} 241 | exe 'silent! ' . l:pos . ' vnew' 242 | if exists('a:1') 243 | if a:1[-1 : ] == '%' 244 | exe 'vertical resize ' . (l:winwidth * a:1[0 : -2] / 100) 245 | else 246 | exe 'vertical resize ' . a:1 247 | endif 248 | endif 249 | let l:newBufNum = bufnr('%') 250 | call self.active() 251 | exe 'bw! ' . l:newBufNum 252 | endfunction " }}} 253 | 254 | "" {{{ 255 | " Метод определяет локальную опцию буферу. 256 | " Данное значение будет установлено для опции при каждой активации буфера методом active, gactive или vactive. 257 | " @param string name Имя целевой опции. 258 | " @param string value Устанавливаемое значение. 259 | "" }}} 260 | function! s:Class.option(name, value) " {{{ 261 | let self.options[a:name] = a:value 262 | endfunction " }}} 263 | 264 | "" {{{ 265 | " Метод делает буфер временным устанавливая опцию buftype в значение nofile. 266 | "" }}} 267 | function! s:Class.temp() " {{{ 268 | call self.option('buftype', 'nofile') 269 | call self.option('swapfile', '0') 270 | endfunction " }}} 271 | 272 | " Метод listen примеси EventHandle выносится в закрытую область класса. 273 | let s:Class._listen = s:Class.listen 274 | unlet s:Class.listen 275 | "" {{{ 276 | " Метод определяет функцию-обработчик (слушатель) для события клавиатуры. 277 | " Слушатель должен быть методом данного буфера или ссылкой на глобальную функцию. 278 | " @param string mode Режим привязки. Возможно одно из следующих значений: n, v, o, i, l, c. 279 | " @param string sequence Комбинация клавишь, для которой создается привязка. 280 | " @param string listener Имя метода вызываемого буфера или ссылка на глобальную функцию, используемую в качестве функции-обработчика. 281 | "" }}} 282 | function! s:Class.map(mode, sequence, listener) " {{{ 283 | " Исключаем автоматический перевод комбинаций вида в ^... при вызове noremap. 284 | let l:modSeq = substitute(a:sequence, '<', '\\<', '') 285 | let l:modSeq = substitute(l:modSeq, '>', '\\>', '') 286 | call self._listen('keyPress_' . a:mode . a:sequence, a:listener) 287 | let self.listenerMap[a:mode . a:sequence] = a:mode . 'noremap ' . a:sequence . ' :call vim_lib#sys#Buffer#.current().fire("' . a:mode . '", "' . l:modSeq . '")' 288 | endfunction " }}} 289 | 290 | "" {{{ 291 | " Метод определяет функцию-обработчик (слушатель) для события редактора. 292 | " Слушатель должен быть методом данного буфера или ссылкой на глобальную функцию. 293 | " @param string events Имена событий, перечисленных через запятую, к которым выполняется привязка. Доступно одной из приведенных в разделе |autocommand-events| значений. 294 | " @param string listener Имя метода вызываемого буфера или ссылка на глобальную функцию, используемую в качестве функции-обработчика. 295 | "" }}} 296 | function! s:Class.au(events, listener) " {{{ 297 | call self._listen('autocmd_' . a:events, a:listener) 298 | let self.listenerAu[a:events] = 'au ' . a:events . ' ' . bufname(self.getNum()) . ' :call vim_lib#sys#Buffer#.current().doau("' . a:events . '")' 299 | endfunction " }}} 300 | 301 | " Метод ignore примеси EventHandle выносится в закрытую область класса. 302 | let s:Class._ignore = s:Class.ignore 303 | "" {{{ 304 | " Метод удаляет функции-обработчики (слушатели) для события клавиатуры. 305 | " @param string mode Режим привязки. Возможно одно из следующих значений: n, v, o, i, l, c. 306 | " @param string sequence Комбинация клавишь, для которой удаляется привязка. 307 | " @param string listener [optional] Имя удаляемой функции-слушателя. Если параметр не задан, удаляются все слушатели данной комбинации клавишь. 308 | "" }}} 309 | function! s:Class.ignoreMap(mode, sequence, ...) " {{{ 310 | if exists('a:1') 311 | call self._ignore('keyPress_' . a:mode . a:sequence, a:1) 312 | else 313 | call self._ignore('keyPress_' . a:mode . a:sequence) 314 | if has_key(self.listenerMap, a:mode . a:sequence) 315 | unlet self.listenerMap[a:mode . a:sequence] 316 | endif 317 | endif 318 | endfunction " }}} 319 | 320 | "" {{{ 321 | " Метод удаляет функции-обработчики (слушатели) для события редактора. 322 | " @param string events Имена событий, перечисленных через запятую, для которым отменяется привязка. Доступно одной из приведенных в разделе |autocommand-events| значений. 323 | " @param string listener [optional] Имя удаляемой функции-слушателя. Если параметр не задан, удаляются все слушатели данного события редактора. 324 | "" }}} 325 | function! s:Class.ignoreAu(events, ...) " {{{ 326 | if exists('a:1') 327 | call self._ignore('autocmd_' . a:events, a:1) 328 | else 329 | call self._ignore('autocmd_' . a:events) 330 | if has_key(self.listenerAu, a:events) 331 | unlet self.listenerAu[a:events] 332 | endif 333 | endif 334 | if len(self.listeners['autocmd_' . a:events]) == 0 335 | exe 'au! ' . a:events . ' ' . bufname(self.getNum()) 336 | endif 337 | endfunction " }}} 338 | 339 | " Метод fire примеси EventHandle выносится в закрытую область класса. 340 | let s:Class._fire = s:Class.fire 341 | "" {{{ 342 | " Метод генерирует событие клавиатуры для данного буфера. 343 | " @param string mode Режим привязки. Возможно одно из следующих значений: n, v, o, i, l, c. 344 | " @param string sequence Комбинация клавишь, для которой генерируется событие нажатия. 345 | "" }}} 346 | function! s:Class.fire(mode, sequence) " {{{ 347 | call self._fire('keyPress_' . a:mode . a:sequence) 348 | endfunction " }}} 349 | 350 | "" {{{ 351 | " Метод генерирует событие редактора для данного буфера. 352 | " @param string events Имена событий, перечисленных через запятую, для которых выполняется генерация. Доступно одной из приведенных в разделе |autocommand-events| значений. 353 | "" }}} 354 | function! s:Class.doau(events) " {{{ 355 | call self._fire('autocmd_' . a:events) 356 | endfunction " }}} 357 | 358 | let g:vim_lib#sys#Buffer# = s:Class 359 | -------------------------------------------------------------------------------- /autoload/vim_lib/sys/tests/TestBuffer.vim: -------------------------------------------------------------------------------- 1 | " Date Create: 2015-01-07 15:58:24 2 | " Last Change: 2015-02-09 14:55:07 3 | " Author: Artur Sh. Mamedbekov (Artur-Mamedbekov@yandex.ru) 4 | " License: GNU GPL v3 (http://www.gnu.org/copyleft/gpl.html) 5 | 6 | let s:Buffer = vim_lib#sys#Buffer# 7 | 8 | let s:Test = vim_lib#base#Test#.expand() 9 | 10 | " new, current {{{ 11 | "" {{{ 12 | " Должен запоминать заданный номер буфера. 13 | " @covers vim_lib#sys#Buffer#.new 14 | "" }}} 15 | function s:Test.testNew_saveNum() " {{{ 16 | let l:obj = s:Buffer.new(bufnr('%')) 17 | call self.assertEquals(l:obj.getNum(), bufnr('%')) 18 | endfunction " }}} 19 | 20 | "" {{{ 21 | " Должен выбрасывать исключение в случае, если требуемого буфера не сущесвует. 22 | " @covers vim_lib#sys#Buffer#.new 23 | "" }}} 24 | function s:Test.testNew_throwOutOfRange() " {{{ 25 | try 26 | let l:obj = s:Buffer.new(bufnr('$') + 1) 27 | call self.fail('testNew_throwOutOfRange', 'Expected exception is not thrown.') 28 | catch /IndexOutOfRangeException:.*/ 29 | endtry 30 | endfunction " }}} 31 | 32 | "" {{{ 33 | " Если параметр не указан, должен создавать новый, анонимный буфер. 34 | " @covers vim_lib#sys#Buffer#.new 35 | "" }}} 36 | function s:Test.testNew_createBuffer() " {{{ 37 | let l:bufCount = bufnr('$') 38 | let l:obj = s:Buffer.new() 39 | call self.assertTrue(bufnr('$') > l:bufCount) 40 | exe 'bw! ' . l:obj.getNum() 41 | endfunction " }}} 42 | 43 | "" {{{ 44 | " Если параметр текстовый, должен создавать новый, именованный буфер. 45 | " @covers vim_lib#sys#Buffer#.new 46 | "" }}} 47 | function! s:Test.testNew_createBufferOnName() " {{{ 48 | let l:bufCount = bufnr('$') 49 | let l:obj = s:Buffer.new('#TestBuffer#') 50 | call self.assertTrue(bufnr('$') > l:bufCount) 51 | exe 'bw! ' . l:obj.getNum() 52 | endfunction " }}} 53 | 54 | "" {{{ 55 | " Если параметр текстовый и буфер с таким именем уже существует, должен возвращать его. 56 | " @covers vim_lib#sys#Buffer#.new 57 | "" }}} 58 | function! s:Test.testNew_returtNamedBuffer() " {{{ 59 | let l:objA = s:Buffer.new('#TestBuffer#') 60 | let l:objB = s:Buffer.new('#TestBuffer#') 61 | call self.assertEquals(l:objA.getNum(), l:objB.getNum()) 62 | exe 'bw! ' . l:objA.getNum() 63 | endfunction " }}} 64 | 65 | "" {{{ 66 | " Должен сохранять и восстанавливать объекты из пула. 67 | " @covers vim_lib#sys#Buffer#.new 68 | "" }}} 69 | function s:Test.testNew_usePool() " {{{ 70 | let l:obj = s:Buffer.new() 71 | let l:obj.test = 1 72 | let l:obj2 = s:Buffer.new(l:obj.getNum()) 73 | call self.assertEquals(l:obj.test, l:obj2.test) 74 | exe 'bw! ' . l:obj.getNum() 75 | endfunction " }}} 76 | 77 | "" {{{ 78 | " Должен создавать объект текущего буфера. 79 | " @covers vim_lib#sys#Buffer#.current 80 | "" }}} 81 | function! s:Test.testCurrent() " {{{ 82 | let l:obj = s:Buffer.current() 83 | call self.assertEquals(l:obj.getNum(), bufnr('%')) 84 | endfunction " }}} 85 | " }}} 86 | " getNum {{{ 87 | "" {{{ 88 | " Должен возвращать номер буфера. 89 | " @covers vim_lib#sys#Buffer#.getNum 90 | "" }}} 91 | function s:Test.testGetNum() " {{{ 92 | let l:obj = s:Buffer.new(bufnr('%')) 93 | call self.assertEquals(l:obj.getNum(), bufnr('%')) 94 | endfunction " }}} 95 | " }}} 96 | " delete {{{ 97 | "" {{{ 98 | " Должен удалять буфер. 99 | " @covers vim_lib#sys#Buffer#.delete 100 | "" }}} 101 | function s:Test.testDelete() " {{{ 102 | let l:bufCount = bufnr('$') 103 | let l:obj = s:Buffer.new() 104 | call l:obj.delete() 105 | call self.assertTrue(bufnr('$') == l:bufCount) 106 | endfunction " }}} 107 | " }}} 108 | " active, gactive, vactive {{{ 109 | "" {{{ 110 | " Должен делать вызываемый буфер активным в текущем окне. 111 | " @covers vim_lib#sys#Buffer#.active 112 | "" }}} 113 | function s:Test.testActive() " {{{ 114 | let l:obj = s:Buffer.new(bufnr('%')) 115 | new 116 | let l:newBuff = bufnr('%') 117 | call l:obj.active() 118 | call self.assertEquals(l:obj.getNum(), bufnr('%')) 119 | exe 'q' 120 | exe 'bw! ' . l:newBuff 121 | endfunction " }}} 122 | 123 | "" {{{ 124 | " Должен делать вызываемый буфер активным в новом, горизонтальном окне. 125 | " @covers vim_lib#sys#Buffer#.gactive 126 | "" }}} 127 | function s:Test.testGactive_openNewHorizontalWin() " {{{ 128 | let l:winCount = winnr('$') 129 | let l:obj = s:Buffer.new() 130 | call l:obj.gactive('t') 131 | call self.assertTrue(winnr('$') > l:winCount) 132 | exe 'q' 133 | endfunction " }}} 134 | 135 | "" {{{ 136 | " Должен автоматически удалять временный буфер, создаваемый при открытии нового окна. 137 | " @covers vim_lib#sys#Buffer#.gactive 138 | "" }}} 139 | function s:Test.testGactive_delTmpBuffer() " {{{ 140 | let l:bufCount = bufnr('$') 141 | let l:obj = s:Buffer.new(bufnr('%')) 142 | call l:obj.gactive('t') 143 | call self.assertTrue(bufnr('$') == l:bufCount) 144 | exe 'q' 145 | endfunction " }}} 146 | 147 | "" {{{ 148 | " Должен устанавливать высоту нового окна. 149 | " @covers vim_lib#sys#Buffer#.gactive 150 | "" }}} 151 | function! s:Test.testGactive_resize() " {{{ 152 | let l:winCount = winnr('$') 153 | let l:obj = s:Buffer.new(bufnr('%')) 154 | call l:obj.gactive('t', 20) 155 | call self.assertEquals(winheight(0), 20) 156 | exe 'q' 157 | endfunction " }}} 158 | 159 | "" {{{ 160 | " Должен делать вызываемый буфер активным в новом, вертикальном окне. 161 | " @covers vim_lib#sys#Buffer#.vactive 162 | "" }}} 163 | function s:Test.testVactive_openNewVerticalWin() " {{{ 164 | let l:winCount = winnr('$') 165 | let l:obj = s:Buffer.new() 166 | call l:obj.vactive('l') 167 | call self.assertTrue(winnr('$') > l:winCount) 168 | exe 'q' 169 | endfunction " }}} 170 | 171 | "" {{{ 172 | " Должен автоматически удалять временный буфер, создаваемый при открытии нового окна. 173 | " @covers vim_lib#sys#Buffer#.vactive 174 | "" }}} 175 | function s:Test.testVactive_delTmpBuffer() " {{{ 176 | let l:bufCount = bufnr('$') 177 | let l:obj = s:Buffer.new(bufnr('%')) 178 | call l:obj.vactive('l') 179 | call self.assertTrue(bufnr('$') == l:bufCount) 180 | exe 'q' 181 | endfunction " }}} 182 | 183 | "" {{{ 184 | " Должен устанавливать ширину нового окна. 185 | " @covers vim_lib#sys#Buffer#.vactive 186 | "" }}} 187 | function! s:Test.testVactive_resize() " {{{ 188 | let l:winCount = winnr('$') 189 | let l:obj = s:Buffer.new(bufnr('%')) 190 | call l:obj.vactive('l', 20) 191 | call self.assertEquals(winwidth(0), 20) 192 | exe 'q' 193 | endfunction " }}} 194 | " }}} 195 | " option, temp {{{ 196 | "" {{{ 197 | " Должен делать вызываемый буфер активным в текущем окне. 198 | " @covers vim_lib#sys#Buffer#.option 199 | "" }}} 200 | function s:Test.testOption() " {{{ 201 | let l:currentBuf = s:Buffer.new(bufnr('%')) 202 | let l:obj = s:Buffer.new() 203 | call l:obj.option('filetype', 'test') 204 | call l:obj.gactive('t') 205 | call self.assertEquals(&l:filetype, 'test') " Опции устанавливаются после активации в новом окне. 206 | call l:currentBuf.active() 207 | call l:obj.active() 208 | call self.assertEquals(&l:filetype, 'test') " Опции устанавливаются после активации в текущем окне. 209 | call l:obj.delete() 210 | endfunction " }}} 211 | 212 | "" {{{ 213 | " Должен устанавливать опцию buftype в значение nofile. 214 | " @covers vim_lib#sys#Buffer#.temp 215 | "" }}} 216 | function! s:Test.testTemp() " {{{ 217 | let l:currentBuf = s:Buffer.new(bufnr('%')) 218 | let l:obj = s:Buffer.new() 219 | call l:obj.temp() 220 | call l:obj.gactive('t') 221 | call self.assertEquals(&l:buftype, 'nofile') 222 | call l:obj.delete() 223 | endfunction " }}} 224 | " }}} 225 | " map, ignoreMap, fire {{{ 226 | "" {{{ 227 | " Должен устанавливать привязку при активации буфера. 228 | " @covers vim_lib#sys#Buffer#.map 229 | "" }}} 230 | function s:Test.testMap_setListener() " {{{ 231 | let l:obj = s:Buffer.new() 232 | call l:obj.map('n', 'q', 'testA') 233 | call l:obj.map('n', 'q', 'testB') 234 | call self.assertEquals(l:obj.listenerMap, {'nq': 'nnoremap q :call vim_lib#sys#Buffer#.current().fire("n", "q")'}) 235 | call self.assertEquals(l:obj.listeners, {'keyPress_nq': ['testA', 'testB']}) 236 | call l:obj.gactive('t') 237 | call self.assertExec('nnoremap q', "\n\n" . 'n q *@:call vim_lib#sys#Buffer#.current().fire("n", "q")') 238 | call l:obj.delete() 239 | endfunction " }}} 240 | 241 | "" {{{ 242 | " Должен удалять конкретный обработчик. 243 | " @covers vim_lib#sys#Buffer#.ignoreMap 244 | "" }}} 245 | function s:Test.testIgnoreMap_deleteListener() " {{{ 246 | let l:obj = s:Buffer.new() 247 | call l:obj.map('n', 'q', 'testA') 248 | call l:obj.map('n', 'q', 'testA') 249 | call l:obj.map('n', 'q', 'testB') 250 | call l:obj.map('n', 'q', 'testB') 251 | call l:obj.ignoreMap('n', 'q', 'testA') 252 | call self.assertDictHasKey(l:obj.listenerMap, 'nq') 253 | call self.assertEquals(l:obj.listeners, {'keyPress_nq': ['testB', 'testB']}) 254 | endfunction " }}} 255 | 256 | "" {{{ 257 | " Должен удалять все привязки. 258 | " @covers vim_lib#sys#Buffer#.ignoreMap 259 | "" }}} 260 | function s:Test.testIgnoreMap_deleteAllListeners() " {{{ 261 | let l:obj = s:Buffer.new() 262 | call l:obj.map('n', 'q', 'test') 263 | call l:obj.ignoreMap('n', 'q') 264 | call self.assertDictNotHasKey(l:obj.listenerMap, 'nq') 265 | call l:obj.gactive('t') 266 | call self.assertExec('nnoremap nq', "\n\n" . 'Привязки не найдены') 267 | call l:obj.delete() 268 | endfunction " }}} 269 | 270 | "" {{{ 271 | " Должен генерировать события. 272 | " @covers vim_lib#sys#Buffer#.fire 273 | "" }}} 274 | function! s:Test.testFire() " {{{ 275 | let l:obj = s:Buffer.new() 276 | let l:obj.x = 0 277 | function! l:obj.inc(...) " {{{ 278 | let self.x += 1 279 | endfunction " }}} 280 | call l:obj.map('n', 'q', 'inc') 281 | call l:obj.map('n', 'q', 'inc') 282 | call l:obj.gactive('t') 283 | call l:obj.fire('n', 'q') 284 | call self.assertEquals(l:obj.x, 2) 285 | call l:obj.delete() 286 | endfunction " }}} 287 | " }}} 288 | " au, ignoreAu, doau {{{ 289 | "" {{{ 290 | " Должен устанавливать привязку при активации буфера. 291 | " @covers vim_lib#sys#Buffer#.au 292 | "" }}} 293 | function s:Test.testAu_setListener() " {{{ 294 | let l:obj = s:Buffer.new() 295 | call l:obj.au('BufEnter', 'testA') 296 | call l:obj.au('BufEnter', 'testB') 297 | call self.assertEquals(l:obj.listenerAu, {'BufEnter': 'au BufEnter ' . bufname(l:obj.getNum()) . ' :call vim_lib#sys#Buffer#.current().doau("BufEnter")'}) 298 | call self.assertEquals(l:obj.listeners, {'autocmd_BufEnter': ['testA', 'testB']}) 299 | call l:obj.gactive('t') 300 | call self.assertExec('autocmd BufEnter ' . bufname(l:obj.getNum()), "\n" . '--- Автокоманды ---' . "\n" . 'BufEnter' . "\n" . ' ' . bufname(l:obj.getNum()) . ' :call vim_lib#sys#Buffer#.current().doau("BufEnter")') 301 | call l:obj.delete() 302 | endfunction " }}} 303 | 304 | "" {{{ 305 | " Должен удалять конкретный обработчик. 306 | " @covers vim_lib#sys#Buffer#.ignoreAu 307 | "" }}} 308 | function s:Test.testIgnoreAu_deleteListener() " {{{ 309 | let l:obj = s:Buffer.new() 310 | call l:obj.au('BufEnter', 'testA') 311 | call l:obj.au('BufEnter', 'testA') 312 | call l:obj.au('BufEnter', 'testB') 313 | call l:obj.au('BufEnter', 'testB') 314 | call l:obj.ignoreAu('BufEnter', 'testA') 315 | call self.assertDictHasKey(l:obj.listenerAu, 'BufEnter') 316 | call self.assertEquals(l:obj.listeners, {'autocmd_BufEnter': ['testB', 'testB']}) 317 | endfunction " }}} 318 | 319 | "" {{{ 320 | " Должен удалять все привязки. 321 | " @covers vim_lib#sys#Buffer#.ignoreAu 322 | "" }}} 323 | function s:Test.testIgnoreAu_deleteAllListeners() " {{{ 324 | let l:obj = s:Buffer.new() 325 | call l:obj.au('BufEnter', 'test') 326 | call l:obj.ignoreAu('BufEnter') 327 | call self.assertDictNotHasKey(l:obj.listenerAu, 'BufEnter') 328 | call l:obj.gactive('t') 329 | call self.assertExec('autocmd BufEnter ' . bufname(l:obj.getNum()), "\n" . '--- Автокоманды ---') 330 | call l:obj.delete() 331 | endfunction " }}} 332 | 333 | "" {{{ 334 | " Должен генерировать события. 335 | " @covers vim_lib#sys#Buffer#.doau 336 | "" }}} 337 | function! s:Test.testDoau() " {{{ 338 | let l:obj = s:Buffer.new() 339 | let l:obj.x = 0 340 | function! l:obj.inc(...) " {{{ 341 | let self.x += 1 342 | endfunction " }}} 343 | call l:obj.au('BufEnter', 'inc') 344 | call l:obj.au('BufEnter', 'inc') 345 | call l:obj.gactive('t') 346 | call l:obj.doau('BufEnter') 347 | call self.assertEquals(l:obj.x, 2) 348 | call l:obj.delete() 349 | endfunction " }}} 350 | " }}} 351 | " render {{{ 352 | "" {{{ 353 | " Должен добавлять содержимое буфера из строки. 354 | " @covers vim_lib#sys#Buffer#.render 355 | "" }}} 356 | function! s:Test.testRender_setStr() " {{{ 357 | let l:obj = s:Buffer.new() 358 | call l:obj.option('buftype', 'nofile') 359 | let l:obj.render = "'test'" 360 | call l:obj.gactive('t') 361 | call self.assertEquals('test', join(getline(0, '$'), '')) 362 | call l:obj.delete() 363 | endfunction " }}} 364 | 365 | "" {{{ 366 | " Должен добавлять содержимое буфера из метода. 367 | " @covers vim_lib#sys#Buffer#.render 368 | "" }}} 369 | function! s:Test.testRender_setMethod() " {{{ 370 | let l:obj = s:Buffer.new() 371 | call l:obj.option('buftype', 'nofile') 372 | function! l:obj.render() " {{{ 373 | return 'test' 374 | endfunction " }}} 375 | call l:obj.gactive('t') 376 | call self.assertEquals('test', join(getline(0, '$'), '')) 377 | call l:obj.delete() 378 | endfunction " }}} 379 | " }}} 380 | " select {{{ 381 | "" {{{ 382 | " Должен делать окно буфера активным. 383 | " @covers vim_lib#sys#Buffer#.select 384 | "" }}} 385 | function! s:Test.testSelect() " {{{ 386 | let l:obj = s:Buffer.new() 387 | call l:obj.gactive('t') 388 | exe winnr('#') . 'wincmd w' 389 | call l:obj.select() 390 | call self.assertEquals(bufwinnr(l:obj.getNum()), winnr()) 391 | call l:obj.delete() 392 | endfunction " }}} 393 | 394 | "" {{{ 395 | " Должен выбрасывать исключение, если буфер не активен. 396 | " @covers vim_lib#sys#Buffer#.select 397 | "" }}} 398 | function! s:Test.testSelect_throwException() " {{{ 399 | let l:obj = s:Buffer.new() 400 | try 401 | call l:obj.select() 402 | call self.fail('testSelect_throwException', 'Expected exception is not thrown.') 403 | catch /RuntimeException:.*/ 404 | endtry 405 | endfunction " }}} 406 | " }}} 407 | " getWinNum {{{ 408 | "" {{{ 409 | " Должен возвращать номер окна, в котором активен буфер. 410 | " @covers vim_lib#sys#Buffer#.getWinNum 411 | "" }}} 412 | function! s:Test.testGetWinNum() " {{{ 413 | let l:obj = s:Buffer.new() 414 | call l:obj.gactive('t') 415 | call self.assertEquals(l:obj.getWinNum(), bufwinnr(l:obj.getNum())) 416 | call l:obj.delete() 417 | endfunction " }}} 418 | 419 | "" {{{ 420 | " Должен возвращать -1, если буфер не активен. 421 | " @covers vim_lib#sys#Buffer#.getWinNum 422 | "" }}} 423 | function! s:Test.testGetWinNum_returnNegative() " {{{ 424 | let l:obj = s:Buffer.new() 425 | call self.assertEquals(l:obj.getWinNum(), -1) 426 | call l:obj.delete() 427 | endfunction " }}} 428 | " }}} 429 | 430 | let g:vim_lib#base#tests#TestBuffer# = s:Test 431 | call s:Test.run() 432 | -------------------------------------------------------------------------------- /doc/vim_lib.rux: -------------------------------------------------------------------------------- 1 | *vim_lib.txt* Для Vim версии 7.0. *vim_lib* 2 | 3 | РУКОВОДСТВО ПО VIM_LIB 4 | 5 | 1. Описание |vim_lib-description| 6 | 2. Пакет base |vim_lib-base| 7 | 2.1. Object |vim_lib-base-object| 8 | 2.2. Stack |vim_lib-base-stack| 9 | 2.3. List |vim_lib-base-list| 10 | 2.4. Dict |vim_lib-base-dict| 11 | 2.5. Test |vim_lib-base-test| 12 | 2.6. File |vim_lib-base-file| 13 | 2.7. EventHandle |vim_lib-base-eventhandle| 14 | 3. Пакет sys |vim_lib-sys| 15 | 3.1. Buffer |vim_lib-sys-buffer| 16 | 3.2. Content |vim_lib-sys-content| 17 | 3.3. Plugin |vim_lib-sys-plugin| 18 | 3.4. NullPlugin |vim_lib-sys-nullplugin| 19 | 3.5. Publisher |vim_lib-sys-publisher| 20 | 3.6. System |vim_lib-sys-system| 21 | 3.7. Autoload |vim_lib-sys-autoload| 22 | 4. Пакет view |vim_lib-view| 23 | 4.1. BufferStack |vim_lib-view-bufferstack| 24 | 25 | ================================================================================ 26 | 1. Описание *vim_lib-description* 27 | 28 | Vim_Lib - это объектно-ориентированная библиотека, включающая множество 29 | готовых решений низкоуровневых задач на языке VimScript. Библиотека 30 | подключается в виде autoload плагина, что позволяет загружать ее компоненты 31 | "по требованию". 32 | 33 | Библиотека имеет многоуровневую, вложенную структуру, которая включает: 34 | 35 | - Пакеты - каталоги классов, группирующие их по области применения. К ним 36 | относятся такие пакеты, как |vim_lib-base|, |vim_lib-sys| и так далее 37 | - Классы - минимальные компоненты библиотеки, решающие конкретные задачи и 38 | объединяющие общую логику. К ним относятся такие классы, как 39 | |vim_lib-base-Object|, |vim_lib-base-Stack|, |vim_lib-sys-Plugin| и так далее 40 | 41 | Все классы библиотеки являются дочерними, по отношению к классу 42 | |vim_lib-base-Object|, что унифицирует их интерфейс и приводит к единому 43 | формату использования, а именно: 44 | > 45 | let s:Stack = vim_lib#base#Stack# " Создаем псевдоним (alias) класса 46 | let s:stk = s:Stack.new() " Инстанциируем класс 47 | " Используем объект 48 | let s:stk.push(1) 49 | echo s:stk.pop() 50 | 51 | Из примера видно, что программный код, использующий любой класс библиотеки, 52 | состоит из трех компонентов: 53 | 54 | - Создание псевдонима - данная операция позволяет не только сократить имя 55 | класса, но и явно назвать те компоненты библиотеки, которые используются 56 | данным кодом 57 | - Инстанциация класса - создание экземпляра класса с помощью конструктора new 58 | - Использование объекта - применение объекта для решения конкретных задач 59 | 60 | Помимо классов, библиотека включает подробную документацию их интерфейса и 61 | модульные тесты, гарантирующие ожидаемую работу компонентов. Документация 62 | содержится внутри файлов классов, а тесты в каталогах "tests" каждого пакета 63 | библиотеки. 64 | 65 | ================================================================================ 66 | 2. Пакет base *vim_lib-base* 67 | 68 | Пакет base включает базовые, низкоуровневые классы библиотеки. 69 | 70 | 2.1. Object *vim_lib-base-object* 71 | ----------- 72 | 73 | Класс Object является родителем всех классов библиотеки и реализует базовый 74 | интерфейс объектов, а именно: 75 | 76 | - Структура объектов - свойства 'class' и 'parent', определенные во всех 77 | объектах 78 | - Свойства класса - необязательный словарь 'properties', элементы которого 79 | представляют свойства класса 80 | - Конструктор - метод 'new', используемый для инстанциации класса 81 | - Механизм наследования - метод 'expand', используемый для расширения 82 | функциональности класса за счет реализации его потомков 83 | - Базовая типизация - метод 'typeof', используемый для определения 84 | принадлежности класса к целевой иерархии наследования 85 | 86 | Все объекты класса Object или его подклассов представляют обычные словари. При 87 | этом свойства определены как элементы этих словарей, а методы как 'dict' 88 | функции. Для связи объекта с классом, его породившим, создается свойство 89 | 'class', которое ссылается на объект класса (классы такой ссылки не имеют). 90 | Если объект является экземпляром подкласса, то в нем определяется свойство 91 | 'parent', содержащее ссылку на соответствующий объект надкласса. Аналогичную 92 | ссылку имеют все подклассы. Другими словари при создании объекта дочернего 93 | класса, автоматически создаются объекты всех надклассов, связанные снизу вверх 94 | с помощью свойства 'parent'. Такое решение позволяет не смешивать свойства 95 | объектов между собой, а также разделять логику конструктора между объектами. 96 | 97 | Для создания объекта, достаточно воспользоваться конструктором 'new': 98 | > 99 | let s:obj = s:MyClass.new() " Метод может принимать параметры 100 | 101 | Конструктор, как и все методы класса Object, является статичным, то есть не 102 | переносятся в экземпляр класса, что не позволяет вызвать конструктор от имени 103 | объекта. Если же необходимо создать объект того же класса, что и исходный 104 | объект, необходимо использовать ссылку 'class': 105 | > 106 | let s:otherObj = s:obj.class.new() " class ссылается на s:MyClass 107 | 108 | Программист может самостоятельно объявить статичные методы класса. Для этого 109 | метод должен начинаться с двух символов подчеркивания (__). 110 | 111 | Пример: 112 | > 113 | function! s:MyClass.__myStaticMethod() 114 | echo 'Hello world' 115 | endfunction 116 | 117 | Основой целью класса Object является реализация механизма наследования и 118 | расширения существующих классов. Это достигается благодаря методу 'expand'. 119 | Для расширения класса через наследование необходимо: 120 | 121 | - Использовать метод 'expand' у надкласса для создания заготовки 122 | - Определить свойства класса с помощью словаря 'properties' 123 | - В случае необходимости, добавить логику из примести в класс с помощью 124 | метода 'mix' 125 | - Реализовать конструктор 'new' 126 | - Реализовать методы класса 127 | 128 | Метод 'expand' создает новый словарь и добавляет к нему все методы класса 129 | Object, а также свойство 'parent', которое ссылается на родительский класс. 130 | Этот словарь будет возвращен из метода и определит заготовку будущего класса. 131 | 132 | Если для класса определен словарь 'properties', то элементы этого словаря 133 | будут скопированы в заготовку объекта методом 'bless': 134 | > 135 | let s:MyClass.properties = {'propA': 1, 'propB': 2} " Свойства класса 136 | 137 | Значения этих свойств могут быть переопределены в конструкторе: 138 | > 139 | function! s:MyClass.new(a, b) 140 | let l:obj = self.bless() 141 | " Переопределение значений свойств 142 | let l:obj.propA = a:a 143 | let l:obj.propB = a:b 144 | return l:obj 145 | endfunction 146 | 147 | Класс Object позволяет использовать "примести" для добавления логики в классы. 148 | Для добавления примеси в класс необходимо: 149 | 150 | - Реализовать класс примеси в виде подкласса Object 151 | - Вызвать метод 'mix' класса, к которому будет добавляться примесь, передав 152 | в качестве единственного параметра класс примеси 153 | 154 | Важно помнить, что метод 'mix' должен вызываться после определения словаря 155 | 'properties' класса, так как в класс добавляются и свойства примеси. 156 | 157 | Пример: 158 | > 159 | let s:MyClass = s:Object.expand() 160 | let s:MyClass.properties = {...} 161 | let s:MyClass.mix(s:MyMix) " Примесь класса MyMix 162 | 163 | Более подробно познакомиться с механизмом примесей вы можете на примере 164 | классов "vim_lib/autoload/base/tests/Object/Mix" и 165 | "vim_lib/autoload/base/tests/Object/Parent". 166 | 167 | Для реализации конструктора нового класса необходимо переопределить метод 168 | 'new' для заготовки, создаваемой методом 'expand'. Конструктор может принимать 169 | любое число аргументов, с помощью которых будут инициализировать экземпляры 170 | этого класса. Конструктор должен создавать новые словари для всех надклассов, 171 | определять ссылки 'class' и 'parent', а также инициализировать свойства 172 | будущего объекта. Сделать это можно следующим способом: 173 | 174 | - Создать заготовку объекта с помощью метода 'bless' класса Object, передав 175 | ему результат работы конструктора надкласса 176 | - Определить для полученной заготовки свойства не переопределяя ссылки 177 | 'parent' и 'class' 178 | - Вернуть получившийся объект 179 | 180 | После определения конструктора класса, необходимо реализовать его методы. Для 181 | этого достаточно создать 'dict' функции для полученного класса, которые будут 182 | обращаться к свойствам экземпляров этого класса через переменную 'self': 183 | > 184 | function! s:MyClass.getA() 185 | return self.a 186 | endfunction 187 | 188 | Важно помнить, что методы класса относятся к тому объекту, в классе которого 189 | они были объявлены. Так, если в классе Parent реализован метод getA, то в 190 | объекте его подкласса Child, этот метод будет доступен только через ссылку 191 | 'parent' объекта: 192 | > 193 | echo s:child.parent.getA() 194 | 195 | Это связано с тем, что для каждого класса создается отдельный объект, хранящий 196 | его свойства. 197 | 198 | Более подробно познакомиться с механизмом наследования вы можете на примере 199 | классов "vim_lib/autoload/base/tests/Object/Parent" и 200 | "vim_lib/autoload/base/tests/Object/Child". 201 | 202 | 2.2. Stack *vim_lib-base-stack* 203 | ---------- 204 | 205 | Класс Stack представляет список данных, организованный по принципу LIFO. Класс 206 | реализует несколько методов манипулирования стеком, а также метод 'length', 207 | вычисляющий текущую длину стека (число элементов в нем). 208 | 209 | Изначально экземпляр данного класса пуст, для добавления в него элементов 210 | используется метод 'push', который вставляет данные в вершину стека. Метод 211 | 'pop' наоборот выталкивает данные из вершины и возвращает их. Если необходимо 212 | получить данные с вершины стека без выталкивания, используется метод 213 | 'current'. 214 | 215 | Пример: 216 | > 217 | let s:Stack = vim_lib#base#Stack# 218 | let s:stk = s:Stack.new() 219 | call s:stk.push(1) 220 | call s:stk.push(2) 221 | echo s:stk.length() " 2 222 | echo s:stk.pop() " 2 223 | echo s:stk.pop() " 1 224 | echo s:stk.length() " 0 225 | 226 | 2.3. List *vim_lib-base-list* 227 | --------- 228 | 229 | Класс List представляет упорядоченный, изменяемый список данных с доступом к 230 | элемента по индексу. Класс реализует основные методы для доступа к элементам 231 | списка, управления им и получения информации о его структуре. 232 | 233 | Список List может быть инициализирован при инстанциации любым массивом, либо 234 | быть пустым. Для определения или получения значений элементов списка 235 | используется метод 'item'. Для выделения среза используется метод 'sec'. Метод 236 | 'length' позволяет определить длину (число элементов) списка. 237 | 238 | Пример: 239 | > 240 | let s:List = vim_lib#base#List# 241 | let s:lst = s:List.new([1,2,3]) 242 | call s:lst.item(0, 5) 243 | echo s:lst.item(0) " 5 244 | echo s:lst.length() " 3 245 | echo s:lst.list() " [5,2,3] 246 | 247 | 2.4. Dict *vim_lib-base-dict* 248 | --------- 249 | 250 | Класс Dict представляет изменяемый словарь данных с доступом к элемента по 251 | ключу. Класс реализует основные методы для доступа к элементам словаря, 252 | управления им и получения информации о его структуре. 253 | 254 | Словарь Dict может быть инициализирован при инстанциации любым словарем или 255 | массивом следующей структуры: 256 | > 257 | [[ключ, значение], ...] 258 | 259 | либо быть пустым. Для определения или получения значений элементов словаря 260 | используется метод 'item'. Для выделения элементов словаря, таких как ключи, 261 | значения и так далее, используются методы 'keys', 'vals' и 'items'. Метод 262 | 'length' позволяет определить длину (число элементов) словаря. 263 | 264 | Пример: 265 | > 266 | let s:Dict = vim_lib#base#Dict# 267 | let s:dct = s:Dict.new({'a': 1}) 268 | call s:dct.item('b', 2) 269 | echo s:dct.length() " 2 270 | echo s:dct.item('a') " 1 271 | echo s:dct.keys() " ['a', 'b'] 272 | 273 | 2.5. Test *vim_lib-base-test* 274 | --------- 275 | 276 | Класс Test является родителем для модульных тестов, используемых в библиотеке. 277 | Его также можно использовать для тестирования собственных классов и плагинов. 278 | Класс не является потомком класса Object, потому имеет несколько отличную 279 | структуру, делающую его дочерние классы службами, нежели объектами. 280 | 281 | Для создания модульного теста, необходимо: 282 | 283 | - Создать заготовку тестирующего класса с помощью метода 'expand' класса Test 284 | - Определить методы для тестирования 285 | - Выполнить тестирование 286 | 287 | Как и потомки класса Object, для создания потомка класса Test используется 288 | метод 'expand': 289 | > 290 | let s:Dict = vim_lib#base#Dict# 291 | let s:Test = vim_lib#base#Test#.expand() 292 | 293 | При этом результирующий класс получает весь необходимый функционал для 294 | тестирования. 295 | 296 | Тесты создаются в виде методов класса, имя которых должно начинаться с 'test'. 297 | В них создается подходящее окружение и оцениваются результаты работы с помощью 298 | методов 'assert': 299 | > 300 | function! s:Test.testItem 301 | let s:obj = s:Dict.new() 302 | call s:obj.item('a', 1) 303 | call self.assertEquals(s:obj.item('a'), 1) 304 | call self.assertEquals(s:obj.length(), 1) 305 | endfunction 306 | 307 | Естественно, тестирующий метод должен быть построен таким образом, чтобы после 308 | его выполнения система находилась в начальном состоянии. Должны быть удалены 309 | все глобальные переменные, которые были созданы внутри метода, либо их 310 | значение должно быть восстановлено. 311 | 312 | Для тестирования выброса исключений, используется следующая схема: 313 | > 314 | function! s:Test.testItem_throwException 315 | let l:obj = s:Dict.new() 316 | try 317 | call l:obj.item('a') 318 | call self.fail('testItem', 'Expected not found') 319 | catch /IndexOutOfRangeException:.*/ 320 | endtry 321 | endfunction 322 | 323 | Здесь, с помощью метода 'fail' мы определяем провальность теста. 324 | 325 | В конце модульного теста необходимо вызвать метод 'run', который будет 326 | выполнять тестирование: 327 | > 328 | call s:Test.run() 329 | 330 | Метод так же может принимать в качестве единственного параметра имя 331 | тестирующего метода. В этом случае тестироваться будут не все методы, а только 332 | заданный: 333 | > 334 | call s:Test.run('testItem') 335 | 336 | Важно помнить, что каждый тестирующий метод должен проверять работы одного 337 | конкретного требования, а не тестировать весь функционал метода. В связи с 338 | этим, для тестирования одного метода класса часто используется несколько 339 | тестирующих методов. 340 | 341 | 2.6. File *vim_lib-base-file* 342 | --------- 343 | 344 | Класс File служит для объектного представления компонентов файловой системы в 345 | редакторе. Объекты данного класса представляют как файлы, так и каталоги, при 346 | чем не обязательно существующие в данный момент. 347 | 348 | Не смотря на то, что класс является подклассом Object, конструкторов в нем 349 | два. Это связано с разными схемами инстанциации, а именно: 350 | 351 | - Метод 'absolute' - создание объекта по абсолютному пути к файлу; 352 | - Метод 'relative' - создание объекта по относительному пути к файлу. Путь 353 | рассчитывается от каталога текущего файла. 354 | 355 | Важно помнить, что конструктор 'relative' рассчитывает путь относительно 356 | текущего файла редактора, а не адреса скрипта, в котором он используется. Так, 357 | вызов конструктора каким-либо плагином для доступа к файлам этого плагина 358 | может привести к созданию неверного пути, так как основанием пути будет не 359 | этот плагин, а текущий файл редактора. Для решения этой проблемы можно 360 | использовать абсолютные пути и метод 'expand': 361 | > 362 | let s:File = vim_lib#base#File# 363 | let s:f = s:File.absolute(expand('') . '/Storage/base.db') 364 | 365 | Класс предоставляет подробную информацию о своем пути с помощью методов 366 | 'getName', 'getDir' и 'getAddress'. 367 | 368 | Пример: 369 | > 370 | echo s:f.getName() " base.db 371 | echo s:f.getDir().getName() " Storage 372 | echo s:f.getAddress() " /.../Storage/base.db 373 | 374 | Для получения доступа к файлам каталога, используются методы 'getChild' и 375 | 'getChildren'. 376 | 377 | Пример: 378 | > 379 | echo s:f.getChild('file.txt').getName() " file.txt 380 | echo s:f.getChildren()[0] " file.txt 381 | 382 | Метод 'isExists' позволяет не только определить, существует ли данный 383 | компонент (файл или каталог) в файловой системе, но и узнать о существовании 384 | компонентов в данном каталоге: 385 | > 386 | let s:dir = s:File.relative('myDir') 387 | if s:dir.isExists() 388 | echo (s:dir.isExists('myFile.txt')? 'myFile существует' : 'myFile существует' 389 | endif 390 | 391 | Если компонент не существует, его можно создать с помощью методов 'createFile' 392 | и 'createDir' для файла и каталога соответственно. Удалить файл можно с 393 | помощью метода 'deleteFile', а каталог с помощью метода 'deleteDir'. 394 | 395 | Пример: 396 | > 397 | let s:newFile = s:File.relative('newFile.txt') 398 | call s:newFile.createFile() 399 | 400 | Для чтения из файла используется метод 'read', который возвращает массив 401 | строк, содержащихся в файле. Метод 'write' добавляет новую строку в файл, а 402 | метод 'rewrite' перезаписывает файл массивом строк. 403 | 404 | Пример: 405 | > 406 | call s:f.write('New string') " Добавляем строку в конец файла 407 | echo s:f.read() " Читаем содержимое файла 408 | 409 | 2.7. EventHandle *vim_lib-base-eventhandle* 410 | ---------------- 411 | 412 | Примесь EventHandle добавляет классу механизм обработки событий. События 413 | генерируются программно и ведут за собой запуск массива обработчиков этих 414 | событий. 415 | 416 | Для добавления примеси классу достаточно использовать метод 'mix': 417 | > 418 | let s:EventHandle = vim_lib#base#EventHandle# 419 | let s:MyClass = s:Object.expand() 420 | let s:MyClass.mix(s:EventHandle) 421 | 422 | Для регистрации обработчика события используется метод 'listen', который 423 | принимает имя обрабатываемого события и имя метода объекта, который будет 424 | вызван при наступлении этого события: 425 | > 426 | let s:obj = s:MyClass.new() 427 | function! s:obj.sayHello(event) 428 | echo 'Hello world' 429 | endfunction 430 | call s:obj.listen('myEvent', 'sayHello') 431 | 432 | В качестве обработчиков событий можно так же использовать глобальные функции: 433 | > 434 | function! g:MyFun(event) 435 | echo 'Hello world' 436 | endfunction 437 | call s:obj.listen('myEvent', function('MyFun')) 438 | 439 | Метод 'listen' позволяет регистрировать несколько обработчиков одного события. 440 | Вызваны они будут в порядке их регистрации: 441 | > 442 | call s:obj.listen('myEvent', 'sayHello') 443 | call s:obj.listen('myEvent', 'sayBye') 444 | 445 | Важно помнить, что метод-, функция-обработчик должна принимать минимум один 446 | параметр - имя события - который передается ей при вызове. 447 | 448 | Для генерации события используется метод 'fire', который принимает имя 449 | генерируемого события и исполняет все его обработчики: 450 | > 451 | call s:obj.fire('myEvent') 452 | 453 | Для удаления обработчиков используется метод 'ignore': 454 | > 455 | call s:obj.ignore('myEvent', 'sayHello') " Отмена обработчика sayHello 456 | call s:obj.ignore('myEvent', function('SayHello')) " Отмена функции 457 | call s:obj.ignore('myEvent') " Отмена всех обработчиков события myEvent 458 | 459 | ================================================================================ 460 | 3. Пакет sys *vim_lib-sys* 461 | 462 | Пакет sys включает классы, представляющие различные компоненты редактора (за 463 | исключением визуальных), а также расширения. 464 | 465 | 3.1. Buffer *vim_lib-sys-buffer* 466 | ----------- 467 | 468 | Класс Buffer представляет новые или открытые буферы редактора. Класс управляет 469 | как опциями и привязками горячих клавиш в буфере, так и открытием новых окон 470 | для их отображения. 471 | 472 | Для управления существующим буфером достаточно создать объект данного класса, 473 | передав в конструктор номер целевого буфера: 474 | > 475 | let s:Buffer = vim_lib#sys#Buffer# 476 | let s:buf = s:Buffer.new(bufnr('%')) " Текущий буфер 477 | 478 | Безвозвратно удалить буфер можно с помощью метода 'delete'. 479 | 480 | Так как буфер может быть выгружен из редактора, с помощью данного класса его 481 | можно вновь загрузить в активное окно с помощью метода 'active'. Данный метод 482 | очень важен, так как не только активирует вызываемый буфер, но и создает все 483 | привязки горячих клавиш и локальные опции, определенные методами 'option' и 484 | 'listen' для данного буфера. 485 | 486 | Пример: 487 | > 488 | call s:buf.gactive('t') " Открыть буфер в новом, горизонтальном окне 489 | let s:newBuf = s:Buffer.new() " Новый, пустой буфер 490 | call s:newBuf.active() " Открыть новый буфер в текущем окне 491 | 492 | Если необходимо загрузить буфер в новое окно, используются аналогичные методы 493 | 'gactive' (окно с горизонтальным разделением) и 'vactive' (окно с вертикальным 494 | разделением), которые, как и метод 'active', выполняют все привязки и задают 495 | опции буфера. Данные методы принимают один обязательный аргумент, определяющий 496 | позицию нового окна. Для 'vactive' это: 'l' - окно слева от текущего, 'r' - 497 | окно справа от текущего; а для 'gactive' это: 't' - окно над текущим, 'b' - 498 | окно под текущим. Окно так же может быть расположено абсолютно (выше/ниже или 499 | левее/правее всех окон), для этого используются соответствующие заглавные 500 | буквы (T/B или L/R). 501 | 502 | С помощью методов 'gactive' и 'vactive' так же можно указать размеры нового 503 | окна: 504 | > 505 | call s:buf.vactive('r', 20) " Ширина в 20 символов 506 | call s:buf.vactive('r', '20%') " Ширина в 20% от текущего окна 507 | 508 | В том случае, когда необходимо перерисовать некоторый буфер с сохранением 509 | позиции курсора ввода, используется метод 'redraw'. Он вызывает метод 510 | 'active', что повторно применяет все привязки и опции буфера. 511 | 512 | Для определения локальных опций буфера, как уже было сказано ранее, 513 | используется метод 'option'. Он запоминает все установленные значения опций и 514 | применяет их к буферу при каждой его активации. 515 | 516 | Пример: 517 | > 518 | call s:buf.option('filetype', 'cpp') " Определение типа файла 519 | call s:buf.option('fileencoding', 'utf-8') " Определение кодировки файла 520 | call s:buf.active() " Применение опций 521 | 522 | Как и опции, можно задать привязки горячих клавиш для буфера с помощью метода 523 | 'map'. Этот метод запоминает все привязки и применяет их к буферу при 524 | каждой его активации. По наступлению события привязки (нажата целевая 525 | клавиша), будет вызван определенный для метод-слушатель объекта буфера: 526 | > 527 | function! s:buf.sayHello(...) 528 | echo 'Hello world' 529 | endfunction 530 | call s:buf.map('n', 'q', 'sayHello') " При нажатии q будет вызван 531 | " метод sayHello объекта 532 | 533 | Метод-слушатель вызывается от имени исходного буфера, что позволяет 534 | использовать переменную 'self'. Так же разрешено определить несколько 535 | слушателей одного и того же события: 536 | > 537 | call s:buf.map('n', 'q', 'sayHello') " При нажатии q будет вызван 538 | call s:buf.map('n', 'q', 'sayBye') " сначала метод sayHello 539 | " а затем sayBye 540 | 541 | Для отмены привязок используется метод 'ignoreMap'. 542 | 543 | С помощью данного класса так же можно задавать обработчики событий редактора 544 | (autocmd). Делается это с помощью метода 'au', который в качестве первого 545 | аргумента принимает имя события (одно из возможных событий редактора 546 | |autocommand-events|), а в качестве второго имя метода-обработчика или ссылку 547 | на глобальную функцию: 548 | > 549 | function! SayHello(...) 550 | echo 'Hello world' 551 | endfunction 552 | call s:buf.au('VimEnter', function('SayHello')) " Событие VimEnter вызовет 553 | " глобальную функцию sayHello 554 | 555 | Метод может применяться для установки нескольких обработчиков одного события: 556 | > 557 | call s:buf.au('VimEnter', function('SayHello')) 558 | call s:buf.au('VimEnter', function('SayHello')) 559 | 560 | Для отмены привязок используется метод 'ignoreAu'. 561 | 562 | Объекты данного класса являются постоянными. Это хорошо видно на следующем 563 | примере: 564 | > 565 | let s:bufA = s:Buffer.current() " Текущий буфер 566 | let s:bufA.test = 1 567 | let s:bufB = s:Buffer.current() " Текущий буфер 568 | echo s:bufB.test " 1 569 | 570 | Важной функцией для работы с новыми, пустыми буферами является механизм 571 | рендеринга содержимого. Если создается новый буфер, с помощью класса Buffer 572 | его можно заполнить при активации некоторой строкой, либо использовать команду 573 | редактора или заготовленный метод объекта буфера. Делается это с помощью 574 | свойства/метода 'render'. Это свойство может принимать следующие значения: 575 | 576 | - Команда редактора - в этом случае заданная команда будет выполнена, а ее 577 | результат попадет в тело буфера 578 | - Строка - в этом случае строка будет помещена в тело буфера 579 | - Функция - в этом случае возвращаемая функцией строка будет помещена в тело 580 | буфера. Так как функция представляет метод объекта буфера, она вызывается 581 | от его имени, что позволяет использовать в ней переменную 'self' 582 | 583 | Пример: 584 | > 585 | let s:buf = s:Buffer.new() 586 | let s:buf.render = "'Hello world'" " Строка в качестве значения 587 | " или 588 | let s:buf.render = "system('date')" " Команда редактора 589 | " или 590 | function! s:buf.render() " Метод буфера 591 | return self.getNum() 592 | endfunction 593 | call s:buf.gactive('t') " Использование render для заполнения буфера 594 | 595 | С помощью класса Buffer так же осуществляется управления окнами редактора. 596 | Так, метод 'select' позволяет сделать текущим то окно, в котором отображается 597 | вызываемый буфер: 598 | > 599 | let s:bufCur = s:Buffer.current() " Текущий буфер 600 | let s:buf = s:Buffer.new() 601 | call s:buf.gactive('t') " Активным окном является окно буфера s:buf 602 | call s:bufCur.select() " Активным окном сделано прежнее окно 603 | 604 | Метод 'unload' позволяет закрыть все окна, в которых активен буфер. 605 | 606 | Ниже приведен пример создания нового буфера с указанием опций и привязок: 607 | > 608 | let s:buf = s:Buffer.new() 609 | call s:buf.option('filetype', 'myFile') 610 | call s:buf.listen('n', 'q', 'delete') " Закрытие буфера по клавише q 611 | let s:buf.render = "system('date')" " Буфер содержит текущую дату 612 | call s:buf.gactive('t') 613 | 614 | 3.2. Content *vim_lib-sys-content* 615 | ------------ 616 | 617 | Класс Content служит для управления содержимым текущего буфера. С помощью 618 | данного класса возможно как получать, так и изменять содержимое буфера. 619 | Важной особенностью данного класса является то, что его экземпляр работает 620 | только с текущим буфером, потому нет необходимости в более чем одном 621 | экземпляре данного класса. Именно поэтому он является Singleton. 622 | 623 | Так как Content, как и большинство классов библиотеки, является подклассом 624 | класса Object, для получения его экземпляра используется конструктор 'new'. 625 | 626 | Практически все методы данного класса могут принимать дополнительный параметр, 627 | так и вызываться без него. В первом случае метод добавляет или заменяет данные 628 | в буфере, во втором возвращает данные. Так, метод 'pos' без параметров 629 | возвращает информацию о текущем положении курсора в виде словаря со следующей 630 | структурой: {'l': номерСтроки, 'c': номерСтолбца}, но в этот метод так же 631 | можно передать словарь аналогичной структуры (в таком случае оба элемента 632 | являются необязательными) и метод установит курсор в заданное положение: 633 | > 634 | let s:Content = vim_lib#sys#Content# 635 | let s:c = s:Content.new() 636 | echo s:c.pos() " Получить информацию о положении курсора 637 | echo s:c.pos({'l': 5}) " Установить курсор на пятую строку 638 | 639 | Для изменения или получения содержимого буфера используются методы 'rewrite', 640 | 'add', 'line', 'word' и 'WORD'. 641 | 642 | Пример: 643 | > 644 | call s:c.rewrite('Test') " Установить содержимое 645 | call s:c.add(1, 'Hello world') " Вставить строку Hello world в начало 646 | echo s:c.line(1) " Hello world 647 | echo s:c.line(1, 'Rewrite line') " Заменить первую строку на данную 648 | echo s:c.word() " Получить слово под курсором 649 | echo s:c.word('Test') " Заменить слово под курсором на данное 650 | 651 | Метод 'WORD' отличается тем, что он взаимодействует со словами, ограничителями 652 | которых являются пробелы, но не знаки препинания, как в случае с методом 'word'. 653 | 654 | Метод 'select' позволяет получить выделенный пользователем текст в виде строки. 655 | 656 | Метод 'isEmpty' позволяет определить, является ли текущий буфер пустым. 657 | 658 | 3.3. Plugin *vim_lib-sys-plugin* 659 | ----------- 660 | 661 | Класс Plugin служит для реализации основной логики при инициализации сторонних 662 | плагинов редактора. Основной целью данного плагина является унификация 663 | механизмов инициализации, использования и управления плагинами. 664 | 665 | Плагин, реализованный с использованием данного класса имеет следующую структуру: 666 | 667 | - Логика инициализации плагина выносится в файл с именем 668 | 'plugin/имяПлагина.vim'. Этот скрипт включает объявление глобальной 669 | переменной, хранящей объект плагина, инициализацию плагина с помощью опций, 670 | добавление команд и привязок плагина 671 | - Интерфейс плагина, то есть все методы, с помощью которых выполняется 672 | управление плагином и редактором с его помощью, выносится в файл с именем 673 | 'autoload/имяПлагина.vim'. Данный скрипт содержит загружаемые 674 | автоматически функции, являющиеся основными управляющими компонентами 675 | плагина. 676 | 677 | Инициализация плагина с помощью данного класса выполняется в несколько этапов: 678 | 679 | 1. Создание объекта плагина 680 | 2. Выполнение инициализации плагина 681 | 3. Регистрация плагина 682 | 683 | Пример: 684 | > 685 | let s:Plugin = vim_lib#sys#Plugin# 686 | let s:p = s:Plugin.new('myPlugin', '1') " 1 этап 687 | let s:p.optionA = 0 " 2 этап 688 | function! s:p.run() " 2 этап 689 | ... 690 | function 691 | call s:p.comm('MyCommand', 'method()') " 2 этап 692 | call s:p.map('r', 'method') " 2 этап 693 | call s:p.reg() " 3 этап 694 | 695 | Первый этап позволяет определить имя плагина (первый параметр метода 'new'), 696 | его текущую версию (второй параметр) и, возможно, зависимости плагина 697 | (необязательный третий параметр), которые представляются с помощью словаря со 698 | следующей структурой: 699 | 700 | - version - минимальная, требуемая версия редактора vim 701 | - has - массив модулей окружения, необходимых для работы модуля 702 | - plugins - массив имен плагинов редактора, необходимых для работы данного 703 | плагина. Данная зависимость так же определяет порядок загрузки плагина. 704 | Загрузка текущего плагина должна производится после загрузки всех 705 | используемых плагинов. 706 | 707 | Пример: 708 | > 709 | let s:p = s:Plugin.new('myPlugin', '1', 710 | \ {'version': 740, 'has': ['pithon'], 'plugins': ['vim_prj']}) 711 | 712 | Второй этап использует следующие методы для инициализации плагина: 713 | 714 | - объектПлагина.свойство - инициализация опций плагина значениями по умолчанию 715 | - comm - инициализация команд плагина 716 | - map - инициализация привязок плагина 717 | - au - инициализация обработчиков событий редактора плагином 718 | - menu - инициализация пунктов меню плагина 719 | - run - логика инициализации плагина в свободной форме 720 | 721 | Важно использовать именно эти команды для инициализации плагина. Благодаря им 722 | становится проще отключить плагин в будущем (см. |NullPlugin|). 723 | 724 | Третий этап включает обязательный вызов метода 'reg', который создает 725 | глобальную переменную плагина 'имяПлагина#' и присваивает ей созданный на 726 | предыдущих этапах объект плагина. В будущем доступ к плагину осуществляется 727 | через эту переменную. Данный метод так же выполняет некоторые системные 728 | конфигурации для регистрации плагина. 729 | 730 | После инсталляции плагина в редактор он может быть инициализирован тремя 731 | различными способами: 732 | 733 | 1. Пользовательская инициализация - для инициализации опций плагина, 734 | пользователю достаточно добавить в скрипты, выполняющиеся до загрузки 735 | плагина (такие как .vimrc), код инициализации. Этот код представляет 736 | объявление следующих глобальных переменных: 737 | 738 | - 'имяПлагина#options' - словарь опций плагина. На пример так: 739 | myPlugin#options = {"optionA": 0} 740 | - 'имяПлагина#map' - словарь привязок плагина. На пример так: 741 | myPlugin#map = {'methodB': 'r'}. Пустая строка в качестве значения 742 | элемента отменяет привязку. 743 | 744 | 2. Дефолтная инициализация - данная инициализация выполняется внутри плагина 745 | с помощью методов 'comm', 'map', 'au', 'menu' и 'run' 746 | 3. Переинициализация - для переопределения опций плагина после его загрузки 747 | пользователю достаточно добавить в скрипты, выполняющиеся после загрузки 748 | плагина (такие как ftplugin) код инициализации свойств глобального объекта. 749 | На пример так: myPlugin#.optionA = 2 750 | 751 | Интерфейс плагина, использующего данный класс, выносится в файл 752 | autoload/имяПлагина.vim. 753 | 754 | Пример: 755 | > 756 | function! myPlugin#method() 757 | echo 'Hello world' 758 | endfunction 759 | 760 | 3.4. NullPlugin *vim_lib-sys-nullplugin* 761 | --------------- 762 | 763 | Класс Plugin позволяет легко отключить любой плагин без необходимости 764 | добавления логики в файл инициализации плагина. Для отключения плагина 765 | достаточно добавить в любой файл, выполняемый до загрузки плагина (на пример 766 | .vimrc) объявление переменной 'имяПлагина#' инициализируя ее значением ноль. 767 | 768 | Пример: 769 | > 770 | let myPlugin# = 0 771 | 772 | Если конструктор класса Plugin обнаружит наличие такой переменной со значением 773 | ноль, он вернет объект класса NullPlugin, который включает те же методы, что и 774 | класс Plugin, но не выполняет никаких действий. Такой подход позволяет 775 | отменить инициализацию плагина без необходимости добавления в файл 776 | инициализации конструкций вида: 777 | > 778 | if exists('myPlugin') 779 | finish 780 | endif 781 | 782 | 3.5. Publisher *vim_lib-sys-publisher* 783 | -------------- 784 | 785 | Класс Publisher позволяет установленным плагинам использовать событийную модель. 786 | Любой плагин может использовать данный класс как для обработки событий других 787 | плагинов, так и для информирования их о собственных событиях. 788 | 789 | Для добавления обработчика события необходимо определить глобальную функцию, 790 | которая и будет являться обработчиком события, а затем зарегистрировать ее в 791 | качестве обработчика с помощью метода 'listen': 792 | > 793 | let s:Publisher = vim_lib#sys#Publisher# 794 | let s:p = s:Publisher.new() 795 | function! MyListener(event) 796 | ... 797 | endfunction 798 | call s:p.listen('MyEvent', function('MyListener')) 799 | 800 | Обратите внимание на то, что функция обработчик имеет единственный аргумент 801 | 'event'. Этим параметром в функцию будет передан объект, представляющий 802 | событие. Данный объект используется для получения информации о событии. 803 | 804 | Если необходимо прекратить обработку некоторого события, используется метод 805 | 'ignore': 806 | > 807 | call s:p.ignore('MyEvent', function('MyListener') " Отменить обработчик 808 | call s:p.ignore('MyEvent') " Отменить все обработчики события 809 | 810 | Если ваш скрипт создает события, то с помощью класса Publisher о них можно 811 | оповестить всех подписчиков. Для этого используется метод 'fire': 812 | > 813 | call s:p.fire('MyEvent') " Выброс события 814 | call s:p.fire('MyEvent', {'plugin': 'myPlugin'}) " Выброс события с объектом 815 | 816 | В таком случае будут вызваны все обработчики указанного события. Если передать 817 | в качестве второго параметра объект, он будет передан в качестве единственного 818 | параметра обработчику. 819 | 820 | Обработчики вызываются в порядке их добавления методом 'listen'. Так же 821 | возможно добавить один и тот же обработчик одного и того же события более 822 | одного раза: 823 | > 824 | call s:p.listen('MyEvent', function('MyListener')) 825 | call s:p.listen('MyEvent', function('MyListener')) 826 | 827 | Для разделения событий плагинов, рекомендуется использовать префикс с именем 828 | плагина: 829 | > 830 | call s:p.listen('myPlugin#MyEvent', function('MyListener')) " Обработчик 831 | call s:p.fire('myPlugin#MyEvent') " Выброс события плагином 832 | 833 | 3.6. System *vim_lib-sys-system* 834 | ----------- 835 | 836 | Класс System предоставляет интерфейс для доступа к командам операционной 837 | системы (ОС) и редактору Vim. Класс позволяет выполнять команды в командной 838 | оболочке ОС, а также печатать сообщения в редакторе. 839 | 840 | Для выполнения команд в командной оболочке ОС используется два метода: 841 | 842 | - 'run' - метод выполняет команду в фоновом режиме и возвращает результат 843 | ее работы 844 | - 'exe' - метод выполняет команду с переходом в командную оболочку 845 | 846 | Пример: 847 | > 848 | let s:Sys = vim_lib#sys#System#.new() 849 | let s:ls = s:Sys.run('ls') 850 | 851 | Для печати сообщений в редакторе Vim используются методы 'echo', 'echom' и 852 | 'print'. Они позволяют определить цвет сообщения с помощью второго параметра: 853 | > 854 | call s:Sys.echo('Hello world', 'MoreMsg') 855 | 856 | Метод 'print' отличается от двух других тем, что после печати сообщения, 857 | редактор ожидает реакции пользователя (нажатие клавиши Enter). 858 | 859 | Метод 'read' позволяет получить от пользователя строку, которую он введет в 860 | командном режиме: 861 | > 862 | let s:result = s:Sys.read('Press name: ') 863 | 864 | Для определения обработчиков горячих клавиш редактора, автокоманд и пунктов 865 | меню используются методы 'map', 'au' и 'menu'. Они аналогичны одноименным 866 | методам класса 'Buffer' c той лишь разницей, что выполняют привязку для всего 867 | редактора, а не только для текущего буфера. 868 | 869 | 3.7. Autoload *vim_lib-sys-autoload* 870 | ------------- 871 | 872 | Компонент Autoload отличается от других компонентов библиотеки тем, что он не 873 | является классом. Данный компонент подключается в файле инициализации 874 | редактора, таком как .vimrc, и определяет порядок загрузки скриптов 875 | инициализации и плагинов (runtimepath). 876 | 877 | Компонент определяет следующий порядок загрузки редактора: 878 | 879 | 1. $VIMRUNTIME/ - базовые скрипты инициализации редактора 880 | 2. $VIM/vimrc - глобальный скрипт инициализации 881 | 3. $HOME/.vimrc или $HOME/_vimrc - пользовательский скрипт инициализации 882 | 4. .vimrc - локальный скрипт инициализации 883 | 5. $VIM/vimfiles/ - глобальные скрипты инициализации, применяемые для всех 884 | пользователей 885 | 6. $HOME/.vim/ или $HOME/vimfiles/ - пользовательские скрипты инициализации 886 | 7. .vim/ - скрипты инициализации локального проекта 887 | 8. /// - плагины заданного уровня инициализации 888 | 889 | Для подключения компонента необходимо добавить следующую запись в целевой файл 890 | инициализации .vimrc: 891 | > 892 | filetype off 893 | set rtp=~/.vim/bundle/vim_lib " Путь до библиотеки vim_lib 894 | call vim_lib#sys#Autoload#init('~/.vim', 'bundle') " Каталог со скриптами 895 | " инициализации и 896 | " каталог плагинов 897 | ... 898 | filetype indent plugin on 899 | 900 | Целевым файлом инициализации может являться любой vimrc файл, загружаемый на 901 | любом этапе инициализации ($VIM/vimfiles/.vimrc, $HOME/.vimrc, .vimrc). 902 | 903 | Важно помнить, что запись: 904 | > 905 | set rtp=~/.vim/bundle/vim_lib 906 | 907 | последующих подключениях эта запись должна быть опущена. 908 | 909 | Пример: 910 | > 911 | $VIM/.vimrc 912 | filetype off 913 | set rtp=~/usr/share/vim/vimfiles/bundle/vim_lib 914 | call vim_lib#sys#Autoload#init('/usr/share/vim/vimfiles', 'bundle') 915 | ... 916 | filetype indent plugin on 917 | 918 | $HOME/.vimrc 919 | filetype off 920 | call vim_lib#sys#Autoload#init('~/.vim', 'bundle') 921 | ... 922 | filetype indent plugin on 923 | 924 | Метод 'vim_lib#sys#Autoload#init' определяет текущий уровень инициализации. 925 | Делается это благодаря первому аргументу, который определяет адрес каталога 926 | скриптов инициализации для текущего уровня. На пример, для уровня пользователя 927 | может использоваться следующая команда: 928 | > 929 | call vim_lib#sys#Autoload#init('~/.vim', 'bundle') 930 | 931 | Она задает адрес каталога инициализации для текущего пользователя. 932 | 933 | Второй аргумент определяет имя каталога, в котором будут хранится плагины 934 | редактора. В примерах выше это каталог bundle. Данный каталог должен 935 | располагаться непосредственно в каталоге, определенном первым аргументом метода. 936 | 937 | Компонент Autoload определяет команду 'Plugin', которая позволяет определить 938 | подключенные на данном уровне плагины. Делается это следующим образом: 939 | > 940 | filetype off 941 | call vim_lib#sys#Autoload#init('~/.vim', 'bundle') 942 | Plugin 'vim_prj' " Подключение плагина vim_prj на пользовательском уровне 943 | filetype indent plugin on 944 | 945 | Плагин может быть подключен только в том случае, если он установлен в каталоге 946 | плагинов данного уровня инициализации. 947 | 948 | Плагин подключается для текущего уровня инициализации. Это означает, что 949 | возможно подключить некоторый плагин только для данного пользователя или 950 | некоторого проекта. 951 | 952 | Любой плагин можно отключить просто удалив (или закомментировав) команду 953 | 'Plugin', подключающую его. 954 | 955 | Команда 'Plugin' принимает второй необязательный аргумент, который определяет 956 | словарь свойств, создаваемых для подключаемого плагина. 957 | 958 | Пример: 959 | > 960 | Plugin 'vim_prj' { 961 | \ 'options': {'a': 1}, 962 | \ 'map': {'method': 'q'} 963 | \} 964 | 965 | Из примера видно, что подключение плагина 'vim_prj' сопровождается установкой 966 | его свойства 'a' и привязки клавиши 'q'. 967 | 968 | Сторонние плагины могут использовать метод 'isPlug' данного компонента для 969 | определения, подключен ли некоторый необязательный плагин. Такие плагины не 970 | зависят от целевого плагина, а лишь используют его возможности по мере 971 | возможности. 972 | 973 | ================================================================================ 974 | 4. Пакет view *vim_lib-view* 975 | 976 | Пакет view включает классы, представляющие визуальные компоненты редактора и 977 | виджеты. 978 | 979 | 4.1. BufferStack *vim_lib-view-bufferstack* 980 | ---------------- 981 | 982 | Класс BufferStack представляет виджет, позволяющий загружать в одно окно 983 | редактора несколько буферов. Закрытие (удаление) текущего буфера окна 984 | автоматически активирует предыдущий буфер. Это удобно при реализации 985 | последовательного перехода между связанными буферами. На пример, этот виджет 986 | используется в плагине https://github.com/Bashka/vim_git для последовательного 987 | перехода от экрана с историей коммитов, к просмотру изменений конкретного 988 | коммита и т.д. 989 | 990 | Для создания виджета необходимо инстанциировать класс, определить для него 991 | начальный стек буферов, после чего активировать текущий буфер. 992 | 993 | Пример: 994 | > 995 | let s:BufferStack = vim_lib#view#BufferStack# 996 | let s:Buffer = vim_lib#sys#Buffer# 997 | let s:b = s:Buffer.new() 998 | let s:b.render = "'First buffer'" 999 | let s:bs = s:BufferStack.new() 1000 | call s:bs.push(s:b) " Добавление буфера в стек 1001 | call s:bs.gactive('t') " Активация буфера в новом окне 1002 | 1003 | После активации буфера возможно добавление новых буферов в стек. Это позволит 1004 | наращивать стек последовательно, сохраняя старые буферы в нем для возможности 1005 | последующего возврата к ним: 1006 | > 1007 | let s:b = s:Buffer.new() 1008 | let s:b.render = "'Second buffer'" 1009 | call s:bs.push(s:b) " Добавление буфера в стек 1010 | call s:bs.active() " Активация нового буфера поверх старого 1011 | 1012 | Метод 'push' класс автоматически добавляет привязку к клавише 'q', нажатие на 1013 | которой в стеке закроет его и активирует предыдущий. Аналогичное действие 1014 | выполняет метод 'delete' объекта: 1015 | > 1016 | call s:bs.delete() " Активным будет буфер First buffer 1017 | 1018 | ================================================================================ 1019 | vim:tw=78:ts=8:ft=help:norl:colorcolumn=+3: 1020 | --------------------------------------------------------------------------------