Я разрабатываю расширение для VS Code, которое позволит вам создавать CodeLenses, написав такой комментарий:
// Code-Lens-Action insert-snippet <SNIPPET_NAME>
... который вставляет фрагмент кода с заданным именем над этой строкой.
Я застрял на этапе проверки имени фрагмента, потому что не мог найти способ вывести список всех активных фрагментов (то есть фрагментов, определенных пользователем, а также встроенных фрагментов и фрагментов, предоставленных другими расширениями. Проще говоря: все фрагменты, которые могут отображаться в IntelliSense при кодировании в том же файле, что и CodeLens) для языка текущего активного файла. Я хочу уведомить пользователя о том, что он, возможно, ошибся в названии фрагмента.
Я знаю об этом ТАК ответьте, и я, вероятно, буду использовать функцию vscode.commands.executeCommand
, но мне нужно знать, правильно ли имя.
Я попробовал это:
vscode.commands.executeCommand("editor.action.insertSnippet", { "name": "non-existent-name" })
.then(
val => {
vscode.window.showInformationMessage(val);
},
reason => {
vscode.window.showErrorMessage(reason);
}
);
Но он не вызывает обратный вызов onrejected
. Кроме того, эта команда не принимает никаких объектов Position или Range (я думаю, я не нашел никакой документации по этому вопросу, кроме этого ответа SO), она просто вставляет фрагмент в текущую позицию курсора. Поэтому мне нужно переместить курсор перед вставкой, поэтому мне нужно знать, правильно ли имя перед вставкой, чтобы не перемещать курсор, если имя неверно.
Уточнение обновление:
Поэтому я не знаю, где вы планируете разместить курсор на этой строке.
Я вообще не хочу использовать текущую позицию курсора. Я предполагаю, что вы использовали его в своем примере, потому что вам нужно было что-то передать executeCommand
. Но некоторые позиции в документе не получат всех предложений. После небольшого тестирования (выполненного нажатием Ctrl + Пробел (сочетание клавиш editor.action.triggerSuggest
) несколько раз при изменении положения курсора) я пришел к выводу, что для того, чтобы позиция возвращала все фрагменты, она должна находиться на расстоянии не менее 1 пробела. из любого текста.
Позиция в начале названия фрагмента выполняет то, что вы хотите?
Нет, потому что единственное имя фрагмента, присутствующее в документе, будет внутри комментария, который должен активировать CodeLens, и предложения там не отображаются. Мне нужна позиция в пустом пространстве, где отображаются все предложения.
Итак, дополнительный вопрос: как надежно получить позицию, находящуюся в «свободном» пространстве текущего документа, независимо от языка?
Да, пожалуйста, покажите мне код.
Предложения в комментариях будут работать, ЕСЛИ включена настройка Quick Suggestions
для comments
. Вам придется либо сообщить об этом своим пользователям, либо, я полагаю, вы можете отключить комментарий, получить свои предложения, а затем снова включить комментарии.
Чтобы найти позицию пустого пространства, вы должны иметь возможность выполнять поиск по регулярному выражению по всему тексту - при условии, что такое пустое пространство существует. Я бы сначала попробовал метод отключения комментариев, чтобы увидеть, насколько он мешает.
Начало пустой строки тоже работает.
Вот что-то, что вы можете адаптировать к своим потребностям. Вы можете использовать команду vscode.executeCompletionItemProvider
, чтобы вернуть все «дополнения», соответствующие позиции.
Затем эти дополнения можно отфильтровать, чтобы включать только фрагменты.
Рассмотрим этот код:
// Code-Lens-Action insert-snippet wsNameFolder _cc
где префикс фрагмента — _cc
, а имя фрагмента — wsNameFolder
.
const doc = vscode.window.activeTextEditor.document;
const thisUri = doc.uri;
const pos = vscode.window.activeTextEditor.selection.active;
/** @type { import("vscode").CompletionList } */ // if using javascript
const completionList = await vscode.commands.executeCommand('vscode.executeCompletionItemProvider', thisUri, pos);
let filteredList = [];
const word = doc.getText(doc.getWordRangeAtPosition(pos));
let good = undefined;
if (completionList && completionList.items) {
filteredList = completionList?.items.filter(item => item.kind === vscode.CompletionItemKind.Snippet);
// does the filteredList (of snippets) contain one with a label that matches the "word"
good = filteredList.find(snippet => {
if (typeof snippet.label !== 'string')
return snippet.label.label === word;
});
}
Если вызвать executeCompletionItemProvider
курсором сразу после или внутри _cc
, будут получены те фрагменты, которые соответствуют этому префиксу.
Но если вызвать executeCompletionItemProvider
курсором сразу после wsNameFolder
или внутри него, это имя все равно будет считаться префиксом для поиска подходящих фрагментов. Что, скорее всего, вам не подойдет.
Также. нажатие executeCompletionItemProvider
в пустом месте - не сразу после и не одним словом сработает. Вы получите все предложения, которые затем можно будет отфильтровать. Если курсор стоит перед именем, это тоже работает.
Поэтому я не знаю, где вы планируете разместить курсор на этой строке. Если оно будет после или в названии фрагмента, вы сможете вычислить позицию непосредственно перед именем и использовать эту позицию в вызове executeCompletionItemProvider
.
Следующий код получает Position
непосредственно перед именем фрагмента. Используя это, вы получаете все предложения. Отфильтруйте это по фрагментам, а затем find
тот, где snippet.label.description
соответствует названию фрагмента. Это немного сбивает с толку, но snippet.label.description
на самом деле — это имя, а не описание фрагмента.
"wsNameFolder": { // I assume you are using this
"prefix": "_cc",
"body": [
"${WORKSPACE_NAME}"
],
"description": "Workspace Name and Folder" // not this
},
Если вы действительно используете description
выше, вы тоже можете это найти — эта информация находится в возвращаемых фрагментах.
const doc = vscode.window.activeTextEditor.document;
const thisUri = doc.uri;
const pos = vscode.window.activeTextEditor.selection.active;
const nameStart = doc.getWordRangeAtPosition(pos).start;
// const completionList = await vscode.commands.executeCommand('vscode.executeCompletionItemProvider', thisUri, pos);
const completionList = await vscode.commands.executeCommand('vscode.executeCompletionItemProvider', thisUri, nameStart);
let filteredList = [];
const word = doc.getText(doc.getWordRangeAtPosition(pos));
let good = undefined;
if (completionList && completionList.items) {
filteredList = completionList?.items.filter(item => item.kind === vscode.CompletionItemKind.Snippet);
// does the filteredList (of snippets) contain one with a label that matches the "word"
good = filteredList.find(snippet => {
// return snippet.label.label === word;
if (typeof snippet.label !== 'string')
return snippet.label.description === word;
});
console.info(good);
}
Если предположить, что есть пустые строки, это, похоже, возвращает все предложения, которые затем можно отфильтровать во фрагменты:
const text = doc.getText();
const emptyLines = [...text.matchAll(/^$/gm)];
const firstEmptyLinePos = doc.positionAt(emptyLines[0].index);
/** @type { import("vscode").CompletionList } */ // if using javascript
const completionList = await vscode.commands.executeCommand('vscode.executeCompletionItemProvider', thisUri, firstEmptyLinePos);
Большое спасибо! executeCompletionItemProvider
было именно то, что мне нужно. Но я не хочу, чтобы действие CodeLens зависело от текущей позиции курсора. Я хочу всегда получать все активные фрагменты (поэтому предложения по умолчанию). Можно ли передать такую позицию executeCommand
, чтобы он всегда получал все фрагменты?
Выполняет ли позиция в начале имени фрагмента то, что вы хотите?
Я обновлю пост, потому что этот комментарий становится слишком длинным.
Я добавил поиск пустого кода строки в конец ответа.
Хорошо, обычно это должно сработать. Спасибо.
Я могу заставить что-то работать с префиксом фрагмента, но не используя имя фрагмента. Кроме того, я полагаю, что если курсор не находится ни на одном слове, вы можете получить все возможные фрагменты. Хотите увидеть код?