Skip to content

Commit 6098c69

Browse files
committed
feat: search snippets using filter input
1 parent 8fd587a commit 6098c69

File tree

5 files changed

+153
-14
lines changed

5 files changed

+153
-14
lines changed

src/extensionsIntegrated/CustomSnippets/main.js

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
/* eslint-disable no-invalid-this */
12
define(function (require, exports, module) {
23
const AppInit = require("utils/AppInit");
34
const CommandManager = require("command/CommandManager");
@@ -36,6 +37,8 @@ define(function (require, exports, module) {
3637

3738
// also register the handlers
3839
_registerHandlers();
40+
41+
$("#filter-snippets-input").val("");
3942
SnippetsList.showSnippetsList(); // to show the snippets list in the snippets panel
4043
}
4144

@@ -49,6 +52,8 @@ define(function (require, exports, module) {
4952
customSnippetsPanel.hide();
5053
} else {
5154
customSnippetsPanel.show();
55+
56+
$("#filter-snippets-input").val("");
5257
SnippetsList.showSnippetsList(); // we just remake the snippets list UI to make sure it is always on point
5358
}
5459
}
@@ -101,6 +106,7 @@ define(function (require, exports, module) {
101106
const $addSnippetBtn = $("#add-snippet-btn");
102107
const $addNewSnippetBtn = $("#add-new-snippet-btn");
103108
const $backToListMenuBtn = $("#back-to-list-menu-btn");
109+
const $filterInput = $("#filter-snippets-input");
104110

105111
$addSnippetBtn.on("click", function () {
106112
UIHelper.showAddSnippetMenu();
@@ -127,6 +133,17 @@ define(function (require, exports, module) {
127133
$descInput.on("input", Helper.toggleSaveButtonDisability);
128134
$templateInput.on("input", Helper.toggleSaveButtonDisability);
129135
$fileExtnInput.on("input", Helper.toggleSaveButtonDisability);
136+
137+
// filter input event handler
138+
$filterInput.on("keyup input", function (event) {
139+
// if user presses 'esc' we clear the input field
140+
if (event && event.key === 'Escape') {
141+
$(this).val("");
142+
SnippetsList.showSnippetsList();
143+
return;
144+
}
145+
SnippetsList.showSnippetsList();
146+
});
130147
}
131148

132149
AppInit.appReady(function () {

src/extensionsIntegrated/CustomSnippets/src/UIHelper.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,8 @@ define(function (require, exports, module) {
7575
$backToListMenuBtn.addClass("hidden");
7676
$addNewSnippetBtn.removeClass("hidden");
7777
$filterSnippetsPanel.removeClass("hidden");
78+
79+
$("#filter-snippets-input").val("");
7880
}
7981

8082
/**

src/extensionsIntegrated/CustomSnippets/src/driver.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ define(function (require, exports, module) {
55
const Helper = require("./helper");
66
const UIHelper = require("./UIHelper");
77
const SnippetsState = require("./snippetsState");
8+
const SnippetsList = require("./snippetsList");
89

910
/**
1011
* This function handles the save button click handler
@@ -17,6 +18,10 @@ define(function (require, exports, module) {
1718
Helper.clearAllInputFields();
1819
Helper.toggleSaveButtonDisability();
1920
SnippetsState.saveSnippetsToState();
21+
22+
// we need to move back to snippets list view after a snippet is saved
23+
UIHelper.showSnippetListMenu();
24+
SnippetsList.showSnippetsList();
2025
} else {
2126
UIHelper.showDuplicateAbbreviationError(snippetData.abbreviation);
2227
}
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
define(function (require, exports, module) {
2+
/**
3+
* this function creates a filter string for a snippet containing all the searchable fields
4+
* the priority order: abbreviation > description > template text > file extension
5+
*
6+
* @private
7+
* @param {object} snippetItem
8+
* @returns {string} - all the searchable fields (in lowercase)
9+
*/
10+
function _createFilterString(snippetItem) {
11+
const fields = [
12+
snippetItem.abbreviation || "",
13+
snippetItem.description || "",
14+
snippetItem.templateText || "",
15+
snippetItem.fileExtension || ""
16+
];
17+
return fields.join(" ").toLowerCase();
18+
}
19+
20+
/**
21+
* This function calculates a priority score for search matches
22+
* the higher scores means better match and is shown before lower score matches
23+
* priority order: abbreviation > description > template text > file extension
24+
*
25+
* @private
26+
* @param {object} snippet
27+
* @param {Array} filterTerms
28+
* @returns {number} - priority score
29+
*/
30+
function _calculateMatchPriority(snippet, filterTerms) {
31+
let score = 0;
32+
const abbr = (snippet.abbreviation || "").toLowerCase();
33+
const desc = (snippet.description || "").toLowerCase();
34+
const template = (snippet.templateText || "").toLowerCase();
35+
const fileExt = (snippet.fileExtension || "").toLowerCase();
36+
37+
filterTerms.forEach(function(term) {
38+
// abbreviation matching. this has the highest priority
39+
if (abbr.indexOf(term) === 0) {
40+
score += 1000; // exact start match in abbreviation
41+
} else if (abbr.indexOf(term) > -1) {
42+
score += 500; // partial match in abbreviation
43+
}
44+
45+
// description matching. this has the second highest priority
46+
if (desc.indexOf(term) === 0) {
47+
score += 100;
48+
} else if (desc.indexOf(term) > -1) {
49+
score += 50;
50+
}
51+
52+
// Template text matching. this has the third highest priority
53+
if (template.indexOf(term) === 0) {
54+
score += 20;
55+
} else if (template.indexOf(term) > -1) {
56+
score += 10;
57+
}
58+
59+
// File extension matching, lowests priority
60+
if (fileExt.indexOf(term) > -1) {
61+
score += 5;
62+
}
63+
});
64+
65+
return score;
66+
}
67+
68+
/**
69+
* This function filters snippets based on the filter input value
70+
*
71+
* @param {Array} snippetList - array of snippet objects
72+
* @returns {Array} - filtered array of snippet objects, sorted by relevance
73+
*/
74+
function filterSnippets(snippetList) {
75+
const $filterInput = $("#filter-snippets-input");
76+
const filterText = $filterInput.val().trim().toLowerCase();
77+
78+
if (!filterText) {
79+
return snippetList; // return all snippets if no filter
80+
}
81+
82+
const filterTerms = filterText.split(/\s+/);
83+
84+
// filter snippets that match all terms
85+
const matchingSnippets = snippetList.filter(function(snippet) {
86+
const filterString = _createFilterString(snippet);
87+
88+
// all terms must match (AND logic)
89+
return filterTerms.every(function(term) {
90+
return filterString.indexOf(term) > -1;
91+
});
92+
});
93+
94+
// sort by relevance (higher priority scores first)
95+
return matchingSnippets.sort(function(a, b) {
96+
const scoreA = _calculateMatchPriority(a, filterTerms);
97+
const scoreB = _calculateMatchPriority(b, filterTerms);
98+
return scoreB - scoreA; // in descending order (highest score will be at first)
99+
});
100+
}
101+
102+
exports.filterSnippets = filterSnippets;
103+
});

src/extensionsIntegrated/CustomSnippets/src/snippetsList.js

Lines changed: 26 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ define(function (require, exports, module) {
99
const Global = require("./global");
1010
const SnippetsState = require("./snippetsState");
1111
const UIHelper = require("./UIHelper");
12+
const FilterSnippets = require("./filterSnippets");
1213

1314
/**
1415
* This function is responsible to create a snippet item
@@ -86,12 +87,30 @@ define(function (require, exports, module) {
8687
if (snippetList.length === 0) {
8788
UIHelper.showEmptySnippetMessage();
8889
} else {
89-
UIHelper.showSnippetsList(); // to remove the hidden class from the snippets list wrapper
90+
// Apply filter to the snippets list
91+
const filteredSnippets = FilterSnippets.filterSnippets(snippetList);
9092

91-
// rebuild the snippets menu
92-
for (let i = 0; i < snippetList.length; i++) {
93-
const snippetItem = snippetList[i];
94-
_createSnippetItem(snippetItem);
93+
// Check if we have any snippets after filtering
94+
if (filteredSnippets.length === 0) {
95+
// Show a message indicating no matches found
96+
UIHelper.showEmptySnippetMessage();
97+
const $emptyMessage = $("#no-snippets-message");
98+
const $filterInput = $("#filter-snippets-input");
99+
const filterText = $filterInput.val().trim();
100+
101+
if (filterText) {
102+
$emptyMessage.text(`No snippets match "${filterText}"`);
103+
} else {
104+
$emptyMessage.text("No custom snippets added yet!");
105+
}
106+
} else {
107+
UIHelper.showSnippetsList(); // to remove the hidden class from the snippets list wrapper
108+
109+
// rebuild the snippets menu with filtered results
110+
for (let i = 0; i < filteredSnippets.length; i++) {
111+
const snippetItem = filteredSnippets[i];
112+
_createSnippetItem(snippetItem);
113+
}
95114
}
96115
}
97116
}
@@ -108,17 +127,10 @@ define(function (require, exports, module) {
108127

109128
if (index !== -1) {
110129
Global.SnippetHintsList.splice(index, 1); // removes it from the actual array
111-
$snippetItem.remove(); // remove from the dom
112-
113130
// save to preferences after deleting snippet
114131
SnippetsState.saveSnippetsToState();
115-
116-
_updateSnippetsCount();
117-
118-
// if snippetHintsList is now empty we need to show the empty snippet message
119-
if (Global.SnippetHintsList.length === 0) {
120-
UIHelper.showEmptySnippetMessage();
121-
}
132+
// Refresh the entire list to properly handle filtering
133+
showSnippetsList();
122134
}
123135
}
124136

0 commit comments

Comments
 (0)