meaoiu
v0.1.7
Published
盒中有识算之聪慧喵,告世人云:喵谕者,古今地外皆曰好喵,乃奇民要术喵~
Maintainers
Readme
喵谕 Meaoiu
知识好喵~
这是什么喵 (简介)
这是一门用手糊出来的、不确定是否适合正常人的编程语言喵~ 它不仅有一套不需要去挂精神科的普通语法,还极其自然地自带了代码格式化、静态诊断、自动补全等一整套工具喵~
不简之介
想懂就来这看看喵 -> 喵谕语言半解
怎么领回家喵 (安装)
别问我为什么,既然是 Node.js 做的破烂,安装当然是用 npm 喵~
npm install -g meaoiu(你也可以自己把这团代码抓下来执行 npm install 然后 npm run build,随你便喵)
怎么求它施法喵 (命令接口使用)
别告诉我你连终端模拟器都不认识喵~
大喊 meaoiu 召唤它喵!
文件名后缀是 .miu 喵~ 奇怪的东西不可以喵~
咏唱你的小作文喵:meaoiu <你的大典.miu>
- --diagnose:静态诊断喵~ 看看你的著作里藏了什么语病喵~
- --format:格式整理喵~ 能把你那像狗啃一样乱的缩进和换行变得像猫舔一样乱喵~
- --definition <行:列>:定义追溯喵~ 比如
meaoiu 你的大典.miu --definition 2:3,去抓这个玩具的本体喵~ - --references <行:列>:调查引用喵~ 找出这个玩具的各种名字喵~
- --hover <行:列>:悬停提示喵~ 凑数的东西喵~
- --complete <行:列>:补全建议喵~ 不想打字就用这个喵~ ~~(可能要打更多字)~~
搞半天想自己拼喵 (编程接口使用)
(这里抄:Meaoiu-LanguageServer)
想自己做抓板配件?真上进喵~ 那些 LSP 零件都把肚皮露出来了喵~
灵魂就在 StateManager 和 ServiceState 喵~
import { StateManager, getDiagnostics, getFormattedCode } from 'meaoiu';
// 1. 拿出状态管理器喵
// true 表示行列号从 1 开始算,适合大多数人类喵
const stateManager = new StateManager(true);
// 2. 把你的小作文塞进去,伪装成文档状态喵
const docState = StateManager.makeDocState(233, "扒[='闲话'=]喵~");
// 3. 让管理器咬住文档状态,吐出服务状态喵
const serviceState = stateManager.useState(docState);
// 4. 开始压榨各种价值喵!
// 格式化代码喵 (格式化不需要传 ServiceState,直接传源码字符串喵)
const prettyCode = getFormattedCode('【#(偷懒)#]好不好?好喵~').format();
// 诊断代码,看语病喵
const { syntaxErrors, semanticErrors } = getDiagnostics(serviceState);
// 别的魔法喵 (定义引用查找、补全等都要传入 serviceState 和 行列位置)
import { getCompletions, findDefinition, getHoverInfo } from 'meaoiu';
// 注意你的光标位置喵
const pos = { line: 1, character: 4 }; // 对应 LSP 协议,传入的列号名为 character,输出的以实际为准(常用 col)
const completions = getCompletions(serviceState, pos);
const definition = findDefinition(serviceState, pos);
const hover = getHoverInfo(serviceState, pos);有没有看懂喵?看不懂就去复读一百遍 ServiceState 的源码喵!
防误食小零件清单喵 (编程接口说明)
既然你诚心诚意地想知道每个零件怎么用,我就大发慈悲地告诉你喵~
灵魂玩具喵 (StateManager 与 ServiceState)
灵魂就是这两个东西喵~ 帮你节省脑细胞(电脑的)喵~
StateManager 负责记住你的小作文版本,减少重复解析喵~
constructor(useOnebased = true):useOnebased为true时行列号从 1 开始数,适合人类喵;为false时从 0 开始,适合机器喵~static makeDocState(version: number, sourceCode: string): DocState:随便捏个文档状态玩具喵~version随便写个数字,变了就重新解析喵~updateState(doc: DocState): ServiceState:强行更新状态(不管版本变没变)喵~useState(doc: DocState): ServiceState:聪明地更新状态(版本变了才重新解析)喵~getParseResult(doc: DocState): ParseResult | undefined和getAnalyzeResult(doc: DocState): AnalyzeResult | undefined:直接掏缓存喵~
ServiceState 是一次解析的成果,包含抽象语法树和符号表喵~ 我不喜欢你 new 它喵,请让 StateManager 喂给你喵~
version: number:文档版本号喵~parseResult: ParseResult:语法解析结果,里面有program(AST 根节点)和errors(语法错误)喵~analyzeResult: AnalyzeResult:符号分析结果,里面有rootScope、symbolMap、nodeScopeMap和errors(语义错误)喵~
记仇纸条喵 (MeaoiuError)
各种错误都用它的记录喵~ 让你知道为什么哈喵~
class MeaoiuError {
readonly message: string;
readonly phase: 0 | 1 | 2 | 3 | 4 | 5; // 错在哪个阶段
readonly line: number;
readonly col: number;
readonly endLine: number;
readonly endCol: number;
readonly messageWithPhase: string; // 如 "句法错误喵:..."
toString(): string; // 方便生成一条 "[行:列] 消息"
static readonly phaseTextTuple: readonly [string, string, string, string, string, string];
}- 字段说明:
phase:数字枚举喵~ 含义:0 未知错误、1 内部错误、2 词法错误、3 句法错误、4 语义错误、5 运行错误喵~ 可以用MeaoiuError.phaseTextTuple[phase]拿到名字喵~line/col/endLine/endCol:错误坐标喵~message:错误消息喵~messageWithPhase就是说了错在哪阶段的版本喵~
- 使用示例:
import { getDiagnostics, MeaoiuError } from 'meaoiu'; const { syntaxErrors, semanticErrors } = getDiagnostics(serviceState); [...syntaxErrors, ...semanticErrors].forEach(err => { console.log(`[${MeaoiuError.phaseTextTuple[err.phase]}] L${err.line}:${err.col} ${err.message}`); });
格式整理喵 (getFormattedCode)
type FormatResult = {
errors: MeaoiuError[]; // 解析错误
format: () => string; // 调用获得格式化结果
};
function getFormattedCode(sourceCode: string): FormatResult- 请求参数:
sourceCode—— 你乱糟糟的小人乍丶一乂字符串喵~ - 响应格式:
FormatResult喵~ 包含errors数组和format函数喵~ 调用format()就能得到整理好的小作文字符串喵~(缩进、换行、括号都会变漂亮喵) - 使用示例:
import { getFormattedCode } from 'meaoiu'; const { errors, format } = getFormattedCode('扒【=零食=] 吃~'); console.log(errors); // 查看解析错误 // if (errors.length) throw new Error('再继续下去会坏掉的喵'); // 可以顺便爆炸 console.log(format()); // 输出 "扒[= 零食 =]吃~"
静态诊断喵 (getDiagnostics)
function getDiagnostics(serviceState: ServiceState): Diagnostics- 请求参数:
serviceState—— 上面说过了喵~ - 响应格式:
{ syntaxErrors: MeaoiuError[], semanticErrors: MeaoiuError[] }喵~syntaxErrors:语法错误,比如括号不配对、关键词写错喵~semanticErrors:语义错误,比如变量未定义、类型不匹配、重复声明喵~
- 使用示例:
import { getDiagnostics } from 'meaoiu'; const { syntaxErrors, semanticErrors } = getDiagnostics(serviceState); syntaxErrors.forEach(err => console.error(`第 ${err.line} 行有语病喵:${err.message}`));
补全建议喵 (getCompletions)
function getCompletions(serviceState: ServiceState, position: { line: number; character: number }): Suggestion[]- 请求参数:
serviceState:上上面说过了喵~position:光标位置喵~line和character的行列号取决于你构造StateManager时用的useOnebased喵~
- 响应格式:
Suggestion[],每个建议有label(显示的文本)和kind(类型)喵~kind是SuggestionKind枚举值3 | 6 | 14 | 18,对应 LSP 的CompletionItemKind喵~ - 使用示例:
import { getCompletions } from 'meaoiu'; const suggestions = getCompletions(serviceState, { line: 2, character: 5 }); suggestions.forEach(s => console.log(`可以补全 ${s.label} (种类 ${s.kind})`));
定义追溯喵 (findDefinition)
function findDefinition(serviceState: ServiceState, position: { line: number; character: number }): SymbolInfo | undefined- 请求参数:同上喵,光标位置喵~
- 响应格式:
SymbolInfo或undefined(没找到)喵~SymbolInfo包含名字、种类、类型、声明位置、引用位置等喵~ - 使用示例:
import { findDefinition } from 'meaoiu'; const def = findDefinition(serviceState, { line: 3, character: 7 }); if (def) console.log(`定义在 L${def.declarations[0]?.line}:${def.declarations[0]?.col}`));
引用调查喵 (findReferences)
function findReferences(serviceState: ServiceState, position: { line: number; character: number }): Identifier[]- 请求参数:光标位置喵~
- 响应格式:
Identifier[],该符号的所有名字喵~ 每个Identifier都有line、col、endLine、endCol喵~ - 使用示例:
import { findReferences } from 'meaoiu'; const refs = findReferences(serviceState, { line: 4, character: 2 }); refs.forEach(ref => console.log(`在 L${ref.line}:${ref.col} 被提到`));
悬停提示喵 (getHoverInfo)
type MarkupKind = 'plaintext' | 'markdown';
type HoverInfo = {
contents: {
kind: MarkupKind;
value: string;
};
range: Range;
};
function getHoverInfo(
serviceState: ServiceState,
position: { line: number; character: number },
contentsKind?: MarkupKind, // 默认 'markdown'
): HoverInfo | undefined- 请求参数:光标位置喵~ 可选
contentsKind指定内容格式喵~ - 响应格式:
HoverInfo或undefined喵~HoverInfo包含contents.value(内容)、contents.kind(内容格式)和range(悬停范围)喵~ - 使用示例:
import { getHoverInfo } from 'meaoiu'; const hover = getHoverInfo(serviceState, { line: 5, character: 9 }, 'plaintext'); if (hover) console.log(`悬停内容:${hover.contents.value}`);
语义高亮喵 (getHighlightTokens 和 legend)
function getHighlightTokens(serviceState: ServiceState): HighlightToken[]- 请求参数:
serviceState喵~ - 响应格式:
HighlightToken[],每个 token 包含line、col、length、tokenType、tokenModifiers喵~ 配合legend使用喵~ - 使用示例:
import { getHighlightTokens, legend } from 'meaoiu'; const tokens = getHighlightTokens(serviceState); // legend.tokenTypes 和 legend.tokenModifiers 告诉你每个数字有何意味喵 tokens.forEach(t => console.log(`在 L${t.line}:${t.col} 有类型 ${legend.tokenTypes[t.tokenType]}`));
内联提示喵 (getInlayHints)
type InlayHint = {
position: Position;
label: string;
kind: 1 | 2; // 1 = Type, 2 = Parameter
paddingLeft?: boolean;
paddingRight: boolean;
};
function getInlayHints(serviceState: ServiceState): InlayHint[]- 请求参数:
serviceState喵~ - 响应格式:
InlayHint[],每个提示有position、label、kind、paddingLeft、paddingRight喵~kind为1(类型提示)或2(参数提示)喵~ - 使用示例:
import { getInlayHints } from 'meaoiu'; const hints = getInlayHints(serviceState); hints.forEach(h => console.log(`在 L${h.position.line}:${h.position.character} 显示 "${h.label}"`));
执行喵 (execute)
让编好的喵谕跑起来喵~
async function execute(
sourceCode: string,
ioConfig: {
onPrint: (formattedString: string) => void;
onPrompt: (question: string) => Promise<string>;
styleize?: ((value: unknown, strValue: string) => string) | boolean;
},
options?: { useOnebased?: boolean, logLevel?: 0 | 1 | 2 | 3 },
): Promise<void>- 请求参数:
ioConfig:必须传入输入输出函数,可以设置样式,默认(true)用 ANSI 颜色输出喵~options:决定报错用什么坐标和输出多少废话(0 debug 1 info 2 warn 3 error)喵~
- 使用示例:
import { execute } from 'meaoiu'; import readline from 'node:readline/promises'; const ioConfig = { onPrint: console.log, onPrompt: question => { const rl = readline.createInterface({ input: process.stdin, output: process.stdout }); return rl.question(question).finally(() => rl.close()); }, styleize: (value, strValue) => value == null ? `💥${strValue}💥` : strValue, }; try { await execute("蹭饭~扒[=饭, '好了喵~'=]喵~", ioConfig, { useOnebased: true, logLevel: 3 }); // 输出:💥空碗💥 好了喵~ } catch (error) { console.error(`坏了喵:${error}`); process.exit(1); }
小工具喵 (rangeOf)
function rangeOf(location: { line: number; col: number; endLine: number; endCol: number }): Range方便对接 LSP 协议喵~ location 可以是各种节点或错误喵~
符号表相关喵 (SymbolKind, SymbolTag, SymbolInfo, Scope)
这些是类型定义喵,你遇见 SymbolInfo 时会用到喵~
SymbolKind(12 | 13 | 20):FUNCTION = 12、VARIABLE = 13、PARAMETER = 20符号种类喵~SymbolTag(0 | 1 | 2):NORMAL = 0、MOVED = 1、DECAYED = 2符号状态喵(移动语义相关)~SymbolInfo:符号的完整档案喵~ 包含name、kind、tag、type、valueRef、declarations、references、isBuiltIn喵~Scope:作用域树节点喵~ 包含parent、children、symbols喵~
重新示范一次喵
最后给你一个完整的组装示例喵:
import {
StateManager,
getFormattedCode,
getDiagnostics,
getCompletions,
findDefinition,
findReferences,
getHoverInfo,
getHighlightTokens,
getInlayHints,
legend,
SymbolKind
} from 'meaoiu';
// 1. 准备状态管理器
const stateManager = new StateManager(true);
// 2. 塞入你的大作
const doc = StateManager.makeDocState(1, `
蹭 a~ a 就是 1~
想要 [= x =] 加一 [# 叼回来 x - 1~ #]~
扒 [= 3 =] 加一~
`);
// 3. 获取服务状态
const serviceState = stateManager.useState(doc);
// 4. 格式化
console.log(getFormattedCode(doc.getText()).format());
// 5. 诊断
const diag = getDiagnostics(serviceState);
console.log('语法错误:', diag.syntaxErrors.length);
console.log('语义错误:', diag.semanticErrors.length);
// 6. 补全(假设光标在第 2 行第 3 列)
const completions = getCompletions(serviceState, { line: 2, character: 3 });
console.log('补全建议:', completions.map(c => c.label));
// 7. 定义查找(假设查找 "加一" 的定义)
const def = findDefinition(serviceState, { line: 3, character: 5 });
if (def?.kind === SymbolKind.FUNCTION) {
console.log(`函数 "${def.name}" 定义在 L${def.declarations[0]?.line}`);
}
// 8. 引用查找
const refs = findReferences(serviceState, { line: 2, character: 3 });
console.log(`共有 ${refs.length} 处引用`);
// 9. 悬停
const hover = getHoverInfo(serviceState, { line: 1, character: 3 });
if (hover) console.log('悬停内容:', hover.contents.value);
// 10. 语义高亮
const tokens = getHighlightTokens(serviceState);
console.log(`生成了 ${tokens.length} 个高亮 token`);
// 11. 内联提示
const hints = getInlayHints(serviceState);
console.log(`生成了 ${hints.length} 个内联提示`);好了喵,零件清单给你了,自己拼喵!拼坏了别来找我喵~
