xref: /illumos-gate/usr/src/cmd/vi/port/ex_vwind.c (revision 55fea89dcaa64928bed4327112404dcb3e07b79f)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright (c) 1989, 2010, Oracle and/or its affiliates. All rights reserved.
24  */
25 
26 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
27 /*	  All Rights Reserved  	*/
28 
29 
30 /* Copyright (c) 1981 Regents of the University of California */
31 
32 #include "ex.h"
33 #include "ex_tty.h"
34 #include "ex_vis.h"
35 
36 /*
37  * Routines to adjust the window, showing specified lines
38  * in certain positions on the screen, and scrolling in both
39  * directions.  Code here is very dependent on mode (open versus visual).
40  */
41 
42 /*
43  * Move in a nonlocal way to line addr.
44  * If it isn't on screen put it in specified context.
45  * New position for cursor is curs.
46  * Like most routines here, we vsave().
47  */
48 void
vmoveto(addr,curs,context)49 vmoveto(addr, curs, context)
50 	line *addr;
51 	unsigned char *curs;
52 	unsigned char context;
53 {
54 
55 	markit(addr);
56 	vsave();
57 	vjumpto(addr, curs, context);
58 }
59 
60 /*
61  * Vjumpto is like vmoveto, but doesn't mark previous
62  * context or save linebuf as current line.
63  */
64 void
vjumpto(line * addr,unsigned char * curs,unsigned char context)65 vjumpto(line *addr, unsigned char *curs, unsigned char context)
66 {
67 
68 	noteit(0);
69 	if (context != 0)
70 		vcontext(addr, context);
71 	else
72 		vshow(addr, NOLINE);
73 	noteit(1);
74 	vnline(curs);
75 }
76 
77 /*
78  * Go up or down cnt (negative is up) to new position curs.
79  */
80 void
vupdown(int cnt,unsigned char * curs)81 vupdown(int cnt, unsigned char *curs)
82 {
83 
84 	if (cnt > 0)
85 		vdown(cnt, 0, 0);
86 	else if (cnt < 0)
87 		vup(-cnt, 0, 0);
88 	if (vcnt == 0)
89 		vrepaint(curs);
90 	else
91 		vnline(curs);
92 }
93 
94 /*
95  * Go up cnt lines, afterwards preferring to be ind
96  * logical lines from the top of the screen.
97  * If scroll, then we MUST use a scroll.
98  * Otherwise clear and redraw if motion is far.
99  */
100 void
vup(int cnt,int ind,bool scroll)101 vup(int cnt, int ind, bool scroll)
102 {
103 	int i, tot;
104 
105 	if (dot == one) {
106 		(void) beep();
107 		return;
108 	}
109 	vsave();
110 	i = lineDOT() - 1;
111 	if (cnt > i) {
112 		ind -= cnt - i;
113 		if (ind < 0)
114 			ind = 0;
115 		cnt = i;
116 	}
117 	if (!scroll && cnt <= vcline) {
118 		vshow(dot - cnt, NOLINE);
119 		return;
120 	}
121 	cnt -= vcline, dot -= vcline, vcline = 0;
122 	if (hold & HOLDWIG)
123 		goto contxt;
124 	if (state == VISUAL && !insert_line && !scroll_reverse &&
125 	    cnt <= WTOP - ZERO && vfit(dot - cnt, cnt) <= WTOP - ZERO)
126 		goto okr;
127 	tot = WECHO - WTOP;
128 	if (state != VISUAL || (!insert_line && !scroll_reverse) || (!scroll && (cnt > tot || vfit(dot - cnt, cnt) > tot / 3 + 1))) {
129 		if (ind > basWLINES / 2)
130 			ind = basWLINES / 3;
131 contxt:
132 		vcontext(dot + ind - cnt, '.');
133 		return;
134 	}
135 okr:
136 	vrollR(cnt);
137 	if (scroll) {
138 		vcline += ind, dot += ind;
139 		if (vcline >= vcnt)
140 			dot -= vcline - vcnt + 1, vcline = vcnt - 1;
141 		getDOT();
142 	}
143 }
144 
145 /*
146  * Like vup, but scrolling down.
147  */
148 void
vdown(int cnt,int ind,bool scroll)149 vdown(int cnt, int ind, bool scroll)
150 {
151 	int i, tot;
152 
153 	if (dot == dol) {
154 		(void) beep();
155 		return;
156 	}
157 	vsave();
158 	i = dol - dot;
159 	if (cnt > i) {
160 		ind -= cnt - i;
161 		if (ind < 0)
162 			ind = 0;
163 		cnt = i;
164 	}
165 	i = vcnt - vcline - 1;
166 	if (!scroll && cnt <= i) {
167 		vshow(dot + cnt, NOLINE);
168 		return;
169 	}
170 	cnt -= i, dot += i, vcline += i;
171 	if (hold & HOLDWIG)
172 		goto dcontxt;
173 	if (!scroll) {
174 		tot = WECHO - WTOP;
175 		if (state != VISUAL || cnt - tot > 0 || vfit(dot, cnt) > tot / 3 + 1) {
176 dcontxt:
177 			vcontext(dot + cnt, '.');
178 			return;
179 		}
180 	}
181 	if (cnt > 0)
182 		vroll(cnt);
183 	if (state == VISUAL && scroll) {
184 		vcline -= ind, dot -= ind;
185 		if (vcline < 0)
186 			dot -= vcline, vcline = 0;
187 		getDOT();
188 	}
189 }
190 
191 /*
192  * Show line addr in context where on the screen.
193  * Work here is in determining new top line implied by
194  * this placement of line addr, since we always draw from the top.
195  */
196 void
vcontext(line * addr,unsigned char where)197 vcontext(line *addr, unsigned char where)
198 {
199 	line *top;
200 
201 	getaline(*addr);
202 	if (state != VISUAL)
203 		top = addr;
204 	else switch (where) {
205 
206 	case '^':
207 		addr = vback(addr, basWLINES - vdepth());
208 		getaline(*addr);
209 		/* FALLTHROUGH */
210 
211 	case '-':
212 		top = vback(addr, basWLINES - vdepth());
213 		getaline(*addr);
214 		break;
215 
216 	case '.':
217 		top = vback(addr, basWLINES / 2 - vdepth());
218 		getaline(*addr);
219 		break;
220 
221 	default:
222 		top = addr;
223 		break;
224 	}
225 	if (state == ONEOPEN && LINE(0) == WBOT)
226 		vup1();
227 	vcnt = vcline = 0;
228 	vclean();
229 	if (state == CRTOPEN)
230 		vup1();
231 	vshow(addr, top);
232 }
233 
234 /*
235  * Get a clean line.  If we are in a hard open
236  * we may be able to reuse the line we are on
237  * if it is blank.  This is a real win.
238  */
239 void
vclean(void)240 vclean(void)
241 {
242 
243 	if (state != VISUAL && state != CRTOPEN) {
244 		destcol = 0;
245 		if (!ateopr())
246 			vup1();
247 		vcnt = 0;
248 	}
249 }
250 
251 /*
252  * Show line addr with the specified top line on the screen.
253  * Top may be 0; in this case have vcontext compute the top
254  * (and call us recursively).  Eventually, we clear the screen
255  * (or its open mode equivalent) and redraw.
256  */
257 void
vshow(line * addr,line * top)258 vshow(line *addr, line *top)
259 {
260 #ifndef CBREAK
261 	bool fried = 0;
262 #endif
263 	int cnt = addr - dot;
264 	int i = vcline + cnt;
265 	short oldhold = hold;
266 
267 	if (state != HARDOPEN && state != ONEOPEN && i >= 0 && i < vcnt) {
268 		dot = addr;
269 		getDOT();
270 		vcline = i;
271 		return;
272 	}
273 	if (state != VISUAL) {
274 		dot = addr;
275 		vopen(dot, WBOT);
276 		return;
277 	}
278 	if (top == 0) {
279 		vcontext(addr, '.');
280 		return;
281 	}
282 	dot = top;
283 #ifndef CBREAK
284 	if (vcookit(2))
285 		fried++, vcook();
286 #endif
287 	oldhold = hold;
288 	hold |= HOLDAT;
289 	vclear();
290 	vreset(0);
291 	vredraw(WTOP);
292 	/* error if vcline >= vcnt ! */
293 	vcline = addr - top;
294 	dot = addr;
295 	getDOT();
296 	hold = oldhold;
297 	vsync(LASTLINE);
298 #ifndef CBREAK
299 	if (fried)
300 		flusho(), vraw();
301 #endif
302 }
303 
304 /*
305  * reset the state.
306  * If inecho then leave us at the beginning of the echo
307  * area;  we are called this way in the middle of a :e escape
308  * from visual, e.g.
309  */
310 void
vreset(bool inecho)311 vreset(bool inecho)
312 {
313 
314 	vcnt = vcline = 0;
315 	WTOP = basWTOP;
316 	WLINES = basWLINES;
317 	if (inecho)
318 		splitw = 1, vgoto(WECHO, 0);
319 }
320 
321 /*
322  * Starting from which line preceding tp uses almost (but not more
323  * than) cnt physical lines?
324  */
325 line *
vback(tp,cnt)326 vback(tp, cnt)
327 	int cnt;
328 	line *tp;
329 {
330 	int d;
331 
332 	if (cnt > 0)
333 		for (; tp > one; tp--) {
334 			getaline(tp[-1]);
335 			d = vdepth();
336 			if (d > cnt)
337 				break;
338 			cnt -= d;
339 		}
340 	return (tp);
341 }
342 
343 /*
344  * How much scrolling will it take to roll cnt lines starting at tp?
345  */
346 int
vfit(line * tp,int cnt)347 vfit(line *tp, int cnt)
348 {
349 	int j;
350 
351 	j = 0;
352 	while (cnt > 0) {
353 		cnt--;
354 		getaline(tp[cnt]);
355 		j += vdepth();
356 	}
357 	if (tp > dot)
358 		j -= WBOT - LASTLINE;
359 	return (j);
360 }
361 
362 /*
363  * Roll cnt lines onto the screen.
364  */
365 void
vroll(int cnt)366 vroll(int cnt)
367 {
368 #ifndef CBREAK
369 	bool fried = 0;
370 #endif
371 	short oldhold = hold;
372 
373 #ifdef ADEBUG
374 	if (trace)
375 		tfixnl(), fprintf(trace, "vroll(%d)\n", cnt);
376 #endif
377 	if (state != VISUAL)
378 		hold |= HOLDAT|HOLDROL;
379 	if (WBOT == WECHO) {
380 		vcnt = 0;
381 		if (state == ONEOPEN)
382 			vup1();
383 	}
384 #ifndef CBREAK
385 	if (vcookit(cnt))
386 		fried++, vcook();
387 #endif
388 	for (; cnt > 0 && Peekkey != ATTN; cnt--) {
389 		dot++, vcline++;
390 		vopen(dot, LASTLINE);
391 		vscrap();
392 	}
393 	hold = oldhold;
394 	if (state == HARDOPEN)
395 		sethard();
396 	vsyncCL();
397 #ifndef CBREAK
398 	if (fried)
399 		flusho(), vraw();
400 #endif
401 }
402 
403 /*
404  * Roll backwards (scroll up).
405  */
406 void
vrollR(int cnt)407 vrollR(int cnt)
408 {
409 	bool fried = 0;
410 	short oldhold = hold;
411 
412 #ifdef ADEBUG
413 	if (trace)
414 		tfixnl(), fprintf(trace, "vrollR(%d), dot=%d\n", cnt, lineDOT());
415 #endif
416 #ifndef CBREAK
417 	if (vcookit(cnt))
418 		fried++, vcook();
419 #endif
420 	if (WBOT == WECHO)
421 		vcnt = 0;
422 	heldech = 0;
423 	hold |= HOLDAT|HOLDECH;
424 	for (; cnt > 0 && Peekkey != ATTN; cnt--) {
425 		dot--;
426 		vopen(dot, WTOP);
427 		vscrap();
428 	}
429 	hold = oldhold;
430 	if (heldech)
431 		vclrech(0);
432 	vsync(LINE(vcnt-1));
433 #ifndef CBREAK
434 	if (fried)
435 		flusho(), vraw();
436 #endif
437 }
438 
439 /*
440  * Go into cooked mode (allow interrupts) during
441  * a scroll if we are at less than 1200 baud and not
442  * a 'vi' command, of if we are in a 'vi' command and the
443  * scroll is more than 2 full screens.
444  *
445  * BUG:		An interrupt during a scroll in this way
446  *		dumps to command mode.
447  */
448 int
vcookit(int cnt)449 vcookit(int cnt)
450 {
451 
452 	return (cnt > 1 && (ospeed < B1200 && !initev || cnt > lines * 2));
453 }
454 
455 /*
456  * Determine displayed depth of current line.
457  */
458 int
vdepth(void)459 vdepth(void)
460 {
461 	int d;
462 
463 	d = (column(NOSTR) + WCOLS - 1 + (Putchar == listchar) + insert_null_glitch) / WCOLS;
464 #ifdef ADEBUG
465 	if (trace)
466 		tfixnl(), fprintf(trace, "vdepth returns %d\n", d == 0 ? 1 : d);
467 #endif
468 	return (d == 0 ? 1 : d);
469 }
470 
471 /*
472  * Move onto a new line, with cursor at position curs.
473  */
474 void
vnline(unsigned char * curs)475 vnline(unsigned char *curs)
476 {
477 	unsigned char *owcursor;
478 	int j;
479 	if (curs) {
480 		if(curs >= strend(linebuf)) {
481 			if(!*linebuf)
482 				wcursor = linebuf;
483 			else {
484 				wcursor = strend(linebuf);
485 				wcursor = lastchr(linebuf, wcursor);
486 			}
487 		} else {
488 			owcursor = wcursor = curs;
489 			j = wcursor - linebuf;
490 			for(wcursor = linebuf; wcursor - linebuf < j; ) {
491 				owcursor = wcursor;
492 				wcursor = nextchr(wcursor);
493 			}
494 			if(wcursor - linebuf > j)
495 				wcursor = owcursor;
496 		}
497 
498 	} else if (vmoving)
499 		wcursor = vfindcol(vmovcol);
500 	else
501 		wcursor = vskipwh(linebuf);
502 	cursor = linebuf;
503 	(void) vmove();
504 }
505