diff --git a/editor/editors.go b/editor/editors.go index ee8cd47..04edc38 100644 --- a/editor/editors.go +++ b/editor/editors.go @@ -1,4 +1,4 @@ -// 编辑器操作. +// Editor manipulations. package editor import ( @@ -11,6 +11,7 @@ import ( "runtime" "strconv" "strings" + "time" "github.com/b3log/wide/conf" "github.com/b3log/wide/file" @@ -20,23 +21,24 @@ import ( "github.com/gorilla/websocket" ) -var editorWS = map[string]*websocket.Conn{} - -// 建立编辑器通道. +// WSHandler handles request of creating editor channel. func WSHandler(w http.ResponseWriter, r *http.Request) { - session, _ := session.HTTPSession.Get(r, "wide-session") - sid := session.Values["id"].(string) + httpSession, _ := session.HTTPSession.Get(r, "wide-session") + sid := httpSession.Values["id"].(string) - editorWS[sid], _ = websocket.Upgrade(w, r, nil, 1024, 1024) + conn, _ := websocket.Upgrade(w, r, nil, 1024, 1024) + editorChan := util.WSChannel{Sid: sid, Conn: conn, Request: r, Time: time.Now()} + + session.EditorWS[sid] = &editorChan ret := map[string]interface{}{"output": "Editor initialized", "cmd": "init-editor"} - editorWS[sid].WriteJSON(&ret) + editorChan.Conn.WriteJSON(&ret) - glog.Infof("Open a new [Editor] with session [%s], %d", sid, len(editorWS)) + glog.Infof("Open a new [Editor] with session [%s], %d", sid, len(session.EditorWS)) args := map[string]interface{}{} for { - if err := editorWS[sid].ReadJSON(&args); err != nil { + if err := session.EditorWS[sid].Conn.ReadJSON(&args); err != nil { if err.Error() == "EOF" { return } @@ -73,14 +75,14 @@ func WSHandler(w http.ResponseWriter, r *http.Request) { ret = map[string]interface{}{"output": string(output.Bytes()), "cmd": "autocomplete"} - if err := editorWS[sid].WriteJSON(&ret); err != nil { + if err := session.EditorWS[sid].Conn.WriteJSON(&ret); err != nil { glog.Error("Editor WS ERROR: " + err.Error()) return } } } -// 自动完成(代码补全). +// AutocompleteHandler handles request of code autocompletion. func AutocompleteHandler(w http.ResponseWriter, r *http.Request) { var args map[string]interface{} @@ -133,7 +135,7 @@ func AutocompleteHandler(w http.ResponseWriter, r *http.Request) { glog.V(5).Infof("gocode set lib-path %s", libPath) - // FIXME: 使用 gocode set lib-path 在多工作空间环境下肯定是有问题的,需要考虑其他实现方式 + // FIXME: using gocode set lib-path has some issues while accrossing workspaces gocode := conf.Wide.GetExecutableInGOBIN("gocode") argv := []string{"set", "lib-path", libPath} exec.Command(gocode, argv...).Run() @@ -157,7 +159,7 @@ func AutocompleteHandler(w http.ResponseWriter, r *http.Request) { w.Write(output) } -// 查看表达式信息. +// GetExprInfoHandler handles request of getting expression infomation. func GetExprInfoHandler(w http.ResponseWriter, r *http.Request) { data := map[string]interface{}{"succ": true} defer util.RetJSON(w, r, data) @@ -203,7 +205,6 @@ func GetExprInfoHandler(w http.ResponseWriter, r *http.Request) { // glog.Infof("offset [%d]", offset) - // TODO: 目前是调用 liteide_stub 工具来查找声明,后续需要重新实现 ide_stub := conf.Wide.GetExecutableInGOBIN("ide_stub") argv := []string{"type", "-cursor", filename + ":" + strconv.Itoa(offset), "-info", "."} cmd := exec.Command(ide_stub, argv...) @@ -229,7 +230,7 @@ func GetExprInfoHandler(w http.ResponseWriter, r *http.Request) { data["info"] = exprInfo } -// 查找声明. +// FindDeclarationHandler handles request of finding declaration. func FindDeclarationHandler(w http.ResponseWriter, r *http.Request) { data := map[string]interface{}{"succ": true} defer util.RetJSON(w, r, data) @@ -275,7 +276,6 @@ func FindDeclarationHandler(w http.ResponseWriter, r *http.Request) { // glog.Infof("offset [%d]", offset) - // TODO: 目前是调用 liteide_stub 工具来查找声明,后续需要重新实现 ide_stub := conf.Wide.GetExecutableInGOBIN("ide_stub") argv := []string{"type", "-cursor", filename + ":" + strconv.Itoa(offset), "-def", "."} cmd := exec.Command(ide_stub, argv...) @@ -309,7 +309,7 @@ func FindDeclarationHandler(w http.ResponseWriter, r *http.Request) { data["cursorCh"] = cursorCh } -// 查找使用. +// FindUsagesHandler handles request of finding usages. func FindUsagesHandler(w http.ResponseWriter, r *http.Request) { data := map[string]interface{}{"succ": true} defer util.RetJSON(w, r, data) @@ -355,7 +355,6 @@ func FindUsagesHandler(w http.ResponseWriter, r *http.Request) { offset := getCursorOffset(code, line, ch) // glog.Infof("offset [%d]", offset) - // TODO: 目前是调用 liteide_stub 工具来查找使用,后续需要重新实现 ide_stub := conf.Wide.GetExecutableInGOBIN("ide_stub") argv := []string{"type", "-cursor", filename + ":" + strconv.Itoa(offset), "-use", "."} cmd := exec.Command(ide_stub, argv...) @@ -396,18 +395,19 @@ func FindUsagesHandler(w http.ResponseWriter, r *http.Request) { data["founds"] = usages } -// 计算光标偏移位置. +// getCursorOffset calculates the cursor offset. // -// line 指定了行号(第一行为 0),ch 指定了列号(第一列为 0). +// line is the line number, starts with 0 that means the first line +// ch is the column number, starts with 0 that means the first column func getCursorOffset(code string, line, ch int) (offset int) { lines := strings.Split(code, "\n") - // 计算前几行长度 + // calculate sum length of lines before for i := 0; i < line; i++ { offset += len(lines[i]) } - // 计算当前行、当前列长度 + // calculate length of the current line and column curLine := lines[line] var buffer bytes.Buffer r := []rune(curLine) @@ -415,8 +415,8 @@ func getCursorOffset(code string, line, ch int) (offset int) { buffer.WriteString(string(r[i])) } - offset += line // 加换行符 - offset += len(buffer.String()) // 加当前行列偏移 + offset += len(buffer.String()) // append length of current line + offset += line // append number of '\n' return offset } diff --git a/editor/formatter.go b/editor/formatter.go index 74cb070..5e7f283 100644 --- a/editor/formatter.go +++ b/editor/formatter.go @@ -15,8 +15,9 @@ import ( "github.com/golang/glog" ) -// 格式化 Go 源码文件. -// 根据用户的 GoFormat 配置选择格式化工具: +// GoFmtHandler handles request of formatting Go source code. +// +// This function will select a format tooll based on user's configuration: // 1. gofmt // 2. goimports func GoFmtHandler(w http.ResponseWriter, r *http.Request) { @@ -38,8 +39,8 @@ func GoFmtHandler(w http.ResponseWriter, r *http.Request) { filePath := args["file"].(string) apiPath := runtime.GOROOT() + conf.PathSeparator + "src" + conf.PathSeparator + "pkg" - if strings.HasPrefix(filePath, apiPath) { // 如果是 Go API 源码文件 - // 忽略修改 + if strings.HasPrefix(filePath, apiPath) { // if it is Go API source code + // ignore it return } @@ -88,8 +89,8 @@ func GoFmtHandler(w http.ResponseWriter, r *http.Request) { } } -// 格式化 HTML 文件. -// FIXME:依赖的工具 gohtml 格式化 HTML 时有问题 +// HTMLFmtHandler handles request of formatting HTML source code. +// FIXME: gohtml has some issues... func HTMLFmtHandler(w http.ResponseWriter, r *http.Request) { data := map[string]interface{}{"succ": true} defer util.RetJSON(w, r, data) @@ -143,68 +144,3 @@ func HTMLFmtHandler(w http.ResponseWriter, r *http.Request) { return } } - -// 格式化 JSON 文件. -func JSONFmtHandler(w http.ResponseWriter, r *http.Request) { - data := map[string]interface{}{"succ": true} - defer util.RetJSON(w, r, data) - - var args map[string]interface{} - - if err := json.NewDecoder(r.Body).Decode(&args); err != nil { - glog.Error(err) - data["succ"] = false - - return - } - - filePath := args["file"].(string) - - fout, err := os.Create(filePath) - - if nil != err { - glog.Error(err) - data["succ"] = false - - return - } - - code := args["code"].(string) - - fout.WriteString(code) - if err := fout.Close(); nil != err { - glog.Error(err) - data["succ"] = false - - return - } - - obj := new(interface{}) - if err := json.Unmarshal([]byte(code), &obj); nil != err { - glog.Error(err) - data["succ"] = false - - return - } - - glog.Info(obj) - - bytes, err := json.MarshalIndent(obj, "", " ") - if nil != err { - data["succ"] = false - - return - } - - code = string(bytes) - data["code"] = code - - fout, err = os.Create(filePath) - fout.WriteString(code) - if err := fout.Close(); nil != err { - glog.Error(err) - data["succ"] = false - - return - } -} diff --git a/event/events.go b/event/events.go index e5fe7d0..7f28277 100644 --- a/event/events.go +++ b/event/events.go @@ -1,53 +1,52 @@ -// 事件处理. +// Event manipulations. package event import "github.com/golang/glog" const ( - EvtCodeGOPATHNotFound = iota // 事件代码:找不到环境变量 $GOPATH - EvtCodeGOROOTNotFound // 事件代码:找不到环境变量 $GOROOT - EvtCodeGocodeNotFound // 事件代码:找不到 gocode - EvtCodeIDEStubNotFound // 事件代码:找不到 IDE stub - EvtCodeServerInternalError // 事件代码:服务器内部错误 + EvtCodeGOPATHNotFound = iota // event code: not found $GOPATH env variable + EvtCodeGOROOTNotFound // event code: not found $GOROOT env variable + EvtCodeGocodeNotFound // event code: not found gocode + EvtCodeIDEStubNotFound // event code: not found ide_stub + EvtCodeServerInternalError // event code: server internal error ) -// 事件队列最大长度. +// Max length of queue. const MaxQueueLength = 10 -// 事件结构. +// Event. type Event struct { - Code int `json:"code"` // 事件代码 - Sid string `json:"sid"` // 用户会话 id - Data interface{} `json:"data"` // 事件数据 + Code int `json:"code"` // event code + Sid string `json:"sid"` // wide session id related + Data interface{} `json:"data"` // event data } -// 全局事件队列. +// Global event queue. // -// 入队的事件将分发到每个用户的事件队列中. +// Every event in this queue will be dispatched to each user event queue. var EventQueue = make(chan *Event, MaxQueueLength) -// 用户事件队列. +// User event queue. type UserEventQueue struct { - Sid string // 关联的会话 id - Queue chan *Event // 队列 - Handlers []Handler // 事件处理器集 + Sid string // wide session id related + Queue chan *Event // queue + Handlers []Handler // event handlers } -// 事件队列集类型. type Queues map[string]*UserEventQueue -// 用户事件队列集. +// User event queues. // // var UserEventQueues = Queues{} -// 加载事件处理. +// Load initializes the event handling. func Load() { go func() { for event := range EventQueue { - glog.V(5).Infof("收到全局事件 [%d]", event.Code) + glog.V(5).Infof("Received a global event [code=%d]", event.Code) - // 将事件分发到每个用户的事件队列里 + // dispatch the event to each user event queue for _, userQueue := range UserEventQueues { event.Sid = userQueue.Sid @@ -57,14 +56,14 @@ func Load() { }() } -// 为用户队列添加事件处理器. +// AddHandler adds the specified handlers to user event queues. func (uq *UserEventQueue) AddHandler(handlers ...Handler) { for _, handler := range handlers { uq.Handlers = append(uq.Handlers, handler) } } -// 初始化一个用户事件队列. +// New initializes a user event queue with the specified wide session id. func (ueqs Queues) New(sid string) *UserEventQueue { q := ueqs[sid] if nil != q { @@ -80,11 +79,11 @@ func (ueqs Queues) New(sid string) *UserEventQueue { ueqs[sid] = q - go func() { // 队列开始监听事件 + go func() { // start listening for evt := range q.Queue { glog.V(5).Infof("Session [%s] received a event [%d]", sid, evt.Code) - // 将事件交给事件处理器进行处理 + // process event by each handlers for _, handler := range q.Handlers { handler.Handle(evt) } @@ -94,7 +93,7 @@ func (ueqs Queues) New(sid string) *UserEventQueue { return q } -// 关闭一个用户事件队列. +// Close closes a user event queue with the specified wide session id. func (ueqs Queues) Close(sid string) { q := ueqs[sid] if nil == q { @@ -104,15 +103,15 @@ func (ueqs Queues) Close(sid string) { delete(ueqs, sid) } -// 事件处理接口. +// Type of event handler. type Handler interface { Handle(event *Event) } -// 函数指针包装. +// Type of handler function. type HandleFunc func(event *Event) -// 事件处理默认实现. +// Default implementation of event handling. func (fn HandleFunc) Handle(event *Event) { fn(event) } diff --git a/file/files.go b/file/files.go index 13efade..95c3b71 100644 --- a/file/files.go +++ b/file/files.go @@ -23,7 +23,7 @@ type FileNode struct { Name string `json:"name"` Path string `json:"path"` IconSkin string `json:"iconSkin"` // Value should be end with a space - Type string `json:"type"` // "f": file,"d": directory + Type string `json:"type"` // "f": file, "d": directory Mode string `json:"mode"` FileNodes []*FileNode `json:"children"` } diff --git a/i18n/locales.go b/i18n/locales.go index 632caa5..edc0a02 100644 --- a/i18n/locales.go +++ b/i18n/locales.go @@ -1,4 +1,4 @@ -// 国际化操作. +// Internationalization manipulations. package i18n import ( @@ -10,16 +10,17 @@ import ( "github.com/golang/glog" ) +// Locale. type locale struct { Name string Langs map[string]interface{} TimeZone string } -// 所有的 locales. +// All locales. var Locales = map[string]locale{} -// 加载国际化配置. +// Load loads i18n message configurations. func Load() { f, _ := os.Open("i18n") names, _ := f.Readdirnames(-1) @@ -57,12 +58,12 @@ func load(localeStr string) { glog.V(5).Infof("Loaded [%s] locale configuration", localeStr) } -// 获取语言配置项. +// Get gets message with the specified locale and key. func Get(locale, key string) interface{} { return Locales[locale].Langs[key] } -// 获取语言配置. +// GetAll gets all messages with the specified locale. func GetAll(locale string) map[string]interface{} { return Locales[locale].Langs } diff --git a/main.go b/main.go index 1e56ad8..8272e05 100644 --- a/main.go +++ b/main.go @@ -309,7 +309,6 @@ func main() { http.HandleFunc("/find/decl", handlerWrapper(editor.FindDeclarationHandler)) http.HandleFunc("/find/usages", handlerWrapper(editor.FindUsagesHandler)) http.HandleFunc("/html/fmt", handlerWrapper(editor.HTMLFmtHandler)) - http.HandleFunc("/json/fmt", handlerWrapper(editor.JSONFmtHandler)) // shell http.HandleFunc("/shell/ws", handlerWrapper(shell.WSHandler)) diff --git a/notification/notifications.go b/notification/notifications.go index a479037..011012d 100644 --- a/notification/notifications.go +++ b/notification/notifications.go @@ -1,4 +1,4 @@ -// 通知. +// Notification manipulations. package notification import ( @@ -16,15 +16,15 @@ import ( ) const ( - Error = "ERROR" // 通知.严重程度:ERROR - Warn = "WARN" // 通知.严重程度:WARN - Info = "INFO" // 通知.严重程度:INFO + Error = "ERROR" // notification.severity: ERROR + Warn = "WARN" // notification.severity: WARN + Info = "INFO" // notification.severity: INFO - Setup = "Setup" // 通知.类型:安装 - Server = "Server" // 通知.类型:服务器 + Setup = "Setup" // notification.type: setup + Server = "Server" // notification.type: server ) -// 通知结构. +// Notification. type Notification struct { event *event.Event Type string `json:"type"` @@ -32,9 +32,9 @@ type Notification struct { Message string `json:"message"` } -// 用户事件处理:将事件转为通知,并通过通知通道推送给前端. +// event2Notification processes user event by converting the specified event to a notification, and then push it to front +// browser with notification channel. // -// 当用户事件队列接收到事件时将会调用该函数进行处理. func event2Notification(e *event.Event) { if nil == session.NotificationWS[e.Sid] { return @@ -71,7 +71,7 @@ func event2Notification(e *event.Event) { wsChannel.Refresh() } -// 建立通知通道. +// WSHandler handles request of creating notification channel. func WSHandler(w http.ResponseWriter, r *http.Request) { sid := r.URL.Query()["sid"][0] @@ -89,7 +89,7 @@ func WSHandler(w http.ResponseWriter, r *http.Request) { glog.V(4).Infof("Open a new [Notification] with session [%s], %d", sid, len(session.NotificationWS)) - // 添加用户事件处理器 + // add user event handler wSession.EventQueue.AddHandler(event.HandleFunc(event2Notification)) input := map[string]interface{}{} diff --git a/output/outputs.go b/output/outputs.go index 55d8a8b..f3591a7 100644 --- a/output/outputs.go +++ b/output/outputs.go @@ -1,4 +1,4 @@ -// 构建、运行、go tool 操作. +// Build, run and go tool manipulations. package output import ( @@ -25,11 +25,11 @@ import ( ) const ( - lintSeverityError = "error" // Lint 严重级别:错误 - lintSeverityWarn = "warning" // Lint 严重级别:警告 + lintSeverityError = "error" // lint severity: error + lintSeverityWarn = "warning" // lint severity: warning ) -// 代码 Lint 结构. +// Code lint. type Lint struct { File string `json:"file"` LineNo int `json:"lineNo"` @@ -37,7 +37,7 @@ type Lint struct { Msg string `json:"msg"` } -// 建立输出通道. +// WSHandler handles request of creating output channel. func WSHandler(w http.ResponseWriter, r *http.Request) { sid := r.URL.Query()["sid"][0] @@ -52,7 +52,7 @@ func WSHandler(w http.ResponseWriter, r *http.Request) { glog.V(4).Infof("Open a new [Output] with session [%s], %d", sid, len(session.OutputWS)) } -// 运行一个可执行文件. +// RunHandler handles request of executing a binary file. func RunHandler(w http.ResponseWriter, r *http.Request) { data := map[string]interface{}{"succ": true} defer util.RetJSON(w, r, data) @@ -105,7 +105,7 @@ func RunHandler(w http.ResponseWriter, r *http.Request) { return } - // 添加到用户进程集中 + // add the process to user's process set processes.add(wSession, cmd.Process) channelRet := map[string]interface{}{} @@ -117,7 +117,7 @@ func RunHandler(w http.ResponseWriter, r *http.Request) { glog.V(3).Infof("Session [%s] is running [id=%d, file=%s]", sid, runningId, filePath) - // 在读取程序输出前先返回一次,使前端获取到 run 状态与 pid + // push once for front-end to get the 'run' state and pid if nil != session.OutputWS[sid] { wsChannel := session.OutputWS[sid] @@ -129,15 +129,14 @@ func RunHandler(w http.ResponseWriter, r *http.Request) { return } - // 更新通道最近使用时间 - wsChannel.Time = time.Now() + wsChannel.Refresh() } for { buf, err := reader.ReadBytes('\n') if nil != err || 0 == len(buf) { - // 从用户进程集中移除这个执行完毕(或是被主动停止)的进程 + // remove the exited process from user process set processes.remove(wSession, cmd.Process) glog.V(3).Infof("Session [%s] 's running [id=%d, file=%s] has done", sid, runningId, filePath) @@ -153,8 +152,7 @@ func RunHandler(w http.ResponseWriter, r *http.Request) { break } - // 更新通道最近使用时间 - wsChannel.Time = time.Now() + wsChannel.Refresh() } break @@ -170,15 +168,14 @@ func RunHandler(w http.ResponseWriter, r *http.Request) { break } - // 更新通道最近使用时间 - wsChannel.Time = time.Now() + wsChannel.Refresh() } } } }(rand.Int()) } -// 构建可执行文件. +// BuildHandler handles request of building. func BuildHandler(w http.ResponseWriter, r *http.Request) { data := map[string]interface{}{"succ": true} defer util.RetJSON(w, r, data) @@ -237,7 +234,7 @@ func BuildHandler(w http.ResponseWriter, r *http.Request) { executable = filepath.Join(curDir, executable) - // 先把可执行文件删了 + // remove executable file before building err = os.RemoveAll(executable) if nil != err { glog.Info(err) @@ -269,7 +266,7 @@ func BuildHandler(w http.ResponseWriter, r *http.Request) { channelRet := map[string]interface{}{} if nil != session.OutputWS[sid] { - // 在前端 output 中显示“开始构建” + // display "START [go build]" in front-end browser channelRet["output"] = "" + i18n.Get(locale, "start-build").(string) + "\n" channelRet["cmd"] = "start-build" @@ -282,8 +279,7 @@ func BuildHandler(w http.ResponseWriter, r *http.Request) { return } - // 更新通道最近使用时间 - wsChannel.Time = time.Now() + wsChannel.Refresh() } reader := bufio.NewReader(io.MultiReader(stdout, stderr)) @@ -301,19 +297,18 @@ func BuildHandler(w http.ResponseWriter, r *http.Request) { glog.V(3).Infof("Session [%s] is building [id=%d, dir=%s]", sid, runningId, curDir) - // 一次性读取 + // read all buf, _ := ioutil.ReadAll(reader) channelRet := map[string]interface{}{} channelRet["cmd"] = "build" channelRet["executable"] = executable - if 0 == len(buf) { // 说明构建成功,没有错误信息输出 - // 设置下一次执行命令(前端会根据该参数发送请求) + if 0 == len(buf) { // build success channelRet["nextCmd"] = args["nextCmd"] channelRet["output"] = "" + i18n.Get(locale, "build-succ").(string) + "\n" - go func() { // 运行 go install,生成的库用于 gocode lib-path + go func() { // go install, for subsequent gocode lib-path cmd := exec.Command("go", "install") cmd.Dir = curDir @@ -324,15 +319,16 @@ func BuildHandler(w http.ResponseWriter, r *http.Request) { glog.Warning(string(out)) } }() - } else { // 构建失败 - // 解析错误信息,返回给编辑器 gutter lint + } else { // build error + // build gutter lint + errOut := string(buf) channelRet["output"] = "" + i18n.Get(locale, "build-error").(string) + "\n" + errOut lines := strings.Split(errOut, "\n") if lines[0][0] == '#' { - lines = lines[1:] // 跳过第一行 + lines = lines[1:] // skip the first line } lints := []*Lint{} @@ -343,7 +339,7 @@ func BuildHandler(w http.ResponseWriter, r *http.Request) { } if line[0] == '\t' { - // 添加到上一个 lint 中 + // append to the last lint last := len(lints) msg := lints[last-1].Msg msg += line @@ -385,14 +381,13 @@ func BuildHandler(w http.ResponseWriter, r *http.Request) { glog.Error(err) } - // 更新通道最近使用时间 - wsChannel.Time = time.Now() + wsChannel.Refresh() } }(rand.Int()) } -// go test. +// GoTestHandler handles request of go test. func GoTestHandler(w http.ResponseWriter, r *http.Request) { data := map[string]interface{}{"succ": true} defer util.RetJSON(w, r, data) @@ -443,7 +438,7 @@ func GoTestHandler(w http.ResponseWriter, r *http.Request) { channelRet := map[string]interface{}{} if nil != session.OutputWS[sid] { - // 在前端 output 中显示“开始 go test + // display "START [go test]" in front-end browser channelRet["output"] = "" + i18n.Get(locale, "start-test").(string) + "\n" channelRet["cmd"] = "start-test" @@ -456,8 +451,7 @@ func GoTestHandler(w http.ResponseWriter, r *http.Request) { return } - // 更新通道最近使用时间 - wsChannel.Time = time.Now() + wsChannel.Refresh() } reader := bufio.NewReader(io.MultiReader(stdout, stderr)) @@ -477,10 +471,10 @@ func GoTestHandler(w http.ResponseWriter, r *http.Request) { channelRet := map[string]interface{}{} channelRet["cmd"] = "go test" - // 一次性读取 + // read all buf, _ := ioutil.ReadAll(reader) - // 同步点,等待 go test 执行完成 + // waiting for go test finished cmd.Wait() if !cmd.ProcessState.Success() { @@ -501,13 +495,12 @@ func GoTestHandler(w http.ResponseWriter, r *http.Request) { glog.Error(err) } - // 更新通道最近使用时间 - wsChannel.Time = time.Now() + wsChannel.Refresh() } }(rand.Int()) } -// go install. +// GoInstallHandler handles request of go install. func GoInstallHandler(w http.ResponseWriter, r *http.Request) { data := map[string]interface{}{"succ": true} defer util.RetJSON(w, r, data) @@ -560,7 +553,7 @@ func GoInstallHandler(w http.ResponseWriter, r *http.Request) { channelRet := map[string]interface{}{} if nil != session.OutputWS[sid] { - // 在前端 output 中显示“开始 go install” + // display "START [go install]" in front-end browser channelRet["output"] = "" + i18n.Get(locale, "start-install").(string) + "\n" channelRet["cmd"] = "start-install" @@ -573,8 +566,7 @@ func GoInstallHandler(w http.ResponseWriter, r *http.Request) { return } - // 更新通道最近使用时间 - wsChannel.Time = time.Now() + wsChannel.Refresh() } reader := bufio.NewReader(io.MultiReader(stdout, stderr)) @@ -592,19 +584,20 @@ func GoInstallHandler(w http.ResponseWriter, r *http.Request) { glog.V(3).Infof("Session [%s] is running [go install] [id=%d, dir=%s]", sid, runningId, curDir) - // 一次性读取 + // read all buf, _ := ioutil.ReadAll(reader) channelRet := map[string]interface{}{} channelRet["cmd"] = "go install" - if 0 != len(buf) { // 构建失败 - // 解析错误信息,返回给编辑器 gutter lint + if 0 != len(buf) { // build error + // build gutter lint + errOut := string(buf) lines := strings.Split(errOut, "\n") if lines[0][0] == '#' { - lines = lines[1:] // 跳过第一行 + lines = lines[1:] // skip the first line } lints := []*Lint{} @@ -615,7 +608,7 @@ func GoInstallHandler(w http.ResponseWriter, r *http.Request) { } if line[0] == '\t' { - // 添加到上一个 lint 中 + // append to the last lint last := len(lints) msg := lints[last-1].Msg msg += line @@ -661,14 +654,13 @@ func GoInstallHandler(w http.ResponseWriter, r *http.Request) { glog.Error(err) } - // 更新通道最近使用时间 - wsChannel.Time = time.Now() + wsChannel.Refresh() } }(rand.Int()) } -// go get. +// GoGetHandler handles request of go get. func GoGetHandler(w http.ResponseWriter, r *http.Request) { data := map[string]interface{}{"succ": true} defer util.RetJSON(w, r, data) @@ -719,7 +711,7 @@ func GoGetHandler(w http.ResponseWriter, r *http.Request) { channelRet := map[string]interface{}{} if nil != session.OutputWS[sid] { - // 在前端 output 中显示“开始 go get + // display "START [go get]" in front-end browser channelRet["output"] = "" + i18n.Get(locale, "start-get").(string) + "\n" channelRet["cmd"] = "start-get" @@ -732,8 +724,7 @@ func GoGetHandler(w http.ResponseWriter, r *http.Request) { return } - // 更新通道最近使用时间 - wsChannel.Time = time.Now() + wsChannel.Refresh() } reader := bufio.NewReader(io.MultiReader(stdout, stderr)) @@ -754,7 +745,7 @@ func GoGetHandler(w http.ResponseWriter, r *http.Request) { channelRet := map[string]interface{}{} channelRet["cmd"] = "go get" - // 一次性读取 + // read all buf, _ := ioutil.ReadAll(reader) if 0 != len(buf) { @@ -776,13 +767,12 @@ func GoGetHandler(w http.ResponseWriter, r *http.Request) { glog.Error(err) } - // 更新通道最近使用时间 - wsChannel.Time = time.Now() + wsChannel.Refresh() } }(rand.Int()) } -// 结束正在运行的进程. +// StopHandler handles request of stoping a running process. func StopHandler(w http.ResponseWriter, r *http.Request) { data := map[string]interface{}{"succ": true} defer util.RetJSON(w, r, data) diff --git a/output/processes.go b/output/processes.go index 32d3634..ef6c533 100644 --- a/output/processes.go +++ b/output/processes.go @@ -8,18 +8,18 @@ import ( "github.com/golang/glog" ) -// 进程集类型. +// Type of process set. type procs map[string][]*os.Process -// 所有用户正在运行的程序进程集. +// Processse of all users. // // var processes = procs{} -// 排它锁,防止并发修改. +// Exclusive lock. var mutex sync.Mutex -// 添加用户执行进程. +// add adds the specified process to the user process set. func (procs *procs) add(wSession *session.WideSession, proc *os.Process) { mutex.Lock() defer mutex.Unlock() @@ -30,13 +30,13 @@ func (procs *procs) add(wSession *session.WideSession, proc *os.Process) { userProcesses = append(userProcesses, proc) (*procs)[sid] = userProcesses - // 会话关联进程 + // bind process with wide session wSession.SetProcesses(userProcesses) glog.V(3).Infof("Session [%s] has [%d] processes", sid, len((*procs)[sid])) } -// 移除用户执行进程. +// remove removes the specified process from the user process set. func (procs *procs) remove(wSession *session.WideSession, proc *os.Process) { mutex.Lock() defer mutex.Unlock() @@ -48,10 +48,10 @@ func (procs *procs) remove(wSession *session.WideSession, proc *os.Process) { var newProcesses []*os.Process for i, p := range userProcesses { if p.Pid == proc.Pid { - newProcesses = append(userProcesses[:i], userProcesses[i+1:]...) + newProcesses = append(userProcesses[:i], userProcesses[i+1:]...) // remove it (*procs)[sid] = newProcesses - // 会话关联进程 + // bind process with wide session wSession.SetProcesses(newProcesses) glog.V(3).Infof("Session [%s] has [%d] processes", sid, len((*procs)[sid])) @@ -61,7 +61,7 @@ func (procs *procs) remove(wSession *session.WideSession, proc *os.Process) { } } -// 结束用户正在执行的进程. +// kill kills a process specified by the given pid. func (procs *procs) kill(wSession *session.WideSession, pid int) { mutex.Lock() defer mutex.Unlock() @@ -80,7 +80,7 @@ func (procs *procs) kill(wSession *session.WideSession, pid int) { newProcesses = append(userProcesses[:i], userProcesses[i+1:]...) (*procs)[sid] = newProcesses - // 会话关联进程 + // bind process with wide session wSession.SetProcesses(newProcesses) glog.V(3).Infof("Killed a process [pid=%d] of session [%s]", pid, sid) diff --git a/session/sessions.go b/session/sessions.go index abf75a1..d073391 100644 --- a/session/sessions.go +++ b/session/sessions.go @@ -1,11 +1,11 @@ -// 会话操作. +// Session manipulations. // -// Wide 服务器端需要维护两种会话: +// Wide server side needs maintain two kinds of sessions: // -// 1. HTTP 会话:主要用于验证登录 -// 2. Wide 会话:浏览器 tab 打开/刷新会创建一个,并和 HTTP 会话进行关联 +// 1. HTTP session: mainly used for login authentication +// 2. Wide session: browser tab open/refresh will create one, and associates with HTTP session // -// 当会话失效时:释放所有和该会话相关的资源,例如运行中的程序进程、事件队列等. +// When a session gone: release all resources associated with it, such as running processes, event queues. package session import ( @@ -26,52 +26,55 @@ import ( ) const ( - SessionStateActive = iota // 会话状态:活的 - SessionStateClosed // 会话状态:已关闭(这个状态目前暂时没有使用到) + SessionStateActive = iota // session state: active + SessionStateClosed // session state: closed (not used so far) ) var ( - // 会话通道. + // Session channels. SessionWS = map[string]*util.WSChannel{} - // 输出通道. + // Editor channels. + EditorWS = map[string]*util.WSChannel{} + + // Output channels. OutputWS = map[string]*util.WSChannel{} - // 通知通道. + // Notification channels. NotificationWS = map[string]*util.WSChannel{} ) -// 用户 HTTP 会话,用于验证登录. +// HTTP session store. var HTTPSession = sessions.NewCookieStore([]byte("BEYOND")) -// Wide 会话,对应一个浏览器 tab. +// Wide session, associated with a browser tab. type WideSession struct { - Id string // 唯一标识 - Username string // 用户名 - HTTPSession *sessions.Session // 关联的 HTTP 会话 - Processes []*os.Process // 关联的进程集 - EventQueue *event.UserEventQueue // 关联的事件队列 - State int // 状态 - Content *conf.LatestSessionContent // 最近一次会话内容 - Created time.Time // 创建时间 - Updated time.Time // 最近一次使用时间 + Id string // id + Username string // username + HTTPSession *sessions.Session // HTTP session related + Processes []*os.Process // process set + EventQueue *event.UserEventQueue // event queue + State int // state + Content *conf.LatestSessionContent // the latest session content + Created time.Time // create time + Updated time.Time // the latest use time } -// 会话集类型. +// Type of wide sessions. type Sessions []*WideSession -// 所有 Wide 会话集. +// Wide sessions. var WideSessions Sessions -// 排它锁,防止并发修改. +// Exclusive lock. var mutex sync.Mutex -// 在一些特殊情况(例如浏览器不间断刷新/在源代码视图刷新)下 Wide 会话集内会出现无效会话,该函数定时(1 小时)检查并移除这些无效会话. +// In some special cases (such as a browser uninterrupted refresh / refresh in the source code view) will occur +// some invalid sessions, the function checks and removes these invalid sessions periodically (1 hour). // -// 无效会话:在检查时间内 30 分钟都没有使用过的会话,参考 WideSession.Updated 字段. +// Invalid sessions: sessions that not used within 30 minutes, refers to WideSession.Updated field. func FixedTimeRelease() { go func() { - // 1 小时进行一次检查 for _ = range time.Tick(time.Hour) { hour, _ := time.ParseDuration("-30m") threshold := time.Now().Add(hour) @@ -87,7 +90,9 @@ func FixedTimeRelease() { }() } -// 建立会话通道. 通道断开时销毁会话状态,回收相关资源. +// WSHandler handles request of creating session channel. +// +// When a channel closed, releases all resources associated with it. func WSHandler(w http.ResponseWriter, r *http.Request) { sid := r.URL.Query()["sid"][0] wSession := WideSessions.Get(sid) @@ -129,7 +134,7 @@ func WSHandler(w http.ResponseWriter, r *http.Request) { } } -// 会话内容保存. +// SaveContent handles request of session content storing. func SaveContent(w http.ResponseWriter, r *http.Request) { data := map[string]interface{}{"succ": true} defer util.RetJSON(w, r, data) @@ -157,7 +162,7 @@ func SaveContent(w http.ResponseWriter, r *http.Request) { for _, user := range conf.Wide.Users { if user.Name == wSession.Username { - // 更新配置(内存变量),conf.FixedTimeSave() 会负责定时持久化 + // update the variable in-memory, conf.FixedTimeSave() function will persist it periodically user.LatestSessionContent = wSession.Content wSession.Refresh() @@ -167,19 +172,19 @@ func SaveContent(w http.ResponseWriter, r *http.Request) { } } -// 设置会话关联的进程集. +// SetProcesses binds process set with the wide session. func (s *WideSession) SetProcesses(ps []*os.Process) { s.Processes = ps s.Refresh() } -// 刷新会话最近一次使用时间. +// Refresh refreshes the channel by updating its use time. func (s *WideSession) Refresh() { s.Updated = time.Now() } -// 创建一个 Wide 会话. +// New creates a wide session. func (sessions *Sessions) New(httpSession *sessions.Session) *WideSession { mutex.Lock() defer mutex.Unlock() @@ -189,7 +194,7 @@ func (sessions *Sessions) New(httpSession *sessions.Session) *WideSession { id := strconv.Itoa(rand.Int()) now := time.Now() - // 创建用户事件队列 + // create user event queue userEventQueue := event.UserEventQueues.New(id) ret := &WideSession{ @@ -208,7 +213,7 @@ func (sessions *Sessions) New(httpSession *sessions.Session) *WideSession { return ret } -// 获取 Wide 会话. +// Get gets a wide session with the specified session id. func (sessions *Sessions) Get(sid string) *WideSession { mutex.Lock() defer mutex.Unlock() @@ -222,26 +227,26 @@ func (sessions *Sessions) Get(sid string) *WideSession { return nil } -// 移除 Wide 会话,释放相关资源. +// Remove removes a wide session specified with the given session id, releases resources associated with it. // -// 会话相关资源: +// Session-related resources: // -// 1. 用户事件队列 -// 2. 运行中的进程集 -// 3. WebSocket 通道 +// 1. user event queue +// 2. process set +// 3. websocket channels func (sessions *Sessions) Remove(sid string) { mutex.Lock() defer mutex.Unlock() for i, s := range *sessions { if s.Id == sid { - // 从会话集中移除 + // remove from session set *sessions = append((*sessions)[:i], (*sessions)[i+1:]...) - // 关闭用户事件队列 + // close user event queue event.UserEventQueues.Close(sid) - // 杀进程 + // kill processes for _, p := range s.Processes { if err := p.Kill(); nil != err { glog.Errorf("Can't kill process [%d] of session [%s]", p.Pid, sid) @@ -250,7 +255,7 @@ func (sessions *Sessions) Remove(sid string) { } } - // 回收所有通道 + // close websocket channels if ws, ok := OutputWS[sid]; ok { ws.Close() delete(OutputWS, sid) @@ -268,12 +273,13 @@ func (sessions *Sessions) Remove(sid string) { glog.V(3).Infof("Removed a session [%s]", s.Id) - cnt := 0 // 统计当前 HTTP 会话关联的 Wide 会话数量 + cnt := 0 // count wide sessions associated with HTTP session for _, s := range *sessions { if s.HTTPSession.ID == s.HTTPSession.ID { cnt++ } } + glog.V(3).Infof("User [%s] has [%d] sessions", s.Username, cnt) return @@ -281,7 +287,7 @@ func (sessions *Sessions) Remove(sid string) { } } -// 获取 username 指定的用户的所有 Wide 会话. +// GetByUsername gets wide sessions. func (sessions *Sessions) GetByUsername(username string) []*WideSession { mutex.Lock() defer mutex.Unlock() diff --git a/session/users.go b/session/users.go index 99a4143..e33e304 100644 --- a/session/users.go +++ b/session/users.go @@ -1,6 +1,6 @@ package session -// TODO: 这个文件内的功能目前没有使用,只是开了个头 :p +// TODO: this file not used currently, just a beginning :p import ( "encoding/json" diff --git a/shell/shells.go b/shell/shells.go index a1f4a9b..f7bc754 100644 --- a/shell/shells.go +++ b/shell/shells.go @@ -18,12 +18,12 @@ import ( "github.com/gorilla/websocket" ) -// Shell 通道. +// Shell channel. // // > var ShellWS = map[string]*util.WSChannel{} -// Shell 首页. +// IndexHandler handles request of Shell index. func IndexHandler(w http.ResponseWriter, r *http.Request) { httpSession, _ := session.HTTPSession.Get(r, "wide-session") @@ -36,7 +36,7 @@ func IndexHandler(w http.ResponseWriter, r *http.Request) { httpSession.Options.MaxAge = conf.Wide.HTTPSessionMaxAge httpSession.Save(r, w) - // 创建一个 Wide 会话 + // create a wide session wideSession := session.WideSessions.New(httpSession) username := httpSession.Values["username"].(string) @@ -61,7 +61,7 @@ func IndexHandler(w http.ResponseWriter, r *http.Request) { t.Execute(w, model) } -// 建立 Shell 通道. +// WSHandler handles request of creating Shell channel. func WSHandler(w http.ResponseWriter, r *http.Request) { httpSession, _ := session.HTTPSession.Get(r, "wide-session") username := httpSession.Values["username"].(string) @@ -122,12 +122,10 @@ func WSHandler(w http.ResponseWriter, r *http.Request) { return } - // 更新通道最近使用时间 - wsChan.Time = time.Now() + wsChan.Refresh() } } -// 以管道方式执行多个命令. func pipeCommands(username string, commands ...*exec.Cmd) string { for i, command := range commands[:len(commands)-1] { setCmdEnv(command, username) @@ -147,7 +145,7 @@ func pipeCommands(username string, commands ...*exec.Cmd) string { out, err := last.CombinedOutput() - // 结束进程,释放资源 + // release resources for _, command := range commands[:len(commands)-1] { command.Wait() } diff --git a/util/net.go b/util/net.go index 0af6072..c2b1b2b 100644 --- a/util/net.go +++ b/util/net.go @@ -1,4 +1,4 @@ -// 工具. +// Utilities. package util import ( @@ -8,10 +8,10 @@ import ( type mynet struct{} -// 网络工具. +// Network utilities. var Net = mynet{} -// 获取第一块网卡的 IP 地址. +// LocalIP gets the first NIC's IP address. func (*mynet) LocalIP() (string, error) { tt, err := net.Interfaces() diff --git a/util/os.go b/util/os.go index 8517abe..5049832 100644 --- a/util/os.go +++ b/util/os.go @@ -9,15 +9,15 @@ import ( type myos struct{} -// 操作系统工具. +// OS utilities. var OS = myos{} -// 判断是否是 Windows 操作系统. +// IsWindows determines whether current OS is Windows. func (*myos) IsWindows() bool { return "windows" == runtime.GOOS } -// 获取当前执行程序的工作目录的绝对路径. +// Pwd gets the path of current working directory. func (*myos) Pwd() string { file, _ := exec.LookPath(os.Args[0]) pwd, _ := filepath.Abs(file) diff --git a/util/panic.go b/util/panic.go index c9f941b..38c1cb1 100644 --- a/util/panic.go +++ b/util/panic.go @@ -6,7 +6,7 @@ import ( "github.com/golang/glog" ) -// panic 恢复. +// Recover recovers a panic. func Recover() { if re := recover(); re != nil { glog.Errorf("PANIC RECOVERED:\n %v, %s", re, debug.Stack()) diff --git a/util/ret.go b/util/ret.go index 4c35476..4474d5e 100644 --- a/util/ret.go +++ b/util/ret.go @@ -7,7 +7,7 @@ import ( "github.com/golang/glog" ) -// HTTP 返回 JSON 统一处理. +// RetJSON writes HTTP response with "Content-Type, application/json". func RetJSON(w http.ResponseWriter, r *http.Request, res map[string]interface{}) { w.Header().Set("Content-Type", "application/json") diff --git a/util/websocket.go b/util/websocket.go index 6315614..04c15de 100644 --- a/util/websocket.go +++ b/util/websocket.go @@ -7,20 +7,20 @@ import ( "github.com/gorilla/websocket" ) -// 一个用户会话的 WebSocket 通道结构. +// WebSocket channel. type WSChannel struct { - Sid string // 用户会话 id - Conn *websocket.Conn // WebSocket 连接 - Request *http.Request // 关联的 HTTP 请求 - Time time.Time // 该通道最近一次使用时间 + Sid string // wide session id + Conn *websocket.Conn // websocket connection + Request *http.Request // HTTP request related + Time time.Time // the latest use time } -// 关闭通道. +// Close closed the channel. func (c *WSChannel) Close() { c.Conn.Close() } -// Refresh refreshes the channel by updating its Time. +// Refresh refreshes the channel by updating its use time. func (c *WSChannel) Refresh() { c.Time = time.Now() }