This commit is contained in:
parent
45084f1628
commit
2565035b54
1
main.go
1
main.go
|
@ -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)
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -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();
|
||||
|
|
|
@ -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>
|
||||
|
|
Loading…
Reference in New Issue