mirror of https://github.com/golang/go.git
internal/lsp/cache: register a file watcher for explicit GOWORK values
When the go.work file is set by the GOWORK environment variable, we must create an additional file watching pattern. Fixes golang/go#53631 Change-Id: I2d78c5a9ee8a71551d5274db7eb4e6c623d8db74 Reviewed-on: https://go-review.googlesource.com/c/tools/+/421501 gopls-CI: kokoro <noreply+kokoro@google.com> Run-TryBot: Robert Findley <rfindley@google.com> TryBot-Result: Gopher Robot <gobot@golang.org> Reviewed-by: Suzy Mueller <suzmue@golang.org>
This commit is contained in:
parent
98aef77998
commit
92d58ea4e7
|
|
@ -1548,7 +1548,7 @@ func Hello() {
|
|||
}
|
||||
-- go.mod --
|
||||
module mod.com
|
||||
-- main.go --
|
||||
-- cmd/main.go --
|
||||
package main
|
||||
|
||||
import "mod.com/bob"
|
||||
|
|
@ -1558,11 +1558,12 @@ func main() {
|
|||
}
|
||||
`
|
||||
Run(t, mod, func(t *testing.T, env *Env) {
|
||||
env.Await(FileWatchMatching("bob"))
|
||||
env.RemoveWorkspaceFile("bob")
|
||||
env.Await(
|
||||
env.DiagnosticAtRegexp("main.go", `"mod.com/bob"`),
|
||||
env.DiagnosticAtRegexp("cmd/main.go", `"mod.com/bob"`),
|
||||
EmptyDiagnostics("bob/bob.go"),
|
||||
RegistrationMatching("didChangeWatchedFiles"),
|
||||
NoFileWatchMatching("bob"),
|
||||
)
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -41,6 +41,8 @@ use (
|
|||
WithOptions(
|
||||
EnvVars{"GOWORK": "$SANDBOX_WORKDIR/config/go.work"},
|
||||
).Run(t, files, func(t *testing.T, env *Env) {
|
||||
// When we have an explicit GOWORK set, we should get a file watch request.
|
||||
env.Await(FileWatchMatching(`config.go\.work`))
|
||||
// Even though work/b is not open, we should get its diagnostics as it is
|
||||
// included in the workspace.
|
||||
env.OpenFile("work/a/a.go")
|
||||
|
|
|
|||
|
|
@ -887,6 +887,10 @@ func (s *snapshot) fileWatchingGlobPatterns(ctx context.Context) map[string]stru
|
|||
fmt.Sprintf("**/*.{%s}", extensions): {},
|
||||
}
|
||||
|
||||
if s.view.explicitGowork != "" {
|
||||
patterns[s.view.explicitGowork.Filename()] = struct{}{}
|
||||
}
|
||||
|
||||
// Add a pattern for each Go module in the workspace that is not within the view.
|
||||
dirs := s.workspace.dirs(ctx, s)
|
||||
for _, dir := range dirs {
|
||||
|
|
|
|||
|
|
@ -85,8 +85,9 @@ type State struct {
|
|||
showMessage []*protocol.ShowMessageParams
|
||||
showMessageRequest []*protocol.ShowMessageRequestParams
|
||||
|
||||
registrations []*protocol.RegistrationParams
|
||||
unregistrations []*protocol.UnregistrationParams
|
||||
registrations []*protocol.RegistrationParams
|
||||
registeredCapabilities map[string]protocol.Registration
|
||||
unregistrations []*protocol.UnregistrationParams
|
||||
|
||||
// outstandingWork is a map of token->work summary. All tokens are assumed to
|
||||
// be string, though the spec allows for numeric tokens as well. When work
|
||||
|
|
@ -226,6 +227,12 @@ func (a *Awaiter) onRegistration(_ context.Context, m *protocol.RegistrationPara
|
|||
defer a.mu.Unlock()
|
||||
|
||||
a.state.registrations = append(a.state.registrations, m)
|
||||
if a.state.registeredCapabilities == nil {
|
||||
a.state.registeredCapabilities = make(map[string]protocol.Registration)
|
||||
}
|
||||
for _, reg := range m.Registrations {
|
||||
a.state.registeredCapabilities[reg.Method] = reg
|
||||
}
|
||||
a.checkConditionsLocked()
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -394,32 +394,66 @@ func NoLogMatching(typ protocol.MessageType, re string) LogExpectation {
|
|||
}
|
||||
}
|
||||
|
||||
// RegistrationExpectation is an expectation on the capability registrations
|
||||
// received by the editor from gopls.
|
||||
type RegistrationExpectation struct {
|
||||
check func([]*protocol.RegistrationParams) Verdict
|
||||
description string
|
||||
// FileWatchMatching expects that a file registration matches re.
|
||||
func FileWatchMatching(re string) SimpleExpectation {
|
||||
return SimpleExpectation{
|
||||
check: checkFileWatch(re, Met, Unmet),
|
||||
description: fmt.Sprintf("file watch matching %q", re),
|
||||
}
|
||||
}
|
||||
|
||||
// Check implements the Expectation interface.
|
||||
func (e RegistrationExpectation) Check(s State) Verdict {
|
||||
return e.check(s.registrations)
|
||||
// NoFileWatchMatching expects that no file registration matches re.
|
||||
func NoFileWatchMatching(re string) SimpleExpectation {
|
||||
return SimpleExpectation{
|
||||
check: checkFileWatch(re, Unmet, Met),
|
||||
description: fmt.Sprintf("no file watch matching %q", re),
|
||||
}
|
||||
}
|
||||
|
||||
// Description implements the Expectation interface.
|
||||
func (e RegistrationExpectation) Description() string {
|
||||
return e.description
|
||||
func checkFileWatch(re string, onMatch, onNoMatch Verdict) func(State) Verdict {
|
||||
rec := regexp.MustCompile(re)
|
||||
return func(s State) Verdict {
|
||||
r := s.registeredCapabilities["workspace/didChangeWatchedFiles"]
|
||||
watchers := jsonProperty(r.RegisterOptions, "watchers").([]interface{})
|
||||
for _, watcher := range watchers {
|
||||
pattern := jsonProperty(watcher, "globPattern").(string)
|
||||
if rec.MatchString(pattern) {
|
||||
return onMatch
|
||||
}
|
||||
}
|
||||
return onNoMatch
|
||||
}
|
||||
}
|
||||
|
||||
// jsonProperty extracts a value from a path of JSON property names, assuming
|
||||
// the default encoding/json unmarshaling to the empty interface (i.e.: that
|
||||
// JSON objects are unmarshalled as map[string]interface{})
|
||||
//
|
||||
// For example, if obj is unmarshalled from the following json:
|
||||
//
|
||||
// {
|
||||
// "foo": { "bar": 3 }
|
||||
// }
|
||||
//
|
||||
// Then jsonProperty(obj, "foo", "bar") will be 3.
|
||||
func jsonProperty(obj interface{}, path ...string) interface{} {
|
||||
if len(path) == 0 || obj == nil {
|
||||
return obj
|
||||
}
|
||||
m := obj.(map[string]interface{})
|
||||
return jsonProperty(m[path[0]], path[1:]...)
|
||||
}
|
||||
|
||||
// RegistrationMatching asserts that the client has received a capability
|
||||
// registration matching the given regexp.
|
||||
func RegistrationMatching(re string) RegistrationExpectation {
|
||||
rec, err := regexp.Compile(re)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
check := func(params []*protocol.RegistrationParams) Verdict {
|
||||
for _, p := range params {
|
||||
//
|
||||
// TODO(rfindley): remove this once TestWatchReplaceTargets has been revisited.
|
||||
//
|
||||
// Deprecated: use (No)FileWatchMatching
|
||||
func RegistrationMatching(re string) SimpleExpectation {
|
||||
rec := regexp.MustCompile(re)
|
||||
check := func(s State) Verdict {
|
||||
for _, p := range s.registrations {
|
||||
for _, r := range p.Registrations {
|
||||
if rec.Match([]byte(r.Method)) {
|
||||
return Met
|
||||
|
|
@ -428,38 +462,18 @@ func RegistrationMatching(re string) RegistrationExpectation {
|
|||
}
|
||||
return Unmet
|
||||
}
|
||||
return RegistrationExpectation{
|
||||
return SimpleExpectation{
|
||||
check: check,
|
||||
description: fmt.Sprintf("registration matching %q", re),
|
||||
}
|
||||
}
|
||||
|
||||
// UnregistrationExpectation is an expectation on the capability
|
||||
// unregistrations received by the editor from gopls.
|
||||
type UnregistrationExpectation struct {
|
||||
check func([]*protocol.UnregistrationParams) Verdict
|
||||
description string
|
||||
}
|
||||
|
||||
// Check implements the Expectation interface.
|
||||
func (e UnregistrationExpectation) Check(s State) Verdict {
|
||||
return e.check(s.unregistrations)
|
||||
}
|
||||
|
||||
// Description implements the Expectation interface.
|
||||
func (e UnregistrationExpectation) Description() string {
|
||||
return e.description
|
||||
}
|
||||
|
||||
// UnregistrationMatching asserts that the client has received an
|
||||
// unregistration whose ID matches the given regexp.
|
||||
func UnregistrationMatching(re string) UnregistrationExpectation {
|
||||
rec, err := regexp.Compile(re)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
check := func(params []*protocol.UnregistrationParams) Verdict {
|
||||
for _, p := range params {
|
||||
func UnregistrationMatching(re string) SimpleExpectation {
|
||||
rec := regexp.MustCompile(re)
|
||||
check := func(s State) Verdict {
|
||||
for _, p := range s.unregistrations {
|
||||
for _, r := range p.Unregisterations {
|
||||
if rec.Match([]byte(r.Method)) {
|
||||
return Met
|
||||
|
|
@ -468,7 +482,7 @@ func UnregistrationMatching(re string) UnregistrationExpectation {
|
|||
}
|
||||
return Unmet
|
||||
}
|
||||
return UnregistrationExpectation{
|
||||
return SimpleExpectation{
|
||||
check: check,
|
||||
description: fmt.Sprintf("unregistration matching %q", re),
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue