--- /dev/null
+// 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.
+
+package lsp
+
+import (
+ "sync"
+ "time"
+)
+
+type debounceFunc struct {
+ order uint64
+ done chan struct{}
+}
+
+type debouncer struct {
+ mu sync.Mutex
+ funcs map[string]*debounceFunc
+}
+
+func newDebouncer() *debouncer {
+ return &debouncer{
+ funcs: make(map[string]*debounceFunc),
+ }
+}
+
+// debounce waits timeout before running f, if no subsequent call is made with
+// the same key in the intervening time. If a later call to debounce with the
+// same key occurs while the original call is blocking, the original call will
+// return immediately without running its f.
+//
+// If order is specified, it will be used to order calls logically, so calls
+// with lesser order will not cancel calls with greater order.
+func (d *debouncer) debounce(key string, order uint64, timeout time.Duration, f func()) {
+ if timeout == 0 {
+ // Degenerate case: no debouncing.
+ f()
+ return
+ }
+
+ // First, atomically acquire the current func, cancel it, and insert this
+ // call into d.funcs.
+ d.mu.Lock()
+ current, ok := d.funcs[key]
+ if ok && current.order > order {
+ // If we have a logical ordering of events (as is the case for snapshots),
+ // don't overwrite a later event with an earlier event.
+ d.mu.Unlock()
+ return
+ }
+ if ok {
+ close(current.done)
+ }
+ done := make(chan struct{})
+ next := &debounceFunc{
+ order: order,
+ done: done,
+ }
+ d.funcs[key] = next
+ d.mu.Unlock()
+
+ // Next, wait to be cancelled or for our wait to expire. There is a race here
+ // that we must handle: our timer could expire while another goroutine holds
+ // d.mu.
+ select {
+ case <-done:
+ case <-time.After(timeout):
+ d.mu.Lock()
+ if d.funcs[key] != next {
+ // We lost the race: another event has arrived for the key and started
+ // waiting. We could reasonably choose to run f at this point, but doing
+ // nothing is simpler.
+ d.mu.Unlock()
+ return
+ }
+ delete(d.funcs, key)
+ d.mu.Unlock()
+ f()
+ }
+}