aboutsummaryrefslogtreecommitdiff
path: root/crates/libanalysis/src/lib.rs
blob: 027d7439b3834a4d43c5a64ee6197ece6725db31 (plain)
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
extern crate failure;
extern crate parking_lot;
#[macro_use]
extern crate log;
extern crate once_cell;
extern crate libsyntax2;
extern crate libeditor;
extern crate fst;
extern crate rayon;
extern crate relative_path;

mod symbol_index;
mod module_map;
mod api;
mod imp;

use std::{
    sync::{
        Arc,
        atomic::{AtomicBool},
    },
};

use relative_path::RelativePath;

use self::{
    module_map::{ChangeKind},
    imp::{WorldData, FileData},
};
pub use self::symbol_index::Query;
pub use self::api::{
    Analysis, SourceChange, SourceFileEdit, FileSystemEdit, Position, Diagnostic, Runnable, RunnableKind
};

pub type Result<T> = ::std::result::Result<T, ::failure::Error>;

pub trait FileResolver: Send + Sync + 'static {
    fn file_stem(&self, id: FileId) -> String;
    fn resolve(&self, id: FileId, path: &RelativePath) -> Option<FileId>;
}

#[derive(Debug)]
pub struct WorldState {
    data: Arc<WorldData>
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct FileId(pub u32);

impl WorldState {
    pub fn new() -> WorldState {
        WorldState {
            data: Arc::new(WorldData::default()),
        }
    }

    pub fn analysis(
        &self,
        file_resolver: impl FileResolver,
    ) -> Analysis {
        let imp = imp::AnalysisImpl {
            needs_reindex: AtomicBool::new(false),
            file_resolver: Arc::new(file_resolver),
            data: self.data.clone()
        };
        Analysis { imp }
    }

    pub fn change_file(&mut self, file_id: FileId, text: Option<String>) {
        self.change_files(::std::iter::once((file_id, text)));
    }

    pub fn change_files(&mut self, changes: impl Iterator<Item=(FileId, Option<String>)>) {
        let data = self.data_mut();
        for (file_id, text) in changes {
            let change_kind = if data.file_map.remove(&file_id).is_some() {
                if text.is_some() {
                    ChangeKind::Update
                } else {
                    ChangeKind::Delete
                }
            } else {
                ChangeKind::Insert
            };
            data.module_map.update_file(file_id, change_kind);
            data.file_map.remove(&file_id);
            if let Some(text) = text {
                let file_data = FileData::new(text);
                data.file_map.insert(file_id, Arc::new(file_data));
            } else {
                data.file_map.remove(&file_id);
            }
        }
    }

    fn data_mut(&mut self) -> &mut WorldData {
        if Arc::get_mut(&mut self.data).is_none() {
            self.data = Arc::new(WorldData {
                file_map: self.data.file_map.clone(),
                module_map: self.data.module_map.clone(),
            });
        }
        Arc::get_mut(&mut self.data).unwrap()
    }
}