Merge pull request #98 from b3log/1.1.0

1.1.0
This commit is contained in:
Liang Ding 2014-10-26 18:29:50 +08:00
commit 7fc41ca5fb
10 changed files with 138 additions and 76 deletions

View File

@ -36,6 +36,7 @@ type User struct {
Password string Password string
Workspace string // 该用户的工作空间 GOPATH 路径 Workspace string // 该用户的工作空间 GOPATH 路径
Locale string Locale string
GoFormat string
LatestSessionContent *LatestSessionContent LatestSessionContent *LatestSessionContent
} }
@ -83,7 +84,7 @@ func FixedTimeCheckEnv() {
os.Exit(-1) os.Exit(-1)
} }
gocode := Wide.GetGocode() gocode := Wide.GetExecutableInGOBIN("gocode")
cmd := exec.Command(gocode, "close") cmd := exec.Command(gocode, "close")
_, err := cmd.Output() _, err := cmd.Output()
if nil != err { if nil != err {
@ -91,7 +92,7 @@ func FixedTimeCheckEnv() {
glog.Warningf("Not found gocode [%s]", gocode) glog.Warningf("Not found gocode [%s]", gocode)
} }
ide_stub := Wide.GetIDEStub() ide_stub := Wide.GetExecutableInGOBIN("ide_stub")
cmd = exec.Command(ide_stub, "version") cmd = exec.Command(ide_stub, "version")
_, err = cmd.Output() _, err = cmd.Output()
if nil != err { if nil != err {
@ -131,11 +132,23 @@ func (c *conf) GetWorkspace() string {
return filepath.FromSlash(strings.Replace(c.Workspace, "{pwd}", c.Pwd, 1)) return filepath.FromSlash(strings.Replace(c.Workspace, "{pwd}", c.Pwd, 1))
} }
// 获取 user 的工作空间路径. // 获取 username 指定的用户的 Go 源码格式化工具路径,查找不到时返回 "gofmt".
func (user *User) getWorkspace() string { func (c *conf) GetGoFmt(username string) string {
ret := strings.Replace(user.Workspace, "{pwd}", Wide.Pwd, 1) for _, user := range c.Users {
if user.Name == username {
switch user.GoFormat {
case "gofmt":
return "gofmt"
case "goimports":
return c.GetExecutableInGOBIN("goimports")
default:
glog.Errorf("Unsupported Go Format tool [%s]", user.GoFormat)
return "gofmt"
}
}
}
return filepath.FromSlash(ret) return "gofmt"
} }
// 获取 username 指定的用户配置. // 获取 username 指定的用户配置.
@ -149,40 +162,40 @@ func (*conf) GetUser(username string) *User {
return nil return nil
} }
// 获取 gocode 路径. // 获取 GOBIN 中 executable 指定的文件路径.
func (*conf) GetGocode() string { //
return getGOBIN() + "gocode" // 函数内部会判断操作系统,如果是 Windows 则在 executable 实参后加入 .exe 后缀.
} func (*conf) GetExecutableInGOBIN(executable string) string {
if util.OS.IsWindows() {
// 获取 ide_stub 路径. executable += ".exe"
func (*conf) GetIDEStub() string {
return getGOBIN() + "ide_stub"
}
// 获取 GOBIN 路径,末尾带路径分隔符.
func getGOBIN() string {
// $GOBIN/
ret := os.Getenv("GOBIN")
if "" != ret {
return ret + PathSeparator
} }
// $GOPATH/bin/$GOOS_$GOARCH/ gopaths := strings.Split(os.Getenv("GOPATH"), PathListSeparator)
ret = os.Getenv("GOPATH") + PathSeparator + "bin" + PathSeparator +
os.Getenv("GOOS") + "_" + os.Getenv("GOARCH") for _, gopath := range gopaths {
if isExist(ret) { // $GOPATH/bin/$GOOS_$GOARCH/executable
return ret + PathSeparator ret := gopath + PathSeparator + "bin" + PathSeparator +
os.Getenv("GOOS") + "_" + os.Getenv("GOARCH") + PathSeparator + executable
if isExist(ret) {
return ret
}
// $GOPATH/bin/{runtime.GOOS}_{runtime.GOARCH}/executable
ret = gopath + PathSeparator + "bin" + PathSeparator +
runtime.GOOS + "_" + runtime.GOARCH + PathSeparator + executable
if isExist(ret) {
return ret
}
// $GOPATH/bin/executable
ret = gopath + PathSeparator + "bin" + PathSeparator + executable
if isExist(ret) {
return ret
}
} }
// $GOPATH/bin/{runtime.GOOS}_{runtime.GOARCH}/ // $GOBIN/executable
ret = os.Getenv("GOPATH") + PathSeparator + "bin" + PathSeparator + return os.Getenv("GOBIN") + PathSeparator + executable
runtime.GOOS + "_" + runtime.GOARCH
if isExist(ret) {
return ret + PathSeparator
}
// $GOPATH/bin/
return os.Getenv("GOPATH") + PathSeparator + "bin" + PathSeparator
} }
// 保存 Wide 配置. // 保存 Wide 配置.

View File

@ -18,6 +18,7 @@
"Password": "admin", "Password": "admin",
"Workspace": "{pwd}/data/user_workspaces/admin", "Workspace": "{pwd}/data/user_workspaces/admin",
"Locale": "zh_CN", "Locale": "zh_CN",
"GoFormat": "gofmt",
"LatestSessionContent": { "LatestSessionContent": {
"FileTree": [], "FileTree": [],
"Files": [], "Files": [],

View File

@ -56,7 +56,7 @@ func WSHandler(w http.ResponseWriter, r *http.Request) {
// glog.Infof("offset: %d", offset) // glog.Infof("offset: %d", offset)
gocode := conf.Wide.GetGocode() gocode := conf.Wide.GetExecutableInGOBIN("gocode")
argv := []string{"-f=json", "autocomplete", strconv.Itoa(offset)} argv := []string{"-f=json", "autocomplete", strconv.Itoa(offset)}
var output bytes.Buffer var output bytes.Buffer
@ -124,16 +124,18 @@ func AutocompleteHandler(w http.ResponseWriter, r *http.Request) {
// glog.Infof("offset: %d", offset) // glog.Infof("offset: %d", offset)
userWorkspace := conf.Wide.GetUserWorkspace(username) userWorkspace := conf.Wide.GetUserWorkspace(username)
workspaces := strings.Split(userWorkspace, conf.PathListSeparator)
libPath := ""
for _, workspace := range workspaces {
userLib := workspace + conf.PathSeparator + "pkg" + conf.PathSeparator +
runtime.GOOS + "_" + runtime.GOARCH
libPath += userLib + conf.PathListSeparator
}
//glog.Infof("User [%s] workspace [%s]", username, userWorkspace) glog.V(5).Infof("gocode set lib-path %s", libPath)
userLib := userWorkspace + conf.PathSeparator + "pkg" + conf.PathSeparator +
runtime.GOOS + "_" + runtime.GOARCH
libPath := userLib
//glog.Infof("gocode set lib-path %s", libPath)
// FIXME: 使用 gocode set lib-path 在多工作空间环境下肯定是有问题的,需要考虑其他实现方式 // FIXME: 使用 gocode set lib-path 在多工作空间环境下肯定是有问题的,需要考虑其他实现方式
gocode := conf.Wide.GetGocode() gocode := conf.Wide.GetExecutableInGOBIN("gocode")
argv := []string{"set", "lib-path", libPath} argv := []string{"set", "lib-path", libPath}
exec.Command(gocode, argv...).Run() exec.Command(gocode, argv...).Run()
@ -205,7 +207,7 @@ func GetExprInfoHandler(w http.ResponseWriter, r *http.Request) {
// glog.Infof("offset [%d]", offset) // glog.Infof("offset [%d]", offset)
// TODO: 目前是调用 liteide_stub 工具来查找声明,后续需要重新实现 // TODO: 目前是调用 liteide_stub 工具来查找声明,后续需要重新实现
ide_stub := conf.Wide.GetIDEStub() ide_stub := conf.Wide.GetExecutableInGOBIN("ide_stub")
argv := []string{"type", "-cursor", filename + ":" + strconv.Itoa(offset), "-info", "."} argv := []string{"type", "-cursor", filename + ":" + strconv.Itoa(offset), "-info", "."}
cmd := exec.Command(ide_stub, argv...) cmd := exec.Command(ide_stub, argv...)
cmd.Dir = curDir cmd.Dir = curDir
@ -279,7 +281,7 @@ func FindDeclarationHandler(w http.ResponseWriter, r *http.Request) {
// glog.Infof("offset [%d]", offset) // glog.Infof("offset [%d]", offset)
// TODO: 目前是调用 liteide_stub 工具来查找声明,后续需要重新实现 // TODO: 目前是调用 liteide_stub 工具来查找声明,后续需要重新实现
ide_stub := conf.Wide.GetIDEStub() ide_stub := conf.Wide.GetExecutableInGOBIN("ide_stub")
argv := []string{"type", "-cursor", filename + ":" + strconv.Itoa(offset), "-def", "."} argv := []string{"type", "-cursor", filename + ":" + strconv.Itoa(offset), "-def", "."}
cmd := exec.Command(ide_stub, argv...) cmd := exec.Command(ide_stub, argv...)
cmd.Dir = curDir cmd.Dir = curDir
@ -361,7 +363,7 @@ func FindUsagesHandler(w http.ResponseWriter, r *http.Request) {
// glog.Infof("offset [%d]", offset) // glog.Infof("offset [%d]", offset)
// TODO: 目前是调用 liteide_stub 工具来查找使用,后续需要重新实现 // TODO: 目前是调用 liteide_stub 工具来查找使用,后续需要重新实现
ide_stub := conf.Wide.GetIDEStub() ide_stub := conf.Wide.GetExecutableInGOBIN("ide_stub")
argv := []string{"type", "-cursor", filename + ":" + strconv.Itoa(offset), "-use", "."} argv := []string{"type", "-cursor", filename + ":" + strconv.Itoa(offset), "-use", "."}
cmd := exec.Command(ide_stub, argv...) cmd := exec.Command(ide_stub, argv...)
cmd.Dir = curDir cmd.Dir = curDir

View File

@ -5,24 +5,30 @@ import (
"net/http" "net/http"
"os" "os"
"os/exec" "os/exec"
"runtime"
"strings"
"github.com/88250/gohtml" "github.com/88250/gohtml"
"github.com/b3log/wide/conf"
"github.com/b3log/wide/session"
"github.com/b3log/wide/util" "github.com/b3log/wide/util"
"github.com/golang/glog" "github.com/golang/glog"
) )
// TODO: 加入 goimports 格式化 Go 源码文件 // 格式化 Go 源码文件.
// 根据用户的 GoFormat 配置选择格式化工具:
// gofmt 格式化 Go 源码文件. // 1. gofmt
// 2. goimports
func GoFmtHandler(w http.ResponseWriter, r *http.Request) { func GoFmtHandler(w http.ResponseWriter, r *http.Request) {
data := map[string]interface{}{"succ": true} data := map[string]interface{}{"succ": true}
defer util.RetJSON(w, r, data) defer util.RetJSON(w, r, data)
decoder := json.NewDecoder(r.Body) session, _ := session.HTTPSession.Get(r, "wide-session")
username := session.Values["username"].(string)
var args map[string]interface{} var args map[string]interface{}
if err := decoder.Decode(&args); err != nil { if err := json.NewDecoder(r.Body).Decode(&args); err != nil {
glog.Error(err) glog.Error(err)
data["succ"] = false data["succ"] = false
@ -31,6 +37,12 @@ func GoFmtHandler(w http.ResponseWriter, r *http.Request) {
filePath := args["file"].(string) filePath := args["file"].(string)
apiPath := runtime.GOROOT() + conf.PathSeparator + "src" + conf.PathSeparator + "pkg"
if strings.HasPrefix(filePath, apiPath) { // 如果是 Go API 源码文件
// 忽略修改
return
}
fout, err := os.Create(filePath) fout, err := os.Create(filePath)
if nil != err { if nil != err {
@ -50,8 +62,10 @@ func GoFmtHandler(w http.ResponseWriter, r *http.Request) {
return return
} }
fmt := conf.Wide.GetGoFmt(username)
argv := []string{filePath} argv := []string{filePath}
cmd := exec.Command("gofmt", argv...) cmd := exec.Command(fmt, argv...)
bytes, _ := cmd.Output() bytes, _ := cmd.Output()
output := string(bytes) output := string(bytes)

View File

@ -45,9 +45,24 @@ func GetFiles(w http.ResponseWriter, r *http.Request) {
session, _ := session.HTTPSession.Get(r, "wide-session") session, _ := session.HTTPSession.Get(r, "wide-session")
username := session.Values["username"].(string) username := session.Values["username"].(string)
userSrc := conf.Wide.GetUserWorkspace(username) + conf.PathSeparator + "src" userWorkspace := conf.Wide.GetUserWorkspace(username)
workspaces := strings.Split(userWorkspace, conf.PathListSeparator)
root := FileNode{Name: "projects", Path: userSrc, IconSkin: "ico-ztree-dir ", Type: "d", FileNodes: []*FileNode{}} root := FileNode{Name: "root", Path: "", IconSkin: "ico-ztree-dir ", Type: "d", FileNodes: []*FileNode{}}
// 工作空间节点处理
for _, workspace := range workspaces {
workspacePath := workspace + conf.PathSeparator + "src"
workspaceNode := FileNode{Name: workspace[strings.LastIndex(workspace, conf.PathSeparator)+1:] + " (" +
workspace + ")",
Path: workspacePath, IconSkin: "ico-ztree-dir ", Type: "d", FileNodes: []*FileNode{}}
walk(workspacePath, &workspaceNode)
// 添加工作空间节点
root.FileNodes = append(root.FileNodes, &workspaceNode)
}
// 构造 Go API 节点 // 构造 Go API 节点
apiPath := runtime.GOROOT() + conf.PathSeparator + "src" + conf.PathSeparator + "pkg" apiPath := runtime.GOROOT() + conf.PathSeparator + "src" + conf.PathSeparator + "pkg"
@ -65,9 +80,6 @@ func GetFiles(w http.ResponseWriter, r *http.Request) {
close(goapiBuildOKSignal) close(goapiBuildOKSignal)
}() }()
// 构造用户工作空间文件树
walk(userSrc, &root)
// 等待放行 // 等待放行
<-goapiBuildOKSignal <-goapiBuildOKSignal
@ -381,28 +393,30 @@ func createFile(path, fileType string) bool {
case "f": case "f":
file, err := os.OpenFile(path, os.O_CREATE, 0664) file, err := os.OpenFile(path, os.O_CREATE, 0664)
if nil != err { if nil != err {
glog.Info(err) glog.Error(err)
return false return false
} }
defer file.Close() defer file.Close()
glog.Infof("Created file [%s]", path) glog.V(5).Infof("Created file [%s]", path)
return true return true
case "d": case "d":
err := os.Mkdir(path, 0775) err := os.Mkdir(path, 0775)
if nil != err { if nil != err {
glog.Info(err) glog.Error(err)
return false
} }
glog.Infof("Created directory [%s]", path) glog.V(5).Infof("Created directory [%s]", path)
return true return true
default: default:
glog.Infof("Unsupported file type [%s]", fileType) glog.Errorf("Unsupported file type [%s]", fileType)
return false return false
} }

21
main.go
View File

@ -53,8 +53,6 @@ func init() {
// 登录. // 登录.
func loginHandler(w http.ResponseWriter, r *http.Request) { func loginHandler(w http.ResponseWriter, r *http.Request) {
i18n.Load()
if "GET" == r.Method { if "GET" == r.Method {
// 展示登录页面 // 展示登录页面
@ -126,8 +124,6 @@ func logoutHandler(w http.ResponseWriter, r *http.Request) {
// Wide 首页. // Wide 首页.
func indexHandler(w http.ResponseWriter, r *http.Request) { func indexHandler(w http.ResponseWriter, r *http.Request) {
i18n.Load()
httpSession, _ := session.HTTPSession.Get(r, "wide-session") httpSession, _ := session.HTTPSession.Get(r, "wide-session")
if httpSession.IsNew { if httpSession.IsNew {
@ -175,8 +171,6 @@ func serveSingle(pattern string, filename string) {
// 起始页请求处理. // 起始页请求处理.
func startHandler(w http.ResponseWriter, r *http.Request) { func startHandler(w http.ResponseWriter, r *http.Request) {
i18n.Load()
httpSession, _ := session.HTTPSession.Get(r, "wide-session") httpSession, _ := session.HTTPSession.Get(r, "wide-session")
if httpSession.IsNew { if httpSession.IsNew {
@ -209,8 +203,6 @@ func startHandler(w http.ResponseWriter, r *http.Request) {
// 键盘快捷键页请求处理. // 键盘快捷键页请求处理.
func keyboardShortcutsHandler(w http.ResponseWriter, r *http.Request) { func keyboardShortcutsHandler(w http.ResponseWriter, r *http.Request) {
i18n.Load()
httpSession, _ := session.HTTPSession.Get(r, "wide-session") httpSession, _ := session.HTTPSession.Get(r, "wide-session")
if httpSession.IsNew { if httpSession.IsNew {
@ -241,8 +233,6 @@ func keyboardShortcutsHandler(w http.ResponseWriter, r *http.Request) {
// 关于页请求处理. // 关于页请求处理.
func aboutHandler(w http.ResponseWriter, r *http.Request) { func aboutHandler(w http.ResponseWriter, r *http.Request) {
i18n.Load()
httpSession, _ := session.HTTPSession.Get(r, "wide-session") httpSession, _ := session.HTTPSession.Get(r, "wide-session")
if httpSession.IsNew { if httpSession.IsNew {
@ -356,10 +346,21 @@ func main() {
func handlerWrapper(f func(w http.ResponseWriter, r *http.Request)) func(w http.ResponseWriter, r *http.Request) { func handlerWrapper(f func(w http.ResponseWriter, r *http.Request)) func(w http.ResponseWriter, r *http.Request) {
handler := panicRecover(f) handler := panicRecover(f)
handler = stopwatch(handler) handler = stopwatch(handler)
handler = i18nLoad(handler)
return handler return handler
} }
// 国际化处理包装.
func i18nLoad(handler func(w http.ResponseWriter, r *http.Request)) func(w http.ResponseWriter, r *http.Request) {
return func(w http.ResponseWriter, r *http.Request) {
i18n.Load()
// Handler 处理
handler(w, r)
}
}
// Handler 包装请求计时. // Handler 包装请求计时.
func stopwatch(handler func(w http.ResponseWriter, r *http.Request)) func(w http.ResponseWriter, r *http.Request) { func stopwatch(handler func(w http.ResponseWriter, r *http.Request)) func(w http.ResponseWriter, r *http.Request) {
return func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, r *http.Request) {

View File

@ -225,7 +225,7 @@ func BuildHandler(w http.ResponseWriter, r *http.Request) {
} }
suffix := "" suffix := ""
if "windows" == runtime.GOOS { if util.OS.IsWindows() {
suffix = ".exe" suffix = ".exe"
} }
executable := "main" + suffix executable := "main" + suffix

View File

@ -25,8 +25,6 @@ var ShellWS = map[string]*util.WSChannel{}
// Shell 首页. // Shell 首页.
func IndexHandler(w http.ResponseWriter, r *http.Request) { func IndexHandler(w http.ResponseWriter, r *http.Request) {
i18n.Load()
httpSession, _ := session.HTTPSession.Get(r, "wide-session") httpSession, _ := session.HTTPSession.Get(r, "wide-session")
if httpSession.IsNew { if httpSession.IsNew {

View File

@ -509,6 +509,10 @@ var wide = {
wide.run(); wide.run();
return false; return false;
} }
if (!wide.curProcessId) {
return false;
}
var request = newWideRequest(); var request = newWideRequest();
request.pid = wide.curProcessId; request.pid = wide.curProcessId;

15
util/os.go Normal file
View File

@ -0,0 +1,15 @@
package util
import (
"runtime"
)
type myos struct{}
// 操作系统工具.
var OS = myos{}
// 判断是否是 Windows 操作系统.
func (*myos) IsWindows() bool {
return "windows" == runtime.GOOS
}