This is LispText.m in view mode; [Download] [Up]
/* ** LispText.m ** A Text object to drive the Lisp object. ** Lee Boynton, NeXT, Inc., 1989 */ #import <stdlib.h> #import <string.h> #import <sys/time.h> #import <appkit/Window.h> #import <appkit/publicWraps.h> #import "Lisp.h" #import "LispText.h" @implementation LispText #define DEFAULT_HISTORY 64 #define CLOSE_PAREN (')') #define OPEN_PAREN ('(') #define NEW_LINE ('\n') #define CTRL_A (1) #define CTRL_B (2) #define CTRL_C (3) #define CTRL_D (4) #define CTRL_E (5) #define CTRL_F (6) #define CTRL_K (11) #define CTRL_N (14) #define CTRL_P (16) unsigned short lispFilter(unsigned short theChar, int flags, unsigned short charSet) { if (flags & NX_COMMANDMASK) theChar = 0; else if (theChar == NX_DELETE) theChar = NX_BACKSPACE; else if (theChar == NX_CR || theChar == NEW_LINE) theChar = 0; else if (theChar == '\t') theChar = '\t'; else if (theChar < ' ') theChar = 0; return theChar; } static void initLispCategoryTable(id self) { static unsigned char lispTable[256]; unsigned char punctuationCategory, normalCategory; unsigned char nullCategory, whiteSpaceCategory; int i; bcopy([self charCategoryTable],lispTable,sizeof(lispTable)); nullCategory = lispTable[0]; punctuationCategory = lispTable[';']; normalCategory = lispTable['a']; whiteSpaceCategory = lispTable[' ']; lispTable['!'] = normalCategory; lispTable['&'] = normalCategory; lispTable['('] = punctuationCategory; lispTable[')'] = punctuationCategory; lispTable['*'] = normalCategory; lispTable['+'] = normalCategory; lispTable['-'] = normalCategory; lispTable['.'] = normalCategory; lispTable['/'] = normalCategory; lispTable[':'] = normalCategory; lispTable['<'] = normalCategory; lispTable['='] = normalCategory; lispTable['>'] = normalCategory; lispTable['?'] = normalCategory; lispTable['@'] = normalCategory; lispTable['['] = punctuationCategory; lispTable['\\'] = normalCategory; lispTable[']'] = punctuationCategory; lispTable['{'] = punctuationCategory; lispTable['|'] = normalCategory; lispTable['}'] = punctuationCategory; for(i=128;i<256;i++) lispTable[i] = nullCategory; [self setCharCategoryTable:lispTable]; } + newFrame:(NXRect *)frameRect text:(char *)theText alignment:(int)align { int i; self = [super newFrame:frameRect text:theText alignment:align]; [self setCharFilter:lispFilter]; [self setOpaque:YES]; [self setOverstrikeDiacriticals:NO]; initLispCategoryTable(self); inputPosition = 0; maxHistory = DEFAULT_HISTORY; history = (char **)malloc(maxHistory*sizeof(char*)); for (i=0; i<maxHistory; i++) history[i] = NULL; historyHead = 0; connectedToLisp=NO; theLisp = [Lisp new]; [theLisp setDelegate:self]; return self; } - free { [theLisp free]; return [super free]; } - lispOutput:(const char *)theString { int offset; NXSelPt selStart, selEnd; if (!theString) return self; offset = strlen(theString); if (!connectedToLisp) { int i = [self textLength]; [self setSel:0 :i]; [self replaceSel:theString]; inputPosition = offset; [window makeFirstResponder:self]; connectedToLisp=YES; } else { [self getSel:&selStart :&selEnd]; [self setSel:inputPosition :inputPosition]; [self replaceSel:theString]; inputPosition += offset; [self setSel:selStart.cp+offset :selEnd.cp+offset]; } [self scrollSelToVisible]; return self; } /* ** NXBGetc - a text stream macro to get the previous character. */ typedef struct { id text; NXTextBlock *block; } textInfo; static char getPrevious(NXStream *s) { textInfo *info = (textInfo *) s->info; NXTextBlock *block = info->block->prior; if (!block) { return EOF; } s->buf_base = block->text; s->buf_ptr = s->buf_base + block->chars; s->offset -= block->chars; info->block = block; return *(--s->buf_ptr); } #define NXBGetc(s) \ (((s)->buf_base == (s)->buf_ptr) ? getPrevious(s) : \ *(--((s)->buf_ptr)) ) int findMatchingOpenParen(id text, char thisChar, char thatChar, int curPos) { int count = 1; char ch; NXStream *s = [text stream]; NXSeek(s, curPos, NX_FROMSTART); while ((ch = NXBGetc(s)) != EOF) { if (ch == thatChar && !(--count)) return NXTell(s); else if (ch == thisChar) count++; } return -1; } int findMatchingCloseParen(id text, char thisChar, char thatChar, int curPos) { int count = 1; char ch; NXStream *s = [text stream]; NXSeek(s, curPos, NX_FROMSTART); while ((ch = NXGetc(s)) != EOF) { if (ch == thatChar && !(--count)) return NXTell(s); else if (ch == thisChar) count++; } return -1; } void highlightChar(id text, int charPosition) { NXSelPt selStart, selEnd; [text getSel:&selStart :&selEnd]; [text setSel:charPosition :charPosition+1]; [[text window] flushWindow]; NXPing(); [[text window] disableFlushWindow]; [text setSel:selStart.cp :selEnd.cp]; [[text window] reenableFlushWindow]; } int searchBackwardsForChar(id text, char theChar, int curPos) { char ch; NXStream *s = [text stream]; NXSeek(s, curPos, NX_FROMSTART); while ((ch = NXBGetc(s)) != EOF) { if (ch == theChar) return NXTell(s); } return -1; } void insertChar(id self, char c, int pos) { char buf[2]; buf[0] = c; buf[1] = 0; [self setSel:pos :pos]; [self replaceSel:buf]; [self scrollSelToVisible]; } static void addToHistory(id self, char *buf) { int i = (self->historyHead++) % self->maxHistory; if (self->history[i]) free(self->history[i]); self->history[i] = buf; } static char *getHistory(id self, int index) { int i = index % self->maxHistory; return self->history[i]; } - keyDown:(NXEvent *)theEvent { static int history_index=0; int i, lastPos = [self textLength]; unsigned short val; NXSelPt selStart, selEnd; if (!connectedToLisp) { NXBeep(); return self; } [self getSel:&selStart :&selEnd]; val = theEvent->data.key.charCode + (theEvent->data.key.charSet << 8); switch (val) { case CLOSE_PAREN: if (selStart.cp < inputPosition) [self setSel:lastPos :lastPos]; i = findMatchingOpenParen(self,CLOSE_PAREN,OPEN_PAREN,selEnd.cp); [super keyDown:theEvent]; if (i >= 0) highlightChar(self,i); return self; case NX_DELETE: case NX_BACKSPACE: if (selEnd.cp == selStart.cp) { if (lastPos <= inputPosition || selStart.cp <= inputPosition) { NXBeep(); return self; } } else { if (selEnd.cp <= inputPosition || selStart.cp <= inputPosition) { NXBeep(); return self; } } break; case NX_CR: case NEW_LINE: if (selEnd.cp >= inputPosition) { char *buf; i = lastPos - inputPosition; buf = (char *)malloc(i+2); if (i) [self getSubstring:buf start:inputPosition length:i]; buf[i] = NEW_LINE; buf[i+1] = 0; inputPosition = lastPos+1; [theLisp inputLisp:buf]; if (i) { buf[i] = 0; addToHistory(self,buf); history_index = historyHead; } [self setSel:lastPos :lastPos]; insertChar(self,NEW_LINE,lastPos); return self; } else { int len = selEnd.cp-selStart.cp; if (len) { char *buf = (char *)malloc(len+1); [self getSubstring:buf start:selStart.cp length:len]; buf[len] = 0; [self setSel:lastPos :lastPos]; [self replaceSel:buf]; free(buf); [self scrollSelToVisible]; } else [self setSel:lastPos :lastPos]; return self; } case CTRL_C: [self setSel:lastPos :lastPos]; [theLisp interruptLisp]; return self; break; case CTRL_A: [self setSel:inputPosition :inputPosition]; return self; case CTRL_E: [self setSel:lastPos :lastPos]; return self; case CTRL_B: if (selStart.cp < inputPosition) [self setSel:inputPosition :inputPosition]; else if (selStart.cp != selEnd.cp) [self setSel:selStart.cp :selStart.cp]; else if (selStart.cp > inputPosition) [self setSel:selStart.cp-1 :selStart.cp-1]; else NXBeep(); return self; case CTRL_F: if (selEnd.cp < inputPosition) [self setSel:inputPosition :inputPosition]; else if (selStart.cp != selEnd.cp) [self setSel:selEnd.cp :selEnd.cp]; else if (selEnd.cp < lastPos) [self setSel:selEnd.cp+1 :selEnd.cp+1]; else NXBeep(); return self; case CTRL_D: if (selStart.cp >= inputPosition) { if (selStart.cp == selEnd.cp) if (selEnd.cp >= lastPos) { NXBeep(); return; } else [self setSel:selEnd.cp :selEnd.cp+1]; [self replaceSel:""]; } else NXBeep(); return self; case CTRL_K: if (selStart.cp >= inputPosition) { if (inputPosition != lastPos) [self setSel:selStart.cp :lastPos]; [self replaceSel:""]; } else NXBeep(); return self; case CTRL_P: { char *s; if (!historyHead || (history_index <= (historyHead-self->maxHistory))) return self; s = getHistory(self, history_index-1); if (!s) return self; history_index--; [window disableFlushWindow]; [self setSel:inputPosition :lastPos]; [self replaceSel:s]; [self scrollSelToVisible]; [window reenableFlushWindow]; [window flushWindow]; return self; } case CTRL_N: { char *s; if (!historyHead || (history_index >= (historyHead))) return self; s = getHistory(self, history_index); if (s) s = getHistory(self, ++history_index); [window disableFlushWindow]; [self setSel:inputPosition :lastPos]; [self replaceSel:s]; [self scrollSelToVisible]; [window reenableFlushWindow]; [window flushWindow]; return self; } default: if (selStart.cp < inputPosition) [self setSel:lastPos :lastPos]; break; } return [super keyDown:theEvent]; } - clear:sender { NXSelPt selStart, selEnd; [self getSel:&selStart :&selEnd]; if (selEnd.cp < inputPosition || selStart.cp < inputPosition) return self; else return [super clear:sender]; } - cut:sender { NXSelPt selStart, selEnd; [self getSel:&selStart :&selEnd]; if (selEnd.cp < inputPosition || selStart.cp < inputPosition) return self; else return [super cut:sender]; } - paste:sender { int size = [self textLength]; NXSelPt selStart, selEnd; [self getSel:&selStart :&selEnd]; if (selEnd.cp < inputPosition || selStart.cp < inputPosition) [self setSel:size :size]; return [super paste:sender]; } - mouseDown: (NXEvent *) theEvent { BOOL doubleClick; doubleClick = (theEvent->data.mouse.click == 2); [super mouseDown:theEvent]; if (theEvent->data.mouse.click == 2) { char theChar; int i; NXSelPt selStart, selEnd; [self getSel:&selStart :&selEnd]; [self getSubstring:&theChar start:selStart.cp length:1]; if (theChar == OPEN_PAREN) { i = findMatchingCloseParen(self,theChar,CLOSE_PAREN,selEnd.cp+1); if (i >= 0) [self setSel:selStart.cp :i]; } else if (theChar == CLOSE_PAREN) { i = findMatchingOpenParen(self,theChar,OPEN_PAREN,selEnd.cp-1); if (i >= 0) [self setSel:i :selEnd.cp]; } } return self; } - clearAll:sender { [self setSel:0 :[self textLength]]; [super clear:self]; inputPosition = 0; return self; } - inputLisp:(const char *)theString { return [theLisp inputLisp:theString]; } - interruptLisp { return [theLisp interruptLisp]; } @end
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.