1. abudden
  2. TagHighlight

Source

TagHighlight / autoload / TagHighlight / RunPythonScript.vim

  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
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
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
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
" Tag Highlighter:
"   Author:  A. S. Budden <abudden _at_ gmail _dot_ com>
" Copyright: Copyright (C) 2009-2013 A. S. Budden
"            Permission is hereby granted to use and distribute this code,
"            with or without modifications, provided that this copyright
"            notice is copied with it. Like anything else that's free,
"            the TagHighlight plugin is provided *as is* and comes with no
"            warranty of any kind, either expressed or implied. By using
"            this plugin, you agree that in no event will the copyright
"            holder be liable for any damages resulting from the use
"            of this software.

" ---------------------------------------------------------------------
try
	if &cp || v:version < 700 || (exists('g:loaded_TagHLRunPythonScript') && (g:plugin_development_mode != 1))
		throw "Already loaded"
	endif
catch
	finish
endtry
let g:loaded_TagHLRunPythonScript = 1

let s:python_variant = 'None'

" A simply python script that will try to import the print function
" and will fail gracefully if the python version is too old.  It's
" unlikely that the sys.hexversion check will ever fail as if the version
" is older than 2.6, the import_check will have failed.  I've left it in,
" however, in case I ever need to depend on 2.7
let s:version_and_future_check = 
			\ "try:\n" .
			\ "    import import_check\n" .
			\ "    import vim\n" .
			\ "    import sys\n" .
			\ "    vim.command('''let g:taghl_python_version = '%s' ''' % sys.version)\n" .
			\ "    if sys.hexversion < 0x02060000:\n" .
			\ "        raise ValueError('Incorrect python version')\n" .
			\ "    vim.command('let g:taghl_python_operational = 1')\n" .
			\ "except:\n" .
			\ "    pass\n"

" This script is responsible for finding a means of running the python app.
" If vim is compiled with python support (and we can run a simple test
" command), use that method.  If not, but python is in the path, use it to
" run the script.  If python is not in path, we'll have to rely on a compiled
" executable version.

function! s:GetPath()
	if has("win32")
		let path = substitute($PATH, '\\\?;', ',', 'g')
	else
		let path = substitute($PATH, ':', ',', 'g')
	endif
	return path
endfunction

function! s:RunShellCommand(args)
	let syscmd = ""
	for arg in a:args
		if len(syscmd) > 0
			let syscmd .= " "
		endif
		if stridx(arg, " ") != -1
			let syscmd .= shellescape(arg)
		else
			let syscmd .= arg
		endif
	endfor
	call TagHLDebug(syscmd, "Information")
	let result = system(syscmd)
	echo result
	return result
endfunction

function! s:GetVarType(value)
	if type(a:value) == type(0)
		return "int"
	elseif type(a:value) == type("")
		return "string"
	elseif type(a:value) == type([])
		return "list"
	elseif type(a:value) == type({})
		return "dict"
	elseif type(a:value) == type(function("tr"))
		return "function"
	elseif type(a:value) == type(0.0)
		return "float"
	endif
endfunction

function! s:SetPyVariable(PY, pyoption, type, value)
	let handled_option = 0
	if a:type == 'bool'
		let handled_option = 1
		if (a:value == 1) || (a:value == 'True')
			exe a:PY a:pyoption '= True'
		else
			exe a:PY a:pyoption '= False'
		endif
	elseif a:type == 'string'
		let handled_option = 1
		exe a:PY a:pyoption '= r"""'.a:value.'"""'
	elseif a:type == 'int'
		let handled_option = 1
		exe a:PY a:pyoption '= ' . a:value
	elseif a:type == 'list'
		let handled_option = 1
		exe a:PY a:pyoption '= []'
		for entry in a:value
			exe a:PY a:pyoption '+= [r"""' . entry . '"""]'
		endfor
	elseif a:type == 'dict'
		let handled_option = 1
		exe a:PY a:pyoption '= {}'
		for key in keys(a:value)
			call s:SetPyVariable(a:PY,
						\ a:pyoption.'[r"""'.key.'"""]',
						\ s:GetVarType(a:value[key]),
						\ a:value[key])
		endfor
	endif
	return handled_option
endfunction

function! TagHighlight#RunPythonScript#RunGenerator(options)
	" Will only actually load the options once
	call TagHighlight#Option#LoadOptions()

	" This will only search for python the first time or if
	" the variant priority or forced variant preferences have
	" changed.
	call TagHighlight#RunPythonScript#FindPython()

	call TagHLDebug("Using variant: " .s:python_variant, "Information")

	if index(["if_pyth","if_pyth3"], s:python_variant) != -1
		let PY = s:python_cmd[0]
		exe PY 'from module.utilities import TagHighlightOptionDict' 
		exe PY 'from module.worker import RunWithOptions'
		exe PY 'options = TagHighlightOptionDict()'
		let handled_options = []
		" We're using the custom interpreter: create an options object
		" All options supported by both Vim and the Python script must
		" have CommandLineSwitches key and not have PythonOnly set to True
		for option in g:TagHighlightPrivate['PluginOptions']
			if has_key(option, 'CommandLineSwitches') &&
						\ has_key(a:options, option['Destination'])
				" We can handle this one automatically
				let pyoption = 'options["'.option['Destination'].'"]'
				let result = s:SetPyVariable(PY, pyoption,
							\ option['Type'],
							\ a:options[option['Destination']])
				if result != 0
					let handled_options += [option['Destination']]
				endif
			endif
		endfor
		exe PY 'manually_set = ' string(handled_options)
		for check_opt in keys(a:options)
			if index(handled_options, check_opt) == -1
				call TagHLDebug("Unhandled run option: " . check_opt, "Information")
			endif
		endfor
		exe PY 'RunWithOptions(options, manually_set)'
	elseif index(["python","compiled"], s:python_variant) != -1
		let args = s:python_cmd[:]
		" We're calling the script externally, build a list of arguments
		for option in g:TagHighlightPrivate['PluginOptions']
			if has_key(option, 'CommandLineSwitches') &&
						\ has_key(a:options, option['Destination'])
				if type(option['CommandLineSwitches']) == type([])
					let switch = option['CommandLineSwitches'][0]
				else
					let switch = option['CommandLineSwitches']
				endif
				if switch[:1] == "--"
					let as_one = 1
				elseif switch[:0] == "-"
					let as_one = 0
				else
					call TagHLDebug("Invalid configuration for option " . option['Destination'], "Error")
				endif
				" We can handle this one automatically
				if option['Type'] == 'bool'
					if (a:options[option['Destination']] == 1) || (a:options[option['Destination']] == 'True')
						let bvalue = 1
					else
						let bvalue = 0
					endif
					if (((bvalue == 1) && option['Default'] == 'False')
								\ || ((bvalue == 0) && option['Default'] == 'True'))
						let args += [switch]
					endif
				elseif option['Type'] == 'string'
					if as_one == 1
						let args += [switch . '=' . a:options[option['Destination']]]
					else
						let args += [switch, a:options[option['Destination']]]
					endif
				elseif option['Type'] == 'int'
					if as_one == 1
						let args += [switch . '=' . a:options[option['Destination']]]
					else
						let args += [switch, a:options[option['Destination']]]
					endif
				elseif option['Type'] == 'list'
					for entry in a:options[option['Destination']]
						if as_one == 1
							let args += [switch . '=' . entry]
						else
							let args += [switch, entry]
						endif
					endfor
				elseif option['Type'] == 'dict'
					" Not sure how robust this is likely to be...
					" or for that matter how likely it is that the result
					" will be the same as the if_pyth version...
					let args += [switch, string(a:options[option['Destination']])]
				endif
			endif
		endfor
		let sysoutput = s:RunShellCommand(args)
	else
		throw "Tag highlighter: invalid or not implemented python variant"
	endif
endfunction

function! TagHighlight#RunPythonScript#FindExeInPath(file)
	let full_file = a:file
	if has("win32") || has("win32unix")
		if a:file !~ '.exe$'
			let full_file = a:file . '.exe.'
		endif
	endif
	let short_file = fnamemodify(full_file, ':p:t')
	let file_exe_list = split(globpath(s:GetPath(), short_file, 1), '\n')
	
	if len(file_exe_list) > 0 && executable(file_exe_list[0])
		let file_exe = file_exe_list[0]
	else
		return 'None'
	endif
	"let file_exe = substitute(file_exe, '\\', '/', 'g')
	return file_exe
endfunction

function! TagHighlight#RunPythonScript#FindPython()
	let forced_variant = TagHighlight#Option#GetOption('ForcedPythonVariant')
	" Supported variants
	let supported_variants = ['if_pyth3', 'if_pyth', 'python', 'compiled']
	" Priority of those variants (default is that specified above)
	let variant_priority = TagHighlight#Option#GetOption('PythonVariantPriority')

	" If we've run before and nothing has changed, just return
	if s:python_variant != 'None'
		if forced_variant == s:stored_forced_variant
					\ && s:stored_variant_priority == variant_priority
					\ && s:python_path == TagHighlight#Option#GetOption("PathToPython")
			return s:python_variant
		endif
	endif

	let s:python_variant = 'None'
	let s:python_version = 'Unknown'
	let s:python_cmd = []
	let s:python_path = ""

	" Make sure that the user specified variant is supported
	if index(supported_variants, forced_variant) == -1
		let forced_variant = 'None'
	endif

	let s:stored_forced_variant = forced_variant
	let s:stored_variant_priority = variant_priority

	let add_to_py_path = substitute(g:TagHighlightPrivate['PluginPath'], '\\', '/','g')

	" Make sure that all variants in the priority list are supported
	call filter(variant_priority, 'index(supported_variants, v:val) != -1')

	" Try each variant in the priority list until we find one that works
	for variant in variant_priority
		if forced_variant == variant || forced_variant == 'None'
			try " Fix for bug in Vim versions before 7.3-388
				if variant == 'if_pyth3' && has('python3')
					" Check whether the python 3 interface works
					let g:taghl_findpython_testvar = 0
					try
						py3 import sys
						exe 'py3 sys.path = ["'.add_to_py_path.'"] + sys.path'
						let g:taghl_python_operational = 0
						exe 'py3' s:version_and_future_check
						py3 import vim

						if g:taghl_python_operational != 1
							throw "Python doesn't seem to be working"
						endif
						let s:python_version = g:taghl_python_version
						unlet g:taghl_python_operational
						unlet g:taghl_python_version

						" If we got this far, it should be working
						let s:python_variant = 'if_pyth3'
						let s:python_cmd = ['py3']
					catch
						call TagHLDebug("Cannot use python3 interface", "Status")
					endtry
				elseif variant == 'if_pyth' && has('python')
					" Check whether the python 2 interface works
					let g:taghl_findpython_testvar = 0
					try
						py import sys
						exe 'py sys.path = ["'.add_to_py_path.'"] + sys.path'
						let g:taghl_python_operational = 0
						exe 'py' s:version_and_future_check
						py import vim

						if g:taghl_python_operational != 1
							throw "Python doesn't seem to be working"
						endif
						let s:python_version = g:taghl_python_version
						unlet g:taghl_python_operational
						unlet g:taghl_python_version

						" If we got this far, it should be working
						let s:python_variant = 'if_pyth'
						let s:python_cmd = ['py']
					catch
						call TagHLDebug("Cannot use python2 interface", "Status")
					endtry
				elseif variant == 'python'
					" Try calling an external python

					" Has a specific path to python been set?
					let python_path = TagHighlight#Option#GetOption('PathToPython')
					if python_path != 'None' && executable(python_path)
						" We've found python, it's probably usable
						let s:python_variant = 'python'
						let s:python_path = python_path
						let s:python_cmd = [python_path, g:TagHighlightPrivate['PluginPath'] . '/TagHighlight.py']
					else
						" See if it's in the path
						let python_path = TagHighlight#RunPythonScript#FindExeInPath('python')
						if python_path != 'None'
							let s:python_variant = 'python'
							let s:python_path = python_path
							let s:python_cmd = [python_path, g:TagHighlightPrivate['PluginPath'] . '/TagHighlight.py']
						endif
					endif

					" Now run some simple test code to make sure it works correctly and
					" is a reasonable version
					let result = s:RunShellCommand([s:python_path, g:TagHighlightPrivate['PluginPath'] . '/version_check.py'])
					let lines = split(result, '\n')
					let s:python_version = lines[1]
					if lines[0] != 'OK'
						let s:python_variant = 'None'
						let s:python_path = ''
						let s:python_cmd = []
					endif
				elseif variant == 'compiled'
					" See if there's a compiled executable version of the
					" highlighter
					if has("win32")
						let compiled_highlighter = split(globpath(&rtp, "plugin/TagHighlight/Compiled/Win32/TagHighlight.exe", 1), "\n")
						if len(compiled_highlighter) > 0  && executable(compiled_highlighter[0])
							let s:python_variant = 'compiled'
							let s:python_version = 'Compiled Highlighter'
							let s:python_cmd = [compiled_highlighter[0]]
						endif
					elseif has("unix")
						let compiled_highlighter = split(globpath(&rtp, "plugin/TagHighlight/Compiled/Linux/TagHighlight"), "\n")
						if len(compiled_highlighter) > 0  && executable(compiled_highlighter[0])
							let s:python_variant = 'compiled'
							let s:python_version = 'Compiled Highlighter'
							let s:python_cmd = [compiled_highlighter[0]]
						endif
					endif
				endif
			catch /^Vim\%((\a\+)\)\=:E83[67]/
				call TagHLDebug("Attempted to use conflicting pythons in pre-7.3-288 Vim", "Status")
			endtry
		endif
		
		if s:python_variant != 'None'
			" Found one!
			break
		endif
	endfor

	if s:python_variant != 'None'
		call TagHLDebug("Python variant is " . s:python_variant, "Information")
		call TagHLDebug("Python Command is " . join(s:python_cmd, " "), "Information")
		call TagHLDebug("Python Path is " . s:python_path, "Information")
		call TagHLDebug("Python version reported as: " . s:python_version,
					\ 'Information')
	else
		throw "Tag highlighter: could not find python (2.6+) or the compiled version of the highlighter."
	endif

	return s:python_variant
endfunction