Skip to content

Commit e79d2ea

Browse files
committed
lsp/fscache: set version on token.File
Files can be loaded directly by different parts of the LSP, but also by modpkgload. It is desirable to keep a file fully self-contained: once it is loaded, it should ideally keep with it all its metadata. This avoids going back to the fs to fetch further metadata later on, which could run the risk of race conditions. A while ago we added File.[Set]Content() so that we could easily get from an AST back to the bytes that built it. Various messages from the LSP server to the client require that version numbers of files are included which helps to avoid race conditions, especially if the client is sending async modification messages whilst a sync rpc message is in progress. To better support this, we add the version number to the token.File and so once a file has been loaded (and probably parsed to an AST), we can get back not just to its raw bytes, but also to its version number. Also whilst we're here, improve the naming of fields in token.File. Signed-off-by: Matthew Sackman <[email protected]> Change-Id: Ic1736cb997d0c71375a5476ceabc85c42586293a Reviewed-on: https://cue.gerrithub.io/c/cue-lang/cue/+/1225196 Reviewed-by: Daniel Martí <[email protected]> Reviewed-by: Roger Peppe <[email protected]> Unity-Result: CUE porcuepine <[email protected]> TryBot-Result: CUEcueckoo <[email protected]>
1 parent bbf6f4f commit e79d2ea

File tree

4 files changed

+55
-32
lines changed

4 files changed

+55
-32
lines changed

cue/token/position.go

Lines changed: 26 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ func (p hiddenPos) Experiment() (x cueexperiment.File) {
9898
// NOTE: this is an internal API and may change at any time without notice.
9999
func (p hiddenPos) Priority() (pr layer.Priority, ok bool) {
100100
if f := p.file; f != nil {
101-
return f.p, f.isData
101+
return f.priority, f.isData
102102
}
103103
return 0, false
104104
}
@@ -272,13 +272,14 @@ type File struct {
272272
base index
273273
size index // file size as provided to AddFile
274274

275-
// lines, infos, and content are protected by set.mutex
276-
lines []index // lines contains the offset of the first character for each line (the first entry is always 0)
277-
infos []lineInfo
278-
content []byte
275+
// lines, infos, content, and revision are protected by [File.mutex]
276+
lines []index // lines contains the offset of the first character for each line (the first entry is always 0)
277+
infos []lineInfo
278+
content []byte
279+
revision int32
279280

280281
experiments *cueexperiment.File
281-
p layer.Priority
282+
priority layer.Priority
282283
isData bool
283284
}
284285

@@ -326,7 +327,7 @@ func (f *hiddenFile) SetExperiments(experiments *cueexperiment.File) {
326327
// whether this file should be treated as containing data defaults, which
327328
// have different merging semantics from regular defaults.
328329
func (f *hiddenFile) SetLayer(priority int8, isData bool) {
329-
f.p = layer.Priority(priority)
330+
f.priority = layer.Priority(priority)
330331
f.isData = isData
331332
}
332333

@@ -466,6 +467,24 @@ func (f *hiddenFile) Content() []byte {
466467
return f.content
467468
}
468469

470+
// NOTE: this is an internal API and may change at any time without notice.
471+
//
472+
// SetRevision sets the file's version.
473+
func (f *hiddenFile) SetRevision(version int32) {
474+
f.mutex.Lock()
475+
f.revision = version
476+
f.mutex.Unlock()
477+
}
478+
479+
// NOTE: this is an internal API and may change at any time without notice.
480+
//
481+
// Revision retrieves the file's version.
482+
func (f *hiddenFile) Revision() int32 {
483+
f.mutex.RLock()
484+
defer f.mutex.RUnlock()
485+
return f.revision
486+
}
487+
469488
// A lineInfo object describes alternative file and line number
470489
// information (such as provided via a //line comment in a .go
471490
// file) for a given file offset.

internal/lsp/cache/rename.go

Lines changed: 16 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -15,15 +15,11 @@
1515
package cache
1616

1717
import (
18-
"errors"
19-
"io/fs"
20-
2118
"cuelang.org/go/cue/ast"
2219
"cuelang.org/go/cue/literal"
2320
"cuelang.org/go/cue/token"
2421
"cuelang.org/go/internal/golangorgx/gopls/protocol"
2522
"cuelang.org/go/internal/lsp/definitions"
26-
"cuelang.org/go/internal/lsp/fscache"
2723
)
2824

2925
// Rename implements the LSP Rename functionality.
@@ -41,7 +37,12 @@ func (w *Workspace) Rename(tokFile *token.File, fdfns *definitions.FileDefinitio
4137
}
4238
}
4339

44-
changes := make(map[protocol.DocumentURI][]protocol.TextEdit)
40+
type versionedEdits struct {
41+
edits []protocol.TextEdit
42+
version int32
43+
}
44+
45+
changes := make(map[protocol.DocumentURI]*versionedEdits)
4546
for _, target := range targets {
4647
startPos := target.Pos().Position()
4748
endPos := target.End().Position()
@@ -62,7 +63,14 @@ func (w *Workspace) Rename(tokFile *token.File, fdfns *definitions.FileDefinitio
6263
if lit, ok := target.(*ast.BasicLit); (ok && lit.Kind == token.STRING) || !ast.IsValidIdent(name) {
6364
name = literal.Label.Quote(name)
6465
}
65-
changes[uri] = append(changes[uri], protocol.TextEdit{
66+
ve, found := changes[uri]
67+
if !found {
68+
ve = &versionedEdits{
69+
version: targetFile.Revision(),
70+
}
71+
changes[uri] = ve
72+
}
73+
ve.edits = append(ve.edits, protocol.TextEdit{
6674
Range: r,
6775
NewText: name,
6876
})
@@ -73,25 +81,9 @@ func (w *Workspace) Rename(tokFile *token.File, fdfns *definitions.FileDefinitio
7381
}
7482

7583
var docChanges []protocol.DocumentChanges
76-
err := w.overlayFS.View(func(txn *fscache.ViewTxn) error {
77-
for uri, edits := range changes {
78-
handle, err := txn.Get(uri)
79-
version := int32(0)
80-
if errors.Is(err, fs.ErrNotExist) {
81-
// noop
82-
} else if err != nil {
83-
return err
84-
} else {
85-
version = handle.Version()
86-
}
87-
docChanges = append(docChanges, protocol.TextEditsToDocumentChanges(uri, version, edits)...)
88-
}
89-
return nil
90-
})
91-
if err != nil {
92-
w.debugLog(err.Error())
84+
for uri, edits := range changes {
85+
docChanges = append(docChanges, protocol.TextEditsToDocumentChanges(uri, edits.version, edits.edits)...)
9386
}
94-
9587
return &protocol.WorkspaceEdit{DocumentChanges: docChanges}
9688
}
9789

internal/lsp/fscache/fs_cache.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,7 @@ func (p *cueFileParser) ReadCUE(config parser.Config) (syntax *ast.File, cfg par
149149
}
150150

151151
// Version implements [FileHandle]
152-
func (entry *diskFileEntry) Version() int32 { return -1 }
152+
func (entry *diskFileEntry) Version() int32 { panic("Should never be called") }
153153

154154
// Content implements [FileHandle]
155155
func (entry *diskFileEntry) Content() []byte { return slices.Clone(entry.content) }

internal/lsp/fscache/fs_overlay.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,18 @@ func (entry *overlayFileEntry) open() *overlayFile {
7878
}
7979
}
8080

81+
// ReadCUE implements [FileHandle]
82+
func (entry *overlayFileEntry) ReadCUE(config parser.Config) (syntax *ast.File, cfg parser.Config, err error) {
83+
syntax, cfg, err = entry.cueFileParser.ReadCUE(config)
84+
if syntax != nil {
85+
file := syntax.Pos().File()
86+
if file != nil {
87+
file.SetRevision(entry.version)
88+
}
89+
}
90+
return
91+
}
92+
8193
var _ iofs.File = (*overlayFile)(nil)
8294

8395
type overlayFile struct {

0 commit comments

Comments
 (0)