diff --git a/configure.ac b/configure.ac
index 0c42e1e2666294bc4f802dedf2844f641b716306..c47885baab0186b9a4a3091e2cf8f8705dadca60 100644
--- a/configure.ac
+++ b/configure.ac
@@ -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
diff --git a/doc/man/nanorc.5 b/doc/man/nanorc.5
index 6d616198f31a7ca2f0352a2b93c77cb0b7fcb2e9..d94c7ed815738cbd428983ea35a78f3371a93184 100644
--- a/doc/man/nanorc.5
+++ b/doc/man/nanorc.5
@@ -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
diff --git a/doc/syntax/asm.nanorc b/doc/syntax/asm.nanorc
index f1719a4a5ce37aa020b478d3635a0563b340ced8..228a185f6a17f8e00ca0fc38188d1b240c3b7ea6 100644
--- a/doc/syntax/asm.nanorc
+++ b/doc/syntax/asm.nanorc
@@ -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)"
diff --git a/doc/syntax/autoconf.nanorc b/doc/syntax/autoconf.nanorc
index 58a9b072a53ce1cdf9c3f918952fc902f9807905..b578abef1e849100a18d6c967f4b53ea09126c3d 100644
--- a/doc/syntax/autoconf.nanorc
+++ b/doc/syntax/autoconf.nanorc
@@ -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)\>"
diff --git a/doc/syntax/awk.nanorc b/doc/syntax/awk.nanorc
index bfc6387cf9f32f17cb5058e84044470e62ceb45a..34e7176f0896c80968103cdaf3da00c1e7bbc091 100644
--- a/doc/syntax/awk.nanorc
+++ b/doc/syntax/awk.nanorc
@@ -2,6 +2,7 @@
 
 syntax "awk" "\.awk$"
 magic "awk.*script text"
+comment "#"
 
 # Records.
 icolor brightred "\$[0-9A-Z_!@#$*?-]+"
diff --git a/doc/syntax/c.nanorc b/doc/syntax/c.nanorc
index ef156ab4670505b35d5cc86993818772bbceaf35..0553b6b567c1f35c338c60216a40ab7a94a2fd5f 100644
--- a/doc/syntax/c.nanorc
+++ b/doc/syntax/c.nanorc
@@ -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)\>"
diff --git a/doc/syntax/cmake.nanorc b/doc/syntax/cmake.nanorc
index dc4370249ae1ec3bcf576c2619729ece12021637..524c842d5183fd9e5dd62e8137963c1677474dfd 100644
--- a/doc/syntax/cmake.nanorc
+++ b/doc/syntax/cmake.nanorc
@@ -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)\>"
diff --git a/doc/syntax/css.nanorc b/doc/syntax/css.nanorc
index a806d7c560ee33acea4bcaaf48e4054aafa2616a..a0531cdc67ebdbfc0f8ccb4a350cad81da038ea2 100644
--- a/doc/syntax/css.nanorc
+++ b/doc/syntax/css.nanorc
@@ -1,6 +1,7 @@
 ## Here is an example for CSS files.
 
 syntax "css" "\.css$"
+comment "/*|*/"
 
 color brightred     "."
 color brightyellow  start="\{" end="\}"
diff --git a/doc/syntax/debian.nanorc b/doc/syntax/debian.nanorc
index 7d0a637509343574fd58140fe8f86ba9846df6e4..10e8a321fc47e87c2642c716940f1654a5153114 100644
--- a/doc/syntax/debian.nanorc
+++ b/doc/syntax/debian.nanorc
@@ -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.
diff --git a/doc/syntax/default.nanorc b/doc/syntax/default.nanorc
index 34ff29b06e145992122be8186c8ffc693069f74d..61d5152438a81a270edb16333f74ff337e878b9c 100644
--- a/doc/syntax/default.nanorc
+++ b/doc/syntax/default.nanorc
@@ -2,6 +2,7 @@
 ## for files that do not match any other syntax.
 
 syntax "default"
+comment "#"
 
 # Spaces in front of tabs.
 color ,red " +	+"
diff --git a/doc/syntax/elisp.nanorc b/doc/syntax/elisp.nanorc
index 2a2bd2eb3616172900c685f21eb9cb7fb043e61a..785827eebfd0ca60c6a4cf0fc65ce754e3f2a61c 100644
--- a/doc/syntax/elisp.nanorc
+++ b/doc/syntax/elisp.nanorc
@@ -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)\>"
diff --git a/doc/syntax/fortran.nanorc b/doc/syntax/fortran.nanorc
index 45873ac753438b3d5c9e30dc20b12304d1a491d7..2a56bc78918e62c2172dbe5ba0925fa82954d475 100644
--- a/doc/syntax/fortran.nanorc
+++ b/doc/syntax/fortran.nanorc
@@ -1,6 +1,7 @@
 ## Here is an example for Fortran 90/95.
 
 syntax "fortran" "\.(f|f90|f95)$"
+comment "!"
 
 color red "\<[0-9]+\>"
 
diff --git a/doc/syntax/gentoo.nanorc b/doc/syntax/gentoo.nanorc
index 249e224d7e73e05e91088584d8499327cd9540d9..7de1cba994ca62d92baeb4c07ea64f04427a3be3 100644
--- a/doc/syntax/gentoo.nanorc
+++ b/doc/syntax/gentoo.nanorc
@@ -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)\>"
diff --git a/doc/syntax/go.nanorc b/doc/syntax/go.nanorc
index e52d188baf8483e9c1fd322b290dcbe77a0da72b..308e976d710c5a1c905fd5901ec6d03a66c68ba8 100644
--- a/doc/syntax/go.nanorc
+++ b/doc/syntax/go.nanorc
@@ -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
diff --git a/doc/syntax/groff.nanorc b/doc/syntax/groff.nanorc
index 5f57f8e6554c05f60c304bb7edd4ade9ab2e3f90..109f9cd6be4be314270478cbd5fdb6e174f9b2ca 100644
--- a/doc/syntax/groff.nanorc
+++ b/doc/syntax/groff.nanorc
@@ -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:]]*"
diff --git a/doc/syntax/guile.nanorc b/doc/syntax/guile.nanorc
index 1feb09b2ad38972068448d570b5c4e72a18bd206..1f93a7c0b3675036cf19761cf21dd601dde3c807 100644
--- a/doc/syntax/guile.nanorc
+++ b/doc/syntax/guile.nanorc
@@ -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)\>"
diff --git a/doc/syntax/html.nanorc b/doc/syntax/html.nanorc
index 8782dc3642dc7b61e87bd3d2d540cbd250baf8cb..8cadb276db1ded21c6a99359fc67c3855f511421 100644
--- a/doc/syntax/html.nanorc
+++ b/doc/syntax/html.nanorc
@@ -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="-->"
diff --git a/doc/syntax/java.nanorc b/doc/syntax/java.nanorc
index 829c8afa13dafb905755862938ea90a828904d6f..f0bd01dde7558157273f7900812968aac59d822d 100644
--- a/doc/syntax/java.nanorc
+++ b/doc/syntax/java.nanorc
@@ -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)\>"
diff --git a/doc/syntax/javascript.nanorc b/doc/syntax/javascript.nanorc
index 991f54bf0ebf70381d2a999f63e23b6ae6f62e0d..f6a541525845d159a746f6c93953bf9cfc2ed962 100644
--- a/doc/syntax/javascript.nanorc
+++ b/doc/syntax/javascript.nanorc
@@ -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)\>"
diff --git a/doc/syntax/json.nanorc b/doc/syntax/json.nanorc
index 0f799f169ff37b9a84573ac247a5ccc00219f048..20d50c1dacef7529c5357447a61edc00f34b5e99 100644
--- a/doc/syntax/json.nanorc
+++ b/doc/syntax/json.nanorc
@@ -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]+)?"
diff --git a/doc/syntax/lua.nanorc b/doc/syntax/lua.nanorc
index 3dbbea8d35e2abd4c9df67de16507888356b51fb..70f69d47ecf293f95628f3345994800470962d0e 100644
--- a/doc/syntax/lua.nanorc
+++ b/doc/syntax/lua.nanorc
@@ -5,6 +5,7 @@
 ## Version: 2011-05-05
 
 syntax "lua" "\.lua$"
+comment "--"
 
 color brightwhite "\[\[.*\]\]"
 
diff --git a/doc/syntax/makefile.nanorc b/doc/syntax/makefile.nanorc
index e376445074dffcffd30b287749555a11c0b1b627..188f004ad66ee8133137b24230cb653463256ab9 100644
--- a/doc/syntax/makefile.nanorc
+++ b/doc/syntax/makefile.nanorc
@@ -1,6 +1,7 @@
 ## Here is an example for Makefiles.
 
 syntax "makefile" "Makefile[^/]*$" "\.(make|mk)$"
+comment "#"
 
 color red "[:=]"
 color magenta "\<(if|ifeq|else|endif)\>"
diff --git a/doc/syntax/man.nanorc b/doc/syntax/man.nanorc
index 6c296bb6243f3e943cc5db4bbcddf008793c9bb3..dd7d23ea5a615c8e119cf5ca356cdb0d2d3eeebc 100644
--- a/doc/syntax/man.nanorc
+++ b/doc/syntax/man.nanorc
@@ -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)"
diff --git a/doc/syntax/mgp.nanorc b/doc/syntax/mgp.nanorc
index c0a5d203009d1bfb002ff2d22bfecf9c5101e96d..1e9d718f8238a4df064e044e9fbe1973f5331a6f 100644
--- a/doc/syntax/mgp.nanorc
+++ b/doc/syntax/mgp.nanorc
@@ -2,6 +2,7 @@
 
 syntax "mgp" "\.mgp$"
 header "^%include.*"
+comment "#"
 
 icolor green "^%[a-z].*$"
 color cyan "(^|[[:space:]])#.*$"
diff --git a/doc/syntax/nanorc.nanorc b/doc/syntax/nanorc.nanorc
index e73070811629da33dc029fa727a6d75d5927253f..3d2e353c7939a39716b3258502f781cf5a813f06 100644
--- a/doc/syntax/nanorc.nanorc
+++ b/doc/syntax/nanorc.nanorc
@@ -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))?\>"
diff --git a/doc/syntax/nftables.nanorc b/doc/syntax/nftables.nanorc
index 9c24677391d21510f607412ce69b0e8cc0d48b4c..5bec940d931cba508f1a7b5ef16b0d6f839582a2 100644
--- a/doc/syntax/nftables.nanorc
+++ b/doc/syntax/nftables.nanorc
@@ -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)\>"
diff --git a/doc/syntax/objc.nanorc b/doc/syntax/objc.nanorc
index a2a11d6305d2ebbfc05adcac38a1ee6f32024f9a..5a14ada33e44c258f6c5a02304d98c43e0c9124d 100644
--- a/doc/syntax/objc.nanorc
+++ b/doc/syntax/objc.nanorc
@@ -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_]+\>"
diff --git a/doc/syntax/ocaml.nanorc b/doc/syntax/ocaml.nanorc
index 5806c67cf330533ba876f89d329be42dc89cd0c0..b2b3aa0fcdd7b42a37227d879e9b9d7f83809d7c 100644
--- a/doc/syntax/ocaml.nanorc
+++ b/doc/syntax/ocaml.nanorc
@@ -1,6 +1,7 @@
 ## Syntax highlighting for OCaml.
 
 syntax "ocaml" "\.mli?$"
+comment "(*|*)"
 
 # Uid:
 color red "\<[A-Z][0-9a-z_]{2,}\>"
diff --git a/doc/syntax/patch.nanorc b/doc/syntax/patch.nanorc
index 744408cdb8076cfbcefdd741cd24786e6c24e9d2..b3660bf9e93e2229d7ff7f0363d1c7632ad90fd6 100644
--- a/doc/syntax/patch.nanorc
+++ b/doc/syntax/patch.nanorc
@@ -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 "^\+.*"
diff --git a/doc/syntax/perl.nanorc b/doc/syntax/perl.nanorc
index 6a70d3d1c16d6386f656e77f6a0cbdd35b937975..97ab68ac3d61e26db36eb41561fb2834d42734e9 100644
--- a/doc/syntax/perl.nanorc
+++ b/doc/syntax/perl.nanorc
@@ -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)\>"
diff --git a/doc/syntax/php.nanorc b/doc/syntax/php.nanorc
index ea9aceeda58787091a769211cf7f4db24ffc1180..0b803c9b9a31563dc18ae4a6e6e0e5c79abe7a14 100644
--- a/doc/syntax/php.nanorc
+++ b/doc/syntax/php.nanorc
@@ -2,6 +2,7 @@
 
 syntax "php" "\.php[2345s~]?$"
 magic "PHP script text"
+comment "//"
 
 # PHP markings.
 color brightgreen "(<\?(php)?|\?>)"
diff --git a/doc/syntax/po.nanorc b/doc/syntax/po.nanorc
index c4e11ebd7b7381d64b1b62aba60fc5c22f8b370c..ac8d63c6f908a42937566ed7e8a3aa70feacfdfd 100644
--- a/doc/syntax/po.nanorc
+++ b/doc/syntax/po.nanorc
@@ -1,6 +1,7 @@
 ## Colouring for PO files.
 
 syntax "po" "\.pot?$"
+comment "#"
 
 # Comments.
 color green "^#.*$"
diff --git a/doc/syntax/postgresql.nanorc b/doc/syntax/postgresql.nanorc
index cf389c4c654600ccd4b887c92d2843f1c54977e0..6dd471d1a4faabe1fdf5d35aeea6ce7473d0516c 100644
--- a/doc/syntax/postgresql.nanorc
+++ b/doc/syntax/postgresql.nanorc
@@ -2,6 +2,7 @@
 
 syntax "sql" "\.sql[2345s~]?$"
 magic "PostgreSQL script text"
+comment "-- "
 
 # Functions.
 color white "\<[a-z_]*\("
diff --git a/doc/syntax/pov.nanorc b/doc/syntax/pov.nanorc
index b7033b1090d6b58f94e14f1e12c695e86ea4a18d..7c5d764be583eaf7912141a3b891dfd2361a9e1d 100644
--- a/doc/syntax/pov.nanorc
+++ b/doc/syntax/pov.nanorc
@@ -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)\>"
diff --git a/doc/syntax/python.nanorc b/doc/syntax/python.nanorc
index 5c35f3b677feb6237f06d3c0f25cd66a6a2c9ad6..7d4c40bfa5d6107f02d904b8097fe08df552bc5e 100644
--- a/doc/syntax/python.nanorc
+++ b/doc/syntax/python.nanorc
@@ -3,6 +3,7 @@
 syntax "python" "\.py$"
 header "^#!.*python[-0-9._]*"
 linter pyflakes
+comment "#"
 
 # Function definitions.
 icolor brightblue "def [0-9A-Z_]+"
diff --git a/doc/syntax/ruby.nanorc b/doc/syntax/ruby.nanorc
index a70722160a3b7f300f2bb65ff7cbf375d646fb53..0c5abce94d6ef0a5ccb4df862babc9298bcca1eb 100644
--- a/doc/syntax/ruby.nanorc
+++ b/doc/syntax/ruby.nanorc
@@ -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)\>"
diff --git a/doc/syntax/sh.nanorc b/doc/syntax/sh.nanorc
index 11eb9a458035d30a19f4eee557e260ea35b59f97..17e6dd9d03a4cd450fa5f51c0a2acd0223a32c61 100644
--- a/doc/syntax/sh.nanorc
+++ b/doc/syntax/sh.nanorc
@@ -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)\>"
diff --git a/doc/syntax/spec.nanorc b/doc/syntax/spec.nanorc
index 47de211aa4f6d9c3a199efb6b9d45324a3568198..407cab17c314f59e5e2a647eaab40852afd255ca 100644
--- a/doc/syntax/spec.nanorc
+++ b/doc/syntax/spec.nanorc
@@ -1,6 +1,7 @@
 ## Syntax highlighting for RPM spec files.
 
 syntax "spec" "\.(spec$|spec\.*)"
+comment "#"
 
 # Main tags.
 color brightblue "((Icon|ExclusiveOs|ExcludeOs)[[:space:]]*:)"
diff --git a/doc/syntax/tcl.nanorc b/doc/syntax/tcl.nanorc
index 8bcec2467542b115752005f3bb4b00941f6faa4d..adbb60597db4c3770df055ce9d7d846012d95516 100644
--- a/doc/syntax/tcl.nanorc
+++ b/doc/syntax/tcl.nanorc
@@ -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)\>"
diff --git a/doc/syntax/tex.nanorc b/doc/syntax/tex.nanorc
index a89cff90bb7e0314bf8919cee9a0008d0960d53f..6f2d7ab52454e93a482ae4bff22c77b7c5c42fa7 100644
--- a/doc/syntax/tex.nanorc
+++ b/doc/syntax/tex.nanorc
@@ -2,6 +2,7 @@
 
 syntax "tex" "\.tex$"
 linter chktex -v0 -q -I
+comment "%"
 
 icolor green "\\.|\\[A-Z]*"
 color magenta "[{}]"
diff --git a/doc/syntax/texinfo.nanorc b/doc/syntax/texinfo.nanorc
index 63354bae9c7cd7bc53694f13865eefab302aabad..9d10ecad2b959a09299209f695f51a1cfd98555e 100644
--- a/doc/syntax/texinfo.nanorc
+++ b/doc/syntax/texinfo.nanorc
@@ -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:]]+.*$"
diff --git a/doc/syntax/xml.nanorc b/doc/syntax/xml.nanorc
index 10e6867b67c2661420a2265eca11c16d3ad7c4f4..914cdf96430902a64959866377073453c72bef2a 100644
--- a/doc/syntax/xml.nanorc
+++ b/doc/syntax/xml.nanorc
@@ -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=">"
diff --git a/doc/texinfo/nano.texi b/doc/texinfo/nano.texi
index 3589f210fe3dbf0d4408291c1c93fc8410dc42af..2b3564fe55623e574540c23a5c157d1e2b167fc0 100644
--- a/doc/texinfo/nano.texi
+++ b/doc/texinfo/nano.texi
@@ -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).
 
diff --git a/src/global.c b/src/global.c
index 5805c187bc2a4aaa1d3ad1ab672ca65a5f52cc31..1e15b177dec740c4c77ab567d40a45b3fcce0eca 100644
--- a/src/global.c
+++ b/src/global.c
@@ -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;
diff --git a/src/nano.c b/src/nano.c
index 68a135686d975c30ebf33154fee5499d812d9c08..e4afe8b44c37db9c38f846cc26056c645dd4da7a 100644
--- a/src/nano.c
+++ b/src/nano.c
@@ -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
diff --git a/src/nano.h b/src/nano.h
index 9ffbbc160f4e9183c4d84863ecdf45bf76b30caf..829799d2339d87cb3b3752d82aeb89b730aa9180 100644
--- a/src/nano.h
+++ b/src/nano.h
@@ -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;
diff --git a/src/proto.h b/src/proto.h
index 1f5e16204f268d83aa087bf38c7e25eeab1b3e04..16a2a90760d57cb1b8f964cf2d26b04c53cb4ad6 100644
--- a/src/proto.h
+++ b/src/proto.h
@@ -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);
diff --git a/src/rcfile.c b/src/rcfile.c
index c760c3ffcf6e56493d9184861899e944870afdee..8face04c308eda5bfcc3531b1d94f3f3bfb4e88d 100644
--- a/src/rcfile.c
+++ b/src/rcfile.c
@@ -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);
diff --git a/src/text.c b/src/text.c
index 3f2bd4c9dac1c9e8c0fdfde28765d67243b9f3d0..9132cfb316de0287bc082367d9de50f2d71f5f53 100644
--- a/src/text.c
+++ b/src/text.c
@@ -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,