// Copyright 2020 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // Command generate updates settings.md from the UserOptions struct. package main import ( "bytes" "encoding/json" "fmt" "io/ioutil" "os" "path/filepath" "regexp" "strings" "golang.org/x/tools/internal/lsp/source" ) func main() { if _, err := doMain(".", true); err != nil { fmt.Fprintf(os.Stderr, "Generation failed: %v\n", err) os.Exit(1) } } func doMain(baseDir string, write bool) (bool, error) { api := &source.APIJSON{} if err := json.Unmarshal([]byte(source.GeneratedAPIJSON), api); err != nil { return false, err } if ok, err := rewriteFile(filepath.Join(baseDir, "gopls/doc/settings.md"), api, write, rewriteSettings); !ok || err != nil { return ok, err } if ok, err := rewriteFile(filepath.Join(baseDir, "gopls/doc/commands.md"), api, write, rewriteCommands); !ok || err != nil { return ok, err } return true, nil } func rewriteFile(file string, api *source.APIJSON, write bool, rewrite func([]byte, *source.APIJSON) ([]byte, error)) (bool, error) { doc, err := ioutil.ReadFile(file) if err != nil { return false, err } content, err := rewrite(doc, api) if err != nil { return false, fmt.Errorf("rewriting %q: %v", file, err) } if !bytes.Equal(doc, content) && !write { return false, nil } if err := ioutil.WriteFile(file, content, 0); err != nil { return false, err } return true, nil } var parBreakRE = regexp.MustCompile("\n{2,}") func rewriteSettings(doc []byte, api *source.APIJSON) ([]byte, error) { result := doc for category, opts := range api.Options { section := bytes.NewBuffer(nil) for _, opt := range opts { var enumValues strings.Builder if len(opt.EnumValues) > 0 { enumValues.WriteString("Must be one of:\n\n") for _, val := range opt.EnumValues { if val.Doc != "" { // Don't break the list item by starting a new paragraph. unbroken := parBreakRE.ReplaceAllString(val.Doc, "\\\n") fmt.Fprintf(&enumValues, " * %s\n", unbroken) } else { fmt.Fprintf(&enumValues, " * `%s`\n", val.Value) } } } fmt.Fprintf(section, "### **%v** *%v*\n%v%v\n\nDefault: `%v`.\n", opt.Name, opt.Type, opt.Doc, enumValues.String(), opt.Default) } var err error result, err = replaceSection(result, category, section.Bytes()) if err != nil { return nil, err } } section := bytes.NewBuffer(nil) for _, lens := range api.Lenses { fmt.Fprintf(section, "### **%v**\nIdentifier: `%v`\n\n%v\n\n", lens.Title, lens.Lens, lens.Doc) } return replaceSection(result, "Lenses", section.Bytes()) } func rewriteCommands(doc []byte, api *source.APIJSON) ([]byte, error) { section := bytes.NewBuffer(nil) for _, command := range api.Commands { fmt.Fprintf(section, "### **%v**\nIdentifier: `%v`\n\n%v\n\n", command.Title, command.Command, command.Doc) } return replaceSection(doc, "Commands", section.Bytes()) } func replaceSection(doc []byte, sectionName string, replacement []byte) ([]byte, error) { re := regexp.MustCompile(fmt.Sprintf(`(?s)\n(.*?)`, sectionName, sectionName)) idx := re.FindSubmatchIndex(doc) if idx == nil { return nil, fmt.Errorf("could not find section %q", sectionName) } result := append([]byte(nil), doc[:idx[2]]...) result = append(result, replacement...) result = append(result, doc[idx[3]:]...) return result, nil }