wide/vendor/github.com/nsf/gocode/config.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()
}