this repo has no description
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

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 <matthew@cue.works>
Change-Id: Ic1736cb997d0c71375a5476ceabc85c42586293a
Reviewed-on: https://cue.gerrithub.io/c/cue-lang/cue/+/1225196
Reviewed-by: Daniel Martí <mvdan@mvdan.cc>
Reviewed-by: Roger Peppe <rogpeppe@gmail.com>
Unity-Result: CUE porcuepine <cue.porcuepine@gmail.com>
TryBot-Result: CUEcueckoo <cueckoo@cuelang.org>

+55 -32
+26 -7
cue/token/position.go
··· 98 98 // NOTE: this is an internal API and may change at any time without notice. 99 99 func (p hiddenPos) Priority() (pr layer.Priority, ok bool) { 100 100 if f := p.file; f != nil { 101 - return f.p, f.isData 101 + return f.priority, f.isData 102 102 } 103 103 return 0, false 104 104 } ··· 272 272 base index 273 273 size index // file size as provided to AddFile 274 274 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 279 280 280 281 experiments *cueexperiment.File 281 - p layer.Priority 282 + priority layer.Priority 282 283 isData bool 283 284 } 284 285 ··· 326 327 // whether this file should be treated as containing data defaults, which 327 328 // have different merging semantics from regular defaults. 328 329 func (f *hiddenFile) SetLayer(priority int8, isData bool) { 329 - f.p = layer.Priority(priority) 330 + f.priority = layer.Priority(priority) 330 331 f.isData = isData 331 332 } 332 333 ··· 464 465 f.mutex.RLock() 465 466 defer f.mutex.RUnlock() 466 467 return f.content 468 + } 469 + 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 467 486 } 468 487 469 488 // A lineInfo object describes alternative file and line number
+16 -24
internal/lsp/cache/rename.go
··· 15 15 package cache 16 16 17 17 import ( 18 - "errors" 19 - "io/fs" 20 - 21 18 "cuelang.org/go/cue/ast" 22 19 "cuelang.org/go/cue/literal" 23 20 "cuelang.org/go/cue/token" 24 21 "cuelang.org/go/internal/golangorgx/gopls/protocol" 25 22 "cuelang.org/go/internal/lsp/definitions" 26 - "cuelang.org/go/internal/lsp/fscache" 27 23 ) 28 24 29 25 // Rename implements the LSP Rename functionality. ··· 41 37 } 42 38 } 43 39 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) 45 46 for _, target := range targets { 46 47 startPos := target.Pos().Position() 47 48 endPos := target.End().Position() ··· 62 63 if lit, ok := target.(*ast.BasicLit); (ok && lit.Kind == token.STRING) || !ast.IsValidIdent(name) { 63 64 name = literal.Label.Quote(name) 64 65 } 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{ 66 74 Range: r, 67 75 NewText: name, 68 76 }) ··· 73 81 } 74 82 75 83 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)...) 93 86 } 94 - 95 87 return &protocol.WorkspaceEdit{DocumentChanges: docChanges} 96 88 } 97 89
+1 -1
internal/lsp/fscache/fs_cache.go
··· 149 149 } 150 150 151 151 // Version implements [FileHandle] 152 - func (entry *diskFileEntry) Version() int32 { return -1 } 152 + func (entry *diskFileEntry) Version() int32 { panic("Should never be called") } 153 153 154 154 // Content implements [FileHandle] 155 155 func (entry *diskFileEntry) Content() []byte { return slices.Clone(entry.content) }
+12
internal/lsp/fscache/fs_overlay.go
··· 78 78 } 79 79 } 80 80 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 + 81 93 var _ iofs.File = (*overlayFile)(nil) 82 94 83 95 type overlayFile struct {