diff --git a/src/plugins/searchDecorations.js b/src/plugins/searchDecorations.js index fd6eb9cb561..b868f58cee5 100644 --- a/src/plugins/searchDecorations.js +++ b/src/plugins/searchDecorations.js @@ -91,18 +91,34 @@ export function runSearch(doc, query, options) { } } + const mentionQuery = query.trim().startsWith('@') + ? query.trim().slice(1).toLowerCase() + : query.trim().toLowerCase() + doc.descendants((node, offset, _position) => { - if (!node.isText) { + // Add decorations for text matches + if (node.isText) { + const matches = node.text.matchAll(new RegExp(query, 'gi')) + + for (const match of matches) { + results.push({ + from: match.index + offset, + to: match.index + offset + query.length, + }) + } + return } - const matches = node.text.matchAll(new RegExp(query, 'gi')) - - for (const match of matches) { - results.push({ - from: match.index + offset, - to: match.index + offset + query.length, - }) + // Add decorations for mention matches + if (node.type.name === 'mention' && mentionQuery !== '') { + if (node.attrs.label.toLowerCase().startsWith(mentionQuery)) { + results.push({ + from: offset, + to: offset + node.nodeSize, + mention: true, + }) + } } }) @@ -142,7 +158,9 @@ export function highlightResults(doc, results) { decorations.push( Decoration.inline(result.from, result.to, { 'data-text-el': 'search-decoration', - style: 'background-color: #ead637; color: black; border-radius: 2px;', + style: result.mention + ? 'outline: 2px solid #ead637; background-color: #ead637; color: black; border-radius: 2px;' + : 'background-color: #ead637; color: black; border-radius: 2px;', }), ) }) diff --git a/src/tests/plugins/searchDecorations.spec.js b/src/tests/plugins/searchDecorations.spec.js index ddedb0fb458..00b689b558b 100644 --- a/src/tests/plugins/searchDecorations.spec.js +++ b/src/tests/plugins/searchDecorations.spec.js @@ -3,6 +3,7 @@ * SPDX-License-Identifier: AGPL-3.0-or-later */ +import Mentions from '../../extensions/Mention.js' import { highlightResults, runSearch } from '../../plugins/searchDecorations.js' import createCustomEditor from '../testHelpers/createCustomEditor.ts' @@ -76,10 +77,34 @@ describe('search plugin', () => { testSearch(doc, 'cat', expected) }) + + it('finds matches with mentions', () => { + const doc = + '
janes task, mention Jane Doe
' + + const expected = { + results: [ + { from: 1, to: 5 }, + { from: 21, to: 22, mention: true }, + ], + } + + testSearch(doc, 'jane', expected) + }) + + it('finds matches with mentions with @', () => { + const doc = + 'janes task, mention Jane Doe
' + + const expected = { + results: [{ from: 21, to: 22, mention: true }], + } + testSearch(doc, '@jane', expected) + }) }) const testSearch = (content, query, expectedSearchResults) => { - const editor = createCustomEditor(content) + const editor = createCustomEditor(content, [Mentions]) const doc = editor.state.doc const searched = runSearch(doc, query) expect(searched).toHaveProperty('results', expectedSearchResults.results)