f93b8662afd701763c24634879d05dc08b3178de max Fri Jun 5 02:24:16 2026 -0700 Add exon search: jump to GENE exon N from position box I'm comitting this thinking that the way that we implement searches leads to duplication of code that doesn't look great to me. While this feature looks good, the code duplication across C/JS should probably get reduced with a different approach to the "quick jump" way of the page. We have currently three ways to quick jump, I think: - chr:start-end - rsxxxxx - gene symbol + autosuggest pick - HGVS? They are recognized by both the javascript and the C code with regexes. I think all of these should be probably be only implemented in the C code. The JS only sends the current string to the C code and then gets back if this can be autocompleted and to which position and what to show in the autosuggest area. For example if you type "SOD1<space>e" the C code could send back "Continue typing to jump to exon" and once you're at "SOD1<space>exon 5" the C code sends back "Hit enter to jump to chrX:123123-123213". This would work with any type of identifier and the code would stay in the C code, not more duplication and it would be much clearer to the user what is recognized in the search box. Users can now type "TP53 exon 5" or "TP53:e.5[+/-offset]" in the genome browser position/search box to navigate directly to that exon. The ":e.N" notation follows the VICC Gene Fusion Specification. An optional intronic offset (":e.5+2") lands N bases past the exon boundary, useful for splice site inspection. C (hgFind.c): findGeneExon() resolves the query against the SQL genePred tables listed in the hg.conf "geneTracks" key (default: mane, ncbiRefSeqSelect, knownGene, ncbiRefSeq, ncbiRefSeqHistorical). bigGenePred tracks (e.g. mane) are supported via bigBedOpenExtraIndex. Uses the existing exonToPos() function for strand-aware exon lookup. fixSinglePos() is called so hgp->singlePos is populated for callers. hgApi.c: new cmd=geneExonToPos returns {"pos":"chrom:start-end"} JSON so JS can navigate in place without a full page redirect to hgSearch. Direct URL links (hgTracks?position=GENE+exon+N) also work because findGeneExon() is hooked into hgPositionsFind(). JS: autocomplete.js injects a local "Jump to exon N" suggestion as soon as the exon pattern is detected, or a hint item when the query is still partial ("GENE ex"). Selecting either navigates via hgApi. hgTracks.js routes the two new autocomplete item types to the hgApi call. utils.js adds the two regexes (geneExonExp, geneExonCoordExp). query.html: documents both syntaxes; the :e.N notation links to the VICC Gene Fusion Specification at fusions.cancervariants.org. diff --git src/hg/js/utils.js src/hg/js/utils.js index aba6171b47e..1dd4f526b3f 100644 --- src/hg/js/utils.js +++ src/hg/js/utils.js @@ -12,30 +12,35 @@ /* Support these formats for range specifiers. Note the ()'s around chrom, * start and end portions for substring retrieval: */ // \s = whitespace, \w = azAZ0-9_, var canonicalRangeExp = /^[\s]*([\w._#-]+)[\s]*:[\s]*([-0-9,]+)[\s]*[-_][\s]*([0-9,]+)[\s]*$/; var gbrowserRangeExp = /^[\s]*([\w._#-]+)[\s]*:[\s]*([0-9,]+)[\s]*\.\.[\s]*([0-9,]+)[\s]*$/; var lengthRangeExp = /^[\s]*([\w._#-]+)[\s]*:[\s]*([0-9,]+)[\s]*\+[\s]*([0-9,]+)[\s]*$/; var bedRangeExp = /^[\s]*([\w._#-]+)[\s]+([0-9,]+)[\s]+([0-9,]+)[\s]*$/; var sqlRangeExp = /^[\s]*([\w._#-]+)[\s]*\|[\s]*([0-9,]+)[\s]*\|[\s]*([0-9,]+)[\s]*$/; var singleBaseExp = /^[\s]*([\w._#-]+)[\s]*:[\s]*([0-9,]+)[\s]*$/; // also allow gnomad variants, ex: 12-1234-A-C var gnomadVarExp = "^(([0-9]+)|(X|Y|M|MT))-([0-9]+)-([A-Za-z]+)-([A-Za-z]+)$"; // allow gnomad ranges, ex: 12-1234-11223344 var gnomadRangeExp = "^(([0-9]+)|(X|Y|M|MT))-([0-9]+)-([0-9]+)$"; +// gene symbol + exon number: "TP53 exon 5", "BRCA1 exon 10" +var geneExonExp = /^[\s]*([A-Za-z][A-Za-z0-9._-]*)[\s]+exon[\s]+([0-9]+)[\s]*$/i; +// compact exon notation: "TP53:e.5", "NM_000546:e.5+2", "BRCA2:e.10-3" +var geneExonCoordExp = /^[\s]*([A-Za-z0-9._-]+):e\.([0-9]+)([+-][0-9]+)?[\s]*$/i; + function createInfoIcon(text) { /* Create an info icon (i in circle) with tooltip text. * Returns a span element containing the SVG icon. * Uses addMouseover() for consistent tooltip behavior. */ var span = document.createElement("span"); span.style.marginLeft = "4px"; span.innerHTML = "<svg style='height:1.1em; vertical-align:top' viewBox='0 0 24 24' fill='none' xmlns='http://www.w3.org/2000/svg'>" + "<circle cx='12' cy='12' r='10' stroke='#1C274C' stroke-width='1.5'/>" + "<path d='M12 17V11' stroke='#1C274C' stroke-width='1.5' stroke-linecap='round'/>" + "<circle cx='1' cy='1' r='1' transform='matrix(1 0 0 -1 11 9)' fill='#1C274C'/>" + "</svg>"; addMouseover(span, text); return span; }