241 lines
7.6 KiB
Go
241 lines
7.6 KiB
Go
package main
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"fmt"
|
|
"io"
|
|
"io/ioutil"
|
|
"os"
|
|
"reflect"
|
|
"regexp"
|
|
"strconv"
|
|
)
|
|
|
|
//-------------------------------------------------------------------------
|
|
// config
|
|
//
|
|
// Structure represents persistent config storage of the gocode daemon. Usually
|
|
// the config is located somewhere in ~/.config/gocode directory.
|
|
//-------------------------------------------------------------------------
|
|
|
|
type config struct {
|
|
ProposeBuiltins bool `json:"propose-builtins"`
|
|
LibPath string `json:"lib-path"`
|
|
CustomPkgPrefix string `json:"custom-pkg-prefix"`
|
|
CustomVendorDir string `json:"custom-vendor-dir"`
|
|
Autobuild bool `json:"autobuild"`
|
|
ForceDebugOutput string `json:"force-debug-output"`
|
|
PackageLookupMode string `json:"package-lookup-mode"`
|
|
CloseTimeout int `json:"close-timeout"`
|
|
UnimportedPackages bool `json:"unimported-packages"`
|
|
Partials bool `json:"partials"`
|
|
IgnoreCase bool `json:"ignore-case"`
|
|
ClassFiltering bool `json:"class-filtering"`
|
|
}
|
|
|
|
var g_config_desc = map[string]string{
|
|
"propose-builtins": "If set to {true}, gocode will add built-in types, functions and constants to autocompletion proposals.",
|
|
"lib-path": "A string option. Allows you to add search paths for packages. By default, gocode only searches {$GOPATH/pkg/$GOOS_$GOARCH} and {$GOROOT/pkg/$GOOS_$GOARCH} in terms of previously existed environment variables. Also you can specify multiple paths using ':' (colon) as a separator (on Windows use semicolon ';'). The paths specified by {lib-path} are prepended to the default ones.",
|
|
"custom-pkg-prefix": "",
|
|
"custom-vendor-dir": "",
|
|
"autobuild": "If set to {true}, gocode will try to automatically build out-of-date packages when their source files are modified, in order to obtain the freshest autocomplete results for them. This feature is experimental.",
|
|
"force-debug-output": "If is not empty, gocode will forcefully redirect the logging into that file. Also forces enabling of the debug mode on the server side.",
|
|
"package-lookup-mode": "If set to {go}, use standard Go package lookup rules. If set to {gb}, use gb-specific lookup rules. See {https://github.com/constabulary/gb} for details.",
|
|
"close-timeout": "If there have been no completion requests after this number of seconds, the gocode process will terminate. Default is 30 minutes.",
|
|
"unimported-packages": "If set to {true}, gocode will try to import certain known packages automatically for identifiers which cannot be resolved otherwise. Currently only a limited set of standard library packages is supported.",
|
|
"partials": "If set to {false}, gocode will not filter autocompletion results based on entered prefix before the cursor. Instead it will return all available autocompletion results viable for a given context. Whether this option is set to {true} or {false}, gocode will return a valid prefix length for output formats which support it. Setting this option to a non-default value may result in editor misbehaviour.",
|
|
"ignore-case": "If set to {true}, gocode will perform case-insensitive matching when doing prefix-based filtering.",
|
|
"class-filtering": "Enables or disables gocode's feature where it performs class-based filtering if partial input matches corresponding class keyword: const, var, type, func, package.",
|
|
}
|
|
|
|
var g_default_config = config{
|
|
ProposeBuiltins: false,
|
|
LibPath: "",
|
|
CustomPkgPrefix: "",
|
|
Autobuild: false,
|
|
ForceDebugOutput: "",
|
|
PackageLookupMode: "go",
|
|
CloseTimeout: 1800,
|
|
UnimportedPackages: false,
|
|
Partials: true,
|
|
IgnoreCase: false,
|
|
ClassFiltering: true,
|
|
}
|
|
var g_config = g_default_config
|
|
|
|
var g_string_to_bool = map[string]bool{
|
|
"t": true,
|
|
"true": true,
|
|
"y": true,
|
|
"yes": true,
|
|
"on": true,
|
|
"1": true,
|
|
"f": false,
|
|
"false": false,
|
|
"n": false,
|
|
"no": false,
|
|
"off": false,
|
|
"0": false,
|
|
}
|
|
|
|
func set_value(v reflect.Value, value string) {
|
|
switch t := v; t.Kind() {
|
|
case reflect.Bool:
|
|
v, ok := g_string_to_bool[value]
|
|
if ok {
|
|
t.SetBool(v)
|
|
}
|
|
case reflect.String:
|
|
t.SetString(value)
|
|
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
|
v, err := strconv.ParseInt(value, 10, 64)
|
|
if err == nil {
|
|
t.SetInt(v)
|
|
}
|
|
case reflect.Float32, reflect.Float64:
|
|
v, err := strconv.ParseFloat(value, 64)
|
|
if err == nil {
|
|
t.SetFloat(v)
|
|
}
|
|
}
|
|
}
|
|
|
|
func list_value(v reflect.Value, name string, w io.Writer) {
|
|
switch t := v; t.Kind() {
|
|
case reflect.Bool:
|
|
fmt.Fprintf(w, "%s %v\n", name, t.Bool())
|
|
case reflect.String:
|
|
fmt.Fprintf(w, "%s \"%v\"\n", name, t.String())
|
|
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
|
fmt.Fprintf(w, "%s %v\n", name, t.Int())
|
|
case reflect.Float32, reflect.Float64:
|
|
fmt.Fprintf(w, "%s %v\n", name, t.Float())
|
|
}
|
|
}
|
|
|
|
func (this *config) list() string {
|
|
str, typ := this.value_and_type()
|
|
buf := bytes.NewBuffer(make([]byte, 0, 256))
|
|
for i := 0; i < str.NumField(); i++ {
|
|
v := str.Field(i)
|
|
name := typ.Field(i).Tag.Get("json")
|
|
list_value(v, name, buf)
|
|
}
|
|
return buf.String()
|
|
}
|
|
|
|
func (this *config) list_option(name string) string {
|
|
str, typ := this.value_and_type()
|
|
buf := bytes.NewBuffer(make([]byte, 0, 256))
|
|
for i := 0; i < str.NumField(); i++ {
|
|
v := str.Field(i)
|
|
nm := typ.Field(i).Tag.Get("json")
|
|
if nm == name {
|
|
list_value(v, name, buf)
|
|
}
|
|
}
|
|
return buf.String()
|
|
}
|
|
|
|
func (this *config) set_option(name, value string) string {
|
|
str, typ := this.value_and_type()
|
|
buf := bytes.NewBuffer(make([]byte, 0, 256))
|
|
for i := 0; i < str.NumField(); i++ {
|
|
v := str.Field(i)
|
|
nm := typ.Field(i).Tag.Get("json")
|
|
if nm == name {
|
|
set_value(v, value)
|
|
list_value(v, name, buf)
|
|
}
|
|
}
|
|
this.write()
|
|
return buf.String()
|
|
|
|
}
|
|
|
|
func (this *config) value_and_type() (reflect.Value, reflect.Type) {
|
|
v := reflect.ValueOf(this).Elem()
|
|
return v, v.Type()
|
|
}
|
|
|
|
func (this *config) write() error {
|
|
data, err := json.Marshal(this)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// make sure config dir exists
|
|
dir := config_dir()
|
|
if !file_exists(dir) {
|
|
os.MkdirAll(dir, 0755)
|
|
}
|
|
|
|
f, err := os.Create(config_file())
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer f.Close()
|
|
|
|
_, err = f.Write(data)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (this *config) read() error {
|
|
data, err := ioutil.ReadFile(config_file())
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
err = json.Unmarshal(data, this)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func quoted(v interface{}) string {
|
|
switch v.(type) {
|
|
case string:
|
|
return fmt.Sprintf("%q", v)
|
|
case int:
|
|
return fmt.Sprint(v)
|
|
case bool:
|
|
return fmt.Sprint(v)
|
|
default:
|
|
panic("unreachable")
|
|
}
|
|
}
|
|
|
|
var descRE = regexp.MustCompile(`{[^}]+}`)
|
|
|
|
func preprocess_desc(v string) string {
|
|
return descRE.ReplaceAllStringFunc(v, func(v string) string {
|
|
return color_cyan + v[1:len(v)-1] + color_none
|
|
})
|
|
}
|
|
|
|
func (this *config) options() string {
|
|
var buf bytes.Buffer
|
|
fmt.Fprintf(&buf, "%sConfig file location%s: %s\n", color_white_bold, color_none, config_file())
|
|
dv := reflect.ValueOf(g_default_config)
|
|
v, t := this.value_and_type()
|
|
for i, n := 0, t.NumField(); i < n; i++ {
|
|
f := t.Field(i)
|
|
index := f.Index
|
|
tag := f.Tag.Get("json")
|
|
fmt.Fprintf(&buf, "\n%s%s%s\n", color_yellow_bold, tag, color_none)
|
|
fmt.Fprintf(&buf, "%stype%s: %s\n", color_yellow, color_none, f.Type)
|
|
fmt.Fprintf(&buf, "%svalue%s: %s\n", color_yellow, color_none, quoted(v.FieldByIndex(index).Interface()))
|
|
fmt.Fprintf(&buf, "%sdefault%s: %s\n", color_yellow, color_none, quoted(dv.FieldByIndex(index).Interface()))
|
|
fmt.Fprintf(&buf, "%sdescription%s: %s\n", color_yellow, color_none, preprocess_desc(g_config_desc[tag]))
|
|
}
|
|
|
|
return buf.String()
|
|
}
|