Python: select the start-end range of syntax errors

Python 3.10's syntax errors can specify a range.
Use this for text editor error selection.
This commit is contained in:
Campbell Barton 2022-03-28 17:04:19 +11:00
parent 0ce6ed4753
commit 1466f480c4
3 changed files with 74 additions and 13 deletions

View File

@ -37,12 +37,14 @@
static void python_script_error_jump_text(Text *text, const char *filepath)
{
int lineno;
int offset;
python_script_error_jump(filepath, &lineno, &offset);
int lineno, lineno_end;
int offset, offset_end;
python_script_error_jump(filepath, &lineno, &offset, &lineno_end, &offset_end);
if (lineno != -1) {
/* select the line with the error */
txt_move_to(text, lineno - 1, INT_MAX, false);
/* Start at the end so cursor motion that looses the selection,
* leaves the cursor from the most useful place.
* Also, the end can't always be set, so don't give it priority. */
txt_move_to(text, lineno_end - 1, offset_end, false);
txt_move_to(text, lineno - 1, offset, true);
}
}

View File

@ -24,7 +24,7 @@ static const char *traceback_filepath(PyTracebackObject *tb, PyObject **coerce)
return PyBytes_AS_STRING(*coerce);
}
/* copied from pythonrun.c, 3.4.0 */
/* copied from pythonrun.c, 3.10.0 */
_Py_static_string(PyId_string, "<string>");
static int parse_syntax_error(PyObject *err,
@ -32,14 +32,18 @@ static int parse_syntax_error(PyObject *err,
PyObject **filename,
int *lineno,
int *offset,
int *end_lineno,
int *end_offset,
PyObject **text)
{
long hold;
Py_ssize_t hold;
PyObject *v;
_Py_IDENTIFIER(msg);
_Py_IDENTIFIER(filename);
_Py_IDENTIFIER(lineno);
_Py_IDENTIFIER(offset);
_Py_IDENTIFIER(end_lineno);
_Py_IDENTIFIER(end_offset);
_Py_IDENTIFIER(text);
*message = NULL;
@ -71,7 +75,7 @@ static int parse_syntax_error(PyObject *err,
if (!v) {
goto finally;
}
hold = PyLong_AsLong(v);
hold = PyLong_AsSsize_t(v);
Py_DECREF(v);
if (hold < 0 && PyErr_Occurred()) {
goto finally;
@ -87,7 +91,7 @@ static int parse_syntax_error(PyObject *err,
Py_DECREF(v);
}
else {
hold = PyLong_AsLong(v);
hold = PyLong_AsSsize_t(v);
Py_DECREF(v);
if (hold < 0 && PyErr_Occurred()) {
goto finally;
@ -95,6 +99,49 @@ static int parse_syntax_error(PyObject *err,
*offset = (int)hold;
}
if (Py_TYPE(err) == (PyTypeObject *)PyExc_SyntaxError) {
v = _PyObject_GetAttrId(err, &PyId_end_lineno);
if (!v) {
PyErr_Clear();
*end_lineno = *lineno;
}
else if (v == Py_None) {
*end_lineno = *lineno;
Py_DECREF(v);
}
else {
hold = PyLong_AsSsize_t(v);
Py_DECREF(v);
if (hold < 0 && PyErr_Occurred()) {
goto finally;
}
*end_lineno = hold;
}
v = _PyObject_GetAttrId(err, &PyId_end_offset);
if (!v) {
PyErr_Clear();
*end_offset = -1;
}
else if (v == Py_None) {
*end_offset = -1;
Py_DECREF(v);
}
else {
hold = PyLong_AsSsize_t(v);
Py_DECREF(v);
if (hold < 0 && PyErr_Occurred()) {
goto finally;
}
*end_offset = hold;
}
}
else {
/* `SyntaxError` subclasses. */
*end_lineno = *lineno;
*end_offset = -1;
}
v = _PyObject_GetAttrId(err, &PyId_text);
if (!v) {
goto finally;
@ -115,7 +162,8 @@ finally:
}
/* end copied function! */
void python_script_error_jump(const char *filepath, int *r_lineno, int *r_offset)
void python_script_error_jump(
const char *filepath, int *r_lineno, int *r_offset, int *r_lineno_end, int *r_offset_end)
{
PyObject *exception, *value;
PyTracebackObject *tb;
@ -123,6 +171,9 @@ void python_script_error_jump(const char *filepath, int *r_lineno, int *r_offset
*r_lineno = -1;
*r_offset = 0;
*r_lineno_end = -1;
*r_offset_end = 0;
PyErr_Fetch(&exception, &value, (PyObject **)&tb);
if (exception == NULL) {
return;
@ -137,7 +188,14 @@ void python_script_error_jump(const char *filepath, int *r_lineno, int *r_offset
PyObject *message;
PyObject *filepath_exc_py, *text_py;
if (parse_syntax_error(value, &message, &filepath_exc_py, r_lineno, r_offset, &text_py)) {
if (parse_syntax_error(value,
&message,
&filepath_exc_py,
r_lineno,
r_offset,
r_lineno_end,
r_offset_end,
&text_py)) {
const char *filepath_exc = PyUnicode_AsUTF8(filepath_exc_py);
/* python adds a '/', prefix, so check for both */
if ((BLI_path_cmp(filepath_exc, filepath) == 0) ||
@ -170,7 +228,7 @@ void python_script_error_jump(const char *filepath, int *r_lineno, int *r_offset
Py_DECREF(coerce);
if (match) {
*r_lineno = tb->tb_lineno;
*r_lineno = *r_lineno_end = tb->tb_lineno;
/* used to break here, but better find the inner most line */
}
}

View File

@ -10,7 +10,8 @@
extern "C" {
#endif
void python_script_error_jump(const char *filepath, int *r_lineno, int *r_offset);
void python_script_error_jump(
const char *filepath, int *r_lineno, int *r_offset, int *r_lineno_end, int *r_offset_end);
#ifdef __cplusplus
}