mirror of
				https://github.com/TriliumNext/Notes.git
				synced 2025-11-04 07:01:31 +08:00 
			
		
		
		
	add highlighting to search results, closes #2977
This commit is contained in:
		
							parent
							
								
									89a4165c77
								
							
						
					
					
						commit
						570fabdc4a
					
				@ -176,7 +176,7 @@ class Froca {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        const searchResultNoteIds = await server.get('search-note/' + note.noteId);
 | 
			
		||||
        const {searchResultNoteIds, highlightedTokens} = await server.get('search-note/' + note.noteId);
 | 
			
		||||
 | 
			
		||||
        if (!Array.isArray(searchResultNoteIds)) {
 | 
			
		||||
            throw new Error(`Search note '${note.noteId}' failed: ${searchResultNoteIds}`);
 | 
			
		||||
@ -207,6 +207,7 @@ class Froca {
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        froca.notes[note.noteId].searchResultsLoaded = true;
 | 
			
		||||
        froca.notes[note.noteId].highlightedTokens = highlightedTokens;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /** @returns {NoteShort[]} */
 | 
			
		||||
 | 
			
		||||
@ -2,6 +2,7 @@ import linkService from "./link.js";
 | 
			
		||||
import noteContentRenderer from "./note_content_renderer.js";
 | 
			
		||||
import froca from "./froca.js";
 | 
			
		||||
import attributeRenderer from "./attribute_renderer.js";
 | 
			
		||||
import libraryLoader from "./library_loader.js";
 | 
			
		||||
 | 
			
		||||
const TPL = `
 | 
			
		||||
<div class="note-list">
 | 
			
		||||
@ -60,27 +61,27 @@ const TPL = `
 | 
			
		||||
        padding-top: 10px;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    .note-book-title {
 | 
			
		||||
    .note-book-header {
 | 
			
		||||
        margin-bottom: 0;
 | 
			
		||||
        word-break: break-all;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    /* not-expanded title is limited to one line only */
 | 
			
		||||
    .note-book-card:not(.expanded) .note-book-title {
 | 
			
		||||
    .note-book-card:not(.expanded) .note-book-header {
 | 
			
		||||
        overflow: hidden;
 | 
			
		||||
        white-space: nowrap;
 | 
			
		||||
        text-overflow: ellipsis;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    .note-book-title .rendered-note-attributes {
 | 
			
		||||
    .note-book-header .rendered-note-attributes {
 | 
			
		||||
        font-size: medium;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    .note-book-title .rendered-note-attributes:before {
 | 
			
		||||
    .note-book-header .rendered-note-attributes:before {
 | 
			
		||||
        content: "\\00a0\\00a0";
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    .note-book-title .note-icon {
 | 
			
		||||
    .note-book-header .note-icon {
 | 
			
		||||
        font-size: 100%;
 | 
			
		||||
        display: inline-block;
 | 
			
		||||
        padding-right: 7px;
 | 
			
		||||
@ -112,7 +113,7 @@ const TPL = `
 | 
			
		||||
        max-height: 100%;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    .note-book-title {
 | 
			
		||||
    .note-book-header {
 | 
			
		||||
        flex-grow: 0;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
@ -198,6 +199,15 @@ class NoteListRenderer {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        const highlightedTokens = this.parentNote.highlightedTokens || [];
 | 
			
		||||
        if (highlightedTokens.length > 0) {
 | 
			
		||||
            await libraryLoader.requireLibrary(libraryLoader.MARKJS);
 | 
			
		||||
 | 
			
		||||
            this.highlightRegex = new RegExp(highlightedTokens.join("|"), 'gi');
 | 
			
		||||
        } else {
 | 
			
		||||
            this.highlightRegex = null;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this.$noteList.show();
 | 
			
		||||
 | 
			
		||||
        const $container = this.$noteList.find('.note-list-container').empty();
 | 
			
		||||
@ -262,12 +272,13 @@ class NoteListRenderer {
 | 
			
		||||
        const $card = $('<div class="note-book-card">')
 | 
			
		||||
            .attr('data-note-id', note.noteId)
 | 
			
		||||
            .append(
 | 
			
		||||
                $('<h5 class="note-book-title">')
 | 
			
		||||
                $('<h5 class="note-book-header">')
 | 
			
		||||
                    .append($expander)
 | 
			
		||||
                    .append($('<span class="note-icon">').addClass(note.getIcon()))
 | 
			
		||||
                    .append(this.viewType === 'grid'
 | 
			
		||||
                        ? $("<span>").text(note.title)
 | 
			
		||||
                        : await linkService.createNoteLink(notePath, {showTooltip: false, showNotePath: this.showNotePath})
 | 
			
		||||
                        ? $('<span class="note-book-title">').text(note.title)
 | 
			
		||||
                        : (await linkService.createNoteLink(notePath, {showTooltip: false, showNotePath: this.showNotePath}))
 | 
			
		||||
                            .addClass("note-book-title")
 | 
			
		||||
                    )
 | 
			
		||||
                    .append($renderedAttributes)
 | 
			
		||||
            );
 | 
			
		||||
@ -281,6 +292,15 @@ class NoteListRenderer {
 | 
			
		||||
 | 
			
		||||
        $expander.on('click', () => this.toggleContent($card, note, !$card.hasClass("expanded")));
 | 
			
		||||
 | 
			
		||||
        if (this.highlightRegex) {
 | 
			
		||||
            $card.find(".note-book-title").markRegExp(this.highlightRegex, {
 | 
			
		||||
                element: "span",
 | 
			
		||||
                className: "ck-find-result",
 | 
			
		||||
                separateWordSearch: false,
 | 
			
		||||
                caseSensitive: false
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        await this.toggleContent($card, note, expand);
 | 
			
		||||
 | 
			
		||||
        return $card;
 | 
			
		||||
@ -291,7 +311,7 @@ class NoteListRenderer {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        const $expander = $card.find('> .note-book-title .note-expander');
 | 
			
		||||
        const $expander = $card.find('> .note-book-header .note-expander');
 | 
			
		||||
 | 
			
		||||
        if (expand || this.viewType === 'grid') {
 | 
			
		||||
            $card.addClass("expanded");
 | 
			
		||||
@ -315,6 +335,15 @@ class NoteListRenderer {
 | 
			
		||||
                trim: this.viewType === 'grid' // for grid only short content is needed
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            if (this.highlightRegex) {
 | 
			
		||||
                $renderedContent.markRegExp(this.highlightRegex, {
 | 
			
		||||
                    element: "span",
 | 
			
		||||
                    className: "ck-find-result",
 | 
			
		||||
                    separateWordSearch: false,
 | 
			
		||||
                    caseSensitive: false
 | 
			
		||||
                });
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            $content.append($renderedContent);
 | 
			
		||||
            $content.addClass("type-" + type);
 | 
			
		||||
        } catch (e) {
 | 
			
		||||
 | 
			
		||||
@ -7,16 +7,16 @@ const scriptService = require('../../services/script');
 | 
			
		||||
const searchService = require('../../services/search/services/search');
 | 
			
		||||
const bulkActionService = require("../../services/bulk_actions");
 | 
			
		||||
const {formatAttrForSearch} = require("../../services/attribute_formatter");
 | 
			
		||||
const utils = require("../../services/utils.js");
 | 
			
		||||
 | 
			
		||||
function searchFromNoteInt(note) {
 | 
			
		||||
    let searchResultNoteIds;
 | 
			
		||||
    let searchResultNoteIds, highlightedTokens;
 | 
			
		||||
 | 
			
		||||
    const searchScript = note.getRelationValue('searchScript');
 | 
			
		||||
    const searchString = note.getLabelValue('searchString');
 | 
			
		||||
 | 
			
		||||
    if (searchScript) {
 | 
			
		||||
        searchResultNoteIds = searchFromRelation(note, 'searchScript');
 | 
			
		||||
        highlightedTokens = [];
 | 
			
		||||
    } else {
 | 
			
		||||
        const searchContext = new SearchContext({
 | 
			
		||||
            fastSearch: note.hasLabel('fastSearch'),
 | 
			
		||||
@ -32,14 +32,19 @@ function searchFromNoteInt(note) {
 | 
			
		||||
 | 
			
		||||
        searchResultNoteIds = searchService.findResultsWithQuery(searchString, searchContext)
 | 
			
		||||
            .map(sr => sr.noteId);
 | 
			
		||||
 | 
			
		||||
        highlightedTokens = searchContext.highlightedTokens;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // we won't return search note's own noteId
 | 
			
		||||
    // also don't allow root since that would force infinite cycle
 | 
			
		||||
    return searchResultNoteIds.filter(resultNoteId => !['root', note.noteId].includes(resultNoteId));
 | 
			
		||||
    return {
 | 
			
		||||
        searchResultNoteIds: searchResultNoteIds.filter(resultNoteId => !['root', note.noteId].includes(resultNoteId)),
 | 
			
		||||
        highlightedTokens
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async function searchFromNote(req) {
 | 
			
		||||
function searchFromNote(req) {
 | 
			
		||||
    const note = becca.getNote(req.params.noteId);
 | 
			
		||||
 | 
			
		||||
    if (!note) {
 | 
			
		||||
@ -47,7 +52,7 @@ async function searchFromNote(req) {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (note.isDeleted) {
 | 
			
		||||
        // this can be triggered from recent changes and it's harmless to return empty list rather than fail
 | 
			
		||||
        // this can be triggered from recent changes, and it's harmless to return empty list rather than fail
 | 
			
		||||
        return [];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -55,7 +60,7 @@ async function searchFromNote(req) {
 | 
			
		||||
        return [400, `Note ${req.params.noteId} is not a search note.`]
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return await searchFromNoteInt(note);
 | 
			
		||||
    return searchFromNoteInt(note);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function searchAndExecute(req) {
 | 
			
		||||
@ -74,7 +79,7 @@ function searchAndExecute(req) {
 | 
			
		||||
        return [400, `Note ${req.params.noteId} is not a search note.`]
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const searchResultNoteIds = searchFromNoteInt(note);
 | 
			
		||||
    const {searchResultNoteIds} = searchFromNoteInt(note);
 | 
			
		||||
 | 
			
		||||
    bulkActionService.executeActions(note, searchResultNoteIds);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user