bug-gnustep
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

NSMutableAttributedString problems


From: Alexander Malmberg
Subject: NSMutableAttributedString problems
Date: Tue, 05 Mar 2002 22:43:44 +0100

Hi,

If you create an NSMutableAttributedString with two attributes (ie.
first some characters with one set of attributes, then some characters
with a second set of attributes) and then replace all characters with
some other string, the GSAttrInfo for the second set of attributes will
remain with its location set just past the end of the new string. I
think this is wrong. Anyway, appending a string after that will cause
the newly appended string to get the second set of attributes, which is
definitely wrong. (I've attached a test case that shows all of this.)

I assumed that there should not be any trailing attributes like this,
and that the sanity test was wrong in allowing it. I've attached a patch
that fixes the sanity test and replaceCharactersInRange:withString:.
I've tested with activated sanity tests, and it seems to work.

- Alexander Malmberg
#include <Foundation/NSObject.h>
#include <Foundation/NSString.h>
#include <Foundation/NSAttributedString.h>
#include <Foundation/NSAutoreleasePool.h>
#include <Foundation/NSValue.h>

int main(int argc, char **argv)
{
        CREATE_AUTORELEASE_POOL(arp);
        NSMutableAttributedString *s;

        s=[[NSMutableAttributedString alloc] init];

        /* test 1 */
        [s replaceCharactersInRange: NSMakeRange(0,[s length])
                withString: @"test2\n"];
        [s replaceCharactersInRange: NSMakeRange([s length],0)
                withString: @"test3\n"];
        [s appendAttributedString:
                [[[NSAttributedString alloc]
                        initWithString: @"test_attr"
                        attributes:
                                [NSDictionary dictionaryWithObjectsAndKeys: 
@"foo",@"zot",nil]]
                                        autorelease]];

        printf("<<\n%@>>\n\n",s);

        /* test 2 */

        /* this will crash with the new test since the 'zot=foo' attribute
        remains after the string */
        [s replaceCharactersInRange: NSMakeRange(0,[s length])
                withString: @"test1\n"];
        printf("<<\n%@>>\n\n",s);

        /* test 3 */
        [s replaceCharactersInRange: NSMakeRange(0,[s length])
                withString: @"test3\n"];

        /* this will produce incorrect results. the newly added 'test3\n' will
        get 'zot=foo' attribute that remained after the string */
        [s replaceCharactersInRange: NSMakeRange([s length],0)
                withString: @"test3\n"];


        /* the stuff below is just some tests to make sure normal behaviour
        didn't break */
        [s appendAttributedString:
                [[[NSAttributedString alloc]
                        initWithString: @"test_attr"
                        attributes: [NSDictionary dictionaryWithObjectsAndKeys: 
@"foo",@"bar",nil]] autorelease]];

        printf("<<\n%@>>\n\n",s);

        DESTROY(s);
        s=[[NSMutableAttributedString alloc] init];

        {
                int i;
                for (i=0;i<5;i++)
                {
                        NSAttributedString *as=[[NSAttributedString alloc]
                                initWithString: [NSString stringWithFormat: 
@"foo %i\n",i]
                                attributes: [NSDictionary 
dictionaryWithObjectsAndKeys: [NSNumber numberWithInt: i],@"num",nil]];
                        [s appendAttributedString: as];
                }
        }
        printf("<<\n%@\n>>\n\n",s);

/*      [s replaceCharactersInRange: NSMakeRange(0,[s length]) withString: nil];
        printf("<<\n%@\n>>\n\n",s);*/

        [s replaceCharactersInRange: NSMakeRange(0,[s length]) withString:
@"foo 1\nfoo 2\nfoo 3\nfoo 4\nfoo 5\nabc\n"];
        printf("<<\n%@\n>>\n\n",s);

        [s replaceCharactersInRange: NSMakeRange(0,[s length]) withString: nil];
        printf("<<\n%@\n>>\n\n",s);

        DESTROY(arp);

        return 0;
}

Index: GSAttributedString.m
===================================================================
RCS file: /cvsroot/gnustep/gnustep/core/base/Source/GSAttributedString.m,v
retrieving revision 1.14
diff -u -r1.14 GSAttributedString.m
--- GSAttributedString.m        13 Feb 2002 18:49:32 -0000      1.14
+++ GSAttributedString.m        5 Mar 2002 21:27:04 -0000
@@ -533,7 +533,7 @@
     {
       info = OBJECTAT(i);
       NSAssert(info->loc > l, NSInternalInconsistencyException);
-      NSAssert(info->loc <= len, NSInternalInconsistencyException);
+      NSAssert(info->loc < len, NSInternalInconsistencyException);
       l = info->loc;
     }
 }
@@ -783,6 +783,8 @@
   attrs = _attributesAtIndexEffectiveRange(start, &effectiveRange,
     tmpLength, _infoArray, &arrayIndex);
 
+  moveLocations = [aString length] - range.length;
+
   arrayIndex++;
   if (NSMaxRange(effectiveRange) < NSMaxRange(range))
     {
@@ -811,10 +813,31 @@
                }
            }
        }
-      info->loc = NSMaxRange(range);
+      if (NSMaxRange(range)<[_textChars length])
+      {
+       info->loc = NSMaxRange(range);
+      }
+      else
+      {
+       if (arrayIndex!=0)
+         {
+           REMOVEAT(arrayIndex);
+           arraySize--;
+         }
+       else
+         {
+           NSDictionary *d=[NSDictionary dictionary];
+           unCacheAttributes(info->attrs);
+           DESTROY(info->attrs);
+           d=cacheAttributes(d);
+           info->attrs=d;
+           /* set location so it will be correct after being modified
+           below */
+           info->loc = NSMaxRange(range);
+         }
+      }
     }
 
-  moveLocations = [aString length] - range.length;
   /*
    * If we are replacing a range with a zero length string and the
    * range we are using matches the range replaced, then we must
@@ -824,15 +847,28 @@
     {
       attrs = _attributesAtIndexEffectiveRange(start, &effectiveRange,
         tmpLength, _infoArray, &arrayIndex);
-      arrayIndex++;
+      arrayIndex ++;
 
       if (effectiveRange.location == range.location
-        && effectiveRange.length == range.length)
-        {
-          arrayIndex--;
-          REMOVEAT(arrayIndex);
-          arraySize--;
-        }
+       && effectiveRange.length == range.length)
+       {
+         arrayIndex--;
+         if (arrayIndex!=0)
+           {
+             REMOVEAT(arrayIndex);
+             arraySize--;
+           }
+         else
+           {
+             NSDictionary *d=[NSDictionary dictionary];
+             info=OBJECTAT(0);
+             unCacheAttributes(info->attrs);
+             DESTROY(info->attrs);
+             d=cacheAttributes(d);
+             info->attrs=d;
+             info->loc=NSMaxRange(range);
+           }
+       }
     }
 
   /*
Index: GSTextStorage.m
===================================================================
RCS file: /cvsroot/gnustep/gnustep/core/gui/Source/GSTextStorage.m,v
retrieving revision 1.31
diff -u -r1.31 GSTextStorage.m
--- GSTextStorage.m     13 Feb 2002 21:10:55 -0000      1.31
+++ GSTextStorage.m     5 Mar 2002 21:26:37 -0000
@@ -406,7 +406,7 @@
     {
       info = OBJECTAT(i);
       NSAssert(info->loc > l, NSInternalInconsistencyException);
-      NSAssert(info->loc <= len, NSInternalInconsistencyException);
+      NSAssert(info->loc < len, NSInternalInconsistencyException);
       l = info->loc;
     }
 }
@@ -709,6 +709,8 @@
   attrs = _attributesAtIndexEffectiveRange(start, &effectiveRange,
     tmpLength, _infoArray, &arrayIndex);
 
+  moveLocations = [aString length] - range.length;
+
   arrayIndex++;
   if (NSMaxRange(effectiveRange) < NSMaxRange(range))
     {
@@ -737,11 +739,31 @@
                }
            }
        }
-      info->loc = NSMaxRange(range);
+      if (NSMaxRange(range)<[_textChars length])
+      {
+       info->loc = NSMaxRange(range);
+      }
+      else
+      {
+       if (arrayIndex!=0)
+         {
+           REMOVEAT(arrayIndex);
+           arraySize--;
+         }
+       else
+         {
+           NSDictionary *d=[NSDictionary dictionary];
+           unCacheAttributes(info->attrs);
+           DESTROY(info->attrs);
+           d=cacheAttributes(d);
+           info->attrs=d;
+           /* set location so it will be correct after being modified
+           below */
+           info->loc = NSMaxRange(range);
+         }
+      }
     }
 
-  moveLocations = [aString length] - range.length;
-
   /*
    * If we are replacing a range with a zero length string and the
    * range we are using matches the range replaced, then we must
@@ -754,12 +776,25 @@
       arrayIndex ++;
 
       if (effectiveRange.location == range.location
-        && effectiveRange.length == range.length)
-        {
-          arrayIndex--;
-          REMOVEAT(arrayIndex);
-          arraySize--;
-        }
+       && effectiveRange.length == range.length)
+       {
+         arrayIndex--;
+         if (arrayIndex!=0)
+           {
+             REMOVEAT(arrayIndex);
+             arraySize--;
+           }
+         else
+           {
+             NSDictionary *d=[NSDictionary dictionary];
+             info=OBJECTAT(0);
+             unCacheAttributes(info->attrs);
+             DESTROY(info->attrs);
+             d=cacheAttributes(d);
+             info->attrs=d;
+             info->loc=NSMaxRange(range);
+           }
+       }
     }
 
   /*

reply via email to

[Prev in Thread] Current Thread [Next in Thread]