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

如何正确定义CodeMirror 6语言?我的代码为何无自动补全?

Firestore语句自动补全无响应的问题排查与修复

问题背景

想要为以下Firestore语句实现基础自动补全:

db.collection("Items").where("name", "==", "temp").limit(1).get();

已通过CodeMirror 6的StreamLanguage编写语言定义,React中集成后编辑器可正常加载,但输入时无自动补全功能。

原语言定义代码

import {
  IndentContext,
  LanguageSupport,
  StreamLanguage,
  StringStream
} from "@codemirror/language";
import { tags as t } from "@lezer/highlight";

export const FireStoreLanguage = StreamLanguage.define({
  name: "firestore",
  startState: (indentUnit: number) => {
    return {};
  },
  token: (stream: StringStream, state: any = {}): string | null => {
    console.log(stream);
    if (stream.match("db")) {
      state.db = true;
      return "keyword";
    }
    if (stream.match(".")) {
      if (state.db) {
        state.db = false;
        state.collection = true;
        return "keyword";
      } else if (state.collection) {
        state.collection = false;
        state.where = true;
        return "keyword";
      } else if (state.where) {
        state.where = false;
        state.limit = true;
        return "keyword";
      } else if (state.limit) {
        state.limit = false;
        return "keyword";
      }
    }
    if (stream.match("collection")) {
      if (state.db) {
        state.collection = true;
        return "keyword";
      }
    }
    if (stream.match("where")) {
      if (state.collection) {
        state.where = true;
        return "keyword";
      }
    }
    if (stream.match("limit")) {
      if (state.where) {
        state.limit = true;
        return "keyword";
      }
    }
    if (stream.match("get")) {
      if (state.limit) {
        state.limit = false;
        return "keyword";
      }
    }
    if (stream.match(/"(?:[^\"]|\.)*"/)) {
      if (state.collection) {
        return "string";
      }
      if (state.where) {
        state.where = false;
        state.whereValue = true;
        return "string";
      }
      if (state.whereValue) {
        state.whereValue = false;
        return "string";
      }
      if (stream.match("==")) {
        if (state.whereValue) {
          state.whereValue = false;
          state.whereOperator = true;
          return "operator";
        }
      }
      if (stream.match(/[0-9]+/)) {
        if (state.limit) {
          return "number";
        }
      }
    }
    stream.next();
    return null;
  },
  blankLine: (state: {}, indentUnit: number): void => {},
  copyState: (state: {}) => {},
  indent: (
    state: {},
    textAfter: string,
    context: IndentContext
  ): number | null => {
    return 1;
  },
  languageData: {
    commentTokens: { line: ";" },
  },
  tokenTable: {
    db: t.keyword,
    dot: t.punctuation,
    collection: t.keyword,
    get: t.keyword,
    lParen: t.punctuation,
    rParen: t.punctuation,
    string: t.string,
  },
});

export function firestore() {
  return new LanguageSupport(FireStoreLanguage);
}

原React集成代码

import CodeMirror from "@uiw/react-codemirror";
import React from "react";
import { firestore } from "./firestore";

function App() {
  const onChange = React.useCallback((value, viewUpdate) => {
    console.log("value:", value);
  }, []);
  return (
    <CodeMirror
      value={``}
      height="100vh"
      extensions={[firestore()]}
      onChange={onChange}
    />
  );
}

export default App;

问题排查

当前代码存在以下关键问题:

  • 未实现自动补全逻辑:StreamLanguage仅负责语法高亮和词法分析,CodeMirror的自动补全功能需要通过@codemirror/autocomplete模块单独实现,你的代码完全未涉及补全相关配置。
  • Token函数逻辑漏洞:字符串匹配分支内的==运算符、数字匹配代码永远不会执行(stream.match(/".../)已消耗流内容,后续匹配无法生效);状态切换逻辑不严谨,容易出现状态混乱导致词法分析错误。
  • 未启用补全扩展:即使实现了补全逻辑,也需要将autocompletion扩展添加到编辑器配置中,否则补全功能无法触发。

修复方案

1. 安装自动补全依赖

执行命令安装补全模块:

npm install @codemirror/autocomplete

2. 修改语言定义,集成补全功能

更新firestore.ts代码,修复Token逻辑并添加补全配置:

import {
  IndentContext,
  LanguageSupport,
  StreamLanguage,
  StringStream
} from "@codemirror/language";
import { tags as t } from "@lezer/highlight";
import { autocompletion, completeFromList, CompletionContext } from "@codemirror/autocomplete";

// 定义Firestore相关补全项
const firestoreCompletions = [
  { label: "db", type: "keyword" },
  { label: "collection", type: "function" },
  { label: "where", type: "function" },
  { label: "limit", type: "function" },
  { label: "get", type: "function" },
  { label: "==", type: "operator" },
  { label: "Items", type: "variable" },
  { label: "name", type: "variable" }
];

// 自定义补全源,根据上下文提供精准补全
function firestoreCompletionSource(context: CompletionContext) {
  const tokenBefore = context.tokenBefore();
  
  if (tokenBefore?.type === "keyword" && tokenBefore.value === "db") {
    return completeFromList([{ label: ".collection", type: "function" }])(context);
  }
  if (tokenBefore?.value === "collection" || tokenBefore?.value === ")") {
    return completeFromList([{ label: ".where", type: "function" }])(context);
  }
  if (tokenBefore?.value === "where" || tokenBefore?.value === ")") {
    return completeFromList([{ label: ".limit", type: "function" }])(context);
  }
  if (tokenBefore?.value === "limit" || tokenBefore?.value === ")") {
    return completeFromList([{ label: ".get", type: "function" }])(context);
  }
  
  return completeFromList(firestoreCompletions)(context);
}

export const FireStoreLanguage = StreamLanguage.define({
  name: "firestore",
  startState: (indentUnit: number) => {
    return { db: false, collection: false, where: false, limit: false, whereValue: false };
  },
  token: (stream: StringStream, state: any): string | null => {
    if (stream.eatSpace()) return null;

    // 匹配关键字
    if (stream.match("db")) {
      state.db = true;
      state.collection = false;
      state.where = false;
      state.limit = false;
      return "keyword";
    }
    if (stream.match("collection")) {
      if (state.db) {
        state.collection = true;
        state.db = false;
        return "keyword";
      }
    }
    if (stream.match("where")) {
      if (state.collection) {
        state.where = true;
        state.collection = false;
        return "keyword";
      }
    }
    if (stream.match("limit")) {
      if (state.where) {
        state.limit = true;
        state.where = false;
        return "keyword";
      }
    }
    if (stream.match("get")) {
      if (state.limit) {
        state.limit = false;
        return "keyword";
      }
    }

    // 匹配点运算符
    if (stream.match(".")) {
      return "punctuation";
    }

    // 匹配字符串
    if (stream.match(/"(?:[^"\\]|\\.)*"/)) {
      if (state.collection) state.collection = false;
      else if (state.where) {
        state.where = false;
        state.whereValue = true;
      } else if (state.whereValue) state.whereValue = false;
      return "string";
    }

    // 匹配运算符
    if (stream.match(/==|!=|<|>|<=|>=/)) {
      if (state.whereValue) state.whereValue = false;
      return "operator";
    }

    // 匹配数字
    if (stream.match(/[0-9]+/)) {
      if (state.limit) state.limit = false;
      return "number";
    }

    // 匹配括号
    if (stream.match(/\(|\)/)) {
      return "punctuation";
    }

    stream.next();
    return null;
  },
  blankLine: (state: {}, indentUnit: number): void => {},
  copyState: (state: any) => ({ ...state }),
  indent: (
    state: {},
    textAfter: string,
    context: IndentContext
  ): number | null => {
    return null;
  },
  languageData: {
    commentTokens: { line: ";" },
  },
  tokenTable: {
    db: t.keyword,
    dot: t.punctuation,
    collection: t.keyword,
    get: t.keyword,
    lParen: t.punctuation,
    rParen: t.punctuation,
    string: t.string,
    operator: t.operator,
    number: t.number,
  },
});

export function firestore() {
  return new LanguageSupport(FireStoreLanguage, [
    autocompletion({ override: [firestoreCompletionSource] })
  ]);
}

3. 确认React集成配置

原React代码无需修改,因为firestore()函数已包含补全扩展,编辑器会自动启用补全功能。

关键说明

  • 补全逻辑通过firestoreCompletionSource实现,可根据输入上下文提供精准补全(如输入db后自动提示.collection)。
  • 修复了Token函数的状态管理问题,确保语法高亮和状态切换准确。
  • 通过autocompletion扩展启用补全功能,并将自定义补全源注入到语言支持中。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.03 23:01:05