diff options
author | Bram Moolenaar <Bram@vim.org> | 2010-07-17 21:19:38 +0200 |
---|---|---|
committer | Bram Moolenaar <Bram@vim.org> | 2010-07-17 21:19:38 +0200 |
commit | bd5e15fd5c7e42505d6b0e20f4198d24fc7e219d (patch) | |
tree | 70e3f86ae76494fc094bbe25c58ba2befbcf4872 | |
parent | 02c707a87da1b0f78d10a689cc03941a2e8acbc6 (diff) | |
download | vim-git-bd5e15fd5c7e42505d6b0e20f4198d24fc7e219d.tar.gz |
Added support for Python 3. (Roland Puntaier)
-rw-r--r-- | Filelist | 2 | ||||
-rw-r--r-- | Makefile | 5 | ||||
-rw-r--r-- | runtime/autoload/python3complete.vim | 606 | ||||
-rw-r--r-- | runtime/doc/todo.txt | 7 | ||||
-rw-r--r-- | runtime/syntax/vim.vim | 14 | ||||
-rw-r--r-- | src/Make_bc5.mak | 56 | ||||
-rw-r--r-- | src/Make_cyg.mak | 34 | ||||
-rw-r--r-- | src/Make_ming.mak | 48 | ||||
-rw-r--r-- | src/Make_mvc.mak | 45 | ||||
-rw-r--r-- | src/Makefile | 84 | ||||
-rwxr-xr-x | src/auto/configure | 290 | ||||
-rw-r--r-- | src/buffer.c | 3 | ||||
-rw-r--r-- | src/config.h.in | 9 | ||||
-rw-r--r-- | src/config.mk.in | 6 | ||||
-rw-r--r-- | src/configure.in | 180 | ||||
-rw-r--r-- | src/eval.c | 17 | ||||
-rw-r--r-- | src/ex_cmds.h | 4 | ||||
-rw-r--r-- | src/ex_docmd.c | 13 | ||||
-rw-r--r-- | src/globals.h | 10 | ||||
-rw-r--r-- | src/if_python.c | 21 | ||||
-rw-r--r-- | src/if_python3.c | 2796 | ||||
-rw-r--r-- | src/main.c | 3 | ||||
-rw-r--r-- | src/proto.h | 4 | ||||
-rw-r--r-- | src/proto/if_python3.pro | 8 | ||||
-rw-r--r-- | src/structs.h | 8 | ||||
-rw-r--r-- | src/version.c | 9 | ||||
-rw-r--r-- | src/vim.h | 1 | ||||
-rw-r--r-- | src/window.c | 4 |
28 files changed, 4229 insertions, 58 deletions
@@ -216,6 +216,7 @@ SRC_DOS_UNIX = \ src/if_perl.xs \ src/if_perlsfio.c \ src/if_python.c \ + src/if_python3.c \ src/if_ruby.c \ src/if_sniff.h \ src/if_tcl.c \ @@ -225,6 +226,7 @@ SRC_DOS_UNIX = \ src/proto/if_perl.pro \ src/proto/if_perlsfio.pro \ src/proto/if_python.pro \ + src/proto/if_python3.pro \ src/proto/if_ruby.pro \ src/proto/if_tcl.pro \ src/typemap \ @@ -80,8 +80,9 @@ DOSBIN_S = dosbin_s # runtime/doc/*.txt and nsis/gvim.nsi. Other things in README_os2.txt. For a # minor/major version: src/GvimExt/GvimExt.reg, src/vim.def, src/vim16.def. # - Correct included_patches[] in src/version.c. -# - Compile Vim with GTK, Perl, Python, TCL, Ruby, MZscheme, Lua (if you can -# make it work), Cscope and "huge" features. Exclude workshop and SNiFF. +# - Compile Vim with GTK, Perl, Python, Python3, TCL, Ruby, MZscheme, Lua (if +# you can make it work), Cscope and "huge" features. Exclude workshop and +# SNiFF. # - With these features: "make proto" (requires cproto and Motif installed; # ignore warnings for missing include files, fix problems for syntax errors). # - With these features: "make depend" (works best with gcc). diff --git a/runtime/autoload/python3complete.vim b/runtime/autoload/python3complete.vim new file mode 100644 index 000000000..b02200be7 --- /dev/null +++ b/runtime/autoload/python3complete.vim @@ -0,0 +1,606 @@ +"python3complete.vim - Omni Completion for python +" Maintainer: Aaron Griffin <aaronmgriffin@gmail.com> +" Version: 0.9 +" Last Updated: 18 Jun 2009 +" +" Roland Puntaier: this file contains adaptations for python3 and is parallel to pythoncomplete.vim +" +" Changes +" TODO: +" 'info' item output can use some formatting work +" Add an "unsafe eval" mode, to allow for return type evaluation +" Complete basic syntax along with import statements +" i.e. "import url<c-x,c-o>" +" Continue parsing on invalid line?? +" +" v 0.9 +" * Fixed docstring parsing for classes and functions +" * Fixed parsing of *args and **kwargs type arguments +" * Better function param parsing to handle things like tuples and +" lambda defaults args +" +" v 0.8 +" * Fixed an issue where the FIRST assignment was always used instead of +" using a subsequent assignment for a variable +" * Fixed a scoping issue when working inside a parameterless function +" +" +" v 0.7 +" * Fixed function list sorting (_ and __ at the bottom) +" * Removed newline removal from docs. It appears vim handles these better in +" recent patches +" +" v 0.6: +" * Fixed argument completion +" * Removed the 'kind' completions, as they are better indicated +" with real syntax +" * Added tuple assignment parsing (whoops, that was forgotten) +" * Fixed import handling when flattening scope +" +" v 0.5: +" Yeah, I skipped a version number - 0.4 was never public. +" It was a bugfix version on top of 0.3. This is a complete +" rewrite. +" + +if !has('python3') + echo "Error: Required vim compiled with +python3" + finish +endif + +function! python3complete#Complete(findstart, base) + "findstart = 1 when we need to get the text length + if a:findstart == 1 + let line = getline('.') + let idx = col('.') + while idx > 0 + let idx -= 1 + let c = line[idx] + if c =~ '\w' + continue + elseif ! c =~ '\.' + let idx = -1 + break + else + break + endif + endwhile + + return idx + "findstart = 0 when we need to return the list of completions + else + "vim no longer moves the cursor upon completion... fix that + let line = getline('.') + let idx = col('.') + let cword = '' + while idx > 0 + let idx -= 1 + let c = line[idx] + if c =~ '\w' || c =~ '\.' + let cword = c . cword + continue + elseif strlen(cword) > 0 || idx == 0 + break + endif + endwhile + execute "py3 vimpy3complete('" . cword . "', '" . a:base . "')" + return g:python3complete_completions + endif +endfunction + +function! s:DefPython() +py3 << PYTHONEOF +import sys, tokenize, io, types +from token import NAME, DEDENT, NEWLINE, STRING + +debugstmts=[] +def dbg(s): debugstmts.append(s) +def showdbg(): + for d in debugstmts: print("DBG: %s " % d) + +def vimpy3complete(context,match): + global debugstmts + debugstmts = [] + try: + import vim + cmpl = Completer() + cmpl.evalsource('\n'.join(vim.current.buffer),vim.eval("line('.')")) + all = cmpl.get_completions(context,match) + all.sort(key=lambda x:x['abbr'].replace('_','z')) + dictstr = '[' + # have to do this for double quoting + for cmpl in all: + dictstr += '{' + for x in cmpl: dictstr += '"%s":"%s",' % (x,cmpl[x]) + dictstr += '"icase":0},' + if dictstr[-1] == ',': dictstr = dictstr[:-1] + dictstr += ']' + #dbg("dict: %s" % dictstr) + vim.command("silent let g:python3complete_completions = %s" % dictstr) + #dbg("Completion dict:\n%s" % all) + except vim.error: + dbg("VIM Error: %s" % vim.error) + +class Completer(object): + def __init__(self): + self.compldict = {} + self.parser = PyParser() + + def evalsource(self,text,line=0): + sc = self.parser.parse(text,line) + src = sc.get_code() + dbg("source: %s" % src) + try: exec(src,self.compldict) + except: dbg("parser: %s, %s" % (sys.exc_info()[0],sys.exc_info()[1])) + for l in sc.locals: + try: exec(l,self.compldict) + except: dbg("locals: %s, %s [%s]" % (sys.exc_info()[0],sys.exc_info()[1],l)) + + def _cleanstr(self,doc): + return doc.replace('"',' ').replace("'",' ') + + def get_arguments(self,func_obj): + def _ctor(class_ob): + try: return class_ob.__init__ + except AttributeError: + for base in class_ob.__bases__: + rc = _ctor(base) + if rc is not None: return rc + return None + + arg_offset = 1 + if type(func_obj) == type: func_obj = _ctor(func_obj) + elif type(func_obj) == types.MethodType: arg_offset = 1 + else: arg_offset = 0 + + arg_text='' + if type(func_obj) in [types.FunctionType, types.LambdaType,types.MethodType]: + try: + cd = func_obj.__code__ + real_args = cd.co_varnames[arg_offset:cd.co_argcount] + defaults = func_obj.__defaults__ or [] + defaults = ["=%s" % name for name in defaults] + defaults = [""] * (len(real_args)-len(defaults)) + defaults + items = [a+d for a,d in zip(real_args,defaults)] + if func_obj.__code__.co_flags & 0x4: + items.append("...") + if func_obj.__code__.co_flags & 0x8: + items.append("***") + arg_text = (','.join(items)) + ')' + except: + dbg("arg completion: %s: %s" % (sys.exc_info()[0],sys.exc_info()[1])) + pass + if len(arg_text) == 0: + # The doc string sometimes contains the function signature + # this works for alot of C modules that are part of the + # standard library + doc = func_obj.__doc__ + if doc: + doc = doc.lstrip() + pos = doc.find('\n') + if pos > 0: + sigline = doc[:pos] + lidx = sigline.find('(') + ridx = sigline.find(')') + if lidx > 0 and ridx > 0: + arg_text = sigline[lidx+1:ridx] + ')' + if len(arg_text) == 0: arg_text = ')' + return arg_text + + def get_completions(self,context,match): + #dbg("get_completions('%s','%s')" % (context,match)) + stmt = '' + if context: stmt += str(context) + if match: stmt += str(match) + try: + result = None + all = {} + ridx = stmt.rfind('.') + if len(stmt) > 0 and stmt[-1] == '(': + result = eval(_sanitize(stmt[:-1]), self.compldict) + doc = result.__doc__ + if doc is None: doc = '' + args = self.get_arguments(result) + return [{'word':self._cleanstr(args),'info':self._cleanstr(doc)}] + elif ridx == -1: + match = stmt + all = self.compldict + else: + match = stmt[ridx+1:] + stmt = _sanitize(stmt[:ridx]) + result = eval(stmt, self.compldict) + all = dir(result) + + dbg("completing: stmt:%s" % stmt) + completions = [] + + try: maindoc = result.__doc__ + except: maindoc = ' ' + if maindoc is None: maindoc = ' ' + for m in all: + if m == "_PyCmplNoType": continue #this is internal + try: + dbg('possible completion: %s' % m) + if m.find(match) == 0: + if result is None: inst = all[m] + else: inst = getattr(result,m) + try: doc = inst.__doc__ + except: doc = maindoc + typestr = str(inst) + if doc is None or doc == '': doc = maindoc + + wrd = m[len(match):] + c = {'word':wrd, 'abbr':m, 'info':self._cleanstr(doc)} + if "function" in typestr: + c['word'] += '(' + c['abbr'] += '(' + self._cleanstr(self.get_arguments(inst)) + elif "method" in typestr: + c['word'] += '(' + c['abbr'] += '(' + self._cleanstr(self.get_arguments(inst)) + elif "module" in typestr: + c['word'] += '.' + elif "type" in typestr: + c['word'] += '(' + c['abbr'] += '(' + completions.append(c) + except: + i = sys.exc_info() + dbg("inner completion: %s,%s [stmt='%s']" % (i[0],i[1],stmt)) + return completions + except: + i = sys.exc_info() + dbg("completion: %s,%s [stmt='%s']" % (i[0],i[1],stmt)) + return [] + +class Scope(object): + def __init__(self,name,indent,docstr=''): + self.subscopes = [] + self.docstr = docstr + self.locals = [] + self.parent = None + self.name = name + self.indent = indent + + def add(self,sub): + #print('push scope: [%s@%s]' % (sub.name,sub.indent)) + sub.parent = self + self.subscopes.append(sub) + return sub + + def doc(self,str): + """ Clean up a docstring """ + d = str.replace('\n',' ') + d = d.replace('\t',' ') + while d.find(' ') > -1: d = d.replace(' ',' ') + while d[0] in '"\'\t ': d = d[1:] + while d[-1] in '"\'\t ': d = d[:-1] + dbg("Scope(%s)::docstr = %s" % (self,d)) + self.docstr = d + + def local(self,loc): + self._checkexisting(loc) + self.locals.append(loc) + + def copy_decl(self,indent=0): + """ Copy a scope's declaration only, at the specified indent level - not local variables """ + return Scope(self.name,indent,self.docstr) + + def _checkexisting(self,test): + "Convienance function... keep out duplicates" + if test.find('=') > -1: + var = test.split('=')[0].strip() + for l in self.locals: + if l.find('=') > -1 and var == l.split('=')[0].strip(): + self.locals.remove(l) + + def get_code(self): + str = "" + if len(self.docstr) > 0: str += '"""'+self.docstr+'"""\n' + for l in self.locals: + if l.startswith('import'): str += l+'\n' + str += 'class _PyCmplNoType:\n def __getattr__(self,name):\n return None\n' + for sub in self.subscopes: + str += sub.get_code() + for l in self.locals: + if not l.startswith('import'): str += l+'\n' + + return str + + def pop(self,indent): + #print('pop scope: [%s] to [%s]' % (self.indent,indent)) + outer = self + while outer.parent != None and outer.indent >= indent: + outer = outer.parent + return outer + + def currentindent(self): + #print('parse current indent: %s' % self.indent) + return ' '*self.indent + + def childindent(self): + #print('parse child indent: [%s]' % (self.indent+1)) + return ' '*(self.indent+1) + +class Class(Scope): + def __init__(self, name, supers, indent, docstr=''): + Scope.__init__(self,name,indent, docstr) + self.supers = supers + def copy_decl(self,indent=0): + c = Class(self.name,self.supers,indent, self.docstr) + for s in self.subscopes: + c.add(s.copy_decl(indent+1)) + return c + def get_code(self): + str = '%sclass %s' % (self.currentindent(),self.name) + if len(self.supers) > 0: str += '(%s)' % ','.join(self.supers) + str += ':\n' + if len(self.docstr) > 0: str += self.childindent()+'"""'+self.docstr+'"""\n' + if len(self.subscopes) > 0: + for s in self.subscopes: str += s.get_code() + else: + str += '%spass\n' % self.childindent() + return str + + +class Function(Scope): + def __init__(self, name, params, indent, docstr=''): + Scope.__init__(self,name,indent, docstr) + self.params = params + def copy_decl(self,indent=0): + return Function(self.name,self.params,indent, self.docstr) + def get_code(self): + str = "%sdef %s(%s):\n" % \ + (self.currentindent(),self.name,','.join(self.params)) + if len(self.docstr) > 0: str += self.childindent()+'"""'+self.docstr+'"""\n' + str += "%spass\n" % self.childindent() + return str + +class PyParser: + def __init__(self): + self.top = Scope('global',0) + self.scope = self.top + + def _parsedotname(self,pre=None): + #returns (dottedname, nexttoken) + name = [] + if pre is None: + tokentype, token, indent = self.donext() + if tokentype != NAME and token != '*': + return ('', token) + else: token = pre + name.append(token) + while True: + tokentype, token, indent = self.donext() + if token != '.': break + tokentype, token, indent = self.donext() + if tokentype != NAME: break + name.append(token) + return (".".join(name), token) + + def _parseimportlist(self): + imports = [] + while True: + name, token = self._parsedotname() + if not name: break + name2 = '' + if token == 'as': name2, token = self._parsedotname() + imports.append((name, name2)) + while token != "," and "\n" not in token: + tokentype, token, indent = self.donext() + if token != ",": break + return imports + + def _parenparse(self): + name = '' + names = [] + level = 1 + while True: + tokentype, token, indent = self.donext() + if token in (')', ',') and level == 1: + if '=' not in name: name = name.replace(' ', '') + names.append(name.strip()) + name = '' + if token == '(': + level += 1 + name += "(" + elif token == ')': + level -= 1 + if level == 0: break + else: name += ")" + elif token == ',' and level == 1: + pass + else: + name += "%s " % str(token) + return names + + def _parsefunction(self,indent): + self.scope=self.scope.pop(indent) + tokentype, fname, ind = self.donext() + if tokentype != NAME: return None + + tokentype, open, ind = self.donext() + if open != '(': return None + params=self._parenparse() + + tokentype, colon, ind = self.donext() + if colon != ':': return None + + return Function(fname,params,indent) + + def _parseclass(self,indent): + self.scope=self.scope.pop(indent) + tokentype, cname, ind = self.donext() + if tokentype != NAME: return None + + super = [] + tokentype, thenext, ind = self.donext() + if thenext == '(': + super=self._parenparse() + elif thenext != ':': return None + + return Class(cname,super,indent) + + def _parseassignment(self): + assign='' + tokentype, token, indent = self.donext() + if tokentype == tokenize.STRING or token == 'str': + return '""' + elif token == '(' or token == 'tuple': + return '()' + elif token == '[' or token == 'list': + return '[]' + elif token == '{' or token == 'dict': + return '{}' + elif tokentype == tokenize.NUMBER: + return '0' + elif token == 'open' or token == 'file': + return 'file' + elif token == 'None': + return '_PyCmplNoType()' + elif token == 'type': + return 'type(_PyCmplNoType)' #only for method resolution + else: + assign += token + level = 0 + while True: + tokentype, token, indent = self.donext() + if token in ('(','{','['): + level += 1 + elif token in (']','}',')'): + level -= 1 + if level == 0: break + elif level == 0: + if token in (';','\n'): break + assign += token + return "%s" % assign + + def donext(self): + type, token, (lineno, indent), end, self.parserline = next(self.gen) + if lineno == self.curline: + #print('line found [%s] scope=%s' % (line.replace('\n',''),self.scope.name)) + self.currentscope = self.scope + return (type, token, indent) + + def _adjustvisibility(self): + newscope = Scope('result',0) + scp = self.currentscope + while scp != None: + if type(scp) == Function: + slice = 0 + #Handle 'self' params + if scp.parent != None and type(scp.parent) == Class: + slice = 1 + newscope.local('%s = %s' % (scp.params[0],scp.parent.name)) + for p in scp.params[slice:]: + i = p.find('=') + if len(p) == 0: continue + pvar = '' + ptype = '' + if i == -1: + pvar = p + ptype = '_PyCmplNoType()' + else: + pvar = p[:i] + ptype = _sanitize(p[i+1:]) + if pvar.startswith('**'): + pvar = pvar[2:] + ptype = '{}' + elif pvar.startswith('*'): + pvar = pvar[1:] + ptype = '[]' + + newscope.local('%s = %s' % (pvar,ptype)) + + for s in scp.subscopes: + ns = s.copy_decl(0) + newscope.add(ns) + for l in scp.locals: newscope.local(l) + scp = scp.parent + + self.currentscope = newscope + return self.currentscope + + #p.parse(vim.current.buffer[:],vim.eval("line('.')")) + def parse(self,text,curline=0): + self.curline = int(curline) + buf = io.StringIO(''.join(text) + '\n') + self.gen = tokenize.generate_tokens(buf.readline) + self.currentscope = self.scope + + try: + freshscope=True + while True: + tokentype, token, indent = self.donext() + #dbg( 'main: token=[%s] indent=[%s]' % (token,indent)) + + if tokentype == DEDENT or token == "pass": + self.scope = self.scope.pop(indent) + elif token == 'def': + func = self._parsefunction(indent) + if func is None: + print("function: syntax error...") + continue + dbg("new scope: function") + freshscope = True + self.scope = self.scope.add(func) + elif token == 'class': + cls = self._parseclass(indent) + if cls is None: + print("class: syntax error...") + continue + freshscope = True + dbg("new scope: class") + self.scope = self.scope.add(cls) + + elif token == 'import': + imports = self._parseimportlist() + for mod, alias in imports: + loc = "import %s" % mod + if len(alias) > 0: loc += " as %s" % alias + self.scope.local(loc) + freshscope = False + elif token == 'from': + mod, token = self._parsedotname() + if not mod or token != "import": + print("from: syntax error...") + continue + names = self._parseimportlist() + for name, alias in names: + loc = "from %s import %s" % (mod,name) + if len(alias) > 0: loc += " as %s" % alias + self.scope.local(loc) + freshscope = False + elif tokentype == STRING: + if freshscope: self.scope.doc(token) + elif tokentype == NAME: + name,token = self._parsedotname(token) + if token == '=': + stmt = self._parseassignment() + dbg("parseassignment: %s = %s" % (name, stmt)) + if stmt != None: + self.scope.local("%s = %s" % (name,stmt)) + freshscope = False + except StopIteration: #thrown on EOF + pass + except: + dbg("parse error: %s, %s @ %s" % + (sys.exc_info()[0], sys.exc_info()[1], self.parserline)) + return self._adjustvisibility() + +def _sanitize(str): + val = '' + level = 0 + for c in str: + if c in ('(','{','['): + level += 1 + elif c in (']','}',')'): + level -= 1 + elif level == 0: + val += c + return val + +sys.path.extend(['.','..']) +PYTHONEOF +endfunction + +call s:DefPython() diff --git a/runtime/doc/todo.txt b/runtime/doc/todo.txt index 4082b0a57..db01d3aa1 100644 --- a/runtime/doc/todo.txt +++ b/runtime/doc/todo.txt @@ -1089,17 +1089,12 @@ Before (beta) release 7.3: - Add fixes for 7.2 to version7.txt - Add hg history to version7.txt - Remove UF_VERSION_CRYPT_PREV and UF_VERSION_PREV. +- Build the MS-Windows version with Python 2.6.5 and 3.1.2? Before release 7.3: - Rename vim73 branch to default (hints: Xavier de Gaye, 2010 May 23) Vim 7.3: -Patches to possibly include: -- Patch for Python 3 support. (Roland Puntaier, 2009 Sep 22) - Includes changes for omnicompletion. - Needs some more testing. - Update 2010 Apr 20, patch by Andy Kittner, May 16 - Build the MS-Windows version with Python 2.6.5 and 3.1.2? Needs some work: - Have a look at patch to enable screen access from Python. (Marko Mahnic, 2010 Apr 12) diff --git a/runtime/syntax/vim.vim b/runtime/syntax/vim.vim index 46667486a..db1f522f7 100644 --- a/runtime/syntax/vim.vim +++ b/runtime/syntax/vim.vim @@ -16,7 +16,7 @@ syn keyword vimTodo contained COMBAK FIXME TODO XXX syn cluster vimCommentGroup contains=vimTodo,@Spell " regular vim commands {{{2 -syn keyword vimCommand contained abc[lear] argdo argu[ment] bel[owright] bN[ext] breakd[el] b[uffer] caddb[uffer] cb[uffer] cex[pr] cg[etfile] checkt[ime] cnew[er] col[der] con[tinue] cq[uit] delc[ommand] diffoff diffu[pdate] dr[op] echoe[rr] el[se] endfo[r] endw[hile] f[ile] fin[d] fo[ld] foldo[pen] gr[ep] his[tory] il[ist] iuna[bbrev] keepalt lad[dexpr] later lcl[ose] lf[ile] lg[etfile] ll lmapc[lear] lnf[ile] lockv[ar] lp[revious] lv[imgrep] ma[rk] mk[exrc] mkv[imrc] mz[scheme] new noh[lsearch] on[ly] ped[it] popu prev[ious] prof[ile] pta[g] ptn[ext] pts[elect] py[thon] r[ead] redr[aw] ret[ab] rightb[elow] rundo san[dbox] sbf[irst] sbN[ext] scripte[ncoding] setg[lobal] sh[ell] sla[st] sme sni[ff] sor[t] spelli[nfo] sp[lit] startg[replace] st[op] sunme syncbind tabd[o] tabl[ast] tabN[ext] tabs tcld[o] th[row] tm[enu] tp[revious] tu undoj[oin] uns[ilent] vert[ical] vi[sual] wa[ll] winp[os] wp[revious] ws[verb] xa[ll] xmenu xnoremenu +syn keyword vimCommand contained abc[lear] argdo argu[ment] bel[owright] bN[ext] breakd[el] b[uffer] caddb[uffer] cb[uffer] cex[pr] cg[etfile] checkt[ime] cnew[er] col[der] con[tinue] cq[uit] delc[ommand] diffoff diffu[pdate] dr[op] echoe[rr] el[se] endfo[r] endw[hile] f[ile] fin[d] fo[ld] foldo[pen] gr[ep] his[tory] il[ist] iuna[bbrev] keepalt lad[dexpr] later lcl[ose] lf[ile] lg[etfile] ll lmapc[lear] lnf[ile] lockv[ar] lp[revious] lv[imgrep] ma[rk] mk[exrc] mkv[imrc] mz[scheme] new noh[lsearch] on[ly] ped[it] popu prev[ious] prof[ile] pta[g] ptn[ext] pts[elect] py[thon] py3 r[ead] redr[aw] ret[ab] rightb[elow] rundo san[dbox] sbf[irst] sbN[ext] scripte[ncoding] setg[lobal] sh[ell] sla[st] sme sni[ff] sor[t] spelli[nfo] sp[lit] startg[replace] st[op] sunme syncbind tabd[o] tabl[ast] tabN[ext] tabs tcld[o] th[row] tm[enu] tp[revious] tu undoj[oin] uns[ilent] vert[ical] vi[sual] wa[ll] winp[os] wp[revious] ws[verb] xa[ll] xmenu xnoremenu syn keyword vimCommand contained abo[veleft] arge[dit] as[cii] bf[irst] bo[tright] breakl[ist] buffers cad[dexpr] cc cf[ile] c[hange] cla[st] cn[ext] colo[rscheme] cope[n] cr[ewind] d[elete] diffpatch dig[raphs] ds[earch] echom[sg] elsei[f] endf[unction] ene[w] files fini[sh] foldc[lose] for grepa[dd] iabc[lear] imapc[lear] j[oin] keepj[umps] laddf[ile] lb[uffer] le[ft] lfir[st] lgr[ep] lla[st] lnew[er] lNf[ile] lol[der] lr[ewind] lvimgrepa[dd] marks mks[ession] mod[e] nbc[lose] n[ext] nu[mber] o[pen] pe[rl] popu[p] p[rint] promptf[ind] ptf[irst] ptN[ext] pu[t] qa[ll] rec[over] redraws[tatus] retu[rn] rub[y] ru[ntime] sa[rgument] sbl[ast] sbp[revious] scrip[tnames] setl[ocal] sign sl[eep] smenu sno[magic] so[urce] spellr[epall] spr[evious] star[tinsert] stopi[nsert] sunmenu t tabe[dit] tabm[ove] tabo[nly] ta[g] tclf[ile] tj[ump] tn[ext] tr[ewind] tu[nmenu] undol[ist] up[date] vie[w] vmapc[lear] wh[ile] win[size] wq wundo x[it] XMLent xunme syn keyword vimCommand contained al[l] argg[lobal] bad[d] bl[ast] bp[revious] br[ewind] bun[load] caddf[ile] ccl[ose] cfir[st] changes cl[ist] cN[ext] comc[lear] co[py] cuna[bbrev] delf[unction] diffpu[t] di[splay] dsp[lit] echon em[enu] en[dif] ex filetype fir[st] folddoc[losed] fu[nction] ha[rdcopy] if is[earch] ju[mps] kee[pmarks] lan[guage] lc[d] lefta[bove] lgetb[uffer] lgrepa[dd] lli[st] lne[xt] lo[adview] lop[en] ls lw[indow] mat[ch] mksp[ell] m[ove] nb[key] N[ext] ol[dfiles] opt[ions] perld[o] pp[op] P[rint] promptr[epl] ptj[ump] ptp[revious] pw[d] q[uit] redi[r] reg[isters] rew[ind] rubyd[o] rv[iminfo] sav[eas] sbm[odified] sbr[ewind] se[t] sf[ind] sil[ent] sm[agic] sn[ext] snoreme spelld[ump] spellu[ndo] sre[wind] startr[eplace] sts[elect] sus[pend] tab tabf[ind] tabnew tabp[revious] tags te[aroff] tl[ast] tN[ext] try una[bbreviate] unh[ide] verb[ose] vim[grep] vne[w] winc[md] wn[ext] wqa[ll] wv[iminfo] xmapc[lear] XMLns xunmenu syn keyword vimCommand contained arga[dd] argl[ocal] ba[ll] bm[odified] brea[k] bro[wse] bw[ipeout] cal[l] cd cgetb[uffer] chd[ir] clo[se] cnf[ile] comp[iler] cpf[ile] cw[indow] delm[arks] diffsplit dj[ump] earlier e[dit] emenu* endt[ry] exi[t] fina[lly] fix[del] foldd[oopen] go[to] hid[e] ij[ump] isp[lit] k laddb[uffer] la[st] lch[dir] lex[pr] lgete[xpr] l[ist] lmak[e] lN[ext] loc[kmarks] lpf[ile] lt[ag] mak[e] menut[ranslate] mkvie[w] mzf[ile] nbs[tart] nmapc[lear] omapc[lear] pc[lose] po[p] pre[serve] profd[el] ps[earch] ptl[ast] ptr[ewind] pyf[ile] quita[ll] red[o] res[ize] ri[ght] rubyf[ile] sal[l] sba[ll] sbn[ext] sb[uffer] setf[iletype] sfir[st] sim[alt] sm[ap] sN[ext] snoremenu spe[llgood] spellw[rong] sta[g] stj[ump] sun[hide] sv[iew] tabc[lose] tabfir[st] tabn[ext] tabr[ewind] tc[l] tf[irst] tm to[pleft] ts[elect] u[ndo] unlo[ckvar] ve[rsion] vimgrepa[dd] vs[plit] windo wN[ext] w[rite] X xme xnoreme y[ank] @@ -594,16 +594,16 @@ if (g:vimsyn_embed =~ 'P' && has("python")) && filereadable(s:pythonpath) unlet! b:current_syntax exe "syn include @vimPythonScript ".s:pythonpath if exists("g:vimsyn_folding") && g:vimsyn_folding =~ 'P' - syn region vimPythonRegion fold matchgroup=vimScriptDelim start=+py\%[thon]\s*<<\s*\z(.*\)$+ end=+^\z1$+ contains=@vimPythonScript - syn region vimPythonRegion fold matchgroup=vimScriptDelim start=+py\%[thon]\s*<<\s*$+ end=+\.$+ contains=@vimPythonScript + syn region vimPythonRegion fold matchgroup=vimScriptDelim start=+py[3]\%[thon]\s*<<\s*\z(.*\)$+ end=+^\z1$+ contains=@vimPythonScript + syn region vimPythonRegion fold matchgroup=vimScriptDelim start=+py[3]\%[thon]\s*<<\s*$+ end=+\.$+ contains=@vimPythonScript else - syn region vimPythonRegion matchgroup=vimScriptDelim start=+py\%[thon]\s*<<\s*\z(.*\)$+ end=+^\z1$+ contains=@vimPythonScript - syn region vimPythonRegion matchgroup=vimScriptDelim start=+py\%[thon]\s*<<\s*$+ end=+\.$+ contains=@vimPythonScript + syn region vimPythonRegion matchgroup=vimScriptDelim start=+py[3]\%[thon]\s*<<\s*\z(.*\)$+ end=+^\z1$+ contains=@vimPythonScript + syn region vimPythonRegion matchgroup=vimScriptDelim start=+py[3]\%[thon]\s*<<\s*$+ end=+\.$+ contains=@vimPythonScript endif syn cluster vimFuncBodyList add=vimPythonRegion else - syn region vimEmbedError start=+py\%[thon]\s*<<\s*\z(.*\)$+ end=+^\z1$+ - syn region vimEmbedError start=+py\%[thon]\s*<<\s*$+ end=+\.$+ + syn region vimEmbedError start=+py[3]\%[thon]\s*<<\s*\z(.*\)$+ end=+^\z1$+ + syn region vimEmbedError start=+py[3]\%[thon]\s*<<\s*$+ end=+\.$+ endif unlet s:pythonpath diff --git a/src/Make_bc5.mak b/src/Make_bc5.mak index c3ddf00f6..739fc62ed 100644 --- a/src/Make_bc5.mak +++ b/src/Make_bc5.mak @@ -44,6 +44,9 @@ # PYTHON define to path to Python dir to get PYTHON support (not defined) # PYTHON_VER define to version of Python being used (22) # DYNAMIC_PYTHON no or yes: use yes to load the Python DLL dynamically (no) +# PYTHON3 define to path to Python3 dir to get PYTHON3 support (not defined) +# PYTHON3_VER define to version of Python3 being used (31) +# DYNAMIC_PYTHON3 no or yes: use yes to load the Python3 DLL dynamically (no) # TCL define to path to TCL dir to get TCL support (not defined) # TCL_VER define to version of TCL being used (83) # DYNAMIC_TCL no or yes: use yes to load the TCL DLL dynamically (no) @@ -141,6 +144,9 @@ NETBEANS = yes ### PYTHON: uncomment this line if you want python support in vim # PYTHON=c:\python22 +### PYTHON3: uncomment this line if you want python3 support in vim +# PYTHON3=c:\python31 + ### RUBY: uncomment this line if you want ruby support in vim # RUBY=c:\ruby @@ -207,6 +213,7 @@ ALIGN = 4 !if ("$(FASTCALL)"=="") && \ ("$(LUA)"=="") && \ ("$(PYTHON)"=="") && \ + ("$(PYTHON3)"=="") && \ ("$(PERL)"=="") && \ ("$(TCL)"=="") && \ ("$(RUBY)"=="") && \ @@ -328,8 +335,14 @@ PERL_LIB_FLAG = /nodefaultlib: !endif !ifdef PYTHON +!ifdef PYTHON3 +DYNAMIC_PYTHON=yes +DYNAMIC_PYTHON3=yes +!endif +!endif + +!ifdef PYTHON INTERP_DEFINES = $(INTERP_DEFINES) -DFEAT_PYTHON -INCLUDE = $(PYTHON)\include;$(INCLUDE) !ifndef PYTHON_VER PYTHON_VER = 22 !endif @@ -339,6 +352,18 @@ PYTHON_LIB_FLAG = /nodefaultlib: !endif !endif +!ifdef PYTHON3 +INTERP_DEFINES = $(INTERP_DEFINES) -DFEAT_PYTHON3 +!ifndef PYTHON3_VER +PYTHON3_VER = 31 +!endif +!if "$(DYNAMIC_PYTHON3)" == "yes" +INTERP_DEFINES = $(INTERP_DEFINES) -DDYNAMIC_PYTHON3 -DDYNAMIC_PYTHON3_DLL=\"python$(PYTHON3_VER).dll\" +PYTHON3_LIB_FLAG = /nodefaultlib: +!endif +!endif + + !ifdef RUBY !ifndef RUBY_VER RUBY_VER = 16 @@ -618,6 +643,11 @@ vimobj = $(vimobj) \ $(OBJDIR)\if_python.obj !endif +!ifdef PYTHON3 +vimobj = $(vimobj) \ + $(OBJDIR)\if_python3.obj +!endif + !ifdef RUBY vimobj = $(vimobj) \ $(OBJDIR)\if_ruby.obj @@ -734,6 +764,12 @@ MSG = $(MSG) PYTHON MSG = $(MSG)(dynamic) ! endif !endif +!ifdef PYTHON3 +MSG = $(MSG) PYTHON3 +! if "$(DYNAMIC_PYTHON3)" == "yes" +MSG = $(MSG)(dynamic) +! endif +!endif !ifdef RUBY MSG = $(MSG) RUBY ! if "$(DYNAMIC_RUBY)" == "yes" @@ -827,6 +863,9 @@ clean: !ifdef PYTHON -@del python.lib !endif +!ifdef PYTHON3 + -@del python3.lib +!endif !ifdef RUBY -@del ruby.lib !endif @@ -867,6 +906,9 @@ $(DLLTARGET): $(OBJDIR) $(vimdllobj) !ifdef PYTHON $(PYTHON_LIB_FLAG)python.lib+ !endif +!ifdef PYTHON3 + $(PYTHON3_LIB_FLAG)python3.lib+ +!endif !ifdef RUBY $(RUBY_LIB_FLAG)ruby.lib+ !endif @@ -919,6 +961,9 @@ $(TARGET): $(OBJDIR) $(vimobj) $(OBJDIR)\$(RESFILE) !ifdef PYTHON $(PYTHON_LIB_FLAG)python.lib+ !endif +!ifdef PYTHON3 + $(PYTHON3_LIB_FLAG)python3.lib+ +!endif !ifdef RUBY $(RUBY_LIB_FLAG)ruby.lib+ !endif @@ -962,7 +1007,10 @@ if_perl.c: if_perl.xs typemap $(PERL)\lib\ExtUtils\typemap if_perl.xs > $@ $(OBJDIR)\if_python.obj: if_python.c python.lib - $(CC) $(CCARG) $(CC1) $(CC2)$@ -pc if_python.c + $(CC) -I$(PYTHON)\include $(CCARG) $(CC1) $(CC2)$@ -pc if_python.c + +$(OBJDIR)\if_python3.obj: if_python3.c python3.lib + $(CC) -I$(PYTHON3)\include $(CCARG) $(CC1) $(CC2)$@ -pc if_python3.c $(OBJDIR)\if_ruby.obj: if_ruby.c ruby.lib $(CC) $(CCARG) $(CC1) $(CC2)$@ -pc if_ruby.c @@ -1017,6 +1065,9 @@ perl.lib: $(PERL)\lib\CORE\perl$(PERL_VER).lib python.lib: $(PYTHON)\libs\python$(PYTHON_VER).lib coff2omf $(PYTHON)\libs\python$(PYTHON_VER).lib $@ +python3.lib: $(PYTHON3)\libs\python$(PYTHON3_VER).lib + coff2omf $(PYTHON3)\libs\python$(PYTHON3_VER).lib $@ + ruby.lib: $(RUBY)\lib\$(RUBY_INSTALL_NAME).lib coff2omf $(RUBY)\lib\$(RUBY_INSTALL_NAME).lib $@ @@ -1065,3 +1116,4 @@ $(OBJDIR)\bcc.cfg: Make_bc5.mak $(OBJDIR) | $@ # vi:set sts=4 sw=4: + diff --git a/src/Make_cyg.mak b/src/Make_cyg.mak index b3650bb05..0ca7115ce 100644 --- a/src/Make_cyg.mak +++ b/src/Make_cyg.mak @@ -14,6 +14,9 @@ # PYTHON define to path to Python dir to get PYTHON support (not defined) # PYTHON_VER define to version of Python being used (22) # DYNAMIC_PYTHON no or yes: use yes to load the Python DLL dynamically (yes) +# PYTHON3 define to path to Python3 dir to get PYTHON3 support (not defined) +# PYTHON3_VER define to version of Python3 being used (22) +# DYNAMIC_PYTHON3 no or yes: use yes to load the Python3 DLL dynamically (yes) # TCL define to path to TCL dir to get TCL support (not defined) # TCL_VER define to version of TCL being used (83) # DYNAMIC_TCL no or yes: use yes to load the TCL DLL dynamically (yes) @@ -139,7 +142,6 @@ endif ############################## ifdef PYTHON DEFINES += -DFEAT_PYTHON -INCLUDES += -I$(PYTHON)/include EXTRA_OBJS += $(OUTDIR)/if_python.o ifndef DYNAMIC_PYTHON @@ -158,6 +160,29 @@ endif endif ############################## +# DYNAMIC_PYTHON3=yes works. +# DYNAMIC_PYTHON3=no does not (unresolved externals on link). +############################## +ifdef PYTHON3 +DEFINES += -DFEAT_PYTHON3 +EXTRA_OBJS += $(OUTDIR)/if_python3.o + +ifndef DYNAMIC_PYTHON3 +DYNAMIC_PYTHON3 = yes +endif + +ifndef PYTHON3_VER +PYTHON3_VER = 31 +endif + +ifeq (yes, $(DYNAMIC_PYTHON3)) +DEFINES += -DDYNAMIC_PYTHON3 -DDYNAMIC_PYTHON3_DLL=\"python$(PYTHON3_VER).dll\" +else +EXTRA_LIBS += $(PYTHON3)/libs/python$(PYTHON3_VER).lib +endif +endif + +############################## # DYNAMIC_RUBY=yes works. # DYNAMIC_RUBY=no does not (process exits). ############################## @@ -563,6 +588,12 @@ $(OUTDIR)/if_cscope.o: if_cscope.c $(INCL) if_cscope.h $(OUTDIR)/if_ole.o: if_ole.cpp $(INCL) $(CC) -c $(CFLAGS) if_ole.cpp -o $(OUTDIR)/if_ole.o +$(OUTDIR)/if_python.o : if_python.c $(INCL) + $(CC) -c $(CFLAGS) -I$(PYTHON)/include $< -o $@ + +$(OUTDIR)/if_python3.o : if_python3.c $(INCL) + $(CC) -c $(CFLAGS) -I$(PYTHON3)/include $< -o $@ + if_perl.c: if_perl.xs typemap $(PERL)/bin/perl `cygpath -d $(PERL)/lib/ExtUtils/xsubpp` \ -prototypes -typemap \ @@ -612,3 +643,4 @@ else @echo char_u *compiled_user = (char_u *)"$(USERNAME)"; >> pathdef.c @echo char_u *compiled_sys = (char_u *)"$(USERDOMAIN)"; >> pathdef.c endif + diff --git a/src/Make_ming.mak b/src/Make_ming.mak index 51fd523df..306dff925 100644 --- a/src/Make_ming.mak +++ b/src/Make_ming.mak @@ -194,6 +194,28 @@ PYTHONINC=-I $(PYTHON)/win32inc endif endif +#PYTHON3: See comment for Python 2 above + +ifdef PYTHON3 +ifndef DYNAMIC_PYTHON3 +DYNAMIC_PYTHON3=yes +endif + +ifndef PYTHON3_VER +PYTHON3_VER=31 +endif + +ifeq (no,$(DYNAMIC_PYTHON3)) +PYTHON3LIB=-L$(PYTHON3)/libs -lPYTHON$(PYTHON3_VER) +endif + +ifeq ($(CROSS),no) +PYTHON3INC=-I $(PYTHON3)/include +else +PYTHON3INC=-I $(PYTHON3)/win32inc +endif +endif + # TCL interface: # TCL=[Path to TCL directory] # DYNAMIC_TCL=yes (to load the TCL DLL dynamically) @@ -334,9 +356,16 @@ endif endif ifdef PYTHON -CFLAGS += -DFEAT_PYTHON $(PYTHONINC) +CFLAGS += -DFEAT_PYTHON ifeq (yes, $(DYNAMIC_PYTHON)) -CFLAGS += -DDYNAMIC_PYTHON -DDYNAMIC_PYTHON_DLL=\"python$(PYTHON_VER).dll\" +CFLAGS += -DDYNAMIC_PYTHON +endif +endif + +ifdef PYTHON3 +CFLAGS += -DFEAT_PYTHON3 +ifeq (yes, $(DYNAMIC_PYTHON3)) +CFLAGS += -DDYNAMIC_PYTHON3 endif endif @@ -468,6 +497,9 @@ endif ifdef PYTHON OBJ += $(OUTDIR)/if_python.o endif +ifdef PYTHON3 +OBJ += $(OUTDIR)/if_python3.o +endif ifdef RUBY OBJ += $(OUTDIR)/if_ruby.o endif @@ -576,7 +608,7 @@ uninstal.exe: uninstal.c $(CC) $(CFLAGS) -o uninstal.exe uninstal.c $(LIB) $(TARGET): $(OUTDIR) $(OBJ) - $(CC) $(CFLAGS) $(LFLAGS) -o $@ $(OBJ) $(LIB) -lole32 -luuid $(LUA_LIB) $(MZSCHEME_LIBDIR) $(MZSCHEME_LIB) $(PYTHONLIB) $(RUBYLIB) + $(CC) $(CFLAGS) $(LFLAGS) -o $@ $(OBJ) $(LIB) -lole32 -luuid $(LUA_LIB) $(MZSCHEME_LIBDIR) $(MZSCHEME_LIB) $(PYTHONLIB) $(PYTHON3LIB) $(RUBYLIB) upx: exes upx gvim.exe @@ -608,6 +640,12 @@ INCL = vim.h feature.h os_win32.h os_dos.h ascii.h keymap.h term.h macros.h \ structs.h regexp.h option.h ex_cmds.h proto.h globals.h farsi.h \ gui.h +$(OUTDIR)/if_python.o : if_python.c $(INCL) + $(CC) -c $(CFLAGS) $(PYTHONINC) -DDYNAMIC_PYTHON_DLL=\"python$(PYTHON_VER).dll\" $< -o $@ + +$(OUTDIR)/if_python3.o : if_python3.c $(INCL) + $(CC) -c $(CFLAGS) $(PYTHON3INC) -DDYNAMIC_PYTHON3_DLL=\"PYTHON$(PYTHON3_VER).dll\" $< -o $@ + $(OUTDIR)/%.o : %.c $(INCL) $(CC) -c $(CFLAGS) $< -o $@ @@ -659,7 +697,7 @@ ifneq (sh.exe, $(SHELL)) @echo 'char_u *default_vim_dir = (char_u *)"$(VIMRCLOC)";' >> pathdef.c @echo 'char_u *default_vimruntime_dir = (char_u *)"$(VIMRUNTIMEDIR)";' >> pathdef.c @echo 'char_u *all_cflags = (char_u *)"$(CC) $(CFLAGS)";' >> pathdef.c - @echo 'char_u *all_lflags = (char_u *)"$(CC) $(CFLAGS) $(LFLAGS) -o $(TARGET) $(LIB) -lole32 -luuid $(LUA_LIB) $(MZSCHEME_LIBDIR) $(MZSCHEME_LIB) $(PYTHONLIB) $(RUBYLIB)";' >> pathdef.c + @echo 'char_u *all_lflags = (char_u *)"$(CC) $(CFLAGS) $(LFLAGS) -o $(TARGET) $(LIB) -lole32 -luuid $(LUA_LIB) $(MZSCHEME_LIBDIR) $(MZSCHEME_LIB) $(PYTHONLIB) $(PYTHON3LIB) $(RUBYLIB)";' >> pathdef.c @echo 'char_u *compiled_user = (char_u *)"$(USERNAME)";' >> pathdef.c @echo 'char_u *compiled_sys = (char_u *)"$(USERDOMAIN)";' >> pathdef.c else @@ -669,7 +707,7 @@ else @echo char_u *default_vim_dir = (char_u *)"$(VIMRCLOC)"; >> pathdef.c @echo char_u *default_vimruntime_dir = (char_u *)"$(VIMRUNTIMEDIR)"; >> pathdef.c @echo char_u *all_cflags = (char_u *)"$(CC) $(CFLAGS)"; >> pathdef.c - @echo char_u *all_lflags = (char_u *)"$(CC) $(CFLAGS) $(LFLAGS) -o $(TARGET) $(LIB) -lole32 -luuid $(LUA_LIB) $(MZSCHEME_LIBDIR) $(MZSCHEME_LIB) $(PYTHONLIB) $(RUBYLIB)"; >> pathdef.c + @echo char_u *all_lflags = (char_u *)"$(CC) $(CFLAGS) $(LFLAGS) -o $(TARGET) $(LIB) -lole32 -luuid $(LUA_LIB) $(MZSCHEME_LIBDIR) $(MZSCHEME_LIB) $(PYTHONLIB) $(PYTHON3LIB) $(RUBYLIB)"; >> pathdef.c @echo char_u *compiled_user = (char_u *)"$(USERNAME)"; >> pathdef.c @echo char_u *compiled_sys = (char_u *)"$(USERDOMAIN)"; >> pathdef.c endif diff --git a/src/Make_mvc.mak b/src/Make_mvc.mak index 7a144cc30..c0704503c 100644 --- a/src/Make_mvc.mak +++ b/src/Make_mvc.mak @@ -52,6 +52,11 @@ # DYNAMIC_PYTHON=yes (to load the Python DLL dynamically) # PYTHON_VER=[Python version, eg 15, 20] (default is 22) # +# Python3 interface: +# PYTHON3=[Path to Python3 directory] +# DYNAMIC_PYTHON3=yes (to load the Python3 DLL dynamically) +# PYTHON3_VER=[Python3 version, eg 30, 31] (default is 31) +# # Ruby interface: # RUBY=[Path to Ruby directory] # DYNAMIC_RUBY=yes (to load the Ruby DLL dynamically) @@ -166,6 +171,9 @@ OBJDIR = $(OBJDIR)L !ifdef PYTHON OBJDIR = $(OBJDIR)Y !endif +!ifdef PYTHON3 +OBJDIR = $(OBJDIR)H +!endif !ifdef TCL OBJDIR = $(OBJDIR)T !endif @@ -641,6 +649,13 @@ LUA_LIB = "$(LUA)\lib\lua$(LUA_VER).lib" !endif !endif +!ifdef PYTHON +!ifdef PYTHON3 +DYNAMIC_PYTHON=yes +DYNAMIC_PYTHON3=yes +!endif +!endif + # PYTHON interface !ifdef PYTHON !ifndef PYTHON_VER @@ -662,6 +677,27 @@ PYTHON_LIB = $(PYTHON)\libs\python$(PYTHON_VER).lib !endif !endif +# PYTHON3 interface +!ifdef PYTHON3 +!ifndef PYTHON3_VER +PYTHON3_VER = 31 +!endif +!message Python3 requested (version $(PYTHON3_VER)) - root dir is "$(PYTHON3)" +!if "$(DYNAMIC_PYTHON3)" == "yes" +!message Python3 DLL will be loaded dynamically +!endif +CFLAGS = $(CFLAGS) -DFEAT_PYTHON3 +PYTHON3_OBJ = $(OUTDIR)\if_python3.obj +PYTHON3_INC = /I "$(PYTHON3)\Include" /I "$(PYTHON3)\PC" +!if "$(DYNAMIC_PYTHON3)" == "yes" +CFLAGS = $(CFLAGS) -DDYNAMIC_PYTHON3 \ + -DDYNAMIC_PYTHON3_DLL=\"python$(PYTHON3_VER).dll\" +PYTHON3_LIB = /nodefaultlib:python$(PYTHON3_VER).lib +!else +PYTHON3_LIB = $(PYTHON3)\libs\python$(PYTHON3_VER).lib +!endif +!endif + # MzScheme interface !ifdef MZSCHEME !message MzScheme requested - root dir is "$(MZSCHEME)" @@ -835,7 +871,7 @@ conflags = $(conflags) /map /mapinfo:lines LINKARGS1 = $(linkdebug) $(conflags) LINKARGS2 = $(CON_LIB) $(GUI_LIB) $(LIBC) $(OLE_LIB) user32.lib $(SNIFF_LIB) \ - $(LUA_LIB) $(MZSCHEME_LIB) $(PERL_LIB) $(PYTHON_LIB) $(RUBY_LIB) \ + $(LUA_LIB) $(MZSCHEME_LIB) $(PERL_LIB) $(PYTHON_LIB) $(PYTHON3_LIB) $(RUBY_LIB) \ $(TCL_LIB) $(NETBEANS_LIB) $(XPM_LIB) $(LINK_PDB) # Report link time code generation progress if used. @@ -851,12 +887,12 @@ all: $(VIM).exe vimrun.exe install.exe uninstal.exe xxd/xxd.exe \ GvimExt/gvimext.dll $(VIM).exe: $(OUTDIR) $(OBJ) $(GUI_OBJ) $(OLE_OBJ) $(OLE_IDL) $(MZSCHEME_OBJ) \ - $(LUA_OBJ) $(PERL_OBJ) $(PYTHON_OBJ) $(RUBY_OBJ) $(TCL_OBJ) \ + $(LUA_OBJ) $(PERL_OBJ) $(PYTHON_OBJ) $(PYTHON3_OBJ) $(RUBY_OBJ) $(TCL_OBJ) \ $(SNIFF_OBJ) $(CSCOPE_OBJ) $(NETBEANS_OBJ) $(XPM_OBJ) \ version.c version.h $(CC) $(CFLAGS) version.c $(link) $(LINKARGS1) -out:$(VIM).exe $(OBJ) $(GUI_OBJ) $(OLE_OBJ) \ - $(LUA_OBJ) $(MZSCHEME_OBJ) $(PERL_OBJ) $(PYTHON_OBJ) $(RUBY_OBJ) \ + $(LUA_OBJ) $(MZSCHEME_OBJ) $(PERL_OBJ) $(PYTHON_OBJ) $(PYTHON3_OBJ) $(RUBY_OBJ) \ $(TCL_OBJ) $(SNIFF_OBJ) $(CSCOPE_OBJ) $(NETBEANS_OBJ) \ $(XPM_OBJ) $(OUTDIR)\version.obj $(LINKARGS2) @@ -1016,6 +1052,9 @@ mzscheme_base.c: $(OUTDIR)/if_python.obj: $(OUTDIR) if_python.c $(INCL) $(CC) $(CFLAGS) $(PYTHON_INC) if_python.c +$(OUTDIR)/if_python3.obj: $(OUTDIR) if_python3.c $(INCL) + $(CC) $(CFLAGS) $(PYTHON3_INC) if_python3.c + $(OUTDIR)/if_ole.obj: $(OUTDIR) if_ole.cpp $(INCL) if_ole.h $(OUTDIR)/if_ruby.obj: $(OUTDIR) if_ruby.c $(INCL) diff --git a/src/Makefile b/src/Makefile index 6dfe2bff8..b01029f94 100644 --- a/src/Makefile +++ b/src/Makefile @@ -41,6 +41,7 @@ # --enable-luainterp for Lua interpreter # --enable-perlinterp for Perl interpreter # --enable-pythoninterp for Python interpreter +# --enable-python3interp for Python3 interpreter # --enable-rubyinterp for Ruby interpreter # --enable-tclinterp for Tcl interpreter # --enable-cscope for Cscope interface @@ -383,7 +384,12 @@ CClink = $(CC) # NOTE: This may cause threading to be enabled, which has side effects (such # as using different libraries and debugging becomes more difficult). # NOTE: Using this together with Perl may cause a crash in initialization. +# For Python3 support make a symbolic link in /usr/local/bin: +# ln -s python3 python3.1 +# If both python2.x and python3.x are enabled then the linking will be via +# dlopen(), dlsym(), dlclose(), i.e. pythonX.Y.so must be available #CONF_OPT_PYTHON = --enable-pythoninterp +#CONF_OPT_PYTHON3 = --enable-python3interp # TCL # Uncomment this when you want to include the Tcl interface. @@ -1304,7 +1310,7 @@ SHELL = /bin/sh .SUFFIXES: .c .o .pro PRE_DEFS = -Iproto $(DEFS) $(GUI_DEFS) $(GUI_IPATH) $(CPPFLAGS) $(EXTRA_IPATHS) -POST_DEFS = $(X_CFLAGS) $(LUA_CFLAGS) $(MZSCHEME_CFLAGS) $(PERL_CFLAGS) $(ECL_CFLAGS) $(PYTHON_CFLAGS) $(TCL_CFLAGS) $(RUBY_CFLAGS) $(EXTRA_DEFS) +POST_DEFS = $(X_CFLAGS) $(LUA_CFLAGS) $(MZSCHEME_CFLAGS) $(PERL_CFLAGS) $(ECL_CFLAGS) $(TCL_CFLAGS) $(RUBY_CFLAGS) $(EXTRA_DEFS) ALL_CFLAGS = $(PRE_DEFS) $(CFLAGS) $(PROFILE_CFLAGS) $(POST_DEFS) @@ -1319,7 +1325,23 @@ LINT_EXTRA = -DUSE_SNIFF -DHANGUL_INPUT -D"__attribute__(x)=" DEPEND_CFLAGS = -DPROTO -DDEPEND -DFEAT_GUI $(LINT_CFLAGS) ALL_LIB_DIRS = $(GUI_LIBS_DIR) $(X_LIBS_DIR) -ALL_LIBS = $(GUI_LIBS1) $(GUI_X_LIBS) $(GUI_LIBS2) $(X_PRE_LIBS) $(X_LIBS) $(X_EXTRA_LIBS) $(LIBS) $(EXTRA_LIBS) $(LUA_LIBS) $(MZSCHEME_LIBS) $(PERL_LIBS) $(PYTHON_LIBS) $(TCL_LIBS) $(RUBY_LIBS) $(PROFILE_LIBS) +ALL_LIBS = \ + $(GUI_LIBS1) \ + $(GUI_X_LIBS) \ + $(GUI_LIBS2) \ + $(X_PRE_LIBS) \ + $(X_LIBS) \ + $(X_EXTRA_LIBS) \ + $(LIBS) \ + $(EXTRA_LIBS) \ + $(LUA_LIBS) \ + $(MZSCHEME_LIBS) \ + $(PERL_LIBS) \ + $(PYTHON_LIBS) \ + $(PYTHON3_LIBS) \ + $(TCL_LIBS) \ + $(RUBY_LIBS) \ + $(PROFILE_LIBS) # abbreviations DEST_BIN = $(DESTDIR)$(BINDIR) @@ -1422,15 +1444,24 @@ BASIC_SRC = \ window.c \ $(OS_EXTRA_SRC) -SRC = $(BASIC_SRC) $(GUI_SRC) $(HANGULIN_SRC) $(LUA_SRC) $(MZSCHEME_SRC) \ - $(PERL_SRC) $(PYTHON_SRC) $(TCL_SRC) $(RUBY_SRC) \ - $(SNIFF_SRC) $(WORKSHOP_SRC) $(WSDEBUG_SRC) +SRC = $(BASIC_SRC) \ + $(GUI_SRC) \ + $(HANGULIN_SRC) \ + $(LUA_SRC) \ + $(MZSCHEME_SRC) \ + $(PERL_SRC) \ + $(PYTHON_SRC) $(PYTHON3_SRC) \ + $(TCL_SRC) \ + $(RUBY_SRC) \ + $(SNIFF_SRC) \ + $(WORKSHOP_SRC) \ + $(WSDEBUG_SRC) TAGS_SRC = *.c *.cpp if_perl.xs EXTRA_SRC = hangulin.c if_lua.c if_mzsch.c auto/if_perl.c if_perlsfio.c \ - if_python.c if_tcl.c if_ruby.c if_sniff.c gui_beval.c \ - workshop.c wsdebug.c integration.c netbeans.c + if_python.c if_python3.c if_tcl.c if_ruby.c if_sniff.c \ + gui_beval.c workshop.c wsdebug.c integration.c netbeans.c # All sources, also the ones that are not configured ALL_SRC = $(BASIC_SRC) $(ALL_GUI_SRC) $(EXTRA_SRC) @@ -1438,7 +1469,7 @@ ALL_SRC = $(BASIC_SRC) $(ALL_GUI_SRC) $(EXTRA_SRC) # Which files to check with lint. Select one of these three lines. ALL_SRC # checks more, but may not work well for checking a GUI that wasn't configured. # The perl sources also don't work well with lint. -LINT_SRC = $(BASIC_SRC) $(GUI_SRC) $(HANGULIN_SRC) $(PYTHON_SRC) $(TCL_SRC) \ +LINT_SRC = $(BASIC_SRC) $(GUI_SRC) $(HANGULIN_SRC) $(PYTHON_SRC) $(PYTHON3_SRC) $(TCL_SRC) \ $(SNIFF_SRC) $(WORKSHOP_SRC) $(WSDEBUG_SRC) $(NETBEANS_SRC) #LINT_SRC = $(SRC) #LINT_SRC = $(ALL_SRC) @@ -1499,6 +1530,7 @@ OBJ = \ $(MZSCHEME_OBJ) \ $(PERL_OBJ) \ $(PYTHON_OBJ) \ + $(PYTHON3_OBJ) \ $(TCL_OBJ) \ $(RUBY_OBJ) \ $(OS_EXTRA_OBJ) \ @@ -1528,6 +1560,7 @@ PRO_AUTO = \ if_cscope.pro \ if_xcmdsrv.pro \ if_python.pro \ + if_python3.pro \ if_ruby.pro \ main.pro \ mark.pro \ @@ -1589,7 +1622,7 @@ config auto/config.mk: auto/configure config.mk.in config.h.in CC="$(CC)" CPPFLAGS="$(CPPFLAGS)" CFLAGS="$(CFLAGS)" \ LDFLAGS="$(LDFLAGS)" $(CONF_SHELL) srcdir="$(srcdir)" \ ./configure $(CONF_OPT_GUI) $(CONF_OPT_X) $(CONF_OPT_XSMP) \ - $(CONF_OPT_DARWIN) $(CONF_OPT_PERL) $(CONF_OPT_PYTHON) \ + $(CONF_OPT_DARWIN) $(CONF_OPT_PERL) $(CONF_OPT_PYTHON) $(CONF_OPT_PYTHON3) \ $(CONF_OPT_TCL) $(CONF_OPT_RUBY) $(CONF_OPT_NLS) \ $(CONF_OPT_CSCOPE) $(CONF_OPT_MULTIBYTE) $(CONF_OPT_INPUT) \ $(CONF_OPT_OUTPUT) $(CONF_OPT_GPM) $(CONF_OPT_WORKSHOP) \ @@ -2464,8 +2497,24 @@ objects/if_perl.o: auto/if_perl.c objects/if_perlsfio.o: if_perlsfio.c $(CCC) -o $@ if_perlsfio.c -objects/if_python.o: if_python.c - $(CCC) -o $@ $(PYTHON_CFLAGS_EXTRA) if_python.c +objects/py_config.o: $(PYTHON_CONFDIR)/config.c + $(CCC) $(PYTHON_CFLAGS) -o $@ $(PYTHON_CONFDIR)/config.c \ + -I$(PYTHON_CONFDIR) -DHAVE_CONFIG_H -DNO_MAIN + +objects/py_getpath.o: $(PYTHON_CONFDIR)/getpath.c + $(CCC) $(PYTHON_CFLAGS) -o $@ $(PYTHON_CONFDIR)/getpath.c \ + -I$(PYTHON_CONFDIR) -DHAVE_CONFIG_H -DNO_MAIN \ + $(PYTHON_GETPATH_CFLAGS) + +objects/py3_config.o: $(PYTHON3_CONFDIR)/config.c + $(CCC) $(PYTHON3_CFLAGS) -o $@ $(PYTHON3_CONFDIR)/config.c \ + -I$(PYTHON3_CONFDIR) -DHAVE_CONFIG_H -DNO_MAIN + +objects/if_python.o: if_python.c + $(CCC) $(PYTHON_CFLAGS) $(PYTHON_CFLAGS_EXTRA) -o $@ if_python.c + +objects/if_python3.o: if_python3.c + $(CCC) $(PYTHON3_CFLAGS) $(PYTHON3_CFLAGS_EXTRA) -o $@ if_python3.c objects/if_ruby.o: if_ruby.c $(CCC) -o $@ if_ruby.c @@ -2536,15 +2585,6 @@ objects/os_unix.o: os_unix.c objects/pathdef.o: auto/pathdef.c $(CCC) -o $@ auto/pathdef.c -objects/py_config.o: $(PYTHON_CONFDIR)/config.c - $(CCC) -o $@ $(PYTHON_CONFDIR)/config.c \ - -I$(PYTHON_CONFDIR) -DHAVE_CONFIG_H -DNO_MAIN - -objects/py_getpath.o: $(PYTHON_CONFDIR)/getpath.c - $(CCC) -o $@ $(PYTHON_CONFDIR)/getpath.c \ - -I$(PYTHON_CONFDIR) -DHAVE_CONFIG_H -DNO_MAIN \ - $(PYTHON_GETPATH_CFLAGS) - objects/pty.o: pty.c $(CCC) -o $@ pty.c @@ -2978,6 +3018,10 @@ objects/if_python.o: if_python.c vim.h auto/config.h feature.h os_unix.h \ auto/osdef.h ascii.h keymap.h term.h macros.h option.h structs.h \ regexp.h gui.h gui_beval.h proto/gui_beval.pro ex_cmds.h proto.h \ globals.h farsi.h arabic.h +objects/if_python3.o: if_python3.c vim.h auto/config.h feature.h os_unix.h \ + auto/osdef.h ascii.h keymap.h term.h macros.h option.h structs.h \ + regexp.h gui.h gui_beval.h proto/gui_beval.pro ex_cmds.h proto.h \ + globals.h farsi.h arabic.h objects/if_tcl.o: if_tcl.c vim.h auto/config.h feature.h os_unix.h auto/osdef.h \ ascii.h keymap.h term.h macros.h option.h structs.h regexp.h gui.h \ gui_beval.h proto/gui_beval.pro ex_cmds.h proto.h globals.h farsi.h \ diff --git a/src/auto/configure b/src/auto/configure index 6d7780657..5d083cb14 100755 --- a/src/auto/configure +++ b/src/auto/configure @@ -639,6 +639,12 @@ TCL_PRO TCL_OBJ TCL_SRC vi_cv_path_tcl +PYTHON3_OBJ +PYTHON3_SRC +PYTHON3_CFLAGS +PYTHON3_LIBS +PYTHON3_CONFDIR +vi_cv_path_python3 PYTHON_OBJ PYTHON_SRC PYTHON_CFLAGS @@ -755,6 +761,8 @@ with_plthome enable_perlinterp enable_pythoninterp with_python_config_dir +enable_python3interp +with_python3_config_dir enable_tclinterp with_tclsh enable_rubyinterp @@ -1417,6 +1425,7 @@ Optional Features: --enable-mzschemeinterp Include MzScheme interpreter. --enable-perlinterp Include Perl interpreter. --enable-pythoninterp Include Python interpreter. + --enable-python3interp Include Python3 interpreter. --enable-tclinterp Include Tcl interpreter. --enable-rubyinterp Include Ruby interpreter. --enable-cscope Include cscope interface. @@ -1458,6 +1467,7 @@ Optional Packages: --with-lua-prefix=PFX Prefix where Lua is installed. --with-plthome=PLTHOME Use PLTHOME. --with-python-config-dir=PATH Python's config directory + --with-python3-config-dir=PATH Python's config directory --with-tclsh=PATH which tclsh to use (default: tclsh8.0) --with-ruby-command=RUBY name of the Ruby command (default: ruby) --with-x use the X Window System @@ -5362,6 +5372,286 @@ fi + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking --enable-python3interp argument" >&5 +$as_echo_n "checking --enable-python3interp argument... " >&6; } +# Check whether --enable-python3interp was given. +if test "${enable_python3interp+set}" = set; then : + enableval=$enable_python3interp; +else + enable_python3interp="no" +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $enable_python3interp" >&5 +$as_echo "$enable_python3interp" >&6; } +if test "$enable_python3interp" = "yes"; then + # Extract the first word of "python3", so it can be a program name with args. +set dummy python3; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if test "${ac_cv_path_vi_cv_path_python3+set}" = set; then : + $as_echo_n "(cached) " >&6 +else + case $vi_cv_path_python3 in + [\\/]* | ?:[\\/]*) + ac_cv_path_vi_cv_path_python3="$vi_cv_path_python3" # Let the user override the test with a path. + ;; + *) + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_path_vi_cv_path_python3="$as_dir/$ac_word$ac_exec_ext" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + + ;; +esac +fi +vi_cv_path_python3=$ac_cv_path_vi_cv_path_python3 +if test -n "$vi_cv_path_python3"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $vi_cv_path_python3" >&5 +$as_echo "$vi_cv_path_python3" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + if test "X$vi_cv_path_python3" != "X"; then + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking Python version" >&5 +$as_echo_n "checking Python version... " >&6; } +if test "${vi_cv_var_python3_version+set}" = set; then : + $as_echo_n "(cached) " >&6 +else + vi_cv_var_python3_version=` + ${vi_cv_path_python3} -c 'import sys; print(sys.version[1:3])'` + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $vi_cv_var_python3_version" >&5 +$as_echo "$vi_cv_var_python3_version" >&6; } + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking Python's install prefix" >&5 +$as_echo_n "checking Python's install prefix... " >&6; } +if test "${vi_cv_path_python3_pfx+set}" = set; then : + $as_echo_n "(cached) " >&6 +else + vi_cv_path_python3_pfx=` + ${vi_cv_path_python3} -c \ + "import sys; print(sys.prefix)"` +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $vi_cv_path_python3_pfx" >&5 +$as_echo "$vi_cv_path_python3_pfx" >&6; } + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking Python's execution prefix" >&5 +$as_echo_n "checking Python's execution prefix... " >&6; } +if test "${vi_cv_path_python3_epfx+set}" = set; then : + $as_echo_n "(cached) " >&6 +else + vi_cv_path_python3_epfx=` + ${vi_cv_path_python3} -c \ + "import sys; print(sys.exec_prefix)"` +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $vi_cv_path_python3_epfx" >&5 +$as_echo "$vi_cv_path_python3_epfx" >&6; } + + + if test "${vi_cv_path_python3path+set}" = set; then : + $as_echo_n "(cached) " >&6 +else + vi_cv_path_python3path=` + unset PYTHONPATH; + ${vi_cv_path_python3} -c \ + "import sys, string; print(':'.join(sys.path))"` +fi + + + + +# Check whether --with-python3-config-dir was given. +if test "${with_python3_config_dir+set}" = set; then : + withval=$with_python3_config_dir; vi_cv_path_python3_conf="${withval}" +fi + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking Python's configuration directory" >&5 +$as_echo_n "checking Python's configuration directory... " >&6; } +if test "${vi_cv_path_python3_conf+set}" = set; then : + $as_echo_n "(cached) " >&6 +else + + vi_cv_path_python3_conf= + for path in "${vi_cv_path_python3_pfx}" "${vi_cv_path_python3_epfx}"; do + for subdir in lib share; do + d="${path}/${subdir}/python3${vi_cv_var_python3_version}/config" + if test -d "$d" && test -f "$d/config.c"; then + vi_cv_path_python3_conf="$d" + fi + done + done + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $vi_cv_path_python3_conf" >&5 +$as_echo "$vi_cv_path_python3_conf" >&6; } + + PYTHON3_CONFDIR="${vi_cv_path_python3_conf}" + + if test "X$PYTHON3_CONFDIR" = "X"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: can't find it!" >&5 +$as_echo "can't find it!" >&6; } + else + + if test "${vi_cv_path_python3_plibs+set}" = set; then : + $as_echo_n "(cached) " >&6 +else + + pwd=`pwd` + tmp_mkf="$pwd/config-PyMake$$" + cat -- "${PYTHON3_CONFDIR}/Makefile" - <<'eof' >"${tmp_mkf}" +__: + @echo "python3_MODLIBS='$(MODLIBS)'" + @echo "python3_LIBS='$(LIBS)'" + @echo "python3_SYSLIBS='$(SYSLIBS)'" + @echo "python3_LINKFORSHARED='$(LINKFORSHARED)'" +eof + eval "`cd ${PYTHON3_CONFDIR} && make -f "${tmp_mkf}" __ | sed '/ directory /d'`" + rm -f -- "${tmp_mkf}" + vi_cv_path_python3_plibs="-L${PYTHON3_CONFDIR} -lpython3${vi_cv_var_python3_version}" + vi_cv_path_python3_plibs="${vi_cv_path_python3_plibs} ${python3_MODLIBS} ${python3_LIBS} ${python3_SYSLIBS} ${python3_LINKFORSHARED}" + vi_cv_path_python3_plibs=`echo $vi_cv_path_python3_plibs | sed s/-ltermcap//` + vi_cv_path_python3_plibs=`echo $vi_cv_path_python3_plibs | sed s/-lffi//` + +fi + + + PYTHON3_LIBS="${vi_cv_path_python3_plibs}" + if test "${vi_cv_path_python3_pfx}" = "${vi_cv_path_python3_epfx}"; then + PYTHON3_CFLAGS="-I${vi_cv_path_python3_pfx}/include/python3${vi_cv_var_python3_version}" + else + PYTHON3_CFLAGS="-I${vi_cv_path_python3_pfx}/include/python3${vi_cv_var_python3_version} -I${vi_cv_path_python3_epfx}/include/python3${vi_cv_var_python3_version}" + fi + PYTHON3_SRC="if_python3.c" + if test "x$MACOSX" = "xyes"; then + PYTHON3_OBJ="objects/if_python3.o" + else + PYTHON3_OBJ="objects/if_python3.o objects/py3_config.o" + fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if -pthread should be used" >&5 +$as_echo_n "checking if -pthread should be used... " >&6; } + threadsafe_flag= + thread_lib= + if test "`(uname) 2>/dev/null`" != Darwin; then + test "$GCC" = yes && threadsafe_flag="-pthread" + if test "`(uname) 2>/dev/null`" = FreeBSD; then + threadsafe_flag="-D_THREAD_SAFE" + thread_lib="-pthread" + fi + fi + libs_save_old=$LIBS + if test -n "$threadsafe_flag"; then + cflags_save=$CFLAGS + CFLAGS="$CFLAGS $threadsafe_flag" + LIBS="$LIBS $thread_lib" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; }; PYTHON3_CFLAGS="$PYTHON3_CFLAGS $threadsafe_flag" +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; }; LIBS=$libs_save_old + +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + CFLAGS=$cflags_save + else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if compile and link flags for Python are sane" >&5 +$as_echo_n "checking if compile and link flags for Python are sane... " >&6; } + cflags_save=$CFLAGS + libs_save=$LIBS + CFLAGS="$CFLAGS $PYTHON3_CFLAGS" + LIBS="$LIBS $PYTHON3_LIBS" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; }; python3_ok=yes +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no: PYTHON3 DISABLED" >&5 +$as_echo "no: PYTHON3 DISABLED" >&6; }; python3_ok=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + CFLAGS=$cflags_save + LIBS=$libs_save + if test "$python3_ok" = yes; then + $as_echo "#define FEAT_PYTHON3 1" >>confdefs.h + + else + LIBS=$libs_save_old + PYTHON3_SRC= + PYTHON3_OBJ= + PYTHON3_LIBS= + PYTHON3_CFLAGS= + fi + fi + fi +fi + + + + + + + +if test "$python_ok" = yes && test "$python3_ok" = yes; then + $as_echo "#define DYNAMIC_PYTHON 1" >>confdefs.h + + $as_echo "#define DYNAMIC_PYTHON3 1" >>confdefs.h + + PYTHON_SRC="if_python.c" + PYTHON_OBJ="objects/if_python.o" + PYTHON_CFLAGS="$PYTHON_CFLAGS -DDYNAMIC_PYTHON_DLL=\\\"libpython${vi_cv_var_python_version}.so\\\"" + PYTHON_LIBS= + PYTHON3_SRC="if_python3.c" + PYTHON3_OBJ="objects/if_python3.o" + PYTHON3_CFLAGS="$PYTHON3_CFLAGS -DDYNAMIC_PYTHON3_DLL=\\\"libpython3${vi_cv_var_python3_version}.so\\\"" + PYTHON3_LIBS= +fi + { $as_echo "$as_me:${as_lineno-$LINENO}: checking --enable-tclinterp argument" >&5 $as_echo_n "checking --enable-tclinterp argument... " >&6; } # Check whether --enable-tclinterp was given. diff --git a/src/buffer.c b/src/buffer.c index 1a3497e74..e7fecd7da 100644 --- a/src/buffer.c +++ b/src/buffer.c @@ -607,6 +607,9 @@ free_buffer(buf) #ifdef FEAT_PYTHON python_buffer_free(buf); #endif +#ifdef FEAT_PYTHON3 + python3_buffer_free(buf); +#endif #ifdef FEAT_RUBY ruby_buffer_free(buf); #endif diff --git a/src/config.h.in b/src/config.h.in index 937fd1ec2..5570f2baf 100644 --- a/src/config.h.in +++ b/src/config.h.in @@ -328,6 +328,15 @@ /* Define if you want to include the Python interpreter. */ #undef FEAT_PYTHON +/* Define if you want to include the Python3 interpreter. */ +#undef FEAT_PYTHON3 + +/* Define for linking via dlopen() or LoadLibrary() */ +#undef DYNAMIC_PYTHON + +/* Define for linking via dlopen() or LoadLibrary() */ +#undef DYNAMIC_PYTHON3 + /* Define if you want to include the Ruby interpreter. */ #undef FEAT_RUBY diff --git a/src/config.mk.in b/src/config.mk.in index 3676d7ed2..7154114ed 100644 --- a/src/config.mk.in +++ b/src/config.mk.in @@ -66,6 +66,12 @@ PYTHON_LIBS = @PYTHON_LIBS@ PYTHON_CONFDIR = @PYTHON_CONFDIR@ PYTHON_GETPATH_CFLAGS = @PYTHON_GETPATH_CFLAGS@ +PYTHON3_SRC = @PYTHON3_SRC@ +PYTHON3_OBJ = @PYTHON3_OBJ@ +PYTHON3_CFLAGS = @PYTHON3_CFLAGS@ +PYTHON3_LIBS = @PYTHON3_LIBS@ +PYTHON3_CONFDIR = @PYTHON3_CONFDIR@ + TCL = @vi_cv_path_tcl@ TCL_SRC = @TCL_SRC@ TCL_OBJ = @TCL_OBJ@ diff --git a/src/configure.in b/src/configure.in index 5896189e9..a6fbc8540 100644 --- a/src/configure.in +++ b/src/configure.in @@ -878,7 +878,7 @@ eof AC_MSG_RESULT(no) fi - dnl check that compiling a simple program still works with the flags + dnl Check that compiling a simple program still works with the flags dnl added for Python. AC_MSG_CHECKING([if compile and link flags for Python are sane]) cflags_save=$CFLAGS @@ -906,6 +906,7 @@ eof fi fi fi + AC_SUBST(PYTHON_CONFDIR) AC_SUBST(PYTHON_LIBS) AC_SUBST(PYTHON_GETPATH_CFLAGS) @@ -913,6 +914,183 @@ AC_SUBST(PYTHON_CFLAGS) AC_SUBST(PYTHON_SRC) AC_SUBST(PYTHON_OBJ) + +AC_MSG_CHECKING(--enable-python3interp argument) +AC_ARG_ENABLE(python3interp, + [ --enable-python3interp Include Python3 interpreter.], , + [enable_python3interp="no"]) +AC_MSG_RESULT($enable_python3interp) +if test "$enable_python3interp" = "yes"; then + dnl -- find the python3 executable + AC_PATH_PROG(vi_cv_path_python3, python3) + if test "X$vi_cv_path_python3" != "X"; then + + dnl -- get its version number + AC_CACHE_CHECK(Python version,vi_cv_var_python3_version, + [[vi_cv_var_python3_version=` + ${vi_cv_path_python3} -c 'import sys; print(sys.version[1:3])'` + ]]) + + dnl -- find where python3 thinks it was installed + AC_CACHE_CHECK(Python's install prefix,vi_cv_path_python3_pfx, + [ vi_cv_path_python3_pfx=` + ${vi_cv_path_python3} -c \ + "import sys; print(sys.prefix)"` ]) + + dnl -- and where it thinks it runs + AC_CACHE_CHECK(Python's execution prefix,vi_cv_path_python3_epfx, + [ vi_cv_path_python3_epfx=` + ${vi_cv_path_python3} -c \ + "import sys; print(sys.exec_prefix)"` ]) + + dnl -- python3's internal library path + + AC_CACHE_VAL(vi_cv_path_python3path, + [ vi_cv_path_python3path=` + unset PYTHONPATH; + ${vi_cv_path_python3} -c \ + "import sys, string; print(':'.join(sys.path))"` ]) + + dnl -- where the Python implementation library archives are + + AC_ARG_WITH(python3-config-dir, + [ --with-python3-config-dir=PATH Python's config directory], + [ vi_cv_path_python3_conf="${withval}" ] ) + + AC_CACHE_CHECK(Python's configuration directory,vi_cv_path_python3_conf, + [ + vi_cv_path_python3_conf= + for path in "${vi_cv_path_python3_pfx}" "${vi_cv_path_python3_epfx}"; do + for subdir in lib share; do + d="${path}/${subdir}/python3${vi_cv_var_python3_version}/config" + if test -d "$d" && test -f "$d/config.c"; then + vi_cv_path_python3_conf="$d" + fi + done + done + ]) + + PYTHON3_CONFDIR="${vi_cv_path_python3_conf}" + + if test "X$PYTHON3_CONFDIR" = "X"; then + AC_MSG_RESULT([can't find it!]) + else + + dnl -- we need to examine Python's config/Makefile too + dnl see what the interpreter is built from + AC_CACHE_VAL(vi_cv_path_python3_plibs, + [ + pwd=`pwd` + tmp_mkf="$pwd/config-PyMake$$" + cat -- "${PYTHON3_CONFDIR}/Makefile" - <<'eof' >"${tmp_mkf}" +__: + @echo "python3_MODLIBS='$(MODLIBS)'" + @echo "python3_LIBS='$(LIBS)'" + @echo "python3_SYSLIBS='$(SYSLIBS)'" + @echo "python3_LINKFORSHARED='$(LINKFORSHARED)'" +eof + dnl -- delete the lines from make about Entering/Leaving directory + eval "`cd ${PYTHON3_CONFDIR} && make -f "${tmp_mkf}" __ | sed '/ directory /d'`" + rm -f -- "${tmp_mkf}" + vi_cv_path_python3_plibs="-L${PYTHON3_CONFDIR} -lpython3${vi_cv_var_python3_version}" + vi_cv_path_python3_plibs="${vi_cv_path_python3_plibs} ${python3_MODLIBS} ${python3_LIBS} ${python3_SYSLIBS} ${python3_LINKFORSHARED}" + dnl remove -ltermcap, it can conflict with an earlier -lncurses + vi_cv_path_python3_plibs=`echo $vi_cv_path_python3_plibs | sed s/-ltermcap//` + vi_cv_path_python3_plibs=`echo $vi_cv_path_python3_plibs | sed s/-lffi//` + ]) + + PYTHON3_LIBS="${vi_cv_path_python3_plibs}" + if test "${vi_cv_path_python3_pfx}" = "${vi_cv_path_python3_epfx}"; then + PYTHON3_CFLAGS="-I${vi_cv_path_python3_pfx}/include/python3${vi_cv_var_python3_version}" + else + PYTHON3_CFLAGS="-I${vi_cv_path_python3_pfx}/include/python3${vi_cv_var_python3_version} -I${vi_cv_path_python3_epfx}/include/python3${vi_cv_var_python3_version}" + fi + PYTHON3_SRC="if_python3.c" + dnl For Mac OSX 10.2 config.o is included in the Python library. + if test "x$MACOSX" = "xyes"; then + PYTHON3_OBJ="objects/if_python3.o" + else + PYTHON3_OBJ="objects/if_python3.o objects/py3_config.o" + fi + + dnl On FreeBSD linking with "-pthread" is required to use threads. + dnl _THREAD_SAFE must be used for compiling then. + dnl The "-pthread" is added to $LIBS, so that the following check for + dnl sigaltstack() will look in libc_r (it's there in libc!). + dnl Otherwise, when using GCC, try adding -pthread to $CFLAGS. GCC + dnl will then define target-specific defines, e.g., -D_REENTRANT. + dnl Don't do this for Mac OSX, -pthread will generate a warning. + AC_MSG_CHECKING([if -pthread should be used]) + threadsafe_flag= + thread_lib= + dnl if test "x$MACOSX" != "xyes"; then + if test "`(uname) 2>/dev/null`" != Darwin; then + test "$GCC" = yes && threadsafe_flag="-pthread" + if test "`(uname) 2>/dev/null`" = FreeBSD; then + threadsafe_flag="-D_THREAD_SAFE" + thread_lib="-pthread" + fi + fi + libs_save_old=$LIBS + if test -n "$threadsafe_flag"; then + cflags_save=$CFLAGS + CFLAGS="$CFLAGS $threadsafe_flag" + LIBS="$LIBS $thread_lib" + AC_TRY_LINK(,[ ], + AC_MSG_RESULT(yes); PYTHON3_CFLAGS="$PYTHON3_CFLAGS $threadsafe_flag", + AC_MSG_RESULT(no); LIBS=$libs_save_old + ) + CFLAGS=$cflags_save + else + AC_MSG_RESULT(no) + fi + + dnl check that compiling a simple program still works with the flags + dnl added for Python. + AC_MSG_CHECKING([if compile and link flags for Python are sane]) + cflags_save=$CFLAGS + libs_save=$LIBS + CFLAGS="$CFLAGS $PYTHON3_CFLAGS" + LIBS="$LIBS $PYTHON3_LIBS" + AC_TRY_LINK(,[ ], + AC_MSG_RESULT(yes); python3_ok=yes, + AC_MSG_RESULT(no: PYTHON3 DISABLED); python3_ok=no) + CFLAGS=$cflags_save + LIBS=$libs_save + if test "$python3_ok" = yes; then + AC_DEFINE(FEAT_PYTHON3) + else + LIBS=$libs_save_old + PYTHON3_SRC= + PYTHON3_OBJ= + PYTHON3_LIBS= + PYTHON3_CFLAGS= + fi + fi + fi +fi + +AC_SUBST(PYTHON3_CONFDIR) +AC_SUBST(PYTHON3_LIBS) +AC_SUBST(PYTHON3_CFLAGS) +AC_SUBST(PYTHON3_SRC) +AC_SUBST(PYTHON3_OBJ) + +dnl if python2.x and python3.x are enabled one can only link in code +dnl with dlopen(), dlsym(), dlclose() +if test "$python_ok" = yes && test "$python3_ok" = yes; then + AC_DEFINE(DYNAMIC_PYTHON) + AC_DEFINE(DYNAMIC_PYTHON3) + PYTHON_SRC="if_python.c" + PYTHON_OBJ="objects/if_python.o" + PYTHON_CFLAGS="$PYTHON_CFLAGS -DDYNAMIC_PYTHON_DLL=\\\"libpython${vi_cv_var_python_version}.so\\\"" + PYTHON_LIBS= + PYTHON3_SRC="if_python3.c" + PYTHON3_OBJ="objects/if_python3.o" + PYTHON3_CFLAGS="$PYTHON3_CFLAGS -DDYNAMIC_PYTHON3_DLL=\\\"libpython3${vi_cv_var_python3_version}.so\\\"" + PYTHON3_LIBS= +fi + AC_MSG_CHECKING(--enable-tclinterp argument) AC_ARG_ENABLE(tclinterp, [ --enable-tclinterp Include Tcl interpreter.], , diff --git a/src/eval.c b/src/eval.c index e10b453d6..295512e34 100644 --- a/src/eval.c +++ b/src/eval.c @@ -5911,8 +5911,8 @@ list_equal(l1, l2, ic) return item1 == NULL && item2 == NULL; } -#if defined(FEAT_RUBY) || defined(FEAT_PYTHON) || defined(FEAT_MZSCHEME) \ - || defined(FEAT_LUA) || defined(PROTO) +#if defined(FEAT_RUBY) || defined(FEAT_PYTHON) || defined(FEAT_PYTHON3) \ + || defined(FEAT_MZSCHEME) || defined(FEAT_LUA) || defined(PROTO) /* * Return the dictitem that an entry in a hashtable points to. */ @@ -11991,6 +11991,11 @@ f_has(argvars, rettv) "python", #endif #endif +#ifdef FEAT_PYTHON3 +#ifndef DYNAMIC_PYTHON3 + "python3", +#endif +#endif #ifdef FEAT_POSTSCRIPT "postscript", #endif @@ -12184,10 +12189,18 @@ f_has(argvars, rettv) else if (STRICMP(name, "ruby") == 0) n = ruby_enabled(FALSE); #endif +#ifdef FEAT_PYTHON #ifdef DYNAMIC_PYTHON else if (STRICMP(name, "python") == 0) n = python_enabled(FALSE); #endif +#endif +#ifdef FEAT_PYTHON3 +#ifdef DYNAMIC_PYTHON3 + else if (STRICMP(name, "python3") == 0) + n = python3_enabled(FALSE); +#endif +#endif #ifdef DYNAMIC_PERL else if (STRICMP(name, "perl") == 0) n = perl_enabled(FALSE); diff --git a/src/ex_cmds.h b/src/ex_cmds.h index 64a5fb187..3e83ef7b3 100644 --- a/src/ex_cmds.h +++ b/src/ex_cmds.h @@ -741,6 +741,10 @@ EX(CMD_python, "python", ex_python, RANGE|EXTRA|NEEDARG|CMDWIN), EX(CMD_pyfile, "pyfile", ex_pyfile, RANGE|FILE1|NEEDARG|CMDWIN), +EX(CMD_python3, "py3", ex_python3, + RANGE|EXTRA|NEEDARG|CMDWIN), +EX(CMD_py3file, "py3file", ex_py3file, + RANGE|FILE1|NEEDARG|CMDWIN), EX(CMD_quit, "quit", ex_quit, BANG|TRLBAR|CMDWIN), EX(CMD_quitall, "quitall", ex_quit_all, diff --git a/src/ex_docmd.c b/src/ex_docmd.c index 499ff1626..590f36b32 100644 --- a/src/ex_docmd.c +++ b/src/ex_docmd.c @@ -129,8 +129,8 @@ static int getargopt __ARGS((exarg_T *eap)); static int check_more __ARGS((int, int)); static linenr_T get_address __ARGS((char_u **, int skip, int to_other_file)); static void get_flags __ARGS((exarg_T *eap)); -#if !defined(FEAT_PERL) || !defined(FEAT_PYTHON) || !defined(FEAT_TCL) \ - || !defined(FEAT_RUBY) || !defined(FEAT_MZSCHEME) +#if !defined(FEAT_PERL) || !defined(FEAT_PYTHON) || !defined(FEAT_PYTHON3) \ + || !defined(FEAT_TCL) || !defined(FEAT_RUBY) || !defined(FEAT_MZSCHEME) # define HAVE_EX_SCRIPT_NI static void ex_script_ni __ARGS((exarg_T *eap)); #endif @@ -265,6 +265,10 @@ static void ex_popup __ARGS((exarg_T *eap)); # define ex_python ex_script_ni # define ex_pyfile ex_ni #endif +#ifndef FEAT_PYTHON3 +# define ex_python3 ex_script_ni +# define ex_py3file ex_ni +#endif #ifndef FEAT_TCL # define ex_tcl ex_script_ni # define ex_tcldo ex_ni @@ -2554,6 +2558,7 @@ do_one_cmd(cmdlinep, sourcing, case CMD_perl: case CMD_psearch: case CMD_python: + case CMD_python3: case CMD_return: case CMD_rightbelow: case CMD_ruby: @@ -2816,6 +2821,10 @@ find_command(eap, full) { while (ASCII_ISALPHA(*p)) ++p; + /* for python 3.x support (:py3, :python3) */ + if (eap->cmd[0] == 'p' && eap->cmd[1] == 'y') + p = skipdigits(p); + /* check for non-alpha command */ if (p == eap->cmd && vim_strchr((char_u *)"@*!=><&~#", *p) != NULL) ++p; diff --git a/src/globals.h b/src/globals.h index e0be8a683..605a3905c 100644 --- a/src/globals.h +++ b/src/globals.h @@ -1415,9 +1415,13 @@ EXTERN char_u e_isadir2[] INIT(= N_("E17: \"%s\" is a directory")); #ifdef FEAT_LIBCALL EXTERN char_u e_libcall[] INIT(= N_("E364: Library call failed for \"%s()\"")); #endif -#if defined(DYNAMIC_PERL) || defined(DYNAMIC_PYTHON) || defined(DYNAMIC_RUBY) \ - || defined(DYNAMIC_TCL) || defined(DYNAMIC_ICONV) \ - || defined(DYNAMIC_GETTEXT) || defined(DYNAMIC_MZSCHEME) \ +#if defined(DYNAMIC_PERL) \ + || defined(DYNAMIC_PYTHON) || defined(DYNAMIC_PYTHON3) \ + || defined(DYNAMIC_RUBY) \ + || defined(DYNAMIC_TCL) \ + || defined(DYNAMIC_ICONV) \ + || defined(DYNAMIC_GETTEXT) \ + || defined(DYNAMIC_MZSCHEME) \ || defined(DYNAMIC_LUA) EXTERN char_u e_loadlib[] INIT(= N_("E370: Could not load library %s")); EXTERN char_u e_loadfunc[] INIT(= N_("E448: Could not load library function %s")); diff --git a/src/if_python.c b/src/if_python.c index e017b7137..ea2e926ab 100644 --- a/src/if_python.c +++ b/src/if_python.c @@ -96,6 +96,19 @@ struct PyMethodDef { Py_ssize_t a; }; # define HINSTANCE long_u /* for generating prototypes */ # endif +#ifndef _WIN32 +# include <dlfcn.h> +# define FARPROC void* +# define HINSTANCE void* +# define load_dll(n) dlopen((n),RTLD_LAZY) +# define close_dll dlclose +# define symbol_from_dll dlsym +#else +# define load_dll LoadLibrary +# define close_dll FreeLibrary +# define symbol_from_dll GetProcAddress +#endif + /* This makes if_python.c compile without warnings against Python 2.5 * on Win32 and Win64. */ #undef PyRun_SimpleString @@ -315,7 +328,7 @@ end_dynamic_python(void) { if (hinstPython) { - FreeLibrary(hinstPython); + close_dll(hinstPython); hinstPython = 0; } } @@ -332,7 +345,7 @@ python_runtime_link_init(char *libname, int verbose) if (hinstPython) return OK; - hinstPython = LoadLibrary(libname); + hinstPython = load_dll(libname); if (!hinstPython) { if (verbose) @@ -342,10 +355,10 @@ python_runtime_link_init(char *libname, int verbose) for (i = 0; python_funcname_table[i].ptr; ++i) { - if ((*python_funcname_table[i].ptr = GetProcAddress(hinstPython, + if ((*python_funcname_table[i].ptr = symbol_from_dll(hinstPython, python_funcname_table[i].name)) == NULL) { - FreeLibrary(hinstPython); + close_dll(hinstPython); hinstPython = 0; if (verbose) EMSG2(_(e_loadfunc), python_funcname_table[i].name); diff --git a/src/if_python3.c b/src/if_python3.c new file mode 100644 index 000000000..1d755e0a9 --- /dev/null +++ b/src/if_python3.c @@ -0,0 +1,2796 @@ +/* vi:set ts=8 sts=4 sw=4: + * + * VIM - Vi IMproved by Bram Moolenaar + * + * Do ":help uganda" in Vim to read copying and usage conditions. + * Do ":help credits" in Vim to see a list of people who contributed. + * See README.txt for an overview of the Vim source code. + */ +/* + * Python extensions by Paul Moore. + * Changes for Unix by David Leonard. + * + * This consists of four parts: + * 1. Python interpreter main program + * 2. Python output stream: writes output via [e]msg(). + * 3. Implementation of the Vim module for Python + * 4. Utility functions for handling the interface between Vim and Python. + */ + +/* + * Roland Puntaier 2009/sept/16: + * Adaptations to support both python3.x and python2.x + */ + +// uncomment this if used with the debug version of python +// #define Py_DEBUG + +#include "vim.h" + +#include <limits.h> + +/* Python.h defines _POSIX_THREADS itself (if needed) */ +#ifdef _POSIX_THREADS +# undef _POSIX_THREADS +#endif + +#if defined(_WIN32) && defined (HAVE_FCNTL_H) +# undef HAVE_FCNTL_H +#endif + +#ifdef _DEBUG +# undef _DEBUG +#endif + +#ifdef HAVE_STDARG_H +# undef HAVE_STDARG_H /* Python's config.h defines it as well. */ +#endif + +#define PY_SSIZE_T_CLEAN + +#ifdef F_BLANK +# undef F_BLANK +#endif + +#ifdef _POSIX_C_SOURCE /* defined in feature.h */ +# undef _POSIX_C_SOURCE +#endif + +#include <Python.h> +#if defined(MACOS) && !defined(MACOS_X_UNIX) +# include "macglue.h" +# include <CodeFragments.h> +#endif +#undef main /* Defined in python.h - aargh */ +#undef HAVE_FCNTL_H /* Clash with os_win32.h */ + +static void init_structs(void); + +#if defined(DYNAMIC_PYTHON3) + +#ifndef _WIN32 +#include <dlfcn.h> +#define FARPROC void* +#define HINSTANCE void* +#define load_dll(n) dlopen((n),RTLD_LAZY) +#define close_dll dlclose +#define symbol_from_dll dlsym +#else +#define load_dll LoadLibrary +#define close_dll FreeLibrary +#define symbol_from_dll GetProcAddress +#endif +/* + * Wrapper defines + */ +#undef PyArg_Parse +# define PyArg_Parse py3_PyArg_Parse +#undef PyArg_ParseTuple +# define PyArg_ParseTuple py3_PyArg_ParseTuple +# define PyDict_SetItemString py3_PyDict_SetItemString +# define PyErr_BadArgument py3_PyErr_BadArgument +# define PyErr_Clear py3_PyErr_Clear +# define PyErr_NoMemory py3_PyErr_NoMemory +# define PyErr_Occurred py3_PyErr_Occurred +# define PyErr_SetNone py3_PyErr_SetNone +# define PyErr_SetString py3_PyErr_SetString +# define PyEval_InitThreads py3_PyEval_InitThreads +# define PyEval_RestoreThread py3_PyEval_RestoreThread +# define PyEval_SaveThread py3_PyEval_SaveThread +# define PyGILState_Ensure py3_PyGILState_Ensure +# define PyGILState_Release py3_PyGILState_Release +# define PyLong_AsLong py3_PyLong_AsLong +# define PyLong_FromLong py3_PyLong_FromLong +# define PyList_GetItem py3_PyList_GetItem +# define PyList_Append py3_PyList_Append +# define PyList_New py3_PyList_New +# define PyList_SetItem py3_PyList_SetItem +# define PyList_Size py3_PyList_Size +# define PySlice_GetIndicesEx py3_PySlice_GetIndicesEx +# define PyImport_ImportModule py3_PyImport_ImportModule +# define PyObject_Init py3__PyObject_Init +# define PyDict_New py3_PyDict_New +# define PyDict_GetItemString py3_PyDict_GetItemString +# define PyModule_GetDict py3_PyModule_GetDict +#undef PyRun_SimpleString +# define PyRun_SimpleString py3_PyRun_SimpleString +# define PySys_SetObject py3_PySys_SetObject +# define PySys_SetArgv py3_PySys_SetArgv +# define PyType_Type (*py3_PyType_Type) +# define PyType_Ready py3_PyType_Ready +#undef Py_BuildValue +# define Py_BuildValue py3_Py_BuildValue +# define Py_Initialize py3_Py_Initialize +# define Py_Finalize py3_Py_Finalize +# define Py_IsInitialized py3_Py_IsInitialized +# define _Py_NoneStruct (*py3__Py_NoneStruct) +# define PyModule_AddObject py3_PyModule_AddObject +# define PyImport_AppendInittab py3_PyImport_AppendInittab +# define _PyUnicode_AsString py3__PyUnicode_AsString +# define PyObject_GenericGetAttr py3_PyObject_GenericGetAttr +# define PySlice_Type (*py3_PySlice_Type) +#ifdef Py_DEBUG + # define _Py_NegativeRefcount py3__Py_NegativeRefcount + # define _Py_RefTotal (*py3__Py_RefTotal) + # define _Py_Dealloc py3__Py_Dealloc + # define _PyObject_DebugMalloc py3__PyObject_DebugMalloc + # define _PyObject_DebugFree py3__PyObject_DebugFree +#else + # define PyObject_Malloc py3_PyObject_Malloc + # define PyObject_Free py3_PyObject_Free +#endif +# define PyType_GenericAlloc py3_PyType_GenericAlloc +# define PyType_GenericNew py3_PyType_GenericNew +# define PyModule_Create2 py3_PyModule_Create2 +#undef PyUnicode_FromString +# define PyUnicode_FromString py3_PyUnicode_FromString +#undef PyUnicode_FromStringAndSize +# define PyUnicode_FromStringAndSize py3_PyUnicode_FromStringAndSize + +#ifdef Py_DEBUG +#undef PyObject_NEW +#define PyObject_NEW(type, typeobj) \ +( (type *) PyObject_Init( \ + (PyObject *) _PyObject_DebugMalloc( _PyObject_SIZE(typeobj) ), (typeobj)) ) +#endif +/* + * Pointers for dynamic link + */ +static int (*py3_PySys_SetArgv)(int, wchar_t **); +static void (*py3_Py_Initialize)(void); +static PyObject* (*py3_PyList_New)(Py_ssize_t size); +static PyGILState_STATE (*py3_PyGILState_Ensure)(void); +static void (*py3_PyGILState_Release)(PyGILState_STATE); +static int (*py3_PySys_SetObject)(char *, PyObject *); +static PyObject* (*py3_PyList_Append)(PyObject *, PyObject *); +static Py_ssize_t (*py3_PyList_Size)(PyObject *); +static int (*py3_PySlice_GetIndicesEx)(PySliceObject *r, Py_ssize_t length, + Py_ssize_t *start, Py_ssize_t *stop, Py_ssize_t *step, Py_ssize_t *slicelength); +static PyObject* (*py3_PyErr_NoMemory)(void); +static void (*py3_Py_Finalize)(void); +static void (*py3_PyErr_SetString)(PyObject *, const char *); +static int (*py3_PyRun_SimpleString)(char *); +static PyObject* (*py3_PyList_GetItem)(PyObject *, Py_ssize_t); +static PyObject* (*py3_PyImport_ImportModule)(const char *); +static int (*py3_PyErr_BadArgument)(void); +static PyTypeObject* py3_PyType_Type; +static PyObject* (*py3_PyErr_Occurred)(void); +static PyObject* (*py3_PyModule_GetDict)(PyObject *); +static int (*py3_PyList_SetItem)(PyObject *, Py_ssize_t, PyObject *); +static PyObject* (*py3_PyDict_GetItemString)(PyObject *, const char *); +static PyObject* (*py3_PyLong_FromLong)(long); +static PyObject* (*py3_PyDict_New)(void); +static PyObject* (*py3_Py_BuildValue)(char *, ...); +static int (*py3_PyType_Ready)(PyTypeObject *type); +static int (*py3_PyDict_SetItemString)(PyObject *dp, char *key, PyObject *item); +static PyObject* (*py3_PyUnicode_FromString)(const char *u); +static PyObject* (*py3_PyUnicode_FromStringAndSize)(const char *u, Py_ssize_t size); +static long (*py3_PyLong_AsLong)(PyObject *); +static void (*py3_PyErr_SetNone)(PyObject *); +static void (*py3_PyEval_InitThreads)(void); +static void(*py3_PyEval_RestoreThread)(PyThreadState *); +static PyThreadState*(*py3_PyEval_SaveThread)(void); +static int (*py3_PyArg_Parse)(PyObject *, char *, ...); +static int (*py3_PyArg_ParseTuple)(PyObject *, char *, ...); +static int (*py3_Py_IsInitialized)(void); +static void (*py3_PyErr_Clear)(void); +static PyObject*(*py3__PyObject_Init)(PyObject *, PyTypeObject *); +static PyObject* py3__Py_NoneStruct; +static int (*py3_PyModule_AddObject)(PyObject *m, const char *name, PyObject *o); +static int (*py3_PyImport_AppendInittab)(const char *name, PyObject* (*initfunc)(void)); +static char* (*py3__PyUnicode_AsString)(PyObject *unicode); +static PyObject* (*py3_PyObject_GenericGetAttr)(PyObject *obj, PyObject *name); +static PyObject* (*py3_PyModule_Create2)(struct PyModuleDef* module, int module_api_version); +static PyObject* (*py3_PyType_GenericAlloc)(PyTypeObject *type, Py_ssize_t nitems); +static PyObject* (*py3_PyType_GenericNew)(PyTypeObject *type, PyObject *args, PyObject *kwds); +static PyTypeObject* py3_PySlice_Type; +#ifdef Py_DEBUG + static void (*py3__Py_NegativeRefcount)(const char *fname, int lineno, PyObject *op); + static Py_ssize_t* py3__Py_RefTotal; + static void (*py3__Py_Dealloc)(PyObject *obj); + static void (*py3__PyObject_DebugFree)(void*); + static void* (*py3__PyObject_DebugMalloc)(size_t); +#else + static void (*py3_PyObject_Free)(void*); + static void* (*py3_PyObject_Malloc)(size_t); +#endif + +static HINSTANCE hinstPy3 = 0; /* Instance of python.dll */ + +/* Imported exception objects */ +static PyObject *p3imp_PyExc_AttributeError; +static PyObject *p3imp_PyExc_IndexError; +static PyObject *p3imp_PyExc_KeyboardInterrupt; +static PyObject *p3imp_PyExc_TypeError; +static PyObject *p3imp_PyExc_ValueError; + +# define PyExc_AttributeError p3imp_PyExc_AttributeError +# define PyExc_IndexError p3imp_PyExc_IndexError +# define PyExc_KeyboardInterrupt p3imp_PyExc_KeyboardInterrupt +# define PyExc_TypeError p3imp_PyExc_TypeError +# define PyExc_ValueError p3imp_PyExc_ValueError + +/* + * Table of name to function pointer of python. + */ +# define PYTHON_PROC FARPROC +static struct +{ + char *name; + PYTHON_PROC *ptr; +} py3_funcname_table[] = +{ + {"PySys_SetArgv", (PYTHON_PROC*)&py3_PySys_SetArgv}, + {"Py_Initialize", (PYTHON_PROC*)&py3_Py_Initialize}, + {"PyArg_ParseTuple", (PYTHON_PROC*)&py3_PyArg_ParseTuple}, + {"PyList_New", (PYTHON_PROC*)&py3_PyList_New}, + {"PyGILState_Ensure", (PYTHON_PROC*)&py3_PyGILState_Ensure}, + {"PyGILState_Release", (PYTHON_PROC*)&py3_PyGILState_Release}, + {"PySys_SetObject", (PYTHON_PROC*)&py3_PySys_SetObject}, + {"PyList_Append", (PYTHON_PROC*)&py3_PyList_Append}, + {"PyList_Size", (PYTHON_PROC*)&py3_PyList_Size}, + {"PySlice_GetIndicesEx", (PYTHON_PROC*)&py3_PySlice_GetIndicesEx}, + {"PyErr_NoMemory", (PYTHON_PROC*)&py3_PyErr_NoMemory}, + {"Py_Finalize", (PYTHON_PROC*)&py3_Py_Finalize}, + {"PyErr_SetString", (PYTHON_PROC*)&py3_PyErr_SetString}, + {"PyRun_SimpleString", (PYTHON_PROC*)&py3_PyRun_SimpleString}, + {"PyList_GetItem", (PYTHON_PROC*)&py3_PyList_GetItem}, + {"PyImport_ImportModule", (PYTHON_PROC*)&py3_PyImport_ImportModule}, + {"PyErr_BadArgument", (PYTHON_PROC*)&py3_PyErr_BadArgument}, + {"PyType_Type", (PYTHON_PROC*)&py3_PyType_Type}, + {"PyErr_Occurred", (PYTHON_PROC*)&py3_PyErr_Occurred}, + {"PyModule_GetDict", (PYTHON_PROC*)&py3_PyModule_GetDict}, + {"PyList_SetItem", (PYTHON_PROC*)&py3_PyList_SetItem}, + {"PyDict_GetItemString", (PYTHON_PROC*)&py3_PyDict_GetItemString}, + {"PyLong_FromLong", (PYTHON_PROC*)&py3_PyLong_FromLong}, + {"PyDict_New", (PYTHON_PROC*)&py3_PyDict_New}, + {"Py_BuildValue", (PYTHON_PROC*)&py3_Py_BuildValue}, + {"PyType_Ready", (PYTHON_PROC*)&py3_PyType_Ready}, + {"PyDict_SetItemString", (PYTHON_PROC*)&py3_PyDict_SetItemString}, + {"PyLong_AsLong", (PYTHON_PROC*)&py3_PyLong_AsLong}, + {"PyErr_SetNone", (PYTHON_PROC*)&py3_PyErr_SetNone}, + {"PyEval_InitThreads", (PYTHON_PROC*)&py3_PyEval_InitThreads}, + {"PyEval_RestoreThread", (PYTHON_PROC*)&py3_PyEval_RestoreThread}, + {"PyEval_SaveThread", (PYTHON_PROC*)&py3_PyEval_SaveThread}, + {"PyArg_Parse", (PYTHON_PROC*)&py3_PyArg_Parse}, + {"PyArg_ParseTuple", (PYTHON_PROC*)&py3_PyArg_ParseTuple}, + {"Py_IsInitialized", (PYTHON_PROC*)&py3_Py_IsInitialized}, + {"_Py_NoneStruct", (PYTHON_PROC*)&py3__Py_NoneStruct}, + {"PyErr_Clear", (PYTHON_PROC*)&py3_PyErr_Clear}, + {"PyObject_Init", (PYTHON_PROC*)&py3__PyObject_Init}, + {"PyModule_AddObject", (PYTHON_PROC*)&py3_PyModule_AddObject}, + {"PyImport_AppendInittab", (PYTHON_PROC*)&py3_PyImport_AppendInittab}, + {"_PyUnicode_AsString", (PYTHON_PROC*)&py3__PyUnicode_AsString}, + {"PyObject_GenericGetAttr", (PYTHON_PROC*)&py3_PyObject_GenericGetAttr}, + {"PyModule_Create2", (PYTHON_PROC*)&py3_PyModule_Create2}, + {"PyType_GenericAlloc", (PYTHON_PROC*)&py3_PyType_GenericAlloc}, + {"PyType_GenericNew", (PYTHON_PROC*)&py3_PyType_GenericNew}, + {"PySlice_Type", (PYTHON_PROC*)&py3_PySlice_Type}, +#ifdef Py_DEBUG + {"_Py_NegativeRefcount", (PYTHON_PROC*)&py3__Py_NegativeRefcount}, + {"_Py_RefTotal", (PYTHON_PROC*)&py3__Py_RefTotal}, + {"_Py_Dealloc", (PYTHON_PROC*)&py3__Py_Dealloc}, + {"_PyObject_DebugFree", (PYTHON_PROC*)&py3__PyObject_DebugFree}, + {"_PyObject_DebugMalloc", (PYTHON_PROC*)&py3__PyObject_DebugMalloc}, +#else + {"PyObject_Malloc", (PYTHON_PROC*)&py3_PyObject_Malloc}, + {"PyObject_Free", (PYTHON_PROC*)&py3_PyObject_Free}, +#endif + {"", NULL}, +}; + +/* + * Free python.dll + */ +static void end_dynamic_python3(void) +{ + if (hinstPy3) + { + close_dll(hinstPy3); + hinstPy3 = 0; + } +} + +/* + * Load library and get all pointers. + * Parameter 'libname' provides name of DLL. + * Return OK or FAIL. + */ +static int py3_runtime_link_init(char *libname, int verbose) +{ + int i; + + if (hinstPy3) + return OK; + hinstPy3 = load_dll(libname); + + if (!hinstPy3) + { + if (verbose) + EMSG2(_(e_loadlib), libname); + return FAIL; + } + + for (i = 0; py3_funcname_table[i].ptr; ++i) + { + if ((*py3_funcname_table[i].ptr = symbol_from_dll(hinstPy3, + py3_funcname_table[i].name)) == NULL) + { + close_dll(hinstPy3); + hinstPy3 = 0; + if (verbose) + EMSG2(_(e_loadfunc), py3_funcname_table[i].name); + return FAIL; + } + } + + /* load unicode functions separately as only the ucs2 or the ucs4 functions + * will be present in the library + */ + void *ucs_from_string, *ucs_from_string_and_size; + + ucs_from_string = symbol_from_dll(hinstPy3, "PyUnicodeUCS2_FromString"); + ucs_from_string_and_size = symbol_from_dll(hinstPy3, + "PyUnicodeUCS2_FromStringAndSize"); + if (!ucs_from_string || !ucs_from_string_and_size) + { + ucs_from_string = symbol_from_dll(hinstPy3, + "PyUnicodeUCS4_FromString"); + ucs_from_string_and_size = symbol_from_dll(hinstPy3, + "PyUnicodeUCS4_FromStringAndSize"); + } + if (ucs_from_string && ucs_from_string_and_size) + { + py3_PyUnicode_FromString = ucs_from_string; + py3_PyUnicode_FromStringAndSize = ucs_from_string_and_size; + } + else + { + close_dll(hinstPy3); + hinstPy3 = 0; + if (verbose) + EMSG2(_(e_loadfunc), "PyUnicode_UCSX_*"); + return FAIL; + } + + return OK; +} + +/* + * If python is enabled (there is installed python on Windows system) return + * TRUE, else FALSE. + */ +int python3_enabled(int verbose) +{ + return py3_runtime_link_init(DYNAMIC_PYTHON3_DLL, verbose) == OK; +} + +/* Load the standard Python exceptions - don't import the symbols from the + * DLL, as this can cause errors (importing data symbols is not reliable). + */ +static void get_py3_exceptions __ARGS((void)); + +static void get_py3_exceptions() +{ + PyObject *exmod = PyImport_ImportModule("builtins"); + PyObject *exdict = PyModule_GetDict(exmod); + p3imp_PyExc_AttributeError = PyDict_GetItemString(exdict, "AttributeError"); + p3imp_PyExc_IndexError = PyDict_GetItemString(exdict, "IndexError"); + p3imp_PyExc_KeyboardInterrupt = PyDict_GetItemString(exdict, "KeyboardInterrupt"); + p3imp_PyExc_TypeError = PyDict_GetItemString(exdict, "TypeError"); + p3imp_PyExc_ValueError = PyDict_GetItemString(exdict, "ValueError"); + Py_XINCREF(p3imp_PyExc_AttributeError); + Py_XINCREF(p3imp_PyExc_IndexError); + Py_XINCREF(p3imp_PyExc_KeyboardInterrupt); + Py_XINCREF(p3imp_PyExc_TypeError); + Py_XINCREF(p3imp_PyExc_ValueError); + Py_XDECREF(exmod); +} +#endif /* DYNAMIC_PYTHON3 */ + +static void call_PyObject_Free(void *p) +{ +#ifdef Py_DEBUG + _PyObject_DebugFree(p); +#else + PyObject_Free(p); +#endif +} +static PyObject* call_PyType_GenericNew(PyTypeObject *type, PyObject *args, PyObject *kwds) +{ + return PyType_GenericNew(type,args,kwds); +} +static PyObject* call_PyType_GenericAlloc(PyTypeObject *type, Py_ssize_t nitems) +{ + return PyType_GenericAlloc(type,nitems); +} + +/****************************************************** + * Internal function prototypes. + */ + +static void DoPy3Command(exarg_T *, const char *); +static Py_ssize_t RangeStart; +static Py_ssize_t RangeEnd; + +static void PythonIO_Flush(void); +static int PythonIO_Init(void); +static void PythonIO_Fini(void); +PyMODINIT_FUNC Py3Init_vim(void); + +/* Utility functions for the vim/python interface + * ---------------------------------------------- + */ +static PyObject *GetBufferLine(buf_T *, Py_ssize_t); + +static int SetBufferLine(buf_T *, Py_ssize_t, PyObject *, Py_ssize_t*); +static int InsertBufferLines(buf_T *, Py_ssize_t, PyObject *, Py_ssize_t*); +static PyObject *GetBufferLineList(buf_T *buf, Py_ssize_t lo, Py_ssize_t hi); + +static PyObject *LineToString(const char *); +static char *StringToLine(PyObject *); + +static int VimErrorCheck(void); + +#define PyErr_SetVim(str) PyErr_SetString(VimError, str) + +/****************************************************** + * 1. Python interpreter main program. + */ + +static int py3initialised = 0; + + +static PyGILState_STATE pygilstate = PyGILState_UNLOCKED; + +/* + * obtain a lock on the Vim data structures + */ +static void Python_Lock_Vim(void) +{ +} + +/* + * release a lock on the Vim data structures + */ +static void Python_Release_Vim(void) +{ +} + +void python3_end() +{ + static int recurse = 0; + + /* If a crash occurs while doing this, don't try again. */ + if (recurse != 0) + return; + + ++recurse; + +#ifdef DYNAMIC_PYTHON3 + if (hinstPy3) +#endif + if (Py_IsInitialized()) + { + // acquire lock before finalizing + pygilstate = PyGILState_Ensure(); + + PythonIO_Fini(); + Py_Finalize(); + } + +#ifdef DYNAMIC_PYTHON3 + end_dynamic_python3(); +#endif + + --recurse; +} + +static int Python3_Init(void) +{ + if (!py3initialised) + { +#ifdef DYNAMIC_PYTHON3 + if (!python3_enabled(TRUE)) + { + EMSG(_("E263: Sorry, this command is disabled, the Python library could not be loaded.")); + goto fail; + } +#endif + + init_structs(); + + /* initialise threads */ + PyEval_InitThreads(); + +#if !defined(MACOS) || defined(MACOS_X_UNIX) + Py_Initialize(); +#else + PyMac_Initialize(); +#endif + +#ifdef DYNAMIC_PYTHON3 + get_py3_exceptions(); +#endif + + if (PythonIO_Init()) + goto fail; + + PyImport_AppendInittab("vim", Py3Init_vim); + + /* Remove the element from sys.path that was added because of our + * argv[0] value in Py3Init_vim(). Previously we used an empty + * string, but dependinding on the OS we then get an empty entry or + * the current directory in sys.path. */ + PyRun_SimpleString("import sys; sys.path = list(filter(lambda x: x != '/must>not&exist', sys.path))"); + + // lock is created and acquired in PyEval_InitThreads() and thread + // state is created in Py_Initialize() + // there _PyGILState_NoteThreadState() also sets gilcounter to 1 + // (python must have threads enabled!) + // so the following does both: unlock GIL and save thread state in TLS + // without deleting thread state + PyGILState_Release(pygilstate); + + py3initialised = 1; + } + + return 0; + +fail: + /* We call PythonIO_Flush() here to print any Python errors. + * This is OK, as it is possible to call this function even + * if PythonIO_Init() has not completed successfully (it will + * not do anything in this case). + */ + PythonIO_Flush(); + return -1; +} + +/* + * External interface + */ +static void DoPy3Command(exarg_T *eap, const char *cmd) +{ +#if defined(MACOS) && !defined(MACOS_X_UNIX) + GrafPtr oldPort; +#endif +#if defined(HAVE_LOCALE_H) || defined(X_LOCALE) + char *saved_locale; +#endif + +#if defined(MACOS) && !defined(MACOS_X_UNIX) + GetPort(&oldPort); + /* Check if the Python library is available */ + if ((Ptr)PyMac_Initialize == (Ptr)kUnresolvedCFragSymbolAddress) + goto theend; +#endif + if (Python3_Init()) + goto theend; + + RangeStart = eap->line1; + RangeEnd = eap->line2; + Python_Release_Vim(); /* leave vim */ + +#if defined(HAVE_LOCALE_H) || defined(X_LOCALE) + /* Python only works properly when the LC_NUMERIC locale is "C". */ + saved_locale = setlocale(LC_NUMERIC, NULL); + if (saved_locale == NULL || STRCMP(saved_locale, "C") == 0) + saved_locale = NULL; + else + { + /* Need to make a copy, value may change when setting new locale. */ + saved_locale = (char *)vim_strsave((char_u *)saved_locale); + (void)setlocale(LC_NUMERIC, "C"); + } +#endif + + pygilstate = PyGILState_Ensure(); + + PyRun_SimpleString((char *)(cmd)); + + PyGILState_Release(pygilstate); + +#if defined(HAVE_LOCALE_H) || defined(X_LOCALE) + if (saved_locale != NULL) + { + (void)setlocale(LC_NUMERIC, saved_locale); + vim_free(saved_locale); + } +#endif + + Python_Lock_Vim(); /* enter vim */ + PythonIO_Flush(); +#if defined(MACOS) && !defined(MACOS_X_UNIX) + SetPort(oldPort); +#endif + +theend: + return; /* keeps lint happy */ +} + +/* + * ":python" + */ +void ex_python3(exarg_T *eap) +{ + char_u *script; + + script = script_get(eap, eap->arg); + if (!eap->skip) + { + if (script == NULL) + DoPy3Command(eap, (char *)eap->arg); + else + DoPy3Command(eap, (char *)script); + } + vim_free(script); +} + +#define BUFFER_SIZE 2048 + +/* + * ":pyfile" + */ + void +ex_py3file(exarg_T *eap) +{ + static char buffer[BUFFER_SIZE]; + const char *file; + char *p; + int i; + + /* Have to do it like this. PyRun_SimpleFile requires you to pass a + * stdio file pointer, but Vim and the Python DLL are compiled with + * different options under Windows, meaning that stdio pointers aren't + * compatible between the two. Yuk. + * + * construct: exec(compile(open('a_filename').read(), 'a_filename', 'exec')) + * + * We need to escape any backslashes or single quotes in the file name, so that + * Python won't mangle the file name. + */ + + strcpy(buffer, "exec(compile(open('"); + p = buffer + 19; /* size of "exec(compile(open('" */ + + for (i=0; i<2; ++i) + { + file = (char *)eap->arg; + while (*file && p < buffer + (BUFFER_SIZE - 3)) + { + if (*file == '\\' || *file == '\'') + *p++ = '\\'; + *p++ = *file++; + } + /* If we didn't finish the file name, we hit a buffer overflow */ + if (*file != '\0') + return; + if (i==0) + { + strcpy(p,"').read(),'"); + p += 11; + } + else + { + strcpy(p,"','exec'))"); + p += 10; + } + } + + + /* Execute the file */ + DoPy3Command(eap, buffer); +} + +/****************************************************** + * 2. Python output stream: writes output via [e]msg(). + */ + +/* Implementation functions + */ + +static PyObject *OutputGetattro(PyObject *, PyObject *); +static int OutputSetattro(PyObject *, PyObject *, PyObject *); + +static PyObject *OutputWrite(PyObject *, PyObject *); +static PyObject *OutputWritelines(PyObject *, PyObject *); + +typedef void (*writefn)(char_u *); +static void writer(writefn fn, char_u *str, Py_ssize_t n); + +/* Output object definition + */ + +typedef struct +{ + PyObject_HEAD + long softspace; + long error; +} OutputObject; + +static struct PyMethodDef OutputMethods[] = { + /* name, function, calling, documentation */ + {"write", OutputWrite, 1, "" }, + {"writelines", OutputWritelines, 1, "" }, + { NULL, NULL, 0, NULL } +}; + +static PyTypeObject OutputType; + +/*************/ + +static PyObject * OutputGetattro(PyObject *self, PyObject *nameobj) +{ + char *name = ""; + if (PyUnicode_Check(nameobj)) + name = _PyUnicode_AsString(nameobj); + + if (strcmp(name, "softspace") == 0) + return PyLong_FromLong(((OutputObject *)(self))->softspace); + + return PyObject_GenericGetAttr(self, nameobj); +} + +static int OutputSetattro(PyObject *self, PyObject *nameobj, PyObject *val) +{ + char *name = ""; + if (PyUnicode_Check(nameobj)) + name = _PyUnicode_AsString(nameobj); + + if (val == NULL) { + PyErr_SetString(PyExc_AttributeError, _("can't delete OutputObject attributes")); + return -1; + } + + if (strcmp(name, "softspace") == 0) + { + if (!PyLong_Check(val)) { + PyErr_SetString(PyExc_TypeError, _("softspace must be an integer")); + return -1; + } + + ((OutputObject *)(self))->softspace = PyLong_AsLong(val); + return 0; + } + + PyErr_SetString(PyExc_AttributeError, _("invalid attribute")); + return -1; +} + +/*************/ + +static PyObject * OutputWrite(PyObject *self, PyObject *args) +{ + int len; + char *str; + int error = ((OutputObject *)(self))->error; + + if (!PyArg_ParseTuple(args, "s#", &str, &len)) + return NULL; + + Py_BEGIN_ALLOW_THREADS + Python_Lock_Vim(); + writer((writefn)(error ? emsg : msg), (char_u *)str, len); + Python_Release_Vim(); + Py_END_ALLOW_THREADS + + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * OutputWritelines(PyObject *self, PyObject *args) +{ + Py_ssize_t n; + Py_ssize_t i; + PyObject *list; + int error = ((OutputObject *)(self))->error; + + if (!PyArg_ParseTuple(args, "O", &list)) + return NULL; + Py_INCREF(list); + + if (!PyList_Check(list)) { + PyErr_SetString(PyExc_TypeError, _("writelines() requires list of strings")); + Py_DECREF(list); + return NULL; + } + + n = PyList_Size(list); + + for (i = 0; i < n; ++i) + { + PyObject *line = PyList_GetItem(list, i); + char *str; + Py_ssize_t len; + + if (!PyArg_Parse(line, "s#", &str, &len)) { + PyErr_SetString(PyExc_TypeError, _("writelines() requires list of strings")); + Py_DECREF(list); + return NULL; + } + + Py_BEGIN_ALLOW_THREADS + Python_Lock_Vim(); + writer((writefn)(error ? emsg : msg), (char_u *)str, len); + Python_Release_Vim(); + Py_END_ALLOW_THREADS + } + + Py_DECREF(list); + Py_INCREF(Py_None); + return Py_None; +} + +/* Output buffer management + */ + +static char_u *buffer = NULL; +static Py_ssize_t buffer_len = 0; +static Py_ssize_t buffer_size = 0; + +static writefn old_fn = NULL; + +static void buffer_ensure(Py_ssize_t n) +{ + Py_ssize_t new_size; + char_u *new_buffer; + + if (n < buffer_size) + return; + + new_size = buffer_size; + while (new_size < n) + new_size += 80; + + if (new_size != buffer_size) + { + new_buffer = alloc((unsigned)new_size); + if (new_buffer == NULL) + return; + + if (buffer) + { + memcpy(new_buffer, buffer, buffer_len); + vim_free(buffer); + } + + buffer = new_buffer; + buffer_size = new_size; + } +} + +static void PythonIO_Flush(void) +{ + if (old_fn && buffer_len) + { + buffer[buffer_len] = 0; + old_fn(buffer); + } + + buffer_len = 0; +} + +static void writer(writefn fn, char_u *str, Py_ssize_t n) +{ + char_u *ptr; + + if (fn != old_fn && old_fn != NULL) + PythonIO_Flush(); + + old_fn = fn; + + while (n > 0 && (ptr = memchr(str, '\n', n)) != NULL) + { + Py_ssize_t len = ptr - str; + + buffer_ensure(buffer_len + len + 1); + + memcpy(buffer + buffer_len, str, len); + buffer_len += len; + buffer[buffer_len] = 0; + fn(buffer); + str = ptr + 1; + n -= len + 1; + buffer_len = 0; + } + + /* Put the remaining text into the buffer for later printing */ + buffer_ensure(buffer_len + n + 1); + memcpy(buffer + buffer_len, str, n); + buffer_len += n; +} + +/***************/ + +static OutputObject Output = +{ + PyObject_HEAD_INIT(&OutputType) + 0, + 0 +}; + +static OutputObject Error = +{ + PyObject_HEAD_INIT(&OutputType) + 0, + 1 +}; + +static int PythonIO_Init(void) +{ + PyType_Ready(&OutputType); + + PySys_SetObject("stdout", (PyObject *)(void *)&Output); + PySys_SetObject("stderr", (PyObject *)(void *)&Error); + + if (PyErr_Occurred()) + { + EMSG(_("E264: Python: Error initialising I/O objects")); + return -1; + } + + return 0; +} +static void PythonIO_Fini(void) +{ + PySys_SetObject("stdout", NULL); + PySys_SetObject("stderr", NULL); +} + +/****************************************************** + * 3. Implementation of the Vim module for Python + */ + +/* Vim module - Implementation functions + * ------------------------------------- + */ + +static PyObject *VimError; + +static PyObject *VimCommand(PyObject *, PyObject *); +static PyObject *VimEval(PyObject *, PyObject *); + +/* Window type - Implementation functions + * -------------------------------------- + */ + +typedef struct +{ + PyObject_HEAD + win_T *win; +} +WindowObject; + +#define INVALID_WINDOW_VALUE ((win_T *)(-1)) + +#define WindowType_Check(obj) ((obj)->ob_base.ob_type == &WindowType) + +static PyObject *WindowNew(win_T *); + +static void WindowDestructor(PyObject *); +static PyObject *WindowGetattro(PyObject *, PyObject *); +static int WindowSetattro(PyObject *, PyObject *, PyObject *); +static PyObject *WindowRepr(PyObject *); + +/* Buffer type - Implementation functions + * -------------------------------------- + */ + +typedef struct +{ + PyObject_HEAD + buf_T *buf; +} +BufferObject; + +#define INVALID_BUFFER_VALUE ((buf_T *)(-1)) + +#define BufferType_Check(obj) ((obj)->ob_base.ob_type == &BufferType) + +static PyObject *BufferNew (buf_T *); + +static void BufferDestructor(PyObject *); + +static PyObject *BufferGetattro(PyObject *, PyObject*); +static PyObject *BufferRepr(PyObject *); + +static Py_ssize_t BufferLength(PyObject *); +static PyObject *BufferItem(PyObject *, Py_ssize_t); +static Py_ssize_t BufferAsItem(PyObject *, Py_ssize_t, PyObject *); +static PyObject* BufferSubscript(PyObject *self, PyObject* idx); + +static PyObject *BufferAppend(PyObject *, PyObject *); +static PyObject *BufferMark(PyObject *, PyObject *); +static PyObject *BufferRange(PyObject *, PyObject *); + +/* Line range type - Implementation functions + * -------------------------------------- + */ + +typedef struct +{ + PyObject_HEAD + BufferObject *buf; + Py_ssize_t start; + Py_ssize_t end; +} +RangeObject; + +#define RangeType_Check(obj) ((obj)->ob_base.ob_type == &RangeType) + +static PyObject *RangeNew(buf_T *, Py_ssize_t, Py_ssize_t); + +static void RangeDestructor(PyObject *); +static PyObject *RangeGetattro(PyObject *, PyObject *); +static PyObject *RangeRepr(PyObject *); +static PyObject* RangeSubscript(PyObject *self, PyObject* idx); + +static Py_ssize_t RangeLength(PyObject *); +static PyObject *RangeItem(PyObject *, Py_ssize_t); +static Py_ssize_t RangeAsItem(PyObject *, Py_ssize_t, PyObject *); + +static PyObject *RangeAppend(PyObject *, PyObject *); + +/* Window list type - Implementation functions + * ------------------------------------------- + */ + +static Py_ssize_t WinListLength(PyObject *); +static PyObject *WinListItem(PyObject *, Py_ssize_t); + +/* Buffer list type - Implementation functions + * ------------------------------------------- + */ + +static Py_ssize_t BufListLength(PyObject *); +static PyObject *BufListItem(PyObject *, Py_ssize_t); + +/* Current objects type - Implementation functions + * ----------------------------------------------- + */ + +static PyObject *CurrentGetattro(PyObject *, PyObject *); +static int CurrentSetattro(PyObject *, PyObject *, PyObject *); + +/* Vim module - Definitions + */ + +static struct PyMethodDef VimMethods[] = { + /* name, function, calling, documentation */ + {"command", VimCommand, 1, "Execute a Vim ex-mode command" }, + {"eval", VimEval, 1, "Evaluate an expression using Vim evaluator" }, + { NULL, NULL, 0, NULL } +}; + +/* Vim module - Implementation + */ +/*ARGSUSED*/ +static PyObject * VimCommand(PyObject *self UNUSED, PyObject *args) +{ + char *cmd; + PyObject *result; + + if (!PyArg_ParseTuple(args, "s", &cmd)) + return NULL; + + PyErr_Clear(); + + Py_BEGIN_ALLOW_THREADS + Python_Lock_Vim(); + + do_cmdline_cmd((char_u *)cmd); + update_screen(VALID); + + Python_Release_Vim(); + Py_END_ALLOW_THREADS + + if (VimErrorCheck()) + result = NULL; + else + result = Py_None; + + Py_XINCREF(result); + return result; +} + +#ifdef FEAT_EVAL +/* + * Function to translate a typval_T into a PyObject; this will recursively + * translate lists/dictionaries into their Python equivalents. + * + * The depth parameter is to avoid infinite recursion, set it to 1 when + * you call VimToPython. + */ +static PyObject * VimToPython(typval_T *our_tv, int depth, PyObject *lookupDict) +{ + PyObject *result; + PyObject *newObj; + char ptrBuf[NUMBUFLEN]; + + /* Avoid infinite recursion */ + if (depth > 100) + { + Py_INCREF(Py_None); + result = Py_None; + return result; + } + + /* Check if we run into a recursive loop. The item must be in lookupDict + * then and we can use it again. */ + if ((our_tv->v_type == VAR_LIST && our_tv->vval.v_list != NULL) + || (our_tv->v_type == VAR_DICT && our_tv->vval.v_dict != NULL)) + { + sprintf(ptrBuf, PRINTF_DECIMAL_LONG_U, + our_tv->v_type == VAR_LIST ? (long_u)our_tv->vval.v_list + : (long_u)our_tv->vval.v_dict); + result = PyDict_GetItemString(lookupDict, ptrBuf); + if (result != NULL) + { + Py_INCREF(result); + return result; + } + } + + if (our_tv->v_type == VAR_STRING) + { + result = Py_BuildValue("s", our_tv->vval.v_string); + } + else if (our_tv->v_type == VAR_NUMBER) + { + char buf[NUMBUFLEN]; + + /* For backwards compatibility numbers are stored as strings. */ + sprintf(buf, "%ld", (long)our_tv->vval.v_number); + result = Py_BuildValue("s", buf); + } +# ifdef FEAT_FLOAT + else if (our_tv->v_type == VAR_FLOAT) + { + char buf[NUMBUFLEN]; + + sprintf(buf, "%f", our_tv->vval.v_float); + result = Py_BuildValue("s", buf); + } +# endif + else if (our_tv->v_type == VAR_LIST) + { + list_T *list = our_tv->vval.v_list; + listitem_T *curr; + + result = PyList_New(0); + + if (list != NULL) + { + PyDict_SetItemString(lookupDict, ptrBuf, result); + + for (curr = list->lv_first; curr != NULL; curr = curr->li_next) + { + newObj = VimToPython(&curr->li_tv, depth + 1, lookupDict); + PyList_Append(result, newObj); + Py_DECREF(newObj); + } + } + } + else if (our_tv->v_type == VAR_DICT) + { + result = PyDict_New(); + + if (our_tv->vval.v_dict != NULL) + { + hashtab_T *ht = &our_tv->vval.v_dict->dv_hashtab; + long_u t = ht->ht_used; + hashitem_T *hi; + dictitem_T *di; + + PyDict_SetItemString(lookupDict, ptrBuf, result); + + for (hi = ht->ht_array; t > 0; ++hi) + { + if (!HASHITEM_EMPTY(hi)) + { + --t; + + di = dict_lookup(hi); + newObj = VimToPython(&di->di_tv, depth + 1, lookupDict); + PyDict_SetItemString(result, (char *)hi->hi_key, newObj); + Py_DECREF(newObj); + } + } + } + } + else + { + Py_INCREF(Py_None); + result = Py_None; + } + + return result; +} +#endif + +/*ARGSUSED*/ +static PyObject * VimEval(PyObject *self UNUSED, PyObject *args) +{ +#ifdef FEAT_EVAL + char *expr; + typval_T *our_tv; + PyObject *result; + PyObject *lookup_dict; + + if (!PyArg_ParseTuple(args, "s", &expr)) + return NULL; + + Py_BEGIN_ALLOW_THREADS + Python_Lock_Vim(); + our_tv = eval_expr((char_u *)expr, NULL); + + Python_Release_Vim(); + Py_END_ALLOW_THREADS + + if (our_tv == NULL) + { + PyErr_SetVim(_("invalid expression")); + return NULL; + } + + /* Convert the Vim type into a Python type. Create a dictionary that's + * used to check for recursive loops. */ + lookup_dict = PyDict_New(); + result = VimToPython(our_tv, 1, lookup_dict); + Py_DECREF(lookup_dict); + + + Py_BEGIN_ALLOW_THREADS + Python_Lock_Vim(); + free_tv(our_tv); + Python_Release_Vim(); + Py_END_ALLOW_THREADS + + return result; +#else + PyErr_SetVim(_("expressions disabled at compile time")); + return NULL; +#endif +} + +/* Common routines for buffers and line ranges + * ------------------------------------------- + */ + +static int CheckBuffer(BufferObject *this) +{ + if (this->buf == INVALID_BUFFER_VALUE) + { + PyErr_SetVim(_("attempt to refer to deleted buffer")); + return -1; + } + + return 0; +} + +static PyObject * RBItem(BufferObject *self, Py_ssize_t n, Py_ssize_t start, Py_ssize_t end) +{ + if (CheckBuffer(self)) + return NULL; + + if (n < 0 || n > end - start) + { + PyErr_SetString(PyExc_IndexError, _("line number out of range")); + return NULL; + } + + return GetBufferLine(self->buf, n+start); +} + +static Py_ssize_t RBAsItem(BufferObject *self, Py_ssize_t n, PyObject *val, Py_ssize_t start, Py_ssize_t end, Py_ssize_t *new_end) +{ + Py_ssize_t len_change; + + if (CheckBuffer(self)) + return -1; + + if (n < 0 || n > end - start) + { + PyErr_SetString(PyExc_IndexError, _("line number out of range")); + return -1; + } + + if (SetBufferLine(self->buf, n+start, val, &len_change) == FAIL) + return -1; + + if (new_end) + *new_end = end + len_change; + + return 0; +} + +static PyObject * RBSlice(BufferObject *self, Py_ssize_t lo, Py_ssize_t hi, Py_ssize_t start, Py_ssize_t end) +{ + Py_ssize_t size; + + if (CheckBuffer(self)) + return NULL; + + size = end - start + 1; + + if (lo < 0) + lo = 0; + else if (lo > size) + lo = size; + if (hi < 0) + hi = 0; + if (hi < lo) + hi = lo; + else if (hi > size) + hi = size; + + return GetBufferLineList(self->buf, lo+start, hi+start); +} + +static PyObject * RBAppend(BufferObject *self, PyObject *args, Py_ssize_t start, Py_ssize_t end, Py_ssize_t *new_end) +{ + PyObject *lines; + Py_ssize_t len_change; + Py_ssize_t max; + Py_ssize_t n; + + if (CheckBuffer(self)) + return NULL; + + max = n = end - start + 1; + + if (!PyArg_ParseTuple(args, "O|n" , &lines, &n)) + return NULL; + + if (n < 0 || n > max) + { + PyErr_SetString(PyExc_ValueError, _("line number out of range")); + return NULL; + } + + if (InsertBufferLines(self->buf, n + start - 1, lines, &len_change) == FAIL) + return NULL; + + if (new_end) + *new_end = end + len_change; + + Py_INCREF(Py_None); + return Py_None; +} + + +static struct PyMethodDef BufferMethods[] = { + /* name, function, calling, documentation */ + {"append", BufferAppend, 1, "Append data to Vim buffer" }, + {"mark", BufferMark, 1, "Return (row,col) representing position of named mark" }, + {"range", BufferRange, 1, "Return a range object which represents the part of the given buffer between line numbers s and e" }, + { NULL, NULL, 0, NULL } +}; + +static PySequenceMethods BufferAsSeq = { + (lenfunc) BufferLength, /* sq_length, len(x) */ + (binaryfunc) 0, /* sq_concat, x+y */ + (ssizeargfunc) 0, /* sq_repeat, x*n */ + (ssizeargfunc) BufferItem, /* sq_item, x[i] */ + 0, /* was_sq_slice, x[i:j] */ + (ssizeobjargproc) BufferAsItem, /* sq_ass_item, x[i]=v */ + 0, /* sq_ass_slice, x[i:j]=v */ + 0, /* sq_contains */ + 0, /* sq_inplace_concat */ + 0, /* sq_inplace_repeat */ +}; + +PyMappingMethods BufferAsMapping = { + /* mp_length */ (lenfunc)BufferLength, + /* mp_subscript */ (binaryfunc)BufferSubscript, + /* mp_ass_subscript */ (objobjargproc)0, +}; + + +/* Buffer object - Definitions + */ + +static PyTypeObject BufferType; + +static PyObject * BufferNew(buf_T *buf) +{ + /* We need to handle deletion of buffers underneath us. + * If we add a "b_python3_ref" field to the buf_T structure, + * then we can get at it in buf_freeall() in vim. We then + * need to create only ONE Python object per buffer - if + * we try to create a second, just INCREF the existing one + * and return it. The (single) Python object referring to + * the buffer is stored in "b_python3_ref". + * Question: what to do on a buf_freeall(). We'll probably + * have to either delete the Python object (DECREF it to + * zero - a bad idea, as it leaves dangling refs!) or + * set the buf_T * value to an invalid value (-1?), which + * means we need checks in all access functions... Bah. + */ + + BufferObject *self; + + if (buf->b_python3_ref != NULL) + { + self = buf->b_python3_ref; + Py_INCREF(self); + } + else + { + self = PyObject_NEW(BufferObject, &BufferType); + buf->b_python3_ref = self; + if (self == NULL) + return NULL; + self->buf = buf; + } + + return (PyObject *)(self); +} + +static void BufferDestructor(PyObject *self) +{ + BufferObject *this = (BufferObject *)(self); + + if (this->buf && this->buf != INVALID_BUFFER_VALUE) + this->buf->b_python3_ref = NULL; +} + +static PyObject * BufferGetattro(PyObject *self, PyObject*nameobj) +{ + BufferObject *this = (BufferObject *)(self); + + char *name = ""; + if (PyUnicode_Check(nameobj)) + name = _PyUnicode_AsString(nameobj); + + if (CheckBuffer(this)) + return NULL; + + if (strcmp(name, "name") == 0) + return Py_BuildValue("s", this->buf->b_ffname); + else if (strcmp(name, "number") == 0) + return Py_BuildValue("n", this->buf->b_fnum); + else if (strcmp(name,"__members__") == 0) + return Py_BuildValue("[ss]", "name", "number"); + else + return PyObject_GenericGetAttr(self, nameobj); +} + +static PyObject * BufferRepr(PyObject *self) +{ + static char repr[100]; + BufferObject *this = (BufferObject *)(self); + + if (this->buf == INVALID_BUFFER_VALUE) + { + vim_snprintf(repr, 100, _("<buffer object (deleted) at %p>"), (self)); + return PyUnicode_FromString(repr); + } + else + { + char *name = (char *)this->buf->b_fname; + Py_ssize_t len; + + if (name == NULL) + name = ""; + len = strlen(name); + + if (len > 35) + name = name + (35 - len); + + vim_snprintf(repr, 100, "<buffer %s%s>", len > 35 ? "..." : "", name); + + return PyUnicode_FromString(repr); + } +} + +/******************/ + +static Py_ssize_t BufferLength(PyObject *self) +{ + if (CheckBuffer((BufferObject *)(self))) + return -1; + + return (Py_ssize_t)(((BufferObject *)(self))->buf->b_ml.ml_line_count); +} + +static PyObject * BufferItem(PyObject *self, Py_ssize_t n) +{ + return RBItem((BufferObject *)(self), n, 1, + (Py_ssize_t)((BufferObject *)(self))->buf->b_ml.ml_line_count); +} + +static Py_ssize_t BufferAsItem(PyObject *self, Py_ssize_t n, PyObject *val) +{ + return RBAsItem((BufferObject *)(self), n, val, 1, + (Py_ssize_t)((BufferObject *)(self))->buf->b_ml.ml_line_count, + NULL); +} + +static PyObject * BufferSlice(PyObject *self, Py_ssize_t lo, Py_ssize_t hi) +{ + return RBSlice((BufferObject *)(self), lo, hi, 1, + (Py_ssize_t)((BufferObject *)(self))->buf->b_ml.ml_line_count); +} + + +static PyObject* BufferSubscript(PyObject *self, PyObject* idx) +{ + if (PyLong_Check(idx)) { + long _idx = PyLong_AsLong(idx); + return BufferItem(self,_idx); + } else if (PySlice_Check(idx)) { + Py_ssize_t start, stop, step, slicelen; + + if (PySlice_GetIndicesEx((PySliceObject *)idx, + (Py_ssize_t)((BufferObject *)(self))->buf->b_ml.ml_line_count+1, + &start, &stop, + &step, &slicelen) < 0) { + return NULL; + } + return BufferSlice(self,start,stop+1); + } else { + PyErr_SetString(PyExc_IndexError, "Index must be int or slice"); + return NULL; + } +} + +static PyObject * BufferAppend(PyObject *self, PyObject *args) +{ + return RBAppend((BufferObject *)(self), args, 1, + (Py_ssize_t)((BufferObject *)(self))->buf->b_ml.ml_line_count, + NULL); +} + +static PyObject * BufferMark(PyObject *self, PyObject *args) +{ + pos_T *posp; + char *pmark;//test + char mark; + buf_T *curbuf_save; + + if (CheckBuffer((BufferObject *)(self))) + return NULL; + + if (!PyArg_ParseTuple(args, "s", &pmark))//test: "c"->"s" + return NULL; + mark = *pmark;//test + + curbuf_save = curbuf; + curbuf = ((BufferObject *)(self))->buf; + posp = getmark(mark, FALSE); + curbuf = curbuf_save; + + if (posp == NULL) + { + PyErr_SetVim(_("invalid mark name")); + return NULL; + } + + /* Ckeck for keyboard interrupt */ + if (VimErrorCheck()) + return NULL; + + if (posp->lnum <= 0) + { + /* Or raise an error? */ + Py_INCREF(Py_None); + return Py_None; + } + + return Py_BuildValue("(ll)", (long)(posp->lnum), (long)(posp->col)); +} + +static PyObject * BufferRange(PyObject *self, PyObject *args) +{ + Py_ssize_t start; + Py_ssize_t end; + + if (CheckBuffer((BufferObject *)(self))) + return NULL; + + if (!PyArg_ParseTuple(args, "nn", &start, &end)) + return NULL; + + return RangeNew(((BufferObject *)(self))->buf, start, end); +} + +/* Line range object - Definitions + */ + +static struct PyMethodDef RangeMethods[] = { + /* name, function, calling, documentation */ + {"append", RangeAppend, 1, "Append data to the Vim range" }, + { NULL, NULL, 0, NULL } +}; + +static PySequenceMethods RangeAsSeq = { + (lenfunc) RangeLength, /* sq_length, len(x) */ + (binaryfunc) 0, /* RangeConcat, */ /* sq_concat, x+y */ + (ssizeargfunc) 0, /* RangeRepeat, */ /* sq_repeat, x*n */ + (ssizeargfunc) RangeItem, /* sq_item, x[i] */ + 0, /* was_sq_slice, x[i:j] */ + (ssizeobjargproc) RangeAsItem, /* sq_as_item, x[i]=v */ + 0, /* sq_ass_slice, x[i:j]=v */ + 0, /* sq_contains */ + 0, /* sq_inplace_concat */ + 0, /* sq_inplace_repeat */ +}; + +PyMappingMethods RangeAsMapping = { + /* mp_length */ (lenfunc)RangeLength, + /* mp_subscript */ (binaryfunc)RangeSubscript, + /* mp_ass_subscript */ (objobjargproc)0, +}; + +static PyTypeObject RangeType; + +/* Line range object - Implementation + */ + +static PyObject * RangeNew(buf_T *buf, Py_ssize_t start, Py_ssize_t end) +{ + BufferObject *bufr; + RangeObject *self; + self = PyObject_NEW(RangeObject, &RangeType); + if (self == NULL) + return NULL; + + bufr = (BufferObject *)BufferNew(buf); + if (bufr == NULL) + { + Py_DECREF(self); + return NULL; + } + Py_INCREF(bufr); + + self->buf = bufr; + self->start = start; + self->end = end; + + return (PyObject *)(self); +} + +static void RangeDestructor(PyObject *self) +{ + Py_DECREF(((RangeObject *)(self))->buf); +} + +static PyObject * RangeGetattro(PyObject *self, PyObject *nameobj) +{ + char *name = ""; + if (PyUnicode_Check(nameobj)) + name = _PyUnicode_AsString(nameobj); + + if (strcmp(name, "start") == 0) + return Py_BuildValue("n", ((RangeObject *)(self))->start - 1); + else if (strcmp(name, "end") == 0) + return Py_BuildValue("n", ((RangeObject *)(self))->end - 1); + else + return PyObject_GenericGetAttr(self, nameobj); +} + +static PyObject * RangeRepr(PyObject *self) +{ + static char repr[100]; + RangeObject *this = (RangeObject *)(self); + + if (this->buf->buf == INVALID_BUFFER_VALUE) + { + vim_snprintf(repr, 100, "<range object (for deleted buffer) at %p>", + (self)); + return PyUnicode_FromString(repr); + } + else + { + char *name = (char *)this->buf->buf->b_fname; + int len; + + if (name == NULL) + name = ""; + len = (int)strlen(name); + + if (len > 45) + name = name + (45 - len); + + vim_snprintf(repr, 100, "<range %s%s (%d:%d)>", + len > 45 ? "..." : "", name, + this->start, this->end); + + return PyUnicode_FromString(repr); + } +} + +/****************/ + +static Py_ssize_t RangeLength(PyObject *self) +{ + /* HOW DO WE SIGNAL AN ERROR FROM THIS FUNCTION? */ + if (CheckBuffer(((RangeObject *)(self))->buf)) + return -1; /* ??? */ + + return (((RangeObject *)(self))->end - ((RangeObject *)(self))->start + 1); +} + +static PyObject * RangeItem(PyObject *self, Py_ssize_t n) +{ + return RBItem(((RangeObject *)(self))->buf, n, + ((RangeObject *)(self))->start, + ((RangeObject *)(self))->end); +} + +static Py_ssize_t RangeAsItem(PyObject *self, Py_ssize_t n, PyObject *val) +{ + return RBAsItem(((RangeObject *)(self))->buf, n, val, + ((RangeObject *)(self))->start, + ((RangeObject *)(self))->end, + &((RangeObject *)(self))->end); +} + +static PyObject * RangeSlice(PyObject *self, Py_ssize_t lo, Py_ssize_t hi) +{ + return RBSlice(((RangeObject *)(self))->buf, lo, hi, + ((RangeObject *)(self))->start, + ((RangeObject *)(self))->end); +} + +static PyObject* RangeSubscript(PyObject *self, PyObject* idx) +{ + if (PyLong_Check(idx)) { + long _idx = PyLong_AsLong(idx); + return RangeItem(self,_idx); + } else if (PySlice_Check(idx)) { + Py_ssize_t start, stop, step, slicelen; + + if (PySlice_GetIndicesEx((PySliceObject *)idx, + ((RangeObject *)(self))->end-((RangeObject *)(self))->start+1, + &start, &stop, + &step, &slicelen) < 0) { + return NULL; + } + return RangeSlice(self,start,stop+1); + } else { + PyErr_SetString(PyExc_IndexError, "Index must be int or slice"); + return NULL; + } +} + +static PyObject * RangeAppend(PyObject *self, PyObject *args) +{ + return RBAppend(((RangeObject *)(self))->buf, args, + ((RangeObject *)(self))->start, + ((RangeObject *)(self))->end, + &((RangeObject *)(self))->end); +} + +/* Buffer list object - Definitions + */ + +typedef struct +{ + PyObject_HEAD +} +BufListObject; + +static PySequenceMethods BufListAsSeq = { + (lenfunc) BufListLength, /* sq_length, len(x) */ + (binaryfunc) 0, /* sq_concat, x+y */ + (ssizeargfunc) 0, /* sq_repeat, x*n */ + (ssizeargfunc) BufListItem, /* sq_item, x[i] */ + 0, /* was_sq_slice, x[i:j] */ + (ssizeobjargproc) 0, /* sq_as_item, x[i]=v */ + 0, /* sq_ass_slice, x[i:j]=v */ + 0, /* sq_contains */ + 0, /* sq_inplace_concat */ + 0, /* sq_inplace_repeat */ +}; + +static PyTypeObject BufListType; + +/* Buffer list object - Implementation + */ + +/*ARGSUSED*/ +static Py_ssize_t BufListLength(PyObject *self UNUSED) +{ + buf_T *b = firstbuf; + Py_ssize_t n = 0; + + while (b) + { + ++n; + b = b->b_next; + } + + return n; +} + +/*ARGSUSED*/ +static PyObject * BufListItem(PyObject *self UNUSED, Py_ssize_t n) +{ + buf_T *b; + + for (b = firstbuf; b; b = b->b_next, --n) + { + if (n == 0) + return BufferNew(b); + } + + PyErr_SetString(PyExc_IndexError, _("no such buffer")); + return NULL; +} + +/* Window object - Definitions + */ + +static struct PyMethodDef WindowMethods[] = { + /* name, function, calling, documentation */ + { NULL, NULL, 0, NULL } +}; + +static PyTypeObject WindowType = { + PyVarObject_HEAD_INIT(NULL, 0) + "vim.window", /* tp_name */ + sizeof(WindowObject), /* tp_basicsize */ + 0, /* tp_itemsize */ + WindowDestructor, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_reserved */ + WindowRepr, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + WindowGetattro, /* tp_getattro */ + WindowSetattro, /* tp_setattro */ + 0, /* tp_as_Window */ + Py_TPFLAGS_DEFAULT, /* tp_flags */ + "vim Window object", /* tp_doc */ + 0, /*tp_traverse*/ + 0, /*tp_clear*/ + 0, /*tp_richcompare*/ + 0, /*tp_weaklistoffset*/ + 0, /*tp_iter*/ + 0, /*tp_iternext*/ + WindowMethods, /*tp_methods*/ + 0, /*tp_members*/ + 0, /*tp_getset*/ + 0, /*tp_base*/ + 0, /*tp_dict*/ + 0, /*tp_descr_get*/ + 0, /*tp_descr_set*/ + 0, /*tp_dictoffset*/ + 0, /*tp_init*/ + call_PyType_GenericAlloc, /*tp_alloc*/ + call_PyType_GenericNew, /*tp_new*/ + call_PyObject_Free, /*tp_free*/ + 0, /*tp_is_gc*/ + 0, /*tp_bases*/ + 0, /*tp_mro*/ + 0, /*tp_cache*/ + 0, /*tp_subclasses*/ + 0, /*tp_weaklist*/ + 0, /*tp_del*/ + 0, /*tp_version_tag*/ +}; + +/* Window object - Implementation + */ + +static PyObject * WindowNew(win_T *win) +{ + /* We need to handle deletion of windows underneath us. + * If we add a "w_python3_ref" field to the win_T structure, + * then we can get at it in win_free() in vim. We then + * need to create only ONE Python object per window - if + * we try to create a second, just INCREF the existing one + * and return it. The (single) Python object referring to + * the window is stored in "w_python3_ref". + * On a win_free() we set the Python object's win_T* field + * to an invalid value. We trap all uses of a window + * object, and reject them if the win_T* field is invalid. + */ + + WindowObject *self; + + if (win->w_python3_ref) + { + self = win->w_python3_ref; + Py_INCREF(self); + } + else + { + self = PyObject_NEW(WindowObject, &WindowType); + if (self == NULL) + return NULL; + self->win = win; + win->w_python3_ref = self; + } + + return (PyObject *)(self); +} + +static void WindowDestructor(PyObject *self) +{ + WindowObject *this = (WindowObject *)(self); + + if (this->win && this->win != INVALID_WINDOW_VALUE) + this->win->w_python3_ref = NULL; +} + +static int CheckWindow(WindowObject *this) +{ + if (this->win == INVALID_WINDOW_VALUE) + { + PyErr_SetVim(_("attempt to refer to deleted window")); + return -1; + } + + return 0; +} + +static PyObject * WindowGetattro(PyObject *self, PyObject *nameobj) +{ + WindowObject *this = (WindowObject *)(self); + + char *name = ""; + if (PyUnicode_Check(nameobj)) + name = _PyUnicode_AsString(nameobj); + + + if (CheckWindow(this)) + return NULL; + + if (strcmp(name, "buffer") == 0) + return (PyObject *)BufferNew(this->win->w_buffer); + else if (strcmp(name, "cursor") == 0) + { + pos_T *pos = &this->win->w_cursor; + + return Py_BuildValue("(ll)", (long)(pos->lnum), (long)(pos->col)); + } + else if (strcmp(name, "height") == 0) + return Py_BuildValue("l", (long)(this->win->w_height)); +#ifdef FEAT_VERTSPLIT + else if (strcmp(name, "width") == 0) + return Py_BuildValue("l", (long)(W_WIDTH(this->win))); +#endif + else if (strcmp(name,"__members__") == 0) + return Py_BuildValue("[sss]", "buffer", "cursor", "height"); + else + return PyObject_GenericGetAttr(self, nameobj); +} + +static int WindowSetattro(PyObject *self, PyObject *nameobj, PyObject *val) +{ + WindowObject *this = (WindowObject *)(self); + + char *name = ""; + if (PyUnicode_Check(nameobj)) + name = _PyUnicode_AsString(nameobj); + + + if (CheckWindow(this)) + return -1; + + if (strcmp(name, "buffer") == 0) + { + PyErr_SetString(PyExc_TypeError, _("readonly attribute")); + return -1; + } + else if (strcmp(name, "cursor") == 0) + { + long lnum; + long col; + + if (!PyArg_Parse(val, "(ll)", &lnum, &col)) + return -1; + + if (lnum <= 0 || lnum > this->win->w_buffer->b_ml.ml_line_count) + { + PyErr_SetVim(_("cursor position outside buffer")); + return -1; + } + + /* Check for keyboard interrupts */ + if (VimErrorCheck()) + return -1; + + /* NO CHECK ON COLUMN - SEEMS NOT TO MATTER */ + + this->win->w_cursor.lnum = lnum; + this->win->w_cursor.col = col; + update_screen(VALID); + + return 0; + } + else if (strcmp(name, "height") == 0) + { + int height; + win_T *savewin; + + if (!PyArg_Parse(val, "i", &height)) + return -1; + +#ifdef FEAT_GUI + need_mouse_correct = TRUE; +#endif + savewin = curwin; + curwin = this->win; + win_setheight(height); + curwin = savewin; + + /* Check for keyboard interrupts */ + if (VimErrorCheck()) + return -1; + + return 0; + } +#ifdef FEAT_VERTSPLIT + else if (strcmp(name, "width") == 0) + { + int width; + win_T *savewin; + + if (!PyArg_Parse(val, "i", &width)) + return -1; + +#ifdef FEAT_GUI + need_mouse_correct = TRUE; +#endif + savewin = curwin; + curwin = this->win; + win_setwidth(width); + curwin = savewin; + + /* Check for keyboard interrupts */ + if (VimErrorCheck()) + return -1; + + return 0; + } +#endif + else + { + PyErr_SetString(PyExc_AttributeError, name); + return -1; + } +} + +static PyObject * WindowRepr(PyObject *self) +{ + static char repr[100]; + WindowObject *this = (WindowObject *)(self); + + if (this->win == INVALID_WINDOW_VALUE) + { + vim_snprintf(repr, 100, _("<window object (deleted) at %p>"), (self)); + return PyUnicode_FromString(repr); + } + else + { + int i = 0; + win_T *w; + + for (w = firstwin; w != NULL && w != this->win; w = W_NEXT(w)) + ++i; + + if (w == NULL) + vim_snprintf(repr, 100, _("<window object (unknown) at %p>"), + (self)); + else + vim_snprintf(repr, 100, _("<window %d>"), i); + + return PyUnicode_FromString(repr); + } +} + +/* Window list object - Definitions + */ + +typedef struct +{ + PyObject_HEAD +} +WinListObject; + +static PySequenceMethods WinListAsSeq = { + (lenfunc) WinListLength, /* sq_length, len(x) */ + (binaryfunc) 0, /* sq_concat, x+y */ + (ssizeargfunc) 0, /* sq_repeat, x*n */ + (ssizeargfunc) WinListItem, /* sq_item, x[i] */ + 0, /* sq_slice, x[i:j] */ + (ssizeobjargproc)0, /* sq_as_item, x[i]=v */ + 0, /* sq_ass_slice, x[i:j]=v */ + 0, /* sq_contains */ + 0, /* sq_inplace_concat */ + 0, /* sq_inplace_repeat */ +}; + +static PyTypeObject WinListType; + +/* Window list object - Implementation + */ +/*ARGSUSED*/ +static Py_ssize_t WinListLength(PyObject *self UNUSED) +{ + win_T *w = firstwin; + Py_ssize_t n = 0; + + while (w != NULL) + { + ++n; + w = W_NEXT(w); + } + + return n; +} + +/*ARGSUSED*/ +static PyObject * WinListItem(PyObject *self UNUSED, Py_ssize_t n) +{ + win_T *w; + + for (w = firstwin; w != NULL; w = W_NEXT(w), --n) + if (n == 0) + return WindowNew(w); + + PyErr_SetString(PyExc_IndexError, _("no such window")); + return NULL; +} + +/* Current items object - Definitions + */ + +typedef struct +{ + PyObject_HEAD +} +CurrentObject; + +static PyTypeObject CurrentType; + +/* Current items object - Implementation + */ +/*ARGSUSED*/ +static PyObject * CurrentGetattro(PyObject *self UNUSED, PyObject *nameobj) +{ + char *name = ""; + if (PyUnicode_Check(nameobj)) + name = _PyUnicode_AsString(nameobj); + + if (strcmp(name, "buffer") == 0) + return (PyObject *)BufferNew(curbuf); + else if (strcmp(name, "window") == 0) + return (PyObject *)WindowNew(curwin); + else if (strcmp(name, "line") == 0) + return GetBufferLine(curbuf, (Py_ssize_t)curwin->w_cursor.lnum); + else if (strcmp(name, "range") == 0) + return RangeNew(curbuf, RangeStart, RangeEnd); + else if (strcmp(name,"__members__") == 0) + return Py_BuildValue("[ssss]", "buffer", "window", "line", "range"); + else + { + PyErr_SetString(PyExc_AttributeError, name); + return NULL; + } +} + +/*ARGSUSED*/ +static int CurrentSetattro(PyObject *self UNUSED, PyObject *nameobj, PyObject *value) +{ + char *name = ""; + if (PyUnicode_Check(nameobj)) + name = _PyUnicode_AsString(nameobj); + + if (strcmp(name, "line") == 0) + { + if (SetBufferLine(curbuf, (Py_ssize_t)curwin->w_cursor.lnum, value, NULL) == FAIL) + return -1; + + return 0; + } + else + { + PyErr_SetString(PyExc_AttributeError, name); + return -1; + } +} + +/* External interface + */ + + void +python3_buffer_free(buf_T *buf) +{ + if (buf->b_python3_ref != NULL) + { + BufferObject *bp = buf->b_python3_ref; + bp->buf = INVALID_BUFFER_VALUE; + buf->b_python3_ref = NULL; + } +} + +#if defined(FEAT_WINDOWS) || defined(PROTO) + void +python3_window_free(win_T *win) +{ + if (win->w_python3_ref != NULL) + { + WindowObject *wp = win->w_python3_ref; + wp->win = INVALID_WINDOW_VALUE; + win->w_python3_ref = NULL; + } +} +#endif + +static BufListObject TheBufferList = +{ + PyObject_HEAD_INIT(&BufListType) +}; + +static WinListObject TheWindowList = +{ + PyObject_HEAD_INIT(&WinListType) +}; + +static CurrentObject TheCurrent = +{ + PyObject_HEAD_INIT(&CurrentType) +}; + +PyDoc_STRVAR(vim_module_doc,"vim python interface\n"); + +static struct PyModuleDef vimmodule; + +PyMODINIT_FUNC Py3Init_vim(void) +{ + PyObject *mod; + /* The special value is removed from sys.path in Python3_Init(). */ + static wchar_t *(argv[2]) = {L"/must>not&exist/foo", NULL}; + + PyType_Ready(&BufferType); + PyType_Ready(&RangeType); + PyType_Ready(&WindowType); + PyType_Ready(&BufListType); + PyType_Ready(&WinListType); + PyType_Ready(&CurrentType); + + /* Set sys.argv[] to avoid a crash in warn(). */ + PySys_SetArgv(1, argv); + + mod = PyModule_Create(&vimmodule); + + VimError = Py_BuildValue("s", "vim.error"); + + PyModule_AddObject(mod, "error", VimError); + Py_INCREF((PyObject *)(void *)&TheBufferList); + PyModule_AddObject(mod, "buffers", (PyObject *)(void *)&TheBufferList); + Py_INCREF((PyObject *)(void *)&TheCurrent); + PyModule_AddObject(mod, "current", (PyObject *)(void *)&TheCurrent); + Py_INCREF((PyObject *)(void *)&TheWindowList); + PyModule_AddObject(mod, "windows", (PyObject *)(void *)&TheWindowList); + + if (PyErr_Occurred()) + return NULL; + + return mod; +} + +/************************************************************************* + * 4. Utility functions for handling the interface between Vim and Python. + */ + + +/* Get a list of lines from the specified buffer. The line numbers + * are in Vim format (1-based). The range is from lo up to, but not + * including, hi. The list is returned as a Python list of string objects. + */ +static PyObject * GetBufferLineList(buf_T *buf, Py_ssize_t lo, Py_ssize_t hi) +{ + Py_ssize_t i; + Py_ssize_t n = hi - lo; + PyObject *list = PyList_New(n); + + if (list == NULL) + return NULL; + + for (i = 0; i < n; ++i) + { + PyObject *str = LineToString((char *)ml_get_buf(buf, (linenr_T)(lo+i), FALSE)); + + /* Error check - was the Python string creation OK? */ + if (str == NULL) + { + Py_DECREF(list); + return NULL; + } + + /* Set the list item */ + if (PyList_SetItem(list, i, str)) + { + Py_DECREF(str); + Py_DECREF(list); + return NULL; + } + } + + /* The ownership of the Python list is passed to the caller (ie, + * the caller should Py_DECREF() the object when it is finished + * with it). + */ + + return list; +} + +/* Get a line from the specified buffer. The line number is + * in Vim format (1-based). The line is returned as a Python + * string object. + */ +static PyObject * GetBufferLine(buf_T *buf, Py_ssize_t n) +{ + return LineToString((char *)ml_get_buf(buf, (linenr_T)n, FALSE)); +} + +/* + * Check if deleting lines made the cursor position invalid. + * Changed the lines from "lo" to "hi" and added "extra" lines (negative if + * deleted). + */ +static void py_fix_cursor(linenr_T lo, linenr_T hi, linenr_T extra) +{ + if (curwin->w_cursor.lnum >= lo) + { + /* Adjust the cursor position if it's in/after the changed + * lines. */ + if (curwin->w_cursor.lnum >= hi) + { + curwin->w_cursor.lnum += extra; + check_cursor_col(); + } + else if (extra < 0) + { + curwin->w_cursor.lnum = lo; + check_cursor(); + } + else + check_cursor_col(); + changed_cline_bef_curs(); + } + invalidate_botline(); +} + +/* Replace a line in the specified buffer. The line number is + * in Vim format (1-based). The replacement line is given as + * a Python string object. The object is checked for validity + * and correct format. Errors are returned as a value of FAIL. + * The return value is OK on success. + * If OK is returned and len_change is not NULL, *len_change + * is set to the change in the buffer length. + */ +static int SetBufferLine(buf_T *buf, Py_ssize_t n, PyObject *line, Py_ssize_t *len_change) +{ + /* First of all, we check the thpe of the supplied Python object. + * There are three cases: + * 1. NULL, or None - this is a deletion. + * 2. A string - this is a replacement. + * 3. Anything else - this is an error. + */ + if (line == Py_None || line == NULL) + { + buf_T *savebuf = curbuf; + + PyErr_Clear(); + curbuf = buf; + + if (u_savedel((linenr_T)n, 1L) == FAIL) + PyErr_SetVim(_("cannot save undo information")); + else if (ml_delete((linenr_T)n, FALSE) == FAIL) + PyErr_SetVim(_("cannot delete line")); + else + { + deleted_lines_mark((linenr_T)n, 1L); + if (buf == curwin->w_buffer) + py_fix_cursor((linenr_T)n, (linenr_T)n + 1, (linenr_T)-1); + } + + curbuf = savebuf; + + if (PyErr_Occurred() || VimErrorCheck()) + return FAIL; + + if (len_change) + *len_change = -1; + + return OK; + } + else if (PyUnicode_Check(line)) + { + char *save = StringToLine(line); + buf_T *savebuf = curbuf; + + if (save == NULL) + return FAIL; + + /* We do not need to free "save" if ml_replace() consumes it. */ + PyErr_Clear(); + curbuf = buf; + + if (u_savesub((linenr_T)n) == FAIL) + { + PyErr_SetVim(_("cannot save undo information")); + vim_free(save); + } + else if (ml_replace((linenr_T)n, (char_u *)save, FALSE) == FAIL) + { + PyErr_SetVim(_("cannot replace line")); + vim_free(save); + } + else + changed_bytes((linenr_T)n, 0); + + curbuf = savebuf; + + /* Check that the cursor is not beyond the end of the line now. */ + if (buf == curwin->w_buffer) + check_cursor_col(); + + if (PyErr_Occurred() || VimErrorCheck()) + return FAIL; + + if (len_change) + *len_change = 0; + + return OK; + } + else + { + PyErr_BadArgument(); + return FAIL; + } +} + +/* Insert a number of lines into the specified buffer after the specifed line. + * The line number is in Vim format (1-based). The lines to be inserted are + * given as a Python list of string objects or as a single string. The lines + * to be added are checked for validity and correct format. Errors are + * returned as a value of FAIL. The return value is OK on success. + * If OK is returned and len_change is not NULL, *len_change + * is set to the change in the buffer length. + */ +static int InsertBufferLines(buf_T *buf, Py_ssize_t n, PyObject *lines, Py_ssize_t *len_change) +{ + /* First of all, we check the type of the supplied Python object. + * It must be a string or a list, or the call is in error. + */ + if (PyUnicode_Check(lines)) + { + char *str = StringToLine(lines); + buf_T *savebuf; + + if (str == NULL) + return FAIL; + + savebuf = curbuf; + + PyErr_Clear(); + curbuf = buf; + + if (u_save((linenr_T)n, (linenr_T)(n+1)) == FAIL) + PyErr_SetVim(_("cannot save undo information")); + else if (ml_append((linenr_T)n, (char_u *)str, 0, FALSE) == FAIL) + PyErr_SetVim(_("cannot insert line")); + else + appended_lines_mark((linenr_T)n, 1L); + + vim_free(str); + curbuf = savebuf; + update_screen(VALID); + + if (PyErr_Occurred() || VimErrorCheck()) + return FAIL; + + if (len_change) + *len_change = 1; + + return OK; + } + else if (PyList_Check(lines)) + { + Py_ssize_t i; + Py_ssize_t size = PyList_Size(lines); + char **array; + buf_T *savebuf; + + array = (char **)alloc((unsigned)(size * sizeof(char *))); + if (array == NULL) + { + PyErr_NoMemory(); + return FAIL; + } + + for (i = 0; i < size; ++i) + { + PyObject *line = PyList_GetItem(lines, i); + array[i] = StringToLine(line); + + if (array[i] == NULL) + { + while (i) + vim_free(array[--i]); + vim_free(array); + return FAIL; + } + } + + savebuf = curbuf; + + PyErr_Clear(); + curbuf = buf; + + if (u_save((linenr_T)n, (linenr_T)(n + 1)) == FAIL) + PyErr_SetVim(_("cannot save undo information")); + else + { + for (i = 0; i < size; ++i) + { + if (ml_append((linenr_T)(n + i), + (char_u *)array[i], 0, FALSE) == FAIL) + { + PyErr_SetVim(_("cannot insert line")); + + /* Free the rest of the lines */ + while (i < size) + vim_free(array[i++]); + + break; + } + vim_free(array[i]); + } + if (i > 0) + appended_lines_mark((linenr_T)n, (long)i); + } + + /* Free the array of lines. All of its contents have now + * been freed. + */ + vim_free(array); + + curbuf = savebuf; + update_screen(VALID); + + if (PyErr_Occurred() || VimErrorCheck()) + return FAIL; + + if (len_change) + *len_change = size; + + return OK; + } + else + { + PyErr_BadArgument(); + return FAIL; + } +} + +/* Convert a Vim line into a Python string. + * All internal newlines are replaced by null characters. + * + * On errors, the Python exception data is set, and NULL is returned. + */ +static PyObject * LineToString(const char *str) +{ + PyObject *result; + Py_ssize_t len = strlen(str); + char *tmp,*p; + + tmp = (char *)alloc((unsigned)(len+1)); + p = tmp; + if (p == NULL) + { + PyErr_NoMemory(); + return NULL; + } + + while (*str) + { + if (*str == '\n') + *p = '\0'; + else + *p = *str; + + ++p; + ++str; + } + *p = '\0'; + + result = PyUnicode_FromStringAndSize(tmp, len); + + vim_free(tmp); + return result; +} + +/* Convert a Python string into a Vim line. + * + * The result is in allocated memory. All internal nulls are replaced by + * newline characters. It is an error for the string to contain newline + * characters. + * + * On errors, the Python exception data is set, and NULL is returned. + */ +static char * StringToLine(PyObject *obj) +{ + const char *str; + char *save; + Py_ssize_t len; + Py_ssize_t i; + char *p; + + if (obj == NULL || !PyUnicode_Check(obj)) + { + PyErr_BadArgument(); + return NULL; + } + + str = _PyUnicode_AsString(obj); + len = PyUnicode_GET_SIZE(obj); + + /* + * Error checking: String must not contain newlines, as we + * are replacing a single line, and we must replace it with + * a single line. + * A trailing newline is removed, so that append(f.readlines()) works. + */ + p = memchr(str, '\n', len); + if (p != NULL) + { + if (p == str + len - 1) + --len; + else + { + PyErr_SetVim(_("string cannot contain newlines")); + return NULL; + } + } + + /* Create a copy of the string, with internal nulls replaced by + * newline characters, as is the vim convention. + */ + save = (char *)alloc((unsigned)(len+1)); + if (save == NULL) + { + PyErr_NoMemory(); + return NULL; + } + + for (i = 0; i < len; ++i) + { + if (str[i] == '\0') + save[i] = '\n'; + else + save[i] = str[i]; + } + + save[i] = '\0'; + + return save; +} + +/* Check to see whether a Vim error has been reported, or a keyboard + * interrupt has been detected. + */ +static int VimErrorCheck(void) +{ + if (got_int) + { + PyErr_SetNone(PyExc_KeyboardInterrupt); + return 1; + } + else if (did_emsg && !PyErr_Occurred()) + { + PyErr_SetNone(VimError); + return 1; + } + + return 0; +} + + +static void init_structs(void) +{ + vim_memset(&OutputType, 0, sizeof(OutputType)); + OutputType.tp_name = "vim.message"; + OutputType.tp_basicsize = sizeof(OutputObject); + OutputType.tp_getattro = OutputGetattro; + OutputType.tp_setattro = OutputSetattro; + OutputType.tp_flags = Py_TPFLAGS_DEFAULT; + OutputType.tp_doc = "vim message object"; + OutputType.tp_methods = OutputMethods; + OutputType.tp_alloc = call_PyType_GenericAlloc; + OutputType.tp_new = call_PyType_GenericNew; + OutputType.tp_free = call_PyObject_Free; + + vim_memset(&BufferType, 0, sizeof(BufferType)); + BufferType.tp_name = "vim.buffer"; + BufferType.tp_basicsize = sizeof(BufferType); + BufferType.tp_dealloc = BufferDestructor; + BufferType.tp_repr = BufferRepr; + BufferType.tp_as_sequence = &BufferAsSeq; + BufferType.tp_as_mapping = &BufferAsMapping; + BufferType.tp_getattro = BufferGetattro; + BufferType.tp_flags = Py_TPFLAGS_DEFAULT; + BufferType.tp_doc = "vim buffer object"; + BufferType.tp_methods = BufferMethods; + BufferType.tp_alloc = call_PyType_GenericAlloc; + BufferType.tp_new = call_PyType_GenericNew; + BufferType.tp_free = call_PyObject_Free; + + vim_memset(&BufListType, 0, sizeof(BufListType)); + BufListType.tp_name = "vim.bufferlist"; + BufListType.tp_basicsize = sizeof(BufListObject); + BufListType.tp_as_sequence = &BufListAsSeq; + BufListType.tp_flags = Py_TPFLAGS_DEFAULT; + BufferType.tp_doc = "vim buffer list"; + + vim_memset(&WinListType, 0, sizeof(WinListType)); + WinListType.tp_name = "vim.windowlist"; + WinListType.tp_basicsize = sizeof(WinListType); + WinListType.tp_as_sequence = &WinListAsSeq; + WinListType.tp_flags = Py_TPFLAGS_DEFAULT; + WinListType.tp_doc = "vim window list"; + + vim_memset(&RangeType, 0, sizeof(RangeType)); + RangeType.tp_name = "vim.range"; + RangeType.tp_basicsize = sizeof(RangeObject); + RangeType.tp_dealloc = RangeDestructor; + RangeType.tp_repr = RangeRepr; + RangeType.tp_as_sequence = &RangeAsSeq; + RangeType.tp_as_mapping = &RangeAsMapping; + RangeType.tp_getattro = RangeGetattro; + RangeType.tp_flags = Py_TPFLAGS_DEFAULT; + RangeType.tp_doc = "vim Range object"; + RangeType.tp_methods = RangeMethods; + RangeType.tp_alloc = call_PyType_GenericAlloc; + RangeType.tp_new = call_PyType_GenericNew; + RangeType.tp_free = call_PyObject_Free; + + vim_memset(&CurrentType, 0, sizeof(CurrentType)); + CurrentType.tp_name = "vim.currentdata"; + CurrentType.tp_basicsize = sizeof(CurrentObject); + CurrentType.tp_getattro = CurrentGetattro; + CurrentType.tp_setattro = CurrentSetattro; + CurrentType.tp_flags = Py_TPFLAGS_DEFAULT; + CurrentType.tp_doc = "vim current object"; + + vim_memset(&vimmodule, 0, sizeof(vimmodule)); + vimmodule.m_name = "vim"; + vimmodule.m_doc = vim_module_doc; + vimmodule.m_size = -1; + vimmodule.m_methods = VimMethods; +} diff --git a/src/main.c b/src/main.c index 8f0907377..cce99cd13 100644 --- a/src/main.c +++ b/src/main.c @@ -1405,6 +1405,9 @@ getout(exitval) #ifdef FEAT_PYTHON python_end(); #endif +#ifdef FEAT_PYTHON3 + python3_end(); +#endif #ifdef FEAT_PERL perl_end(); #endif diff --git a/src/proto.h b/src/proto.h index d9c00916c..40fb2a99a 100644 --- a/src/proto.h +++ b/src/proto.h @@ -186,6 +186,10 @@ void qsort __ARGS((void *base, size_t elm_count, size_t elm_size, int (*cmp)(con # include "if_python.pro" # endif +# ifdef FEAT_PYTHON3 +# include "if_python3.pro" +# endif + # ifdef FEAT_TCL # include "if_tcl.pro" # endif diff --git a/src/proto/if_python3.pro b/src/proto/if_python3.pro new file mode 100644 index 000000000..cca043092 --- /dev/null +++ b/src/proto/if_python3.pro @@ -0,0 +1,8 @@ +/* if_python.c */ +int python3_enabled __ARGS((int verbose)); +void python3_end __ARGS((void)); +void ex_python3 __ARGS((exarg_T *eap)); +void ex_py3file __ARGS((exarg_T *eap)); +void python3_buffer_free __ARGS((buf_T *buf)); +void python3_window_free __ARGS((win_T *win)); +/* vim: set ft=c : */ diff --git a/src/structs.h b/src/structs.h index c0e31c0d7..e0f9f3bef 100644 --- a/src/structs.h +++ b/src/structs.h @@ -1612,6 +1612,10 @@ struct file_buffer void *b_python_ref; /* The Python reference to this buffer */ #endif +#ifdef FEAT_PYTHON3 + void *b_python3_ref; /* The Python3 reference to this buffer */ +#endif + #ifdef FEAT_TCL void *b_tcl_ref; #endif @@ -2106,6 +2110,10 @@ struct window_S void *w_python_ref; /* The Python value for this window */ #endif +#ifdef FEAT_PYTHON3 + void *w_python3_ref; /* The Python value for this window */ +#endif + #ifdef FEAT_TCL void *w_tcl_ref; #endif diff --git a/src/version.c b/src/version.c index 8181986d6..79522a4f3 100644 --- a/src/version.c +++ b/src/version.c @@ -474,6 +474,15 @@ static char *(features[]) = #else "-python", #endif +#ifdef FEAT_PYTHON3 +# ifdef DYNAMIC_PYTHON3 + "+python3/dyn", +# else + "+python3", +# endif +#else + "-python3", +#endif #ifdef FEAT_QUICKFIX "+quickfix", #else @@ -13,6 +13,7 @@ #if defined(__BORLANDC__) && defined(WIN32) && !defined(DEBUG) #if defined(FEAT_PERL) || \ defined(FEAT_PYTHON) || \ + defined(FEAT_PYTHON3) || \ defined(FEAT_RUBY) || \ defined(FEAT_TCL) || \ defined(FEAT_MZSCHEME) || \ diff --git a/src/window.c b/src/window.c index 2d8f801a6..4b721eaa7 100644 --- a/src/window.c +++ b/src/window.c @@ -4386,6 +4386,10 @@ win_free(wp, tp) python_window_free(wp); #endif +#ifdef FEAT_PYTHON3 + python3_window_free(wp); +#endif + #ifdef FEAT_TCL tcl_window_free(wp); #endif |