Add TODO about keybinding cheat sheet
[tig] / tig.c
diff --git a/tig.c b/tig.c
index 06dd14b..94af086 100644 (file)
--- a/tig.c
+++ b/tig.c
@@ -582,24 +582,33 @@ parse_options(int argc, char *argv[])
  * All text after the comment character to the end of the line is ignored.
  * You can use comments to annotate your initialization file.
  *
- * Configuration Commands
- * ~~~~~~~~~~~~~~~~~~~~~~
+ * Some sample options:
  *
- * --
- *
- * *color* object fgcolor bgcolor [attributes]::
+ * ==========================================================================
+ *     # Diff colors
+ *     color diff-header       yellow  default
+ *     color diff-index        blue    default
+ *     color diff-chunk        magenta default
+ *     # UI colors
+ *     color title-blur        white   blue
+ *     color title-focus       white   blue    bold
+ * ==========================================================================
  *
+ * [[color-options]]
+ * Color options
+ * ~~~~~~~~~~~~~
+ * Color options control highlighting and the user interface styles.
  * If  your terminal supports color, these commands can be used to assign
- * foreground/backgound combinations to certain objects. Optionally, an
- * attribute can be given as the last parameter.
+ * foreground/backgound combinations to certain areas. Optionally, an
+ * attribute can be given as the last parameter. The syntax is:
  *
- * Valid objects are described in the "<<color-objs, Color objects>>" section
- * below. Note, object names are case-insensitive, and you may use '-', '_',
- * and '.' interchangeably. So "Diff-Header", "DIFF_HEADER", and "diff.header"
- * are the same.
+ * [verse]
+ * ..........................................................................
+ *     *color* 'area' 'fgcolor' 'bgcolor' '[attributes]'
+ * ..........................................................................
  *
- * Valid colors include: 'white', 'black', 'green', 'magenta', 'blue', 'cyan',
- * 'yellow', 'red', 'default'. Use 'default' to refer to the default terminal
+ * Valid colors include: *white*, *black*, *green*, *magenta*, *blue*, *cyan*,
+ * *yellow*, *red*, *default*. Use *default* to refer to the default terminal
  * colors.
  **/
 
@@ -617,8 +626,8 @@ static struct int_map color_map[] = {
 };
 
 /**
- * Valid attributes include: 'normal', 'blink', 'bold', 'dim', 'reverse', 'standout',
- * and 'underline'. Note, not all attributes may be supported by the terminal.
+ * Valid attributes include: *normal*, *blink*, *bold*, *dim*, *reverse*, *standout*,
+ * and *underline*. Note, not all attributes may be supported by the terminal.
  **/
 
 static struct int_map attr_map[] = {
@@ -633,36 +642,19 @@ static struct int_map attr_map[] = {
 };
 
 /**
- * Some example color options:
- *
- * --------------------------------------------------------------------------
- *     # Diff colors
- *     color diff-header       yellow  default
- *     color diff-index        blue    default
- *     color diff-chunk        magenta default
- *     # UI colors
- *     color title-blur        white   blue
- *     color title-focus       white   blue    bold
- * --------------------------------------------------------------------------
+ * Valid area names are described below. Note, all names are case-insensitive,
+ * and you may use '-', '_', and '.' interchangeably. So "Diff-Header",
+ * "DIFF_HEADER", and "diff.header" are the same.
  *
  * --
  **/
-
-/**
- * [[color-objs]]
- * Color objects
- * ~~~~~~~~~~~~~
- *
- * --
- **/
-
 #define LINE_INFO \
 /**
  * Diff markup::
  *
- * Objects concerning diff start, chunks and lines added and deleted.
+ * Options concerning diff start, chunks and lines added and deleted.
  *
- * 'diff-header', 'diff-chunk', 'diff-add', 'diff-del'
+ * *diff-header*, *diff-chunk*, *diff-add*, *diff-del*
  **/ \
 LINE(DIFF_HEADER,  "diff --git ",      COLOR_YELLOW,   COLOR_DEFAULT,  0), \
 LINE(DIFF_CHUNK,   "@@",               COLOR_MAGENTA,  COLOR_DEFAULT,  0), \
@@ -674,9 +666,9 @@ LINE(DIFF_DEL,         "-",                 COLOR_RED,      COLOR_DEFAULT,  0), \
  * Extra diff information emitted by the git diff machinery, such as mode
  * changes, rename detection, and similarity.
  *
- * 'diff-oldmode', 'diff-newmode', 'diff-copy-from', 'diff-copy-to',
- * 'diff-rename-from', 'diff-rename-to', 'diff-similarity' 'diff-dissimilarity'
- * 'diff-tree', 'diff-index'
+ * *diff-oldmode*, *diff-newmode*, *diff-copy-from*, *diff-copy-to*,
+ * *diff-rename-from*, *diff-rename-to*, *diff-similarity* *diff-dissimilarity*
+ * *diff-tree*, *diff-index*
  **/ \
 LINE(DIFF_INDEX,       "index ",         COLOR_BLUE,   COLOR_DEFAULT,  0), \
 LINE(DIFF_OLDMODE,     "old file mode ", COLOR_YELLOW, COLOR_DEFAULT,  0), \
@@ -695,7 +687,7 @@ LINE(DIFF_TREE,             "diff-tree ",     COLOR_BLUE,   COLOR_DEFAULT,  0), \
  * printed headers , unless `--pretty=raw` was given. This includes lines,
  * such as merge info, commit ID, and author and comitter date.
  *
- * 'pp-author' 'pp-commit' 'pp-merge' 'pp-date' 'pp-adate' 'pp-cdate'
+ * *pp-author*, *pp-commit*, *pp-merge*, *pp-date*, *pp-adate*, *pp-cdate*
  **/ \
 LINE(PP_AUTHOR,           "Author: ",          COLOR_CYAN,     COLOR_DEFAULT,  0), \
 LINE(PP_COMMIT,           "Commit: ",          COLOR_MAGENTA,  COLOR_DEFAULT,  0), \
@@ -709,7 +701,7 @@ LINE(PP_CDATE,         "CommitDate: ",      COLOR_YELLOW,   COLOR_DEFAULT,  0), \
  * Usually shown when `--pretty=raw` is given, however 'commit' is pretty
  * much omnipresent.
  *
- * 'commit' 'parent' 'tree' 'author' 'committer'
+ * *commit*, *parent*, *tree*, *author*, *committer*
  **/ \
 LINE(COMMIT,      "commit ",           COLOR_GREEN,    COLOR_DEFAULT,  0), \
 LINE(PARENT,      "parent ",           COLOR_BLUE,     COLOR_DEFAULT,  0), \
@@ -721,22 +713,22 @@ LINE(COMMITTER,      "committer ",        COLOR_MAGENTA,  COLOR_DEFAULT,  0), \
  *
  * For now only `Signed-off-by lines` are colorized.
  *
- * 'signoff'
+ * *signoff*
  **/ \
 LINE(SIGNOFF,     "    Signed-off-by", COLOR_YELLOW,   COLOR_DEFAULT,  0), \
 /**
  * UI colors::
  *
- * Colors for text not matching any of the above: 'default'
+ * Colors for text not matching any of the above: *default*
  *
- * Status window colors: 'status'
+ * Status window colors: *status*
  *
- * Title window colors: 'title-blur' 'title-focus'
+ * Title window colors: *title-blur*, *title-focus*
  *
- * Cursor line colors: 'cursor'
+ * Cursor line colors: *cursor*
  *
- * Main view specific: 'main-date' 'main-author' 'main-commit' 'main-delim'
- * 'main-tag' 'main-ref'
+ * Main view specific: *main-date*, *main-author*, *main-commit*, *main-delim*,
+ * *main-tag*, *main-ref*
  **/ \
 LINE(DEFAULT,     "",                  COLOR_DEFAULT,  COLOR_DEFAULT,  A_NORMAL), \
 LINE(CURSOR,      "",                  COLOR_WHITE,    COLOR_GREEN,    A_BOLD), \
@@ -853,30 +845,23 @@ struct line {
 };
 
 
+/*
+ * User config file handling.
+ */
+
 #define set_color(color, name, namelen) \
        set_from_int_map(color_map, ARRAY_SIZE(color_map), color, name, namelen)
 
 #define set_attribute(attr, name, namelen) \
        set_from_int_map(attr_map, ARRAY_SIZE(attr_map), attr, name, namelen)
 
+static int   config_lineno;
+static bool  config_errors;
+static char *config_msg;
+
 static int
-read_option(char *opt, int optlen, char *value, int valuelen)
+set_option(char *opt, int optlen, char *value, int valuelen)
 {
-       optlen = strcspn(opt, "#;");
-       if (optlen == 0)
-               /* The whole line is a comment. */
-               return OK;
-
-       else if (opt[optlen] != 0)
-               /* Part of the option name is a comment, so the value part
-                * should be ignored. */
-               valuelen = 0;
-       else
-               /* Else look for comment endings in the value. */
-               valuelen = strcspn(value, "#;");
-
-       opt[optlen] = value[valuelen] = 0;
-
        /* Reads: "color" object fgcolor bgcolor [attr] */
        if (!strcmp(opt, "color")) {
                struct line_info *info;
@@ -884,23 +869,31 @@ read_option(char *opt, int optlen, char *value, int valuelen)
                value = chomp_string(value);
                valuelen = strcspn(value, " \t");
                info = get_line_info(value, valuelen);
-               if (!info)
+               if (!info) {
+                       config_msg = "Unknown color name";
                        return ERR;
+               }
 
                value = chomp_string(value + valuelen);
                valuelen = strcspn(value, " \t");
-               if (set_color(&info->fg, value, valuelen) == ERR)
+               if (set_color(&info->fg, value, valuelen) == ERR) {
+                       config_msg = "Unknown color";
                        return ERR;
+               }
 
                value = chomp_string(value + valuelen);
                valuelen = strcspn(value, " \t");
-               if (set_color(&info->bg, value, valuelen) == ERR)
+               if (set_color(&info->bg, value, valuelen) == ERR) {
+                       config_msg = "Unknown color";
                        return ERR;
+               }
 
                value = chomp_string(value + valuelen);
                if (*value &&
-                   set_attribute(&info->attr, value, strlen(value)) == ERR)
+                   set_attribute(&info->attr, value, strlen(value)) == ERR) {
+                       config_msg = "Unknown attribute";
                        return ERR;
+               }
 
                return OK;
        }
@@ -909,12 +902,47 @@ read_option(char *opt, int optlen, char *value, int valuelen)
 }
 
 static int
+read_option(char *opt, int optlen, char *value, int valuelen)
+{
+       config_lineno++;
+       config_msg = "Internal error";
+
+       optlen = strcspn(opt, "#;");
+       if (optlen == 0) {
+               /* The whole line is a commend or empty. */
+               return OK;
+
+       } else if (opt[optlen] != 0) {
+               /* Part of the option name is a comment, so the value part
+                * should be ignored. */
+               valuelen = 0;
+               opt[optlen] = value[valuelen] = 0;
+       } else {
+               /* Else look for comment endings in the value. */
+               valuelen = strcspn(value, "#;");
+               value[valuelen] = 0;
+       }
+
+       if (set_option(opt, optlen, value, valuelen) == ERR) {
+               fprintf(stderr, "Error on line %d, near '%.*s' option: %s\n",
+                       config_lineno, optlen, opt, config_msg);
+               config_errors = TRUE;
+       }
+
+       /* Always keep going if errors are encountered. */
+       return OK;
+}
+
+static int
 load_options(void)
 {
        char *home = getenv("HOME");
        char buf[1024];
        FILE *file;
 
+       config_lineno = 0;
+       config_errors = FALSE;
+
        if (!home ||
            snprintf(buf, sizeof(buf), "%s/.tig", home) >= sizeof(buf))
                return ERR;
@@ -924,7 +952,11 @@ load_options(void)
        if (!file)
                return OK;
 
-       return read_properties(file, " \t", read_option);
+       if (read_properties(file, " \t", read_option) == ERR ||
+           config_errors == TRUE)
+               fprintf(stderr, "Errors while loading %s.\n", buf);
+
+       return OK;
 }
 
 
@@ -2726,8 +2758,7 @@ read_properties(FILE *pipe, const char *separators,
                        valuelen = 0;
                }
 
-               if (namelen)
-                       state = read_property(name, namelen, value, valuelen);
+               state = read_property(name, namelen, value, valuelen);
        }
 
        if (state != ERR && ferror(pipe))
@@ -2984,6 +3015,8 @@ main(int argc, char *argv[])
  *
  * - Locale support.
  *
+ * - Make '?' show a one page keybinding cheat sheet.
+ *
  * COPYRIGHT
  * ---------
  * Copyright (c) 2006 Jonas Fonseca <fonseca@diku.dk>