terminal.vim 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115
  1. scriptencoding utf-8
  2. let s:is_vim = !has('nvim')
  3. let s:channel_map = {}
  4. let s:is_win = has('win32') || has('win64')
  5. " start terminal, return [bufnr, pid]
  6. function! coc#terminal#start(cmd, cwd, env, strict) abort
  7. if s:is_vim && !has('terminal')
  8. throw 'terminal feature not supported by current vim.'
  9. endif
  10. let cwd = empty(a:cwd) ? getcwd() : a:cwd
  11. execute 'belowright '.get(g:, 'coc_terminal_height', 8).'new +setl\ buftype=nofile'
  12. setl winfixheight
  13. setl norelativenumber
  14. setl nonumber
  15. setl bufhidden=hide
  16. if exists('#User#CocTerminalOpen')
  17. exe 'doautocmd <nomodeline> User CocTerminalOpen'
  18. endif
  19. let bufnr = bufnr('%')
  20. let env = {}
  21. let original = {}
  22. if !empty(a:env)
  23. " use env option when possible
  24. if s:is_vim
  25. let env = copy(a:env)
  26. elseif exists('*setenv')
  27. for key in keys(a:env)
  28. let original[key] = getenv(key)
  29. call setenv(key, a:env[key])
  30. endfor
  31. endif
  32. endif
  33. function! s:OnExit(status) closure
  34. call coc#rpc#notify('CocAutocmd', ['TermExit', bufnr, a:status])
  35. if a:status == 0
  36. execute 'silent! bd! '.bufnr
  37. endif
  38. endfunction
  39. if has('nvim')
  40. let job_id = termopen(a:cmd, {
  41. \ 'cwd': cwd,
  42. \ 'pty': v:true,
  43. \ 'on_exit': {job, status -> s:OnExit(status)},
  44. \ 'env': env,
  45. \ 'clear_env': a:strict ? v:true : v:false
  46. \ })
  47. if !empty(original) && exists('*setenv')
  48. for key in keys(original)
  49. call setenv(key, original[key])
  50. endfor
  51. endif
  52. if job_id == 0
  53. throw 'create terminal job failed'
  54. endif
  55. wincmd p
  56. let s:channel_map[bufnr] = job_id
  57. return [bufnr, jobpid(job_id)]
  58. else
  59. let cmd = s:is_win ? join(a:cmd, ' ') : a:cmd
  60. let res = term_start(cmd, {
  61. \ 'cwd': cwd,
  62. \ 'term_kill': s:is_win ? 'kill' : 'term',
  63. \ 'term_finish': 'close',
  64. \ 'exit_cb': {job, status -> s:OnExit(status)},
  65. \ 'curwin': 1,
  66. \ 'env': env,
  67. \})
  68. if res == 0
  69. throw 'create terminal job failed'
  70. endif
  71. let job = term_getjob(bufnr)
  72. let s:channel_map[bufnr] = job_getchannel(job)
  73. wincmd p
  74. return [bufnr, job_info(job).process]
  75. endif
  76. endfunction
  77. function! coc#terminal#send(bufnr, text, add_new_line) abort
  78. let chan = get(s:channel_map, a:bufnr, v:null)
  79. if empty(chan) | return| endif
  80. if has('nvim')
  81. let lines = split(a:text, '\v\r?\n')
  82. if a:add_new_line && !empty(lines[len(lines) - 1])
  83. if s:is_win
  84. call add(lines, "\r\n")
  85. else
  86. call add(lines, '')
  87. endif
  88. endif
  89. call chansend(chan, lines)
  90. let winid = bufwinid(a:bufnr)
  91. if winid != -1
  92. call coc#compat#execute(winid, 'noa normal! G')
  93. endif
  94. else
  95. if !a:add_new_line
  96. call ch_sendraw(chan, a:text)
  97. else
  98. call ch_sendraw(chan, a:text.(s:is_win ? "\r\n" : "\n"))
  99. endif
  100. endif
  101. endfunction
  102. function! coc#terminal#close(bufnr) abort
  103. if has('nvim')
  104. let job_id = get(s:channel_map, a:bufnr, 0)
  105. if !empty(job_id)
  106. silent! call chanclose(job_id)
  107. endif
  108. endif
  109. exe 'silent! bd! '.a:bufnr
  110. endfunction