golang.org/x/tools/gopls@v0.15.3/internal/cmd/info.go (about) 1 // Copyright 2019 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package cmd 6 7 // This file defines the help, bug, version, api-json, licenses commands. 8 9 import ( 10 "bytes" 11 "context" 12 "encoding/json" 13 "flag" 14 "fmt" 15 "net/url" 16 "os" 17 "sort" 18 "strings" 19 20 "golang.org/x/tools/gopls/internal/debug" 21 "golang.org/x/tools/gopls/internal/filecache" 22 "golang.org/x/tools/gopls/internal/settings" 23 "golang.org/x/tools/gopls/internal/util/browser" 24 goplsbug "golang.org/x/tools/gopls/internal/util/bug" 25 "golang.org/x/tools/internal/tool" 26 ) 27 28 // help implements the help command. 29 type help struct { 30 app *Application 31 } 32 33 func (h *help) Name() string { return "help" } 34 func (h *help) Parent() string { return h.app.Name() } 35 func (h *help) Usage() string { return "" } 36 func (h *help) ShortHelp() string { return "print usage information for subcommands" } 37 func (h *help) DetailedHelp(f *flag.FlagSet) { 38 fmt.Fprint(f.Output(), ` 39 40 Examples: 41 $ gopls help # main gopls help message 42 $ gopls help remote # help on 'remote' command 43 $ gopls help remote sessions # help on 'remote sessions' subcommand 44 `) 45 printFlagDefaults(f) 46 } 47 48 // Run prints help information about a subcommand. 49 func (h *help) Run(ctx context.Context, args ...string) error { 50 find := func(cmds []tool.Application, name string) tool.Application { 51 for _, cmd := range cmds { 52 if cmd.Name() == name { 53 return cmd 54 } 55 } 56 return nil 57 } 58 59 // Find the subcommand denoted by args (empty => h.app). 60 var cmd tool.Application = h.app 61 for i, arg := range args { 62 cmd = find(getSubcommands(cmd), arg) 63 if cmd == nil { 64 return tool.CommandLineErrorf( 65 "no such subcommand: %s", strings.Join(args[:i+1], " ")) 66 } 67 } 68 69 // 'gopls help cmd subcmd' is equivalent to 'gopls cmd subcmd -h'. 70 // The flag package prints the usage information (defined by tool.Run) 71 // when it sees the -h flag. 72 fs := flag.NewFlagSet(cmd.Name(), flag.ExitOnError) 73 return tool.Run(ctx, fs, h.app, append(args[:len(args):len(args)], "-h")) 74 } 75 76 // version implements the version command. 77 type version struct { 78 JSON bool `flag:"json" help:"outputs in json format."` 79 80 app *Application 81 } 82 83 func (v *version) Name() string { return "version" } 84 func (v *version) Parent() string { return v.app.Name() } 85 func (v *version) Usage() string { return "" } 86 func (v *version) ShortHelp() string { return "print the gopls version information" } 87 func (v *version) DetailedHelp(f *flag.FlagSet) { 88 fmt.Fprint(f.Output(), ``) 89 printFlagDefaults(f) 90 } 91 92 // Run prints version information to stdout. 93 func (v *version) Run(ctx context.Context, args ...string) error { 94 var mode = debug.PlainText 95 if v.JSON { 96 mode = debug.JSON 97 } 98 99 return debug.PrintVersionInfo(ctx, os.Stdout, v.app.verbose(), mode) 100 } 101 102 // bug implements the bug command. 103 type bug struct { 104 app *Application 105 } 106 107 func (b *bug) Name() string { return "bug" } 108 func (b *bug) Parent() string { return b.app.Name() } 109 func (b *bug) Usage() string { return "" } 110 func (b *bug) ShortHelp() string { return "report a bug in gopls" } 111 func (b *bug) DetailedHelp(f *flag.FlagSet) { 112 fmt.Fprint(f.Output(), ``) 113 printFlagDefaults(f) 114 } 115 116 const goplsBugPrefix = "x/tools/gopls: <DESCRIBE THE PROBLEM>" 117 const goplsBugHeader = `ATTENTION: Please answer these questions BEFORE submitting your issue. Thanks! 118 119 #### What did you do? 120 If possible, provide a recipe for reproducing the error. 121 A complete runnable program is good. 122 A link on play.golang.org is better. 123 A failing unit test is the best. 124 125 #### What did you expect to see? 126 127 128 #### What did you see instead? 129 130 131 ` 132 133 // Run collects some basic information and then prepares an issue ready to 134 // be reported. 135 func (b *bug) Run(ctx context.Context, args ...string) error { 136 // This undocumented environment variable allows 137 // the cmd integration test (and maintainers) to 138 // trigger a call to bug.Report. 139 if msg := os.Getenv("TEST_GOPLS_BUG"); msg != "" { 140 filecache.Start() // register bug handler 141 goplsbug.Report(msg) 142 return nil 143 } 144 145 // Enumerate bug reports, grouped and sorted. 146 _, reports := filecache.BugReports() 147 sort.Slice(reports, func(i, j int) bool { 148 x, y := reports[i], reports[i] 149 if x.Key != y.Key { 150 return x.Key < y.Key // ascending key order 151 } 152 return y.AtTime.Before(x.AtTime) // most recent first 153 }) 154 keyDenom := make(map[string]int) // key is "file:line" 155 for _, report := range reports { 156 keyDenom[report.Key]++ 157 } 158 159 // Privacy: the content of 'public' will be posted to GitHub 160 // to populate an issue textarea. Even though the user must 161 // submit the form to share the information with the world, 162 // merely populating the form causes us to share the 163 // information with GitHub itself. 164 // 165 // For that reason, we cannot write private information to 166 // public, such as bug reports, which may quote source code. 167 public := &bytes.Buffer{} 168 fmt.Fprint(public, goplsBugHeader) 169 if len(reports) > 0 { 170 fmt.Fprintf(public, "#### Internal errors\n\n") 171 fmt.Fprintf(public, "Gopls detected %d internal errors, %d distinct:\n", 172 len(reports), len(keyDenom)) 173 for key, denom := range keyDenom { 174 fmt.Fprintf(public, "- %s (%d)\n", key, denom) 175 } 176 fmt.Fprintf(public, "\nPlease copy the full information printed by `gopls bug` here, if you are comfortable sharing it.\n\n") 177 } 178 debug.PrintVersionInfo(ctx, public, true, debug.Markdown) 179 body := public.String() 180 title := strings.Join(args, " ") 181 if !strings.HasPrefix(title, goplsBugPrefix) { 182 title = goplsBugPrefix + title 183 } 184 if !browser.Open("https://github.com/golang/go/issues/new?title=" + url.QueryEscape(title) + "&body=" + url.QueryEscape(body)) { 185 fmt.Print("Please file a new issue at golang.org/issue/new using this template:\n\n") 186 fmt.Print(body) 187 } 188 189 // Print bug reports to stdout (not GitHub). 190 keyNum := make(map[string]int) 191 for _, report := range reports { 192 fmt.Printf("-- %v -- \n", report.AtTime) 193 194 // Append seq number (e.g. " (1/2)") for repeated keys. 195 var seq string 196 if denom := keyDenom[report.Key]; denom > 1 { 197 keyNum[report.Key]++ 198 seq = fmt.Sprintf(" (%d/%d)", keyNum[report.Key], denom) 199 } 200 201 // Privacy: 202 // - File and Stack may contain the name of the user that built gopls. 203 // - Description may contain names of the user's packages/files/symbols. 204 fmt.Printf("%s:%d: %s%s\n\n", report.File, report.Line, report.Description, seq) 205 fmt.Printf("%s\n\n", report.Stack) 206 } 207 if len(reports) > 0 { 208 fmt.Printf("Please copy the above information into the GitHub issue, if you are comfortable sharing it.\n") 209 } 210 211 return nil 212 } 213 214 type apiJSON struct { 215 app *Application 216 } 217 218 func (j *apiJSON) Name() string { return "api-json" } 219 func (j *apiJSON) Parent() string { return j.app.Name() } 220 func (j *apiJSON) Usage() string { return "" } 221 func (j *apiJSON) ShortHelp() string { return "print JSON describing gopls API" } 222 func (j *apiJSON) DetailedHelp(f *flag.FlagSet) { 223 fmt.Fprint(f.Output(), ``) 224 printFlagDefaults(f) 225 } 226 227 func (j *apiJSON) Run(ctx context.Context, args ...string) error { 228 js, err := json.MarshalIndent(settings.GeneratedAPIJSON, "", "\t") 229 if err != nil { 230 return err 231 } 232 fmt.Fprint(os.Stdout, string(js)) 233 return nil 234 } 235 236 type licenses struct { 237 app *Application 238 } 239 240 func (l *licenses) Name() string { return "licenses" } 241 func (l *licenses) Parent() string { return l.app.Name() } 242 func (l *licenses) Usage() string { return "" } 243 func (l *licenses) ShortHelp() string { return "print licenses of included software" } 244 func (l *licenses) DetailedHelp(f *flag.FlagSet) { 245 fmt.Fprint(f.Output(), ``) 246 printFlagDefaults(f) 247 } 248 249 const licensePreamble = ` 250 gopls is made available under the following BSD-style license: 251 252 Copyright (c) 2009 The Go Authors. All rights reserved. 253 254 Redistribution and use in source and binary forms, with or without 255 modification, are permitted provided that the following conditions are 256 met: 257 258 * Redistributions of source code must retain the above copyright 259 notice, this list of conditions and the following disclaimer. 260 * Redistributions in binary form must reproduce the above 261 copyright notice, this list of conditions and the following disclaimer 262 in the documentation and/or other materials provided with the 263 distribution. 264 * Neither the name of Google Inc. nor the names of its 265 contributors may be used to endorse or promote products derived from 266 this software without specific prior written permission. 267 268 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 269 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 270 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 271 A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 272 OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 273 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 274 LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 275 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 276 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 277 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 278 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 279 280 gopls implements the LSP specification, which is made available under the following license: 281 282 Copyright (c) Microsoft Corporation 283 284 All rights reserved. 285 286 MIT License 287 288 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation 289 files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, 290 modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software 291 is furnished to do so, subject to the following conditions: 292 293 The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 294 295 THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 296 OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 297 BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT 298 OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 299 300 gopls also includes software made available under these licenses: 301 ` 302 303 func (l *licenses) Run(ctx context.Context, args ...string) error { 304 opts := settings.DefaultOptions(l.app.options) 305 txt := licensePreamble 306 if opts.LicensesText == "" { 307 txt += "(development gopls, license information not available)" 308 } else { 309 txt += opts.LicensesText 310 } 311 fmt.Fprint(os.Stdout, txt) 312 return nil 313 }