Compare commits

...

41 Commits

Author SHA1 Message Date
3d087df732 Merge branch 'rexy712' into rexy712-color 2023-02-28 15:37:01 -08:00
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
da9a59cd52 Merge branch 'rexy712' into rexy712-color 2023-01-21 09:27:07 -08: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
d4e734b159 Merge branch 'rexy712' into rexy712-color 2022-12-19 13:04:56 -08:00
b5809f3793 Merge branch 'master' into rexy712 2022-12-19 13:04:14 -08:00
51144bd61f Merge branch 'rexy712' into rexy712-color 2022-12-04 04:33:17 -08:00
9980a98607 Merge branch 'master' into rexy712 2022-12-04 04:32:02 -08:00
503f496215 Merge branch 'rexy712' into rexy712-color 2022-11-10 16:38:13 -08:00
442bd49137 Merge branch 'master' into rexy712 2022-11-10 16:37:54 -08:00
b0ca683ab3 Merge branch 'rexy712' into rexy712-color 2022-10-04 16:06:52 -07:00
263c7fefbf Merge branch 'master' into rexy712 2022-10-04 16:06:16 -07:00
8a4c62dcd9 Merge branch 'rexy712' into rexy712-color 2022-08-24 13:04:10 -07:00
02249f1655 Merge changes in master 2022-08-24 13:03:13 -07:00
51a10296e8 Merge branch 'rexy712' into rexy712-color 2022-07-22 13:10:15 -07:00
ac58a97a92 Merge branch 'master' of https://git.savannah.gnu.org/git/nano into rexy712 2022-07-22 13:09:24 -07:00
db838f3256 Merge branch 'rexy712' into rexy712-color 2022-07-22 13:06:29 -07:00
140f2d72cb Fix cutting selection containing a folded segment not unfolding before adding to cutbuffer 2022-07-22 13:06:05 -07:00
f7fcc96a04 Merge branch 'rexy712' into rexy712-color 2022-07-15 16:40:12 -07:00
b81dca5014 Merge branch 'master' into rexy712 2022-07-15 16:39:56 -07:00
a5c4a25c16 Merge branch 'rexy712' into rexy712-color 2022-07-13 12:19:27 -07:00
0ac593a9ce Cleanup some folding.c 2022-07-13 12:19:08 -07:00
1dc2b31616 Revert "Remove color support"
This reverts commit 8daf175b09ef1db7b5cabafa98aee5c577bd194e.
2022-07-13 12:00:03 -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
21 changed files with 873 additions and 157 deletions

View File

@ -128,6 +128,20 @@ if test "x$enable_color" != xno; then
color_support=yes color_support=yes
fi 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, AC_ARG_ENABLE(comment,
AS_HELP_STRING([--disable-comment], [Disable the comment/uncomment function])) AS_HELP_STRING([--disable-comment], [Disable the comment/uncomment function]))
if test "x$enable_tiny" = xyes; then if test "x$enable_tiny" = xyes; then

View File

@ -5,7 +5,7 @@
<title>The GNU nano editor FAQ</title> <title>The GNU nano editor FAQ</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <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."> <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%; } .indented { margin-left: 2em; font-family: courier; font-size: 110%; }
</style> </style>
</head> </head>

View File

@ -112,6 +112,7 @@ nanorc file. See \fBnanorc\fR(5).
.BR \-E ", " \-\-tabstospaces .BR \-E ", " \-\-tabstospaces
Convert each typed tab to spaces -- to the number of spaces Convert each typed tab to spaces -- to the number of spaces
that a tab at that position would take up. that a tab at that position would take up.
(Note: pasted tabs are not converted.)
.TP .TP
.BR \-F ", " \-\-multibuffer .BR \-F ", " \-\-multibuffer
Read a file into a new buffer by default. 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 (If you want a plain \fB^Z\fR to suspend nano, add
\fBbind ^Z suspend main\fR to your nanorc.) \fBbind ^Z suspend main\fR to your nanorc.)
.sp .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 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 line nor in one of the \fInanorc\fP files, \fBnano\fP will check the
\fBSPELL\fP environment variable for one. \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 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 it unique. In multibuffer mode, \fBnano\fP will write all the open
buffers to their respective emergency files. 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 .SH BUGS
The recording and playback of keyboard macros works correctly only on a The recording and playback of keyboard macros works correctly only on a

View File

@ -123,6 +123,9 @@ following options: @option{--breaklonglines},
@blankline @blankline
Please report bugs via @url{https://savannah.gnu.org/bugs/?group=nano}. 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 @blankline
For background information see @url{https://nano-editor.org/}. For background information see @url{https://nano-editor.org/}.
@ -406,6 +409,7 @@ nanorc file. @xref{@code{set keycolor}} for details.
@itemx --tabstospaces @itemx --tabstospaces
Convert each typed tab to spaces --- to the number of spaces Convert each typed tab to spaces --- to the number of spaces
that a tab at that position would take up. that a tab at that position would take up.
(Note: pasted tabs are not converted.)
@item -F @item -F
@itemx --multibuffer @itemx --multibuffer
@ -1140,6 +1144,7 @@ greater than 0. The default value is @t{8}.
@item set tabstospaces @item set tabstospaces
Convert each typed tab to spaces --- to the number of spaces Convert each typed tab to spaces --- to the number of spaces
that a tab at that position would take up. that a tab at that position would take up.
(Note: pasted tabs are not converted.)
@item set titlecolor [bold,][italic,]@var{fgcolor},@var{bgcolor} @item set titlecolor [bold,][italic,]@var{fgcolor},@var{bgcolor}
Use this color combination for the title bar. 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. command is encountered.
When @command{nano} is run, this syntax will be automatically When @command{nano} is run, this syntax will be automatically
activated if the current filename matches the extended regular activated (for the relevant buffer) if the absolute filename
expression @var{fileregex}. Or the syntax can be explicitly matches the extended regular expression @var{fileregex}.
activated by using the @option{-Y} or @option{--syntax} 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}. command-line option followed by the @var{name}.
The @code{default} syntax is special: it takes no @var{fileregex}, 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 .B set tabstospaces
Convert each typed tab to spaces -- to the number of spaces Convert each typed tab to spaces -- to the number of spaces
that a tab at that position would take up. that a tab at that position would take up.
(Note: pasted tabs are not converted.)
.TP .TP
.B set titlecolor \fR[\fBbold,\fR][\fBitalic,\fR]\fIfgcolor\fB,\fIbgcolor\fR .B set titlecolor \fR[\fBbold,\fR][\fBitalic,\fR]\fIfgcolor\fB,\fIbgcolor\fR
Use this color combination for the title bar. 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\-Z\fR the title bar plus status bar can be toggled.
With \fBM\-X\fR the help lines. 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 .SH SYNTAX HIGHLIGHTING
Coloring the different syntactic elements of a file Coloring the different syntactic elements of a file
is done via regular expressions (see the \fBcolor\fR command below). 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. command is encountered.
.sp .sp
When \fBnano\fR is run, this syntax will be automatically When \fBnano\fR is run, this syntax will be automatically
activated if the current filename matches the extended regular activated (for the relevant buffer) if the absolute filename
expression \fIfileregex\fR. Or the syntax can be explicitly matches the extended regular expression \fIfileregex\fR.
activated by using the \fB\-Y\fR or \fB\-\-syntax\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. command-line option followed by the \fIname\fR.
.sp .sp
The syntax \fBdefault\fP is special: it takes no \fIfileregex\fR, 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. for \fBunbind\fR it means all menus where the specified \fIkey\fR exists.
.RE .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 .SH FILES
.TP .TP
.I /etc/nanorc .I /etc/nanorc

View File

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

View File

@ -67,8 +67,15 @@ void set_interface_colorpairs(void)
else if (index == ERROR_MESSAGE) { else if (index == ERROR_MESSAGE) {
init_pair(index + 1, COLOR_WHITE, COLOR_RED); init_pair(index + 1, COLOR_WHITE, COLOR_RED);
interface_color_pair[index] = COLOR_PAIR(index + 1) | A_BOLD; interface_color_pair[index] = COLOR_PAIR(index + 1) | A_BOLD;
} else } else if(index == FOLDED_LINE) {
if(COLORS > 15)
init_pair(index + 1, COLOR_BLACK + 8, COLOR_BLACK);
else
init_pair(index + 1, COLOR_WHITE, COLOR_BLACK);
interface_color_pair[index] = COLOR_PAIR(index + 1);
} else {
interface_color_pair[index] = hilite_attribute; interface_color_pair[index] = hilite_attribute;
}
} }
free(color_combo[index]); free(color_combo[index]);

View File

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

View File

@ -273,6 +273,15 @@
#define MSOME MMAIN|MBROWSER #define MSOME MMAIN|MBROWSER
#endif #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. */ /* Enumeration types. */
typedef enum { typedef enum {
UNSPECIFIED, NIX_FILE, DOS_FILE, MAC_FILE UNSPECIFIED, NIX_FILE, DOS_FILE, MAC_FILE
@ -318,6 +327,7 @@ enum {
ERROR_MESSAGE, ERROR_MESSAGE,
KEY_COMBO, KEY_COMBO,
FUNCTION_TAG, FUNCTION_TAG,
FOLDED_LINE,
NUMBER_OF_ELEMENTS NUMBER_OF_ELEMENTS
}; };
@ -374,6 +384,12 @@ enum {
ZERO ZERO
}; };
typedef enum {
FOUND_BRACKET = 0,
NOT_FOUND_BRACKET,
NOT_A_BRACKET
} bracket_search_result;
/* Structure types. */ /* Structure types. */
#ifdef ENABLE_COLOR #ifdef ENABLE_COLOR
typedef struct colortype { typedef struct colortype {
@ -480,6 +496,10 @@ typedef struct linestruct {
bool has_anchor; bool has_anchor;
/* Whether the user has placed an anchor at this line. */ /* Whether the user has placed an anchor at this line. */
#endif #endif
#ifdef ENABLE_FOLDING
bool folded;
/* Whether or not this line is in a fold segment */
#endif
} linestruct; } linestruct;
#ifndef NANO_TINY #ifndef NANO_TINY

View File

@ -681,6 +681,7 @@ void read_file(FILE *f, int fd, const char *filename, bool undoable)
if (ISSET(SOFTWRAP)) if (ISSET(SOFTWRAP))
was_leftedge = leftedge_for(xplustabs(), openfile->current); was_leftedge = leftedge_for(xplustabs(), openfile->current);
#endif #endif
UNFOLD_SEGMENT(openfile->current);
/* Create an empty buffer. */ /* Create an empty buffer. */
topline = make_new_node(NULL); 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 *savefile_gist = N_("Save file without prompting");
const char *findprev_gist = N_("Search next occurrence backward"); const char *findprev_gist = N_("Search next occurrence backward");
const char *findnext_gist = N_("Search next occurrence forward"); 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 #ifndef NANO_TINY
const char *recordmacro_gist = N_("Start/stop recording a macro"); const char *recordmacro_gist = N_("Start/stop recording a macro");
const char *runmacro_gist = N_("Run the last recorded 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); N_("Record"), WHENHELP(recordmacro_gist), TOGETHER);
add_to_funcs(run_macro, MMAIN, add_to_funcs(run_macro, MMAIN,
N_("Run Macro"), WHENHELP(runmacro_gist), BLANKAFTER); 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, add_to_funcs(zap_text, MMAIN,
/* TRANSLATORS: This refers to deleting a line or marked region. */ /* TRANSLATORS: This refers to deleting a line or marked region. */
N_("Zap"), WHENHELP(zap_gist), BLANKAFTER); N_("Zap"), WHENHELP(zap_gist), BLANKAFTER);
@ -1208,6 +1218,9 @@ void shortcut_init(void)
#ifdef ENABLE_FORMATTER #ifdef ENABLE_FORMATTER
add_to_sclist(MMAIN, "M-F", 0, do_formatter, 0); add_to_sclist(MMAIN, "M-F", 0, do_formatter, 0);
add_to_sclist(MEXECUTE, "^O", 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 #endif
add_to_sclist(MMAIN, "^C", 0, report_cursor_position, 0); add_to_sclist(MMAIN, "^C", 0, report_cursor_position, 0);
add_to_sclist(MMAIN, SLASH_OR_DASH, 0, do_gotolinecolumn, 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); add_to_sclist(MMAIN|MHELP, "M-=", 0, do_scroll_down, 0);
#endif #endif
#ifdef ENABLE_MULTIBUFFER #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_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);
add_to_sclist(MMAIN, "M-.", 0, switch_to_next_buffer, 0);
#endif #endif
add_to_sclist(MMOST, "M-V", 0, do_verbatim_input, 0); add_to_sclist(MMOST, "M-V", 0, do_verbatim_input, 0);
#ifndef NANO_TINY #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, "M-B", 0, backwards_void, 0);
add_to_sclist(MWHEREIS|MREPLACE, "^R", 0, flip_replace, 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, "^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 #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, "^P", 0, get_older_item, 0);
add_to_sclist(MWHEREIS|MREPLACE|MREPLACEWITH|MWHEREISFILE|MFINDINHELP|MEXECUTE, "^N", 0, get_newer_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; char *ptr;
/* First, set up the initial help text for the current function. */ /* 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 " htx[0] = N_("Search Command Help Text\n\n "
"Enter the words or characters you would like to " "Enter the words or characters you would like to "
"search for, and then press Enter. If there is a " "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 " "will be replaced.\n\n The following function keys are "
"available in Search mode:\n\n"); "available in Search mode:\n\n");
htx[2] = NULL; 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) { } else if (currmenu == MGOTOLINE) {
htx[0] = N_("Go To Line Help Text\n\n " htx[0] = N_("Go To Line Help Text\n\n "
"Enter the line number that you wish to go to and hit " "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 " "shown in brackets after the search prompt. Hitting "
"Enter without entering any text will perform the " "Enter without entering any text will perform the "
"previous search.\n\n"); "previous search.\n\n");
htx[1] = N_(" The following function keys are available in " htx[1] = N_(" The following function keys "
"Browser Search mode:\n\n"); "are available at this prompt:\n\n");
htx[2] = NULL; htx[2] = NULL;
} else if (currmenu == MGOTODIR) { } else if (currmenu == MGOTODIR) {
htx[0] = N_("Browser Go To Directory Help Text\n\n " 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 " 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 " "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"); "large piece of the buffer, or put the editor to sleep.\n\n");
htx[2] = N_(" The following function keys are " htx[2] = N_(" The following function keys "
"available in Execute Command mode:\n\n"); "are available at this prompt:\n\n");
} else if (currmenu == MLINTER) { } else if (currmenu == MLINTER) {
htx[0] = N_("=== Linter ===\n\n " htx[0] = N_("=== Linter ===\n\n "
"In this mode, the status bar shows an error message or " "In this mode, the status bar shows an error message or "

View File

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

View File

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

View File

@ -365,8 +365,8 @@ void to_para_end(void);
#endif #endif
void to_prev_block(void); void to_prev_block(void);
void to_next_block(void); void to_next_block(void);
void do_prev_word(void); void do_prev_word(bool allow_folded);
bool do_next_word(bool after_ends); bool do_next_word(bool after_ends, bool allow_folded);
void to_prev_word(void); void to_prev_word(void);
void to_next_word(void); void to_next_word(void);
void do_home(void); void do_home(void);
@ -380,6 +380,7 @@ void do_center(void);
#endif #endif
void do_left(void); void do_left(void);
void do_right(void); void do_right(void);
void move_cursor_to_proper_column(void);
/* Most functions in nano.c. */ /* Most functions in nano.c. */
linestruct *make_new_node(linestruct *prevnode); 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); bool interactive);
void do_gotolinecolumn(void); void do_gotolinecolumn(void);
#ifndef NANO_TINY #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 do_find_bracket(void);
void put_or_lift_anchor(void); void put_or_lift_anchor(void);
void to_prev_anchor(void); void to_prev_anchor(void);
@ -579,6 +583,8 @@ linestruct *line_from_number(ssize_t number);
#endif #endif
/* Most functions in winio.c. */ /* Most functions in winio.c. */
linestruct *get_next_visible_line(linestruct *line);
linestruct *get_prev_visible_line(linestruct *line);
#ifndef NANO_TINY #ifndef NANO_TINY
void record_macro(void); void record_macro(void);
void run_macro(void); void run_macro(void);
@ -610,6 +616,7 @@ void warn_and_briefly_pause(const char *msg);
void bottombars(int menu); void bottombars(int menu);
void post_one_key(const char *keystroke, const char *tag, int width); void post_one_key(const char *keystroke, const char *tag, int width);
void place_the_cursor(void); void place_the_cursor(void);
int update_line_at(linestruct *line, size_t index, int row);
int update_line(linestruct *line, size_t index); int update_line(linestruct *line, size_t index);
#ifndef NANO_TINY #ifndef NANO_TINY
int update_softwrapped_line(linestruct *line); int update_softwrapped_line(linestruct *line);
@ -674,3 +681,13 @@ void flip_newbuffer(void);
#endif #endif
void discard_buffer(void); void discard_buffer(void);
void do_cancel(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

@ -134,6 +134,7 @@ static const rcoption rcopts[] = {
{"errorcolor", 0}, {"errorcolor", 0},
{"keycolor", 0}, {"keycolor", 0},
{"functioncolor", 0}, {"functioncolor", 0},
{"foldedcolor", 0},
#endif #endif
{NULL, 0} {NULL, 0}
}; };
@ -1593,6 +1594,8 @@ void parse_rcfile(FILE *rcstream, bool just_syntax, bool intros_only)
color_combo[KEY_COMBO] = parse_interface_color(argument); color_combo[KEY_COMBO] = parse_interface_color(argument);
else if (strcmp(option, "functioncolor") == 0) else if (strcmp(option, "functioncolor") == 0)
color_combo[FUNCTION_TAG] = parse_interface_color(argument); color_combo[FUNCTION_TAG] = parse_interface_color(argument);
else if (strcmp(option, "foldedcolor") == 0)
color_combo[FOLDED_LINE] = parse_interface_color(argument);
else else
#endif #endif
#ifdef ENABLE_OPERATINGDIR #ifdef ENABLE_OPERATINGDIR

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; found_x = found - line->data;
nodelay(midwin, FALSE); nodelay(midwin, FALSE);
@ -754,6 +756,7 @@ void goto_line_posx(ssize_t linenumber, size_t pos_x)
openfile->current_x = pos_x; openfile->current_x = pos_x;
openfile->placewewant = xplustabs(); openfile->placewewant = xplustabs();
UNFOLD_SEGMENT(openfile->current);
refresh_needed = TRUE; refresh_needed = TRUE;
} }
@ -808,16 +811,25 @@ void goto_line_and_column(ssize_t line, ssize_t column, bool retain_answer,
line = 1; line = 1;
#ifdef ENABLE_COLOR #ifdef ENABLE_COLOR
if (line > openfile->edittop->lineno + editwinrows || #ifndef NANO_TINY
(ISSET(SOFTWRAP) && line > openfile->current->lineno)) linestruct *edit_bottom = openfile->edittop;
recook |= perturbed; 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))
#else
if (line > openfile->edittop->lineno + editwinrows)
#endif #endif
recook |= perturbed;
#endif /* ENABLE_COLOR */
/* Iterate to the requested line. */ /* Iterate to the requested line. */
for (openfile->current = openfile->filetop; line > 1 && for (openfile->current = openfile->filetop; line > 1 &&
openfile->current != openfile->filebot; line--) openfile->current != openfile->filebot; line--)
openfile->current = openfile->current->next; openfile->current = openfile->current->next;
UNFOLD_SEGMENT(openfile->current);
/* Take a negative column number to mean: from the end of the line. */ /* Take a negative column number to mean: from the end of the line. */
if (column < 0) if (column < 0)
column = breadth(openfile->current->data) + column + 2; column = breadth(openfile->current->data) + column + 2;
@ -872,23 +884,110 @@ void do_gotolinecolumn(void)
} }
#ifndef NANO_TINY #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 /* Search, starting from the current position, for any of the two characters
* in bracket_pair. If reverse is TRUE, search backwards, otherwise forwards. * in bracket_pair. If reverse is TRUE, search backwards, otherwise forwards.
* Return TRUE when one of the brackets was found, and FALSE otherwise. */ * 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; const char *pointer, *found;
if (reverse) { if (reverse) {
/* First step away from the current bracket. */ /* First step away from the current bracket. */
if (openfile->current_x == 0) { if (*xpos == 0) {
line = line->prev; line = line->prev;
if (line == NULL) if (line == NULL)
return FALSE; return FALSE;
pointer = line->data + strlen(line->data); pointer = line->data + strlen(line->data);
} else } 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. */ /* Now seek for any of the two brackets we are interested in. */
while (!(found = mbrevstrpbrk(line->data, bracket_pair, pointer))) { 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); pointer = line->data + strlen(line->data);
} }
} else { } 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))) { while (!(found = mbstrpbrk(pointer, bracket_pair))) {
line = line->next; 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. */ /* Set the found position to the found bracket. */
openfile->current = line; *inout_line = line;
openfile->current_x = found - line->data; *xpos = found - line->data;
return TRUE; return TRUE;
} }
/* Search for a match to the bracket at the current cursor position, if /* Search for the given bracket's compliment within matchbrackets.
* there is one. */ * ch must be a pointer to within matchbrackets.
void do_find_bracket(void) * 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; size_t charcount = mbstrlen(matchbrackets) / 2;
/* Half the number of characters in matchbrackets. */ /* Half the number of characters in matchbrackets. */
size_t balance = 1; const char *wanted_ch;
/* The initial bracket count. */ /* The location in matchbrackets of the complementing bracket. */
bool reverse; size_t halfway = 0;
/* The direction we search. */ /* The index in matchbrackets where the closing brackets start. */
ch = mbstrchr(matchbrackets, openfile->current->data + openfile->current_x);
if (ch == NULL) {
statusline(AHEM, _("Not a bracket"));
return;
}
/* Find the halfway point in matchbrackets, where the closing ones start. */ /* Find the halfway point in matchbrackets, where the closing ones start. */
for (size_t i = 0; i < charcount; i++) 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 /* When on a closing bracket, we have to search backwards for a matching
* opening bracket; otherwise, forward for a matching closing bracket. */ * 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 /* Step half the number of total characters either backwards or forwards
* through matchbrackets to find the wanted complementary bracket. */ * through matchbrackets to find the wanted complementary bracket. */
wanted_ch = ch; wanted_ch = ch;
while (charcount-- > 0) { while (charcount-- > 0) {
if (reverse) if (*reverse)
wanted_ch = matchbrackets + step_left(matchbrackets, wanted_ch = matchbrackets + step_left(matchbrackets,
wanted_ch - matchbrackets); wanted_ch - matchbrackets);
else else
wanted_ch += char_length(wanted_ch); 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); ch_len = char_length(ch);
wanted_ch_len = char_length(wanted_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, ch_len);
strncpy(bracket_pair + ch_len, wanted_ch, wanted_ch_len); strncpy(bracket_pair + ch_len, wanted_ch, wanted_ch_len);
bracket_pair[ch_len + wanted_ch_len] = '\0'; bracket_pair[ch_len + wanted_ch_len] = '\0';
}
while (find_a_bracket(reverse, bracket_pair)) { /* Get the bracket match position for the character currently under the cursor.
/* Increment/decrement balance for an identical/other bracket. */ * If there is no bracket beneath the cursor, return NOT_A_BRACKET. If there is
balance += (strncmp(openfile->current->data + openfile->current_x, * no match found, return NOT_FOUND_BRACKET. Otherwise returns FOUND_BRACKET
ch, ch_len) == 0) ? 1 : -1; * 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. */ br_ch = mbstrchr(matchbrackets, (*line)->data + (*xpos));
if (balance == 0) {
edit_redraw(was_current, FLOWING); if (br_ch == NULL)
return; 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 = was_current;
openfile->current_x = was_current_x; 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. */ /* 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) { if (line != openfile->current) {
openfile->current = line; openfile->current = line;
openfile->current_x = 0; openfile->current_x = 0;
UNFOLD_SEGMENT(openfile->current);
#ifdef ENABLE_COLOR #ifdef ENABLE_COLOR
if (line->lineno > openfile->edittop->lineno + editwinrows || if (line->lineno > openfile->edittop->lineno + editwinrows ||
(ISSET(SOFTWRAP) && line->lineno > was_current->lineno)) (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) if (indent_len == 0)
return; return;
UNFOLD_SEGMENT(line);
/* Add the fabricated indentation to the beginning of the line. */ /* Add the fabricated indentation to the beginning of the line. */
line->data = nrealloc(line->data, length + indent_len + 1); line->data = nrealloc(line->data, length + indent_len + 1);
memmove(line->data + indent_len, line->data, length + 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) if (indent_len == 0)
return; return;
UNFOLD_SEGMENT(line);
/* Remove the first tab's worth of whitespace from this line. */ /* Remove the first tab's worth of whitespace from this line. */
memmove(line->data, line->data + indent_len, length - indent_len + 1); 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. */ /* Length of postfix. */
size_t line_len = strlen(line->data); size_t line_len = strlen(line->data);
UNFOLD_SEGMENT(line);
if (!ISSET(NO_NEWLINES) && line == openfile->filebot) if (!ISSET(NO_NEWLINES) && line == openfile->filebot)
return FALSE; return FALSE;
@ -416,8 +422,9 @@ void do_comment(void)
/* Comment/uncomment each of the selected lines when possible, and /* Comment/uncomment each of the selected lines when possible, and
* store undo data when a line changed. */ * store undo data when a line changed. */
for (line = top; line != bot->next; line = line->next) 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, ""); update_multiline_undo(line->lineno, "");
}
set_modified(); set_modified();
ensure_firstcolumn_is_aligned(); ensure_firstcolumn_is_aligned();
@ -859,6 +866,13 @@ void do_enter(void)
{ {
linestruct *newnode = make_new_node(openfile->current); linestruct *newnode = make_new_node(openfile->current);
size_t extra = 0; size_t extra = 0;
#ifdef ENABLE_FOLDING
if (openfile->current->folded) {
newnode->folded = TRUE;
openfile->current->folded = FALSE;
}
#endif
#ifndef NANO_TINY #ifndef NANO_TINY
linestruct *sampleline = openfile->current; linestruct *sampleline = openfile->current;
bool allblanks = FALSE; bool allblanks = FALSE;
@ -1585,6 +1599,7 @@ void concat_paragraph(linestruct *line, size_t count)
line->data = nrealloc(line->data, line->data = nrealloc(line->data,
line_len + next_line_len - next_lead_len + 1); line_len + next_line_len - next_lead_len + 1);
strcat(line->data, next_line->data + next_lead_len); strcat(line->data, next_line->data + next_lead_len);
UNFOLD_SEGMENT(line);
#ifndef NANO_TINY #ifndef NANO_TINY
line->has_anchor |= next_line->has_anchor; line->has_anchor |= next_line->has_anchor;
#endif #endif
@ -2852,6 +2867,7 @@ void do_linter(void)
/* Place the cursor to indicate the affected line. */ /* Place the cursor to indicate the affected line. */
place_the_cursor(); place_the_cursor();
UNFOLD_SEGMENT(openfile->current);
wnoutrefresh(midwin); wnoutrefresh(midwin);
kbinput = get_kbinput(footwin, VISIBLE); kbinput = get_kbinput(footwin, VISIBLE);
@ -2993,7 +3009,7 @@ void count_lines_words_and_characters(void)
* incrementing the word count for each successful step. */ * incrementing the word count for each successful step. */
while (openfile->current->lineno < botline->lineno || while (openfile->current->lineno < botline->lineno ||
(openfile->current == botline && openfile->current_x < bot_x)) { (openfile->current == botline && openfile->current_x < bot_x)) {
if (do_next_word(FALSE)) if (do_next_word(FALSE, ALLOW_FOLDED))
words++; words++;
} }

View File

@ -140,6 +140,55 @@ void run_macro(void)
} }
#endif /* !NANO_TINY */ #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. */ /* Allocate the requested space for the keystroke buffer. */
void reserve_space_for(size_t newsize) 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, /* 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) int get_code_from_plantation(void)
{ {
if (*plants_pointer == '{') { if (*plants_pointer == '{') {
@ -383,6 +432,7 @@ int get_code_from_plantation(void)
return PLANTED_COMMAND; return PLANTED_COMMAND;
} else { } else {
char *opening = strchr(plants_pointer, '{'); char *opening = strchr(plants_pointer, '{');
char firstbyte = *plants_pointer;
int length; int length;
if (opening) { if (opening) {
@ -391,12 +441,12 @@ int get_code_from_plantation(void)
} else } else
length = strlen(plants_pointer); 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]); put_back((unsigned char)plants_pointer[index]);
plants_pointer += length; plants_pointer += length;
return ERR; return (firstbyte) ? firstbyte : ERR;
} }
} }
#endif #endif
@ -2488,20 +2538,37 @@ void place_the_cursor(void)
/* Calculate how many rows the lines from edittop to current use. */ /* Calculate how many rows the lines from edittop to current use. */
while (line != NULL && line != openfile->current) { while (line != NULL && line != openfile->current) {
row += 1 + extra_chunks_in(line); #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; line = line->next;
} }
/* Add the number of wraps in the current line before the cursor. */ #ifdef ENABLE_FOLDING
row += get_chunk_and_edge(column, openfile->current, &leftedge); if (!openfile->current->folded)
column -= leftedge;
} else
#endif #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 /* !NANO_TINY */
{ {
row = openfile->current->lineno - openfile->edittop->lineno; row = get_row_from_edittop(openfile->current);
column -= get_page_start(column); column -= get_page_start(column);
} }
#ifdef ENABLE_FOLDING
if (openfile->current->folded) {
openfile->current_x = 0;
column = 0;
}
#endif
if (row < editwinrows) if (row < editwinrows)
wmove(midwin, row, margin + column); wmove(midwin, row, margin + column);
#ifndef NANO_TINY #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. */ /* The number of bytes after which to stop painting, to avoid major slowdowns. */
#define PAINT_LIMIT 2000 #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 #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 /* If line numbering is switched on, put a line number in front of
* the text -- but only for the parts that are not softwrapped. */ * the text -- but only for the parts that are not softwrapped. */
if (margin > 0) { if (margin > 0) {
wattron(midwin, interface_color_pair[LINE_NUMBER]); 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 #ifndef NANO_TINY
if (ISSET(SOFTWRAP) && from_col != 0) if (ISSET(SOFTWRAP) && from_col != 0)
mvwprintw(midwin, row, 0, "%*s", margin - 1, " "); 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 #endif
wprintw(midwin, " "); wprintw(midwin, " ");
} }
}
#endif /* ENABLE_LINENUMBERS */ #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 /* First simply write the converted line -- afterward we'll add colors
* and the marking highlight on just the pieces that need it. */ * and the marking highlight on just the pieces that need it. */
mvwaddstr(midwin, row, margin, converted); mvwaddstr(midwin, row, margin, converted);
@ -2800,18 +2882,54 @@ void draw_row(int row, const char *converted, linestruct *line, size_t from_col)
#endif /* !NANO_TINY */ #endif /* !NANO_TINY */
} }
/* Redraw the given line so that the character at the given index is visible #ifdef ENABLE_FOLDING
* -- if necessary, scroll the line horizontally (when not softwrapping). /* Draw a folded segment at the given row. */
* Return the number of rows "consumed" (relevant when softwrapping). */ int update_folded_line(linestruct *line, int row)
int update_line(linestruct *line, size_t index) {
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
#ifdef ENABLE_COLOR
wattron(midwin, interface_color_pair[FOLDED_LINE]);
mvwaddstr(midwin, row, margin, string);
wattroff(midwin, interface_color_pair[FOLDED_LINE]);
#endif
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; char *converted;
/* The data of the line with tabs and control characters expanded. */ /* The data of the line with tabs and control characters expanded. */
size_t from_col; size_t from_col;
/* From which column a horizontally scrolled line is displayed. */ /* 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 #ifndef NANO_TINY
if (ISSET(SOFTWRAP)) if (ISSET(SOFTWRAP))
return update_softwrapped_line(line); return update_softwrapped_line(line);
@ -2819,7 +2937,6 @@ int update_line(linestruct *line, size_t index)
sequel_column = 0; sequel_column = 0;
#endif #endif
row = line->lineno - openfile->edittop->lineno;
from_col = get_page_start(wideness(line->data, index)); from_col = get_page_start(wideness(line->data, index));
/* Expand the piece to be drawn to its representable form, and draw it. */ /* Expand the piece to be drawn to its representable form, and draw it. */
@ -2842,6 +2959,15 @@ int update_line(linestruct *line, size_t index)
spotlight(light_from_col, light_to_col); spotlight(light_from_col, light_to_col);
return 1; 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 #ifndef NANO_TINY
@ -2874,8 +3000,13 @@ int update_softwrapped_line(linestruct *line)
/* Find out on which screen row the target line should be shown. */ /* Find out on which screen row the target line should be shown. */
while (someline != line && someline != NULL) { while (someline != line && someline != NULL) {
row += 1 + extra_chunks_in(someline); #ifdef ENABLE_FOLDING
someline = someline->next; if (someline->folded)
++row;
else
#endif
row += 1 + extra_chunks_in(someline);
someline = get_next_visible_line(someline);
} }
/* If the first chunk is offscreen, don't even try to display it. */ /* If the first chunk is offscreen, don't even try to display it. */
@ -2887,6 +3018,11 @@ int update_softwrapped_line(linestruct *line)
starting_row = row; starting_row = row;
#ifdef ENABLE_FOLDING
if (line->folded)
return update_folded_line(line, row);
#endif
while (!end_of_line && row < editwinrows) { while (!end_of_line && row < editwinrows) {
to_col = get_softwrap_breakpoint(line->data, from_col, &kickoff, &end_of_line); to_col = get_softwrap_breakpoint(line->data, from_col, &kickoff, &end_of_line);
@ -2933,7 +3069,14 @@ int go_back_chunks(int nrows, linestruct **line, size_t *leftedge)
if (ISSET(SOFTWRAP)) { if (ISSET(SOFTWRAP)) {
/* Recede through the requested number of chunks. */ /* Recede through the requested number of chunks. */
for (i = nrows; i > 0; i--) { 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; *leftedge = 0;
@ -2944,16 +3087,25 @@ int go_back_chunks(int nrows, linestruct **line, size_t *leftedge)
break; break;
i -= chunk; i -= chunk;
*line = (*line)->prev; *line = get_prev_visible_line(*line);
*leftedge = HIGHEST_POSITIVE; *leftedge = HIGHEST_POSITIVE;
} }
#ifdef ENABLE_FOLDING
if ((*line)->folded)
*leftedge = 0;
else
#endif
if (*leftedge == HIGHEST_POSITIVE) if (*leftedge == HIGHEST_POSITIVE)
*leftedge = leftedge_for(*leftedge, *line); *leftedge = leftedge_for(*leftedge, *line);
} else } else
#endif #endif /* !NANO_TINY */
for (i = nrows; i > 0 && (*line)->prev != NULL; i--) {
*line = (*line)->prev; 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; return i;
} }
@ -2973,6 +3125,14 @@ int go_forward_chunks(int nrows, linestruct **line, size_t *leftedge)
/* Advance through the requested number of chunks. */ /* Advance through the requested number of chunks. */
for (i = nrows; i > 0; i--) { 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; bool end_of_line = FALSE;
current_leftedge = get_softwrap_breakpoint((*line)->data, current_leftedge = get_softwrap_breakpoint((*line)->data,
@ -2993,9 +3153,14 @@ int go_forward_chunks(int nrows, linestruct **line, size_t *leftedge)
if (i < nrows) if (i < nrows)
*leftedge = current_leftedge; *leftedge = current_leftedge;
} else } else
#endif #endif /* !NANO_TINY */
for (i = nrows; i > 0 && (*line)->next != NULL; i--) {
*line = (*line)->next; 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; return i;
} }
@ -3022,20 +3187,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. */ /* Draw a scroll bar on the righthand side of the screen. */
void draw_scrollbar(void) void draw_scrollbar(void)
{ {
int fromline = openfile->edittop->lineno - 1;
int totallines = openfile->filebot->lineno; int totallines = openfile->filebot->lineno;
int coveredlines = editwinrows; int fromline = openfile->edittop->lineno - 1;
int coveredlines = 0;
linestruct *line = openfile->edittop;
if (ISSET(SOFTWRAP)) { if (ISSET(SOFTWRAP)) {
linestruct *line = openfile->edittop; int i = 0;
int extras = extra_chunks_in(line) - chunk_for(openfile->firstcolumn, line); #ifdef ENABLE_FOLDING
if (line->folded)
while (line->lineno + extras < fromline + editwinrows && line->next) { coveredlines += get_folded_segment_length(line);
line = line->next; else
extras += extra_chunks_in(line); #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; int lowest = (fromline * editwinrows) / totallines;
@ -3088,7 +3280,12 @@ void edit_scroll(bool direction)
if (thebar) if (thebar)
draw_scrollbar(); 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. */ /* Compensate for the earlier chunks of a softwrapped line. */
nrows += chunk_for(leftedge, line); nrows += chunk_for(leftedge, line);
@ -3100,10 +3297,11 @@ void edit_scroll(bool direction)
/* Draw new content on the blank row (and on the bordering row too /* Draw new content on the blank row (and on the bordering row too
* when it was deemed necessary). */ * when it was deemed necessary). */
int row = get_row_from_edittop(line);
while (nrows > 0 && line != NULL) { while (nrows > 0 && line != NULL) {
nrows -= update_line(line, (line == openfile->current) ? nrows -= update_line_at(line, (line == openfile->current) ?
openfile->current_x : 0); openfile->current_x : 0, row++);
line = line->next; line = get_next_visible_line(line);
} }
} }
@ -3300,8 +3498,7 @@ bool current_is_below_screen(void)
leftedge < leftedge_for(xplustabs(), openfile->current)))); leftedge < leftedge_for(xplustabs(), openfile->current))));
} else } else
#endif #endif
return (openfile->current->lineno >= return (get_row_from_edittop(openfile->current) >= editwinrows - SHIM);
openfile->edittop->lineno + editwinrows - SHIM);
} }
/* Return TRUE if current[current_x] is outside the viewport. */ /* Return TRUE if current[current_x] is outside the viewport. */
@ -3329,12 +3526,17 @@ void edit_redraw(linestruct *old_current, update_type manner)
/* If the mark is on, update all lines between old_current and current. */ /* If the mark is on, update all lines between old_current and current. */
if (openfile->mark) { if (openfile->mark) {
linestruct *line = old_current; linestruct *line = old_current;
int row = get_row_from_edittop(line);
while (line != openfile->current) { while (line != openfile->current) {
update_line(line, 0); update_line_at(line, 0, row);
line = (line->lineno > openfile->current->lineno) ? if (line->lineno > openfile->current->lineno) {
line->prev : line->next; line = get_prev_visible_line(line);
--row;
} else {
line = get_next_visible_line(line);
++row;
}
} }
} else } else
#endif #endif
@ -3389,9 +3591,13 @@ void edit_refresh(void)
while (row < editwinrows && line != NULL) { while (row < editwinrows && line != NULL) {
if (line == openfile->current) if (line == openfile->current)
row += update_line(line, openfile->current_x); row += update_line_at(line, openfile->current_x, row);
else 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; line = line->next;
} }

View File

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