diff --git a/gopls/doc/generate.go b/gopls/doc/generate.go index e63653de6b..c7e0e0ffcc 100644 --- a/gopls/doc/generate.go +++ b/gopls/doc/generate.go @@ -63,6 +63,9 @@ func doMain(baseDir string, write bool) (bool, error) { if ok, err := rewriteFile(filepath.Join(baseDir, "gopls/doc/analyzers.md"), api, write, rewriteAnalyzers); !ok || err != nil { return ok, err } + if ok, err := rewriteFile(filepath.Join(baseDir, "gopls/doc/inlayHints.md"), api, write, rewriteInlayHints); !ok || err != nil { + return ok, err + } return true, nil } @@ -102,6 +105,7 @@ func loadAPI() (*source.APIJSON, error) { } { api.Analyzers = append(api.Analyzers, loadAnalyzers(m)...) } + api.Hints = loadHints(source.AllInlayHints) for _, category := range []reflect.Value{ reflect.ValueOf(defaults.UserOptions), } { @@ -146,6 +150,14 @@ func loadAPI() (*source.APIJSON, error) { Default: def, }) } + case "hints": + for _, a := range api.Hints { + opt.EnumKeys.Keys = append(opt.EnumKeys.Keys, source.EnumKey{ + Name: fmt.Sprintf("%q", a.Name), + Doc: a.Doc, + Default: strconv.FormatBool(a.Default), + }) + } } } } @@ -488,6 +500,23 @@ func loadAnalyzers(m map[string]*source.Analyzer) []*source.AnalyzerJSON { return json } +func loadHints(m map[string]*source.Hint) []*source.HintJSON { + var sorted []string + for _, h := range m { + sorted = append(sorted, h.Name) + } + sort.Strings(sorted) + var json []*source.HintJSON + for _, name := range sorted { + h := m[name] + json = append(json, &source.HintJSON{ + Name: h.Name, + Doc: h.Doc, + }) + } + return json +} + func lowerFirst(x string) string { if x == "" { return x @@ -699,6 +728,21 @@ func rewriteAnalyzers(doc []byte, api *source.APIJSON) ([]byte, error) { return replaceSection(doc, "Analyzers", section.Bytes()) } +func rewriteInlayHints(doc []byte, api *source.APIJSON) ([]byte, error) { + section := bytes.NewBuffer(nil) + for _, hint := range api.Hints { + fmt.Fprintf(section, "## **%v**\n\n", hint.Name) + fmt.Fprintf(section, "%s\n\n", hint.Doc) + switch hint.Default { + case true: + fmt.Fprintf(section, "**Enabled by default.**\n\n") + case false: + fmt.Fprintf(section, "**Disabled by default. Enable it by setting `\"hints\": {\"%s\": true}`.**\n\n", hint.Name) + } + } + return replaceSection(doc, "Hints", 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) diff --git a/gopls/doc/inlayHints.md b/gopls/doc/inlayHints.md new file mode 100644 index 0000000000..a4fd3e5155 --- /dev/null +++ b/gopls/doc/inlayHints.md @@ -0,0 +1,73 @@ +# Hints + +This document describes the inlay hints that `gopls` uses inside the editor. + + +## **assign_variable_types** + +Enable/disable inlay hints for variable types in assign statements: + + i/* int/*, j/* int/* := 0, len(r)-1 + +**Disabled by default. Enable it by setting `"hints": {"assign_variable_types": true}`.** + +## **composite_literal_fields** + +Enable/disable inlay hints for composite literal field names: + + {in: "Hello, world", want: "dlrow ,olleH"} + +**Disabled by default. Enable it by setting `"hints": {"composite_literal_fields": true}`.** + +## **composite_literal_types** + +Enable/disable inlay hints for composite literal types: + + for _, c := range []struct { + in, want string + }{ + /*struct{ in string; want string }*/{"Hello, world", "dlrow ,olleH"}, + } + +**Disabled by default. Enable it by setting `"hints": {"composite_literal_types": true}`.** + +## **constant_values** + +Enable/disable inlay hints for constant values: + + const ( + KindNone Kind = iota/* = 0*/ + KindPrint/* = 1*/ + KindPrintf/* = 2*/ + KindErrorf/* = 3*/ + ) + +**Disabled by default. Enable it by setting `"hints": {"constant_values": true}`.** + +## **function_type_parameters** + +Enable/disable inlay hints for implicit type parameters on generic functions: + + myFoo/*[int, string]*/(1, "hello") + +**Disabled by default. Enable it by setting `"hints": {"function_type_parameters": true}`.** + +## **parameter_names** + +Enable/disable inlay hints for parameter names: + + parseInt(/* str: */ "123", /* radix: */ 8) + +**Disabled by default. Enable it by setting `"hints": {"parameter_names": true}`.** + +## **range_variable_types** + +Enable/disable inlay hints for variable types in range statements: + + for k/* int*/, v/* string/* := range []string{} { + fmt.Println(k, v) + } + +**Disabled by default. Enable it by setting `"hints": {"range_variable_types": true}`.** + + diff --git a/gopls/doc/settings.md b/gopls/doc/settings.md index 092a3c7cfa..0ed0e19bb0 100644 --- a/gopls/doc/settings.md +++ b/gopls/doc/settings.md @@ -35,6 +35,7 @@ still be able to independently override specific experimental features. * [Completion](#completion) * [Diagnostic](#diagnostic) * [Documentation](#documentation) + * [Inlayhint](#inlayhint) * [Navigation](#navigation) ### Build @@ -370,6 +371,18 @@ linksInHover toggles the presence of links to documentation in hover. Default: `true`. +#### Inlayhint + +##### **hints** *map[string]bool* + +**This setting is experimental and may be deleted.** + +hints specify inlay hints that users want to see. +A full list of hints that gopls uses can be found +[here](https://github.com/golang/tools/blob/master/gopls/doc/inlayHints.md). + +Default: `{}`. + #### Navigation ##### **importShortcut** *enum* diff --git a/internal/lsp/general.go b/internal/lsp/general.go index 478152bdf9..385a04a25f 100644 --- a/internal/lsp/general.go +++ b/internal/lsp/general.go @@ -153,6 +153,7 @@ See https://github.com/golang/go/issues/45732 for more information.`, HoverProvider: true, DocumentHighlightProvider: true, DocumentLinkProvider: protocol.DocumentLinkOptions{}, + InlayHintProvider: protocol.InlayHintOptions{}, ReferencesProvider: true, RenameProvider: renameOpts, SignatureHelpProvider: protocol.SignatureHelpOptions{ diff --git a/internal/lsp/lsp_test.go b/internal/lsp/lsp_test.go index 56356e9b5a..2ec833b860 100644 --- a/internal/lsp/lsp_test.go +++ b/internal/lsp/lsp_test.go @@ -67,6 +67,9 @@ func testLSP(t *testing.T, datum *tests.Data) { tests.EnableAllAnalyzers(view, options) view.SetOptions(ctx, options) + // Enable all inlay hints for tests. + tests.EnableAllInlayHints(view, options) + // Only run the -modfile specific tests in module mode with Go 1.14 or above. datum.ModfileFlagAvailable = len(snapshot.ModFiles()) > 0 && testenv.Go1Point() >= 14 release() diff --git a/internal/lsp/source/api_json.go b/internal/lsp/source/api_json.go index 0695efc2fa..4188d9d06f 100755 --- a/internal/lsp/source/api_json.go +++ b/internal/lsp/source/api_json.go @@ -505,6 +505,51 @@ var GeneratedAPIJSON = &APIJSON{ Status: "experimental", Hierarchy: "ui.diagnostic", }, + { + Name: "hints", + Type: "map[string]bool", + Doc: "hints specify inlay hints that users want to see.\nA full list of hints that gopls uses can be found\n[here](https://github.com/golang/tools/blob/master/gopls/doc/inlayHints.md).\n", + EnumKeys: EnumKeys{Keys: []EnumKey{ + { + Name: "\"assign_variable_types\"", + Doc: "Enable/disable inlay hints for variable types in assign statements:\n\n\ti/* int/*, j/* int/* := 0, len(r)-1", + Default: "false", + }, + { + Name: "\"composite_literal_fields\"", + Doc: "Enable/disable inlay hints for composite literal field names:\n\n\t{in: \"Hello, world\", want: \"dlrow ,olleH\"}", + Default: "false", + }, + { + Name: "\"composite_literal_types\"", + Doc: "Enable/disable inlay hints for composite literal types:\n\n\tfor _, c := range []struct {\n\t\tin, want string\n\t}{\n\t\t/*struct{ in string; want string }*/{\"Hello, world\", \"dlrow ,olleH\"},\n\t}", + Default: "false", + }, + { + Name: "\"constant_values\"", + Doc: "Enable/disable inlay hints for constant values:\n\n\tconst (\n\t\tKindNone Kind = iota/* = 0*/\n\t\tKindPrint/* = 1*/\n\t\tKindPrintf/* = 2*/\n\t\tKindErrorf/* = 3*/\n\t)", + Default: "false", + }, + { + Name: "\"function_type_parameters\"", + Doc: "Enable/disable inlay hints for implicit type parameters on generic functions:\n\n\tmyFoo/*[int, string]*/(1, \"hello\")", + Default: "false", + }, + { + Name: "\"parameter_names\"", + Doc: "Enable/disable inlay hints for parameter names:\n\n\tparseInt(/* str: */ \"123\", /* radix: */ 8)", + Default: "false", + }, + { + Name: "\"range_variable_types\"", + Doc: "Enable/disable inlay hints for variable types in range statements:\n\n\tfor k/* int*/, v/* string/* := range []string{} {\n\t\tfmt.Println(k, v)\n\t}", + Default: "false", + }, + }}, + Default: "{}", + Status: "experimental", + Hierarchy: "ui.inlayhint", + }, { Name: "codelenses", Type: "map[string]bool", @@ -979,4 +1024,34 @@ var GeneratedAPIJSON = &APIJSON{ Default: true, }, }, + Hints: []*HintJSON{ + { + Name: "assign_variable_types", + Doc: "Enable/disable inlay hints for variable types in assign statements:\n\n\ti/* int/*, j/* int/* := 0, len(r)-1", + }, + { + Name: "composite_literal_fields", + Doc: "Enable/disable inlay hints for composite literal field names:\n\n\t{in: \"Hello, world\", want: \"dlrow ,olleH\"}", + }, + { + Name: "composite_literal_types", + Doc: "Enable/disable inlay hints for composite literal types:\n\n\tfor _, c := range []struct {\n\t\tin, want string\n\t}{\n\t\t/*struct{ in string; want string }*/{\"Hello, world\", \"dlrow ,olleH\"},\n\t}", + }, + { + Name: "constant_values", + Doc: "Enable/disable inlay hints for constant values:\n\n\tconst (\n\t\tKindNone Kind = iota/* = 0*/\n\t\tKindPrint/* = 1*/\n\t\tKindPrintf/* = 2*/\n\t\tKindErrorf/* = 3*/\n\t)", + }, + { + Name: "function_type_parameters", + Doc: "Enable/disable inlay hints for implicit type parameters on generic functions:\n\n\tmyFoo/*[int, string]*/(1, \"hello\")", + }, + { + Name: "parameter_names", + Doc: "Enable/disable inlay hints for parameter names:\n\n\tparseInt(/* str: */ \"123\", /* radix: */ 8)", + }, + { + Name: "range_variable_types", + Doc: "Enable/disable inlay hints for variable types in range statements:\n\n\tfor k/* int*/, v/* string/* := range []string{} {\n\t\tfmt.Println(k, v)\n\t}", + }, + }, } diff --git a/internal/lsp/source/inlay_hint.go b/internal/lsp/source/inlay_hint.go index 8369681003..99e1ad09d8 100644 --- a/internal/lsp/source/inlay_hint.go +++ b/internal/lsp/source/inlay_hint.go @@ -23,6 +23,87 @@ const ( maxLabelLength = 28 ) +type InlayHintFunc func(node ast.Node, tmap *lsppos.TokenMapper, info *types.Info, q *types.Qualifier) []protocol.InlayHint + +type Hint struct { + Name string + Doc string + Run InlayHintFunc +} + +const ( + ParameterNames = "parameter_names" + AssignVariableTypes = "assign_variable_types" + ConstantValues = "constant_values" + RangeVariableTypes = "range_variable_types" + CompositeLiteralTypes = "composite_literal_types" + CompositeLiteralFieldNames = "composite_literal_fields" + FunctionTypeParameters = "function_type_parameters" +) + +var AllInlayHints = map[string]*Hint{ + AssignVariableTypes: { + Name: AssignVariableTypes, + Doc: `Enable/disable inlay hints for variable types in assign statements: + + i/* int/*, j/* int/* := 0, len(r)-1`, + Run: assignVariableTypes, + }, + ParameterNames: { + Name: ParameterNames, + Doc: `Enable/disable inlay hints for parameter names: + + parseInt(/* str: */ "123", /* radix: */ 8)`, + Run: parameterNames, + }, + ConstantValues: { + Name: ConstantValues, + Doc: `Enable/disable inlay hints for constant values: + + const ( + KindNone Kind = iota/* = 0*/ + KindPrint/* = 1*/ + KindPrintf/* = 2*/ + KindErrorf/* = 3*/ + )`, + Run: constantValues, + }, + RangeVariableTypes: { + Name: RangeVariableTypes, + Doc: `Enable/disable inlay hints for variable types in range statements: + + for k/* int*/, v/* string/* := range []string{} { + fmt.Println(k, v) + }`, + Run: rangeVariableTypes, + }, + CompositeLiteralTypes: { + Name: CompositeLiteralTypes, + Doc: `Enable/disable inlay hints for composite literal types: + + for _, c := range []struct { + in, want string + }{ + /*struct{ in string; want string }*/{"Hello, world", "dlrow ,olleH"}, + }`, + Run: compositeLiteralTypes, + }, + CompositeLiteralFieldNames: { + Name: CompositeLiteralFieldNames, + Doc: `Enable/disable inlay hints for composite literal field names: + + {in: "Hello, world", want: "dlrow ,olleH"}`, + Run: compositeLiteralFields, + }, + FunctionTypeParameters: { + Name: FunctionTypeParameters, + Doc: `Enable/disable inlay hints for implicit type parameters on generic functions: + + myFoo/*[int, string]*/(1, "hello")`, + Run: funcTypeParams, + }, +} + func InlayHint(ctx context.Context, snapshot Snapshot, fh FileHandle, _ protocol.Range) ([]protocol.InlayHint, error) { ctx, done := event.Start(ctx, "source.InlayHint") defer done() @@ -32,38 +113,47 @@ func InlayHint(ctx context.Context, snapshot Snapshot, fh FileHandle, _ protocol return nil, fmt.Errorf("getting file for InlayHint: %w", err) } + // Collect a list of the inlay hints that are enabled. + inlayHintOptions := snapshot.View().Options().InlayHintOptions + var enabledHints []InlayHintFunc + for hint, enabled := range inlayHintOptions.Hints { + if !enabled { + continue + } + if h, ok := AllInlayHints[hint]; ok { + enabledHints = append(enabledHints, h.Run) + } + } + if len(enabledHints) == 0 { + return nil, nil + } + tmap := lsppos.NewTokenMapper(pgf.Src, pgf.Tok) info := pkg.GetTypesInfo() q := Qualifier(pgf.File, pkg.GetTypes(), info) var hints []protocol.InlayHint ast.Inspect(pgf.File, func(node ast.Node) bool { - switch n := node.(type) { - case *ast.CallExpr: - hints = append(hints, parameterNames(n, tmap, info)...) - hints = append(hints, funcTypeParams(n, tmap, info)...) - case *ast.AssignStmt: - hints = append(hints, assignVariableTypes(n, tmap, info, &q)...) - case *ast.RangeStmt: - hints = append(hints, rangeVariableTypes(n, tmap, info, &q)...) - case *ast.GenDecl: - hints = append(hints, constantValues(n, tmap, info)...) - case *ast.CompositeLit: - hints = append(hints, compositeLiterals(n, tmap, info, &q)...) + for _, fn := range enabledHints { + hints = append(hints, fn(node, tmap, info, &q)...) } return true }) return hints, nil } -func parameterNames(node *ast.CallExpr, tmap *lsppos.TokenMapper, info *types.Info) []protocol.InlayHint { - signature, ok := info.TypeOf(node.Fun).(*types.Signature) +func parameterNames(node ast.Node, tmap *lsppos.TokenMapper, info *types.Info, _ *types.Qualifier) []protocol.InlayHint { + callExpr, ok := node.(*ast.CallExpr) + if !ok { + return nil + } + signature, ok := info.TypeOf(callExpr.Fun).(*types.Signature) if !ok { return nil } var hints []protocol.InlayHint - for i, v := range node.Args { + for i, v := range callExpr.Args { start, ok := tmap.Position(v.Pos()) if !ok { continue @@ -92,8 +182,12 @@ func parameterNames(node *ast.CallExpr, tmap *lsppos.TokenMapper, info *types.In return hints } -func funcTypeParams(node *ast.CallExpr, tmap *lsppos.TokenMapper, info *types.Info) []protocol.InlayHint { - id, ok := node.Fun.(*ast.Ident) +func funcTypeParams(node ast.Node, tmap *lsppos.TokenMapper, info *types.Info, _ *types.Qualifier) []protocol.InlayHint { + ce, ok := node.(*ast.CallExpr) + if !ok { + return nil + } + id, ok := ce.Fun.(*ast.Ident) if !ok { return nil } @@ -119,12 +213,14 @@ func funcTypeParams(node *ast.CallExpr, tmap *lsppos.TokenMapper, info *types.In }} } -func assignVariableTypes(node *ast.AssignStmt, tmap *lsppos.TokenMapper, info *types.Info, q *types.Qualifier) []protocol.InlayHint { - if node.Tok != token.DEFINE { +func assignVariableTypes(node ast.Node, tmap *lsppos.TokenMapper, info *types.Info, q *types.Qualifier) []protocol.InlayHint { + stmt, ok := node.(*ast.AssignStmt) + if !ok || stmt.Tok != token.DEFINE { return nil } + var hints []protocol.InlayHint - for _, v := range node.Lhs { + for _, v := range stmt.Lhs { if h := variableType(v, tmap, info, q); h != nil { hints = append(hints, *h) } @@ -132,12 +228,16 @@ func assignVariableTypes(node *ast.AssignStmt, tmap *lsppos.TokenMapper, info *t return hints } -func rangeVariableTypes(node *ast.RangeStmt, tmap *lsppos.TokenMapper, info *types.Info, q *types.Qualifier) []protocol.InlayHint { +func rangeVariableTypes(node ast.Node, tmap *lsppos.TokenMapper, info *types.Info, q *types.Qualifier) []protocol.InlayHint { + rStmt, ok := node.(*ast.RangeStmt) + if !ok { + return nil + } var hints []protocol.InlayHint - if h := variableType(node.Key, tmap, info, q); h != nil { + if h := variableType(rStmt.Key, tmap, info, q); h != nil { hints = append(hints, *h) } - if h := variableType(node.Value, tmap, info, q); h != nil { + if h := variableType(rStmt.Value, tmap, info, q); h != nil { hints = append(hints, *h) } return hints @@ -160,13 +260,14 @@ func variableType(e ast.Expr, tmap *lsppos.TokenMapper, info *types.Info, q *typ } } -func constantValues(node *ast.GenDecl, tmap *lsppos.TokenMapper, info *types.Info) []protocol.InlayHint { - if node.Tok != token.CONST { +func constantValues(node ast.Node, tmap *lsppos.TokenMapper, info *types.Info, _ *types.Qualifier) []protocol.InlayHint { + genDecl, ok := node.(*ast.GenDecl) + if !ok || genDecl.Tok != token.CONST { return nil } var hints []protocol.InlayHint - for _, v := range node.Specs { + for _, v := range genDecl.Specs { spec, ok := v.(*ast.ValueSpec) if !ok { continue @@ -210,36 +311,26 @@ func constantValues(node *ast.GenDecl, tmap *lsppos.TokenMapper, info *types.Inf return hints } -func compositeLiterals(node *ast.CompositeLit, tmap *lsppos.TokenMapper, info *types.Info, q *types.Qualifier) []protocol.InlayHint { - typ := info.TypeOf(node) +func compositeLiteralFields(node ast.Node, tmap *lsppos.TokenMapper, info *types.Info, q *types.Qualifier) []protocol.InlayHint { + compLit, ok := node.(*ast.CompositeLit) + if !ok { + return nil + } + typ := info.TypeOf(compLit) if typ == nil { return nil } - - prefix := "" if t, ok := typ.(*types.Pointer); ok { typ = t.Elem() - prefix = "&" } - strct, ok := typ.Underlying().(*types.Struct) if !ok { return nil } var hints []protocol.InlayHint - if node.Type == nil { - // The type for this struct is implicit, add an inlay hint. - if start, ok := tmap.Position(node.Lbrace); ok { - hints = append(hints, protocol.InlayHint{ - Position: &start, - Label: buildLabel(fmt.Sprintf("%s%s", prefix, types.TypeString(typ, *q))), - Kind: protocol.Type, - }) - } - } - for i, v := range node.Elts { + for i, v := range compLit.Elts { if _, ok := v.(*ast.KeyValueExpr); !ok { start, ok := tmap.Position(v.Pos()) if !ok { @@ -259,6 +350,35 @@ func compositeLiterals(node *ast.CompositeLit, tmap *lsppos.TokenMapper, info *t return hints } +func compositeLiteralTypes(node ast.Node, tmap *lsppos.TokenMapper, info *types.Info, q *types.Qualifier) []protocol.InlayHint { + compLit, ok := node.(*ast.CompositeLit) + if !ok { + return nil + } + typ := info.TypeOf(compLit) + if typ == nil { + return nil + } + if compLit.Type != nil { + return nil + } + prefix := "" + if t, ok := typ.(*types.Pointer); ok { + typ = t.Elem() + prefix = "&" + } + // The type for this composite literal is implicit, add an inlay hint. + start, ok := tmap.Position(compLit.Lbrace) + if !ok { + return nil + } + return []protocol.InlayHint{{ + Position: &start, + Label: buildLabel(fmt.Sprintf("%s%s", prefix, types.TypeString(typ, *q))), + Kind: protocol.Type, + }} +} + func buildLabel(s string) []protocol.InlayHintLabelPart { label := protocol.InlayHintLabelPart{ Value: s, diff --git a/internal/lsp/source/options.go b/internal/lsp/source/options.go index d1d34efe78..5da14ebfe9 100644 --- a/internal/lsp/source/options.go +++ b/internal/lsp/source/options.go @@ -130,6 +130,7 @@ func DefaultOptions() *Options { Nil: true, }, }, + InlayHintOptions: InlayHintOptions{}, DocumentationOptions: DocumentationOptions{ HoverKind: FullDocumentation, LinkTarget: "pkg.go.dev", @@ -289,6 +290,7 @@ type UIOptions struct { CompletionOptions NavigationOptions DiagnosticOptions + InlayHintOptions // Codelenses overrides the enabled/disabled state of code lenses. See the // "Code Lenses" section of the @@ -407,6 +409,13 @@ type DiagnosticOptions struct { ExperimentalWatchedFileDelay time.Duration `status:"experimental"` } +type InlayHintOptions struct { + // Hints specify inlay hints that users want to see. + // A full list of hints that gopls uses can be found + // [here](https://github.com/golang/tools/blob/master/gopls/doc/inlayHints.md). + Hints map[string]bool `status:"experimental"` +} + type NavigationOptions struct { // ImportShortcut specifies whether import statements should link to // documentation or go to definitions. @@ -915,6 +924,9 @@ func (o *Options) set(name string, value interface{}, seen map[string]struct{}) case "analyses": result.setBoolMap(&o.Analyses) + case "hints": + result.setBoolMap(&o.Hints) + case "annotations": result.setAnnotationMap(&o.Annotations) @@ -1351,6 +1363,7 @@ type APIJSON struct { Commands []*CommandJSON Lenses []*LensJSON Analyzers []*AnalyzerJSON + Hints []*HintJSON } type OptionJSON struct { @@ -1416,12 +1429,8 @@ func collectEnums(opt *OptionJSON) string { } func shouldShowEnumKeysInSettings(name string) bool { - // Both of these fields have too many possible options to print. - return !hardcodedEnumKeys(name) -} - -func hardcodedEnumKeys(name string) bool { - return name == "analyses" || name == "codelenses" + // These fields have too many possible options to print. + return !(name == "analyses" || name == "codelenses" || name == "hints") } type EnumKeys struct { @@ -1489,3 +1498,17 @@ func (a *AnalyzerJSON) String() string { func (a *AnalyzerJSON) Write(w io.Writer) { fmt.Fprintf(w, "%s (%s): %v", a.Name, a.Doc, a.Default) } + +type HintJSON struct { + Name string + Doc string + Default bool +} + +func (h *HintJSON) String() string { + return h.Name +} + +func (h *HintJSON) Write(w io.Writer) { + fmt.Fprintf(w, "%s (%s): %v", h.Name, h.Doc, h.Default) +} diff --git a/internal/lsp/tests/util.go b/internal/lsp/tests/util.go index 11dda1f8ed..98562d6365 100644 --- a/internal/lsp/tests/util.go +++ b/internal/lsp/tests/util.go @@ -512,6 +512,15 @@ func EnableAllAnalyzers(view source.View, opts *source.Options) { } } +func EnableAllInlayHints(view source.View, opts *source.Options) { + if opts.Hints == nil { + opts.Hints = make(map[string]bool) + } + for name := range source.AllInlayHints { + opts.Hints[name] = true + } +} + func WorkspaceSymbolsString(ctx context.Context, data *Data, queryURI span.URI, symbols []protocol.SymbolInformation) (string, error) { queryDir := filepath.Dir(queryURI.Filename()) var filtered []string