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 }