Speed improvements for reading text lines and the option to specify a range for Text...
authorIan Thompson <quornian@googlemail.com>
Tue, 12 Aug 2008 15:17:08 +0000 (15:17 +0000)
committerIan Thompson <quornian@googlemail.com>
Tue, 12 Aug 2008 15:17:08 +0000 (15:17 +0000)
release/scripts/bpymodules/BPyTextPlugin.py
release/scripts/textplugin_imports.py
release/scripts/textplugin_outliner.py
release/scripts/textplugin_suggest.py
release/scripts/textplugin_templates.py
source/blender/python/api2_2x/Text.c
source/blender/python/api2_2x/Text.h
source/blender/python/api2_2x/doc/Text.py

index 94a2b8be38a3c9666910acc4c1dbdf46855192b5..9d33eca9de7000388e44998f805e37683677018d 100644 (file)
@@ -449,7 +449,7 @@ def parse_text(txt):
        desc.set_time()
        
        global _parse_cache
-       _parse_cache[hash(txt.name)] = desc
+       _parse_cache[hash(txt)] = desc
        return desc
 
 def get_modules(since=1):
@@ -511,9 +511,12 @@ def get_context(txt):
        
        """
        
-       global CTX_NORMAL, CTX_SINGLE_QUOTE, CTX_DOUBLE_QUOTE, CTX_COMMENT
        l, cursor = txt.getCursorPos()
-       lines = txt.asLines()[:l+1]
+       lines = txt.asLines(0, l+1)
+       
+       # FIXME: This method is too slow in large files for it to be called as often
+       # as it is. So for lines below the 1000th line we do this... (quorn)
+       if l > 1000: return CTX_NORMAL
        
        # Detect context (in string or comment)
        in_str = CTX_NORMAL
index 0e32a6fb2a6562b5ff4be2bd6492aba6838d619a..ec608243c2b98b72d2e78aa5be30b25a3ac622dc 100644 (file)
@@ -49,9 +49,7 @@ def main():
        immediate = True
        pos += 5
        for i in range(pos, c):
-               if line[i]=='.':
-                       pos = i+1
-               elif not line[i].isalnum() and line[i] != '_':
+               if not line[i].isalnum() and line[i] != '_' and line[i] != '.':
                        immediate = False
                        break
        
@@ -59,7 +57,7 @@ def main():
        if immediate:
                items = [(m, 'm') for m in get_modules()]
                items.sort(cmp = suggest_cmp)
-               txt.suggest(items, '')
+               txt.suggest(items, line[pos:c])
                return
        
        # Found 'from' earlier, suggest import if not already there
index 345361b47d4cb110c61af2d76c5c3a1175cd1904..3879a2819a547c7486d34d1a82f7202d805e1736 100644 (file)
@@ -30,23 +30,27 @@ def make_menu(items, eventoffs):
                                letters.append(c)
                                break
        
-       dict = {}
+       entries = {}
        i = 0
        for item in items:
                i += 1
                c = item[0].lower()
-               if not dict.has_key(c): dict[c] = []
-               dict[c].append((item, i+eventoffs))
+               entries.setdefault(c, []).append((item, i+eventoffs))
        
        subs = []
        for c in letters:
-               subs.append((c, dict[c]))
+               subs.append((c, entries[c]))
        
        return subs
 
 def find_word(txt, word):
        i = 0
-       for line in txt.asLines():
+       txt.reset()
+       while True:
+               try:
+                       line = txt.readline()
+               except StopIteration:
+                       break
                c = line.find(word)
                if c != -1:
                        txt.setCursorPos(i, c)
index 0e83e1e8cc7eb3abd80c1f26600a8d7f99298a91..1e011a2e82dce7057b3418bee9e333150748f951 100644 (file)
@@ -66,6 +66,7 @@ def main():
        # Otherwise we suggest globals, keywords, etc.
        list = []
        pre = get_targets(line, c)
+       desc = get_cached_descriptor(txt)
        
        for k in KEYWORDS:
                list.append((k, 'k'))
@@ -73,13 +74,16 @@ def main():
        for k, v in get_builtins().items():
                list.append((k, type_char(v)))
        
-       for k, v in get_imports(txt).items():
+       for k, v in desc.imports.items():
                list.append((k, type_char(v)))
        
-       for k, v in get_defs(txt).items():
+       for k, v in desc.classes.items():
                list.append((k, 'f'))
        
-       for k in get_vars(txt):
+       for k, v in desc.defs.items():
+               list.append((k, 'f'))
+       
+       for k, v in desc.vars.items():
                list.append((k, 'v'))
        
        list.sort(cmp = suggest_cmp)
index 508ede11ddc2ac31068d0469b42f7d173ee244ef..25dadf4de540877692a8fd3a37d2581bbfdde76f 100644 (file)
@@ -49,7 +49,7 @@ def main():
                return
        
        row, c = txt.getCursorPos()
-       line = txt.asLines()[row]
+       line = txt.asLines(row, row+1)[0]
        indent=0
        while indent<c and (line[indent]==' ' or line[indent]=='\t'):
                indent += 1
index 0f0abda04e1d3de81768f6caa3f0d9fd952a9e1c..7e31b3bb25bf49b59233ced48530e0721371964c 100644 (file)
@@ -100,7 +100,7 @@ static PyObject *Text_write( BPy_Text * self, PyObject * value );
 static PyObject *Text_insert( BPy_Text * self, PyObject * value );
 static PyObject *Text_delete( BPy_Text * self, PyObject * value );
 static PyObject *Text_set( BPy_Text * self, PyObject * args );
-static PyObject *Text_asLines( BPy_Text * self );
+static PyObject *Text_asLines( BPy_Text * self, PyObject * args );
 static PyObject *Text_getCursorPos( BPy_Text * self );
 static PyObject *Text_setCursorPos( BPy_Text * self, PyObject * args );
 static PyObject *Text_getSelectPos( BPy_Text * self );
@@ -136,8 +136,8 @@ static PyMethodDef BPy_Text_methods[] = {
         "(chars) - Deletes a number of characters to the left (chars<0) or right (chars>0)"},
        {"set", ( PyCFunction ) Text_set, METH_VARARGS,
         "(name, val) - Set attribute 'name' to value 'val'"},
-       {"asLines", ( PyCFunction ) Text_asLines, METH_NOARGS,
-        "() - Return text buffer as a list of lines"},
+       {"asLines", ( PyCFunction ) Text_asLines, METH_VARARGS,
+        "(start=0, end=nlines) - Return text buffer as a list of lines between start and end"},
        {"getCursorPos", ( PyCFunction ) Text_getCursorPos, METH_NOARGS,
         "() - Return cursor position as (row, col) tuple"},
        {"setCursorPos", ( PyCFunction ) Text_setCursorPos, METH_VARARGS,
@@ -377,8 +377,8 @@ PyObject *Text_CreatePyObject( Text * txt )
                                              "couldn't create BPy_Text PyObject" );
 
        pytxt->text = txt;
-       pytxt->iol = 0;
-       pytxt->ioc = 0;
+       pytxt->iol = NULL;
+       pytxt->ioc = -1;
 
        return ( PyObject * ) pytxt;
 }
@@ -430,8 +430,8 @@ static PyObject *Text_clear( BPy_Text * self)
 
 static PyObject *Text_reset( BPy_Text * self )
 {
-       self->iol = 0;
-       self->ioc = 0;
+       self->iol = NULL;
+       self->ioc = -1;
 
        Py_RETURN_NONE;
 }
@@ -439,29 +439,33 @@ static PyObject *Text_reset( BPy_Text * self )
 static PyObject *Text_readline( BPy_Text * self )
 {
        PyObject *tmpstr;
-       TextLine *line;
-       int i;
        
        if( !self->text )
                return EXPP_ReturnPyObjError( PyExc_RuntimeError,
                                              "This object isn't linked to a Blender Text Object" );
 
-       for (i=0, line=self->text->lines.first; i<self->iol && line; i++, line=line->next);
+       /* Reset */
+       if (!self->iol && self->ioc == -1) {
+               self->iol = self->text->lines.first;
+               self->ioc = 0;
+       }
 
-       if (!line) {
+       if (!self->iol) {
                PyErr_SetString( PyExc_StopIteration, "End of buffer reached" );
                return PyString_FromString( "" );
        }
 
-       if (self->ioc > line->len)
+       if (self->ioc > self->iol->len) {
+               self->iol = NULL;
                return EXPP_ReturnPyObjError( PyExc_RuntimeError,
                                                  "Line length exceeded, text may have changed while reading" );
+       }
 
-       tmpstr = PyString_FromString( line->line + self->ioc );
-       if (line->next)
+       tmpstr = PyString_FromString( self->iol->line + self->ioc );
+       if (self->iol->next)
                PyString_ConcatAndDel( &tmpstr, PyString_FromString("\n") );
 
-       self->iol++;
+       self->iol = self->iol->next;
        self->ioc = 0;
 
        return tmpstr;
@@ -485,6 +489,8 @@ static PyObject *Text_write( BPy_Text * self, PyObject * value )
        txt_move_eof( self->text, 0 );
        txt_set_undostate( oldstate );
 
+       Text_reset( self );
+
        Py_RETURN_NONE;
 }
 
@@ -505,6 +511,8 @@ static PyObject *Text_insert( BPy_Text * self, PyObject * value )
        txt_insert_buf( self->text, str );
        txt_set_undostate( oldstate );
 
+       Text_reset( self );
+
        Py_RETURN_NONE;
 }
 
@@ -531,6 +539,8 @@ static PyObject *Text_delete( BPy_Text * self, PyObject * value )
                num--;
        }
        txt_set_undostate( oldstate );
+       
+       Text_reset( self );
 
        Py_RETURN_NONE;
 }
@@ -554,27 +564,39 @@ static PyObject *Text_set( BPy_Text * self, PyObject * args )
        Py_RETURN_NONE;
 }
 
-static PyObject *Text_asLines( BPy_Text * self )
+static PyObject *Text_asLines( BPy_Text * self, PyObject * args )
 {
        TextLine *line;
        PyObject *list, *tmpstr;
+       int start=0, end=-1, i;
 
        if( !self->text )
                return EXPP_ReturnPyObjError( PyExc_RuntimeError,
                                              "This object isn't linked to a Blender Text Object" );
 
+       if( !PyArg_ParseTuple( args, "|ii", &start, &end ) )
+                       return EXPP_ReturnPyObjError( PyExc_TypeError,
+                                                         "expected upto two optional ints as arguments" );
+       
+       if (start<0)
+               start=0;
+
        line = self->text->lines.first;
+       for (i = 0; i < start && line->next; i++)
+               line= line->next;
+
        list = PyList_New( 0 );
 
        if( !list )
                return EXPP_ReturnPyObjError( PyExc_MemoryError,
                                              "couldn't create PyList" );
 
-       while( line ) {
+       while( line && (i < end || end == -1) ) {
                tmpstr = PyString_FromString( line->line );
                PyList_Append( list, tmpstr );
                Py_DECREF(tmpstr);
                line = line->next;
+               i++;
        }
 
        return list;
index eb64b6d43eab03c6eb026077776fb75950b9e6bc..73943ddb9cd3963d781e9a44ad727648cade95b2 100644 (file)
@@ -41,7 +41,7 @@ extern PyTypeObject Text_Type;
 typedef struct {
        PyObject_HEAD
        Text * text; /* libdata must be second */
-       int iol; /* index of line being read */
+       TextLine * iol; /* current line being read or NULL if reset */
        int ioc; /* character offset in line being read */
 } BPy_Text;
 
index c4a25899343674883220b6919f339c2d51ae83af..022205573aad9eb78f80a1e49a343542f06f31bd 100644 (file)
@@ -108,7 +108,8 @@ class Text:
        def readline():
                """
                Reads a line of text from the buffer from the current IO pointer
-               position to the end of the line.
+               position to the end of the line. If the text has changed since the last
+               read, reset() *must* be called.
                @rtype: string
                """
 
@@ -137,11 +138,19 @@ class Text:
                @param data:  The string to insert into the text buffer.
                """
 
-       def asLines():
+       def asLines(start=0, end=-1):
                """
-               Retrieve the contents of this Text buffer as a list of strings.
+               Retrieve the contents of this Text buffer as a list of strings between
+               the start and end lines specified. If end < 0 all lines from start will
+               be included.
+               @type start int
+               @param start:  Optional index of first line of the span to return
+               @type end int
+               @param end:  Optional index of the line to which the span is taken or
+                       -1 to include all lines from start
                @rtype: list of strings
-               @return:  A list of strings, one for each line in the buffer
+               @return:  A list of strings, one for each line in the buffer between 
+                       start and end.
                """
 
        def getCursorPos():
@@ -154,7 +163,29 @@ class Text:
 
        def setCursorPos(row, col):
                """
-               Set the position of the cursor in this Text buffer.
+               Set the position of the cursor in this Text buffer. Any selection will
+               be cleared. Use setSelectPos to extend a selection from the point
+               specified here.
+               @type row: int
+               @param row:  The index of the line in which to position the cursor.
+               @type col: int
+               @param col:  The index of the character within the line to position the
+                       cursor.
+               """
+
+       def getSelectPos():
+               """
+               Retrieve the position of the selection cursor in this Text buffer.
+               @rtype: (int, int)
+               @return:  A pair (row, col) indexing the line and character of the
+                       selection cursor.
+               """
+
+       def setSelectPos(row, col):
+               """
+               Set the position of the selection cursor in this Text buffer. This
+               method should be called after setCursorPos to extend the selection to
+               the specified point.
                @type row: int
                @param row:  The index of the line in which to position the cursor.
                @type col: int
@@ -180,7 +211,7 @@ class Text:
                        the list. This is usually whatever precedes the cursor so that
                        backspace will update it.
                """
-       
+
        def showDocs(docs):
                """
                Displays a word-wrapped message box containing the specified