github.com/jd-ly/tools@v0.5.7/internal/lsp/debounce.go (about)

     1  // Copyright 2020 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 lsp
     6  
     7  import (
     8  	"sync"
     9  	"time"
    10  )
    11  
    12  type debounceFunc struct {
    13  	order uint64
    14  	done  chan struct{}
    15  }
    16  
    17  type debouncer struct {
    18  	mu    sync.Mutex
    19  	funcs map[string]*debounceFunc
    20  }
    21  
    22  func newDebouncer() *debouncer {
    23  	return &debouncer{
    24  		funcs: make(map[string]*debounceFunc),
    25  	}
    26  }
    27  
    28  // debounce waits timeout before running f, if no subsequent call is made with
    29  // the same key in the intervening time. If a later call to debounce with the
    30  // same key occurs while the original call is blocking, the original call will
    31  // return immediately without running its f.
    32  //
    33  // If order is specified, it will be used to order calls logically, so calls
    34  // with lesser order will not cancel calls with greater order.
    35  func (d *debouncer) debounce(key string, order uint64, timeout time.Duration, f func()) {
    36  	if timeout == 0 {
    37  		// Degenerate case: no debouncing.
    38  		f()
    39  		return
    40  	}
    41  
    42  	// First, atomically acquire the current func, cancel it, and insert this
    43  	// call into d.funcs.
    44  	d.mu.Lock()
    45  	current, ok := d.funcs[key]
    46  	if ok && current.order > order {
    47  		// If we have a logical ordering of events (as is the case for snapshots),
    48  		// don't overwrite a later event with an earlier event.
    49  		d.mu.Unlock()
    50  		return
    51  	}
    52  	if ok {
    53  		close(current.done)
    54  	}
    55  	done := make(chan struct{})
    56  	next := &debounceFunc{
    57  		order: order,
    58  		done:  done,
    59  	}
    60  	d.funcs[key] = next
    61  	d.mu.Unlock()
    62  
    63  	// Next, wait to be cancelled or for our wait to expire. There is a race here
    64  	// that we must handle: our timer could expire while another goroutine holds
    65  	// d.mu.
    66  	select {
    67  	case <-done:
    68  	case <-time.After(timeout):
    69  		d.mu.Lock()
    70  		if d.funcs[key] != next {
    71  			// We lost the race: another event has arrived for the key and started
    72  			// waiting. We could reasonably choose to run f at this point, but doing
    73  			// nothing is simpler.
    74  			d.mu.Unlock()
    75  			return
    76  		}
    77  		delete(d.funcs, key)
    78  		d.mu.Unlock()
    79  		f()
    80  	}
    81  }