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 }