/* edit - Macintosh editing functions */
/* XLISP-STAT 2.1 Copyright (c) 1990, by Luke Tierney */
/* Additions to Xlisp 2.1, Copyright (c) 1989 by David Michael Betz */
/* You may give out copies of this software; for conditions see the */
/* file COPYING included with this distribution. */
#include "xlisp.h"
#include "xlstat.h"
#include "xlgraph.h"
#include "TransEdit1.h"
# define MIN(x,y) (((x) < (y)) ? (x) : (y))
# define BUFMAX STRMAX
# define MAXCOUNT 10000L
# define KEEPCOUNT 5000L
extern WindowPtr ttyWind;
extern LVAL s_true, sk_remove, s_input_stream, s_input_enabled;
static char ttybuf[BUFMAX];
static int ttybufcount = 0, ttyfixcount = 0;
# define enter 3
# ifndef shiftKey
# define shiftKey 0x0200
# endif
/* forward declarations */
LOCAL int paste_stream(WindowPtr w, LVAL stream, int input);
LOCAL VOID flush_window(WindowPtr w, long byteCount);
LOCAL VOID adjust_fixed_count(WindowPtr w);
LOCAL int input_enabled(WindowPtr w);
LOCAL VOID tty_return(void);
LOCAL int GetInputLine(void);
LOCAL int check_parens(unsigned char *s, int n);
LOCAL VOID pardelay(void);
LOCAL VOID do_tab(TEHandle teEdit, int start);
LOCAL VOID fix_blanks(TEHandle teEdit, int nblanks, int curline);
LOCAL int at_text_end(TEHandle teEdit);
LOCAL int last_is_return(TEHandle teEdit);
LOCAL int num_to_skip(unsigned char *s, int n);
LOCAL int is_special(unsigned char *s, int n);
LOCAL pascal VOID close_listener(void);
LOCAL pascal VOID tty_key(void);
LOCAL pascal VOID edit_idle(void);
LOCAL pascal VOID close_edit(void);
LOCAL VOID AdjusrDisplay(WindowPtr w);
LOCAL VOID AdjustDisplay(WindowPtr w)
{
GrafPtr port;
GetPort(&port);
EWindowAdjustDisplay (w);
SetPort(port);
}
static LVAL get_output_stream(WindowPtr w)
{
LVAL object = get_window_object(w), s_output_stream = xlenter("OUTPUT-STREAM");
LVAL stream = NIL;
if (objectp(object)) lex_slot_value(object, s_output_stream, &stream);
return(ustreamp(stream) ? stream :NIL);
}
static LVAL get_input_stream(void)
{
if (ttyWind == nil) return(NIL);
else return(getvalue(s_input_stream));
}
static VOID flush_output_stream(WindowPtr w)
{
LVAL stream = get_output_stream(w);
int ch;
TEHandle te = GetEWindowTE(w);
if (w == ttyWind) TtyFlush();
if (stream != NIL && (ch = xlgetc(stream)) != EOF) {
xlungetc(stream, ch);
paste_stream(w, stream, FALSE);
if ((w == ttyWind || ttyWind == nil) && (*te)->teLength > MAXCOUNT)
flush_window(w, (*te)->teLength - KEEPCOUNT);
adjust_fixed_count(w);
}
}
LOCAL VOID flush_window(WindowPtr w, long byteCount)
{
TEHandle te = GetEWindowTE(w);
TESetSelect (0L, byteCount, te); /* select text */
TEDelete (te); /* clobber it */
TESetSelect((*te)->teLength, (*te)->teLength, te);
adjust_fixed_count(w);
AdjustDisplay(w);
}
static VOID InsertText(char *buf, long count, TEHandle te)
{
if ((*te)->teLength + count > 32000) xlfail("Buffer is too big");
else TEInsert(buf, count, te);
}
static get_fixed_count(WindowPtr w, int *count)
{
if (w == ttyWind || ttyWind == nil) {
if (count != nil) *count = ttyfixcount;
return(TRUE);
}
else return(FALSE);
}
static VOID set_fixed_count(WindowPtr w, int count)
{
if (w == ttyWind || ttyWind == nil) ttyfixcount = count;
}
VOID adjust_insert(WindowPtr w)
{
int count;
TEHandle te = GetEWindowTE(w);
flush_output_stream(w);
if (get_fixed_count(w, &count) && (*te)->selStart < count) {
TESetSelect((*te)->teLength, (*te)->teLength, te);
}
}
LOCAL VOID adjust_fixed_count(WindowPtr w)
{
int count;
TEHandle te = GetEWindowTE(w);
if (get_fixed_count(w, &count)) {
set_fixed_count(w, (*te)->teLength);
}
if (get_fixed_count(w, &count) && (*te)->selStart < count) {
TESetSelect((*te)->teLength, (*te)->teLength, te);
}
}
LOCAL int paste_stream(WindowPtr w, LVAL stream, int input)
{
int buffpos = 0, ch;
if (! IsEWindow(w) || stream == NIL) return(FALSE);
while((ch = xlgetc(stream)) != EOF) {
if (ch == '\n') ch = RETURNCHAR;
buf[buffpos++] = ch;
if (buffpos > BUFMAX) {
InsertText(buf, (long) buffpos, GetEWindowTE(w));
buffpos = 0;
}
}
InsertText(buf, (long) buffpos, GetEWindowTE(w));
AdjustDisplay(w);
if (input && w == ttyWind) return_action(GetEWindowTE(w));
if (! input_enabled(w) || w == ttyWind) SetEWindowDirty(w, FALSE);
return(TRUE);
}
static paste_string(WindowPtr w, char *str, int input)
{
int buffpos = 0, ch;
long len;
if (! IsEWindow(w) || str == nil) return(FALSE);
len = strlen(str);
while(len -- > 0) {
ch = *str++;
if (ch == '\n') ch = RETURNCHAR;
buf[buffpos++] = ch;
if (buffpos > BUFMAX) {
InsertText(buf, (long) buffpos, GetEWindowTE(w));
buffpos = 0;
}
}
InsertText(buf, (long) buffpos, GetEWindowTE(w));
AdjustDisplay(w);
if (input && w == ttyWind) return_action(GetEWindowTE(w));
return(TRUE);
AdjustDisplay(w);
if (input && w == ttyWind) return_action(GetEWindowTE(w));
if (! input_enabled(w) || w == ttyWind) SetEWindowDirty(w, FALSE);
return(TRUE);
}
LOCAL int input_enabled(WindowPtr w)
{
LVAL enabled = s_true, object = get_window_object(w);
if (w == ttyWind) return(TRUE);
if (objectp(object)) lex_slot_value(object, s_input_enabled, &enabled);
return((enabled != NIL));
}
static VOID tty_enter(void)
{
TEHandle te = GetEWindowTE(ttyWind);
if (te == nil) return;
adjust_insert(ttyWind);
if ((*te)->selStart < (*te)->teLength)
TESetSelect((*te)->teLength, (*te)->teLength, te);
else tty_return();
}
LOCAL VOID tty_return(void)
{
TEHandle te = GetEWindowTE(ttyWind);
if (te == nil) return;
adjust_insert(ttyWind);
TEKey (RETURNCHAR, te);
AdjustDisplay(ttyWind);
return_action(te);
}
VOID TtyPutc(int c)
{
if (c == '\n') c = RETURNCHAR;
ttybuf[ttybufcount++] = c;
if (c == RETURNCHAR || ttybufcount >= (BUFMAX - 1)) TtyFlush();
}
VOID TtyPrint(char *s)
{
while (strlen(s) > 0) TtyPutc(*s++);
TtyFlush();
}
VOID TtyFlush(void)
{
TEHandle te = GetEWindowTE(ttyWind);
int count;
if (ttybufcount > 0) {
if (get_fixed_count(ttyWind, &count) && (*te)->selStart < count) {
TESetSelect((*te)->teLength, (*te)->teLength, te);
}
adjust_fixed_count(ttyWind);
InsertText(ttybuf, (long) ttybufcount, GetEWindowTE(ttyWind));
ttybufcount = 0;
if ((*te)->teLength > MAXCOUNT) {
flush_window(ttyWind, (*te)->teLength - KEEPCOUNT);
}
TESetSelect((*te)->teLength, (*te)->teLength, te);
adjust_fixed_count(ttyWind);
AdjustDisplay(ttyWind);
}
}
LOCAL int GetInputLine(void)
{
int i, has_fixed, count;
unsigned char **mytext;
TEHandle te = GetEWindowTE(ttyWind);
LVAL stream = get_input_stream();
mytext = (unsigned char **)TEGetText(te);
has_fixed = get_fixed_count(ttyWind, &count);
if (! has_fixed) count = 0;
if (ustreamp(stream) && check_parens(*mytext + count, (*te)->teLength - count)) {
for (i = count; i < (*te)->teLength; i++)
xlputc(stream, (*mytext)[i]);
if (has_fixed) adjust_fixed_count(ttyWind);
return(TRUE);
}
else return(FALSE);
}
LOCAL int check_parens(unsigned char *s, int n)
{
int parcount = 0, inquotes = FALSE, incomment = FALSE;
char ch;
while (n-- > 0) {
ch = *s++;
switch (ch) {
case '"': inquotes = ! inquotes; break;
case ';': if (! inquotes) incomment = TRUE; break;
case '\r':
case '\n': incomment = FALSE; break;
case '(': if (! inquotes && ! incomment) parcount++; break;
case ')': if (! inquotes && ! incomment) parcount--; break;
}
}
return (parcount <= 0);
}
LVAL xsedit_window_paste_stream(void)
{
LVAL stream;
WindowPtr w;
w = (WindowPtr) get_edit_window_address(xlgetarg());
stream = xlgetfile(FALSE);
xllastarg();
adjust_insert(w);
return((paste_stream(w, stream, TRUE)) ? s_true : NIL);
}
LVAL xsedit_window_paste_string(void)
{
LVAL string;
WindowPtr w;
w = (WindowPtr) get_edit_window_address(xlgetarg());
string = xlgastring();
xllastarg();
adjust_insert(w);
return((paste_string(w, getstring(string), TRUE)) ? s_true : NIL);
}
LVAL xsedit_window_flush_window(void)
{
WindowPtr w;
long count;
w = (WindowPtr) get_edit_window_address(xlgetarg());
count = (moreargs()) ? getfixnum(xlgafixnum()) : 32767;
xllastarg();
flush_window(w, count);
return(NIL);
}
static flash_matching_paren(TEHandle teEdit, int start)
{
int parcount = 0, inquotes = FALSE, sel, par;
unsigned char ch, *s;
sel = (*teEdit)->selStart;
s = *((unsigned char **)TEGetText(teEdit)) + sel - 1;
par = sel;
do {
par--;
ch = *s--;
switch (ch) {
case '"': inquotes = ! inquotes; break;
case '(': if (! inquotes) parcount--; break;
case ')': if (! inquotes) parcount++; break;
}
} while (par > start && parcount > 0);
if (ch == '(' && parcount == 0) {
TESetSelect(par, par + 1, teEdit);
pardelay();
TESetSelect(sel, sel, teEdit);
}
return (parcount <= 0);
}
LOCAL VOID pardelay(void)
{
unsigned long t = 5, f;
Delay(t, &f);
}
LOCAL VOID do_tab(TEHandle teEdit, int start)
{
int sel, curline, lastline, pos, nblanks, inquote, parcount;
unsigned char *s, ch;
/* for an edit window get rid of the inserted tab
if (teEdit != TTY.teEdit) TEKey('\b', teEdit);*/
sel = (*teEdit)->selStart;
s = *((unsigned char **)TEGetText(teEdit));
/* find beginning of the line */
curline = sel;
while (curline > start && s[curline - 1] != RETURNCHAR) curline--;
if (curline == start) return;
/* find unmatched paren */
parcount = 0;
inquote = FALSE;
pos = curline;
while (parcount >= 0 && --pos >= start) {
ch = s[pos];
switch (ch) {
case ')': if (! inquote) parcount++; break;
case '(': if (! inquote) parcount--; break;
case '"': inquote = ! inquote; break;
}
}
if (parcount == 0) return;
/* find beginning of the previous line */
lastline = pos;
while (lastline > 0 && s[lastline - 1] != RETURNCHAR) lastline--;
/* skip forward an s-expression or to first non blank */
pos += num_to_skip(s + pos, curline - pos);
if (pos > curline) pos = curline;
nblanks = pos - lastline;
/* adjust for the number of blanks already present, replace tabs by blanks */
for (pos = curline;
pos < (*teEdit)->teLength && (s[pos] == ' ' || s[pos] == '\t');
nblanks--, pos++)
if (s[pos] == '\t') s[pos] = ' ';
/* insert or delete the appropriate number of blanks */
if (nblanks == 0) return;
sel += nblanks;
if (pos > (*teEdit)->teLength) pos = (*teEdit)->teLength;
TESetSelect(pos, pos, teEdit);
fix_blanks(teEdit, nblanks, curline);
if (sel > (*teEdit)->teLength) sel = (*teEdit)->teLength;
TESetSelect(sel, sel, teEdit);
}
LOCAL VOID fix_blanks(TEHandle teEdit, int nblanks, int curline)
{
int i;
if (nblanks > 0) {
for (i = 0; i < nblanks; i++) buf[i] = ' ';
TESetSelect(curline, curline, teEdit);
TEInsert(buf, (long) nblanks, teEdit);
}
else {
TESetSelect(curline, curline - nblanks, teEdit);
TECut(teEdit);
}
}
LOCAL int at_text_end(TEHandle teEdit)
{
int i, result = TRUE;
unsigned char *s = *((unsigned char **)TEGetText(teEdit));
for (i = (*teEdit)->selStart; result && i < (*teEdit)->teLength; i++)
if (! isspace(s[i])) result = FALSE;
return(result);
}
LOCAL int last_is_return(TEHandle teEdit)
{
int i;
unsigned char *s = *((unsigned char **)TEGetText(teEdit));
for (i = (*teEdit)->selStart - 1; i >= 0 && isspace(s[i]); i--)
;
i = MIN((*teEdit)->selStart - 1, i + 1);
return(s[i] == RETURNCHAR);
}
VOID return_action(TEHandle te)
{
if (at_text_end(te) && last_is_return(te) && GetInputLine()) {
flush_output_stream(ttyWind);
getttyline(get_input_stream());
}
}
LOCAL int num_to_skip(unsigned char *s, int n)
{
unsigned char str[4];
int i, pos, oldpos;
pos = 0;
if (n <= 0) pos = 0;
else if (*s == '(') {
s++; n--; pos = 1;
/* skip blanks */
while (n > 0 && (*s == ' ' || *s == '\t')) { s++; n--; pos++; }
/* check for end of line or list or lisp comment*/
if (n > 0 && *s != RETURNCHAR && *s != ';' && *s != '(') {
/* check for special symbols */
for (i = 0; i < 3 && i < n; i++)
str[i] = toupper(s[i]);
str[i] = '\0';
if (is_special(s, n) /* strcmp(str, "DEF") == 0 || strcmp(str, "LET") == 0
|| strcmp(str, "FLE") == 0 */ )
pos = 2;
else {
/* skip over the s-expression */
oldpos = pos;
while (n > 0 && *s != ' ' && *s != '\t' && *s != RETURNCHAR)
{ s++; n--; pos++; }
/* check for another s expression */
for (i = 0; n > 0 && (*s == ' ' || *s == '\t'); s++, n--, i++) ;
if (n == 0 || *s == RETURNCHAR)
pos = (oldpos == pos) ? oldpos + 1 : oldpos;
else pos += i;
}
}
}
else {
/* skip over any blanks */
for (i = 0; n > 0 && (*s == ' ' || *s == '\t'); s++, n--, i++) ;
if (n > 0 && *s != RETURNCHAR) pos += i;
}
return(pos);
}
LOCAL int is_special(unsigned char *s, int n)
{
char str[10];
int i;
for (i = 0; i < n && i < 9; i++) str[i] = toupper(s[i]);
str[i] = '\0';
if (n >= 5 && strncmp(str, "DEFUN", 5) == 0) return(TRUE);
if (n >= 8 && strncmp(str, "DEFMACRO", 8) == 0) return(TRUE);
if (n >= 7 && strncmp(str, "DEFMETH", 7) == 0) return(TRUE);
if (n >= 8 && strncmp(str, "DEFPROTO", 8) == 0) return(TRUE);
if (n >= 3 && strncmp(str, "LET", 3) == 0) return(TRUE);
if (n >= 4 && strncmp(str, "FLET", 4) == 0) return(TRUE);
if (n >= 4 && strncmp(str, "COND", 4) == 0) return(TRUE);
if (n >= 4 && strncmp(str, "CASE", 4) == 0) return(TRUE);
if (n >= 6 && strncmp(str, "LABELS", 6) == 0) return(TRUE);
if (n >= 6 && strncmp(str, "LAMBDA", 6) == 0) return(TRUE);
return(FALSE);
}
LOCAL pascal VOID close_listener(void)
{
HideWindow(ttyWind);
}
Boolean edit_key_filter(WindowPtr editWind, int c)
{
TEHandle editTE;
int count, has_fixed;
editTE = GetEWindowTE(editWind);
if (editWind == nil) return(TRUE);
has_fixed = get_fixed_count(editWind, &count);
if (! has_fixed) count = 0;
if (! input_enabled(editWind)) return (TRUE);
switch (c) {
case '\034':
case '\035':
case '\036':
case '\037': return(FALSE); /* arrow keys */
case '\t': adjust_insert(editWind); do_tab(editTE, count); return(TRUE);
case '\b':
adjust_insert(editWind);
if ((*editTE)->selStart > count
|| ((*editTE)->selStart == count
&& (*editTE)->selStart < (*editTE)->selEnd)) {
return(FALSE);
}
else return(TRUE);
case enter: if (editWind == ttyWind && ttyWind != nil) tty_enter(); return(TRUE);
case RETURNCHAR:
if (editWind == ttyWind) {
tty_return();
return(TRUE);
}
else {
adjust_insert(editWind);
return(FALSE);
}
case ')':
adjust_insert(editWind);
TEKey(c, editTE);
flash_matching_paren(editTE, count);
return(TRUE);
default:
adjust_insert(editWind);
return(FALSE);
}
}
LOCAL pascal VOID tty_key(void)
{
SetEWindowDirty (ttyWind, FALSE);
}
LOCAL pascal VOID edit_idle (void)
{
GrafPtr port;
TEHandle editTE;
GetPort(&port);
flush_output_stream(port);
if (! input_enabled(port)) {
editTE = GetEWindowTE(port);
if (editTE != nil) TEDeactivate (editTE);
}
}
LOCAL pascal VOID close_edit(void)
{
GrafPtr w;
LVAL object;
GetPort(&w);
object = get_window_object(w);
if (objectp(object)) send_message(object, sk_remove);
}
VOID make_listener_window(Rect r)
{
ttyWind = NewEWindow (&r, "\pXLISP-STAT", TRUE, (WindowPtr) -1L, TRUE, 0L, FALSE);
SetEWindowProcs(ttyWind, tty_key, nil, close_listener, edit_idle);
SetEWindowStyle (ttyWind,
listenerFontNum, listenerFontSize, listenerFontStyle,
0, teJustLeft);
}
VOID set_edit_window_procs(WindowPtr w)
{
if (IsEWindow(w)) {
SetEWindowProcs(w, nil, nil, close_edit, edit_idle);
}
}
LVAL xsedit_window_update(void) { return(NIL); }
LVAL xsedit_window_activate(void) { return(NIL); }
syntax highlighted by Code2HTML, v. 0.9.1