@@ -22,7 +22,7 @@ import {
2222} from '@jupyterlab/cells' ;
2323
2424import { Widget } from '@lumino/widgets' ;
25- import { find } from '@lumino/algorithm' ;
25+ import { find , StringExt } from '@lumino/algorithm' ;
2626import { Drag } from '@lumino/dragdrop' ;
2727import { MimeData } from '@lumino/coreutils' ;
2828
@@ -144,6 +144,7 @@ interface ICodeSnippetDisplayProps {
144144 */
145145interface ICodeSnippetDisplayState {
146146 codeSnippets : ICodeSnippet [ ] ;
147+ matchIndices : number [ ] [ ] ;
147148 searchValue : string ;
148149 filterTags : string [ ] ;
149150}
@@ -161,6 +162,7 @@ export class CodeSnippetDisplay extends React.Component<
161162 super ( props ) ;
162163 this . state = {
163164 codeSnippets : this . props . codeSnippets ,
165+ matchIndices : [ ] ,
164166 searchValue : '' ,
165167 filterTags : [ ]
166168 } ;
@@ -271,55 +273,86 @@ export class CodeSnippetDisplay extends React.Component<
271273 } ;
272274
273275 // Create 6 dots drag/drop image on hover
274- private dragHoverStyle = ( id : string ) : void => {
275- const _id : number = parseInt ( id , 10 ) ;
276-
276+ private dragHoverStyle = ( id : number ) : void => {
277277 document
278278 . getElementsByClassName ( CODE_SNIPPET_DRAG_HOVER )
279- [ _id ] . classList . add ( CODE_SNIPPET_DRAG_HOVER_SELECTED ) ;
279+ [ id ] . classList . add ( CODE_SNIPPET_DRAG_HOVER_SELECTED ) ;
280280 } ;
281281
282282 // Remove 6 dots off hover
283- private dragHoverStyleRemove = ( id : string ) : void => {
284- const _id : number = parseInt ( id , 10 ) ;
283+ private dragHoverStyleRemove = ( id : number ) : void => {
285284 if ( document . getElementsByClassName ( CODE_SNIPPET_DRAG_HOVER ) ) {
286285 document
287286 . getElementsByClassName ( CODE_SNIPPET_DRAG_HOVER )
288- [ _id ] . classList . remove ( CODE_SNIPPET_DRAG_HOVER_SELECTED ) ;
287+ [ id ] . classList . remove ( CODE_SNIPPET_DRAG_HOVER_SELECTED ) ;
289288 }
290289 } ;
291290
292291 // Bold text in snippet name based on search
293292 private boldNameOnSearch = (
294- searchValue : string ,
293+ id : number ,
295294 language : string ,
296295 name : string
297296 ) : JSX . Element => {
298297 const displayName = language + name ;
299298
300- if (
301- searchValue !== '' &&
302- displayName . toLowerCase ( ) . includes ( searchValue . toLowerCase ( ) )
303- ) {
304- const startIndex : number = displayName
305- . toLowerCase ( )
306- . indexOf ( searchValue . toLowerCase ( ) ) ;
299+ // check if the searchValue is not ''
300+ if ( this . state . searchValue !== '' ) {
301+ const elements = [ ] ;
302+ const boldIndices = this . state . matchIndices [ id ] . slice ( ) ;
307303
308- const endIndex : number = startIndex + searchValue . length ;
304+ // get first match index in the name
305+ let i = 0 ;
306+ while ( i < boldIndices . length ) {
307+ if ( boldIndices [ i ] >= language . length ) {
308+ elements . push ( displayName . substring ( language . length , boldIndices [ i ] ) ) ;
309+ break ;
310+ }
311+ i ++ ;
312+ }
309313
310- if ( endIndex <= language . length ) {
314+ // when there is no match in name but language
315+ if ( i >= boldIndices . length ) {
311316 return < span > { name } </ span > ;
312317 } else {
313- const start = displayName . substring ( language . length , startIndex ) ;
314- const bolded = displayName . substring ( startIndex , endIndex ) ;
315- const end = displayName . substring ( endIndex ) ;
316- return (
317- < span >
318- { start }
319- < mark className = { SEARCH_BOLD } > { bolded } </ mark >
320- { end }
321- </ span >
322- ) ;
318+ // current and next indices are bold indices
319+ let currIndex = boldIndices [ i ] ;
320+ let nextIndex ;
321+ // check if the match is the end of the name
322+ if ( i < boldIndices . length - 1 ) {
323+ i ++ ;
324+ nextIndex = boldIndices [ i ] ;
325+ } else {
326+ nextIndex = null ;
327+ }
328+ while ( nextIndex !== null ) {
329+ // make the current index bold
330+ elements . push (
331+ < mark key = { id + '_' + currIndex } className = { SEARCH_BOLD } >
332+ { displayName . substring ( currIndex , currIndex + 1 ) }
333+ </ mark >
334+ ) ;
335+ // add the regular string until we reach the next bold index
336+ elements . push ( displayName . substring ( currIndex + 1 , nextIndex ) ) ;
337+ currIndex = nextIndex ;
338+ if ( i < boldIndices . length - 1 ) {
339+ i ++ ;
340+ nextIndex = boldIndices [ i ] ;
341+ } else {
342+ nextIndex = null ;
343+ }
344+ }
345+ if ( nextIndex === null ) {
346+ elements . push (
347+ < mark key = { id + '_' + currIndex } className = { SEARCH_BOLD } >
348+ { displayName . substring ( currIndex , currIndex + 1 ) }
349+ </ mark >
350+ ) ;
351+ elements . push (
352+ displayName . substring ( currIndex + 1 , displayName . length )
353+ ) ;
354+ }
355+ return < span > { elements } </ span > ;
323356 }
324357 }
325358 return < span onDoubleClick = { this . handleRenameSnippet } > { name } </ span > ;
@@ -331,8 +364,6 @@ export class CodeSnippetDisplay extends React.Component<
331364 event : React . MouseEvent < HTMLSpanElement , MouseEvent >
332365 ) : Promise < void > {
333366 const contentsService = CodeSnippetContentsService . getInstance ( ) ;
334- console . log ( event . currentTarget ) ;
335- console . log ( event . target ) ;
336367 const target = event . target as HTMLElement ;
337368 const oldPath = 'snippets/' + target . innerHTML + '.json' ;
338369
@@ -348,8 +379,6 @@ export class CodeSnippetDisplay extends React.Component<
348379 new_element . setSelectionRange ( 0 , new_element . value . length ) ;
349380
350381 new_element . onblur = async ( ) : Promise < void > => {
351- console . log ( target . innerHTML ) ;
352- console . log ( new_element . value ) ;
353382 if ( target . innerHTML !== new_element . value ) {
354383 const newPath = 'snippets/' + new_element . value + '.json' ;
355384 try {
@@ -527,7 +556,7 @@ export class CodeSnippetDisplay extends React.Component<
527556 target . removeEventListener ( 'mouseup' , this . _evtMouseUp , true ) ;
528557
529558 return this . _drag . start ( clientX , clientY ) . then ( ( ) => {
530- this . dragHoverStyleRemove ( codeSnippet . id . toString ( ) ) ;
559+ this . dragHoverStyleRemove ( codeSnippet . id ) ;
531560 this . _drag = null ;
532561 this . _dragData = null ;
533562 } ) ;
@@ -543,10 +572,10 @@ export class CodeSnippetDisplay extends React.Component<
543572 }
544573
545574 //Set the position of the preview to be next to the snippet title.
546- private _setPreviewPosition ( id : string ) : void {
547- const intID = parseInt ( id , 10 ) ;
548- const realTarget = document . getElementsByClassName ( TITLE_CLASS ) [ intID ] ;
549- const newTarget = document . getElementsByClassName ( CODE_SNIPPET_ITEM ) [ intID ] ;
575+
576+ private _setPreviewPosition ( id : number ) : void {
577+ const realTarget = document . getElementsByClassName ( TITLE_CLASS ) [ id ] ;
578+ const newTarget = document . getElementsByClassName ( CODE_SNIPPET_ITEM ) [ id ] ;
550579 // distDown is the number of pixels to shift the preview down
551580 const distDown : number = realTarget . getBoundingClientRect ( ) . top - 43 ; //this is bumping it up
552581 const elementSnippet = newTarget as HTMLElement ;
@@ -1066,7 +1095,7 @@ export class CodeSnippetDisplay extends React.Component<
10661095 // Render display of code snippet list
10671096 private renderCodeSnippet = (
10681097 codeSnippet : ICodeSnippet ,
1069- id : string
1098+ id : number
10701099 ) : JSX . Element => {
10711100 const buttonClasses = BUTTON_CLASS ;
10721101 const displayName = '[' + codeSnippet . language + '] ' + codeSnippet . name ;
@@ -1089,7 +1118,7 @@ export class CodeSnippetDisplay extends React.Component<
10891118 < div
10901119 key = { codeSnippet . name }
10911120 className = { CODE_SNIPPET_ITEM }
1092- id = { id }
1121+ id = { id . toString ( ) }
10931122 onMouseOver = { ( ) : void => {
10941123 this . dragHoverStyle ( id ) ;
10951124 } }
@@ -1100,7 +1129,7 @@ export class CodeSnippetDisplay extends React.Component<
11001129 < div
11011130 className = { CODE_SNIPPET_DRAG_HOVER }
11021131 title = "Drag to move"
1103- id = { id }
1132+ id = { id . toString ( ) }
11041133 onMouseDown = { ( event ) : void => {
11051134 this . handleDragSnippet ( event ) ;
11061135 } }
@@ -1110,7 +1139,7 @@ export class CodeSnippetDisplay extends React.Component<
11101139 onMouseEnter = { ( ) : void => {
11111140 showPreview (
11121141 {
1113- id : parseInt ( id , 10 ) ,
1142+ id : id ,
11141143 title : displayName ,
11151144 body : new PreviewHandler ( ) ,
11161145 codeSnippet : codeSnippet
@@ -1123,16 +1152,12 @@ export class CodeSnippetDisplay extends React.Component<
11231152 this . _evtMouseLeave ( ) ;
11241153 } }
11251154 >
1126- < div key = { displayName } className = { TITLE_CLASS } id = { id } >
1127- < div
1128- id = { id }
1129- title = { codeSnippet . name }
1130- className = { DISPLAY_NAME_CLASS }
1131- >
1132- { this . renderLanguageIcon ( codeSnippet . language ) }
1133- { this . boldNameOnSearch ( this . state . searchValue , language , name ) }
1155+ < div key = { displayName } className = { TITLE_CLASS } id = { id . toString ( ) } >
1156+ < div id = { id . toString ( ) } title = { name } className = { DISPLAY_NAME_CLASS } >
1157+ { this . renderLanguageIcon ( language ) }
1158+ { this . boldNameOnSearch ( id , language , name ) }
11341159 </ div >
1135- < div className = { ACTION_BUTTONS_WRAPPER_CLASS } id = { id } >
1160+ < div className = { ACTION_BUTTONS_WRAPPER_CLASS } id = { id . toString ( ) } >
11361161 { actionButtons . map ( btn => {
11371162 return (
11381163 < button
@@ -1156,8 +1181,8 @@ export class CodeSnippetDisplay extends React.Component<
11561181 } ) }
11571182 </ div >
11581183 </ div >
1159- < div className = { CODE_SNIPPET_DESC } id = { id } >
1160- < p id = { id } > { `${ codeSnippet . description } ` } </ p >
1184+ < div className = { CODE_SNIPPET_DESC } id = { id . toString ( ) } >
1185+ < p id = { id . toString ( ) } > { `${ codeSnippet . description } ` } </ p >
11611186 </ div >
11621187 </ div >
11631188 </ div >
@@ -1171,24 +1196,27 @@ export class CodeSnippetDisplay extends React.Component<
11711196 if ( state . searchValue === '' && state . filterTags . length === 0 ) {
11721197 return {
11731198 codeSnippets : props . codeSnippets ,
1199+ matchIndices : [ ] ,
11741200 searchValue : '' ,
11751201 filterTags : [ ]
11761202 } ;
11771203 }
11781204
11791205 if ( state . searchValue !== '' || state . filterTags . length !== 0 ) {
1180- const newSnippets = props . codeSnippets . filter ( codeSnippet => {
1181- return (
1182- ( state . searchValue !== '' &&
1183- codeSnippet . name . toLowerCase ( ) . includes ( state . searchValue ) ) ||
1184- ( state . searchValue !== '' &&
1185- codeSnippet . language . toLowerCase ( ) . includes ( state . searchValue ) ) ||
1186- ( codeSnippet . tags &&
1187- codeSnippet . tags . some ( tag => state . filterTags . includes ( tag ) ) )
1188- ) ;
1189- } ) ;
1206+ // const newSnippets = props.codeSnippets.filter(codeSnippet => {
1207+ // return (
1208+ // state.matchIndices[codeSnippet.id] !== null ||
1209+ // // (state.searchValue !== '' &&
1210+ // // codeSnippet.name.toLowerCase().includes(state.searchValue)) ||
1211+ // // (state.searchValue !== '' &&
1212+ // // codeSnippet.language.toLowerCase().includes(state.searchValue)) ||
1213+ // (codeSnippet.tags &&
1214+ // codeSnippet.tags.some(tag => state.filterTags.includes(tag)))
1215+ // );
1216+ // });
11901217 return {
1191- codeSnippets : newSnippets ,
1218+ codeSnippets : state . codeSnippets ,
1219+ matchIndices : state . matchIndices ,
11921220 searchValue : state . searchValue ,
11931221 filterTags : state . filterTags
11941222 } ;
@@ -1198,29 +1226,73 @@ export class CodeSnippetDisplay extends React.Component<
11981226
11991227 filterSnippets = ( searchValue : string , filterTags : string [ ] ) : void => {
12001228 // filter with search
1201- let filteredSnippets = this . props . codeSnippets . filter (
1202- codeSnippet =>
1203- codeSnippet . name . toLowerCase ( ) . includes ( searchValue . toLowerCase ( ) ) ||
1204- codeSnippet . language . toLowerCase ( ) . includes ( searchValue . toLowerCase ( ) )
1205- ) ;
1229+ let matchIndices : number [ ] [ ] = [ ] ;
1230+ const matchResults : StringExt . IMatchResult [ ] = [ ] ;
1231+ let filteredSnippets = this . props . codeSnippets ;
1232+ const filteredSnippetsScore : {
1233+ score : number ;
1234+ snippet : ICodeSnippet ;
1235+ } [ ] = [ ] ;
1236+ if ( searchValue !== '' ) {
1237+ filteredSnippets . forEach ( snippet => {
1238+ const matchResult = StringExt . matchSumOfSquares (
1239+ ( snippet . language + snippet . name ) . toLowerCase ( ) ,
1240+ searchValue . replace ( ' ' , '' ) . toLowerCase ( )
1241+ ) ;
1242+
1243+ if ( matchResult ) {
1244+ matchResults . push ( matchResult ) ;
1245+ filteredSnippetsScore . push ( {
1246+ score : matchResult . score ,
1247+ snippet : snippet
1248+ } ) ;
1249+ }
1250+ } ) ;
1251+
1252+ // sort snippets by its score
1253+ filteredSnippetsScore . sort ( ( a , b ) => a . score - b . score ) ;
1254+ const newFilteredSnippets : ICodeSnippet [ ] = [ ] ;
1255+ filteredSnippetsScore . forEach ( snippetScore =>
1256+ newFilteredSnippets . push ( snippetScore . snippet )
1257+ ) ;
1258+ filteredSnippets = newFilteredSnippets ;
1259+
1260+ // sort the matchResults by its score
1261+ matchResults . sort ( ( a , b ) => a . score - b . score ) ;
1262+ matchResults . forEach ( res => matchIndices . push ( res . indices ) ) ;
1263+ }
12061264
12071265 // filter with tags
12081266 if ( filterTags . length !== 0 ) {
1209- filteredSnippets = filteredSnippets . filter ( codeSnippet => {
1267+ const newMatchIndices = matchIndices . slice ( ) ;
1268+ filteredSnippets = filteredSnippets . filter ( ( codeSnippet , id ) => {
12101269 return filterTags . some ( tag => {
12111270 if ( codeSnippet . tags ) {
1212- return codeSnippet . tags . includes ( tag ) ;
1271+ if ( codeSnippet . tags . includes ( tag ) ) {
1272+ return true ;
1273+ }
12131274 }
1275+ // if the snippet does not have the tag, remove its mathed index
1276+ const matchedIndex = matchIndices [ id ] ;
1277+ const indexToDelete = newMatchIndices . indexOf ( matchedIndex ) ;
1278+ newMatchIndices . splice ( indexToDelete , 1 ) ;
12141279 return false ;
12151280 } ) ;
12161281 } ) ;
1282+ matchIndices = newMatchIndices ;
12171283 }
12181284
1219- this . setState ( {
1220- codeSnippets : filteredSnippets ,
1221- searchValue : searchValue ,
1222- filterTags : filterTags
1223- } ) ;
1285+ this . setState (
1286+ {
1287+ codeSnippets : filteredSnippets ,
1288+ matchIndices : matchIndices ,
1289+ searchValue : searchValue ,
1290+ filterTags : filterTags
1291+ } ,
1292+ ( ) => {
1293+ console . log ( 'snippets filtered' ) ;
1294+ }
1295+ ) ;
12241296 } ;
12251297
12261298 getActiveTags ( ) : string [ ] {
@@ -1370,7 +1442,7 @@ export class CodeSnippetDisplay extends React.Component<
13701442 < div className = { CODE_SNIPPETS_CONTAINER } >
13711443 < div >
13721444 { this . state . codeSnippets . map ( ( codeSnippet , id ) =>
1373- this . renderCodeSnippet ( codeSnippet , id . toString ( ) )
1445+ this . renderCodeSnippet ( codeSnippet , id )
13741446 ) }
13751447 </ div >
13761448 </ div >
0 commit comments