Permalink
Browse files

More mess, change jsonrpc to simple rpc (uses gob).

  • Loading branch information...
1 parent 6379b17 commit 94b7b0162dd7eb58f13c6c143ba5e30c54f5d6e1 @nsf committed Jul 9, 2010
Showing with 436 additions and 99 deletions.
  1. +7 −1 Makefile
  2. +55 −43 gocode.go
  3. +92 −4 gocodelib.go
  4. +23 −51 gocodeserver.go
  5. +6 −0 goremote/Makefile
  6. +253 −0 goremote/goremote.go
View
8 Makefile
@@ -1,6 +1,12 @@
include $(GOROOT)/src/Make.$(GOARCH)
TARG=gocode
-GOFILES=gocode.go gocodelib.go gocodeserver.go
+GOFILES=gocode.go gocodelib.go gocodeserver.go gorpc.go
include $(GOROOT)/src/Make.cmd
+
+gorpc.go: gocodeserver.go goremote/goremote
+ ./goremote/goremote gocodeserver.go > gorpc.go
+
+goremote/goremote: goremote/goremote.go
+ cd goremote && make
View
98 gocode.go
@@ -1,41 +1,80 @@
package main
import (
-// "os"
-// "io/ioutil"
-// "strings"
+ "io/ioutil"
"rpc"
- "rpc/jsonrpc"
"flag"
"fmt"
"os"
)
var (
server = flag.Bool("s", false, "run a server instead of a client")
+ format = flag.String("f", "vim", "output format (currently only 'vim' is valid)")
)
+func getSocketFilename() string {
+ user := os.Getenv("USER")
+ if user == "" {
+ user = "all"
+ }
+ return fmt.Sprintf("%s/acrserver.%s", os.TempDir(), user)
+}
+
func serverFunc() {
- acrRPC := new(ACR)
- rpc.Register(acrRPC)
- acrRPC.acr = NewACRServer("/tmp/acrserver")
- defer os.Remove("/tmp/acrserver")
- acrRPC.acr.Loop()
+ socketfname := getSocketFilename()
+ daemon = NewAutoCompletionDaemon(socketfname)
+ defer os.Remove(socketfname)
+
+ rpcremote := new(RPCRemote)
+ rpc.Register(rpcremote)
+
+ daemon.acr.Loop()
+}
+
+func Cmd_AutoComplete(c *rpc.Client) {
+ file, err := ioutil.ReadAll(os.Stdin)
+ if err != nil {
+ panic(err.String())
+ }
+ apropos := flag.Args()[1]
+ abbrs, words := Client_AutoComplete(c, file, apropos)
+ if len(words) != len(abbrs) {
+ panic("Lengths should match!")
+ }
+
+ fmt.Printf("[")
+ for i := 0; i < len(words); i++ {
+ fmt.Printf("{'word': '%s', 'abbr': '%s'}", words[i], abbrs[i])
+ if i != len(words)-1 {
+ fmt.Printf(", ")
+ }
+ }
+ fmt.Printf("]")
+}
+
+func Cmd_Close(c *rpc.Client) {
+ Client_Close(c, 0)
+}
+
+var cmds = map[string]func(*rpc.Client) {
+ "autocomplete": Cmd_AutoComplete,
+ "close": Cmd_Close,
}
func clientFunc() {
// client
- var args, reply int
- client, err := jsonrpc.Dial("unix", "/tmp/acrserver")
+ client, err := rpc.Dial("unix", getSocketFilename())
if err != nil {
- panic(err.String())
+ fmt.Printf("Failed to connect to the ACR server\n%s\n", err.String())
+ return
}
- err = client.Call("ACR.Shutdown", &args, &reply)
- if err != nil {
- panic(err.String())
+ defer client.Close()
+
+ if len(flag.Args()) > 0 {
+ cmds[flag.Args()[0]](client)
}
- fmt.Printf("close request send\n")
}
func main() {
@@ -45,31 +84,4 @@ func main() {
} else {
clientFunc()
}
- /*
- if len(os.Args) != 2 {
- panic("usage: ./gocode <apropos request>")
- }
-
- data, err := ioutil.ReadAll(os.Stdin)
- if err != nil {
- panic("Bad stdin")
- }
- ctx := NewAutoCompleteContext()
- ctx.processData(data)
-
- request := os.Args[1]
- parts := strings.Split(request, ".", 2)
- res := ""
- switch len(parts) {
- case 1:
- for _, decl := range ctx.m[ctx.cfns[request]] {
- prettyPrintDecl(os.Stdout, decl, "")
- }
- case 2:
- for _, decl := range ctx.m[ctx.cfns[parts[0]]] {
- prettyPrintDecl(os.Stdout, decl, parts[1])
- }
- }
- print(res)
- */
}
View
96 gocodelib.go
@@ -230,6 +230,10 @@ func processExport(s string) (string, string) {
}
func (self *AutoCompleteContext) processPackage(filename string, uniquename string, pkgname string) {
+ if self.cache[filename] {
+ return
+ }
+ self.cache[filename] = true
data, err := ioutil.ReadFile(filename)
if err != nil {
panic("Failed to open archive file")
@@ -347,6 +351,9 @@ func prettyPrintTypeExpr(out io.Writer, e ast.Expr) {
prettyPrintTypeExpr(out, t.Value)
case *ast.InterfaceType:
fmt.Fprintf(out, "interface{}")
+ case *ast.Ellipsis:
+ fmt.Fprintf(out, "...")
+ prettyPrintTypeExpr(out, t.Elt)
default:
fmt.Fprintf(out, "\n[!!] unknown type: %s\n", ty.String())
}
@@ -383,6 +390,13 @@ func prettyPrintFuncFieldList(out io.Writer, f *ast.FieldList) int {
return count
}
+func startsWith(s, prefix string) bool {
+ if len(s) >= len(prefix) && s[0:len(prefix)] == prefix {
+ return true
+ }
+ return false
+}
+
func prettyPrintDecl(out io.Writer, d ast.Decl, p string) {
switch t := d.(type) {
case *ast.GenDecl:
@@ -391,7 +405,7 @@ func prettyPrintDecl(out io.Writer, d ast.Decl, p string) {
for _, spec := range t.Specs {
c := spec.(*ast.ValueSpec)
for _, name := range c.Names {
- if len(name.Name()) < len(p) || (p != "" && name.Name()[0:len(p)] != p) {
+ if p != "" && !startsWith(name.Name(), p) {
continue
}
fmt.Fprintf(out, "const %s\n", name.Name())
@@ -400,7 +414,7 @@ func prettyPrintDecl(out io.Writer, d ast.Decl, p string) {
case token.TYPE:
for _, spec := range t.Specs {
t := spec.(*ast.TypeSpec)
- if len(t.Name.Name()) < len(p) || (p != "" && t.Name.Name()[0:len(p)] != p) {
+ if p != "" && !startsWith(t.Name.Name(), p) {
continue
}
fmt.Fprintf(out, "type %s\n", t.Name.Name())
@@ -409,7 +423,7 @@ func prettyPrintDecl(out io.Writer, d ast.Decl, p string) {
for _, spec := range t.Specs {
v := spec.(*ast.ValueSpec)
for _, name := range v.Names {
- if len(name.Name()) < len(p) || (p != "" && name.Name()[0:len(p)] != p) {
+ if p != "" && !startsWith(name.Name(), p) {
continue
}
fmt.Fprintf(out, "var %s\n", name.Name())
@@ -423,7 +437,7 @@ func prettyPrintDecl(out io.Writer, d ast.Decl, p string) {
//XXX: skip method, temporary
break
}
- if len(t.Name.Name()) < len(p) || (p != "" && t.Name.Name()[0:len(p)] != p) {
+ if p != "" && !startsWith(t.Name.Name(), p) {
break
}
fmt.Fprintf(out, "func %s(", t.Name.Name())
@@ -443,6 +457,53 @@ func prettyPrintDecl(out io.Writer, d ast.Decl, p string) {
}
}
+func autoCompleteDecl(out io.Writer, d ast.Decl, p string) {
+ switch t := d.(type) {
+ case *ast.GenDecl:
+ switch t.Tok {
+ case token.CONST:
+ for _, spec := range t.Specs {
+ c := spec.(*ast.ValueSpec)
+ for _, name := range c.Names {
+ if p != "" && !startsWith(name.Name(), p) {
+ continue
+ }
+ fmt.Fprintf(out, "%s\n", name.Name()[len(p):])
+ }
+ }
+ case token.TYPE:
+ for _, spec := range t.Specs {
+ t := spec.(*ast.TypeSpec)
+ if p != "" && !startsWith(t.Name.Name(), p) {
+ continue
+ }
+ fmt.Fprintf(out, "%s\n", t.Name.Name()[len(p):])
+ }
+ case token.VAR:
+ for _, spec := range t.Specs {
+ v := spec.(*ast.ValueSpec)
+ for _, name := range v.Names {
+ if p != "" && !startsWith(name.Name(), p) {
+ continue
+ }
+ fmt.Fprintf(out, "%s\n", name.Name()[len(p):])
+ }
+ }
+ default:
+ fmt.Fprintf(out, "[!!] STUB\n")
+ }
+ case *ast.FuncDecl:
+ if t.Recv != nil {
+ //XXX: skip method, temporary
+ break
+ }
+ if p != "" && !startsWith(t.Name.Name(), p) {
+ break
+ }
+ fmt.Fprintf(out, "%s(\n", t.Name.Name()[len(p):])
+ }
+}
+
func findFile(imp string) string {
goroot := os.Getenv("GOROOT")
goarch := os.Getenv("GOARCH")
@@ -489,13 +550,15 @@ type AutoCompleteContext struct {
// alias name ->
// unique package name
cfns map[string]string
+ cache map[string]bool
debuglog io.Writer
}
func NewAutoCompleteContext() *AutoCompleteContext {
self := new(AutoCompleteContext)
self.m = make(map[string][]ast.Decl)
self.cfns = make(map[string]string)
+ self.cache = make(map[string]bool)
return self
}
@@ -506,3 +569,28 @@ func (self *AutoCompleteContext) Add(globalname string, decls []ast.Decl) {
func (self *AutoCompleteContext) AddAlias(alias string, globalname string) {
self.cfns[alias] = globalname
}
+
+func (self *AutoCompleteContext) Apropos(file []byte, apropos string) ([]string, []string) {
+ self.processData(file)
+
+ buf := bytes.NewBuffer(make([]byte, 0, 4096))
+ buf2 := bytes.NewBuffer(make([]byte, 0, 4096))
+
+ parts := strings.Split(apropos, ".", 2)
+ switch len(parts) {
+ case 1:
+ for _, decl := range self.m[self.cfns[apropos]] {
+ prettyPrintDecl(buf, decl, "")
+ autoCompleteDecl(buf2, decl, "")
+ }
+ case 2:
+ for _, decl := range self.m[self.cfns[parts[0]]] {
+ prettyPrintDecl(buf, decl, parts[1])
+ autoCompleteDecl(buf2, decl, parts[1])
+ }
+ }
+
+ result := strings.Split(buf.String(), "\n", -1)
+ result2 := strings.Split(buf2.String(), "\n", -1)
+ return result, result2
+}
View
74 gocodeserver.go
@@ -2,36 +2,35 @@ package main
import (
"net"
- "rpc/jsonrpc"
- "fmt"
- "os"
- "io"
+ "rpc"
+ "os/signal"
)
//-------------------------------------------------------------------------
-// ACR type (used in RPC)
-//-------------------------------------------------------------------------
-type ACR struct {
+type AutoCompletionDaemon struct {
acr *ACRServer
+ ctx *AutoCompleteContext
}
-// shutdown request
-
-func (self *ACR) Shutdown(notused1 *int, notused2 *int) os.Error {
- self.acr.Close()
- return nil
+func NewAutoCompletionDaemon(path string) *AutoCompletionDaemon {
+ self := new(AutoCompletionDaemon)
+ self.acr = NewACRServer(path)
+ self.ctx = NewAutoCompleteContext()
+ return self
}
-// top-level imported module autocomplete request
+var daemon *AutoCompletionDaemon
+
+//-------------------------------------------------------------------------
-type ACRImportedAC struct {
- file string
- apropos string
+func Server_AutoComplete(file []byte, apropos string) ([]string, []string) {
+ return daemon.ctx.Apropos(file, apropos)
}
-type ACRImportedACReply struct {
- completions []string
+func Server_Close(notused int) int {
+ daemon.acr.Close()
+ return 0
}
//-------------------------------------------------------------------------
@@ -72,38 +71,6 @@ func acceptConnections(in chan net.Conn, listener *net.UnixListener) {
}
}
-type RPCDebugProxy struct {
- rwc io.ReadWriteCloser
- debugout io.Writer
-}
-
-func (self *RPCDebugProxy) Read(p []byte) (n int, err os.Error) {
- tmp := make([]byte, len(p))
- n, err = self.rwc.Read(tmp)
- copy(p, tmp)
- fmt.Fprintf(self.debugout, "Request:\n")
- self.debugout.Write(tmp)
- return
-}
-
-func (self *RPCDebugProxy) Write(p []byte) (n int, err os.Error) {
- n, err = self.rwc.Write(p)
- fmt.Fprintf(self.debugout, "Response:\n")
- self.debugout.Write(p)
- return
-}
-
-func (self *RPCDebugProxy) Close() os.Error {
- return self.rwc.Close()
-}
-
-func NewRPCDebugProxy(rwc io.ReadWriteCloser, debugout io.Writer) *RPCDebugProxy {
- self := new(RPCDebugProxy)
- self.rwc = rwc
- self.debugout = debugout
- return self
-}
-
func (self *ACRServer) Loop() {
conn_in := make(chan net.Conn)
go acceptConnections(conn_in, self.listener)
@@ -112,13 +79,18 @@ func (self *ACRServer) Loop() {
select {
case c := <-conn_in:
go func(c net.Conn) {
- jsonrpc.ServeConn(NewRPCDebugProxy(c, os.Stdout))
+ rpc.ServeConn(c)
}(c)
case cmd := <-self.cmd_in:
switch cmd {
case ACR_CLOSE:
return
}
+ case sig := <-signal.Incoming:
+ usig := sig.(signal.UnixSignal)
+ if usig == signal.SIGINT || usig == signal.SIGTERM {
+ return
+ }
}
}
}
View
6 goremote/Makefile
@@ -0,0 +1,6 @@
+include $(GOROOT)/src/Make.$(GOARCH)
+
+TARG=goremote
+GOFILES=goremote.go
+
+include $(GOROOT)/src/Make.cmd
View
253 goremote/goremote.go
@@ -0,0 +1,253 @@
+package main
+
+import (
+ "go/parser"
+ "strings"
+ "reflect"
+ "flag"
+ "go/ast"
+ "bytes"
+ "fmt"
+ "io"
+ "os"
+)
+
+const prefix = "Server_"
+
+func prettyPrintTypeExpr(out io.Writer, e ast.Expr) {
+ ty := reflect.Typeof(e)
+ switch t := e.(type) {
+ case *ast.StarExpr:
+ fmt.Fprintf(out, "*")
+ prettyPrintTypeExpr(out, t.X)
+ case *ast.Ident:
+ fmt.Fprintf(out, t.Name())
+ case *ast.ArrayType:
+ fmt.Fprintf(out, "[]")
+ prettyPrintTypeExpr(out, t.Elt)
+ case *ast.SelectorExpr:
+ prettyPrintTypeExpr(out, t.X)
+ fmt.Fprintf(out, ".%s", t.Sel.Name())
+ case *ast.FuncType:
+ fmt.Fprintf(out, "func(")
+ prettyPrintFuncFieldList(out, t.Params)
+ fmt.Fprintf(out, ")")
+
+ buf := bytes.NewBuffer(make([]byte, 0, 256))
+ nresults := prettyPrintFuncFieldList(buf, t.Results)
+ if nresults > 0 {
+ results := buf.String()
+ if strings.Index(results, " ") != -1 {
+ results = "(" + results + ")"
+ }
+ fmt.Fprintf(out, " %s", results)
+ }
+ case *ast.MapType:
+ fmt.Fprintf(out, "map[")
+ prettyPrintTypeExpr(out, t.Key)
+ fmt.Fprintf(out, "]")
+ prettyPrintTypeExpr(out, t.Value)
+ case *ast.InterfaceType:
+ fmt.Fprintf(out, "interface{}")
+ case *ast.Ellipsis:
+ fmt.Fprintf(out, "...")
+ prettyPrintTypeExpr(out, t.Elt)
+ default:
+ fmt.Fprintf(out, "\n[!!] unknown type: %s\n", ty.String())
+ }
+}
+
+func prettyPrintFuncFieldList(out io.Writer, f *ast.FieldList) int {
+ count := 0
+ if f == nil {
+ return count
+ }
+ for i, field := range f.List {
+ // names
+ if field.Names != nil {
+ for j, name := range field.Names {
+ fmt.Fprintf(out, "%s", name.Name())
+ if j != len(field.Names)-1 {
+ fmt.Fprintf(out, ", ")
+ }
+ count++
+ }
+ fmt.Fprintf(out, " ")
+ } else {
+ count++
+ }
+
+ // type
+ prettyPrintTypeExpr(out, field.Type)
+
+ // ,
+ if i != len(f.List)-1 {
+ fmt.Fprintf(out, ", ")
+ }
+ }
+ return count
+}
+
+func prettyPrintFuncFieldListUsingArgs(out io.Writer, f *ast.FieldList) int {
+ count := 0
+ if f == nil {
+ return count
+ }
+ for i, field := range f.List {
+ // names
+ if field.Names != nil {
+ for j, _ := range field.Names {
+ fmt.Fprintf(out, "Arg%d", count)
+ if j != len(field.Names)-1 {
+ fmt.Fprintf(out, ", ")
+ }
+ count++
+ }
+ fmt.Fprintf(out, " ")
+ } else {
+ count++
+ }
+
+ // type
+ prettyPrintTypeExpr(out, field.Type)
+
+ // ,
+ if i != len(f.List)-1 {
+ fmt.Fprintf(out, ", ")
+ }
+ }
+ return count
+}
+
+func generateStructWrapper(out io.Writer, fun *ast.FieldList, structname, name string) int {
+ fmt.Fprintf(out, "type %s_%s struct {\n", structname, name)
+ argn := 0
+ for _, field := range fun.List {
+ fmt.Fprintf(out, "\t")
+ // names
+ if field.Names != nil {
+ for j, _ := range field.Names {
+ fmt.Fprintf(out, "Arg%d", argn)
+ if j != len(field.Names)-1 {
+ fmt.Fprintf(out, ", ")
+ }
+ argn++
+ }
+ fmt.Fprintf(out, " ")
+ } else {
+ fmt.Fprintf(out, "Arg%d ", argn)
+ argn++
+ }
+
+ // type
+ prettyPrintTypeExpr(out, field.Type)
+
+ // \n
+ fmt.Fprintf(out, "\n")
+ }
+ fmt.Fprintf(out, "}\n")
+ return argn
+}
+
+// function that is being exposed to an RPC API, but calls simple "Server_" one
+func generateServerRPCWrapper(out io.Writer, fun *ast.FuncDecl, name string, argcnt, replycnt int) {
+ fmt.Fprintf(out, "func (self *RPCRemote) RPCServer_%s(args *Args_%s, reply *Reply_%s) os.Error {\n",
+ name, name, name)
+
+ fmt.Fprintf(out, "\t")
+ for i := 0; i < replycnt; i++ {
+ fmt.Fprintf(out, "reply.Arg%d", i)
+ if i != replycnt-1 {
+ fmt.Fprintf(out, ", ")
+ }
+ }
+ fmt.Fprintf(out, " = %s(", fun.Name.Name())
+ for i := 0; i < argcnt; i++ {
+ fmt.Fprintf(out, "args.Arg%d", i)
+ if i != argcnt-1 {
+ fmt.Fprintf(out, ", ")
+ }
+ }
+ fmt.Fprintf(out, ")\n")
+ fmt.Fprintf(out, "\treturn nil\n}\n")
+}
+
+func generateClientRPCWrapper(out io.Writer, fun *ast.FuncDecl, name string, argcnt, replycnt int) {
+ fmt.Fprintf(out, "func Client_%s(cli *rpc.Client, ", name)
+ prettyPrintFuncFieldListUsingArgs(out, fun.Type.Params)
+ fmt.Fprintf(out, ")")
+
+ buf := bytes.NewBuffer(make([]byte, 0, 256))
+ nresults := prettyPrintFuncFieldList(buf, fun.Type.Results)
+ if nresults > 0 {
+ results := buf.String()
+ if strings.Index(results, " ") != -1 {
+ results = "(" + results + ")"
+ }
+ fmt.Fprintf(out, " %s", results)
+ }
+ fmt.Fprintf(out, " {\n")
+ fmt.Fprintf(out, "\tvar args Args_%s\n", name)
+ fmt.Fprintf(out, "\tvar reply Reply_%s\n", name)
+ for i := 0; i < argcnt; i++ {
+ fmt.Fprintf(out, "\targs.Arg%d = Arg%d\n", i, i)
+ }
+ fmt.Fprintf(out, "\terr := cli.Call(\"RPCRemote.RPCServer_%s\", &args, &reply)\n", name)
+ fmt.Fprintf(out, "\tif err != nil {\n")
+ fmt.Fprintf(out, "\t\tpanic(err.String())\n\t}\n")
+
+ fmt.Fprintf(out, "\treturn ")
+ for i := 0; i < replycnt; i++ {
+ fmt.Fprintf(out, "reply.Arg%d", i)
+ if i != replycnt-1 {
+ fmt.Fprintf(out, ", ")
+ }
+ }
+ fmt.Fprintf(out, "\n}\n")
+}
+
+func wrapFunction(out io.Writer, fun *ast.FuncDecl) {
+ name := fun.Name.Name()[len(prefix):]
+ fmt.Fprintf(out, "// wrapper for: %s\n\n", fun.Name.Name())
+ argcnt := generateStructWrapper(out, fun.Type.Params, "Args", name)
+ replycnt := generateStructWrapper(out, fun.Type.Results, "Reply", name)
+ generateServerRPCWrapper(out, fun, name, argcnt, replycnt)
+ generateClientRPCWrapper(out, fun, name, argcnt, replycnt)
+ fmt.Fprintf(out, "\n")
+}
+
+func processFile(out io.Writer, filename string) {
+ file, err := parser.ParseFile(filename, nil, nil, 0)
+ if err != nil {
+ panic(err.String())
+ }
+
+ for _, decl := range file.Decls {
+ if fdecl, ok := decl.(*ast.FuncDecl); ok {
+ namelen := len(fdecl.Name.Name())
+ if namelen >= len(prefix) && fdecl.Name.Name()[0:len(prefix)] == prefix {
+ wrapFunction(out, fdecl)
+ }
+ }
+ }
+}
+
+const head = `package main
+
+import (
+ "os"
+ "rpc"
+)
+
+type RPCRemote struct {
+}
+
+`
+
+func main() {
+ flag.Parse()
+ fmt.Fprintf(os.Stdout, head)
+ for _, file := range flag.Args() {
+ processFile(os.Stdout, file)
+ }
+}

0 comments on commit 94b7b01

Please sign in to comment.