From b99287c50539964a1a25d8779cf8a6b311ed65e5 Mon Sep 17 00:00:00 2001 From: Tad Hardesty Date: Sun, 8 Dec 2019 20:40:45 -0800 Subject: [PATCH] Improve define history type safety --- src/dmdoc/main.rs | 4 +- src/dreammaker/preprocessor.rs | 160 +++++++++++++++++++-------------- src/langserver/completion.rs | 4 +- src/langserver/main.rs | 21 +++-- 4 files changed, 105 insertions(+), 84 deletions(-) diff --git a/src/dmdoc/main.rs b/src/dmdoc/main.rs index 5b408bf7..7f7adc23 100644 --- a/src/dmdoc/main.rs +++ b/src/dmdoc/main.rs @@ -114,7 +114,7 @@ fn main() -> Result<(), Box> { 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> { } // 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); diff --git a/src/dreammaker/preprocessor.rs b/src/dreammaker/preprocessor.rs index 4e10bc82..2236e383 100644 --- a/src/dreammaker/preprocessor.rs +++ b/src/dreammaker/preprocessor.rs @@ -63,8 +63,85 @@ impl fmt::Display for Define { } } +type InnerDefineHistory = IntervalTree; + /// An interval tree representing historic macro definitions. -pub type DefineHistory = IntervalTree; +#[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; + + 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, annotations: Option, - history: DefineHistory, + history: InnerDefineHistory, defines: DefineMap, maps: Vec, skins: Vec, @@ -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(&mut self, path: PathBuf, read: R) -> FileId { diff --git a/src/langserver/completion.rs b/src/langserver/completion.rs index 71da20c1..3133cdc3 100644 --- a/src/langserver/completion.rs +++ b/src/langserver/completion.rs @@ -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(), diff --git a/src/langserver/main.rs b/src/langserver/main.rs index 84d7edcc..cf3c17a5 100644 --- a/src/langserver/main.rs +++ b/src/langserver/main.rs @@ -145,7 +145,7 @@ struct Engine<'a> { root: Option, context: &'a dm::Context, - preprocessor: Option>, + defines: Option, objtree: Arc, references_table: Option, @@ -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::(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(),