mirror of
https://github.com/laurent22/joplin.git
synced 2024-12-24 10:27:10 +02:00
This commit is contained in:
parent
e0daf807a6
commit
11c1c0638d
@ -2,11 +2,10 @@ import * as React from 'react';
|
|||||||
import { AppState } from '../app.reducer';
|
import { AppState } from '../app.reducer';
|
||||||
import CommandService, { SearchResult as CommandSearchResult } from '@joplin/lib/services/CommandService';
|
import CommandService, { SearchResult as CommandSearchResult } from '@joplin/lib/services/CommandService';
|
||||||
import KeymapService from '@joplin/lib/services/KeymapService';
|
import KeymapService from '@joplin/lib/services/KeymapService';
|
||||||
import shim from '@joplin/lib/shim';
|
|
||||||
const { connect } = require('react-redux');
|
const { connect } = require('react-redux');
|
||||||
import { _ } from '@joplin/lib/locale';
|
import { _ } from '@joplin/lib/locale';
|
||||||
import { themeStyle } from '@joplin/lib/theme';
|
import { themeStyle } from '@joplin/lib/theme';
|
||||||
import SearchEngine from '@joplin/lib/services/search/SearchEngine';
|
import SearchEngine, { ComplexTerm } from '@joplin/lib/services/search/SearchEngine';
|
||||||
import gotoAnythingStyleQuery from '@joplin/lib/services/search/gotoAnythingStyleQuery';
|
import gotoAnythingStyleQuery from '@joplin/lib/services/search/gotoAnythingStyleQuery';
|
||||||
import BaseModel, { ModelType } from '@joplin/lib/BaseModel';
|
import BaseModel, { ModelType } from '@joplin/lib/BaseModel';
|
||||||
import Tag from '@joplin/lib/models/Tag';
|
import Tag from '@joplin/lib/models/Tag';
|
||||||
@ -14,7 +13,7 @@ import Folder from '@joplin/lib/models/Folder';
|
|||||||
import Note from '@joplin/lib/models/Note';
|
import Note from '@joplin/lib/models/Note';
|
||||||
import ItemList from '../gui/ItemList';
|
import ItemList from '../gui/ItemList';
|
||||||
import HelpButton from '../gui/HelpButton';
|
import HelpButton from '../gui/HelpButton';
|
||||||
const { surroundKeywords, nextWhitespaceIndex, removeDiacritics } = require('@joplin/lib/string-utils.js');
|
import { surroundKeywords, nextWhitespaceIndex, removeDiacritics } from '@joplin/lib/string-utils';
|
||||||
import { mergeOverlappingIntervals } from '@joplin/lib/ArrayUtils';
|
import { mergeOverlappingIntervals } from '@joplin/lib/ArrayUtils';
|
||||||
import markupLanguageUtils from '../utils/markupLanguageUtils';
|
import markupLanguageUtils from '../utils/markupLanguageUtils';
|
||||||
import focusEditorIfEditorCommand from '@joplin/lib/services/commands/focusEditorIfEditorCommand';
|
import focusEditorIfEditorCommand from '@joplin/lib/services/commands/focusEditorIfEditorCommand';
|
||||||
@ -23,6 +22,7 @@ import { MarkupLanguage, MarkupToHtml } from '@joplin/renderer';
|
|||||||
import Resource from '@joplin/lib/models/Resource';
|
import Resource from '@joplin/lib/models/Resource';
|
||||||
import { NoteEntity, ResourceEntity } from '@joplin/lib/services/database/types';
|
import { NoteEntity, ResourceEntity } from '@joplin/lib/services/database/types';
|
||||||
import Dialog from '../gui/Dialog';
|
import Dialog from '../gui/Dialog';
|
||||||
|
import AsyncActionQueue from '@joplin/lib/AsyncActionQueue';
|
||||||
|
|
||||||
const logger = Logger.create('GotoAnything');
|
const logger = Logger.create('GotoAnything');
|
||||||
|
|
||||||
@ -129,8 +129,7 @@ class DialogComponent extends React.PureComponent<Props, State> {
|
|||||||
private inputRef: any;
|
private inputRef: any;
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
|
||||||
private itemListRef: any;
|
private itemListRef: any;
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
|
private listUpdateQueue_: AsyncActionQueue;
|
||||||
private listUpdateIID_: any;
|
|
||||||
private markupToHtml_: MarkupToHtml;
|
private markupToHtml_: MarkupToHtml;
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
|
||||||
private userCallback_: any = null;
|
private userCallback_: any = null;
|
||||||
@ -141,6 +140,7 @@ class DialogComponent extends React.PureComponent<Props, State> {
|
|||||||
const startString = props?.userData?.startString ? props?.userData?.startString : '';
|
const startString = props?.userData?.startString ? props?.userData?.startString : '';
|
||||||
|
|
||||||
this.userCallback_ = props?.userData?.callback;
|
this.userCallback_ = props?.userData?.callback;
|
||||||
|
this.listUpdateQueue_ = new AsyncActionQueue(100);
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
query: startString,
|
query: startString,
|
||||||
@ -235,7 +235,7 @@ class DialogComponent extends React.PureComponent<Props, State> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public componentWillUnmount() {
|
public componentWillUnmount() {
|
||||||
if (this.listUpdateIID_) shim.clearTimeout(this.listUpdateIID_);
|
void this.listUpdateQueue_.reset();
|
||||||
|
|
||||||
this.props.dispatch({
|
this.props.dispatch({
|
||||||
type: 'VISIBLE_DIALOGS_REMOVE',
|
type: 'VISIBLE_DIALOGS_REMOVE',
|
||||||
@ -263,12 +263,7 @@ class DialogComponent extends React.PureComponent<Props, State> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public scheduleListUpdate() {
|
public scheduleListUpdate() {
|
||||||
if (this.listUpdateIID_) shim.clearTimeout(this.listUpdateIID_);
|
this.listUpdateQueue_.push(() => this.updateList());
|
||||||
|
|
||||||
this.listUpdateIID_ = shim.setTimeout(async () => {
|
|
||||||
await this.updateList();
|
|
||||||
this.listUpdateIID_ = null;
|
|
||||||
}, 100);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async keywords(searchQuery: string) {
|
public async keywords(searchQuery: string) {
|
||||||
@ -360,7 +355,6 @@ class DialogComponent extends React.PureComponent<Props, State> {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
const limit = 20;
|
const limit = 20;
|
||||||
const searchKeywords = await this.keywords(searchQuery);
|
|
||||||
|
|
||||||
// Note: any filtering must be done **before** fetching the notes, because we're
|
// Note: any filtering must be done **before** fetching the notes, because we're
|
||||||
// going to apply a limit to the number of fetched notes.
|
// going to apply a limit to the number of fetched notes.
|
||||||
@ -381,6 +375,10 @@ class DialogComponent extends React.PureComponent<Props, State> {
|
|||||||
results = results.filter(r => !!notesById[r.id])
|
results = results.filter(r => !!notesById[r.id])
|
||||||
.map(r => ({ ...r, title: notesById[r.id].title }));
|
.map(r => ({ ...r, title: notesById[r.id].title }));
|
||||||
|
|
||||||
|
const normalizedKeywords = (await this.keywords(searchQuery)).map(
|
||||||
|
({ valueRegex }: ComplexTerm) => new RegExp(removeDiacritics(valueRegex), 'ig'),
|
||||||
|
);
|
||||||
|
|
||||||
for (let i = 0; i < results.length; i++) {
|
for (let i = 0; i < results.length; i++) {
|
||||||
const row = results[i];
|
const row = results[i];
|
||||||
const path = Folder.folderPathString(this.props.folders, row.parent_id);
|
const path = Folder.folderPathString(this.props.folders, row.parent_id);
|
||||||
@ -388,21 +386,14 @@ class DialogComponent extends React.PureComponent<Props, State> {
|
|||||||
if (row.fields.includes('body')) {
|
if (row.fields.includes('body')) {
|
||||||
let fragments = '...';
|
let fragments = '...';
|
||||||
|
|
||||||
if (i < limit) { // Display note fragments of search keyword matches
|
const loadFragments = (markupLanguage: MarkupLanguage, content: string) => {
|
||||||
const { markupLanguage, content } = getContentMarkupLanguageAndBody(
|
|
||||||
row,
|
|
||||||
notesById,
|
|
||||||
resources,
|
|
||||||
);
|
|
||||||
|
|
||||||
const indices = [];
|
const indices = [];
|
||||||
const body = this.markupToHtml().stripMarkup(markupLanguage, content, { collapseWhiteSpaces: true });
|
const body = this.markupToHtml().stripMarkup(markupLanguage, content, { collapseWhiteSpaces: true });
|
||||||
|
const normalizedBody = removeDiacritics(body);
|
||||||
|
|
||||||
// Iterate over all matches in the body for each search keyword
|
// Iterate over all matches in the body for each search keyword
|
||||||
for (let { valueRegex } of searchKeywords) {
|
for (const keywordRegex of normalizedKeywords) {
|
||||||
valueRegex = removeDiacritics(valueRegex);
|
for (const match of normalizedBody.matchAll(keywordRegex)) {
|
||||||
|
|
||||||
for (const match of removeDiacritics(body).matchAll(new RegExp(valueRegex, 'ig'))) {
|
|
||||||
// Populate 'indices' with [begin index, end index] of each note fragment
|
// Populate 'indices' with [begin index, end index] of each note fragment
|
||||||
// Begins at the regex matching index, ends at the next whitespace after seeking 15 characters to the right
|
// Begins at the regex matching index, ends at the next whitespace after seeking 15 characters to the right
|
||||||
indices.push([match.index, nextWhitespaceIndex(body, match.index + match[0].length + 15)]);
|
indices.push([match.index, nextWhitespaceIndex(body, match.index + match[0].length + 15)]);
|
||||||
@ -418,6 +409,19 @@ class DialogComponent extends React.PureComponent<Props, State> {
|
|||||||
fragments = mergedIndices.map((f: any) => body.slice(f[0], f[1])).join(' ... ');
|
fragments = mergedIndices.map((f: any) => body.slice(f[0], f[1])).join(' ... ');
|
||||||
// Add trailing ellipsis if the final fragment doesn't end where the note is ending
|
// Add trailing ellipsis if the final fragment doesn't end where the note is ending
|
||||||
if (mergedIndices.length && mergedIndices[mergedIndices.length - 1][1] !== body.length) fragments += ' ...';
|
if (mergedIndices.length && mergedIndices[mergedIndices.length - 1][1] !== body.length) fragments += ' ...';
|
||||||
|
};
|
||||||
|
|
||||||
|
if (i < limit) { // Display note fragments of search keyword matches
|
||||||
|
const { markupLanguage, content } = getContentMarkupLanguageAndBody(
|
||||||
|
row,
|
||||||
|
notesById,
|
||||||
|
resources,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Don't load fragments for long notes -- doing so can lead to UI freezes.
|
||||||
|
if (content.length < 100_000) {
|
||||||
|
loadFragments(markupLanguage, content);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
results[i] = { ...row, path, fragments };
|
results[i] = { ...row, path, fragments };
|
||||||
|
@ -16,7 +16,7 @@ import { isCallbackUrl, parseCallbackUrl } from '../../callbackUrlUtils';
|
|||||||
import replaceUnsupportedCharacters from '../../utils/replaceUnsupportedCharacters';
|
import replaceUnsupportedCharacters from '../../utils/replaceUnsupportedCharacters';
|
||||||
import { htmlentitiesDecode } from '@joplin/utils/html';
|
import { htmlentitiesDecode } from '@joplin/utils/html';
|
||||||
const { sprintf } = require('sprintf-js');
|
const { sprintf } = require('sprintf-js');
|
||||||
const { pregQuote, scriptType, removeDiacritics } = require('../../string-utils.js');
|
import { pregQuote, scriptType, removeDiacritics } from '../../string-utils';
|
||||||
|
|
||||||
enum SearchType {
|
enum SearchType {
|
||||||
Auto = 'auto',
|
Auto = 'auto',
|
||||||
@ -59,7 +59,7 @@ export interface ComplexTerm {
|
|||||||
value: string;
|
value: string;
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
|
||||||
scriptType: any;
|
scriptType: any;
|
||||||
valueRegex?: RegExp;
|
valueRegex?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Terms {
|
export interface Terms {
|
||||||
|
@ -2,7 +2,7 @@ const Entities = require('html-entities').AllHtmlEntities;
|
|||||||
const htmlentities = new Entities().encode;
|
const htmlentities = new Entities().encode;
|
||||||
const stringUtilsCommon = require('./string-utils-common.js');
|
const stringUtilsCommon = require('./string-utils-common.js');
|
||||||
|
|
||||||
export const pregQuote = stringUtilsCommon.pregQuote;
|
export const pregQuote = stringUtilsCommon.pregQuote as (str: string, delimiter?: string)=> string;
|
||||||
export const replaceRegexDiacritics = stringUtilsCommon.replaceRegexDiacritics;
|
export const replaceRegexDiacritics = stringUtilsCommon.replaceRegexDiacritics;
|
||||||
|
|
||||||
const defaultDiacriticsRemovalMap = [
|
const defaultDiacriticsRemovalMap = [
|
||||||
|
Loading…
Reference in New Issue
Block a user