Fixes all discrepancies between IDE and Engine since Engine delivered implementation of suggestions.

Original commit: feca0b72d7
This commit is contained in:
Adam Obuchowicz 2020-07-22 14:37:57 +02:00 committed by GitHub
parent 6eacb28c91
commit 568fa31b49
7 changed files with 110 additions and 75 deletions

View File

@ -151,7 +151,7 @@ trait API {
#[MethodInput=CompletionInput,rpc_name="search/completion"] #[MethodInput=CompletionInput,rpc_name="search/completion"]
fn completion fn completion
( &self ( &self
, module : String , file : Path
, position : Position , position : Position
, self_type : Option<String> , self_type : Option<String>
, return_type : Option<String> , return_type : Option<String>

View File

@ -123,8 +123,8 @@ pub enum Notification {
ExecutionFailed(ExecutionFailed), ExecutionFailed(ExecutionFailed),
/// Sent from server to the client to inform abouth the change in the suggestions database. /// Sent from server to the client to inform abouth the change in the suggestions database.
#[serde(rename = "search/suggestionsDatabaseUpdate")] #[serde(rename = "search/suggestionsDatabaseUpdates")]
SuggestionDatabaseUpdate(SuggestionDatabaseUpdateEvent), SuggestionDatabaseUpdates(SuggestionDatabaseUpdatesEvent),
} }
/// Sent from the server to the client to inform about new information for certain expressions /// Sent from the server to the client to inform about new information for certain expressions
@ -511,9 +511,8 @@ pub type SuggestionsDatabaseVersion = usize;
pub struct SuggestionEntryArgument { pub struct SuggestionEntryArgument {
/// The argument name. /// The argument name.
pub name: String, pub name: String,
/// The arguement type. String 'Any' is used to specify generic types. /// The argument type. String 'Any' is used to specify generic types.
#[serde(rename = "type")] // To avoid collision with the `type` keyword. pub repr_type: String,
pub arg_type: String,
/// Indicates whether the argument is lazy. /// Indicates whether the argument is lazy.
pub is_suspended: bool, pub is_suspended: bool,
/// Optional default value. /// Optional default value.
@ -525,8 +524,8 @@ pub struct SuggestionEntryArgument {
#[serde(rename_all="camelCase")] #[serde(rename_all="camelCase")]
#[allow(missing_docs)] #[allow(missing_docs)]
pub struct SuggestionEntryScope { pub struct SuggestionEntryScope {
pub start : usize, pub start : Position,
pub end : usize, pub end : Position,
} }
/// A type of suggestion entry. /// A type of suggestion entry.
@ -537,17 +536,20 @@ pub enum SuggestionEntryType {Atom,Method,Function,Local}
/// A Suggestion Entry. /// A Suggestion Entry.
#[derive(Hash, Debug, Clone, PartialEq, Eq,Serialize,Deserialize)] #[derive(Hash, Debug, Clone, PartialEq, Eq,Serialize,Deserialize)]
#[serde(rename_all="camelCase")]
#[allow(missing_docs)] #[allow(missing_docs)]
#[serde(tag="type")]
#[serde(rename_all="camelCase")]
pub enum SuggestionEntry { pub enum SuggestionEntry {
SuggestionEntryAtom { #[serde(rename_all="camelCase")]
Atom {
name : String, name : String,
module : String, module : String,
arguments : Vec<SuggestionEntryArgument>, arguments : Vec<SuggestionEntryArgument>,
return_type : String, return_type : String,
documentation : Option<String>, documentation : Option<String>,
}, },
SuggestionEntryMethod { #[serde(rename_all="camelCase")]
Method {
name : String, name : String,
module : String, module : String,
arguments : Vec<SuggestionEntryArgument>, arguments : Vec<SuggestionEntryArgument>,
@ -555,14 +557,16 @@ pub enum SuggestionEntry {
return_type : String, return_type : String,
documentation : Option<String>, documentation : Option<String>,
}, },
SuggestionEntryFunction { #[serde(rename_all="camelCase")]
Function {
name : String, name : String,
module : String, module : String,
arguments : Vec<SuggestionEntryArgument>, arguments : Vec<SuggestionEntryArgument>,
return_type : String, return_type : String,
scope : SuggestionEntryScope, scope : SuggestionEntryScope,
}, },
SuggestionEntryLocal { #[serde(rename_all="camelCase")]
Local {
name : String, name : String,
module : String, module : String,
return_type : String, return_type : String,
@ -574,10 +578,10 @@ impl SuggestionEntry {
/// Get name of the suggested entity. /// Get name of the suggested entity.
pub fn name(&self) -> &String { pub fn name(&self) -> &String {
match self { match self {
Self::SuggestionEntryAtom {name,..} => name, Self::Atom {name,..} => name,
Self::SuggestionEntryFunction {name,..} => name, Self::Function {name,..} => name,
Self::SuggestionEntryLocal {name,..} => name, Self::Local {name,..} => name,
Self::SuggestionEntryMethod {name,..} => name, Self::Method {name,..} => name,
} }
} }
} }
@ -597,24 +601,31 @@ pub struct SuggestionsDatabaseEntry {
pub enum SuggestionsDatabaseUpdateKind {Add,Update,Delete} pub enum SuggestionsDatabaseUpdateKind {Add,Update,Delete}
/// The update of the suggestions database. /// The update of the suggestions database.
#[derive(Hash, Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] #[derive(Hash,Debug,Clone,PartialEq,Eq,Serialize,Deserialize)]
#[serde(rename_all="camelCase")]
#[allow(missing_docs)] #[allow(missing_docs)]
#[serde(tag="type")]
pub enum SuggestionsDatabaseUpdate { pub enum SuggestionsDatabaseUpdate {
#[serde(rename_all="camelCase")]
Add { Add {
id : SuggestionEntryId, id : SuggestionEntryId,
entry : SuggestionEntry, suggestion : SuggestionEntry,
}, },
#[serde(rename_all="camelCase")]
Remove { Remove {
id : SuggestionEntryId id : SuggestionEntryId,
}, },
#[serde(rename_all="camelCase")]
Modify {
id : SuggestionEntryId,
return_type : String,
}
} }
/// Notification about change in the suggestions database. /// Notification about change in the suggestions database.
#[derive(Hash,Debug,Clone,PartialEq,Eq,Serialize,Deserialize)] #[derive(Hash,Debug,Clone,PartialEq,Eq,Serialize,Deserialize)]
#[serde(rename_all="camelCase")] #[serde(rename_all="camelCase")]
#[allow(missing_docs)] #[allow(missing_docs)]
pub struct SuggestionDatabaseUpdateEvent { pub struct SuggestionDatabaseUpdatesEvent {
pub updates : Vec<SuggestionsDatabaseUpdate>, pub updates : Vec<SuggestionsDatabaseUpdate>,
pub current_version : SuggestionsDatabaseVersion, pub current_version : SuggestionsDatabaseVersion,
} }

View File

@ -20,3 +20,12 @@ pub const DEFAULT_PROJECT_NAME:&str = "Unnamed";
/// Visualization folder where IDE can look for user-defined visualizations per project. /// Visualization folder where IDE can look for user-defined visualizations per project.
pub const VISUALIZATION_DIRECTORY:&str = "visualization"; pub const VISUALIZATION_DIRECTORY:&str = "visualization";
/// A module with language-specific constants.
pub mod keywords {
/// A keyword indicating current module.
pub const HERE:&str = "here";
/// The "void" atom returned by function meant to not return any argument.
pub const NOTHING:&str = "Nothing";
}

View File

@ -121,7 +121,7 @@ pub struct ParsedInput {
} }
impl ParsedInput { impl ParsedInput {
/// Contructor from the plain input. /// Constructor from the plain input.
fn new(mut input:String, parser:&Parser) -> FallibleResult<Self> { fn new(mut input:String, parser:&Parser) -> FallibleResult<Self> {
let leading_spaces = input.chars().take_while(|c| *c == ' ').count(); let leading_spaces = input.chars().take_while(|c| *c == ' ').count();
// To properly guess what is "still typed argument" we simulate type of one letter by user. // To properly guess what is "still typed argument" we simulate type of one letter by user.
@ -225,7 +225,7 @@ pub struct Searcher {
logger : Logger, logger : Logger,
data : Rc<RefCell<Data>>, data : Rc<RefCell<Data>>,
notifier : notification::Publisher<Notification>, notifier : notification::Publisher<Notification>,
module : Rc<model::module::QualifiedName>, module : model::module::Path,
position : Immutable<TextLocation>, position : Immutable<TextLocation>,
database : Rc<model::SuggestionDatabase>, database : Rc<model::SuggestionDatabase>,
language_server : Rc<language_server::Connection>, language_server : Rc<language_server::Connection>,
@ -241,11 +241,11 @@ impl Searcher {
, position : TextLocation , position : TextLocation
) -> Self { ) -> Self {
let this = Self { let this = Self {
module,
position : Immutable(position), position : Immutable(position),
logger : Logger::sub(parent,"Searcher Controller"), logger : Logger::sub(parent,"Searcher Controller"),
data : default(), data : default(),
notifier : default(), notifier : default(),
module : Rc::new(project.qualified_module_name(&module)),
database : project.suggestion_db(), database : project.suggestion_db(),
language_server : project.json_rpc(), language_server : project.json_rpc(),
parser : project.parser(), parser : project.parser(),
@ -344,7 +344,7 @@ impl Searcher {
fn get_suggestion_list_from_engine fn get_suggestion_list_from_engine
(&self, return_type:Option<String>, tags:Option<Vec<language_server::SuggestionEntryType>>) { (&self, return_type:Option<String>, tags:Option<Vec<language_server::SuggestionEntryType>>) {
let ls = &self.language_server; let ls = &self.language_server;
let module = self.module.as_ref(); let module = self.module.file_path();
let self_type = None; let self_type = None;
let position = self.position.deref().into(); let position = self.position.deref().into();
let request = ls.completion(module,&position,&self_type,&return_type,&tags); let request = ls.completion(module,&position,&self_type,&return_type,&tags);
@ -408,7 +408,7 @@ mod test {
logger : default(), logger : default(),
data : default(), data : default(),
notifier : default(), notifier : default(),
module : Rc::new(module_path.qualified_module_name("Test")), module : module_path,
position : Immutable(TextLocation::at_document_begin()), position : Immutable(TextLocation::at_document_begin()),
database : default(), database : default(),
language_server : language_server::Connection::new_mock_rc(client), language_server : language_server::Connection::new_mock_rc(client),
@ -450,7 +450,7 @@ mod test {
current_version: default(), current_version: default(),
}; };
expect_call!(client.completion( expect_call!(client.completion(
module = "Test.Test".to_string(), module = Path::from_mock_module_name("Test").file_path().clone(),
position = TextLocation::at_document_begin().into(), position = TextLocation::at_document_begin().into(),
self_type = None, self_type = None,
return_type = None, return_type = None,

View File

@ -107,7 +107,7 @@ impl GraphInfo {
/// After removing last node, we want to insert a placeholder value for definition value. /// After removing last node, we want to insert a placeholder value for definition value.
/// This defines its AST. Currently it is just `Nothing`. /// This defines its AST. Currently it is just `Nothing`.
pub fn empty_graph_body() -> Ast { pub fn empty_graph_body() -> Ast {
Ast::cons("Nothing").with_new_id() Ast::cons(constants::keywords::NOTHING).with_new_id()
} }
/// Removes the node from graph. /// Removes the node from graph.
@ -430,7 +430,7 @@ main =
println!("zz"); println!("zz");
let (node,) = graph.nodes().expect_tuple(); let (node,) = graph.nodes().expect_tuple();
assert_eq!(node.expression().repr(), "Nothing"); assert_eq!(node.expression().repr(), constants::keywords::NOTHING);
graph.expect_code("main = Nothing"); graph.expect_code("main = Nothing");
} }

View File

@ -252,7 +252,11 @@ impl Project {
execution context being already dropped."); execution context being already dropped.");
} }
} }
Event::Notification(Notification::SuggestionDatabaseUpdate(update)) => { Event::Notification(Notification::ExecutionFailed(update)) => {
error!(logger,"Execution failed in context {update.context_id}. Error: \
{update.message}.");
}
Event::Notification(Notification::SuggestionDatabaseUpdates(update)) => {
if let Some(suggestion_db) = weak_suggestion_db.upgrade() { if let Some(suggestion_db) = weak_suggestion_db.upgrade() {
suggestion_db.apply_update_event(update); suggestion_db.apply_update_event(update);
} }

View File

@ -6,7 +6,7 @@ use crate::double_representation::module::QualifiedName;
use enso_protocol::language_server; use enso_protocol::language_server;
use language_server::types::SuggestionsDatabaseVersion; use language_server::types::SuggestionsDatabaseVersion;
use language_server::types::SuggestionDatabaseUpdateEvent; use language_server::types::SuggestionDatabaseUpdatesEvent;
pub use language_server::types::SuggestionEntryArgument as Argument; pub use language_server::types::SuggestionEntryArgument as Argument;
pub use language_server::types::SuggestionEntryId as EntryId; pub use language_server::types::SuggestionEntryId as EntryId;
@ -49,37 +49,33 @@ impl Entry {
pub fn from_ls_entry(entry:language_server::types::SuggestionEntry) -> FallibleResult<Self> { pub fn from_ls_entry(entry:language_server::types::SuggestionEntry) -> FallibleResult<Self> {
use language_server::types::SuggestionEntry::*; use language_server::types::SuggestionEntry::*;
let this = match entry { let this = match entry {
SuggestionEntryAtom {name,module,arguments,return_type,documentation} => Atom {name,module,arguments,return_type,documentation} => Self {
Self { name,arguments,return_type,documentation,
name,arguments,return_type,documentation, module : module.try_into()?,
module : module.try_into()?, self_type : None,
self_type : None, kind : EntryKind::Atom,
kind : EntryKind::Atom, },
}, Method {name,module,arguments,self_type,return_type,documentation} => Self {
SuggestionEntryMethod {name,module,arguments,self_type,return_type,documentation} => name,arguments,return_type,documentation,
Self { module : module.try_into()?,
name,arguments,return_type,documentation, self_type : Some(self_type),
module : module.try_into()?, kind : EntryKind::Method,
self_type : Some(self_type), },
kind : EntryKind::Method, Function {name,module,arguments,return_type,..} => Self {
}, name,arguments,return_type,
SuggestionEntryFunction {name,module,arguments,return_type,..} => module : module.try_into()?,
Self { self_type : None,
name,arguments,return_type, documentation : default(),
module : module.try_into()?, kind : EntryKind::Function,
self_type : None, },
documentation : default(), Local {name,module,return_type,..} => Self {
kind : EntryKind::Function, name,return_type,
}, arguments : default(),
SuggestionEntryLocal {name,module,return_type,..} => module : module.try_into()?,
Self { self_type : None,
name,return_type, documentation : default(),
arguments : default(), kind : EntryKind::Local,
module : module.try_into()?, },
self_type : None,
documentation : default(),
kind : EntryKind::Local,
},
}; };
Ok(this) Ok(this)
} }
@ -88,7 +84,9 @@ impl Entry {
pub fn code_to_insert(&self) -> String { pub fn code_to_insert(&self) -> String {
let module = self.module.name(); let module = self.module.name();
if self.self_type.as_ref().contains(&module) { if self.self_type.as_ref().contains(&module) {
iformat!("{module}.{self.name}") format!("{}.{}",module,self.name)
} else if self.self_type.as_ref().contains(&constants::keywords::HERE) {
format!("{}.{}",constants::keywords::HERE,self.name)
} else { } else {
self.name.clone() self.name.clone()
} }
@ -158,15 +156,28 @@ impl SuggestionDatabase {
} }
/// Apply the update event to the database. /// Apply the update event to the database.
pub fn apply_update_event(&self, event:SuggestionDatabaseUpdateEvent) { pub fn apply_update_event(&self, event:SuggestionDatabaseUpdatesEvent) {
for update in event.updates { for update in event.updates {
let mut entries = self.entries.borrow_mut(); let mut entries = self.entries.borrow_mut();
match update { match update {
Update::Add {id,entry} => match entry.try_into() { Update::Add {id,suggestion} => match suggestion.try_into() {
Ok(entry) => { entries.insert(id,Rc::new(entry)); }, Ok(entry) => { entries.insert(id,Rc::new(entry)); },
Err(err) => { error!(self.logger, "Discarding update for {id}: {err}") }, Err(err) => { error!(self.logger, "Discarding update for {id}: {err}") },
}, },
Update::Remove {id} => { entries.remove(&id); }, Update::Remove {id} => {
let removed = entries.remove(&id);
if removed.is_none() {
error!(self.logger, "Received Remove event for nonexistent id: {id}");
}
},
Update::Modify {id,return_type} => {
if let Some(old_entry) = entries.get(&id) {
let new_entry = Entry {return_type,..old_entry.deref().clone()};
entries.insert(id,Rc::new(new_entry));
} else {
error!(self.logger, "Received Modify event for nonexistent id: {id}");
}
}
}; };
} }
self.version.set(event.current_version); self.version.set(event.current_version);
@ -241,7 +252,7 @@ mod test {
assert_eq!(db.version.get() , 123); assert_eq!(db.version.get() , 123);
// Non-empty db // Non-empty db
let entry = language_server::types::SuggestionEntry::SuggestionEntryAtom { let entry = language_server::types::SuggestionEntry::Atom {
name : "TextAtom".to_string(), name : "TextAtom".to_string(),
module : "TestProject.TestModule".to_string(), module : "TestProject.TestModule".to_string(),
arguments : vec![], arguments : vec![],
@ -261,21 +272,21 @@ mod test {
#[test] #[test]
fn applying_update() { fn applying_update() {
let entry1 = language_server::types::SuggestionEntry::SuggestionEntryAtom { let entry1 = language_server::types::SuggestionEntry::Atom {
name : "Entry1".to_string(), name : "Entry1".to_string(),
module : "TestProject.TestModule".to_string(), module : "TestProject.TestModule".to_string(),
arguments : vec![], arguments : vec![],
return_type : "TestAtom".to_string(), return_type : "TestAtom".to_string(),
documentation : None documentation : None
}; };
let entry2 = language_server::types::SuggestionEntry::SuggestionEntryAtom { let entry2 = language_server::types::SuggestionEntry::Atom {
name : "Entry2".to_string(), name : "Entry2".to_string(),
module : "TestProject.TestModule".to_string(), module : "TestProject.TestModule".to_string(),
arguments : vec![], arguments : vec![],
return_type : "TestAtom".to_string(), return_type : "TestAtom".to_string(),
documentation : None documentation : None
}; };
let new_entry2 = language_server::types::SuggestionEntry::SuggestionEntryAtom { let new_entry2 = language_server::types::SuggestionEntry::Atom {
name : "NewEntry2".to_string(), name : "NewEntry2".to_string(),
module : "TestProject.TestModule".to_string(), module : "TestProject.TestModule".to_string(),
arguments : vec![], arguments : vec![],
@ -293,7 +304,7 @@ mod test {
// Remove // Remove
let remove_update = Update::Remove {id:2}; let remove_update = Update::Remove {id:2};
let update = SuggestionDatabaseUpdateEvent { let update = SuggestionDatabaseUpdatesEvent {
updates : vec![remove_update], updates : vec![remove_update],
current_version : 2 current_version : 2
}; };
@ -302,8 +313,8 @@ mod test {
assert_eq!(db.version.get(), 2 ); assert_eq!(db.version.get(), 2 );
// Add // Add
let add_update = Update::Add {id:2, entry:new_entry2}; let add_update = Update::Add {id:2, suggestion:new_entry2};
let update = SuggestionDatabaseUpdateEvent { let update = SuggestionDatabaseUpdatesEvent {
updates : vec![add_update], updates : vec![add_update],
current_version : 3, current_version : 3,
}; };