[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);
+ }
+ }
}
/*
- NSMutableAttributedString problems,
Alexander Malmberg <=