Add support for tab-completion when selecting by rule
[alpine.git] / pico / random.c
blob1d1b666e83dc755622b0fd75e6d5340d31d984ae
1 /*
2 * ========================================================================
3 * Copyright 2006-2007 University of Washington
4 * Copyright 2013-2022 Eduardo Chappa
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
10 * http://www.apache.org/licenses/LICENSE-2.0
12 * ========================================================================
16 * Program: Random routines
18 * This file contains the command processing functions for a number of random
19 * commands. There is no functional grouping here, for sure.
22 #include "headers.h"
24 #include "osdep/terminal.h"
26 int worthit(int *);
28 int tabsize; /* Tab size (0: use real tabs) */
32 * Display the current position of the cursor, in origin 1 X-Y coordinates,
33 * the character that is under the cursor (in octal), and the fraction of the
34 * text that is before the cursor. The displayed column is not the current
35 * column, but the column that would be used on an infinite width display.
36 * Normally this is bound to "C-X =".
38 int
39 showcpos(int f, int n)
41 register LINE *clp;
42 register long nch;
43 register int cbo;
44 register long nbc;
45 register int lines;
46 register int thisline = 0;
47 char buffer[100];
49 clp = lforw(curbp->b_linep); /* Grovel the data. */
50 cbo = 0;
51 nch = 0L;
52 lines = 0;
53 for (;;) {
54 if (clp==curwp->w_dotp && cbo==curwp->w_doto) {
55 thisline = lines;
56 nbc = nch;
58 if (cbo == llength(clp)) {
59 if (clp == curbp->b_linep)
60 break;
61 clp = lforw(clp);
62 cbo = 0;
63 lines++;
64 } else
65 ++cbo;
66 ++nch;
69 snprintf(buffer,sizeof(buffer),"line %d of %d (%d%%%%), character %ld of %ld (%d%%%%)",
70 thisline+1, lines+1, (int)((100L*(thisline+1))/(lines+1)),
71 nbc, nch, (nch) ? (int)((100L*nbc)/nch) : 0);
73 emlwrite(buffer, NULL);
74 return (TRUE);
79 * Return current column. Stop at first non-blank given TRUE argument.
81 int
82 getccol(int bflg)
84 UCS c;
85 int i, col;
87 col = 0;
88 for (i=0; i<curwp->w_doto; ++i) {
89 c = lgetc(curwp->w_dotp, i).c;
90 if (c!=' ' && c!='\t' && bflg)
91 break;
93 if (c == '\t'){
94 col |= 0x07;
95 ++col;
97 else if (ISCONTROL(c)){
98 col += 2;
100 else{
101 int ww;
103 ww = wcellwidth(c);
104 col += (ww >= 0 ? ww : 1);
108 return(col);
114 * Set tab size if given non-default argument (n <> 1). Otherwise, insert a
115 * tab into file. If given argument, n, of zero, change to true tabs.
116 * If n > 1, simulate tab stop every n-characters using spaces. This has to be
117 * done in this slightly funny way because the tab (in ASCII) has been turned
118 * into "C-I" (in 10 bit code) already. Bound to "C-I".
121 tab(int f, int n)
123 if (n < 0)
124 return (FALSE);
126 if (n == 0 || n > 1) {
127 tabsize = n;
128 return(TRUE);
131 if (! tabsize)
132 return(linsert(1, '\t'));
134 return(linsert(tabsize - (getccol(FALSE) % tabsize), ' '));
139 * Insert a newline. Bound to "C-M".
142 newline(int f, int n)
144 register int s;
146 if (curbp->b_mode&MDVIEW) /* don't allow this command if */
147 return(rdonly()); /* we are in read only mode */
149 if (n < 0)
150 return (FALSE);
152 if(TERM_OPTIMIZE && (curwp->w_dotp != curwp->w_bufp->b_linep)){
153 int l;
155 if(worthit(&l)){
156 if(curwp->w_doto != 0)
157 l++;
158 scrolldown(curwp, l, n);
162 /* if we are in C mode and this is a default <NL> */
163 /* pico's never in C mode */
165 if(Pmaster && Pmaster->allow_flowed_text && curwp->w_doto
166 && ucs4_isspace(lgetc(curwp->w_dotp, curwp->w_doto - 1).c)
167 && !(curwp->w_doto == 3
168 && lgetc(curwp->w_dotp, 0).c == '-'
169 && lgetc(curwp->w_dotp, 1).c == '-'
170 && lgetc(curwp->w_dotp, 2).c == ' ')){
172 * flowed mode, make the newline a hard one by
173 * stripping trailing space.
175 int i, dellen;
176 for(i = curwp->w_doto - 1;
177 i && ucs4_isspace(lgetc(curwp->w_dotp, i - 1).c);
178 i--);
179 dellen = curwp->w_doto - i;
180 curwp->w_doto = i;
181 ldelete(dellen, NULL);
183 /* insert some lines */
184 while (n--) {
185 if ((s=lnewline()) != TRUE)
186 return (s);
188 return (TRUE);
194 * Delete forward. This is real easy, because the basic delete routine does
195 * all of the work. Watches for negative arguments, and does the right thing.
196 * If any argument is present, it kills rather than deletes, to prevent loss
197 * of text if typed with a big argument. Normally bound to "C-D".
200 forwdel(int f, int n)
202 if (curbp->b_mode&MDVIEW) /* don't allow this command if */
203 return(rdonly()); /* we are in read only mode */
205 if (n < 0)
206 return (backdel(f, -n));
208 if(TERM_OPTIMIZE && (curwp->w_dotp != curwp->w_bufp->b_linep)){
209 int l;
211 if(worthit(&l) && curwp->w_doto == llength(curwp->w_dotp))
212 scrollup(curwp, l+1, 1);
215 if (f != FALSE) { /* Really a kill. */
216 if ((lastflag&CFKILL) == 0)
217 kdelete();
218 thisflag |= CFKILL;
221 return (ldelete((long) n, f ? kinsert : NULL));
227 * Delete backwards. This is quite easy too, because it's all done with other
228 * functions. Just move the cursor back, and delete forwards. Like delete
229 * forward, this actually does a kill if presented with an argument. Bound to
230 * both "RUBOUT" and "C-H".
233 backdel(int f, int n)
235 register int s;
237 if (curbp->b_mode&MDVIEW) /* don't allow this command if */
238 return(rdonly()); /* we are in read only mode */
240 if (n < 0)
241 return (forwdel(f, -n));
243 if(TERM_OPTIMIZE && curwp->w_dotp != curwp->w_bufp->b_linep){
244 int l;
246 if(worthit(&l) && curwp->w_doto == 0 &&
247 lback(curwp->w_dotp) != curwp->w_bufp->b_linep){
248 if(l == curwp->w_toprow)
249 scrollup(curwp, l+1, 1);
250 else if(llength(lback(curwp->w_dotp)) == 0)
251 scrollup(curwp, l-1, 1);
252 else
253 scrollup(curwp, l, 1);
257 if (f != FALSE) { /* Really a kill. */
258 if ((lastflag&CFKILL) == 0)
259 kdelete();
261 thisflag |= CFKILL;
264 if ((s=backchar(f, n)) == TRUE)
265 s = ldelete((long) n, f ? kinsert : NULL);
267 return (s);
273 * killtext - delete the line that the cursor is currently in.
274 * a greatly pared down version of its former self.
277 killtext(int f, int n)
279 register int chunk;
280 int opt_scroll = 0;
282 if (curbp->b_mode&MDVIEW) /* don't allow this command if */
283 return(rdonly()); /* we are in read only mode */
285 if ((lastflag&CFKILL) == 0) /* Clear kill buffer if */
286 kdelete(); /* last wasn't a kill. */
288 if(gmode & MDDTKILL){ /* */
289 if((chunk = llength(curwp->w_dotp) - curwp->w_doto) == 0){
290 chunk = 1;
291 if(TERM_OPTIMIZE)
292 opt_scroll = 1;
295 else{
296 gotobol(FALSE, 1); /* wack from bol past newline */
297 chunk = llength(curwp->w_dotp) + 1;
298 if(TERM_OPTIMIZE)
299 opt_scroll = 1;
302 /* optimize what motion we can */
303 if(opt_scroll && (curwp->w_dotp != curwp->w_bufp->b_linep)){
304 int l;
306 if(worthit(&l))
307 scrollup(curwp, l, 1);
310 thisflag |= CFKILL;
311 return(ldelete((long) chunk, kinsert));
316 * Yank text back from the kill buffer. This is really easy. All of the work
317 * is done by the standard insert routines. All you do is run the loop, and
318 * check for errors. Bound to "C-Y".
321 yank(int f, int n)
323 int c, i;
324 REGION region, *added_region;
325 LINE *dotp;
327 if (curbp->b_mode&MDVIEW) /* don't allow this command if */
328 return(rdonly()); /* we are in read only mode */
330 if (n < 0)
331 return (FALSE);
333 if(TERM_OPTIMIZE && (curwp->w_dotp != curwp->w_bufp->b_linep)){
334 int l;
336 if(worthit(&l) && !(lastflag&CFFILL)){
337 register int t = 0;
338 register int i = 0;
339 register int ch;
341 while((ch=fremove(i++)) >= 0)
342 if(ch == '\n')
343 t++;
344 if(t+l < curwp->w_toprow+curwp->w_ntrows)
345 scrolldown(curwp, l, t);
349 if(lastflag & CFFILL){ /* if last command was fillpara() */
350 if(lastflag & CFFLBF){
351 gotoeob(FALSE, 1);
352 dotp = curwp->w_dotp;
353 gotobob(FALSE, 1);
354 curwp->w_doto = 0;
355 getregion(&region, dotp, llength(dotp));
357 else{
358 added_region = get_last_region_added();
359 if(added_region){
360 curwp->w_dotp = added_region->r_linep;
361 curwp->w_doto = added_region->r_offset;
362 region = (*added_region);
364 else
365 return(FALSE);
368 if(!ldelete(region.r_size, NULL))
369 return(FALSE);
370 } /* then splat out the saved buffer */
372 while (n--) {
373 i = 0;
374 while ((c = ((lastflag&CFFILL)
375 ? ((lastflag & CFFLBF) ? kremove(i) : fremove(i))
376 : kremove(i))) >= 0) {
377 if (c == '\n') {
378 if (lnewline() == FALSE)
379 return (FALSE);
380 } else {
381 if (linsert(1, c) == FALSE)
382 return (FALSE);
385 ++i;
389 if(lastflag&CFFLPA){ /* if last command was fill paragraph */
390 curwp->w_dotp = lforw(curwp->w_dotp);
391 curwp->w_doto = 0;
393 curwp->w_flag |= WFMODE;
395 if(!Pmaster){
396 sgarbk = TRUE;
397 emlwrite("", NULL);
401 return (TRUE);
407 * worthit - generic sort of test to roughly gage usefulness of using
408 * optimized scrolling.
410 * note:
411 * returns the line on the screen, l, that the dot is currently on
414 worthit(int *l)
416 int i; /* l is current line */
417 unsigned below; /* below is avg # of ch/line under . */
419 *l = doton(&i, &below);
420 below = (i > 0) ? below/(unsigned)i : 0;
422 return(below > 3);