repl.vim 5.35 KB
Newer Older
HiPhish's avatar
HiPhish committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 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 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97
" Author: Alejandro "HiPhish" Sanchez
" License:  The MIT License (MIT) {{{
"    Copyright (c) 2017 HiPhish
" 
"    Permission is hereby granted, free of charge, to any person obtaining a
"    copy of this software and associated documentation files (the
"    "Software"), to deal in the Software without restriction, including
"    without limitation the rights to use, copy, modify, merge, publish,
"    distribute, sublicense, and/or sell copies of the Software, and to permit
"    persons to whom the Software is furnished to do so, subject to the
"    following conditions:
" 
"    The above copyright notice and this permission notice shall be included
"    in all copies or substantial portions of the Software.
" 
"    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
"    OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
"    MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
"    NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
"    DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
"    OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
"    USE OR OTHER DEALINGS IN THE SOFTWARE.
" }}}

if !has('nvim') || exists('g:repl_nvim')
  finish
endif
let g:repl_nvim = 1


" ----------------------------------------------------------------------------
" The ':Repl' command
" ----------------------------------------------------------------------------
" The ':Repl' command is the public interface  for end users. The user can
" pass any number of command-line arguments.
command! -complete=file -bang -nargs=* Repl call <SID>repl(<q-mods>, '<bang>', <f-args>)


" ----------------------------------------------------------------------------
" The 's:repl' function is what is actually called by the ':Repl' command
" ----------------------------------------------------------------------------
function! s:repl(mods, bang, ...)
	" First we need to determine the REPL type. If there were no arguments
	" passed or the first argument is '-' deduce the type from the current
	" file type. Otherwise the type is the first argument.
	let l:type = ''
	if a:0 > 0 && a:1 !=? '-'
		if exists('g:repl["'.a:1.'"]')
			let l:type = a:1
		else
			echohl ErrorMsg
			echom 'No REPL of type '''.a:1.''' defined'
			echohl None
			return
		endif
	else
		for l:ft in split(&filetype, '\v\.')
			if exists('g:repl["'.l:ft.'"]')
				let l:type = l:ft
			endif
		endfor
		if exists('g:repl["'.&filetype.'"]')
			let l:type = l:ft
		endif
		if empty(l:type)
			echohl ErrorMsg
			echom 'No REPL for current file type defined'
			echohl None
			return
		endif
	endif

	" If the '!' was not supplied and there is already an instance running
	" jump to that instance.
	if empty(a:bang) && exists('g:repl["'.l:type.'"].instances') && len(g:repl[l:type].instances) > 0
		" Always use the youngest instance
		let l:buffer = g:repl[l:type].instances[0].buffer
		let l:windows = win_findbuf(l:buffer)

		if empty(l:windows)
			silent execute a:mods 'new'
			silent execute 'buffer' l:buffer
		else
			call win_gotoid(l:windows[0])
		endif

		return
	endif

	" The actual option values to use are determined at runtime. Global
	" settings take precedence, so we loop over the global dictionary and
	" create local variants of every setting.
	"
	" After a local variable has been initialised with the global default we
	" loop over the lower scopes in a given order. If we encounter the same
	" setting it overwrites the previous values. The scopes are ordered by
	" ascending significance, with the most significant being last.
98 99
	let l:repl = copy(g:repl[l:type])
	for l:key in keys(l:repl)
HiPhish's avatar
HiPhish committed
100 101 102
		for l:scope in ['t', 'w', 'b']
			let l:entry = l:scope.':repl["'.l:type.'"]["'.l:key.'"]'
			if exists(l:entry)
103
				silent execute 'let l:repl["'.l:key.'"] = '.l:entry
HiPhish's avatar
HiPhish committed
104 105
			endif
		endfor
106 107 108 109
		" If the option is a function reference call it
		if type(l:repl[l:key]) == type(function('type'))
			let l:repl[l:key] = l:repl[l:key]()
		endif
HiPhish's avatar
HiPhish committed
110 111 112 113
	endfor

	" Append the argument to the command to the argument list (but skip the
	" first argument, that is the file type)
114
	let l:repl.args = l:repl.args + a:000[1:]
HiPhish's avatar
HiPhish committed
115 116 117

	" Open a new buffer and launch the terminal
	silent execute a:mods 'new'
118 119 120
	silent execute 'terminal' l:repl.binary join(l:repl.args, ' ')
	silent execute 'set syntax='.l:repl.syntax
	silent let b:term_title = l:repl.title
HiPhish's avatar
HiPhish committed
121 122 123 124 125

	" Collect information about this REPL instance
	let b:repl = {
		\ '-': {
			\ 'type'   : l:type,
126 127
			\ 'binary' : l:repl.binary,
			\ 'args'   : l:repl.args,
HiPhish's avatar
HiPhish committed
128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154
			\ 'job_id' : b:terminal_job_id,
			\ 'buffer' : bufnr('%')
		\ }
	\ }

	" Add This instance to the top of the list of instances
	if exists('g:repl["'.l:type.'"].instances')
		call insert(g:repl[l:type].instances, b:repl['-'])
	else
		let g:repl[l:type].instances = [b:repl['-']]
	endif

	" Hook up autocommand to clean up after the REPL terminates; the
	" autocommand is not guaranteed to have access to the b:repl variable,
	" that's why we instead use the literal job-id to identify this instance.
	silent execute 'au BufDelete <buffer> call <SID>remove_instance('. b:repl['-'].job_id .', "'.l:type.'")'
endfunction

" Remove an instance from the global list of instances
function! s:remove_instance(job_id, type)
	for i in range(len(g:repl[a:type].instances))
		if g:repl[a:type].instances[i].job_id == a:job_id
			call remove(g:repl[a:type].instances, i)
			break
		endif
	endfor
endfunction