惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

GbyAI
GbyAI
N
News and Events Feed by Topic
D
DataBreaches.Net
MongoDB | Blog
MongoDB | Blog
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
Engineering at Meta
Engineering at Meta
T
Tailwind CSS Blog
博客园_首页
Microsoft Azure Blog
Microsoft Azure Blog
Y
Y Combinator Blog
博客园 - Franky
Hugging Face - Blog
Hugging Face - Blog
月光博客
月光博客
A
About on SuperTechFans
I
InfoQ
S
Securelist
Last Week in AI
Last Week in AI
S
Schneier on Security
C
CXSECURITY Database RSS Feed - CXSecurity.com
Hacker News: Ask HN
Hacker News: Ask HN
Schneier on Security
Schneier on Security
Know Your Adversary
Know Your Adversary
腾讯CDC
大猫的无限游戏
大猫的无限游戏
S
Security @ Cisco Blogs
博客园 - 三生石上(FineUI控件)
Simon Willison's Weblog
Simon Willison's Weblog
D
Darknet – Hacking Tools, Hacker News & Cyber Security
T
Tor Project blog
美团技术团队
aimingoo的专栏
aimingoo的专栏
G
Google Developers Blog
罗磊的独立博客
Vercel News
Vercel News
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
The Cloudflare Blog
S
Secure Thoughts
www.infosecurity-magazine.com
www.infosecurity-magazine.com
Latest news
Latest news
Recent Announcements
Recent Announcements
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
L
LINUX DO - 热门话题
Security Latest
Security Latest
TaoSecurity Blog
TaoSecurity Blog
Cyberwarzone
Cyberwarzone
有赞技术团队
有赞技术团队

姓王者的博客

Linux用户Secure Boot自主维护指南 | 姓王者的博客 MAD Bugs 已经开始——关于信息安全的军备竞赛 | 姓王者的博客 解决钉钉Dingtalk无法在Linux新版内核上启动问题-修复可执行栈错误 | 姓王者的博客 突发:GitHub 正遭受大规模 Issue 赌博广告轰炸 | 姓王者的博客 Ubuntu26.04-beta体验:坚毅浣熊! | 姓王者的博客 fakeclaw装作龙虾发贴吧 | 姓王者的博客 找回12年前的QQ记忆 | 姓王者的博客 在Linux上玩Flash网页游戏-洛克王国 | 姓王者的博客 Copilot将使用交互数据来训练 | 姓王者的博客 重要通知-请更新我的GPG公钥 | 姓王者的博客 为了自由Android | 姓王者的博客 GPL"2,3"事 | 姓王者的博客 短文-对VitePlus的一点🤏小贡献 | 姓王者的博客 Bing收录没了?亲测有效的快速恢复指南 | 姓王者的博客 解决桌面设备二维码快速识别的工具-ClipQR | 姓王者的博客 解决 Nautilus 自定义终端插件安装依赖问题 | 姓王者的博客 OpenClaw 该熄火了 | 姓王者的博客 Vite8 - 统一的基建开始 | 姓王者的博客 Astro 6 推出啦 | 姓王者的博客 ubuntu的openvpn异常暂停推送更新 | 姓王者的博客 Ubuntu 24.04 安装 Win10 虚拟机 | 姓王者的博客 ESA-后记:热爱阿里云 | 姓王者的博客 Moonbit 0.8.0 重大发布,我也要改一下我的包 | 姓王者的博客 ESA Pages 边缘开发大赛获奖 | 姓王者的博客 Astro: 优化katex,mermaid和灯箱使用 | 姓王者的博客 从edgeone迁移到esa | 姓王者的博客 出租人类:AI时代的荒诞与真实 | 姓王者的博客 Astro 5.17构建性能优化实践:从18s到13s | 姓王者的博客 Moonbit License Checker 开发使用 | 姓王者的博客 Stalux Astro博客主题自荐 | 姓王者的博客 把Hexo永久链接迁移到Astro | 姓王者的博客 再见👋 LeanCloud | 姓王者的博客 2025年终总结 | 姓王者的博客 许可合规-fancybox | 姓王者的博客 博客主题的软著下来了 | 姓王者的博客 友链图谱 - 汇聚千丝万缕的联系 | 姓王者的博客 chen-er 专为Chen式ER图打造的npm包 | 姓王者的博客 为什么我推荐你使用GPG来加密你的邮件 | 姓王者的博客 2025第三方客户端登录东北大学邮箱 | 姓王者的博客 好久没更新了,过去与未来 | 姓王者的博客 1024 重要的日子 | 姓王者的博客 再也不见Windows10 | 姓王者的博客 偷梁换柱,解决Ubuntu24.04安装Packet Tracer缺失依赖问题 | 姓王者的博客 中秋-来试试Moonbit吧 | 姓王者的博客 Obsidian使用体验 | 姓王者的博客 猪猪侠·一只老猪的逆袭 | 姓王者的博客 国庆日纪念 | 姓王者的博客 GNU 42周年,AI时代的自由精神 | 姓王者的博客 解决Linux上启动游戏总是默认English的情况 | 姓王者的博客 7x24:运维使命 | 姓王者的博客 Tauri2.x实现系统菜单导航Vue路由 | 姓王者的博客 计算机图形学-基本图形生成算法 | 姓王者的博客 数据库原理-关系数据 | 姓王者的博客 数据库原理-设计技巧 | 姓王者的博客 数据库原理E-R模型 | 姓王者的博客 旧忆 - 我曾玩过的游戏 | 姓王者的博客 再谈自由软件 | 姓王者的博客 可能解决Tauri多窗口应用阻塞问题 | 姓王者的博客 Xingwangzhe! Z-Library We miss you and we need your help | 姓王者的博客 计算机组成原理第二章 - 定点数与浮点数 | 姓王者的博客 计算机组成原理第一章 | 姓王者的博客 不小心写死循环窗口弹出了 | 姓王者的博客 美化Grub界面 | 姓王者的博客 计算机图形学-图形的表示与数据结构 | 姓王者的博客 计算机图形学绪论 | 姓王者的博客 为什么说,大学教育与社会脱节 | 姓王者的博客 VSCode Remote 远程连接服务器记录 | 姓王者的博客 解决Tauri2.x拖拽事件问题 | 姓王者的博客 新学期第一课《计算机图形学》报告 | 姓王者的博客 Tauri在GNOME46+上通知无效的临时解决方法 | 姓王者的博客 窃文者:未经授权转载我文章 | 姓王者的博客 GPG公钥分享文化 | 姓王者的博客 解决在ubuntu上,打包vscode插件问题 | 姓王者的博客 伪造squaremap的玩家显示 | 姓王者的博客 爆,沉浸式翻译泄露敏感信息 | 姓王者的博客 读书:《Free as in Freedom》——若为自由故 | 姓王者的博客 首页文章列表懒加载优化 | 姓王者的博客 Ubuntu 24.04 安装 Vivado 2018.3 | 姓王者的博客 腾讯Edgeone免费版体验 | 姓王者的博客 在 Ubuntu 上实现 Thetis FIDO U2F 密钥登录 | 姓王者的博客 Thetis物理密钥,为什么我们应该使用物理密钥 | 姓王者的博客 高考生过来看!教你精准转换录取位次! | 姓王者的博客 ubuntu无法访问windows磁盘问题 | 姓王者的博客 收信有感,防范钓鱼邮件 | 姓王者的博客 自由不止软件-记录一次zlib上传书籍 | 姓王者的博客 时隔两年,通关夺命邮差2 | 姓王者的博客 博客一周年了,竟然坚持了下来 | 姓王者的博客 Minecraft大电影:不建不散! | 姓王者的博客 是时候了解docker了! | 姓王者的博客 编译原理:LL(1)文法 | 姓王者的博客 编译原理:文法转换 | 姓王者的博客 离散数学:子群的陪集及拉格朗日定理 | 姓王者的博客 离散数学:半群,独异点 | 姓王者的博客 《人工智能生成合成内容标识办法》与个人博客--我们应该做什么? | 姓王者的博客 通识学习:形式语言与自动机,布尔代数与数进制 | 姓王者的博客 Webmapview:一个我的世界内置网页地图浏览Fabric模组 | 姓王者的博客 海岛机器人农场试玩 | 姓王者的博客 正则表达式学习 | 姓王者的博客 抓取个人博客文章目录到github主页 | 姓王者的博客 制作github贪吃蛇贡献图 | 姓王者的博客
vscode插件开发心得:RustedWarfareModSupport | 姓王者的博客
作者:xingwangzhe · 2025-03-09 · via 姓王者的博客

🕒 阅读时间:6 分钟 📝 字数:2067 👀 阅读量: Loading...

我并没有重复造轮子,因为根本就没轮子 :(

时隔多年,我还是回到了我最喜欢的RTS游戏:Steam 上的 Rusted Warfare - RTS

这是我的插件地址 RustedWarfareModSupport - Visual Studio Marketplace

开发过程

起步,想使用yo code?

这脚手架有点太简陋了,所以我直接git clone 一下微软官方的lsp-sample插件来改一改,顺带学习一下lsp

:::tip 什么是lsp? LSP 通常指 ​ 语言服务器协议(Language Server Protocol) ​,是一种用于在集成开发环境(IDE)和语言服务器之间通信的开放标准。它通过标准化的请求和响应机制,让开发者工具(如 VS Code、Emacs)与语言支持服务(如代码补全、语法检查、重构工具)解耦,从而实现跨平台、跨编辑器的语言功能支持。 :::

果然官方的就是好啊,注释什么的都一应俱全,但为什么不把这个作为yo code的选项之一呢?我还是不太明白 不过,既然有了模板,那就开始改造!

代码表,数据从哪来?

倒是能看到许多excal表格,但这不是一个对js/ts友好的数据结构,找了一下,看到了一个插件仓库

Blackburn507/RWini_Plugin: A plugin improves coding for MODs of a RTS game Rusted Warfare.

但是我好奇为什么没在vscode插件市场上看到,看了一下是MIT协议,里面的/db/文件夹有三个数据json,看起来不方便,于是我开始拆分

拆分dataVAseKey.json

const fs = require("fs");

const path = require("path");

// 读取 dataBaseKey.json 文件内容

const dataBaseKeyJson = fs.readFileSync("dataBaseKey.json", "utf-8");

const dataBaseKey = JSON.parse(dataBaseKeyJson);

// 定义 CompletionItemKind 常量

const CompletionItemKind = {

Text: 1,

};

// 定义一个函数来生成 CompletionItem[] 数据

function generateCompletionItems(section, items) {

return Object.entries(items).map(

([label, [type, descriptionSection, detail, documentation]], index) => ({

label,

insertText: `${label}:`,

labelDetails: { description: `[${section}]` },

kind: CompletionItemKind.Text,

detail,

documentation,

type,

}),

);

}

// 遍历 dataBaseKey 中的每个 section

for (const [section, items] of Object.entries(dataBaseKey)) {

const completionItems = generateCompletionItems(section, items);

// 生成文件内容

const importStatement = `import { CompletionItem, CompletionItemKind } from 'vscode-languageserver';`;

const exportStatement = `export const ${section.toUpperCase()}: CompletionItem[] = [`;

const itemsContent = completionItems

.map(

(item) => ` {

label: '${item.label || ""}',

insertText: '${item.insertText || ""}',

labelDetails: { detail :' ${(item.type || "") + " " + (item.detail || "").replace(/'/g, "\\'")}', description: '[${section}]' },

kind: CompletionItemKind.Text,

detail :'${(item.detail || "").replace(/'/g, "\\'")}',

documentation :'${(item.documentation || "").replace(/'/g, "\\'")}',

data: '${(item.type || "").replace(/'/g, "\\'")}'

},`,

)

.join("\n");

const closingBracket = `];`;

const fileContent = `${importStatement}\n${exportStatement}\n${itemsContent}\n${closingBracket}`;

// 写入文件

const filePath = path.join(__dirname, `${section}.ts`);

fs.writeFileSync(filePath, fileContent, "utf-8");

}

console.log("文件生成完成");

拆分dataBaseValue.json

const fs = require("fs");

const path = require("path");

// 读取 dataBaseValue.json 文件内容

const dataBaseValueJson = fs.readFileSync("dataBaseValue.json", "utf-8");

const dataBaseValue = JSON.parse(dataBaseValueJson);

// 定义 CompletionItemKind 常量

const CompletionItemKind = {

Text: 1,

};

// 定义一个函数来生成 CompletionItem[] 数据

function generateCompletionItems(section, items) {

return Object.entries(items).map(([label, [type, description, detail]], index) => ({

label,

insertText: `${label}`,

labelDetails: { description: `[${section}]` },

kind: CompletionItemKind.Text,

detail,

documentation: description,

type,

}));

}

// 遍历 dataBaseValue 中的每个 section

for (const [section, items] of Object.entries(dataBaseValue)) {

const completionItems = generateCompletionItems(section, items);

// 生成文件内容

const importStatement = `import { CompletionItem, CompletionItemKind } from 'vscode-languageserver';`;

const exportStatement = `export const ${section.toUpperCase()}: CompletionItem[] = [`;

const itemsContent = completionItems

.map(

(item) => ` {

label: '${item.label || ""}',

insertText: '${item.insertText || ""}',

labelDetails: { detail :' ${(item.type || "") + " " + (item.detail || "").replace(/'/g, "\\'")}', description: '[${section}]' },

kind: CompletionItemKind.Text,

detail :'${(item.detail || "").replace(/'/g, "\\'")}',

documentation :'${(item.documentation || "").replace(/'/g, "\\'")}',

data: '${(item.type || "").replace(/'/g, "\\'")}'

},`,

)

.join("\n");

const closingBracket = `];`;

const fileContent = `${importStatement}\n${exportStatement}\n${itemsContent}\n${closingBracket}`;

// 写入文件

const filePath = path.join(__dirname, `${section}.ts`);

fs.writeFileSync(filePath, fileContent, "utf-8");

}

console.log("文件生成完成");

现在获得了这些文件,对于这些文件,应该定义一下良好的数据结构

定义Completionitem[]

定义这个对象数组,方便补全,下面是其中的一个例子

import { CompletionItem, CompletionItemKind } from 'vscode-languageserver';

export const ACTION: CompletionItem[] = [

{

label: 'text',

insertText: 'text:',

labelDetails: { detail :' file-text 文本', description: '[action]' },

kind: CompletionItemKind.Text,

detail :'文本',

documentation :'界面中显示的文字',

data: 'file-text'

},

{

label: 'textPostFix',

insertText: 'textPostFix:',

labelDetails: { detail :' file-text 文本动态更改', description: '[action]' },

kind: CompletionItemKind.Text,

detail :'文本动态更改',

documentation :'显示为后缀的文本,与textAddUnitName一起用于创建文本UI',

data: 'file-text'

},

...

]

合在一起,方便调用

import { CompletionItem } from 'vscode-languageserver';

import { ACTION } from './action';

import { AI } from './ai';

import { ANIMATION } from './animation';

import { ATTACHMENT } from './attachment';

import { ATTACK } from './attack';

import { CANBUILD } from './canBuild';

import { CORE } from './core';

import { EFFECT } from './effect';

import { GRAPHICS } from './graphics';

import { LEG } from './leg';

import { MOVEMENT } from './movement';

import { PLACEMENTRULE } from './placementRule';

import { PROJECTILE } from './projectile';

import { RESOURCE } from './resource';

import { TURRET } from './turret';

export const ALLSECTIONS: Record<string, CompletionItem[]> ={

ACTION,

AI,

ANIMATION,

ATTACHMENT,

ATTACK,

CANBUILD,

CORE,

EFFECT,

GRAPHICS,

LEG,

MOVEMENT,

PLACEMENTRULE,

PROJECTILE,

RESOURCE,

TURRET

};

关键在于connection.onCompletion函数的调用

connection.onCompletion(

(_textDocumentPosition: TextDocumentPositionParams): CompletionItem[] => {

const document = documents.get(_textDocumentPosition.textDocument.uri);

if (!document) {return [];}

// 获取当前行号

const cursorLine = _textDocumentPosition.position.line;

// 从当前行向上查找最近的一个节定义

let currentSectionName = null;

for (let i = cursorLine; i >= 0; i--) {

const lineStart = document.positionAt(document.offsetAt({ line: i, character: 0 }));

const lineEnd = document.positionAt(document.offsetAt({ line: i + 1, character: 0 }) - 1);

const lineContent = document.getText({ start: lineStart, end: lineEnd }).trim();

const sectionMatch = lineContent.match(/^\[(.+)\]$/);

if (sectionMatch) {

currentSectionName = sectionMatch[1];

break;

}

}

const cursorCharacter = _textDocumentPosition.position.character;

// 获取当前行内容

const lineStart = document.positionAt(document.offsetAt({ line: cursorLine, character: 0 }));

const lineEnd = document.positionAt(document.offsetAt({ line: cursorLine + 1, character: 0 }) - 1);

const currentLineContent = document.getText({ start: lineStart, end: lineEnd });

// 判断光标是否在 [] 内

const openBracketIndex = currentLineContent.lastIndexOf('[', cursorCharacter);

const closeBracketIndex = currentLineContent.indexOf(']', cursorCharacter);

const isInsideBrackets = openBracketIndex !== -1 && (closeBracketIndex === -1 || openBracketIndex > closeBracketIndex);

if (isInsideBrackets) {

return SECTIONSNAME;

}

// 判断光标是否在 :后

const colonIndex = currentLineContent.indexOf(':');

const pointIndex = currentLineContent.indexOf('.');

if (!currentSectionName) {

return [];

}

else if((colonIndex!==-1)&&(cursorCharacter>colonIndex)){

if((pointIndex!==-1)&&(cursorCharacter==(pointIndex+1))){

return ALLVALUES.UNITPROPERTY;

}

const key = currentLineContent.substring(0, colonIndex).trim();

let sectionName=currentSectionName;

sectionName=sectionName.toUpperCase();

const sectionConfig = ALLSECTIONS[sectionName];

let keyConfig = null;

if (sectionConfig) {

for (const item of sectionConfig) {

if (item.label === key) {

keyConfig = item;

break;

}

}

}

if (keyConfig) {

const data = keyConfig.data;

if (data.includes('bool')) {

return ALLVALUES.BOOL;

} else if (data.includes('logicBoolean')) {

return [...ALLVALUES.LOGICBBOOLEAN, ...ALLVALUES.BOOL,...ALLVALUES.FUNCTION,...ALLVALUES.UNITREF];

} else if (key.includes('spawnUnits')){

return [...ALLVALUES.LOGICBBOOLEAN,...ALLVALUES.BOOL,...ALLVALUES.FUNCTION,...ALLVALUES.UNITREF,...ALLVALUES.SPAWNUNIT];

} else if (data.includes('projectileRef')){

return [...ALLVALUES.PROJECTILE];

} else if (data.includes('event')){

return [...ALLVALUES.EVENTS];

}

}

return [];;

}

else {

// 根据节名返回相应的补全项

switch (currentSectionName) {

case 'core':

return ALLSECTIONS.CORE;

case 'graphics':

return ALLSECTIONS.GRAPHICS;

case 'attack':

return ALLSECTIONS.ATTACK;

case 'movement':

return ALLSECTIONS.MOVEMENT;

case 'ai':

return ALLSECTIONS.AI;

default:

if (currentSectionName.startsWith('canBuild_')) {

return ALLSECTIONS.CANBUILD;

} else if (currentSectionName.startsWith('turret_')) {

return ALLSECTIONS.TURRET;

} else if (currentSectionName.startsWith('projectile_')){

return ALLSECTIONS.PROJECTILE;

} else if (currentSectionName.startsWith('arm_')){

return ALLSECTIONS.ARM;

} else if (currentSectionName.startsWith('leg_')){

return ALLSECTIONS.LEG;

} else if (currentSectionName.startsWith('attachment_')){

return ALLSECTIONS.ATTACHMENT;

} else if (currentSectionName.startsWith('effect_')){

return ALLSECTIONS.EFFECT;

} else if (currentSectionName.startsWith('animation_')){

return ALLSECTIONS.ANIMATION;

} else if (currentSectionName.startsWith('action_')){

return ALLSECTIONS.ACTION;

} else if (currentSectionName.startsWith('hiddenAction_')){

return ALLSECTIONS.ACTION;

} else if (currentSectionName.startsWith('placementRule_')){

return ALLSECTIONS.PLACEMENTRULE;

} else if (currentSectionName.startsWith('resource_')){

return ALLSECTIONS.RESOURCE;

} else if (currentSectionName.startsWith('global_resource_')){

return ALLSECTIONS.RESOURCE;

} else if (currentSectionName.startsWith('decal_')){

return ALLSECTIONS.DECAL;

} else if (currentSectionName.startsWith('commnet_')){

return [];

} else if (currentSectionName.startsWith('template_')){

return ALLSECTIONS.TEMPLATE;

}

return [];

}

}

return [];

});

太长了不爱看?让我们分开来解析一下!

代码功能概述

这段代码是一个用于处理代码补全的函数,通过监听 connection 对象的 onCompletion 事件,根据用户在文本编辑器中的光标位置和上下文信息,提供相应的代码补全项。具体来说,它会根据当前所在的节(section)、光标位置是否在方括号内或冒号之后等条件,返回不同的补全项数组。

代码详细分析

1. 事件监听与回调函数

javascript

connection.onCompletion(

(_textDocumentPosition: TextDocumentPositionParams): CompletionItem[] => {

// 代码主体

}

);

  • connection.onCompletion 是一个事件监听器,用于监听代码补全请求事件。
  • 回调函数接收一个 TextDocumentPositionParams 类型的参数 _textDocumentPosition,该参数包含了当前文本文件的位置信息。
  • 回调函数返回一个 CompletionItem 类型的数组,用于提供代码补全项。

2. 获取当前文档

javascript

const document = documents.get(_textDocumentPosition.textDocument.uri);

if (!document) {

return [];

}

  • 通过 documents.get 方法根据当前文本文件的 URI 获取对应的文档对象。
  • 如果文档对象不存在,则返回一个空数组,表示没有补全项。

3. 获取当前行号

javascript

const cursorLine = _textDocumentPosition.position.line;

  • _textDocumentPosition 参数中获取当前光标的行号。

4. 查找最近的节定义

javascript

let currentSectionName = null;

for (let i = cursorLine; i >= 0; i--) {

const lineStart = document.positionAt(document.offsetAt({ line: i, character: 0 }));

const lineEnd = document.positionAt(document.offsetAt({ line: i + 1, character: 0 }) - 1);

const lineContent = document.getText({ start: lineStart, end: lineEnd }).trim();

const sectionMatch = lineContent.match(/^\[(.+)\]$/);

if (sectionMatch) {

currentSectionName = sectionMatch[1];

break;

}

}

  • 从当前行开始向上遍历,查找最近的一个节定义(以 [节名] 形式表示)。
  • 使用正则表达式 /^\[(.+)\]$/ 匹配节定义,如果匹配成功,则将节名存储在 currentSectionName 变量中,并跳出循环。

5. 获取当前光标位置和行内容

javascript

const cursorCharacter = _textDocumentPosition.position.character;

const lineStart = document.positionAt(document.offsetAt({ line: cursorLine, character: 0 }));

const lineEnd = document.positionAt(document.offsetAt({ line: cursorLine + 1, character: 0 }) - 1);

const currentLineContent = document.getText({ start: lineStart, end: lineEnd });

  • 获取当前光标的列号 cursorCharacter
  • 获取当前行的起始位置和结束位置,并提取当前行的内容 currentLineContent

6. 判断光标是否在方括号内

javascript

const openBracketIndex = currentLineContent.lastIndexOf("[", cursorCharacter);

const closeBracketIndex = currentLineContent.indexOf("]", cursorCharacter);

const isInsideBrackets =

openBracketIndex !== -1 && (closeBracketIndex === -1 || openBracketIndex > closeBracketIndex);

if (isInsideBrackets) {

return SECTIONSNAME;

}

  • 使用 lastIndexOfindexOf 方法分别查找当前行中最后一个左方括号和第一个右方括号的位置。
  • 根据左方括号和右方括号的位置判断光标是否在方括号内。
  • 如果光标在方括号内,则返回 SECTIONSNAME 数组作为补全项。

7. 判断光标是否在冒号之后

javascript

const colonIndex = currentLineContent.indexOf(":");

const pointIndex = currentLineContent.indexOf(".");

if (!currentSectionName) {

return [];

} else if (colonIndex !== -1 && cursorCharacter > colonIndex) {

if (pointIndex !== -1 && cursorCharacter == pointIndex + 1) {

return ALLVALUES.UNITPROPERTY;

}

const key = currentLineContent.substring(0, colonIndex).trim();

let sectionName = currentSectionName;

sectionName = sectionName.toUpperCase();

const sectionConfig = ALLSECTIONS[sectionName];

let keyConfig = null;

if (sectionConfig) {

for (const item of sectionConfig) {

if (item.label === key) {

keyConfig = item;

break;

}

}

}

if (keyConfig) {

const data = keyConfig.data;

if (data.includes("bool")) {

return ALLVALUES.BOOL;

} else if (data.includes("logicBoolean")) {

return [

...ALLVALUES.LOGICBBOOLEAN,

...ALLVALUES.BOOL,

...ALLVALUES.FUNCTION,

...ALLVALUES.UNITREF,

];

} else if (key.includes("spawnUnits")) {

return [

...ALLVALUES.LOGICBBOOLEAN,

...ALLVALUES.BOOL,

...ALLVALUES.FUNCTION,

...ALLVALUES.UNITREF,

...ALLVALUES.SPAWNUNIT,

];

} else if (data.includes("projectileRef")) {

return [...ALLVALUES.PROJECTILE];

} else if (data.includes("event")) {

return [...ALLVALUES.EVENTS];

}

}

return [];

}

  • 查找当前行中冒号和点号的位置。
  • 如果没有找到节名,则返回一个空数组。
  • 如果光标在冒号之后,进一步判断:
    • 如果光标在点号之后,则返回 ALLVALUES.UNITPROPERTY 数组作为补全项。
    • 提取冒号之前的键名,并将节名转换为大写。
    • 根据节名从 ALLSECTIONS 对象中获取对应的配置信息。
    • 在配置信息中查找与键名匹配的项。
    • 根据匹配项的 data 属性,返回不同的补全项数组。

8. 根据节名返回补全项

javascript

else {

switch (currentSectionName) {

case 'core':

return ALLSECTIONS.CORE;

case 'graphics':

return ALLSECTIONS.GRAPHICS;

case 'attack':

return ALLSECTIONS.ATTACK;

case 'movement':

return ALLSECTIONS.MOVEMENT;

case 'ai':

return ALLSECTIONS.AI;

default:

if (currentSectionName.startsWith('canBuild_')) {

return ALLSECTIONS.CANBUILD;

} else if (currentSectionName.startsWith('turret_')) {

return ALLSECTIONS.TURRET;

} else if (currentSectionName.startsWith('projectile_')) {

return ALLSECTIONS.PROJECTILE;

} else if (currentSectionName.startsWith('arm_')) {

return ALLSECTIONS.ARM;

} else if (currentSectionName.startsWith('leg_')) {

return ALLSECTIONS.LEG;

} else if (currentSectionName.startsWith('attachment_')) {

return ALLSECTIONS.ATTACHMENT;

} else if (currentSectionName.startsWith('effect_')) {

return ALLSECTIONS.EFFECT;

} else if (currentSectionName.startsWith('animation_')) {

return ALLSECTIONS.ANIMATION;

} else if (currentSectionName.startsWith('action_')) {

return ALLSECTIONS.ACTION;

} else if (currentSectionName.startsWith('hiddenAction_')) {

return ALLSECTIONS.ACTION;

} else if (currentSectionName.startsWith('placementRule_')) {

return ALLSECTIONS.PLACEMENTRULE;

} else if (currentSectionName.startsWith('resource_')) {

return ALLSECTIONS.RESOURCE;

} else if (currentSectionName.startsWith('global_resource_')) {

return ALLSECTIONS.RESOURCE;

} else if (currentSectionName.startsWith('decal_')) {

return ALLSECTIONS.DECAL;

} else if (currentSectionName.startsWith('commnet_')) {

return [];

} else if (currentSectionName.startsWith('template_')) {

return ALLSECTIONS.TEMPLATE;

}

return [];

}

}

  • 如果光标不在方括号内也不在冒号之后,则根据节名返回不同的补全项数组。
  • 使用 switch 语句处理一些常见的节名,如 coregraphics 等。
  • 对于以特定前缀开头的节名,也返回相应的补全项数组。
  • 如果节名不匹配任何条件,则返回一个空数组。

9. 默认返回值

javascript

  • 如果以上所有条件都不满足,则返回一个空数组,表示没有补全项。

总结

写的我有点神游了,本来想any一把梭的,但是想了想还是规范一下吧. 造轮子果然很难,但写完之后用轮子方便了不少,感慨那些写代码提示的人有多辛苦 这学期要学编译原理,汇编语言,不知道这对我的插件开发会不会有更好的帮助呢?