new feature: comment/uncomment current line or selected lines

This allows for commenting or uncommenting a line or a bunch of lines
with a single keystroke (default binding: M-3).  The characters used
for commenting/uncommenting are specified by the active syntax file.

Reviewed-by: Benno Schulenberg <bensberg@justemail.net>
Signed-off-by: Mike Scalora <mike@scalora.org>
master
Mike Scalora 2016-05-25 22:13:50 +02:00 committed by Benno Schulenberg
parent d3bd855c9d
commit 6a2032f5b0
49 changed files with 416 additions and 5 deletions

View File

@ -84,6 +84,22 @@ else
fi
fi
AC_ARG_ENABLE(comment,
AS_HELP_STRING([--disable-comment], [Disable comment/uncomment functions]))
if test "x$enable_tiny" = xyes; then
if test "x$enable_comment" = xyes; then
AC_MSG_ERROR([--enable-comment cannot work with --enable-tiny])
else
# Disabling nanorc silently disables comment support.
enable_comment=no
fi
fi
if test "x$disable_comment" != xyes; then
if test "x$enable_comment" != xno; then
AC_DEFINE(ENABLE_COMMENT, 1, [Define this to disable the comment/uncomment functionality.])
fi
fi
AC_ARG_ENABLE(extra,
AS_HELP_STRING([--disable-extra], [Disable extra features, currently only easter eggs]))
if test "x$enable_extra" = xno; then

View File

@ -302,6 +302,16 @@ syntax should be used for that file. This
functionality only works when \fBlibmagic\fP is installed on the
system and will be silently ignored otherwise.
.TP
.BI comment " string"
Use the given string for commenting and uncommenting lines. A vertical bar or
pipe character (|) designates bracket-style comments; for example, "/*|*/" for
CSS files. The characters before the pipe are prepended to the line and the
characters after the pipe are appended at the end of the line. If no pipe
character is present, the entire string is prepended; for example, "#" for
Python files. If empty double quotes are specified, the comment/uncomment
function is disabled; for example, "" for JSON. Double quotes or backslashes
may be escaped with a backslash; for example, ".\\"" for man page source.
.TP
.B color \fIfgcolor\fR,\fIbgcolor\fR """\fIregex\fR""" ...
Display all pieces of text that match
the extended regular expression \fIregex\fP with foreground color
@ -336,7 +346,7 @@ to \fBicolor\fP.
.BI extendsyntax " str directive " \fR[ "arg " \fR...]
Extend the syntax previously defined as \fIstr\fP to include
new information. This allows you to add a new \fBcolor\fP, \fBicolor\fP,
\fBheader\fP, \fBmagic\fP, \fBlinter\fP, or \fBformatter\fP directive
\fBheader\fP, \fBmagic\fP, \fBcomment\fP, \fBlinter\fP, or \fBformatter\fP directive
to an already defined syntax -- useful when you want to
slightly improve a syntax defined in one of the system-installed
files (which are normally not writable)
@ -455,6 +465,10 @@ Indents (shifts to the right) the currently marked text.
.B unindent
Unindents (shifts to the left) the currently marked text.
.TP
.B comment
Comments or uncomments the current line or marked lines, using the comment
style specified in the active syntax.
.TP
.B left
Goes left one position (in the editor or browser).
.TP

View File

@ -2,6 +2,7 @@
syntax "asm" "\.(S|s|asm)$"
magic "[Aa]ssembl(y|er)"
comment "//"
color red "\<[A-Z_]{2,}\>"
color brightgreen "\.(data|subsection|text)"

View File

@ -1,6 +1,7 @@
## Here is an example for Autoconf.
syntax "autoconf" "\.(ac|m4)$"
comment "#"
# Keywords:
color yellow "\<(if|test|then|elif|else|fi|for|in|do|done)\>"

View File

@ -2,6 +2,7 @@
syntax "awk" "\.awk$"
magic "awk.*script text"
comment "#"
# Records.
icolor brightred "\$[0-9A-Z_!@#$*?-]+"

View File

@ -2,6 +2,7 @@
syntax "c" "\.(c(c|pp|xx|\+\+)?|C)$" "\.(h(h|pp|xx)?|H)$" "\.ii?$"
magic "(ASCII|UTF-8 Unicode) C(\+\+)? program text"
comment "//"
color brightred "\<[A-Z_][0-9A-Z_]+\>"
color green "\<(float|double|bool|char|int|short|long|sizeof|enum|void|auto|static|const|struct|union|typedef|extern|(un)?signed|inline)\>"

View File

@ -1,6 +1,7 @@
## Syntax highlighting for CMake files.
syntax "cmake" "(CMakeLists\.txt|\.cmake)$"
comment "#"
icolor green "^[[:space:]]*[A-Z0-9_]+"
icolor brightyellow "^[[:space:]]*(include|include_directories|include_external_msproject)\>"

View File

@ -1,6 +1,7 @@
## Here is an example for CSS files.
syntax "css" "\.css$"
comment "/*|*/"
color brightred "."
color brightyellow start="\{" end="\}"

View File

@ -1,6 +1,7 @@
## Here is an example for apt's sources.list.
syntax "sources.list" "sources\.list(~|\.old|\.save)?$" "sources\.list\.d/.*\.list(~|\.old|\.save)?$"
comment "#"
# Coloring the deb lines, working from tail to head. First the
# components -- well, everything, and thus also the components.

View File

@ -2,6 +2,7 @@
## for files that do not match any other syntax.
syntax "default"
comment "#"
# Spaces in front of tabs.
color ,red " + +"

View File

@ -1,6 +1,7 @@
## Here is an example for Emacs Lisp.
syntax "elisp" "\.el$"
comment ";"
# Basic functions/macros
color brightcyan "\<(if|when|unless|cond|and|or|lambda|let|progn|while|dolist|dotimes)\>"

View File

@ -1,6 +1,7 @@
## Here is an example for Fortran 90/95.
syntax "fortran" "\.(f|f90|f95)$"
comment "!"
color red "\<[0-9]+\>"

View File

@ -1,6 +1,7 @@
## Here is an example for Gentoo ebuilds/eclasses.
syntax "ebuild" "\.e(build|class)$"
comment "#"
## All the standard portage functions
color brightgreen "(^|\<default_)src_(unpack|prepare|configure|compile|install|test)\>"

View File

@ -1,6 +1,7 @@
## Here is an example for Go.
syntax "go" "\.go$"
comment "//"
# Set up a formatter since spelling is probably useless...
formatter gofmt -w

View File

@ -1,6 +1,7 @@
## Here is an example for groff.
syntax "groff" "\.m[ems]$" "\.rof" "\.tmac$" "^tmac."
comment ".\""
# The argument of .ds or .nr
color cyan "^\.(ds|nr) [^[:space:]]*"

View File

@ -3,6 +3,7 @@
syntax "guile" "\.scm$"
header "^#!.*guile"
magic "guile"
comment ";"
# Basic scheme functions
color green "\<(do|if|lambda|let(rec)?|map|unless|when)\>"

View File

@ -2,7 +2,10 @@
syntax "html" "\.html?$"
magic "HTML document text"
comment "<!--|-->"
color cyan start="<" end=">"
color red "&[^;[:space:]]*;"
color green ""(\\.|[^"])*""
color yellow start="<!--" end="-->"

View File

@ -2,6 +2,7 @@
syntax "java" "\.java$"
magic "Java "
comment "//"
color green "\<(boolean|byte|char|double|float|int|long|new|short|this|transient|void)\>"
color red "\<(break|case|catch|continue|default|do|else|finally|for|if|return|switch|throw|try|while)\>"

View File

@ -1,6 +1,7 @@
## Here is an example for Javascript.
syntax "javascript" "\.js$"
comment "//"
color brightred "\<[A-Z_][0-9A-Z_]+\>"
color green "\<(const|function|let|this|typeof|var|void)\>"

View File

@ -5,6 +5,8 @@
# License: GPLv3 or newer
syntax "json" "\.json$"
# No comments are permitted in JSON.
comment ""
# Numbers (used as value).
color green ":[[:space:]]*\-?(0|[1-9][0-9]*)(\.[0-9]+)?([Ee]?[-+]?[0-9]+)?"

View File

@ -5,6 +5,7 @@
## Version: 2011-05-05
syntax "lua" "\.lua$"
comment "--"
color brightwhite "\[\[.*\]\]"

View File

@ -1,6 +1,7 @@
## Here is an example for Makefiles.
syntax "makefile" "Makefile[^/]*$" "\.(make|mk)$"
comment "#"
color red "[:=]"
color magenta "\<(if|ifeq|else|endif)\>"

View File

@ -2,6 +2,7 @@
syntax "man" "\.[1-9]x?$"
magic "troff or preprocessor input text"
comment ".\""
color green "\.(SH|SS|TH) .*$"
color brightgreen "\.(SH|SS|TH) " "\.([HIT]P)"

View File

@ -2,6 +2,7 @@
syntax "mgp" "\.mgp$"
header "^%include.*"
comment "#"
icolor green "^%[a-z].*$"
color cyan "(^|[[:space:]])#.*$"

View File

@ -1,9 +1,10 @@
## Here is an example for nanorc files.
syntax "nanorc" "\.?nanorc$"
comment "#"
# Possible errors and parameters
icolor brightred "^[[:space:]]*((un)?(bind|set)|include|syntax|header|magic|linter|i?color|extendsyntax).*$"
icolor brightred "^[[:space:]]*((un)?(bind|set)|include|syntax|header|comment|magic|linter|i?color|extendsyntax).*$"
# Keywords
icolor brightgreen "^[[:space:]]*(set|unset)[[:space:]]+(allow_insecure_backup|autoindent|backup|backwards|boldtext|casesensitive|const(antshow)?|cut|fill|historylog|justifytrim|locking|morespace|mouse|multibuffer|noconvert|nohelp|nonewlines|nowrap|pos(ition)?log|preserve|quickblank|quiet|rebinddelete|rebindkeypad|regexp|smarthome|smooth|softwrap|suspend|tabsize|tabstospaces|tempfile|unix|view|wordbounds)\>"
@ -11,8 +12,8 @@ icolor yellow "^[[:space:]]*set[[:space:]]+(functioncolor|keycolor|statuscolor|t
icolor brightgreen "^[[:space:]]*set[[:space:]]+(backupdir|brackets|functioncolor|keycolor|matchbrackets|operatingdir|punct|quotestr|speller|statuscolor|titlecolor|whitespace)[[:space:]]+"
icolor brightgreen "^[[:space:]]*bind[[:space:]]+((\^|M-)([[:alpha:]]|space|[]]|[0-9^_=+{}|;:'\",./<>\?-])|F([1-9]|1[0-6])|Ins|Del)[[:space:]]+[[:alpha:]]+[[:space:]]+(all|main|search|replace(2|with)?|gotoline|writeout|insert|ext(ernal)?cmd|help|spell|linter|browser|whereisfile|gotodir)([[:space:]]+#|[[:space:]]*$)"
icolor brightgreen "^[[:space:]]*unbind[[:space:]]+((\^|M-)([[:alpha:]]|space|[]]|[0-9^_=+{}|;:'\",./<>\?-])|F([1-9]|1[0-6])|Ins|Del)[[:space:]]+(all|main|search|replace(2|with)?|gotoline|writeout|insert|ext(ernal)?cmd|help|spell|linter|browser|whereisfile|gotodir)([[:space:]]+#|[[:space:]]*$)"
icolor brightgreen "^[[:space:]]*extendsyntax[[:space:]]+[[:alpha:]]+[[:space:]]+(i?color|header|magic|linter|formatter)[[:space:]]+.*$"
icolor green "^[[:space:]]*((un)?(bind|set)|include|syntax|header|magic|linter|formatter|extendsyntax)\>"
icolor brightgreen "^[[:space:]]*extendsyntax[[:space:]]+[[:alpha:]]+[[:space:]]+(i?color|header|magic|comment|linter|formatter)[[:space:]]+.*$"
icolor green "^[[:space:]]*((un)?(bind|set)|include|syntax|header|magic|comment|linter|formatter|extendsyntax)\>"
# Colors
icolor yellow "^[[:space:]]*i?color[[:space:]]*(bright)?(white|black|red|blue|green|yellow|magenta|cyan)?(,(white|black|red|blue|green|yellow|magenta|cyan))?\>"

View File

@ -2,6 +2,7 @@
syntax "nftables" "\.(nft|nftables)$"
header "^#!.*(nft|nftables)"
comment "#"
# Objects and operations
color green "\<(chain|hook|policy|priority|ruleset|set|table|type|v?map)\>"

View File

@ -1,6 +1,7 @@
## Here is an example for C/C++/Obj-C.
syntax "m" "\.m$"
comment "//"
# Stuffs,
color brightwhite "\<[A-Z_][0-9A-Z_]+\>"

View File

@ -1,6 +1,7 @@
## Syntax highlighting for OCaml.
syntax "ocaml" "\.mli?$"
comment "(*|*)"
# Uid:
color red "\<[A-Z][0-9a-z_]{2,}\>"

View File

@ -2,6 +2,8 @@
syntax "patch" "\.(patch|diff|debdiff)$"
magic "diff output text"
# There is no official support for comments in patch files.
comment ""
# Added lines.
color brightgreen "^\+.*"

View File

@ -3,6 +3,7 @@
syntax "perl" "\.p[lm]$"
header "^#!.*perl[-0-9._]*"
magic "Perl script text"
comment "#"
color red "\<(accept|alarm|atan2|bin(d|mode)|c(aller|h(dir|mod|op|own|root)|lose(dir)?|onnect|os|rypt)|d(bm(close|open)|efined|elete|ie|o|ump)|e(ach|of|val|x(ec|ists|it|p))|f(cntl|ileno|lock|ork))\>" "\<(get(c|login|peername|pgrp|ppid|priority|pwnam|(host|net|proto|serv)byname|pwuid|grgid|(host|net)byaddr|protobynumber|servbyport)|([gs]et|end)(pw|gr|host|net|proto|serv)ent|getsock(name|opt)|gmtime|goto|grep|hex|index|int|ioctl|join)\>" "\<(keys|kill|last|length|link|listen|local(time)?|log|lstat|m|mkdir|msg(ctl|get|snd|rcv)|next|oct|open(dir)?|ord|pack|pipe|pop|printf?|push|q|qq|qx|rand|re(ad(dir|link)?|cv|do|name|quire|set|turn|verse|winddir)|rindex|rmdir|s|scalar|seek(dir)?)\>" "\<(se(lect|mctl|mget|mop|nd|tpgrp|tpriority|tsockopt)|shift|shm(ctl|get|read|write)|shutdown|sin|sleep|socket(pair)?|sort|spli(ce|t)|sprintf|sqrt|srand|stat|study|substr|symlink|sys(call|read|tem|write)|tell(dir)?|time|tr(y)?|truncate|umask)\>" "\<(un(def|link|pack|shift)|utime|values|vec|wait(pid)?|wantarray|warn|write)\>"
color magenta "\<(continue|else|elsif|do|for|foreach|if|unless|until|while|eq|ne|lt|gt|le|ge|cmp|x|my|sub|use|package|can|isa)\>"

View File

@ -2,6 +2,7 @@
syntax "php" "\.php[2345s~]?$"
magic "PHP script text"
comment "//"
# PHP markings.
color brightgreen "(<\?(php)?|\?>)"

View File

@ -1,6 +1,7 @@
## Colouring for PO files.
syntax "po" "\.pot?$"
comment "#"
# Comments.
color green "^#.*$"

View File

@ -2,6 +2,7 @@
syntax "sql" "\.sql[2345s~]?$"
magic "PostgreSQL script text"
comment "-- "
# Functions.
color white "\<[a-z_]*\("

View File

@ -1,6 +1,7 @@
## Here is an example for POV-Ray.
syntax "pov" "\.(pov|POV|povray|POVRAY)$"
comment "//"
color brightcyan "^[[:space:]]*#[[:space:]]*(declare)"
color brightyellow "\<(sphere|cylinder|translate|matrix|rotate|scale)\>"

View File

@ -3,6 +3,7 @@
syntax "python" "\.py$"
header "^#!.*python[-0-9._]*"
linter pyflakes
comment "#"
# Function definitions.
icolor brightblue "def [0-9A-Z_]+"

View File

@ -3,6 +3,7 @@
syntax "ruby" "\.rb$"
header "^#!.*ruby[-0-9._]*"
linter ruby -w -c
comment "#"
# Reserved words.
color yellow "\<(BEGIN|END|alias|and|begin|break|case|class|def|defined\?|do|else|elsif|end|ensure|false|for|if|in|module)\>"

View File

@ -4,6 +4,7 @@ syntax "sh" "\.sh$"
header "^#!.*((ba|da|k|pdk)?sh[-0-9_]*|openrc-run|runscript)"
magic "(POSIX|Bourne.*) shell script text"
linter dash -n
comment "#"
icolor brightgreen "^[0-9A-Z_]+\(\)"
color green "\<(break|case|continue|do|done|elif|else|esac|exit|fi|for|function|if|in|read|return|select|shift|then|time|until|while)\>"

View File

@ -1,6 +1,7 @@
## Syntax highlighting for RPM spec files.
syntax "spec" "\.(spec$|spec\.*)"
comment "#"
# Main tags.
color brightblue "((Icon|ExclusiveOs|ExcludeOs)[[:space:]]*:)"

View File

@ -1,6 +1,7 @@
## Syntax highlighting for Tcl files.
syntax "tcl" "\.tcl$"
comment "#"
# Standard Tcl [info commands]:
color green "\<(after|append|array|auto_execok|auto_import|auto_load|auto_load_index|auto_qualify|binary|break|case|catch|cd|clock|close|concat|continue|encoding|eof|error|eval|exec|exit|expr|fblocked|fconfigure|fcopy|file|fileevent|flush|for|foreach|format|gets|glob|global|history|if|incr|info|interp|join|lappend|lindex|linsert|list|llength|load|lrange|lreplace|lsearch|lset|lsort|namespace|open|package|pid|puts|pwd|read|regexp|regsub|rename|return|scan|seek|set|socket|source|split|string|subst|switch|tclLog|tell|time|trace|unknown|unset|update|uplevel|upvar|variable|vwait|while)\>"

View File

@ -2,6 +2,7 @@
syntax "tex" "\.tex$"
linter chktex -v0 -q -I
comment "%"
icolor green "\\.|\\[A-Z]*"
color magenta "[{}]"

View File

@ -3,6 +3,7 @@
syntax "texinfo" "\.texi$"
header "^\\input texinfo"
magic "Texinfo source text"
comment "@c "
# Command arguments, trailing and enclosed.
color cyan "^@[a-z]+[[:space:]]+.*$"

View File

@ -2,6 +2,7 @@
syntax "xml" "\.([jrsx]html?|jnlp|mml|pom|rng|sgml?|svg|w[as]dl|wsdd|xjb|xml|xs(d|lt?)|xul)$"
magic "(XML|SGML) (sub)?document text"
comment "<!--|-->"
# The entire content of the tag:
color green start="<" end=">"

View File

@ -886,6 +886,16 @@ to be edited, to determine whether this syntax should be used for that
file. This functionality only works when libmagic is installed on the
system and will be silently ignored otherwise.
@item comment "string"
Use the given string for commenting and uncommenting lines. A vertical bar or
pipe character (|) designates bracket-style comments; for example, "/*|*/" for
CSS files. The characters before the pipe are prepended to the line and the
characters after the pipe are appended at the end of the line. If no pipe
character is present, the entire string is prepended; for example, "#" for
Python files. If empty double quotes are specified, the comment/uncomment
functions are disabled; for example, "" for JSON. Double quotes or backslashes
may be escaped with a backslash; for example, ".\\"" for man page source.
@item color fgcolor,bgcolor "regex" @dots{}
Display all pieces of text that match the
extended regular expression "regex" with foreground color "fgcolor" and
@ -918,7 +928,7 @@ to @code{icolor}.
@item extendsyntax str directive [arg @dots{}]
Extend the syntax previously defined as str to include new information.
This allows you to add a new @code{color}, @code{icolor}, @code{header},
@code{magic}, @code{linter}, or @code{formatter} directive to an already
@code{magic}, @code{comment}, @code{linter}, or @code{formatter} directive to an already
defined syntax --- useful when you want to slightly improve a syntax defined
in one of the system-installed files (which are normally not writable).
@ -1043,6 +1053,10 @@ Indents (shifts to the right) the currently marked text.
@item unindent
Unindents (shifts to the left) the currently marked text.
@item comment
Comments or uncomments the current line or marked lines, using the comment
style specified in the active syntax.
@item left
Goes left one position (in the editor or browser).

View File

@ -554,6 +554,9 @@ void shortcut_init(void)
N_("Copy the current line and store it in the cutbuffer");
const char *nano_indent_msg = N_("Indent the current line");
const char *nano_unindent_msg = N_("Unindent the current line");
#ifdef ENABLE_COMMENT
const char *nano_comment_msg = N_("Comment/uncomment the current line or marked lines");
#endif
const char *nano_undo_msg = N_("Undo the last operation");
const char *nano_redo_msg = N_("Redo the last undone operation");
#endif
@ -937,6 +940,10 @@ void shortcut_init(void)
add_to_funcs(do_suspend_void, MMAIN,
N_("Suspend"), IFSCHELP(nano_suspend_msg), BLANKAFTER, VIEW);
#ifdef ENABLE_COMMENT
add_to_funcs(do_comment, MMAIN,
N_("Comment Lines"), IFSCHELP(nano_comment_msg), BLANKAFTER, NOVIEW);
#endif
#ifndef NANO_TINY
add_to_funcs(do_savefile, MMAIN,
N_("Save"), IFSCHELP(nano_savefile_msg), BLANKAFTER, NOVIEW);
@ -1103,6 +1110,9 @@ void shortcut_init(void)
add_to_sclist(MMAIN, "M-{", do_unindent, 0);
add_to_sclist(MMAIN, "M-U", do_undo, 0);
add_to_sclist(MMAIN, "M-E", do_redo, 0);
#endif
#ifdef ENABLE_COMMENT
add_to_sclist(MMAIN, "M-3", do_comment, 0);
#endif
add_to_sclist(MMOST, "^B", do_left, 0);
add_to_sclist(MMOST, "Left", do_left, 0);
@ -1419,6 +1429,10 @@ sc *strtosc(const char *input)
else if (!strcasecmp(input, "endpara"))
s->scfunc = do_para_end_void;
#endif
#ifdef ENABLE_COMMENT
else if (!strcasecmp(input, "comment"))
s->scfunc = do_comment;
#endif
#ifndef NANO_TINY
else if (!strcasecmp(input, "indent"))
s->scfunc = do_indent_void;

View File

@ -996,6 +996,9 @@ void version(void)
#ifdef DISABLE_COLOR
printf(" --disable-color");
#endif
#ifndef ENABLE_COMMENT
printf(" --disable-comment");
#endif
#ifdef DISABLE_EXTRA
printf(" --disable-extra");
#endif

View File

@ -191,6 +191,9 @@ typedef enum {
ADD, DEL, BACK, CUT, CUT_EOF, REPLACE,
#ifndef DISABLE_WRAPPING
SPLIT_BEGIN, SPLIT_END,
#endif
#ifndef DISABLE_COMMENT
COMMENT, UNCOMMENT, PREFLIGHT,
#endif
JOIN, PASTE, INSERT, ENTER, OTHER
} undo_type;
@ -251,6 +254,8 @@ typedef struct syntaxtype {
/* The command with which to lint this type of file. */
char *formatter;
/* The formatting command (for programming languages mainly). */
char *comment;
/* The line comment prefix (and postfix) for this type of file. */
colortype *color;
/* The colors and their regexes used in this syntax. */
int nmultis;
@ -322,6 +327,14 @@ typedef struct partition {
} partition;
#ifndef NANO_TINY
typedef struct undo_group {
ssize_t top_line;
/* First line of group. */
ssize_t bottom_line;
/* Last line of group. */
struct undo_group *next;
} undo_group;
typedef struct undo {
ssize_t lineno;
undo_type type;
@ -336,6 +349,8 @@ typedef struct undo {
/* The file size after the action. */
int xflags;
/* Some flag data we need. */
undo_group *grouping;
/* Undo info specific to groups of lines. */
/* Cut-specific stuff we need. */
filestruct *cutbuffer;

View File

@ -653,6 +653,9 @@ void do_unindent(void);
void do_undo(void);
void do_redo(void);
#endif
#ifndef DISABLE_COMMENT
void do_comment(void);
#endif
void do_enter(void);
#ifndef NANO_TINY
RETSIGTYPE cancel_command(int signal);
@ -745,6 +748,11 @@ void mark_order(const filestruct **top, size_t *top_x, const filestruct
void discard_until(const undo *thisitem, openfilestruct *thefile);
void add_undo(undo_type action);
void update_undo(undo_type action);
#ifndef DISABLE_COMMENT
void add_comment_undo(undo_type action, const char *comment_seq, size_t undo_x);
void update_comment_undo(ssize_t lineno);
bool comment_line(undo_type action, filestruct *f, const char *comment_seq);
#endif
#endif
size_t get_totsize(const filestruct *begin, const filestruct *end);
filestruct *fsfromline(ssize_t lineno);

View File

@ -306,6 +306,7 @@ void parse_syntax(char *ptr)
live_syntax->magics = NULL;
live_syntax->linter = NULL;
live_syntax->formatter = NULL;
live_syntax->comment = NULL;
live_syntax->color = NULL;
lastcolor = NULL;
live_syntax->nmultis = 0;
@ -868,6 +869,24 @@ void pick_up_name(const char *kind, char *ptr, char **storage)
/* Allow unsetting the command by using an empty string. */
if (!strcmp(ptr, "\"\""))
*storage = NULL;
else if (*ptr == '"') {
*storage = mallocstrcpy(NULL, ++ptr);
char* q = *storage;
char* p = *storage;
/* Snip out the backslashes of escaped characters. */
while (*p != '"') {
if (*p == '\0') {
rcfile_error(N_("Argument of '%s' lacks closing \""), kind);
free(*storage);
*storage = NULL;
return;
} else if (*p == '\\' && *(p + 1) != '\0') {
p++;
}
*q++ = *p++;
}
*q = '\0';
}
else
*storage = mallocstrcpy(NULL, ptr);
}
@ -982,6 +1001,12 @@ void parse_rcfile(FILE *rcstream
grab_and_store("magic", ptr, &live_syntax->magics);
#else
;
#endif
else if (strcasecmp(keyword, "comment") == 0)
#ifdef ENABLE_COMMENT
pick_up_name("comment", ptr, &live_syntax->comment);
#else
;
#endif
else if (strcasecmp(keyword, "color") == 0)
parse_colors(ptr, NANO_REG_EXTENDED);

View File

@ -425,7 +425,194 @@ void do_unindent(void)
{
do_indent(-tabsize);
}
#endif /* !NANO_TINY */
#ifdef ENABLE_COMMENT
/* Test whether the string is empty or consists of only blanks. */
bool white_string(const char *s)
{
while (*s != '\0' && (is_blank_mbchar(s) || *s == '\r'))
s += move_mbright(s, 0);
return !*s;
}
/* Comment or uncomment the current line or the marked lines. */
void do_comment()
{
const char *comment_seq = "#";
undo_type action = UNCOMMENT;
filestruct *top, *bot, *f;
size_t top_x, bot_x, was_x;
bool empty, all_empty = TRUE;
bool file_changed = FALSE;
/* Whether any comment has been added or deleted. */
assert(openfile->current != NULL && openfile->current->data != NULL);
#ifndef DISABLE_COLOR
if (openfile->syntax && openfile->syntax->comment)
comment_seq = openfile->syntax->comment;
/* Does the syntax not allow comments? */
if (strlen(comment_seq) == 0) {
statusbar(_("Commenting is not supported for this file type"));
return;
}
#endif
/* Determine which lines to work on. */
if (openfile->mark_set)
mark_order((const filestruct **) &top, &top_x,
(const filestruct **) &bot, &bot_x, NULL);
else {
top = openfile->current;
bot = top;
}
/* Remember the cursor x position to be restored when undoing. */
was_x = openfile->current_x;
/* Figure out whether to comment or uncomment the selected line or lines. */
for (f = top; f != bot->next; f = f->next) {
empty = white_string(f->data);
/* If this line is not blank and not commented, we comment all. */
if (!empty && !comment_line(PREFLIGHT, f, comment_seq)) {
action = COMMENT;
break;
}
all_empty = all_empty && empty;
}
/* If all selected lines are blank, we comment them. */
action = all_empty ? COMMENT : action;
/* Process the selected line or lines. */
for (f = top; f != bot->next; f = f->next) {
if (comment_line(action, f, comment_seq)) {
if (!file_changed) {
/* Start building undo data on the first modified line. */
add_comment_undo(action, comment_seq, was_x);
file_changed = TRUE;
}
/* Add undo data for each modified line. */
update_comment_undo(f->lineno);
}
}
if (file_changed) {
set_modified();
refresh_needed = TRUE;
} else
statusbar(_("Cannot comment past end of file"));
}
/* Test whether the given line can be uncommented, or add or remove a comment,
* depending on action. Return TRUE if the line is uncommentable, or when
* anything was added or removed; FALSE otherwise. */
bool comment_line(undo_type action, filestruct *f, const char *comment_seq)
{
size_t comment_seq_len = strlen(comment_seq);
const char *post_seq = strchr(comment_seq, '|');
/* The postfix, if this is a bracketing type comment sequence. */
size_t pre_len = post_seq ? post_seq++ - comment_seq : comment_seq_len;
/* Length of prefix. */
size_t post_len = post_seq ? comment_seq_len - pre_len - 1 : 0;
/* Length of postfix. */
size_t line_len = strlen(f->data);
if (!ISSET(NO_NEWLINES) && f == openfile->filebot)
return FALSE;
if (action == COMMENT) {
/* Make room for the comment sequence(s), move the text right and
* copy them in. */
f->data = charealloc(f->data, line_len + pre_len + post_len + 1);
charmove(&f->data[pre_len], f->data, line_len);
charmove(f->data, comment_seq, pre_len);
if (post_len)
charmove(&f->data[pre_len + line_len], post_seq, post_len);
f->data[pre_len + line_len + post_len] = '\0';
openfile->totsize += pre_len + post_len;
/* If needed, adjust the position of the mark and of the cursor. */
if (openfile->mark_set && f == openfile->mark_begin)
openfile->mark_begin_x += pre_len;
if (f == openfile->current) {
openfile->current_x += pre_len;
openfile->placewewant = xplustabs();
}
return TRUE;
}
/* If the line is commented, report it as uncommentable, or uncomment it. */
if (strncmp(f->data, comment_seq, pre_len) == 0 && (post_len == 0 ||
strcmp(&f->data[line_len - post_len], post_seq) == 0)) {
if (action == PREFLIGHT)
return TRUE;
/* Erase the comment prefix by moving the non-comment part. */
charmove(f->data, &f->data[pre_len], line_len - pre_len);
/* Truncate the postfix if there was one. */
f->data[line_len - pre_len - post_len] = '\0';
openfile->totsize -= pre_len + post_len;
/* If needed, adjust the position of the mark and then the cursor. */
if (openfile->mark_set && f == openfile->mark_begin) {
if (openfile->mark_begin_x < pre_len)
openfile->mark_begin_x = 0;
else
openfile->mark_begin_x -= pre_len;
}
if (f == openfile->current) {
if (openfile->current_x < pre_len)
openfile->current_x = 0;
else
openfile->current_x -= pre_len;
openfile->placewewant = xplustabs();
}
return TRUE;
}
return FALSE;
}
/* Perform an undo or redo for a comment or uncomment action. */
void handle_comment_action(undo *u, bool undoing, bool add_comment)
{
undo_group *group = u->grouping;
/* When redoing, reposition the cursor and let the commenter adjust it. */
if (!undoing)
goto_line_posx(u->lineno, u->begin);
while (group) {
filestruct *f = fsfromline(group->top_line);
while (f && f->lineno <= group->bottom_line) {
comment_line(undoing ^ add_comment ?
COMMENT : UNCOMMENT, f, u->strdata);
f = f->next;
}
group = group->next;
}
/* When undoing, reposition the cursor to the recorded location. */
if (undoing)
goto_line_posx(u->lineno, u->begin);
refresh_needed = TRUE;
}
#endif /* ENABLE_COMMENT */
#ifndef NANO_TINY
#define redo_paste undo_cut
#define undo_paste redo_cut
@ -574,6 +761,16 @@ void do_undo(void)
unlink_node(f->next);
goto_line_posx(u->lineno, u->begin);
break;
#ifdef ENABLE_COMMENT
case COMMENT:
handle_comment_action(u, TRUE, TRUE);
undidmsg = _("comment");
break;
case UNCOMMENT:
handle_comment_action(u, TRUE, FALSE);
undidmsg = _("uncomment");
break;
#endif
case INSERT:
undidmsg = _("text insert");
filestruct *oldcutbuffer = cutbuffer, *oldcutbottom = cutbottom;
@ -738,6 +935,16 @@ void do_redo(void)
free_filestruct(u->cutbuffer);
u->cutbuffer = NULL;
break;
#ifdef ENABLE_COMMENT
case COMMENT:
handle_comment_action(u, FALSE, TRUE);
redidmsg = _("comment");
break;
case UNCOMMENT:
handle_comment_action(u, FALSE, FALSE);
redidmsg = _("uncomment");
break;
#endif
default:
statusline(ALERT, _("Internal error: unknown type. "
"Please save your work."));
@ -909,11 +1116,20 @@ bool execute_command(const char *command)
void discard_until(const undo *thisitem, openfilestruct *thefile)
{
undo *dropit = thefile->undotop;
undo_group *group;
while (dropit != NULL && dropit != thisitem) {
thefile->undotop = dropit->next;
free(dropit->strdata);
free_filestruct(dropit->cutbuffer);
#ifdef ENABLE_COMMENT
group = dropit->grouping;
while (group != NULL) {
undo_group *next = group->next;
free(group);
group = next;
}
#endif
free(dropit);
dropit = thefile->undotop;
}
@ -969,6 +1185,7 @@ void add_undo(undo_type action)
u->mark_set = FALSE;
u->wassize = openfile->totsize;
u->xflags = 0;
u->grouping = NULL;
switch (u->type) {
/* We need to start copying data into the undo buffer
@ -1036,6 +1253,11 @@ void add_undo(undo_type action)
break;
case ENTER:
break;
#ifdef ENABLE_COMMENT
case COMMENT:
case UNCOMMENT:
break;
#endif
default:
statusline(ALERT, _("Internal error: unknown type. "
"Please save your work."));
@ -1049,6 +1271,42 @@ void add_undo(undo_type action)
openfile->last_action = action;
}
#ifdef ENABLE_COMMENT
/* Add a comment undo item. This should be called once for each use
* of the comment/uncomment feature that modifies the document. */
void add_comment_undo(undo_type action, const char *comment_seq, size_t was_x)
{
add_undo(action);
/* Store the comment sequence used for the operation, because it could
* change when the file name changes; we need to know what it was. */
openfile->current_undo->strdata = mallocstrcpy(NULL, comment_seq);
/* Remember the position of the cursor before the change was made. */
openfile->current_undo->begin = was_x;
}
/* Update a comment undo item. This should be called once for each line
* affected by the comment/uncomment feature. */
void update_comment_undo(ssize_t lineno)
{
undo *u = openfile->current_undo;
/* If there already is a group and the current line is contiguous with it,
* extend the group; otherwise, create a new group. */
if (u->grouping && u->grouping->bottom_line + 1 == lineno)
u->grouping->bottom_line++;
else {
undo_group *born = (undo_group *)nmalloc(sizeof(undo_group));
born->next = u->grouping;
u->grouping = born;
born->top_line = lineno;
born->bottom_line = lineno;
}
}
#endif /* ENABLE_COMMENT */
/* Update an undo item, or determine whether a new one is really needed
* and bounce the data to add_undo instead. The latter functionality
* just feels gimmicky and may just be more hassle than it's worth,