Skip to content

Add lightweight multi-language snippet system#2071

Open
DaviGayDaSilva wants to merge 2 commits intoAcode-Foundation:mainfrom
DaviGayDaSilva:main
Open

Add lightweight multi-language snippet system#2071
DaviGayDaSilva wants to merge 2 commits intoAcode-Foundation:mainfrom
DaviGayDaSilva:main

Conversation

@DaviGayDaSilva
Copy link
Copy Markdown

PR: Sistema de Snippets para Acode Fork
Este PR adiciona um sistema de snippets ao fork do Acode, focado em simplicidade, performance e suporte a múltiplas linguagens.
O que foi adicionado:
Sistema de snippets com expansão automática por atalhos
Suporte a placeholders ($1, $2, $0)
Organização de snippets por linguagem
Autocomplete baseado em snippets
Sugestões de snippets enquanto o usuário digita
Importação e exportação de snippets via JSON
Linguagens suportadas: HTML, CSS, JavaScript, TypeScript, Python, Java, C, C++, C#, PHP, Ruby, Go, Kotlin, Swift, Dart, Rust, SQL, Bash, JSON, YAML, XML, Markdown, Lua, Perl, R, Scala, Haskell, Elixir, Clojure, Objective-C, Groovy, PowerShell, Shell Script, VBScript, Assembly, MATLAB, Julia, COBOL e Fortran.
Objetivo: Facilitar a criação de código com snippets simples e rápidos, mantendo o editor leve e compatível com o Acode original.
Notas técnicas:
Snippets armazenados em formato JSON
Integração direta com o editor sem alterar funções principais
Foco em desempenho no Android
Estrutura pronta para futuras melhorias como snippets avançados e compartilhamento entre usuários

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented Apr 28, 2026

Greptile Summary

This PR adds a multi-language snippet system to Acode, including built-in snippets for 40 languages, user-defined snippet CRUD persisted in app settings, JSON import/export, a CodeMirror completion source, and a Tab-key expansion handler.

  • openSnippetManager is never called: the callback switch in editorSettings.js has no case "snippet-manager", so tapping the settings row falls through to default and writes a junk key to settings — the snippet manager UI is completely unreachable.
  • Unhandled rejection on language picker cancel: the inner select() call inside openSnippetManager is not wrapped in try/catch, unlike the outer one, causing an unhandled promise rejection when the user dismisses the dialog.

Confidence Score: 3/5

Not safe to merge — the snippet manager setting row is wired up but the UI can never be opened due to a missing switch case.

Two P1 bugs: the missing case 'snippet-manager' in callback makes the entire feature unreachable, and the unguarded inner select can produce unhandled rejections. These are present defects on the changed path, pulling the score below the P1 ceiling of 4.

src/settings/editorSettings.js requires the most attention — both P1 issues are here.

Important Files Changed

Filename Overview
src/settings/editorSettings.js Adds "Snippet manager" settings row and openSnippetManager() UI, but the callback switch never calls openSnippetManager(), making the feature entirely unreachable; inner select() cancel also unhandled.
src/lib/snippets.js New file: implements the snippet system — built-in snippets, user snippet CRUD, JSON import/export, CodeMirror completion source, and Tab-key expansion. Import replaces all user snippets (no merge).
src/lib/editorManager.js Wires snippet completion source and Tab keymap into applyFileToEditor; language ID is captured once at file-open so won't track subsequent language-mode changes.
src/lib/settings.js Adds snippets default settings object ({ enabled: true, user: {} }) — straightforward and correct.

Comments Outside Diff (1)

  1. src/settings/editorSettings.js, line 240-254 (link)

    P1 openSnippetManager is never invoked

    The callback function has no case "snippet-manager" to call openSnippetManager(). When the user taps the "Snippet manager" row, the key falls through to default, which calls appSettings.update({ "snippet-manager": value }) — silently writing a junk key to settings instead of opening the manager. The feature is entirely unreachable from the UI.

Reviews (1): Last reviewed commit: "Merge pull request #1 from DaviGayDaSilv..." | Re-trigger Greptile

Comment on lines +279 to +295
return;
}

if (action === "language") {
const language = await select(
"Choose language",
getSupportedSnippetLanguages().map((lang) => ({
value: lang,
text: lang,
})),
);
if (!language) return;
const current = getUserSnippetsForLanguage(language);
const initial = JSON.stringify(current, null, 2);
const result = await prompt(
`Edit snippets for ${language} as JSON array [{prefix, body, description}]`,
initial,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Unhandled rejection when cancelling the language picker

The outer select call is guarded with try/catch, implying that select rejects (throws) on cancel. The inner select call for the language choice is not wrapped the same way, so if the user dismisses it, an unhandled promise rejection is thrown and the if (!language) return; guard is never reached.

Suggested change
return;
}
if (action === "language") {
const language = await select(
"Choose language",
getSupportedSnippetLanguages().map((lang) => ({
value: lang,
text: lang,
})),
);
if (!language) return;
const current = getUserSnippetsForLanguage(language);
const initial = JSON.stringify(current, null, 2);
const result = await prompt(
`Edit snippets for ${language} as JSON array [{prefix, body, description}]`,
initial,
if (action === "language") {
let language = null;
try {
language = await select(
"Choose language",
getSupportedSnippetLanguages().map((lang) => ({
value: lang,
text: lang,
})),
);
} catch (_) {
return;
}
if (!language) return;

Comment thread src/lib/snippets.js
Comment on lines +404 to +416
const current = appSettings.value?.snippets;
if (!current || typeof current !== "object") {
return { enabled: true, user: {} };
}
return {
enabled: current.enabled !== false,
user: current.user && typeof current.user === "object" ? current.user : {},
};
}

export function getSupportedSnippetLanguages() {
return [...SUPPORTED_LANGUAGES];
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 importUserSnippetsFromJson silently replaces all user snippets

The import overwrites the entire user object rather than merging. A user who pastes a partial export (e.g., just JavaScript snippets) will lose all snippets for every other language they previously defined. Consider merging per-language keys — or at minimum warn the user in the UI that existing snippets will be replaced.

Comment thread src/lib/editorManager.js
Comment on lines +1162 to +1179
const snippetLanguageId = getFileLanguageId(file);
exts.push(
EditorState.languageData.of(() => [
{
autocomplete: createSnippetCompletionSource(snippetLanguageId),
},
]),
);
exts.push(
Prec.high(
keymap.of([
{
key: "Tab",
run: (view) => expandSnippetShortcut(view, snippetLanguageId),
},
]),
),
);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Language ID is captured once per file-open, not per language switch

snippetLanguageId is evaluated once in applyFileToEditor and then closed over in both the completion source and the Tab keymap. If the user later changes the file's language (mode) without re-opening the file, the snippet engine will keep using the stale language ID. The existing languageCompartment.reconfigure() path won't update these closures. Consider deriving the language ID dynamically from the view's state inside both callbacks instead.

@DaviGayDaSilva
Copy link
Copy Markdown
Author

Dude, seriously, are you flirting on GitHub? You can't do that, man.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant