1 /* edit - Macintosh editing functions */
2 /* XLISP-STAT 2.1 Copyright (c) 1990, by Luke Tierney */
3 /* Additions to Xlisp 2.1, Copyright (c) 1989 by David Michael Betz */
4 /* You may give out copies of this software; for conditions see the */
5 /* file COPYING included with this distribution. */
10 #include "TransEdit1.h"
12 # define MIN(x,y) (((x) < (y)) ? (x) : (y))
14 # define BUFMAX STRMAX
15 # define MAXCOUNT 10000L
16 # define KEEPCOUNT 5000L
18 extern WindowPtr ttyWind
;
19 extern LVAL s_true
, sk_remove
, s_input_stream
, s_input_enabled
;
20 static char ttybuf
[BUFMAX
];
21 static int ttybufcount
= 0, ttyfixcount
= 0;
25 # define shiftKey 0x0200
28 /* forward declarations */
29 LOCAL
int paste_stream(WindowPtr w
, LVAL stream
, int input
);
30 LOCAL VOID
flush_window(WindowPtr w
, long byteCount
);
31 LOCAL VOID
adjust_fixed_count(WindowPtr w
);
32 LOCAL
int input_enabled(WindowPtr w
);
33 LOCAL VOID
tty_return(void);
34 LOCAL
int GetInputLine(void);
35 LOCAL
int check_parens(unsigned char *s
, int n
);
36 LOCAL VOID
pardelay(void);
37 LOCAL VOID
do_tab(TEHandle teEdit
, int start
);
38 LOCAL VOID
fix_blanks(TEHandle teEdit
, int nblanks
, int curline
);
39 LOCAL
int at_text_end(TEHandle teEdit
);
40 LOCAL
int last_is_return(TEHandle teEdit
);
41 LOCAL
int num_to_skip(unsigned char *s
, int n
);
42 LOCAL
int is_special(unsigned char *s
, int n
);
43 LOCAL
pascal VOID
close_listener(void);
44 LOCAL
pascal VOID
tty_key(void);
45 LOCAL
pascal VOID
edit_idle(void);
46 LOCAL
pascal VOID
close_edit(void);
47 LOCAL VOID
AdjusrDisplay(WindowPtr w
);
49 LOCAL VOID
AdjustDisplay(WindowPtr w
)
53 EWindowAdjustDisplay (w
);
57 static LVAL
get_output_stream(WindowPtr w
)
59 LVAL object
= get_window_object(w
), s_output_stream
= xlenter("OUTPUT-STREAM");
62 if (objectp(object
)) lex_slot_value(object
, s_output_stream
, &stream
);
63 return(ustreamp(stream
) ? stream
:NIL
);
66 static LVAL
get_input_stream(void)
68 if (ttyWind
== nil
) return(NIL
);
69 else return(getvalue(s_input_stream
));
72 static VOID
flush_output_stream(WindowPtr w
)
74 LVAL stream
= get_output_stream(w
);
76 TEHandle te
= GetEWindowTE(w
);
78 if (w
== ttyWind
) TtyFlush();
79 if (stream
!= NIL
&& (ch
= xlgetc(stream
)) != EOF
) {
81 paste_stream(w
, stream
, FALSE
);
82 if ((w
== ttyWind
|| ttyWind
== nil
) && (*te
)->teLength
> MAXCOUNT
)
83 flush_window(w
, (*te
)->teLength
- KEEPCOUNT
);
84 adjust_fixed_count(w
);
88 LOCAL VOID
flush_window(WindowPtr w
, long byteCount
)
90 TEHandle te
= GetEWindowTE(w
);
92 TESetSelect (0L, byteCount
, te
); /* select text */
93 TEDelete (te
); /* clobber it */
94 TESetSelect((*te
)->teLength
, (*te
)->teLength
, te
);
95 adjust_fixed_count(w
);
99 static VOID
InsertText(char *buf
, long count
, TEHandle te
)
101 if ((*te
)->teLength
+ count
> 32000) xlfail("Buffer is too big");
102 else TEInsert(buf
, count
, te
);
105 static get_fixed_count(WindowPtr w
, int *count
)
107 if (w
== ttyWind
|| ttyWind
== nil
) {
108 if (count
!= nil
) *count
= ttyfixcount
;
114 static VOID
set_fixed_count(WindowPtr w
, int count
)
116 if (w
== ttyWind
|| ttyWind
== nil
) ttyfixcount
= count
;
119 VOID
adjust_insert(WindowPtr w
)
122 TEHandle te
= GetEWindowTE(w
);
124 flush_output_stream(w
);
125 if (get_fixed_count(w
, &count
) && (*te
)->selStart
< count
) {
126 TESetSelect((*te
)->teLength
, (*te
)->teLength
, te
);
130 LOCAL VOID
adjust_fixed_count(WindowPtr w
)
133 TEHandle te
= GetEWindowTE(w
);
135 if (get_fixed_count(w
, &count
)) {
136 set_fixed_count(w
, (*te
)->teLength
);
138 if (get_fixed_count(w
, &count
) && (*te
)->selStart
< count
) {
139 TESetSelect((*te
)->teLength
, (*te
)->teLength
, te
);
143 LOCAL
int paste_stream(WindowPtr w
, LVAL stream
, int input
)
147 if (! IsEWindow(w
) || stream
== NIL
) return(FALSE
);
149 while((ch
= xlgetc(stream
)) != EOF
) {
150 if (ch
== '\n') ch
= RETURNCHAR
;
152 if (buffpos
> BUFMAX
) {
153 InsertText(buf
, (long) buffpos
, GetEWindowTE(w
));
157 InsertText(buf
, (long) buffpos
, GetEWindowTE(w
));
159 if (input
&& w
== ttyWind
) return_action(GetEWindowTE(w
));
160 if (! input_enabled(w
) || w
== ttyWind
) SetEWindowDirty(w
, FALSE
);
164 static paste_string(WindowPtr w
, char *str
, int input
)
169 if (! IsEWindow(w
) || str
== nil
) return(FALSE
);
174 if (ch
== '\n') ch
= RETURNCHAR
;
176 if (buffpos
> BUFMAX
) {
177 InsertText(buf
, (long) buffpos
, GetEWindowTE(w
));
181 InsertText(buf
, (long) buffpos
, GetEWindowTE(w
));
183 if (input
&& w
== ttyWind
) return_action(GetEWindowTE(w
));
186 if (input
&& w
== ttyWind
) return_action(GetEWindowTE(w
));
187 if (! input_enabled(w
) || w
== ttyWind
) SetEWindowDirty(w
, FALSE
);
191 LOCAL
int input_enabled(WindowPtr w
)
193 LVAL enabled
= s_true
, object
= get_window_object(w
);
195 if (w
== ttyWind
) return(TRUE
);
196 if (objectp(object
)) lex_slot_value(object
, s_input_enabled
, &enabled
);
197 return((enabled
!= NIL
));
200 static VOID
tty_enter(void)
202 TEHandle te
= GetEWindowTE(ttyWind
);
204 if (te
== nil
) return;
205 adjust_insert(ttyWind
);
206 if ((*te
)->selStart
< (*te
)->teLength
)
207 TESetSelect((*te
)->teLength
, (*te
)->teLength
, te
);
211 LOCAL VOID
tty_return(void)
213 TEHandle te
= GetEWindowTE(ttyWind
);
215 if (te
== nil
) return;
216 adjust_insert(ttyWind
);
217 TEKey (RETURNCHAR
, te
);
218 AdjustDisplay(ttyWind
);
224 if (c
== '\n') c
= RETURNCHAR
;
225 ttybuf
[ttybufcount
++] = c
;
226 if (c
== RETURNCHAR
|| ttybufcount
>= (BUFMAX
- 1)) TtyFlush();
229 VOID
TtyPrint(char *s
)
231 while (strlen(s
) > 0) TtyPutc(*s
++);
237 TEHandle te
= GetEWindowTE(ttyWind
);
240 if (ttybufcount
> 0) {
241 if (get_fixed_count(ttyWind
, &count
) && (*te
)->selStart
< count
) {
242 TESetSelect((*te
)->teLength
, (*te
)->teLength
, te
);
244 adjust_fixed_count(ttyWind
);
245 InsertText(ttybuf
, (long) ttybufcount
, GetEWindowTE(ttyWind
));
247 if ((*te
)->teLength
> MAXCOUNT
) {
248 flush_window(ttyWind
, (*te
)->teLength
- KEEPCOUNT
);
250 TESetSelect((*te
)->teLength
, (*te
)->teLength
, te
);
251 adjust_fixed_count(ttyWind
);
252 AdjustDisplay(ttyWind
);
256 LOCAL
int GetInputLine(void)
258 int i
, has_fixed
, count
;
259 unsigned char **mytext
;
260 TEHandle te
= GetEWindowTE(ttyWind
);
261 LVAL stream
= get_input_stream();
263 mytext
= (unsigned char **)TEGetText(te
);
264 has_fixed
= get_fixed_count(ttyWind
, &count
);
265 if (! has_fixed
) count
= 0;
266 if (ustreamp(stream
) && check_parens(*mytext
+ count
, (*te
)->teLength
- count
)) {
267 for (i
= count
; i
< (*te
)->teLength
; i
++)
268 xlputc(stream
, (*mytext
)[i
]);
269 if (has_fixed
) adjust_fixed_count(ttyWind
);
275 LOCAL
int check_parens(unsigned char *s
, int n
)
277 int parcount
= 0, inquotes
= FALSE
, incomment
= FALSE
;
283 case '"': inquotes
= ! inquotes
; break;
284 case ';': if (! inquotes
) incomment
= TRUE
; break;
286 case '\n': incomment
= FALSE
; break;
287 case '(': if (! inquotes
&& ! incomment
) parcount
++; break;
288 case ')': if (! inquotes
&& ! incomment
) parcount
--; break;
291 return (parcount
<= 0);
294 LVAL
xsedit_window_paste_stream(void)
299 w
= (WindowPtr
) get_edit_window_address(xlgetarg());
300 stream
= xlgetfile(FALSE
);
304 return((paste_stream(w
, stream
, TRUE
)) ? s_true
: NIL
);
307 LVAL
xsedit_window_paste_string(void)
312 w
= (WindowPtr
) get_edit_window_address(xlgetarg());
313 string
= xlgastring();
317 return((paste_string(w
, getstring(string
), TRUE
)) ? s_true
: NIL
);
320 LVAL
xsedit_window_flush_window(void)
325 w
= (WindowPtr
) get_edit_window_address(xlgetarg());
326 count
= (moreargs()) ? getfixnum(xlgafixnum()) : 32767;
329 flush_window(w
, count
);
333 static flash_matching_paren(TEHandle teEdit
, int start
)
335 int parcount
= 0, inquotes
= FALSE
, sel
, par
;
336 unsigned char ch
, *s
;
338 sel
= (*teEdit
)->selStart
;
339 s
= *((unsigned char **)TEGetText(teEdit
)) + sel
- 1;
345 case '"': inquotes
= ! inquotes
; break;
346 case '(': if (! inquotes
) parcount
--; break;
347 case ')': if (! inquotes
) parcount
++; break;
349 } while (par
> start
&& parcount
> 0);
350 if (ch
== '(' && parcount
== 0) {
351 TESetSelect(par
, par
+ 1, teEdit
);
353 TESetSelect(sel
, sel
, teEdit
);
355 return (parcount
<= 0);
358 LOCAL VOID
pardelay(void)
360 unsigned long t
= 5, f
;
364 LOCAL VOID
do_tab(TEHandle teEdit
, int start
)
366 int sel
, curline
, lastline
, pos
, nblanks
, inquote
, parcount
;
367 unsigned char *s
, ch
;
369 /* for an edit window get rid of the inserted tab
370 if (teEdit != TTY.teEdit) TEKey('\b', teEdit);*/
372 sel
= (*teEdit
)->selStart
;
373 s
= *((unsigned char **)TEGetText(teEdit
));
375 /* find beginning of the line */
377 while (curline
> start
&& s
[curline
- 1] != RETURNCHAR
) curline
--;
378 if (curline
== start
) return;
380 /* find unmatched paren */
384 while (parcount
>= 0 && --pos
>= start
) {
387 case ')': if (! inquote
) parcount
++; break;
388 case '(': if (! inquote
) parcount
--; break;
389 case '"': inquote
= ! inquote
; break;
392 if (parcount
== 0) return;
394 /* find beginning of the previous line */
396 while (lastline
> 0 && s
[lastline
- 1] != RETURNCHAR
) lastline
--;
398 /* skip forward an s-expression or to first non blank */
399 pos
+= num_to_skip(s
+ pos
, curline
- pos
);
401 if (pos
> curline
) pos
= curline
;
402 nblanks
= pos
- lastline
;
404 /* adjust for the number of blanks already present, replace tabs by blanks */
406 pos
< (*teEdit
)->teLength
&& (s
[pos
] == ' ' || s
[pos
] == '\t');
408 if (s
[pos
] == '\t') s
[pos
] = ' ';
410 /* insert or delete the appropriate number of blanks */
411 if (nblanks
== 0) return;
414 if (pos
> (*teEdit
)->teLength
) pos
= (*teEdit
)->teLength
;
415 TESetSelect(pos
, pos
, teEdit
);
416 fix_blanks(teEdit
, nblanks
, curline
);
417 if (sel
> (*teEdit
)->teLength
) sel
= (*teEdit
)->teLength
;
418 TESetSelect(sel
, sel
, teEdit
);
421 LOCAL VOID
fix_blanks(TEHandle teEdit
, int nblanks
, int curline
)
426 for (i
= 0; i
< nblanks
; i
++) buf
[i
] = ' ';
427 TESetSelect(curline
, curline
, teEdit
);
428 TEInsert(buf
, (long) nblanks
, teEdit
);
431 TESetSelect(curline
, curline
- nblanks
, teEdit
);
436 LOCAL
int at_text_end(TEHandle teEdit
)
438 int i
, result
= TRUE
;
439 unsigned char *s
= *((unsigned char **)TEGetText(teEdit
));
441 for (i
= (*teEdit
)->selStart
; result
&& i
< (*teEdit
)->teLength
; i
++)
442 if (! isspace(s
[i
])) result
= FALSE
;
446 LOCAL
int last_is_return(TEHandle teEdit
)
449 unsigned char *s
= *((unsigned char **)TEGetText(teEdit
));
451 for (i
= (*teEdit
)->selStart
- 1; i
>= 0 && isspace(s
[i
]); i
--)
453 i
= MIN((*teEdit
)->selStart
- 1, i
+ 1);
454 return(s
[i
] == RETURNCHAR
);
457 VOID
return_action(TEHandle te
)
459 if (at_text_end(te
) && last_is_return(te
) && GetInputLine()) {
460 flush_output_stream(ttyWind
);
461 getttyline(get_input_stream());
465 LOCAL
int num_to_skip(unsigned char *s
, int n
)
467 unsigned char str
[4];
473 else if (*s
== '(') {
478 while (n
> 0 && (*s
== ' ' || *s
== '\t')) { s
++; n
--; pos
++; }
480 /* check for end of line or list or lisp comment*/
481 if (n
> 0 && *s
!= RETURNCHAR
&& *s
!= ';' && *s
!= '(') {
483 /* check for special symbols */
484 for (i
= 0; i
< 3 && i
< n
; i
++)
485 str
[i
] = toupper(s
[i
]);
487 if (is_special(s
, n
) /* strcmp(str, "DEF") == 0 || strcmp(str, "LET") == 0
488 || strcmp(str, "FLE") == 0 */ )
492 /* skip over the s-expression */
494 while (n
> 0 && *s
!= ' ' && *s
!= '\t' && *s
!= RETURNCHAR
)
497 /* check for another s expression */
498 for (i
= 0; n
> 0 && (*s
== ' ' || *s
== '\t'); s
++, n
--, i
++) ;
499 if (n
== 0 || *s
== RETURNCHAR
)
500 pos
= (oldpos
== pos
) ? oldpos
+ 1 : oldpos
;
507 /* skip over any blanks */
508 for (i
= 0; n
> 0 && (*s
== ' ' || *s
== '\t'); s
++, n
--, i
++) ;
509 if (n
> 0 && *s
!= RETURNCHAR
) pos
+= i
;
514 LOCAL
int is_special(unsigned char *s
, int n
)
519 for (i
= 0; i
< n
&& i
< 9; i
++) str
[i
] = toupper(s
[i
]);
522 if (n
>= 5 && strncmp(str
, "DEFUN", 5) == 0) return(TRUE
);
523 if (n
>= 8 && strncmp(str
, "DEFMACRO", 8) == 0) return(TRUE
);
524 if (n
>= 7 && strncmp(str
, "DEFMETH", 7) == 0) return(TRUE
);
525 if (n
>= 8 && strncmp(str
, "DEFPROTO", 8) == 0) return(TRUE
);
526 if (n
>= 3 && strncmp(str
, "LET", 3) == 0) return(TRUE
);
527 if (n
>= 4 && strncmp(str
, "FLET", 4) == 0) return(TRUE
);
528 if (n
>= 4 && strncmp(str
, "COND", 4) == 0) return(TRUE
);
529 if (n
>= 4 && strncmp(str
, "CASE", 4) == 0) return(TRUE
);
530 if (n
>= 6 && strncmp(str
, "LABELS", 6) == 0) return(TRUE
);
531 if (n
>= 6 && strncmp(str
, "LAMBDA", 6) == 0) return(TRUE
);
535 LOCAL
pascal VOID
close_listener(void)
540 Boolean
edit_key_filter(WindowPtr editWind
, int c
)
543 int count
, has_fixed
;
545 editTE
= GetEWindowTE(editWind
);
546 if (editWind
== nil
) return(TRUE
);
547 has_fixed
= get_fixed_count(editWind
, &count
);
548 if (! has_fixed
) count
= 0;
550 if (! input_enabled(editWind
)) return (TRUE
);
555 case '\037': return(FALSE
); /* arrow keys */
556 case '\t': adjust_insert(editWind
); do_tab(editTE
, count
); return(TRUE
);
558 adjust_insert(editWind
);
559 if ((*editTE
)->selStart
> count
560 || ((*editTE
)->selStart
== count
561 && (*editTE
)->selStart
< (*editTE
)->selEnd
)) {
565 case enter
: if (editWind
== ttyWind
&& ttyWind
!= nil
) tty_enter(); return(TRUE
);
567 if (editWind
== ttyWind
) {
572 adjust_insert(editWind
);
576 adjust_insert(editWind
);
578 flash_matching_paren(editTE
, count
);
581 adjust_insert(editWind
);
586 LOCAL
pascal VOID
tty_key(void)
588 SetEWindowDirty (ttyWind
, FALSE
);
591 LOCAL
pascal VOID
edit_idle (void)
597 flush_output_stream(port
);
598 if (! input_enabled(port
)) {
599 editTE
= GetEWindowTE(port
);
600 if (editTE
!= nil
) TEDeactivate (editTE
);
604 LOCAL
pascal VOID
close_edit(void)
610 object
= get_window_object(w
);
611 if (objectp(object
)) send_message(object
, sk_remove
);
614 VOID
make_listener_window(Rect r
)
616 ttyWind
= NewEWindow (&r
, "\pXLISP-STAT", TRUE
, (WindowPtr
) -1L, TRUE
, 0L, FALSE
);
617 SetEWindowProcs(ttyWind
, tty_key
, nil
, close_listener
, edit_idle
);
618 SetEWindowStyle (ttyWind
,
619 listenerFontNum
, listenerFontSize
, listenerFontStyle
,
623 VOID
set_edit_window_procs(WindowPtr w
)
626 SetEWindowProcs(w
, nil
, nil
, close_edit
, edit_idle
);
630 LVAL
xsedit_window_update(void) { return(NIL
); }
631 LVAL
xsedit_window_activate(void) { return(NIL
); }