" ============================================================================= " Filename: autoload/calendar/constructor/view_textbox.vim " Author: itchyny " License: MIT License " Last Change: 2021/09/18 13:37:00. " ============================================================================= let s:save_cpo = &cpo set cpo&vim function! calendar#constructor#view_textbox#new(instance) abort return extend({ 'instance': a:instance }, s:constructor) endfunction let s:constructor = {} function! s:constructor.new(source) dict abort return extend(extend(s:super_constructor.new(a:source), s:instance), self.instance) endfunction let s:instance = {} let s:instance._key = [] let s:instance.__key = [] let s:instance._texts = [] let s:instance.select = 0 let s:instance.noindex = [] let s:instance._select_title = 0 let s:instance.syntax = [] let s:instance.length = 0 let s:instance._current_contents = {} let s:instance._prev_contents = {} let s:instance._prevprev_contents = {} let s:instance._next_contents = {} let s:instance._current_group_id = '' let s:instance._nocontents = 1 function! s:instance.width() dict abort let frame = calendar#setting#frame() let width = calendar#string#strdisplaywidth(frame.vertical) return self.maxwidth() / width * width + 2 endfunction function! s:instance.contents() dict abort if self._key == [self.is_selected(), self.select, self.sizex(), self.sizey(), get(self, 'min_index'), get(self, 'max_index')] + self.get_key() return deepcopy(self._texts) endif let s = [] let frame = calendar#setting#frame() let width = calendar#string#strdisplaywidth(frame.vertical) let flen = len(frame.vertical) let top = frame.topleft . repeat(frame.horizontal, (self.sizex() - 2) / width - 2) . frame.topright let bottom = frame.bottomleft . repeat(frame.horizontal, (self.sizex() - 2) / width - 2) . frame.bottomright let w = self.sizex() - 4 - width * 2 let sizey = self.sizey() - 1 let self.cnt = self.get_contents() let self.length = len(self.cnt) if len(self.cnt) for i in range(min([len(self.cnt) - self.min_index, sizey])) call add(s, self.cnt[self.min_index + i]) endfor endif if len(s) < sizey for i in range(sizey - len(s)) call add(s, '') endfor else let s = s[:sizey] endif call map(s, 'calendar#string#truncate(v:val, w)') let texts = map(range(len(s)), 'calendar#text#new(" " . frame.vertical . " " . s[v:val] . " " . frame.vertical . " ", 0, v:val + 1, "")') call insert(texts, calendar#text#new(' ' . top . ' ', 0, 0, ''), 0) call add(texts, calendar#text#new(' ' . bottom . ' ', 0, len(s), '')) let selsyn = '' for [i, syn] in self.syntax if self.min_index <= i && i + 1 < self.min_index + sizey let len = len(s[i - self.min_index]) + 2 call add(texts, calendar#text#new(len, 1 + flen, 1 + i - self.min_index, syn)) if i == self.select let selsyn = syn endif endif endfor if self.is_selected() if self.min_index <= self.select && self.select < self.min_index + sizey if self._select_line let len = len(s[self.select - self.min_index]) + 2 call add(texts, calendar#text#new(len, 1 + flen, 1 + self.select - self.min_index, selsyn . 'Select')) endif endif call add(texts, calendar#text#new(0, 1 + flen, 1 + self.select - self.min_index, 'Cursor')) endif let self._texts = deepcopy(texts) let self._key = [self.is_selected(), self.select, self.sizex(), self.sizey(), get(self, 'min_index'), get(self, 'max_index')] + self.get_key() return texts endfunction function! s:instance.get_key() dict abort return [] endfunction function! s:instance.get_contents() dict abort if self.__key == [self.select, self.sizex(), self.sizey(), get(self, 'min_index'), get(self, 'max_index')] + self.get_key() return self.cnt endif let self.noindex = [] let self.syntax = [] let cnt = [] let frame = calendar#setting#frame() let width = calendar#string#strdisplaywidth(frame.vertical) let cnts = self.get_raw_contents() let w = self.sizex() - width * 2 if len(cnts) let self._current_contents = {} let self._prev_contents = {} let self._prevprev_contents = {} let self._next_contents = {} for j in range(len(cnts)) let t = cnts[j] if len(cnt) if !self._select_title call add(self.noindex, len(cnt)) endif call add(cnt, '') endif if !self._select_title call add(self.noindex, len(cnt)) endif if len(cnt) == self.select let self._current_contents = { 'title': t.title } endif call add(cnt, repeat(' ', max([(self.sizex() - 4 - width * 2 - calendar#string#strdisplaywidth(t.title)) / 2, 0])) . t.title) if !self._select_title call add(self.noindex, len(cnt)) endif call add(cnt, '') while index(self.noindex, self.select) >= 0 let self.select += 1 endwhile if j == len(cnts) - 1 let self.select = min([self.select, len(cnt) + len(t.items) - 1]) endif for tt in t.items let lencnt = len(cnt) if lencnt == self.select - 1 let self._prev_contents = deepcopy(tt) elseif lencnt == self.select - 2 let self._prevprev_contents = deepcopy(tt) elseif lencnt == self.select + 1 let self._next_contents = deepcopy(tt) elseif lencnt == self.select let self._current_contents = deepcopy(tt) let self._current_group_id = get(t, 'id', '') endif if get(tt, 'status', '') ==# 'completed' call add(self.syntax, [len(cnt), 'Comment']) elseif has_key(tt, 'syntax') call add(self.syntax, [len(cnt), tt.syntax]) endif call add(cnt, get(tt, 'prefix', '') . get(tt, 'title', get(tt, 'summary', ''))) endfor endfor if self._nocontents && has_key(self, 'min_index') && has_key(self, 'max_index') unlet! self.min_index self.max_index endif if self._current_group_id == '' let self._current_group_id = get(cnts[0], 'id', '') endif let [self.min_index, self.max_index] = self.min_max_index(len(cnt)) let self._nocontents = 0 else let [self.min_index, self.max_index] = [0, 0] let self._nocontents = 1 let self.select = 0 let self._prev_contents = {} let self._prevprev_contents = {} let self._next_contents = {} let self._current_contents = {} let self._current_group_id = '' endif let self.__key = [self.select, self.sizex(), self.sizey(), get(self, 'min_index'), get(self, 'max_index')] + self.get_key() return cnt endfunction function! s:instance.min_max_index(length) dict abort let height = self.sizey() - 2 let length = max([a:length, height]) if has_key(self, 'min_index') if self.select < self.min_index let min = self.select elseif self.select >= self.max_index let min = self.min_index + self.select - self.max_index else let min = self.min_index endif else let min = self.select - height / 2 + 2 endif let min = max([min, 0]) let max = min([min + height - 1, length - 1]) let min = max([max - height + 1, 0]) let max = min([min + height - 1, length - 1]) return [min, max] endfunction function! s:instance.move_select(diff) dict abort let self.select = max([min([self.select + a:diff, self.length - 1]), 0]) let diff = a:diff > 0 ? 1 : -1 while index(self.noindex, self.select) >= 0 if self.select < 3 let self.min_index = - self.length let diff = 1 endif let self.select += diff endwhile let self.select = max([min([self.select, self.length - 1]), 0]) let self.__updated = 1 let [self.min_index, self.max_index] = self.min_max_index(self.length) endfunction function! s:instance.current_contents() dict abort return self._current_contents endfunction function! s:instance.prev_contents() dict abort return self._prev_contents endfunction function! s:instance.prevprev_contents() dict abort return self._prevprev_contents endfunction function! s:instance.next_contents() dict abort return self._next_contents endfunction function! s:instance.current_group_id() dict abort return self._current_group_id endfunction function! s:instance._action(action) dict abort let hour = self.select let self.__updated = 0 let [select, min_index, max_index] = [self.select, self.min_index, self.max_index] if index(['down', 'up'], a:action) >= 0 call self.move_select(v:count1 * (a:action ==# 'down' ? 1 : -1)) elseif index(['down_big', 'up_big'], a:action) >= 0 let diff = self.max_index - self.min_index let dir = a:action ==# 'down_big' ? 1 : -1 let di = max([min([v:count1 * dir * diff * 2 / 3, self.length - 1 - hour]), - hour]) if dir > 0 let self.min_index = self.min_index + v:count1 * dir * diff let self.max_index = self.min_index + diff else let self.max_index = self.max_index + v:count1 * dir * diff let self.min_index = self.max_index - diff endif call self.move_select(di) elseif index(['down_large', 'up_large'], a:action) >= 0 let diff = self.max_index - self.min_index let dir = a:action ==# 'down_large' ? 1 : -1 let di = max([min([(dir > 0 ? self.max_index : self.min_index) - hour + (v:count1 - 1) * dir * diff, self.length - 1 - hour]), - hour]) if dir > 0 let self.min_index = self.min_index + v:count1 * dir * diff let self.max_index = self.min_index + diff else let self.max_index = self.max_index + v:count1 * dir * diff let self.min_index = self.max_index - diff endif call self.move_select(di) elseif a:action ==# 'first_line' || a:action ==# 'first_line_head' || (a:action ==# 'last_line' && v:count) call self.move_select((v:count ? min([v:count1, self.length - 1]) : 0) - hour) elseif a:action ==# 'last_line' call self.move_select((v:count ? max([v:count1, self.length - 1]) : self.length - 1) - hour) elseif a:action ==# 'last_line_last' call self.move_select((v:count ? max([v:count1, self.length - 1]) : self.length - 1) - hour) elseif index(['scroll_down', 'scroll_up'], a:action) >= 0 let diff = v:count1 * (a:action =~# 'down' ? 1 : -1) let old_indeces = [self.min_index, self.max_index] let self.min_index += diff let self.max_index += diff let new_indeces = self.min_max_index(self.length) if old_indeces == new_indeces call self.move_select(diff) endif elseif index(['scroll_top_head', 'scroll_top', 'scroll_bottom_head', 'scroll_bottom', 'scroll_center_head', 'scroll_center'], a:action) >= 0 let diff = a:action =~# 'center' ? hour - (self.max_index - self.min_index) / 2 + 1 - self.min_index : (self.length - 1) * (a:action =~# 'top' ? 1 : -1) let self.min_index += diff let self.max_index += diff elseif a:action ==# 'status' let message = get(self.current_contents(), 'title', get(self.current_contents(), 'summary', '')) call calendar#echo#message(message) return 1 elseif index(['yank', 'yank_line'], a:action) >= 0 call self.yank() return 1 elseif index(['delete', 'delete_line'], a:action) >= 0 if calendar#setting#get('yank_deleting') call self.yank() endif return 1 elseif a:action ==# 'enter' let url = get(self.current_contents(), 'htmlLink', '') if url !=# '' call calendar#webapi#open_url(url) endif elseif a:action ==# 'command_enter' && mode() ==# 'c' && getcmdtype() ==# ':' let cmd = calendar#util#getcmdline() if cmd =~# '\v^\s*\d+\s*$' let c = max([min([matchstr(cmd, '\d\+') * 1 - 1, self.length - 1]), 0]) let idxes = filter(range(self.length), 'index(self.noindex, v:val) < 0') call self.move_select(get(idxes, c, get(idxes, -1, 0)) - hour) return calendar#util#update_keys() endif elseif a:action ==# 'command_enter' && mode() ==# 'c' && (getcmdtype() ==# '/' || getcmdtype() ==# '?') \ || a:action ==# 'next_match' || a:action ==# 'prev_match' let iscmd = a:action ==# 'command_enter' let pattern = iscmd ? getcmdline() : @/ if iscmd && getcmdtype() ==# '/' || a:action ==# 'next_match' && v:searchforward \ || a:action ==# 'prev_match' && !v:searchforward let indexes = range(self.select + 1 - iscmd, self.length - 1) + range(self.select + 1) let status = '/' . pattern else let indexes = range(self.select - 1 + iscmd, 0, -1) + range(self.length - 1, self.select, -1) let status = '?' . pattern endif let exitvalue = iscmd ? "\:\silent call b:calendar.update()\" \ . ":\silent let v:searchforward=" . (getcmdtype() ==# '/') . "\" \ . ":\echo " . string(status) . "\" : 0 if iscmd let @/ = pattern else echo status endif try for i in indexes if self.cnt[i] =~ pattern " do not use =~# (use 'ignorecase') call self.move_select(i - self.select) return exitvalue endif endfor catch endtry return exitvalue endif if self.__updated && [select, max_index] == [self.select, self.max_index] && (min_index == self.min_index || !min_index) return '' endif endfunction function! s:instance.yank() dict abort let message = get(self.current_contents(), 'title', get(self.current_contents(), 'summary', '')) call calendar#util#yank(message) endfunction function! s:instance.action(action) dict abort return self._action(a:action) endfunction let s:super_constructor = calendar#constructor#view#new(s:instance) let &cpo = s:save_cpo unlet s:save_cpo