golang.org/x/tools/gopls@v0.15.3/internal/cache/filemap.go (about)

     1  // Copyright 2022 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 cache
     6  
     7  import (
     8  	"path/filepath"
     9  
    10  	"golang.org/x/tools/gopls/internal/file"
    11  	"golang.org/x/tools/gopls/internal/protocol"
    12  	"golang.org/x/tools/gopls/internal/util/persistent"
    13  )
    14  
    15  // A fileMap maps files in the snapshot, with some additional bookkeeping:
    16  // It keeps track of overlays as well as directories containing any observed
    17  // file.
    18  type fileMap struct {
    19  	files    *persistent.Map[protocol.DocumentURI, file.Handle]
    20  	overlays *persistent.Map[protocol.DocumentURI, *overlay] // the subset of files that are overlays
    21  	dirs     *persistent.Set[string]                         // all dirs containing files; if nil, dirs have not been initialized
    22  }
    23  
    24  func newFileMap() *fileMap {
    25  	return &fileMap{
    26  		files:    new(persistent.Map[protocol.DocumentURI, file.Handle]),
    27  		overlays: new(persistent.Map[protocol.DocumentURI, *overlay]),
    28  		dirs:     new(persistent.Set[string]),
    29  	}
    30  }
    31  
    32  // clone creates a copy of the fileMap, incorporating the changes specified by
    33  // the changes map.
    34  func (m *fileMap) clone(changes map[protocol.DocumentURI]file.Handle) *fileMap {
    35  	m2 := &fileMap{
    36  		files:    m.files.Clone(),
    37  		overlays: m.overlays.Clone(),
    38  	}
    39  	if m.dirs != nil {
    40  		m2.dirs = m.dirs.Clone()
    41  	}
    42  
    43  	// Handle file changes.
    44  	//
    45  	// Note, we can't simply delete the file unconditionally and let it be
    46  	// re-read by the snapshot, as (1) the snapshot must always observe all
    47  	// overlays, and (2) deleting a file forces directories to be reevaluated, as
    48  	// it may be the last file in a directory. We want to avoid that work in the
    49  	// common case where a file has simply changed.
    50  	//
    51  	// For that reason, we also do this in two passes, processing deletions
    52  	// first, as a set before a deletion would result in pointless work.
    53  	for uri, fh := range changes {
    54  		if !fileExists(fh) {
    55  			m2.delete(uri)
    56  		}
    57  	}
    58  	for uri, fh := range changes {
    59  		if fileExists(fh) {
    60  			m2.set(uri, fh)
    61  		}
    62  	}
    63  	return m2
    64  }
    65  
    66  func (m *fileMap) destroy() {
    67  	m.files.Destroy()
    68  	m.overlays.Destroy()
    69  	if m.dirs != nil {
    70  		m.dirs.Destroy()
    71  	}
    72  }
    73  
    74  // get returns the file handle mapped by the given key, or (nil, false) if the
    75  // key is not present.
    76  func (m *fileMap) get(key protocol.DocumentURI) (file.Handle, bool) {
    77  	return m.files.Get(key)
    78  }
    79  
    80  // foreach calls f for each (uri, fh) in the map.
    81  func (m *fileMap) foreach(f func(uri protocol.DocumentURI, fh file.Handle)) {
    82  	m.files.Range(f)
    83  }
    84  
    85  // set stores the given file handle for key, updating overlays and directories
    86  // accordingly.
    87  func (m *fileMap) set(key protocol.DocumentURI, fh file.Handle) {
    88  	m.files.Set(key, fh, nil)
    89  
    90  	// update overlays
    91  	if o, ok := fh.(*overlay); ok {
    92  		m.overlays.Set(key, o, nil)
    93  	} else {
    94  		// Setting a non-overlay must delete the corresponding overlay, to preserve
    95  		// the accuracy of the overlay set.
    96  		m.overlays.Delete(key)
    97  	}
    98  
    99  	// update dirs, if they have been computed
   100  	if m.dirs != nil {
   101  		m.addDirs(key)
   102  	}
   103  }
   104  
   105  // addDirs adds all directories containing u to the dirs set.
   106  func (m *fileMap) addDirs(u protocol.DocumentURI) {
   107  	dir := filepath.Dir(u.Path())
   108  	for dir != "" && !m.dirs.Contains(dir) {
   109  		m.dirs.Add(dir)
   110  		dir = filepath.Dir(dir)
   111  	}
   112  }
   113  
   114  // delete removes a file from the map, and updates overlays and dirs
   115  // accordingly.
   116  func (m *fileMap) delete(key protocol.DocumentURI) {
   117  	m.files.Delete(key)
   118  	m.overlays.Delete(key)
   119  
   120  	// Deleting a file may cause the set of dirs to shrink; therefore we must
   121  	// re-evaluate the dir set.
   122  	//
   123  	// Do this lazily, to avoid work if there are multiple deletions in a row.
   124  	if m.dirs != nil {
   125  		m.dirs.Destroy()
   126  		m.dirs = nil
   127  	}
   128  }
   129  
   130  // getOverlays returns a new unordered array of overlay files.
   131  func (m *fileMap) getOverlays() []*overlay {
   132  	var overlays []*overlay
   133  	m.overlays.Range(func(_ protocol.DocumentURI, o *overlay) {
   134  		overlays = append(overlays, o)
   135  	})
   136  	return overlays
   137  }
   138  
   139  // getDirs reports returns the set of dirs observed by the fileMap.
   140  //
   141  // This operation mutates the fileMap.
   142  // The result must not be mutated by the caller.
   143  func (m *fileMap) getDirs() *persistent.Set[string] {
   144  	if m.dirs == nil {
   145  		m.dirs = new(persistent.Set[string])
   146  		m.files.Range(func(u protocol.DocumentURI, _ file.Handle) {
   147  			m.addDirs(u)
   148  		})
   149  	}
   150  	return m.dirs
   151  }