You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

VSCode扩展开发求助:如何识别SCSS中光标所在的父选择器

实现VSCode「HTML to CSS autocompletion」扩展的SCSS &选择器补全功能

一、必须导入的VSCode核心模块

你需要从vscode模块中导入以下核心API,用于文档解析、光标定位和补全逻辑:

import {
  CompletionItem,
  CompletionItemProvider,
  TextDocument,
  Position,
  Range,
  workspace,
  CompletionItemKind
} from 'vscode';

二、识别光标所在的块级父选择器

要获取光标所在的父选择器,核心是逆向解析文档文本,定位最近的代码块:

  1. 获取当前文档和光标位置的上下文
  2. 从光标位置向上遍历,找到最近的{字符,再向前截取到上一个}或文档开头的文本,提取其中的选择器
  3. 清洗选择器文本,去除多余空格、换行和无效字符,得到纯净的父选择器(如.description)

示例解析函数:

function getParentSelector(document: TextDocument, position: Position): string | null {
  let currentPos = position.translate(0, -1);
  
  // 向上查找最近的代码块起始符{
  while (currentPos.character >= 0) {
    const char = document.getText(new Range(currentPos, currentPos.translate(0, 1)));
    if (char === '{') {
      // 从{的位置向前定位选择器起始点
      let selectorStart = currentPos.translate(0, -1);
      while (selectorStart.character >= 0 && !['}', '\n', ';'].includes(document.getText(new Range(selectorStart, selectorStart.translate(0, 1))))) {
        selectorStart = selectorStart.translate(0, -1);
      }
      // 提取并清洗选择器文本
      const selector = document.getText(new Range(selectorStart.translate(0, 1), currentPos)).trim();
      return selector || null;
    }
    // 换行后继续向上查找
    if (currentPos.character === 0) {
      if (currentPos.line === 0) break;
      currentPos = document.lineAt(currentPos.line - 1).range.end;
    } else {
      currentPos = currentPos.translate(0, -1);
    }
  }
  return null;
}

三、实现&触发的补全逻辑

当用户输入&时,执行以下流程:

  1. 检查光标前的字符是否为&,触发补全逻辑
  2. 读取当前工作区所有HTML文件,提取所有class属性值,筛选出以父选择器开头的类名
  3. 将筛选后的类名替换前缀为&,生成对应的补全项

示例补全提供者实现:

class SCSSAmpersandCompletionProvider implements CompletionItemProvider {
  async provideCompletionItems(document: TextDocument, position: Position) {
    // 判断是否触发&补全
    const preCharRange = new Range(position.translate(0, -1), position);
    const preChar = document.getText(preCharRange);
    if (preChar !== '&') return [];

    // 获取父选择器
    const parentSelector = getParentSelector(document, position);
    if (!parentSelector || !parentSelector.startsWith('.')) return [];

    const parentClass = parentSelector.slice(1); // 去掉前缀.,得到description
    const completionItems: CompletionItem[] = [];

    // 遍历工作区所有HTML文件提取类名
    const htmlFiles = await workspace.findFiles('**/*.html');
    for (const file of htmlFiles) {
      const doc = await workspace.openTextDocument(file);
      const text = doc.getText();
      // 匹配所有class属性值
      const classRegex = /class="([^"]+)"/g;
      let match;
      while ((match = classRegex.exec(text)) !== null) {
        const classes = match[1].split(' ');
        classes.forEach(cls => {
          if (cls.startsWith(`${parentClass}__`)) {
            // 替换前缀为&,生成SCSS选择器
            const scssSelector = `&${cls.slice(parentClass.length)}`;
            const item = new CompletionItem(scssSelector, CompletionItemKind.Property);
            item.insertText = scssSelector;
            completionItems.push(item);
          }
        });
      }
    }

    // 去重后返回补全项
    return [...new Map(completionItems.map(item => [item.label, item])).values()];
  }
}

四、注册补全提供者

在扩展的activate方法中注册该提供者,指定触发语言为SCSS:

export function activate(context: vscode.ExtensionContext) {
  const provider = new SCSSAmpersandCompletionProvider();
  const disposable = vscode.languages.registerCompletionItemProvider(
    'scss',
    provider,
    '&' // 设置触发字符为&
  );
  context.subscriptions.push(disposable);
}

内容的提问来源于stack exchange,提问作者Nabs

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.28 01:27:41