1
0
mirror of https://github.com/SpaceVim/SpaceVim.git synced 2025-01-23 07:10:06 +08:00

refactor(chat): use bundle vim-chat plugin

This commit is contained in:
Shidong Wang 2021-10-24 16:25:43 +08:00
parent cefb375609
commit b64b80a2bb
No known key found for this signature in database
GPG Key ID: 41BB7053E835C848
9 changed files with 2050 additions and 3 deletions

View File

@ -7,9 +7,9 @@
"=============================================================================
function! SpaceVim#layers#chat#plugins() abort
return [
\ ['vim-chat/vim-chat',{ 'merged' : 0, 'loadconf' : 1}],
\ ]
return [
\ [g:_spacevim_root_dir . 'bundle/vim-chat', {'merged' : 0, 'loadconf' : 1}],
\ ]
endfunction
let s:BASE64 = SpaceVim#api#import('data#base64')

1
bundle/vim-chat/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
tags

View File

@ -0,0 +1,9 @@
cmdargs:
# Checking more strictly
severity: style_problem
policies:
ProhibitImplicitScopeVariable:
enabled: false
ProhibitAbbreviationOption:
enabled: false

31
bundle/vim-chat/README.md Normal file
View File

@ -0,0 +1,31 @@
## vim-chat
chat in neovim and vim8
### Install
```viml
call dein#add('wsdjeg/vim-chat')
```
now you can chatting with me by `call chat#chatting#OpenMsgWin()`, insert `/help` for help message;
### 微信及QQ聊天
#### 依赖
- [mojo-webqq](https://github.com/sjdy521/Mojo-Webqq) 和 [mojo-weixin](https://github.com/sjdy521/Mojo-Weixin)用于将QQ及微信协议转换成irc协议
- IRC 依赖模块: `cpanm -v Mojo::IRC::Server::Chinese`, 详见: [irc.md](https://github.com/sjdy521/Mojo-Webqq/blob/master/IRC.md)
- [irssi](https://irssi.org/): irc 聊天客户端
- [feh](https://feh.finalrewind.org/): 图片查看器,用于打开二维码
- [neovim](https://github.com/neovim/neovim) vim8 的异步存在[问题](https://github.com/vim/vim/issues/1198)无法实现相应功能故只能在neovim下使用
- Linux: 目前仅在Linux下测试成功 Windows 下请直接使用QQ客户端
#### 启动
首先启动 QQ 服务器 `call chat#qq#start()`, 然后会自动弹出一个二维码手机扫描下就可以登录了。neovim 默认使用 `Alt + x` 打开/关闭聊天窗口。
#### 效果图
![Markdown](http://i2.kiimg.com/1949/c18404d7afdc7f3a.gif)

View File

@ -0,0 +1,13 @@
function! chat#startServer(name) abort
endfunction
function! chat#openWin(...) abort
if a:0 == 0
call chat#chatting#OpenMsgWin()
else
if !empty(globpath(&rtp,'autoload/chat/' . a:1 .'.vim'))
call call('chat#' . a:1 . '#OpenMsgWin', [])
endif
endif
endfunction

View File

@ -0,0 +1,486 @@
scriptencoding utf-8
let s:server_lib = get(g:, 'chatting_server_lib', fnamemodify('~/sources/Chatting/target/Chatting-1.0-SNAPSHOT.jar', ':p'))
let s:login_user = ''
let s:server_job_id = 0
let s:client_job_id = 0
let s:debug_log = []
let s:chatting_commands = []
let s:current_channel = ''
let s:last_channel = ''
let s:server_ip = get(g:, 'chatting_server_ip', 'perfi.wang')
let s:server_port = get(g:, 'chatting_server_port', 2013)
let s:vim8_ch_waittime = get(g:, 'chatting_ch_waittime', 100)
let s:close_windows_char = get(g:, 'chatting_close_win_char',"\<M-c>")
let s:messages = []
let s:opened_channels = []
let s:unread_msg_num = {}
let s:st_sep = ''
function! s:push_message(msg) abort
if type(a:msg) == type([])
for m in a:msg
if !empty(m)
call s:hander_msg(m)
endif
endfor
else
if !empty(a:msg)
call s:hander_msg(a:msg)
endif
endif
endfunction
function! s:hander_msg(msg) abort
let info = json_decode(a:msg)
call add(s:messages, info)
if info['type'] ==# 'onWindowChange'
let s:current_channel = info['name']
if index(s:opened_channels, s:current_channel) == -1
call add(s:opened_channels, s:current_channel)
endif
if s:current_channel !=# ''
call s:update_statusline()
endif
elseif info['type'] ==# 'group_message'
let n = get(s:unread_msg_num, info['group_name'], 0)
let n += 1
if has_key(s:unread_msg_num, info['group_name'])
call remove(s:unread_msg_num, info['group_name'])
endif
call extend(s:unread_msg_num, {info['group_name'] : n})
if s:current_channel !=# ''
call s:update_statusline()
endif
elseif info['type'] ==# 'info_message'
if info['context'] =~# 'you are logined as '
let s:login_user = substitute(info['context'], 'you are logined as ', '', 'g')
endif
elseif info['type'] ==# 'onGetConnection'
let s:chatting_commands = split(info['commands'], ',')
endif
endfunction
function! chat#chatting#start() abort
if s:server_job_id == 0
call s:log('startting server, server_lib is ' . s:server_lib . '(' . (empty(glob(s:server_lib)) ? 'no such file' : 'file exists' ). ')')
if has('nvim')
let s:server_job_id = jobstart(['java', '-cp', s:server_lib, 'com.wsdjeg.chat.Server'],{
\ 'on_stdout' : function('s:server_handler'),
\ })
elseif exists('*job#start') && !has('nvim')
let s:server_job_id = job#start(['java', '-cp', s:server_lib, 'com.wsdjeg.chat.Server'],{
\ 'on_stdout' : function('s:server_handler'),
\ })
endif
endif
endfunction
function! s:server_handler(id, data, event) abort
endfunction
function! s:client_handler(id, data, event) abort
if a:event ==# 'stdout'
call s:push_message(a:data)
call s:update_msg_screen()
elseif a:event ==# 'exit'
call s:log('client exit with code:' . a:data)
let s:client_job_id = 0
endif
endfunction
function! s:ch_callbakc(ch, msg) abort
call s:push_message(a:msg)
call s:update_msg_screen()
endfunction
function! s:start_client() abort
if has('nvim')
if s:client_job_id == 0
let s:client_job_id = jobstart(['java', '-cp', s:server_lib, 'com.wsdjeg.chat.Client', s:server_ip, s:server_port],{
\ 'on_stdout' : function('s:client_handler'),
\ 'on_exit' : function('s:client_handler')
\ })
call s:log('Server_lib:' . s:server_lib)
endif
else
let s:channel = ch_open(s:server_ip . ':' . s:server_port,
\ {'callback': function('s:ch_callbakc') ,'mode': 'nl', 'waittime': s:vim8_ch_waittime})
call s:log('Client channel status:' . ch_status(s:channel))
endif
call s:log('Client startting with server ip(' . s:server_ip . ') port(' . s:server_port . ')')
endfunction
let s:name = '__Chatting__'
let s:c_base = '>>> '
let s:c_begin = ''
let s:c_char = ''
let s:c_end = ''
let s:msg_win_opened = 0
function! chat#chatting#OpenMsgWin() abort
if has('nvim')
if s:client_job_id == 0
call s:start_client()
endif
else
if !exists('s:channel') || ch_status(s:channel) !=# 'open'
call s:start_client()
endif
endif
if bufwinnr(s:name) < 0
if bufnr(s:name) != -1
exe 'silent! botright split ' . '+b' . bufnr(s:name)
else
exe 'silent! botright split ' . s:name
endif
else
exec bufwinnr(s:name) . 'wincmd w'
endif
call s:windowsinit()
call s:init_hi()
setl modifiable
let s:msg_win_opened = 1
if !empty(s:last_channel)
let s:current_channel = s:last_channel
endif
call s:update_msg_screen()
call s:echon()
while get(s:, 'quit_chating_win', 0) == 0
let nr = getchar()
if nr !=# "\<Up>" && nr !=# "\<Down>"
let s:complete_input_history_num = [0,0]
endif
if nr != 9
let s:complete_num = 0
endif
if nr == 13
call s:enter()
elseif nr ==# "\<Right>" || nr == 6 "<Right> 向右移动光标
let s:c_begin = s:c_begin . s:c_char
let s:c_char = matchstr(s:c_end, '^.')
let s:c_end = substitute(s:c_end, '^.', '', 'g')
elseif nr == 21 " ctrl+u clean the message
let s:c_begin = ''
elseif nr == 9 " use <tab> complete str
if s:complete_num == 0
let complete_base = s:c_begin
else
let s:c_begin = complete_base
endif
let s:c_begin = s:complete(complete_base, s:complete_num)
let s:complete_num += 1
elseif nr == 11 " ctrl+k delete the chars from cursor to the end
let s:c_char = ''
let s:c_end = ''
elseif nr ==# "\<M-Left>" || nr ==# "\<M-h>"
"<Alt>+<Left> 移动到左边一个聊天窗口
call s:previous_channel()
elseif nr ==# "\<M-Right>" || nr ==# "\<M-l>"
"<Alt>+<Right> 移动到右边一个聊天窗口
call s:next_channel()
elseif nr ==# "\<Left>" || nr == 2 "<Left> 向左移动光标
if s:c_begin !=# ''
let s:c_end = s:c_char . s:c_end
let s:c_char = matchstr(s:c_begin, '.$')
let s:c_begin = substitute(s:c_begin, '.$', '', 'g')
endif
elseif nr ==# "\<PageUp>"
let l = line('.') - winheight('$')
if l < 0
exe 0
else
exe l
endif
elseif nr ==# "\<PageDown>"
exe line('.') + winheight('$')
elseif nr ==# "\<Home>" || nr == 1 "<Home> 或 <ctrl> + a 将光标移动到行首
let s:c_end = substitute(s:c_begin . s:c_char . s:c_end, '^.', '', 'g')
let s:c_char = matchstr(s:c_begin, '^.')
let s:c_begin = ''
elseif nr ==# "\<End>" || nr == 5 "<End> 或 <ctrl> + e 将光标移动到行末
let s:c_begin = s:c_begin . s:c_char . s:c_end
let s:c_char = ''
let s:c_end = ''
elseif nr ==# s:close_windows_char
let s:quit_chating_win = 1
elseif nr == 8 || nr ==# "\<bs>" " ctrl+h or <bs> delete last char
let s:c_begin = substitute(s:c_begin,'.$','','g')
elseif nr ==# "\<Up>"
if s:complete_input_history_num == [0,0]
let complete_input_history_base = s:c_begin
let s:c_char = ''
let s:c_end = ''
else
let s:c_begin = complete_input_history_base
endif
let s:complete_input_history_num[0] += 1
let s:c_begin = s:complete_input_history(complete_input_history_base, s:complete_input_history_num)
elseif nr ==# "\<Down>"
if s:complete_input_history_num == [0,0]
let complete_input_history_base = s:c_begin
let s:c_char = ''
let s:c_end = ''
else
let s:c_begin = complete_input_history_base
endif
let s:complete_input_history_num[1] += 1
let s:c_begin = s:complete_input_history(complete_input_history_base, s:complete_input_history_num)
else
let s:c_begin .= nr2char(nr)
endif
call s:echon()
endwhile
setl nomodifiable
exe 'bd ' . bufnr(s:name)
let s:quit_chating_win = 0
let s:last_channel = s:current_channel
let s:current_channel = ''
let s:msg_win_opened = 0
normal! :
endfunction
function! s:update_msg_screen() abort
if s:msg_win_opened
normal! ggdG
for msg in s:messages
if msg['type'] ==# 'group_message' && msg['group_name'] ==# s:current_channel
call append(line('$'), '[' . msg['time'] . '] < ' . msg['sendder'] . ' > ' . msg['context'])
elseif msg['type'] ==# 'info_message' && msg['context'] !~# '^join channel :'
call append(line('$'), '[' . msg['time'] . '] ' . msg['context'])
elseif msg['type'] ==# 'user_message'
\ && (
\ msg['sendder'] ==# s:current_channel
\ ||
\ (msg['sendder'] ==# s:login_user && msg['receiver'] ==# s:current_channel)
\ )
call append(line('$'), '[' . msg['time'] . '] < ' . msg['sendder'] . ' > ' . msg['context'])
endif
endfor
normal! gg
delete
normal! G
redraw
call s:echon()
endif
endfunction
function! s:echon() abort
redraw!
echohl Comment | echon s:c_base
echohl None | echon s:c_begin
echohl Wildmenu | echon s:c_char
echohl None | echon s:c_end
endfunction
fu! s:windowsinit() abort
" option
setl fileformat=unix
setl fileencoding=utf-8
setl iskeyword=@,48-57,_
setl noreadonly
setl buftype=nofile
setl bufhidden=wipe
setl noswapfile
setl nobuflisted
setl nolist
setl nonumber
setl norelativenumber
setl wrap
setl winfixwidth
setl winfixheight
setl textwidth=0
setl nospell
setl nofoldenable
setl cursorline
endf
function! s:log(msg) abort
let time = strftime('%H:%M:%S')
let msg = '[' . time . '] ' . string(a:msg)
call add(s:debug_log, msg)
endfunction
function! s:debug() abort
tabnew
for line in s:debug_log
call append(line('$'), line)
endfor
nnoremap <buffer><silent> q :bd!<CR>
endfunction
let s:enter_history = []
function! s:enter() abort
if s:c_begin . s:c_char . s:c_end ==# '/quit'
let s:quit_chating_win = 1
let s:c_end = ''
let s:c_char = ''
let s:c_begin = ''
return
endif
if has('nvim')
if s:client_job_id != 0
call jobsend(s:client_job_id, [s:c_begin . s:c_char . s:c_end, ''])
endif
else
call ch_sendraw(s:channel, s:c_begin . s:c_char . s:c_end ."\n")
endif
call add(s:enter_history, s:c_begin . s:c_char . s:c_end)
let s:c_end = ''
let s:c_char = ''
let s:c_begin = ''
endfunction
let s:complete_num = 0
function! s:complete(base,num) abort
if a:base =~# '^/[a-z]*$'
let rsl = filter(copy(s:chatting_commands), "v:val =~# a:base .'[^\ .]*'")
if len(rsl) > 0
return rsl[a:num % len(rsl)] . ' '
endif
endif
return a:base
endfunction
let s:complete_input_history_num = [0,0]
function! s:complete_input_history(base,num) abort
let results = filter(copy(s:enter_history), "v:val =~# '^' . a:base")
if len(results) > 0
call add(results, a:base)
let index = ((len(results) - 1) - a:num[0] + a:num[1]) % len(results)
return results[index]
else
return a:base
endif
endfunction
function! s:init_hi() abort
if get(s:, 'init_hi_done', 0) == 0
" current channel
hi! ChattingHI1 ctermbg=003 ctermfg=Black guibg=#fabd2f guifg=#282828
" channel with new msg
hi! ChattingHI2 ctermbg=005 ctermfg=Black guibg=#b16286 guifg=#282828
" normal channel
hi! ChattingHI3 ctermbg=007 ctermfg=Black guibg=#8ec07c guifg=#282828
" end
hi! ChattingHI4 ctermbg=243 guibg=#7c6f64
" current channel + end
hi! ChattingHI5 guibg=#7c6f64 guifg=#fabd2f
" current channel + new msg channel
hi! ChattingHI6 guibg=#b16286 guifg=#fabd2f
" current channel + normal channel
hi! ChattingHI7 guibg=#8ec07c guifg=#fabd2f
" new msg channel + end
hi! ChattingHI8 guibg=#7c6f64 guifg=#b16286
" new msg channel + current channel
hi! ChattingHI9 guibg=#fabd2f guifg=#b16286
" new msg channel + normal channel
hi! ChattingHI10 guibg=#8ec07c guifg=#b16286
" new msg channel + new msg channel
hi! ChattingHI11 guibg=#b16286 guifg=#b16286
" normal channel + end
hi! ChattingHI12 guibg=#7c6f64 guifg=#8ec07c
" normal channel + normal channel
hi! ChattingHI13 guibg=#8ec07c guifg=#8ec07c
" normal channel + new msg channel
hi! ChattingHI14 guibg=#b16286 guifg=#8ec07c
" normal channel + current channel
hi! ChattingHI15 guibg=#fabd2f guifg=#8ec07c
let s:init_hi_done = 1
endif
endfunction
function! s:update_statusline() abort
let st = ''
for ch in s:opened_channels
let ch = substitute(ch, ' ', '\ ', 'g')
if ch == s:current_channel
if has_key(s:unread_msg_num, s:current_channel)
call remove(s:unread_msg_num, s:current_channel)
endif
let st .= '%#ChattingHI1#[' . ch . ']'
if index(s:opened_channels, ch) == len(s:opened_channels) - 1
let st .= '%#ChattingHI5#' . s:st_sep
elseif get(s:unread_msg_num, s:opened_channels[index(s:opened_channels, ch) + 1], 0) > 0
let st .= '%#ChattingHI6#' . s:st_sep
else
let st .= '%#ChattingHI7#' . s:st_sep
endif
else
let n = get(s:unread_msg_num, ch, 0)
if n > 0
let st .= '%#ChattingHI2#[' . ch . '(' . n . 'new)]'
if index(s:opened_channels, ch) == len(s:opened_channels) - 1
let st .= '%#ChattingHI8#' . s:st_sep
elseif get(s:unread_msg_num, s:opened_channels[index(s:opened_channels, ch) + 1], 0) > 0
\ && s:opened_channels[index(s:opened_channels, ch) + 1] !=# s:current_channel
let st .= '%#ChattingHI11#' . s:st_sep
elseif s:opened_channels[index(s:opened_channels, ch) + 1] ==# s:current_channel
let st .= '%#ChattingHI9#' . s:st_sep
else
let st .= '%#ChattingHI10#' . s:st_sep
endif
else
let st .= '%#ChattingHI3#[' . ch . ']'
if index(s:opened_channels, ch) == len(s:opened_channels) - 1
let st .= '%#ChattingHI12#' . s:st_sep
elseif get(s:unread_msg_num, s:opened_channels[index(s:opened_channels, ch) + 1], 0) > 0
\ && s:opened_channels[index(s:opened_channels, ch) + 1] !=# s:current_channel
let st .= '%#ChattingHI14#' . s:st_sep
elseif s:opened_channels[index(s:opened_channels, ch) + 1] ==# s:current_channel
let st .= '%#ChattingHI15#' . s:st_sep
else
let st .= '%#ChattingHI13#' . s:st_sep
endif
endif
endif
endfor
let st .= '%#ChattingHI4# '
exe 'set statusline=' . st
endfunction
function! s:previous_channel() abort
let id = index(s:opened_channels, s:current_channel)
let id -= 1
if id < 0
let id = id + len(s:opened_channels)
endif
let s:current_channel = s:opened_channels[id]
if s:current_channel =~# '^#'
call s:send('/join ' . s:current_channel)
else
call s:send('/query ' . s:current_channel)
endif
call s:update_msg_screen()
call s:update_statusline()
endfunction
function! s:next_channel() abort
let id = index(s:opened_channels, s:current_channel)
let id += 1
if id > len(s:opened_channels) - 1
let id = id - len(s:opened_channels)
endif
let s:current_channel = s:opened_channels[id]
if s:current_channel =~# '^#'
call s:send('/join ' . s:current_channel)
else
call s:send('/query ' . s:current_channel)
endif
call s:update_msg_screen()
call s:update_statusline()
endfunction
function! s:send(msg) abort
if has('nvim')
if s:client_job_id != 0
call jobsend(s:client_job_id, [a:msg, ''])
endif
else
call ch_sendraw(s:channel, a:msg ."\n")
endif
endfunction
call chat#debug#defind('chatting', function('s:debug'))

View File

@ -0,0 +1,9 @@
let s:debug_func = {}
function! chat#debug#defind(key, value) abort
call extend(s:debug_func, {a:key : a:value})
endfunction
function! chat#debug#getLog(key) abort
call call(s:debug_func[a:key],[])
endfunction

View File

@ -0,0 +1,814 @@
scriptencoding utf-8
let s:save_cpo = &cpoptions
set cpoptions&vim
let s:server_log = []
function! s:check_executable(exe) abort
if !executable(a:exe)
echohl WarningMsg
echo 'vim-chat need ' . a:exe . ' in your PATH'
echohl None
endif
endfunction
let s:run_script = "
\ use Mojo::Webqq;\n
\ my $qq = " . get(g:, 'VimQQaccount', '279834419') . ";\n
\ my $client = Mojo::Webqq->new(qq=>$qq);\n
\ $client->load('ShowMsg');\n
\ $client->load('IRCShell',data=>{load_friend=>1,});\n
\ $client->log->handle(\*STDOUT);\n
\ $client->run();\n
\ "
let s:local_Mojo_dir = get(g:, 'local_Mojo_dir', '~/src/Mojo-Webqq/lib')
if isdirectory(s:local_Mojo_dir)
let s:run_script = "use lib '" . s:local_Mojo_dir . "'\n" . s:run_script
endif
let s:run_job_id = 0
let s:irssi_job_id = 0
let s:feh_code_id = 0
let s:qq_channels = []
let s:irssi_commands = ['/join', '/query', '/list', '/quit', '/msg', '/wc']
let s:history = []
let s:current_channel = ''
let s:last_channel = ''
let s:last_channel_input_methon = ''
let s:friends = [] " each item is ['channel','nickname']
let s:input_history = []
let s:complete_num = 0
let s:complete_input_history_num = [0,0]
let s:opened_channels = []
let s:irssi_log = []
let s:unread_msg_num = {}
let s:st_sep = ''
let s:ch_input_method = [] " [ch_name, input_methon] 1:en 2:cn
function! s:init_hi() abort
if get(s:, 'init_hi_done', 0) == 0
" current channel
hi! VimQQ1 ctermbg=003 ctermfg=Black guibg=#fabd2f guifg=#282828
" channel with new msg
hi! VimQQ2 ctermbg=005 ctermfg=Black guibg=#b16286 guifg=#282828
" normal channel
hi! VimQQ3 ctermbg=007 ctermfg=Black guibg=#8ec07c guifg=#282828
" end
hi! VimQQ4 ctermbg=243 guibg=#7c6f64
" current channel + end
hi! VimQQ5 guibg=#7c6f64 guifg=#fabd2f
" current channel + new msg channel
hi! VimQQ6 guibg=#b16286 guifg=#fabd2f
" current channel + normal channel
hi! VimQQ7 guibg=#8ec07c guifg=#fabd2f
" new msg channel + end
hi! VimQQ8 guibg=#7c6f64 guifg=#b16286
" new msg channel + current channel
hi! VimQQ9 guibg=#fabd2f guifg=#b16286
" new msg channel + normal channel
hi! VimQQ10 guibg=#8ec07c guifg=#b16286
" new msg channel + new msg channel
hi! VimQQ11 guibg=#b16286 guifg=#b16286
" normal channel + end
hi! VimQQ12 guibg=#7c6f64 guifg=#8ec07c
" normal channel + normal channel
hi! VimQQ13 guibg=#8ec07c guifg=#8ec07c
" normal channel + new msg channel
hi! VimQQ14 guibg=#b16286 guifg=#8ec07c
" normal channel + current channel
hi! VimQQ15 guibg=#fabd2f guifg=#8ec07c
let s:init_hi_done = 1
endif
endfunction
function! s:jobstart(...) abort
if has('nvim')
if a:0 == 1
return jobstart(a:1)
elseif a:0 == 2
return jobstart(a:1, a:2)
endif
elseif exists('*job#start') && !has('nvim')
endif
endfunction
function! s:jobstop(id) abort
if has('nvim')
call jobstop(a:id)
elseif exists('*job#stop') && !has('nvim')
endif
endfunction
function! s:jobsend(id,data) abort
if has('nvim')
if type(a:data) == type('')
let data = [a:data, '']
elseif type(a:data) == type([]) && a:data[-1] !=# ''
let data = a:data + ['']
else
let data = a:data
endif
call jobsend(a:id, data)
elseif exists('*job#send') && !has('nvim')
call job#send(a:id, a:data)
endif
endfunction
function! s:feh_code(png) abort
call s:stop_feh()
let s:feh_code_id = s:jobstart(['feh', '--title', 'webqqcode', a:png])
endfunction
function! s:stop_feh() abort
if s:feh_code_id != 0
call s:jobstop(s:feh_code_id)
let s:feh_code_id =0
endif
endfunction
function! s:irssi_handler(id, data, event) abort
if a:event ==# 'exit'
let s:irssi_job_id = 0
elseif a:event ==# 'stderr'
call add(s:irssi_log, ['stderr', a:data])
elseif a:event ==# 'stdout'
call add(s:irssi_log, ['stdout', a:data])
endif
endfunction
function! s:start_irssi() abort
if s:irssi_job_id == 0
let argv = ['irssi','-c', '127.0.0.1', '-p', '6667']
let s:irssi_job_id = s:jobstart(argv, {
\ 'on_stdout': function('s:irssi_handler'),
\ 'on_stderr': function('s:irssi_handler'),
\ 'on_exit': function('s:irssi_handler'),
\ })
endif
endfunction
function! s:handler_stdout_data(data) abort
if !empty(a:data)
call add(s:server_log, a:data)
endif
if match(a:data, '二维码已下载到本地\[ /tmp/mojo_webqq_qrcode_') != -1
let png = matchstr(a:data, '/tmp/mojo_webqq_qrcode_default.png')
if !empty(png)
call s:feh_code(png)
endif
elseif matchstr(a:data, '帐号(\d*)登录成功') !=# ''
call s:stop_feh()
elseif matchstr(a:data,'频道\ #.*\ 已创建') !=# ''
let ch = matchstr(a:data,'#[^\ .]*')
if index(s:qq_channels, ch) == -1
call add(s:qq_channels, ch)
endif
elseif matchstr(a:data, '\[\d\d/\d\d/\d\d \d\d\:\d\d\:\d\d\] \[info\] \[.*\:虚拟用户\] 加入频道 #我的好友') !=# ''
" [16/10/31 20:06:09] [info] [nullptr:虚拟用户] 加入频道 #我的好友
" [28:-42]
let friend = ['我的好友',a:data[28:-42]]
if index(s:friends, friend) == -1
call add(s:friends, friend)
endif
elseif matchstr(a:data, '\[\d\d/\d\d/\d\d \d\d\:\d\d\:\d\d\] \[群消息\]') !=# ''
" send:[16/10/22 18:26:58] [群消息] 我->Vim/exVim 开发讨论群 : 测试补全
" start index 32
if matchstr(a:data, '[^\ .]*->[^\ .]*\s\:\s') !=# ''
let idx1 = match(a:data, '->')
let idx2 = match(a:data, ' : ')
let msg = [ a:data[32:idx1-1], '#' . a:data[idx1+2:idx2-1], a:data[idx2+3:]]
let msg[1] = substitute(msg[1], '[\ !@&]', '', 'g')
call add(s:history, msg)
let friend = [msg[1], msg[0]]
if index(s:friends, friend) == -1
call add(s:friends, friend)
endif
if msg[1] == s:current_channel
call s:update_msg_screen()
endif
" get:[16/10/22 18:26:58] [群消息] 灰灰|Vim/exVim 开发讨论群 : 测试补全
elseif matchstr(a:data, '[^\ .]*|[^\ .]*\s\:\s') !=# ''
let idx1 = match(a:data, '|')
let idx2 = match(a:data, ' : ')
let msg = [ a:data[32:idx1-1], '#' .a:data[idx1+1:idx2-1], a:data[idx2+3:]]
let msg[1] = substitute(msg[1], '[\ !@&]', '', 'g')
call add(s:history, msg)
let friend = [msg[1], msg[0]]
if index(s:friends, friend) == -1
call add(s:friends, friend)
endif
if msg[1] == s:current_channel
call s:update_msg_screen()
elseif index(s:opened_channels, msg[1]) != -1 && s:current_channel !=# msg[1]
let n = get(s:unread_msg_num, msg[1], 0)
let n += 1
if has_key(s:unread_msg_num, msg[1])
call remove(s:unread_msg_num, msg[1])
endif
call extend(s:unread_msg_num, {msg[1] : n})
if s:current_channel !=# ''
call s:update_statusline()
endif
endif
endif
elseif matchstr(a:data, '\[\d\d/\d\d/\d\d \d\d\:\d\d\:\d\d\] \[好友消息\]') !=# ''
" send: [16/10/22 14:25:56] [好友消息] 我->老婆 : 1
if matchstr(a:data, '[^\ .]*->[^\ .]*') !=# ''
let msg = split(matchstr(a:data, '[^\ .]*->[^\ .]*'), '->')
let f = msg[1]
let msg[1] = ''
call add(msg, substitute(a:data,'\[\d\d/\d\d/\d\d \d\d\:\d\d\:\d\d\] \[好友消息\].*->[^\ .]*\ \:\ ','','g'))
call add(msg, f)
call add(s:history, msg)
let friend = ['我的好友',f]
if index(s:friends, friend) == -1
call add(s:friends, friend)
endif
if f == s:current_channel
call s:update_msg_screen()
endif
" get: [16/10/22 14:25:59] [好友消息] 老婆|我的好友 : 测试
elseif matchstr(a:data, '[^\ .]*|[^\ .]*') !=# ''
let msg = split(matchstr(a:data, '[^\ .]*|[^\ .]*'), '|')
let f = msg[0]
let msg[1] = ''
call add(msg, substitute(a:data,'\[\d\d/\d\d/\d\d \d\d\:\d\d\:\d\d\] \[好友消息\].*|[^\ .]*\ \:\ ','','g'))
call add(msg, f)
call add(s:history, msg)
let friend = ['我的好友',f]
if index(s:friends, friend) == -1
call add(s:friends, friend)
endif
if f == s:current_channel
call s:update_msg_screen()
elseif index(s:opened_channels, msg[3]) != -1 && s:current_channel !=# msg[3]
let n = get(s:unread_msg_num, msg[3], 0)
let n += 1
if has_key(s:unread_msg_num, msg[3])
call remove(s:unread_msg_num, msg[3])
endif
call extend(s:unread_msg_num, {msg[3] : n})
if s:current_channel !=# ''
call s:update_statusline()
endif
elseif index(s:opened_channels, msg[3]) == -1
let n = get(s:unread_msg_num, msg[3], 0)
let n += 1
if has_key(s:unread_msg_num, msg[3])
call remove(s:unread_msg_num, msg[3])
endif
call extend(s:unread_msg_num, {msg[3] : n})
call add(s:opened_channels, msg[3])
if s:current_channel !=# ''
call s:update_statusline()
endif
endif
endif
endif
endfunction
function! Test(str) abort
exe a:str
endfunction
function! s:start_handler(id, data, event) abort
if a:event ==# 'stdout'
if type(a:data) == type([])
for a in a:data
call s:handler_stdout_data(a)
endfor
elseif type(a:data) == type('')
call s:handler_stdout_data(a:data)
else
endif
elseif a:event ==# 'stderr'
elseif a:event ==# 'exit'
endif
endfunction
function! chat#qq#start() abort
call s:check_executable('feh')
call s:check_executable('irssi')
call s:check_executable('perl')
let argv = ['perl', '-e', s:run_script]
if s:run_job_id == 0
let s:run_job_id = s:jobstart(argv, {
\ 'on_stdout': function('s:start_handler'),
\ 'on_stderr': function('s:start_handler'),
\ 'on_exit': function('s:start_handler'),
\ })
if s:run_job_id != 0
echo 'qq server has been started!'
else
echo 'failed to start qq server!'
endif
else
echo 'qq server has been started!'
endif
endfunction
function! s:send(...) abort
if a:0 > 0
if s:irssi_job_id == 0
call s:start_irssi()
endif
call s:jobsend(s:irssi_job_id, a:1)
endif
endfunction
let s:name = '__VimQQ__'
let s:c_base = '>>>'
let s:c_begin = ''
let s:c_char = ''
let s:c_end = ''
function! chat#qq#OpenMsgWin() abort
if s:run_job_id == 0
echohl WarningMsg
echo "qq server has not beed started, please use ':call chat#qq#start()'"
echohl NONE
return
endif
if bufwinnr('s:name') < 0
if bufnr('s:name') != -1
exe 'silent! botright split ' . '+b' . bufnr(s:name)
else
exe 'silent! botright split ' . s:name
endif
else
exec bufwinnr('s:name') . 'wincmd w'
endif
setl modifiable
call s:init_hi()
call s:windowsinit()
if s:last_channel !=# ''
let s:current_channel = s:last_channel
call s:update_statusline()
call s:update_msg_screen()
if s:last_channel_input_methon == 1
call system('fcitx-remote -c')
elseif s:last_channel_input_methon == 2
call system('fcitx-remote -o')
endif
endif
call s:echon()
while get(s:, 'quit_qq_win', 0) == 0
let nr = getchar()
if nr != 9
let s:complete_num = 0
endif
if nr !=# "\<Up>" && nr !=# "\<Down>"
let s:complete_input_history_num = [0,0]
endif
if nr == 13 "<cr> 执行命令,或发送消息
call s:parser_input(s:c_begin . s:c_char . s:c_end)
let s:c_begin = ''
let s:c_char = ''
let s:c_end = ''
elseif nr ==# "\<M-Left>" || nr ==# "\<M-h>"
"<Alt>+<Left> 移动到左边一个聊天窗口
call s:previous_channel()
elseif nr ==# "\<M-Right>" || nr ==# "\<M-l>"
"<Alt>+<Right> 移动到右边一个聊天窗口
call s:next_channel()
elseif nr ==# "\<Right>" || nr == 6 "<Right> 向右移动光标
let s:c_begin = s:c_begin . s:c_char
let s:c_char = matchstr(s:c_end, '^.')
let s:c_end = substitute(s:c_end, '^.', '', 'g')
elseif nr ==# "\<Left>" || nr == 2 "<Left> 向左移动光标
if s:c_begin !=# ''
let s:c_end = s:c_char . s:c_end
let s:c_char = matchstr(s:c_begin, '.$')
let s:c_begin = substitute(s:c_begin, '.$', '', 'g')
endif
elseif nr ==# "\<Home>" || nr == 1 "<Home> 或 <ctrl> + a 将光标移动到行首
let s:c_end = substitute(s:c_begin . s:c_char . s:c_end, '^.', '', 'g')
let s:c_char = matchstr(s:c_begin, '^.')
let s:c_begin = ''
elseif nr ==# "\<End>" || nr == 5 "<End> 或 <ctrl> + e 将光标移动到行末
let s:c_begin = s:c_begin . s:c_char . s:c_end
let s:c_char = ''
let s:c_end = ''
elseif nr ==# "\<M-x>" "<Alt>+x 关闭聊天窗口
let s:quit_qq_win = 1
let s:last_channel = s:current_channel
let s:current_channel = ''
if executable('fcitx-remote')
let s:last_channel_input_methon = system('fcitx-remote')
endif
elseif nr == 8 || nr ==# "\<bs>" " ctrl+h or <bs> delete last char
let s:c_begin = substitute(s:c_begin,'.$','','g')
elseif nr == 23 " ctrl+w delete last word
let s:c_begin = substitute(s:c_begin,'[^\ .*]\+\s*$','','g')
elseif nr == 11 " ctrl+k delete the chars from cursor to the end
let s:c_char = ''
let s:c_end = ''
elseif nr ==# "\<M-f>" " Alt + f :按单词前移(右向)
if matchstr(s:c_end, '^\ *[^\ .]\+') !=# ''
let s:c_begin = s:c_begin . s:c_char . matchstr(s:c_end, '^\ *[^\ .]\+')
let s:c_end = substitute(s:c_end, '^\ *[^\ .]\+', '', 'g')
let s:c_char = matchstr(s:c_end, '^.')
let s:c_end = substitute(s:c_end, '^.', '', 'g')
endif
elseif nr ==# "\<M-b>"
let s:c_end = matchstr(s:c_begin, '[^\ .]\+\s*$') . s:c_char . s:c_end
let s:c_begin = substitute(s:c_begin, '[^\ .]\+\s*$', '', 'g')
let s:c_char = matchstr(s:c_end, '^.')
let s:c_end = substitute(s:c_end, '^.', '', 'g')
elseif nr ==# "\<M-d>" " Alt + d 从光标处删除至词尾
let s:c_end = s:c_char . s:c_end
let s:c_end = substitute(s:c_end, '^\s*[^\ .]*', '', 'g')
let s:c_char = matchstr(s:c_end, '^.')
let s:c_end = substitute(s:c_end, '^.', '', 'g')
elseif nr == 4 " ctrl+d delete the char under the cursor
let s:c_char = matchstr(s:c_end, '^.')
let s:c_end = substitute(s:c_end, '^.', '', 'g')
elseif nr == 21 " ctrl+u clean the message
let s:c_begin = ''
elseif nr == 9 " use <tab> complete str
if s:complete_num == 0
let complete_base = s:c_begin
else
let s:c_begin = complete_base
endif
let s:c_begin = s:complete(complete_base, s:complete_num)
let s:complete_num += 1
elseif nr == 47 " if type / and str is none, switch to en method
if s:c_begin ==# '' && s:c_char ==# '' && s:c_end ==# '' && executable('fcitx-remote')
call system('fcitx-remote -c')
endif
let s:c_begin .= nr2char(nr)
elseif nr ==# "\<PageUp>"
let l = line('.') - winheight('$')
if l < 0
exe 0
else
exe l
endif
elseif nr ==# "\<PageDown>"
exe line('.') + winheight('$')
elseif nr ==# "\<Up>"
if s:complete_input_history_num == [0,0]
let complete_input_history_base = s:c_begin
let s:c_char = ''
let s:c_end = ''
else
let s:c_begin = complete_input_history_base
endif
let s:complete_input_history_num[0] += 1
let s:c_begin = s:complete_input_history(complete_input_history_base, s:complete_input_history_num)
elseif nr ==# "\<Down>"
if s:complete_input_history_num == [0,0]
let complete_input_history_base = s:c_begin
let s:c_char = ''
let s:c_end = ''
else
let s:c_begin = complete_input_history_base
endif
let s:complete_input_history_num[1] += 1
let s:c_begin = s:complete_input_history(complete_input_history_base, s:complete_input_history_num)
else
let s:c_begin .= nr2char(nr)
endif
call s:echon()
endwhile
setl nomodifiable
exe 'bd ' . bufnr(s:name)
let s:quit_qq_win = 0
normal! :
if executable('fcitx-remote')
call system('fcitx-remote -c') " switch 2 en
else
doautocmd InsertEnter
doautocmd InsertLeave
endif
endf
function! s:complete(str, num) abort
if a:str =~# '^/[a-z]*$'
let rsl = filter(copy(s:irssi_commands), "v:val =~# a:str .'[^\ .]*'")
if len(rsl) > 0
return rsl[a:num % len(rsl)] . ' '
endif
elseif matchstr(a:str, '@[^\ .]$') !=# ''
let n_base = matchstr(a:str, '[^@^\ .]$')
let names = filter(deepcopy(s:friends), "v:val[0] ==# s:current_channel && v:val[1] =~# '^' . n_base")
if len(names) > 0
return substitute(a:str, '[^@^\ .]$', '', 'g') . names[a:num % len(names)][1]
endif
elseif a:str =~# '/join\s\+#[^\ .]*$' || a:str =~# '^/join\s\+$'
let results = filter(deepcopy(s:qq_channels), "v:val =~# '" . substitute(a:str , '^/join\s\+', '', 'g') . "'")
if len(results) > 0
return '/join ' . results[a:num % len(results)]
endif
elseif a:str =~# '^/query\s\+.\+'
let n_base = substitute(a:str, '^/query\s\+', '', 'g')
let names = filter(deepcopy(s:friends), "v:val[0] ==# '我的好友' && v:val[1] =~# '^' . n_base")
if len(names) > 0
return '/query ' . names[a:num % len(names)][1]
endif
elseif a:str =~# '^/msg\s\+'
let n_base = substitute(a:str, '^/msg\s\+', '', 'g')
let res = [] " a list of string
if matchstr(n_base, '^.') ==# '#'
let res = filter(deepcopy(s:qq_channels), "v:val =~# '^' . n_base")
else
for name in filter(deepcopy(s:friends), "v:val[0] ==# '我的好友' && v:val[1] =~# '^' . n_base")
call add(res, name[1])
endfor
let res += filter(deepcopy(s:qq_channels), "v:val =~# '^' . n_base")
endif
if len(res) > 0
return '/msg ' . res[a:num % len(res)] . ' '
endif
elseif index(s:qq_channels, s:current_channel) != -1 && a:str !~# '^/query'
let names = filter(deepcopy(s:friends), "v:val[0] ==# s:current_channel && v:val[1] =~# '^' . a:str")
if len(names) > 0
return names[a:num % len(names)][1] . ': '
endif
endif
return a:str
endfunction
function! s:complete_input_history(str,num) abort
let results = filter(copy(s:input_history), "v:val =~# '^' . a:str")
if len(results) > 0
call add(results, a:str)
let index = ((len(results) - 1) - a:num[0] + a:num[1]) % len(results)
return results[index]
else
return a:str
endif
endfunction
function! s:echon() abort
redraw!
echohl Comment | echon s:c_base
echohl None | echon s:c_begin
echohl Wildmenu | echon s:c_char
echohl None | echon s:c_end
endfunction
function! s:get_str_with_width(str,width) abort
let str = a:str
let result = ''
let tmp = ''
for i in range(strchars(str))
let tmp .= matchstr(str, '^.')
if strwidth(tmp) > a:width
return result
else
let result = tmp
endif
let str = substitute(str, '^.', '', 'g')
endfor
return result
endfunction
function! s:get_lines_with_width(str, width) abort
let str = a:str
let lines = []
let line = ''
let tmp = ''
for i in range(strchars(str))
let tmp .= matchstr(str, '^.')
if strwidth(tmp) > a:width
call add(lines, line)
let tmp = matchstr(str, '^.')
endif
let line = tmp
let str = substitute(str, '^.', '', 'g')
endfor
call add(lines, line)
return lines
endfunction
function! s:update_msg_screen() abort
if index(s:qq_channels, s:current_channel) == -1
let msgs = filter(deepcopy(s:history), 'len(v:val) == 4 && v:val[3] == s:current_channel')
let line = [line('.'),line('$')]
normal! ggdG
for msg in msgs
let name = s:get_str_with_width(msg[0], 13) " the width of the name must <= 13
let message = s:get_lines_with_width(msg[2], winwidth('$') - 16)
let first_line = repeat(' ', 13 - strwidth(name)) . name . ' ' . nr2char(9474) . ' ' . message[0]
call append(line('$'), first_line)
if len(message) > 1
for l in message[1:]
call append(line('$'), repeat(' ', 13) . ' ' . nr2char(9474) . ' ' . l)
endfor
endif
endfor
normal! gg
delete
if line[0] == line[1]
normal! G
else
exe line[0]
endif
else
let msgs = filter(deepcopy(s:history), 'v:val[1] == s:current_channel')
let line = [line('.'),line('$')]
normal! ggdG
for msg in msgs
let name = s:get_str_with_width(msg[0], 13) " the width of the name must <= 13
let message = s:get_lines_with_width(msg[2], winwidth('$') - 16)
let first_line = repeat(' ', 13 - strwidth(name)) . name . ' ' . nr2char(9474) . ' ' . message[0]
call append(line('$'), first_line)
if len(message) > 1
for l in message[1:]
call append(line('$'), repeat(' ', 13) . ' ' . nr2char(9474) . ' ' . l)
endfor
endif
endfor
normal! gg
delete
if line[0] == line[1]
normal! G
else
exe line[0]
endif
endif
redraw
call s:echon()
endfunction
function! s:next_channel() abort
let id = index(s:opened_channels, s:current_channel)
let id += 1
if id > len(s:opened_channels) - 1
let id = id - len(s:opened_channels)
endif
let s:current_channel = s:opened_channels[id]
if s:current_channel =~# '^#'
call s:send('/join ' . s:current_channel)
else
call s:send('/query ' . s:current_channel)
endif
call s:update_msg_screen()
call s:update_statusline()
endfunction
function! s:previous_channel() abort
let id = index(s:opened_channels, s:current_channel)
let id -= 1
if id < 0
let id = id + len(s:opened_channels)
endif
let s:current_channel = s:opened_channels[id]
if s:current_channel =~# '^#'
call s:send('/join ' . s:current_channel)
else
call s:send('/query ' . s:current_channel)
endif
call s:update_msg_screen()
call s:update_statusline()
endfunction
function! s:parser_input(str) abort
if a:str !=# ''
call add(s:input_history, a:str)
endif
if a:str =~# '^/quit\s*$'
let s:quit_qq_win = 1
let s:last_channel = s:current_channel
let s:current_channel = ''
if executable('fcitx-remote')
let s:last_channel_input_methon = system('fcitx-remote')
endif
elseif a:str ==# '/wc'
let cid = index(s:opened_channels, s:current_channel)
if cid == -1
elseif cid == len(s:opened_channels) - 1
call remove(s:opened_channels, cid)
call s:send('/WINDOW CLOSE')
let s:current_channel = get(s:opened_channels, cid - 1, '')
else
call remove(s:opened_channels, cid)
call s:send('/WINDOW CLOSE')
let s:current_channel = get(s:opened_channels, cid, '')
endif
call s:update_statusline()
call s:update_msg_screen()
redraw
elseif a:str =~# '^/join'
call s:send(a:str)
let s:current_channel = '#' . split(a:str, '#')[1]
if index(s:opened_channels, s:current_channel) == -1
call add(s:opened_channels, s:current_channel)
endif
call s:update_statusline()
call s:update_msg_screen()
redraw
elseif a:str =~# '^/query\ \+.\+'
call s:send(a:str)
let s:current_channel = substitute(a:str, '^/query\ \+', '', 'g')
if index(s:opened_channels, s:current_channel) == -1
call add(s:opened_channels, s:current_channel)
endif
call s:update_statusline()
call s:update_msg_screen()
redraw
elseif a:str =~# '^/msg\ \+'
call s:send(a:str)
let ch = matchstr(substitute(a:str, '^/msg\ \+', '', 'g'), '^[^\ .]*' )
if index(s:opened_channels, ch) == -1
if index(s:qq_channels, ch) != -1 || index(s:friends, ['我的好友', ch]) != -1
call add(s:opened_channels, ch)
call s:update_statusline()
redraw
endif
endif
elseif a:str !~# '^/.*'
call s:send(a:str)
endif
endfunction
function! s:update_statusline() abort
let st = ''
for ch in s:opened_channels
let ch = substitute(ch, ' ', '\ ', 'g')
if ch == s:current_channel
if has_key(s:unread_msg_num, s:current_channel)
call remove(s:unread_msg_num, s:current_channel)
endif
let st .= '%#VimQQ1#[' . ch . ']'
if index(s:opened_channels, ch) == len(s:opened_channels) - 1
let st .= '%#VimQQ5#' . s:st_sep
elseif get(s:unread_msg_num, s:opened_channels[index(s:opened_channels, ch) + 1], 0) > 0
let st .= '%#VimQQ6#' . s:st_sep
else
let st .= '%#VimQQ7#' . s:st_sep
endif
else
let n = get(s:unread_msg_num, ch, 0)
if n > 0
let st .= '%#VimQQ2#[' . ch . '(' . n . 'new)]'
if index(s:opened_channels, ch) == len(s:opened_channels) - 1
let st .= '%#VimQQ8#' . s:st_sep
elseif get(s:unread_msg_num, s:opened_channels[index(s:opened_channels, ch) + 1], 0) > 0
\ && s:opened_channels[index(s:opened_channels, ch) + 1] !=# s:current_channel
let st .= '%#VimQQ11#' . s:st_sep
elseif s:opened_channels[index(s:opened_channels, ch) + 1] ==# s:current_channel
let st .= '%#VimQQ9#' . s:st_sep
else
let st .= '%#VimQQ10#' . s:st_sep
endif
else
let st .= '%#VimQQ3#[' . ch . ']'
if index(s:opened_channels, ch) == len(s:opened_channels) - 1
let st .= '%#VimQQ12#' . s:st_sep
elseif get(s:unread_msg_num, s:opened_channels[index(s:opened_channels, ch) + 1], 0) > 0
\ && s:opened_channels[index(s:opened_channels, ch) + 1] !=# s:current_channel
let st .= '%#VimQQ14#' . s:st_sep
elseif s:opened_channels[index(s:opened_channels, ch) + 1] ==# s:current_channel
let st .= '%#VimQQ15#' . s:st_sep
else
let st .= '%#VimQQ13#' . s:st_sep
endif
endif
endif
endfor
let st .= '%#VimQQ4# '
exe 'set statusline=' . st
endfunction
fu! s:windowsinit() abort
" option
setl fileformat=unix
setl fileencoding=utf-8
setl iskeyword=@,48-57,_
setl noreadonly
setl buftype=nofile
setl bufhidden=wipe
setl noswapfile
setl nobuflisted
setl nolist
setl nonumber
setl norelativenumber
setl wrap
setl winfixwidth
setl winfixheight
setl textwidth=0
setl nospell
setl nofoldenable
endf
" public api
function! chat#qq#ViewLog(...) abort
let nr = str2nr(get(a:000, 0, -1))
tabnew +setl\ nobuflisted
nnoremap <buffer><silent> q :bd!<CR>
for msg in s:server_log
call append(line('$'), msg)
endfor
if nr != -1 && nr < len(s:server_log)
exe len(s:server_log) - nr
endif
endfunction
" disable indentline in msg window
let g:indentLine_bufNameExclude = get(g:, 'indentLine_bufNameExclude', [])
if index(g:indentLine_bufNameExclude, s:name) == -1
call add(g:indentLine_bufNameExclude, s:name)
endif
let &cpoptions = s:save_cpo
unlet s:save_cpo

View File

@ -0,0 +1,684 @@
scriptencoding utf-8
let s:save_cpo = &cpoptions
set cpoptions&vim
" Debug
let s:server_log = []
let s:run_script = "
\ use local::lib;\n
\ use Mojo::Weixin;\n
\ my $client = Mojo::Weixin->new();\n
\ $client->load('ShowMsg');\n
\ $client->load('IRCShell',data=>{\n
\ listen=>[ {host=>'127.0.0.1',port=>6668},],\n
\ load_friend=>1,\n
\ });\n
\ $client->log->handle(\*STDOUT);\n
\ $client->run();\n
\ "
let s:run_job_id = 0
let s:irssi_job_id = 0
let s:feh_code_id = 0
let s:channels = []
let s:irssi_commands = ['/join', '/query', '/list', '/quit']
let s:history = []
let s:current_channel = ''
let s:last_channel = ''
let s:friends = [] " each item is ['channel','nickname']
let s:input_history = []
let s:complete_num = 0
let s:complete_input_history_num = [0,0]
let s:opened_channels = []
let s:irssi_log = []
let s:unread_msg_num = {}
let s:st_sep = ''
function! s:jobstart(...) abort
if has('nvim')
if a:0 == 1
return jobstart(a:1)
elseif a:0 == 2
return jobstart(a:1, a:2)
endif
elseif exists('*job#start') && !has('nvim')
endif
endfunction
function! s:jobstop(id) abort
if has('nvim')
call jobstop(a:id)
elseif exists('*job#stop') && !has('nvim')
endif
endfunction
function! s:jobsend(id,data) abort
if has('nvim')
if type(a:data) == type('')
let data = [a:data, '']
elseif type(a:data) == type([]) && a:data[-1] !=# ''
let data = a:data + ['']
else
let data = a:data
endif
call jobsend(a:id, data)
elseif exists('*job#send') && !has('nvim')
call job#send(a:id, a:data)
endif
endfunction
function! s:feh_code(png) abort
call s:stop_feh()
let s:feh_code_id = s:jobstart(['feh', a:png])
endfunction
function! s:stop_feh() abort
if s:feh_code_id != 0
call s:jobstop(s:feh_code_id)
let s:feh_code_id =0
endif
endfunction
function! s:irssi_handler(id, data, event) abort
if a:event ==# 'exit'
let s:irssi_job_id = 0
elseif a:event ==# 'stderr'
call add(s:irssi_log, ['stderr', a:data])
elseif a:event ==# 'stdout'
call add(s:irssi_log, ['stdout', a:data])
endif
endfunction
function! s:start_irssi() abort
if s:irssi_job_id == 0
let argv = ['irssi','-c', '127.0.0.1', '-p', '6668']
let s:irssi_job_id = s:jobstart(argv, {
\ 'on_stdout': function('s:irssi_handler'),
\ 'on_stderr': function('s:irssi_handler'),
\ 'on_exit': function('s:irssi_handler'),
\ })
endif
endfunction
function! s:handler_stdout_data(data) abort
if !empty(a:data)
call add(s:server_log, a:data)
endif
if match(a:data, '二维码已下载到本地\[ /tmp/mojo_weixin_qrcode_default.jpg') != -1
let png = matchstr(a:data, '/tmp/mojo_weixin_qrcode_default.jpg')
call s:feh_code(png)
elseif matchstr(a:data, '\[\d\d/\d\d/\d\d \d\d\:\d\d\:\d\d\] \[info\] 微信登录成功') !=# ''
call s:stop_feh()
elseif matchstr(a:data,'频道\ #.*\ 已创建') !=# ''
let ch = matchstr(a:data,'#[^\ .]*')
if index(s:channels, ch) == -1
call add(s:channels, ch)
endif
elseif matchstr(a:data, '\[\d\d/\d\d/\d\d \d\d\:\d\d\:\d\d\] \[info\] \[.*\:虚拟用户\] 加入频道 #我的好友') !=# ''
" [16/10/31 20:06:09] [info] [nullptr:虚拟用户] 加入频道 #我的好友
" [28:-42]
let friend = ['我的好友',a:data[28:-42]]
if index(s:friends, friend) == -1
call add(s:friends, friend)
endif
elseif matchstr(a:data, '\[\d\d/\d\d/\d\d \d\d\:\d\d\:\d\d\] \[群消息\]') !=# ''
" send:[16/10/22 18:26:58] [群消息] 我->Vim/exVim 开发讨论群 : 测试补全
" start index 32
if matchstr(a:data, '[^\ .]*->[^\ .]*') !=# ''
let idx1 = match(a:data, '->')
let idx2 = match(a:data, ' : ')
let msg = [ a:data[32:idx1-1], '#' . a:data[idx1+2:idx2-1], a:data[idx2+3:]]
let msg[1] = substitute(msg[1], '[\ !@&]', '', 'g')
call add(s:history, msg)
let friend = [msg[1], msg[0]]
if index(s:friends, friend) == -1
call add(s:friends, friend)
endif
if msg[1] == s:current_channel
call s:update_msg_screen()
endif
" get:[16/10/22 18:26:58] [群消息] 灰灰|Vim/exVim 开发讨论群 : 测试补全
elseif matchstr(a:data, '[^\ .]*|[^\ .]*') !=# ''
let idx1 = match(a:data, '|')
let idx2 = match(a:data, ' : ')
let msg = [ a:data[32:idx1-1], '#' .a:data[idx1+1:idx2-1], a:data[idx2+3:]]
let msg[1] = substitute(msg[1], '[\ !@&]', '', 'g')
call add(s:history, msg)
let friend = [msg[1], msg[0]]
if index(s:friends, friend) == -1
call add(s:friends, friend)
endif
if msg[1] == s:current_channel
call s:update_msg_screen()
elseif index(s:opened_channels, msg[1]) != -1 && s:current_channel !=# msg[1]
let n = get(s:unread_msg_num, msg[1], 0)
let n += 1
if has_key(s:unread_msg_num, msg[1])
call remove(s:unread_msg_num, msg[1])
endif
call extend(s:unread_msg_num, {msg[1] : n})
if s:current_channel !=# ''
call s:update_statusline()
endif
endif
endif
elseif matchstr(a:data, '\[\d\d/\d\d/\d\d \d\d\:\d\d\:\d\d\] \[好友消息\]') !=# ''
" send: [16/10/22 14:25:56] [好友消息] 我->老婆 : 1
if matchstr(a:data, '[^\ .]*->[^\ .]*') !=# ''
let msg = split(matchstr(a:data, '[^\ .]*->[^\ .]*'), '->')
let f = msg[1]
let msg[1] = ''
call add(msg, substitute(a:data,'\[\d\d/\d\d/\d\d \d\d\:\d\d\:\d\d\] \[好友消息\].*->[^\ .]*\ \:\ ','','g'))
call add(msg, f)
call add(s:history, msg)
let friend = ['我的好友',f]
if index(s:friends, friend) == -1
call add(s:friends, friend)
endif
if f == s:current_channel
call s:update_msg_screen()
endif
" get: [16/10/22 14:25:59] [好友消息] 老婆|我的好友 : 测试
elseif matchstr(a:data, '[^\ .]*|[^\ .]*') !=# ''
let msg = split(matchstr(a:data, '[^\ .]*|[^\ .]*'), '|')
let f = msg[0]
let msg[1] = ''
call add(msg, substitute(a:data,'\[\d\d/\d\d/\d\d \d\d\:\d\d\:\d\d\] \[好友消息\].*|[^\ .]*\ \:\ ','','g'))
call add(msg, f)
call add(s:history, msg)
let friend = ['我的好友',f]
if index(s:friends, friend) == -1
call add(s:friends, friend)
endif
if f == s:current_channel
call s:update_msg_screen()
elseif index(s:opened_channels, msg[3]) != -1 && s:current_channel !=# msg[3]
let n = get(s:unread_msg_num, msg[3], 0)
let n += 1
if has_key(s:unread_msg_num, msg[3])
call remove(s:unread_msg_num, msg[3])
endif
call extend(s:unread_msg_num, {msg[3] : n})
if s:current_channel !=# ''
call s:update_statusline()
endif
elseif index(s:opened_channels, msg[3]) == -1
let n = get(s:unread_msg_num, msg[3], 0)
let n += 1
if has_key(s:unread_msg_num, msg[3])
call remove(s:unread_msg_num, msg[3])
endif
call extend(s:unread_msg_num, {msg[3] : n})
call add(s:opened_channels, msg[3])
if s:current_channel !=# ''
call s:update_statusline()
endif
endif
endif
endif
endfunction
function! Test(str) abort
exe a:str
endfunction
function! s:start_handler(id, data, event) abort
if a:event ==# 'stdout'
if type(a:data) == type([])
for a in a:data
call s:handler_stdout_data(a)
endfor
elseif type(a:data) == type('')
call s:handler_stdout_data(a:data)
else
endif
elseif a:event ==# 'stderr'
elseif a:event ==# 'exit'
endif
endfunction
function! chat#weixin#start() abort
let argv = ['perl', '-e', s:run_script]
if s:run_job_id == 0
let s:run_job_id = s:jobstart(argv, {
\ 'on_stdout': function('s:start_handler'),
\ 'on_stderr': function('s:start_handler'),
\ 'on_exit': function('s:start_handler'),
\ })
endif
endfunction
function! s:send(...) abort
if a:0 > 0
if s:irssi_job_id == 0
call s:start_irssi()
endif
call s:jobsend(s:irssi_job_id, a:1)
endif
endfunction
let s:name = '__VimWeixin__'
function! chat#weixin#OpenMsgWin() abort
if bufwinnr('s:name') < 0
if bufnr('s:name') != -1
exe 'silent! botright split ' . '+b' . bufnr(s:name)
else
exe 'silent! botright split ' . s:name
endif
else
exec bufwinnr('s:name') . 'wincmd w'
endif
setl modifiable
let s:c_base = '>>>'
let s:c_begin = ''
let s:c_char = ''
let s:c_end = ''
call s:windowsinit()
redraw
if s:last_channel !=# ''
if s:last_channel =~# '^#'
call s:send('/join ' . s:last_channel)
else
call s:send('/query ' . s:last_channel)
endif
let s:current_channel = s:last_channel
call s:update_statusline()
call s:update_msg_screen()
endif
call s:echon()
while get(s:, 'quit_msg_win', 0) == 0
let nr = getchar()
if nr != 9
let s:complete_num = 0
endif
if nr !=# "\<Up>" && nr !=# "\<Down>"
let s:complete_input_history_num = [0,0]
endif
if nr == 13 "<cr> 执行命令,或发送消息
call s:parser_input(s:c_begin . s:c_char . s:c_end)
let s:c_begin = ''
let s:c_char = ''
let s:c_end = ''
elseif nr ==# "\<M-Left>" "<Alt>+<Left> 移动到左边一个聊天窗口
call s:previous_channel()
elseif nr ==# "\<M-Right>" "<Alt>+<Right> 移动到右边一个聊天窗口
call s:next_channel()
elseif nr ==# "\<Right>" || nr == 6 "<Right> 向右移动光标
let s:c_begin = s:c_begin . s:c_char
let s:c_char = matchstr(s:c_end, '^.')
let s:c_end = substitute(s:c_end, '^.', '', 'g')
elseif nr ==# "\<Left>" || nr == 2 "<Left> 向左移动光标
if s:c_begin !=# ''
let s:c_end = s:c_char . s:c_end
let s:c_char = matchstr(s:c_begin, '.$')
let s:c_begin = substitute(s:c_begin, '.$', '', 'g')
endif
elseif nr ==# "\<Home>" || nr == 1 "<Home> 将光标移动到行首
let s:c_end = substitute(s:c_begin . s:c_char . s:c_end, '^.', '', 'g')
let s:c_char = matchstr(s:c_begin, '^.')
let s:c_begin = ''
elseif nr ==# "\<End>" || nr == 5 "<End> 将光标移动到行末
let s:c_begin = s:c_begin . s:c_char . s:c_end
let s:c_char = ''
let s:c_end = ''
elseif nr ==# "\<M-x>" "<Alt>+x 关闭聊天窗口
let s:quit_msg_win = 1
let s:last_channel = s:current_channel
let s:current_channel = ''
elseif nr == 8 || nr ==# "\<bs>" " ctrl+h or <bs> delete last char
let s:c_begin = substitute(s:c_begin,'.$','','g')
elseif nr == 23 " ctrl+w delete last word
let s:c_begin = substitute(s:c_begin,'[^\ .*]\+\s*$','','g')
elseif nr == 11 " ctrl+k delete the chars from cursor to the end
let s:c_char = ''
let s:c_end = ''
elseif nr ==# "\<M-f>" " Alt + f :按单词前移(右向)
if matchstr(s:c_end, '^\ *[^\ .]\+') !=# ''
let s:c_begin = s:c_begin . s:c_char . matchstr(s:c_end, '^\ *[^\ .]\+')
let s:c_end = substitute(s:c_end, '^\ *[^\ .]\+', '', 'g')
let s:c_char = matchstr(s:c_end, '^.')
let s:c_end = substitute(s:c_end, '^.', '', 'g')
endif
elseif nr ==# "\<M-b>"
let s:c_end = matchstr(s:c_begin, '[^\ .]\+\s*$') . s:c_char . s:c_end
let s:c_begin = substitute(s:c_begin, '[^\ .]\+\s*$', '', 'g')
let s:c_char = matchstr(s:c_end, '^.')
let s:c_end = substitute(s:c_end, '^.', '', 'g')
elseif nr ==# "\<M-d>" " Alt + d 从光标处删除至词尾
let s:c_end = s:c_char . s:c_end
let s:c_end = substitute(s:c_end, '^\s*[^\ .]*', '', 'g')
let s:c_char = matchstr(s:c_end, '^.')
let s:c_end = substitute(s:c_end, '^.', '', 'g')
elseif nr == 4 " ctrl+d delete the char under the cursor
let s:c_char = matchstr(s:c_end, '^.')
let s:c_end = substitute(s:c_end, '^.', '', 'g')
elseif nr == 21 " ctrl+u clean the message
let s:c_begin = ''
elseif nr == 9 " use <tab> complete str
if s:complete_num == 0
let complete_base = s:c_begin
else
let s:c_begin = complete_base
endif
let s:c_begin = s:complete(complete_base, s:complete_num)
let s:complete_num += 1
elseif nr == 47 " if type / and str is none, switch to en method
if s:c_begin ==# '' && s:c_char ==# '' && s:c_end ==# '' && executable('fcitx-remote')
call system('fcitx-remote -c')
endif
let s:c_begin .= nr2char(nr)
elseif nr ==# "\<PageUp>"
let l = line('.') - winheight('$')
if l < 0
exe 0
else
exe l
endif
elseif nr ==# "\<PageDown>"
exe line('.') + winheight('$')
elseif nr ==# "\<Up>"
if s:complete_input_history_num == [0,0]
let complete_input_history_base = s:c_begin
let s:c_char = ''
let s:c_end = ''
else
let s:c_begin = complete_input_history_base
endif
let s:complete_input_history_num[0] += 1
let s:c_begin = s:complete_input_history(complete_input_history_base, s:complete_input_history_num)
elseif nr ==# "\<Down>"
if s:complete_input_history_num == [0,0]
let complete_input_history_base = s:c_begin
let s:c_char = ''
let s:c_end = ''
else
let s:c_begin = complete_input_history_base
endif
let s:complete_input_history_num[1] += 1
let s:c_begin = s:complete_input_history(complete_input_history_base, s:complete_input_history_num)
else
let s:c_begin .= nr2char(nr)
endif
call s:echon()
endwhile
setl nomodifiable
exe 'bd ' . bufnr(s:name)
let s:quit_msg_win = 0
normal! :
if executable('fcitx-remote')
call system('fcitx-remote -c') " switch 2 en
else
doautocmd InsertEnter
doautocmd InsertLeave
endif
endf
function! s:complete(str, num) abort
if a:str =~# '^/[a-z]*$'
let rsl = filter(copy(s:irssi_commands), "v:val =~# a:str .'[^\ .]*'")
if len(rsl) > 0
return rsl[a:num % len(rsl)] . ' '
else
return a:str
endif
elseif a:str =~# '/join\s\+#\?$'
if len(s:channels) > 0
return a:str[-1:] ==# '#' ? a:str[:-2] . s:channels[0] : a:str . s:channels[0]
else
return a:str
endif
elseif a:str =~# '/join\s\+#.\+$'
let results = filter(deepcopy(s:channels), "v:val =~# '" . substitute(a:str , '^/join\s\+', '', 'g') . "'")
if len(results) > 0
return '/join ' . results[a:num % len(results)]
endif
return a:str
elseif index(s:channels, s:current_channel) != -1 && a:str !~# '^/query'
let names = filter(deepcopy(s:friends), "v:val[0] ==# s:current_channel && v:val[1] =~# '^' . a:str")
if len(names) > 0
return names[a:num % len(names)][1] . ': '
endif
return a:str
elseif a:str =~# '^/query\s\+.\+'
let n_base = substitute(a:str, '^/query\s\+', '', 'g')
let names = filter(deepcopy(s:friends), "v:val[0] ==# '我的好友' && v:val[1] =~# '^' . n_base")
if len(names) > 0
return '/query ' . names[a:num % len(names)][1]
endif
return a:str
else
return a:str
endif
endfunction
function! s:complete_input_history(str,num) abort
let results = filter(copy(s:input_history), "v:val =~# '^' . a:str")
if len(results) > 0
call add(results, a:str)
let index = ((len(results) - 1) - a:num[0] + a:num[1]) % len(results)
return results[index]
else
return a:str
endif
endfunction
function! s:echon() abort
redraw
echohl Comment | echon s:c_base
echohl None | echon s:c_begin
echohl Wildmenu | echon s:c_char
echohl None | echon s:c_end
endfunction
function! s:update_msg_screen() abort
if index(s:channels, s:current_channel) == -1
let msgs = filter(deepcopy(s:history), 'len(v:val) == 4 && v:val[3] == s:current_channel')
let line = [line('.'),line('$')]
normal! ggdG
for msg in msgs
call append(line('$'), msg[0] . repeat(' ', 13 - strwidth(msg[0])) . ' | ' . msg[2])
endfor
if line[0] == line[1]
normal! G
else
exe line[0]
endif
else
let msgs = filter(deepcopy(s:history), 'v:val[1] == s:current_channel')
let line = [line('.'),line('$')]
normal! ggdG
for msg in msgs
call append(line('$'), msg[0] . repeat(' ', 13 - strwidth(msg[0])) . ' | ' . msg[2])
endfor
if line[0] == line[1]
normal! G
else
exe line[0]
endif
endif
redraw
call s:echon()
endfunction
function! s:next_channel() abort
let id = index(s:opened_channels, s:current_channel)
let id += 1
if id > len(s:opened_channels) - 1
let id = id - len(s:opened_channels)
endif
let s:current_channel = s:opened_channels[id]
if s:current_channel =~# '^#'
call s:send('/join ' . s:current_channel)
else
call s:send('/query ' . s:current_channel)
endif
call s:update_msg_screen()
call s:update_statusline()
endfunction
function! s:previous_channel() abort
let id = index(s:opened_channels, s:current_channel)
let id -= 1
if id < 0
let id = id + len(s:opened_channels)
endif
let s:current_channel = s:opened_channels[id]
if s:current_channel =~# '^#'
call s:send('/join ' . s:current_channel)
else
call s:send('/query ' . s:current_channel)
endif
call s:update_msg_screen()
call s:update_statusline()
endfunction
function! s:parser_input(str) abort
if a:str !=# ''
call add(s:input_history, a:str)
endif
if a:str =~# '^/quit\s*$'
let s:quit_msg_win = 1
let s:last_channel = s:current_channel
let s:current_channel = ''
elseif a:str ==# '/wc'
let cid = index(s:opened_channels, s:current_channel)
if cid == -1
elseif cid == len(s:opened_channels) - 1
call remove(s:opened_channels, cid)
call s:send('/WINDOW CLOSE')
let s:current_channel = get(s:opened_channels, cid - 1, '')
else
call remove(s:opened_channels, cid)
call s:send('/WINDOW CLOSE')
let s:current_channel = get(s:opened_channels, cid, '')
endif
call s:update_statusline()
call s:update_msg_screen()
redraw
elseif a:str =~# '^/join'
call s:send(a:str)
let s:current_channel = '#' . split(a:str, '#')[1]
if index(s:opened_channels, s:current_channel) == -1
call add(s:opened_channels, s:current_channel)
endif
call s:update_statusline()
call s:update_msg_screen()
redraw
elseif a:str =~# '^/query\ \+.\+'
call s:send(a:str)
let s:current_channel = substitute(a:str, '^/query\ \+', '', 'g')
if index(s:opened_channels, s:current_channel) == -1
call add(s:opened_channels, s:current_channel)
endif
call s:update_statusline()
call s:update_msg_screen()
redraw
elseif a:str !~# '^/.*'
call s:send(a:str)
endif
endfunction
function! s:update_statusline() abort
" current channel
hi! VimQQ1 ctermbg=003 ctermfg=Black guibg=#fabd2f guifg=#282828
" channel with new msg
hi! VimQQ2 ctermbg=005 ctermfg=Black guibg=#b16286 guifg=#282828
" normal channel
hi! VimQQ3 ctermbg=007 ctermfg=Black guibg=#8ec07c guifg=#282828
" end
hi! VimQQ4 ctermbg=243 guibg=#7c6f64
" current channel + end
hi! VimQQ5 guibg=#7c6f64 guifg=#fabd2f
" current channel + new msg channel
hi! VimQQ6 guibg=#b16286 guifg=#fabd2f
" current channel + normal channel
hi! VimQQ7 guibg=#8ec07c guifg=#fabd2f
" new msg channel + end
hi! VimQQ8 guibg=#7c6f64 guifg=#b16286
" new msg channel + current channel
hi! VimQQ9 guibg=#fabd2f guifg=#b16286
" new msg channel + normal channel
hi! VimQQ10 guibg=#8ec07c guifg=#b16286
" new msg channel + new msg channel
hi! VimQQ11 guibg=#b16286 guifg=#b16286
" normal channel + end
hi! VimQQ12 guibg=#7c6f64 guifg=#8ec07c
" normal channel + normal channel
hi! VimQQ13 guibg=#8ec07c guifg=#8ec07c
" normal channel + new msg channel
hi! VimQQ14 guibg=#b16286 guifg=#8ec07c
" normal channel + current channel
hi! VimQQ15 guibg=#fabd2f guifg=#8ec07c
let st = ''
for ch in s:opened_channels
let ch = substitute(ch, ' ', '\ ', 'g')
if ch == s:current_channel
if has_key(s:unread_msg_num, s:current_channel)
call remove(s:unread_msg_num, s:current_channel)
endif
let st .= '%#VimQQ1#[' . ch . ']'
if index(s:opened_channels, ch) == len(s:opened_channels) - 1
let st .= '%#VimQQ5#' . s:st_sep
elseif get(s:unread_msg_num, s:opened_channels[index(s:opened_channels, ch) + 1], 0) > 0
let st .= '%#VimQQ6#' . s:st_sep
else
let st .= '%#VimQQ7#' . s:st_sep
endif
else
let n = get(s:unread_msg_num, ch, 0)
if n > 0
let st .= '%#VimQQ2#[' . ch . '(' . n . 'new)]'
if index(s:opened_channels, ch) == len(s:opened_channels) - 1
let st .= '%#VimQQ8#' . s:st_sep
elseif get(s:unread_msg_num, s:opened_channels[index(s:opened_channels, ch) + 1], 0) > 0
\ && s:opened_channels[index(s:opened_channels, ch) + 1] !=# s:current_channel
let st .= '%#VimQQ11#' . s:st_sep
elseif s:opened_channels[index(s:opened_channels, ch) + 1] ==# s:current_channel
let st .= '%#VimQQ9#' . s:st_sep
else
let st .= '%#VimQQ10#' . s:st_sep
endif
else
let st .= '%#VimQQ3#[' . ch . ']'
if index(s:opened_channels, ch) == len(s:opened_channels) - 1
let st .= '%#VimQQ12#' . s:st_sep
elseif get(s:unread_msg_num, s:opened_channels[index(s:opened_channels, ch) + 1], 0) > 0
\ && s:opened_channels[index(s:opened_channels, ch) + 1] !=# s:current_channel
let st .= '%#VimQQ14#' . s:st_sep
elseif s:opened_channels[index(s:opened_channels, ch) + 1] ==# s:current_channel
let st .= '%#VimQQ15#' . s:st_sep
else
let st .= '%#VimQQ13#' . s:st_sep
endif
endif
endif
endfor
let st .= '%#VimQQ4# '
exe 'set statusline=' . st
redraw
call s:echon()
endfunction
fu! s:windowsinit() abort
" option
setl fileformat=unix
setl fileencoding=utf-8
setl iskeyword=@,48-57,_
setl noreadonly
setl buftype=nofile
setl bufhidden=wipe
setl noswapfile
setl nobuflisted
setl nolist
setl nonumber
setl wrap
setl winfixwidth
setl winfixheight
setl textwidth=0
setl nospell
setl nofoldenable
endf
let &cpoptions = s:save_cpo
unlet s:save_cpo