From 83839669bb8c9c6a52c436b8b25d518606db5094 Mon Sep 17 00:00:00 2001 From: Nico Rehwaldt Date: Fri, 13 Sep 2024 16:39:02 +0200 Subject: [PATCH] feat(search): integrate with unified search This ensures we build upon the new search abstraction, built via https://github.com/bpmn-io/diagram-js/pull/916. --- lib/features/search/BpmnSearchProvider.js | 192 ++++-------------- lib/features/search/index.js | 4 +- .../features/search/BpmnSearchProviderSpec.js | 53 +++-- 3 files changed, 78 insertions(+), 171 deletions(-) diff --git a/lib/features/search/BpmnSearchProvider.js b/lib/features/search/BpmnSearchProvider.js index 1d535bcfaf..c975e4b715 100644 --- a/lib/features/search/BpmnSearchProvider.js +++ b/lib/features/search/BpmnSearchProvider.js @@ -1,5 +1,3 @@ -import { omit } from 'min-dash'; - import { getLabel, isLabel @@ -11,7 +9,11 @@ import { * @typedef {import('diagram-js/lib/features/search-pad/SearchPad').default} SearchPad * * @typedef {import('diagram-js/lib/features/search-pad/SearchPadProvider').default} SearchPadProvider - * @typedef {import('diagram-js/lib/features/search-pad/SearchPadProvider').SearchResult} SearchResult + * @typedef {import('diagram-js/lib/features/search-pad/SearchPadProvider').SearchResult} SearchPadResult + * @typedef {import('diagram-js/lib/features/search-pad/SearchPadProvider').Token} SearchPadToken + * @typedef {import('diagram-js/lib/features/search/search').default} Search + * @typedef {import('diagram-js/lib/features/search/search').SearchResult} SearchResult + * @typedef {import('diagram-js/lib/features/search/search').Token} SearchToken */ /** @@ -22,10 +24,12 @@ import { * @param {ElementRegistry} elementRegistry * @param {SearchPad} searchPad * @param {Canvas} canvas + * @param {Search} search */ -export default function BpmnSearchProvider(elementRegistry, searchPad, canvas) { +export default function BpmnSearchProvider(elementRegistry, searchPad, canvas, search) { this._elementRegistry = elementRegistry; this._canvas = canvas; + this._search = search; searchPad.registerProvider(this); } @@ -33,13 +37,14 @@ export default function BpmnSearchProvider(elementRegistry, searchPad, canvas) { BpmnSearchProvider.$inject = [ 'elementRegistry', 'searchPad', - 'canvas' + 'canvas', + 'search' ]; /** * @param {string} pattern * - * @return {SearchResult[]} + * @return {SearchPadResult[]} */ BpmnSearchProvider.prototype.find = function(pattern) { var rootElements = this._canvas.getRootElements(); @@ -48,157 +53,40 @@ BpmnSearchProvider.prototype.find = function(pattern) { return !isLabel(element) && !rootElements.includes(element); }); - return elements - .reduce(function(results, element) { - var label = getLabel(element); - - var primaryTokens = findMatches(label, pattern), - secondaryTokens = findMatches(element.id, pattern); - - if (hasMatch(primaryTokens) || hasMatch(secondaryTokens)) { - return [ - ...results, - { - primaryTokens, - secondaryTokens, - element - } - ]; - } - - return results; - }, []) - .sort(function(a, b) { - return compareTokens(a.primaryTokens, b.primaryTokens) - || compareTokens(a.secondaryTokens, b.secondaryTokens) - || compareStrings(getLabel(a.element), getLabel(b.element)) - || compareStrings(a.element.id, b.element.id); - }) - .map(function(result) { + return this._search( + elements.map(element => { return { - element: result.element, - primaryTokens: result.primaryTokens.map(function(token) { - return omit(token, [ 'index' ]); - }), - secondaryTokens: result.secondaryTokens.map(function(token) { - return omit(token, [ 'index' ]); - }) + element, + label: getLabel(element), + id: element.id }; - }); + }), + pattern, { + keys: [ + 'label', + 'id' + ] + } + ).map(toSearchPadResult); }; /** - * @param {Token} token - * - * @return {boolean} - */ -function isMatch(token) { - return 'matched' in token; -} - -/** - * @param {Token[]} tokens - * - * @return {boolean} - */ -function hasMatch(tokens) { - return tokens.find(isMatch); -} - -/** - * Compares two token arrays. - * - * @param {Token[]} tokensA - * @param {Token[]} tokensB - * - * @returns {number} - */ -function compareTokens(tokensA, tokensB) { - const tokensAHasMatch = hasMatch(tokensA), - tokensBHasMatch = hasMatch(tokensB); - - if (tokensAHasMatch && !tokensBHasMatch) { - return -1; - } - - if (!tokensAHasMatch && tokensBHasMatch) { - return 1; - } - - if (!tokensAHasMatch && !tokensBHasMatch) { - return 0; - } - - const tokensAFirstMatch = tokensA.find(isMatch), - tokensBFirstMatch = tokensB.find(isMatch); - - if (tokensAFirstMatch.index < tokensBFirstMatch.index) { - return -1; - } - - if (tokensAFirstMatch.index > tokensBFirstMatch.index) { - return 1; - } - - return 0; -} - -/** - * Compares two strings. - * - * @param {string} [a] - * @param {string} [b] - * - * @returns {number} - */ -function compareStrings(a = '', b = '') { - return a.localeCompare(b); -} - -/** - * @param {string} text - * @param {string} pattern + * @param {SearchResult} token * - * @return {Token[]} + * @return {SearchPadResult} */ -function findMatches(text, pattern) { - var tokens = [], - originalText = text; - - if (!text) { - return tokens; - } - - text = text.toLowerCase(); - pattern = pattern.toLowerCase(); - - var index = text.indexOf(pattern); - - if (index > -1) { - if (index !== 0) { - tokens.push({ - normal: originalText.slice(0, index), - index: 0 - }); - } - - tokens.push({ - matched: originalText.slice(index, index + pattern.length), - index: index - }); - - if (pattern.length + index < text.length) { - tokens.push({ - normal: originalText.slice(index + pattern.length), - index: index + pattern.length - }); - } - } else { - tokens.push({ - normal: originalText, - index: 0 - }); - } - - return tokens; +function toSearchPadResult(result) { + + const { + item: { + element + }, + tokens + } = result; + + return { + element, + primaryTokens: tokens.label, + secondaryTokens: tokens.id + }; } \ No newline at end of file diff --git a/lib/features/search/index.js b/lib/features/search/index.js index b7050aee94..2db2c9a0ea 100644 --- a/lib/features/search/index.js +++ b/lib/features/search/index.js @@ -1,11 +1,13 @@ import SearchPadModule from 'diagram-js/lib/features/search-pad'; +import SearchModule from 'diagram-js/lib/features/search'; import BpmnSearchProvider from './BpmnSearchProvider'; export default { __depends__: [ - SearchPadModule + SearchPadModule, + SearchModule ], __init__: [ 'bpmnSearch' ], bpmnSearch: [ 'type', BpmnSearchProvider ] diff --git a/test/spec/features/search/BpmnSearchProviderSpec.js b/test/spec/features/search/BpmnSearchProviderSpec.js index 8ce04cbf98..2bae873adc 100644 --- a/test/spec/features/search/BpmnSearchProviderSpec.js +++ b/test/spec/features/search/BpmnSearchProviderSpec.js @@ -3,6 +3,10 @@ import { inject } from 'test/TestHelper'; +import { + pick +} from 'min-dash'; + import coreModule from 'lib/core'; import modelingModule from 'lib/features/modeling'; import bpmnSearchModule from 'lib/features/search'; @@ -72,13 +76,13 @@ describe('features - BPMN search provider', function() { var elements = bpmnSearch.find(pattern); // then - expect(elements[0].primaryTokens).to.eql([ - { normal: 'has matched ID' } + expectTokens(elements[0].primaryTokens, [ + { value: 'has matched ID' } ]); - expect(elements[0].secondaryTokens).to.eql([ - { normal: 'some_' }, - { matched: 'DataStore' }, - { normal: '_123456_id' } + expectTokens(elements[0].secondaryTokens, [ + { value: 'some_' }, + { value: 'DataStore', match: true }, + { value: '_123456_id' } ]); })); @@ -122,8 +126,10 @@ describe('features - BPMN search provider', function() { var elements = bpmnSearch.find(pattern); // then - expect(elements[0].primaryTokens).to.eql([ - { matched: 'all matched' } + expectTokens(elements[0].primaryTokens, [ + { value: 'all', match: true }, + { value: ' ' }, + { value: 'matched', match: true } ]); })); @@ -137,9 +143,9 @@ describe('features - BPMN search provider', function() { var elements = bpmnSearch.find(pattern); // then - expect(elements[0].primaryTokens).to.eql([ - { matched: 'before' }, - { normal: ' 321' } + expectTokens(elements[0].primaryTokens, [ + { value: 'before', match: true }, + { value: ' 321' } ]); })); @@ -153,10 +159,10 @@ describe('features - BPMN search provider', function() { var elements = bpmnSearch.find(pattern); // then - expect(elements[0].primaryTokens).to.eql([ - { normal: '123 ' }, - { matched: 'middle' }, - { normal: ' 321' } + expectTokens(elements[0].primaryTokens, [ + { value: '123 ' }, + { value: 'middle', match: true }, + { value: ' 321' } ]); })); @@ -170,9 +176,9 @@ describe('features - BPMN search provider', function() { var elements = bpmnSearch.find(pattern); // then - expect(elements[0].primaryTokens).to.eql([ - { normal: '123 ' }, - { matched: 'after' } + expectTokens(elements[0].primaryTokens, [ + { value: '123 ' }, + { value: 'after', match: true } ]); })); @@ -224,3 +230,14 @@ describe('features - BPMN search provider', function() { }); }); + + +// helpers /////////////// + +function expectTokens(tokens, expectedTokens) { + const cleanTokens = tokens.map( + token => pick(token, [ 'value', 'match' ]) + ); + + expect(cleanTokens).to.eql(expectedTokens); +} \ No newline at end of file