mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-30 23:31:12 +00:00
completed refactor
added cargo lock Signed-off-by: faldor20 <eli.jambu@yahoo.com>
This commit is contained in:
parent
58dec9af28
commit
a2c8acd9ac
6 changed files with 217 additions and 362 deletions
38
Cargo.lock
generated
38
Cargo.lock
generated
|
@ -3060,7 +3060,6 @@ dependencies = [
|
|||
"roc_derive_key",
|
||||
"roc_error_macros",
|
||||
"roc_exhaustive",
|
||||
"roc_load",
|
||||
"roc_module",
|
||||
"roc_packaging",
|
||||
"roc_parse",
|
||||
|
@ -3073,7 +3072,6 @@ dependencies = [
|
|||
"roc_types",
|
||||
"roc_unify",
|
||||
"tempfile",
|
||||
"test_solve_helpers",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -3096,6 +3094,42 @@ dependencies = [
|
|||
"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]]
|
||||
name = "roc_std"
|
||||
version = "0.0.1"
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
use std::{
|
||||
collections::HashMap,
|
||||
future::Future,
|
||||
io::Write,
|
||||
path::{Path, PathBuf},
|
||||
};
|
||||
|
||||
|
@ -55,10 +53,12 @@ fn format_var_type(
|
|||
subs.rollback_to(snapshot);
|
||||
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: String,
|
||||
version: u32,
|
||||
version: i32,
|
||||
) -> (impl FnOnce() -> Vec<AnalyzedDocument>, DocInfo) {
|
||||
let fi = source_url.to_file_path().unwrap();
|
||||
let src_dir = find_src_dir(&fi).to_path_buf();
|
||||
|
@ -70,7 +70,7 @@ pub(crate) fn global_anal(
|
|||
source: source.clone(),
|
||||
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 documents_future = move || {
|
||||
let arena = Bump::new();
|
||||
|
@ -200,7 +200,7 @@ impl<'a> AnalyzedDocumentBuilder<'a> {
|
|||
path: PathBuf,
|
||||
source: Box<str>,
|
||||
module_id: ModuleId,
|
||||
version: u32,
|
||||
version: i32,
|
||||
) -> AnalyzedDocument {
|
||||
let subs;
|
||||
let abilities;
|
||||
|
@ -295,24 +295,9 @@ struct AnalyzedModule {
|
|||
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)]
|
||||
|
||||
pub struct AnalyzedDocument {
|
||||
//TOODO make not pugb
|
||||
pub doc_info: DocInfo,
|
||||
pub analysys_result: AnalysisResult,
|
||||
}
|
||||
|
@ -321,9 +306,25 @@ pub struct DocInfo {
|
|||
pub url: Url,
|
||||
pub line_info: LineInfo,
|
||||
pub source: String,
|
||||
pub version: u32,
|
||||
pub version: i32,
|
||||
}
|
||||
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 {
|
||||
let start = Position::new(0, 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 offset = position.offset;
|
||||
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
|
||||
.iter()
|
||||
.rev()
|
||||
|
@ -410,9 +394,6 @@ impl AnalyzedDocument {
|
|||
fn module(&self) -> Option<&AnalyzedModule> {
|
||||
self.analysys_result.module.as_ref()
|
||||
}
|
||||
pub fn doc_info(&self) -> &DocInfo {
|
||||
&self.doc_info
|
||||
}
|
||||
|
||||
fn location(&self, range: Range) -> Location {
|
||||
Location {
|
||||
|
@ -488,25 +469,19 @@ impl AnalyzedDocument {
|
|||
&self,
|
||||
position: Position,
|
||||
latest_doc: &DocInfo,
|
||||
symbol_prefix: String,
|
||||
) -> Option<Vec<CompletionItem>> {
|
||||
let symbol_prefix = latest_doc.get_prefix_at_position(position);
|
||||
writeln!(
|
||||
std::io::stderr(),
|
||||
eprintln!(
|
||||
"starting to get completion items for prefix: {:?} docVersion:{:?}",
|
||||
symbol_prefix,
|
||||
latest_doc.version
|
||||
symbol_prefix, latest_doc.version
|
||||
);
|
||||
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
|
||||
let mut position = position.to_roc_position(&latest_doc.line_info);
|
||||
position.offset = (position.offset as i32 - len_diff - 1) as u32;
|
||||
writeln!(
|
||||
std::io::stderr(),
|
||||
"completion offset: {:?}",
|
||||
position.offset
|
||||
);
|
||||
eprintln!("completion offset: {:?}", position.offset);
|
||||
|
||||
let AnalyzedModule {
|
||||
module_id,
|
||||
|
@ -535,7 +510,7 @@ impl AnalyzedDocument {
|
|||
module_id,
|
||||
interns,
|
||||
);
|
||||
writeln!(std::io::stderr(), "got completions: ");
|
||||
eprintln!("got completions: ");
|
||||
Some(completions)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
use std::io::Write;
|
||||
|
||||
use roc_can::{
|
||||
def::Def,
|
||||
expr::{Declarations, Expr, WhenBranch},
|
||||
|
@ -62,7 +60,7 @@ impl Visitor for CompletionVisitor<'_> {
|
|||
}
|
||||
impl CompletionVisitor<'_> {
|
||||
fn extract_defs(&mut self, def: &Def) -> Vec<(Symbol, Variable)> {
|
||||
writeln!(std::io::stderr(), "completion begin");
|
||||
eprintln!("completion begin");
|
||||
def.pattern_vars
|
||||
.iter()
|
||||
.map(|(symbol, var)| (symbol.clone(), var.clone()))
|
||||
|
@ -70,54 +68,32 @@ impl CompletionVisitor<'_> {
|
|||
}
|
||||
fn expression_defs(&self, expr: &Expr) -> Vec<(Symbol, Variable)> {
|
||||
match expr {
|
||||
// Expr::Num(_, _, _, _) => todo!(),
|
||||
// Expr::Int(_, _, _, _, _) => todo!(),
|
||||
// Expr::Float(_, _, _, _, _) => todo!(),
|
||||
// Expr::Str(_) => todo!(),
|
||||
// Expr::SingleQuote(_, _, _, _) => todo!(),
|
||||
// Expr::List { elem_var, loc_elems } => todo!(),
|
||||
// Expr::IngestedFile(_, _, _) => todo!(),
|
||||
// Expr::Var(_, _) => todo!(),
|
||||
// Expr::AbilityMember(_, _, _) => todo!(),
|
||||
Expr::When { expr_var, branches,.. } => {
|
||||
|
||||
let out:Vec<_> =
|
||||
branches.iter().flat_map(|WhenBranch{ patterns, value, ..}|{
|
||||
Expr::When {
|
||||
expr_var, branches, ..
|
||||
} => {
|
||||
let out: Vec<_> = branches
|
||||
.iter()
|
||||
.flat_map(
|
||||
|WhenBranch {
|
||||
patterns, value, ..
|
||||
}| {
|
||||
if value.region.contains_pos(self.position) {
|
||||
patterns.iter().flat_map(|pattern|{
|
||||
patterns
|
||||
.iter()
|
||||
.flat_map(|pattern| {
|
||||
//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()
|
||||
self.patterns(&pattern.pattern.value, expr_var)
|
||||
})
|
||||
.collect()
|
||||
} else {
|
||||
vec![]
|
||||
}
|
||||
else{
|
||||
vec![]}
|
||||
}).collect();
|
||||
out
|
||||
},
|
||||
// Expr::If { cond_var, branch_var, branches, final_else } => todo!(),
|
||||
_=>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!(),
|
||||
)
|
||||
.collect();
|
||||
out
|
||||
}
|
||||
_ => vec![],
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -145,6 +121,7 @@ impl CompletionVisitor<'_> {
|
|||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn list_destructure(
|
||||
&self,
|
||||
list_elems: &ListPatterns,
|
||||
|
@ -157,49 +134,20 @@ impl CompletionVisitor<'_> {
|
|||
.flat_map(|a| self.patterns(&a.value, var))
|
||||
.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)> {
|
||||
let var = match as_pat {
|
||||
Pattern::AppliedTag {
|
||||
whole_var,
|
||||
ext_var,
|
||||
tag_name,
|
||||
arguments,
|
||||
} => 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!(),
|
||||
Pattern::AppliedTag { whole_var, .. } => whole_var,
|
||||
Pattern::UnwrappedOpaque { whole_var, .. } => whole_var,
|
||||
Pattern::RecordDestructure { whole_var, .. } => whole_var,
|
||||
Pattern::TupleDestructure { whole_var, .. } => whole_var,
|
||||
Pattern::List { list_var, .. } => list_var,
|
||||
_ => return None,
|
||||
};
|
||||
Some((as_symbol, var.clone()))
|
||||
|
@ -217,24 +165,12 @@ impl CompletionVisitor<'_> {
|
|||
vec![]
|
||||
}
|
||||
}
|
||||
// Pattern::AppliedTag {
|
||||
// whole_var,
|
||||
// ext_var,
|
||||
// tag_name,
|
||||
// arguments,
|
||||
// } => whole_var,
|
||||
// Pattern::UnwrappedOpaque {
|
||||
// whole_var,
|
||||
// opaque,
|
||||
// argument,
|
||||
// specialized_def_type,
|
||||
// type_arguments,
|
||||
// lambda_set_variables,
|
||||
// } => whole_var,
|
||||
Pattern::AppliedTag { arguments, .. } => self.tag_destructure(arguments),
|
||||
Pattern::UnwrappedOpaque { argument, .. } => {
|
||||
self.patterns(&argument.1.value, &argument.0)
|
||||
}
|
||||
Pattern::List {
|
||||
list_var,
|
||||
elem_var,
|
||||
patterns,
|
||||
elem_var, patterns, ..
|
||||
} => self.list_destructure(patterns, elem_var),
|
||||
roc_can::pattern::Pattern::As(pat, symbol) => self
|
||||
.as_pattern(&pat.value, symbol.clone())
|
||||
|
@ -246,25 +182,6 @@ impl CompletionVisitor<'_> {
|
|||
roc_can::pattern::Pattern::TupleDestructure { 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![],
|
||||
}
|
||||
}
|
||||
|
@ -327,10 +244,7 @@ fn get_completions<'a>(
|
|||
visitor.visit_decls(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(
|
||||
subs: &mut Subs,
|
||||
module_id: &ModuleId,
|
||||
|
@ -348,8 +262,7 @@ fn make_completion_item(
|
|||
_ => CompletionItemKind::VARIABLE,
|
||||
},
|
||||
a => {
|
||||
writeln!(
|
||||
std::io::stderr(),
|
||||
eprintln!(
|
||||
"No specific completionKind for variable type :{:?} defaulting to 'Variable'",
|
||||
a
|
||||
);
|
||||
|
@ -433,8 +346,7 @@ fn find_record_fields(var: Variable, subs: &mut Subs) -> Vec<(String, Variable)>
|
|||
})
|
||||
.collect(),
|
||||
Err(err) => {
|
||||
writeln!(
|
||||
std::io::stderr(),
|
||||
eprintln!(
|
||||
"WARN:Error getting record field types for completion{:?}",
|
||||
err
|
||||
);
|
||||
|
@ -448,11 +360,7 @@ fn find_record_fields(var: Variable, subs: &mut Subs) -> Vec<(String, Variable)>
|
|||
let elems: Vec<_> = match elems {
|
||||
Ok(elem) => elem.map(|(num, var)| (num.to_string(), var)).collect(),
|
||||
Err(err) => {
|
||||
writeln!(
|
||||
std::io::stderr(),
|
||||
"WARN:Error getting tuple elems for completion{:?}",
|
||||
err
|
||||
);
|
||||
eprintln!("WARN:Error getting tuple elems for completion{:?}", err);
|
||||
vec![]
|
||||
}
|
||||
};
|
||||
|
@ -460,8 +368,7 @@ fn find_record_fields(var: Variable, subs: &mut Subs) -> Vec<(String, Variable)>
|
|||
}
|
||||
|
||||
_ => {
|
||||
writeln!(
|
||||
std::io::stderr(),
|
||||
eprintln!(
|
||||
"WARN: Trying to get field completion for a type that is not a record ",
|
||||
);
|
||||
vec![]
|
||||
|
@ -470,22 +377,28 @@ fn find_record_fields(var: Variable, subs: &mut Subs) -> Vec<(String, Variable)>
|
|||
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
|
||||
//TODO! ideally i could recover using some previous typecheck result that isn't broken
|
||||
writeln!(
|
||||
std::io::stderr(),
|
||||
"ERROR: variable type of record was of type error cannot access field",
|
||||
);
|
||||
eprintln!("ERROR: variable type of record was of type error cannot access field",);
|
||||
vec![]
|
||||
}
|
||||
a => {
|
||||
writeln!(
|
||||
std::io::stderr(),
|
||||
"variable before field was unsuported type:{:?}",
|
||||
a
|
||||
);
|
||||
eprintln!("variable before field was unsuported type:{:?}", a);
|
||||
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(
|
||||
position: Position,
|
||||
symbol_prefix: String,
|
||||
|
@ -494,26 +407,12 @@ pub fn field_completion(
|
|||
subs: &mut Subs,
|
||||
module_id: &ModuleId,
|
||||
) -> Option<Vec<CompletionItem>> {
|
||||
writeln!(std::io::stderr(), "getting record field completions: ");
|
||||
let mut parts: Vec<_> = symbol_prefix.split('.').collect();
|
||||
let (variable, fields) = parts.split_first_mut()?;
|
||||
eprintln!("getting record field completions: ");
|
||||
let (variable, middle, field) = get_field_completion_parts(&symbol_prefix)?;
|
||||
|
||||
let mut empty = "";
|
||||
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(),
|
||||
eprintln!(
|
||||
"getting record field completions: variable:{:?} field{:?} middle{:?} ",
|
||||
variable,
|
||||
field,
|
||||
middle
|
||||
variable, field, middle
|
||||
);
|
||||
//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
|
||||
|
@ -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
|
||||
let second_last = middle.iter().fold(completion, |state, a| {
|
||||
let fields_vars = find_record_fields(state.1, subs);
|
||||
match fields_vars
|
||||
.into_iter()
|
||||
.find(|field| a.to_string() == field.0)
|
||||
{
|
||||
match fields_vars.into_iter().find(|field| a == &field.0) {
|
||||
None => state,
|
||||
Some(a) => a,
|
||||
}
|
||||
|
|
1
crates/lang_srv/src/log.rs
Normal file
1
crates/lang_srv/src/log.rs
Normal file
|
@ -0,0 +1 @@
|
|||
|
|
@ -1,29 +1,13 @@
|
|||
use std::{
|
||||
cell::OnceCell,
|
||||
collections::HashMap,
|
||||
future::Future,
|
||||
io::{stderr, Write},
|
||||
ops::Deref,
|
||||
rc::Rc,
|
||||
sync::Arc,
|
||||
};
|
||||
use std::{collections::HashMap, sync::Arc};
|
||||
|
||||
use tokio::{
|
||||
io::AsyncRead,
|
||||
sync::{Mutex, MutexGuard, RwLock, RwLockReadGuard, RwLockWriteGuard},
|
||||
task::JoinHandle,
|
||||
};
|
||||
use tokio::sync::{Mutex, MutexGuard};
|
||||
use tower_lsp::lsp_types::{
|
||||
notification::Notification, CompletionResponse, Diagnostic, GotoDefinitionResponse, Hover,
|
||||
Position, SemanticTokensResult, TextEdit, Url,
|
||||
CompletionResponse, Diagnostic, GotoDefinitionResponse, Hover, Position, SemanticTokensResult,
|
||||
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)]
|
||||
pub(crate) struct LatestDocument {
|
||||
info: DocInfo,
|
||||
|
@ -74,24 +58,6 @@ pub(crate) struct DocumentPair {
|
|||
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)]
|
||||
pub(crate) struct Registry {
|
||||
documents: Mutex<HashMap<Url, DocumentPair>>,
|
||||
|
@ -104,13 +70,6 @@ impl Registry {
|
|||
) {
|
||||
let url = document.url().clone();
|
||||
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());
|
||||
match documents.get_mut(&url) {
|
||||
Some(old_doc) => {
|
||||
|
@ -143,16 +102,10 @@ impl Registry {
|
|||
}
|
||||
}
|
||||
|
||||
pub async fn apply_change(&self, analysed_docs: Vec<AnalyzedDocument>) -> () {
|
||||
writeln!(
|
||||
std::io::stderr(),
|
||||
"updated the latest document with docinfo"
|
||||
);
|
||||
|
||||
pub async fn apply_changes(&self, analysed_docs: Vec<AnalyzedDocument>) -> () {
|
||||
let mut documents = self.documents.lock().await;
|
||||
writeln!(
|
||||
std::io::stderr(),
|
||||
"finised doc analasys updating docs {:?}",
|
||||
eprintln!(
|
||||
"finised doc analysis updating docs {:?}",
|
||||
analysed_docs
|
||||
.iter()
|
||||
.map(|a| a.doc_info.url.to_string())
|
||||
|
@ -169,9 +122,9 @@ impl Registry {
|
|||
let doc = lock.get_mut(&url);
|
||||
match doc {
|
||||
Some(a) => {
|
||||
writeln!(
|
||||
std::io::stderr(),
|
||||
"set the docInfo to version:{:?}",
|
||||
eprintln!(
|
||||
"set the docInfo for {:?} to version:{:?}",
|
||||
url.as_str(),
|
||||
partial.version
|
||||
);
|
||||
|
||||
|
@ -234,16 +187,13 @@ impl Registry {
|
|||
) -> Option<CompletionResponse> {
|
||||
let lock = self.documents.lock().await;
|
||||
let pair = lock.get(url)?;
|
||||
writeln!(stderr(), "got document");
|
||||
eprintln!("got document");
|
||||
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);
|
||||
|
||||
//this strategy probably won't work for record fields
|
||||
let completions =
|
||||
pair.last_good_document
|
||||
.completion_items(position, &latest_doc_info, symbol_prefix)?;
|
||||
let completions = pair
|
||||
.last_good_document
|
||||
.completion_items(position, &latest_doc_info)?;
|
||||
|
||||
Some(CompletionResponse::Array(completions))
|
||||
}
|
||||
|
|
|
@ -1,17 +1,14 @@
|
|||
use analysis::HIGHLIGHT_TOKENS_LEGEND;
|
||||
use registry::{DocumentChange, Registry};
|
||||
use registry::Registry;
|
||||
use std::future::Future;
|
||||
use std::io::Write;
|
||||
use std::sync::{Arc, OnceLock};
|
||||
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
use tokio::sync::{oneshot, Mutex, MutexGuard, RwLock};
|
||||
use tokio::task::{JoinError, JoinHandle};
|
||||
use tower_lsp::jsonrpc::Result;
|
||||
use tower_lsp::lsp_types::request::RegisterCapability;
|
||||
use tower_lsp::lsp_types::*;
|
||||
use tower_lsp::{Client, LanguageServer, LspService, Server};
|
||||
|
||||
use crate::analysis::global_anal;
|
||||
use crate::analysis::global_analysis;
|
||||
|
||||
mod analysis;
|
||||
mod convert;
|
||||
|
@ -25,7 +22,7 @@ struct RocLs {
|
|||
struct Inner {
|
||||
client: Client,
|
||||
registry: Registry,
|
||||
change_handle: parking_lot::Mutex<(
|
||||
change_cancel_handle: parking_lot::Mutex<(
|
||||
tokio::sync::watch::Sender<i32>,
|
||||
tokio::sync::watch::Receiver<i32>,
|
||||
)>,
|
||||
|
@ -41,7 +38,7 @@ impl RocLs {
|
|||
inner: Arc::new(Inner {
|
||||
client,
|
||||
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
|
||||
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
|
||||
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 {
|
||||
let text_document_sync = TextDocumentSyncCapability::Options(
|
||||
|
@ -105,27 +103,17 @@ impl RocLs {
|
|||
|
||||
/// Records a document content change.
|
||||
async fn change(&self, fi: Url, text: String, version: i32) {
|
||||
writeln!(std::io::stderr(), "starting change");
|
||||
let updating_doc_info = self.inner.documents_updating.acquire().await.unwrap();
|
||||
let mut new_change = {
|
||||
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
|
||||
eprintln!("starting change");
|
||||
|
||||
let (updating_doc_info, mut new_change) = self.change_preamble(version).await;
|
||||
tokio::time::sleep(Duration::from_millis(20)).await;
|
||||
if (new_change.has_changed().unwrap()) {
|
||||
writeln!(std::io::stderr(), "newer task started almost immediately");
|
||||
if new_change.has_changed().unwrap() {
|
||||
eprintln!("newer task started almost immediately");
|
||||
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
|
||||
.registry
|
||||
|
@ -133,64 +121,76 @@ impl RocLs {
|
|||
.await;
|
||||
drop(updating_doc_info);
|
||||
|
||||
writeln!(std::io::stderr(), "finished applying");
|
||||
eprintln!("finished updating docinfo, starting analysis ",);
|
||||
|
||||
let inner_ref = self.inner.clone();
|
||||
let handle: JoinHandle<core::result::Result<&str, JoinError>> =
|
||||
tokio::task::spawn(async move {
|
||||
let results = tokio::task::spawn_blocking(results).await?;
|
||||
let handle = async {
|
||||
let results = match 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;
|
||||
Ok("okay")
|
||||
});
|
||||
inner_ref.registry().apply_changes(results).await;
|
||||
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
|
||||
let cancelled = tokio::select! {
|
||||
a=handle=>{
|
||||
match a{
|
||||
|
||||
Err(a)=>
|
||||
{
|
||||
writeln!(std::io::stderr(), "error in task{:?}",a);
|
||||
true},
|
||||
Ok(_)=>false
|
||||
|
||||
}
|
||||
},
|
||||
_=new_change.changed()=>true
|
||||
let finished = tokio::select! {
|
||||
a=handle=>a,
|
||||
_=new_change.changed()=>Err("Cancellation triggered from change".to_string())
|
||||
};
|
||||
if cancelled {
|
||||
writeln!(std::io::stderr(), "cancelled change");
|
||||
match finished {
|
||||
Err(e) => {
|
||||
eprintln!("cancelled change. Reason:{:?}", e);
|
||||
return;
|
||||
}
|
||||
writeln!(std::io::stderr(), "applied_change getting diagnostics");
|
||||
|
||||
//We do this to briefly yeild
|
||||
Ok(_) => (),
|
||||
}
|
||||
eprintln!("applied_change getting and returning diagnostics");
|
||||
|
||||
let diagnostics = self.inner.registry().diagnostics(&fi).await;
|
||||
writeln!(std::io::stderr(), "applied_change returning diagnostics");
|
||||
|
||||
self.inner
|
||||
.client
|
||||
.publish_diagnostics(fi, diagnostics, Some(version))
|
||||
.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 {
|
||||
fn registry(&self) -> &Registry {
|
||||
&self.registry
|
||||
}
|
||||
fn registry_write(&mut self) -> &mut Registry {
|
||||
&mut self.registry
|
||||
}
|
||||
|
||||
async fn close(&self, fi: Url) {
|
||||
// self.registry()
|
||||
// .apply_change(DocumentChange::Closed(fi))
|
||||
// .await;
|
||||
async fn close(&self, _fi: Url) {
|
||||
()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -304,16 +304,15 @@ impl LanguageServer for RocLs {
|
|||
}
|
||||
async fn completion(&self, params: CompletionParams) -> Result<Option<CompletionResponse>> {
|
||||
let doc = params.text_document_position;
|
||||
writeln!(std::io::stderr(), "starting completion");
|
||||
writeln!(
|
||||
std::io::stderr(),
|
||||
eprintln!("starting completion");
|
||||
eprintln!(
|
||||
"permits::{:?} ",
|
||||
self.inner.documents_updating.available_permits()
|
||||
);
|
||||
|
||||
//We need to wait untill any changes that were in progress when we requested completion have applied
|
||||
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 {
|
||||
self.inner
|
||||
|
@ -323,7 +322,7 @@ impl LanguageServer for RocLs {
|
|||
})
|
||||
.await;
|
||||
|
||||
writeln!(std::io::stderr(), "finished completion");
|
||||
eprintln!("finished completion");
|
||||
res
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue