Как программно получить все активные фрагменты для данного языка в расширении?

Я разрабатываю расширение для 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, и предложения там не отображаются. Мне нужна позиция в пустом пространстве, где отображаются все предложения.

Итак, дополнительный вопрос: как надежно получить позицию, находящуюся в «свободном» пространстве текущего документа, независимо от языка?

Я могу заставить что-то работать с префиксом фрагмента, но не используя имя фрагмента. Кроме того, я полагаю, что если курсор не находится ни на одном слове, вы можете получить все возможные фрагменты. Хотите увидеть код?

Mark 29.06.2024 04:24

Да, пожалуйста, покажите мне код.

Chrysaloid 29.06.2024 10:18

Предложения в комментариях будут работать, ЕСЛИ включена настройка Quick Suggestions для comments. Вам придется либо сообщить об этом своим пользователям, либо, я полагаю, вы можете отключить комментарий, получить свои предложения, а затем снова включить комментарии.

Mark 01.07.2024 05:37

Чтобы найти позицию пустого пространства, вы должны иметь возможность выполнять поиск по регулярному выражению по всему тексту - при условии, что такое пустое пространство существует. Я бы сначала попробовал метод отключения комментариев, чтобы увидеть, насколько он мешает.

Mark 01.07.2024 05:40

Начало пустой строки тоже работает.

Mark 01.07.2024 05:48
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
2
5
65
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Вот что-то, что вы можете адаптировать к своим потребностям. Вы можете использовать команду 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, чтобы он всегда получал все фрагменты?

Chrysaloid 30.06.2024 12:12

Выполняет ли позиция в начале имени фрагмента то, что вы хотите?

Mark 30.06.2024 21:33

Я обновлю пост, потому что этот комментарий становится слишком длинным.

Chrysaloid 01.07.2024 00:09

Я добавил поиск пустого кода строки в конец ответа.

Mark 01.07.2024 06:11

Хорошо, обычно это должно сработать. Спасибо.

Chrysaloid 01.07.2024 20:54

Другие вопросы по теме