1 
2 
3 /* This version of microEmacs is based on the public domain C
4  * version written by Dave G. Conroy.
5  * The D programming language version is written by Walter Bright.
6  * http://www.digitalmars.com/d/
7  * This program is in the public domain.
8  */
9 
10 /*
11  * The routines in this file move the cursor around on the screen. They
12  * compute a new value for the cursor, then adjust ".". The display code
13  * always updates the cursor location, so only moves between lines, or
14  * functions that adjust the top line in the window and invalidate the
15  * framing, are hard.
16  */
17 
18 module basic;
19 
20 import std.conv;
21 
22 import ed;
23 import line;
24 import main;
25 import window;
26 import display;
27 import console;
28 
29 /*********************************
30  * Move the cursor to the
31  * beginning of the current line.
32  * Trivial.
33  */
34 
35 int gotobol(bool f, int n)
36 {	int s;
37 
38 	if (curwp.w_doto == 0)
39 		s = backline(f, n);
40 	else
41 	{
42 		if (curwp.w_markp)
43 			curwp.w_flag |= WFMOVE;
44 		s = TRUE;
45 		curwp.w_doto = 0;
46 		if( n > 1 )
47 			s = backline(f, n-1);
48 	}
49 	curgoal = 0;
50 	return s;
51 }
52 
53 /*
54  * Move the cursor backwards by "n" characters. If "n" is less than zero call
55  * "forwchar" to actually do the move. Otherwise compute the new cursor
56  * location. Error if you try and move out of the buffer. Set the flag if the
57  * line pointer for dot changes.
58  */
59 int backchar(bool f, int n)
60 {
61         if (n < 0)
62                 return (forwchar(f, -n));
63 	if (curwp.w_markp && n)
64 		curwp.w_flag |= WFMOVE;
65         while (n--) {
66                 if (curwp.w_doto == 0) {
67 		        LINE   *lp;
68                         if ((lp=lback(curwp.w_dotp)) == curbp.b_linep)
69                                 return (FALSE);
70                         curwp.w_dotp  = lp;
71                         curwp.w_doto  = llength(lp);
72                         curwp.w_flag |= WFMOVE;
73                 } else
74                         curwp.w_doto--;
75         }
76         return (TRUE);
77 }
78 
79 /**************************************
80  * Move the cursor to the end of the current line.
81  * If already at the end, advance to next line.
82  */
83 
84 int gotoeol(bool f, int n)
85 {
86     int s;
87 
88     s = TRUE;
89     if( curwp.w_doto != llength(curwp.w_dotp) )
90     {
91 	if (curwp.w_markp)
92 	    curwp.w_flag |= WFMOVE;
93 	n--;
94     }
95     if( n > 0 )
96 	s = forwline(f,n);
97     curwp.w_doto = llength(curwp.w_dotp);
98     return( s );
99 }
100 
101 /*
102  * Move the cursor forwards by "n" characters. If "n" is less than zero call
103  * "backchar" to actually do the move. Otherwise compute the new cursor
104  * location, and move ".". Error if you try and move off the end of the
105  * buffer. Set the flag if the line pointer for dot changes.
106  */
107 int forwchar(bool f, int n)
108 {
109         if (n < 0)
110                 return (backchar(f, -n));
111 	if (curwp.w_markp && n)
112 		curwp.w_flag |= WFMOVE;
113         while (n--) {
114                 if (curwp.w_doto == llength(curwp.w_dotp)) {
115                         if (curwp.w_dotp == curbp.b_linep)
116                                 return (FALSE);
117                         curwp.w_dotp  = lforw(curwp.w_dotp);
118                         curwp.w_doto  = 0;
119                         curwp.w_flag |= WFMOVE;
120                 } else
121                         curwp.w_doto++;
122         }
123         return (TRUE);
124 }
125 
126 /*
127  * Goto the beginning of the buffer. Massive adjustment of dot. This is
128  * considered to be hard motion; it really isn't if the original value of dot
129  * is the same as the new value of dot. Normally bound to "M-<".
130  */
131 int gotobob(bool f, int n)
132 {
133         curwp.w_dotp  = lforw(curbp.b_linep);
134         curwp.w_doto  = 0;
135         curwp.w_flag |= WFHARD;
136         return (TRUE);
137 }
138 
139 /*
140  * Move to the end of the buffer. Dot is always put at the end of the file
141  * (ZJ). The standard screen code does most of the hard parts of update.
142  * Bound to "M.".
143  */
144 int gotoeob(bool f, int n)
145 {
146         curwp.w_dotp  = curbp.b_linep;
147         curwp.w_doto  = 0;
148         curwp.w_flag |= WFHARD;
149         return (TRUE);
150 }
151 
152 /*
153  * Move forward by full lines. If the number of lines to move is less than
154  * zero, call the backward line function to actually do it. The last command
155  * controls how the goal column is set. Bound to "C-N". No errors are
156  * possible.
157  */
158 int forwline(bool f, int n)
159 {
160         if (n < 0)
161                 return (backline(f, -n));
162         auto dlp = curwp.w_dotp;
163 
164 	/* Reset goal if last command not backline() or forwline()	*/
165         if ((lastflag&CFCPCN) == 0)
166                 curgoal = getcol(dlp,curwp.w_doto);
167         thisflag |= CFCPCN;			/* this command was a	*/
168 						/* forwline or backline	*/
169         while (n-- && dlp!=curbp.b_linep)
170                 dlp = lforw(dlp);
171         curwp.w_dotp  = dlp;
172         curwp.w_doto  = getgoal(dlp);
173         curwp.w_flag |= WFMOVE;
174         return (TRUE);
175 }
176 
177 /*******************************
178  * Proceed to beginning of next line.
179  */
180 
181 int basic_nextline(bool f, int n)
182 {
183     if (curwp.w_doto == 0 || gotobol(FALSE,1))
184     {
185 	lastflag &= ~CFCPCN;
186 	return forwline(f,n);
187     }
188     return FALSE;
189 }
190 
191 /*
192  * This function is like "forwline", but goes backwards. The scheme is exactly
193  * the same. Check for arguments that are less than zero and call your
194  * alternate. Figure out the new line and call "movedot" to perform the
195  * motion. No errors are possible. Bound to "C-P".
196  */
197 int backline(bool f, int n)
198 {
199         if (n < 0)
200                 return (forwline(f, -n));
201         auto dlp = curwp.w_dotp;
202 
203 	/* Reset goal if last command not backline() or forwline()	*/
204         if ((lastflag&CFCPCN) == 0)
205                 curgoal = getcol(dlp,curwp.w_doto);
206         thisflag |= CFCPCN;
207 
208         while (n-- && lback(dlp)!=curbp.b_linep)
209                 dlp = lback(dlp);
210         curwp.w_dotp  = dlp;
211         curwp.w_doto  = getgoal(dlp);
212         curwp.w_flag |= WFMOVE;
213         return (TRUE);
214 }
215 
216 /**********************************
217  * Goto line number.
218  */
219 
220 int gotoline(bool f, int n)
221 {	string number;
222 
223 	if (mlreply("Goto line: ", null, number) == FALSE)
224 		return FALSE;
225 	try
226 	{
227 	    const num = to!(int)(number);
228 	    gotobob(f, n);			/* move to beginning of buffer	*/
229 	    return forwline(f, num - 1);
230 	}
231 	catch (Throwable o)
232 	{
233 	}
234 	return FALSE;
235 }
236 
237 /*
238  * This routine, given a pointer to a LINE, and the current cursor goal
239  * column, return the best choice for the offset. The offset is returned.
240  * Used by forwline() and backline().
241  */
242 int getgoal(LINE* dlp)
243 {
244         int    c;
245         int    col;
246         int    newcol;
247         int    dbo;
248 
249         col = 0;
250         dbo = 0;
251         while (dbo != llength(dlp)) {
252                 c = lgetc(dlp, dbo);
253                 newcol = col;
254                 if (c == '\t')
255                         newcol |= 0x07;
256                 else if (c<0x20 || c==0x7F)
257                         ++newcol;
258                 ++newcol;
259                 if (newcol > curgoal)
260                         break;
261                 col = newcol;
262                 ++dbo;
263         }
264         return (dbo);
265 }
266 
267 /*
268  * Scroll forward by a specified number of lines, or by a full page if no
269  * argument. Bound to "C-V". The "2" in the arithmetic on the window size is
270  * the overlap; this value is the default overlap value in ITS EMACS. Because
271  * this zaps the top line in the display window, we have to do a hard update.
272  */
273 int forwpage(bool f, int n)
274 {
275         if (f == FALSE) {
276                 n = curwp.w_ntrows - 2;        /* Default scroll.      */
277                 if (n <= 0)                     /* Forget the overlap   */
278                         n = 1;                  /* if tiny window.      */
279         } else if (n < 0)
280                 return (backpage(f, -n));
281         else if (CVMVAS)                       /* Convert from pages   */
282                 n *= curwp.w_ntrows;           /* to lines.            */
283         auto lp = curwp.w_linep;
284         while (n-- && lp!=curbp.b_linep)
285                 lp = lforw(lp);
286         curwp.w_linep = lp;
287         curwp.w_dotp  = lp;
288         curwp.w_doto  = 0;
289         curwp.w_flag |= WFHARD;
290         return (TRUE);
291 }
292 
293 /*
294  * This command is like "forwpage", but it goes backwards. The "2", like
295  * above, is the overlap between the two windows. The value is from the ITS
296  * EMACS manual. Bound to "M-V". We do a hard update for exactly the same
297  * reason.
298  */
299 int backpage(bool f, int n)
300 {
301         if (f == FALSE) {
302                 n = curwp.w_ntrows - 2;        /* Default scroll.      */
303                 if (n <= 0)                     /* Don't blow up if the */
304                         n = 1;                  /* window is tiny.      */
305         } else if (n < 0)
306                 return (forwpage(f, -n));
307         else if (CVMVAS)                       /* Convert from pages   */
308                 n *= curwp.w_ntrows;           /* to lines.            */
309         auto lp = curwp.w_linep;
310         while (n-- && lback(lp)!=curbp.b_linep)
311                 lp = lback(lp);
312         curwp.w_linep = lp;
313         curwp.w_dotp  = lp;
314         curwp.w_doto  = 0;
315         curwp.w_flag |= WFHARD;
316         return (TRUE);
317 }
318 
319 /*
320  * Set the mark in the current window to the value of "." in the window. No
321  * errors are possible. Bound to "M-.".
322  */
323 int basic_setmark(bool f, int n)
324 {
325 	removemark(f,n);		/* delete old mark		*/
326         curwp.w_markp = curwp.w_dotp;
327         curwp.w_marko = curwp.w_doto;
328 	/* Get starting column for column regions	*/
329 	markcol = getcol(curwp.w_markp,curwp.w_doto);
330         mlwrite("[Mark set]");
331         return (TRUE);
332 }
333 
334 /*************************
335  * Remove mark from current window.
336  */
337 
338 int removemark(bool f, int n)
339 {
340 	if (curwp.w_markp)
341 	{	curwp.w_flag |= WFHARD;
342 		curwp.w_markp = null;
343 	        mlwrite("[Mark removed]");
344 	}
345 	else
346 		mlwrite("[No mark]");
347         return (TRUE);
348 }
349 
350 /*
351  * Swap the values of "." and "mark" in the current window. This is pretty
352  * easy, bacause all of the hard work gets done by the standard routine
353  * that moves the mark about. The only possible error is "no mark". Bound to
354  * "C-X C-X".
355  */
356 int swapmark(bool f, int n)
357 {
358         if (curwp.w_markp == null) {
359                 mlwrite("No mark in this window");
360                 return (FALSE);
361         }
362         auto odotp = curwp.w_dotp;
363         auto odoto = curwp.w_doto;
364         curwp.w_dotp  = curwp.w_markp;
365         curwp.w_doto  = curwp.w_marko;
366         curwp.w_markp = odotp;
367         curwp.w_marko = odoto;
368         curwp.w_flag |= WFMOVE;
369         return (TRUE);
370 }