Skip to content

Commit 8993506

Browse files
fix
1 parent 5430ac0 commit 8993506

3 files changed

Lines changed: 67 additions & 35 deletions

File tree

pyrefly/lib/lsp/non_wasm/server.rs

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -746,6 +746,7 @@ pub struct Server {
746746
/// should be mapped through here in case they correspond to a cell.
747747
open_notebook_cells: RwLock<HashMap<Url, PathBuf>>,
748748
open_files: RwLock<HashMap<PathBuf, Arc<LspFile>>>,
749+
open_files_with_unsaved_changes: Mutex<HashSet<PathBuf>>,
749750
/// Last published fingerprint for unversioned file-backed workspace diagnostics.
750751
published_workspace_diagnostics: Mutex<HashMap<Url, u64>>,
751752
/// Tracks URIs (including virtual/untitled ones) to synthetic on-disk paths so we can
@@ -2416,6 +2417,7 @@ impl Server {
24162417
state: State::new(config_finder, thread_count),
24172418
open_notebook_cells: RwLock::new(HashMap::new()),
24182419
open_files: RwLock::new(HashMap::new()),
2420+
open_files_with_unsaved_changes: Mutex::new(HashSet::new()),
24192421
published_workspace_diagnostics: Mutex::new(HashMap::new()),
24202422
unsaved_file_tracker: UnsavedFileTracker::new(),
24212423
indexed_configs: Mutex::new(HashSet::new()),
@@ -3285,6 +3287,7 @@ impl Server {
32853287

32863288
fn did_save(&self, url: Url) {
32873289
if let Some(path) = self.path_for_uri(&url) {
3290+
self.open_files_with_unsaved_changes.lock().remove(&path);
32883291
self.invalidate(TelemetryEventKind::InvalidateDisk, move |t| {
32893292
t.invalidate_disk(&[path])
32903293
})
@@ -3322,6 +3325,7 @@ impl Server {
33223325
} else {
33233326
None
33243327
};
3328+
self.open_files_with_unsaved_changes.lock().remove(&path);
33253329
self.version_info.lock().insert(path.clone(), version);
33263330
self.open_files.write().insert(path.clone(), contents);
33273331
self.queue_source_db_rebuild_and_recheck(telemetry, telemetry_event, false);
@@ -3387,6 +3391,9 @@ impl Server {
33873391
params.content_changes,
33883392
)));
33893393
drop(lock);
3394+
self.open_files_with_unsaved_changes
3395+
.lock()
3396+
.insert(file_path.clone());
33903397
// Update version_info only after the mutation has fully succeeded.
33913398
self.version_info.lock().insert(file_path.clone(), version);
33923399
if !subsequent_mutation {
@@ -3549,6 +3556,9 @@ impl Server {
35493556
let new_notebook = Arc::new(LspNotebook::new(ruff_notebook, notebook_document));
35503557
*original = Arc::new(LspFile::Notebook(new_notebook));
35513558
drop(lock);
3559+
self.open_files_with_unsaved_changes
3560+
.lock()
3561+
.insert(file_path.clone());
35523562
// Update version_info only after the mutation has fully succeeded, so
35533563
// that on error the version stays at the old value and subsequent
35543564
// notifications operate against consistent state.
@@ -3586,6 +3596,28 @@ impl Server {
35863596
config_changed || files_added_or_removed
35873597
}
35883598

3599+
fn refresh_clean_open_files_from_disk(&self, events: &CategorizedEvents) {
3600+
let unsaved = self.open_files_with_unsaved_changes.lock().clone();
3601+
let mut open_files = self.open_files.write();
3602+
for path in events.modified.iter() {
3603+
if unsaved.contains(path) {
3604+
continue;
3605+
}
3606+
let Some(open_file) = open_files.get_mut(path) else {
3607+
continue;
3608+
};
3609+
let LspFile::Source(current_contents) = open_file.as_ref() else {
3610+
continue;
3611+
};
3612+
let Ok(updated_contents) = std::fs::read_to_string(path) else {
3613+
continue;
3614+
};
3615+
if current_contents.as_str() != updated_contents {
3616+
*open_file = Arc::new(LspFile::from_source(updated_contents));
3617+
}
3618+
}
3619+
}
3620+
35893621
fn did_change_watched_files(
35903622
&self,
35913623
params: DidChangeWatchedFilesParams,
@@ -3621,6 +3653,8 @@ impl Server {
36213653

36223654
let should_requery_build_system = should_requery_build_system(&events);
36233655

3656+
self.refresh_clean_open_files_from_disk(&events);
3657+
36243658
// Rewatch files if necessary (config changed, files added/removed, etc.)
36253659
if Self::should_rewatch(&events) {
36263660
info!("[Pyrefly] Re-registering file watchers");
@@ -3710,6 +3744,7 @@ impl Server {
37103744
},
37113745
}
37123746
drop(open_files);
3747+
self.open_files_with_unsaved_changes.lock().remove(&path);
37133748
self.unsaved_file_tracker.forget_uri_path(&url);
37143749
self.queue_source_db_rebuild_and_recheck(telemetry, telemetry_event, false);
37153750
self.recheck_queue.queue_task(

pyrefly/lib/state/load.rs

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,13 +81,33 @@ pub struct Load {
8181
}
8282

8383
impl Load {
84+
fn load_open_filesystem_path(
85+
path: &Path,
86+
memory_lookup: &MemoryFilesLookup,
87+
) -> Option<Arc<FileContents>> {
88+
if let Some(contents) = memory_lookup.get(path) {
89+
return Some(Arc::clone(contents));
90+
}
91+
let canonical_path = path.canonicalize().ok()?;
92+
if canonical_path == path {
93+
return None;
94+
}
95+
memory_lookup.get(&canonical_path).map(Arc::clone)
96+
}
97+
8498
/// Return the code for this module, optional notebook cell mapping, and whether there was an error while loading (a self-error).
8599
pub fn load_from_path(
86100
path: &ModulePath,
87101
memory_lookup: &MemoryFilesLookup,
88102
) -> (FileContents, Option<anyhow::Error>) {
89103
let res = match path.details() {
90-
ModulePathDetails::FileSystem(path) => Self::load_from_filesystem(path),
104+
ModulePathDetails::FileSystem(path) => {
105+
if let Some(contents) = Self::load_open_filesystem_path(path, memory_lookup) {
106+
Ok((*contents).clone())
107+
} else {
108+
Self::load_from_filesystem(path)
109+
}
110+
}
91111
ModulePathDetails::Namespace(_) => Ok(FileContents::from_source("".to_owned())),
92112
ModulePathDetails::Memory(path) => memory_lookup
93113
.get(path)

pyrefly/lib/state/state.rs

Lines changed: 11 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -948,13 +948,7 @@ impl<'a> Transaction<'a> {
948948
.get_cached_loader(&self.get_module(handle).config.read())
949949
.find_import(module, Some(handle.path())),
950950
};
951-
path.map(|path| {
952-
Handle::new(
953-
module,
954-
self.prefer_memory_path(path),
955-
handle.sys_info().dupe(),
956-
)
957-
})
951+
path.map(|path| Handle::new(module, path, handle.sys_info().dupe()))
958952
}
959953

960954
/// Create a handle for import `module` within the handle `handle`, preferring `.py` over `.pyi`
@@ -970,13 +964,7 @@ impl<'a> Transaction<'a> {
970964
.get_cached_loader(&self.get_module(handle).config.read())
971965
.find_import_prefer_executable(module, Some(handle.path())),
972966
};
973-
path.map(|path| {
974-
Handle::new(
975-
module,
976-
self.prefer_memory_path(path),
977-
handle.sys_info().dupe(),
978-
)
979-
})
967+
path.map(|path| Handle::new(module, path, handle.sys_info().dupe()))
980968
}
981969

982970
/// Create a handle for import `module` within the handle `handle`
@@ -1576,23 +1564,6 @@ impl<'a> Transaction<'a> {
15761564
MemoryFilesLookup::new(&self.readable.memory, &self.data.memory_overlay)
15771565
}
15781566

1579-
fn prefer_memory_path(&self, path: ModulePath) -> ModulePath {
1580-
let ModulePathDetails::FileSystem(filesystem_path) = path.details() else {
1581-
return path;
1582-
};
1583-
let memory_lookup = self.memory_lookup();
1584-
if memory_lookup.get(filesystem_path).is_some() {
1585-
return ModulePath::memory((**filesystem_path).clone());
1586-
}
1587-
if let Ok(canonical_path) = filesystem_path.canonicalize()
1588-
&& canonical_path != **filesystem_path
1589-
&& memory_lookup.get(&canonical_path).is_some()
1590-
{
1591-
return ModulePath::memory(canonical_path);
1592-
}
1593-
path
1594-
}
1595-
15961567
fn get_cached_loader(&self, loader: &ArcId<ConfigFile>) -> Arc<LoaderFindCache> {
15971568
self.data
15981569
.updated_loaders
@@ -2069,7 +2040,13 @@ impl<'a> Transaction<'a> {
20692040
for (path, contents) in files {
20702041
if self.memory_lookup().get(&path) != contents.as_ref() {
20712042
self.data.memory_overlay.set(path.clone(), contents);
2072-
changed.insert(ModulePath::memory(path));
2043+
changed.insert(ModulePath::memory(path.clone()));
2044+
changed.insert(ModulePath::filesystem(path.clone()));
2045+
if let Ok(canonical_path) = path.canonicalize()
2046+
&& canonical_path != path
2047+
{
2048+
changed.insert(ModulePath::filesystem(canonical_path));
2049+
}
20732050
}
20742051
}
20752052
self.stats.lock().set_memory_dirty = changed.len();
@@ -2333,7 +2310,7 @@ impl<'a> TransactionHandle<'a> {
23332310
// Explicit path — already resolved. Bypass imports entirely.
23342311
FindingOrError::new_finding(Handle::new(
23352312
module,
2336-
self.transaction.prefer_memory_path(path.dupe()),
2313+
path.dupe(),
23372314
self.module_data.handle.sys_info().dupe(),
23382315
))
23392316
}
@@ -2358,7 +2335,7 @@ impl<'a> TransactionHandle<'a> {
23582335
path.map(|path| {
23592336
Handle::new(
23602337
module,
2361-
self.transaction.prefer_memory_path(path.dupe()),
2338+
path.dupe(),
23622339
self.module_data.handle.sys_info().dupe(),
23632340
)
23642341
})

0 commit comments

Comments
 (0)