f1a1f4935555f419677116baf4996747689fec17
[stack/conf/vim.git] / addons / TagHighlight / autoload / TagHighlight / RunPythonScript.vim
1 " Tag Highlighter:
2 "   Author:  A. S. Budden <abudden _at_ gmail _dot_ com>
3 " Copyright: Copyright (C) 2009-2011 A. S. Budden
4 "            Permission is hereby granted to use and distribute this code,
5 "            with or without modifications, provided that this copyright
6 "            notice is copied with it. Like anything else that's free,
7 "            the TagHighlight plugin is provided *as is* and comes with no
8 "            warranty of any kind, either expressed or implied. By using
9 "            this plugin, you agree that in no event will the copyright
10 "            holder be liable for any damages resulting from the use
11 "            of this software.
12
13 " ---------------------------------------------------------------------
14 try
15         if &cp || v:version < 700 || (exists('g:loaded_TagHLRunPythonScript') && (g:plugin_development_mode != 1))
16                 throw "Already loaded"
17         endif
18 catch
19         finish
20 endtry
21 let g:loaded_TagHLRunPythonScript = 1
22
23 let s:python_variant = 'None'
24
25 " A simply python script that will try to import the print function
26 " and will fail gracefully if the python version is too old.  It's
27 " unlikely that the sys.hexversion check will ever fail as if the version
28 " is older than 2.6, the import_check will have failed.  I've left it in,
29 " however, in case I ever need to depend on 2.7
30 let s:version_and_future_check = 
31                         \ "try:\n" .
32                         \ "    import import_check\n" .
33                         \ "    import vim\n" .
34                         \ "    import sys\n" .
35                         \ "    vim.command('''let g:taghl_python_version = '%s' ''' % sys.version)\n" .
36                         \ "    if sys.hexversion < 0x02060000:\n" .
37                         \ "        raise ValueError('Incorrect python version')\n" .
38                         \ "    vim.command('let g:taghl_python_operational = 1')\n" .
39                         \ "except:\n" .
40                         \ "    pass\n"
41
42 " This script is responsible for finding a means of running the python app.
43 " If vim is compiled with python support (and we can run a simple test
44 " command), use that method.  If not, but python is in the path, use it to
45 " run the script.  If python is not in path, we'll have to rely on a compiled
46 " executable version.
47
48 function! s:GetPath()
49         if has("win32")
50                 let path = substitute($PATH, '\\\?;', ',', 'g')
51         else
52                 let path = substitute($PATH, ':', ',', 'g')
53         endif
54         return path
55 endfunction
56
57 function! s:RunShellCommand(args)
58         let syscmd = ""
59         for arg in a:args
60                 if len(syscmd) > 0
61                         let syscmd .= " "
62                 endif
63                 if stridx(arg, " ") != -1
64                         let syscmd .= shellescape(arg)
65                 else
66                         let syscmd .= arg
67                 endif
68         endfor
69         call TagHLDebug(syscmd, "Information")
70         let result = system(syscmd)
71         echo result
72         return result
73 endfunction
74
75 function! TagHighlight#RunPythonScript#RunGenerator(options)
76         " Will only actually load the options once
77         call TagHighlight#Option#LoadOptions()
78
79         " This will only search for python the first time or if
80         " the variant priority or forced variant preferences have
81         " changed.
82         call TagHighlight#RunPythonScript#FindPython()
83
84         call TagHLDebug("Using variant: " .s:python_variant, "Information")
85
86         if index(["if_pyth","if_pyth3"], s:python_variant) != -1
87                 let PY = s:python_cmd[0]
88                 exe PY 'from module.utilities import TagHighlightOptionDict' 
89                 exe PY 'from module.worker import RunWithOptions'
90                 exe PY 'options = TagHighlightOptionDict()'
91                 let handled_options = []
92                 " We're using the custom interpreter: create an options object
93                 " All options supported by both Vim and the Python script must
94                 " have VimOptionMap and CommandLineSwitches keys
95                 for option in g:TagHighlightPrivate['PluginOptions']
96                         if has_key(option, 'VimOptionMap') && 
97                                                 \ has_key(option, 'CommandLineSwitches') &&
98                                                 \ has_key(a:options, option['VimOptionMap'])
99                                 " We can handle this one automatically
100                                 let pyoption = 'options["'.option['Destination'].'"]'
101                                 if option['Type'] == 'bool'
102                                         let handled_options += [option['VimOptionMap']]
103                                         let value = a:options[option['VimOptionMap']]
104                                         if (value == 1) || (value == 'True')
105                                                 exe PY pyoption '= True'
106                                         else
107                                                 exe PY pyoption '= False'
108                                         endif
109                                 elseif option['Type'] == 'string'
110                                         let handled_options += [option['VimOptionMap']]
111                                         exe PY pyoption '= r"""'.a:options[option['VimOptionMap']].'"""'
112                                 elseif option['Type'] == 'int'
113                                         let handled_options += [option['VimOptionMap']]
114                                         exe PY pyoption '= ' . a:options[option['VimOptionMap']]
115                                 elseif option['Type'] == 'list'
116                                         let handled_options += [option['VimOptionMap']]
117                                         exe PY pyoption '= []'
118                                         for entry in a:options[option['VimOptionMap']]
119                                                 exe PY pyoption '+= [r"""' . entry . '"""]'
120                                         endfor
121                                 endif
122                         endif
123                 endfor
124                 for check_opt in keys(a:options)
125                         if index(handled_options, check_opt) == -1
126                                 call TagHLDebug("Unhandled run option: " . check_opt, "Information")
127                         endif
128                 endfor
129                 exe PY 'RunWithOptions(options)'
130         elseif index(["python","compiled"], s:python_variant) != -1
131                 let args = s:python_cmd[:]
132                 " We're calling the script externally, build a list of arguments
133                 for option in g:TagHighlightPrivate['PluginOptions']
134                         if has_key(option, 'VimOptionMap') && 
135                                                 \ has_key(option, 'CommandLineSwitches') &&
136                                                 \ has_key(a:options, option['VimOptionMap'])
137                                 if type(option['CommandLineSwitches']) == type([])
138                                         let switch = option['CommandLineSwitches'][0]
139                                 else
140                                         let switch = option['CommandLineSwitches']
141                                 endif
142                                 if switch[:1] == "--"
143                                         let as_one = 1
144                                 elseif switch[:0] == "-"
145                                         let as_one = 0
146                                 else
147                                         call TagHLDebug("Invalid configuration for option " . option['VimOptionMap'], "Error")
148                                 endif
149                                 " We can handle this one automatically
150                                 if option['Type'] == 'bool'
151                                         if (a:options[option['VimOptionMap']] == 1) || (a:options[option['VimOptionMap']] == 'True')
152                                                 let bvalue = 1
153                                         else
154                                                 let bvalue = 0
155                                         endif
156                                         if (((bvalue == 1) && option['Default'] == 'False')
157                                                                 \ || ((bvalue == 0) && option['Default'] == 'True'))
158                                                 let args += [switch]
159                                         endif
160                                 elseif option['Type'] == 'string'
161                                         if as_one == 1
162                                                 let args += [switch . '=' . a:options[option['VimOptionMap']]]
163                                         else
164                                                 let args += [switch, a:options[option['VimOptionMap']]]
165                                         endif
166                                 elseif option['Type'] == 'int'
167                                         if as_one == 1
168                                                 let args += [switch . '=' . a:options[option['VimOptionMap']]]
169                                         else
170                                                 let args += [switch, a:options[option['VimOptionMap']]]
171                                         endif
172                                 elseif option['Type'] == 'list'
173                                         for entry in a:options[option['VimOptionMap']]
174                                                 if as_one == 1
175                                                         let args += [switch . '=' . entry]
176                                                 else
177                                                         let args += [switch, entry]
178                                                 endif
179                                         endfor
180                                 endif
181                         endif
182                 endfor
183                 let sysoutput = s:RunShellCommand(args)
184         else
185                 throw "Tag highlighter: invalid or not implemented python variant"
186         endif
187 endfunction
188
189 function! TagHighlight#RunPythonScript#FindExeInPath(file)
190         let full_file = a:file
191         if has("win32") || has("win32unix")
192                 if a:file !~ '.exe$'
193                         let full_file = a:file . '.exe.'
194                 endif
195         endif
196         let short_file = fnamemodify(full_file, ':p:t')
197         let file_exe_list = split(globpath(s:GetPath(), short_file, 1), '\n')
198         
199         if len(file_exe_list) > 0 && executable(file_exe_list[0])
200                 let file_exe = file_exe_list[0]
201         else
202                 return 'None'
203         endif
204         "let file_exe = substitute(file_exe, '\\', '/', 'g')
205         return file_exe
206 endfunction
207
208 function! TagHighlight#RunPythonScript#FindPython()
209         let forced_variant = TagHighlight#Option#GetOption('ForcedPythonVariant')
210         " Supported variants
211         let supported_variants = ['if_pyth3', 'if_pyth', 'python', 'compiled']
212         " Priority of those variants (default is that specified above)
213         let variant_priority = TagHighlight#Option#GetOption('PythonVariantPriority')
214
215         " If we've run before and nothing has changed, just return
216         if s:python_variant != 'None'
217                 if forced_variant == s:stored_forced_variant
218                                         \ && s:stored_variant_priority == variant_priority
219                                         \ && s:python_path == TagHighlight#Option#GetOption("PathToPython")
220                         return s:python_variant
221                 endif
222         endif
223
224         let s:python_variant = 'None'
225         let s:python_version = 'Unknown'
226         let s:python_cmd = []
227         let s:python_path = ""
228
229         " Make sure that the user specified variant is supported
230         if index(supported_variants, forced_variant) == -1
231                 let forced_variant = 'None'
232         endif
233
234         let s:stored_forced_variant = forced_variant
235         let s:stored_variant_priority = variant_priority
236
237         let add_to_py_path = substitute(g:TagHighlightPrivate['PluginPath'], '\\', '/','g')
238
239         " Make sure that all variants in the priority list are supported
240         call filter(variant_priority, 'index(supported_variants, v:val) != -1')
241
242         " Try each variant in the priority list until we find one that works
243         for variant in variant_priority
244                 if forced_variant == variant || forced_variant == 'None'
245                         try " Fix for bug in Vim versions before 7.3-388
246                                 if variant == 'if_pyth3' && has('python3')
247                                         " Check whether the python 3 interface works
248                                         let g:taghl_findpython_testvar = 0
249                                         try
250                                                 py3 import sys
251                                                 exe 'py3 sys.path = ["'.add_to_py_path.'"] + sys.path'
252                                                 let g:taghl_python_operational = 0
253                                                 exe 'py3' s:version_and_future_check
254                                                 py3 import vim
255
256                                                 if g:taghl_python_operational != 1
257                                                         throw "Python doesn't seem to be working"
258                                                 endif
259                                                 let s:python_version = g:taghl_python_version
260                                                 unlet g:taghl_python_operational
261                                                 unlet g:taghl_python_version
262
263                                                 " If we got this far, it should be working
264                                                 let s:python_variant = 'if_pyth3'
265                                                 let s:python_cmd = ['py3']
266                                         catch
267                                                 call TagHLDebug("Cannot use python3 interface", "Status")
268                                         endtry
269                                 elseif variant == 'if_pyth' && has('python')
270                                         " Check whether the python 2 interface works
271                                         let g:taghl_findpython_testvar = 0
272                                         try
273                                                 py import sys
274                                                 exe 'py sys.path = ["'.add_to_py_path.'"] + sys.path'
275                                                 let g:taghl_python_operational = 0
276                                                 exe 'py' s:version_and_future_check
277                                                 py import vim
278
279                                                 if g:taghl_python_operational != 1
280                                                         throw "Python doesn't seem to be working"
281                                                 endif
282                                                 let s:python_version = g:taghl_python_version
283                                                 unlet g:taghl_python_operational
284                                                 unlet g:taghl_python_version
285
286                                                 " If we got this far, it should be working
287                                                 let s:python_variant = 'if_pyth'
288                                                 let s:python_cmd = ['py']
289                                         catch
290                                                 call TagHLDebug("Cannot use python2 interface", "Status")
291                                         endtry
292                                 elseif variant == 'python'
293                                         " Try calling an external python
294
295                                         " Has a specific path to python been set?
296                                         let python_path = TagHighlight#Option#GetOption('PathToPython')
297                                         if python_path != 'None' && executable(python_path)
298                                                 " We've found python, it's probably usable
299                                                 let s:python_variant = 'python'
300                                                 let s:python_path = python_path
301                                                 let s:python_cmd = [python_path, g:TagHighlightPrivate['PluginPath'] . '/TagHighlight.py']
302                                         else
303                                                 " See if it's in the path
304                                                 let python_path = TagHighlight#RunPythonScript#FindExeInPath('python')
305                                                 if python_path != 'None'
306                                                         let s:python_variant = 'python'
307                                                         let s:python_path = python_path
308                                                         let s:python_cmd = [python_path, g:TagHighlightPrivate['PluginPath'] . '/TagHighlight.py']
309                                                 endif
310                                         endif
311
312                                         " Now run some simple test code to make sure it works correctly and
313                                         " is a reasonable version
314                                         let result = s:RunShellCommand([s:python_path, g:TagHighlightPrivate['PluginPath'] . '/version_check.py'])
315                                         let lines = split(result, '\n')
316                                         let s:python_version = lines[1]
317                                         if lines[0] != 'OK'
318                                                 let s:python_variant = 'None'
319                                                 let s:python_path = ''
320                                                 let s:python_cmd = []
321                                         endif
322                                 elseif variant == 'compiled'
323                                         " See if there's a compiled executable version of the
324                                         " highlighter
325                                         if has("win32")
326                                                 let compiled_highlighter = split(globpath(&rtp, "plugin/TagHighlight/Compiled/Win32/TagHighlight.exe", 1), "\n")
327                                                 if len(compiled_highlighter) > 0  && executable(compiled_highlighter[0])
328                                                         let s:python_variant = 'compiled'
329                                                         let s:python_version = 'Compiled Highlighter'
330                                                         let s:python_cmd = [compiled_highlighter[0]]
331                                                 endif
332                                         elseif has("unix")
333                                                 let compiled_highlighter = split(globpath(&rtp, "plugin/TagHighlight/Compiled/Linux/TagHighlight"), "\n")
334                                                 if len(compiled_highlighter) > 0  && executable(compiled_highlighter[0])
335                                                         let s:python_variant = 'compiled'
336                                                         let s:python_version = 'Compiled Highlighter'
337                                                         let s:python_cmd = [compiled_highlighter[0]]
338                                                 endif
339                                         endif
340                                 endif
341                         catch /^Vim\%((\a\+)\)\=:E83[67]/
342                                 call TagHLDebug("Attempted to use conflicting pythons in pre-7.3-288 Vim", "Status")
343                         endtry
344                 endif
345                 
346                 if s:python_variant != 'None'
347                         " Found one!
348                         break
349                 endif
350         endfor
351
352         if s:python_variant != 'None'
353                 call TagHLDebug("Python variant is " . s:python_variant, "Information")
354                 call TagHLDebug("Python Command is " . join(s:python_cmd, " "), "Information")
355                 call TagHLDebug("Python Path is " . s:python_path, "Information")
356                 call TagHLDebug("Python version reported as: " . s:python_version,
357                                         \ 'Information')
358         else
359                 throw "Tag highlighter: could not find python (2.6+) or the compiled version of the highlighter."
360         endif
361
362         return s:python_variant
363 endfunction
364