diff options
Diffstat (limited to 'Mac/Contrib/PyIDE-src/IDELib/Widgets/PyFontify.py')
-rw-r--r-- | Mac/Contrib/PyIDE-src/IDELib/Widgets/PyFontify.py | 153 |
1 files changed, 153 insertions, 0 deletions
diff --git a/Mac/Contrib/PyIDE-src/IDELib/Widgets/PyFontify.py b/Mac/Contrib/PyIDE-src/IDELib/Widgets/PyFontify.py new file mode 100644 index 0000000..6503636 --- /dev/null +++ b/Mac/Contrib/PyIDE-src/IDELib/Widgets/PyFontify.py @@ -0,0 +1,153 @@ +"""Module to analyze Python source code; for syntax coloring tools. + +Interface: + tags = fontify(pytext, searchfrom, searchto) + +The 'pytext' argument is a string containing Python source code. +The (optional) arguments 'searchfrom' and 'searchto' may contain a slice in pytext. +The returned value is a lists of tuples, formatted like this: + [('keyword', 0, 6, None), ('keyword', 11, 17, None), ('comment', 23, 53, None), etc. ] +The tuple contents are always like this: + (tag, startindex, endindex, sublist) +tag is one of 'keyword', 'string', 'comment' or 'identifier' +sublist is not used, hence always None. +""" + +# Based on FontText.py by Mitchell S. Chapman, +# which was modified by Zachary Roadhouse, +# then un-Tk'd by Just van Rossum. +# Many thanks for regular expression debugging & authoring are due to: +# Tim (the-incredib-ly y'rs) Peters and Cristian Tismer +# So, who owns the copyright? ;-) How about this: +# Copyright 1996-1997: +# Mitchell S. Chapman, +# Zachary Roadhouse, +# Tim Peters, +# Just van Rossum + +__version__ = "0.3.1" + +import string, regex + +# First a little helper, since I don't like to repeat things. (Tismer speaking) +import string +def replace(where, what, with): + return string.join(string.split(where, what), with) + +# This list of keywords is taken from ref/node13.html of the +# Python 1.3 HTML documentation. ("access" is intentionally omitted.) +keywordsList = [ + "del", "from", "lambda", "return", + "and", "elif", "global", "not", "try", + "break", "else", "if", "or", "while", + "class", "except", "import", "pass", + "continue", "finally", "in", "print", + "def", "for", "is", "raise"] + +# Build up a regular expression which will match anything +# interesting, including multi-line triple-quoted strings. +commentPat = "#.*" + +pat = "q[^\q\n]*\(\\\\[\000-\377][^\q\n]*\)*q" +quotePat = replace(pat, "q", "'") + "\|" + replace(pat, 'q', '"') + +# Way to go, Tim! +pat = """ + qqq + [^\\q]* + \( + \( \\\\[\000-\377] + \| q + \( \\\\[\000-\377] + \| [^\\q] + \| q + \( \\\\[\000-\377] + \| [^\\q] + \) + \) + \) + [^\\q]* + \)* + qqq +""" +pat = string.join(string.split(pat), '') # get rid of whitespace +tripleQuotePat = replace(pat, "q", "'") + "\|" + replace(pat, 'q', '"') + +# Build up a regular expression which matches all and only +# Python keywords. This will let us skip the uninteresting +# identifier references. +# nonKeyPat identifies characters which may legally precede +# a keyword pattern. +nonKeyPat = "\(^\|[^a-zA-Z0-9_.\"']\)" + +keyPat = nonKeyPat + "\(" +for keyword in keywordsList: + keyPat = keyPat + keyword + "\|" +keyPat = keyPat[:-2] + "\)" + nonKeyPat + +matchPat = keyPat + "\|" + commentPat + "\|" + tripleQuotePat + "\|" + quotePat +matchRE = regex.compile(matchPat) + +idKeyPat = "[ \t]*[A-Za-z_][A-Za-z_0-9.]*" # Ident w. leading whitespace. +idRE = regex.compile(idKeyPat) + + +def fontify(pytext, searchfrom = 0, searchto = None): + if searchto is None: + searchto = len(pytext) + # Cache a few attributes for quicker reference. + search = matchRE.search + group = matchRE.group + idSearch = idRE.search + idGroup = idRE.group + + tags = [] + tags_append = tags.append + commentTag = 'comment' + stringTag = 'string' + keywordTag = 'keyword' + identifierTag = 'identifier' + + start = 0 + end = searchfrom + while 1: + start = search(pytext, end) + if start < 0 or start >= searchto: + break # EXIT LOOP + match = group(0) + end = start + len(match) + c = match[0] + if c not in "#'\"": + # Must have matched a keyword. + if start <> searchfrom: + # there's still a redundant char before and after it, strip! + match = match[1:-1] + start = start + 1 + else: + # this is the first keyword in the text. + # Only a space at the end. + match = match[:-1] + end = end - 1 + tags_append((keywordTag, start, end, None)) + # If this was a defining keyword, look ahead to the + # following identifier. + if match in ["def", "class"]: + start = idSearch(pytext, end) + if start == end: + match = idGroup(0) + end = start + len(match) + tags_append((identifierTag, start, end, None)) + elif c == "#": + tags_append((commentTag, start, end, None)) + else: + tags_append((stringTag, start, end, None)) + return tags + + +def test(path): + f = open(path) + text = f.read() + f.close() + tags = fontify(text) + for tag, start, end, sublist in tags: + print tag, `text[start:end]` |