nano

nano with my custom patches
git clone git://bsandro.tech/nano
Log | Files | Refs | README | LICENSE

move.c (20963B)


      1 /**************************************************************************
      2  *   move.c  --  This file is part of GNU nano.                           *
      3  *                                                                        *
      4  *   Copyright (C) 1999-2011, 2013-2025 Free Software Foundation, Inc.    *
      5  *   Copyright (C) 2014-2018 Benno Schulenberg                            *
      6  *                                                                        *
      7  *   GNU nano is free software: you can redistribute it and/or modify     *
      8  *   it under the terms of the GNU General Public License as published    *
      9  *   by the Free Software Foundation, either version 3 of the License,    *
     10  *   or (at your option) any later version.                               *
     11  *                                                                        *
     12  *   GNU nano is distributed in the hope that it will be useful,          *
     13  *   but WITHOUT ANY WARRANTY; without even the implied warranty          *
     14  *   of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.              *
     15  *   See the GNU General Public License for more details.                 *
     16  *                                                                        *
     17  *   You should have received a copy of the GNU General Public License    *
     18  *   along with this program.  If not, see https://gnu.org/licenses/.     *
     19  *                                                                        *
     20  **************************************************************************/
     21 
     22 #include "prototypes.h"
     23 
     24 #include <string.h>
     25 
     26 /* Move to the first line of the file. */
     27 void to_first_line(void)
     28 {
     29 	openfile->current = openfile->filetop;
     30 	openfile->current_x = 0;
     31 	openfile->placewewant = 0;
     32 
     33 	refresh_needed = TRUE;
     34 }
     35 
     36 /* Move to the last line of the file. */
     37 void to_last_line(void)
     38 {
     39 	openfile->current = openfile->filebot;
     40 	openfile->current_x = (inhelp) ? 0 : strlen(openfile->filebot->data);
     41 	openfile->placewewant = xplustabs();
     42 
     43 	/* Set the last line of the screen as the target for the cursor. */
     44 	openfile->cursor_row = editwinrows - 1;
     45 
     46 	refresh_needed = TRUE;
     47 #ifdef ENABLE_COLOR
     48 	recook |= perturbed;
     49 #endif
     50 	focusing = FALSE;
     51 }
     52 
     53 /* Determine the actual current chunk and the target column. */
     54 void get_edge_and_target(size_t *leftedge, size_t *target_column)
     55 {
     56 #ifndef NANO_TINY
     57 	if (ISSET(SOFTWRAP)) {
     58 		size_t shim = editwincols * (1 + (tabsize / editwincols));
     59 
     60 		*leftedge = leftedge_for(xplustabs(), openfile->current);
     61 		*target_column = (openfile->placewewant + shim - *leftedge) % editwincols;
     62 	} else
     63 #endif
     64 	{
     65 		*leftedge = 0;
     66 		*target_column = openfile->placewewant;
     67 	}
     68 }
     69 
     70 /* Return the index in line->data that corresponds to the given column on the
     71  * chunk that starts at the given leftedge.  If the target column has landed
     72  * on a tab, prevent the cursor from falling back a row when moving forward,
     73  * or from skipping a row when moving backward, by incrementing the index. */
     74 size_t proper_x(linestruct *line, size_t *leftedge, bool forward,
     75 				size_t column, bool *shifted)
     76 {
     77 	size_t index = actual_x(line->data, column);
     78 
     79 #ifndef NANO_TINY
     80 	if (ISSET(SOFTWRAP) && line->data[index] == '\t' &&
     81 				((forward && wideness(line->data, index) < *leftedge) ||
     82 				(!forward && column / tabsize == (*leftedge - 1) / tabsize &&
     83 				column / tabsize < (*leftedge + editwincols - 1) / tabsize))) {
     84 		index++;
     85 
     86 		if (shifted != NULL)
     87 			*shifted = TRUE;
     88 	}
     89 
     90 	if (ISSET(SOFTWRAP))
     91 		*leftedge = leftedge_for(wideness(line->data, index), line);
     92 #endif
     93 
     94 	return index;
     95 }
     96 
     97 /* Adjust the values for current_x and placewewant in case we have landed in
     98  * the middle of a tab that crosses a row boundary. */
     99 void set_proper_index_and_pww(size_t *leftedge, size_t target, bool forward)
    100 {
    101 	size_t was_edge = *leftedge;
    102 	bool shifted = FALSE;
    103 
    104 	openfile->current_x = proper_x(openfile->current, leftedge, forward,
    105 						actual_last_column(*leftedge, target), &shifted);
    106 
    107 	/* If the index was incremented, try going to the target column. */
    108 	if (shifted || *leftedge < was_edge)
    109 		openfile->current_x = proper_x(openfile->current, leftedge, forward,
    110 						actual_last_column(*leftedge, target), &shifted);
    111 
    112 	openfile->placewewant = *leftedge + target;
    113 }
    114 
    115 /* Move up almost one screenful. */
    116 void do_page_up(void)
    117 {
    118 	int mustmove = (editwinrows < 3) ? 1 : editwinrows - 2;
    119 	size_t leftedge, target_column;
    120 
    121 #ifndef NANO_TINY
    122 	/* If we're not in smooth scrolling mode, put the cursor at the
    123 	 * beginning of the top line of the edit window, as Pico does. */
    124 	if (ISSET(JUMPY_SCROLLING)) {
    125 		openfile->current = openfile->edittop;
    126 		leftedge = openfile->firstcolumn;
    127 		openfile->cursor_row = 0;
    128 		target_column = 0;
    129 	} else
    130 #endif
    131 		get_edge_and_target(&leftedge, &target_column);
    132 
    133 	/* Move up the required number of lines or chunks.  If we can't, we're
    134 	 * at the top of the file, so put the cursor there and get out. */
    135 	if (go_back_chunks(mustmove, &openfile->current, &leftedge) > 0) {
    136 		to_first_line();
    137 		return;
    138 	}
    139 
    140 	set_proper_index_and_pww(&leftedge, target_column, FALSE);
    141 
    142 	/* Move the viewport so that the cursor stays immobile, if possible. */
    143 	adjust_viewport(STATIONARY);
    144 	refresh_needed = TRUE;
    145 }
    146 
    147 /* Move down almost one screenful. */
    148 void do_page_down(void)
    149 {
    150 	int mustmove = (editwinrows < 3) ? 1 : editwinrows - 2;
    151 	size_t leftedge, target_column;
    152 
    153 #ifndef NANO_TINY
    154 	/* If we're not in smooth scrolling mode, put the cursor at the
    155 	 * beginning of the top line of the edit window, as Pico does. */
    156 	if (ISSET(JUMPY_SCROLLING)) {
    157 		openfile->current = openfile->edittop;
    158 		leftedge = openfile->firstcolumn;
    159 		openfile->cursor_row = 0;
    160 		target_column = 0;
    161 	} else
    162 #endif
    163 		get_edge_and_target(&leftedge, &target_column);
    164 
    165 	/* Move down the required number of lines or chunks.  If we can't, we're
    166 	 * at the bottom of the file, so put the cursor there and get out. */
    167 	if (go_forward_chunks(mustmove, &openfile->current, &leftedge) > 0) {
    168 		to_last_line();
    169 		return;
    170 	}
    171 
    172 	set_proper_index_and_pww(&leftedge, target_column, TRUE);
    173 
    174 	/* Move the viewport so that the cursor stays immobile, if possible. */
    175 	adjust_viewport(STATIONARY);
    176 	refresh_needed = TRUE;
    177 }
    178 
    179 #ifndef NANO_TINY
    180 /* Place the cursor on the first row in the viewport. */
    181 void to_top_row(void)
    182 {
    183 	size_t leftedge, offset;
    184 
    185 	get_edge_and_target(&leftedge, &offset);
    186 
    187 	openfile->current = openfile->edittop;
    188 	leftedge = openfile->firstcolumn;
    189 
    190 	set_proper_index_and_pww(&leftedge, offset, FALSE);
    191 
    192 	refresh_needed = (openfile->mark != NULL);
    193 }
    194 
    195 /* Place the cursor on the last row in the viewport, when possible. */
    196 void to_bottom_row(void)
    197 {
    198 	size_t leftedge, offset;
    199 
    200 	get_edge_and_target(&leftedge, &offset);
    201 
    202 	openfile->current = openfile->edittop;
    203 	leftedge = openfile->firstcolumn;
    204 
    205 	go_forward_chunks(editwinrows - 1, &openfile->current, &leftedge);
    206 	set_proper_index_and_pww(&leftedge, offset, TRUE);
    207 
    208 	refresh_needed = (openfile->mark != NULL);
    209 }
    210 
    211 /* Put the cursor line at the center, then the top, then the bottom. */
    212 void do_cycle(void)
    213 {
    214 	if (cycling_aim == 0)
    215 		adjust_viewport(CENTERING);
    216 	else {
    217 		openfile->cursor_row = (cycling_aim == 1) ? 0 : editwinrows - 1;
    218 		adjust_viewport(STATIONARY);
    219 	}
    220 
    221 	cycling_aim = (cycling_aim + 1) % 3;
    222 
    223 	draw_all_subwindows();
    224 	full_refresh();
    225 }
    226 
    227 /* Scroll the line with the cursor to the center of the screen. */
    228 void do_center(void)
    229 {
    230 	adjust_viewport(CENTERING);
    231 	draw_all_subwindows();
    232 	full_refresh();
    233 }
    234 #endif /* !NANO_TINY */
    235 
    236 #ifdef ENABLE_JUSTIFY
    237 /* Move to the first beginning of a paragraph before the current line. */
    238 void do_para_begin(linestruct **line)
    239 {
    240 	if ((*line)->prev != NULL)
    241 		*line = (*line)->prev;
    242 
    243 	while (!begpar(*line, 0))
    244 		*line = (*line)->prev;
    245 }
    246 
    247 /* Move down to the last line of the first found paragraph. */
    248 void do_para_end(linestruct **line)
    249 {
    250 	while ((*line)->next != NULL && !inpar(*line))
    251 		*line = (*line)->next;
    252 
    253 	while ((*line)->next != NULL && inpar((*line)->next) &&
    254 									!begpar((*line)->next, 0))
    255 		*line = (*line)->next;
    256 }
    257 
    258 /* Move up to first start of a paragraph before the current line. */
    259 void to_para_begin(void)
    260 {
    261 	linestruct *was_current = openfile->current;
    262 
    263 	do_para_begin(&openfile->current);
    264 	openfile->current_x = 0;
    265 
    266 	edit_redraw(was_current, CENTERING);
    267 }
    268 
    269 /* Move down to just after the first found end of a paragraph. */
    270 void to_para_end(void)
    271 {
    272 	linestruct *was_current = openfile->current;
    273 
    274 	do_para_end(&openfile->current);
    275 
    276 	/* Step beyond the last line of the paragraph, if possible;
    277 	 * otherwise, move to the end of the line. */
    278 	if (openfile->current->next != NULL) {
    279 		openfile->current = openfile->current->next;
    280 		openfile->current_x = 0;
    281 	} else
    282 		openfile->current_x = strlen(openfile->current->data);
    283 
    284 	edit_redraw(was_current, CENTERING);
    285 #ifdef ENABLE_COLOR
    286 	recook |= perturbed;
    287 #endif
    288 }
    289 #endif /* ENABLE_JUSTIFY */
    290 
    291 /* Move to the preceding block of text. */
    292 void to_prev_block(void)
    293 {
    294 	linestruct *was_current = openfile->current;
    295 	bool is_text = FALSE, seen_text = FALSE;
    296 
    297 	/* Skip backward until first blank line after some nonblank line(s). */
    298 	while (openfile->current->prev != NULL && (!seen_text || is_text)) {
    299 		openfile->current = openfile->current->prev;
    300 		is_text = !white_string(openfile->current->data);
    301 		seen_text = seen_text || is_text;
    302 	}
    303 
    304 	/* Step forward one line again if we passed text but this line is blank. */
    305 	if (seen_text && openfile->current->next != NULL &&
    306 				white_string(openfile->current->data))
    307 		openfile->current = openfile->current->next;
    308 
    309 	openfile->current_x = 0;
    310 	edit_redraw(was_current, CENTERING);
    311 }
    312 
    313 /* Move to the next block of text. */
    314 void to_next_block(void)
    315 {
    316 	linestruct *was_current = openfile->current;
    317 	bool is_white = white_string(openfile->current->data);
    318 	bool seen_white = is_white;
    319 
    320 	/* Skip forward until first nonblank line after some blank line(s). */
    321 	while (openfile->current->next != NULL && (!seen_white || is_white)) {
    322 		openfile->current = openfile->current->next;
    323 		is_white = white_string(openfile->current->data);
    324 		seen_white = seen_white || is_white;
    325 	}
    326 
    327 	openfile->current_x = 0;
    328 	edit_redraw(was_current, CENTERING);
    329 #ifdef ENABLE_COLOR
    330 	recook |= perturbed;
    331 #endif
    332 }
    333 
    334 /* Move to the previous word. */
    335 void do_prev_word(void)
    336 {
    337 	bool punctuation_as_letters = ISSET(WORD_BOUNDS);
    338 	bool seen_a_word = FALSE, step_forward = FALSE;
    339 
    340 	/* Move backward until we pass over the start of a word. */
    341 	while (TRUE) {
    342 		/* If at the head of a line, move to the end of the preceding one. */
    343 		if (openfile->current_x == 0) {
    344 			if (openfile->current->prev == NULL)
    345 				break;
    346 			openfile->current = openfile->current->prev;
    347 			openfile->current_x = strlen(openfile->current->data);
    348 		}
    349 
    350 		/* Step back one character. */
    351 		openfile->current_x = step_left(openfile->current->data,
    352 												openfile->current_x);
    353 
    354 		if (is_word_char(openfile->current->data + openfile->current_x,
    355 								punctuation_as_letters)) {
    356 			seen_a_word = TRUE;
    357 			/* If at the head of a line now, this surely is a word start. */
    358 			if (openfile->current_x == 0)
    359 				break;
    360 #ifdef ENABLE_UTF8
    361 		} else if (is_zerowidth(openfile->current->data + openfile->current_x)) {
    362 			; /* skip */
    363 #endif
    364 		} else if (seen_a_word) {
    365 			/* This is space now: we've overshot the start of the word. */
    366 			step_forward = TRUE;
    367 			break;
    368 		}
    369 	}
    370 
    371 	if (step_forward)
    372 		/* Move one character forward again to sit on the start of the word. */
    373 		openfile->current_x = step_right(openfile->current->data,
    374 												openfile->current_x);
    375 }
    376 
    377 /* Move to the next word.  If after_ends is TRUE, stop at the ends of words
    378  * instead of at their beginnings.  Return TRUE if we started on a word. */
    379 bool do_next_word(bool after_ends)
    380 {
    381 	bool punctuation_as_letters = ISSET(WORD_BOUNDS);
    382 	bool started_on_word = is_word_char(openfile->current->data +
    383 								openfile->current_x, punctuation_as_letters);
    384 	bool seen_space = !started_on_word;
    385 #ifndef NANO_TINY
    386 	bool seen_word = started_on_word;
    387 #endif
    388 
    389 	/* Move forward until we reach the start of a word. */
    390 	while (TRUE) {
    391 		/* If at the end of a line, move to the beginning of the next one. */
    392 		if (openfile->current->data[openfile->current_x] == '\0') {
    393 			/* When at end of file, stop. */
    394 			if (openfile->current->next == NULL)
    395 				break;
    396 			openfile->current = openfile->current->next;
    397 			openfile->current_x = 0;
    398 			seen_space = TRUE;
    399 		} else {
    400 			/* Step forward one character. */
    401 			openfile->current_x = step_right(openfile->current->data,
    402 												openfile->current_x);
    403 		}
    404 
    405 #ifndef NANO_TINY
    406 		if (after_ends) {
    407 			/* If this is a word character, continue; else it's a separator,
    408 			 * and if we've already seen a word, then it's a word end. */
    409 			if (is_word_char(openfile->current->data + openfile->current_x,
    410 								punctuation_as_letters))
    411 				seen_word = TRUE;
    412 #ifdef ENABLE_UTF8
    413 			else if (is_zerowidth(openfile->current->data + openfile->current_x))
    414 				; /* skip */
    415 #endif
    416 			else if (seen_word)
    417 				break;
    418 		} else
    419 #endif
    420 		{
    421 #ifdef ENABLE_UTF8
    422 			if (is_zerowidth(openfile->current->data + openfile->current_x))
    423 				; /* skip */
    424 			else
    425 #endif
    426 			/* If this is not a word character, then it's a separator; else
    427 			 * if we've already seen a separator, then it's a word start. */
    428 			if (!is_word_char(openfile->current->data + openfile->current_x,
    429 								punctuation_as_letters))
    430 				seen_space = TRUE;
    431 			else if (seen_space)
    432 				break;
    433 		}
    434 	}
    435 
    436 	return started_on_word;
    437 }
    438 
    439 /* Move to the previous word in the file, and update the screen afterwards. */
    440 void to_prev_word(void)
    441 {
    442 	linestruct *was_current = openfile->current;
    443 
    444 	do_prev_word();
    445 
    446 	edit_redraw(was_current, FLOWING);
    447 }
    448 
    449 /* Move to the next word in the file.  If the AFTER_ENDS flag is set, stop
    450  * at word ends instead of beginnings.  Update the screen afterwards. */
    451 void to_next_word(void)
    452 {
    453 	linestruct *was_current = openfile->current;
    454 
    455 	do_next_word(ISSET(AFTER_ENDS));
    456 
    457 	edit_redraw(was_current, FLOWING);
    458 }
    459 
    460 /* Move to the beginning of the current line (or softwrapped chunk).
    461  * When enabled, do a smart home.  When softwrapping, go the beginning
    462  * of the full line when already at the start of a chunk. */
    463 void do_home(void)
    464 {
    465 	linestruct *was_current = openfile->current;
    466 	size_t was_column = xplustabs();
    467 	bool moved_off_chunk = TRUE;
    468 #ifndef NANO_TINY
    469 	bool moved = FALSE;
    470 	size_t leftedge = 0;
    471 	size_t left_x = 0;
    472 
    473 	if (ISSET(SOFTWRAP)) {
    474 		leftedge = leftedge_for(was_column, openfile->current);
    475 		left_x = proper_x(openfile->current, &leftedge, FALSE, leftedge, NULL);
    476 	}
    477 
    478 	if (ISSET(SMART_HOME)) {
    479 		size_t indent_x = indent_length(openfile->current->data);
    480 
    481 		if (openfile->current->data[indent_x] != '\0') {
    482 			/* If we're exactly on the indent, move fully home.  Otherwise,
    483 			 * when not softwrapping or not after the first nonblank chunk,
    484 			 * move to the first nonblank character. */
    485 			if (openfile->current_x == indent_x) {
    486 				openfile->current_x = 0;
    487 				moved = TRUE;
    488 			} else if (left_x <= indent_x) {
    489 				openfile->current_x = indent_x;
    490 				moved = TRUE;
    491 			}
    492 		}
    493 	}
    494 
    495 	if (!moved && ISSET(SOFTWRAP)) {
    496 		/* If already at the left edge of the screen, move fully home.
    497 		 * Otherwise, move to the left edge. */
    498 		if (openfile->current_x == left_x)
    499 			openfile->current_x = 0;
    500 		else {
    501 			openfile->current_x = left_x;
    502 			openfile->placewewant = leftedge;
    503 			moved_off_chunk = FALSE;
    504 		}
    505 	} else if (!moved)
    506 #endif
    507 		openfile->current_x = 0;
    508 
    509 	if (moved_off_chunk)
    510 		openfile->placewewant = xplustabs();
    511 
    512 	/* If we changed chunk, we might be offscreen.  Otherwise,
    513 	 * update current if the mark is on or we changed "page". */
    514 	if (ISSET(SOFTWRAP) && moved_off_chunk)
    515 		edit_redraw(was_current, FLOWING);
    516 	else if (line_needs_update(was_column, openfile->placewewant))
    517 		update_line(openfile->current, openfile->current_x);
    518 }
    519 
    520 /* Move to the end of the current line (or softwrapped chunk).
    521  * When softwrapping and already at the end of a chunk, go to the
    522  * end of the full line. */
    523 void do_end(void)
    524 {
    525 	linestruct *was_current = openfile->current;
    526 	size_t was_column = xplustabs();
    527 	size_t line_len = strlen(openfile->current->data);
    528 	bool moved_off_chunk = TRUE;
    529 
    530 #ifndef NANO_TINY
    531 	if (ISSET(SOFTWRAP)) {
    532 		bool kickoff = TRUE;
    533 		bool last_chunk = FALSE;
    534 		size_t leftedge = leftedge_for(was_column, openfile->current);
    535 		size_t rightedge = get_softwrap_breakpoint(openfile->current->data,
    536 											leftedge, &kickoff, &last_chunk);
    537 		size_t right_x;
    538 
    539 		/* If we're on the last chunk, we're already at the end of the line.
    540 		 * Otherwise, we're one column past the end of the line.  Shifting
    541 		 * backwards one column might put us in the middle of a multi-column
    542 		 * character, but actual_x() will fix that. */
    543 		if (!last_chunk)
    544 			rightedge--;
    545 
    546 		right_x = actual_x(openfile->current->data, rightedge);
    547 
    548 		/* If already at the right edge of the screen, move fully to
    549 		 * the end of the line.  Otherwise, move to the right edge. */
    550 		if (openfile->current_x == right_x)
    551 			openfile->current_x = line_len;
    552 		else {
    553 			openfile->current_x = right_x;
    554 			openfile->placewewant = rightedge;
    555 			moved_off_chunk = FALSE;
    556 		}
    557 	} else
    558 #endif
    559 		openfile->current_x = line_len;
    560 
    561 	if (moved_off_chunk)
    562 		openfile->placewewant = xplustabs();
    563 
    564 	/* If we changed chunk, we might be offscreen.  Otherwise,
    565 	 * update current if the mark is on or we changed "page". */
    566 	if (ISSET(SOFTWRAP) && moved_off_chunk)
    567 		edit_redraw(was_current, FLOWING);
    568 	else if (line_needs_update(was_column, openfile->placewewant))
    569 		update_line(openfile->current, openfile->current_x);
    570 }
    571 
    572 /* Move the cursor to the preceding line or chunk. */
    573 void do_up(void)
    574 {
    575 	linestruct *was_current = openfile->current;
    576 	size_t leftedge, target_column;
    577 
    578 	get_edge_and_target(&leftedge, &target_column);
    579 
    580 	/* If we can't move up one line or chunk, we're at top of file. */
    581 	if (go_back_chunks(1, &openfile->current, &leftedge) > 0)
    582 		return;
    583 
    584 	set_proper_index_and_pww(&leftedge, target_column, FALSE);
    585 
    586 	if (openfile->cursor_row == 0 && !ISSET(JUMPY_SCROLLING) &&
    587 						(tabsize < editwincols || !ISSET(SOFTWRAP)))
    588 		edit_scroll(BACKWARD);
    589 	else
    590 		edit_redraw(was_current, FLOWING);
    591 
    592 	/* <Up> should not change placewewant, so restore it. */
    593 	openfile->placewewant = leftedge + target_column;
    594 }
    595 
    596 /* Move the cursor to next line or chunk. */
    597 void do_down(void)
    598 {
    599 	linestruct *was_current = openfile->current;
    600 	size_t leftedge, target_column;
    601 
    602 	get_edge_and_target(&leftedge, &target_column);
    603 
    604 	/* If we can't move down one line or chunk, we're at bottom of file. */
    605 	if (go_forward_chunks(1, &openfile->current, &leftedge) > 0)
    606 		return;
    607 
    608 	set_proper_index_and_pww(&leftedge, target_column, TRUE);
    609 
    610 	if (openfile->cursor_row == editwinrows - 1 && !ISSET(JUMPY_SCROLLING) &&
    611 								(tabsize < editwincols || !ISSET(SOFTWRAP)))
    612 		edit_scroll(FORWARD);
    613 	else
    614 		edit_redraw(was_current, FLOWING);
    615 
    616 	/* <Down> should not change placewewant, so restore it. */
    617 	openfile->placewewant = leftedge + target_column;
    618 }
    619 
    620 #if !defined(NANO_TINY) || defined(ENABLE_HELP)
    621 /* Scroll up one line or chunk without moving the cursor textwise. */
    622 void do_scroll_up(void)
    623 {
    624 	/* When the top of the file is onscreen, we can't scroll. */
    625 	if (openfile->edittop->prev == NULL && openfile->firstcolumn == 0)
    626 		return;
    627 
    628 	if (openfile->cursor_row == editwinrows - 1)
    629 		do_up();
    630 
    631 	if (editwinrows > 1)
    632 		edit_scroll(BACKWARD);
    633 }
    634 
    635 /* Scroll down one line or chunk without moving the cursor textwise. */
    636 void do_scroll_down(void)
    637 {
    638 	if (openfile->cursor_row == 0)
    639 		do_down();
    640 
    641 	if (editwinrows > 1 && (openfile->edittop->next != NULL
    642 #ifndef NANO_TINY
    643 				|| (ISSET(SOFTWRAP) && (extra_chunks_in(openfile->edittop) >
    644 					chunk_for(openfile->firstcolumn, openfile->edittop)))
    645 #endif
    646 										))
    647 		edit_scroll(FORWARD);
    648 }
    649 #endif /* !NANO_TINY || ENABLE_HELP */
    650 
    651 /* Move left one character. */
    652 void do_left(void)
    653 {
    654 	linestruct *was_current = openfile->current;
    655 
    656 	if (openfile->current_x > 0) {
    657 		openfile->current_x = step_left(openfile->current->data,
    658 												openfile->current_x);
    659 #ifdef ENABLE_UTF8
    660 		while (openfile->current_x > 0 &&
    661 					is_zerowidth(openfile->current->data + openfile->current_x))
    662 			openfile->current_x = step_left(openfile->current->data,
    663 												openfile->current_x);
    664 #endif
    665 	} else if (openfile->current != openfile->filetop) {
    666 		openfile->current = openfile->current->prev;
    667 		openfile->current_x = strlen(openfile->current->data);
    668 	}
    669 
    670 	edit_redraw(was_current, FLOWING);
    671 }
    672 
    673 /* Move right one character. */
    674 void do_right(void)
    675 {
    676 	linestruct *was_current = openfile->current;
    677 
    678 	if (openfile->current->data[openfile->current_x] != '\0') {
    679 		openfile->current_x = step_right(openfile->current->data,
    680 												openfile->current_x);
    681 #ifdef ENABLE_UTF8
    682 		while (openfile->current->data[openfile->current_x] != '\0' &&
    683 					is_zerowidth(openfile->current->data + openfile->current_x))
    684 			openfile->current_x = step_right(openfile->current->data,
    685 												openfile->current_x);
    686 #endif
    687 	} else if (openfile->current != openfile->filebot) {
    688 		openfile->current = openfile->current->next;
    689 		openfile->current_x = 0;
    690 	}
    691 
    692 	edit_redraw(was_current, FLOWING);
    693 }