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