Start adding type-information to tooltips
This commit is contained in:
parent
6619f1f0a9
commit
7d3aaa143a
@ -1,8 +1,10 @@
|
||||
use std::collections::HashMap;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use dashmap::DashMap;
|
||||
use reid::ast::lexer::{FullToken, Position};
|
||||
use reid::{compile_module, parse_module};
|
||||
use reid::mir::{self, Context, StructType, TypeKind};
|
||||
use reid::{compile_module, parse_module, perform_all_passes};
|
||||
use tower_lsp::jsonrpc::Result;
|
||||
use tower_lsp::lsp_types::{
|
||||
self, CompletionItem, CompletionOptions, CompletionParams, CompletionResponse, Diagnostic, DiagnosticSeverity,
|
||||
@ -18,6 +20,7 @@ struct Backend {
|
||||
client: Client,
|
||||
tokens: DashMap<String, Vec<FullToken>>,
|
||||
ast: DashMap<String, reid::ast::Module>,
|
||||
types: DashMap<String, DashMap<FullToken, Option<TypeKind>>>,
|
||||
}
|
||||
|
||||
#[tower_lsp::async_trait]
|
||||
@ -72,7 +75,7 @@ impl LanguageServer for Backend {
|
||||
async fn hover(&self, params: HoverParams) -> Result<Option<Hover>> {
|
||||
let path = PathBuf::from(params.text_document_position_params.text_document.uri.path());
|
||||
let file_name = path.file_name().unwrap().to_str().unwrap().to_owned();
|
||||
let tokens = self.tokens.get(&file_name).unwrap();
|
||||
let tokens = self.tokens.get(&file_name);
|
||||
let position = params.text_document_position_params.position;
|
||||
|
||||
self.client
|
||||
@ -81,20 +84,33 @@ impl LanguageServer for Backend {
|
||||
format!("line {}, col {}", position.line, position.character),
|
||||
)
|
||||
.await;
|
||||
let token = tokens.iter().find(|tok| {
|
||||
tok.position.1 == position.line + 1
|
||||
&& (tok.position.0 <= position.character + 1
|
||||
&& (tok.position.0 + tok.token.len() as u32) > position.character + 1)
|
||||
});
|
||||
let token = if let Some(tokens) = &tokens {
|
||||
tokens.iter().find(|tok| {
|
||||
tok.position.1 == position.line + 1
|
||||
&& (tok.position.0 <= position.character + 1
|
||||
&& (tok.position.0 + tok.token.len() as u32) > position.character + 1)
|
||||
})
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let ty = if let Some(token) = token {
|
||||
if let Some(ty) = self.types.get(&file_name).unwrap().get(token) {
|
||||
ty.clone()
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
Ok(Some(Hover {
|
||||
contents: HoverContents::Scalar(MarkedString::String(format!("{:?}", token))),
|
||||
contents: HoverContents::Scalar(MarkedString::String(format!("{:?}", ty))),
|
||||
range: None,
|
||||
}))
|
||||
}
|
||||
|
||||
async fn did_open(&self, params: DidOpenTextDocumentParams) {
|
||||
self.client.log_message(MessageType::INFO, "opened!").await;
|
||||
self.recompile(TextDocumentItem {
|
||||
uri: params.text_document.uri,
|
||||
language_id: params.text_document.language_id,
|
||||
@ -105,7 +121,6 @@ impl LanguageServer for Backend {
|
||||
}
|
||||
|
||||
async fn did_change(&self, params: DidChangeTextDocumentParams) {
|
||||
self.client.log_message(MessageType::INFO, "changed!").await;
|
||||
self.recompile(TextDocumentItem {
|
||||
text: params.content_changes[0].text.clone(),
|
||||
uri: params.text_document.uri,
|
||||
@ -124,6 +139,7 @@ impl Backend {
|
||||
|
||||
let mut reid_error = None;
|
||||
let mut tokens = None;
|
||||
let token_types = DashMap::new();
|
||||
|
||||
match parse_module(¶ms.text, file_name.clone(), &mut map) {
|
||||
Ok(module) => {
|
||||
@ -131,14 +147,34 @@ impl Backend {
|
||||
.log_message(MessageType::INFO, format!("successfully parsed!"))
|
||||
.await;
|
||||
tokens = Some(module.1.clone());
|
||||
match compile_module(module.0, module.1, &mut map, Some(path), true) {
|
||||
Ok(_) => {}
|
||||
match compile_module(module.0, module.1, &mut map, Some(path.clone()), true) {
|
||||
Ok(module) => {
|
||||
let module_id = module.module_id;
|
||||
let mut context = Context::from(vec![module], path.parent().unwrap().to_owned());
|
||||
match perform_all_passes(&mut context, &mut map) {
|
||||
Ok(_) => {
|
||||
for module in context.modules.values() {
|
||||
if module.module_id != module_id {
|
||||
continue;
|
||||
}
|
||||
for (idx, token) in module.tokens.iter().enumerate() {
|
||||
token_types.insert(token.clone(), find_type_in_context(&module, idx));
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
reid_error = Some(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
reid_error = Some(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(_) => {}
|
||||
Err(e) => {
|
||||
reid_error = Some(e);
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(tokens) = &tokens {
|
||||
@ -151,9 +187,6 @@ impl Backend {
|
||||
.into_position(&tokens)
|
||||
.unwrap_or((Position(0, 0), Position(0, 0)));
|
||||
self.client.log_message(MessageType::INFO, format!("{:?}", &meta)).await;
|
||||
self.client
|
||||
.log_message(MessageType::INFO, format!("{:?}", &tokens))
|
||||
.await;
|
||||
self.client
|
||||
.log_message(MessageType::INFO, format!("{:?}", &positions))
|
||||
.await;
|
||||
@ -193,6 +226,7 @@ impl Backend {
|
||||
if let Some(tokens) = tokens.take() {
|
||||
self.tokens.insert(file_name.clone(), tokens);
|
||||
}
|
||||
self.types.insert(file_name.clone(), token_types);
|
||||
}
|
||||
}
|
||||
|
||||
@ -205,6 +239,55 @@ async fn main() {
|
||||
client,
|
||||
ast: DashMap::new(),
|
||||
tokens: DashMap::new(),
|
||||
types: DashMap::new(),
|
||||
});
|
||||
Server::new(stdin, stdout, socket).serve(service).await;
|
||||
}
|
||||
|
||||
pub fn find_type_in_context(module: &mir::Module, token_idx: usize) -> Option<TypeKind> {
|
||||
for import in &module.imports {
|
||||
if import.1.contains(token_idx) {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
for typedef in &module.typedefs {
|
||||
if !typedef.meta.contains(token_idx) {
|
||||
continue;
|
||||
}
|
||||
|
||||
match &typedef.kind {
|
||||
mir::TypeDefinitionKind::Struct(StructType(fields)) => {
|
||||
for field in fields {
|
||||
if field.2.contains(token_idx) {
|
||||
return Some(field.1.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for function in &module.functions {
|
||||
if !(function.signature() + function.block_meta()).contains(token_idx) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for param in &function.parameters {
|
||||
if param.meta.contains(token_idx) {
|
||||
return Some(param.ty.clone());
|
||||
}
|
||||
}
|
||||
|
||||
return match &function.kind {
|
||||
mir::FunctionDefinitionKind::Local(block, _) => find_type_in_block(&block, token_idx),
|
||||
mir::FunctionDefinitionKind::Extern(_) => None,
|
||||
mir::FunctionDefinitionKind::Intrinsic(_) => None,
|
||||
};
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
pub fn find_type_in_block(block: &mir::Block, token_idx: usize) -> Option<TypeKind> {
|
||||
for statement in &block.statements {}
|
||||
|
||||
None
|
||||
}
|
||||
|
@ -11,6 +11,7 @@ default = ["color"]
|
||||
|
||||
color = ["colored"]
|
||||
log_output = []
|
||||
context_debug = []
|
||||
|
||||
[dependencies]
|
||||
## Make it easier to generate errors
|
||||
|
@ -7,7 +7,7 @@ static HEXADECIMAL_NUMERICS: &[char] = &[
|
||||
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f',
|
||||
];
|
||||
|
||||
#[derive(Eq, PartialEq, Clone, PartialOrd, Ord)]
|
||||
#[derive(Eq, PartialEq, Clone, PartialOrd, Ord, Hash)]
|
||||
pub enum Token {
|
||||
/// Values
|
||||
Identifier(String),
|
||||
@ -211,7 +211,7 @@ impl std::fmt::Debug for Token {
|
||||
}
|
||||
|
||||
/// A token with a position
|
||||
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord)]
|
||||
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct FullToken {
|
||||
pub token: Token,
|
||||
pub position: Position,
|
||||
|
@ -193,7 +193,7 @@ pub struct FunctionDefinition(pub FunctionSignature, pub bool, pub Block, pub To
|
||||
pub struct FunctionSignature {
|
||||
pub name: String,
|
||||
pub self_kind: SelfKind,
|
||||
pub params: Vec<(String, Type)>,
|
||||
pub params: Vec<(String, Type, TokenRange)>,
|
||||
pub return_type: Option<Type>,
|
||||
#[allow(dead_code)]
|
||||
pub range: TokenRange,
|
||||
|
@ -668,7 +668,7 @@ impl Parse for FunctionDefinition {
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct FunctionParam(String, Type);
|
||||
struct FunctionParam(String, Type, TokenRange);
|
||||
|
||||
impl Parse for FunctionParam {
|
||||
fn parse(mut stream: TokenStream) -> Result<Self, Error> {
|
||||
@ -676,7 +676,7 @@ impl Parse for FunctionParam {
|
||||
return Err(stream.expected_err("parameter name")?);
|
||||
};
|
||||
stream.expect(Token::Colon)?;
|
||||
Ok(FunctionParam(arg_name, stream.parse()?))
|
||||
Ok(FunctionParam(arg_name, stream.parse()?, stream.get_range().unwrap()))
|
||||
}
|
||||
}
|
||||
|
||||
@ -738,11 +738,11 @@ impl Parse for FunctionSignature {
|
||||
match &self_kind {
|
||||
SelfKind::None => {
|
||||
if let Ok(param) = stream.parse::<FunctionParam>() {
|
||||
params.push((param.0, param.1));
|
||||
params.push((param.0, param.1, param.2));
|
||||
while let Some(Token::Comma) = stream.peek() {
|
||||
stream.next();
|
||||
let param = stream.parse::<FunctionParam>()?;
|
||||
params.push((param.0, param.1));
|
||||
params.push((param.0, param.1, param.2));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -750,7 +750,7 @@ impl Parse for FunctionSignature {
|
||||
while let Some(Token::Comma) = stream.peek() {
|
||||
stream.next();
|
||||
let param = stream.parse::<FunctionParam>()?;
|
||||
params.push((param.0, param.1));
|
||||
params.push((param.0, param.1, param.2));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -51,7 +51,7 @@ impl ast::Module {
|
||||
.map(|p| mir::FunctionParam {
|
||||
name: p.0,
|
||||
ty: p.1 .0.into_mir(module_id),
|
||||
meta: p.1 .1.as_meta(module_id),
|
||||
meta: p.2.as_meta(module_id),
|
||||
})
|
||||
.collect(),
|
||||
kind: mir::FunctionDefinitionKind::Extern(false),
|
||||
@ -164,7 +164,7 @@ impl ast::FunctionDefinition {
|
||||
params.extend(signature.params.iter().cloned().map(|p| FunctionParam {
|
||||
name: p.0,
|
||||
ty: p.1 .0.into_mir(module_id),
|
||||
meta: p.1 .1.as_meta(module_id),
|
||||
meta: p.2.as_meta(module_id),
|
||||
}));
|
||||
mir::FunctionDefinition {
|
||||
name: signature.name.clone(),
|
||||
|
@ -273,6 +273,9 @@ pub fn perform_all_passes<'map>(
|
||||
));
|
||||
}
|
||||
|
||||
#[cfg(feature = "context_debug")]
|
||||
dbg!(&context);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -43,6 +43,18 @@ impl Metadata {
|
||||
pub fn into_positions(&self, tokens: &Vec<FullToken>) -> Option<(Position, Position)> {
|
||||
self.range.into_position(tokens)
|
||||
}
|
||||
|
||||
pub fn is_after(&self, token_idx: usize) -> bool {
|
||||
return token_idx < self.range.start;
|
||||
}
|
||||
|
||||
pub fn is_before(&self, token_idx: usize) -> bool {
|
||||
return token_idx > self.range.end;
|
||||
}
|
||||
|
||||
pub fn contains(&self, token_idx: usize) -> bool {
|
||||
return token_idx >= self.range.start && token_idx <= self.range.end;
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::Add for Metadata {
|
||||
|
Loading…
Reference in New Issue
Block a user