Skip to content

Custom Snippets Feature #2278

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 61 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
61 commits
Select commit Hold shift + click to select a range
d5b0c2a
feat: custom snippets working prototype
devvaannsh May 19, 2025
ccc95a2
feat: integrated custom snippets with code hint manager
devvaannsh May 24, 2025
9b02ad1
fix: avoid duplicate snippet hints
devvaannsh May 24, 2025
3915f6f
fix: show snippet icon at the side of the hint properly
devvaannsh May 24, 2025
3e93bb1
feat: save snippets to prefs so that it stays even on reloads
devvaannsh May 24, 2025
e5e4abd
fix: remove popup auto-complete suggestions from appearing when input…
devvaannsh May 24, 2025
69d8a6c
fix: highlight matching characters for custom snippets
devvaannsh May 24, 2025
03032f3
fix: all snippets are not properly shown when there are no default hints
devvaannsh May 24, 2025
325ad19
feat: display snippet description in the code hints
devvaannsh May 25, 2025
d7942a4
fix: description showing at the side of the snippet instead of bottom
devvaannsh May 25, 2025
ceb414c
feat: make all the input fields mandatory
devvaannsh May 26, 2025
4cd49fc
fix: tooltip not appearing when snippet hints are hovered
devvaannsh May 26, 2025
481a2e0
refactor: update template box from input box to textarea
devvaannsh May 26, 2025
c16f240
refactor: update the styling of the custom snippet list
devvaannsh May 27, 2025
1d40593
fix: styling issues
devvaannsh May 27, 2025
3edf351
feat: add filter snippets input to the toolbar
devvaannsh May 27, 2025
5b917f5
fix: filter input not getting hidden in add snippet panel
devvaannsh May 27, 2025
44dbe1e
feat: search snippets using filter input
devvaannsh May 27, 2025
270047f
refactor: improve code readability
devvaannsh May 27, 2025
508d255
feat: add snippet list header
devvaannsh May 27, 2025
3b0cf96
refactor: add small padding bottom in snippets list so that all conte…
devvaannsh May 27, 2025
619a3fe
fix: validate file extension input values to make sure users always t…
devvaannsh May 28, 2025
29be450
fix: hide snippet header when no snippets are present
devvaannsh May 28, 2025
0ca6f24
fix: scrollbar invisible when not enough content to scroll
devvaannsh May 28, 2025
d93d803
refactor: reduce bottom padding for snippet list wrapper
devvaannsh May 28, 2025
7265bc6
refactor: increase padding for improved readability
devvaannsh May 28, 2025
de4e026
refactor: add snippet UI
devvaannsh May 28, 2025
d8cbb49
refactor: made placeholder text a bit lighter
devvaannsh May 28, 2025
180cfaa
feat: show tooltip when add-snippet labels are hovered
devvaannsh May 28, 2025
a8cad32
refactor: update '<' to '< back' button
devvaannsh May 28, 2025
252c254
feat: made description and file extension input as optional
devvaannsh May 28, 2025
abab3af
refactor: increase input field width as content was not properly visible
devvaannsh May 28, 2025
ae8bc32
feat: show edit snippet view when user clicks on a snippet
devvaannsh May 28, 2025
5fdd5c5
fix: remove resizer from textarea box in edit snippet menu
devvaannsh May 28, 2025
5aa56ac
fix: prevent focus change when tab key is pressed inside the template…
devvaannsh May 28, 2025
ead8314
fix: make the buttons UI consistent to other parts
devvaannsh May 28, 2025
36c8ea0
fix: empty snippets were getting added when save button was clicked w…
devvaannsh May 28, 2025
1185fa4
feat: update toolbar title based on the current active view
devvaannsh May 28, 2025
5c44f38
feat: add support for tab stops
devvaannsh May 29, 2025
57fdc41
fix: update snippets count when a snippet is deleted
devvaannsh May 29, 2025
7c8c120
refactor: give the delete button more padding
devvaannsh May 29, 2025
1d265fd
fix: hide snippets count when no snippets are present
devvaannsh May 29, 2025
4041aa2
fix: get word before cursor doesn't break at tab spacing
devvaannsh May 29, 2025
f775d26
fix: use language mode to show snippet hints instead of file extension
devvaannsh May 31, 2025
29d2093
fix: don't allow abbreviations to be more than 30 chars
devvaannsh May 31, 2025
e61fff2
fix: pasted content is exceeding the max char limit
devvaannsh May 31, 2025
fc8f7f8
fix: allow keyboard shortcuts to work normally on the input fields
devvaannsh May 31, 2025
4ca17c8
refactor: code beautification
devvaannsh May 31, 2025
a920dd7
fix: add char limit to description too
devvaannsh May 31, 2025
e1d63ea
refactor: use same color like keyboard shortcut panel header
devvaannsh May 31, 2025
7dc3193
refactor: responsive styling
devvaannsh Jun 1, 2025
5e48db1
refactor: add snippet and edit snippet panel UI
devvaannsh Jun 1, 2025
633e7cc
refactor: error message styling
devvaannsh Jun 1, 2025
2eea977
refactor: move all files outside of src directory inside custom snippets
devvaannsh Jun 6, 2025
90668f3
feat: add license to all files
devvaannsh Jun 6, 2025
bbceda9
fix: show exact matches before partial matches
devvaannsh Jun 6, 2025
ae13eb6
fix: description not properly visible issue
devvaannsh Jun 6, 2025
b9acff9
fix: add proper indentation when a multi line snippet is expanded
devvaannsh Jun 6, 2025
934b56e
fix: remove optional chaining as its not supported
devvaannsh Jun 6, 2025
c752da0
feat: add showHintsAtTop api inside codehintmanager
devvaannsh Jun 7, 2025
da0e9df
fix: match api signatures to keep it consistent and removed unused code
devvaannsh Jun 16, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
131 changes: 108 additions & 23 deletions src/editor/CodeHintManager.js
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,9 @@ define(function (require, exports, module) {
codeHintsEnabled = true,
codeHintOpened = false;

// API for extensions to show hints at the top
let hintsAtTopHandler = null;

PreferencesManager.definePreference("showCodeHints", "boolean", true, {
description: Strings.DESCRIPTION_SHOW_CODE_HINTS
});
Expand Down Expand Up @@ -447,11 +450,27 @@ define(function (require, exports, module) {
return hintList.callMoveUp(callMoveUpEvent);
}

var response = sessionProvider.getHints(lastChar);
var response = null;

// Get hints from regular provider if available
if (sessionProvider) {
response = sessionProvider.getHints(lastChar);
}

lastChar = null;

// we need this to track if we used hintsAtTopHandler as fallback
// because otherwise we will end up calling it twice
var usedTopHintsAsFallback = false;

// If regular provider doesn't have hints, try hints-at-top handler
if (!response && hintsAtTopHandler && hintsAtTopHandler.getHints) {
response = hintsAtTopHandler.getHints(sessionEditor, lastChar);
usedTopHintsAsFallback = true;
}

if (!response) {
// the provider wishes to close the session
// No provider wishes to show hints, close the session
_endSession();
} else {
// if the response is true, end the session and begin another
Expand All @@ -461,6 +480,18 @@ define(function (require, exports, module) {
_endSession();
_beginSession(previousEditor);
} else if (response.hasOwnProperty("hints")) { // a synchronous response
// allow extensions to modify the response by adding hints at the top
// BUT only if we didn't already use the top hints as fallback
if (!usedTopHintsAsFallback && sessionProvider && hintsAtTopHandler && hintsAtTopHandler.getHints) {
var topHints = hintsAtTopHandler.getHints(sessionEditor, lastChar);

if (topHints && topHints.hints && topHints.hints.length > 0) {
// Prepend the top hints to the existing response
var combinedHints = topHints.hints.concat(response.hints);
response = $.extend({}, response, { hints: combinedHints });
}
}

if (hintList.isOpen()) {
// the session is open
hintList.update(response);
Expand All @@ -477,6 +508,15 @@ define(function (require, exports, module) {
if (!hintList) {
return;
}
// allow extensions to modify the response by adding hints at the top
if (sessionProvider && hintsAtTopHandler && hintsAtTopHandler.getHints) {
var topHints = hintsAtTopHandler.getHints(sessionEditor, lastChar);
if (topHints && topHints.hints && topHints.hints.length > 0) {
// Prepend the top hints to the existing response
var combinedHints = topHints.hints.concat(hints.hints);
hints = $.extend({}, hints, { hints: combinedHints });
}
}

if (hintList.isOpen()) {
// the session is open
Expand All @@ -487,7 +527,7 @@ define(function (require, exports, module) {
});
}
}
}
};

/**
* Try to begin a new hinting session.
Expand All @@ -509,48 +549,71 @@ define(function (require, exports, module) {
var language = editor.getLanguageForSelection(),
enabledProviders = _getProvidersForLanguageId(language.getId());

enabledProviders.some(function (item, index) {
if (item.provider.hasHints(editor, lastChar)) {
sessionProvider = item.provider;
return true;
}
});
// Check if hints-at-top handler has hints first to avoid duplication
var hasTopHints = false;
if (hintsAtTopHandler && hintsAtTopHandler.hasHints) {
hasTopHints = hintsAtTopHandler.hasHints(editor, lastChar);
}

// If a provider is found, initialize the hint list and update it
if (sessionProvider) {
var insertHintOnTab,
// Find a suitable provider only if hints-at-top handler doesn't have hints
if (!hasTopHints) {
enabledProviders.some(function (item, index) {
if (item.provider.hasHints(editor, lastChar)) {
sessionProvider = item.provider;
return true;
}
});
}

// If a provider is found or top hints are available, initialize the hint list and update it
if (sessionProvider || hasTopHints) {
var insertHintOnTab = PreferencesManager.get("insertHintOnTab"),
maxCodeHints = PreferencesManager.get("maxCodeHints");
if (sessionProvider.insertHintOnTab !== undefined) {

if (sessionProvider && sessionProvider.insertHintOnTab !== undefined) {
insertHintOnTab = sessionProvider.insertHintOnTab;
} else {
insertHintOnTab = PreferencesManager.get("insertHintOnTab");
}

sessionEditor = editor;
hintList = new CodeHintList(sessionEditor, insertHintOnTab, maxCodeHints);
hintList.onHighlight(function ($hint, $hintDescContainer, reason) {
if (hintList.enableDescription && $hintDescContainer && $hintDescContainer.length) {
// If the current hint provider listening for hint item highlight change
if (sessionProvider.onHighlight) {
if (sessionProvider && sessionProvider.onHighlight) {
sessionProvider.onHighlight($hint, $hintDescContainer, reason);
}

// Update the hint description
if (sessionProvider.updateHintDescription) {
if (sessionProvider && sessionProvider.updateHintDescription) {
sessionProvider.updateHintDescription($hint, $hintDescContainer);
}
} else {
if (sessionProvider.onHighlight) {
if (sessionProvider && sessionProvider.onHighlight) {
sessionProvider.onHighlight($hint, undefined, reason);
}
}
});
hintList.onSelect(function (hint) {
var restart = sessionProvider.insertHint(hint),
previousEditor = sessionEditor;
_endSession();
if (restart) {
_beginSession(previousEditor);
// allow extensions to handle special hint selections
var handled = false;
if (hintsAtTopHandler && hintsAtTopHandler.insertHint) {
handled = hintsAtTopHandler.insertHint(hint);
}

if (handled) {
// If hints-at-top handler handled it, end the session
_endSession();
} else if (sessionProvider) {
// Regular hint provider handling
var restart = sessionProvider.insertHint(hint),
previousEditor = sessionEditor;
_endSession();
if (restart) {
_beginSession(previousEditor);
}
} else {
// if none of the provider handled it, we just end the session
_endSession();
}
});
hintList.onClose(()=>{
Expand Down Expand Up @@ -697,6 +760,26 @@ define(function (require, exports, module) {
return (hintList && hintList.isOpen());
}

/**
* Register a handler to show hints at the top of the hint list.
* This API allows extensions to add their own hints at the top of the standard hint list.
*
* @param {Object} handler - A hint provider object with standard methods:
* - hasHints: function(editor, implicitChar) - returns true if hints are available
* - getHints: function(editor, implicitChar) - returns hint response object with hints array
* - insertHint: function(hint) - handles hint insertion, returns true if handled
*/
function showHintsAtTop(handler) {
hintsAtTopHandler = handler;
}

/**
* Unregister the hints at top handler.
*/
function clearHintsAtTop() {
hintsAtTopHandler = null;
}

/**
* Explicitly start a new session. If we have an existing session,
* then close the current one and restart a new one.
Expand Down Expand Up @@ -780,6 +863,8 @@ define(function (require, exports, module) {
exports.isOpen = isOpen;
exports.registerHintProvider = registerHintProvider;
exports.hasValidExclusion = hasValidExclusion;
exports.showHintsAtTop = showHintsAtTop;
exports.clearHintsAtTop = clearHintsAtTop;

exports.SELECTION_REASON = CodeHintListModule._SELECTION_REASON;
});
Loading
Loading