@@ -3,10 +3,13 @@ import { cpp } from "@codemirror/lang-cpp"
|
||||
import { python } from "@codemirror/lang-python"
|
||||
import { EditorView } from "@codemirror/view"
|
||||
import { Codemirror } from "vue-codemirror"
|
||||
import type { Extension } from "@codemirror/state"
|
||||
import { autocompletion } from "@codemirror/autocomplete"
|
||||
import { LANGUAGE } from "utils/types"
|
||||
import { oneDark } from "../themes/oneDark"
|
||||
import { smoothy } from "../themes/smoothy"
|
||||
import { enhanceCompletion } from "shared/extensions/autocompletion"
|
||||
|
||||
|
||||
interface Props {
|
||||
language?: LANGUAGE
|
||||
fontSize?: number
|
||||
@@ -36,13 +39,16 @@ const styleTheme = EditorView.baseTheme({
|
||||
},
|
||||
})
|
||||
|
||||
const lang = computed((): Extension => {
|
||||
const langExtension = computed(() => {
|
||||
return ["Python2", "Python3"].includes(props.language) ? python() : cpp()
|
||||
})
|
||||
|
||||
const extensions = computed(() => [
|
||||
styleTheme,
|
||||
lang.value,
|
||||
langExtension.value,
|
||||
autocompletion({
|
||||
override: [enhanceCompletion(props.language)],
|
||||
}),
|
||||
isDark.value ? oneDark : smoothy,
|
||||
])
|
||||
</script>
|
||||
|
||||
@@ -3,12 +3,15 @@ import { cpp } from "@codemirror/lang-cpp"
|
||||
import { python } from "@codemirror/lang-python"
|
||||
import { EditorView } from "@codemirror/view"
|
||||
import { Codemirror } from "vue-codemirror"
|
||||
import { autocompletion } from "@codemirror/autocomplete"
|
||||
import type { Extension } from "@codemirror/state"
|
||||
import { LANGUAGE } from "utils/types"
|
||||
import { oneDark } from "../themes/oneDark"
|
||||
import { smoothy } from "../themes/smoothy"
|
||||
import { useCodeSync, SYNC_ERROR_CODES } from "../composables/sync"
|
||||
import { useBreakpoints } from "../composables/breakpoints"
|
||||
import { enhanceCompletion } from "shared/extensions/autocompletion"
|
||||
|
||||
const isDark = useDark()
|
||||
|
||||
interface EditorReadyPayload {
|
||||
@@ -55,14 +58,17 @@ const styleTheme = EditorView.baseTheme({
|
||||
},
|
||||
})
|
||||
|
||||
const lang = computed((): Extension => {
|
||||
const langExtension = computed((): Extension => {
|
||||
return ["Python2", "Python3"].includes(props.language) ? python() : cpp()
|
||||
})
|
||||
|
||||
const extensions = computed(() => [
|
||||
styleTheme,
|
||||
lang.value,
|
||||
langExtension.value,
|
||||
isDark.value ? oneDark : smoothy,
|
||||
autocompletion({
|
||||
override: [enhanceCompletion(props.language)],
|
||||
}),
|
||||
getInitialExtension(),
|
||||
])
|
||||
|
||||
|
||||
670
src/shared/extensions/autocompletion.ts
Normal file
670
src/shared/extensions/autocompletion.ts
Normal file
@@ -0,0 +1,670 @@
|
||||
import type {
|
||||
Completion,
|
||||
CompletionContext,
|
||||
CompletionResult,
|
||||
CompletionSource,
|
||||
} from "@codemirror/autocomplete"
|
||||
import type { EditorView } from "@codemirror/view"
|
||||
import { LANGUAGE } from "utils/types"
|
||||
|
||||
type ChineseCompletion = Pick<
|
||||
Completion,
|
||||
"label" | "detail" | "type" | "info" | "boost" | "apply"
|
||||
> & { apply?: string | Completion["apply"] }
|
||||
|
||||
// 中文注释提示
|
||||
const chineseAnnotations: Record<string, ChineseCompletion[]> = {
|
||||
python: [
|
||||
{
|
||||
label: "print",
|
||||
detail: "打印输出",
|
||||
type: "function",
|
||||
info: "内置函数,将对象输出到标准输出,可用 sep 和 end 指定分隔符与结尾字符",
|
||||
boost: 100,
|
||||
apply: "print()",
|
||||
},
|
||||
{
|
||||
label: "input",
|
||||
detail: "读取输入",
|
||||
type: "function",
|
||||
info: "内置函数,读取一行输入并返回字符串,可传入提示信息",
|
||||
boost: 95,
|
||||
apply: "input()",
|
||||
},
|
||||
{
|
||||
label: "len",
|
||||
detail: "获取长度",
|
||||
type: "function",
|
||||
info: "返回对象的长度,常用于列表、字符串、字典等序列或集合类型",
|
||||
boost: 90,
|
||||
apply: "len()",
|
||||
},
|
||||
{
|
||||
label: "range",
|
||||
detail: "生成整数序列",
|
||||
type: "function",
|
||||
info: "返回不可变的整数序列,支持 start、stop、step,常用于 for 循环",
|
||||
boost: 85,
|
||||
apply: "range()",
|
||||
},
|
||||
{
|
||||
label: "enumerate",
|
||||
detail: "枚举索引与元素",
|
||||
type: "function",
|
||||
info: "遍历可迭代对象时同时得到索引和值,可通过 start 指定起始索引",
|
||||
boost: 82,
|
||||
apply: "enumerate()",
|
||||
},
|
||||
{
|
||||
label: "zip",
|
||||
detail: "并行遍历",
|
||||
type: "function",
|
||||
info: "将多个可迭代对象聚合为元组迭代器,长度以最短序列为准",
|
||||
boost: 80,
|
||||
apply: "zip()",
|
||||
},
|
||||
{
|
||||
label: "map",
|
||||
detail: "映射函数",
|
||||
type: "function",
|
||||
info: "对可迭代对象的每个元素应用函数,返回惰性迭代器",
|
||||
boost: 78,
|
||||
apply: "map()",
|
||||
},
|
||||
{
|
||||
label: "filter",
|
||||
detail: "过滤元素",
|
||||
type: "function",
|
||||
info: "保留函数返回真值的元素,返回惰性迭代器",
|
||||
boost: 76,
|
||||
apply: "filter()",
|
||||
},
|
||||
{
|
||||
label: "sorted",
|
||||
detail: "排序",
|
||||
type: "function",
|
||||
info: "返回排序后的新列表,支持 key 与 reverse 参数",
|
||||
boost: 74,
|
||||
apply: "sorted()",
|
||||
},
|
||||
{
|
||||
label: "sum",
|
||||
detail: "求和",
|
||||
type: "function",
|
||||
info: "对可迭代对象元素求和,可指定起始值",
|
||||
boost: 72,
|
||||
apply: "sum()",
|
||||
},
|
||||
{
|
||||
label: "open",
|
||||
detail: "文件读写",
|
||||
type: "function",
|
||||
info: "打开文件并返回文件对象,常与 with 语句搭配确保自动关闭",
|
||||
boost: 70,
|
||||
apply: "open()",
|
||||
},
|
||||
{
|
||||
label: "abs",
|
||||
detail: "绝对值",
|
||||
type: "function",
|
||||
info: "返回数字的绝对值",
|
||||
boost: 68,
|
||||
apply: "abs()",
|
||||
},
|
||||
{
|
||||
label: "round",
|
||||
detail: "四舍五入",
|
||||
type: "function",
|
||||
info: "按指定精度进行四舍五入,默认到整数",
|
||||
boost: 66,
|
||||
apply: "round()",
|
||||
},
|
||||
{
|
||||
label: "isinstance",
|
||||
detail: "类型检查",
|
||||
type: "function",
|
||||
info: "判断对象是否为某个类型或类型元组的实例",
|
||||
boost: 64,
|
||||
apply: "isinstance()",
|
||||
},
|
||||
{
|
||||
label: "type",
|
||||
detail: "获取类型",
|
||||
type: "function",
|
||||
info: "返回对象的类型,或在三个参数形式下动态创建类型",
|
||||
boost: 62,
|
||||
apply: "type()",
|
||||
},
|
||||
{
|
||||
label: "list",
|
||||
detail: "列表构造",
|
||||
type: "function",
|
||||
info: "将可迭代对象转换为列表,或创建空列表",
|
||||
boost: 60,
|
||||
apply: "list()",
|
||||
},
|
||||
{
|
||||
label: "dict",
|
||||
detail: "字典构造",
|
||||
type: "function",
|
||||
info: "根据映射或键值对序列创建字典",
|
||||
boost: 58,
|
||||
apply: "dict()",
|
||||
},
|
||||
{
|
||||
label: "set",
|
||||
detail: "集合构造",
|
||||
type: "function",
|
||||
info: "根据可迭代对象创建集合,自动去重",
|
||||
boost: 56,
|
||||
apply: "set()",
|
||||
},
|
||||
{
|
||||
label: "tuple",
|
||||
detail: "元组构造",
|
||||
type: "function",
|
||||
info: "将可迭代对象转换为元组,或创建空元组",
|
||||
boost: 54,
|
||||
apply: "tuple()",
|
||||
},
|
||||
{
|
||||
label: "int",
|
||||
detail: "转整数",
|
||||
type: "function",
|
||||
info: "将参数转换为整型,支持基数转换",
|
||||
boost: 74,
|
||||
apply: "int()",
|
||||
},
|
||||
{
|
||||
label: "float",
|
||||
detail: "转浮点数",
|
||||
type: "function",
|
||||
info: "将参数转换为浮点数,支持字符串与数字",
|
||||
boost: 72,
|
||||
apply: "float()",
|
||||
},
|
||||
{
|
||||
label: "str",
|
||||
detail: "转字符串",
|
||||
type: "function",
|
||||
info: "将对象转换为字符串表示,常用于输出",
|
||||
boost: 70,
|
||||
apply: "str()",
|
||||
},
|
||||
{
|
||||
label: "bool",
|
||||
detail: "转布尔值",
|
||||
type: "function",
|
||||
info: "根据真值测试转换为 True/False,空对象为 False",
|
||||
boost: 68,
|
||||
apply: "bool()",
|
||||
},
|
||||
{
|
||||
label: "def",
|
||||
detail: "定义函数",
|
||||
type: "keyword",
|
||||
info: "定义可复用的函数块,支持位置参数、关键字参数与默认值",
|
||||
boost: 52,
|
||||
},
|
||||
{
|
||||
label: "class",
|
||||
detail: "定义类",
|
||||
type: "keyword",
|
||||
info: "定义自定义类型,支持继承与魔术方法",
|
||||
boost: 50,
|
||||
},
|
||||
{
|
||||
label: "with",
|
||||
detail: "上下文管理",
|
||||
type: "keyword",
|
||||
info: "进入上下文管理器,自动处理资源的进入与退出",
|
||||
boost: 48,
|
||||
},
|
||||
{
|
||||
label: "try",
|
||||
detail: "异常捕获",
|
||||
type: "keyword",
|
||||
info: "开始异常处理块,与 except/finally/else 结合使用",
|
||||
boost: 46,
|
||||
},
|
||||
{
|
||||
label: "except",
|
||||
detail: "处理异常",
|
||||
type: "keyword",
|
||||
info: "捕获特定异常类型并处理,常配合 try 使用",
|
||||
boost: 44,
|
||||
},
|
||||
{
|
||||
label: "finally",
|
||||
detail: "清理收尾",
|
||||
type: "keyword",
|
||||
info: "无论是否发生异常都执行的收尾代码块",
|
||||
boost: 42,
|
||||
},
|
||||
{
|
||||
label: "import",
|
||||
detail: "导入模块",
|
||||
type: "keyword",
|
||||
info: "导入模块或包中的名称,可与 as 指定别名",
|
||||
boost: 40,
|
||||
},
|
||||
{
|
||||
label: "from",
|
||||
detail: "按需导入",
|
||||
type: "keyword",
|
||||
info: "从模块中按名称导入对象,可结合 import 与 as",
|
||||
boost: 38,
|
||||
},
|
||||
{
|
||||
label: "return",
|
||||
detail: "返回值",
|
||||
type: "keyword",
|
||||
info: "结束函数并返回值,未指定值时返回 None",
|
||||
boost: 36,
|
||||
},
|
||||
{
|
||||
label: "append",
|
||||
detail: "列表追加",
|
||||
type: "method",
|
||||
info: "在列表尾部添加新元素,等价于 list.append(value)",
|
||||
boost: 48,
|
||||
apply: "append()",
|
||||
},
|
||||
{
|
||||
label: "insert",
|
||||
detail: "列表插入",
|
||||
type: "method",
|
||||
info: "在指定位置插入元素,list.insert(index, value)",
|
||||
boost: 46,
|
||||
apply: "insert()",
|
||||
},
|
||||
{
|
||||
label: "remove",
|
||||
detail: "删除匹配值",
|
||||
type: "method",
|
||||
info: "删除列表中第一次出现的指定值,若不存在将抛出异常",
|
||||
boost: 44,
|
||||
apply: "remove()",
|
||||
},
|
||||
{
|
||||
label: "pop",
|
||||
detail: "弹出元素",
|
||||
type: "method",
|
||||
info: "移除并返回列表指定位置(默认尾部)的元素",
|
||||
boost: 42,
|
||||
apply: "pop()",
|
||||
},
|
||||
{
|
||||
label: "count",
|
||||
detail: "统计次数",
|
||||
type: "method",
|
||||
info: "返回某个对象在列表中出现的次数",
|
||||
boost: 40,
|
||||
apply: "count()",
|
||||
},
|
||||
{
|
||||
label: "reverse",
|
||||
detail: "反转列表",
|
||||
type: "method",
|
||||
info: "原地反转列表中元素的顺序",
|
||||
boost: 38,
|
||||
apply: "reverse()",
|
||||
},
|
||||
{
|
||||
label: "sort",
|
||||
detail: "列表排序",
|
||||
type: "method",
|
||||
info: "对列表进行原地排序,可指定 key 与 reverse",
|
||||
boost: 36,
|
||||
apply: "sort()",
|
||||
},
|
||||
{
|
||||
label: "add",
|
||||
detail: "集合添加",
|
||||
type: "method",
|
||||
info: "向集合添加单个元素,若已存在则忽略",
|
||||
boost: 50,
|
||||
apply: "add()",
|
||||
},
|
||||
{
|
||||
label: "clear",
|
||||
detail: "清空集合",
|
||||
type: "method",
|
||||
info: "移除集合中所有元素,变为空集",
|
||||
boost: 48,
|
||||
apply: "clear()",
|
||||
},
|
||||
{
|
||||
label: "keys",
|
||||
detail: "字典键",
|
||||
type: "method",
|
||||
info: "返回字典键的可迭代视图",
|
||||
boost: 42,
|
||||
apply: "keys()",
|
||||
},
|
||||
{
|
||||
label: "values",
|
||||
detail: "字典值",
|
||||
type: "method",
|
||||
info: "返回字典值的可迭代视图",
|
||||
boost: 40,
|
||||
apply: "values()",
|
||||
},
|
||||
{
|
||||
label: "split",
|
||||
detail: "字符串切割",
|
||||
type: "method",
|
||||
info: "按分隔符切分字符串,返回列表,默认按空白字符",
|
||||
boost: 52,
|
||||
apply: "split()",
|
||||
},
|
||||
{
|
||||
label: "replace",
|
||||
detail: "字符串替换",
|
||||
type: "method",
|
||||
info: "将字符串中的子串替换为新内容,可限定次数",
|
||||
boost: 50,
|
||||
apply: "replace()",
|
||||
},
|
||||
{
|
||||
label: "format",
|
||||
detail: "格式化字符串",
|
||||
type: "method",
|
||||
info: "使用占位符或命名参数进行字符串格式化",
|
||||
boost: 48,
|
||||
apply: "format()",
|
||||
},
|
||||
{
|
||||
label: "strip",
|
||||
detail: "去首尾指定字符",
|
||||
type: "method",
|
||||
info: "移除字符串首尾指定字符,默认移除空白",
|
||||
boost: 46,
|
||||
apply: "strip()",
|
||||
},
|
||||
{
|
||||
label: "lower",
|
||||
detail: "转小写",
|
||||
type: "method",
|
||||
info: "将字符串中的字母转换为小写形式",
|
||||
boost: 44,
|
||||
apply: "lower()",
|
||||
},
|
||||
{
|
||||
label: "upper",
|
||||
detail: "转大写",
|
||||
type: "method",
|
||||
info: "将字符串中的字母转换为大写形式",
|
||||
boost: 42,
|
||||
apply: "upper()",
|
||||
},
|
||||
{
|
||||
label: "swapcase",
|
||||
detail: "大小写互换",
|
||||
type: "method",
|
||||
info: "将字符串中的大小写字母互换",
|
||||
boost: 40,
|
||||
apply: "swapcase()",
|
||||
},
|
||||
{
|
||||
label: "find",
|
||||
detail: "查找子串位置",
|
||||
type: "method",
|
||||
info: "返回子串首次出现的索引,未找到返回 -1",
|
||||
boost: 38,
|
||||
apply: "find()",
|
||||
},
|
||||
{
|
||||
label: "index",
|
||||
detail: "查找子串索引",
|
||||
type: "method",
|
||||
info: "返回子串首次出现的索引,未找到抛出异常",
|
||||
boost: 36,
|
||||
apply: "index()",
|
||||
},
|
||||
{
|
||||
label: "startswith",
|
||||
detail: "前缀判断",
|
||||
type: "method",
|
||||
info: "判断字符串是否以指定前缀开头,可指定范围",
|
||||
boost: 34,
|
||||
apply: "startswith()",
|
||||
},
|
||||
{
|
||||
label: "endswith",
|
||||
detail: "后缀判断",
|
||||
type: "method",
|
||||
info: "判断字符串是否以指定后缀结尾,可指定范围",
|
||||
boost: 32,
|
||||
apply: "endswith()",
|
||||
},
|
||||
{
|
||||
label: "isalnum",
|
||||
detail: "是否字母数字",
|
||||
type: "method",
|
||||
info: "检测字符串是否只由字母和数字组成",
|
||||
boost: 30,
|
||||
apply: "isalnum()",
|
||||
},
|
||||
{
|
||||
label: "isalpha",
|
||||
detail: "是否字母",
|
||||
type: "method",
|
||||
info: "检测字符串是否只由字母组成",
|
||||
boost: 28,
|
||||
apply: "isalpha()",
|
||||
},
|
||||
{
|
||||
label: "isdigit",
|
||||
detail: "是否数字",
|
||||
type: "method",
|
||||
info: "检测字符串是否只由数字组成",
|
||||
boost: 26,
|
||||
apply: "isdigit()",
|
||||
},
|
||||
{
|
||||
label: "islower",
|
||||
detail: "是否全小写",
|
||||
type: "method",
|
||||
info: "检测字符串是否全部由小写字母组成且至少有一个字母",
|
||||
boost: 24,
|
||||
apply: "islower()",
|
||||
},
|
||||
{
|
||||
label: "isupper",
|
||||
detail: "是否全大写",
|
||||
type: "method",
|
||||
info: "检测字符串中所有字母是否都为大写且至少有一个字母",
|
||||
boost: 22,
|
||||
apply: "isupper()",
|
||||
},
|
||||
],
|
||||
c: [
|
||||
{
|
||||
label: "printf",
|
||||
detail: "格式化输出",
|
||||
type: "function",
|
||||
info: "标准输出函数,格式化打印字符串,常配合 %d/%s 等占位符",
|
||||
boost: 90,
|
||||
apply: "printf();",
|
||||
},
|
||||
{
|
||||
label: "scanf",
|
||||
detail: "格式化输入",
|
||||
type: "function",
|
||||
info: "标准输入函数,按格式读取数据,使用地址符 & 接收变量",
|
||||
boost: 88,
|
||||
apply: "scanf();",
|
||||
},
|
||||
{
|
||||
label: "puts",
|
||||
detail: "输出字符串",
|
||||
type: "function",
|
||||
info: "输出以 \\0 结尾的字符串并自动换行,比 printf 简洁",
|
||||
boost: 84,
|
||||
apply: "puts();",
|
||||
},
|
||||
{
|
||||
label: "gets",
|
||||
detail: "读取字符串",
|
||||
type: "function",
|
||||
info: "读取一行字符串到缓冲区(不安全,建议使用 fgets)",
|
||||
boost: 60,
|
||||
apply: "gets();",
|
||||
},
|
||||
{
|
||||
label: "fgets",
|
||||
detail: "安全读行",
|
||||
type: "function",
|
||||
info: "从文件流读取一行到缓冲区,限制读取长度,避免溢出",
|
||||
boost: 82,
|
||||
apply: "fgets();",
|
||||
},
|
||||
{
|
||||
label: "memset",
|
||||
detail: "内存填充",
|
||||
type: "function",
|
||||
info: "将一段内存按字节填充为指定值,常用于初始化数组/结构体",
|
||||
boost: 80,
|
||||
apply: "memset();",
|
||||
},
|
||||
{
|
||||
label: "memcpy",
|
||||
detail: "内存拷贝",
|
||||
type: "function",
|
||||
info: "从源地址复制指定字节到目标地址,注意避免重叠",
|
||||
boost: 78,
|
||||
apply: "memcpy();",
|
||||
},
|
||||
{
|
||||
label: "strlen",
|
||||
detail: "字符串长度",
|
||||
type: "function",
|
||||
info: "计算以 \\0 结尾的字符串长度(不含终止符)",
|
||||
boost: 76,
|
||||
apply: "strlen();",
|
||||
},
|
||||
{
|
||||
label: "strcmp",
|
||||
detail: "字符串比较",
|
||||
type: "function",
|
||||
info: "按字典序比较两个字符串,相等返回 0,小于返回负数",
|
||||
boost: 74,
|
||||
apply: "strcmp();",
|
||||
},
|
||||
{
|
||||
label: "strcpy",
|
||||
detail: "字符串拷贝",
|
||||
type: "function",
|
||||
info: "将源字符串复制到目标(包含终止符),目标需有足够空间",
|
||||
boost: 72,
|
||||
apply: "strcpy();",
|
||||
},
|
||||
{
|
||||
label: "int main",
|
||||
detail: "程序入口",
|
||||
type: "keyword",
|
||||
info: "C 程序入口函数,通常返回 0 表示正常退出",
|
||||
boost: 70,
|
||||
},
|
||||
{
|
||||
label: "for",
|
||||
detail: "循环语句",
|
||||
type: "keyword",
|
||||
info: "for (init; condition; step) 结构,用于固定次数循环",
|
||||
boost: 68,
|
||||
},
|
||||
{
|
||||
label: "while",
|
||||
detail: "条件循环",
|
||||
type: "keyword",
|
||||
info: "while (condition) 循环,条件为真时重复执行",
|
||||
boost: 66,
|
||||
},
|
||||
{
|
||||
label: "if",
|
||||
detail: "条件判断",
|
||||
type: "keyword",
|
||||
info: "if (condition) 分支,可搭配 else / else if",
|
||||
boost: 64,
|
||||
},
|
||||
{
|
||||
label: "struct",
|
||||
detail: "结构体定义",
|
||||
type: "keyword",
|
||||
info: "定义自定义数据结构,可组合不同类型的成员",
|
||||
boost: 62,
|
||||
},
|
||||
{
|
||||
label: "typedef",
|
||||
detail: "类型别名",
|
||||
type: "keyword",
|
||||
info: "为已有类型定义别名,提升可读性",
|
||||
boost: 60,
|
||||
},
|
||||
{
|
||||
label: "const",
|
||||
detail: "只读限定",
|
||||
type: "keyword",
|
||||
info: "声明常量或只读指针,防止意外修改",
|
||||
boost: 58,
|
||||
},
|
||||
{
|
||||
label: "return",
|
||||
detail: "返回值",
|
||||
type: "keyword",
|
||||
info: "结束函数并返回值,main 返回 0 表示成功",
|
||||
boost: 56,
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
export function enhanceCompletion(language: LANGUAGE): CompletionSource {
|
||||
return async function (
|
||||
context: CompletionContext,
|
||||
): Promise<CompletionResult | null> {
|
||||
const word = context.matchBefore(/\w+/)
|
||||
if (!word) return null
|
||||
|
||||
const trulyLanguage = language.startsWith("Python") ? "python" : "c"
|
||||
const completions: Completion[] = (
|
||||
chineseAnnotations[trulyLanguage] || []
|
||||
).map((completion) => {
|
||||
const insertText =
|
||||
typeof completion.apply === "string"
|
||||
? completion.apply
|
||||
: completion.label
|
||||
const cursorOffset = insertText.includes("(")
|
||||
? insertText.indexOf("(") + 1
|
||||
: insertText.length
|
||||
|
||||
if (
|
||||
(completion.type === "function" || completion.type === "method") &&
|
||||
insertText.includes(")")
|
||||
) {
|
||||
return {
|
||||
...completion,
|
||||
apply: (
|
||||
view: EditorView,
|
||||
_c: Completion,
|
||||
from: number,
|
||||
to: number,
|
||||
) => {
|
||||
view.dispatch({
|
||||
changes: { from, to, insert: insertText },
|
||||
selection: { anchor: from + cursorOffset },
|
||||
})
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
return completion
|
||||
})
|
||||
|
||||
return {
|
||||
from: word.from,
|
||||
options: completions,
|
||||
validFor: /^\w+$/,
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user