aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/nvim/eval.c24
-rw-r--r--src/nvim/eval.lua1
-rw-r--r--src/nvim/ex_cmds.lua24
-rw-r--r--src/nvim/ex_cmds2.c120
-rw-r--r--src/nvim/ex_docmd.c4
-rw-r--r--src/nvim/option.c4
-rw-r--r--src/nvim/option_defs.h1
-rw-r--r--src/nvim/options.lua8
-rw-r--r--src/nvim/testdir/pyxfile/py2_magic.py4
-rw-r--r--src/nvim/testdir/pyxfile/py2_shebang.py4
-rw-r--r--src/nvim/testdir/pyxfile/py3_magic.py4
-rw-r--r--src/nvim/testdir/pyxfile/py3_shebang.py4
-rw-r--r--src/nvim/testdir/pyxfile/pyx.py2
-rw-r--r--src/nvim/testdir/test_pyx2.vim74
-rw-r--r--src/nvim/testdir/test_pyx3.vim74
15 files changed, 351 insertions, 1 deletions
diff --git a/src/nvim/eval.c b/src/nvim/eval.c
index 3a231ab8f1..f1cce716e0 100644
--- a/src/nvim/eval.c
+++ b/src/nvim/eval.c
@@ -10704,6 +10704,7 @@ static void f_has(typval_T *argvars, typval_T *rettv, FunPtr fptr)
"postscript",
"printer",
"profile",
+ "pythonx",
"reltime",
"quickfix",
"rightleft",
@@ -13026,6 +13027,10 @@ static void f_pumvisible(typval_T *argvars, typval_T *rettv, FunPtr fptr)
*/
static void f_pyeval(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
+ if (p_pyx == 0) {
+ p_pyx = 2;
+ }
+
script_host_eval("python", argvars, rettv);
}
@@ -13034,9 +13039,24 @@ static void f_pyeval(typval_T *argvars, typval_T *rettv, FunPtr fptr)
*/
static void f_py3eval(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
+ if (p_pyx == 0) {
+ p_pyx = 3;
+ }
+
script_host_eval("python3", argvars, rettv);
}
+// "pyxeval()" function
+static void f_pyxeval(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+{
+ init_pyxversion();
+ if (p_pyx == 2) {
+ f_pyeval(argvars, rettv, NULL);
+ } else {
+ f_py3eval(argvars, rettv, NULL);
+ }
+}
+
/*
* "range()" function
*/
@@ -20133,7 +20153,9 @@ void ex_function(exarg_T *eap)
arg = skipwhite(skiptowhite(p));
if (arg[0] == '<' && arg[1] =='<'
&& ((p[0] == 'p' && p[1] == 'y'
- && (!ASCII_ISALPHA(p[2]) || p[2] == 't'))
+ && (!ASCII_ISALNUM(p[2]) || p[2] == 't'
+ || ((p[2] == '3' || p[2] == 'x')
+ && !ASCII_ISALPHA(p[3]))))
|| (p[0] == 'p' && p[1] == 'e'
&& (!ASCII_ISALPHA(p[2]) || p[2] == 'r'))
|| (p[0] == 't' && p[1] == 'c'
diff --git a/src/nvim/eval.lua b/src/nvim/eval.lua
index e72bb7b870..78cac4878d 100644
--- a/src/nvim/eval.lua
+++ b/src/nvim/eval.lua
@@ -226,6 +226,7 @@ return {
pumvisible={},
py3eval={args=1},
pyeval={args=1},
+ pyxeval={args=1},
range={args={1, 3}},
readfile={args={1, 3}},
reltime={args={0, 2}},
diff --git a/src/nvim/ex_cmds.lua b/src/nvim/ex_cmds.lua
index 79ca5363e0..4806eff1b4 100644
--- a/src/nvim/ex_cmds.lua
+++ b/src/nvim/ex_cmds.lua
@@ -2067,6 +2067,30 @@ return {
func='ex_py3file',
},
{
+ command='pyx',
+ flags=bit.bor(RANGE, EXTRA, NEEDARG, CMDWIN),
+ addr_type=ADDR_LINES,
+ func='ex_pyx',
+ },
+ {
+ command='pyxdo',
+ flags=bit.bor(RANGE, DFLALL, EXTRA, NEEDARG, CMDWIN),
+ addr_type=ADDR_LINES,
+ func='ex_pyxdo',
+ },
+ {
+ command='pythonx',
+ flags=bit.bor(RANGE, EXTRA, NEEDARG, CMDWIN),
+ addr_type=ADDR_LINES,
+ func='ex_pyx',
+ },
+ {
+ command='pyxfile',
+ flags=bit.bor(RANGE, FILE1, NEEDARG, CMDWIN),
+ addr_type=ADDR_LINES,
+ func='ex_pyxfile',
+ },
+ {
command='quit',
flags=bit.bor(BANG, RANGE, COUNT, NOTADR, TRLBAR, CMDWIN),
addr_type=ADDR_WINDOWS,
diff --git a/src/nvim/ex_cmds2.c b/src/nvim/ex_cmds2.c
index 1ffcf67ef7..51d20c746a 100644
--- a/src/nvim/ex_cmds2.c
+++ b/src/nvim/ex_cmds2.c
@@ -2809,6 +2809,126 @@ void ex_options(exarg_T *eap)
cmd_source((char_u *)SYS_OPTWIN_FILE, NULL);
}
+// Detect Python 3 or 2, and initialize 'pyxversion'.
+void init_pyxversion(void)
+{
+ if (p_pyx == 0) {
+ if (!eval_has_provider("python3")) {
+ p_pyx = 3;
+ } else if (!eval_has_provider("python")) {
+ p_pyx = 2;
+ }
+ }
+}
+
+// Does a file contain one of the following strings at the beginning of any
+// line?
+// "#!(any string)python2" => returns 2
+// "#!(any string)python3" => returns 3
+// "# requires python 2.x" => returns 2
+// "# requires python 3.x" => returns 3
+// otherwise return 0.
+static int requires_py_version(char_u *filename)
+{
+ FILE *file;
+ int requires_py_version = 0;
+ int i, lines;
+
+ lines = (int)p_mls;
+ if (lines < 0) {
+ lines = 5;
+ }
+
+ file = mch_fopen((char *)filename, "r");
+ if (file != NULL) {
+ for (i = 0; i < lines; i++) {
+ if (vim_fgets(IObuff, IOSIZE, file)) {
+ break;
+ }
+ if (i == 0 && IObuff[0] == '#' && IObuff[1] == '!') {
+ // Check shebang.
+ if (strstr((char *)IObuff + 2, "python2") != NULL) {
+ requires_py_version = 2;
+ break;
+ }
+ if (strstr((char *)IObuff + 2, "python3") != NULL) {
+ requires_py_version = 3;
+ break;
+ }
+ }
+ IObuff[21] = '\0';
+ if (STRCMP("# requires python 2.x", IObuff) == 0) {
+ requires_py_version = 2;
+ break;
+ }
+ if (STRCMP("# requires python 3.x", IObuff) == 0) {
+ requires_py_version = 3;
+ break;
+ }
+ }
+ fclose(file);
+ }
+ return requires_py_version;
+}
+
+
+// Source a python file using the requested python version.
+static void source_pyx_file(exarg_T *eap, char_u *fname)
+{
+ exarg_T ex;
+ long int v = requires_py_version(fname);
+
+ init_pyxversion();
+ if (v == 0) {
+ // user didn't choose a preference, 'pyx' is used
+ v = p_pyx;
+ }
+
+ // now source, if required python version is not supported show
+ // unobtrusive message.
+ if (eap == NULL) {
+ memset(&ex, 0, sizeof(ex));
+ } else {
+ ex = *eap;
+ }
+ ex.arg = fname;
+ ex.cmd = (char_u *)(v == 2 ? "pyfile" : "pyfile3");
+
+ if (v == 2) {
+ ex_pyfile(&ex);
+ } else {
+ ex_py3file(&ex);
+ }
+}
+
+// ":pyxfile {fname}"
+void ex_pyxfile(exarg_T *eap)
+{
+ source_pyx_file(eap, eap->arg);
+}
+
+// ":pyx"
+void ex_pyx(exarg_T *eap)
+{
+ init_pyxversion();
+ if (p_pyx == 2) {
+ ex_python(eap);
+ } else {
+ ex_python3(eap);
+ }
+}
+
+// ":pyxdo"
+void ex_pyxdo(exarg_T *eap)
+{
+ init_pyxversion();
+ if (p_pyx == 2) {
+ ex_pydo(eap);
+ } else {
+ ex_pydo3(eap);
+ }
+}
+
/// ":source {fname}"
void ex_source(exarg_T *eap)
{
diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c
index 8bc6193d08..ffa913efcf 100644
--- a/src/nvim/ex_docmd.c
+++ b/src/nvim/ex_docmd.c
@@ -2158,6 +2158,10 @@ static char_u * do_one_cmd(char_u **cmdlinep,
case CMD_python:
case CMD_py3:
case CMD_python3:
+ case CMD_pythonx:
+ case CMD_pyx:
+ case CMD_pyxdo:
+ case CMD_pyxfile:
case CMD_return:
case CMD_rightbelow:
case CMD_ruby:
diff --git a/src/nvim/option.c b/src/nvim/option.c
index 8c692f9f42..6b1c5c998f 100644
--- a/src/nvim/option.c
+++ b/src/nvim/option.c
@@ -4322,6 +4322,10 @@ static char *set_num_option(int opt_idx, char_u *varp, long value,
if (p_uc && !old_value) {
ml_open_files();
}
+ } else if (pp == &p_pyx) {
+ if (p_pyx != 0 && p_pyx != 2 && p_pyx != 3) {
+ errmsg = e_invarg;
+ }
} else if (pp == &p_ul || pp == &curbuf->b_p_ul) {
// sync undo before 'undolevels' changes
// use the old value, otherwise u_sync() may not work properly
diff --git a/src/nvim/option_defs.h b/src/nvim/option_defs.h
index 6a0d0e32e0..7b99b6f266 100644
--- a/src/nvim/option_defs.h
+++ b/src/nvim/option_defs.h
@@ -516,6 +516,7 @@ EXTERN char_u *p_pex; // 'patchexpr'
EXTERN char_u *p_pm; // 'patchmode'
EXTERN char_u *p_path; // 'path'
EXTERN char_u *p_cdpath; // 'cdpath'
+EXTERN long p_pyx; // 'pyxversion'
EXTERN long p_rdt; // 'redrawtime'
EXTERN int p_remap; // 'remap'
EXTERN long p_re; // 'regexpengine'
diff --git a/src/nvim/options.lua b/src/nvim/options.lua
index 0cc6f58c5f..aba8f8b893 100644
--- a/src/nvim/options.lua
+++ b/src/nvim/options.lua
@@ -1810,6 +1810,14 @@ return {
defaults={if_true={vi=0}}
},
{
+ full_name='pyxversion', abbreviation='pyx',
+ type='number', scope={'global'},
+ secure=true,
+ vi_def=true,
+ varname='p_pyx',
+ defaults={if_true={vi=0}}
+ },
+ {
full_name='quoteescape', abbreviation='qe',
type='string', scope={'buffer'},
vi_def=true,
diff --git a/src/nvim/testdir/pyxfile/py2_magic.py b/src/nvim/testdir/pyxfile/py2_magic.py
new file mode 100644
index 0000000000..819892fd16
--- /dev/null
+++ b/src/nvim/testdir/pyxfile/py2_magic.py
@@ -0,0 +1,4 @@
+# requires python 2.x
+
+import sys
+print(sys.version)
diff --git a/src/nvim/testdir/pyxfile/py2_shebang.py b/src/nvim/testdir/pyxfile/py2_shebang.py
new file mode 100644
index 0000000000..13bfc491ec
--- /dev/null
+++ b/src/nvim/testdir/pyxfile/py2_shebang.py
@@ -0,0 +1,4 @@
+#!/usr/bin/python2
+
+import sys
+print(sys.version)
diff --git a/src/nvim/testdir/pyxfile/py3_magic.py b/src/nvim/testdir/pyxfile/py3_magic.py
new file mode 100644
index 0000000000..d4b7ee0071
--- /dev/null
+++ b/src/nvim/testdir/pyxfile/py3_magic.py
@@ -0,0 +1,4 @@
+# requires python 3.x
+
+import sys
+print(sys.version)
diff --git a/src/nvim/testdir/pyxfile/py3_shebang.py b/src/nvim/testdir/pyxfile/py3_shebang.py
new file mode 100644
index 0000000000..ec05808ca4
--- /dev/null
+++ b/src/nvim/testdir/pyxfile/py3_shebang.py
@@ -0,0 +1,4 @@
+#!/usr/bin/python3
+
+import sys
+print(sys.version)
diff --git a/src/nvim/testdir/pyxfile/pyx.py b/src/nvim/testdir/pyxfile/pyx.py
new file mode 100644
index 0000000000..261a6512c0
--- /dev/null
+++ b/src/nvim/testdir/pyxfile/pyx.py
@@ -0,0 +1,2 @@
+import sys
+print(sys.version)
diff --git a/src/nvim/testdir/test_pyx2.vim b/src/nvim/testdir/test_pyx2.vim
new file mode 100644
index 0000000000..50e57c3bfb
--- /dev/null
+++ b/src/nvim/testdir/test_pyx2.vim
@@ -0,0 +1,74 @@
+" Test for pyx* commands and functions with Python 2.
+
+set pyx=2
+if !has('python')
+ finish
+endif
+
+let s:py2pattern = '^2\.[0-7]\.\d\+'
+let s:py3pattern = '^3\.\d\+\.\d\+'
+
+
+func Test_has_pythonx()
+ call assert_true(has('pythonx'))
+endfunc
+
+
+func Test_pyx()
+ redir => var
+ pyx << EOF
+import sys
+print(sys.version)
+EOF
+ redir END
+ call assert_match(s:py2pattern, split(var)[0])
+endfunc
+
+
+func Test_pyxdo()
+ pyx import sys
+ enew
+ pyxdo return sys.version.split("\n")[0]
+ call assert_match(s:py2pattern, split(getline('.'))[0])
+endfunc
+
+
+func Test_pyxeval()
+ pyx import sys
+ call assert_match(s:py2pattern, split(pyxeval('sys.version'))[0])
+endfunc
+
+
+func Test_pyxfile()
+ " No special comments nor shebangs
+ redir => var
+ pyxfile pyxfile/pyx.py
+ redir END
+ call assert_match(s:py2pattern, split(var)[0])
+
+ " Python 2 special comment
+ redir => var
+ pyxfile pyxfile/py2_magic.py
+ redir END
+ call assert_match(s:py2pattern, split(var)[0])
+
+ " Python 2 shebang
+ redir => var
+ pyxfile pyxfile/py2_shebang.py
+ redir END
+ call assert_match(s:py2pattern, split(var)[0])
+
+ if has('python3')
+ " Python 3 special comment
+ redir => var
+ pyxfile pyxfile/py3_magic.py
+ redir END
+ call assert_match(s:py3pattern, split(var)[0])
+
+ " Python 3 shebang
+ redir => var
+ pyxfile pyxfile/py3_shebang.py
+ redir END
+ call assert_match(s:py3pattern, split(var)[0])
+ endif
+endfunc
diff --git a/src/nvim/testdir/test_pyx3.vim b/src/nvim/testdir/test_pyx3.vim
new file mode 100644
index 0000000000..64546b4688
--- /dev/null
+++ b/src/nvim/testdir/test_pyx3.vim
@@ -0,0 +1,74 @@
+" Test for pyx* commands and functions with Python 3.
+
+set pyx=3
+if !has('python3')
+ finish
+endif
+
+let s:py2pattern = '^2\.[0-7]\.\d\+'
+let s:py3pattern = '^3\.\d\+\.\d\+'
+
+
+func Test_has_pythonx()
+ call assert_true(has('pythonx'))
+endfunc
+
+
+func Test_pyx()
+ redir => var
+ pyx << EOF
+import sys
+print(sys.version)
+EOF
+ redir END
+ call assert_match(s:py3pattern, split(var)[0])
+endfunc
+
+
+func Test_pyxdo()
+ pyx import sys
+ enew
+ pyxdo return sys.version.split("\n")[0]
+ call assert_match(s:py3pattern, split(getline('.'))[0])
+endfunc
+
+
+func Test_pyxeval()
+ pyx import sys
+ call assert_match(s:py3pattern, split(pyxeval('sys.version'))[0])
+endfunc
+
+
+func Test_pyxfile()
+ " No special comments nor shebangs
+ redir => var
+ pyxfile pyxfile/pyx.py
+ redir END
+ call assert_match(s:py3pattern, split(var)[0])
+
+ " Python 3 special comment
+ redir => var
+ pyxfile pyxfile/py3_magic.py
+ redir END
+ call assert_match(s:py3pattern, split(var)[0])
+
+ " Python 3 shebang
+ redir => var
+ pyxfile pyxfile/py3_shebang.py
+ redir END
+ call assert_match(s:py3pattern, split(var)[0])
+
+ if has('python')
+ " Python 2 special comment
+ redir => var
+ pyxfile pyxfile/py2_magic.py
+ redir END
+ call assert_match(s:py2pattern, split(var)[0])
+
+ " Python 2 shebang
+ redir => var
+ pyxfile pyxfile/py2_shebang.py
+ redir END
+ call assert_match(s:py2pattern, split(var)[0])
+ endif
+endfunc