@@ -11,6 +11,9 @@ import * as ActivePage from "../states/active-page";
1111import { focusWords } from "../test/test-ui" ;
1212import * as Loader from "../elements/loader" ;
1313import { Command , CommandsSubgroup } from "./types" ;
14+ import { areSortedArraysEqual } from "../utils/arrays" ;
15+ import { parseIntOptional } from "../utils/numbers" ;
16+ import { debounce } from "throttle-debounce" ;
1417
1518type CommandlineMode = "search" | "input" ;
1619type InputModeParams = {
@@ -325,6 +328,7 @@ function hideCommands(): void {
325328 throw new Error ( "Commandline element not found" ) ;
326329 }
327330 element . innerHTML = "" ;
331+ lastList = undefined ;
328332}
329333
330334let cachedSingleSubgroup : CommandsSubgroup | null = null ;
@@ -349,18 +353,24 @@ async function getList(): Promise<Command[]> {
349353 return ( await getSubgroup ( ) ) . list ;
350354}
351355
356+ let lastList : Command [ ] | undefined ;
357+
352358async function showCommands ( ) : Promise < void > {
353359 const element = document . querySelector ( "#commandLine .suggestions" ) ;
354360 if ( element === null ) {
355361 throw new Error ( "Commandline element not found" ) ;
356362 }
357363
358364 if ( inputValue === "" && usingSingleList ) {
359- element . innerHTML = "" ;
365+ hideCommands ( ) ;
360366 return ;
361367 }
362368
363369 const list = ( await getList ( ) ) . filter ( ( c ) => c . found === true ) ;
370+ if ( lastList && areSortedArraysEqual ( list , lastList ) ) {
371+ return ;
372+ }
373+ lastList = list ;
364374
365375 let html = "" ;
366376 let index = 0 ;
@@ -458,28 +468,8 @@ async function showCommands(): Promise<void> {
458468 if ( firstActive !== null && ! usingSingleList ) {
459469 activeIndex = firstActive ;
460470 }
461- element . innerHTML = html ;
462471
463- for ( const command of element . querySelectorAll ( ".command" ) ) {
464- command . addEventListener ( "mouseenter" , async ( ) => {
465- if ( ! mouseMode ) return ;
466- activeIndex = parseInt ( command . getAttribute ( "data-index" ) ?? "0" ) ;
467- await updateActiveCommand ( ) ;
468- } ) ;
469- command . addEventListener ( "mouseleave" , async ( ) => {
470- if ( ! mouseMode ) return ;
471- activeIndex = parseInt ( command . getAttribute ( "data-index" ) ?? "0" ) ;
472- await updateActiveCommand ( ) ;
473- } ) ;
474- command . addEventListener ( "click" , async ( ) => {
475- const previous = activeIndex ;
476- activeIndex = parseInt ( command . getAttribute ( "data-index" ) ?? "0" ) ;
477- if ( previous !== activeIndex ) {
478- await updateActiveCommand ( ) ;
479- }
480- await runActiveCommand ( ) ;
481- } ) ;
482- }
472+ element . innerHTML = html ;
483473}
484474
485475async function updateActiveCommand ( ) : Promise < void > {
@@ -573,23 +563,20 @@ async function runActiveCommand(): Promise<void> {
573563 }
574564}
575565
566+ let lastActiveIndex : string | undefined ;
576567function keepActiveCommandInView ( ) : void {
577568 if ( mouseMode ) return ;
578- try {
579- const scroll =
580- Math . abs (
581- ( $ ( ".suggestions" ) . offset ( ) ?. top as number ) -
582- ( $ ( ".command.active" ) . offset ( ) ?. top as number ) -
583- ( $ ( ".suggestions" ) . scrollTop ( ) as number )
584- ) -
585- ( $ ( ".suggestions" ) . outerHeight ( ) as number ) / 2 +
586- ( $ ( $ ( ".command" ) [ 0 ] as HTMLElement ) . outerHeight ( ) as number ) ;
587- $ ( ".suggestions" ) . scrollTop ( scroll ) ;
588- } catch ( e ) {
589- if ( e instanceof Error ) {
590- console . log ( "could not scroll suggestions: " + e . message ) ;
591- }
569+
570+ const active : HTMLElement | null = document . querySelector (
571+ ".suggestions .command.active"
572+ ) ;
573+
574+ if ( active === null || active . dataset [ "index" ] === lastActiveIndex ) {
575+ return ;
592576 }
577+
578+ active . scrollIntoView ( { behavior : "auto" , block : "center" } ) ;
579+ lastActiveIndex = active . dataset [ "index" ] ;
593580}
594581
595582function updateInput ( setInput ?: string ) : void {
@@ -665,22 +652,25 @@ const modal = new AnimatedModal({
665652 setup : async ( modalEl ) : Promise < void > => {
666653 const input = modalEl . querySelector ( "input" ) as HTMLInputElement ;
667654
668- input . addEventListener ( "input" , async ( e ) => {
669- inputValue = ( e . target as HTMLInputElement ) . value ;
670- if ( subgroupOverride === null ) {
671- if ( Config . singleListCommandLine === "on" ) {
672- usingSingleList = true ;
673- } else {
674- usingSingleList = inputValue . startsWith ( ">" ) ;
655+ input . addEventListener (
656+ "input" ,
657+ debounce ( 50 , async ( e ) => {
658+ inputValue = ( e . target as HTMLInputElement ) . value ;
659+ if ( subgroupOverride === null ) {
660+ if ( Config . singleListCommandLine === "on" ) {
661+ usingSingleList = true ;
662+ } else {
663+ usingSingleList = inputValue . startsWith ( ">" ) ;
664+ }
675665 }
676- }
677- if ( mode !== "search" ) return ;
678- mouseMode = false ;
679- activeIndex = 0 ;
680- await filterSubgroup ( ) ;
681- await showCommands ( ) ;
682- await updateActiveCommand ( ) ;
683- } ) ;
666+ if ( mode !== "search" ) return ;
667+ mouseMode = false ;
668+ activeIndex = 0 ;
669+ await filterSubgroup ( ) ;
670+ await showCommands ( ) ;
671+ await updateActiveCommand ( ) ;
672+ } )
673+ ) ;
684674
685675 input . addEventListener ( "keydown" , async ( e ) => {
686676 mouseMode = false ;
@@ -740,5 +730,36 @@ const modal = new AnimatedModal({
740730 modalEl . addEventListener ( "mousemove" , ( _e ) => {
741731 mouseMode = true ;
742732 } ) ;
733+
734+ const suggestions = document . querySelector ( ".suggestions" ) as HTMLElement ;
735+ let lastHover : HTMLElement | undefined ;
736+
737+ suggestions . addEventListener ( "mousemove" , async ( e ) => {
738+ const target = e . target as HTMLElement | null ;
739+ if ( target === lastHover ) return ;
740+
741+ const dataIndex = parseIntOptional ( target ?. getAttribute ( "data-index" ) ) ;
742+
743+ if ( ! dataIndex ) return ;
744+
745+ lastHover = e . target as HTMLElement ;
746+ activeIndex = dataIndex ;
747+ await updateActiveCommand ( ) ;
748+ } ) ;
749+
750+ suggestions . addEventListener ( "click" , async ( e ) => {
751+ const target = e . target as HTMLElement | null ;
752+
753+ const dataIndex = parseIntOptional ( target ?. getAttribute ( "data-index" ) ) ;
754+
755+ if ( ! dataIndex ) return ;
756+
757+ const previous = activeIndex ;
758+ activeIndex = dataIndex ;
759+ if ( previous !== activeIndex ) {
760+ await updateActiveCommand ( ) ;
761+ }
762+ await runActiveCommand ( ) ;
763+ } ) ;
743764 } ,
744765} ) ;
0 commit comments