tornavis/source/blender/editors/space_text/text_format_py.c

303 lines
9.4 KiB
C
Raw Normal View History

/*
* ***** BEGIN GPL LICENSE BLOCK *****
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
* All rights reserved.
*
* The Original Code is: all of this file.
*
* Contributor(s): none yet.
*
* ***** END GPL LICENSE BLOCK *****
*/
/** \file blender/editors/space_text/text_format_py.c
* \ingroup sptext
*/
#include <string.h>
#include "BLI_blenlib.h"
#include "DNA_text_types.h"
#include "DNA_space_types.h"
#include "BKE_text.h"
#include "text_format.h"
/* *** Local Functions (for format_line) *** */
/* Checks the specified source string for a Python built-in function name. This
* name must start at the beginning of the source string and must be followed by
* a non-identifier (see text_check_identifier(char)) or null character.
*
* If a built-in function is found, the length of the matching name is returned.
* Otherwise, -1 is returned.
*
* See:
* http://docs.python.org/py3k/reference/lexical_analysis.html#keywords
*/
2012-12-29 03:57:52 +01:00
static int txtfmt_py_find_builtinfunc(const char *string)
{
int i, len;
2012-12-29 02:54:58 +01:00
/* list is from...
* ", ".join(['"%s"' % kw
* for kw in __import__("keyword").kwlist
* if kw not in {"False", "None", "True", "def", "class"}])
*
* ... and for this code:
* print("\n".join(['else if (STR_LITERAL_STARTSWITH(string, "%s", len)) i = len;' % kw
* for kw in __import__("keyword").kwlist
* if kw not in {"False", "None", "True", "def", "class"}]))
2012-12-29 02:54:58 +01:00
*/
if (STR_LITERAL_STARTSWITH(string, "and", len)) i = len;
else if (STR_LITERAL_STARTSWITH(string, "as", len)) i = len;
else if (STR_LITERAL_STARTSWITH(string, "assert", len)) i = len;
else if (STR_LITERAL_STARTSWITH(string, "break", len)) i = len;
else if (STR_LITERAL_STARTSWITH(string, "continue", len)) i = len;
else if (STR_LITERAL_STARTSWITH(string, "del", len)) i = len;
else if (STR_LITERAL_STARTSWITH(string, "elif", len)) i = len;
else if (STR_LITERAL_STARTSWITH(string, "else", len)) i = len;
else if (STR_LITERAL_STARTSWITH(string, "except", len)) i = len;
else if (STR_LITERAL_STARTSWITH(string, "finally", len)) i = len;
else if (STR_LITERAL_STARTSWITH(string, "for", len)) i = len;
else if (STR_LITERAL_STARTSWITH(string, "from", len)) i = len;
else if (STR_LITERAL_STARTSWITH(string, "global", len)) i = len;
else if (STR_LITERAL_STARTSWITH(string, "if", len)) i = len;
else if (STR_LITERAL_STARTSWITH(string, "import", len)) i = len;
else if (STR_LITERAL_STARTSWITH(string, "in", len)) i = len;
else if (STR_LITERAL_STARTSWITH(string, "is", len)) i = len;
else if (STR_LITERAL_STARTSWITH(string, "lambda", len)) i = len;
else if (STR_LITERAL_STARTSWITH(string, "nonlocal", len)) i = len;
else if (STR_LITERAL_STARTSWITH(string, "not", len)) i = len;
else if (STR_LITERAL_STARTSWITH(string, "or", len)) i = len;
else if (STR_LITERAL_STARTSWITH(string, "pass", len)) i = len;
else if (STR_LITERAL_STARTSWITH(string, "raise", len)) i = len;
else if (STR_LITERAL_STARTSWITH(string, "return", len)) i = len;
else if (STR_LITERAL_STARTSWITH(string, "try", len)) i = len;
else if (STR_LITERAL_STARTSWITH(string, "while", len)) i = len;
else if (STR_LITERAL_STARTSWITH(string, "with", len)) i = len;
else if (STR_LITERAL_STARTSWITH(string, "yield", len)) i = len;
else i = 0;
/* If next source char is an identifier (eg. 'i' in "definate") no match */
if (i == 0 || text_check_identifier(string[i]))
return -1;
return i;
}
/* Checks the specified source string for a Python special name. This name must
* start at the beginning of the source string and must be followed by a non-
* identifier (see text_check_identifier(char)) or null character.
*
* If a special name is found, the length of the matching name is returned.
* Otherwise, -1 is returned. */
2012-12-29 03:57:52 +01:00
static int txtfmt_py_find_specialvar(const char *string)
{
int i, len;
if (STR_LITERAL_STARTSWITH(string, "def", len)) i = len;
else if (STR_LITERAL_STARTSWITH(string, "class", len)) i = len;
else i = 0;
2012-12-29 03:57:52 +01:00
/* If next source char is an identifier (eg. 'i' in "definate") no match */
if (i == 0 || text_check_identifier(string[i]))
return -1;
return i;
}
2012-12-29 03:57:52 +01:00
static int txtfmt_py_find_decorator(const char *string)
{
if (string[0] == '@') {
int i = 1;
while (text_check_identifier(string[i])) {
i++;
}
return i;
}
return -1;
}
2012-12-29 03:57:52 +01:00
static int txtfmt_py_find_bool(const char *string)
{
int i, len;
if (STR_LITERAL_STARTSWITH(string, "None", len)) i = len;
else if (STR_LITERAL_STARTSWITH(string, "True", len)) i = len;
else if (STR_LITERAL_STARTSWITH(string, "False", len)) i = len;
else i = 0;
2012-12-29 03:57:52 +01:00
/* If next source char is an identifier (eg. 'i' in "Nonetheless") no match */
if (i == 0 || text_check_identifier(string[i]))
return -1;
return i;
}
2012-12-29 03:57:52 +01:00
static void txtfmt_py_format_line(SpaceText *st, TextLine *line, const int do_next)
{
FlattenString fs;
2012-12-29 03:57:52 +01:00
const char *str;
char *fmt;
char orig, cont, find, prev = ' ';
int len, i;
/* Get continuation from previous line */
if (line->prev && line->prev->format != NULL) {
fmt = line->prev->format;
cont = fmt[strlen(fmt) + 1]; /* Just after the null-terminator */
}
else {
cont = 0;
}
/* Get original continuation from this line */
if (line->format != NULL) {
fmt = line->format;
orig = fmt[strlen(fmt) + 1]; /* Just after the null-terminator */
}
else {
orig = 0xFF;
}
len = flatten_string(st, &fs, line->line);
str = fs.buf;
if (!text_check_format_len(line, len)) {
flatten_string_free(&fs);
return;
}
fmt = line->format;
while (*str) {
/* Handle escape sequences by skipping both \ and next char */
if (*str == '\\') {
*fmt = prev; fmt++; str++;
if (*str == '\0') break;
*fmt = prev; fmt++; str += BLI_str_utf8_size_safe(str);
continue;
}
/* Handle continuations */
else if (cont) {
/* Triple strings ("""...""" or '''...''') */
if (cont & TXT_TRISTR) {
find = (cont & TXT_DBLQUOTSTR) ? '"' : '\'';
if (*str == find && *(str + 1) == find && *(str + 2) == find) {
*fmt = 'l'; fmt++; str++;
*fmt = 'l'; fmt++; str++;
cont = 0;
}
/* Handle other strings */
}
else {
find = (cont & TXT_DBLQUOTSTR) ? '"' : '\'';
if (*str == find) cont = 0;
}
*fmt = 'l';
str += BLI_str_utf8_size_safe(str) - 1;
}
/* Not in a string... */
else {
/* Deal with comments first */
if (prev == '#' || *str == '#') {
*fmt = '#';
str += BLI_str_utf8_size_safe(str) - 1;
}
else if (*str == '"' || *str == '\'') {
/* Strings */
find = *str;
cont = (*str == '"') ? TXT_DBLQUOTSTR : TXT_SNGQUOTSTR;
if (*(str + 1) == find && *(str + 2) == find) {
*fmt = 'l'; fmt++; str++;
*fmt = 'l'; fmt++; str++;
cont |= TXT_TRISTR;
}
*fmt = 'l';
}
/* Whitespace (all ws. has been converted to spaces) */
else if (*str == ' ') {
*fmt = '_';
}
/* Numbers (digits not part of an identifier and periods followed by digits) */
else if ((prev != 'q' && text_check_digit(*str)) || (*str == '.' && text_check_digit(*(str + 1)))) {
*fmt = 'n';
}
/* Booleans */
else if (prev != 'q' && (i = txtfmt_py_find_bool(str)) != -1) {
if (i > 0) {
memset(fmt, 'n', i);
i--; fmt += i; str += i;
}
else {
str += BLI_str_utf8_size_safe(str) - 1;
*fmt = 'q';
}
}
/* Punctuation */
else if (text_check_delim(*str)) {
*fmt = '!';
}
/* Identifiers and other text (no previous ws. or delims. so text continues) */
else if (prev == 'q') {
str += BLI_str_utf8_size_safe(str) - 1;
*fmt = 'q';
}
/* Not ws, a digit, punct, or continuing text. Must be new, check for special words */
else {
/* Special vars(v) or built-in keywords(b) */
if ((i = txtfmt_py_find_specialvar(str)) != -1) prev = 'v';
else if ((i = txtfmt_py_find_builtinfunc(str)) != -1) prev = 'b';
else if ((i = txtfmt_py_find_decorator(str)) != -1) prev = 'v'; /* could have a new color for this */
if (i > 0) {
memset(fmt, prev, i);
i--; fmt += i; str += i;
}
else {
str += BLI_str_utf8_size_safe(str) - 1;
*fmt = 'q';
}
}
}
2012-12-29 03:57:52 +01:00
prev = *fmt; fmt++; str++;
}
/* Terminate and add continuation char */
*fmt = '\0'; fmt++;
*fmt = cont;
/* If continuation has changed and we're allowed, process the next line */
if (cont != orig && do_next && line->next) {
txtfmt_py_format_line(st, line->next, do_next);
}
flatten_string_free(&fs);
}
void ED_text_format_register_py(void)
{
static TextFormatType tft = {0};
static const char *ext[] = {"py", NULL};
tft.format_line = txtfmt_py_format_line;
tft.ext = ext;
ED_text_format_register(&tft);
}