Skip to content

Commit 776029e

Browse files
committed
[WIP] fuzzy search
1 parent b389ccb commit 776029e

File tree

1 file changed

+171
-46
lines changed

1 file changed

+171
-46
lines changed

src/CodeSnippetDisplay.tsx

Lines changed: 171 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,7 @@ interface ICodeSnippetDisplayProps {
144144
*/
145145
interface ICodeSnippetDisplayState {
146146
codeSnippets: ICodeSnippet[];
147+
matchIndices: { [key: number]: 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,56 +273,98 @@ 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

299+
// check if this snippet is one of the filtered snippets
300+
// console.log(displayName);
300301
if (
301-
searchValue !== '' &&
302-
displayName.toLowerCase().includes(searchValue.toLowerCase())
302+
this.state.searchValue !== '' &&
303+
Object.keys(this.state.matchIndices).includes(id.toString())
303304
) {
304-
const startIndex: number = displayName
305-
.toLowerCase()
306-
.indexOf(searchValue.toLowerCase());
307-
308-
const endIndex: number = startIndex + searchValue.length;
305+
// remove space
306+
// const tempSearchValue = searchValue.replace(/ /g, '');
307+
// const char_list = tempSearchValue.split('');
308+
309+
// console.log(char_list);
310+
// // form regular expression
311+
// let re = '/';
312+
// for (const ch of char_list) {
313+
// re += ch + '.*';
314+
// }
315+
// re += '/g';
316+
317+
// console.log(re);
318+
319+
// const startIndex: number = displayName
320+
// .toLowerCase()
321+
// .indexOf(searchValue.toLowerCase());
322+
323+
// const endIndex: number = startIndex + searchValue.length;
324+
325+
const elements = [];
326+
const boldIndices = this.state.matchIndices[id];
327+
for (const index of boldIndices) {
328+
if (index >= language.length) {
329+
elements.push(
330+
<span>{displayName.substring(language.length, index)}</span>
331+
);
332+
elements.push(
333+
<mark className={SEARCH_BOLD}>
334+
{displayName.substring(index, index + 1)}
335+
</mark>
336+
);
337+
}
338+
}
309339

310-
if (endIndex <= language.length) {
311-
return <span>{name}</span>;
312-
} else {
313-
const start = displayName.substring(language.length, startIndex);
314-
const bolded = displayName.substring(startIndex, endIndex);
315-
const end = displayName.substring(endIndex);
316-
return (
340+
if (boldIndices[-1] < displayName.length) {
341+
elements.push(
317342
<span>
318-
{start}
319-
<mark className={SEARCH_BOLD}>{bolded}</mark>
320-
{end}
343+
{displayName.substring(boldIndices[-1] + 1, displayName.length)}
321344
</span>
322345
);
323346
}
347+
348+
if (elements.length === 0) {
349+
return <span>{name}</span>;
350+
} else {
351+
return <span>{elements}</span>;
352+
}
353+
354+
// if (endIndex <= language.length) {
355+
// return <span>{name}</span>;
356+
// } else {
357+
// const start = displayName.substring(language.length, startIndex);
358+
// const bolded = displayName.substring(startIndex, endIndex);
359+
// const end = displayName.substring(endIndex);
360+
// return (
361+
// <span>
362+
// {start}
363+
// <mark className={SEARCH_BOLD}>{bolded}</mark>
364+
// {end}
365+
// </span>
366+
// );
367+
// }
324368
}
325369
return <span onDoubleClick={this.handleRenameSnippet}>{name}</span>;
326370
};
@@ -516,7 +560,7 @@ export class CodeSnippetDisplay extends React.Component<
516560
target.removeEventListener('mouseup', this._evtMouseUp, true);
517561

518562
return this._drag.start(clientX, clientY).then(() => {
519-
this.dragHoverStyleRemove(codeSnippet.id.toString());
563+
this.dragHoverStyleRemove(codeSnippet.id);
520564
this._drag = null;
521565
this._dragData = null;
522566
});
@@ -532,9 +576,8 @@ export class CodeSnippetDisplay extends React.Component<
532576
}
533577

534578
//Set the position of the preview to be next to the snippet title.
535-
private _setPreviewPosition(id: string): void {
536-
const intID = parseInt(id, 10);
537-
const realTarget = document.getElementsByClassName(TITLE_CLASS)[intID];
579+
private _setPreviewPosition(id: number): void {
580+
const realTarget = document.getElementsByClassName(TITLE_CLASS)[id];
538581
// distDown is the number of pixels to shift the preview down
539582
let distDown: number = realTarget.getBoundingClientRect().top - 43;
540583
if (realTarget.getBoundingClientRect().top > window.screen.height / 2) {
@@ -1047,7 +1090,7 @@ export class CodeSnippetDisplay extends React.Component<
10471090
// Render display of code snippet list
10481091
private renderCodeSnippet = (
10491092
codeSnippet: ICodeSnippet,
1050-
id: string
1093+
id: number
10511094
): JSX.Element => {
10521095
const buttonClasses = BUTTON_CLASS;
10531096
const displayName = '[' + codeSnippet.language + '] ' + codeSnippet.name;
@@ -1070,7 +1113,7 @@ export class CodeSnippetDisplay extends React.Component<
10701113
<div
10711114
key={codeSnippet.name}
10721115
className={CODE_SNIPPET_ITEM}
1073-
id={id}
1116+
id={id.toString()}
10741117
onMouseOver={(): void => {
10751118
this.dragHoverStyle(id);
10761119
}}
@@ -1081,7 +1124,7 @@ export class CodeSnippetDisplay extends React.Component<
10811124
<div
10821125
className={CODE_SNIPPET_DRAG_HOVER}
10831126
title="Drag to move"
1084-
id={id}
1127+
id={id.toString()}
10851128
onMouseDown={(event): void => {
10861129
this.handleDragSnippet(event);
10871130
}}
@@ -1091,7 +1134,7 @@ export class CodeSnippetDisplay extends React.Component<
10911134
onMouseEnter={(): void => {
10921135
showPreview(
10931136
{
1094-
id: parseInt(id, 10),
1137+
id: id,
10951138
title: displayName,
10961139
body: new PreviewHandler(),
10971140
codeSnippet: codeSnippet
@@ -1104,16 +1147,16 @@ export class CodeSnippetDisplay extends React.Component<
11041147
this._evtMouseLeave();
11051148
}}
11061149
>
1107-
<div key={displayName} className={TITLE_CLASS} id={id}>
1150+
<div key={displayName} className={TITLE_CLASS} id={id.toString()}>
11081151
<div
1109-
id={id}
1152+
id={id.toString()}
11101153
title={codeSnippet.name}
11111154
className={DISPLAY_NAME_CLASS}
11121155
>
11131156
{this.renderLanguageIcon(codeSnippet.language)}
1114-
{this.boldNameOnSearch(this.state.searchValue, language, name)}
1157+
{this.boldNameOnSearch(id, language, name)}
11151158
</div>
1116-
<div className={ACTION_BUTTONS_WRAPPER_CLASS} id={id}>
1159+
<div className={ACTION_BUTTONS_WRAPPER_CLASS} id={id.toString()}>
11171160
{actionButtons.map(btn => {
11181161
return (
11191162
<button
@@ -1137,8 +1180,8 @@ export class CodeSnippetDisplay extends React.Component<
11371180
})}
11381181
</div>
11391182
</div>
1140-
<div className={CODE_SNIPPET_DESC} id={id}>
1141-
<p id={id}>{`${codeSnippet.description}`}</p>
1183+
<div className={CODE_SNIPPET_DESC} id={id.toString()}>
1184+
<p id={id.toString()}>{`${codeSnippet.description}`}</p>
11421185
</div>
11431186
</div>
11441187
</div>
@@ -1152,6 +1195,7 @@ export class CodeSnippetDisplay extends React.Component<
11521195
if (state.searchValue === '' && state.filterTags.length === 0) {
11531196
return {
11541197
codeSnippets: props.codeSnippets,
1198+
matchIndices: {},
11551199
searchValue: '',
11561200
filterTags: []
11571201
};
@@ -1170,20 +1214,100 @@ export class CodeSnippetDisplay extends React.Component<
11701214
});
11711215
return {
11721216
codeSnippets: newSnippets,
1217+
matchIndices: state.matchIndices,
11731218
searchValue: state.searchValue,
11741219
filterTags: state.filterTags
11751220
};
11761221
}
11771222
return null;
11781223
}
11791224

1225+
/**
1226+
* Return an object with the entry of (id, matched_indices)
1227+
* @param id unique id of snippet
1228+
* @param regex regular expression to match
1229+
* @param str name or language of the code snippet
1230+
* @param char_list list of characters searched
1231+
*/
1232+
matchSnippet(
1233+
id: number,
1234+
regex: RegExp,
1235+
str: string,
1236+
char_list: string[]
1237+
): [number, number[]] {
1238+
const matches: { [key: string]: number } = {};
1239+
let match = [];
1240+
while ((match = regex.exec(str))) {
1241+
matches[match[0]] = regex.lastIndex - 1;
1242+
// console.log(match);
1243+
// console.log("match found at " + match.index);
1244+
}
1245+
1246+
console.log(str);
1247+
1248+
// Object.keys(matches).length
1249+
if (Object.keys(matches).length !== char_list.length) {
1250+
return null;
1251+
}
1252+
1253+
console.log(matches);
1254+
return [id, Object.values(matches)];
1255+
}
1256+
11801257
filterSnippets = (searchValue: string, filterTags: string[]): void => {
1258+
// remove space
1259+
const tempSearchValue = searchValue.replace(/ /g, '');
1260+
const char_list = tempSearchValue.split('');
1261+
1262+
// form regular expression
1263+
// let re = '.*';
1264+
// for (const ch of char_list) {
1265+
// re += ch + '.*';
1266+
// }
1267+
let re = '[';
1268+
for (const ch of char_list) {
1269+
re += ch;
1270+
}
1271+
re += ']';
1272+
1273+
const regex = new RegExp(re, 'g');
1274+
// console.log(regex);
1275+
1276+
// const str = 'Pythonmost_frequent';
1277+
1278+
// let match = [];
1279+
// let match_index = [];
1280+
// while(match = regex.exec(str)) {
1281+
// match_index.push(match.index)
1282+
// // console.log(match);
1283+
// // console.log("match found at " + match.index);
1284+
// }
1285+
1286+
// if(match_index.length !== char_list.length){
1287+
// match_index = []
1288+
// }
1289+
// console.log(match_index);
1290+
1291+
// const found = [...'Pythonmost_frequent'.matchAll(regex)];
1292+
// console.log(found.index);
1293+
1294+
// TODO: filter codes nippet with regex!
11811295
// filter with search
1182-
let filteredSnippets = this.props.codeSnippets.filter(
1183-
codeSnippet =>
1184-
codeSnippet.name.toLowerCase().includes(searchValue.toLowerCase()) ||
1185-
codeSnippet.language.toLowerCase().includes(searchValue.toLowerCase())
1186-
);
1296+
const matchIndices: { [key: number]: number[] } = {};
1297+
let filteredSnippets = this.props.codeSnippets.filter(codeSnippet => {
1298+
const matchIndex = this.matchSnippet(
1299+
codeSnippet.id,
1300+
regex,
1301+
codeSnippet.language + codeSnippet.name,
1302+
char_list
1303+
);
1304+
if (matchIndex) {
1305+
matchIndices[matchIndex[0]] = matchIndex[1];
1306+
}
1307+
return matchIndex;
1308+
});
1309+
console.log(filteredSnippets);
1310+
console.log(matchIndices);
11871311

11881312
// filter with tags
11891313
if (filterTags.length !== 0) {
@@ -1199,6 +1323,7 @@ export class CodeSnippetDisplay extends React.Component<
11991323

12001324
this.setState({
12011325
codeSnippets: filteredSnippets,
1326+
matchIndices: matchIndices,
12021327
searchValue: searchValue,
12031328
filterTags: filterTags
12041329
});
@@ -1350,8 +1475,8 @@ export class CodeSnippetDisplay extends React.Component<
13501475
/>
13511476
<div className={CODE_SNIPPETS_CONTAINER}>
13521477
<div>
1353-
{this.state.codeSnippets.map((codeSnippet, id) =>
1354-
this.renderCodeSnippet(codeSnippet, id.toString())
1478+
{this.state.codeSnippets.map(codeSnippet =>
1479+
this.renderCodeSnippet(codeSnippet, codeSnippet.id)
13551480
)}
13561481
</div>
13571482
</div>

0 commit comments

Comments
 (0)