mirror of https://github.com/golang/go.git
internal/lsp: move configuration options to structs
This cl is the first in a set that change the configuration behaviour. This one should have no behaviour differences, but makes a lot of preparatory changes. The same options are set to the same values in the same places. The options are now stored on the Session instead of the Server The View supports options, but does not have any yet. Change-Id: Ie966cceca6878861686a1766d63bb8a78021259b Reviewed-on: https://go-review.googlesource.com/c/tools/+/193726 Run-TryBot: Ian Cottrell <iancottrell@google.com> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Rebecca Stambler <rstambler@golang.org>
This commit is contained in:
parent
fa2c3f315e
commit
f1f4a3381f
|
|
@ -76,6 +76,7 @@ func (c *cache) NewSession(ctx context.Context) source.Session {
|
|||
s := &session{
|
||||
cache: c,
|
||||
id: strconv.FormatInt(index, 10),
|
||||
options: source.DefaultSessionOptions,
|
||||
overlays: make(map[span.URI]*overlay),
|
||||
filesWatchMap: NewWatchMap(),
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,6 +28,8 @@ type session struct {
|
|||
cache *cache
|
||||
id string
|
||||
|
||||
options source.SessionOptions
|
||||
|
||||
viewMu sync.Mutex
|
||||
views []*view
|
||||
viewMap map[span.URI]source.View
|
||||
|
|
@ -54,6 +56,14 @@ type overlay struct {
|
|||
unchanged bool
|
||||
}
|
||||
|
||||
func (s *session) Options() source.SessionOptions {
|
||||
return s.options
|
||||
}
|
||||
|
||||
func (s *session) SetOptions(options source.SessionOptions) {
|
||||
s.options = options
|
||||
}
|
||||
|
||||
func (s *session) Shutdown(ctx context.Context) {
|
||||
s.viewMu.Lock()
|
||||
defer s.viewMu.Unlock()
|
||||
|
|
|
|||
|
|
@ -30,6 +30,8 @@ type view struct {
|
|||
session *session
|
||||
id string
|
||||
|
||||
options source.ViewOptions
|
||||
|
||||
// mu protects all mutable state of the view.
|
||||
mu sync.Mutex
|
||||
|
||||
|
|
@ -118,6 +120,10 @@ func (v *view) Folder() span.URI {
|
|||
return v.folder
|
||||
}
|
||||
|
||||
func (v *view) Options() source.ViewOptions {
|
||||
return v.options
|
||||
}
|
||||
|
||||
// Config returns the configuration used for the view's interaction with the
|
||||
// go/packages API. It is shared across all views.
|
||||
func (v *view) Config(ctx context.Context) *packages.Config {
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ import (
|
|||
|
||||
func (s *Server) getSupportedCodeActions() []protocol.CodeActionKind {
|
||||
allCodeActionKinds := make(map[protocol.CodeActionKind]struct{})
|
||||
for _, kinds := range s.supportedCodeActions {
|
||||
for _, kinds := range s.session.Options().SupportedCodeActions {
|
||||
for kind := range kinds {
|
||||
allCodeActionKinds[kind] = struct{}{}
|
||||
}
|
||||
|
|
@ -51,7 +51,7 @@ func (s *Server) codeAction(ctx context.Context, params *protocol.CodeActionPara
|
|||
|
||||
// Determine the supported actions for this file kind.
|
||||
fileKind := f.Handle(ctx).Kind()
|
||||
supportedCodeActions, ok := s.supportedCodeActions[fileKind]
|
||||
supportedCodeActions, ok := s.session.Options().SupportedCodeActions[fileKind]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("no supported code actions for %v file kind", fileKind)
|
||||
}
|
||||
|
|
@ -87,7 +87,7 @@ func (s *Server) codeAction(ctx context.Context, params *protocol.CodeActionPara
|
|||
if wanted[protocol.QuickFix] {
|
||||
// First, add the quick fixes reported by go/analysis.
|
||||
// TODO: Enable this when this actually works. For now, it's needless work.
|
||||
if s.wantSuggestedFixes {
|
||||
if s.session.Options().SuggestedFixes {
|
||||
gof, ok := f.(source.GoFile)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("%s is not a Go file", f.URI())
|
||||
|
|
|
|||
|
|
@ -19,17 +19,13 @@ import (
|
|||
func (s *Server) completion(ctx context.Context, params *protocol.CompletionParams) (*protocol.CompletionList, error) {
|
||||
uri := span.NewURI(params.TextDocument.URI)
|
||||
view := s.session.ViewOf(uri)
|
||||
options := s.session.Options()
|
||||
f, err := getGoFile(ctx, view, uri)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
candidates, surrounding, err := source.Completion(ctx, view, f, params.Position, source.CompletionOptions{
|
||||
WantDeepCompletion: !s.disableDeepCompletion,
|
||||
WantFuzzyMatching: !s.disableFuzzyMatching,
|
||||
NoDocumentation: !s.wantCompletionDocumentation,
|
||||
WantFullDocumentation: s.hoverKind == fullDocumentation,
|
||||
WantUnimported: s.wantUnimportedCompletions,
|
||||
})
|
||||
options.Completion.FullDocumentation = options.HoverKind == source.FullDocumentation
|
||||
candidates, surrounding, err := source.Completion(ctx, view, f, params.Position, options.Completion)
|
||||
if err != nil {
|
||||
log.Print(ctx, "no completions found", tag.Of("At", params.Position), tag.Of("Failure", err))
|
||||
}
|
||||
|
|
@ -50,12 +46,12 @@ func (s *Server) completion(ctx context.Context, params *protocol.CompletionPara
|
|||
return &protocol.CompletionList{
|
||||
// When using deep completions/fuzzy matching, report results as incomplete so
|
||||
// client fetches updated completions after every key stroke.
|
||||
IsIncomplete: !s.disableDeepCompletion,
|
||||
Items: s.toProtocolCompletionItems(candidates, rng),
|
||||
IsIncomplete: options.Completion.Deep,
|
||||
Items: s.toProtocolCompletionItems(candidates, rng, options),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *Server) toProtocolCompletionItems(candidates []source.CompletionItem, rng protocol.Range) []protocol.CompletionItem {
|
||||
func (s *Server) toProtocolCompletionItems(candidates []source.CompletionItem, rng protocol.Range, options source.SessionOptions) []protocol.CompletionItem {
|
||||
var (
|
||||
items = make([]protocol.CompletionItem, 0, len(candidates))
|
||||
numDeepCompletionsSeen int
|
||||
|
|
@ -64,7 +60,7 @@ func (s *Server) toProtocolCompletionItems(candidates []source.CompletionItem, r
|
|||
// Limit the number of deep completions to not overwhelm the user in cases
|
||||
// with dozens of deep completion matches.
|
||||
if candidate.Depth > 0 {
|
||||
if s.disableDeepCompletion {
|
||||
if !options.Completion.Deep {
|
||||
continue
|
||||
}
|
||||
if numDeepCompletionsSeen >= source.MaxDeepCompletions {
|
||||
|
|
@ -73,8 +69,8 @@ func (s *Server) toProtocolCompletionItems(candidates []source.CompletionItem, r
|
|||
numDeepCompletionsSeen++
|
||||
}
|
||||
insertText := candidate.InsertText
|
||||
if s.insertTextFormat == protocol.SnippetTextFormat {
|
||||
insertText = candidate.Snippet(s.usePlaceholders)
|
||||
if options.InsertTextFormat == protocol.SnippetTextFormat {
|
||||
insertText = candidate.Snippet(options.UsePlaceholders)
|
||||
}
|
||||
item := protocol.CompletionItem{
|
||||
Label: candidate.Label,
|
||||
|
|
@ -84,7 +80,7 @@ func (s *Server) toProtocolCompletionItems(candidates []source.CompletionItem, r
|
|||
NewText: insertText,
|
||||
Range: rng,
|
||||
},
|
||||
InsertTextFormat: s.insertTextFormat,
|
||||
InsertTextFormat: options.InsertTextFormat,
|
||||
AdditionalTextEdits: candidate.AdditionalTextEdits,
|
||||
// This is a hack so that the client sorts completion results in the order
|
||||
// according to their score. This can be removed upon the resolution of
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ func (s *Server) Diagnostics(ctx context.Context, view source.View, uri span.URI
|
|||
if !ok {
|
||||
return
|
||||
}
|
||||
reports, err := source.Diagnostics(ctx, view, gof, s.disabledAnalyses)
|
||||
reports, err := source.Diagnostics(ctx, view, gof, s.session.Options().DisabledAnalyses)
|
||||
if err != nil {
|
||||
log.Error(ctx, "failed to compute diagnostics", err, telemetry.File)
|
||||
return
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ func (s *Server) foldingRange(ctx context.Context, params *protocol.FoldingRange
|
|||
return nil, err
|
||||
}
|
||||
|
||||
ranges, err := source.FoldingRange(ctx, view, f, s.lineFoldingOnly)
|
||||
ranges, err := source.FoldingRange(ctx, view, f, s.session.Options().LineFoldingOnly)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,21 +32,24 @@ func (s *Server) initialize(ctx context.Context, params *protocol.InitializePara
|
|||
s.state = serverInitializing
|
||||
s.stateMu.Unlock()
|
||||
|
||||
options := s.session.Options()
|
||||
defer func() { s.session.SetOptions(options) }()
|
||||
|
||||
// TODO: Remove the option once we are certain there are no issues here.
|
||||
s.textDocumentSyncKind = protocol.Incremental
|
||||
options.TextDocumentSyncKind = protocol.Incremental
|
||||
if opts, ok := params.InitializationOptions.(map[string]interface{}); ok {
|
||||
if opt, ok := opts["noIncrementalSync"].(bool); ok && opt {
|
||||
s.textDocumentSyncKind = protocol.Full
|
||||
options.TextDocumentSyncKind = protocol.Full
|
||||
}
|
||||
|
||||
// Check if user has enabled watching for file changes.
|
||||
s.watchFileChanges, _ = opts["watchFileChanges"].(bool)
|
||||
setBool(&options.WatchFileChanges, opts, "watchFileChanges")
|
||||
}
|
||||
|
||||
// Default to using synopsis as a default for hover information.
|
||||
s.hoverKind = synopsisDocumentation
|
||||
options.HoverKind = source.SynopsisDocumentation
|
||||
|
||||
s.supportedCodeActions = map[source.FileKind]map[protocol.CodeActionKind]bool{
|
||||
options.SupportedCodeActions = map[source.FileKind]map[protocol.CodeActionKind]bool{
|
||||
source.Go: {
|
||||
protocol.SourceOrganizeImports: true,
|
||||
protocol.QuickFix: true,
|
||||
|
|
@ -55,7 +58,7 @@ func (s *Server) initialize(ctx context.Context, params *protocol.InitializePara
|
|||
source.Sum: {},
|
||||
}
|
||||
|
||||
s.setClientCapabilities(params.Capabilities)
|
||||
s.setClientCapabilities(&options, params.Capabilities)
|
||||
|
||||
folders := params.WorkspaceFolders
|
||||
if len(folders) == 0 {
|
||||
|
|
@ -117,7 +120,7 @@ func (s *Server) initialize(ctx context.Context, params *protocol.InitializePara
|
|||
TriggerCharacters: []string{"(", ","},
|
||||
},
|
||||
TextDocumentSync: &protocol.TextDocumentSyncOptions{
|
||||
Change: s.textDocumentSyncKind,
|
||||
Change: options.TextDocumentSyncKind,
|
||||
OpenClose: true,
|
||||
Save: &protocol.SaveOptions{
|
||||
IncludeText: false,
|
||||
|
|
@ -142,24 +145,24 @@ func (s *Server) initialize(ctx context.Context, params *protocol.InitializePara
|
|||
}, nil
|
||||
}
|
||||
|
||||
func (s *Server) setClientCapabilities(caps protocol.ClientCapabilities) {
|
||||
func (s *Server) setClientCapabilities(o *source.SessionOptions, caps protocol.ClientCapabilities) {
|
||||
// Check if the client supports snippets in completion items.
|
||||
s.insertTextFormat = protocol.PlainTextTextFormat
|
||||
o.InsertTextFormat = protocol.PlainTextTextFormat
|
||||
if caps.TextDocument.Completion.CompletionItem.SnippetSupport {
|
||||
s.insertTextFormat = protocol.SnippetTextFormat
|
||||
o.InsertTextFormat = protocol.SnippetTextFormat
|
||||
}
|
||||
// Check if the client supports configuration messages.
|
||||
s.configurationSupported = caps.Workspace.Configuration
|
||||
s.dynamicConfigurationSupported = caps.Workspace.DidChangeConfiguration.DynamicRegistration
|
||||
s.dynamicWatchedFilesSupported = caps.Workspace.DidChangeWatchedFiles.DynamicRegistration
|
||||
o.ConfigurationSupported = caps.Workspace.Configuration
|
||||
o.DynamicConfigurationSupported = caps.Workspace.DidChangeConfiguration.DynamicRegistration
|
||||
o.DynamicWatchedFilesSupported = caps.Workspace.DidChangeWatchedFiles.DynamicRegistration
|
||||
|
||||
// Check which types of content format are supported by this client.
|
||||
s.preferredContentFormat = protocol.PlainText
|
||||
o.PreferredContentFormat = protocol.PlainText
|
||||
if len(caps.TextDocument.Hover.ContentFormat) > 0 {
|
||||
s.preferredContentFormat = caps.TextDocument.Hover.ContentFormat[0]
|
||||
o.PreferredContentFormat = caps.TextDocument.Hover.ContentFormat[0]
|
||||
}
|
||||
// Check if the client supports only line folding.
|
||||
s.lineFoldingOnly = caps.TextDocument.FoldingRange.LineFoldingOnly
|
||||
o.LineFoldingOnly = caps.TextDocument.FoldingRange.LineFoldingOnly
|
||||
}
|
||||
|
||||
func (s *Server) initialized(ctx context.Context, params *protocol.InitializedParams) error {
|
||||
|
|
@ -167,8 +170,11 @@ func (s *Server) initialized(ctx context.Context, params *protocol.InitializedPa
|
|||
s.state = serverInitialized
|
||||
s.stateMu.Unlock()
|
||||
|
||||
options := s.session.Options()
|
||||
defer func() { s.session.SetOptions(options) }()
|
||||
|
||||
var registrations []protocol.Registration
|
||||
if s.configurationSupported && s.dynamicConfigurationSupported {
|
||||
if options.ConfigurationSupported && options.DynamicConfigurationSupported {
|
||||
registrations = append(registrations,
|
||||
protocol.Registration{
|
||||
ID: "workspace/didChangeConfiguration",
|
||||
|
|
@ -181,7 +187,7 @@ func (s *Server) initialized(ctx context.Context, params *protocol.InitializedPa
|
|||
)
|
||||
}
|
||||
|
||||
if s.watchFileChanges && s.dynamicWatchedFilesSupported {
|
||||
if options.WatchFileChanges && options.DynamicWatchedFilesSupported {
|
||||
registrations = append(registrations, protocol.Registration{
|
||||
ID: "workspace/didChangeWatchedFiles",
|
||||
Method: "workspace/didChangeWatchedFiles",
|
||||
|
|
@ -200,9 +206,9 @@ func (s *Server) initialized(ctx context.Context, params *protocol.InitializedPa
|
|||
})
|
||||
}
|
||||
|
||||
if s.configurationSupported {
|
||||
if options.ConfigurationSupported {
|
||||
for _, view := range s.session.Views() {
|
||||
if err := s.fetchConfig(ctx, view); err != nil {
|
||||
if err := s.fetchConfig(ctx, view, &options); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
|
@ -213,7 +219,7 @@ func (s *Server) initialized(ctx context.Context, params *protocol.InitializedPa
|
|||
return nil
|
||||
}
|
||||
|
||||
func (s *Server) fetchConfig(ctx context.Context, view source.View) error {
|
||||
func (s *Server) fetchConfig(ctx context.Context, view source.View, options *source.SessionOptions) error {
|
||||
configs, err := s.client.Configuration(ctx, &protocol.ConfigurationParams{
|
||||
Items: []protocol.ConfigurationItem{{
|
||||
ScopeURI: protocol.NewURI(view.Folder()),
|
||||
|
|
@ -228,14 +234,14 @@ func (s *Server) fetchConfig(ctx context.Context, view source.View) error {
|
|||
return err
|
||||
}
|
||||
for _, config := range configs {
|
||||
if err := s.processConfig(ctx, view, config); err != nil {
|
||||
if err := s.processConfig(ctx, view, options, config); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Server) processConfig(ctx context.Context, view source.View, config interface{}) error {
|
||||
func (s *Server) processConfig(ctx context.Context, view source.View, options *source.SessionOptions, config interface{}) error {
|
||||
// TODO: We should probably store and process more of the config.
|
||||
if config == nil {
|
||||
return nil // ignore error if you don't have a config
|
||||
|
|
@ -274,28 +280,23 @@ func (s *Server) processConfig(ctx context.Context, view source.View, config int
|
|||
|
||||
// Check if the user wants documentation in completion items.
|
||||
// This defaults to true.
|
||||
s.wantCompletionDocumentation = true
|
||||
if wantCompletionDocumentation, ok := c["wantCompletionDocumentation"].(bool); ok {
|
||||
s.wantCompletionDocumentation = wantCompletionDocumentation
|
||||
}
|
||||
options.Completion.Documentation = true
|
||||
setBool(&options.Completion.Documentation, c, "wantCompletionDocumentation")
|
||||
setBool(&options.UsePlaceholders, c, "usePlaceholders")
|
||||
|
||||
// Check if placeholders are enabled.
|
||||
if usePlaceholders, ok := c["usePlaceholders"].(bool); ok {
|
||||
s.usePlaceholders = usePlaceholders
|
||||
}
|
||||
// Set the hover kind.
|
||||
if hoverKind, ok := c["hoverKind"].(string); ok {
|
||||
switch hoverKind {
|
||||
case "NoDocumentation":
|
||||
s.hoverKind = noDocumentation
|
||||
options.HoverKind = source.NoDocumentation
|
||||
case "SingleLine":
|
||||
s.hoverKind = singleLine
|
||||
options.HoverKind = source.SingleLine
|
||||
case "SynopsisDocumentation":
|
||||
s.hoverKind = synopsisDocumentation
|
||||
options.HoverKind = source.SynopsisDocumentation
|
||||
case "FullDocumentation":
|
||||
s.hoverKind = fullDocumentation
|
||||
options.HoverKind = source.FullDocumentation
|
||||
case "Structured":
|
||||
s.hoverKind = structured
|
||||
options.HoverKind = source.Structured
|
||||
default:
|
||||
log.Error(ctx, "unsupported hover kind", nil, tag.Of("HoverKind", hoverKind))
|
||||
// The default value is already be set to synopsis.
|
||||
|
|
@ -303,28 +304,23 @@ func (s *Server) processConfig(ctx context.Context, view source.View, config int
|
|||
}
|
||||
|
||||
// Check if the user wants to see suggested fixes from go/analysis.
|
||||
if wantSuggestedFixes, ok := c["wantSuggestedFixes"].(bool); ok {
|
||||
s.wantSuggestedFixes = wantSuggestedFixes
|
||||
}
|
||||
setBool(&options.SuggestedFixes, c, "wantSuggestedFixes")
|
||||
|
||||
// Check if the user has explicitly disabled any analyses.
|
||||
if disabledAnalyses, ok := c["experimentalDisabledAnalyses"].([]interface{}); ok {
|
||||
s.disabledAnalyses = make(map[string]struct{})
|
||||
options.DisabledAnalyses = make(map[string]struct{})
|
||||
for _, a := range disabledAnalyses {
|
||||
if a, ok := a.(string); ok {
|
||||
s.disabledAnalyses[a] = struct{}{}
|
||||
options.DisabledAnalyses[a] = struct{}{}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
s.disableDeepCompletion, _ = c["disableDeepCompletion"].(bool)
|
||||
s.disableFuzzyMatching, _ = c["disableFuzzyMatching"].(bool)
|
||||
setNotBool(&options.Completion.Deep, c, "disableDeepCompletion")
|
||||
setNotBool(&options.Completion.FuzzyMatching, c, "disableFuzzyMatching")
|
||||
|
||||
// Check if want unimported package completions.
|
||||
if wantUnimportedCompletions, ok := c["wantUnimportedCompletions"].(bool); ok {
|
||||
s.wantUnimportedCompletions = wantUnimportedCompletions
|
||||
}
|
||||
|
||||
setBool(&options.Completion.Unimported, c, "wantUnimportedCompletions")
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
@ -349,3 +345,15 @@ func (s *Server) exit(ctx context.Context) error {
|
|||
os.Exit(0)
|
||||
return nil
|
||||
}
|
||||
|
||||
func setBool(b *bool, m map[string]interface{}, name string) {
|
||||
if v, ok := m[name].(bool); ok {
|
||||
*b = v
|
||||
}
|
||||
}
|
||||
|
||||
func setNotBool(b *bool, m map[string]interface{}, name string) {
|
||||
if v, ok := m[name].(bool); ok {
|
||||
*b = !v
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,22 +15,6 @@ import (
|
|||
"golang.org/x/tools/internal/telemetry/log"
|
||||
)
|
||||
|
||||
type hoverKind int
|
||||
|
||||
const (
|
||||
singleLine = hoverKind(iota)
|
||||
noDocumentation
|
||||
synopsisDocumentation
|
||||
fullDocumentation
|
||||
|
||||
// structured is an experimental setting that returns a structured hover format.
|
||||
// This format separates the signature from the documentation, so that the client
|
||||
// can do more manipulation of these fields.
|
||||
//
|
||||
// This should only be used by clients that support this behavior.
|
||||
structured
|
||||
)
|
||||
|
||||
func (s *Server) hover(ctx context.Context, params *protocol.TextDocumentPositionParams) (*protocol.Hover, error) {
|
||||
uri := span.NewURI(params.TextDocument.URI)
|
||||
view := s.session.ViewOf(uri)
|
||||
|
|
@ -58,31 +42,32 @@ func (s *Server) hover(ctx context.Context, params *protocol.TextDocumentPositio
|
|||
}
|
||||
|
||||
func (s *Server) toProtocolHoverContents(ctx context.Context, h *source.HoverInformation) protocol.MarkupContent {
|
||||
options := s.session.Options()
|
||||
content := protocol.MarkupContent{
|
||||
Kind: s.preferredContentFormat,
|
||||
Kind: options.PreferredContentFormat,
|
||||
}
|
||||
signature := h.Signature
|
||||
if content.Kind == protocol.Markdown {
|
||||
signature = fmt.Sprintf("```go\n%s\n```", h.Signature)
|
||||
}
|
||||
switch s.hoverKind {
|
||||
case singleLine:
|
||||
switch options.HoverKind {
|
||||
case source.SingleLine:
|
||||
content.Value = h.SingleLine
|
||||
case noDocumentation:
|
||||
case source.NoDocumentation:
|
||||
content.Value = signature
|
||||
case synopsisDocumentation:
|
||||
case source.SynopsisDocumentation:
|
||||
if h.Synopsis != "" {
|
||||
content.Value = fmt.Sprintf("%s\n%s", h.Synopsis, signature)
|
||||
} else {
|
||||
content.Value = signature
|
||||
}
|
||||
case fullDocumentation:
|
||||
case source.FullDocumentation:
|
||||
if h.FullDocumentation != "" {
|
||||
content.Value = fmt.Sprintf("%s\n%s", signature, h.FullDocumentation)
|
||||
} else {
|
||||
content.Value = signature
|
||||
}
|
||||
case structured:
|
||||
case source.Structured:
|
||||
b, err := json.Marshal(h)
|
||||
if err != nil {
|
||||
log.Error(ctx, "failed to marshal structured hover", err)
|
||||
|
|
|
|||
|
|
@ -55,23 +55,27 @@ func testLSP(t *testing.T, exporter packagestest.Exporter) {
|
|||
for filename, content := range data.Config.Overlay {
|
||||
session.SetOverlay(span.FileURI(filename), content)
|
||||
}
|
||||
options := session.Options()
|
||||
options.SupportedCodeActions = map[source.FileKind]map[protocol.CodeActionKind]bool{
|
||||
source.Go: {
|
||||
protocol.SourceOrganizeImports: true,
|
||||
protocol.QuickFix: true,
|
||||
},
|
||||
source.Mod: {},
|
||||
source.Sum: {},
|
||||
}
|
||||
options.HoverKind = source.SynopsisDocumentation
|
||||
session.SetOptions(options)
|
||||
|
||||
r := &runner{
|
||||
server: &Server{
|
||||
session: session,
|
||||
undelivered: make(map[span.URI][]source.Diagnostic),
|
||||
supportedCodeActions: map[source.FileKind]map[protocol.CodeActionKind]bool{
|
||||
source.Go: {
|
||||
protocol.SourceOrganizeImports: true,
|
||||
protocol.QuickFix: true,
|
||||
},
|
||||
source.Mod: {},
|
||||
source.Sum: {},
|
||||
},
|
||||
hoverKind: synopsisDocumentation,
|
||||
},
|
||||
data: data,
|
||||
ctx: ctx,
|
||||
}
|
||||
|
||||
tests.Run(t, r, data)
|
||||
}
|
||||
|
||||
|
|
@ -106,14 +110,12 @@ func (r *runner) Diagnostics(t *testing.T, data tests.Diagnostics) {
|
|||
}
|
||||
|
||||
func (r *runner) Completion(t *testing.T, data tests.Completions, snippets tests.CompletionSnippets, items tests.CompletionItems) {
|
||||
defer func() {
|
||||
r.server.disableDeepCompletion = true
|
||||
r.server.disableFuzzyMatching = true
|
||||
r.server.wantUnimportedCompletions = false
|
||||
}()
|
||||
original := r.server.session.Options()
|
||||
modified := original
|
||||
defer func() { r.server.session.SetOptions(original) }()
|
||||
|
||||
// Set this as a default.
|
||||
r.server.wantCompletionDocumentation = true
|
||||
modified.Completion.Documentation = true
|
||||
|
||||
for src, itemList := range data {
|
||||
var want []source.CompletionItem
|
||||
|
|
@ -121,9 +123,10 @@ func (r *runner) Completion(t *testing.T, data tests.Completions, snippets tests
|
|||
want = append(want, *items[pos])
|
||||
}
|
||||
|
||||
r.server.disableDeepCompletion = !strings.Contains(string(src.URI()), "deepcomplete")
|
||||
r.server.disableFuzzyMatching = !strings.Contains(string(src.URI()), "fuzzymatch")
|
||||
r.server.wantUnimportedCompletions = strings.Contains(string(src.URI()), "unimported")
|
||||
modified.Completion.Deep = strings.Contains(string(src.URI()), "deepcomplete")
|
||||
modified.Completion.FuzzyMatching = strings.Contains(string(src.URI()), "fuzzymatch")
|
||||
modified.Completion.Unimported = strings.Contains(string(src.URI()), "unimported")
|
||||
r.server.session.SetOptions(modified)
|
||||
|
||||
list := r.runCompletion(t, src)
|
||||
|
||||
|
|
@ -140,21 +143,15 @@ func (r *runner) Completion(t *testing.T, data tests.Completions, snippets tests
|
|||
}
|
||||
}
|
||||
|
||||
origPlaceHolders := r.server.usePlaceholders
|
||||
origTextFormat := r.server.insertTextFormat
|
||||
defer func() {
|
||||
r.server.usePlaceholders = origPlaceHolders
|
||||
r.server.insertTextFormat = origTextFormat
|
||||
}()
|
||||
|
||||
r.server.insertTextFormat = protocol.SnippetTextFormat
|
||||
modified.InsertTextFormat = protocol.SnippetTextFormat
|
||||
for _, usePlaceholders := range []bool{true, false} {
|
||||
r.server.usePlaceholders = usePlaceholders
|
||||
modified.UsePlaceholders = usePlaceholders
|
||||
|
||||
for src, want := range snippets {
|
||||
r.server.disableDeepCompletion = !strings.Contains(string(src.URI()), "deepcomplete")
|
||||
r.server.disableFuzzyMatching = !strings.Contains(string(src.URI()), "fuzzymatch")
|
||||
r.server.wantUnimportedCompletions = strings.Contains(string(src.URI()), "unimported")
|
||||
modified.Completion.Deep = strings.Contains(string(src.URI()), "deepcomplete")
|
||||
modified.Completion.FuzzyMatching = strings.Contains(string(src.URI()), "fuzzymatch")
|
||||
modified.Completion.Unimported = strings.Contains(string(src.URI()), "unimported")
|
||||
r.server.session.SetOptions(modified)
|
||||
|
||||
list := r.runCompletion(t, src)
|
||||
|
||||
|
|
@ -266,11 +263,16 @@ func summarizeCompletionItems(i int, want []source.CompletionItem, got []protoco
|
|||
}
|
||||
|
||||
func (r *runner) FoldingRange(t *testing.T, data tests.FoldingRanges) {
|
||||
original := r.server.session.Options()
|
||||
modified := original
|
||||
defer func() { r.server.session.SetOptions(original) }()
|
||||
|
||||
for _, spn := range data {
|
||||
uri := spn.URI()
|
||||
|
||||
// Test all folding ranges.
|
||||
r.server.lineFoldingOnly = false
|
||||
modified.LineFoldingOnly = false
|
||||
r.server.session.SetOptions(modified)
|
||||
ranges, err := r.server.FoldingRange(r.ctx, &protocol.FoldingRangeParams{
|
||||
TextDocument: protocol.TextDocumentIdentifier{
|
||||
URI: protocol.NewURI(uri),
|
||||
|
|
@ -283,7 +285,8 @@ func (r *runner) FoldingRange(t *testing.T, data tests.FoldingRanges) {
|
|||
r.foldingRanges(t, "foldingRange", uri, ranges)
|
||||
|
||||
// Test folding ranges with lineFoldingOnly = true.
|
||||
r.server.lineFoldingOnly = true
|
||||
modified.LineFoldingOnly = true
|
||||
r.server.session.SetOptions(modified)
|
||||
ranges, err = r.server.FoldingRange(r.ctx, &protocol.FoldingRangeParams{
|
||||
TextDocument: protocol.TextDocumentIdentifier{
|
||||
URI: protocol.NewURI(uri),
|
||||
|
|
|
|||
|
|
@ -76,28 +76,6 @@ type Server struct {
|
|||
stateMu sync.Mutex
|
||||
state serverState
|
||||
|
||||
// Configurations.
|
||||
// TODO(rstambler): Separate these into their own struct?
|
||||
usePlaceholders bool
|
||||
hoverKind hoverKind
|
||||
disableDeepCompletion bool
|
||||
disableFuzzyMatching bool
|
||||
watchFileChanges bool
|
||||
wantCompletionDocumentation bool
|
||||
wantUnimportedCompletions bool
|
||||
insertTextFormat protocol.InsertTextFormat
|
||||
configurationSupported bool
|
||||
dynamicConfigurationSupported bool
|
||||
dynamicWatchedFilesSupported bool
|
||||
preferredContentFormat protocol.MarkupKind
|
||||
disabledAnalyses map[string]struct{}
|
||||
wantSuggestedFixes bool
|
||||
lineFoldingOnly bool
|
||||
|
||||
supportedCodeActions map[source.FileKind]map[protocol.CodeActionKind]bool
|
||||
|
||||
textDocumentSyncKind protocol.TextDocumentSyncKind
|
||||
|
||||
session source.Session
|
||||
|
||||
// undelivered is a cache of any diagnostics that the server
|
||||
|
|
|
|||
|
|
@ -266,7 +266,7 @@ func (c *completer) setSurrounding(ident *ast.Ident) {
|
|||
},
|
||||
}
|
||||
|
||||
if c.opts.WantFuzzyMatching {
|
||||
if c.opts.FuzzyMatching {
|
||||
c.matcher = fuzzy.NewMatcher(c.surrounding.Prefix(), fuzzy.Symbol)
|
||||
} else {
|
||||
c.matcher = prefixMatcher(strings.ToLower(c.surrounding.Prefix()))
|
||||
|
|
@ -379,16 +379,6 @@ type candidate struct {
|
|||
imp *imports.ImportInfo
|
||||
}
|
||||
|
||||
type CompletionOptions struct {
|
||||
WantDeepCompletion bool
|
||||
WantFuzzyMatching bool
|
||||
|
||||
WantUnimported bool
|
||||
|
||||
NoDocumentation bool
|
||||
WantFullDocumentation bool
|
||||
}
|
||||
|
||||
// Completion returns a list of possible candidates for completion, given a
|
||||
// a file and a position.
|
||||
//
|
||||
|
|
@ -472,7 +462,7 @@ func Completion(ctx context.Context, view View, f GoFile, pos protocol.Position,
|
|||
startTime: startTime,
|
||||
}
|
||||
|
||||
if opts.WantDeepCompletion {
|
||||
if opts.Deep {
|
||||
// Initialize max search depth to unlimited.
|
||||
c.deepState.maxDepth = -1
|
||||
}
|
||||
|
|
@ -673,7 +663,7 @@ func (c *completer) lexical() error {
|
|||
}
|
||||
}
|
||||
|
||||
if c.opts.WantUnimported {
|
||||
if c.opts.Unimported {
|
||||
// Suggest packages that have not been imported yet.
|
||||
pkgs, err := CandidateImports(c.ctx, c.view, c.filename)
|
||||
if err != nil {
|
||||
|
|
|
|||
|
|
@ -114,7 +114,7 @@ func (c *completer) item(cand candidate) (CompletionItem, error) {
|
|||
placeholderSnippet: placeholderSnippet,
|
||||
}
|
||||
// If the user doesn't want documentation for completion items.
|
||||
if c.opts.NoDocumentation {
|
||||
if !c.opts.Documentation {
|
||||
return item, nil
|
||||
}
|
||||
declRange, err := objToRange(c.ctx, c.view, obj)
|
||||
|
|
@ -160,7 +160,7 @@ func (c *completer) item(cand candidate) (CompletionItem, error) {
|
|||
return item, nil
|
||||
}
|
||||
item.Documentation = hover.Synopsis
|
||||
if c.opts.WantFullDocumentation {
|
||||
if c.opts.FullDocumentation {
|
||||
item.Documentation = hover.FullDocumentation
|
||||
}
|
||||
return item, nil
|
||||
|
|
|
|||
|
|
@ -0,0 +1,77 @@
|
|||
// Copyright 2019 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.
|
||||
|
||||
package source
|
||||
|
||||
import "golang.org/x/tools/internal/lsp/protocol"
|
||||
|
||||
var (
|
||||
DefaultSessionOptions = SessionOptions{
|
||||
TextDocumentSyncKind: protocol.Incremental,
|
||||
HoverKind: SynopsisDocumentation,
|
||||
InsertTextFormat: protocol.PlainTextTextFormat,
|
||||
SupportedCodeActions: map[FileKind]map[protocol.CodeActionKind]bool{
|
||||
Go: {
|
||||
protocol.SourceOrganizeImports: true,
|
||||
protocol.QuickFix: true,
|
||||
},
|
||||
Mod: {},
|
||||
Sum: {},
|
||||
},
|
||||
Completion: CompletionOptions{
|
||||
Documentation: true,
|
||||
},
|
||||
}
|
||||
DefaultViewOptions = ViewOptions{}
|
||||
)
|
||||
|
||||
type SessionOptions struct {
|
||||
Env []string
|
||||
BuildFlags []string
|
||||
UsePlaceholders bool
|
||||
HoverKind HoverKind
|
||||
SuggestedFixes bool
|
||||
DisabledAnalyses map[string]struct{}
|
||||
|
||||
WatchFileChanges bool
|
||||
InsertTextFormat protocol.InsertTextFormat
|
||||
ConfigurationSupported bool
|
||||
DynamicConfigurationSupported bool
|
||||
DynamicWatchedFilesSupported bool
|
||||
PreferredContentFormat protocol.MarkupKind
|
||||
LineFoldingOnly bool
|
||||
|
||||
SupportedCodeActions map[FileKind]map[protocol.CodeActionKind]bool
|
||||
|
||||
TextDocumentSyncKind protocol.TextDocumentSyncKind
|
||||
|
||||
Completion CompletionOptions
|
||||
}
|
||||
|
||||
type ViewOptions struct {
|
||||
}
|
||||
|
||||
type CompletionOptions struct {
|
||||
Deep bool
|
||||
FuzzyMatching bool
|
||||
Unimported bool
|
||||
Documentation bool
|
||||
FullDocumentation bool
|
||||
}
|
||||
|
||||
type HoverKind int
|
||||
|
||||
const (
|
||||
SingleLine = HoverKind(iota)
|
||||
NoDocumentation
|
||||
SynopsisDocumentation
|
||||
FullDocumentation
|
||||
|
||||
// structured is an experimental setting that returns a structured hover format.
|
||||
// This format separates the signature from the documentation, so that the client
|
||||
// can do more manipulation of these fields.
|
||||
//
|
||||
// This should only be used by clients that support this behavior.
|
||||
Structured
|
||||
)
|
||||
|
|
@ -102,9 +102,10 @@ func (r *runner) Completion(t *testing.T, data tests.Completions, snippets tests
|
|||
Line: float64(src.Start().Line() - 1),
|
||||
Character: float64(src.Start().Column() - 1),
|
||||
}, source.CompletionOptions{
|
||||
WantDeepCompletion: deepComplete,
|
||||
WantFuzzyMatching: fuzzyMatch,
|
||||
WantUnimported: unimported,
|
||||
Documentation: true,
|
||||
Deep: deepComplete,
|
||||
FuzzyMatching: fuzzyMatch,
|
||||
Unimported: unimported,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("failed for %v: %v", src, err)
|
||||
|
|
@ -156,8 +157,9 @@ func (r *runner) Completion(t *testing.T, data tests.Completions, snippets tests
|
|||
Line: float64(src.Start().Line() - 1),
|
||||
Character: float64(src.Start().Column() - 1),
|
||||
}, source.CompletionOptions{
|
||||
WantDeepCompletion: strings.Contains(string(src.URI()), "deepcomplete"),
|
||||
WantFuzzyMatching: strings.Contains(string(src.URI()), "fuzzymatch"),
|
||||
Documentation: true,
|
||||
Deep: strings.Contains(string(src.URI()), "deepcomplete"),
|
||||
FuzzyMatching: strings.Contains(string(src.URI()), "fuzzymatch"),
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("failed for %v: %v", src, err)
|
||||
|
|
|
|||
|
|
@ -190,6 +190,12 @@ type Session interface {
|
|||
// DidChangeOutOfBand is called when a file under the root folder
|
||||
// changes. The file is not necessarily open in the editor.
|
||||
DidChangeOutOfBand(uri span.URI)
|
||||
|
||||
// Options returns a copy of the SessionOptions for this session.
|
||||
Options() SessionOptions
|
||||
|
||||
// SetOptions sets the options of this session to new values.
|
||||
SetOptions(SessionOptions)
|
||||
}
|
||||
|
||||
// View represents a single workspace.
|
||||
|
|
@ -243,6 +249,9 @@ type View interface {
|
|||
// RunProcessEnvFunc runs fn with the process env for this view inserted into opts.
|
||||
// Note: the process env contains cached module and filesystem state.
|
||||
RunProcessEnvFunc(ctx context.Context, fn func(*imports.Options) error, opts *imports.Options) error
|
||||
|
||||
// Options returns a copy of the ViewOptions for this view.
|
||||
Options() ViewOptions
|
||||
}
|
||||
|
||||
// File represents a source file of any type.
|
||||
|
|
|
|||
|
|
@ -42,6 +42,7 @@ func (s *Server) didOpen(ctx context.Context, params *protocol.DidOpenTextDocume
|
|||
}
|
||||
|
||||
func (s *Server) didChange(ctx context.Context, params *protocol.DidChangeTextDocumentParams) error {
|
||||
options := s.session.Options()
|
||||
if len(params.ContentChanges) < 1 {
|
||||
return jsonrpc2.NewErrorf(jsonrpc2.CodeInternalError, "no content changes provided")
|
||||
}
|
||||
|
|
@ -54,7 +55,7 @@ func (s *Server) didChange(ctx context.Context, params *protocol.DidChangeTextDo
|
|||
|
||||
// We only accept an incremental change if the server expected it.
|
||||
if !isFullChange {
|
||||
switch s.textDocumentSyncKind {
|
||||
switch options.TextDocumentSyncKind {
|
||||
case protocol.Full:
|
||||
return errors.Errorf("expected a full content change, received incremental changes for %s", uri)
|
||||
case protocol.Incremental:
|
||||
|
|
|
|||
|
|
@ -14,7 +14,8 @@ import (
|
|||
)
|
||||
|
||||
func (s *Server) didChangeWatchedFiles(ctx context.Context, params *protocol.DidChangeWatchedFilesParams) error {
|
||||
if !s.watchFileChanges {
|
||||
options := s.session.Options()
|
||||
if !options.WatchFileChanges {
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -35,8 +35,10 @@ func (s *Server) addView(ctx context.Context, name string, uri span.URI) error {
|
|||
s.stateMu.Lock()
|
||||
state := s.state
|
||||
s.stateMu.Unlock()
|
||||
options := s.session.Options()
|
||||
defer func() { s.session.SetOptions(options) }()
|
||||
if state >= serverInitialized {
|
||||
s.fetchConfig(ctx, view)
|
||||
s.fetchConfig(ctx, view, &options)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue