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.