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 implement commands that work word at a time.
12  * There are all sorts of word mode commands. If I do any sentence and/or
13  * paragraph mode commands, they are likely to be put in this file.
14  */
15 
16 module word;
17 
18 import std.ascii;
19 
20 import ed;
21 import main;
22 import window;
23 import random;
24 import line;
25 import terminal;
26 import region;
27 import basic;
28 import display;
29 
30 /* Word wrap. Back-over whatever precedes the point on the current
31  * line and stop on the first word-break or the beginning of the line. If we
32  * reach the beginning of the line, jump back to the end of the word and start
33  * a new line.  Otherwise, break the line at the word-break, eat it, and jump
34  * back to the end of the word.
35  *      NOTE:  This function may leaving trailing blanks.
36  * Returns true on success, false on errors.
37  */
38 int word_wrap(bool f, int n)
39 {
40         int cnt;
41 	LINE* oldp;
42 
43         oldp = curwp.w_dotp;
44         cnt = -1;
45         do {                            
46                 cnt++;
47                 if (! backchar(false, 1))
48 		    goto err;
49         } while (! inword());
50         if (! word_back(false, 1))
51 	    goto err;
52 	/* If still on same line (but not at the beginning)	*/
53         if (oldp == curwp.w_dotp && curwp.w_doto)
54 	{   int i;
55 
56 	    if (!random_backdel(false, 1))
57 		goto err;
58 	    if (!random_newline(false, 1))
59 		goto err;
60 	    oldp = lback(curwp.w_dotp);
61 	    i = 0;
62 	    while (1)
63 	    {
64 		auto c = lgetc(oldp,i);
65 		if (c != ' ' && c != '\t')
66 		    break;
67 		line_insert(1,c);
68 		i++;
69 	    }
70         }
71 	while (inword() == true)
72 	    if (forwchar(false, 1) == false)
73 		goto err;
74         return forwchar(false, cnt);
75 
76 err:
77 	return false;
78 }
79 
80 /****************************
81  * Word wrap the current line.
82  */
83 
84 int word_wrap_line(bool f, int n)
85 {
86     int i;
87     int j;
88     int col;
89     char c;
90     int inword;
91     int lasti;
92     LINE* oldp;
93     LINE* dotpsave;
94     int dotosave;
95 
96     if (n < 0)
97 	goto err;
98 
99     if (window_marking(curwp))
100     {   REGION region;
101 	int s;
102 
103 	if ((s = getregion(&region)) != true)
104 	    return s;
105 	dotpsave = curwp.w_dotp;
106 	dotosave = curwp.w_doto;
107 	curwp.w_dotp = region.r_linep;
108 	curwp.w_doto = region.r_offset;
109 	n = region.r_nlines;
110     }
111 
112     while (n-- > 0)
113     {
114       L1:
115 	col = 0;
116 	lasti = 0;
117 	inword = 0;
118 	for (i = 0; i < llength(curwp.w_dotp); i++)
119 	{
120 	    c = lgetc(curwp.w_dotp, i);
121 	    if (c == ' ' || c == '\t')
122 	    {
123 		if (inword)
124 		    lasti = i;
125 		inword = 0;
126 	    }
127 	    else
128 	    {
129 		inword = 1;
130 	    }
131 	    col = getcol(curwp.w_dotp, i);
132 	    if (col >= term.t_ncol && lasti)
133 	    {
134 		if (!forwchar(0, lasti - curwp.w_doto))
135 		    goto err;
136 		if (!random_newline(0,1))
137 		    goto err;
138 
139 		/* Remove leading whitespace from new line	*/
140 		while (1)
141 		{
142 		    if (!llength(curwp.w_dotp))
143 			break;
144 		    c = lgetc(curwp.w_dotp, 0);
145 		    if (c == ' ' || c == '\t')
146 		    {
147 			if (!random_forwdel(0, 1))
148 			    goto err;
149 		    }
150 		    else
151 			break;
152 		}
153 
154 		/* Match indenting of original line (oldp)	*/
155 		oldp = lback(curwp.w_dotp);
156 		for (j = 0; j < llength(oldp); j++)
157 		{
158 		    c = lgetc(oldp, j);
159 		    if (c == ' ' || c == '\t')
160 		    {
161 			if (!line_insert(1, c))
162 			    goto err;
163 		    }
164 		    else
165 			break;
166 		}
167 
168 		goto L1;
169 	    }
170 	}
171 	if (!forwline(0, 1))
172 	    goto err;
173     }
174     if (window_marking(curwp))
175     {
176 	if (dotosave > llength(dotpsave))
177 	    dotosave = llength(dotpsave);
178 	curwp.w_dotp = dotpsave;
179 	curwp.w_doto = dotosave;
180     }
181     return true;
182 
183 err:
184     return false;
185 }
186 
187 /*************************
188  * Select word that the cursor is on.
189  */
190 
191 int word_select(bool f, int n)
192 {
193     int inw;
194     int s;
195 
196     inw = inword();
197     do
198 	s = backchar(false, 1);
199     while (s && inword() == inw);
200 
201     return s &&
202 	forwchar(false,1) &&
203 	basic_setmark(false,1) &&
204 	word_forw(f,n);
205 }
206 
207 /******************************
208  * Select line that the cursor is on.
209  */
210 
211 int word_lineselect(bool f, int n)
212 {
213     return (curwp.w_doto == 0 || gotobol(false,1)) &&
214 	basic_setmark(false,1) &&
215 	forwline(f,n);
216 }
217 
218 /*
219  * Move the cursor backward by "n" words. All of the details of motion are
220  * performed by the "backchar" and "forwchar" routines. Error if you try to
221  * move beyond the buffers.
222  */
223 int word_back(bool f, int n)
224 {
225         if (n < 0)
226                 return (word_forw(f, -n));
227         if (backchar(false, 1) == false)
228                 return (false);
229         while (n--) {
230 	    auto inw = inword();
231 	    do
232 		if (backchar(false, 1) == false)
233 		    return (false);
234 	    while (inword() == inw);
235         }
236         return (forwchar(false, 1));
237 }
238 
239 /*
240  * Move the cursor forward by the specified number of words. All of the motion
241  * is done by "forwchar". Error if you try and move beyond the buffer's end.
242  */
243 int word_forw(bool f, int n)
244 {
245         if (n < 0)
246                 return (word_back(f, -n));
247         while (n--) {
248 	    auto inw = inword();
249 	    do
250 		if (forwchar(false, 1) == false)
251 		    return (false);
252 	    while (inword() == inw);
253         }
254         return (true);
255 }
256 
257 /*
258  * Move the cursor forward by the specified number of words. As you move,
259  * convert any characters to upper case. Error if you try and move beyond the
260  * end of the buffer. Bound to "M-U".
261  */
262 int word_upper(bool f, int n)
263 {
264     return word_setcase(f,n,0);
265 }
266 
267 /*
268  * Move the cursor forward by the specified number of words. As you move
269  * convert characters to lower case. Error if you try and move over the end of
270  * the buffer. Bound to "M-L".
271  */
272 int word_lower(bool f, int n)
273 {
274     return word_setcase(f,n,1);
275 }
276 
277 /*************************
278  * Move the cursor forward by the specified number of words. As you move
279  * convert the first character of the word to upper case, and subsequent
280  * characters to lower case. Error if you try and move past the end of the
281  * buffer. Bound to "M-C".
282  */
283 
284 int capword(bool f, int n)
285 {
286     return word_setcase(f,n,2);
287 }
288 
289 private int word_setcase(bool f, int n, int flag)
290 {
291     char    c;
292 
293     if (n < 0)
294 	return (false);
295     while (n--) {
296 	while (inword() == false) {
297 	    if (forwchar(false, 1) == false)
298 		return (false);
299 	}
300 	if (flag == 2 && inword() != false) {
301 	    c = lgetc(curwp.w_dotp, curwp.w_doto);
302 	    if (isLower(c))
303 	    {   c -= 'a'-'A';
304 		lputc(curwp.w_dotp, curwp.w_doto, c);
305 		line_change(WFHARD);
306 	    }
307 	    if (forwchar(false, 1) == false)
308 		return (false);
309 	}
310 	while (inword() != false) {
311 	    c = lgetc(curwp.w_dotp, curwp.w_doto);
312 	    final switch (flag)
313 	    {   case 0:
314 		    if (isLower(c)) {
315 			c -= 'a'-'A';
316 			goto L1;
317 		    }
318 		    break;
319 		case 1:
320 		case 2:
321 		    if (isUpper(c)) {
322 			c += 'a'-'A';
323 		    L1: lputc(curwp.w_dotp, curwp.w_doto, c);
324 			line_change(WFHARD);
325 		    }
326 		    break;
327 	    }
328 	    if (forwchar(false, 1) == false)
329 		return (false);
330 	}
331     }
332     return (true);
333 }
334 
335 /*
336  * Kill forward by "n" words. Remember the location of dot. Move forward by
337  * the right number of words. Put dot back where it was and issue the kill
338  * command for the right number of characters. Bound to "M-D".
339  */
340 int delfword(bool f, int n)
341 {
342         int    size;
343         LINE*  dotp;
344         int    doto;
345 
346         if (n < 0)
347                 return (false);
348         dotp = curwp.w_dotp;
349         doto = curwp.w_doto;
350         size = 0;
351         while (n--) {
352                 while (inword() == false) {
353                         if (forwchar(false, 1) == false)
354                                 return (false);
355                         ++size;
356                 }
357                 while (inword() != false) {
358                         if (forwchar(false, 1) == false)
359                                 return (false);
360                         ++size;
361                 }
362         }
363         curwp.w_dotp = dotp;
364         curwp.w_doto = doto;
365         return (line_delete(size, true));
366 }
367 
368 /*
369  * Kill backwards by "n" words. Move backwards by the desired number of words,
370  * counting the characters. When dot is finally moved to its resting place,
371  * fire off the kill command. Bound to "M-Rubout" and to "M-Backspace".
372  */
373 int delbword(bool f, int n)
374 {
375         int    size;
376 
377         if (n < 0)
378                 return (false);
379         if (backchar(false, 1) == false)
380                 return (false);
381         size = 0;
382         while (n--) {
383                 while (inword() == false) {
384                         if (backchar(false, 1) == false)
385                                 return (false);
386                         ++size;
387                 }
388                 while (inword() != false) {
389                         if (backchar(false, 1) == false)
390                                 return (false);
391                         ++size;
392                 }
393         }
394         if (forwchar(false, 1) == false)
395                 return false;
396         return line_delete(size, true);
397 }
398 
399 /*
400  * Return true if the character at dot is a character that is considered to be
401  * part of a word. The word character list is hard coded. Should be setable.
402  * This routine MUST return only a 1 or a 0.
403  */
404 bool inword()
405 {
406         if (curwp.w_doto == llength(curwp.w_dotp))
407                 return false;
408         auto c = lgetc(curwp.w_dotp, curwp.w_doto);
409 	return (isAlphaNum(c) ||
410 		 c=='$' || c=='_');	/* For identifiers      */
411 }