mirror of
https://github.com/SpaceManiac/SpacemanDMM.git
synced 2025-12-23 05:36:47 +00:00
Improve define history type safety
This commit is contained in:
parent
a0fcb422d5
commit
b99287c505
4 changed files with 105 additions and 84 deletions
|
|
@ -114,7 +114,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||
let parser = dm::parser::Parser::new(&context, indents);
|
||||
parser.parse_with_module_docs()
|
||||
};
|
||||
pp.finalize();
|
||||
let define_history = pp.finalize();
|
||||
|
||||
println!("collating documented types");
|
||||
let mut types_with_docs = BTreeMap::new();
|
||||
|
|
@ -134,7 +134,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||
}
|
||||
|
||||
// if macros have docs, that counts as a module too
|
||||
for (range, (name, define)) in pp.history().iter() {
|
||||
for (range, (name, define)) in define_history.iter() {
|
||||
progress.update(&format!("#define {}", name));
|
||||
|
||||
let (docs, has_params, params, is_variadic);
|
||||
|
|
|
|||
|
|
@ -63,8 +63,85 @@ impl fmt::Display for Define {
|
|||
}
|
||||
}
|
||||
|
||||
type InnerDefineHistory = IntervalTree<Location, (String, Define)>;
|
||||
|
||||
/// An interval tree representing historic macro definitions.
|
||||
pub type DefineHistory = IntervalTree<Location, (String, Define)>;
|
||||
#[derive(Debug)]
|
||||
pub struct DefineHistory {
|
||||
env_file: PathBuf,
|
||||
last_input_loc: Location,
|
||||
tree: InnerDefineHistory,
|
||||
}
|
||||
|
||||
impl DefineHistory {
|
||||
/// Branch a child preprocessor from this preprocessor's historic state at
|
||||
/// the start of the given file.
|
||||
pub fn branch_at_file<'ctx2>(&self, file: FileId, context: &'ctx2 Context) -> Preprocessor<'ctx2> {
|
||||
let location = Location { file, line: 0, column: 0 };
|
||||
let defines = DefineMap::from_history(self, location);
|
||||
|
||||
Preprocessor {
|
||||
context,
|
||||
env_file: self.env_file.clone(),
|
||||
include_stack: Default::default(),
|
||||
include_locations: Default::default(),
|
||||
history: Default::default(), // TODO: support branching a second time
|
||||
defines,
|
||||
maps: Default::default(),
|
||||
skins: Default::default(),
|
||||
scripts: Default::default(),
|
||||
ifdef_stack: Default::default(), // should be fine
|
||||
ifdef_history: Default::default(),
|
||||
last_input_loc: location,
|
||||
last_printable_input_loc: location,
|
||||
output: Default::default(),
|
||||
danger_idents: Default::default(),
|
||||
docs_in: Default::default(),
|
||||
docs_out: Default::default(),
|
||||
in_interp_string: 0,
|
||||
annotations: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Branch a child preprocessor from this preprocessor's current state.
|
||||
pub fn branch_at_end<'ctx2>(&self, context: &'ctx2 Context) -> Preprocessor<'ctx2> {
|
||||
Preprocessor {
|
||||
context,
|
||||
env_file: self.env_file.clone(),
|
||||
include_stack: Default::default(),
|
||||
include_locations: Default::default(),
|
||||
history: Default::default(), // TODO: support branching a second time
|
||||
defines: DefineMap::from_history(self, self.last_input_loc),
|
||||
maps: Default::default(),
|
||||
skins: Default::default(),
|
||||
scripts: Default::default(),
|
||||
ifdef_stack: Default::default(), // should be fine
|
||||
ifdef_history: Default::default(),
|
||||
last_input_loc: self.last_input_loc,
|
||||
last_printable_input_loc: self.last_input_loc,
|
||||
output: Default::default(),
|
||||
danger_idents: Default::default(),
|
||||
docs_in: Default::default(),
|
||||
docs_out: Default::default(),
|
||||
in_interp_string: 0,
|
||||
annotations: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::Deref for DefineHistory {
|
||||
type Target = IntervalTree<Location, (String, Define)>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.tree
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::DerefMut for DefineHistory {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.tree
|
||||
}
|
||||
}
|
||||
|
||||
/// A map from macro names to their locations and definitions.
|
||||
///
|
||||
|
|
@ -134,7 +211,7 @@ impl DefineMap {
|
|||
}
|
||||
|
||||
/// Cut a DefineMap from the state of a DefineHistory at the given location.
|
||||
fn from_history(history: &DefineHistory, location: Location) -> DefineMap {
|
||||
fn from_history(history: &InnerDefineHistory, location: Location) -> DefineMap {
|
||||
let mut map = DefineMap::default();
|
||||
for (range, &(ref name, ref define)) in history.range(range(location, location)) {
|
||||
map.insert(name.clone(), (range.start, define.clone()));
|
||||
|
|
@ -142,6 +219,7 @@ impl DefineMap {
|
|||
map
|
||||
}
|
||||
|
||||
/*
|
||||
/// Test whether two DefineMaps are equal, ignoring definition locations.
|
||||
fn equals(&self, rhs: &DefineMap) -> bool {
|
||||
if self.len() != rhs.len() {
|
||||
|
|
@ -157,6 +235,7 @@ impl DefineMap {
|
|||
})
|
||||
})
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
|
@ -284,7 +363,7 @@ pub struct Preprocessor<'ctx> {
|
|||
ifdef_history: IntervalTree<Location, bool>,
|
||||
annotations: Option<AnnotationTree>,
|
||||
|
||||
history: DefineHistory,
|
||||
history: InnerDefineHistory,
|
||||
defines: DefineMap,
|
||||
maps: Vec<PathBuf>,
|
||||
skins: Vec<PathBuf>,
|
||||
|
|
@ -373,8 +452,8 @@ impl<'ctx> Preprocessor<'ctx> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Move all active defines to the define history.
|
||||
pub fn finalize(&mut self) {
|
||||
/// Finalize this preprocessor into its complete define history.
|
||||
pub fn finalize(mut self) -> DefineHistory {
|
||||
let mut i = 0;
|
||||
for (name, vector) in self.defines.inner.drain() {
|
||||
for (start, define) in vector {
|
||||
|
|
@ -389,16 +468,11 @@ impl<'ctx> Preprocessor<'ctx> {
|
|||
self.history.insert(range(start, end), (name.clone(), define));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Access the define history. Will be incomplete until finalized.
|
||||
pub fn history(&self) -> &DefineHistory {
|
||||
&self.history
|
||||
}
|
||||
|
||||
/// Access currently active defines.
|
||||
pub fn defines_at(&self, location: Location) -> DefineMap {
|
||||
DefineMap::from_history(&self.history, location)
|
||||
DefineHistory {
|
||||
env_file: self.env_file,
|
||||
last_input_loc: self.last_input_loc,
|
||||
tree: self.history,
|
||||
}
|
||||
}
|
||||
|
||||
/// Access the ifdef history.
|
||||
|
|
@ -406,60 +480,7 @@ impl<'ctx> Preprocessor<'ctx> {
|
|||
&self.ifdef_history
|
||||
}
|
||||
|
||||
/// Branch a child preprocessor from this preprocessor's historic state at
|
||||
/// the start of the given file.
|
||||
pub fn branch_at_file<'ctx2>(&self, file: FileId, context: &'ctx2 Context) -> Preprocessor<'ctx2> {
|
||||
let location = Location { file, line: 0, column: 0 };
|
||||
let defines = DefineMap::from_history(&self.history, location);
|
||||
|
||||
Preprocessor {
|
||||
context,
|
||||
env_file: self.env_file.clone(),
|
||||
include_stack: Default::default(),
|
||||
include_locations: Default::default(),
|
||||
history: Default::default(), // TODO: support branching a second time
|
||||
defines,
|
||||
maps: Default::default(),
|
||||
skins: Default::default(),
|
||||
scripts: Default::default(),
|
||||
ifdef_stack: Default::default(), // should be fine
|
||||
ifdef_history: Default::default(),
|
||||
last_input_loc: location,
|
||||
last_printable_input_loc: location,
|
||||
output: Default::default(),
|
||||
danger_idents: Default::default(),
|
||||
docs_in: Default::default(),
|
||||
docs_out: Default::default(),
|
||||
in_interp_string: 0,
|
||||
annotations: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Branch a child preprocessor from this preprocessor's current state.
|
||||
pub fn branch<'ctx2>(&self, context: &'ctx2 Context) -> Preprocessor<'ctx2> {
|
||||
Preprocessor {
|
||||
context,
|
||||
env_file: self.env_file.clone(),
|
||||
include_stack: Default::default(),
|
||||
include_locations: Default::default(),
|
||||
history: Default::default(), // TODO: support branching a second time
|
||||
defines: self.defines.clone(),
|
||||
maps: Default::default(),
|
||||
skins: Default::default(),
|
||||
scripts: Default::default(),
|
||||
ifdef_stack: Default::default(), // should be fine
|
||||
ifdef_history: Default::default(),
|
||||
last_input_loc: self.last_input_loc,
|
||||
last_printable_input_loc: self.last_printable_input_loc,
|
||||
output: Default::default(),
|
||||
danger_idents: Default::default(),
|
||||
docs_in: Default::default(),
|
||||
docs_out: Default::default(),
|
||||
in_interp_string: 0,
|
||||
annotations: None,
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
/// Check whether this preprocessor's state as of the end of the given file
|
||||
/// matches the given child preprocessor.
|
||||
pub fn matches_end_of_file(&self, file: FileId, other: &Preprocessor) -> bool {
|
||||
|
|
@ -467,6 +488,7 @@ impl<'ctx> Preprocessor<'ctx> {
|
|||
let defines = DefineMap::from_history(&self.history, location);
|
||||
defines.equals(&other.defines)
|
||||
}
|
||||
*/
|
||||
|
||||
/// Push a DM file to the top of this preprocessor's stack.
|
||||
pub fn push_file<R: io::Read + 'static>(&mut self, path: PathBuf, read: R) -> FileId {
|
||||
|
|
|
|||
|
|
@ -389,9 +389,9 @@ impl<'a> Engine<'a> {
|
|||
}
|
||||
|
||||
// macros
|
||||
if let Some(ref preprocessor) = self.preprocessor {
|
||||
if let Some(ref defines) = self.defines {
|
||||
// TODO: verify that the macro is in scope at the location
|
||||
for (_, &(ref name, ref define)) in preprocessor.history().iter() {
|
||||
for (_, &(ref name, ref define)) in defines.iter() {
|
||||
if contains(name, query) {
|
||||
results.push(CompletionItem {
|
||||
label: name.to_owned(),
|
||||
|
|
|
|||
|
|
@ -145,7 +145,7 @@ struct Engine<'a> {
|
|||
root: Option<Url>,
|
||||
|
||||
context: &'a dm::Context,
|
||||
preprocessor: Option<dm::preprocessor::Preprocessor<'a>>,
|
||||
defines: Option<dm::preprocessor::DefineHistory>,
|
||||
objtree: Arc<dm::objtree::ObjectTree>,
|
||||
references_table: Option<find_references::ReferencesTable>,
|
||||
|
||||
|
|
@ -165,7 +165,7 @@ impl<'a> Engine<'a> {
|
|||
root: None,
|
||||
|
||||
context,
|
||||
preprocessor: None,
|
||||
defines: None,
|
||||
objtree: Default::default(),
|
||||
references_table: None,
|
||||
|
||||
|
|
@ -338,8 +338,7 @@ impl<'a> Engine<'a> {
|
|||
self.update_objtree();
|
||||
self.references_table = Some(find_references::ReferencesTable::new(&self.objtree));
|
||||
// TODO: run dreamchecker here if enabled
|
||||
pp.finalize();
|
||||
self.preprocessor = Some(pp);
|
||||
self.defines = Some(pp.finalize());
|
||||
self.issue_notification::<extras::WindowStatus>(Default::default());
|
||||
let elapsed = start.elapsed();
|
||||
eprintln!(
|
||||
|
|
@ -438,13 +437,13 @@ impl<'a> Engine<'a> {
|
|||
Err(_) => return Err(invalid_request(format!("outside workspace: {}", url))),
|
||||
};
|
||||
|
||||
let preprocessor = match self.preprocessor {
|
||||
Some(ref pp) => pp,
|
||||
None => return Err(invalid_request("no preprocessor")),
|
||||
let defines = match self.defines {
|
||||
Some(ref d) => d,
|
||||
None => return Err(invalid_request("no preprocessor history")),
|
||||
};
|
||||
let (real_file_id, mut preprocessor) = match self.context.get_file(&stripped) {
|
||||
Some(id) => (id, preprocessor.branch_at_file(id, &self.context)),
|
||||
None => (FileId::default(), preprocessor.branch(&self.context)),
|
||||
Some(id) => (id, defines.branch_at_file(id, &self.context)),
|
||||
None => (FileId::default(), defines.branch_at_end(&self.context)),
|
||||
};
|
||||
let contents = self.docs.read(url).map_err(invalid_request)?;
|
||||
let file_id = preprocessor.push_file(stripped.to_owned(), contents);
|
||||
|
|
@ -792,8 +791,8 @@ handle_method_call! {
|
|||
};
|
||||
|
||||
let mut results = Vec::new();
|
||||
if let Some(ref preprocessor) = self.preprocessor {
|
||||
for (range, &(ref name, _)) in preprocessor.history().iter() {
|
||||
if let Some(ref defines) = self.defines {
|
||||
for (range, &(ref name, _)) in defines.iter() {
|
||||
if query.matches_define(name) {
|
||||
results.push(SymbolInformation {
|
||||
name: name.to_owned(),
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue