Lua语法检测工具文档

工具概述

Lua语法检测工具是一个Unity编辑器扩展工具,用于批量检查指定目录下所有Lua脚本的语法正确性。该工具通过创建Lua虚拟机环境,尝试加载每个Lua文件来验证其语法是否合法,帮助开发者在早期发现潜在的语法错误。

功能特性

  • 批量扫描指定目录下的所有Lua文件
  • 使用原生Lua虚拟机进行语法验证
  • 显示详细的错误信息,包括文件路径和具体错误描述
  • 支持递归扫描子目录
  • 提供简洁直观的GUI界面

使用说明

安装与启动

  1. 将脚本放置在Unity项目的Editor文件夹下
  2. 在Unity编辑器中,通过菜单栏选择 Tools/lua语法检测 打开工具窗口

界面说明

工具窗口包含以下主要部分:

  • 路径选择:显示和修改Lua文件所在目录(默认为Assets/_GameCenter/ClientLua/Model/Language
  • 检测按钮:点击后开始扫描指定目录并检测所有Lua文件语法

操作步骤

  1. 确认或修改Lua文件目录路径
  2. 点击”开始检测”按钮
  3. 查看Unity控制台输出:
    • 检测开始和结束的标记信息
    • 每个文件的检测结果
    • 语法错误的详细描述

输出结果解读

  • 正常文件:不会产生额外输出
  • 语法错误文件:输出格式为:
    1
    2
    [ERROR] 文件相对路径
    错误描述
  • 文件读取错误:输出格式为:
    1
    2
    [FILE ERROR] 读取文件失败: 文件相对路径
    错误原因

技术实现

核心逻辑

  1. 文件扫描:递归查找指定目录下的所有.lua文件
  2. Lua环境初始化:创建Lua虚拟机状态
  3. 语法检测:对每个文件内容执行luaL_loadstring操作
  4. 错误处理:捕获并记录加载过程中产生的错误
  5. 资源清理:关闭Lua虚拟机状态

关键方法

  • StartCheckLua:主检测流程控制
  • CheckLuaSyntax:执行实际的语法检测
  • OnGUI:提供用户界面交互

注意事项

  1. 工具仅检测语法正确性,不执行实际代码
  2. 需要确保Lua文件路径正确且可访问
  3. 检测结果输出到Unity控制台
  4. 工具不会修改任何Lua文件内容
  5. 必须选择Assets目录下的路径
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
using UnityEditor;
using UnityEngine;
using System.IO;
using LuaInterface;
using System;

/*
* CheckLuaSyntaxWindow
*
* 功能:批量检测指定文件夹下所有 Lua 脚本是否存在语法错误。
*
* 使用说明:
* 1. 将本脚本放入 Unity 工程的 Editor 目录中。
* 2. 在 Unity 菜单栏中点击:Tools > lua语法检测 打开工具窗口。
* 3. 在弹出的窗口中,点击 “...” 按钮选择待检测的 Lua 文件夹(必须位于 Assets 下)。
* 4. 点击“开始检测”按钮,工具将自动递归扫描所有 .lua 文件并检测语法问题。
*
* 特性:
* - 支持全项目范围递归查找 `.lua` 文件。
* - 采用底层 Lua VM(通过 LuaDLL)进行语法编译检查,检测准确。
* - 检测结果以 Unity 控制台日志输出,包含:
* * [ERROR] 表示语法错误(含错误信息)
* * [FILE ERROR] 表示读取文件失败
* - 所有语法错误将在控制台中清晰展示,便于开发者快速定位问题。
*
* 注意事项:
* - 所选目录 **必须在 Assets 目录中**,否则路径转换会失败。
* - 仅检查语法正确性,不执行 Lua 逻辑、不校验运行时引用。
* - 使用的是底层 C API(LuaDLL),**不会影响项目中现有的 Lua VM(如 LuaState)**。
* - 工具默认打开标准 Lua 库,兼容绝大部分普通语法。
* - 检测后请注意控制台输出,按需修复报错脚本。
*/
public class CheckLuaSyntaxWindow : EditorWindow
{
private static string folderPath, defaultPath = "Assets/_GameCenter/ClientLua/Model/Language";

[MenuItem("Tools/lua语法检测")]
public static void ShowWindow()
{
GetWindow<CheckLuaSyntaxWindow>("Lua 语法检测");
}

private void OnGUI()
{
folderPath = string.IsNullOrEmpty(folderPath) ? defaultPath : folderPath;
GUILayout.Label("选择 Lua 文件目录", EditorStyles.boldLabel);
EditorGUILayout.BeginHorizontal();
EditorGUILayout.LabelField("目录路径:", folderPath);
if (GUILayout.Button("...", GUILayout.Width(30)))
{
var path = EditorUtility.OpenFolderPanel("选择 Lua 文件目录", folderPath, "");
if (string.IsNullOrEmpty(path)) return;
if (!path.Contains("Assets"))
{
Debug.LogError("所选文件夹不在 Assets 目录下,无法处理!");
return;
}
folderPath = path.Substring(path.IndexOf("Assets"));
}
EditorGUILayout.EndHorizontal();
if (GUILayout.Button("开始检测")) StartCheckLua(folderPath);
}

private void StartCheckLua(string path)
{
Debug.Log($"===== 开始 Lua 语法检测 ===== {path} {Application.dataPath}");

// 更健壮的路径处理
var fullPath = Path.GetFullPath(Path.Combine(Application.dataPath, path.Replace("Assets/", "")));
if (!Directory.Exists(fullPath))
{
Debug.LogError($"路径不存在: {fullPath}");
return;
}
var L = IntPtr.Zero;
try
{
L = LuaDLL.lua_open();
if (L == IntPtr.Zero)
{
Debug.LogError("无法创建 Lua 状态");
return;
}
LuaDLL.luaL_openlibs(L);
var luaFiles = Directory.GetFiles(fullPath, "*.lua", SearchOption.AllDirectories);
foreach (var file in luaFiles)
{
var relativePath = "Assets" + file.Substring(Application.dataPath.Length);
try
{
var luaCode = File.ReadAllText(file);
var error = CheckLuaSyntax(luaCode, L);
if (!string.IsNullOrEmpty(error))
Debug.LogError($"[ERROR] {relativePath}\n{error}");
}
catch (Exception e)
{
Debug.LogError($"[FILE ERROR] 读取文件失败: {relativePath}\n{e.Message}");
}
}
}
finally
{
if (L != IntPtr.Zero) LuaDLL.lua_close(L);
}
Debug.Log("===== 检测完成 =====");
}

private string CheckLuaSyntax(string code, IntPtr L)
{
try
{
// 清空栈
LuaDLL.lua_settop(L, 0);
if (LuaDLL.luaL_loadstring(L, code) == 0)
{
LuaDLL.lua_pop(L, 1); // 弹出成功加载的函数
return null;
}
else
{
var error = LuaDLL.lua_tostring(L, -1);
LuaDLL.lua_pop(L, 1); // 弹出错误消息
return error;
}
}
catch (Exception e)
{
return e.Message;
}
}
}