File: filemap.go

package info (click to toggle)
golang-github-cue-lang-cue 0.12.0.-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 19,072 kB
  • sloc: sh: 57; makefile: 17
file content (151 lines) | stat: -rw-r--r-- 4,350 bytes parent folder | download
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
}