This commit is contained in:
Liang Ding 2015-02-15 14:24:49 +08:00
parent 45084f1628
commit 2565035b54
4 changed files with 211 additions and 3 deletions

View File

@ -172,6 +172,7 @@ func main() {
http.HandleFunc(conf.Wide.Context+"/playground/build", handlerWrapper(playground.BuildHandler))
http.HandleFunc(conf.Wide.Context+"/playground/run", handlerWrapper(playground.RunHandler))
http.HandleFunc(conf.Wide.Context+"/playground/stop", handlerWrapper(playground.StopHandler))
http.HandleFunc(conf.Wide.Context+"/playground/autocomplete", handlerWrapper(playground.AutocompleteHandler))
logger.Infof("Wide is running [%s]", conf.Wide.Server+conf.Wide.Context)

View File

@ -0,0 +1,97 @@
// Copyright (c) 2014-2015, b3log.org
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package playground
import (
"bytes"
"encoding/json"
"net/http"
"os/exec"
"strconv"
"strings"
"github.com/b3log/wide/session"
"github.com/b3log/wide/util"
)
// AutocompleteHandler handles request of code autocompletion.
func AutocompleteHandler(w http.ResponseWriter, r *http.Request) {
var args map[string]interface{}
if err := json.NewDecoder(r.Body).Decode(&args); err != nil {
logger.Error(err)
http.Error(w, err.Error(), 500)
return
}
session, _ := session.HTTPSession.Get(r, "wide-session")
if session.IsNew {
http.Error(w, "Forbidden", http.StatusForbidden)
return
}
code := args["code"].(string)
line := int(args["cursorLine"].(float64))
ch := int(args["cursorCh"].(float64))
offset := getCursorOffset(code, line, ch)
argv := []string{"-f=json", "autocomplete", strconv.Itoa(offset)}
gocode := util.Go.GetExecutableInGOBIN("gocode")
cmd := exec.Command(gocode, argv...)
stdin, _ := cmd.StdinPipe()
stdin.Write([]byte(code))
stdin.Close()
output, err := cmd.CombinedOutput()
if nil != err {
logger.Error(err)
http.Error(w, err.Error(), 500)
return
}
w.Header().Set("Content-Type", "application/json")
w.Write(output)
}
// getCursorOffset calculates the cursor offset.
//
// 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)
for i := 0; i < ch; i++ {
buffer.WriteString(string(r[i]))
}
offset += len(buffer.String()) // append length of current line
offset += line // append number of '\n'
return offset
}

View File

@ -58,6 +58,109 @@ var playground = {
});
},
init: function () {
CodeMirror.registerHelper("hint", "go", function (editor) {
var word = /[\w$]+/;
var cur = editor.getCursor(), curLine = editor.getLine(cur.line);
var start = cur.ch, end = start;
while (end < curLine.length && word.test(curLine.charAt(end))) {
++end;
}
while (start && word.test(curLine.charAt(start - 1))) {
--start;
}
var request = newWideRequest();
request.code = editor.getValue();
request.cursorLine = cur.line;
request.cursorCh = cur.ch;
var autocompleteHints = [];
$.ajax({
async: false, // 同步执行
type: 'POST',
url: config.context + '/playground/autocomplete',
data: JSON.stringify(request),
dataType: "json",
success: function (data) {
var autocompleteArray = data[1];
if (autocompleteArray) {
for (var i = 0; i < autocompleteArray.length; i++) {
var displayText = '',
text = autocompleteArray[i].name;
switch (autocompleteArray[i].class) {
case "type":
displayText = '<span class="fn-clear"><span class="ico-type ico"></span>'// + autocompleteArray[i].class
+ '<b>' + autocompleteArray[i].name + '</b> '
+ autocompleteArray[i].type + '</span>';
break;
case "const":
displayText = '<span class="fn-clear"><span class="ico-const ico"></span>'// + autocompleteArray[i].class
+ '<b>' + autocompleteArray[i].name + '</b> '
+ autocompleteArray[i].type + '</span>';
break;
case "var":
displayText = '<span class="fn-clear"><span class="ico-var ico"></span>'// + autocompleteArray[i].class
+ '<b>' + autocompleteArray[i].name + '</b> '
+ autocompleteArray[i].type + '</span>';
break;
case "package":
displayText = '<span class="fn-clear"><span class="ico-package ico"></span>'// + autocompleteArray[i].class
+ '<b>' + autocompleteArray[i].name + '</b> '
+ autocompleteArray[i].type + '</span>';
break;
case "func":
displayText = '<span><span class="ico-func ico"></span>'// + autocompleteArray[i].class
+ '<b>' + autocompleteArray[i].name + '</b>'
+ autocompleteArray[i].type.substring(4) + '</span>';
text += '()';
break;
default:
console.warn("Can't handle autocomplete [" + autocompleteArray[i].class + "]");
break;
}
autocompleteHints[i] = {
displayText: displayText,
text: text
};
}
}
}
});
return {list: autocompleteHints, from: CodeMirror.Pos(cur.line, start), to: CodeMirror.Pos(cur.line, end)};
});
CodeMirror.commands.autocompleteAnyWord = function (cm) {
cm.showHint({hint: CodeMirror.hint.auto});
};
CodeMirror.commands.autocompleteAfterDot = function (cm) {
var mode = cm.getMode();
if (mode && "go" !== mode.name) {
return CodeMirror.Pass;
}
var token = cm.getTokenAt(cm.getCursor());
if ("comment" === token.type || "string" === token.type) {
return CodeMirror.Pass;
}
setTimeout(function () {
if (!cm.state.completionActive) {
cm.showHint({hint: CodeMirror.hint.go, completeSingle: false});
}
}, 50);
return CodeMirror.Pass;
};
playground.editor = CodeMirror.fromTextArea($("#editor")[0], {
lineNumbers: true,
autofocus: true,
@ -71,7 +174,11 @@ var playground = {
indentUnit: 4,
foldGutter: true,
cursorHeight: 1,
viewportMargin: 1000
viewportMargin: 500,
extraKeys: {
"Ctrl-\\": "autocompleteAnyWord",
".": "autocompleteAfterDot"
}
});
$("#editorDiv").show();

View File

@ -8,15 +8,18 @@
<meta name="description" content="A Web-based IDE for Teams using Golang, do your development anytime, anywhere."/>
<meta name="author" content="B3log">
<link rel="stylesheet" href="{{.conf.StaticServer}}/static/css/base.css?{{.conf.StaticResourceVersion}}">
<link rel="stylesheet" href="{{.conf.StaticServer}}/static/css/dialog.css?{{.conf.StaticResourceVersion}}">
<link rel="stylesheet" href="{{.conf.StaticServer}}/static/css/base.css?{{.conf.StaticResourceVersion}}">
<link rel="stylesheet" href="{{.conf.StaticServer}}/static/css/wide.css?{{.conf.StaticResourceVersion}}">
<link rel="stylesheet" href="{{.conf.StaticServer}}/static/css/about.css?{{.conf.StaticResourceVersion}}">
<link rel="stylesheet" href="{{.conf.StaticServer}}/static/css/sign.css?{{.conf.StaticResourceVersion}}">
<link rel="stylesheet" href="{{.conf.StaticServer}}/static/css/side.css?{{.conf.StaticResourceVersion}}">
<link rel="stylesheet" href="{{.conf.StaticServer}}/static/css/playground.css?{{.conf.StaticResourceVersion}}">
<link rel="stylesheet" href="{{.conf.StaticServer}}/static/css/themes/default.css?{{.conf.StaticResourceVersion}}">
<link rel="stylesheet" href="{{.conf.StaticServer}}/static/js/lib/codemirror-{{.codeMirrorVer}}/codemirror.css?{{.conf.StaticResourceVersion}}">
<link rel="stylesheet" href="{{.conf.StaticServer}}/static/js/lib/codemirror-{{.codeMirrorVer}}/codemirror.css?{{.conf.StaticResourceVersion}}">
<link rel="stylesheet" href="{{$.conf.StaticServer}}/static/js/overwrite/codemirror/theme/wide.css?{{.conf.StaticResourceVersion}}">
<link rel="stylesheet" href="{{.conf.StaticServer}}/static/js/lib/codemirror-{{.codeMirrorVer}}/addon/hint/show-hint.css">
<link rel="icon" type="image/x-icon" href="/favicon.ico" />
</head>