source-search.js (3840B)
1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */ 4 5 import buildQuery from "../build-query"; 6 7 /** 8 * @memberof utils/source-search 9 * @static 10 */ 11 function SearchState() { 12 this.posFrom = this.posTo = this.query = null; 13 this.overlay = null; 14 this.results = []; 15 } 16 17 /** 18 * @memberof utils/source-search 19 * @static 20 */ 21 function getSearchState(cm) { 22 const state = cm.state.search || (cm.state.search = new SearchState()); 23 return state; 24 } 25 26 function isWhitespace(query) { 27 return !query.match(/\S/); 28 } 29 30 export function getMatchIndex(count, currentIndex, rev) { 31 if (!rev) { 32 if (currentIndex == count - 1) { 33 return 0; 34 } 35 36 return currentIndex + 1; 37 } 38 39 if (currentIndex == 0) { 40 return count - 1; 41 } 42 43 return currentIndex - 1; 44 } 45 46 /** 47 * If there's a saved search, selects the next results. 48 * Otherwise, creates a new search and selects the first 49 * result. 50 * 51 * @memberof utils/source-search 52 * @static 53 */ 54 function doSearch( 55 ctx, 56 rev, 57 query, 58 keepSelection, 59 modifiers, 60 { shouldScroll = true } 61 ) { 62 const { editor } = ctx; 63 if (!query || isWhitespace(query)) { 64 editor.clearSearchMatches(); 65 return null; 66 } 67 const regexQuery = buildQuery(query, modifiers, { 68 ignoreSpaces: true, 69 // regex must be global for the overlay 70 isGlobal: true, 71 }); 72 73 if (editor.searchState.query?.toString() !== regexQuery.toString()) { 74 editor.highlightSearchMatches(regexQuery, "cm-highlight"); 75 } 76 const cursor = editor.getNextSearchCursor(rev); 77 if (!cursor) { 78 return null; 79 } 80 editor.setPositionContentMarker({ 81 id: editor.markerTypes.ACTIVE_SELECTION_MARKER, 82 positionClassName: "cm-matchhighlight", 83 positions: [{ from: cursor.from, to: cursor.to }], 84 }); 85 if (shouldScroll) { 86 editor.scrollToPosition(cursor.from); 87 } 88 return editor.getPositionFromSearchCursor(cursor); 89 } 90 91 export function searchSourceForHighlight( 92 ctx, 93 rev, 94 query, 95 keepSelection, 96 modifiers 97 ) { 98 const { editor } = ctx; 99 if (!query || isWhitespace(query)) { 100 editor.clearSearchMatches(); 101 return; 102 } 103 104 const regexQuery = buildQuery(query, modifiers, { 105 ignoreSpaces: true, 106 // regex must be global for the overlay 107 isGlobal: true, 108 }); 109 110 if (editor.searchState.query?.toString() !== regexQuery.toString()) { 111 editor.highlightSearchMatches(regexQuery, "cm-highlight"); 112 } 113 } 114 115 /** 116 * Remove overlay. 117 * 118 * @memberof utils/source-search 119 * @static 120 */ 121 export function removeOverlay(ctx) { 122 const state = getSearchState(ctx.cm); 123 ctx.cm.removeOverlay(state.overlay); 124 const { line, ch } = ctx.cm.getCursor(); 125 ctx.cm.doc.setSelection({ line, ch }, { line, ch }, { scroll: false }); 126 } 127 128 /** 129 * Clears the currently saved search. 130 * 131 * @memberof utils/source-search 132 * @static 133 */ 134 export function clearSearch(ctx) { 135 const { editor } = ctx; 136 editor.clearSearchMatches(); 137 editor.removePositionContentMarker("active-selection-marker"); 138 } 139 140 /** 141 * Starts a new search. 142 * 143 * @memberof utils/source-search 144 * @static 145 */ 146 export function find(ctx, query, keepSelection, modifiers, options) { 147 clearSearch(ctx); 148 return doSearch(ctx, false, query, keepSelection, modifiers, options); 149 } 150 151 /** 152 * Finds the next item based on the currently saved search. 153 * 154 * @memberof utils/source-search 155 * @static 156 */ 157 export function findNext(ctx, query, keepSelection, modifiers) { 158 return doSearch(ctx, false, query, keepSelection, modifiers, {}); 159 } 160 161 /** 162 * Finds the previous item based on the currently saved search. 163 * 164 * @memberof utils/source-search 165 * @static 166 */ 167 export function findPrev(ctx, query, keepSelection, modifiers) { 168 return doSearch(ctx, true, query, keepSelection, modifiers, {}); 169 } 170 171 export { buildQuery };