浏览代码

添加一些注释

ZhangYuYu 3 年之前
父节点
当前提交
7e8e20dc4c

+ 2 - 0
.vscode/settings.json

@@ -0,0 +1,2 @@
+{
+}

+ 54 - 54
src/api/index.ts

@@ -259,35 +259,35 @@ const api = {
     }
 }
 
-export interface CodeDTO {
-    code: { [key: string]: string }
+export interface CodeDTO { //用户写的代码
+    code: { [key: string]: string } //key是文件名,value是文件内容
 }
 
-export interface PageSerializer {
-    page?: number
-    limit?: number
-    size?: number
-    total?: number
+export interface PageSerializer { //分页
+    page?: number //当前页
+    limit?: number //页大小,每页的item数
+    size?: number //一次显示的页码数量
+    total?: number //总页数
 }
 
-export interface ResultSerializer {
-    id?: number
-    score?: number
-    lastCommitAt?: number
+export interface ResultSerializer { //考试结果
+    id?: number 
+    score?: number //分数
+    lastCommitAt?: number //最后一次提交时间
     deleted?: boolean
-    exam?: {
+    exam?: { //考试
         id?: number
         name?: string
         course?: string
     }
-    owner?: {
+    owner?: { //用户
         id?: number
         username?: string
     }
-    commits?: Array<{
+    commits?: Array<{ //提交记录
         id?: number
-        finished?: boolean
-        score?: number
+        finished?: boolean //是否完成提交
+        score?: number //本次提交分数
         // eslint-disable-next-line camelcase
         created_at?: number
     }>
@@ -296,10 +296,10 @@ export interface ResultSerializer {
 export interface CommitSerializer {
     id?: number
     hash?: string
-    finished?: boolean
-    score?: number
+    finished?: boolean //是否完成提交
+    score?: number //本次提交分数
     createdAt?: number
-    result?: {
+    result?: { //属于哪个考试结果
         id?: number
         score?: number
         exam?: {
@@ -311,7 +311,7 @@ export interface CommitSerializer {
             username?: string
         }
     }
-    records?: Array<{
+    records?: Array<{ //一次提交可能有多次record,也就是多次运行的过程
         id?: number
         status?: BuildStatus
         score?: number
@@ -322,28 +322,28 @@ export interface CommitSerializer {
     }>
 }
 
-export interface ExamSerializer {
+export interface ExamSerializer { //考试
     id?: number
     name?: string
     // eslint-disable-next-line camelcase
-    created_at?: number
-    startAt?: number
-    endAt?: number
-    status?: ExamStatus
-    creator?: {
+    created_at?: number 
+    startAt?: number //考试开始时间
+    endAt?: number //考试结束时间
+    status?: ExamStatus //考试状态
+    creator?: { //创建者
         id?: number
         username?: string
     }
-    problems?: ProblemSerializer[]
+    problems?: ProblemSerializer[] //题目列表
     joined?: boolean
-    courseId?: number
+    courseId?: number //所属课程id
     courseName?: string
-    result?: ResultSerializer
-    isCodeAnalysisNeeded?: boolean
+    result?: ResultSerializer //考试结果(针对某个用户来说的结果)
+    isCodeAnalysisNeeded?: boolean //是否需要代码分析
     url?: string
 }
 
-export type ProblemType =
+export type ProblemType = //题目类型
     | 'JAVA' // 普通Java题目,统计测试用例通过情况
     | 'JAVA_DATABASE' // 带数据库的Java题目,统计测试用例通过情况
     | 'PYTHON' // 普通Python题目,统计测试用例通过情况
@@ -354,10 +354,10 @@ export type ProblemType =
     | 'JAVA_COMPUTER_ORGANIZATION' // 带计算机组成原理的Java题目,统计测试用例通过情况
     | 'JAVA_SOFTWARE_DESIGN' // 带软件设计的Java题目,统计测试用例通过情况
 
-export interface ProblemSerializer {
+export interface ProblemSerializer { //题目
     id?: number
     name?: string
-    type?: ProblemType
+    type?: ProblemType //题目类型
     description?: string
     publicFiles?: Array<string>
     protectedFiles?: Array<string>
@@ -371,14 +371,14 @@ export interface ProblemSerializer {
     }
 }
 
-export interface ReceivedData<R = {}, E = 0> {
-    msg?: string
-    err?: E | 0 | 401
-    res?: R
-    page?: PageSerializer
+export interface ReceivedData<R = {}, E = 0> { //从coder接收到的返回数据的类型
+    msg?: string //信息
+    err?: E | 0 | 401 //错误码
+    res?: R  //返回内容
+    page?: PageSerializer //页码
 }
 
-export type UserRole =
+export type UserRole = //用户角色
     | 'STUDENT'
     | 'TEACHER'
     | 'ADMIN'
@@ -388,36 +388,36 @@ export type UserRole =
     | 'SEECX'
     | 'ASSISTANT'
 
-export interface UserSerializer {
+export interface UserSerializer { //用户
     id?: number
     score?: number
     username?: string
     email?: string
     phone?: string
-    role?: UserRole
+    role?: UserRole //用户角色
 }
 
-export interface CodeSerializer {
-    examId?: number
+export interface CodeSerializer { //一场考试所有题目的代码
+    examId?: number //考试id
     fetchAt?: number
-    startAt?: number
+    startAt?: number 
     endAt?: number
-    codeList?: Array<NestedCodeItemVO>
+    codeList?: Array<NestedCodeItemVO> //代码列表,一个题目的代码是一个NestCodeItemVO
 }
 
-export interface NestedCodeItemVO {
-    questionId?: number
-    language?: string
-    projectName?: string
-    editable?: {
-        [key: string]: string
+export interface NestedCodeItemVO { //一个题目的代码
+    questionId?: number //题目id
+    language?: string  //编程语言
+    projectName?: string //题目所属的项目
+    editable?: { //可编辑的文件
+        [key: string]: string //key是文件地址,value是文件内容
     }
-    uneditable?: {
+    uneditable?: { //不可编辑的文件
         [key: string]: string
     }
 }
 
-export type ExamStatus =
+export type ExamStatus = //考试状态
     | 'PREPARING' // 正在准备试题,通常是正在PUSH试题
     | 'UNAVAILABLE' // 考试不可用,通常是PUSH试题失败
     | 'READY' // 考试已准备好,在考试尚未开始时使用,不持久化到数据库中
@@ -425,7 +425,7 @@ export type ExamStatus =
     | 'FINISHED' // 考试已完成,在考试结束后使用,不持久化到数据库中
     | 'CLOSED' // 考试已关闭,表示考试生命周期结束,所有试卷均被存档
 
-export type BuildStatus =
+export type BuildStatus = //构建状态
     | 'SUCCESS' // 构建成功,表示没有错误
     | 'UNSTABLE' // 构建不稳定,表示有测试错误等非致命错误
     | 'FAILURE' // 构建失败,表示有语法错误等致命错误

+ 40 - 6
src/components/drawer/index.tsx

@@ -14,6 +14,7 @@ const defaultConfig: DrawerProps = {
     width: '80%',
 };
 
+//将抽屉里的所有元素设为不可见
 const removePopUpMenu = () => {
     const remove = () => {
         const tips = document.querySelectorAll<HTMLDivElement>('.mxPopupMenu');
@@ -27,44 +28,77 @@ const removePopUpMenu = () => {
     setTimeout(remove, 500);
 };
 
+//record<k,v>批量定义对象的属性。所有的属性的key一定是k类型,一般是string。所有属性的值都是v类型,一般可以是各种类型,而且可以是A|B,
+//适用于这个对象的属性的值的类型相同或总体相似,而且属性很多,所以要批量定义的情况。也可以当作“记录”。即{id1:sm1;id2:sm2;...}来使用。
+
+
+//drawerListener是全方位改变抽屉的所有属性状态的函数列表,可能不止一个函数,根据id不同,他们改变的抽屉不同。不是renderContent函数,包含了renderContent的改变,也会改变config与update的值,是一个总的改变逻辑
+//drawerListener的类型确定了(这个对象一定是有一系列同质属性的对象。{id1:func1;id2:func2;...}),但是值现在先设为空值,可以通过drawerListeners[key]=value的方式逐个往对象里面加属性加值
+//Partial是指这个对象的每一个属性都是可选的。
 const drawerListeners: Record<
     string,
+
+    //属性的值是一个函数。这个函数是渲染/生成抽屉的、控制抽屉状态的函数
     (renderContent: CustomDrawerProps['renderContent'], props: Partial<DrawerProps>) => void
     > = {};
 
+
 export function updateDrawer({ id, renderContent, ...restProps }: CustomDrawerProps) {
+    //这是一个函数调用,drawerListeners[id]是个函数,输入renderContent,restProps参数,调用这个函数,改变抽屉状态,id可能标明是哪个抽屉。函数及参数则标明用什么样的数据以何种逻辑改变这个抽屉的状态
     drawerListeners[id](renderContent, restProps);
 }
 
+
+/*
+抽屉组件,渲染抽屉内容的逻辑是一个函数,在drawerListeners这种函数列表里面的某个函数,以id为识别码,不同的id是不同的功能抽屉。同一个id是展示同一种内容和功能的抽屉,哪怕
+渲染的样式、数据会变化
+参数:
+id 抽屉id
+renderContent 一个函数,存放渲染的数据内容以及渲染逻辑
+restProps 抽屉所有属性中除了渲染的内容之外的抽屉的其他属性参数,比如是否可见,大小等等,可以说是抽屉的config
+*/
 export default function CustomDrawer({ id, renderContent, ...restProps }: CustomDrawerProps) {
+    //下面三行用react的状态盛放抽屉的三个参数:
+    //是否强制渲染,默认否
     const [, setForceRender] = useState(false);
+    //抽屉的配置项config
     const [drawerConfig, setConfig] = useState(restProps);
-
+    //抽屉的内容渲染函数
     const [childrenRender, setChildrenRender] = useState<CustomDrawerProps['renderContent']>(
         () => renderContent,
     );
 
+    //forceRender是一个具体的函数,遵循规格要求,
     const forceRender = (
-        contentFn?: CustomDrawerProps['renderContent'],
+        //CustomDrawerProps['renderContent']是一个类型,不是一个值
+        contentFn?: CustomDrawerProps['renderContent'], //规定必须是这个类型
         {
-            update = false,
-            ...restDrawerProps
+            update = false, //是更新还是清除原来所有属性值,换上全新的属性值
+            ...restDrawerProps //其他的配置项
         }: Omit<CustomDrawerProps, 'renderContent' | 'id'> = {},
     ) => {
+        //状态改变函数,它的参数是原来的状态的值,也就是第一次执行的时候,是最初的restProps
         setConfig((config) =>
-            update ? { ...config, ...restDrawerProps } : { ...restDrawerProps },
+            update ? { ...config, ...restDrawerProps } : { ...restDrawerProps },//是否是更新,如果是更新则更新,如果是重置则重置
         );
+        //重置内容渲染函数
         if (contentFn) {
             setChildrenRender(() => contentFn);
         }
+        //反转falseRender的boolean值
         setForceRender((f) => !f);
     };
 
+    //初始化这个抽屉组件前,说明组件要被使用了,所以它的改变这个抽屉的所有属性的函数
+    //这个副作用函数只是赋值了函数列表,给函数列表增添了一个函数,但是没有调用,没有执行代码
     useLayoutEffect(() => {
         drawerListeners[id] = forceRender;
     }, []);
 
+    //关闭抽屉
     const handleCloseDrawer = () => {
+        //改变路径,初始化页面有路径监听器,改变路径之后,会监听到变化
+        //
         history.push({
             query: {},
         });
@@ -76,7 +110,7 @@ export default function CustomDrawer({ id, renderContent, ...restProps }: Custom
         <Drawer
             getContainer={() => document.querySelector('.mo-mainBench') || document.body}
             style={{ position: 'absolute' }}
-            onClose={handleCloseDrawer}
+            onClose={handleCloseDrawer} //关闭的时候一定会关闭抽屉,这个是默认行为,onClose里面是关闭抽屉时还需要做的额外的事情
             {...defaultConfig}
             {...drawerConfig}
         >

+ 55 - 14
src/components/editor/index.tsx

@@ -6,21 +6,23 @@ import {defaultOptions} from './config';
 import './language/jsonlog';
 import './style.scss';
 
+//editor的属性
 interface IEditorProps {
-    className?: string;
-    style?: CSSProperties;
-    value?: string;
-    language?: string;
-    sync?: boolean;
-    options?: monaco.editor.IStandaloneEditorConstructionOptions;
-    cursorPosition?: monaco.IPosition;
-    placeholder?: string;
-    onChange?: (value: string, instance: monaco.editor.IStandaloneCodeEditor) => void;
-    onBlur?: (value: string) => void;
-    onFocus?: (value: string) => void;
-    onCursorSelection?: (value: string) => void;
+    className?: string; //类名
+    style?: CSSProperties; //css style
+    value?: string; //数据
+    language?: string; //编程语言
+    sync?: boolean; //同步?
+    options?: monaco.editor.IStandaloneEditorConstructionOptions; //monaco构造函数那些选项
+    cursorPosition?: monaco.IPosition; //光标位置
+    placeholder?: string; //占位符
+    onChange?: (value: string, instance: monaco.editor.IStandaloneCodeEditor) => void; //当内容改变时的处理逻辑
+    onBlur?: (value: string) => void; //当光标失去焦点时的逻辑
+    onFocus?: (value: string) => void; //当光标聚焦时的逻辑
+    onCursorSelection?: (value: string) => void; //当光标选中某值的逻辑
 }
 
+//editor组件,接收以下参数
 export default function Editor({
                                    className,
                                    style,
@@ -35,32 +37,44 @@ export default function Editor({
                                    onFocus,
                                    onCursorSelection,
                                }: IEditorProps) {
+    //引用
     const container = useRef<HTMLDivElement>(null);
     const monacoEditor = useRef<monaco.editor.IStandaloneCodeEditor>();
     const placeholderDOM = useRef<HTMLPreElement>(null);
 
+    //初始化monaco
     const initMonaco = () => {
+        //如果当前container引用不为空
         if (container.current) {
+            //monaco的options
             const editorOptions = {
+                //配置中配置了默认的options,当没有传入相关的参数时有默认的配置
                 ...defaultOptions,
                 ...options,
-                value,
-                language: language || 'sql',
+                value, //value是传入的value
+                language: language || 'sql', //语言如果没有传,就是sql
             };
 
+            //将引用赋值给monaco实例,创建monaco实例,挂在div上
             monacoEditor.current = monaco.editor.create(container.current, editorOptions);
 
+            //处理placeHolder,根据value是否为空,决定placeHolder的显隐
             handleShowPlaceholder(value);
 
+            //如果monaco实例已经初始化了,且传了光标位置参数
             if (monacoEditor.current && cursorPosition) {
+                //在monaco实例上更新光标的位置参数
                 monacoEditor.current.setPosition(cursorPosition);
+                //聚焦
                 monacoEditor.current.focus();
+                //?
                 monacoEditor.current.revealPosition(
                     cursorPosition,
                     monaco.editor.ScrollType.Immediate,
                 );
             }
 
+            //初始化editor,除了monaco的初始化之外的editor的东西,主要是editor的事件
             initEditor();
             registerCommands();
         }
@@ -69,8 +83,10 @@ export default function Editor({
     /**
      * TODO: I don't why lost these commands, it should be figure it out after a while
      */
+    //注册键盘命令
     const registerCommands = () => {
         if (monacoEditor.current) {
+            //添加按键监听
             monacoEditor.current.addCommand(monaco.KeyMod.CtrlCmd | monaco.KeyCode.KeyZ, () => {
                 monacoEditor.current?.trigger('editor', 'undo', null);
             });
@@ -101,34 +117,48 @@ export default function Editor({
         }
     };
 
+    //初始化编辑器事件
     const initEditorEvent = () => {
+        //如果有monaco实例
         if (monacoEditor.current) {
+            //监听编辑器里内容发生改变并处理
+            //内容改变事件
             monacoEditor.current.onDidChangeModelContent(() => {
+                //获取变化后的编辑器里的语句
                 const newValue = monacoEditor.current!.getValue();
+                //如果传递了变化处理函数,则调用变化处理函数处理
                 if (onChange) {
+                    
                     onChange(newValue, monacoEditor.current!);
                 }
+                //每次变化都需要根据value来处理placeHolder的显隐
                 handleShowPlaceholder(newValue);
             });
 
+            //监听光标失去焦点事件并处理
             monacoEditor.current.onDidBlurEditorText(() => {
+                //如果传递了当光标失去焦点时的处理函数,则调用函数处理
                 if (onBlur) {
                     const val = monacoEditor.current!.getValue();
                     onBlur(val);
                 }
             });
 
+            //监听因为打开窗体而模糊事件并处理
             monacoEditor.current.onDidBlurEditorWidget(() => {
                 handleShowPlaceholder(monacoEditor.current!.getValue());
             });
 
+            //监听光标获取焦点事件并处理
             monacoEditor.current.onDidFocusEditorText(() => {
+                //如果传递了光标获取焦点处理函数,则调用函数处理
                 if (onFocus) {
                     const val = monacoEditor.current!.getValue();
                     onFocus(val);
                 }
             });
 
+            //监听光标选中的区域变化并处理
             monacoEditor.current.onDidChangeCursorSelection(() => {
                 const ranges = monacoEditor.current!.getSelections() || [];
                 const model = monacoEditor.current!.getModel();
@@ -145,6 +175,7 @@ export default function Editor({
             /**
              * 改变contextMenu的定位为fixed,避免容器内overflow:hidden属性截断contextMenu
              */
+            //监听召唤出右键菜单事件并处理
             monacoEditor.current.onContextMenu((e) => {
                 const contextMenuElement = monacoEditor.current
                     ?.getDomNode()
@@ -169,24 +200,30 @@ export default function Editor({
         }
     };
 
+    //初始化编辑器
     const initEditor = () => {
         initEditorEvent();
     };
 
+    //处理占位符的显隐
     const handleShowPlaceholder = (val?: string) => {
+        //如果没有数据,则显示placeHolder
         if (!val) {
             placeholderDOM.current!.style.display = 'initial';
+            //如果有数据,说明有文件被打开,则隐藏placeHolder
         } else {
             placeholderDOM.current!.style.display = 'none';
         }
     };
 
+    //销毁monaco
     const destroyMonaco = () => {
         if (monacoEditor.current) {
             monacoEditor.current.dispose();
         }
     };
 
+    //当value变化时,执行的副作用函数
     useEffect(() => {
         if (monacoEditor.current) {
             if (sync) {
@@ -208,18 +245,21 @@ export default function Editor({
         }
     }, [value]);
 
+    //当语言变化时,执行的副作用函数
     useEffect(() => {
         if (monacoEditor.current) {
             monaco.editor.setModelLanguage(monacoEditor.current.getModel()!, language || 'ini');
         }
     }, [language]);
 
+    //当monaco options变化时执行的副作用函数
     useEffect(() => {
         if (monacoEditor.current && options) {
             monacoEditor.current.updateOptions(options);
         }
     }, [options]);
 
+    //页面加载时,执行的副作用函数
     useEffect(() => {
         initMonaco();
 
@@ -229,6 +269,7 @@ export default function Editor({
     }, []);
 
     return (
+        //div用来挂monaco-editor
         <div
             className={classNames('code-editor', className)}
             style={{

+ 17 - 1
src/components/notification/index.tsx

@@ -7,26 +7,37 @@ import './notification.scss';
 type INotificationConfigs = Pick<ArgsProps, 'message'> & { key: string };
 
 class Index {
+    //molecule的通知管理里面是否存在这个通知
     private assertNotExistNotification(key: string) {
+        //解构出所有的items的列表
         const { data } = molecule.notification.getState();
         return !data?.find((n) => n.id === key);
     }
+    //设置通知为可见
     private showNotification() {
+        //boolean
         const { showNotifications } = molecule.notification.getState();
+        //如果通知不可见,那么设置为可见
         if (!showNotifications) {
             molecule.notification.toggleNotification();
         }
     }
+    //
     private highlightNotification(key: string) {
         const notificationItem = document.getElementById(key)?.parentElement;
+        //如果找到了这个通知
         if (notificationItem) {
+            //将通知设为可聚焦的
             notificationItem.tabIndex = 0;
+            //通知聚焦
             notificationItem.focus();
         }
     }
     /**
      * Add a notification both in antd and molecule
      */
+
+    //把antd与molecule的通知同时打开,antd内嵌molecule的通知,由antd触发
     openWithMolecule(config: INotificationConfigs) {
         const { showNotifications } = molecule.notification.getState();
         // the antd's notification pops up only when the molecule's notification invisible
@@ -41,14 +52,16 @@ class Index {
                 onClose() {},
                 onClick: () => {
                     antNotification.close(config.key);
+                    //展示molecule所有通知列表
                     this.showNotification();
                     setTimeout(() => {
+                        //高亮这个通知
                         this.highlightNotification(config.key);
                     }, 0);
                 },
             });
         }
-
+        //将这个通知加到通知列表中
         if (this.assertNotExistNotification(config.key)) {
             molecule.notification.add([
                 {
@@ -65,6 +78,7 @@ class Index {
     /**
      * Open a notification both in antd and molecule in bottomRight with danger icon
      */
+    //打叉
     error({ key, message }: { key: string; message: string }) {
         this.openWithMolecule({
             key,
@@ -84,6 +98,7 @@ class Index {
         });
     }
 
+    //打感叹号
     info({ key, message }: { key: string; message: string }) {
         this.openWithMolecule({
             key,
@@ -103,6 +118,7 @@ class Index {
         });
     }
 
+    //打勾
     success({ key, message }: { key: string; message: string }) {
         this.openWithMolecule({
             key,

+ 2 - 0
src/components/questionListPanel/index.tsx

@@ -14,6 +14,8 @@ import {connect} from "react-redux";
 import Scrollbar from "react-scrollbars-custom";
 import {HEADER_HEIGTH, MAX_GROW_HEIGHT} from "@dtinsight/molecule/esm/components/collapse";
 
+
+
 export interface IQuestionListProps {
     questions: ProblemSerializer[],
     currentId: number

+ 23 - 0
src/extensions/chatGPT/base.tsx

@@ -0,0 +1,23 @@
+import { IActivityBarItem, ISidebarPane, } from '@dtinsight/molecule/esm/model';
+
+export const CHAT_GPT_ID = 'chatGPT';
+
+export const chatGPTSidebar: ISidebarPane = {
+    id: CHAT_GPT_ID,
+    title: 'chatGPTPane',
+    render: () => {
+        return (
+            <div>
+                <p>chatGPT sideBar!</p>
+            </div>
+        );
+    },
+};
+
+export const chatGPTActivityBar: IActivityBarItem = {
+    id: CHAT_GPT_ID,
+    sortIndex: 1, // sorting the dataSource to the first position
+    name: 'chatGPT',
+    title: 'chatGPT',
+    icon: 'symbol-snippet',
+};

+ 31 - 0
src/extensions/chatGPT/index.ts

@@ -0,0 +1,31 @@
+import { IExtension } from '@dtinsight/molecule/esm/model/extension';
+import { IExtensionService } from '@dtinsight/molecule/esm/services';
+import molecule from "@dtinsight/molecule";
+import {chatGPTSidebar, chatGPTActivityBar} from "./base";
+
+export class ChatGPTExtension implements IExtension {
+    id: string = '';
+    name: string = '';
+
+    constructor(
+        id: string = 'chatGPTExtension', 
+        name: string = 'chatGPTExtension'
+    ) {
+        this.id = id;
+        this.name = name;
+    }
+
+    activate(extensionCtx: IExtensionService): void {
+        this.initUI();
+    }
+
+    initUI() {
+        molecule.sidebar.add(chatGPTSidebar);
+        molecule.activityBar.add(chatGPTActivityBar);
+    }
+
+    dispose(extensionCtx: IExtensionService): void {
+        molecule.sidebar.remove(chatGPTSidebar.id);
+        molecule.activityBar.remove(chatGPTActivityBar.id);
+    }
+}

+ 3 - 0
src/extensions/index.ts

@@ -7,6 +7,7 @@ import { MenuBarExtension } from './menubar';
 import { ActionExtension } from './action';
 import { GoToGitlabExtension } from './gitlab';
 import InitializeExtension from "./init";
+import {ChatGPTExtension} from './chatGPT'
 
 const extensions: IExtension[] = [
     new FirstExtension(),
@@ -17,6 +18,8 @@ const extensions: IExtension[] = [
     OneDarkPro,
     QuietLight,
     new InitializeExtension(),
+    new ChatGPTExtension(),
+
 ];
 
 export default extensions;

+ 1 - 1
src/extensions/init.tsx

@@ -47,7 +47,7 @@ export default class InitializeExtension implements IExtension {
 
     activate(): void {
         const { CONTEXT_MENU_SEARCH } = molecule.builtin.getConstants();
-        molecule.activityBar.remove([CONTEXT_MENU_SEARCH!]);
+        // molecule.activityBar.remove([CONTEXT_MENU_SEARCH!]);
         initializeColorTheme();
         initMenuBar();
         initLogin();

+ 3 - 0
src/extensions/theFirstExtension/base.tsx

@@ -7,6 +7,9 @@ export const STATUS_BAR_LANGUAGE: IStatusBarItem = {
     sortIndex: 3,
 }
 
+//IEditorTab是editorService的state形式,所有的想要在编辑区展示的东西都需要写成这个格式,然后估计还有一个什么group
+
+//这个Tab是一个loadingTab,提示正在初始化的
 export const LoadingTab: IEditorTab = {
     id: LOADING_TAB_ID,
     name: 'Loading Project',

+ 32 - 6
src/extensions/theFirstExtension/editorController.tsx

@@ -14,33 +14,53 @@ import {
 import {listen} from "@codingame/monaco-jsonrpc";
 import normalizeUrl from "normalize-url";
 
+//设置入口,入口就是没有任何的Tab打开的时候,编辑区显示的内容
 export function setEntry() {
     molecule.editor.setEntry(<Welcome/>)
 }
 
+//激活编辑器的回调
+//当Tab更新时更新文件树的数据,状态管理中的当前题目的可编辑部分的数据,因为提交的也只有可编辑部分而已。而且key是文件地址不是文件名
 export function activateEditCallback() {
+    //Tab更新事件
     molecule.editor.onUpdateTab((tab) => {
+        //获取更新后的文件数据
         const nextFileValue = molecule.editor.editorInstance?.getModel()?.getValue()
+        //当前正在编辑的是哪个tab
         const tabId = molecule.editor.getState().current?.tab?.id
+        //如果更新后的数据及当前tab都存在
         if (nextFileValue && tabId) {
+            //根据tabId(跟文件名相同应该),找到文件node
             const node = molecule.folderTree.get(tabId)
+            //如果这个文件node存在
             if (node){
+                //文件node里的内容是之前的文件内容,取出来
                 const prevFileValue = node.data.value
+                //文件地址也取出来
                 const location = node.location
+                //如果更新后的内容不等于之前内容
                 if (prevFileValue !== nextFileValue) {
+                    //更新文件node的数据
                     node.data.value = nextFileValue
+                    //更新folderTree数据
                     molecule.folderTree.update(node)
+                    //获取当前问题
                     const question = store.getState().loadCurrentQuestion
+                    //如果当前问题存在,当前问题有可编辑的部分,当前node的地址存在
+                    //更新状态中的当前问题,更新的数据就从editor中更新到了treeNode中,再更新到了状态中的当前问题中
                     if (question && question.editable && location) {
+
                         question.editable[location] = nextFileValue
                         store.dispatch(loadCurrentQuestion(question))
                     }
+                    //将此文件标记为蓝色,做版本控制,表示这个文件改动了
                     if (location != null) {
                         const el = window.document.querySelector(`div[data-key='${node.id}'].mo-tree__treenode span.mo-tree__treenode__title`) as HTMLElement | null;
                         if (el) el.style.color = '#2491f7'
                         // console.log(molecule.editor.editorInstance.getModel())
                         // console.log(monaco.editor.getModel(monaco.Uri.parse(location)))
                     }
+                    //打印数据已更新
                     console.log('value update')
                 }
             }
@@ -48,23 +68,29 @@ export function activateEditCallback() {
     })
 }
 
+//激活编辑器的语言服务
 export function activateLanguageService() {
+    //生成urlmonaco-languageclient
     function createUrl(hostname: string, port: number, path: string): string {
+        //websocket
         return normalizeUrl(`ws://${hostname}:${port}${path}`);
     }
+    //安装monaco
     MonacoServices.install(monaco);
     // create the web socket
+    //语言解析服务器的hostname,port
     const url = createUrl('localhost', 8086, '')
     const webSocket = new WebSocket(url);
 
 // listen when the web socket is opened
+//监听这个socket,并建立这个连接的客户端
     listen({
         webSocket,
         onConnection: connection => {
             // create and start the language client
-            const languageClient = createLanguageClient(connection);
-            const disposable = languageClient.start();
-            connection.onClose(() => disposable.dispose());
+            const languageClient = createLanguageClient(connection); //连接上了就创建客户端
+            const disposable = languageClient.start(); //启动客户端
+            connection.onClose(() => disposable.dispose()); //连接关闭时,销毁客户端
 
             console.log(`Connected to "${url}" and started the language client.`);
         }
@@ -73,13 +99,13 @@ export function activateLanguageService() {
     function createLanguageClient(connection: MessageConnection): MonacoLanguageClient {
         return new MonacoLanguageClient({
             name: "Sample Language Client",
-            clientOptions: {
+            clientOptions: { //客户端配置
                 // use a language id as a document selector
                 documentSelector: ['java'],
                 // disable the default error handler
                 errorHandler: {
-                    error: () => ErrorAction.Continue,
-                    closed: () => CloseAction.DoNotRestart
+                    error: () => ErrorAction.Continue, //错误的处理
+                    closed: () => CloseAction.DoNotRestart //连接关闭的处理
                 }
             },
             // create a language client connection from the JSON RPC connection on demand

+ 57 - 0
src/extensions/theFirstExtension/folderTreeController.ts

@@ -16,6 +16,7 @@ import {store} from "../../redux/store";
 import notification from "../../components/notification";
 import {message} from "antd";
 
+//简写与语言的对应关系
 const extToLang:{[prop:string]: string} = {
     'html': 'html',
     'js': 'javascript',
@@ -28,52 +29,82 @@ const extToLang:{[prop:string]: string} = {
     'md': 'markdown'
 }
 
+//初始化文件树
 export async function initFolderTree() {
+    //发布订阅模式,每当 dispatch action 的时候就会执行
     store.subscribe(async () => {
+        //如果状态处于正在加载项目状态
         if (store.getState().loadProject.isLoading) {
+            //获取当前打开的所有Tab
             const groups = molecule.editor.getState().groups
+
             const hide = message.loading('Please wait, Project is loading...', 0)
+            //获取要加载项目的文件树
             const res = await API.getFolderTree();
+            //提示:正在加载,请稍后
             hide()
+            //先将现在打开的所有Tab关闭
             groups?.forEach((group) => {
                 molecule.editor.closeAll(group.id)
             })
+            //如果获取文件树错误,弹出错误提示:错误描述
             if (res.id === 'error') {
                 message.error(res.data.description)
             }
+            //如果获取文件树成功,则弹出成功提示:加载已完成
             else message.success('Loading is Complete')
+            //如果系统状态是加载已完成
             if (!store.getState().loadProject.isLoading && store.getState().loadProject.isValid) {
+
                 const folderTreeData = cloneDeep(res);
+                //重置文件树数据
                 molecule.folderTree.reset();
+                //添加新的文件树数据
                 molecule.folderTree.add(folderTreeData);
             }
         }
     })
 }
 
+//处理选中文件树节点事件
 export function handleSelectFolderTree() {
+    //双击文件事件处理
     molecule.folderTree.onSelectFile((file: IFolderTreeNodeProps) => {
+        //将这个文件树节点以Tab的形式打开
         molecule.editor.open(transformToEditorTab(file))
+        //更新状态栏语言
         updateStatusBarLanguage(file.data.language);
     });
 }
 
+//处理重命名事件
 export  function handleRenameFile(){
+    //处理文件名更新事件
     molecule.folderTree.onUpdateFileName(file => {
+        //文件树node解构出这些信息
         const {name, id, location, data} = file
+        //分离出文件名和后缀
         const frag = name!.split('.');
+        //找到父节点,也就是其所属文件夹
         const parentNode = molecule.folderTree.getParentNode(id);
         let count = -1;
+        //查找父文件夹的孩子信息,是否存在这个文件,存在几个
         if (parentNode && parentNode.children){
             parentNode.children.forEach(((value) => value.name === name?count++:count+=0))
         }
+        //如果在同一个文件夹下出现了1个以上同名文件,那么这个文件的名字为已更新的名字添加后缀count
         if (count>0) frag![0] = frag![0] + `(${count})`;
+        //更新后拼接上名字
         const newName = frag.join('.');
+        //文件的扩展名
         const ext = frag[frag.length-1];
+        //转化成语言
         const newLang = extToLang[ext] || 'file'
         const newLoc = location?.split('/') || [];
+        //将文件树的这个node的地址中的文件名也更新为新的文件名
         newLoc[newLoc.length - 1] = newName;
         const newLocation = newLoc.join('/');
+        //创建新的文件树节点,但是id还是那个id
         const newFile = {
             ...file,
             id,
@@ -85,29 +116,41 @@ export  function handleRenameFile(){
                 language: newLang
             }
         };
+        //更新文件树的这个节点
         folderTree.update(newFile);
+        //更新Tab中的语言
         const groupId = molecule.editor.getGroupIdByTab(id.toString());
         const isValidGroupId = !!groupId || groupId === 0;
+        //如果这个文件被在编辑器打开了
         if (isValidGroupId) {
             const prevTab =
                 molecule.editor.getTabById<BuiltInEditorTabDataType>(
                     id.toString(),
                     groupId
                 );
+                //创建新的Tab
             const newTab: IEditorTab = { id: id.toString(), name , breadcrumb: transformToEditorTab(newFile).breadcrumb};
             const prevTabData = prevTab?.data;
             newTab.data = { ...prevTabData, language: newLang};
+            //如果当前的正在编辑的文件,现在的激活文件Tab,就是这个被重命名的文件
             if (molecule.editor.getState().current?.activeTab === id.toString()) {
+                //更新状态栏语言
                 updateStatusBarLanguage(newLang);
             }
+            //更新Tab
             molecule.editor.updateTab(newTab);
         }
     })
 }
 
+
+//处理创建新文件事件
 export function handleNewFile(){
+    //当创建新文件时
     molecule.folderTree.onCreate((type, nodeId) => {
+        //文件夹
         if (type == 'Folder'){
+            //往文件树中添加文件夹节点
             molecule.folderTree.add(
                 new TreeNodeModel({
                     id: `${nodeId}_${new Date().getTime()}`,
@@ -120,7 +163,9 @@ export function handleNewFile(){
                 }), nodeId
             );
         }
+        //文件
         else {
+            //往文件树添加文件
             molecule.folderTree.add(
                 new TreeNodeModel({
                     id: `${nodeId}_${new Date().getTime()}`,
@@ -135,25 +180,37 @@ export function handleNewFile(){
         }
     })
 }
+//更新状态栏语言
 export function updateStatusBarLanguage(language: string) {
+    //如果传入的是空,不做处理
     if (!language) return;
+    //语言改成里面的每个词的首字母大写
     language = language.toUpperCase();
+    //获取状态栏的语言item
     const languageStatusItem = molecule.statusBar.getStatusBarItem(STATUS_BAR_LANGUAGE.id, Float.right);
+    //如果存在语言Item
     if (languageStatusItem) {
+        //更新语言
         languageStatusItem.name = language;
+        //更新语言item
         molecule.statusBar.update(languageStatusItem, Float.right);
+        //如果不存在语言item,创建新的语言item
     } else {
         molecule.statusBar.add(Object.assign({}, STATUS_BAR_LANGUAGE, { name: language } ), Float.right);
     }
 }
 
+//处理状态栏语言
 export function handleStatusBarLanguage() {
     const moleculeEditor = molecule.editor;
+    //当选中了这个Tab,也就是选中这个Tab作为当前编辑的Tab
     moleculeEditor.onSelectTab((tabId, groupId) => {
+        //如果groupId或者tab不存在,则返回
         if (!groupId) return;
         const group = moleculeEditor.getGroupById(groupId);
         if (!group) return;
         const tab: any = moleculeEditor.getTabById(tabId, group.id!);
+        //如果都存在,则更新状态栏语言
         if (tab) {
             updateStatusBarLanguage(tab.data!.language!);
         }

+ 132 - 0
src/pages/chatGPT/index.tsx

@@ -0,0 +1,132 @@
+import { useRef, useState } from 'react'
+// import './App.less'
+import { Button ,Spin,message} from 'antd';
+import { AudioOutlined ,RedditOutlined} from '@ant-design/icons';
+import 'antd/dist/reset.css';
+import  { Configuration, OpenAIApi } from "openai";
+
+//问答列表
+interface answerListType{
+	type: string, //类型
+	content?: string, //内容
+	isFinshed: number, //是否完成
+}
+
+//作为一个函数组件
+function ChatGPT() {
+	//输入
+  	const [inputValue, setInputValue] = useState<string>("")
+	//问答列表
+	const [answerList, setAnswerList] = useState<answerListType[]>([])
+	
+	const preItem = useRef<HTMLDivElement>(null)
+//message解构出来的
+	const [messageApi,contextHolder] = message.useMessage();
+
+	//openai的配置参数,包括key
+	const configuration = new Configuration({
+		apiKey: "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",
+	});
+
+	//构造openai
+	const openai = new OpenAIApi(configuration);
+
+//提问
+	const quize = ()=>{
+
+		console.log("提问",inputValue)
+		setAnswerList([...answerList,{
+			type:'question', //提问
+			content:inputValue, //输入
+			isFinshed: 1, //已完成
+		},{
+			type: 'answer', //回答
+			content: '正在思考',//正在思考
+			isFinshed: 0, //未完成
+		}])
+		console.log("resf",preItem)
+		preItem?.current?.scrollIntoView()
+		//清空输入框
+		setInputValue("")
+		getMessage(inputValue)
+	}
+
+	const getMessage = async (questionText:string)=>{
+		openai.createCompletion({
+			model: "text-davinci-003", //用哪个模型
+			prompt: questionText, //提问内容
+			max_tokens: 600, //最大...?
+			temperature: 1, //?
+		}).then((response )=>{ //拿到结果
+			// 打印 API 返回的结果
+			console.log(response,response.data.choices[0],answerList);
+			setAnswerList(answerList=>answerList.map((item,index)=>{
+					if(index<answerList.length-1){
+						return item
+					}else{
+						return {
+							type: 'answer', //回答
+							content: response?.data?.choices[0]?.text?.replace(/\n{2}/, " "), //这里改动了什么?
+							isFinshed: 1, //是否完成
+						}
+					}
+				}))
+		}).catch((e)=>{
+			messageApi.open({ //弹出信息
+				type: 'error',
+				content: '你的 Key 失效了!',
+			  });
+		})
+	}
+
+  return (
+    <div className="ChatGPT">
+		{contextHolder}
+		<div className='input_box'>
+			<input type="text" className='input' value={inputValue} onChange={e=>setInputValue(e.target.value)}></input>
+			<Button 
+				type="primary" 
+				size='large' 
+				className='btn'
+				onClick={quize}
+			>提问</Button>
+		</div>
+		<div className='answer_box'>
+			<div className='answer'>{
+				answerList.map((item,index)=>{
+					if(item.type==='question'){
+						// 问题
+						return (
+							<div className='item' key={index}>
+								<div><AudioOutlined style={{fontSize:'24px',color:'#646cffaa'}}/> </div>
+								<span className='span'>{item.content}</span>
+							</div>
+						)
+					}else{
+						// 回答
+						if(item.isFinshed === 0){
+							// 加载中
+							return (
+								<div className='item' ref={preItem} key={index}>
+									<div><RedditOutlined style={{fontSize:'24px',color:'#42b883aa'}}/> </div>
+									<span className='span'><Spin /></span>
+								</div>
+							)
+						}else{
+							// 回复答案
+							return (
+								<div className='item' key={index}>
+									<div><RedditOutlined style={{fontSize:'24px',color:'#42b883aa'}}/> </div> 
+									<span className='span'>{item.content}</span>
+								</div>
+							)
+						}
+					}
+				})
+			}</div>
+		</div>
+    </div>
+  )
+}
+
+export default ChatGPT

+ 6 - 1
src/pages/index.tsx

@@ -14,6 +14,8 @@ import {IExplorerPanelItem} from "@dtinsight/molecule/esm/model";
 import {IActionBarItemProps} from "@dtinsight/molecule/esm/components";
 import {store} from "@/redux/store";
 
+//主页面初始化的逻辑,是主页
+
 const moInstance = create({
     extensions,
 });
@@ -30,6 +32,8 @@ moInstance.onBeforeInit(() => {
 })
 const MoleculeProvider = () => moInstance.render(<Workbench />);
 export default function HomePage() {
+
+    //如果用户未登录seecoder平台,弹出未登录通知,否则自动登录IDE
     useEffect(() => {
         if (!getCookie('token')) {
             notification.error({key: 'unLogin', message:'Not Logged In'})
@@ -91,6 +95,7 @@ export default function HomePage() {
     useEffect(() => {
         function handleBeforeLeave(e: BeforeUnloadEvent) {
             const { groups } = molecule.editor.getState();
+            //如果编辑区有打开文件
             if (groups?.length) {
                 // refer to: https://developer.mozilla.org/en-US/docs/Web/API/BeforeUnloadEvent
                 // prettier-ignore
@@ -102,7 +107,7 @@ export default function HomePage() {
             }
         }
         window.addEventListener('beforeunload', handleBeforeLeave);
-
+        //清除监听器
         return () => window.removeEventListener('beforeunload', handleBeforeLeave);
     }, []);
 

+ 66 - 41
src/pages/seec/result.tsx

@@ -7,25 +7,26 @@ import LogPane from "@/components/logPane";
 import {SyncOutlined} from "@ant-design/icons";
 import {store} from "@/redux/store";
 
-interface IRecordProps {
+interface IRecordProps { //这个题目在coder上判题的结果,也就是一次提交一次record
     id?: number
     number?: number
-    status?: BuildStatus
-    consoleOutput?: string
-    score?: number
-    commit?: {
-        id?: number
-        createdAt?: number
+    status?: BuildStatus //构建状态
+    consoleOutput?: string //控制台输出
+    score?: number //分数
+    commit?: { //是哪次提交的record
+        id?: number //id
+        createdAt?: number //创建时间
     }
-    failures?: Array<IFailureListProps>
+    failures?: Array<IFailureListProps> //测试用例failure列表
 }
 
-interface IFailureListProps {
-    name?: string
-    details?: string
+interface IFailureListProps { //测试用例failure
+    name?: string //失败名
+    details?: string //详细信息
     trace?: string
 }
 
+//将构建状态描述转化成需要显示的描述
 function buildStatusToTagType(status?: BuildStatus) {
     switch (status) {
         case 'RUNNING':
@@ -41,87 +42,110 @@ function buildStatusToTagType(status?: BuildStatus) {
     return 'default'
 }
 
-const columns: ProColumns<IFailureListProps>[] = [
-    {
-        title: 'Failure Test Case',
-        dataIndex: 'name',
-        key: 'name'
-    },
+
+const columns: ProColumns<IFailureListProps>[] = [ //列信息,一个对象是一列,这里只有一列
+    { 
+        title: 'Failure Test Case',  //列标题
+        dataIndex: 'name', //列数据
+        key: 'name' //key
+    },//与Table中的dataSource应该对应
 ]
 export default () => {
+    //题目代码的运行及测试记录
     const [record, setRecord] = useState<IRecordProps>({})
+    //控制台输出是否可见
     const [logModalVisible, setLogModalVisible] = useState(false)
+    //是否加载
     const [loading, setLoading] = useState<boolean>(false);
+    
+    //刷新
     const handleRefresh = async () => {
+        //从状态管理获取考试
         const examId = store.getState().loadQuestion.examId
+        //如果没有考试,则提示选择一个考试再查看结果
         if (!examId) message.error("Please select an exam")
         else {
+            //如果有考试,则提示用户正在加载这场考试的结果
             const hide = message.loading('Please wait, Result is loading...', 0)
+            //把正在加载的标志设为true
             setLoading(true)
+            //由考试id及token获取resultId,由resultId获取result全部信息,result全部信息包含了result中所有commit的信息
             const exam: ReceivedData<ExamSerializer> = await api.getByExamId(examId!)
             const resultId = exam.res?.result?.id
             const examResult: ReceivedData<ResultSerializer> = await api.getByResultId(resultId!)
             const commits = examResult.res?.commits
             // commits!.find((commit => commit.created_at === examResult.res?.lastCommitAt))
             if (commits && commits.length > 0) {
+                //取最近的commit记录,这个commit记录的record才是这个用户这场考试的目前最新的结果
                 const commit = commits[0]
-                if (commit.finished) {
+                if (commit.finished) { //如果已经完成提交、运行并测试的过程
                     const commitId = commit.id
                     const commitResult: ReceivedData<CommitSerializer> = await api.getByCommitId(commitId!)
+                    //取最近的判题记录id
                     const recordId = commitResult.res?.records![0].id
+                    //通过判题记录id拿到判题record数据
                     const data = await api.getByRecordId(recordId!) as ReceivedData<IRecordProps>
+                    //如果成功获取数据,则更新record
                     if (data.err === 0) setRecord(data.res || {})
                     else {
+                        //否则提示获取数据错误
                         message.error('Error while searching result')
                     }
                     console.log(data)
-                } else {
-                    message.info("Program is running...")
+                } else { //如果最近的这个提交还没有完成提交、运行并测试的过程
+                    message.info("Program is running...") //提示程序正在运行
                 }
-            } else {
+            } else { //如果还未提交过
                 //test
                 // const data = await api.getRecordTest(1) as ReceivedData<IRecordProps>
                 // setRecord(data.res || {})
-                message.info("Please Submit your commit first")
+                message.info("Please Submit your commit first") //提示你必须先提交
             }
-            setLoading(false)
+            setLoading(false) //把正在加载的状态设为false
             hide()
         }
     };
 
+    //初始化页面时,刷新一遍数据
     useEffect(() => {
         (async () => { await handleRefresh()})()
     }, [])
+    
+    //展示控制台输出方法
     const showConsoleOutput = () => {
+        //即将LogModal组件设为可见
         setLogModalVisible(true)
     }
 
+    //隐藏控制台输出方法
     const hideConsoleOutput = () => {
+        //即将组件设为不可见
         setLogModalVisible(false)
     }
 
     return (
         <div>
-            <ProTable<IFailureListProps>
-                rowKey="name"
-                columns={columns}
-                toolbar={{
+            <ProTable<IFailureListProps> //高级表格,IFa...是dataType
+                rowKey="name" //行的key
+                columns={columns} //表格的列:name,只有一列
+                toolbar={{ //工具栏
                     actions: [
                         <Button type = "primary" disabled={loading} onClick={() => showConsoleOutput()}>
                             Console
-                        </Button>,
+                        </Button>,//控制控制台输出的显隐
                         <Button shape="circle" icon={<SyncOutlined />} loading={loading} onClick={() => handleRefresh()}>
-                        </Button>
+                        </Button> //刷新结果
                     ]
                 }}
-                dataSource={record.failures}
-                tableExtraRender={(_, data) => (
-                    <Card>
+                dataSource={record.failures} //数据 是所有失败的测试用例运行结果的列表
+                //自定义表格的主体函数
+                tableExtraRender={(_, data) => ( //这个card描述了这个题目的总的结果:构建状态、测试用例失败数、分数
+                    <Card> 
                         <Descriptions size="small" column={3}>
                             <Descriptions.Item label="Status">
-                                <Tag color={buildStatusToTagType(record.status)}>
+                                <Tag color={buildStatusToTagType(record.status)}> 
                                     {record.status}
-                                </Tag>
+                                </Tag> 
                             </Descriptions.Item>
                             <Descriptions.Item label="Failure Case">{data.length}</Descriptions.Item>
                             <Descriptions.Item label="Score">
@@ -132,24 +156,25 @@ export default () => {
                         </Descriptions>
                     </Card>
                 )}
-                pagination={{
-                    pageSize: 10
+                pagination={{ //分页器
+                    pageSize: 10 //每页展示十条记录
                 }}
-                expandable={{
-                    expandedRowRender: record => <Card>
+                expandable={{ //配置展开功能
+                    //配置额外的展开行
+                    expandedRowRender: record => <Card> 
                             <pre>
                                 {record.trace}
                             </pre>
-                    </Card>
+                    </Card>//这个题目的记录的总的结果的trace
                 }}
                 search={false}
                 options={false}
             />
 
             <Modal
-                title = 'Console Output'
+                title = 'Console Output' //展示控制台输出
                 onCancel={hideConsoleOutput}
-                open= {logModalVisible}
+                open= {logModalVisible} //控制是否可见
                 centered
                 footer={null}
                 width={800}

+ 11 - 0
src/pages/welcome/hooks.ts

@@ -3,6 +3,8 @@ import { constants } from '@dtinsight/molecule/esm/services/builtinService/const
 import { KeybindingHelper } from '@dtinsight/molecule/esm/services/keybinding';
 import { useEffect, useState } from 'react';
 
+
+//这里仅知道功能的名字,还不知道内置数据中,将这些绑定给了哪个快捷键
 const KEYBINDINGS = () => [
     {
         id: constants.ACTION_QUICK_COMMAND,
@@ -18,7 +20,11 @@ const KEYBINDINGS = () => [
     },
 ];
 
+//返回一个快捷键和功能对应的数据结构
+//需要到内置数据中查找绑定给了哪个快捷键
 export const useGetKeys = () => {
+
+    //keys是状态,不断修改这个状态,用KEYBINDINGS()的数据
     const [keys, setKeys] = useState<
         {
             keybindings: string;
@@ -28,10 +34,13 @@ export const useGetKeys = () => {
     >([]);
 
     useEffect(() => {
+        //res在KEYBINDINGS()基础上添加了一列
         const res = KEYBINDINGS()
             .map((acessCommand) => {
                 const simpleKeybindings =
+                //查询内置的快捷键绑定
                     KeybindingHelper.queryGlobalKeybinding(acessCommand.id);
+                    //如果绑定了,转化为string格式的keybindings,也就是描述
                 if (simpleKeybindings?.length) {
                     const keybindings =
                         KeybindingHelper.convertSimpleKeybindingToString(
@@ -39,9 +48,11 @@ export const useGetKeys = () => {
                         );
                     return { ...acessCommand, keybindings };
                 }
+                //如果没绑定,转化为null
                 return null;
             })
             .filter(Boolean);
+
         setKeys(
             res as {
                 keybindings: string;

+ 7 - 1
src/pages/welcome/index.tsx

@@ -3,18 +3,24 @@ import Logo from './logo';
 import { prefixClaName } from '@dtinsight/molecule/esm/common/className';
 import { useGetKeys } from './hooks';
 
+//这是一个欢迎页,有logo,有描述快捷键及功能的提示文字
+//logo是svg画的seecoder logo图片,存在本地。
 export default function Welcome() {
+    
+    //获取已挑选好的三个快捷功能名和快捷键名的对应关系的数据结构
     const keys = useGetKeys();
 
     return (
         <div className={prefixClaName('welcome')}>
-            <Logo className="logo" />
+            <Logo className="logo" />  
             <h1 className="title" style={{width: "155px"}}>SEECODER</h1>
             <div className="keybindings">
                 <ul>
                     {keys.map((item) => {
+                        //用无序列表展示快捷键,展示快捷键的name和keybindings(快捷键的string描述形式)
                         return (
                             <li className="keys" key={item.id}>
+                                
                                 <span>{item.name}</span>
                                 <span>
                                     {item.keybindings.split('').join(' ')}

+ 1 - 0
src/pages/welcome/logo.tsx

@@ -1,6 +1,7 @@
 import React from 'react';
 
 // @ts-ignore
+//这是一个用svg画的logo,采用压缩后的数据表示图像,优点是比较小,可以直接保存在项目里面,不需要请求
 export default function ({ className }) {
     return (
         <span style={{ fontSize: 0 }}>

+ 35 - 0
src/utils/index.ts

@@ -34,16 +34,23 @@ export function deleteCookie(name: string, domain?: string, path: string = '/')
     document.cookie = `${name}=; expires=${d.toUTCString()}${cookieDomain}; path=${path}`;
 }
 
+//更新账户活动项的右键菜单,通过更改molecule.activityBar的状态数据实现
 export function updateAccountContext(contextMenu: IActivityMenuItemProps[]) {
+    //找到现有的所有状态项描述数据
     const nextData = molecule.activityBar.getState().data || [];
+    //查阅内置的账户状态项的id
     const {ACTIVITY_BAR_GLOBAL_ACCOUNT} = molecule.builtin.getConstants();
+    //找到这个状态项描述数据
     const target = nextData.find((item) => item.id === ACTIVITY_BAR_GLOBAL_ACCOUNT);
     if (target) {
+        //contextMenu也是有一定格式的数据结构,里面可包含事件函数,比如onClick()
         target.contextMenu = contextMenu;
     }
+    //更新状态数据
     molecule.activityBar.setState({data: nextData});
 }
 
+//登录IDE
 export function goToLogin() {
     if (!store.getState().loadUser.username) {
         Api.current().then(response => {
@@ -86,23 +93,34 @@ export function goToLogin() {
     }
 }
 
+//获取题目列表
 export function getQuestionList(examId: number) {
+    //如果要获取的这个考试的题目列表和状态管理中当前的考试相同
     if (examId === store.getState().loadQuestion.examId) {
+        //则取出状态管理中的题目列表
         return store.getState().loadQuestionList
+        //如果不是,是请求新的考试的题目列表
     } else {
+        //通过考试id获取题目列表
         Api.getByExamId(examId).then((res: ReceivedData<ExamSerializer>) => {
+            //如果获取成功,没有错误
             if (res.err === 0) {
                 const exam = res.res
+                //如果这场考试不为空,且题目列表不为空
                 if (exam && exam.problems) {
+                    //触发加载题目列表事件
                     store.dispatch(loadQuestionList(exam.problems))
                     return res
                 }
             }
+            //如果获取题目列表失败,提示:"获取题目列表失败"
             else {
                 notification.error({key: 'QuestionError', message: res.msg? res.msg: 'Get QuestionsList Error'})
             }
+            //如果捕捉到了请求失败的原因
         }).catch(reason => {
                 console.log(reason)
+                //提示请求失败的原因
                 notification.error({key: 'QuestionError', message: reason})
             }
         )
@@ -122,34 +140,51 @@ const extToLang: { [prop: string]: string } = {
 }
 let id = 1
 
+//提交题目
 export function submitQuestion() {
+    //取出状态管理中的考试id
     const examId = store.getState().loadQuestion.examId
+    //如果没有考试,则提示:"请选择一个考试"
     if (!examId) message.error("Please select an exam")
+    //如果有考试
     else {
+        //获取考试的所有题目
         const question = store.getState().loadQuestion
+        //获取当前题目
         const current = store.getState().loadCurrentQuestion
+        //获取当前题目id
         const questionId = current.questionId
+        //如果当前考试所有题目存在,且代码列表存在
         if (question && question.codeList) {
+            //更新当前考试所有题目代码中的当前题目的代码为current里的代码
             for (let i = 0; i < question.codeList.length; i++){
                 if (question.codeList[i].questionId === current.questionId) {
                     question.codeList[i] = current
+                    //触发状态改变,更新question
                     store.dispatch(loadQuestion(question))
                 }
             }
         }
+        //向coder提交要更新的题目代码
         api.updateOnlineCodeByExamIdAndQuestionId(examId!, questionId!, {
+            //仅更新editable部分
             code: store.getState().loadCurrentQuestion.editable!
         }).then(r => {
+            //如果提交成功,将所有的文件名颜色设为默认色
             if (r.err === 0) {
                 const elList = window.document.querySelectorAll("div.mo-tree__treenode span.mo-tree__treenode__title")
                 elList.forEach((el) => {
                     const span = el as HTMLElement
                     span.style.color = ''
                 })
+                //提示:"Success"
                 message.success("Success")
             }
+            //如果提交失败
             else {
+                //提示:"Failure"
                 message.error("Failure")
+                //通知具体的错误信息
                 notification.error({key: "SubmitError", message: r.msg})
             }
         }).catch(r => message.error("Failure"))