Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
97 changes: 97 additions & 0 deletions crates/base_db/src/change.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
//! Defines a unit of change that can applied to the database to get the next
//! state. Changes are transactional.

use std::{fmt, sync::Arc};

use rustc_hash::FxHashSet;
use salsa::Durability;
use vfs::FileId;

use crate::{CrateGraph, SourceDatabaseExt, SourceRoot, SourceRootId};

/// Encapsulate a bunch of raw `.set` calls on the database.
#[derive(Default)]
pub struct Change {
pub roots: Option<Vec<SourceRoot>>,
pub files_changed: Vec<(FileId, Option<Arc<String>>)>,
pub crate_graph: Option<CrateGraph>,
}

impl fmt::Debug for Change {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
let mut d = fmt.debug_struct("AnalysisChange");
if let Some(roots) = &self.roots {
d.field("roots", roots);
}
if !self.files_changed.is_empty() {
d.field("files_changed", &self.files_changed.len());
}
if self.crate_graph.is_some() {
d.field("crate_graph", &self.crate_graph);
}
d.finish()
}
}

impl Change {
pub fn new() -> Change {
Change::default()
}

pub fn set_roots(&mut self, roots: Vec<SourceRoot>) {
self.roots = Some(roots);
}

pub fn change_file(&mut self, file_id: FileId, new_text: Option<Arc<String>>) {
self.files_changed.push((file_id, new_text))
}

pub fn set_crate_graph(&mut self, graph: CrateGraph) {
self.crate_graph = Some(graph);
}

pub fn apply(self, db: &mut dyn SourceDatabaseExt) {
let _p = profile::span("RootDatabase::apply_change");
// db.request_cancellation();
// log::info!("apply_change {:?}", change);
if let Some(roots) = self.roots {
let mut local_roots = FxHashSet::default();
let mut library_roots = FxHashSet::default();
for (idx, root) in roots.into_iter().enumerate() {
let root_id = SourceRootId(idx as u32);
let durability = durability(&root);
if root.is_library {
library_roots.insert(root_id);
} else {
local_roots.insert(root_id);
}
for file_id in root.iter() {
db.set_file_source_root_with_durability(file_id, root_id, durability);
}
db.set_source_root_with_durability(root_id, Arc::new(root), durability);
}
// db.set_local_roots_with_durability(Arc::new(local_roots), Durability::HIGH);
// db.set_library_roots_with_durability(Arc::new(library_roots), Durability::HIGH);
}

for (file_id, text) in self.files_changed {
let source_root_id = db.file_source_root(file_id);
let source_root = db.source_root(source_root_id);
let durability = durability(&source_root);
// XXX: can't actually remove the file, just reset the text
let text = text.unwrap_or_default();
db.set_file_text_with_durability(file_id, text, durability)
}
if let Some(crate_graph) = self.crate_graph {
db.set_crate_graph_with_durability(Arc::new(crate_graph), Durability::HIGH)
}
}
}

fn durability(source_root: &SourceRoot) -> Durability {
if source_root.is_library {
Durability::HIGH
} else {
Durability::LOW
}
}
175 changes: 92 additions & 83 deletions crates/base_db/src/fixture.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,24 +65,26 @@ use test_utils::{extract_range_or_offset, Fixture, RangeOrOffset, CURSOR_MARKER}
use vfs::{file_set::FileSet, VfsPath};

use crate::{
input::CrateName, CrateGraph, CrateId, Edition, Env, FileId, FilePosition, SourceDatabaseExt,
SourceRoot, SourceRootId,
input::CrateName, Change, CrateGraph, CrateId, Edition, Env, FileId, FilePosition,
SourceDatabaseExt, SourceRoot, SourceRootId,
};

pub const WORKSPACE: SourceRootId = SourceRootId(0);

pub trait WithFixture: Default + SourceDatabaseExt + 'static {
fn with_single_file(text: &str) -> (Self, FileId) {
let fixture = ChangeFixture::parse(text);
let mut db = Self::default();
let (_, files) = with_files(&mut db, text);
assert_eq!(files.len(), 1);
(db, files[0])
fixture.change.apply(&mut db);
assert_eq!(fixture.files.len(), 1);
(db, fixture.files[0])
}

fn with_files(ra_fixture: &str) -> Self {
let fixture = ChangeFixture::parse(ra_fixture);
let mut db = Self::default();
let (pos, _) = with_files(&mut db, ra_fixture);
assert!(pos.is_none());
fixture.change.apply(&mut db);
assert!(fixture.file_position.is_none());
db
}

Expand All @@ -96,9 +98,10 @@ pub trait WithFixture: Default + SourceDatabaseExt + 'static {
}

fn with_range_or_offset(ra_fixture: &str) -> (Self, FileId, RangeOrOffset) {
let fixture = ChangeFixture::parse(ra_fixture);
let mut db = Self::default();
let (pos, _) = with_files(&mut db, ra_fixture);
let (file_id, range_or_offset) = pos.unwrap();
fixture.change.apply(&mut db);
let (file_id, range_or_offset) = fixture.file_position.unwrap();
(db, file_id, range_or_offset)
}

Expand All @@ -113,89 +116,95 @@ pub trait WithFixture: Default + SourceDatabaseExt + 'static {

impl<DB: SourceDatabaseExt + Default + 'static> WithFixture for DB {}

fn with_files(
db: &mut dyn SourceDatabaseExt,
fixture: &str,
) -> (Option<(FileId, RangeOrOffset)>, Vec<FileId>) {
let fixture = Fixture::parse(fixture);

let mut files = Vec::new();
let mut crate_graph = CrateGraph::default();
let mut crates = FxHashMap::default();
let mut crate_deps = Vec::new();
let mut default_crate_root: Option<FileId> = None;

let mut file_set = FileSet::default();
let source_root_id = WORKSPACE;
let source_root_prefix = "/".to_string();
let mut file_id = FileId(0);

let mut file_position = None;

for entry in fixture {
let text = if entry.text.contains(CURSOR_MARKER) {
let (range_or_offset, text) = extract_range_or_offset(&entry.text);
assert!(file_position.is_none());
file_position = Some((file_id, range_or_offset));
text.to_string()
} else {
entry.text.clone()
};
pub struct ChangeFixture {
pub file_position: Option<(FileId, RangeOrOffset)>,
pub files: Vec<FileId>,
pub change: Change,
}

let meta = FileMeta::from(entry);
assert!(meta.path.starts_with(&source_root_prefix));
impl ChangeFixture {
pub fn parse(ra_fixture: &str) -> ChangeFixture {
let fixture = Fixture::parse(ra_fixture);
let mut change = Change::new();

let mut files = Vec::new();
let mut crate_graph = CrateGraph::default();
let mut crates = FxHashMap::default();
let mut crate_deps = Vec::new();
let mut default_crate_root: Option<FileId> = None;
let mut default_cfg = CfgOptions::default();

let mut file_set = FileSet::default();
let source_root_prefix = "/".to_string();
let mut file_id = FileId(0);

let mut file_position = None;

for entry in fixture {
let text = if entry.text.contains(CURSOR_MARKER) {
let (range_or_offset, text) = extract_range_or_offset(&entry.text);
assert!(file_position.is_none());
file_position = Some((file_id, range_or_offset));
text.to_string()
} else {
entry.text.clone()
};

let meta = FileMeta::from(entry);
assert!(meta.path.starts_with(&source_root_prefix));

if let Some(krate) = meta.krate {
let crate_id = crate_graph.add_crate_root(
file_id,
meta.edition,
Some(krate.clone()),
meta.cfg,
meta.env,
Default::default(),
);
let crate_name = CrateName::new(&krate).unwrap();
let prev = crates.insert(crate_name.clone(), crate_id);
assert!(prev.is_none());
for dep in meta.deps {
let dep = CrateName::new(&dep).unwrap();
crate_deps.push((crate_name.clone(), dep))
}
} else if meta.path == "/main.rs" || meta.path == "/lib.rs" {
assert!(default_crate_root.is_none());
default_crate_root = Some(file_id);
default_cfg = meta.cfg;
}

change.change_file(file_id, Some(Arc::new(text)));
let path = VfsPath::new_virtual_path(meta.path);
file_set.insert(file_id, path.into());
files.push(file_id);
file_id.0 += 1;
}

if let Some(krate) = meta.krate {
let crate_id = crate_graph.add_crate_root(
file_id,
meta.edition,
Some(krate.clone()),
meta.cfg,
meta.env,
if crates.is_empty() {
let crate_root = default_crate_root.unwrap();
crate_graph.add_crate_root(
crate_root,
Edition::Edition2018,
Some("test".to_string()),
default_cfg,
Env::default(),
Default::default(),
);
let crate_name = CrateName::new(&krate).unwrap();
let prev = crates.insert(crate_name.clone(), crate_id);
assert!(prev.is_none());
for dep in meta.deps {
let dep = CrateName::new(&dep).unwrap();
crate_deps.push((crate_name.clone(), dep))
} else {
for (from, to) in crate_deps {
let from_id = crates[&from];
let to_id = crates[&to];
crate_graph.add_dep(from_id, CrateName::new(&to).unwrap(), to_id).unwrap();
}
} else if meta.path == "/main.rs" || meta.path == "/lib.rs" {
assert!(default_crate_root.is_none());
default_crate_root = Some(file_id);
}

db.set_file_text(file_id, Arc::new(text));
db.set_file_source_root(file_id, source_root_id);
let path = VfsPath::new_virtual_path(meta.path);
file_set.insert(file_id, path.into());
files.push(file_id);
file_id.0 += 1;
}
change.set_roots(vec![SourceRoot::new_local(file_set)]);
change.set_crate_graph(crate_graph);

if crates.is_empty() {
let crate_root = default_crate_root.unwrap();
crate_graph.add_crate_root(
crate_root,
Edition::Edition2018,
None,
CfgOptions::default(),
Env::default(),
Default::default(),
);
} else {
for (from, to) in crate_deps {
let from_id = crates[&from];
let to_id = crates[&to];
crate_graph.add_dep(from_id, CrateName::new(&to).unwrap(), to_id).unwrap();
}
ChangeFixture { file_position, files, change }
}

db.set_source_root(source_root_id, Arc::new(SourceRoot::new_local(file_set)));
db.set_crate_graph(Arc::new(crate_graph));

(file_position, files)
}

struct FileMeta {
Expand Down
2 changes: 2 additions & 0 deletions crates/base_db/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
//! base_db defines basic database traits. The concrete DB is defined by ide.
mod cancellation;
mod input;
mod change;
pub mod fixture;

use std::{panic, sync::Arc};
Expand All @@ -10,6 +11,7 @@ use syntax::{ast, Parse, SourceFile, TextRange, TextSize};

pub use crate::{
cancellation::Canceled,
change::Change,
input::{
CrateData, CrateGraph, CrateId, CrateName, Dependency, Edition, Env, FileId, ProcMacroId,
SourceRoot, SourceRootId,
Expand Down
Loading