Permalink
Fetching contributors…
Cannot retrieve contributors at this time
244 lines (219 sloc) 5.97 KB
package main
import (
"bytes"
"fmt"
"go/build"
"log"
"net"
"net/rpc"
"os"
"path/filepath"
"reflect"
"runtime"
"time"
)
func do_server() int {
g_config.read()
if g_config.ForceDebugOutput != "" {
// forcefully enable debugging and redirect logging into the
// specified file
*g_debug = true
f, err := os.Create(g_config.ForceDebugOutput)
if err != nil {
panic(err)
}
log.SetOutput(f)
}
addr := *g_addr
if *g_sock == "unix" {
addr = get_socket_filename()
if file_exists(addr) {
log.Printf("unix socket: '%s' already exists\n", addr)
return 1
}
}
g_daemon = new_daemon(*g_sock, addr)
if *g_sock == "unix" {
// cleanup unix socket file
defer os.Remove(addr)
}
rpc.Register(new(RPC))
g_daemon.loop()
return 0
}
//-------------------------------------------------------------------------
// daemon
//-------------------------------------------------------------------------
type daemon struct {
listener net.Listener
cmd_in chan int
autocomplete *auto_complete_context
pkgcache package_cache
declcache *decl_cache
context package_lookup_context
}
func new_daemon(network, address string) *daemon {
var err error
d := new(daemon)
d.listener, err = net.Listen(network, address)
if err != nil {
panic(err)
}
d.cmd_in = make(chan int, 1)
d.pkgcache = new_package_cache()
d.declcache = new_decl_cache(&d.context)
d.autocomplete = new_auto_complete_context(d.pkgcache, d.declcache)
return d
}
func (this *daemon) drop_cache() {
this.pkgcache = new_package_cache()
this.declcache = new_decl_cache(&this.context)
this.autocomplete = new_auto_complete_context(this.pkgcache, this.declcache)
}
const (
daemon_close = iota
)
func (this *daemon) loop() {
conn_in := make(chan net.Conn)
go func() {
for {
c, err := this.listener.Accept()
if err != nil {
panic(err)
}
conn_in <- c
}
}()
timeout := time.Duration(g_config.CloseTimeout) * time.Second
countdown := time.NewTimer(timeout)
for {
// handle connections or server CMDs (currently one CMD)
select {
case c := <-conn_in:
rpc.ServeConn(c)
countdown.Reset(timeout)
runtime.GC()
case cmd := <-this.cmd_in:
switch cmd {
case daemon_close:
return
}
case <-countdown.C:
return
}
}
}
func (this *daemon) close() {
this.cmd_in <- daemon_close
}
var g_daemon *daemon
//-------------------------------------------------------------------------
// server_* functions
//
// Corresponding client_* functions are autogenerated by goremote.
//-------------------------------------------------------------------------
func server_auto_complete(file []byte, filename string, cursor int, context_packed go_build_context) (c []candidate, d int) {
context := unpack_build_context(&context_packed)
defer func() {
if err := recover(); err != nil {
print_backtrace(err)
c = []candidate{
{"PANIC", "PANIC", decl_invalid},
}
// drop cache
g_daemon.drop_cache()
}
}()
// TODO: Probably we don't care about comparing all the fields, checking GOROOT and GOPATH
// should be enough.
if !reflect.DeepEqual(g_daemon.context.Context, context.Context) {
g_daemon.context = context
g_daemon.drop_cache()
}
switch g_config.PackageLookupMode {
case "bzl":
// when package lookup mode is bzl, we set GOPATH to "" explicitly and
// BzlProjectRoot becomes valid (or empty)
var err error
g_daemon.context.GOPATH = ""
g_daemon.context.BzlProjectRoot, err = find_bzl_project_root(g_config.LibPath, filename)
if *g_debug && err != nil {
log.Printf("Bzl project root not found: %s", err)
}
case "gb":
// when package lookup mode is gb, we set GOPATH to "" explicitly and
// GBProjectRoot becomes valid (or empty)
var err error
g_daemon.context.GOPATH = ""
g_daemon.context.GBProjectRoot, err = find_gb_project_root(filename)
if *g_debug && err != nil {
log.Printf("Gb project root not found: %s", err)
}
case "go":
// get current package path for GO15VENDOREXPERIMENT hack
g_daemon.context.CurrentPackagePath = ""
pkg, err := g_daemon.context.ImportDir(filepath.Dir(filename), build.FindOnly)
if err == nil {
if *g_debug {
log.Printf("Go project path: %s", pkg.ImportPath)
}
g_daemon.context.CurrentPackagePath = pkg.ImportPath
} else if *g_debug {
log.Printf("Go project path not found: %s", err)
}
}
if *g_debug {
var buf bytes.Buffer
log.Printf("Got autocompletion request for '%s'\n", filename)
log.Printf("Cursor at: %d\n", cursor)
if cursor > len(file) || cursor < 0 {
log.Println("ERROR! Cursor is outside of the boundaries of the buffer, " +
"this is most likely a text editor plugin bug. Text editor is responsible " +
"for passing the correct cursor position to gocode.")
} else {
buf.WriteString("-------------------------------------------------------\n")
buf.Write(file[:cursor])
buf.WriteString("#")
buf.Write(file[cursor:])
log.Print(buf.String())
log.Println("-------------------------------------------------------")
}
}
candidates, d := g_daemon.autocomplete.apropos(file, filename, cursor)
if *g_debug {
log.Printf("Offset: %d\n", d)
log.Printf("Number of candidates found: %d\n", len(candidates))
log.Printf("Candidates are:\n")
for _, c := range candidates {
abbr := fmt.Sprintf("%s %s %s", c.Class, c.Name, c.Type)
if c.Class == decl_func {
abbr = fmt.Sprintf("%s %s%s", c.Class, c.Name, c.Type[len("func"):])
}
log.Printf(" %s\n", abbr)
}
log.Println("=======================================================")
}
return candidates, d
}
func server_close(notused int) int {
g_daemon.close()
return 0
}
func server_status(notused int) string {
return g_daemon.autocomplete.status()
}
func server_drop_cache(notused int) int {
// drop cache
g_daemon.drop_cache()
return 0
}
func server_set(key, value string) string {
if key == "\x00" {
return g_config.list()
} else if value == "\x00" {
return g_config.list_option(key)
}
// drop cache on settings changes
g_daemon.drop_cache()
return g_config.set_option(key, value)
}