06d7be056190c14b85e71bc12523f18ea6815b5e
markd
  Mon Dec 7 00:50:29 2020 -0800
BLAT mmap index support merge with master

diff --git src/hg/hgc/vcfClick.c src/hg/hgc/vcfClick.c
index 1fb648f..a2064a2 100644
--- src/hg/hgc/vcfClick.c
+++ src/hg/hgc/vcfClick.c
@@ -89,137 +89,146 @@
     printf("<B>Filter:</B> "NA"<BR>\n");
 else if (rec->filterCount == 1 && sameString(rec->filters[0], "PASS"))
     printf("<B>Filter:</B> PASS<BR>\n");
 else
     {
     printf("<B>Filter failures:</B> ");
     printf("<font style='font-weight: bold; color: #FF0000;'>\n");
     struct vcfFile *vcff = rec->file;
     printKeysWithDescriptions(vcff, rec->filterCount, rec->filters, vcff->filterDefs, FALSE);
     printf("</font>\n");
     }
 }
 
 
 
-static void printTabularHeaderRow(const struct vcfInfoDef *def)
+static int printTabularHeaderRow(const struct vcfInfoDef *def)
 /* Parse the column header parts out of def->description and print as table header row;
- * call this only when looksTabular returns TRUE. */
+ * call this only when looksTabular returns TRUE.
+ * Returns the number of columns in the header */
 {
 regmatch_t substrArr[PATH_LEN];
 if (regexMatchSubstr(def->description, COL_DESC_REGEX, substrArr, ArraySize(substrArr)))
     {
     puts("<TR>");
     // Make a copy of the part of def->description that matches the regex,
     // then chop by '|' and print out header column tags:
     int matchSize = substrArr[0].rm_eo - substrArr[0].rm_so;
     char copy[matchSize+1];
     safencpy(copy, sizeof(copy), def->description + substrArr[0].rm_so, matchSize);
     // Turn '_' into ' ' so description words can wrap inside headers, saving some space
     subChar(copy, '_', ' ');
     char *words[PATH_LEN];
     int descColCount = chopByChar(copy, '|', words, ArraySize(words));
     int i;
     for (i = 0;  i < descColCount; i++)
         printf("<TH class='withThinBorder'>%s</TH>", words[i]);
     puts("</TR>");
+    return descColCount;
     }
 else
     errAbort("printTabularHeaderRow: code bug, if looksTabular returns true then "
              "regex should work here");
+    return -1;
 }
 
-static void printTabularData(struct vcfInfoElement *el)
+static void printTabularData(struct vcfInfoElement *el, int headerCount)
 /* Print a row for each value in el, separating columns by '|'. */
 {
 int j;
 for (j = 0;  j < el->count;  j++)
     {
     puts("<TR>");
     char *val = el->values[j].datString;
     if (!isEmpty(val))
         {
         int len = strlen(val);
         char copy[len+1];
         safencpy(copy, sizeof(copy), val, len);
         char *words[PATH_LEN];
-        int colCount = chopByChar(copy, '|', words, ArraySize(words));
+        chopByChar(copy, '|', words, ArraySize(words));
         int k;
-        for (k = 0;  k < colCount;  k++)
+        // printTabularHeaderRow strips off (but still prints!) a trailing '|'
+        // because of the regex, so enforce that here too so the rows after
+        // the header don't get all out of whack
+        for (k = 0;  k < headerCount;  k++)
             printf("<TD class='withThinBorder'>%s</TD>", words[k]);
         }
     puts("</TR>");
     }
 }
 
 
-static void vcfInfoDetails(struct vcfRecord *rec)
+static void vcfInfoDetails(struct vcfRecord *rec, char *trackName)
 /* Expand info keys to descriptions, then print out keys and values. */
 {
 if (rec->infoCount == 0)
     return;
 struct vcfFile *vcff = rec->file;
-puts("<B>INFO column annotations:</B><BR>");
-puts("<TABLE border=0 cellspacing=0 cellpadding=2>");
+puts("<table>"); // wrapper table for collapsible section
+jsBeginCollapsibleSectionFontSize(cart, trackName, "infoFields", "INFO column annotations:", FALSE, "medium");
+puts("<TABLE class=\"stdTbl\">\n");
 int i;
 for (i = 0;  i < rec->infoCount;  i++)
     {
     struct vcfInfoElement *el = &(rec->infoElements[i]);
     const struct vcfInfoDef *def = vcfInfoDefForKey(vcff, el->key);
     printf("<TR valign='top'><TD align=\"right\"><B>%s:</B></TD><TD>",
            el->key);
     int j;
     enum vcfInfoType type = def ? def->type : vcfInfoString;
     if (type == vcfInfoFlag && el->count == 0)
 	printf("Yes"); // no values, so we can't call vcfPrintDatum...
     // However, if this is older VCF, type vcfInfoFlag might have a value.
     if (looksTabular(def, el))
         {
         // Make a special display below
         printf("<em>see below</em>");
         }
     else
         {
         for (j = 0;  j < el->count;  j++)
             {
             if (j > 0)
                 printf(", ");
             if (el->missingData[j])
                 printf(".");
             else
                 vcfPrintDatum(stdout, el->values[j], type);
             }
         }
-    if (def != NULL)
+    if (def != NULL && !looksTabular(def, el))
 	printf("&nbsp;&nbsp;</TD><TD>%s", def->description);
     else
 	printf("</TD><TD>");
     printf("</TD></TR>\n");
     }
 puts("</TABLE>");
+jsEndCollapsibleSection();
+puts("</table>"); // close the wrapper around the collapsible section
 // Now show the tabular fields, if any
 for (i = 0;  i < rec->infoCount;  i++)
     {
     struct vcfInfoElement *el = &(rec->infoElements[i]);
     const struct vcfInfoDef *def = vcfInfoDefForKey(vcff, el->key);
     if (looksTabular(def, el))
         {
         puts("<BR>");
         printf("<B>%s</B>: %s<BR>\n", el->key, def->description);
         puts("<TABLE class='stdTbl'>");
-        printTabularHeaderRow(def);
-        printTabularData(el);
+        int headerCount = printTabularHeaderRow(def);
+        printTabularData(el, headerCount);
         puts("</TABLE>");
         }
     }
 }
 
 static void vcfGenotypeTable(struct vcfRecord *rec, char *track, char **displayAls)
 /* Put the table containing details about each genotype into a collapsible section. */
 {
 static struct dyString *tmp1 = NULL;
 if (tmp1 == NULL)
     tmp1 = dyStringNew(0);
 jsBeginCollapsibleSection(cart, track, "genotypes", "Detailed genotypes", FALSE);
 dyStringClear(tmp1);
 dyStringAppend(tmp1, rec->format);
 struct vcfFile *vcff = rec->file;
@@ -492,31 +501,31 @@
                               seqName, rec->chromStart, formName);
         printf("</TABLE></FORM>\n");
         }
     }
 char leftBase = rec->alleles[0][0];
 unsigned int vcfStart = vcfRecordTrimIndelLeftBase(rec);
 boolean showLeftBase = (rec->chromStart == vcfStart+1);
 (void)vcfRecordTrimAllelesRight(rec);
 char *displayAls[rec->alleleCount];
 makeDisplayAlleles(rec, showLeftBase, leftBase, 20, TRUE, FALSE, displayAls);
 printPosOnChrom(seqName, rec->chromStart, rec->chromEnd, NULL, FALSE, rec->name);
 printf("<B>Reference allele:</B> %s<BR>\n", displayAls[0]);
 vcfAltAlleleDetails(rec, displayAls);
 vcfQualDetails(rec);
 vcfFilterDetails(rec);
-vcfInfoDetails(rec);
+vcfInfoDetails(rec, tdb->track);
 pgSnpCodingDetail(rec);
 makeDisplayAlleles(rec, showLeftBase, leftBase, 5, FALSE, TRUE, displayAls);
 vcfGenotypesDetails(rec, tdb, displayAls);
 }
 
 void doVcfDetailsCore(struct trackDb *tdb, char *fileOrUrl, boolean isTabix)
 /* Show item details using fileOrUrl. */
 {
 genericHeader(tdb, NULL);
 int start = cartInt(cart, "o");
 int end = cartInt(cart, "t");
 int vcfMaxErr = -1;
 struct vcfFile *vcff = NULL;
 /* protect against temporary network or parsing error */
 struct errCatch *errCatch = errCatchNew();