// src/provider.ts
var ProviderManager = class {
- constructor() {
+ constructor(channel) {
+ this.channel = channel;
this.providers = new Map();
}
regist(provider, name) {
let providers = Array.from(this.providers.values());
await Promise.all(providers.map((provider) => {
return provider.init();
- }));
+ })).catch((e) => {
+ this.appendError("init", e);
+ });
}
async getSnippets(filetype) {
let names = Array.from(this.providers.keys());
let list = [];
for (let name of names) {
let provider = this.providers.get(name);
- let snippets = await provider.getSnippets(filetype);
- snippets.map((s) => s.provider = name);
- list.push(...snippets);
+ try {
+ let snippets = await provider.getSnippets(filetype);
+ snippets.map((s) => s.provider = name);
+ list.push(...snippets);
+ } catch (e) {
+ this.appendError(`getSnippets of ${name}`, e);
+ }
}
return list;
}
async getSnippetFiles(filetype) {
let files = [];
- for (let provider of this.providers.values()) {
- let res = await provider.getSnippetFiles(filetype);
- files = files.concat(res);
+ for (let [name, provider] of this.providers.entries()) {
+ try {
+ let res = await provider.getSnippetFiles(filetype);
+ files = files.concat(res);
+ } catch (e) {
+ this.appendError(`getSnippetFiles of ${name}`, e);
+ }
}
return files;
}
- async getTriggerSnippets(autoTrigger = false) {
- let bufnr = await import_coc3.workspace.nvim.call("bufnr", "%");
+ async getTriggerSnippets(bufnr, autoTrigger = false) {
let doc = import_coc3.workspace.getDocument(bufnr);
if (!doc)
return [];
- doc.forceSync();
let position = await import_coc3.window.getCursorPosition();
let names = Array.from(this.providers.keys());
let list = [];
for (let name of names) {
let provider = this.providers.get(name);
- let items = await provider.getTriggerSnippets(doc, position, autoTrigger);
- for (let item of items) {
- if (list.findIndex((o) => o.prefix == item.prefix) == -1) {
- list.push(item);
+ try {
+ let items = await provider.getTriggerSnippets(doc, position, autoTrigger);
+ for (let item of items) {
+ if (list.findIndex((o) => o.prefix == item.prefix) == -1) {
+ list.push(item);
+ }
}
+ } catch (e) {
+ this.appendError(`getTriggerSnippets of ${name}`, e);
}
}
list.sort((a, b) => b.priority - a.priority);
}
return list;
}
+ appendError(name, e) {
+ this.channel.appendLine(`[Error ${new Date().toLocaleTimeString()}] Error on ${name}: ${typeof e === "string" ? e : e.message}`);
+ if (e instanceof Error) {
+ this.channel.appendLine(e.stack);
+ }
+ }
async provideCompletionItems(document, position, _token, context) {
let doc = import_coc3.workspace.getDocument(document.uri);
if (!doc)
continue;
if (snip.context) {
let provider = this.providers.get(snip.provider);
- let valid = await provider.checkContext(snip.context);
+ let valid;
+ try {
+ valid = await provider.checkContext(snip.context);
+ } catch (e) {
+ this.appendError(`checkContext of ${snip.provider}`, e);
+ valid = false;
+ }
if (!valid)
continue;
contextPrefixes.push(snip.prefix);
let provider = this.providers.get(item.data.provider);
if (provider) {
let filetype = await import_coc3.workspace.nvim.eval("&filetype");
- let insertSnippet = await provider.resolveSnippetBody(item.data.snip, item.textEdit.range, item.data.line);
+ let insertSnippet;
+ try {
+ insertSnippet = await provider.resolveSnippetBody(item.data.snip, item.textEdit.range, item.data.line);
+ } catch (e) {
+ this.appendError(`resolveSnippetBody of ${item.data.provider}`, e);
+ return item;
+ }
item.textEdit.newText = insertSnippet;
if (import_coc3.snippetManager) {
let snip = await Promise.resolve(import_coc3.snippetManager.resolveSnippet(insertSnippet));
if (map && map[filetype]) {
filetypes.push(map[filetype]);
}
+ if (filetype == "javascriptreact" && !filetypes.includes("javascript")) {
+ filetypes.push("javascript");
+ }
+ if (filetype == "typescriptreact" && !filetypes.includes("typescript")) {
+ filetypes.push("typescript");
+ }
let extendFiletypes = filetypes.reduce((arr, curr) => {
return arr.concat(this.getExtendsFiletypes(curr));
}, []);
code = code.trim().slice(1);
if (code.startsWith("p")) {
code = code.slice(1).trim();
- let lines = code.split("\n");
- lines = lines.map((line) => line.replace(/\t/g, " "));
- lines = lines.map((line) => ` ${line}`);
- lines.unshift("try:");
- lines.unshift("import traceback");
+ let lines = [
+ "import traceback",
+ "try:",
+ ' snip._reset("")'
+ ];
+ lines.push(...code.split("\n").map((line) => " " + line.replace(/\t/g, " ")));
lines.push("except Exception as e:");
lines.push(" snip.rv = traceback.format_exc()");
await nvim.command(`${pyMethod} ${lines.join("\n")}`);
async resolveSnippetBody(snippet, range, line) {
let {nvim} = import_coc8.workspace;
let {body, context, originRegex} = snippet;
- let buf = await nvim.buffer;
- let filepath = await buf.name;
let indentCount = await nvim.call("indent", ".");
let ind = " ".repeat(indentCount);
if (body.indexOf("`!p") !== -1) {
for (let [idx, val] of values.entries()) {
vals[idx] = val;
}
- let pyCodes = [];
- pyCodes.push("import re, os, vim, string, random");
- pyCodes.push(`t = ('', ${vals.join(",")})`);
- pyCodes.push(`fn = r'${import_path4.default.basename(filepath)}'`);
- pyCodes.push(`path = r'${filepath}'`);
+ let pyCodes = [
+ "import re, os, vim, string, random",
+ `t = (${vals.join(",")})`,
+ `fn = vim.eval('expand("%:t")') or ""`,
+ `path = vim.eval('expand("%:p")') or ""`
+ ];
if (context) {
pyCodes.push(`snip = ContextSnippet()`);
pyCodes.push(`context = ${context}`);
pyCodes.push(`pattern = re.compile(r"${originRegex.replace(/"/g, '\\"')}")`);
pyCodes.push(`match = pattern.search("${line.replace(/"/g, '\\"')}")`);
}
- try {
- await nvim.command(`${this.pyMethod} ${pyCodes.join("\n")}`);
- } catch (e) {
- this.channel.appendLine(`[Error ${new Date().toLocaleTimeString()}]: ${e.message}`);
- this.channel.appendLine(`code: ${pyCodes.join("\n")}`);
- }
+ await nvim.command(`${this.pyMethod} ${this.addPythonTryCatch(pyCodes.join("\n"))}`);
}
return this.parser.resolveUltisnipsBody(body);
}
+ addPythonTryCatch(code) {
+ if (!import_coc8.workspace.isVim)
+ return code;
+ let lines = [
+ "import traceback, vim",
+ `vim.vars['errmsg'] = ''`,
+ "try:"
+ ];
+ lines.push(...code.split("\n").map((line) => " " + line));
+ lines.push("except Exception as e:");
+ lines.push(` vim.vars['errmsg'] = traceback.format_exc()`);
+ return lines.join("\n");
+ }
async checkContext(context) {
let {nvim} = import_coc8.workspace;
- let pyCodes = [];
- pyCodes.push("import re, os, vim, string, random");
- pyCodes.push(`snip = ContextSnippet()`);
- pyCodes.push(`context = ${context}`);
- await nvim.command(`${this.pyMethod} ${pyCodes.join("\n")}`);
- let res = await nvim.call(`${this.pyMethod}eval`, "True if context else False");
- return res;
+ let pyCodes = [
+ "import re, os, vim, string, random",
+ "snip = ContextSnippet()",
+ `context = ${context}`
+ ];
+ await nvim.command(`${this.pyMethod} ${this.addPythonTryCatch(pyCodes.join("\n"))}`);
+ return await nvim.call(`${this.pyMethod}eval`, "True if context else False");
}
async getTriggerSnippets(document, position, autoTrigger) {
let snippets = await this.getSnippets(document.filetype);
if (!import_fs5.default.existsSync(dir))
import_fs5.default.mkdirSync(dir);
let tmpfile = import_path4.default.join(import_os3.default.tmpdir(), `coc.nvim-${process.pid}`, `coc-ultisnips-${uid()}.py`);
- import_fs5.default.writeFileSync(tmpfile, "# -*- coding: utf-8 -*-\n" + pythonCode, "utf8");
+ let code = this.addPythonTryCatch(pythonCode);
+ import_fs5.default.writeFileSync(tmpfile, "# -*- coding: utf-8 -*-\n" + code, "utf8");
await import_coc8.workspace.nvim.command(`exe '${this.pyMethod}file '.fnameescape('${tmpfile}')`);
pythonCodes.clear();
} catch (e) {
#
# Online reference: https://github.com/SirVer/ultisnips/blob/master/doc/UltiSnips.txt
`;
+async function waitDocument(doc, changedtick) {
+ if (import_coc10.workspace.isNvim)
+ return true;
+ return new Promise((resolve) => {
+ let timeout = setTimeout(() => {
+ disposable.dispose();
+ resolve(doc.changedtick == changedtick);
+ }, 200);
+ let disposable = doc.onDocumentChange(() => {
+ clearTimeout(timeout);
+ disposable.dispose();
+ if (doc.changedtick == changedtick) {
+ resolve(true);
+ } else {
+ resolve(false);
+ }
+ });
+ });
+}
async function activate(context) {
let {subscriptions} = context;
const {nvim} = import_coc10.workspace;
const configuration = import_coc10.workspace.getConfiguration("snippets");
const filetypeExtends = configuration.get("extends", {});
- const manager = new ProviderManager();
const trace = configuration.get("trace", "error");
let mru = import_coc10.workspace.createMru("snippets-mru");
const channel = import_coc10.window.createOutputChannel("snippets");
+ const manager = new ProviderManager(channel);
let snippetsDir = configuration.get("userSnippetsDirectory");
if (snippetsDir) {
snippetsDir = snippetsDir.replace(/^~/, import_os4.default.homedir());
insertLeaveTs = Date.now();
}, null, subscriptions);
let inserting = false;
- const handleTextChange = async (bufnr, pre) => {
+ const handleTextChange = async (bufnr, pre, changedtick) => {
let lastInsertTs = insertTs;
- insertTs = void 0;
if (inserting)
return;
let doc = import_coc10.workspace.getDocument(bufnr);
let now = Date.now();
if (!lastInsertTs || now - lastInsertTs > 100 || !pre.endsWith(lastInsert))
return;
- let edits = await manager.getTriggerSnippets(true);
+ let res = await waitDocument(doc, changedtick);
+ if (!res)
+ return;
+ let edits = await manager.getTriggerSnippets(bufnr, true);
if (edits.length == 0)
return;
if (edits.length > 1) {
channel.appendLine(`Multiple snippet found for auto trigger: ${edits.map((s) => s.prefix).join(", ")}`);
import_coc10.window.showMessage("Multiple snippet found for auto trigger, check output by :CocCommand workspace.showOutput", "warning");
}
- if (insertLeaveTs > now || inserting)
+ if (insertLeaveTs > now || insertTs > now || inserting)
return;
inserting = true;
try {
inserting = false;
};
import_coc10.events.on("TextChangedI", async (bufnr, info) => {
- await handleTextChange(bufnr, info.pre);
+ await handleTextChange(bufnr, info.pre, info.changedtick);
}, null, subscriptions);
import_coc10.events.on("TextChangedP", async (bufnr, info) => {
- await handleTextChange(bufnr, info.pre);
+ await handleTextChange(bufnr, info.pre, info.changedtick);
}, null, subscriptions);
}
let statusItem;
}
manager.init().then(() => {
statusItem == null ? void 0 : statusItem.hide();
- }, (e) => {
- statusItem == null ? void 0 : statusItem.hide();
- import_coc10.window.showMessage(`Error on load snippets: ${e.message}`, "error");
});
if (manager.hasProvider) {
- let disposable = import_coc10.languages.registerCompletionItemProvider("snippets", "S", null, manager, configuration.get("triggerCharacters", []), configuration.get("priority", 90));
+ let disposable = import_coc10.languages.registerCompletionItemProvider("snippets", configuration.get("shortcut", "S"), null, manager, configuration.get("triggerCharacters", []), configuration.get("priority", 90));
subscriptions.push(disposable);
}
async function fallback() {
await nvim.call("coc#start", [{source: "snippets"}]);
}
- async function doExpand() {
- let edits = await manager.getTriggerSnippets();
+ async function doExpand(bufnr) {
+ let edits = await manager.getTriggerSnippets(bufnr);
if (edits.length == 0)
return false;
if (edits.length == 1) {
let doc = await import_coc10.workspace.document;
if (!doc)
return;
- doc.forceSync();
let range = await import_coc10.workspace.getSelectedRange(mode, doc);
let text = doc.textDocument.getText(range);
if (text)
await import_coc10.workspace.jumpTo(uri, null, configuration.get("editSnippetsCommand"));
}));
subscriptions.push(import_coc10.workspace.registerKeymap(["i"], "snippets-expand", async () => {
- let expanded = await doExpand();
+ let bufnr = await nvim.eval('bufnr("%")');
+ let expanded = await doExpand(bufnr);
if (!expanded)
await fallback();
- }, {silent: true, sync: false, cancel: true}));
+ }, {silent: true, sync: true, cancel: true}));
subscriptions.push(import_coc10.workspace.registerKeymap(["i"], "snippets-expand-jump", async () => {
- let expanded = await doExpand();
+ let bufnr = await nvim.eval('bufnr("%")');
+ let expanded = await doExpand(bufnr);
if (!expanded) {
- let bufnr = await nvim.call("bufnr", "%");
let session = import_coc10.snippetManager.getSession(bufnr);
if (session && session.isActive) {
await nvim.call("coc#_cancel", []);
}
await fallback();
}
- }, {silent: true, sync: false, cancel: true}));
+ }, {silent: true, sync: true, cancel: true}));
subscriptions.push(import_coc10.workspace.registerKeymap(["v"], "snippets-select", async () => {
let doc = await import_coc10.workspace.document;
if (!doc)
await import_coc10.window.moveTo(range.start);
}, {silent: true, sync: false, cancel: true}));
let languageProvider = new languages_default(channel, trace);
- subscriptions.push(import_coc10.languages.registerCompletionItemProvider("snippets-source", "S", ["snippets"], languageProvider, ["$"], configuration.get("priority", 90)));
+ subscriptions.push(import_coc10.languages.registerCompletionItemProvider("snippets-source", configuration.get("shortcut", "S"), ["snippets"], languageProvider, ["$"], configuration.get("priority", 90)));
subscriptions.push(statusItem);
subscriptions.push(channel);
subscriptions.push(import_coc10.listManager.registerList(new snippet_default(import_coc10.workspace.nvim, manager, mru)));
return {
expandable: async () => {
- let edits;
- try {
- edits = await manager.getTriggerSnippets();
- } catch (e) {
- channel.appendLine(`[Error ${new Date().toLocaleTimeString()}] Error on getTriggerSnippets: ${e}`);
- }
+ let bufnr = await nvim.eval('bufnr("%")');
+ let edits = await manager.getTriggerSnippets(bufnr);
return edits && edits.length > 0;
}
};