completed refactor

added cargo lock

Signed-off-by: faldor20 <eli.jambu@yahoo.com>
This commit is contained in:
Eli Dowling 2023-12-10 10:28:17 +10:00 committed by faldor20
parent 58dec9af28
commit a2c8acd9ac
No known key found for this signature in database
GPG key ID: F2216079B890CD57
6 changed files with 217 additions and 362 deletions

38
Cargo.lock generated
View file

@ -3060,7 +3060,6 @@ dependencies = [
"roc_derive_key", "roc_derive_key",
"roc_error_macros", "roc_error_macros",
"roc_exhaustive", "roc_exhaustive",
"roc_load",
"roc_module", "roc_module",
"roc_packaging", "roc_packaging",
"roc_parse", "roc_parse",
@ -3073,7 +3072,6 @@ dependencies = [
"roc_types", "roc_types",
"roc_unify", "roc_unify",
"tempfile", "tempfile",
"test_solve_helpers",
] ]
[[package]] [[package]]
@ -3096,6 +3094,42 @@ dependencies = [
"bitflags 1.3.2", "bitflags 1.3.2",
] ]
[[package]]
name = "roc_solve_tests"
version = "0.0.1"
dependencies = [
"arrayvec 0.7.4",
"bumpalo",
"indoc",
"insta",
"lazy_static",
"libtest-mimic",
"pretty_assertions",
"regex",
"roc_builtins",
"roc_can",
"roc_checkmate",
"roc_collections",
"roc_debug_flags",
"roc_derive",
"roc_derive_key",
"roc_error_macros",
"roc_exhaustive",
"roc_load_internal",
"roc_module",
"roc_packaging",
"roc_parse",
"roc_problem",
"roc_region",
"roc_reporting",
"roc_solve_problem",
"roc_solve_schema",
"roc_target",
"roc_types",
"roc_unify",
"tempfile",
]
[[package]] [[package]]
name = "roc_std" name = "roc_std"
version = "0.0.1" version = "0.0.1"

View file

@ -1,7 +1,5 @@
use std::{ use std::{
collections::HashMap, collections::HashMap,
future::Future,
io::Write,
path::{Path, PathBuf}, path::{Path, PathBuf},
}; };
@ -55,10 +53,12 @@ fn format_var_type(
subs.rollback_to(snapshot); subs.rollback_to(snapshot);
type_str type_str
} }
pub(crate) fn global_anal( ///Returns a closure that will run the global analysis and the docinfo for the provided source
///This means that you can get positions within the source code before the analysis completes
pub(crate) fn global_analysis(
source_url: Url, source_url: Url,
source: String, source: String,
version: u32, version: i32,
) -> (impl FnOnce() -> Vec<AnalyzedDocument>, DocInfo) { ) -> (impl FnOnce() -> Vec<AnalyzedDocument>, DocInfo) {
let fi = source_url.to_file_path().unwrap(); let fi = source_url.to_file_path().unwrap();
let src_dir = find_src_dir(&fi).to_path_buf(); let src_dir = find_src_dir(&fi).to_path_buf();
@ -70,7 +70,7 @@ pub(crate) fn global_anal(
source: source.clone(), source: source.clone(),
version, version,
}; };
//We will return this before the analisys has completed to enable completion //We will return this before the analysis has completed to enable completion
let doc_info_return = doc_info.clone(); let doc_info_return = doc_info.clone();
let documents_future = move || { let documents_future = move || {
let arena = Bump::new(); let arena = Bump::new();
@ -200,7 +200,7 @@ impl<'a> AnalyzedDocumentBuilder<'a> {
path: PathBuf, path: PathBuf,
source: Box<str>, source: Box<str>,
module_id: ModuleId, module_id: ModuleId,
version: u32, version: i32,
) -> AnalyzedDocument { ) -> AnalyzedDocument {
let subs; let subs;
let abilities; let abilities;
@ -295,24 +295,9 @@ struct AnalyzedModule {
module_id_to_url: ModuleIdToUrl, module_id_to_url: ModuleIdToUrl,
} }
//This needs a rewrite
/*
Requirements:
We should be able to get the latest results of analysis as well as the last good analysis resutl
We should be able to get the source, lineinfo and url of the document without having completed the anaylys
We need a way for functions that need the completed anaylisis to be completed to wait on that completion
*/
/*
access completed result
I could make a type that either contains an async task or the async result, when you access it it blocks and either awaits the task or returns the completed result.
I could make it so that it contains an async mutex and it holds the first lock open then it is created it releases the lock once the task is completed
*/
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct AnalyzedDocument { pub struct AnalyzedDocument {
//TOODO make not pugb
pub doc_info: DocInfo, pub doc_info: DocInfo,
pub analysys_result: AnalysisResult, pub analysys_result: AnalysisResult,
} }
@ -321,9 +306,25 @@ pub struct DocInfo {
pub url: Url, pub url: Url,
pub line_info: LineInfo, pub line_info: LineInfo,
pub source: String, pub source: String,
pub version: u32, pub version: i32,
} }
impl DocInfo { impl DocInfo {
fn debug_log_prefix(&self, offset: u32) {
eprintln!("prefix source{:?}", self.source);
let last_few = self
.source
.split_at((offset - 5) as usize)
.1
.split_at((offset + 5) as usize)
.0;
let splitter = last_few.split_at(5);
eprintln!(
"starting to get completion items at offset:{:?} content:: '{:?}|{:?}'",
offset, splitter.0, splitter.1
);
}
fn whole_document_range(&self) -> Range { fn whole_document_range(&self) -> Range {
let start = Position::new(0, 0); let start = Position::new(0, 0);
let end = Position::new(self.line_info.num_lines(), 0); let end = Position::new(self.line_info.num_lines(), 0);
@ -333,23 +334,6 @@ impl DocInfo {
let position = position.to_roc_position(&self.line_info); let position = position.to_roc_position(&self.line_info);
let offset = position.offset; let offset = position.offset;
let source = self.source.as_bytes().split_at(offset as usize).0; let source = self.source.as_bytes().split_at(offset as usize).0;
// writeln!(std::io::stderr(), "prefix source{:?}", self.source);
// let last_few = self
// .source
// .split_at((offset - 5) as usize)
// .1
// .split_at((offset + 5) as usize)
// .0;
// let splitter = last_few.split_at(5);
// writeln!(
// std::io::stderr(),
// "starting to get completion items at offset:{:?} content:: '{:?}|{:?}'",
// offset,
// splitter.0,
// splitter.1
// );
let mut symbol = source let mut symbol = source
.iter() .iter()
.rev() .rev()
@ -410,9 +394,6 @@ impl AnalyzedDocument {
fn module(&self) -> Option<&AnalyzedModule> { fn module(&self) -> Option<&AnalyzedModule> {
self.analysys_result.module.as_ref() self.analysys_result.module.as_ref()
} }
pub fn doc_info(&self) -> &DocInfo {
&self.doc_info
}
fn location(&self, range: Range) -> Location { fn location(&self, range: Range) -> Location {
Location { Location {
@ -488,25 +469,19 @@ impl AnalyzedDocument {
&self, &self,
position: Position, position: Position,
latest_doc: &DocInfo, latest_doc: &DocInfo,
symbol_prefix: String,
) -> Option<Vec<CompletionItem>> { ) -> Option<Vec<CompletionItem>> {
let symbol_prefix = latest_doc.get_prefix_at_position(position); let symbol_prefix = latest_doc.get_prefix_at_position(position);
writeln!( eprintln!(
std::io::stderr(),
"starting to get completion items for prefix: {:?} docVersion:{:?}", "starting to get completion items for prefix: {:?} docVersion:{:?}",
symbol_prefix, symbol_prefix, latest_doc.version
latest_doc.version
); );
let len_diff = latest_doc.source.len() as i32 - self.doc_info.source.len() as i32; let len_diff = latest_doc.source.len() as i32 - self.doc_info.source.len() as i32;
let mut position = position.to_roc_position(&latest_doc.line_info); //We offset the position because we need the position to be in the correct scope in the most recently parsed version of the source. The quick and dirty method is to just remove the difference in lenght between the source files from the offset. This could cause issues, but is very easy
//TODO: this is kind of a hack and should be removed once we can do some minimal parsing without full type checking //TODO: this is kind of a hack and should be removed once we can do some minimal parsing without full type checking
let mut position = position.to_roc_position(&latest_doc.line_info);
position.offset = (position.offset as i32 - len_diff - 1) as u32; position.offset = (position.offset as i32 - len_diff - 1) as u32;
writeln!( eprintln!("completion offset: {:?}", position.offset);
std::io::stderr(),
"completion offset: {:?}",
position.offset
);
let AnalyzedModule { let AnalyzedModule {
module_id, module_id,
@ -535,7 +510,7 @@ impl AnalyzedDocument {
module_id, module_id,
interns, interns,
); );
writeln!(std::io::stderr(), "got completions: "); eprintln!("got completions: ");
Some(completions) Some(completions)
} }
} }

View file

@ -1,5 +1,3 @@
use std::io::Write;
use roc_can::{ use roc_can::{
def::Def, def::Def,
expr::{Declarations, Expr, WhenBranch}, expr::{Declarations, Expr, WhenBranch},
@ -62,7 +60,7 @@ impl Visitor for CompletionVisitor<'_> {
} }
impl CompletionVisitor<'_> { impl CompletionVisitor<'_> {
fn extract_defs(&mut self, def: &Def) -> Vec<(Symbol, Variable)> { fn extract_defs(&mut self, def: &Def) -> Vec<(Symbol, Variable)> {
writeln!(std::io::stderr(), "completion begin"); eprintln!("completion begin");
def.pattern_vars def.pattern_vars
.iter() .iter()
.map(|(symbol, var)| (symbol.clone(), var.clone())) .map(|(symbol, var)| (symbol.clone(), var.clone()))
@ -70,54 +68,32 @@ impl CompletionVisitor<'_> {
} }
fn expression_defs(&self, expr: &Expr) -> Vec<(Symbol, Variable)> { fn expression_defs(&self, expr: &Expr) -> Vec<(Symbol, Variable)> {
match expr { match expr {
// Expr::Num(_, _, _, _) => todo!(), Expr::When {
// Expr::Int(_, _, _, _, _) => todo!(), expr_var, branches, ..
// Expr::Float(_, _, _, _, _) => todo!(), } => {
// Expr::Str(_) => todo!(), let out: Vec<_> = branches
// Expr::SingleQuote(_, _, _, _) => todo!(), .iter()
// Expr::List { elem_var, loc_elems } => todo!(), .flat_map(
// Expr::IngestedFile(_, _, _) => todo!(), |WhenBranch {
// Expr::Var(_, _) => todo!(), patterns, value, ..
// Expr::AbilityMember(_, _, _) => todo!(), }| {
Expr::When { expr_var, branches,.. } => { if value.region.contains_pos(self.position) {
patterns
let out:Vec<_> = .iter()
branches.iter().flat_map(|WhenBranch{ patterns, value, ..}|{ .flat_map(|pattern| {
if value.region.contains_pos(self.position) { //We use the expression var here because if the pattern is an identifier then it must have the type of the expession given to the when is block
patterns.iter().flat_map(|pattern|{ self.patterns(&pattern.pattern.value, expr_var)
//We use the expression var here because if the pattern is an identifier then it must have the type of the expession given to the when is block })
self.patterns(&pattern.pattern.value,expr_var)}).collect() .collect()
} } else {
else{ vec![]
vec![]} }
}).collect(); },
)
.collect();
out out
}, }
// Expr::If { cond_var, branch_var, branches, final_else } => todo!(), _ => vec![],
_=>vec![]
// Expr::LetRec(_, _, _) => todo!(),
// Expr::LetNonRec(_, _) => todo!(),
// Expr::Call(_, _, _) => todo!(),
// Expr::RunLowLevel { op, args, ret_var } => todo!(),
// Expr::ForeignCall { foreign_symbol, args, ret_var } => todo!(),
// Expr::Closure(_) => todo!(),
// Expr::Record { record_var, fields } => todo!(),
// Expr::EmptyRecord => todo!(),
// Expr::Tuple { tuple_var, elems } => todo!(),
// Expr::Crash { msg, ret_var } => todo!(),
// Expr::RecordAccess { record_var, ext_var, field_var, loc_expr, field } => todo!(),
// Expr::RecordAccessor(_) => todo!(),
// Expr::TupleAccess { tuple_var, ext_var, elem_var, loc_expr, index } => todo!(),
// Expr::RecordUpdate { record_var, ext_var, symbol, updates } => todo!(),
// Expr::Tag { tag_union_var, ext_var, name, arguments } => todo!(),
// Expr::ZeroArgumentTag { closure_name, variant_var, ext_var, name } => todo!(),
// Expr::OpaqueRef { opaque_var, name, argument, specialized_def_type, type_arguments, lambda_set_variables } => todo!(),
// Expr::OpaqueWrapFunction(_) => todo!(),
// Expr::Expect { loc_condition, loc_continuation, lookups_in_cond } => todo!(),
// Expr::ExpectFx { loc_condition, loc_continuation, lookups_in_cond } => todo!(),
// Expr::Dbg { loc_message, loc_continuation, variable, symbol } => todo!(),
// Expr::TypedHole(_) => todo!(),
// Expr::RuntimeError(_) => todo!(),
} }
} }
@ -145,6 +121,7 @@ impl CompletionVisitor<'_> {
}) })
.collect() .collect()
} }
fn list_destructure( fn list_destructure(
&self, &self,
list_elems: &ListPatterns, list_elems: &ListPatterns,
@ -157,49 +134,20 @@ impl CompletionVisitor<'_> {
.flat_map(|a| self.patterns(&a.value, var)) .flat_map(|a| self.patterns(&a.value, var))
.collect() .collect()
} }
fn tag_destructure(&self, arguments: &[(Variable, Loc<Pattern>)]) -> Vec<(Symbol, Variable)> {
arguments
.iter()
.flat_map(|(var, pat)| self.patterns(&pat.value, var))
.collect()
}
fn as_pattern(&self, as_pat: &Pattern, as_symbol: Symbol) -> Option<(Symbol, Variable)> { fn as_pattern(&self, as_pat: &Pattern, as_symbol: Symbol) -> Option<(Symbol, Variable)> {
let var = match as_pat { let var = match as_pat {
Pattern::AppliedTag { Pattern::AppliedTag { whole_var, .. } => whole_var,
whole_var, Pattern::UnwrappedOpaque { whole_var, .. } => whole_var,
ext_var, Pattern::RecordDestructure { whole_var, .. } => whole_var,
tag_name, Pattern::TupleDestructure { whole_var, .. } => whole_var,
arguments, Pattern::List { list_var, .. } => list_var,
} => whole_var,
Pattern::UnwrappedOpaque {
whole_var,
opaque,
argument,
specialized_def_type,
type_arguments,
lambda_set_variables,
} => whole_var,
Pattern::RecordDestructure {
whole_var,
ext_var,
destructs,
} => whole_var,
Pattern::TupleDestructure {
whole_var,
ext_var,
destructs,
} => whole_var,
Pattern::List {
list_var,
elem_var,
patterns,
} => list_var,
// Pattern::NumLiteral(_, _, _, _) => todo!(),
// Pattern::IntLiteral(_, _, _, _, _) => todo!(),
// Pattern::FloatLiteral(_, _, _, _, _) => todo!(),
// Pattern::StrLiteral(_) => todo!(),
// Pattern::SingleQuote(_, _, _, _) => todo!(),
// Pattern::Underscore => todo!(),
// Pattern::AbilityMemberSpecialization { ident, specializes } => todo!(),
// Pattern::Shadowed(_, _, _) => todo!(),
// Pattern::OpaqueNotInScope(_) => todo!(),
// Pattern::UnsupportedPattern(_) => todo!(),
// Pattern::MalformedPattern(_, _) => todo!(),
_ => return None, _ => return None,
}; };
Some((as_symbol, var.clone())) Some((as_symbol, var.clone()))
@ -217,24 +165,12 @@ impl CompletionVisitor<'_> {
vec![] vec![]
} }
} }
// Pattern::AppliedTag { Pattern::AppliedTag { arguments, .. } => self.tag_destructure(arguments),
// whole_var, Pattern::UnwrappedOpaque { argument, .. } => {
// ext_var, self.patterns(&argument.1.value, &argument.0)
// tag_name, }
// arguments,
// } => whole_var,
// Pattern::UnwrappedOpaque {
// whole_var,
// opaque,
// argument,
// specialized_def_type,
// type_arguments,
// lambda_set_variables,
// } => whole_var,
Pattern::List { Pattern::List {
list_var, elem_var, patterns, ..
elem_var,
patterns,
} => self.list_destructure(patterns, elem_var), } => self.list_destructure(patterns, elem_var),
roc_can::pattern::Pattern::As(pat, symbol) => self roc_can::pattern::Pattern::As(pat, symbol) => self
.as_pattern(&pat.value, symbol.clone()) .as_pattern(&pat.value, symbol.clone())
@ -246,25 +182,6 @@ impl CompletionVisitor<'_> {
roc_can::pattern::Pattern::TupleDestructure { destructs, .. } => { roc_can::pattern::Pattern::TupleDestructure { destructs, .. } => {
self.tuple_destructs(destructs) self.tuple_destructs(destructs)
} }
// roc_can::pattern::Pattern::List {
// list_var,
// elem_var,
// patterns,
// } => todo!(),
// roc_can::pattern::Pattern::NumLiteral(_, _, _, _) => todo!(),
// roc_can::pattern::Pattern::IntLiteral(_, _, _, _, _) => todo!(),
// roc_can::pattern::Pattern::FloatLiteral(_, _, _, _, _) => todo!(),
// roc_can::pattern::Pattern::StrLiteral(_) => todo!(),
// roc_can::pattern::Pattern::SingleQuote(_, _, _, _) => todo!(),
// roc_can::pattern::Pattern::Underscore => todo!(),
// roc_can::pattern::Pattern::AbilityMemberSpecialization {
// ident,
// specializes,
// } => todo!(),
// roc_can::pattern::Pattern::Shadowed(_, _, _) => todo!(),
// roc_can::pattern::Pattern::OpaqueNotInScope(_) => todo!(),
// roc_can::pattern::Pattern::UnsupportedPattern(_) => todo!(),
// roc_can::pattern::Pattern::MalformedPattern(_, _) => todo!(),
_ => vec![], _ => vec![],
} }
} }
@ -327,10 +244,7 @@ fn get_completions<'a>(
visitor.visit_decls(decls); visitor.visit_decls(decls);
visitor.found_decls visitor.found_decls
} }
//TODO: this should be replaced with a more specific solution. I can likely use the variable type to figure out what the completion item kind is
// fn make_completion_item_var(subs:&Subs,symbol: &Symbol, var: &Variable) -> CompletionItem {
// make_completion_item(symbol, var, CompletionItemKind::VARIABLE)
// }
fn make_completion_item( fn make_completion_item(
subs: &mut Subs, subs: &mut Subs,
module_id: &ModuleId, module_id: &ModuleId,
@ -348,8 +262,7 @@ fn make_completion_item(
_ => CompletionItemKind::VARIABLE, _ => CompletionItemKind::VARIABLE,
}, },
a => { a => {
writeln!( eprintln!(
std::io::stderr(),
"No specific completionKind for variable type :{:?} defaulting to 'Variable'", "No specific completionKind for variable type :{:?} defaulting to 'Variable'",
a a
); );
@ -433,8 +346,7 @@ fn find_record_fields(var: Variable, subs: &mut Subs) -> Vec<(String, Variable)>
}) })
.collect(), .collect(),
Err(err) => { Err(err) => {
writeln!( eprintln!(
std::io::stderr(),
"WARN:Error getting record field types for completion{:?}", "WARN:Error getting record field types for completion{:?}",
err err
); );
@ -448,11 +360,7 @@ fn find_record_fields(var: Variable, subs: &mut Subs) -> Vec<(String, Variable)>
let elems: Vec<_> = match elems { let elems: Vec<_> = match elems {
Ok(elem) => elem.map(|(num, var)| (num.to_string(), var)).collect(), Ok(elem) => elem.map(|(num, var)| (num.to_string(), var)).collect(),
Err(err) => { Err(err) => {
writeln!( eprintln!("WARN:Error getting tuple elems for completion{:?}", err);
std::io::stderr(),
"WARN:Error getting tuple elems for completion{:?}",
err
);
vec![] vec![]
} }
}; };
@ -460,8 +368,7 @@ fn find_record_fields(var: Variable, subs: &mut Subs) -> Vec<(String, Variable)>
} }
_ => { _ => {
writeln!( eprintln!(
std::io::stderr(),
"WARN: Trying to get field completion for a type that is not a record ", "WARN: Trying to get field completion for a type that is not a record ",
); );
vec![] vec![]
@ -470,22 +377,28 @@ fn find_record_fields(var: Variable, subs: &mut Subs) -> Vec<(String, Variable)>
roc_types::subs::Content::Error => { roc_types::subs::Content::Error => {
//This is caused by typechecking our partially typed variable name causing the typechecking to be confused as the type of the parent variable //This is caused by typechecking our partially typed variable name causing the typechecking to be confused as the type of the parent variable
//TODO! ideally i could recover using some previous typecheck result that isn't broken //TODO! ideally i could recover using some previous typecheck result that isn't broken
writeln!( eprintln!("ERROR: variable type of record was of type error cannot access field",);
std::io::stderr(),
"ERROR: variable type of record was of type error cannot access field",
);
vec![] vec![]
} }
a => { a => {
writeln!( eprintln!("variable before field was unsuported type:{:?}", a);
std::io::stderr(),
"variable before field was unsuported type:{:?}",
a
);
vec![] vec![]
} }
} }
} }
///Splits a completion prefix for a field into it's components
//eg a.b.c.d->("a",["b","c"],"d")
fn get_field_completion_parts(symbol_prefix: &String) -> Option<(String, Vec<String>, String)> {
let parts: Vec<_> = symbol_prefix.split('.').collect();
let (variable, fields) = parts.split_first()?;
let (field, middle) = match fields.split_last() {
Some(a) => (a.0.to_string(), a.1.iter().map(|x| x.to_string()).collect()),
None => ("".to_string(), vec![]),
};
Some((variable.to_string(), middle, field))
}
pub fn field_completion( pub fn field_completion(
position: Position, position: Position,
symbol_prefix: String, symbol_prefix: String,
@ -494,26 +407,12 @@ pub fn field_completion(
subs: &mut Subs, subs: &mut Subs,
module_id: &ModuleId, module_id: &ModuleId,
) -> Option<Vec<CompletionItem>> { ) -> Option<Vec<CompletionItem>> {
writeln!(std::io::stderr(), "getting record field completions: "); eprintln!("getting record field completions: ");
let mut parts: Vec<_> = symbol_prefix.split('.').collect(); let (variable, middle, field) = get_field_completion_parts(&symbol_prefix)?;
let (variable, fields) = parts.split_first_mut()?;
let mut empty = ""; eprintln!(
let (field, middle) = match fields.split_last_mut() {
Some(a) => a,
None => {
let out: &mut [&str] = [].as_mut_slice();
(&mut empty, out)
}
};
writeln!(
std::io::stderr(),
"getting record field completions: variable:{:?} field{:?} middle{:?} ", "getting record field completions: variable:{:?} field{:?} middle{:?} ",
variable, variable, field, middle
field,
middle
); );
//get the variable from within the region //get the variable from within the region
//TODO: this is kind of just a hack. We are gettting all the completions and seeing if any match the part before the dot as a way to get the Variable type of the variable before the dot. I imagine there are much faster ways to do this //TODO: this is kind of just a hack. We are gettting all the completions and seeing if any match the part before the dot as a way to get the Variable type of the variable before the dot. I imagine there are much faster ways to do this
@ -525,10 +424,7 @@ pub fn field_completion(
//We iterate through all the intermediate chunks eg var.field1.field2.field3 this iterates through fields until we get to field2, becuase it's second last //We iterate through all the intermediate chunks eg var.field1.field2.field3 this iterates through fields until we get to field2, becuase it's second last
let second_last = middle.iter().fold(completion, |state, a| { let second_last = middle.iter().fold(completion, |state, a| {
let fields_vars = find_record_fields(state.1, subs); let fields_vars = find_record_fields(state.1, subs);
match fields_vars match fields_vars.into_iter().find(|field| a == &field.0) {
.into_iter()
.find(|field| a.to_string() == field.0)
{
None => state, None => state,
Some(a) => a, Some(a) => a,
} }

View file

@ -0,0 +1 @@

View file

@ -1,29 +1,13 @@
use std::{ use std::{collections::HashMap, sync::Arc};
cell::OnceCell,
collections::HashMap,
future::Future,
io::{stderr, Write},
ops::Deref,
rc::Rc,
sync::Arc,
};
use tokio::{ use tokio::sync::{Mutex, MutexGuard};
io::AsyncRead,
sync::{Mutex, MutexGuard, RwLock, RwLockReadGuard, RwLockWriteGuard},
task::JoinHandle,
};
use tower_lsp::lsp_types::{ use tower_lsp::lsp_types::{
notification::Notification, CompletionResponse, Diagnostic, GotoDefinitionResponse, Hover, CompletionResponse, Diagnostic, GotoDefinitionResponse, Hover, Position, SemanticTokensResult,
Position, SemanticTokensResult, TextEdit, Url, TextEdit, Url,
}; };
use crate::analysis::{global_anal, AnalysisResult, AnalyzedDocument, DocInfo}; use crate::analysis::{AnalyzedDocument, DocInfo};
pub(crate) enum DocumentChange {
Modified(Url, String, u32),
Closed(Url),
}
#[derive(Debug)] #[derive(Debug)]
pub(crate) struct LatestDocument { pub(crate) struct LatestDocument {
info: DocInfo, info: DocInfo,
@ -74,24 +58,6 @@ pub(crate) struct DocumentPair {
last_good_document: Arc<AnalyzedDocument>, last_good_document: Arc<AnalyzedDocument>,
} }
// pub(crate) fn new(latest: AnalyzedDocument, last_good: AnalyzedDocument) -> DocumentPair {
// DocumentPair {
// //TODO not sure if i should actually be cloning here?
// latest_document: (latest.doc_info, Arc::new(RwLock::new(last_good.clone()))),
// last_good_document: last_good,
// }
// }
// pub(crate) fn new_latest_type_checked(latest_doc: AnalyzedDocument) -> DocumentPair {
// DocumentPair {
// //TODO not sure if i should actually be cloning here?
// latest_document: (
// latest_doc.doc_info.clone(),
// Arc::new(RwLock::new(latest_doc.clone())),
// ),
// last_good_document: latest_doc,
// }
// }
#[derive(Debug, Default)] #[derive(Debug, Default)]
pub(crate) struct Registry { pub(crate) struct Registry {
documents: Mutex<HashMap<Url, DocumentPair>>, documents: Mutex<HashMap<Url, DocumentPair>>,
@ -104,13 +70,6 @@ impl Registry {
) { ) {
let url = document.url().clone(); let url = document.url().clone();
let document = Arc::new(document); let document = Arc::new(document);
// writeln!(
// std::io::stderr(),
// "updating doc{:?}. version:{:?}",
// &url,
// &document.doc_info.version
// );
let latest_doc = LatestDocument::new_initialised(document.clone()); let latest_doc = LatestDocument::new_initialised(document.clone());
match documents.get_mut(&url) { match documents.get_mut(&url) {
Some(old_doc) => { Some(old_doc) => {
@ -143,16 +102,10 @@ impl Registry {
} }
} }
pub async fn apply_change(&self, analysed_docs: Vec<AnalyzedDocument>) -> () { pub async fn apply_changes(&self, analysed_docs: Vec<AnalyzedDocument>) -> () {
writeln!(
std::io::stderr(),
"updated the latest document with docinfo"
);
let mut documents = self.documents.lock().await; let mut documents = self.documents.lock().await;
writeln!( eprintln!(
std::io::stderr(), "finised doc analysis updating docs {:?}",
"finised doc analasys updating docs {:?}",
analysed_docs analysed_docs
.iter() .iter()
.map(|a| a.doc_info.url.to_string()) .map(|a| a.doc_info.url.to_string())
@ -169,9 +122,9 @@ impl Registry {
let doc = lock.get_mut(&url); let doc = lock.get_mut(&url);
match doc { match doc {
Some(a) => { Some(a) => {
writeln!( eprintln!(
std::io::stderr(), "set the docInfo for {:?} to version:{:?}",
"set the docInfo to version:{:?}", url.as_str(),
partial.version partial.version
); );
@ -234,16 +187,13 @@ impl Registry {
) -> Option<CompletionResponse> { ) -> Option<CompletionResponse> {
let lock = self.documents.lock().await; let lock = self.documents.lock().await;
let pair = lock.get(url)?; let pair = lock.get(url)?;
writeln!(stderr(), "got document"); eprintln!("got document");
let latest_doc_info = &pair.latest_document.info; let latest_doc_info = &pair.latest_document.info;
writeln!(stderr(), "latest version:{:?} ", latest_doc_info.version); eprintln!("latest version:{:?} ", latest_doc_info.version);
let symbol_prefix = latest_doc_info.get_prefix_at_position(position); let completions = pair
.last_good_document
//this strategy probably won't work for record fields .completion_items(position, &latest_doc_info)?;
let completions =
pair.last_good_document
.completion_items(position, &latest_doc_info, symbol_prefix)?;
Some(CompletionResponse::Array(completions)) Some(CompletionResponse::Array(completions))
} }

View file

@ -1,17 +1,14 @@
use analysis::HIGHLIGHT_TOKENS_LEGEND; use analysis::HIGHLIGHT_TOKENS_LEGEND;
use registry::{DocumentChange, Registry}; use registry::Registry;
use std::future::Future; use std::future::Future;
use std::io::Write;
use std::sync::{Arc, OnceLock}; use std::sync::Arc;
use std::time::Duration; use std::time::Duration;
use tokio::sync::{oneshot, Mutex, MutexGuard, RwLock};
use tokio::task::{JoinError, JoinHandle};
use tower_lsp::jsonrpc::Result; use tower_lsp::jsonrpc::Result;
use tower_lsp::lsp_types::request::RegisterCapability;
use tower_lsp::lsp_types::*; use tower_lsp::lsp_types::*;
use tower_lsp::{Client, LanguageServer, LspService, Server}; use tower_lsp::{Client, LanguageServer, LspService, Server};
use crate::analysis::global_anal; use crate::analysis::global_analysis;
mod analysis; mod analysis;
mod convert; mod convert;
@ -25,7 +22,7 @@ struct RocLs {
struct Inner { struct Inner {
client: Client, client: Client,
registry: Registry, registry: Registry,
change_handle: parking_lot::Mutex<( change_cancel_handle: parking_lot::Mutex<(
tokio::sync::watch::Sender<i32>, tokio::sync::watch::Sender<i32>,
tokio::sync::watch::Receiver<i32>, tokio::sync::watch::Receiver<i32>,
)>, )>,
@ -41,7 +38,7 @@ impl RocLs {
inner: Arc::new(Inner { inner: Arc::new(Inner {
client, client,
registry: Registry::default(), registry: Registry::default(),
change_handle: parking_lot::Mutex::new(tokio::sync::watch::channel(0)), change_cancel_handle: parking_lot::Mutex::new(tokio::sync::watch::channel(0)),
///Used for identifying if the intial stage of a document update is complete ///Used for identifying if the intial stage of a document update is complete
documents_updating: tokio::sync::Semaphore::new(SEMLIMIT as usize), documents_updating: tokio::sync::Semaphore::new(SEMLIMIT as usize),
}), }),
@ -49,7 +46,8 @@ impl RocLs {
} }
///Wait for all the semaphores associated with an in-progress document_info update to be released ///Wait for all the semaphores associated with an in-progress document_info update to be released
async fn wait_for_changes(&self) { async fn wait_for_changes(&self) {
self.inner.documents_updating.acquire_many(SEMLIMIT).await; //We don't actually need to return the permit, all we want is to have it momenterily which guarentees that any updates started before the call to this are complete
drop(self.inner.documents_updating.acquire_many(SEMLIMIT).await.expect("semaphore permits could not be aquired for the document updating process. This is unrecoverable"));
} }
pub fn capabilities() -> ServerCapabilities { pub fn capabilities() -> ServerCapabilities {
let text_document_sync = TextDocumentSyncCapability::Options( let text_document_sync = TextDocumentSyncCapability::Options(
@ -105,27 +103,17 @@ impl RocLs {
/// Records a document content change. /// Records a document content change.
async fn change(&self, fi: Url, text: String, version: i32) { async fn change(&self, fi: Url, text: String, version: i32) {
writeln!(std::io::stderr(), "starting change"); eprintln!("starting change");
let updating_doc_info = self.inner.documents_updating.acquire().await.unwrap();
let mut new_change = { let (updating_doc_info, mut new_change) = self.change_preamble(version).await;
let change_handle = self.inner.change_handle.lock();
//This will cancel any other onging changes in favour of this one
change_handle
.0
.send(version)
.expect("change_handle disposed, this shouldn't happen");
let mut watched = change_handle.1.clone();
drop(watched.borrow_and_update());
watched
};
//We will wait just a tiny amount of time to catch any requests that come in at the exact same time
tokio::time::sleep(Duration::from_millis(20)).await; tokio::time::sleep(Duration::from_millis(20)).await;
if (new_change.has_changed().unwrap()) { if new_change.has_changed().unwrap() {
writeln!(std::io::stderr(), "newer task started almost immediately"); eprintln!("newer task started almost immediately");
return; return;
} }
writeln!(std::io::stderr(), "finished checking for cancellation");
let (results, partial) = global_anal(fi.clone(), text, version as u32); eprintln!("finished checking for cancellation");
let (results, partial) = global_analysis(fi.clone(), text, version);
self.inner self.inner
.registry .registry
@ -133,64 +121,76 @@ impl RocLs {
.await; .await;
drop(updating_doc_info); drop(updating_doc_info);
writeln!(std::io::stderr(), "finished applying"); eprintln!("finished updating docinfo, starting analysis ",);
let inner_ref = self.inner.clone(); let inner_ref = self.inner.clone();
let handle: JoinHandle<core::result::Result<&str, JoinError>> = let handle = async {
tokio::task::spawn(async move { let results = match tokio::task::spawn_blocking(results).await {
let results = tokio::task::spawn_blocking(results).await?; Err(e) => return Err(format!("document analysis failed. reason:{:?}", e)),
Ok(a) => a,
};
inner_ref.registry().apply_change(results).await; inner_ref.registry().apply_changes(results).await;
Ok("okay") Ok(())
}); };
writeln!(std::io::stderr(), "waiting on analisys or cancel"); eprintln!("waiting on analysis or cancel");
//The analysis task can be cancelled by another change coming in which will update the watched variable //The analysis task can be cancelled by another change coming in which will update the watched variable
let cancelled = tokio::select! { let finished = tokio::select! {
a=handle=>{ a=handle=>a,
match a{ _=new_change.changed()=>Err("Cancellation triggered from change".to_string())
Err(a)=>
{
writeln!(std::io::stderr(), "error in task{:?}",a);
true},
Ok(_)=>false
}
},
_=new_change.changed()=>true
}; };
if cancelled { match finished {
writeln!(std::io::stderr(), "cancelled change"); Err(e) => {
return; eprintln!("cancelled change. Reason:{:?}", e);
return;
}
Ok(_) => (),
} }
writeln!(std::io::stderr(), "applied_change getting diagnostics"); eprintln!("applied_change getting and returning diagnostics");
//We do this to briefly yeild
let diagnostics = self.inner.registry().diagnostics(&fi).await; let diagnostics = self.inner.registry().diagnostics(&fi).await;
writeln!(std::io::stderr(), "applied_change returning diagnostics");
self.inner self.inner
.client .client
.publish_diagnostics(fi, diagnostics, Some(version)) .publish_diagnostics(fi, diagnostics, Some(version))
.await; .await;
} }
/// Does some bookkeeping before the change.
///aquires a semphore permit to let future functions know that there is a change ongoing
///sends a message to the watched change_handle to cancel other changes
///returns a copy of the watcher to use in our own cancellation
async fn change_preamble(
&self,
version: i32,
) -> (
tokio::sync::SemaphorePermit<'_>,
tokio::sync::watch::Receiver<i32>,
) {
let updating_doc_info = self.inner.documents_updating.acquire().await.unwrap();
let new_change = {
let cancel_handle = self.inner.change_cancel_handle.lock();
//This will cancel any other onging changes in favour of this one
cancel_handle
.0
.send(version)
.expect("change_handle disposed, this shouldn't happen");
let mut watched = cancel_handle.1.clone();
watched.borrow_and_update();
watched
};
(updating_doc_info, new_change)
}
} }
impl Inner { impl Inner {
fn registry(&self) -> &Registry { fn registry(&self) -> &Registry {
&self.registry &self.registry
} }
fn registry_write(&mut self) -> &mut Registry {
&mut self.registry
}
async fn close(&self, fi: Url) { async fn close(&self, _fi: Url) {
// self.registry() ()
// .apply_change(DocumentChange::Closed(fi))
// .await;
} }
} }
@ -304,16 +304,15 @@ impl LanguageServer for RocLs {
} }
async fn completion(&self, params: CompletionParams) -> Result<Option<CompletionResponse>> { async fn completion(&self, params: CompletionParams) -> Result<Option<CompletionResponse>> {
let doc = params.text_document_position; let doc = params.text_document_position;
writeln!(std::io::stderr(), "starting completion"); eprintln!("starting completion");
writeln!( eprintln!(
std::io::stderr(),
"permits::{:?} ", "permits::{:?} ",
self.inner.documents_updating.available_permits() self.inner.documents_updating.available_permits()
); );
//We need to wait untill any changes that were in progress when we requested completion have applied //We need to wait untill any changes that were in progress when we requested completion have applied
self.wait_for_changes().await; self.wait_for_changes().await;
writeln!(std::io::stderr(), "waited for doc update to get sorted "); eprintln!("waited for doc update to get sorted ");
let res = panic_wrapper_async(|| async { let res = panic_wrapper_async(|| async {
self.inner self.inner
@ -323,7 +322,7 @@ impl LanguageServer for RocLs {
}) })
.await; .await;
writeln!(std::io::stderr(), "finished completion"); eprintln!("finished completion");
res res
} }
} }