From afde1750364866e95e2665303ca5f8e2631c9f4d Mon Sep 17 00:00:00 2001
From: Benno Schulenberg <bensberg@telfort.nl>
Date: Mon, 29 Oct 2018 20:40:55 +0100
Subject: [PATCH 1/2 V4] new feature: a 'tabgives' command to define what the
 Tab key produces

V4: * Rename the command to 'tabgives' to make it clearer that it is
      about what pressing <Tab> produces.
    * When comparing with 'tabgives' fails, fall back to matching a
      normal tabsize worth of whitespace.  This way, <Shift+Tab> can
      always be used to remove existing/erroneous leading whitespace.

The 'tabgives' command is syntax-specific and should be followed by a
string containing the character(s) that a single press of the <Tab> key
should produce -- most likely a single TAB or a small bunch of spaces,
but any string is allowed.  This overrides the 'tabstospaces' option.

When one wants to make sure <Tab> inserts always four spaces and never
a TAB when editing a Python file, one could add to one's nanorc:

  extendsyntax python tabgives "    "

where there are four spaces between the quotes.  And when one wants
to ensure, when editing a Makefile, that <Tab> always inserts a TAB
and never spaces, independent of what tabstospaces is set to, one
could add to one's nanorc:

  extendsyntax makefile tabgives "	"

where there is a literal TAB character between the quotes.

This addresses https://savannah.gnu.org/bugs/?53661,
and addresses https://savannah.gnu.org/bugs/?54760,
and addresses part of https://savannah.gnu.org/bugs/?54775.
---
 src/nano.h           |  2 ++
 src/rcfile.c         |  6 ++++++
 src/text.c           | 22 ++++++++++++++++++++++
 syntax/nanorc.nanorc |  6 +++---
 4 files changed, 33 insertions(+), 3 deletions(-)

diff --git a/src/nano.h b/src/nano.h
index 689f3653..101729ae 100644
--- a/src/nano.h
+++ b/src/nano.h
@@ -234,6 +234,8 @@ typedef struct syntaxtype {
 		/* The list of libmagic results that this syntax applies to. */
 	char *linter;
 		/* The command with which to lint this type of file. */
+	char *tab;
+		/* What the Tab key should produce; NULL for default behavior. */
 #ifdef ENABLE_COMMENT
 	char *comment;
 		/* The line comment prefix (and postfix) for this type of file. */
diff --git a/src/rcfile.c b/src/rcfile.c
index d95d1549..dc585152 100644
--- a/src/rcfile.c
+++ b/src/rcfile.c
@@ -333,6 +333,7 @@ void begin_new_syntax(char *ptr)
 	live_syntax->headers = NULL;
 	live_syntax->magics = NULL;
 	live_syntax->linter = NULL;
+	live_syntax->tab = NULL;
 #ifdef ENABLE_COMMENT
 	live_syntax->comment = mallocstrcpy(NULL, GENERAL_COMMENT_CHARACTER);
 #endif
@@ -947,6 +948,10 @@ bool parse_syntax_commands(char *keyword, char *ptr)
 	else if (strcasecmp(keyword, "comment") == 0) {
 #ifdef ENABLE_COMMENT
 		pick_up_name("comment", ptr, &live_syntax->comment);
+#endif
+	} else if (strcasecmp(keyword, "tabgives") == 0) {
+#ifdef ENABLE_COLOR
+		pick_up_name("tabgives", ptr, &live_syntax->tab);
 #endif
 	} else if (strcasecmp(keyword, "linter") == 0)
 		pick_up_name("linter", ptr, &live_syntax->linter);
@@ -1090,6 +1095,7 @@ void parse_rcfile(FILE *rcstream, bool just_syntax, bool intros_only)
 		} else if (intros_only && (strcasecmp(keyword, "color") == 0 ||
 								strcasecmp(keyword, "icolor") == 0 ||
 								strcasecmp(keyword, "comment") == 0 ||
+								strcasecmp(keyword, "tabgives") == 0 ||
 								strcasecmp(keyword, "linter") == 0)) {
 			if (!opensyntax)
 				jot_error(N_("A '%s' command requires a preceding "
diff --git a/src/text.c b/src/text.c
index b1a66ba9..39bfbc91 100644
--- a/src/text.c
+++ b/src/text.c
@@ -76,6 +76,11 @@ const char *invocation_error(const char *name)
  * of spaces that a tab would normally take up. */
 void do_tab(void)
 {
+#ifdef ENABLE_COLOR
+	if (openfile->syntax && openfile->syntax->tab)
+		do_output(openfile->syntax->tab, strlen(openfile->syntax->tab), TRUE);
+	else
+#endif
 #ifndef NANO_TINY
 	if (ISSET(TABS_TO_SPACES)) {
 		char *spaces = charalloc(tabsize + 1);
@@ -141,6 +146,11 @@ void do_indent(void)
 	indentation = charalloc(tabsize + 1);
 
 	/* Set the indentation to either a bunch of spaces or a single tab. */
+#ifdef ENABLE_COLOR
+	if (openfile->syntax && openfile->syntax->tab)
+		indentation = mallocstrcpy(indentation, openfile->syntax->tab);
+	else
+#endif
 	if (ISSET(TABS_TO_SPACES)) {
 		memset(indentation, ' ', tabsize);
 		indentation[tabsize] = '\0';
@@ -174,6 +184,18 @@ size_t length_of_white(const char *text)
 {
 	size_t white_count = 0;
 
+#ifdef ENABLE_COLOR
+	if (openfile->syntax && openfile->syntax->tab) {
+		size_t thelength = strlen(openfile->syntax->tab);
+
+		while (text[white_count] == openfile->syntax->tab[white_count])
+			if (++white_count == thelength)
+				return thelength;
+
+		white_count = 0;
+	}
+#endif
+
 	while (TRUE) {
 		if (*text == '\t')
 			return ++white_count;
diff --git a/syntax/nanorc.nanorc b/syntax/nanorc.nanorc
index bf31e92d..82dcbc64 100644
--- a/syntax/nanorc.nanorc
+++ b/syntax/nanorc.nanorc
@@ -4,7 +4,7 @@ syntax nanorc "\.?nanorc$"
 comment "#"
 
 # Possible errors and parameters
-icolor brightred "^[[:space:]]*((un)?(bind|set)|include|syntax|header|magic|comment|linter|i?color|extendsyntax).*"
+icolor brightred "^[[:space:]]*((un)?(bind|set)|include|syntax|header|magic|comment|linter|tabgives|i?color|extendsyntax).*"
 
 # Keywords
 icolor brightgreen "^[[:space:]]*(set|unset)[[:space:]]+(afterends|allow_insecure_backup|atblanks|autoindent|backup|backwards|boldtext|breaklonglines|casesensitive|constantshow|cutfromcursor|emptyline|finalnewline|historylog|jumpyscrolling|linenumbers|locking|morespace|mouse|multibuffer|noconvert|nohelp|nopauses|nonewlines|nowrap|positionlog|preserve|quickblank|quiet|rebinddelete|rebindkeypad|regexp|showcursor|smarthome|smooth|softwrap|suspend|tabstospaces|tempfile|trimblanks|unix|view|wordbounds|zap)\>"
@@ -13,9 +13,9 @@ icolor brightgreen "^[[:space:]]*set[[:space:]]+(backupdir|brackets|errorcolor|f
 icolor brightgreen "^[[:space:]]*set[[:space:]]+(fill[[:space:]]+-?[[:digit:]]+|(guidestripe|tabsize)[[:space:]]+[1-9][0-9]*)\>"
 icolor brightgreen "^[[:space:]]*bind[[:space:]]+((\^([[:alpha:]]|[]@\^_]|Space)|M-([[:alpha:]]|[]!"#$%&'()*+,./0-9:;<=>?@\^_`{|}~-]|Space))|F([1-9]|1[0-6])|Ins|Del)[[:space:]]+([[:alpha:]]+|".*")[[:space:]]+(all|main|search|replace(with)?|yesno|gotoline|writeout|insert|ext(ernal)?cmd|help|spell|linter|browser|whereisfile|gotodir)([[:space:]]+#|[[:space:]]*$)"
 icolor brightgreen "^[[:space:]]*unbind[[:space:]]+((\^([[:alpha:]]|[]@\^_]|Space)|M-([[:alpha:]]|[]!"#$%&'()*+,./0-9:;<=>?@\^_`{|}~-]|Space))|F([1-9]|1[0-6])|Ins|Del)[[:space:]]+(all|main|search|replace(with)?|yesno|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|comment|linter)[[:space:]]+.*"
+icolor brightgreen "^[[:space:]]*extendsyntax[[:space:]]+[[:alpha:]]+[[:space:]]+(i?color|header|magic|comment|linter|tabgives)[[:space:]]+.*"
 icolor brightgreen "^[[:space:]]*(syntax|linter)[[:space:]]+[^[:blank:]]+"
-icolor green "^[[:space:]]*((un)?(bind|set)|include|syntax|header|magic|comment|linter|extendsyntax)\>"
+icolor green "^[[:space:]]*((un)?(bind|set)|include|syntax|header|magic|comment|linter|tabgives|extendsyntax)\>"
 
 # Strings
 color brightmagenta "([[:space:]]|(start|end)=)".+"([[:space:]]|$)"
-- 
2.23.0

