Example #1
0
File: cut.c Project: ris21/yoda
/* Move all currently marked text into the cutbuffer, and set the
 * current place we want to where the text used to start. */
void cut_marked(void)
{
    linestruct *top, *bot;
    size_t top_x, bot_x;

    mark_order((const linestruct **)&top, &top_x,
	(const linestruct **)&bot, &bot_x, NULL);

    move_to_filestruct(&cutbuffer, &cutbottom, top, top_x, bot, bot_x);
    openfile->placewewant = xplustabs();
}
Example #2
0
File: search.c Project: ris21/yoda
/* Step through each replace word and prompt user before replacing.
 * Parameters real_current and real_current_x are needed in order to
 * allow the cursor position to be updated when a word before the cursor
 * is replaced by a shorter word.
 *
 * needle is the string to seek.  We replace it with answer.  Return -1
 * if needle isn't found, else the number of replacements performed.  If
 * canceled isn't NULL, set it to TRUE if we canceled. */
ssize_t do_replace_loop(
#ifndef DISABLE_SPELLER
	bool whole_word_only,
#endif
	bool *canceled, const linestruct *real_current, size_t
	*real_current_x, const char *needle)
{
    ssize_t numreplaced = -1;
    size_t match_len;
    bool replaceall = FALSE;
#ifndef NANO_TINY
    bool old_mark_set = openfile->mark_set;
    linestruct *top, *bot;
    size_t top_x, bot_x;
    bool right_side_up = FALSE;
	/* TRUE if (mark_begin, mark_begin_x) is the top of the mark,
	 * FALSE if (current, current_x) is. */

    if (old_mark_set) {
	/* If the mark is on, frame the region, and turn the mark off. */
	mark_order((const linestruct **)&top, &top_x,
	    (const linestruct **)&bot, &bot_x, &right_side_up);
	openfile->mark_set = FALSE;

	/* Start either at the top or the bottom of the marked region. */
	if (!ISSET(BACKWARDS_SEARCH)) {
	    openfile->current = top;
	    openfile->current_x = (top_x == 0 ? 0 : top_x - 1);
	} else {
	    openfile->current = bot;
	    openfile->current_x = bot_x;
	}
    }
#endif /* !NANO_TINY */

    if (canceled != NULL)
	*canceled = FALSE;

    findnextstr_wrap_reset();
    while (findnextstr(
#ifndef DISABLE_SPELLER
	whole_word_only,
#endif
	real_current, *real_current_x, needle, &match_len)) {
	int i = 0;

#ifndef NANO_TINY
	if (old_mark_set) {
	    /* When we've found an occurrence outside of the marked region,
	     * stop the fanfare. */
	    if (openfile->current->lineno > bot->lineno ||
		openfile->current->lineno < top->lineno ||
		(openfile->current == bot && openfile->current_x > bot_x) ||
		(openfile->current == top && openfile->current_x < top_x))
		break;
	}
#endif

	/* Indicate that we found the search string. */
	if (numreplaced == -1)
	    numreplaced = 0;

	if (!replaceall) {
	    size_t xpt = xplustabs();
	    char *exp_word = display_string(openfile->current->data,
		xpt, strnlenpt(openfile->current->data,
		openfile->current_x + match_len) - xpt, FALSE);

	    edit_refresh();

	    curs_set(0);

	    do_replace_highlight(TRUE, exp_word);

	    /* TRANSLATORS: This is a prompt. */
	    i = do_yesno_prompt(TRUE, _("Replace this instance?"));

	    do_replace_highlight(FALSE, exp_word);

	    free(exp_word);

	    curs_set(1);

	    if (i == -1) {	/* We canceled the replace. */
		if (canceled != NULL)
		    *canceled = TRUE;
		break;
	    }
	}

	if (i > 0 || replaceall) {	/* Yes, replace it!!!! */
	    char *copy;
	    size_t length_change;

#ifndef NANO_TINY
	    add_undo(REPLACE);
#endif
	    if (i == 2)
		replaceall = TRUE;

	    copy = replace_line(needle);

	    length_change = strlen(copy) - strlen(openfile->current->data);

#ifndef NANO_TINY
	    /* If the mark was on and (mark_begin, mark_begin_x) was the
	     * top of it, don't change mark_begin_x. */
	    if (!old_mark_set || !right_side_up) {
		/* Keep mark_begin_x in sync with the text changes. */
		if (openfile->current == openfile->mark_begin &&
			openfile->mark_begin_x > openfile->current_x) {
		    if (openfile->mark_begin_x < openfile->current_x +
			match_len)
			openfile->mark_begin_x = openfile->current_x;
		    else
			openfile->mark_begin_x += length_change;
		    bot_x = openfile->mark_begin_x;
		}
	    }

	    /* If the mark was on and (current, current_x) was the top
	     * of it, don't change real_current_x. */
	    if (!old_mark_set || right_side_up) {
#endif
		/* Keep real_current_x in sync with the text changes. */
		if (openfile->current == real_current &&
			openfile->current_x <= *real_current_x) {
		    if (*real_current_x < openfile->current_x + match_len)
			*real_current_x = openfile->current_x + match_len;
		    *real_current_x += length_change;
#ifndef NANO_TINY
		    bot_x = *real_current_x;
		}
#endif
	    }

	    /* Set the cursor at the last character of the replacement
	     * text, so searching will resume after the replacement
	     * text.  Note that current_x might be set to (size_t)-1
	     * here. */
#ifndef NANO_TINY
	    if (!ISSET(BACKWARDS_SEARCH))
#endif
		openfile->current_x += match_len + length_change - 1;

	    /* Clean up. */
	    openfile->totsize += mbstrlen(copy) -
		mbstrlen(openfile->current->data);
	    free(openfile->current->data);
	    openfile->current->data = copy;

#ifndef DISABLE_COLOR
	    /* Reset the precalculated multiline-regex hints only when
	     * the first replacement has been made. */
	    if (numreplaced == 0)
		reset_multis(openfile->current, TRUE);
#endif

	    if (!replaceall) {
#ifndef DISABLE_COLOR
		/* If color syntaxes are available and turned on, we
		 * need to call edit_refresh(). */
		if (openfile->colorstrings != NULL &&
			!ISSET(NO_COLOR_SYNTAX))
		    edit_refresh();
		else
#endif
		    update_line(openfile->current, openfile->current_x);
	    }

	    set_modified();
	    numreplaced++;
	}
    }

    if (numreplaced == -1)
	not_found_msg(needle);

#ifndef NANO_TINY
    if (old_mark_set)
	openfile->mark_set = TRUE;
#endif

    /* If the NO_NEWLINES flag isn't set, and text has been added to the
     * magicline, make a new magicline. */
    if (!ISSET(NO_NEWLINES) && openfile->filebot->data[0] != '\0')
	new_magicline();

    return numreplaced;
}
Example #3
0
/* edit_draw() takes care of the job of actually painting a line into
 * the edit window.  fileptr is the line to be painted, at row line of
 * the window.  converted is the actual string to be written to the
 * window, with tabs and control characters replaced by strings of
 * regular characters.  start is the column number of the first
 * character of this page.  That is, the first character of converted
 * corresponds to character number actual_x(fileptr->data, start) of the
 * line. */
void edit_draw(filestruct *fileptr, const char *converted, int line, size_t start)
{
	size_t startpos = actual_x(fileptr->data, start);
	/* The position in fileptr->data of the leftmost character
	 * that displays at least partially on the window. */
	size_t endpos = actual_x(fileptr->data, start + COLS - 1) + 1;
	/* The position in fileptr->data of the first character that is
	 * completely off the window to the right.
	 *
	 * Note that endpos might be beyond the null terminator of the
	 * string. */

	assert(openfile != openfiles.end() && fileptr != NULL && converted != NULL);
	assert(strlenpt(converted) <= COLS);

	/* Just paint the string in any case (we'll add color or reverse on
	 * just the text that needs it). */
	mvwaddstr(edit, line, 0, converted);
	/* Tell ncurses to really redraw the line without trying to optimize
	 * for what it thinks is already there, because it gets it wrong in
	 * the case of a wide character in column zero. */
#ifndef USE_SLANG
	wredrawln(edit, line, 1);
#endif

	/* If color syntaxes are available and turned on, we need to display
	 * them. */
	if (!openfile->colorstrings.empty() && !ISSET(NO_COLOR_SYNTAX)) {
		/* Set up multi-line color data for this line if it's not yet calculated  */
		if (fileptr->multidata.empty() && openfile->syntax && openfile->syntax->nmultis > 0) {
			fileptr->multidata.resize(openfile->syntax->nmultis, -1); // assume that '-1' applies until we know otherwise
		}
		for (auto tmpcolor : openfile->colorstrings) {
			int x_start;
			/* Starting column for mvwaddnstr.  Zero-based. */
			int paintlen = 0;
			/* Number of chars to paint on this line.  There are
			 * COLS characters on a whole line. */
			size_t index;
			/* Index in converted where we paint. */
			regmatch_t startmatch;
			/* Match position for start_regex. */
			regmatch_t endmatch;
			/* Match position for end_regex. */

			if (tmpcolor->bright) {
				wattron(edit, A_BOLD);
			}
			if (tmpcolor->underline) {
				wattron(edit, A_UNDERLINE);
			}
			wattron(edit, COLOR_PAIR(tmpcolor->pairnum));
			/* Two notes about regexec().  A return value of zero means
			 * that there is a match.  Also, rm_eo is the first
			 * non-matching character after the match. */

			/* First case,tmpcolor is a single-line expression. */
			if (tmpcolor->end == NULL) {
				size_t k = 0;

				/* We increment k by rm_eo, to move past the end of the
				 * last match.  Even though two matches may overlap, we
				 * want to ignore them, so that we can highlight e.g. C
				 * strings correctly. */
				while (k < endpos) {
					/* Note the fifth parameter to regexec().  It says
					 * not to match the beginning-of-line character
					 * unless k is zero.  If regexec() returns
					 * REG_NOMATCH, there are no more matches in the
					 * line. */
					if (regexec(tmpcolor->start, &fileptr->data[k], 1, &startmatch, (k == 0) ? 0 : REG_NOTBOL) == REG_NOMATCH) {
						break;
					}


					/* Translate the match to the beginning of the
					 * line. */
					startmatch.rm_so += k;
					startmatch.rm_eo += k;

					/* Skip over a zero-length regex match. */
					if (startmatch.rm_so == startmatch.rm_eo) {
						startmatch.rm_eo++;
					} else if (startmatch.rm_so < endpos && startmatch.rm_eo > startpos) {
						x_start = (startmatch.rm_so <= startpos) ? 0 : strnlenpt(fileptr->data, startmatch.rm_so) - start;

						index = actual_x(converted, x_start);

						paintlen = actual_x(converted + index, strnlenpt(fileptr->data, startmatch.rm_eo) - start - x_start);

						assert(0 <= x_start && 0 <= paintlen);

						mvwaddnstr(edit, line, x_start, converted + index, paintlen);
					}
					k = startmatch.rm_eo;
				}
			} else if (!fileptr->multidata.empty() && fileptr->multidata[tmpcolor->id] != CNONE) {
				/* This is a multi-line regex.  There are two steps.
				 * First, we have to see if the beginning of the line is
				 * colored by a start on an earlier line, and an end on
				 * this line or later.
				 *
				 * We find the first line before fileptr matching the
				 * start.  If every match on that line is followed by an
				 * end, then go to step two.  Otherwise, find the next
				 * line after start_line matching the end.  If that line
				 * is not before fileptr, then paint the beginning of
				 * this line. */
				const filestruct *start_line = fileptr->prev;
				/* The first line before fileptr matching start. */
				regoff_t start_col;
				/* Where it starts in that line. */
				const filestruct *end_line;
				short md = fileptr->multidata[tmpcolor->id];

				if (md == -1) {
					fileptr->multidata[tmpcolor->id] = CNONE;    /* until we find out otherwise */
				} else if (md == CNONE) {
					unset_formatting(tmpcolor);
					continue;
				} else if (md == CWHOLELINE) {
					mvwaddnstr(edit, line, 0, converted, -1);
					unset_formatting(tmpcolor);
					continue;
				} else if (md == CBEGINBEFORE) {
					regexec(tmpcolor->end, fileptr->data, 1, &endmatch, 0);
					paintlen = actual_x(converted, strnlenpt(fileptr->data, endmatch.rm_eo) - start);
					mvwaddnstr(edit, line, 0, converted, paintlen);
					unset_formatting(tmpcolor);
					continue;
				}

				while (start_line != NULL && regexec(tmpcolor->start, start_line->data, 1, &startmatch, 0) == REG_NOMATCH) {
					/* If there is an end on this line, there is no need
					 * to look for starts on earlier lines. */
					if (regexec(tmpcolor->end, start_line->data, 0, NULL, 0) == 0) {
						goto step_two;
					}
					start_line = start_line->prev;
				}

				/* If the found start has been qualified as an end earlier, believe it and skip to the next step. */
				if (start_line != NULL && !start_line->multidata.empty() && start_line->multidata[tmpcolor->id] == CBEGINBEFORE) {
					goto step_two;
				}

				/* Skip over a zero-length regex match. */
				if (startmatch.rm_so == startmatch.rm_eo) {
					startmatch.rm_eo++;
				} else {
					/* No start found, so skip to the next step. */
					if (start_line == NULL) {
						goto step_two;
					}
					/* Now start_line is the first line before fileptr
					 * containing a start match.  Is there a start on
					 * this line not followed by an end on this line? */
					start_col = 0;
					while (true) {
						start_col += startmatch.rm_so;
						startmatch.rm_eo -= startmatch.rm_so;
						if (regexec(tmpcolor->end, start_line->data + start_col + startmatch.rm_eo, 0, NULL, (start_col + startmatch.rm_eo == 0) ? 0 : REG_NOTBOL) == REG_NOMATCH) {
							/* No end found after this start. */
							break;
						}
						start_col++;
						if (regexec(tmpcolor->start, start_line->data + start_col, 1, &startmatch, REG_NOTBOL) == REG_NOMATCH) {
							/* No later start on this line. */
							goto step_two;
						}
					}
					/* Indeed, there is a start not followed on this
					 * line by an end. */

					/* We have already checked that there is no end
					 * before fileptr and after the start.  Is there an
					 * end after the start at all?  We don't paint
					 * unterminated starts. */
					end_line = fileptr;
					while (end_line != NULL && regexec(tmpcolor->end, end_line->data, 1, &endmatch, 0) == REG_NOMATCH) {
						end_line = end_line->next;
					}

					/* No end found, or it is too early. */
					if (end_line == NULL || (end_line == fileptr && endmatch.rm_eo <= startpos)) {
						goto step_two;
					}

					/* Now paint the start of fileptr.  If the start of
					 * fileptr is on a different line from the end,
					 * paintlen is -1, meaning that everything on the
					 * line gets painted.  Otherwise, paintlen is the
					 * expanded location of the end of the match minus
					 * the expanded location of the beginning of the
					 * page. */
					if (end_line != fileptr) {
						paintlen = -1;
						fileptr->multidata[tmpcolor->id] = CWHOLELINE;
					} else {
						paintlen = actual_x(converted, strnlenpt(fileptr->data, endmatch.rm_eo) - start);
						fileptr->multidata[tmpcolor->id] = CBEGINBEFORE;
					}
					mvwaddnstr(edit, line, 0, converted, paintlen);
					/* If the whole line has been painted, don't bother looking for any more starts. */
					if (paintlen < 0) {
						continue;
					}
step_two:
					/* Second step, we look for starts on this line. */
					start_col = 0;

					while (start_col < endpos) {
						if (regexec(tmpcolor->start, fileptr->data + start_col, 1, &startmatch, (start_col == 0) ? 0 : REG_NOTBOL) == REG_NOMATCH || start_col + startmatch.rm_so >= endpos) {
							/* No more starts on this line. */
							break;
						}
						/* Translate the match to be relative to the
						 * beginning of the line. */
						startmatch.rm_so += start_col;
						startmatch.rm_eo += start_col;

						x_start = (startmatch.rm_so <= startpos) ? 0 : strnlenpt(fileptr->data, startmatch.rm_so) - start;

						index = actual_x(converted, x_start);

						if (regexec(tmpcolor->end, fileptr->data + startmatch.rm_eo, 1, &endmatch, (startmatch.rm_eo == 0) ? 0 : REG_NOTBOL) == 0) {
							/* Translate the end match to be relative to the beginning of the line. */
							endmatch.rm_so += startmatch.rm_eo;
							endmatch.rm_eo += startmatch.rm_eo;
							/* There is an end on this line.  But does
							 * it appear on this page, and is the match
							 * more than zero characters long? */
							if (endmatch.rm_eo > startpos && endmatch.rm_eo > startmatch.rm_so) {
								paintlen = actual_x(converted + index, strnlenpt(fileptr->data, endmatch.rm_eo) - start - x_start);

								assert(0 <= x_start && x_start < COLS);

								mvwaddnstr(edit, line, x_start, converted + index, paintlen);
								if (paintlen > 0) {
									fileptr->multidata[tmpcolor->id] = CSTARTENDHERE;
								}

							}
						} else {
							/* There is no end on this line.  But we
							 * haven't yet looked for one on later
							 * lines. */
							end_line = fileptr->next;

							while (end_line != NULL && regexec(tmpcolor->end, end_line->data, 0, NULL, 0) == REG_NOMATCH) {
								end_line = end_line->next;
							}

							if (end_line != NULL) {
								assert(0 <= x_start && x_start < COLS);

								mvwaddnstr(edit, line, x_start, converted + index, -1);
								/* We painted to the end of the line, so
								 * don't bother checking any more
								 * starts. */
								fileptr->multidata[tmpcolor->id] = CENDAFTER;
								break;
							}
						}
						start_col = startmatch.rm_so + 1;
					}
				}
			}
			unset_formatting(tmpcolor);
		}
	}

	/* If the mark is on, we need to display it. */
	if (openfile->mark_set && (fileptr->lineno <=
	                           openfile->mark_begin->lineno || fileptr->lineno <=
	                           openfile->current->lineno) && (fileptr->lineno >=
	                                   openfile->mark_begin->lineno || fileptr->lineno >=
	                                   openfile->current->lineno)) {
		/* fileptr is at least partially selected. */
		const filestruct *top;
		/* Either current or mark_begin, whichever is first. */
		size_t top_x;
		/* current_x or mark_begin_x, corresponding to top. */
		const filestruct *bot;
		size_t bot_x;
		int x_start;
		/* Starting column for mvwaddnstr().  Zero-based. */
		int paintlen;
		/* Number of characters to paint on this line.  There are
		 * COLS characters on a whole line. */
		size_t index;
		/* Index in converted where we paint. */

		mark_order(&top, &top_x, &bot, &bot_x, NULL);

		if (top->lineno < fileptr->lineno || top_x < startpos) {
			top_x = startpos;
		}
		if (bot->lineno > fileptr->lineno || bot_x > endpos) {
			bot_x = endpos;
		}

		/* The selected bit of fileptr is on this page. */
		if (top_x < endpos && bot_x > startpos) {
			assert(startpos <= top_x);

			/* x_start is the expanded location of the beginning of the
			 * mark minus the beginning of the page. */
			x_start = strnlenpt(fileptr->data, top_x) - start;

			/* If the end of the mark is off the page, paintlen is -1,
			 * meaning that everything on the line gets painted.
			 * Otherwise, paintlen is the expanded location of the end
			 * of the mark minus the expanded location of the beginning
			 * of the mark. */
			if (bot_x >= endpos) {
				paintlen = -1;
			} else
				paintlen = strnlenpt(fileptr->data, bot_x) - (x_start + start);

			/* If x_start is before the beginning of the page, shift
			 * paintlen x_start characters to compensate, and put
			 * x_start at the beginning of the page. */
			if (x_start < 0) {
				paintlen += x_start;
				x_start = 0;
			}

			assert(x_start >= 0 && x_start <= strlen(converted));

			index = actual_x(converted, x_start);

			if (paintlen > 0) {
				paintlen = actual_x(converted + index, paintlen);
			}

			set_color(edit, interface_colors[FUNCTION_TAG]);
			mvwaddnstr(edit, line, x_start, converted + index, paintlen);
			clear_color(edit, interface_colors[FUNCTION_TAG]);
		}
	}
}