server.vim 10.8 KB
Newer Older
C
Cosmin Popescu 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
"============================================================================"
"
"  Vim SQL Workbench/J Implementation
"
"  Copyright (c) Cosmin Popescu
"
"  Author:      Cosmin Popescu <cosminadrianpopescu at gmail dot com>
"  Version:     1.00 (2015-01-08)
"  Requires:    Vim 7
"  License:     GPL
"
"  Description:
"
"  Provides SQL database access to any DBMS supported by SQL Workbench/J. The
"  only dependency is SQL Workbench/J. Also includes powefull intellisense
"  autocomplete based on the current selected database
"
"============================================================================"

let s:current_file = expand('<sfile>:p:h')
C
Cosmin Popescu 已提交
21
let s:nvim = has("nvim")
C
Cosmin Popescu 已提交
22 23 24
let s:channel_handlers = {}
let s:pattern_prompt_begin = '\v^([a-zA-Z_0-9\.]+(\@[a-zA-Z_0-9\/\-]+)*\>[ \s\t]*)+'
let s:pattern_prompt = s:pattern_prompt_begin . '$'
C
Cosmin Popescu 已提交
25
let s:pattern_wait_input = '\v^([a-zA-Z_][a-zA-Z0-9_]*( \[[^\]]+\])?: |([^\>]+\> )?([^\>]+\> )*Username|([^\>]+\> )*Password: |([^\>]+\>[ ]+)?Do you want to run the command [A-Z]+\? \(Yes\/No\/All\)[ ]+)$'
C
Cosmin Popescu 已提交
26
let s:params_history = []
C
Cosmin Popescu 已提交
27
let s:pattern_new_connection = '\v^Connection to "([^"]+)" successful$'
C
Cosmin Popescu 已提交
28
let s:timer = {'id': -1, 'sec' : 0}
C
Cosmin Popescu 已提交
29

C
Cosmin Popescu 已提交
30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56
function! sw#server#get_channel_pid(vid, channel, message)
    for handler_item in items(s:channel_handlers)
        let handler = handler_item[1]
        if handler.vid == a:vid
            for line in split(a:message, '\v[\r\n]')
                let pattern = '\v^([0-9]+) .*vid\=' . a:vid . '$'
                if line =~ pattern
                    let handler.pid = substitute(line, pattern, '\1', 'g')
                    return
                endif
            endfor
        endif
    endfor
endfunction

function! s:get_channel_pid(channel)
    let cmd = 'jps -m'
    if !s:nvim
        let Func = function('sw#server#get_channel_pid', [s:channel_handlers[a:channel].vid])
        let job = job_start(cmd, {'in_mode': 'raw', 'out_mode': 'raw'})
        let channel = job_getchannel(job)
        call ch_setoptions(channel, {'callback': Func})
    else
        let job = jobstart(cmd, {'on_stdout': function('sw#server#nvim_get_channel_pid')})
    endif
endfunction

C
Cosmin Popescu 已提交
57 58
function! s:log_init(channel)
    if g:sw_log_to_file
C
Cosmin Popescu 已提交
59
        let s:channel_handlers[a:channel].log = g:sw_tmp . '/' . sw#servername() . '-' . substitute(fnamemodify(bufname('%'), ':t'), '\.', '-', 'g')
C
Cosmin Popescu 已提交
60 61 62
    else
        let s:channel_handlers[a:channel].log = ''
    endif
C
Cosmin Popescu 已提交
63 64
endfunction

C
Cosmin Popescu 已提交
65 66 67 68 69 70 71
function! s:log_channel(channel, txt)
    if g:sw_log_to_file
        let file = s:channel_handlers[a:channel].log
        let mode = filereadable(file) ? 'ab' : 'wb'
        call writefile(split(a:txt, "\n"), file, mode)
    else
        let s:channel_handlers[a:channel].log .= a:txt
C
Cosmin Popescu 已提交
72
    endif
C
Cosmin Popescu 已提交
73
endfunction
C
Cosmin Popescu 已提交
74

C
Cosmin Popescu 已提交
75 76 77 78
function! sw#server#channel_log(channel)
    return s:channel_handlers[a:channel].log
endfunction

C
Cosmin Popescu 已提交
79 80 81 82 83 84 85 86
function! sw#server#nvim_handle_message(job, lines, ev)
    if a:ev == 'stdout'
        let msg = ''
        for line in a:lines
            let msg .= (msg == '' ? '' : "\n") . line
        endfor

        call sw#server#handle_message(a:job, msg)
C
Cosmin Popescu 已提交
87 88
    elseif a:ev == 'exit'
        call sw#server#disconnect_buffer(a:job)
C
Cosmin Popescu 已提交
89 90 91
    endif
endfunction

C
Cosmin Popescu 已提交
92 93 94 95 96 97
function! sw#server#prompt_for_value(channel, line, timer_id)
    let value = input('SQL Workbench/J is asking for input for ' . a:line . ' ', '')
    call add(s:params_history, {'prompt': a:line, 'value': value})
    call ch_sendraw(a:channel, value . "\n")
endfunction

C
Cosmin Popescu 已提交
98
function! sw#server#handle_message(channel, msg)
C
Cosmin Popescu 已提交
99 100 101
    if has_key(s:channel_handlers[a:channel], 'pid') && s:channel_handlers[a:channel].pid == ''
        call s:get_channel_pid(a:channel)
    endif
C
Cosmin Popescu 已提交
102 103 104 105 106 107 108 109 110 111 112 113
    call s:log_channel(a:channel, a:msg)
    let lines = split(substitute(a:msg, "\r", "", 'g'), "\n")
    let got_prompt = 0
    let max_length = 0
    let text = ''
    for line in lines
        let line = substitute(line, '\v^(\.\.\> )*', '', 'g')
        let text .= (text == '' ? '' : "\n") . substitute(line, s:pattern_prompt_begin, '', 'g')
        if line =~ s:pattern_prompt
            let got_prompt = 1
        endif
        if line =~ s:pattern_wait_input && !(line =~ '\v^Catalog: $') && !(line =~ '\v^Schema: $')
C
Cosmin Popescu 已提交
114
            if s:nvim
C
Cosmin Popescu 已提交
115 116
                let value = input('SQL Workbench/J is asking for input for ' . line . ' ', '')
                call add(s:params_history, {'prompt': line, 'value': value})
C
Cosmin Popescu 已提交
117 118
                call jobsend(b:sw_channel, value . "\n")
            else
C
Cosmin Popescu 已提交
119 120 121
                let Func = function('sw#server#prompt_for_value', [a:channel, line])
                let got_prompt = 1
                let timer_id = timer_start(500, Func)
C
Cosmin Popescu 已提交
122
            endif
C
Cosmin Popescu 已提交
123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145
        endif

        if line =~ s:pattern_new_connection 
            let s:channel_handlers[a:channel].current_url = substitute(line, s:pattern_new_connection, '\1', 'g')
        endif
    endfor
    let s:channel_handlers[a:channel].text .= text . "\n"
    if got_prompt
        let type = s:channel_handlers[a:channel].type
        if (type == 'sqlwindow')
            if s:channel_handlers[a:channel].tmp_handler != ''
                let Func = function(s:channel_handlers[a:channel].tmp_handler)
                call Func(s:channel_handlers[a:channel].text)
                let s:channel_handlers[a:channel].tmp_handler = ''
            else
                call sw#sqlwindow#message_handler(a:channel, s:channel_handlers[a:channel].text)
            endif
        elseif (type == 'dbexplorer')
            call sw#dbexplorer#message_handler(a:channel, s:channel_handlers[a:channel].text)
        endif

        let s:channel_handlers[a:channel].text = ''
        call s:init_timer()
C
Cosmin Popescu 已提交
146
    endif
C
Cosmin Popescu 已提交
147
endfunction
C
Cosmin Popescu 已提交
148

C
Cosmin Popescu 已提交
149
function! s:start_sqlwb(type)
C
Cosmin Popescu 已提交
150 151
    let vid = substitute(v:servername, '\v\/', '-', 'g') . sw#generate_unique_id()
    let cmd = [g:sw_exe, '-feedback=true', '-showProgress=false', '-showTiming=true', '-nosettings', '-variable=vid=' . vid]
C
Cosmin Popescu 已提交
152
    if !s:nvim
153 154 155 156 157 158
        if !filereadable(g:sw_exe)
            echom g:sw_exe . " is not readable. Make sure the setting g:sw_exe is set and the file exists."
        endif
        if match(getfperm(g:sw_exe), "r.x.*") ==# -1
            echom g:sw_exe . " is not executable. Make sure the permissions are set correctly."
        endif
C
Cosmin Popescu 已提交
159
        let job = job_start(cmd, {'in_mode': 'raw', 'out_mode': 'raw'})
C
Cosmin Popescu 已提交
160 161
        let pid = substitute(job, '\v^process ([0-9]+).*$', '\1', 'g')
        let pid = ''
C
Cosmin Popescu 已提交
162
        let channel = job_getchannel(job)
C
Cosmin Popescu 已提交
163
        call ch_setoptions(channel, {'callback': 'sw#server#handle_message', 'close_cb': 'sw#server#disconnect_buffer'})
C
Cosmin Popescu 已提交
164 165
    else
        let channel = jobstart(cmd, {'on_stdout': function('sw#server#nvim_handle_message'), 'on_stderr': function('sw#server#nvim_handle_message'), 'on_exit': function('sw#server#nvim_handle_message')})
C
Cosmin Popescu 已提交
166
        let pid = jobpid(channel)
C
Cosmin Popescu 已提交
167 168
    endif

C
Cosmin Popescu 已提交
169
    let s:channel_handlers[channel] = {'text': '', 'type': a:type, 'buffer': fnamemodify(bufname('%'), ':p'), 'current_url': '', 'tmp_handler': '', 'vid': vid, 'pid': pid}
C
Cosmin Popescu 已提交
170 171 172
    call s:log_init(channel)

    return channel
C
Cosmin Popescu 已提交
173 174
endfunction

C
Cosmin Popescu 已提交
175
function! sw#server#connect_buffer(...)
C
Cosmin Popescu 已提交
176 177 178
    let file = bufname('%')
    let command = 'e'
    if (a:0 >= 2)
C
Cosmin Popescu 已提交
179 180
        let file = a:2
        let command = a:1
C
Cosmin Popescu 已提交
181 182 183
    elseif a:0 >= 1
        let command = a:1
    endif
C
Cosmin Popescu 已提交
184

C
Cosmin Popescu 已提交
185 186
    execute command . " " . file
    call sw#session#init_section()
C
Cosmin Popescu 已提交
187

C
Cosmin Popescu 已提交
188 189 190 191 192
    if (!exists('b:sw_channel'))
        let b:sw_channel = s:start_sqlwb('sqlwindow')
    endif

    call sw#sqlwindow#open_buffer(file, command)
C
Cosmin Popescu 已提交
193 194
endfunction

C
Cosmin Popescu 已提交
195 196 197 198
function! sw#server#execute_sql(sql, ...)
    let channel = ''
    if (exists('b:sw_channel'))
        let channel = b:sw_channel
C
Cosmin Popescu 已提交
199
    endif
C
Cosmin Popescu 已提交
200 201 202 203 204 205
    let callback = ''
    if a:0 >= 2
        let channel = a:1
        let callback = a:2
    elseif a:0 >= 1
        let channel = a:1
C
Cosmin Popescu 已提交
206
    endif
C
Cosmin Popescu 已提交
207 208 209
    if !s:nvim
        if ch_status(channel) != 'open'
            call sw#display_error("The channel is not open. This means that SQL Workbench/J instance for this answer does not responsd anymore. Please do again SWSqlBufferConnect")
C
Cosmin Popescu 已提交
210 211 212
            if exists('b:sw_channel')
                unlet b:sw_channel
            endif
C
Cosmin Popescu 已提交
213 214
            return ''
        endif
C
Cosmin Popescu 已提交
215
    endif
C
Cosmin Popescu 已提交
216 217 218 219
    let text = a:sql . "\n"
    call s:log_channel(channel, text)
    if callback != ''
        let s:channel_handlers[channel].tmp_handler = callback
C
Cosmin Popescu 已提交
220
    endif
C
Cosmin Popescu 已提交
221 222 223 224 225
    if s:nvim
        call jobsend(channel, text)
    else
        call ch_sendraw(channel, text)
    endif
C
Cosmin Popescu 已提交
226 227 228
    if g:sw_command_timer
        call s:init_timer()
        let s:timer.id = timer_start(1000, 'sw#server#timer', {'repeat': -1})
C
Cosmin Popescu 已提交
229
    endif
C
Cosmin Popescu 已提交
230
endfunction
C
Cosmin Popescu 已提交
231

C
Cosmin Popescu 已提交
232
function! s:init_timer()
C
Cosmin Popescu 已提交
233
    if s:timer.id != -1
C
Cosmin Popescu 已提交
234
        call timer_stop(s:timer.id)
C
Cosmin Popescu 已提交
235
    endif
C
Cosmin Popescu 已提交
236
    let s:timer = {'id': -1, 'sec': 0}
C
Cosmin Popescu 已提交
237 238
endfunction

C
Cosmin Popescu 已提交
239 240 241
function! sw#server#timer(timer)
    let s:timer.sec += 1
    echo "Query time: " . s:timer.sec . " seconds"
C
Cosmin Popescu 已提交
242 243
endfunction

C
Cosmin Popescu 已提交
244 245 246 247 248 249 250 251 252
function! sw#server#disconnect_buffer(...)
    let channel = ''
    if (exists('b:sw_channel'))
        let channel = b:sw_channel
        unlet b:sw_channel
    endif
    if a:0
        let channel = a:1
    endif
C
Cosmin Popescu 已提交
253 254 255 256 257 258 259 260 261 262
    if (!s:nvim && ch_status(channel) == 'open') || s:nvim
        try
            call sw#server#execute_sql('exit', channel)
        catch
        endtry
    endif
    let key = substitute(channel, '\v^channel ([0-9]+).*$', 'channel \1 open', 'g')
    if has_key(s:channel_handlers, key)
        unlet s:channel_handlers[key]
    endif
C
Cosmin Popescu 已提交
263
    call s:init_timer()
C
Cosmin Popescu 已提交
264

C
Cosmin Popescu 已提交
265 266 267
    if exists('g:sw_airline_support') && g:sw_airline_support == 1
        call airline#update_statusline()
    endif
C
Cosmin Popescu 已提交
268
endfunction
C
Cosmin Popescu 已提交
269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287

function! sw#server#kill_statement(...)
    let channel = ''
    if exists('b:sw_channel')
        let channel = b:sw_channel
    endif
    if a:0
        let channel = a:1
    endif

    if has_key(s:channel_handlers, channel) && has_key(s:channel_handlers[channel], 'pid')
        let cmd = 'kill -SIGINT ' . s:channel_handlers[channel].pid
        if !s:nvim
            call job_start(cmd)
        else
            call jobstart(cmd)
        endif
    endif
endfunction
C
Cosmin Popescu 已提交
288

C
Cosmin Popescu 已提交
289 290 291 292
function! sw#server#get_buffer_url(buffer)
    for key in keys(s:channel_handlers)
        if s:channel_handlers[key]['buffer'] == a:buffer
            return s:channel_handlers[key]['current_url']
C
Cosmin Popescu 已提交
293 294
        endif
    endfor
C
Cosmin Popescu 已提交
295 296

    return ''
C
Cosmin Popescu 已提交
297 298
endfunction

C
Cosmin Popescu 已提交
299 300 301 302 303 304 305 306
function! sw#server#get_active_connections()
    let result = ''
    for key in keys(s:channel_handlers)
        let url = s:channel_handlers[key]['current_url']
        let result .= (result == '' ? '' : "\n") . s:channel_handlers[key]['buffer'] . ' - ' . (url == '' ? 'NOT CONNECTED' : url)
    endfor

    return result == '' ? 'No active sql workbench buffers' : result
C
Cosmin Popescu 已提交
307
endfunction
C
Cosmin Popescu 已提交
308

C
Cosmin Popescu 已提交
309 310 311 312 313 314 315 316 317 318
function! sw#server#tmp()
    return s:channel_handlers
endfunction

function! sw#server#open_dbexplorer(profile)
    let channel = s:start_sqlwb('dbexplorer')
    let command = sw#get_connect_command(a:profile)
    call sw#server#execute_sql(command, channel)

    return channel
C
Cosmin Popescu 已提交
319
endfunction
C
Cosmin Popescu 已提交
320 321 322 323

function! sw#server#get_parameters_history()
    return s:params_history
endfunction