Skip to content

Commit

Permalink
Fix: when using the toolbox-search, auto select the found dropdown item
Browse files Browse the repository at this point in the history
Fixes: google#1940

Uses the Block State as suggested here:
google#2004 (comment)
  • Loading branch information
ictin committed Jan 3, 2024
1 parent f642f46 commit 21ec048
Show file tree
Hide file tree
Showing 2 changed files with 75 additions and 55 deletions.
82 changes: 48 additions & 34 deletions plugins/toolbox-search/src/block_searcher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import * as Blockly from 'blockly/core';
*/
export class BlockSearcher {
private blockCreationWorkspace = new Blockly.Workspace();
private trigramsToBlocks = new Map<string, Set<string>>();
private trigramsToBlocks = new Map<string, Set<Blockly.serialization.blocks.State>>();

/**
* Populates the cached map of trigrams to the blocks they correspond to.
Expand All @@ -27,65 +27,79 @@ export class BlockSearcher {
const blockCreationWorkspace = new Blockly.Workspace();
blockTypes.forEach((blockType) => {
const block = blockCreationWorkspace.newBlock(blockType);
this.indexBlockText(blockType.replaceAll('_', ' '), blockType);
block.inputList.forEach((input) => {
input.fieldRow.forEach((field) => {
this.indexDropdownOption(field, blockType);
this.indexBlockText(field.getText(), blockType);
});
});
const blockState = Blockly.serialization.blocks.save(block)
if (blockState != null) {
this.indexBlockText(blockType.replaceAll('_', ' '), blockState);
block.inputList.forEach((input) => {
input.fieldRow.forEach((field) => {
this.indexDropdownOption(field, blockState);
this.indexBlockText(field.getText(), blockState);
});
});
}
});
}

/**
* Check if the field is a dropdown, and index every text in the option
*
* @param field We need to check the type of field
* @param blockType The block type to associate the trigrams with.
* @param blockState The block state to associate the trigrams with.
*/
private indexDropdownOption(field: Blockly.Field, blockType: string) {
private indexDropdownOption(field: Blockly.Field, blockState: Blockly.serialization.blocks.State) {
if (field instanceof Blockly.FieldDropdown) {
field.getOptions(true).forEach((option) => {
if (typeof option[0] === 'string') {
this.indexBlockText(option[0], blockType);
} else if ('alt' in option[0]) {
this.indexBlockText(option[0].alt, blockType);
}
});
field.getOptions(true).forEach((option) => {
let state = { ...blockState }
if (typeof option[0] === 'string') {
state.fields = {}
if (field.name) {
state.fields[field.name] = option[1]
}
this.indexBlockText(option[0], state);
} else if ('alt' in option[0]) {
this.indexBlockText(option[0].alt, state);
}
});
}
}

/**
* Filters the available blocks based on the current query string.
*
* @param query The text to use to match blocks against.
* @returns A list of block types matching the query.
*/
blockTypesMatching(query: string): string[] {
* @returns A list of block states matching the query.
*/
blockTypesMatching(query: string): Blockly.serialization.blocks.State[] {
return [
...this.generateTrigrams(query)
.map((trigram) => {
return this.trigramsToBlocks.get(trigram) ?? new Set<string>();
})
.reduce((matches, current) => {
return this.getIntersection(matches, current);
return this.trigramsToBlocks.get(trigram) ?? new Set<Blockly.serialization.blocks.State> ();
})
.reduce((matches, current) => {
return this.getIntersection(matches, current);
})
.values(),
];
}
]
}


private addBlockTrigram(trigram: string, state: Blockly.serialization.blocks.State) {
let blockSet = this.trigramsToBlocks.get(trigram) ?? new Set<Blockly.serialization.blocks.State>();
blockSet.add(state);
this.trigramsToBlocks.set(trigram, blockSet);
}

/**
* Generates trigrams for the given text and associates them with the given
* block type.
* block state.
*
* @param text The text to generate trigrams of.
* @param blockType The block type to associate the trigrams with.
* @param blockState The block state to associate the trigrams with.
*/
private indexBlockText(text: string, blockType: string) {
this.generateTrigrams(text).forEach((trigram) => {
const blockSet = this.trigramsToBlocks.get(trigram) ?? new Set<string>();
blockSet.add(blockType);
this.trigramsToBlocks.set(trigram, blockSet);
private indexBlockText(text: string, blockState: Blockly.serialization.blocks.State) {
this.generateTrigrams(text).forEach((trigram) => {
this.addBlockTrigram(trigram, blockState);

});
}

Expand Down Expand Up @@ -115,7 +129,7 @@ export class BlockSearcher {
* @param b The second set.
* @returns The intersection of the two sets.
*/
private getIntersection(a: Set<string>, b: Set<string>): Set<string> {
private getIntersection(a: Set<Blockly.serialization.blocks.State>, b: Set<Blockly.serialization.blocks.State>): Set<Blockly.serialization.blocks.State> {
return new Set([...a].filter((value) => b.has(value)));
}
}
48 changes: 27 additions & 21 deletions plugins/toolbox-search/src/toolbox_search.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
* in its flyout.
*/
import * as Blockly from 'blockly/core';
import { block } from '../node_modules/blockly/core/tooltip';
import {BlockSearcher} from './block_searcher';

/* eslint-disable @typescript-eslint/naming-convention */
Expand Down Expand Up @@ -166,33 +167,38 @@ export class ToolboxSearchCategory extends Blockly.ToolboxCategory {
}
}



/**
* Filters the available blocks based on the current query string.
*/
private matchBlocks() {
const query = this.searchField?.value || '';

this.flyoutItems_ = query
? this.blockSearcher.blockTypesMatching(query).map((blockType) => {
return {
kind: 'block',
type: blockType,
};
})
: [];

if (!this.flyoutItems_.length) {
this.flyoutItems_.push({
kind: 'label',
text:
query.length < 3
? 'Type to search for blocks'
: 'No matching blocks found',
});
}
this.parentToolbox_.refreshSelection();
this.flyoutItems_ = query ?
this.blockSearcher.blockTypesMatching(query).map(
(blockState) => {
if (blockState.fields) {
return {
kind: 'block',
type: blockState.type,
fields: blockState.fields
};
}
return {
kind: 'block',
type: blockState.type,
};
}) : [];

if (!this.flyoutItems_.length) {
this.flyoutItems_.push({
kind: 'label',
text: query.length < 3 ? 'Type to search for blocks' :
'No matching blocks found',
});
}
this.parentToolbox_.refreshSelection();
}

/**
* Disposes of this category.
*/
Expand Down

0 comments on commit 21ec048

Please sign in to comment.