mirror of
https://github.com/roc-lang/roc.git
synced 2024-11-13 09:49:11 +03:00
completion working when there are errors in the file and has types
Signed-off-by: faldor20 <eli.jambu@yahoo.com>
This commit is contained in:
parent
8c44ec8fd2
commit
9b4230cfc3
@ -364,7 +364,7 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct LineInfo {
|
||||
line_offsets: Vec<u32>,
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
io::Write,
|
||||
path::{Path, PathBuf},
|
||||
};
|
||||
|
||||
@ -13,10 +14,10 @@ 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::LineInfo;
|
||||
use roc_region::all::{LineColumn, LineInfo};
|
||||
use roc_reporting::report::RocDocAllocator;
|
||||
use roc_solve_problem::TypeError;
|
||||
use roc_types::subs::Subs;
|
||||
use roc_types::subs::{Subs, Variable};
|
||||
use tower_lsp::lsp_types::{
|
||||
CompletionItem, CompletionItemKind, Diagnostic, GotoDefinitionResponse, Hover, HoverContents,
|
||||
Location, MarkedString, Position, Range, SemanticTokenType, SemanticTokens,
|
||||
@ -39,6 +40,23 @@ pub(crate) struct GlobalAnalysis {
|
||||
pub documents: Vec<AnalyzedDocument>,
|
||||
}
|
||||
|
||||
fn format_var_type(
|
||||
var: Variable,
|
||||
subs: &mut Subs,
|
||||
module_id: &ModuleId,
|
||||
interns: &Interns,
|
||||
) -> String {
|
||||
let snapshot = subs.snapshot();
|
||||
let type_str = roc_types::pretty_print::name_and_print_var(
|
||||
var,
|
||||
subs,
|
||||
*module_id,
|
||||
interns,
|
||||
roc_types::pretty_print::DebugPrint::NOTHING,
|
||||
);
|
||||
subs.rollback_to(snapshot);
|
||||
type_str
|
||||
}
|
||||
impl GlobalAnalysis {
|
||||
pub fn new(source_url: Url, source: String) -> GlobalAnalysis {
|
||||
let arena = Bump::new();
|
||||
@ -251,7 +269,7 @@ impl<'a> AnalyzedDocumentBuilder<'a> {
|
||||
|
||||
type ModuleIdToUrl = HashMap<ModuleId, Url>;
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Clone)]
|
||||
struct AnalyzedModule {
|
||||
module_id: ModuleId,
|
||||
interns: Interns,
|
||||
@ -263,7 +281,7 @@ struct AnalyzedModule {
|
||||
module_id_to_url: ModuleIdToUrl,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub(crate) struct AnalyzedDocument {
|
||||
url: Url,
|
||||
line_info: LineInfo,
|
||||
@ -302,6 +320,9 @@ impl AnalyzedDocument {
|
||||
let end = Position::new(line_info.num_lines(), 0);
|
||||
Range::new(start, end)
|
||||
}
|
||||
pub fn type_checked(&self) -> bool {
|
||||
self.module.is_some()
|
||||
}
|
||||
|
||||
pub fn diagnostics(&mut self) -> Vec<Diagnostic> {
|
||||
self.diagnostics.clone()
|
||||
@ -338,16 +359,7 @@ impl AnalyzedDocument {
|
||||
} = self.module_mut()?;
|
||||
|
||||
let (region, var) = roc_can::traverse::find_closest_type_at(pos, declarations)?;
|
||||
|
||||
let snapshot = subs.snapshot();
|
||||
let type_str = roc_types::pretty_print::name_and_print_var(
|
||||
var,
|
||||
subs,
|
||||
*module_id,
|
||||
interns,
|
||||
roc_types::pretty_print::DebugPrint::NOTHING,
|
||||
);
|
||||
subs.rollback_to(snapshot);
|
||||
let type_str = format_var_type(var, subs, module_id, interns);
|
||||
|
||||
let range = region.to_range(self.line_info());
|
||||
|
||||
@ -356,53 +368,22 @@ impl AnalyzedDocument {
|
||||
range: Some(range),
|
||||
})
|
||||
}
|
||||
pub fn completion_items(
|
||||
&mut self,
|
||||
position: Position,
|
||||
symbol_prefix: Symbol,
|
||||
) -> Option<Vec<CompletionItem>> {
|
||||
|
||||
pub fn get_prefix_at_position(&self, position: Position) -> String {
|
||||
let line_info = self.line_info();
|
||||
let position = position.to_roc_position(&line_info);
|
||||
let AnalyzedModule {
|
||||
declarations,
|
||||
interns,
|
||||
..
|
||||
} = self.module_mut()?;
|
||||
let symbol_prefix = symbol_prefix.as_str(interns).to_string();
|
||||
|
||||
let completions = get_completions(&symbol_prefix, position, declarations, interns);
|
||||
let completion_items: Vec<CompletionItem> = completions
|
||||
let offset = position.offset;
|
||||
let source = self.source.as_bytes().split_at(offset as usize).0;
|
||||
let mut symbol = source
|
||||
.iter()
|
||||
.flat_map(|comp| match comp {
|
||||
FoundDeclaration::Decl(dec) => match dec {
|
||||
DeclarationInfo::Value { loc_symbol, .. } => vec![CompletionItem {
|
||||
label: loc_symbol.value.as_str(interns).to_string(),
|
||||
kind: Some(CompletionItemKind::VARIABLE),
|
||||
..Default::default()
|
||||
}],
|
||||
DeclarationInfo::Function { loc_symbol, .. } => vec![CompletionItem {
|
||||
label: loc_symbol.value.as_str(interns).to_string(),
|
||||
kind: Some(CompletionItemKind::FUNCTION),
|
||||
..Default::default()
|
||||
}],
|
||||
DeclarationInfo::Destructure { .. } => {
|
||||
//TODO
|
||||
vec![]
|
||||
}
|
||||
DeclarationInfo::Expectation { .. } => vec![],
|
||||
},
|
||||
FoundDeclaration::Def(def) => def
|
||||
.pattern_vars
|
||||
.iter()
|
||||
.map(|(symbol, _)| CompletionItem {
|
||||
label: symbol.as_str(interns).to_string(),
|
||||
kind: Some(CompletionItemKind::FUNCTION),
|
||||
..Default::default()
|
||||
})
|
||||
.collect(),
|
||||
})
|
||||
.collect();
|
||||
Some(completion_items)
|
||||
.rev()
|
||||
//TODO proper regex here
|
||||
.take_while(|&&a| matches!(a as char,'a'..='z'|'A'..='Z'|'0'..='9'|'_'))
|
||||
.map(|&a| a)
|
||||
.collect::<Vec<u8>>();
|
||||
symbol.reverse();
|
||||
|
||||
String::from_utf8(symbol).unwrap()
|
||||
}
|
||||
|
||||
pub fn definition(&self, symbol: Symbol) -> Option<GotoDefinitionResponse> {
|
||||
@ -449,4 +430,78 @@ impl AnalyzedDocument {
|
||||
pub(crate) fn module_url(&self, module_id: ModuleId) -> Option<Url> {
|
||||
self.module()?.module_id_to_url.get(&module_id).cloned()
|
||||
}
|
||||
|
||||
pub fn completion_items(
|
||||
&mut self,
|
||||
position: Position,
|
||||
symbol_prefix: String,
|
||||
) -> Option<Vec<CompletionItem>> {
|
||||
let mut stderr = std::io::stderr();
|
||||
writeln!(&mut stderr, "starting to get completion items");
|
||||
let line_info = self.line_info();
|
||||
let position = position.to_roc_position(&line_info);
|
||||
let AnalyzedModule {
|
||||
declarations,
|
||||
interns,
|
||||
subs,
|
||||
module_id,
|
||||
..
|
||||
} = self.module_mut()?;
|
||||
writeln!(&mut stderr, "prefix is: {:?}", symbol_prefix);
|
||||
|
||||
let completions = get_completions(&symbol_prefix, position, declarations, interns);
|
||||
let completion_items: Vec<CompletionItem> = completions
|
||||
.iter()
|
||||
.flat_map(|comp| match comp {
|
||||
FoundDeclaration::Decl(dec) => match dec {
|
||||
DeclarationInfo::Value {
|
||||
loc_symbol,
|
||||
expr_var,
|
||||
..
|
||||
} => {
|
||||
let type_str = format_var_type(expr_var.clone(), subs, module_id, interns);
|
||||
vec![CompletionItem {
|
||||
label: loc_symbol.value.as_str(interns).to_string(),
|
||||
detail: Some(type_str),
|
||||
kind: Some(CompletionItemKind::VARIABLE),
|
||||
..Default::default()
|
||||
}]
|
||||
}
|
||||
DeclarationInfo::Function {
|
||||
loc_symbol,
|
||||
expr_var,
|
||||
..
|
||||
} => {
|
||||
let type_str = format_var_type(expr_var.clone(), subs, module_id, interns);
|
||||
vec![CompletionItem {
|
||||
label: loc_symbol.value.as_str(interns).to_string(),
|
||||
detail: Some(type_str),
|
||||
kind: Some(CompletionItemKind::FUNCTION),
|
||||
..Default::default()
|
||||
}]
|
||||
}
|
||||
DeclarationInfo::Destructure { .. } => {
|
||||
//TODO
|
||||
vec![]
|
||||
}
|
||||
DeclarationInfo::Expectation { .. } => vec![],
|
||||
},
|
||||
FoundDeclaration::Def(def) => def
|
||||
.pattern_vars
|
||||
.iter()
|
||||
.map(|(symbol, var)| {
|
||||
let type_str = format_var_type(var.clone(), subs, module_id, interns);
|
||||
CompletionItem {
|
||||
label: symbol.as_str(interns).to_string(),
|
||||
detail: Some(type_str),
|
||||
kind: Some(CompletionItemKind::FUNCTION),
|
||||
|
||||
..Default::default()
|
||||
}
|
||||
})
|
||||
.collect(),
|
||||
})
|
||||
.collect();
|
||||
Some(completion_items)
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
use std::collections::HashMap;
|
||||
use std::{collections::HashMap, io::Write};
|
||||
|
||||
use tower_lsp::lsp_types::{
|
||||
CompletionResponse, Diagnostic, GotoDefinitionResponse, Hover, Position, SemanticTokensResult,
|
||||
@ -12,12 +12,47 @@ pub(crate) enum DocumentChange {
|
||||
Closed(Url),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct Document {
|
||||
latest_document: AnalyzedDocument,
|
||||
last_good_document: AnalyzedDocument,
|
||||
}
|
||||
#[derive(Debug, Default)]
|
||||
pub(crate) struct Registry {
|
||||
documents: HashMap<Url, AnalyzedDocument>,
|
||||
documents: HashMap<Url, Document>,
|
||||
}
|
||||
|
||||
impl Registry {
|
||||
fn update_document(&mut self, document: AnalyzedDocument) {
|
||||
let url = document.url().clone();
|
||||
|
||||
match self.documents.get_mut(&url) {
|
||||
Some(doc) => {
|
||||
if document.type_checked() {
|
||||
self.documents.insert(
|
||||
url.clone(),
|
||||
Document {
|
||||
//TODO not sure if i should actually be cloning here?
|
||||
latest_document: document.clone(),
|
||||
last_good_document: document,
|
||||
},
|
||||
);
|
||||
} else {
|
||||
//TODO this seems ugly but for now i'll let it slide. shoudl be immutable
|
||||
doc.latest_document = document;
|
||||
}
|
||||
}
|
||||
None => {
|
||||
self.documents.insert(
|
||||
url.clone(),
|
||||
Document {
|
||||
latest_document: document.clone(),
|
||||
last_good_document: document,
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
pub fn apply_change(&mut self, change: DocumentChange) {
|
||||
match change {
|
||||
DocumentChange::Modified(url, source) => {
|
||||
@ -27,8 +62,7 @@ impl Registry {
|
||||
// Note that this is actually the opposite of what we want - in truth we want to
|
||||
// re-evaluate all dependents!
|
||||
for document in documents {
|
||||
let url = document.url().clone();
|
||||
self.documents.insert(url.clone(), document);
|
||||
self.update_document(document);
|
||||
}
|
||||
}
|
||||
DocumentChange::Closed(_url) => {
|
||||
@ -38,6 +72,9 @@ impl Registry {
|
||||
}
|
||||
|
||||
fn document_by_url(&mut self, url: &Url) -> Option<&mut AnalyzedDocument> {
|
||||
self.documents.get_mut(url).map(|a| &mut a.latest_document)
|
||||
}
|
||||
fn document_pair_by_url(&mut self, url: &Url) -> Option<&mut Document> {
|
||||
self.documents.get_mut(url)
|
||||
}
|
||||
|
||||
@ -78,10 +115,15 @@ impl Registry {
|
||||
url: &Url,
|
||||
position: Position,
|
||||
) -> Option<CompletionResponse> {
|
||||
let document = self.document_by_url(url)?;
|
||||
let Document {
|
||||
latest_document,
|
||||
last_good_document,
|
||||
} = self.document_pair_by_url(url)?;
|
||||
let mut stderr = std::io::stderr();
|
||||
writeln!(&mut stderr, "got document");
|
||||
let symbol_prefix = latest_document.get_prefix_at_position(position);
|
||||
//this strategy probably won't work for record fields
|
||||
let prefix = document.symbol_at(position)?;
|
||||
let completions = document.completion_items(position, prefix)?;
|
||||
let completions = last_good_document.completion_items(position, symbol_prefix)?;
|
||||
|
||||
Some(CompletionResponse::Array(completions))
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user