试用前走查的修改:报错看得见、学生找得到作业、运行区和设计器的若干体验问题
- 运行区:编译错误、运行异常、控制台输出和保存的图片移到窗体下面,不再被实训台挤成 0 高;缩放和放大按钮移到桌面上方,不压窗体的关闭按钮;放大后也能停止程序;点停止后清掉窗体 - 运行异常:常见 .NET 异常配中文说明,显示出错的文件、行号和方法,点击跳到代码 - 运行中的下拉列表挂到页面最外层,能伸出窗体和分组框(小窗体也能选到 COM4、COM5) - 学生登录后进"我的作业";任务面板从工具栏下面开始,不挡导航;记住上次做的实验,从数据库页面返回、恢复数据时默认用它 - 代码页和设计页左栏都占 3/5,文件标签放不下时换行;实验选择框加宽;关闭粘性滚动 - 设计器:双击生成的事件方法带缩进;双击工具箱放进选中控件所在的容器并错开位置;新控件比容器宽时按比例缩小;无边框窗体不画标题栏 - 中文界面:代码编辑器(查找替换等)、属性窗口的颜色选项 - SQL:DATEADD、DATEDIFF、DATEPART 的日期单位(day、hour)解析成 IdentifierLiteral 时报"不支持",补上 - 项目 8 的预置环境数据改成建库那天之前的 7 天;p6-t3 评分项改为"按日期查询环境数据" - 没有 AI 建议时不显示"全部采纳 AI 建议"和相关说明;导出 CSV 只给纯数字的文字加制表符 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
da623fa648
commit
f6024dc113
30 files changed
+537
-255
No files matched your search
@@ -802,9 +802,10 @@ internal sealed class Translator
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>DATEADD(day, ...) 的第一个参数是关键字,解析成列名</summary>
|
||||
/// <summary>DATEADD(day, ...) 的第一个参数是关键字:ScriptDom 解析成 IdentifierLiteral(旧版本是列名)</summary>
|
||||
private static string DatePartName(ScalarExpression expression) => expression switch
|
||||
{
|
||||
IdentifierLiteral i => i.Value,
|
||||
ColumnReferenceExpression c => c.MultiPartIdentifier.Identifiers[^1].Value,
|
||||
StringLiteral s => s.Value,
|
||||
_ => throw SqlException.NotSupported("这种 datepart 写法"),
|
||||
|
||||
@@ -134,12 +134,54 @@ public static class SimHost
|
||||
public static void ReportException(Exception ex)
|
||||
{
|
||||
while (ex is TargetInvocationException { InnerException: not null } wrapper) ex = wrapper.InnerException!;
|
||||
Send(new JsonObject
|
||||
var type = ex.GetType().FullName;
|
||||
// 仿真库自己抛的异常信息已经是中文;.NET 自带的是英文,常见的几种配上中文说明,原文放在括号里
|
||||
var message = HasChinese(ex.Message) ? $"{ex.Message}({type})"
|
||||
: ExplainException(ex) is { } explained ? $"{explained}({type}: {ex.Message})"
|
||||
: $"{type}: {ex.Message}";
|
||||
var frame = StudentFrame(ex);
|
||||
var result = new JsonObject
|
||||
{
|
||||
["type"] = "exception",
|
||||
["message"] = $"{ex.GetType().FullName}: {ex.Message}",
|
||||
["detail"] = StudentStackTrace(ex),
|
||||
});
|
||||
["message"] = message,
|
||||
["detail"] = frame is { } f ? $"出错位置:{f.File} 第 {f.Line} 行({f.Method} 方法)" : StudentStackTrace(ex),
|
||||
};
|
||||
if (frame is { } at)
|
||||
{
|
||||
result["file"] = at.File;
|
||||
result["line"] = at.Line;
|
||||
}
|
||||
Send(result);
|
||||
}
|
||||
|
||||
private static bool HasChinese(string text) => text.Any(c => c >= '\u4e00' && c <= '\u9fff');
|
||||
|
||||
private static string? ExplainException(Exception ex) => ex switch
|
||||
{
|
||||
FormatException => "文字转换成数字或日期时格式不对,比如文本框是空的,或者里面不是数字",
|
||||
NullReferenceException => "用到的对象是 null:变量还没有赋值,或者对象、窗体还没有 new 出来",
|
||||
IndexOutOfRangeException => "数组的下标超出了范围:下标从 0 开始,最大是长度减 1",
|
||||
ArgumentOutOfRangeException => "下标或参数超出了范围,比如列表、字符串的下标太大,或者下拉框还没有选中",
|
||||
DivideByZeroException => "整数除以了 0",
|
||||
OverflowException => "数字太大或太小,超出了这个类型能表示的范围",
|
||||
InvalidCastException => "类型转换失败,这个对象不能转换成要求的类型",
|
||||
KeyNotFoundException => "字典里没有这个键",
|
||||
FileNotFoundException => "找不到这个文件",
|
||||
DirectoryNotFoundException => "找不到这个文件夹",
|
||||
_ => null,
|
||||
};
|
||||
|
||||
/// <summary>堆栈里离出错点最近的一行学生代码:文件名、行号、方法名</summary>
|
||||
private static (string File, int Line, string Method)? StudentFrame(Exception ex)
|
||||
{
|
||||
foreach (var frame in new System.Diagnostics.StackTrace(ex, true).GetFrames())
|
||||
{
|
||||
var file = frame.GetFileName();
|
||||
var line = frame.GetFileLineNumber();
|
||||
if (file == null || line <= 0 || file.Contains("/runtime/Sim.")) continue;
|
||||
return (Path.GetFileName(file), line, frame.GetMethod()?.Name ?? "");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>只保留学生代码的堆栈行,去掉仿真库内部的调用</summary>
|
||||
|
||||
Reference in new issue
Block a user