This is DocText.m in view mode; [Download] [Up]
/* Copyright 1991 Gustavus Adolphus College. All rights reserved. * * Schematik was developed by Gustavus Adolphus College (GAC) with * support from NeXT Computer, Inc. Permission to copy this software, * to redistribute it, and to use it for any purpose is granted, * subject to the following restrictions and understandings. * * 1. Any copy made of this software must include this copyright * notice in full. * * 2. Users of this software agree to make their best efforts (a) to * return to the GAC Mathematics and Computer Science Department any * improvements or extensions that they make, so that these may be * included in future releases; and (b) to inform GAC of noteworthy * uses of this software. * * 3. All materials developed as a consequence of the use of this * software shall duly acknowledge such use, in accordance with the * usual standards of acknowledging credit in academic research. * * 4. GAC makes no express or implied warranty or representation of * any kind with respect to this software, including any warranty * that the operation of this software will be error-free. ANY * IMPLIED WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR * PURPOSE IS HEREBY DISCLAIMED. GAC is under no obligation to * provide any services, by way of maintenance, update, or otherwise. * * 5. In conjunction with products arising from the use of this * material, there shall be no use of the name of Gustavus Adolphus * College nor of any adaptation thereof in any advertising, * promotional, or sales literature without prior written consent * from GAC in each case. */ #import "DocText.h" #import "../defines.h" #import DocWin_h #import FindAgent_h #import InteractionWin_h #import PrefAgent_h #import <appkit/Application.h> #import <appkit/Font.h> #import <appkit/Matrix.h> #import <appkit/Menu.h> #import <appkit/NXCursor.h> #import <appkit/NXImage.h> #import <appkit/ScrollView.h> #import <dpsclient/event.h> #import <sys/types.h> #import <nextdev/keycodes.h> #import <objc/List.h> #import <NXCType.h> #import <string.h> #import <streams/streams.h> #import <ctype.h> extern void usleep(unsigned); extern void NXBeep(void); extern void *malloc(), *realloc(), free(); #define LEFT_PAREN '(' #define RIGHT_PAREN ')' #define LEFT_BRACKET '[' #define RIGHT_BRACKET ']' #define LEFT_BRACE '{' #define RIGHT_BRACE '}' #define LEFT_PARENS "([{" #define RIGHT_PARENS ")]}" #define PBLINKDELAY 175000 #define COMMENTSTRING ";" #define COMMENTCHAR ';' #define BADLINEBEGINSET " \n\t\r\v\f)]};" #define DELIMETERSET " \n\t\r\v\f();\"\'`|[]{}\377" #define PREFIXCHARS "\'`,@#" #define BIG_INDENT 4 #define SMALL_INDENT 2 #define MAXTOK 1024 #define MAX_FWD_ONLY 4 #define TABWIDTH 8 #define PAREN_NO_MATCH -1 #define NOT_REALLY_PAREN -2 #define larrow 172 #define uarrow 173 #define rarrow 174 #define darrow 175 typedef struct cntxt { struct cntxt *next; int elts; int distinguishedElts; int startPosition; int alignmentPosition; BOOL isLet; } Context; static BOOL inSet(const char, const char *); static int findParensMatch(id, const char, int, char *); static int findBeginningOfScope(id, int, char *); static int skipspacecommentnewline(const char[], int, int); static int skipstring(const char[], int, int); static int findEndOfScope(id, int, char *); static int getcolumnofposition(id, int); static void newElt(Context *, BOOL, NXStream *); static char *selectionBuffer=NULL; BOOL inOtherMethod=NO; int last1ClickPos=0; @implementation DocText #define PRE_CHANGE NXSelPt start,end; int tlength=0,i; BOOL wasInOtherMethod=inOtherMethod; \ if (!wasInOtherMethod) \ { inOtherMethod=YES; [super getSel:&start :&end]; tlength=textLength; } #define POST_CHANGE(POSITION,FROM,TO) \ if (!wasInOtherMethod) \ { inOtherMethod=NO; \ for(i=[markerList count]-1; i; i--) \ [[markerList objectAt:i] adjustTo:(POSITION) forPositionsFrom:(FROM) to:(TO) andAfterBy:(textLength-tlength)]; \ if ([window isMemberOf:[InteractionWin class]]) \ [[markerList objectAt:0] adjustTo:(POSITION) forPositionsFrom:(FROM) to:(TO) andAfterBy:(textLength-tlength)]; \ [indentationState changeAt:FROM];} - initFrame:(NXRect *)r { return [self initFrame:r text:"" alignment:NX_LEFTALIGNED]; } - initFrame:(NXRect *)r text:(const char *)text alignment:(int)mode { char *font=[[PrefAgent new] interFont]; [super initFrame:r text:text alignment:mode]; markerList=[[List alloc] initCount:1]; [markerList addObject:[[Marker alloc] initAt:0 type:MARKRIGHT]]; if (!(NXOrderStrings((unsigned char *)font, (unsigned char *)GOODFONT1, YES, strlen(GOODFONT1), NULL)&& NXOrderStrings((unsigned char *)font, (unsigned char *)GOODFONT2, YES, strlen(GOODFONT2), NULL))) [self setFont:[Font newFont:font size:[[PrefAgent new] interFontSize]]]; else [self setFont:[Font newFont:GOODFONT1 size:11]]; [self setSel:0 :0]; indentationState = [[IndentationState alloc] initText:self]; return self; } - free { [[markerList freeObjects] free]; [indentationState free]; return [super free]; } - keyDown:(NXEvent *)theEvent { switch (theEvent->data.key.charCode) { case soh: if (theEvent->flags&NX_CONTROLMASK) // move to beginning of line { NXSelPt start,end; int pos; [super getSel:&start :&end]; pos=[super positionFromLine:[super lineFromPosition:start.cp]]; return [self setSel:pos :pos]; } break; case stx: if (theEvent->flags&NX_CONTROLMASK) // move backward one character { theEvent->flags=NX_NUMERICPADMASK; theEvent->data.key.charSet=1; theEvent->data.key.charCode=larrow; } break; case etx: if (theEvent->flags&(NX_COMMANDMASK|NX_NUMERICPADMASK)) // Enter or Cntl-c return [[[[[[NXApp mainMenu] findCellWithTag:MENU_ACTIONS] target] itemList] selectCellWithTag:MENU_ACTIONS_Evaluate] sendAction]; else if (theEvent->flags&NX_CONTROLMASK) theEvent->data.key.charCode=nul; break; case eot: if (theEvent->flags&NX_CONTROLMASK) // delete next character { NXSelPt start,end; [super getSel:&start :&end]; [self setSel:start.cp+1 :start.cp+1]; // move cursor forward 1 char. theEvent->data.key.charCode=bs; // Text does a backspace } break; case enq: if (theEvent->flags&NX_CONTROLMASK) // move to end of line { NXSelPt start,end; int pos,line; [super getSel:&start :&end]; line=[super lineFromPosition:start.cp]; pos=([super positionFromLine:line+1]<0)?textLength:[super positionFromLine:line+1]-1; return [self setSel:pos :pos]; } break; case ack: if (theEvent->flags&NX_CONTROLMASK) // move forward one character { theEvent->flags=NX_NUMERICPADMASK; theEvent->data.key.charSet=1; theEvent->data.key.charCode=rarrow; } break; case bel: if (theEvent->flags&NX_CONTROLMASK) // bell { NXBeep(); return self; } break; case bs : break; // delete previous character (implemented by Text) case ht : return [self formatTextSelectionOnly:YES]; // indent line case nl : break; // newline (non-indenting cr; implemented by Text) case vt : if (theEvent->flags&NX_CONTROLMASK) // delete forward to end of line { NXSelPt start,end; int pos,line; [super getSel:&start :&end]; line=[super lineFromPosition:start.cp]; pos=([super positionFromLine:line+1]<0)?textLength:[super positionFromLine:line+1]-1; [window disableFlushWindow]; [self setSel:start.cp :pos]; [window reenableFlushWindow]; return [self cut:self]; } break; case cr : {PRE_CHANGE // carriage return [window disableFlushWindow]; [super keyDown:theEvent]; [window reenableFlushWindow]; POST_CHANGE(start.cp+1,start.cp,end.cp)} if (([[PrefAgent new] autoindent]&&!(theEvent->flags&NX_ALTERNATEMASK))|| ((theEvent->flags&NX_ALTERNATEMASK)&&![[PrefAgent new] autoindent])) [self formatTextSelectionOnly:YES]; [window flushWindow]; return self; case so : if (theEvent->flags&NX_CONTROLMASK) // move down one line { theEvent->flags=NX_NUMERICPADMASK; theEvent->data.key.charSet=1; theEvent->data.key.charCode=darrow; } break; case dle: if (theEvent->flags&NX_CONTROLMASK) // move up one line { theEvent->flags=NX_NUMERICPADMASK; theEvent->data.key.charSet=1; theEvent->data.key.charCode=uarrow; } break; case em : if (theEvent->flags&NX_CONTROLMASK) // indent line (shift-tab) { return [self paste:self]; } else return [self formatTextSelectionOnly:YES]; case RIGHT_PAREN : case RIGHT_BRACKET: case RIGHT_BRACE : {PRE_CHANGE [super keyDown:theEvent]; POST_CHANGE(start.cp,start.cp,end.cp) {char c; [super getSel:&start :&end]; [super getSubstring:&c start:end.cp-1 length:1]; if (inSet(c,RIGHT_PARENS)&&[[PrefAgent new] matchParens]) { int pos; char buffer[textLength+1]; [super getSubstring:buffer start:0 length:textLength]; if ((pos = findParensMatch(self,(char)theEvent->data.key.charCode,start.cp-1,buffer))==PAREN_NO_MATCH) NXBeep(); else if (pos >= 0) { [self setSel:pos :pos+1]; NXPing(); usleep(PBLINKDELAY); [self setSel:start.cp :start.cp]; } }}} return self; } {PRE_CHANGE [super keyDown:theEvent]; POST_CHANGE(start.cp,start.cp,end.cp)} return self; } - mouseDown:(NXEvent*)theEvent { NXSelPt start,end; switch (theEvent->data.mouse.click) { case 1 : [super mouseDown:theEvent]; [self getSel:&start :&end]; last1ClickPos = start.cp; return self; case 2 : [window disableFlushWindow]; [super mouseDown:theEvent]; { char c[4]; [super getSel:&start :&end]; [super getSubstring:c start:start.cp-2 length:3]; if (inSet(c[2],LEFT_PARENS RIGHT_PARENS)&&!((c[0]=='#')&&(c[1]=='\\'))) { [window reenableFlushWindow]; if (([[PrefAgent new] dblClickMatch])&&((end.cp-start.cp)==1)) { char buffer[textLength+1]; int temp; [super getSubstring:buffer start:0 length:textLength]; if ((temp = findParensMatch(self, buffer[start.cp], start.cp, buffer))<0) { [window flushWindow]; return self; } if (inSet(buffer[start.cp], LEFT_PARENS)) [super setSel:start.cp :temp+1]; else [super setSel:temp :start.cp+1]; } return self; } else if (inSet(c[2], DELIMETERSET)&&!((c[0]=='#')&&(c[1]=='\\'))) { [[window reenableFlushWindow] flushWindow]; return self; } else { int i,j; char buffer[textLength+1]; [super getSubstring:buffer start:0 length:textLength]; for(i=start.cp; (i>=0)&&(!inSet(buffer[i],DELIMETERSET)||((i>=2)&&(buffer[i-2]=='#')&&(buffer[i-1]=='\\'))); i--); for(j=end.cp; (j<textLength)&&(!inSet(buffer[j],DELIMETERSET)||((j>=2)&&(buffer[j-2]=='#')&&(buffer[j-1]=='\\'))); j++); [super setSel:i+1 :j]; [[window reenableFlushWindow] flushWindow]; } } return self; case 3 : break; default: [self selectScope2]; return self; } return [super mouseDown:theEvent]; } - paste:sender { PRE_CHANGE [super paste:sender]; POST_CHANGE(end.cp+(textLength-tlength),start.cp,end.cp) if ([[PrefAgent new] formatAfterPaste]) { [markerList addObject:[[Marker alloc] initAt:(end.cp+(textLength-tlength)) type:MARKCENTER]]; [self setSel:start.cp :(end.cp+(textLength-tlength))]; [self formatTextSelectionOnly:YES]; end.cp = [[markerList objectAt:([markerList count]-1)] markerPosition]; [self setSel:end.cp :end.cp]; [[markerList removeLastObject] free]; } return self; } - clear:sender { PRE_CHANGE [super clear:sender]; POST_CHANGE(start.cp,start.cp,end.cp) return self; } - delete:sender { PRE_CHANGE [super delete:sender]; POST_CHANGE(start.cp,start.cp,end.cp) return self; } - setText:(const char *)aString { PRE_CHANGE [super setText:aString]; POST_CHANGE(0,0,tlength) return self; } - readText:(NXStream *)stream { PRE_CHANGE [super readText:stream]; { NXStream *stream = [self stream]; int c; while((c = NXGetc(stream)) != EOF) if(c == '\t'){ int pos = NXTell(stream); int spaces = TABWIDTH - getcolumnofposition(self, pos-1) % TABWIDTH; char spacestring[spaces+1]; [self setSel:pos-1 :pos]; memset(spacestring, ' ', spaces); spacestring[spaces] = '\0'; [self replaceSel:spacestring]; stream = [self stream]; NXSeek(stream, pos, NX_FROMSTART); } [self setSel:0 :0]; } POST_CHANGE(0,0,tlength) return self; } - replaceSel:(const char *)aString length:(int)length { PRE_CHANGE [super replaceSel:aString length:length]; POST_CHANGE(end.cp+(textLength-tlength),start.cp,end.cp) return self; } - readSelectionFromPasteboard:pboard { id ret; PRE_CHANGE ret = [super readSelectionFromPasteboard:pboard]; POST_CHANGE(end.cp+(textLength-tlength),start.cp,end.cp) return ret; } - setFont:aFont { [window disableFlushWindow]; [super setFont:aFont]; [[[window display] reenableFlushWindow] flushWindow]; [[window scrollView] setLineScroll:[super lineHeight]]; [[window scrollView] setPageScroll:[super lineHeight]]; return self; } - appendNewlineIfNeeded { NXSelPt start,end; int textPointer = [[markerList objectAt:0] markerPosition]; if ([[PrefAgent new] transcriptMode]) [[markerList objectAt:0] adjustTo:textLength-1]; [super getSel:&start :&end]; [markerList addObject:[[Marker alloc] initAt:end.cp type:MARKCENTER]]; [markerList addObject:[[Marker alloc] initAt:start.cp type:MARKCENTER]]; if (textPointer>0) { char buffer[2]; [super getSubstring:buffer start:textPointer-1 length:1]; if (buffer[0]!=NEWLINECHAR) { [[self setSel:textPointer :textPointer] replaceSel:NEWLINESTRING]; [window setDocEdited:YES]; [self setInsertionBar:textPointer+1]; } } start.cp = [[markerList objectAt:([markerList count]-1)] markerPosition]; end.cp = [[markerList objectAt:([markerList count]-2)] markerPosition]; [self setSel:start.cp :end.cp]; [[markerList removeLastObject] free]; [[markerList removeLastObject] free]; return self; } - appendText:(const char *)aString commented:(BOOL)aBoolean withNewline:(BOOL)aBoolean2 { NXSelPt start,end; int textPointer = [[markerList objectAt:0] markerPosition],i; if ([[PrefAgent new] transcriptMode]) [[markerList objectAt:0] adjustTo:textLength-1]; [super getSel:&start :&end]; [markerList addObject:[[Marker alloc] initAt:end.cp type:MARKCENTER]]; [markerList addObject:[[Marker alloc] initAt:start.cp type:MARKCENTER]]; [window disableFlushWindow]; if (aBoolean) { [super setSel:textPointer :textPointer]; if ([super positionFromLine:[super lineFromPosition:textPointer]]==textPointer) [self replaceSel:COMMENTSTRING]; for(i=0; aString[i]; i++) { [self replaceSel:(aString+i) length:1]; if ((aString[i]==NEWLINECHAR)&&(aString[i+1])&&(aString[i+1]!=NEWLINECHAR)&&(aString[i+1]!=COMMENTCHAR)) [self replaceSel:COMMENTSTRING]; } } else [[self setSel:textPointer :textPointer] replaceSel:aString]; if (aBoolean2) [self replaceSel:NEWLINESTRING]; [window setDocEdited:YES]; if ([[PrefAgent new] autoScroll]) [super scrollSelToVisible]; start.cp = [[markerList objectAt:([markerList count]-1)] markerPosition]; end.cp = [[markerList objectAt:([markerList count]-2)] markerPosition]; [self setSel:start.cp :end.cp]; [[markerList removeLastObject] free]; [[markerList removeLastObject] free]; [[window reenableFlushWindow] flushWindow]; return self; } - (STR)getAll { [super selectAll:self]; return [self getSelection]; } - (STR)getSelection { NXSelPt start,end; int length; [super getSel:&start :&end]; length = end.cp-start.cp; selectionBuffer = (char *)(selectionBuffer==NULL?malloc(length+1):realloc(selectionBuffer, length+1)); [super getSubstring:selectionBuffer start:start.cp length:length]; selectionBuffer[length] = '\0'; if ([window isMemberOf:[InteractionWin class]]&&(*selectionBuffer)) if ([[PrefAgent new] transcriptMode]) [self setInsertionBar:textLength]; else [self setInsertionBar:end.cp]; return selectionBuffer; } - setInsertionBar:(int)anInt { int textPointer = (textLength<=anInt?textLength-1:anInt); [[markerList objectAt:0] adjustTo:textPointer]; return [super setSel:textPointer :textPointer]; } - (BOOL)selectScope { NXSelPt start,end; int startpos,endpos; char textBuffer[textLength+1]; [super getSel:&start :&end]; [super getSubstring:textBuffer start:0 length:textLength]; textBuffer[textLength] = '\0'; if (((startpos = findBeginningOfScope(self, start.cp, textBuffer))<0)||((endpos = findEndOfScope(self, startpos, textBuffer))<0)||(endpos<end.cp)) return NO; [super setSel:startpos :endpos]; return YES; } - (BOOL)selectScope2 { int startpos,endpos; char textBuffer[textLength+1]; [super getSubstring:textBuffer start:0 length:textLength]; textBuffer[textLength] = '\0'; if (((startpos = findBeginningOfScope(self, last1ClickPos, textBuffer))<0)||((endpos = findEndOfScope(self, startpos, textBuffer))<0)||(endpos<last1ClickPos)) return NO; [super setSel:startpos :endpos]; return YES; } - saveSelectionState { NXSelPt start,end; [super getSel:&start :&end]; [markerList addObject:[[Marker alloc] initAt:end.cp type:MARKCENTER]]; [markerList addObject:[[Marker alloc] initAt:start.cp type:MARKCENTER]]; return self; } - restoreSelectionState { int start,end; if ([markerList count]<3) return nil; start = [[markerList objectAt:([markerList count]-1)] markerPosition]; end = [[markerList objectAt:([markerList count]-2)] markerPosition]; [self setSel:start :end]; [[markerList removeLastObject] free]; [[markerList removeLastObject] free]; return self; } - (int)stringSearch:(const char *)theTarget forAgent:theAgent direction:(int)direction ignoreCase:(BOOL)ignorecase wholeWord:(BOOL)wholeword inSel:(BOOL)inSel { NXSelPt start,end; int slength,tlength = strlen(theTarget); int startcp,endcp,i,origTextLength=textLength; char *buffer; [super getSel:&start :&end]; slength = end.cp-start.cp; if (inSel) { buffer = (char *)NXZoneMalloc([theAgent zone], slength+1); [super getSubstring:buffer start:start.cp length:slength]; startcp = 0; endcp = (slength-tlength); } else { buffer = (char *)NXZoneMalloc([theAgent zone], origTextLength+1); [super getSubstring:buffer start:0 length:origTextLength]; startcp = (direction==FINDFORWARD?end.cp:start.cp-tlength); endcp = (direction==FINDFORWARD?origTextLength-tlength+1:0); } for(i=startcp; (direction==FINDFORWARD?i<=endcp:i>=endcp); i+=direction) if (!NXOrderStrings((unsigned char *)theTarget,(unsigned char *)(buffer+i),!ignorecase,tlength,NULL)) if (!(wholeword&&(((i>0)&&NXIsAlNum(buffer[i-1]))||(((i+tlength)<=(inSel?slength:origTextLength))&&NXIsAlNum(buffer[i+tlength]))))) { int offset=i+(inSel?start.cp:0)+(textLength-origTextLength); [super setSel:offset :(offset+tlength)]; NXZoneFree([theAgent zone], buffer); return 1; } if (inSel) { NXZoneFree([theAgent zone], buffer); return 0; } startcp = (direction==FINDFORWARD?0:textLength-tlength+1); endcp = (direction==FINDFORWARD?end.cp-2:start.cp+1); origTextLength = textLength; for(i=startcp; (direction==FINDFORWARD?i<=endcp:i>=endcp); i+=direction) if (!NXOrderStrings((unsigned char *)theTarget,(unsigned char *)(buffer+i),!ignorecase,tlength,NULL)) if (!(wholeword&&(((i>0)&&NXIsAlNum(buffer[i-1]))||(((i+tlength)<=origTextLength)&&NXIsAlNum(buffer[i+tlength]))))) { int offset=i+(textLength-origTextLength); [super setSel:offset :(offset+tlength)]; NXZoneFree([theAgent zone], buffer); return 1; } NXZoneFree([theAgent zone], buffer); return 0; } - formatTextSelectionOnly:(BOOL)selOnly { NXSelPt start, end; int startline, finishline, i; [self getSel:&start :&end]; [markerList addObject:[[Marker alloc] initAt:end.cp type:MARKRIGHT]]; [markerList addObject:[[Marker alloc] initAt:start.cp type:MARKRIGHT]]; if (!selOnly && start.cp >= end.cp){ startline = 1; finishline = [self lineFromPosition:textLength]; } else { char c; startline = [self lineFromPosition:start.cp]; [self getSubstring:&c start:end.cp-1 length:1]; finishline = [self lineFromPosition: ((c == '\n') && (start.cp < end.cp) ? (end.cp - 1) : end.cp)]; } for (i = startline; i <= finishline; i++) { int desired = [indentationState determineIndentation:i]; if (desired >= 0){ BOOL anyTabs = NO; int current; unsigned char c; NXStream *stream = [self stream]; NXSeek(stream, [self positionFromLine:i], NX_FROMSTART); for (current = 0; ; current++){ if((c = NXGetc(stream)) == '\t') anyTabs = YES; else if (c != ' ') break; } #ifdef DEBUG_INDENTATION fprintf(stderr, "Linen %d; current = %d, desired = %d%s\n", i, current, desired, anyTabs?"; tabs present":""); #endif if(anyTabs || current != desired){ int startpos = [self positionFromLine:i]; char spacestring[desired+1]; [self setSel:startpos :startpos+current]; memset(spacestring, ' ', desired); spacestring[desired] = '\0'; [self replaceSel:spacestring]; [window setDocEdited:YES]; } else NXUngetc(stream); } } start.cp = [[markerList objectAt:([markerList count]-1)] markerPosition]; end.cp = [[markerList objectAt:([markerList count]-2)] markerPosition]; [self setSel:start.cp :end.cp]; [[markerList removeLastObject] free]; [[markerList removeLastObject] free]; return self; } @end static BOOL inSet(const char element, const char *set) { int i; for(i=0; *(set+i); i++) if (element==*(set+i)) return YES; return NO; } static int findParensMatch(id self, const char parens, int startpos, char *text) { int i,count=0,init,match,direction,textlength=[self textLength]; switch (init=parens) { case LEFT_PAREN : match = RIGHT_PAREN; direction = 1; break; case LEFT_BRACKET : match = RIGHT_BRACKET; direction = 1; break; case LEFT_BRACE : match = RIGHT_BRACE; direction = 1; break; case RIGHT_PAREN : match = LEFT_PAREN; direction = -1; break; case RIGHT_BRACKET: match = LEFT_BRACKET; direction = -1; break; case RIGHT_BRACE : match = LEFT_BRACE; direction = -1; break; default : return NOT_REALLY_PAREN; } if ((text[startpos-2]=='#')&&(text[startpos-1]=='\\')) return NOT_REALLY_PAREN; for(i=startpos+direction; ((i>=0)&&(i<textlength))&&((count>0)||(text[i]!=match)||((i>=2)&&(text[i-2]=='#')&&(text[i-1]=='\\'))); i+=direction) { if ((direction>0)&&(text[i]=='\"')&&!((text[i-2]=='#')&&(text[i-1]=='\\'))) { i=skipstring(text,textlength,i); if (i<0) break; } else if ((direction>0)&&(text[i]==COMMENTCHAR)&&!((text[i-2]=='#')&&(text[i-1]=='\\'))) i=skipspacecommentnewline(text,textlength,i-1)-1; else if ((text[i]==init)&&!((text[i-2]=='#')&&(text[i-1]=='\\'))) count++; else if ((text[i]==match)&&!((text[i-2]=='#')&&(text[i-1]=='\\'))) count--; } return ((i>=textlength)||(i<0)?PAREN_NO_MATCH:i); } static int findBeginningOfScope(id self, int currpos, char *text) { int line,startpos; line = [self lineFromPosition:currpos]; startpos = [self positionFromLine:line]; for(; line&&(!text[startpos]||inSet(text[startpos],BADLINEBEGINSET)); startpos = [self positionFromLine:--line]); return (line<=0?-1:startpos); } static int skipspacecommentnewline(const char buffer[], int textlength, int endpos) { for(endpos++; (endpos<textlength)&&(buffer[endpos]!=NEWLINECHAR)&&NXIsSpace(buffer[endpos]); endpos++); if (buffer[endpos]==NEWLINECHAR) return endpos+1; else if ((buffer[endpos]==COMMENTCHAR)&&!((endpos>=2)&&(buffer[endpos-2]=='#')&&(buffer[endpos-1]=='\\'))) { for(endpos++; (endpos<textlength)&&(buffer[endpos]!=NEWLINECHAR); endpos++); if (endpos>=textlength) return endpos; return endpos+1; } else return endpos; } static int skipstring(const char buffer[], int textlength, int endpos) { if ((endpos>=2)&&(buffer[endpos-1]=='\\')&&(buffer[endpos-2]=='#')) return endpos; while (1) { for(endpos++; (endpos<textlength)&&(buffer[endpos]!='\"'); endpos++); if (endpos>=textlength) return -1; if ((buffer[endpos-1]!='\\')||((endpos>=2)&&(buffer[endpos-2]=='\\'))) return endpos; } } static int findEndOfScope(id self, int startpos, char *text) { int endpos=0; for(; text[startpos]&&inSet(text[startpos],PREFIXCHARS); startpos++); if ((text[startpos]=='\\')&&(text[startpos-1]=='#')) startpos--; if (inSet(text[startpos],LEFT_PARENS)) { if ((endpos = findParensMatch(self,text[startpos],startpos,text))<0) return -1; } else if (text[startpos]=='\"') { if ((endpos = skipstring(text,strlen(text),startpos))<0) return -1; } else { for(endpos=startpos; text[endpos]&&(!inSet(text[endpos],DELIMETERSET)||((text[endpos-2]=='#')&&(text[endpos-1]=='\\'))); endpos++); endpos--; } endpos = skipspacecommentnewline(text, strlen(text), endpos); if (endpos<=startpos) return -1; return endpos; } static int getcolumnofposition(id self, int charpos) { return charpos-[self positionFromLine:[self lineFromPosition:charpos]]; } @implementation Marker - init { return [self initAt:0 type:MARKCENTER]; } - initAt:(int)anInt type:(signed char)aChar { [super init]; charpos = anInt; type = aChar; return self; } - (int)markerPosition { return charpos; } - adjustTo:(int)anInt forPositionsFrom:(int)begin to:(int)end andAfterBy:(int)anInt2 { if (((charpos==begin)&&(type==MARKRIGHT))||((begin<charpos)&&(charpos<end))||((charpos==end)&&(type==MARKLEFT))) charpos = anInt; else if (end<=charpos) charpos += anInt2; return self; } - adjustTo:(int)anInt { charpos = anInt; return self; } @end @implementation IndentationState - changeAt:(int)position { stream = NULL; if ( [text lineFromPosition:position] < knownLine ){ knownLine = 1; while(contexts){ Context *temp = contexts; contexts = temp->next; free(temp); } inString = NO; } return self; } - init { return [self initText:nil]; } - initText:theText { [super init]; text = theText; knownLine = 1; stream = NULL; contexts = NULL; inString = NO; keywordListVersion = [[PrefAgent new] keywordListVersion]; return self; } - free { while(contexts) { Context *temp = contexts; contexts = temp->next; free(temp); } return [super free]; } - (int)determineIndentation:(int)line { int currentKeywordListVersion = [[PrefAgent new] keywordListVersion]; if (keywordListVersion != currentKeywordListVersion){ keywordListVersion = currentKeywordListVersion; [self changeAt:0]; } if (line < knownLine || (line - knownLine) > MAX_FWD_ONLY){ int l; for(l = line-1; l > 1 && l != knownLine; l--){ char c; [text getSubstring:&c start:[text positionFromLine:l] length:1]; if(!index(BADLINEBEGINSET, c)) break; } if(l == 0) l++; if(l != knownLine){ knownLine = l; stream = NULL; inString = NO; while(contexts){ Context *temp = contexts; contexts = temp->next; free(temp); } } } while(knownLine < line) [self scanLine]; return [self indent]; } - setupStream { if (stream == NULL){ stream = [text stream]; NXSeek(stream, [text positionFromLine:knownLine], NX_FROMSTART); } return self; } - (int)indent { #ifdef DEBUG_INDENTATION fprintf(stderr, "start of IndentationState/indent "); [self describeSelf]; #endif if (inString) return -1; else if (contexts == NULL) return 0; else { Context *c = contexts; if (c->elts < c->distinguishedElts + 1) return BIG_INDENT + getcolumnofposition(text, c->startPosition); else if (c->elts == c->distinguishedElts + 1) return SMALL_INDENT + getcolumnofposition(text, c->startPosition); else return getcolumnofposition(text, c->alignmentPosition); } } - scanLine { BOOL anythingYet = NO; Context *context = contexts; BOOL prefix = NO; char c; #ifdef DEBUG_INDENTATION fprintf(stderr, "start of IndentationState/scanLine "); [self describeSelf]; #endif [self setupStream]; knownLine++; if (inString) goto scan_string; while (1){ switch (c = NXGetc(stream)){ case '\n': case '\377': return self; case ' ': case '\t': continue; case ';': while(NXGetc(stream) != '\n') ; return self; case '\'': case'`': case ',': case '@': case '#': if(!prefix){ prefix = YES; newElt(context, anythingYet, stream); } if(c == '#'){ if(NXGetc(stream) == '\\'){ if(isalpha(c = NXGetc(stream))){ while(index(DELIMETERSET, NXGetc(stream)) == NULL) ; NXUngetc(stream); break; } else if(c == '\n') NXUngetc(stream); else break; } else NXUngetc(stream); } continue; case '(': case '[': case '{': if(!prefix) newElt(context, anythingYet, stream); contexts = malloc(sizeof(Context)); ((Context *)contexts)->next = context; context = contexts; context->isLet = NO; context->elts = 0; context->distinguishedElts = -1; context->startPosition = (context->alignmentPosition=NXTell(stream)) - 1; break; case ')': case ']': case '}': if(context != NULL){ contexts = context->next; free(context); context = contexts; } break; case '"': inString = YES; if(!prefix) newElt(context, anythingYet, stream); scan_string: while (1){ if ((c = NXGetc(stream)) == '\n') return self; else if (c == '\\'){ if ((c = NXGetc(stream)) == '\n') NXUngetc(stream); } else if (c == '"'){ inString = NO; break; } } break; default: #ifdef DEBUG_INDENTATION fprintf(stderr, "start of token "); [self describeSelf]; #endif if(!prefix) newElt(context, anythingYet, stream); if(!prefix && context != NULL && context->elts == 2 && context->isLet) context->distinguishedElts++; if (!prefix && context != NULL && context->elts == 1) { char tokbuf[MAXTOK+1]; int i = 0; do { if (i < MAXTOK) tokbuf[i++] = c; } while(index(DELIMETERSET, c = NXGetc(stream)) == NULL); NXUngetc(stream); tokbuf[i] = '\0'; context->distinguishedElts = [[PrefAgent new] deForSpecialForm:tokbuf]; if(!NXOrderStrings((unsigned char *) tokbuf, (unsigned char *) "let", NO, -1, NULL)) context->isLet = YES; } else{ while(index(DELIMETERSET, NXGetc(stream)) == NULL) ; NXUngetc(stream); } #ifdef DEBUG_INDENTATION fprintf(stderr, "end of token "); [self describeSelf]; #endif break; } prefix = NO; anythingYet = YES; } } - describeSelf { Context *context = contexts; fprintf(stderr, "indentation state\n"); fprintf(stderr, " knownLine: %d\n", knownLine); if(stream == NULL) fprintf(stderr, " no stream\n"); else{ int pos = NXTell(stream); fprintf(stderr, " stream position: %d (row %d, col %d)\n", pos, [text lineFromPosition:pos], getcolumnofposition(text, pos)); } fprintf(stderr, " %sin string\n", inString?"":"not "); while(context){ fprintf(stderr, " elts: %d, des: %d, startpos: %d, alignpos: %d\n", context->elts, context->distinguishedElts, context->startPosition, context->alignmentPosition); context = context->next; } return self; } @end static void newElt(Context *context, BOOL anythingYet, NXStream *stream){ if (context == NULL) return; if(context->elts == 1 || (context->elts > 1 && !anythingYet)) context->alignmentPosition = NXTell(stream)-1; context->elts++; }
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.