2022-05-09 23:13:34 +02:00
/ * *
* ( c ) Antonio Tejada 2022
2022-05-14 22:33:45 +02:00
* https : //github.com/antoniotejada/Trilium-FindWidget
2022-05-09 23:13:34 +02:00
* /
import NoteContextAwareWidget from "./note_context_aware_widget.js" ;
import appContext from "../services/app_context.js" ;
const findWidgetDelayMillis = 200 ;
const waitForEnter = ( findWidgetDelayMillis < 0 ) ;
2022-05-14 21:06:14 +02:00
// tabIndex=-1 on the checkbox labels is necessary so when clicking on the label
// the focusout handler is called with relatedTarget equal to the label instead
2022-05-14 22:33:45 +02:00
// of undefined. It's -1 instead of > 0, so they don't tabstop
2022-05-15 12:09:30 +02:00
const TPL = `
2022-05-14 22:33:45 +02:00
< div style = "contain: none;" >
2022-05-15 22:51:26 +02:00
< style >
. find - widget - box {
padding : 10 px ;
border - top : 1 px solid var ( -- main - border - color ) ;
align - items : center ;
}
. find - widget - box > * {
margin - right : 15 px ;
}
. find - widget - box {
display : flex ;
}
. find - widget - found - wrapper {
font - weight : bold ;
}
< / s t y l e >
< div class = "find-widget-box" >
< input type = "text" class = "font-control find-widget-search-term-input" >
< div class = "form-check" >
< label tabIndex = "-1" class = "form-check-label" >
< input type = "checkbox" class = "form-check-input find-widget-case-sensitive-checkbox" >
case sensitive
< / l a b e l >
< / d i v >
< div class = "form-check" >
< label tabIndex = "-1" class = "form-check-label" >
< input type = "checkbox" class = "form-check-input find-widget-match-words-checkbox" >
match words
< / l a b e l >
< / d i v >
< div class = "find-widget-found-wrapper" >
< span class = "find-widget-current-found" > 0 < / s p a n >
/
< span class = "find-widget-total-found" > 0 < / s p a n >
< / d i v >
2022-05-14 22:33:45 +02:00
< / d i v >
2022-05-09 23:13:34 +02:00
< / d i v > ` ;
function escapeRegExp ( string ) {
return string . replace ( /[.*+?^${}()|[\]\\]/g , '\\$&' ) ;
}
2022-05-14 22:33:45 +02:00
const getActiveContextCodeEditor = async ( ) => await appContext . tabManager . getActiveContextCodeEditor ( ) ;
const getActiveContextTextEditor = async ( ) => await appContext . tabManager . getActiveContextTextEditor ( ) ;
2022-05-09 23:13:34 +02:00
// ck-find-result and ck-find-result_selected are the styles ck-editor
// uses for highlighting matches, use the same one on CodeMirror
// for consistency
const FIND _RESULT _SELECTED _CSS _CLASSNAME = "ck-find-result_selected" ;
const FIND _RESULT _CSS _CLASSNAME = "ck-find-result" ;
export default class FindWidget extends NoteContextAwareWidget {
2022-05-15 12:09:30 +02:00
doRender ( ) {
this . $widget = $ ( TPL ) ;
2022-05-15 22:51:26 +02:00
this . $findBox = this . $widget . find ( '.find-widget-box' ) ;
2022-05-15 12:09:30 +02:00
this . $findBox . hide ( ) ;
2022-05-15 22:51:26 +02:00
this . $input = this . $widget . find ( '.find-widget-search-term-input' ) ;
this . $currentFound = this . $widget . find ( '.find-widget-current-found' ) ;
this . $totalFound = this . $widget . find ( '.find-widget-total-found' ) ;
this . $caseSensitiveCheckbox = this . $widget . find ( ".find-widget-case-sensitive-checkbox" ) ;
this . $matchWordsCheckbox = this . $widget . find ( ".find-widget-match-words-checkbox" ) ;
2022-05-09 23:13:34 +02:00
this . findResult = null ;
2022-05-15 22:51:26 +02:00
this . searchTerm = null ;
2022-05-09 23:13:34 +02:00
2022-05-15 12:09:30 +02:00
this . $input . keydown ( async e => {
2022-05-15 22:51:26 +02:00
if ( ( e . metaKey || e . ctrlKey ) && ( e . key === 'F' || e . key === 'f' ) ) {
2022-05-09 23:13:34 +02:00
// If ctrl+f is pressed when the findbox is shown, select the
// whole input to find
2022-05-15 12:09:30 +02:00
this . $input . select ( ) ;
2022-05-15 22:51:26 +02:00
} else if ( e . key === 'Enter' || e . key === 'F3' ) {
const searchTerm = this . $input . val ( ) ;
if ( waitForEnter && this . searchTerm !== searchTerm ) {
await this . performFind ( searchTerm ) ;
2022-05-09 23:13:34 +02:00
}
2022-05-15 22:51:26 +02:00
const totalFound = parseInt ( this . $totalFound . text ( ) ) ;
const currentFound = parseInt ( this . $currentFound . text ( ) ) - 1 ;
2022-05-14 22:33:45 +02:00
2022-05-15 22:51:26 +02:00
if ( totalFound > 0 ) {
const delta = e . shiftKey ? - 1 : 1 ;
let nextFound = currentFound + delta ;
2022-05-09 23:13:34 +02:00
// Wrap around
2022-05-15 22:51:26 +02:00
if ( nextFound > totalFound - 1 ) {
2022-05-09 23:13:34 +02:00
nextFound = 0 ;
2022-05-15 22:51:26 +02:00
} else if ( nextFound < 0 ) {
nextFound = totalFound - 1 ;
2022-05-09 23:13:34 +02:00
}
2022-05-15 22:51:26 +02:00
this . $currentFound . text ( nextFound + 1 ) ;
2022-05-09 23:13:34 +02:00
const note = appContext . tabManager . getActiveContextNote ( ) ;
2022-05-14 22:33:45 +02:00
if ( note . type === "code" ) {
2022-05-15 21:03:51 +02:00
const codeEditor = await getActiveContextCodeEditor ( ) ;
const doc = codeEditor . doc ;
2022-05-09 23:13:34 +02:00
//
// Dehighlight current, highlight & scrollIntoView next
//
2022-05-15 22:51:26 +02:00
let marker = this . findResult [ currentFound ] ;
2022-05-09 23:13:34 +02:00
let pos = marker . find ( ) ;
marker . clear ( ) ;
marker = doc . markText (
pos . from , pos . to ,
{ "className" : FIND _RESULT _CSS _CLASSNAME }
) ;
2022-05-15 22:51:26 +02:00
this . findResult [ currentFound ] = marker ;
2022-05-09 23:13:34 +02:00
2022-05-15 12:09:30 +02:00
marker = this . findResult [ nextFound ] ;
2022-05-09 23:13:34 +02:00
pos = marker . find ( ) ;
marker . clear ( ) ;
marker = doc . markText (
pos . from , pos . to ,
{ "className" : FIND _RESULT _SELECTED _CSS _CLASSNAME }
) ;
2022-05-15 12:09:30 +02:00
this . findResult [ nextFound ] = marker ;
2022-05-09 23:13:34 +02:00
codeEditor . scrollIntoView ( pos . from ) ;
} else {
const textEditor = await getActiveContextTextEditor ( ) ;
2022-05-14 21:06:14 +02:00
// There are no parameters for findNext/findPrev
// See https://github.com/ckeditor/ckeditor5/blob/b95e2faf817262ac0e1e21993d9c0bde3f1be594/packages/ckeditor5-find-and-replace/src/findnextcommand.js#L57
// curFound wrap around above assumes findNext and
// findPrevious wraparound, which is what they do
2022-05-09 23:13:34 +02:00
if ( delta > 0 ) {
2022-05-14 21:06:14 +02:00
textEditor . execute ( 'findNext' ) ;
2022-05-09 23:13:34 +02:00
} else {
2022-05-14 21:06:14 +02:00
textEditor . execute ( 'findPrevious' ) ;
2022-05-09 23:13:34 +02:00
}
}
}
e . preventDefault ( ) ;
return false ;
2022-05-14 22:33:45 +02:00
} else if ( e . key === 'Escape' ) {
2022-05-09 23:13:34 +02:00
const note = appContext . tabManager . getActiveContextNote ( ) ;
2022-05-14 22:33:45 +02:00
if ( note . type === "code" ) {
2022-05-15 21:03:51 +02:00
const codeEditor = await getActiveContextCodeEditor ( ) ;
2022-05-09 23:13:34 +02:00
codeEditor . focus ( ) ;
} else {
const textEditor = await getActiveContextTextEditor ( ) ;
textEditor . focus ( ) ;
}
}
} ) ;
2022-05-15 12:09:30 +02:00
this . $input . on ( 'input' , ( ) => {
2022-05-09 23:13:34 +02:00
// XXX This should clear the previous search immediately in all cases
// (the search is stale when waitforenter but also while the
// delay is running for non waitforenter case)
if ( ! waitForEnter ) {
// Clear the previous timeout if any, it's ok if timeoutId is
// null or undefined
2022-05-15 12:09:30 +02:00
clearTimeout ( this . timeoutId ) ;
2022-05-09 23:13:34 +02:00
// Defer the search a few millis so the search doesn't start
// immediately, as this can cause search word typing lag with
// one or two-char searchwords and long notes
// See https://github.com/antoniotejada/Trilium-FindWidget/issues/1
2022-05-15 22:51:26 +02:00
const searchTerm = this . $input . val ( ) ;
const matchCase = this . $caseSensitiveCheckbox . prop ( "checked" ) ;
const wholeWord = this . $matchWordsCheckbox . prop ( "checked" ) ;
2022-05-15 12:09:30 +02:00
this . timeoutId = setTimeout ( async ( ) => {
this . timeoutId = null ;
2022-05-15 22:51:26 +02:00
await this . performFind ( searchTerm , matchCase , wholeWord ) ;
2022-05-09 23:13:34 +02:00
} , findWidgetDelayMillis ) ;
}
} ) ;
2022-05-15 22:51:26 +02:00
this . $caseSensitiveCheckbox . change ( ( ) => this . performFind ( ) ) ;
this . $matchWordsCheckbox . change ( ( ) => this . performFind ( ) ) ;
2022-05-14 21:06:14 +02:00
// Note blur doesn't bubble to parent div, but the parent div needs to
// detect when any of the children are not focused and hide. Use
// focusout instead which does bubble to the parent div.
2022-05-15 22:51:26 +02:00
this . $findBox . on ( 'focusout' , async ( e ) => {
2022-05-14 21:06:14 +02:00
// e.relatedTarget is the new focused element, note it can be null
// if nothing is being focused
2022-05-15 12:09:30 +02:00
if ( this . $findBox [ 0 ] . contains ( e . relatedTarget ) ) {
2022-05-14 21:06:14 +02:00
// The focused element is inside this div, ignore
return ;
}
2022-05-15 12:09:30 +02:00
this . $findBox . hide ( ) ;
2022-05-09 23:13:34 +02:00
// Restore any state, if there's a current occurrence clear markers
// and scroll to and select the last occurrence
// XXX Switching to a different tab with crl+tab doesn't invoke
// blur and leaves a stale search which then breaks when
// navigating it
2022-05-15 22:51:26 +02:00
const totalFound = parseInt ( this . $totalFound . text ( ) ) ;
const currentFound = parseInt ( this . $currentFound . text ( ) ) - 1 ;
2022-05-09 23:13:34 +02:00
const note = appContext . tabManager . getActiveContextNote ( ) ;
2022-05-14 22:33:45 +02:00
if ( note . type === "code" ) {
2022-05-15 21:03:51 +02:00
const codeEditor = await getActiveContextCodeEditor ( ) ;
2022-05-15 22:51:26 +02:00
if ( totalFound > 0 ) {
2022-05-15 21:03:51 +02:00
const doc = codeEditor . doc ;
2022-05-15 22:51:26 +02:00
const pos = this . findResult [ currentFound ] . find ( ) ;
2022-05-09 23:13:34 +02:00
// Note setting the selection sets the cursor to
// the end of the selection and scrolls it into
// view
doc . setSelection ( pos . from , pos . to ) ;
// Clear all markers
2022-05-15 12:09:30 +02:00
codeEditor . operation ( ( ) => {
for ( let i = 0 ; i < this . findResult . length ; ++ i ) {
let marker = this . findResult [ i ] ;
2022-05-09 23:13:34 +02:00
marker . clear ( ) ;
}
} ) ;
}
// Restore the highlightSelectionMatches setting
2022-05-15 12:09:30 +02:00
codeEditor . setOption ( "highlightSelectionMatches" , this . oldHighlightSelectionMatches ) ;
this . findResult = null ;
2022-05-15 22:51:26 +02:00
this . searchTerm = null ;
2022-05-09 23:13:34 +02:00
} else {
2022-05-15 22:51:26 +02:00
if ( totalFound > 0 ) {
2022-05-09 23:13:34 +02:00
const textEditor = await getActiveContextTextEditor ( ) ;
// Clear the markers and set the caret to the
// current occurrence
const model = textEditor . model ;
2022-05-15 22:51:26 +02:00
const range = this . findResult . results . get ( currentFound ) . marker . getRange ( ) ;
2022-05-09 23:13:34 +02:00
// From
// https://github.com/ckeditor/ckeditor5/blob/b95e2faf817262ac0e1e21993d9c0bde3f1be594/packages/ckeditor5-find-and-replace/src/findandreplace.js#L92
// XXX Roll our own since already done for codeEditor and
// will probably allow more refactoring?
let findAndReplaceEditing = textEditor . plugins . get ( 'FindAndReplaceEditing' ) ;
findAndReplaceEditing . state . clear ( model ) ;
findAndReplaceEditing . stop ( ) ;
model . change ( writer => {
writer . setSelection ( range , 0 ) ;
} ) ;
textEditor . editing . view . scrollToTheSelection ( ) ;
2022-05-15 12:09:30 +02:00
this . findResult = null ;
2022-05-15 22:51:26 +02:00
this . searchTerm = null ;
2022-05-09 23:13:34 +02:00
} else {
2022-05-15 12:09:30 +02:00
this . findResult = null ;
2022-05-15 22:51:26 +02:00
this . searchTerm = null ;
2022-05-09 23:13:34 +02:00
}
}
} ) ;
2022-05-15 12:09:30 +02:00
return this . $widget ;
2022-05-09 23:13:34 +02:00
}
2022-05-15 21:03:51 +02:00
async findInTextEvent ( ) {
const note = appContext . tabManager . getActiveContextNote ( ) ;
// Only writeable text and code supported
const readOnly = note . getAttribute ( "label" , "readOnly" ) ;
if ( ! readOnly && ( note . type === "code" || note . type === "text" ) ) {
if ( this . $findBox . is ( ":hidden" ) ) {
this . $findBox . show ( ) ;
this . $input . focus ( ) ;
2022-05-15 22:51:26 +02:00
this . $totalFound . text ( 0 ) ;
this . $currentFound . text ( 0 ) ;
2022-05-15 21:03:51 +02:00
// Initialize the input field to the text selection, if any
if ( note . type === "code" ) {
const codeEditor = await getActiveContextCodeEditor ( ) ;
// highlightSelectionMatches is the overlay that highlights
// the words under the cursor. This occludes the search
// markers style, save it, disable it. Will be restored when
// the focus is back into the note
this . oldHighlightSelectionMatches = codeEditor . getOption ( "highlightSelectionMatches" ) ;
codeEditor . setOption ( "highlightSelectionMatches" , false ) ;
// Fill in the findbox with the current selection if any
const selectedText = codeEditor . getSelection ( )
if ( selectedText !== "" ) {
this . $input . val ( selectedText ) ;
}
// Directly perform the search if there's some text to find,
// without delaying or waiting for enter
2022-05-15 22:51:26 +02:00
const searchTerm = this . $input . val ( ) ;
if ( searchTerm !== "" ) {
2022-05-15 21:03:51 +02:00
this . $input . select ( ) ;
2022-05-15 22:51:26 +02:00
await this . performFind ( searchTerm ) ;
2022-05-15 21:03:51 +02:00
}
} else {
const textEditor = await getActiveContextTextEditor ( ) ;
const selection = textEditor . model . document . selection ;
const range = selection . getFirstRange ( ) ;
for ( const item of range . getItems ( ) ) {
// Fill in the findbox with the current selection if
// any
this . $input . val ( item . data ) ;
break ;
}
// Directly perform the search if there's some text to
// find, without delaying or waiting for enter
2022-05-15 22:51:26 +02:00
const searchTerm = this . $input . val ( ) ;
if ( searchTerm !== "" ) {
2022-05-15 21:03:51 +02:00
this . $input . select ( ) ;
2022-05-15 22:51:26 +02:00
await this . performFind ( searchTerm ) ;
2022-05-15 21:03:51 +02:00
}
}
}
}
}
2022-05-15 22:51:26 +02:00
async performTextNoteFind ( searchTerm , matchCase , wholeWord ) {
// Do this even if the searchTerm is empty so the markers are cleared and
2022-05-09 23:13:34 +02:00
// the counters updated
const textEditor = await getActiveContextTextEditor ( ) ;
const model = textEditor . model ;
let findResult = null ;
2022-05-15 22:51:26 +02:00
let totalFound = 0 ;
let currentFound = - 1 ;
2022-05-09 23:13:34 +02:00
// Clear
2022-05-14 22:33:45 +02:00
const findAndReplaceEditing = textEditor . plugins . get ( 'FindAndReplaceEditing' ) ;
2022-05-09 23:13:34 +02:00
findAndReplaceEditing . state . clear ( model ) ;
findAndReplaceEditing . stop ( ) ;
2022-05-15 22:51:26 +02:00
if ( searchTerm !== "" ) {
2022-05-09 23:13:34 +02:00
// Parameters are callback/text, options.matchCase=false, options.wholeWords=false
// See https://github.com/ckeditor/ckeditor5/blob/b95e2faf817262ac0e1e21993d9c0bde3f1be594/packages/ckeditor5-find-and-replace/src/findcommand.js#L44
// XXX Need to use the callback version for regexp
2022-05-15 22:51:26 +02:00
// searchTerm = escapeRegExp(searchTerm);
// let re = new RegExp(searchTerm, 'gi');
2022-05-09 23:13:34 +02:00
// let m = text.match(re);
2022-05-15 22:51:26 +02:00
// totalFound = m ? m.length : 0;
2022-05-14 21:06:14 +02:00
const options = { "matchCase" : matchCase , "wholeWords" : wholeWord } ;
2022-05-15 22:51:26 +02:00
findResult = textEditor . execute ( 'find' , searchTerm , options ) ;
totalFound = findResult . results . length ;
2022-05-09 23:13:34 +02:00
// Find the result beyond the cursor
2022-05-14 22:33:45 +02:00
const cursorPos = model . document . selection . getLastPosition ( ) ;
2022-05-09 23:13:34 +02:00
for ( let i = 0 ; i < findResult . results . length ; ++ i ) {
2022-05-14 22:33:45 +02:00
const marker = findResult . results . get ( i ) . marker ;
const fromPos = marker . getStart ( ) ;
if ( fromPos . compareWith ( cursorPos ) !== "before" ) {
2022-05-15 22:51:26 +02:00
currentFound = i ;
2022-05-09 23:13:34 +02:00
break ;
}
}
}
this . findResult = findResult ;
2022-05-15 22:51:26 +02:00
this . $totalFound . text ( totalFound ) ;
2022-05-09 23:13:34 +02:00
// Calculate curfound if not already, highlight it as
// selected
2022-05-15 22:51:26 +02:00
if ( totalFound > 0 ) {
currentFound = Math . max ( 0 , currentFound ) ;
2022-05-09 23:13:34 +02:00
// XXX Do this accessing the private data?
// See
// https://github.com/ckeditor/ckeditor5/blob/b95e2faf817262ac0e1e21993d9c0bde3f1be594/packages/ckeditor5-find-and-replace/src/findnextcommand.js
2022-05-15 22:51:26 +02:00
for ( let i = 0 ; i < currentFound ; ++ i ) {
textEditor . execute ( 'findNext' , searchTerm ) ;
2022-05-09 23:13:34 +02:00
}
}
2022-05-15 22:51:26 +02:00
this . $currentFound . text ( currentFound + 1 ) ;
this . searchTerm = searchTerm ;
2022-05-09 23:13:34 +02:00
}
2022-05-15 22:51:26 +02:00
async performCodeNoteFind ( searchTerm , matchCase , wholeWord ) {
2022-05-09 23:13:34 +02:00
let findResult = null ;
2022-05-15 22:51:26 +02:00
let totalFound = 0 ;
let currentFound = - 1 ;
2022-05-09 23:13:34 +02:00
// See https://codemirror.net/addon/search/searchcursor.js for tips
2022-05-14 22:33:45 +02:00
const codeEditor = await getActiveContextCodeEditor ( ) ;
const doc = codeEditor . doc ;
const text = doc . getValue ( ) ;
2022-05-09 23:13:34 +02:00
// Clear all markers
if ( this . findResult != null ) {
2022-05-15 12:09:30 +02:00
codeEditor . operation ( ( ) => {
for ( let i = 0 ; i < this . findResult . length ; ++ i ) {
const marker = this . findResult [ i ] ;
2022-05-09 23:13:34 +02:00
marker . clear ( ) ;
}
} ) ;
}
2022-05-15 22:51:26 +02:00
if ( searchTerm !== "" ) {
searchTerm = escapeRegExp ( searchTerm ) ;
2022-05-09 23:13:34 +02:00
// Find and highlight matches
2022-05-14 21:06:14 +02:00
// Find and highlight matches
// XXX Using \\b and not using the unicode flag probably doesn't
// work with non ascii alphabets, findAndReplace uses a more
// complicated regexp, see
// https://github.com/ckeditor/ckeditor5/blob/b95e2faf817262ac0e1e21993d9c0bde3f1be594/packages/ckeditor5-find-and-replace/src/utils.js#L145
const wholeWordChar = wholeWord ? "\\b" : "" ;
2022-05-15 22:51:26 +02:00
const re = new RegExp ( wholeWordChar + searchTerm + wholeWordChar ,
2022-05-14 21:06:14 +02:00
'g' + ( matchCase ? '' : 'i' ) ) ;
2022-05-09 23:13:34 +02:00
let curLine = 0 ;
let curChar = 0 ;
let curMatch = null ;
findResult = [ ] ;
// All those markText take several seconds on eg this ~500-line
// script, batch them inside an operation so they become
// unnoticeable. Alternatively, an overlay could be used, see
// https://codemirror.net/addon/search/match-highlighter.js ?
2022-05-15 12:09:30 +02:00
codeEditor . operation ( ( ) => {
2022-05-09 23:13:34 +02:00
for ( let i = 0 ; i < text . length ; ++ i ) {
// Fetch next match if it's the first time or
// if past the current match start
if ( ( curMatch == null ) || ( curMatch . index < i ) ) {
curMatch = re . exec ( text ) ;
if ( curMatch == null ) {
// No more matches
break ;
}
}
// Create a non-selected highlight marker for the match, the
// selected marker highlight will be done later
2022-05-14 22:33:45 +02:00
if ( i === curMatch . index ) {
2022-05-09 23:13:34 +02:00
let fromPos = { "line" : curLine , "ch" : curChar } ;
// XXX If multiline is supported, this needs to
// recalculate curLine since the match may span
// lines
let toPos = { "line" : curLine , "ch" : curChar + curMatch [ 0 ] . length } ;
// XXX or css = "color: #f3"
let marker = doc . markText ( fromPos , toPos , { "className" : FIND _RESULT _CSS _CLASSNAME } ) ;
findResult . push ( marker ) ;
// Set the first match beyond the cursor as current
// match
2022-05-15 22:51:26 +02:00
if ( currentFound === - 1 ) {
2022-05-14 22:33:45 +02:00
const cursorPos = codeEditor . getCursor ( ) ;
2022-05-09 23:13:34 +02:00
if ( ( fromPos . line > cursorPos . line ) ||
2022-05-15 21:03:51 +02:00
( ( fromPos . line === cursorPos . line ) &&
2022-05-09 23:13:34 +02:00
( fromPos . ch >= cursorPos . ch ) ) ) {
2022-05-15 22:51:26 +02:00
currentFound = totalFound ;
2022-05-09 23:13:34 +02:00
}
}
2022-05-15 22:51:26 +02:00
totalFound ++ ;
2022-05-09 23:13:34 +02:00
}
// Do line and char position tracking
2022-05-14 22:33:45 +02:00
if ( text [ i ] === "\n" ) {
2022-05-09 23:13:34 +02:00
curLine ++ ;
curChar = 0 ;
} else {
curChar ++ ;
}
}
} ) ;
}
this . findResult = findResult ;
2022-05-15 22:51:26 +02:00
this . $totalFound . text ( totalFound ) ;
2022-05-09 23:13:34 +02:00
// Calculate curfound if not already, highlight it as selected
2022-05-15 22:51:26 +02:00
if ( totalFound > 0 ) {
currentFound = Math . max ( 0 , currentFound )
let marker = findResult [ currentFound ] ;
2022-05-09 23:13:34 +02:00
let pos = marker . find ( ) ;
codeEditor . scrollIntoView ( pos . to ) ;
marker . clear ( ) ;
2022-05-15 22:51:26 +02:00
findResult [ currentFound ] = doc . markText ( pos . from , pos . to ,
2022-05-09 23:13:34 +02:00
{ "className" : FIND _RESULT _SELECTED _CSS _CLASSNAME }
) ;
}
2022-05-15 22:51:26 +02:00
this . $currentFound . text ( currentFound + 1 ) ;
this . searchTerm = searchTerm ;
2022-05-09 23:13:34 +02:00
}
2022-05-14 21:06:14 +02:00
/ * *
* Perform the find and highlight the find results .
*
2022-05-15 22:51:26 +02:00
* @ param [ searchTerm ] { string } optional parameter , taken from the input box if
2022-05-14 21:06:14 +02:00
* missing .
2022-05-15 22:51:26 +02:00
* @ param [ matchCase ] { boolean } optional parameter , taken from the checkbox
2022-05-14 21:06:14 +02:00
* state if missing .
2022-05-15 22:51:26 +02:00
* @ param [ wholeWord ] { boolean } optional parameter , taken from the checkbox
2022-05-14 21:06:14 +02:00
* state if missing .
* /
2022-05-15 22:51:26 +02:00
async performFind ( searchTerm , matchCase , wholeWord ) {
searchTerm = ( searchTerm === undefined ) ? this . $input . val ( ) : searchTerm ;
matchCase = ( matchCase === undefined ) ? this . $caseSensitiveCheckbox . prop ( "checked" ) : matchCase ;
wholeWord = ( wholeWord === undefined ) ? this . $matchWordsCheckbox . prop ( "checked" ) : wholeWord ;
2022-05-09 23:13:34 +02:00
const note = appContext . tabManager . getActiveContextNote ( ) ;
2022-05-14 22:33:45 +02:00
if ( note . type === "code" ) {
2022-05-15 22:51:26 +02:00
await this . performCodeNoteFind ( searchTerm , matchCase , wholeWord ) ;
2022-05-09 23:13:34 +02:00
} else {
2022-05-15 22:51:26 +02:00
await this . performTextNoteFind ( searchTerm , matchCase , wholeWord ) ;
2022-05-09 23:13:34 +02:00
}
}
isEnabled ( ) {
2022-05-15 21:03:51 +02:00
return super . isEnabled ( ) && ( this . note . type === 'text' || this . note . type === 'code' ) ;
2022-05-09 23:13:34 +02:00
}
async entitiesReloadedEvent ( { loadResults } ) {
if ( loadResults . isNoteContentReloaded ( this . noteId ) ) {
this . refresh ( ) ;
}
}
}