mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-30 15:21:12 +00:00
refactor: Removed panicks from completion
Signed-off-by: faldor20 <eli.jambu@yahoo.com>
This commit is contained in:
parent
5ccaa1dc74
commit
61b37b276e
4 changed files with 285 additions and 274 deletions
|
@ -3,8 +3,6 @@ use std::{
|
|||
future::Future,
|
||||
io::Write,
|
||||
path::{Path, PathBuf},
|
||||
result,
|
||||
slice::SliceIndex,
|
||||
};
|
||||
|
||||
use bumpalo::Bump;
|
||||
|
@ -13,26 +11,15 @@ use roc_collections::MutMap;
|
|||
use roc_load::{CheckedModule, LoadedModule};
|
||||
use roc_module::symbol::{Interns, ModuleId, Symbol};
|
||||
use roc_packaging::cache::{self, RocCacheDir};
|
||||
use roc_region::all::{LineColumn, LineInfo};
|
||||
use roc_region::all::LineInfo;
|
||||
use roc_reporting::report::RocDocAllocator;
|
||||
use roc_solve_problem::TypeError;
|
||||
use roc_types::subs::{Subs, Variable};
|
||||
use tokio::{sync::Mutex, task::JoinHandle};
|
||||
use tower_lsp::lsp_types::{
|
||||
CompletionItem, CompletionItemKind, CompletionItemTag, Diagnostic, GotoDefinitionResponse,
|
||||
Hover, HoverContents, Location, MarkedString, Position, Range, SemanticTokenType,
|
||||
SemanticTokens, SemanticTokensResult, TextEdit, Url,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
analysis::completion::{
|
||||
field_completion, find_record_fields, get_completion_items, get_completions,
|
||||
make_completion_items, make_completion_items_string,
|
||||
},
|
||||
convert::{
|
||||
diag::{IntoLspDiagnostic, ProblemFmt},
|
||||
ToRange, ToRocPosition,
|
||||
},
|
||||
use tower_lsp::lsp_types::{
|
||||
CompletionItem, Diagnostic, GotoDefinitionResponse, Hover, HoverContents, Location,
|
||||
MarkedString, Position, Range, SemanticTokenType, SemanticTokens, SemanticTokensResult,
|
||||
TextEdit, Url,
|
||||
};
|
||||
|
||||
mod completion;
|
||||
|
@ -40,6 +27,14 @@ mod parse_ast;
|
|||
mod semantic_tokens;
|
||||
mod tokens;
|
||||
|
||||
use crate::{
|
||||
analysis::completion::{field_completion, get_completion_items},
|
||||
convert::{
|
||||
diag::{IntoLspDiagnostic, ProblemFmt},
|
||||
ToRange, ToRocPosition,
|
||||
},
|
||||
};
|
||||
|
||||
use self::{parse_ast::Ast, semantic_tokens::arrange_semantic_tokens, tokens::Token};
|
||||
pub const HIGHLIGHT_TOKENS_LEGEND: &[SemanticTokenType] = Token::LEGEND;
|
||||
|
||||
|
@ -64,18 +59,20 @@ pub(crate) fn global_anal(
|
|||
source_url: Url,
|
||||
source: String,
|
||||
version: u32,
|
||||
) -> (impl Future<Output = Vec<AnalyzedDocument>>, DocInfo) {
|
||||
) -> (impl FnOnce() -> Vec<AnalyzedDocument>, DocInfo) {
|
||||
let fi = source_url.to_file_path().unwrap();
|
||||
let src_dir = find_src_dir(&fi).to_path_buf();
|
||||
let line_info = LineInfo::new(&source);
|
||||
|
||||
let doc_info = DocInfo {
|
||||
url: source_url.clone(),
|
||||
line_info: line_info.clone(),
|
||||
source: source.clone(),
|
||||
version,
|
||||
};
|
||||
//We will return this before the analisys has completed to enable completion
|
||||
let doc_info_return = doc_info.clone();
|
||||
let documents_future = async move {
|
||||
let documents_future = move || {
|
||||
let arena = Bump::new();
|
||||
let loaded = roc_load::load_and_typecheck_str(
|
||||
&arena,
|
||||
|
@ -138,12 +135,10 @@ pub(crate) fn global_anal(
|
|||
root_module: &mut root_module,
|
||||
};
|
||||
|
||||
writeln!(std::io::stderr(), "sources:{:?}", sources);
|
||||
for (module_id, (path, source)) in sources {
|
||||
documents.push(builder.build_document(path, source, module_id, version));
|
||||
}
|
||||
|
||||
writeln!(std::io::stderr(), "documents:{:?}", documents.len());
|
||||
documents
|
||||
};
|
||||
(documents_future, doc_info_return)
|
||||
|
@ -338,6 +333,23 @@ 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()
|
||||
|
@ -478,16 +490,17 @@ impl AnalyzedDocument {
|
|||
latest_doc: &DocInfo,
|
||||
symbol_prefix: String,
|
||||
) -> Option<Vec<CompletionItem>> {
|
||||
let mut position = position;
|
||||
let symbol_prefix = latest_doc.get_prefix_at_position(position);
|
||||
writeln!(
|
||||
std::io::stderr(),
|
||||
"starting to get completion items for prefix: {:?}",
|
||||
symbol_prefix
|
||||
"starting to get completion items for prefix: {:?} docVersion:{:?}",
|
||||
symbol_prefix,
|
||||
latest_doc.version
|
||||
);
|
||||
let len_diff = latest_doc.source.len() as i32 - self.doc_info.source.len() as i32;
|
||||
|
||||
//TODO: this is a hack we can move our position back by getting the difference in the number of chars on this line and what the line was before and doing the same with the number of lines
|
||||
let mut position = position.to_roc_position(&latest_doc.line_info);
|
||||
//TODO: this is kind of a hack and should be removed once we can do some minimal parsing without full type checking
|
||||
position.offset = (position.offset as i32 - len_diff - 1) as u32;
|
||||
writeln!(
|
||||
std::io::stderr(),
|
||||
|
@ -503,7 +516,8 @@ impl AnalyzedDocument {
|
|||
..
|
||||
} = self.module()?;
|
||||
|
||||
if symbol_prefix.contains('.') {
|
||||
let is_field_completion = symbol_prefix.contains('.');
|
||||
if is_field_completion {
|
||||
field_completion(
|
||||
position,
|
||||
symbol_prefix,
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
use std::{io::Write, path::Prefix};
|
||||
use std::io::Write;
|
||||
|
||||
use roc_can::{
|
||||
def::Def,
|
||||
expr::{Declarations, Expr, WhenBranch},
|
||||
pattern::{ListPatterns, Pattern, RecordDestruct, TupleDestruct},
|
||||
traverse::{walk_decl, walk_def, walk_expr, DeclarationInfo, FoundDeclaration, Visitor},
|
||||
traverse::{walk_decl, walk_def, walk_expr, DeclarationInfo, Visitor},
|
||||
};
|
||||
use roc_module::symbol::{self, Interns, ModuleId, Symbol};
|
||||
use roc_module::symbol::{Interns, ModuleId, Symbol};
|
||||
use roc_region::all::{Loc, Position, Region};
|
||||
use roc_types::subs::{GetSubsSlice, Subs, Variable};
|
||||
use roc_types::subs::{Subs, Variable};
|
||||
use tower_lsp::lsp_types::{CompletionItem, CompletionItemKind};
|
||||
|
||||
use crate::analysis::format_var_type;
|
||||
|
@ -34,20 +34,7 @@ impl Visitor for CompletionVisitor<'_> {
|
|||
walk_expr(self, expr, var);
|
||||
}
|
||||
}
|
||||
fn visit_annotation(&mut self, _pat: &roc_can::def::Annotation) {
|
||||
let mut stderr = std::io::stderr();
|
||||
// writeln!(&mut stderr, "annotation:{:?}", _pat);
|
||||
}
|
||||
|
||||
// fn visit_pattern(&mut self, pat: &Pattern, region: Region, opt_var: Option<Variable>) {
|
||||
// if region.contains_pos(self.position) {
|
||||
// // if let Some(var) = opt_var {
|
||||
// // self.region_typ = Some((region, var));
|
||||
// // }
|
||||
|
||||
// walk_pattern(self, pat);
|
||||
// }
|
||||
// }
|
||||
fn visit_decl(&mut self, decl: DeclarationInfo<'_>) {
|
||||
match decl {
|
||||
DeclarationInfo::Value { loc_expr, .. }
|
||||
|
@ -74,22 +61,8 @@ impl Visitor for CompletionVisitor<'_> {
|
|||
}
|
||||
}
|
||||
impl CompletionVisitor<'_> {
|
||||
fn make_completion_items(&mut self, found: Vec<FoundDeclaration>) -> Vec<(Symbol, Variable)> {
|
||||
found
|
||||
.iter()
|
||||
.flat_map(|comp| match comp {
|
||||
FoundDeclaration::Decl(dec) => self.decl_to_completion_item(dec),
|
||||
FoundDeclaration::Def(def) => def
|
||||
.pattern_vars
|
||||
.iter()
|
||||
.map(|(symbol, var)| (symbol.clone(), var.clone()))
|
||||
.collect(),
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
fn extract_defs(&mut self, def: &Def) -> Vec<(Symbol, Variable)> {
|
||||
let mut stderr = std::io::stderr();
|
||||
writeln!(&mut stderr, "completion begin");
|
||||
writeln!(std::io::stderr(), "completion begin");
|
||||
def.pattern_vars
|
||||
.iter()
|
||||
.map(|(symbol, var)| (symbol.clone(), var.clone()))
|
||||
|
@ -106,10 +79,10 @@ impl CompletionVisitor<'_> {
|
|||
// Expr::IngestedFile(_, _, _) => todo!(),
|
||||
// Expr::Var(_, _) => todo!(),
|
||||
// Expr::AbilityMember(_, _, _) => todo!(),
|
||||
Expr::When { loc_cond, cond_var, expr_var, region, branches, branches_cond_var, exhaustive } => {
|
||||
Expr::When { expr_var, branches,.. } => {
|
||||
|
||||
let out:Vec<_> =
|
||||
branches.iter().flat_map(|WhenBranch{ patterns, value, guard, redundant }|{
|
||||
branches.iter().flat_map(|WhenBranch{ patterns, value, ..}|{
|
||||
if value.region.contains_pos(self.position) {
|
||||
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
|
||||
|
@ -185,7 +158,7 @@ impl CompletionVisitor<'_> {
|
|||
.collect()
|
||||
}
|
||||
|
||||
fn as_pattern(&self, as_pat: &Pattern, as_symbol: Symbol) -> (Symbol, Variable) {
|
||||
fn as_pattern(&self, as_pat: &Pattern, as_symbol: Symbol) -> Option<(Symbol, Variable)> {
|
||||
let var = match as_pat {
|
||||
Pattern::AppliedTag {
|
||||
whole_var,
|
||||
|
@ -227,9 +200,9 @@ impl CompletionVisitor<'_> {
|
|||
// Pattern::OpaqueNotInScope(_) => todo!(),
|
||||
// Pattern::UnsupportedPattern(_) => todo!(),
|
||||
// Pattern::MalformedPattern(_, _) => todo!(),
|
||||
_ => todo!(),
|
||||
_ => return None,
|
||||
};
|
||||
(as_symbol, var.clone())
|
||||
Some((as_symbol, var.clone()))
|
||||
}
|
||||
fn patterns(
|
||||
&self,
|
||||
|
@ -263,19 +236,16 @@ impl CompletionVisitor<'_> {
|
|||
elem_var,
|
||||
patterns,
|
||||
} => self.list_destructure(patterns, elem_var),
|
||||
roc_can::pattern::Pattern::As(pat, symbol) => {
|
||||
vec![self.as_pattern(&pat.value, symbol.clone())]
|
||||
roc_can::pattern::Pattern::As(pat, symbol) => self
|
||||
.as_pattern(&pat.value, symbol.clone())
|
||||
.map(|a| vec![a])
|
||||
.unwrap_or(vec![]),
|
||||
roc_can::pattern::Pattern::RecordDestructure { destructs, .. } => {
|
||||
self.record_destructs(destructs)
|
||||
}
|
||||
roc_can::pattern::Pattern::TupleDestructure { destructs, .. } => {
|
||||
self.tuple_destructs(destructs)
|
||||
}
|
||||
roc_can::pattern::Pattern::RecordDestructure {
|
||||
whole_var,
|
||||
ext_var,
|
||||
destructs,
|
||||
} => self.record_destructs(destructs),
|
||||
roc_can::pattern::Pattern::TupleDestructure {
|
||||
whole_var,
|
||||
ext_var,
|
||||
destructs,
|
||||
} => self.tuple_destructs(destructs),
|
||||
// roc_can::pattern::Pattern::List {
|
||||
// list_var,
|
||||
// elem_var,
|
||||
|
@ -300,35 +270,15 @@ impl CompletionVisitor<'_> {
|
|||
}
|
||||
|
||||
fn is_match(&self, symbol: &Symbol) -> bool {
|
||||
let mut stderr = std::io::stderr();
|
||||
// writeln!(
|
||||
// &mut stderr,
|
||||
// "check if prefix {:?} matches {:?}",
|
||||
// self.prefix,
|
||||
// symbol.as_str(self.interns)
|
||||
// );
|
||||
symbol.as_str(self.interns).starts_with(&self.prefix)
|
||||
}
|
||||
|
||||
fn decl_to_completion_item(&self, decl: &DeclarationInfo) -> Vec<(Symbol, Variable)> {
|
||||
match decl {
|
||||
DeclarationInfo::Value {
|
||||
loc_symbol,
|
||||
expr_var,
|
||||
pattern,
|
||||
..
|
||||
} => {
|
||||
let mut stderr = std::io::stderr();
|
||||
// writeln!(
|
||||
// &mut stderr,
|
||||
// "decl:{:?}",
|
||||
// loc_symbol.value.as_str(self.interns)
|
||||
// );
|
||||
|
||||
self.patterns(pattern, expr_var)
|
||||
}
|
||||
expr_var, pattern, ..
|
||||
} => self.patterns(pattern, expr_var),
|
||||
DeclarationInfo::Function {
|
||||
loc_symbol,
|
||||
expr_var,
|
||||
pattern,
|
||||
function,
|
||||
|
@ -362,7 +312,7 @@ impl CompletionVisitor<'_> {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn get_completions<'a>(
|
||||
fn get_completions<'a>(
|
||||
position: Position,
|
||||
decls: &'a Declarations,
|
||||
prefix: String,
|
||||
|
@ -390,31 +340,19 @@ fn make_completion_item(
|
|||
) -> CompletionItem {
|
||||
let type_str = format_var_type(var, subs, module_id, interns);
|
||||
let typ = match subs.get(var.clone()).content {
|
||||
// roc_types::subs::Content::FlexVar(_) => todo!(),
|
||||
// roc_types::subs::Content::RigidVar(_) => todo!(),
|
||||
// roc_types::subs::Content::FlexAbleVar(_, _) => todo!(),
|
||||
// roc_types::subs::Content::RigidAbleVar(_, _) => todo!(),
|
||||
// roc_types::subs::Content::RecursionVar { structure, opt_name } => todo!(),
|
||||
// roc_types::subs::Content::LambdaSet(_) => todo!(),
|
||||
// roc_types::subs::Content::ErasedLambda => todo!(),
|
||||
roc_types::subs::Content::Structure(var) => match var {
|
||||
roc_types::subs::FlatType::Apply(_, _) => CompletionItemKind::FUNCTION,
|
||||
roc_types::subs::FlatType::Func(_, _, _) => CompletionItemKind::FUNCTION,
|
||||
// roc_types::subs::FlatType::FunctionOrTagUnion(_, _, _) => todo!(),
|
||||
// roc_types::subs::FlatType::RecursiveTagUnion(_, _, _) => todo!(),
|
||||
// roc_types::subs::FlatType::EmptyRecord |
|
||||
// roc_types::subs::FlatType::Record(_, _) => todo!(),
|
||||
// roc_types::subs::FlatType::EmptyTuple |
|
||||
// roc_types::subs::FlatType::Tuple(_, _) => CompletionItemKind::VARIABLE,
|
||||
roc_types::subs::FlatType::EmptyTagUnion
|
||||
| roc_types::subs::FlatType::TagUnion(_, _) => CompletionItemKind::ENUM,
|
||||
_ => CompletionItemKind::VARIABLE,
|
||||
},
|
||||
// roc_types::subs::Content::Alias(_, _, _, _) => todo!(),
|
||||
// roc_types::subs::Content::RangedNumber(_) => todo!(),
|
||||
// roc_types::subs::Content::Error => todo!(),
|
||||
a => {
|
||||
writeln!(std::io::stderr(), "unhandled variable type:{:?}", a);
|
||||
writeln!(
|
||||
std::io::stderr(),
|
||||
"No specific completionKind for variable type :{:?} defaulting to 'Variable'",
|
||||
a
|
||||
);
|
||||
CompletionItemKind::VARIABLE
|
||||
}
|
||||
};
|
||||
|
@ -427,6 +365,9 @@ fn make_completion_item(
|
|||
..Default::default()
|
||||
}
|
||||
}
|
||||
///Gets completion items using the visitor pattern,
|
||||
///This will walk through declarations that would be accessable from the provided position adding them to a list of completion items untill all accessable declarations have been fully explored
|
||||
|
||||
pub fn get_completion_items(
|
||||
position: Position,
|
||||
prefix: String,
|
||||
|
@ -458,7 +399,7 @@ pub fn make_completion_items(
|
|||
.collect()
|
||||
}
|
||||
|
||||
pub fn make_completion_items_string(
|
||||
fn make_completion_items_string(
|
||||
subs: &mut Subs,
|
||||
module_id: &ModuleId,
|
||||
interns: &Interns,
|
||||
|
@ -470,23 +411,17 @@ pub fn make_completion_items_string(
|
|||
.collect()
|
||||
}
|
||||
|
||||
pub fn find_record_fields(var: Variable, subs: &mut Subs) -> Vec<(String, Variable)> {
|
||||
///Gets completion items for a record field
|
||||
///Uses
|
||||
fn find_record_fields(var: Variable, subs: &mut Subs) -> Vec<(String, Variable)> {
|
||||
let content = subs.get(var);
|
||||
match content.content {
|
||||
// roc_types::subs::Content::FlexVar(_) => todo!(),
|
||||
// roc_types::subs::Content::RigidVar(_) => todo!(),
|
||||
// roc_types::subs::Content::FlexAbleVar(_, _) => todo!(),
|
||||
// roc_types::subs::Content::RigidAbleVar(_, _) => todo!(),
|
||||
// roc_types::subs::Content::RecursionVar { structure, opt_name } => todo!(),
|
||||
// roc_types::subs::Content::LambdaSet(_) => todo!(),
|
||||
// roc_types::subs::Content::ErasedLambda => todo!(),
|
||||
roc_types::subs::Content::Structure(typ) => match typ {
|
||||
// roc_types::subs::FlatType::Apply(_, _) => todo!(),
|
||||
// roc_types::subs::FlatType::Func(_, _, _) => todo!(),
|
||||
roc_types::subs::FlatType::Record(fields, ext) => {
|
||||
let field_types = fields.unsorted_iterator(subs, ext);
|
||||
let fields: Vec<_> = match field_types {
|
||||
Ok(field) => field.map(|a| {
|
||||
Ok(field) => field
|
||||
.map(|a| {
|
||||
let var = match a.1 {
|
||||
roc_types::types::RecordField::Demanded(var)
|
||||
| roc_types::types::RecordField::Required(var)
|
||||
|
@ -495,30 +430,43 @@ pub fn find_record_fields(var: Variable, subs: &mut Subs) -> Vec<(String, Variab
|
|||
| roc_types::types::RecordField::RigidOptional(var) => var,
|
||||
};
|
||||
(a.0.clone().into(), var)
|
||||
}),
|
||||
Err(err) => todo!(),
|
||||
})
|
||||
.collect(),
|
||||
Err(err) => {
|
||||
writeln!(
|
||||
std::io::stderr(),
|
||||
"WARN:Error getting record field types for completion{:?}",
|
||||
err
|
||||
);
|
||||
vec![]
|
||||
}
|
||||
.collect();
|
||||
};
|
||||
fields
|
||||
}
|
||||
_ => todo!(),
|
||||
// roc_types::subs::FlatType::Tuple(_, _) => todo!(),
|
||||
// roc_types::subs::FlatType::TagUnion(_, _) => todo!(),
|
||||
// roc_types::subs::FlatType::FunctionOrTagUnion(_, _, _) => todo!(),
|
||||
// roc_types::subs::FlatType::RecursiveTagUnion(_, _, _) => todo!(),
|
||||
// roc_types::subs::FlatType::EmptyRecord => todo!(),
|
||||
// roc_types::subs::FlatType::EmptyTuple => todo!(),
|
||||
// roc_types::subs::FlatType::EmptyTagUnion => todo!(),
|
||||
_ => {
|
||||
writeln!(
|
||||
std::io::stderr(),
|
||||
"WARN: Trying to get field completion for a type that is not a record ",
|
||||
);
|
||||
vec![]
|
||||
}
|
||||
},
|
||||
// roc_types::subs::Content::Alias(_, _, _, _) => todo!(),
|
||||
// roc_types::subs::Content::RangedNumber(_) => todo!(),
|
||||
roc_types::subs::Content::Error => {
|
||||
writeln!(std::io::stderr(), "ERROR: variable was of type 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",
|
||||
);
|
||||
vec![]
|
||||
}
|
||||
a => {
|
||||
writeln!(std::io::stderr(), "variable before field type:{:?}", a);
|
||||
todo!();
|
||||
writeln!(
|
||||
std::io::stderr(),
|
||||
"variable before field was unsuported type:{:?}",
|
||||
a
|
||||
);
|
||||
vec![]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -579,17 +527,3 @@ pub fn field_completion(
|
|||
make_completion_items_string(subs, module_id, interns, field_completions);
|
||||
Some(field_completions)
|
||||
}
|
||||
// fn make_completion_item_string(
|
||||
// &mut self,
|
||||
// label: String,
|
||||
// var: &Variable,
|
||||
// ) -> CompletionItem {
|
||||
// let type_str = format_var_type(var.clone(), self.subs, self.module_id, self.interns);
|
||||
// CompletionItem {
|
||||
// label,
|
||||
// detail: Some(type_str),
|
||||
// kind: Some(CompletionItemKind::VARIABLE ),
|
||||
|
||||
// ..Default::default()
|
||||
// }
|
||||
// }
|
||||
|
|
|
@ -1,5 +1,11 @@
|
|||
use std::{
|
||||
cell::OnceCell, collections::HashMap, future::Future, io::Write, ops::Deref, rc::Rc, sync::Arc,
|
||||
cell::OnceCell,
|
||||
collections::HashMap,
|
||||
future::Future,
|
||||
io::{stderr, Write},
|
||||
ops::Deref,
|
||||
rc::Rc,
|
||||
sync::Arc,
|
||||
};
|
||||
|
||||
use tokio::{
|
||||
|
@ -98,12 +104,12 @@ impl Registry {
|
|||
) {
|
||||
let url = document.url().clone();
|
||||
let document = Arc::new(document);
|
||||
writeln!(
|
||||
std::io::stderr(),
|
||||
"updating doc{:?}. version:{:?}",
|
||||
&url,
|
||||
&document.doc_info.version
|
||||
);
|
||||
// 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) {
|
||||
|
@ -137,46 +143,42 @@ impl Registry {
|
|||
}
|
||||
}
|
||||
|
||||
pub async fn apply_change(&self, change: DocumentChange) -> () {
|
||||
match change {
|
||||
DocumentChange::Modified(url, source, version) => {
|
||||
let (results, partial) = global_anal(url.clone(), source, version);
|
||||
|
||||
writeln!(std::io::stderr(), "starting global analysis");
|
||||
let handle = tokio::task::spawn(results);
|
||||
//Update the latest document with the partial analysis
|
||||
// Only replace the set of documents and all dependencies that were re-analyzed.
|
||||
// Note that this is actually the opposite of what we want - in truth we want to
|
||||
// re-evaluate all dependents!
|
||||
|
||||
let mut lock = self.documents.lock().await;
|
||||
let doc = lock.get_mut(&url);
|
||||
match doc {
|
||||
Some(a) => a.latest_document = LatestDocument::new(partial),
|
||||
None => (),
|
||||
}
|
||||
drop(lock);
|
||||
pub async fn apply_change(&self, analysed_docs: Vec<AnalyzedDocument>) -> () {
|
||||
writeln!(
|
||||
std::io::stderr(),
|
||||
"updated the latest document with docinfo"
|
||||
);
|
||||
|
||||
let analised_docs = handle.await.unwrap();
|
||||
let mut documents = self.documents.lock().await;
|
||||
writeln!(
|
||||
std::io::stderr(),
|
||||
"finised doc analasys updating docs {:?}",
|
||||
analised_docs
|
||||
analysed_docs
|
||||
.iter()
|
||||
.map(|a| &a.doc_info)
|
||||
.map(|a| a.doc_info.url.to_string())
|
||||
.collect::<Vec<_>>()
|
||||
);
|
||||
|
||||
for document in analised_docs {
|
||||
for document in analysed_docs {
|
||||
Registry::update_document(&mut documents, document);
|
||||
}
|
||||
}
|
||||
DocumentChange::Closed(_url) => (),
|
||||
|
||||
pub async fn apply_doc_info_changes(&self, url: Url, partial: DocInfo) {
|
||||
let mut lock = self.documents.lock().await;
|
||||
let doc = lock.get_mut(&url);
|
||||
match doc {
|
||||
Some(a) => {
|
||||
writeln!(
|
||||
std::io::stderr(),
|
||||
"set the docInfo to version:{:?}",
|
||||
partial.version
|
||||
);
|
||||
|
||||
a.latest_document = LatestDocument::new(partial);
|
||||
}
|
||||
|
||||
None => (),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -192,12 +194,6 @@ impl Registry {
|
|||
None => None,
|
||||
}
|
||||
}
|
||||
// fn document_last_good(self, url: &Url) -> Option<&AnalyzedDocument> {
|
||||
// self.documents.get(url)(|x| x.last_good_document)
|
||||
// }
|
||||
// fn document_last_good(self, url: &Url) -> Option<&AnalyzedDocument> {
|
||||
// self.documents.get(url)(|x| x.last_good_document)
|
||||
// }
|
||||
|
||||
pub async fn diagnostics(&self, url: &Url) -> Vec<Diagnostic> {
|
||||
let Some( document) = self.latest_document_by_url(url).await else {
|
||||
|
@ -238,10 +234,9 @@ impl Registry {
|
|||
) -> Option<CompletionResponse> {
|
||||
let lock = self.documents.lock().await;
|
||||
let pair = lock.get(url)?;
|
||||
let mut stderr = std::io::stderr();
|
||||
writeln!(&mut stderr, "got document");
|
||||
writeln!(stderr(), "got document");
|
||||
let latest_doc_info = &pair.latest_document.info;
|
||||
writeln!(&mut stderr, "latest version:{:?} ", latest_doc_info.version);
|
||||
writeln!(stderr(), "latest version:{:?} ", latest_doc_info.version);
|
||||
|
||||
let symbol_prefix = latest_doc_info.get_prefix_at_position(position);
|
||||
|
||||
|
|
|
@ -2,43 +2,55 @@ use analysis::HIGHLIGHT_TOKENS_LEGEND;
|
|||
use registry::{DocumentChange, Registry};
|
||||
use std::future::Future;
|
||||
use std::io::Write;
|
||||
use std::sync::Arc;
|
||||
use tokio::sync::{Mutex, MutexGuard, RwLock};
|
||||
use tokio::task::JoinHandle;
|
||||
use std::sync::{Arc, OnceLock};
|
||||
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;
|
||||
|
||||
mod analysis;
|
||||
mod convert;
|
||||
mod registry;
|
||||
|
||||
#[derive(Debug)]
|
||||
struct RocLs {
|
||||
pub inner: Arc<Inner>,
|
||||
}
|
||||
#[derive(Debug)]
|
||||
struct Inner {
|
||||
client: Client,
|
||||
registry: Registry,
|
||||
// change_handle: Mutex<Option<JoinHandle<()>>>,
|
||||
change_handle: parking_lot::Mutex<(
|
||||
tokio::sync::watch::Sender<i32>,
|
||||
tokio::sync::watch::Receiver<i32>,
|
||||
)>,
|
||||
documents_updating: tokio::sync::Semaphore,
|
||||
}
|
||||
|
||||
impl std::panic::RefUnwindSafe for RocLs {}
|
||||
const SEMLIMIT: u32 = 20;
|
||||
|
||||
impl RocLs {
|
||||
pub fn new(client: Client) -> Self {
|
||||
Self {
|
||||
inner: Arc::new(Inner {
|
||||
client,
|
||||
registry: Registry::default(),
|
||||
// change_handle: Mutex::new(None),
|
||||
change_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),
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
fn registry(&self) -> &Registry {
|
||||
&self.registry
|
||||
///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;
|
||||
}
|
||||
fn registry_write(&mut self) -> &mut Registry {
|
||||
&mut self.registry
|
||||
}
|
||||
|
||||
pub fn capabilities() -> ServerCapabilities {
|
||||
let text_document_sync = TextDocumentSyncCapability::Options(
|
||||
// TODO: later on make this incremental
|
||||
|
@ -93,35 +105,92 @@ impl RocLs {
|
|||
|
||||
/// Records a document content change.
|
||||
async fn change(&self, fi: Url, text: String, version: i32) {
|
||||
// match self.change_handle.lock().await.as_ref() {
|
||||
// Some(a) => a.abort(),
|
||||
// None => (),
|
||||
// }
|
||||
|
||||
writeln!(std::io::stderr(), "starting change");
|
||||
self.registry()
|
||||
.apply_change(DocumentChange::Modified(fi.clone(), text, version as u32))
|
||||
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
|
||||
tokio::time::sleep(Duration::from_millis(20)).await;
|
||||
if (new_change.has_changed().unwrap()) {
|
||||
writeln!(std::io::stderr(), "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);
|
||||
|
||||
self.inner
|
||||
.registry
|
||||
.apply_doc_info_changes(fi.clone(), partial)
|
||||
.await;
|
||||
// let handle = tokio::task::spawn(self.registry().apply_change(DocumentChange::Modified(
|
||||
// fi.clone(),
|
||||
// text,
|
||||
// version as u32,
|
||||
// )));
|
||||
drop(updating_doc_info);
|
||||
|
||||
writeln!(std::io::stderr(), "finished applying");
|
||||
|
||||
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?;
|
||||
|
||||
inner_ref.registry().apply_change(results).await;
|
||||
Ok("okay")
|
||||
});
|
||||
|
||||
writeln!(std::io::stderr(), "waiting on analisys 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
|
||||
};
|
||||
if cancelled {
|
||||
writeln!(std::io::stderr(), "cancelled change");
|
||||
return;
|
||||
}
|
||||
writeln!(std::io::stderr(), "applied_change getting diagnostics");
|
||||
|
||||
//We do this to briefly yeild
|
||||
|
||||
let diagnostics = self.registry().diagnostics(&fi).await;
|
||||
let diagnostics = self.inner.registry().diagnostics(&fi).await;
|
||||
writeln!(std::io::stderr(), "applied_change returning diagnostics");
|
||||
|
||||
self.client
|
||||
self.inner
|
||||
.client
|
||||
.publish_diagnostics(fi, diagnostics, Some(version))
|
||||
.await;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
// self.registry()
|
||||
// .apply_change(DocumentChange::Closed(fi))
|
||||
// .await;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -135,7 +204,8 @@ impl LanguageServer for RocLs {
|
|||
}
|
||||
|
||||
async fn initialized(&self, _: InitializedParams) {
|
||||
self.client
|
||||
self.inner
|
||||
.client
|
||||
.log_message(MessageType::INFO, "Roc language server initialized.")
|
||||
.await;
|
||||
}
|
||||
|
@ -160,7 +230,7 @@ impl LanguageServer for RocLs {
|
|||
|
||||
async fn did_close(&self, params: DidCloseTextDocumentParams) {
|
||||
let TextDocumentIdentifier { uri } = params.text_document;
|
||||
self.close(uri).await;
|
||||
self.inner.close(uri).await;
|
||||
}
|
||||
|
||||
async fn shutdown(&self) -> Result<()> {
|
||||
|
@ -177,7 +247,12 @@ impl LanguageServer for RocLs {
|
|||
work_done_progress_params: _,
|
||||
} = params;
|
||||
|
||||
panic_wrapper_async(|| async { self.registry().hover(&text_document.uri, position).await })
|
||||
panic_wrapper_async(|| async {
|
||||
self.inner
|
||||
.registry()
|
||||
.hover(&text_document.uri, position)
|
||||
.await
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
||||
|
@ -196,7 +271,8 @@ impl LanguageServer for RocLs {
|
|||
} = params;
|
||||
|
||||
panic_wrapper_async(|| async {
|
||||
self.registry()
|
||||
self.inner
|
||||
.registry()
|
||||
.goto_definition(&text_document.uri, position)
|
||||
.await
|
||||
})
|
||||
|
@ -210,7 +286,7 @@ impl LanguageServer for RocLs {
|
|||
work_done_progress_params: _,
|
||||
} = params;
|
||||
|
||||
panic_wrapper_async(|| async { self.registry().formatting(&text_document.uri) }).await
|
||||
panic_wrapper_async(|| async { self.inner.registry().formatting(&text_document.uri) }).await
|
||||
}
|
||||
|
||||
async fn semantic_tokens_full(
|
||||
|
@ -223,14 +299,25 @@ impl LanguageServer for RocLs {
|
|||
partial_result_params: _,
|
||||
} = params;
|
||||
|
||||
panic_wrapper_async(|| async { self.registry().semantic_tokens(&text_document.uri) }).await
|
||||
panic_wrapper_async(|| async { self.inner.registry().semantic_tokens(&text_document.uri) })
|
||||
.await
|
||||
}
|
||||
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(),
|
||||
"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 ");
|
||||
|
||||
let res = panic_wrapper_async(|| async {
|
||||
self.registry()
|
||||
self.inner
|
||||
.registry()
|
||||
.completion_items(&doc.text_document.uri, doc.position)
|
||||
.await
|
||||
})
|
||||
|
@ -239,25 +326,6 @@ impl LanguageServer for RocLs {
|
|||
writeln!(std::io::stderr(), "finished completion");
|
||||
res
|
||||
}
|
||||
// async fn completion(
|
||||
// &self,
|
||||
// params: GotoDefinitionParams,
|
||||
// ) -> Result<Option<GotoDefinitionResponse>> {
|
||||
// let GotoDefinitionParams {
|
||||
// text_document_position_params:
|
||||
// TextDocumentPositionParams {
|
||||
// text_document,
|
||||
// position,
|
||||
// },
|
||||
// work_done_progress_params: _,
|
||||
// partial_result_params: _,
|
||||
// } = params;
|
||||
|
||||
// panic_wrapper(|| {
|
||||
// self.registry()
|
||||
// .goto_definition(&text_document.uri, position)
|
||||
// })
|
||||
// }
|
||||
}
|
||||
|
||||
fn panic_wrapper<T>(f: impl FnOnce() -> Option<T> + std::panic::UnwindSafe) -> Result<Option<T>> {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue