Compare commits

...

29 Commits

Author SHA1 Message Date
070b784a49 Merge branch 'master' into rexy712 2023-02-28 15:34:34 -08:00
Benno Schulenberg
7681090c12 docs: clarify that a fileregex is matched against the absolute filename 2023-02-16 11:02:15 +01:00
Benno Schulenberg
866490c41f docs: add two examples of custom key bindings to the nanorc manpage 2023-02-16 10:44:10 +01:00
Benno Schulenberg
0662fc4d42 shutdown: ignore a modified buffer when in view mode
In view mode it should be impossible to modify any buffer, but...
when (through some bug) the user did succeed in modifying a buffer,
this should not lead to writing out this modified buffer to disk.

Had this safety stop been present earlier, it would have prevented
the second part of https://savannah.gnu.org/bugs/?63616.
2023-02-14 10:25:38 +01:00
Benno Schulenberg
078d612b9e tweaks: add a missing 'type' attribute to a <style> tag
To stop the W3 validator from complaining.
2023-02-03 17:30:27 +01:00
Benno Schulenberg
26025f79ce docs: add a reference to the 'help-nano' mailing list 2023-01-30 17:13:17 +01:00
Benno Schulenberg
1c307bc1be docs: add a clarifying note to the description of --tabstospaces 2023-01-26 17:01:48 +01:00
Benno Schulenberg
7abddbd752 tweaks: slightly improve a comment, to be more accurate
A string bind can only contain bytes (chars), not keycodes (integers,
in nano upto 0x4FF).  So, apart from an error code or a placeholder
command code, get_code_from_plantation() can only return a byte.
2023-01-26 17:01:02 +01:00
Benno Schulenberg
51c9f7270c input: let the handler of string binds return a byte whenever possible
The function get_code_from_plantation() should return ERR only when
the string bind is fully exhausted.  In the normal case, where some
bytes are still available, it should return the first of these bytes,
so that the {verbatim} function will work too.

This fixes https://savannah.gnu.org/bugs/?63702.

Bug existed since version 7.0, commit 958ec294,
since command cartouches were introduced.
2023-01-26 10:01:18 +01:00
Benno Schulenberg
b896670e85 tweaks: make two strings equal to a third, to slightly ease translation 2023-01-25 16:45:50 +01:00
Benno Schulenberg
69dd0c40bb help: give the "Replace with" prompt its own help text
This addresses https://savannah.gnu.org/bugs/?63691.

Problem existed since the help texts were introduced,
in version 1.1.3, commit b3655b4c.
2023-01-23 16:41:25 +01:00
Matteo Raso
c374c773ad syntax: python: colorize decorators specially
Decorators are documented at https://peps.python.org/pep-0318/.

Signed-off-by: Matteo Raso <matteo_luigi_raso@protonmail.com>
Signed-off-by: Benno Schulenberg <bensberg@telfort.nl>
2023-01-22 12:31:45 +01:00
18eb6bae01 Merge branch 'master' into rexy712 2023-01-21 09:26:20 -08:00
Benno Schulenberg
2fd7888a51 bindings: show ^- instead of ^/ for 'flipgoto' when on a Linux console
This should have been part of commit 344db835 from three days ago.
2023-01-20 14:55:11 +01:00
b5809f3793 Merge branch 'master' into rexy712 2022-12-19 13:04:14 -08:00
9980a98607 Merge branch 'master' into rexy712 2022-12-04 04:32:02 -08:00
442bd49137 Merge branch 'master' into rexy712 2022-11-10 16:37:54 -08:00
263c7fefbf Merge branch 'master' into rexy712 2022-10-04 16:06:16 -07:00
02249f1655 Merge changes in master 2022-08-24 13:03:13 -07:00
ac58a97a92 Merge branch 'master' of https://git.savannah.gnu.org/git/nano into rexy712 2022-07-22 13:09:24 -07:00
140f2d72cb Fix cutting selection containing a folded segment not unfolding before adding to cutbuffer 2022-07-22 13:06:05 -07:00
b81dca5014 Merge branch 'master' into rexy712 2022-07-15 16:39:56 -07:00
0ac593a9ce Cleanup some folding.c 2022-07-13 12:19:08 -07:00
8daf175b09 Remove color support 2022-07-13 11:58:38 -07:00
49935c0456 Fix tiny build. Rename things to hopefully be more fitting 2022-07-12 12:33:41 -07:00
b145ce016b Fix undoing an enter would not always properly unfold 2022-07-11 15:33:30 -07:00
226108a59f Fix do_find_bracket 2022-07-11 15:23:49 -07:00
01b30a5a0e Change folding behavior when no lines are selected as suggested by the nano dev 2022-07-11 15:11:19 -07:00
05fa7110ec Add initial folding line support
This has not been approved to be merged into master by any means but
I need to be able to track the status of my patch. So I'm starting
this branch and self hosted repository for myself.

Signed-off-by: rexy712 <rexy712@protonmail.com>
2022-07-10 20:42:01 -07:00
19 changed files with 857 additions and 156 deletions

View File

@ -128,6 +128,20 @@ if test "x$enable_color" != xno; then
color_support=yes
fi
AC_ARG_ENABLE(folding,
AS_HELP_STRING([--disable-folding], [Disable line folding support]))
if test "x$enable_tiny" = xyes; then
if test "x$enable_folding" = xyes; then
AC_MSG_ERROR([
*** --enable-folding cannot work with --enable-tiny])
else
enable_folding=no
fi
fi
if test "x$enable_folding" != xno; then
AC_DEFINE(ENABLE_FOLDING, 1, [Define this to have line folding support.])
fi
AC_ARG_ENABLE(comment,
AS_HELP_STRING([--disable-comment], [Disable the comment/uncomment function]))
if test "x$enable_tiny" = xyes; then

View File

@ -5,7 +5,7 @@
<title>The GNU nano editor FAQ</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta name="description" content="The genesis story of the nano editor, plus the solution to some common problems.">
<style>
<style type="text/css">
.indented { margin-left: 2em; font-family: courier; font-size: 110%; }
</style>
</head>

View File

@ -112,6 +112,7 @@ nanorc file. See \fBnanorc\fR(5).
.BR \-E ", " \-\-tabstospaces
Convert each typed tab to spaces -- to the number of spaces
that a tab at that position would take up.
(Note: pasted tabs are not converted.)
.TP
.BR \-F ", " \-\-multibuffer
Read a file into a new buffer by default.
@ -391,6 +392,9 @@ Suspension is enabled by default, reachable via \fB^T^Z\fR.
(If you want a plain \fB^Z\fR to suspend nano, add
\fBbind ^Z suspend main\fR to your nanorc.)
.sp
When you want to copy marked text from \fBnano\fR to the system's clipboard,
see one of the examples in the \fBnanorc\fR(5) man page.
.sp
If no alternative spell checker command is specified on the command
line nor in one of the \fInanorc\fP files, \fBnano\fP will check the
\fBSPELL\fP environment variable for one.
@ -404,6 +408,9 @@ that name already exists in the current directory, it will add ".save"
plus a number (e.g.\& ".save.1") to the current filename in order to make
it unique. In multibuffer mode, \fBnano\fP will write all the open
buffers to their respective emergency files.
.sp
If you have any question about how to use \fBnano\fR in some specific
situation, you can ask on \fIhelp-nano@gnu.org\fR.
.SH BUGS
The recording and playback of keyboard macros works correctly only on a

View File

@ -123,6 +123,9 @@ following options: @option{--breaklonglines},
@blankline
Please report bugs via @url{https://savannah.gnu.org/bugs/?group=nano}.
@blankline
Questions about using nano you can ask at @email{help-nano@@gnu.org}.
@blankline
For background information see @url{https://nano-editor.org/}.
@ -406,6 +409,7 @@ nanorc file. @xref{@code{set keycolor}} for details.
@itemx --tabstospaces
Convert each typed tab to spaces --- to the number of spaces
that a tab at that position would take up.
(Note: pasted tabs are not converted.)
@item -F
@itemx --multibuffer
@ -1140,6 +1144,7 @@ greater than 0. The default value is @t{8}.
@item set tabstospaces
Convert each typed tab to spaces --- to the number of spaces
that a tab at that position would take up.
(Note: pasted tabs are not converted.)
@item set titlecolor [bold,][italic,]@var{fgcolor},@var{bgcolor}
Use this color combination for the title bar.
@ -1230,9 +1235,10 @@ will be added to this syntax, until a new @code{syntax}
command is encountered.
When @command{nano} is run, this syntax will be automatically
activated if the current filename matches the extended regular
expression @var{fileregex}. Or the syntax can be explicitly
activated by using the @option{-Y} or @option{--syntax}
activated (for the relevant buffer) if the absolute filename
matches the extended regular expression @var{fileregex}.
Or the syntax can be explicitly activated (for all buffers)
by using the @option{-Y} or @option{--syntax}
command-line option followed by the @var{name}.
The @code{default} syntax is special: it takes no @var{fileregex},

View File

@ -335,6 +335,7 @@ greater than 0. The default value is \fB8\fR.
.B set tabstospaces
Convert each typed tab to spaces -- to the number of spaces
that a tab at that position would take up.
(Note: pasted tabs are not converted.)
.TP
.B set titlecolor \fR[\fBbold,\fR][\fBitalic,\fR]\fIfgcolor\fB,\fIbgcolor\fR
Use this color combination for the title bar.
@ -398,12 +399,6 @@ and disappears after 1.5 seconds or upon the next keystroke.
With \fBM\-Z\fR the title bar plus status bar can be toggled.
With \fBM\-X\fR the help lines.
.SH NOTES
Option \fBset suspendable\fR has been removed.
Suspension is enabled by default, reachable via \fB^T^Z\fR.
(If you want a plain \fB^Z\fR to suspend nano,
add \fBbind ^Z suspend main\fR to your nanorc.)
.SH SYNTAX HIGHLIGHTING
Coloring the different syntactic elements of a file
is done via regular expressions (see the \fBcolor\fR command below).
@ -444,9 +439,10 @@ will be added to this syntax, until a new \fBsyntax\fR
command is encountered.
.sp
When \fBnano\fR is run, this syntax will be automatically
activated if the current filename matches the extended regular
expression \fIfileregex\fR. Or the syntax can be explicitly
activated by using the \fB\-Y\fR or \fB\-\-syntax\fR
activated (for the relevant buffer) if the absolute filename
matches the extended regular expression \fIfileregex\fR.
Or the syntax can be explicitly activated (for all buffers)
by using the \fB\-Y\fR or \fB\-\-syntax\fR
command-line option followed by the \fIname\fR.
.sp
The syntax \fBdefault\fP is special: it takes no \fIfileregex\fR,
@ -1038,6 +1034,20 @@ For \fBbind\fR it means all menus where the specified \fIfunction\fR exists;
for \fBunbind\fR it means all menus where the specified \fIkey\fR exists.
.RE
.SH EXAMPLES
To make \fBCtrl+Z\fR suspend nano:
.sp
.RS
.B bind ^Z suspend main
.RE
.sp
To make \fBShift+Alt+C\fR copy the marked region to the system's clipboard:
.sp
.RS
.B bind Sh-M-C """{execute}| xsel -ib {enter}{undo}""" main
.RE
.sp
.SH FILES
.TP
.I /etc/nanorc

View File

@ -30,6 +30,7 @@ nano_SOURCES = \
color.c \
cut.c \
files.c \
folding.c \
global.c \
help.c \
history.c \

View File

@ -48,6 +48,7 @@ void do_deletion(undo_type action)
memmove(&openfile->current->data[openfile->current_x],
&openfile->current->data[openfile->current_x + charlen],
line_len - charlen + 1);
UNFOLD_SEGMENT(openfile->current);
#ifndef NANO_TINY
/* When softwrapping, a changed number of chunks requires a refresh. */
if (ISSET(SOFTWRAP) && extra_chunks_in(openfile->current) != old_amount)
@ -62,6 +63,9 @@ void do_deletion(undo_type action)
} else if (openfile->current != openfile->filebot) {
linestruct *joining = openfile->current->next;
UNFOLD_SEGMENT(openfile->current);
UNFOLD_SEGMENT(joining);
/* If there is a magic line, and we're before it: don't eat it. */
if (joining == openfile->filebot && openfile->current_x != 0 &&
!ISSET(NO_NEWLINES)) {
@ -143,7 +147,8 @@ void do_backspace(void)
openfile->current_x = step_left(openfile->current->data, openfile->current_x);
do_deletion(BACK);
} else if (openfile->current != openfile->filetop) {
do_left();
openfile->current = openfile->current->prev;
openfile->current_x = strlen(openfile->current->data);
do_deletion(BACK);
}
}
@ -192,7 +197,7 @@ void chop_word(bool forward)
* on the edge of the original line, then put the cursor on that
* edge instead, so that lines will not be joined unexpectedly. */
if (!forward) {
do_prev_word();
do_prev_word(ALLOW_FOLDED);
if (openfile->current != is_current) {
if (is_current_x > 0) {
openfile->current = is_current;
@ -201,13 +206,14 @@ void chop_word(bool forward)
openfile->current_x = strlen(openfile->current->data);
}
} else {
do_next_word(ISSET(AFTER_ENDS));
do_next_word(ISSET(AFTER_ENDS), ALLOW_FOLDED);
if (openfile->current != is_current &&
is_current->data[is_current_x] != '\0') {
openfile->current = is_current;
openfile->current_x = strlen(is_current->data);
}
}
UNFOLD_SEGMENT(openfile->current);
/* Set the mark at the start of that word. */
openfile->mark = openfile->current;
@ -262,9 +268,20 @@ void extract_segment(linestruct *top, size_t top_x, linestruct *bot, size_t bot_
if (top == bot && top_x == bot_x)
return;
/* In case bot is the start of a folded segment.
* Folds within [top, bot] are taken care of in the following loop */
UNFOLD_SEGMENT(bot);
if (top != bot)
for (linestruct *line = top->next; line != bot->next; line = line->next)
#ifdef ENABLE_FOLDING
top->folded = FALSE;
#endif
for (linestruct *line = top->next; line != bot->next; line = line->next){
had_anchor |= line->has_anchor;
#ifdef ENABLE_FOLDING
line->folded = FALSE;
#endif
}
#endif
if (top == bot) {
@ -378,6 +395,8 @@ void ingraft_buffer(linestruct *topline)
#endif
linestruct *botline = topline;
UNFOLD_SEGMENT(line);
while (botline->next != NULL)
botline = botline->next;
@ -728,6 +747,7 @@ void paste_text(void)
statusline(AHEM, _("Cutbuffer is empty"));
return;
}
UNFOLD_SEGMENT(openfile->current);
#ifndef NANO_TINY
add_undo(PASTE, NULL);

View File

@ -273,6 +273,15 @@
#define MSOME MMAIN|MBROWSER
#endif
#ifdef ENABLE_FOLDING
#define UNFOLD_SEGMENT(line) unfold_folded_segment(line)
#else
#define UNFOLD_SEGMENT(line)
#endif
#define ALLOW_FOLDED TRUE
#define DISALLOW_FOLDED FALSE
/* Enumeration types. */
typedef enum {
UNSPECIFIED, NIX_FILE, DOS_FILE, MAC_FILE
@ -374,6 +383,12 @@ enum {
ZERO
};
typedef enum {
FOUND_BRACKET = 0,
NOT_FOUND_BRACKET,
NOT_A_BRACKET
} bracket_search_result;
/* Structure types. */
#ifdef ENABLE_COLOR
typedef struct colortype {
@ -480,6 +495,10 @@ typedef struct linestruct {
bool has_anchor;
/* Whether the user has placed an anchor at this line. */
#endif
#ifdef ENABLE_FOLDING
bool folded;
/* Whether or not this line is in a fold segment */
#endif
} linestruct;
#ifndef NANO_TINY

View File

@ -681,6 +681,7 @@ void read_file(FILE *f, int fd, const char *filename, bool undoable)
if (ISSET(SOFTWRAP))
was_leftedge = leftedge_for(xplustabs(), openfile->current);
#endif
UNFOLD_SEGMENT(openfile->current);
/* Create an empty buffer. */
topline = make_new_node(NULL);

138
src/folding.c Normal file
View File

@ -0,0 +1,138 @@
/**************************************************************************
* folding.c -- This file is part of GNU nano. *
* *
* Copyright (C) 2022 rexy712 *
* *
* GNU nano is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published *
* by the Free Software Foundation, either version 3 of the License, *
* or (at your option) any later version. *
* *
* GNU nano is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty *
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. *
* See the GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see http://www.gnu.org/licenses/. *
* *
**************************************************************************/
#include "prototypes.h"
#include <ctype.h>
/* isspace */
#ifdef ENABLE_FOLDING
/* Remove a folded segment from the given line until end of segment */
void unfold_folded_segment_from(linestruct *line)
{
if (line != NULL)
refresh_needed = TRUE;
for(;line != NULL && line->folded;line = line->next)
line->folded = FALSE;
}
/* Remove a folded segment containing the given line */
void unfold_folded_segment(linestruct *line)
{
unfold_folded_segment_from(get_start_of_folded_segment(line));
}
/* Get the first member of the folded segment */
linestruct *get_start_of_folded_segment(linestruct *line)
{
if (!line->folded)
return line;
while (line->prev != NULL && line->prev->folded)
line = line->prev;
return line;
}
/* Get the last member of the folded segment */
linestruct *get_end_of_folded_segment(linestruct *line)
{
if (!line->folded)
return line;
while (line->next != NULL && line->next->folded)
line = line->next;
return line;
}
/* Get the span length of a folded_segment */
int get_folded_segment_length(linestruct *line)
{
int i = 1;
if (!line->folded)
return 0;
line = get_start_of_folded_segment(line);
for (;line->next != NULL && line->next->folded;line = line->next)
++i;
return i;
}
/* Remove any folded_segments within the range [top, bottom].
* Returns true if any segments are removed. */
bool do_unfold_segment(linestruct *line)
{
/* Attempt to find a bracketed region first.
* If not, top is unmodified. */
if (!line->folded) {
linestruct *bot;
find_bracketed_region(openfile->current, &line, &bot);
}
if (line->folded) {
unfold_folded_segment(line);
move_cursor_to_proper_column();
return TRUE;
}
return FALSE;
}
/* Fold the currently selected lines unless the current selection
* already contains a folded_segment. In that case, remove any
* segments that are selected. */
void do_fold_segment(void)
{
linestruct *top, *bot;
get_range(&top, &bot);
if (top == bot) {
/* First try to unfold if this line/bracketed region is folded */
if (do_unfold_segment(top))
return;
/* When not selecting multiple lines, try to find bounding
* brackets to act as top and bot. */
if (!find_bracketed_region(openfile->current, &top, &bot)) {
statusline(AHEM, _("No valid region found for automatic fold"));
return;
}
}
for (linestruct* line = top;line != bot->next;line = line->next)
line->folded = TRUE;
if (top->lineno < openfile->edittop->lineno &&
bot->lineno > openfile->edittop->lineno)
openfile->edittop = top;
/* Place the cursor at the start of the fold segment.
* Anywhere else within the segment is invalid. */
if (openfile->current->folded)
openfile->current = get_start_of_folded_segment(top);
openfile->mark = NULL;
refresh_needed = TRUE;
}
#endif /* ENABLE_FOLDING */

View File

@ -642,6 +642,9 @@ void shortcut_init(void)
const char *savefile_gist = N_("Save file without prompting");
const char *findprev_gist = N_("Search next occurrence backward");
const char *findnext_gist = N_("Search next occurrence forward");
#ifdef ENABLE_FOLDING
const char *fold_gist = N_("Fold/unfold the currently selected lines");
#endif
#ifndef NANO_TINY
const char *recordmacro_gist = N_("Start/stop recording a macro");
const char *runmacro_gist = N_("Run the last recorded macro");
@ -998,7 +1001,14 @@ void shortcut_init(void)
N_("Record"), WHENHELP(recordmacro_gist), TOGETHER);
add_to_funcs(run_macro, MMAIN,
N_("Run Macro"), WHENHELP(runmacro_gist), BLANKAFTER);
#endif
#ifdef ENABLE_FOLDING
add_to_funcs(do_fold_segment, MMAIN, N_("Fold"),
WHENHELP(fold_gist), BLANKAFTER);
#endif
#ifndef NANO_TINY
add_to_funcs(zap_text, MMAIN,
/* TRANSLATORS: This refers to deleting a line or marked region. */
N_("Zap"), WHENHELP(zap_gist), BLANKAFTER);
@ -1208,6 +1218,9 @@ void shortcut_init(void)
#ifdef ENABLE_FORMATTER
add_to_sclist(MMAIN, "M-F", 0, do_formatter, 0);
add_to_sclist(MEXECUTE, "^O", 0, do_formatter, 0);
#endif
#ifdef ENABLE_FOLDING
add_to_sclist(MMAIN, "M-[", 0, do_fold_segment, 0);
#endif
add_to_sclist(MMAIN, "^C", 0, report_cursor_position, 0);
add_to_sclist(MMAIN, SLASH_OR_DASH, 0, do_gotolinecolumn, 0);
@ -1341,10 +1354,10 @@ void shortcut_init(void)
add_to_sclist(MMAIN|MHELP, "M-=", 0, do_scroll_down, 0);
#endif
#ifdef ENABLE_MULTIBUFFER
add_to_sclist(MMAIN, "M-,", 0, switch_to_prev_buffer, 0);
add_to_sclist(MMAIN, "M-<", 0, switch_to_prev_buffer, 0);
add_to_sclist(MMAIN, "M-.", 0, switch_to_next_buffer, 0);
add_to_sclist(MMAIN, "M-,", 0, switch_to_prev_buffer, 0);
add_to_sclist(MMAIN, "M->", 0, switch_to_next_buffer, 0);
add_to_sclist(MMAIN, "M-.", 0, switch_to_next_buffer, 0);
#endif
add_to_sclist(MMOST, "M-V", 0, do_verbatim_input, 0);
#ifndef NANO_TINY
@ -1404,7 +1417,7 @@ void shortcut_init(void)
add_to_sclist(MWHEREIS|MREPLACE, "M-B", 0, backwards_void, 0);
add_to_sclist(MWHEREIS|MREPLACE, "^R", 0, flip_replace, 0);
add_to_sclist(MWHEREIS|MGOTOLINE, "^T", 0, flip_goto, 0);
add_to_sclist(MWHEREIS|MGOTOLINE, "^/", 0, flip_goto, 0);
add_to_sclist(MWHEREIS|MGOTOLINE, SLASH_OR_DASH, 0, flip_goto, 0);
#ifdef ENABLE_HISTORIES
add_to_sclist(MWHEREIS|MREPLACE|MREPLACEWITH|MWHEREISFILE|MFINDINHELP|MEXECUTE, "^P", 0, get_older_item, 0);
add_to_sclist(MWHEREIS|MREPLACE|MREPLACEWITH|MWHEREISFILE|MFINDINHELP|MEXECUTE, "^N", 0, get_newer_item, 0);

View File

@ -50,7 +50,7 @@ void help_init(void)
char *ptr;
/* First, set up the initial help text for the current function. */
if (currmenu & (MWHEREIS|MREPLACE|MREPLACEWITH)) {
if (currmenu & (MWHEREIS|MREPLACE)) {
htx[0] = N_("Search Command Help Text\n\n "
"Enter the words or characters you would like to "
"search for, and then press Enter. If there is a "
@ -65,6 +65,13 @@ void help_init(void)
"will be replaced.\n\n The following function keys are "
"available in Search mode:\n\n");
htx[2] = NULL;
} else if (currmenu == MREPLACEWITH) {
htx[0] = N_("=== Replacement ===\n\n "
"Type the characters that should replace the characters that "
"you typed at the previous prompt, and press Enter.\n\n");
htx[1] = N_(" The following function keys "
"are available at this prompt:\n\n");
htx[2] = NULL;
} else if (currmenu == MGOTOLINE) {
htx[0] = N_("Go To Line Help Text\n\n "
"Enter the line number that you wish to go to and hit "
@ -126,8 +133,8 @@ void help_init(void)
"shown in brackets after the search prompt. Hitting "
"Enter without entering any text will perform the "
"previous search.\n\n");
htx[1] = N_(" The following function keys are available in "
"Browser Search mode:\n\n");
htx[1] = N_(" The following function keys "
"are available at this prompt:\n\n");
htx[2] = NULL;
} else if (currmenu == MGOTODIR) {
htx[0] = N_("Browser Go To Directory Help Text\n\n "
@ -167,8 +174,8 @@ void help_init(void)
htx[1] = N_("If you just need another blank buffer, do not enter any "
"command.\n\n You can also pick one of four tools, or cut a "
"large piece of the buffer, or put the editor to sleep.\n\n");
htx[2] = N_(" The following function keys are "
"available in Execute Command mode:\n\n");
htx[2] = N_(" The following function keys "
"are available at this prompt:\n\n");
} else if (currmenu == MLINTER) {
htx[0] = N_("=== Linter ===\n\n "
"In this mode, the status bar shows an error message or "

View File

@ -37,6 +37,12 @@ void to_first_line(void)
void to_last_line(void)
{
openfile->current = openfile->filebot;
#ifdef ENABLE_FOLDING
if (openfile->current->folded) {
openfile->current = get_start_of_folded_segment(openfile->current);
openfile->current_x = 0;
} else
#endif
openfile->current_x = (inhelp) ? 0 : strlen(openfile->filebot->data);
openfile->placewewant = xplustabs();
@ -112,6 +118,15 @@ void set_proper_index_and_pww(size_t *leftedge, size_t target, bool forward)
openfile->placewewant = *leftedge + target;
}
/* Move the cursor position on the current line to the desired column */
void move_cursor_to_proper_column(void)
{
size_t leftedge;
size_t target_column;
get_edge_and_target(&leftedge, &target_column);
set_proper_index_and_pww(&leftedge, target_column, TRUE);
}
/* Move up almost one screenful. */
void do_page_up(void)
{
@ -235,19 +250,28 @@ void to_para_end(void)
void to_prev_block(void)
{
linestruct *was_current = openfile->current;
linestruct *line_step = get_prev_visible_line(openfile->current);
bool is_text = FALSE, seen_text = FALSE;
/* Skip backward until first blank line after some nonblank line(s). */
while (openfile->current->prev != NULL && (!seen_text || is_text)) {
openfile->current = openfile->current->prev;
while (line_step != NULL && (!seen_text || is_text)) {
openfile->current = line_step;
line_step = get_prev_visible_line(line_step);
#ifdef ENABLE_FOLDING
if (openfile->current->folded)
continue;
#endif
is_text = !white_string(openfile->current->data);
seen_text = seen_text || is_text;
}
/* Step forward one line again if we passed text but this line is blank. */
if (seen_text && openfile->current->next != NULL &&
white_string(openfile->current->data))
openfile->current = openfile->current->next;
line_step = get_next_visible_line(openfile->current);
if (seen_text && line_step != NULL &&
white_string(openfile->current->data)) {
openfile->current = line_step;
line_step = get_next_visible_line(line_step);
}
openfile->current_x = 0;
edit_redraw(was_current, CENTERING);
@ -257,12 +281,18 @@ void to_prev_block(void)
void to_next_block(void)
{
linestruct *was_current = openfile->current;
linestruct *line_step = get_next_visible_line(openfile->current);
bool is_white = white_string(openfile->current->data);
bool seen_white = is_white;
/* Skip forward until first nonblank line after some blank line(s). */
while (openfile->current->next != NULL && (!seen_white || is_white)) {
openfile->current = openfile->current->next;
while (line_step != NULL && (!seen_white || is_white)) {
openfile->current = line_step;
line_step = get_next_visible_line(line_step);
#ifdef ENABLE_FOLDING
if (openfile->current->folded)
continue;
#endif
is_white = white_string(openfile->current->data);
seen_white = seen_white || is_white;
}
@ -274,18 +304,33 @@ void to_next_block(void)
#endif
}
/* Move to the previous word. */
void do_prev_word(void)
/* Move to the previous word.
* when allow_folded as true, will move to previous word even if inside
* a folded segment */
void do_prev_word(bool allow_folded)
{
bool punctuation_as_letters = ISSET(WORD_BOUNDS);
bool seen_a_word = FALSE, step_forward = FALSE;
#ifdef ENABLE_FOLDING
if (!allow_folded && openfile->current->folded) {
openfile->current = get_prev_visible_line(openfile->current);
openfile->current_x = strlen(openfile->current->data);
}
#endif
/* Move backward until we pass over the start of a word. */
while (TRUE) {
/* If at the head of a line, move to the end of the preceding one. */
if (openfile->current_x == 0) {
if (openfile->current->prev == NULL)
break;
#ifdef ENABLE_FOLDING
if (!allow_folded) {
openfile->current = get_prev_visible_line(openfile->current);
if (openfile->current->folded)
openfile->current = openfile->current->prev;
} else
#endif
openfile->current = openfile->current->prev;
openfile->current_x = strlen(openfile->current->data);
}
@ -315,11 +360,14 @@ void do_prev_word(void)
/* Move one character forward again to sit on the start of the word. */
openfile->current_x = step_right(openfile->current->data,
openfile->current_x);
UNFOLD_SEGMENT(openfile->current);
}
/* Move to the next word. If after_ends is TRUE, stop at the ends of words
* instead of at their beginnings. Return TRUE if we started on a word. */
bool do_next_word(bool after_ends)
* instead of at their beginnings. If allow_folded is true, go to next word
* even if it's inside a folded segment.
* Return TRUE if we started on a word. */
bool do_next_word(bool after_ends, bool allow_folded)
{
bool punctuation_as_letters = ISSET(WORD_BOUNDS);
bool started_on_word = is_word_char(openfile->current->data +
@ -329,6 +377,12 @@ bool do_next_word(bool after_ends)
bool seen_word = started_on_word;
#endif
#ifdef ENABLE_FOLDING
if (!allow_folded && openfile->current->folded) {
openfile->current = get_next_visible_line(openfile->current);
openfile->current_x = 0;
}
#endif
/* Move forward until we reach the start of a word. */
while (TRUE) {
/* If at the end of a line, move to the beginning of the next one. */
@ -336,6 +390,16 @@ bool do_next_word(bool after_ends)
/* When at end of file, stop. */
if (openfile->current->next == NULL)
break;
#ifdef ENABLE_FOLDING
if (!allow_folded) {
openfile->current = get_next_visible_line(openfile->current);
if (openfile->current->folded) {
/* get_next_visible_line() gives the start of a segment, so skip to the end */
openfile->current = get_end_of_folded_segment(openfile->current);
openfile->current = openfile->current->next;
}
} else
#endif
openfile->current = openfile->current->next;
openfile->current_x = 0;
seen_space = TRUE;
@ -376,6 +440,8 @@ bool do_next_word(bool after_ends)
}
}
UNFOLD_SEGMENT(openfile->current);
return started_on_word;
}
@ -384,7 +450,7 @@ void to_prev_word(void)
{
linestruct *was_current = openfile->current;
do_prev_word();
do_prev_word(DISALLOW_FOLDED);
edit_redraw(was_current, FLOWING);
}
@ -395,7 +461,7 @@ void to_next_word(void)
{
linestruct *was_current = openfile->current;
do_next_word(ISSET(AFTER_ENDS));
do_next_word(ISSET(AFTER_ENDS), DISALLOW_FOLDED);
edit_redraw(was_current, FLOWING);
}
@ -413,6 +479,11 @@ void do_home(void)
size_t leftedge = 0;
size_t left_x = 0;
#ifdef ENABLE_FOLDING
if (openfile->current->folded)
return;
#endif
if (ISSET(SOFTWRAP)) {
leftedge = leftedge_for(was_column, openfile->current);
left_x = proper_x(openfile->current, &leftedge, FALSE, leftedge, NULL);
@ -470,6 +541,11 @@ void do_end(void)
size_t line_len = strlen(openfile->current->data);
bool moved_off_chunk = TRUE;
#ifdef ENABLE_FOLDING
if (openfile->current->folded)
return;
#endif
#ifndef NANO_TINY
if (ISSET(SOFTWRAP)) {
bool kickoff = TRUE;
@ -612,7 +688,7 @@ void do_left(void)
openfile->current_x);
#endif
} else if (openfile->current != openfile->filetop) {
openfile->current = openfile->current->prev;
openfile->current = get_prev_visible_line(openfile->current);
openfile->current_x = strlen(openfile->current->data);
}
@ -624,6 +700,13 @@ void do_right(void)
{
linestruct *was_current = openfile->current;
#ifdef ENABLE_FOLDING
if (openfile->current->folded) {
openfile->current = get_next_visible_line(openfile->current);
if (openfile->current == NULL)
openfile->current = was_current;
} else
#endif
if (openfile->current->data[openfile->current_x] != '\0') {
openfile->current_x = step_right(openfile->current->data,
openfile->current_x);
@ -634,7 +717,7 @@ void do_right(void)
openfile->current_x);
#endif
} else if (openfile->current != openfile->filebot) {
openfile->current = openfile->current->next;
openfile->current = get_next_visible_line(openfile->current);
openfile->current_x = 0;
}

View File

@ -78,6 +78,9 @@ linestruct *make_new_node(linestruct *prevnode)
#ifndef NANO_TINY
newnode->has_anchor = FALSE;
#endif
#ifdef ENABLE_FOLDING
newnode->folded = FALSE;
#endif
return newnode;
}
@ -117,6 +120,7 @@ void delete_node(linestruct *line)
/* Disconnect a node from a linked list of linestructs and delete it. */
void unlink_node(linestruct *line)
{
UNFOLD_SEGMENT(line);
if (line->prev != NULL)
line->prev->next = line->next;
if (line->next != NULL)
@ -156,6 +160,9 @@ linestruct *copy_node(const linestruct *src)
#ifndef NANO_TINY
dst->has_anchor = src->has_anchor;
#endif
#ifdef ENABLE_FOLDING
dst->folded = src->folded;
#endif
return dst;
}
@ -303,7 +310,7 @@ void do_exit(void)
/* When unmodified, simply close. Else, when doing automatic saving
* and the file has a name, simply save. Otherwise, ask the user. */
if (!openfile->modified)
if (!openfile->modified || ISSET(VIEW_MODE))
choice = NO;
else if (ISSET(SAVE_ON_EXIT) && openfile->filename[0] != '\0')
choice = YES;
@ -1461,6 +1468,7 @@ void inject(char *burst, size_t count)
#ifndef NANO_TINY
size_t original_row = 0;
size_t old_amount = 0;
UNFOLD_SEGMENT(thisline);
if (ISSET(SOFTWRAP)) {
if (openfile->current_y == editwinrows - 1)

View File

@ -365,8 +365,8 @@ void to_para_end(void);
#endif
void to_prev_block(void);
void to_next_block(void);
void do_prev_word(void);
bool do_next_word(bool after_ends);
void do_prev_word(bool allow_folded);
bool do_next_word(bool after_ends, bool allow_folded);
void to_prev_word(void);
void to_next_word(void);
void do_home(void);
@ -380,6 +380,7 @@ void do_center(void);
#endif
void do_left(void);
void do_right(void);
void move_cursor_to_proper_column(void);
/* Most functions in nano.c. */
linestruct *make_new_node(linestruct *prevnode);
@ -476,6 +477,9 @@ void goto_line_and_column(ssize_t line, ssize_t column, bool retain_answer,
bool interactive);
void do_gotolinecolumn(void);
#ifndef NANO_TINY
int find_matching_bracket_pos(linestruct **line, size_t *xpos);
bool find_bracketed_region(linestruct *in_region,
linestruct **top, linestruct **bot);
void do_find_bracket(void);
void put_or_lift_anchor(void);
void to_prev_anchor(void);
@ -579,6 +583,8 @@ linestruct *line_from_number(ssize_t number);
#endif
/* Most functions in winio.c. */
linestruct *get_next_visible_line(linestruct *line);
linestruct *get_prev_visible_line(linestruct *line);
#ifndef NANO_TINY
void record_macro(void);
void run_macro(void);
@ -610,6 +616,7 @@ void warn_and_briefly_pause(const char *msg);
void bottombars(int menu);
void post_one_key(const char *keystroke, const char *tag, int width);
void place_the_cursor(void);
int update_line_at(linestruct *line, size_t index, int row);
int update_line(linestruct *line, size_t index);
#ifndef NANO_TINY
int update_softwrapped_line(linestruct *line);
@ -674,3 +681,13 @@ void flip_newbuffer(void);
#endif
void discard_buffer(void);
void do_cancel(void);
/* Most functions in folding.c. */
#ifdef ENABLE_FOLDING
void do_fold_segment(void);
void unfold_folded_segment(linestruct *line);
void unfold_folded_segment_from(linestruct *line);
linestruct *get_start_of_folded_segment(linestruct* line);
linestruct *get_end_of_folded_segment(linestruct* line);
int get_folded_segment_length(linestruct *line);
#endif

View File

@ -308,6 +308,8 @@ int findnextstr(const char *needle, bool whole_word_only, int modus,
}
}
UNFOLD_SEGMENT(line);
found_x = found - line->data;
nodelay(midwin, FALSE);
@ -754,6 +756,7 @@ void goto_line_posx(ssize_t linenumber, size_t pos_x)
openfile->current_x = pos_x;
openfile->placewewant = xplustabs();
UNFOLD_SEGMENT(openfile->current);
refresh_needed = TRUE;
}
@ -808,16 +811,25 @@ void goto_line_and_column(ssize_t line, ssize_t column, bool retain_answer,
line = 1;
#ifdef ENABLE_COLOR
if (line > openfile->edittop->lineno + editwinrows ||
#ifndef NANO_TINY
linestruct *edit_bottom = openfile->edittop;
for (int i = 0;edit_bottom != openfile->filebot && i < editwinrows;++i)
edit_bottom = get_next_visible_line(edit_bottom);
if (line > edit_bottom->lineno ||
(ISSET(SOFTWRAP) && line > openfile->current->lineno))
recook |= perturbed;
#else
if (line > openfile->edittop->lineno + editwinrows)
#endif
recook |= perturbed;
#endif /* ENABLE_COLOR */
/* Iterate to the requested line. */
for (openfile->current = openfile->filetop; line > 1 &&
openfile->current != openfile->filebot; line--)
openfile->current = openfile->current->next;
UNFOLD_SEGMENT(openfile->current);
/* Take a negative column number to mean: from the end of the line. */
if (column < 0)
column = breadth(openfile->current->data) + column + 2;
@ -872,23 +884,110 @@ void do_gotolinecolumn(void)
}
#ifndef NANO_TINY
/* Search backwards for a line ending with a bracket, including the current line.
* top is an in/out parameter. The passed value is the start line for searching.
* xpos is an out parameter. It is the position in the line where the bracket is
* found. bracket_half is the halfway mark in the matchbrackets array. This way in
* a loop the halfway value isn't recomputed. */
bool find_start_of_bracketed_region(linestruct **top, size_t *xpos, size_t bracket_half)
{
/* Search backwards for a line ending with a bracket, including the current line */
for (;(*top) != NULL;*top = (*top)->prev) {
char *found;
const char *bracket;
int len = strlen((*top)->data);
/* Find the last bracket on the search line */
found = mbrevstrpbrk((*top)->data, matchbrackets, (*top)->data + len);
if (found == NULL)
continue;
*xpos = found - (*top)->data;
bracket = mbstrchr(matchbrackets, found);
/* We're looking for opening braces, not closing */
if (bracket >= matchbrackets + bracket_half)
continue;
/* Check if this is the last character in the line */
char *next = (*top)->data + step_right((*top)->data, *xpos);
if (*next == '\0')
return TRUE;
}
*xpos = 0;
return FALSE;
}
/* Get the halfway point in matchbrackets, where open and close
* brackets meet. */
size_t get_matchbrackets_halfway(void)
{
size_t charcount = mbstrlen(matchbrackets) / 2;
size_t halfway = 0;
for (size_t i = 0;i < charcount;++i)
halfway += char_length(matchbrackets + halfway);
return halfway;
}
/* Get a region surrounding the line in_region. A region here is defined
* as a block of text starting with an opening bracket at the end of a line,
* and ending with a matching closing bracket.
* top and bot are out parameters. Returns the top/bottom line of the region.
* top_pos and bot_pos are out parameters. Returns the position within the
* top and bottom lines where the brackets are found. */
bool find_bracketed_region(linestruct *in_region,
linestruct **top, linestruct **bot)
{
size_t bracket_half = get_matchbrackets_halfway();
size_t top_pos;
size_t bot_pos;
linestruct *was_top = *top;
linestruct *was_bot = *bot;
*top = in_region;
for (;find_start_of_bracketed_region(top, &top_pos, bracket_half)
;*top = (*top)->prev)
{
*bot = *top;
bot_pos = top_pos;
if (!find_matching_bracket_pos(bot, &bot_pos)) {
if ((*bot)->lineno < in_region->lineno)
continue;
int linedist = (*bot)->lineno - (*top)->lineno;
if (linedist >= 2) {
*top = (*top)->next;
*bot = (*bot)->prev;
return TRUE;
}
}
}
*top = was_top;
*bot = was_bot;
return FALSE;
}
/* Search, starting from the current position, for any of the two characters
* in bracket_pair. If reverse is TRUE, search backwards, otherwise forwards.
* Return TRUE when one of the brackets was found, and FALSE otherwise. */
bool find_a_bracket(bool reverse, const char *bracket_pair)
bool find_a_bracket(bool reverse, const char *bracket_pair,
linestruct **inout_line, size_t *xpos)
{
linestruct *line = openfile->current;
linestruct *line = *inout_line;
const char *pointer, *found;
if (reverse) {
/* First step away from the current bracket. */
if (openfile->current_x == 0) {
if (*xpos == 0) {
line = line->prev;
if (line == NULL)
return FALSE;
pointer = line->data + strlen(line->data);
} else
pointer = line->data + step_left(line->data, openfile->current_x);
pointer = line->data + step_left(line->data, *xpos);
/* Now seek for any of the two brackets we are interested in. */
while (!(found = mbrevstrpbrk(line->data, bracket_pair, pointer))) {
@ -898,7 +997,7 @@ bool find_a_bracket(bool reverse, const char *bracket_pair)
pointer = line->data + strlen(line->data);
}
} else {
pointer = line->data + step_right(line->data, openfile->current_x);
pointer = line->data + step_right(line->data, *xpos);
while (!(found = mbstrpbrk(pointer, bracket_pair))) {
line = line->next;
@ -908,45 +1007,24 @@ bool find_a_bracket(bool reverse, const char *bracket_pair)
}
}
/* Set the current position to the found bracket. */
openfile->current = line;
openfile->current_x = found - line->data;
/* Set the found position to the found bracket. */
*inout_line = line;
*xpos = found - line->data;
return TRUE;
}
/* Search for a match to the bracket at the current cursor position, if
* there is one. */
void do_find_bracket(void)
/* Search for the given bracket's compliment within matchbrackets.
* ch must be a pointer to within matchbrackets.
* reverse is true if the found compliment came before ch in matchbrackets. */
const char *get_bracket_compliment(const char *ch, bool *reverse)
{
linestruct *was_current = openfile->current;
size_t was_current_x = openfile->current_x;
/* The current cursor position, in case we don't find a complement. */
const char *ch;
/* The location in matchbrackets of the bracket under the cursor. */
int ch_len;
/* The length of ch in bytes. */
const char *wanted_ch;
/* The location in matchbrackets of the complementing bracket. */
int wanted_ch_len;
/* The length of wanted_ch in bytes. */
char bracket_pair[MAXCHARLEN * 2 + 1];
/* The pair of characters in ch and wanted_ch. */
size_t halfway = 0;
/* The index in matchbrackets where the closing brackets start. */
size_t charcount = mbstrlen(matchbrackets) / 2;
/* Half the number of characters in matchbrackets. */
size_t balance = 1;
/* The initial bracket count. */
bool reverse;
/* The direction we search. */
ch = mbstrchr(matchbrackets, openfile->current->data + openfile->current_x);
if (ch == NULL) {
statusline(AHEM, _("Not a bracket"));
return;
}
const char *wanted_ch;
/* The location in matchbrackets of the complementing bracket. */
size_t halfway = 0;
/* The index in matchbrackets where the closing brackets start. */
/* Find the halfway point in matchbrackets, where the closing ones start. */
for (size_t i = 0; i < charcount; i++)
@ -954,19 +1032,35 @@ void do_find_bracket(void)
/* When on a closing bracket, we have to search backwards for a matching
* opening bracket; otherwise, forward for a matching closing bracket. */
reverse = (ch >= (matchbrackets + halfway));
*reverse = (ch >= (matchbrackets + halfway));
/* Step half the number of total characters either backwards or forwards
* through matchbrackets to find the wanted complementary bracket. */
wanted_ch = ch;
while (charcount-- > 0) {
if (reverse)
if (*reverse)
wanted_ch = matchbrackets + step_left(matchbrackets,
wanted_ch - matchbrackets);
else
wanted_ch += char_length(wanted_ch);
}
return wanted_ch;
}
/* Create a bracket pair string from the given bracket in ch, assigning
* the result to bracket_pair. Reverse is set to true if we need to
* search backward for a match. */
void create_bracket_pair(char *bracket_pair, const char *ch,
int ch_len, bool* reverse)
{
const char *wanted_ch;
/* The location in matchbrackets of the complementing bracket. */
int wanted_ch_len;
/* The length of wanted_ch in bytes. */
wanted_ch = get_bracket_compliment(ch, reverse);
ch_len = char_length(ch);
wanted_ch_len = char_length(wanted_ch);
@ -974,24 +1068,65 @@ void do_find_bracket(void)
strncpy(bracket_pair, ch, ch_len);
strncpy(bracket_pair + ch_len, wanted_ch, wanted_ch_len);
bracket_pair[ch_len + wanted_ch_len] = '\0';
}
while (find_a_bracket(reverse, bracket_pair)) {
/* Increment/decrement balance for an identical/other bracket. */
balance += (strncmp(openfile->current->data + openfile->current_x,
ch, ch_len) == 0) ? 1 : -1;
/* Get the bracket match position for the character currently under the cursor.
* If there is no bracket beneath the cursor, return NOT_A_BRACKET. If there is
* no match found, return NOT_FOUND_BRACKET. Otherwise returns FOUND_BRACKET
* and sets line and xpos to the found line and offset respectively. */
int find_matching_bracket_pos(linestruct **line, size_t *xpos)
{
const char *br_ch;
/* The location in matchbrackets of the bracket under the cursor. */
int br_ch_len;
/* The length of br_ch in bytes. */
char bracket_pair[MAXCHARLEN * 2 + 1];
/* The pair of characters in ch and wanted_ch. */
size_t balance = 1;
/* The initial bracket count. */
bool reversed;
/* The direction we search. */
/* When balance reached zero, we've found the complementary bracket. */
if (balance == 0) {
br_ch = mbstrchr(matchbrackets, (*line)->data + (*xpos));
if (br_ch == NULL)
return NOT_A_BRACKET;
br_ch_len = char_length(br_ch);
create_bracket_pair(bracket_pair, br_ch, br_ch_len, &reversed);
while (find_a_bracket(reversed, bracket_pair, line, xpos)) {
balance += (strncmp((*line)->data + *xpos,
br_ch, br_ch_len) == 0) ? 1 : -1;
if (balance == 0)
return FOUND_BRACKET;
}
return NOT_FOUND_BRACKET;
}
/* Search for a match to the bracket at the current cursor position, if
* there is one. */
void do_find_bracket(void)
{
linestruct *was_current = openfile->current;
size_t was_current_x = openfile->current_x;
int res = find_matching_bracket_pos(&openfile->current,
&openfile->current_x);
if (res == FOUND_BRACKET) {
UNFOLD_SEGMENT(openfile->current);
edit_redraw(was_current, FLOWING);
return;
}
}
statusline(AHEM, _("No matching bracket"));
/* Restore the cursor position. */
openfile->current = was_current;
openfile->current_x = was_current_x;
statusline(AHEM, res == NOT_FOUND_BRACKET ?
_("No matching bracket") :
_("Not a bracket"));
}
/* Place an anchor at the current line when none exists, otherwise remove it. */
@ -1015,6 +1150,7 @@ void go_to_and_confirm(linestruct *line)
if (line != openfile->current) {
openfile->current = line;
openfile->current_x = 0;
UNFOLD_SEGMENT(openfile->current);
#ifdef ENABLE_COLOR
if (line->lineno > openfile->edittop->lineno + editwinrows ||
(ISSET(SOFTWRAP) && line->lineno > was_current->lineno))

View File

@ -96,6 +96,8 @@ void indent_a_line(linestruct *line, char *indentation)
if (indent_len == 0)
return;
UNFOLD_SEGMENT(line);
/* Add the fabricated indentation to the beginning of the line. */
line->data = nrealloc(line->data, length + indent_len + 1);
memmove(line->data + indent_len, line->data, length + 1);
@ -226,6 +228,8 @@ void unindent_a_line(linestruct *line, size_t indent_len)
if (indent_len == 0)
return;
UNFOLD_SEGMENT(line);
/* Remove the first tab's worth of whitespace from this line. */
memmove(line->data, line->data + indent_len, length - indent_len + 1);
@ -317,6 +321,8 @@ bool comment_line(undo_type action, linestruct *line, const char *comment_seq)
/* Length of postfix. */
size_t line_len = strlen(line->data);
UNFOLD_SEGMENT(line);
if (!ISSET(NO_NEWLINES) && line == openfile->filebot)
return FALSE;
@ -416,8 +422,9 @@ void do_comment(void)
/* Comment/uncomment each of the selected lines when possible, and
* store undo data when a line changed. */
for (line = top; line != bot->next; line = line->next)
if (comment_line(action, line, comment_seq))
if (comment_line(action, line, comment_seq)) {
update_multiline_undo(line->lineno, "");
}
set_modified();
ensure_firstcolumn_is_aligned();
@ -859,6 +866,13 @@ void do_enter(void)
{
linestruct *newnode = make_new_node(openfile->current);
size_t extra = 0;
#ifdef ENABLE_FOLDING
if (openfile->current->folded) {
newnode->folded = TRUE;
openfile->current->folded = FALSE;
}
#endif
#ifndef NANO_TINY
linestruct *sampleline = openfile->current;
bool allblanks = FALSE;
@ -1585,6 +1599,7 @@ void concat_paragraph(linestruct *line, size_t count)
line->data = nrealloc(line->data,
line_len + next_line_len - next_lead_len + 1);
strcat(line->data, next_line->data + next_lead_len);
UNFOLD_SEGMENT(line);
#ifndef NANO_TINY
line->has_anchor |= next_line->has_anchor;
#endif
@ -2852,6 +2867,7 @@ void do_linter(void)
/* Place the cursor to indicate the affected line. */
place_the_cursor();
UNFOLD_SEGMENT(openfile->current);
wnoutrefresh(midwin);
kbinput = get_kbinput(footwin, VISIBLE);
@ -2993,7 +3009,7 @@ void count_lines_words_and_characters(void)
* incrementing the word count for each successful step. */
while (openfile->current->lineno < botline->lineno ||
(openfile->current == botline && openfile->current_x < bot_x)) {
if (do_next_word(FALSE))
if (do_next_word(FALSE, ALLOW_FOLDED))
words++;
}

View File

@ -140,6 +140,55 @@ void run_macro(void)
}
#endif /* !NANO_TINY */
/* Return the current offset of target from openfile->edittop */
int get_row_from_edittop(linestruct *target)
{
#ifdef ENABLE_FOLDING
linestruct *line = openfile->edittop;
int row = 0;
while (line != NULL && line != target && row < editwinrows) {
line = get_end_of_folded_segment(line);
line = line->next;
++row;
}
return row;
#else
return target->lineno - openfile->edittop->lineno;
#endif
}
/* Return the next line, taking fold segments into account */
linestruct *get_next_visible_line(linestruct *line)
{
if (!line)
return NULL;
#ifdef ENABLE_FOLDING
line = get_end_of_folded_segment(line);
if (line == NULL)
return NULL;
#endif
return line->next;
}
/* Return the previous line, taking fold segments into account */
linestruct *get_prev_visible_line(linestruct *line)
{
if (!line)
return NULL;
#ifdef ENABLE_FOLDING
if (line->folded) {
line = get_start_of_folded_segment(line);
return line == NULL ? NULL : line->prev;
}
if (line->prev != NULL && line->prev->folded)
return get_start_of_folded_segment(line->prev);
#endif
return line->prev;
}
/* Allocate the requested space for the keystroke buffer. */
void reserve_space_for(size_t newsize)
{
@ -350,7 +399,7 @@ void implant(const char *string)
}
/* Continue processing an expansion string. Returns either an error code,
* a plain keycode, or a placeholder for a command shortcut. */
* a plain character byte, or a placeholder for a command shortcut. */
int get_code_from_plantation(void)
{
if (*plants_pointer == '{') {
@ -383,6 +432,7 @@ int get_code_from_plantation(void)
return PLANTED_COMMAND;
} else {
char *opening = strchr(plants_pointer, '{');
char firstbyte = *plants_pointer;
int length;
if (opening) {
@ -391,12 +441,12 @@ int get_code_from_plantation(void)
} else
length = strlen(plants_pointer);
for (int index = length - 1; index >= 0; index--)
for (int index = length - 1; index > 0; index--)
put_back((unsigned char)plants_pointer[index]);
plants_pointer += length;
return ERR;
return (firstbyte) ? firstbyte : ERR;
}
}
#endif
@ -2488,20 +2538,37 @@ void place_the_cursor(void)
/* Calculate how many rows the lines from edittop to current use. */
while (line != NULL && line != openfile->current) {
#ifdef ENABLE_FOLDING
if (line->folded) {
line = get_end_of_folded_segment(line);
++row;
} else
#endif
row += 1 + extra_chunks_in(line);
line = line->next;
}
#ifdef ENABLE_FOLDING
if (!openfile->current->folded)
#endif
{
/* Add the number of wraps in the current line before the cursor. */
row += get_chunk_and_edge(column, openfile->current, &leftedge);
column -= leftedge;
}
} else
#endif
#endif /* !NANO_TINY */
{
row = openfile->current->lineno - openfile->edittop->lineno;
row = get_row_from_edittop(openfile->current);
column -= get_page_start(column);
}
#ifdef ENABLE_FOLDING
if (openfile->current->folded) {
openfile->current_x = 0;
column = 0;
}
#endif
if (row < editwinrows)
wmove(midwin, row, margin + column);
#ifndef NANO_TINY
@ -2515,17 +2582,20 @@ void place_the_cursor(void)
/* The number of bytes after which to stop painting, to avoid major slowdowns. */
#define PAINT_LIMIT 2000
/* Draw the given text on the given row of the edit window. line is the
* line to be drawn, and converted is the actual string to be written with
* tabs and control characters replaced by strings of regular characters.
* from_col is the column number of the first character of this "page". */
void draw_row(int row, const char *converted, linestruct *line, size_t from_col)
{
#ifdef ENABLE_LINENUMBERS
/* Draw the line number for the given line, but only if line numbering is on.
* For folded segments, draws a '-' instead of a line number.
* Also draws a mark indicating where anchors are set. */
void draw_linenumber(int row, const linestruct* line, size_t from_col){
/* If line numbering is switched on, put a line number in front of
* the text -- but only for the parts that are not softwrapped. */
if (margin > 0) {
wattron(midwin, interface_color_pair[LINE_NUMBER]);
#ifdef ENABLE_FOLDING
if (line->folded)
mvwprintw(midwin, row, 0, "%*c", margin - 1, '-');
else
#endif
#ifndef NANO_TINY
if (ISSET(SOFTWRAP) && from_col != 0)
mvwprintw(midwin, row, 0, "%*s", margin - 1, " ");
@ -2545,8 +2615,20 @@ void draw_row(int row, const char *converted, linestruct *line, size_t from_col)
#endif
wprintw(midwin, " ");
}
}
#endif /* ENABLE_LINENUMBERS */
/* Draw the given text on the given row of the edit window. line is the
* line to be drawn, and converted is the actual string to be written with
* tabs and control characters replaced by strings of regular characters.
* from_col is the column number of the first character of this "page". */
void draw_row(int row, const char *converted, linestruct *line,
size_t from_col)
{
#ifdef ENABLE_LINENUMBERS
draw_linenumber(row, line, from_col);
#endif
/* First simply write the converted line -- afterward we'll add colors
* and the marking highlight on just the pieces that need it. */
mvwaddstr(midwin, row, margin, converted);
@ -2800,18 +2882,50 @@ void draw_row(int row, const char *converted, linestruct *line, size_t from_col)
#endif /* !NANO_TINY */
}
/* Redraw the given line so that the character at the given index is visible
* -- if necessary, scroll the line horizontally (when not softwrapping).
* Return the number of rows "consumed" (relevant when softwrapping). */
int update_line(linestruct *line, size_t index)
#ifdef ENABLE_FOLDING
/* Draw a folded segment at the given row. */
int update_folded_line(linestruct *line, int row)
{
const int seg_len = get_folded_segment_length(line);
const int string_len = snprintf(NULL, 0, "%d lines folded", seg_len);
const int fill_length = (editwincols > string_len) ?
(editwincols - string_len) : 0;
const int half_fill = fill_length / 2;
char *string = nmalloc(editwincols + 1);
memset(string, '-', half_fill);
snprintf(string+half_fill, editwincols, "%d lines folded", seg_len);
memset(string+half_fill+string_len, '-', fill_length - half_fill);
string[editwincols] = '\0';
#ifdef ENABLE_LINENUMBERS
draw_linenumber(row, line, 0);
#endif
mvwaddstr(midwin, row, margin, string);
free(string);
return 1;
}
#endif /* ENABLE_FOLDING */
/* Redraw the given line at the given row so that the character at the
* given index is visible -- if necessary, scroll the line horizontally
* (when not softwrapping). Return the number of rows "consumed"
* (relevant when softwrapping). */
int update_line_at(linestruct *line, size_t index, int row)
{
int row;
/* The row in the edit window we will be updating. */
char *converted;
/* The data of the line with tabs and control characters expanded. */
size_t from_col;
/* From which column a horizontally scrolled line is displayed. */
#ifdef ENABLE_FOLDING
if (line->folded)
return update_folded_line(line, row);
#endif
#ifndef NANO_TINY
if (ISSET(SOFTWRAP))
return update_softwrapped_line(line);
@ -2819,7 +2933,6 @@ int update_line(linestruct *line, size_t index)
sequel_column = 0;
#endif
row = line->lineno - openfile->edittop->lineno;
from_col = get_page_start(wideness(line->data, index));
/* Expand the piece to be drawn to its representable form, and draw it. */
@ -2842,6 +2955,15 @@ int update_line(linestruct *line, size_t index)
spotlight(light_from_col, light_to_col);
return 1;
}
/* Redraw the given line so that the character at the given index is visible
* -- if necessary, scroll the line horizontally (when not softwrapping).
* Return the number of rows "consumed" (relevant when softwrapping). */
int update_line(linestruct *line, size_t index)
{
return update_line_at(line, index, get_row_from_edittop(line));
}
#ifndef NANO_TINY
@ -2874,8 +2996,13 @@ int update_softwrapped_line(linestruct *line)
/* Find out on which screen row the target line should be shown. */
while (someline != line && someline != NULL) {
#ifdef ENABLE_FOLDING
if (someline->folded)
++row;
else
#endif
row += 1 + extra_chunks_in(someline);
someline = someline->next;
someline = get_next_visible_line(someline);
}
/* If the first chunk is offscreen, don't even try to display it. */
@ -2887,6 +3014,11 @@ int update_softwrapped_line(linestruct *line)
starting_row = row;
#ifdef ENABLE_FOLDING
if (line->folded)
return update_folded_line(line, row);
#endif
while (!end_of_line && row < editwinrows) {
to_col = get_softwrap_breakpoint(line->data, from_col, &kickoff, &end_of_line);
@ -2933,7 +3065,14 @@ int go_back_chunks(int nrows, linestruct **line, size_t *leftedge)
if (ISSET(SOFTWRAP)) {
/* Recede through the requested number of chunks. */
for (i = nrows; i > 0; i--) {
size_t chunk = chunk_for(*leftedge, *line);
size_t chunk;
#ifdef ENABLE_FOLDING
if ((*line)->folded)
chunk = 0;
else
#endif
chunk = chunk_for(*leftedge, *line);
*leftedge = 0;
@ -2944,16 +3083,25 @@ int go_back_chunks(int nrows, linestruct **line, size_t *leftedge)
break;
i -= chunk;
*line = (*line)->prev;
*line = get_prev_visible_line(*line);
*leftedge = HIGHEST_POSITIVE;
}
#ifdef ENABLE_FOLDING
if ((*line)->folded)
*leftedge = 0;
else
#endif
if (*leftedge == HIGHEST_POSITIVE)
*leftedge = leftedge_for(*leftedge, *line);
} else
#endif
for (i = nrows; i > 0 && (*line)->prev != NULL; i--)
*line = (*line)->prev;
#endif /* !NANO_TINY */
{
linestruct *prev = get_prev_visible_line(*line);
for (i = nrows; i > 0 && prev != NULL; i--) {
*line = prev;
prev = get_prev_visible_line(prev);
}
}
return i;
}
@ -2973,6 +3121,14 @@ int go_forward_chunks(int nrows, linestruct **line, size_t *leftedge)
/* Advance through the requested number of chunks. */
for (i = nrows; i > 0; i--) {
#ifdef ENABLE_FOLDING
if ((*line)->folded) {
*line = get_next_visible_line(*line);
current_leftedge = 0;
continue;
}
#endif
bool end_of_line = FALSE;
current_leftedge = get_softwrap_breakpoint((*line)->data,
@ -2993,9 +3149,14 @@ int go_forward_chunks(int nrows, linestruct **line, size_t *leftedge)
if (i < nrows)
*leftedge = current_leftedge;
} else
#endif
for (i = nrows; i > 0 && (*line)->next != NULL; i--)
*line = (*line)->next;
#endif /* !NANO_TINY */
{
linestruct *next = get_next_visible_line(*line);
for (i = nrows; i > 0 && next != NULL; i--) {
*line = next;
next = get_next_visible_line(*line);
}
}
return i;
}
@ -3022,20 +3183,47 @@ bool less_than_a_screenful(size_t was_lineno, size_t was_leftedge)
/* Draw a scroll bar on the righthand side of the screen. */
void draw_scrollbar(void)
{
int fromline = openfile->edittop->lineno - 1;
int totallines = openfile->filebot->lineno;
int coveredlines = editwinrows;
int fromline = openfile->edittop->lineno - 1;
int coveredlines = 0;
linestruct *line = openfile->edittop;
if (ISSET(SOFTWRAP)) {
linestruct *line = openfile->edittop;
int extras = extra_chunks_in(line) - chunk_for(openfile->firstcolumn, line);
while (line->lineno + extras < fromline + editwinrows && line->next) {
line = line->next;
extras += extra_chunks_in(line);
int i = 0;
#ifdef ENABLE_FOLDING
if (line->folded)
coveredlines += get_folded_segment_length(line);
else
#endif
{
i = extra_chunks_in(line) - chunk_for(openfile->firstcolumn, line);
coveredlines = i;
}
coveredlines = line->lineno - fromline;
for (;i < editwinrows && line->next != NULL;++i) {
line = get_next_visible_line(line);
#ifdef ENABLE_FOLDING
if (line->folded)
coveredlines += get_folded_segment_length(line);
else
#endif
{
i += extra_chunks_in(line);
++coveredlines;
}
}
} else {
#ifdef ENABLE_FOLDING
for (int i = 0;i < editwinrows && line->next != NULL;++i) {
if (line->folded)
coveredlines += get_folded_segment_length(line);
else
++coveredlines;
line = get_next_visible_line(line);
}
#else
coveredlines = editwinrows;
#endif /* ENABLE_FOLDING */
}
int lowest = (fromline * editwinrows) / totallines;
@ -3088,7 +3276,12 @@ void edit_scroll(bool direction)
if (thebar)
draw_scrollbar();
if (ISSET(SOFTWRAP)) {
#ifdef ENABLE_FOLDING
if (ISSET(SOFTWRAP) && !line->folded)
#else
if (ISSET(SOFTWRAP))
#endif
{
/* Compensate for the earlier chunks of a softwrapped line. */
nrows += chunk_for(leftedge, line);
@ -3100,10 +3293,11 @@ void edit_scroll(bool direction)
/* Draw new content on the blank row (and on the bordering row too
* when it was deemed necessary). */
int row = get_row_from_edittop(line);
while (nrows > 0 && line != NULL) {
nrows -= update_line(line, (line == openfile->current) ?
openfile->current_x : 0);
line = line->next;
nrows -= update_line_at(line, (line == openfile->current) ?
openfile->current_x : 0, row++);
line = get_next_visible_line(line);
}
}
@ -3300,8 +3494,7 @@ bool current_is_below_screen(void)
leftedge < leftedge_for(xplustabs(), openfile->current))));
} else
#endif
return (openfile->current->lineno >=
openfile->edittop->lineno + editwinrows - SHIM);
return (get_row_from_edittop(openfile->current) >= editwinrows - SHIM);
}
/* Return TRUE if current[current_x] is outside the viewport. */
@ -3329,12 +3522,17 @@ void edit_redraw(linestruct *old_current, update_type manner)
/* If the mark is on, update all lines between old_current and current. */
if (openfile->mark) {
linestruct *line = old_current;
int row = get_row_from_edittop(line);
while (line != openfile->current) {
update_line(line, 0);
update_line_at(line, 0, row);
line = (line->lineno > openfile->current->lineno) ?
line->prev : line->next;
if (line->lineno > openfile->current->lineno) {
line = get_prev_visible_line(line);
--row;
} else {
line = get_next_visible_line(line);
++row;
}
}
} else
#endif
@ -3389,9 +3587,13 @@ void edit_refresh(void)
while (row < editwinrows && line != NULL) {
if (line == openfile->current)
row += update_line(line, openfile->current_x);
row += update_line_at(line, openfile->current_x, row);
else
row += update_line(line, 0);
row += update_line_at(line, 0, row);
#ifdef ENABLE_FOLDING
line = get_end_of_folded_segment(line);
#endif
line = line->next;
}

View File

@ -24,6 +24,9 @@ color brightcyan "\<(exec|print)([[:blank:]]|$)"
# Special values.
color brightmagenta "\<(False|None|True)\>"
# Decorators.
color cyan "@[[:alpha:]_][[:alnum:]_.]*"
# Mono-quoted strings.
color brightgreen "'([^'\]|\\.)*'|"([^"\]|\\.)*"|'''|""""
color normal "'''|""""