83858083785a6b2b1f3c05f93e818e5a89318692
max
  Tue Aug 25 01:42:34 2026 -0700
hgSession: show cytoBand and gene/locus annotations for each saved session; date-only Created/Last used columns with full timestamp on hover. Factor locusName lookup into reusable hLocusName/hLocusNameExpand in hdb.c and reuse from hgBlat. refs #38157

The Assembly column now shows band, gene/locus name and Mbp position (separated by whitespace, normal text size). hLocusNameExpand centralizes the ex:/in:/ig: and | to - expansion that hgBlat's results page did inline.

diff --git src/hg/lib/hdb.c src/hg/lib/hdb.c
index 910a3a8922b..61c17b9f7ce 100644
--- src/hg/lib/hdb.c
+++ src/hg/lib/hdb.c
@@ -1429,30 +1429,70 @@
 boolean hChromBand(char *db, char *chrom, int pos, char retBand[HDB_MAX_BAND_STRING])
 /* Return text string that says what band pos is on.
  * Return FALSE if not on any band, or table missing. */
 {
 if (trackHubDatabase(db) || !hTableExists(db, "cytoBand"))
     return FALSE;
 else
     {
     struct sqlConnection *conn = hAllocConn(db);
     boolean ok = hChromBandConn(conn, chrom, pos, retBand);
     hFreeConn(&conn);
     return ok;
     }
 }
 
+char *hLocusNameExpand(char *raw)
+/* Expand the abbreviations in a locusName-table label into a human-readable string:
+ * "ex:" -> "exon ", "in:" -> "intron ", "ig:" -> "intergenic ", and the "|" that
+ * separates gene symbols -> "-".  Returns a cloneString'd value (caller frees), or
+ * NULL for empty input. */
+{
+if (isEmpty(raw))
+    return NULL;
+struct dyString *dy = dyStringNew(64);
+char *genes = raw;
+if (startsWith("ex:", raw))
+    { dyStringAppend(dy, "exon "); genes = raw + 3; }
+else if (startsWith("in:", raw))
+    { dyStringAppend(dy, "intron "); genes = raw + 3; }
+else if (startsWith("ig:", raw))
+    { dyStringAppend(dy, "intergenic "); genes = raw + 3; }
+char *dupe = cloneString(genes);
+subChar(dupe, '|', '-');
+dyStringAppend(dy, dupe);
+freeMem(dupe);
+return dyStringCannibalize(&dy);
+}
+
+char *hLocusName(struct sqlConnection *conn, char *chrom, int start, int end)
+/* If conn's database has a "locusName" table, look up the gene/locus label that overlaps
+ * the given range and return it expanded into a human-readable string ("intron STON2",
+ * "intergenic FOO-BAR"), or NULL if the table is absent or nothing overlaps.  Caller frees.
+ * Reused by hgBlat (result labels) and hgSession (saved-session region column). */
+{
+if (!sqlTableExists(conn, "locusName"))
+    return NULL;
+struct sqlResult *sr = hRangeQuery(conn, "locusName", chrom, start, end, NULL, 0);
+char **row = sqlNextRow(sr);
+char *label = NULL;
+if (row != NULL)
+    label = hLocusNameExpand(row[4]);
+sqlFreeResult(&sr);
+return label;
+}
+
 boolean hScaffoldPos(char *db, char *chrom, int start, int end,
                      char **retScaffold, int *retStart, int *retEnd)
 /* Return the scaffold, and start end coordinates on a scaffold, for
  * a chromosome range.  If the range extends past end of a scaffold,
  * it is truncated to the scaffold end.
  * Return FALSE if unable to convert */
 {
 int ret = FALSE;
 char table[HDB_MAX_TABLE_STRING];
 safef(table, sizeof(table), "%s_gold", chrom);
 if (!hTableExists(db, table))
     return FALSE;
 else
     {
     char query[256];