69
71
* far too special purpose.</p>
71
73
* <p>Here is the afterword for the class.</p>
74
* <p> And here is some automated cross referencing ...
75
* A method in a protocol: [(NSCopying)-copyWithZone:], a class:
76
* [NSString], a protocol: [(NSCopying)], and a
77
* category: [NSRunLoop(GNUstepExtensions)].
73
80
* And finally, here is the actual class description ... outside the chapter.
81
* This is the class description for <code>AGSOutput</code>, including some
82
* sample uses of GSDoc, such as cross-references (see [NSString]).
83
* Functions, like escapeType(), are automatically referenced (if they are
75
86
@implementation AGSOutput
88
- (NSString*) checkComment: (NSString*)comment
90
info: (NSDictionary*)d
79
NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
92
if ([comment length] == 0)
94
comment = @"<em>Description forthcoming.</em>";
97
NSString *name = [d objectForKey: @"Name"];
98
NSString *type = [d objectForKey: @"Type"];
81
[ud registerDefaults: [NSDictionary dictionaryWithObjectsAndKeys:
82
@"TypesAndConstants", @"ConstantsTemplate",
83
@"Functions", @"FunctionsTemplate",
84
@"TypesAndConstants", @"TypedefsTemplate",
85
@"TypesAndConstants", @"VariablesTemplate",
104
NSLog(@"Warning - No comments for %@", name);
108
NSLog(@"Warning - No comments for %@ %@", type, name);
113
if ([d objectForKey: @"ReturnType"] != nil)
115
NSLog(@"Warning - No comments for [%@ %@]", unit, name);
119
NSLog(@"Warning - No comments for instance variable %@ in %@",
192
232
@"_0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"]);
193
233
identStart = RETAIN([NSCharacterSet characterSetWithCharactersInString:
194
234
@"_abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"]);
235
informalProtocols = [NSMutableArray new];
236
verbose = [[NSUserDefaults standardUserDefaults] boolForKey: @"Verbose"];
237
warn = [[NSUserDefaults standardUserDefaults] boolForKey: @"Warn"];
240
284
types = [info objectForKey: @"Types"];
241
285
variables = [info objectForKey: @"Variables"];
242
286
constants = [info objectForKey: @"Constants"];
287
macros = [info objectForKey: @"Macros"];
244
289
[str appendString: @"<?xml version=\"1.0\"?>\n"];
245
290
[str appendString: @"<!DOCTYPE gsdoc PUBLIC "];
246
[str appendString: @"\"-//GNUstep//DTD gsdoc 0.6.7//EN\" "];
247
[str appendString: @"\"http://www.gnustep.org/gsdoc-0_6_7.xml\">\n"];
291
[str appendString: @"\"-//GNUstep//DTD gsdoc 1.0.1//EN\" "];
292
[str appendString: @"\"http://www.gnustep.org/gsdoc-1_0_1.xml\">\n"];
248
293
[str appendFormat: @"<gsdoc"];
545
if ([macros count] > 0)
547
NSMutableString *m = [NSMutableString new];
550
unsigned c = [macros count];
552
[m appendString: @" <chapter>\n"];
553
[m appendFormat: @" <heading>%@ macros</heading>\n", base];
554
[m appendString: @" <p></p>\n"];
556
names = [macros allKeys];
557
names = [names sortedArrayUsingSelector: @selector(compare:)];
558
for (i = 0; i < c; i++)
560
NSString *name = [names objectAtIndex: i];
561
NSDictionary *d = [macros objectForKey: name];
563
[self outputMacro: d to: m];
566
[m appendString: @" </chapter>\n"];
568
tmp = [self mergeMarkup: m ofKind: @"Macros"];
571
[str appendString: m];
576
[files addObject: tmp];
500
581
if ([variables count] > 0)
502
583
NSMutableString *m = [NSMutableString new];
608
689
NSString *declared = [d objectForKey: @"Declared"];
609
690
NSString *standards = nil;
611
if ([[d objectForKey: @"Implemented"] isEqual: @"YES"] == NO)
692
if (warn == YES && [[d objectForKey: @"Implemented"] isEqual: @"YES"] == NO)
613
694
NSLog(@"Warning ... %@ %@ is not implemented where expected", kind, name);
637
718
[str appendString: @" <desc>\n"];
638
if ([comment length] == 0)
640
comment = @"<em>Description forthcoming.</em>";
719
comment = [self checkComment: comment unit: nil info: d];
642
720
[self reformat: comment withIndent: 10 to: str];
643
721
[str appendString: @" </desc>\n"];
644
722
if (standards != nil)
782
860
[str appendString: @" <desc>\n"];
783
if ([comment length] == 0)
785
comment = @"<em>Description forthcoming.</em>";
861
comment = [self checkComment: comment unit: nil info: d];
787
862
[self reformat: comment withIndent: 10 to: str];
788
863
[str appendString: @" </desc>\n"];
789
864
if (standards != nil)
798
873
* Output the gsdoc code for an instance variable.
800
- (void) outputInstanceVariable: (NSDictionary*)d to: (NSMutableString*)str
875
- (void) outputInstanceVariable: (NSDictionary*)d
876
to: (NSMutableString*)str
802
NSString *type = [d objectForKey: @"Type"];
879
NSString *pref = [d objectForKey: @"Prefix"];
880
NSString *type = [d objectForKey: @"BaseType"];
803
881
NSString *validity = [d objectForKey: @"Validity"];
804
882
NSString *name = [d objectForKey: @"Name"];
805
883
NSString *comment = [d objectForKey: @"Comment"];
817
899
[str appendString: @"\">\n"];
819
901
[str appendString: @" <desc>\n"];
820
if ([comment length] == 0)
822
comment = @"<em>Description forthcoming.</em>";
902
comment = [self checkComment: comment unit: unit info: d];
824
903
[self reformat: comment withIndent: 12 to: str];
825
904
[str appendString: @" </desc>\n"];
826
905
if (standards != nil)
834
913
* Uses -split: and -reformat:withIndent:to:.
915
- (void) outputMacro: (NSDictionary*)d
916
to: (NSMutableString*)str
918
NSString *name = [d objectForKey: @"Name"];
919
NSString *comment = [d objectForKey: @"Comment"];
920
NSString *declared = [d objectForKey: @"Declared"];
921
NSString *standards = nil;
924
if (standards == nil)
926
standards = [d objectForKey: @"Standards"];
929
[str appendString: @" <macro name=\""];
930
[str appendString: name];
931
[str appendString: @"\">\n"];
934
* Storing the argument names array in the 'args' ivar ensures that
935
* when we output comments, the argument names are highlighted.
937
args = [d objectForKey: @"Args"];
938
for (i = 0; i < [args count]; i++)
940
NSString *s = [args objectAtIndex: i];
942
[str appendString: @" <arg>"];
943
[str appendString: s];
944
[str appendString: @"</arg>\n"];
946
if ([[d objectForKey: @"VarArgs"] boolValue] == YES)
948
[str appendString: @" <vararg />\n"];
953
[str appendString: @" <declared>"];
954
[str appendString: declared];
955
[str appendString: @"</declared>\n"];
958
[str appendString: @" <desc>\n"];
959
comment = [self checkComment: comment unit: nil info: d];
960
[self reformat: comment withIndent: 10 to: str];
961
[str appendString: @" </desc>\n"];
962
if (standards != nil)
964
[self reformat: standards withIndent: 8 to: str];
966
[str appendString: @" </macro>\n"];
971
* Uses -split: and -reformat:withIndent:to:.
835
972
* Also has fun with YES, NO, and nil.
837
974
- (void) outputMethod: (NSDictionary*)d
847
984
NSString *override = nil;
848
985
NSString *standards = nil;
850
if (unit != nil && [[d objectForKey: @"Implemented"] isEqual: @"YES"] == NO)
987
if (warn == YES && unit != nil
988
&& [[d objectForKey: @"Implemented"] isEqual: @"YES"] == NO)
852
990
NSLog(@"Warning ... method %@ %@ is not implemented where expected",
990
1128
if ([[d objectForKey: @"VarArgs"] boolValue] == YES)
992
[str appendString: @"<vararg />\n"];
1130
[str appendString: @" <vararg />\n"];
995
1133
[str appendString: @" <desc>\n"];
996
if ([comment length] == 0)
998
comment = @"<em>Description forthcoming.</em>";
1134
comment = [self checkComment: comment unit: unit info: d];
1000
1135
[self reformat: comment withIndent: 12 to: str];
1001
1136
[str appendString: @" </desc>\n"];
1002
1137
if (standards != nil)
1012
1147
NSString *name = [d objectForKey: @"Name"];
1013
1148
NSString *type = [d objectForKey: @"Type"];
1014
NSDictionary *methods = [d objectForKey: @"Methods"];
1015
NSDictionary *ivars = [d objectForKey: @"InstanceVariables"];
1149
NSString *kind = type;
1150
NSMutableDictionary *methods = [d objectForKey: @"Methods"];
1151
NSMutableDictionary *ivars = [d objectForKey: @"InstanceVariables"];
1016
1152
NSString *comment = [d objectForKey: @"Comment"];
1017
1153
NSString *unitName = nil;
1018
1154
NSArray *names;
1019
1155
NSArray *protocols;
1020
1156
NSString *standards = nil;
1158
NSString *unit = nil;
1028
1164
if ([[d objectForKey: @"Implemented"] isEqual: @"YES"] == NO)
1030
NSLog(@"Warning ... unit %@ is not implemented where expected", name);
1166
if ([name hasPrefix: @"NSObject("] == YES)
1168
NSEnumerator *e = [methods objectEnumerator];
1169
NSMutableDictionary *m;
1172
* Assume an unimplemented category of NSObject is an
1173
* informal protocol, and stop warnings being issued
1174
* about unimplemented methods.
1177
kind = @"informal protocol";
1178
while ((m = [e nextObject]) != nil)
1180
[m setObject: @"YES" forKey: @"Implemented"];
1183
[informalProtocols addObject: name];
1185
else if (warn == YES)
1187
NSLog(@"Warning ... unit %@ is not implemented where expected", name);
1034
1192
unitName = name;
1037
r = [comment rangeOfString: @"<standards>"];
1038
if (comment != nil && r.length > 0)
1040
unsigned i = r.location;
1042
r = NSMakeRange(i, [comment length] - i);
1043
r = [comment rangeOfString: @"</standards>"
1044
options: NSLiteralSearch
1197
r = [comment rangeOfString: @"<standards>"];
1046
1198
if (r.length > 0)
1050
r = NSMakeRange(i, NSMaxRange(r) - i);
1051
standards = [comment substringWithRange: r];
1052
m = [comment mutableCopy];
1053
[m deleteCharactersInRange: r];
1059
NSLog(@"unterminated <standards> in comment for %@", name);
1200
unsigned i = r.location;
1202
r = NSMakeRange(i, [comment length] - i);
1203
r = [comment rangeOfString: @"</standards>"
1204
options: NSLiteralSearch
1210
r = NSMakeRange(i, NSMaxRange(r) - i);
1211
standards = [comment substringWithRange: r];
1212
m = [comment mutableCopy];
1213
[m deleteCharactersInRange: r];
1219
NSLog(@"unterminated <standards> in comment for %@", name);
1062
1223
if (standards == nil)
1068
1229
* Make sure we have a 'unit' part and a class 'desc' part (comment)
1069
1230
* to be output.
1071
r = [comment rangeOfString: @"<unit>"];
1072
if (comment != nil && r.length > 0)
1074
unsigned pos = r.location;
1076
r = [comment rangeOfString: @"</unit>"];
1077
if (r.length == 0 || r.location < pos)
1079
NSLog(@"Unterminated <unit> in comment for %@", name);
1085
if (NSMaxRange(r) == [comment length])
1092
unit = [comment substringToIndex: NSMaxRange(r)];
1093
comment = [comment substringFromIndex: NSMaxRange(r)];
1098
if (NSMaxRange(r) == [comment length])
1100
unit = [comment substringFromIndex: pos];
1101
comment = [comment substringToIndex: pos];
1105
unsigned end = NSMaxRange(r);
1107
r = NSMakeRange(pos, end-pos);
1108
unit = [comment substringWithRange: r];
1109
comment = [[comment substringToIndex: pos]
1110
stringByAppendingString: [comment substringFromIndex: end]];
1113
unit = [unit stringByReplacingString: @"unit>" withString: @"chapter>"];
1234
r = [comment rangeOfString: @"<unit>"];
1237
unsigned pos = r.location;
1239
r = [comment rangeOfString: @"</unit>"];
1240
if (r.length == 0 || r.location < pos)
1242
NSLog(@"Unterminated <unit> in comment for %@", name);
1248
if (NSMaxRange(r) == [comment length])
1255
unit = [comment substringToIndex: NSMaxRange(r)];
1256
comment = [comment substringFromIndex: NSMaxRange(r)];
1261
if (NSMaxRange(r) == [comment length])
1263
unit = [comment substringFromIndex: pos];
1264
comment = [comment substringToIndex: pos];
1268
unsigned end = NSMaxRange(r);
1270
r = NSMakeRange(pos, end-pos);
1271
unit = [comment substringWithRange: r];
1272
comment = [[comment substringToIndex: pos]
1273
stringByAppendingString: [comment substringFromIndex: end]];
1276
unit = [unit stringByReplacingString: @"unit>"
1277
withString: @"chapter>"];
1117
1282
unit = [NSString stringWithFormat:
1118
1283
@" <chapter>\n <heading>Software documentation "
1119
@"for the %@ %@</heading>\n </chapter>\n", name, type];
1284
@"for the %@ %@</heading>\n </chapter>\n", name, kind];
1190
1355
for (j = 0; j < ind; j++) [str appendString: @" "];
1191
1356
[str appendString: @"<desc>\n"];
1192
if ([comment length] == 0)
1194
comment = @"<em>Description forthcoming.</em>";
1357
comment = [self checkComment: comment unit: nil info: d];
1196
1358
[self reformat: comment withIndent: ind + 2 to: str];
1197
1359
for (j = 0; j < ind; j++) [str appendString: @" "];
1198
1360
[str appendString: @"</desc>\n"];
1200
1362
names = [[ivars allKeys] sortedArrayUsingSelector: @selector(compare:)];
1201
1363
for (i = 0; i < [names count]; i++)
1203
1365
NSString *vName = [names objectAtIndex: i];
1205
[self outputInstanceVariable: [ivars objectForKey: vName] to: str];
1367
[self outputInstanceVariable: [ivars objectForKey: vName]
1208
1372
names = [[methods allKeys] sortedArrayUsingSelector: @selector(compare:)];
1513
1683
* Phase 2 ... the array of words is checked to see if a word contains
1514
* a well known constant, or a method name specification.
1684
* a well known constant, or a method or function name specification.
1515
1685
* Where these special cases apply, the array of words is modified to
1516
1686
* insert extra gsdoc markup to highlight the constants and to create
1517
* references to where the named methods are documented.
1687
* references to where the named methods or functions are documented.
1519
1689
for (l = 0; l < [a count]; l++)
1843
2030
ref = [NSString stringWithFormat:
1844
2031
@"<ref type=\"method\" id=\"%@\">", mName];
2033
else if (isProtocol == YES)
2035
ref = [NSString stringWithFormat:
2036
@"<ref type=\"method\" id=\"%@\" class=\"%@\">",
2038
if (isProtocol == YES)
2044
sub = [sub stringByReplacingString: @"("
2045
withString: @"<"];
2046
sub = [sub stringByReplacingString: @")"
2047
withString: @">"];
1848
2052
ref = [NSString stringWithFormat:
1849
2053
@"<ref type=\"method\" id=\"%@\" class=\"%@\">",
2055
sub = [NSString stringWithFormat: @"[%@ %@]",
1852
2058
[a insertObject: ref atIndex: l++];
1853
2059
if (sub != nil)
1862
2068
[a insertObject: end atIndex: ++l];
2071
else if (pos == ePos && cName != nil)
2075
if (isProtocol == YES)
2079
r = NSMakeRange(1, [cName length] - 2);
2080
cName = [cName substringWithRange: r];
2081
ref = [NSString stringWithFormat:
2082
@"<ref type=\"protocol\" id=\"(%@)\"><%@></ref>",
2085
else if ([cName hasSuffix: @")"] == YES)
2087
ref = [NSString stringWithFormat:
2088
@"<ref type=\"category\" id=\"%@\">%@</ref>",
2093
ref = [NSString stringWithFormat:
2094
@"<ref type=\"class\" id=\"%@\">%@</ref>",
2097
[a replaceObjectAtIndex: l withObject: ref];
2098
if (ePos < [tmp length])
2100
NSString *end = [tmp substringFromIndex: ePos];
2102
if ([end isEqualToString: @"]"] == YES)
2106
if ([end hasPrefix: @"]"] == YES)
2108
end = [end substringFromIndex: 1];
2110
if ([end length] > 0)
2112
[a insertObject: end atIndex: ++l];
1870
2121
* Now handle bare method names for current class ... outside brackets.
1872
if (hadMethod == NO && ([tmp hasPrefix: @"-"] || [tmp hasPrefix: @"+"]))
2123
if ([tmp hasPrefix: @"-"] || [tmp hasPrefix: @"+"])
1874
2125
unsigned ePos = [tmp length];
1875
2126
NSString *mName = nil;
1933
2187
[a insertObject: end atIndex: ++l];
2194
* Now handle function names. Anything ending in '()' is assumed to
2195
* be a referencable function name except 'main()' ... which is special.
2196
* NB. A comma, fullstop, or semicolon following '()' is counted as if
2197
* the text ended in '()'
2199
r = [tmp rangeOfString: @"()"];
2202
unsigned c = [tmp characterAtIndex: 0];
2203
unsigned len = [tmp length];
2204
NSString *str = [tmp substringToIndex: r.location];
2207
if ([identStart characterIsMember: c] == YES
2208
&& [str isEqual: @"main"] == NO)
2211
if (len > NSMaxRange(r))
2215
end = [tmp substringFromIndex: NSMaxRange(r)];
2216
c = [end characterAtIndex: 0];
2217
if (c == ',' || c == '.' || c == ';')
2219
[a insertObject: end atIndex: l + 1];
2220
tmp = [tmp substringToIndex: NSMaxRange(r)];
2221
[a replaceObjectAtIndex: l withObject: tmp];
2231
str = [NSString stringWithFormat:
2232
@"<ref type=\"function\" id=\"%@\">", str];
2233
[a insertObject: str atIndex: l++];
2234
l++; // Point past the function name in the array.
2235
[a insertObject: @"</ref>" atIndex: l++];
2002
2309
[str appendString: @"<?xml version=\"1.0\"?>\n"];
2003
2310
[str appendString: @"<!DOCTYPE gsdoc PUBLIC "];
2004
[str appendString: @"\"-//GNUstep//DTD gsdoc 0.6.7//EN\" "];
2005
[str appendString: @"\"http://www.gnustep.org/gsdoc-0_6_7.xml\">\n"];
2311
[str appendString: @"\"-//GNUstep//DTD gsdoc 1.0.1//EN\" "];
2312
[str appendString: @"\"http://www.gnustep.org/gsdoc-1_0_1.xml\">\n"];
2006
2313
[str appendString: @"<gsdoc base=\""];
2007
2314
[str appendString: [name lastPathComponent]];
2316
* If a -Up default has been set, create an up link in this
2317
* template file... as long as the specified up link is not
2318
* the template file itself.
2320
if (up != nil && [up isEqual: [name lastPathComponent]] == NO)
2322
[str appendString: @"\" up=\""];
2323
[str appendString: up];
2008
2325
[str appendString: @"\">\n"];
2009
2326
[str appendString: @" <head>\n"];
2010
2327
[str appendString: @" <title>"];
2016
2333
[str appendString: @"\"></author>\n"];
2017
2334
[str appendString: @" </head>\n"];
2018
2335
[str appendString: @" <body>\n"];
2336
[str appendString: @" <front><contents /></front>\n"];
2019
2337
[str appendString: @" </body>\n"];
2020
2338
[str appendString: @"</gsdoc>\n"];
2028
2346
start = [str rangeOfString: tmp];
2029
2347
if (start.length == 0)
2031
start = [str rangeOfString: @"</body>"];
2032
if (start.length == 0)
2034
NSLog(@"No </body> markup in %@ document", kind);
2349
start = [str rangeOfString: @"<back>"];
2350
if (start.length == 0)
2352
start = [str rangeOfString: @"</body>"];
2354
if (start.length == 0)
2356
NSLog(@"No <back> or </body> markup in %@ document", kind);
2037
2359
[str insertString: tmp atIndex: start.location];