6b045fb4555e28c738cab4be57b8a3750584fe9b
angie
  Fri Sep 29 17:14:01 2017 -0700
Don't inject highlight straight into cart when hgFind finds a match for a search with padding -- only set highlight when there's a single match or when the user clicks on a link to select a specific match.  fixes #20245

diff --git src/hg/lib/hgFind.c src/hg/lib/hgFind.c
index 2142977..7018a05 100644
--- src/hg/lib/hgFind.c
+++ src/hg/lib/hgFind.c
@@ -2438,30 +2438,36 @@
 		fprintf(f, "%s&", extraCgi);
 		fprintf(f, "%s=%s&", trackName, vis);
 		// this is magic to tell the browser to make the 
 		// composite and this subTrack visible
 		if (tdb->parent)
 		    {
 		    if (tdbIsSuperTrackChild(tdb))
 			fprintf(f, "%s=show&", tdb->parent->track);
 		    else
 			{
 			// tdb is a subtrack of a composite or a view
 			fprintf(f, "%s_sel=1&", trackName);
 			fprintf(f, "%s_sel=1&", tdb->parent->track);
 			}
 		    }
+                if (isNotEmpty(pos->highlight))
+                    {
+                    char *encHighlight = cgiEncode(pos->highlight);
+                    fprintf(f, "highlight=%s&", encHighlight);
+                    freeMem(encHighlight);
+                    }
 		fprintf(f, "hgFind.matches=%s,\">", encMatches);
 		// Bold canonical genes. 
 		if(pos->canonical) {
 		    fprintf(f, "<B>");
 		    }
 		htmTextOut(f, pos->name);
 		if(pos->canonical) {
 		    fprintf(f, "</B>");
 		    }
 		fprintf(f, " at %s</A>", range);
 		desc = pos->description;
 		if (desc)
 		    {
 		    fprintf(f, " - ");
 		    htmTextOut(f, desc);
@@ -2724,50 +2730,55 @@
     {
     if (!isFuzzy || keyIsPrefixIgnoreCase(term, row[1]))
         {
 	xrefPtr = slPairNew(cloneString(row[1]), cloneString(row[0]));
 	slAddHead(&xrefList, xrefPtr);
 	}
     }
 sqlFreeResult(&sr);
 hFreeConn(&conn);
 slReverse(&xrefList);
 if (xrefList == NULL && hgFindSpecSetting(hfs, "searchBoth") != NULL)
     xrefList = slPairNew(cloneString(""), cloneString(term));
 return(xrefList);
 }
 
-static void addHighlight(struct cart *cart, char *db, char *chrom, unsigned start, unsigned end)
-/* Add the given region to the cart variable highlight. */
+static char *addHighlight(struct cart *cart, char *db, char *chrom, unsigned start, unsigned end)
+/* Add the given region to the existing value of the cart variable highlight.
+ * Return new value for highlight, or NULL if no change is necessary (already highlighted). */
 {
 char *color = "fcfcac";
 struct dyString *dy = dyStringCreate("%s.%s:%u-%u#%s", db, chrom, start+1, end, color);
+boolean alreadySet = FALSE;
 char *existing = cartOptionalString(cart, "highlight");
-if (isEmpty(existing))
-    cartSetString(cart, "highlight", dyStringContents(dy));
-else
+if (isNotEmpty(existing))
     {
     // Don't add region if it is already in the existing highlight setting.
     char *alreadyIn = strstr(existing, dyStringContents(dy));
-    if (!alreadyIn ||
-        !(alreadyIn[dyStringLen(dy)] == '|' || alreadyIn[dyStringLen(dy)] == '\0'))
-        {
+    if (alreadyIn &&
+        (alreadyIn[dyStringLen(dy)] == '|' || alreadyIn[dyStringLen(dy)] == '\0'))
+        alreadySet = TRUE;
+    else
         dyStringPrintf(dy, "|%s", existing);
-        cartSetString(cart, "highlight", dyStringContents(dy));
-        }
     }
+if (alreadySet)
+    {
     dyStringFree(&dy);
+    return NULL;
+    }
+else
+    return dyStringCannibalize(&dy);
 }
 
 static boolean doQuery(char *db, struct hgFindSpec *hfs, char *xrefTerm, char *term,
 		       struct hgPositions *hgp,
 		       boolean relativeFlag, int relStart, int relEnd,
 		       boolean multiTerm)
 /* Perform a query as specified in hfs, assuming table existence has been 
  * checked and xref'ing has been taken care of. */
 {
 struct slName *tableList = hSplitTableNames(db, hfs->searchTable);
 struct slName *tPtr = NULL;
 struct hgPosTable *table = NULL;
 struct hgPos *pos = NULL;
 struct sqlConnection *conn = hAllocConn(db);
 struct sqlResult *sr = NULL;
@@ -2824,31 +2835,31 @@
 	pos->browserName = cloneString(row[3]);
 	if (isNotEmpty(xrefTerm))
 	    {
 	    safef(buf, sizeof(buf), "(%s%s)",
 		  termPrefix ? termPrefix : "", row[3]);
 	    pos->description = cloneString(buf);
 	    }
 	if (relativeFlag && (pos->chromStart + relEnd) <= pos->chromEnd)
 	    {
 	    pos->chromEnd   = pos->chromStart + relEnd;
 	    pos->chromStart = pos->chromStart + relStart;
 	    }
 	else if (padding > 0 && !multiTerm)
 	    {
             // highlight the item bases to distinguish from padding
-            addHighlight(cart, db, pos->chrom, pos->chromStart, pos->chromEnd);
+            pos->highlight = addHighlight(cart, db, pos->chrom, pos->chromStart, pos->chromEnd);
 	    int chromSize = hChromSize(db, pos->chrom);
 	    pos->chromStart -= padding;
 	    pos->chromEnd   += padding;
 	    if (pos->chromStart < 0)
 		pos->chromStart = 0;
 	    if (pos->chromEnd > chromSize)
 		pos->chromEnd = chromSize;
 	    }
 	slAddHead(&table->posList, pos);
 	}
 
     }
 if (table != NULL)
     slReverse(&table->posList);
 sqlFreeResult(&sr);
@@ -3080,31 +3091,32 @@
     if (dyStringLen(dyWarn) > 0)
         warn("%s", dyStringContents(dyWarn));
     if (mapping)
         {
         int padding = 5;
         char *trackTable;
         if (isEmpty(pslTable))
             trackTable = "chromInfo";
         else if (startsWith("lrg", pslTable))
             trackTable = "lrgTranscriptAli";
         else
             trackTable = "refGene";
         singlePos(hgp, "HGVS", NULL, trackTable, term, "",
                   mapping->chrom, mapping->chromStart-padding, mapping->chromEnd+padding);
         // highlight the mapped bases to distinguish from padding
-        addHighlight(cart, db, mapping->chrom, mapping->chromStart, mapping->chromEnd);
+        hgp->tableList->posList->highlight = addHighlight(cart, db, mapping->chrom,
+                                                          mapping->chromStart, mapping->chromEnd);
         foundIt = TRUE;
         }
     dyStringFree(&dyWarn);
     }
 return foundIt;
 }
 
 struct hgPositions *hgPositionsFind(char *db, char *term, char *extraCgi,
 	char *hgAppNameIn, struct cart *cart, boolean multiTerm)
 /* Return table of positions that match term or NULL if none such. */
 {
 struct hgPositions *hgp = NULL, *hgpItem = NULL;
 regmatch_t substrs[4];
 boolean canonicalSpec = FALSE;
 boolean gbrowserSpec = FALSE;
@@ -3253,30 +3265,32 @@
 	    {
 	    struct hgPos *pos = NULL;
 	    for(pos = hpTable->posList; pos != NULL; pos = pos->next)
 		{
 		dyStringPrintf(hgpMatchNames, "%s,", pos->browserName);
 		}
 	    }
 	}
     if (cart != NULL)
         cartSetString(cart, "hgFind.matches", hgpMatchNames->string);
     }
 slReverse(&hgp->tableList);
 if (multiTerm)
     collapseSamePos(hgp);
 fixSinglePos(hgp);
+if (cart && hgp->singlePos && isNotEmpty(hgp->singlePos->highlight))
+    cartSetString(cart, "highlight", hgp->singlePos->highlight);
 return hgp;
 }
 
 
 void hgPositionsHelpHtml(char *organism, char *database)
 /* Display contents of dbDb.htmlPath for database, or print an HTML comment 
  * explaining what's missing. */
 {
 char *htmlPath = hHtmlPath(database);
 char *htmlString = NULL;
 size_t htmlStrLength = 0;
 
 if (strstrNoCase(organism, "zoo")) 
     webNewSection("About the NISC Comparative Sequencing Program Browser");
 else