Skip to content

Commit

Permalink
Implemented inlay hints
Browse files Browse the repository at this point in the history
  • Loading branch information
ascandone committed Dec 19, 2024
1 parent 962f3df commit 5d39782
Show file tree
Hide file tree
Showing 19 changed files with 813 additions and 60 deletions.
29 changes: 29 additions & 0 deletions compiler-core/src/ast/typed.rs
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,35 @@ pub enum TypedExpr {
}

impl TypedExpr {
/// Determines if the expression is a simple literal whose inlayHints must not be showed
/// in a pipeline chain
pub fn is_simple_lit(&self) -> bool {
match self {
TypedExpr::Int { .. }
| TypedExpr::Float { .. }
| TypedExpr::String { .. }
| TypedExpr::BitArray { .. } => true,
TypedExpr::Block { .. }
| TypedExpr::Pipeline { .. }
| TypedExpr::Var { .. }
| TypedExpr::Fn { .. }
| TypedExpr::List { .. }
| TypedExpr::Call { .. }
| TypedExpr::BinOp { .. }
| TypedExpr::Case { .. }
| TypedExpr::RecordAccess { .. }
| TypedExpr::ModuleSelect { .. }
| TypedExpr::Tuple { .. }
| TypedExpr::TupleIndex { .. }
| TypedExpr::Todo { .. }
| TypedExpr::Panic { .. }
| TypedExpr::RecordUpdate { .. }
| TypedExpr::NegateBool { .. }
| TypedExpr::NegateInt { .. }
| TypedExpr::Invalid { .. } => false,
}
}

pub fn is_println(&self) -> bool {
let fun = match self {
TypedExpr::Call { fun, args, .. } if args.len() == 1 => fun.as_ref(),
Expand Down
14 changes: 9 additions & 5 deletions compiler-core/src/language_server.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
mod code_action;
mod compiler;
mod completer;
mod configuration;
mod edits;
mod engine;
mod feedback;
mod files;
mod inlay_hints;
mod messages;
mod progress;
mod router;
Expand Down Expand Up @@ -39,13 +41,15 @@ pub trait DownloadDependencies {
fn download_dependencies(&self, paths: &ProjectPaths) -> Result<Manifest>;
}

pub fn src_span_to_lsp_range(location: SrcSpan, line_numbers: &LineNumbers) -> Range {
let start = line_numbers.line_and_column_number(location.start);
let end = line_numbers.line_and_column_number(location.end);
pub fn src_offset_to_lsp_position(offset: u32, line_numbers: &LineNumbers) -> Position {
let line_col = line_numbers.line_and_column_number(offset);
Position::new(line_col.line - 1, line_col.column - 1)
}

pub fn src_span_to_lsp_range(location: SrcSpan, line_numbers: &LineNumbers) -> Range {
Range::new(
Position::new(start.line - 1, start.column - 1),
Position::new(end.line - 1, end.column - 1),
src_offset_to_lsp_position(location.start, line_numbers),
src_offset_to_lsp_position(location.end, line_numbers),
)
}

Expand Down
17 changes: 17 additions & 0 deletions compiler-core/src/language_server/configuration.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
use serde::Deserialize;
use std::sync::{Arc, RwLock};

pub type SharedConfig = Arc<RwLock<Configuration>>;

#[derive(Debug, Default, Clone, Deserialize, PartialEq, Eq)]
#[serde(default)]
pub struct Configuration {
pub inlay_hints: InlayHintsConfig,
}

#[derive(Debug, Default, Clone, Deserialize, PartialEq, Eq)]
#[serde(default)]
pub struct InlayHintsConfig {
/// Whether to show type inlay hints of multiline pipelines
pub pipelines: bool,
}
32 changes: 30 additions & 2 deletions compiler-core/src/language_server/engine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ use ecow::EcoString;
use itertools::Itertools;
use lsp::CodeAction;
use lsp_types::{
self as lsp, DocumentSymbol, Hover, HoverContents, MarkedString, Position, Range,
self as lsp, DocumentSymbol, Hover, HoverContents, InlayHint, MarkedString, Position, Range,
SignatureHelp, SymbolKind, SymbolTag, TextEdit, Url,
};
use std::sync::Arc;
Expand All @@ -36,7 +36,8 @@ use super::{
LetAssertToCase, RedundantTupleInCaseSubject,
},
completer::Completer,
signature_help, src_span_to_lsp_range, DownloadDependencies, MakeLocker,
configuration::SharedConfig,
inlay_hints, signature_help, src_span_to_lsp_range, DownloadDependencies, MakeLocker,
};

#[derive(Debug, PartialEq, Eq)]
Expand Down Expand Up @@ -75,6 +76,9 @@ pub struct LanguageServerEngine<IO, Reporter> {
/// Used to know if to show the "View on HexDocs" link
/// when hovering on an imported value
hex_deps: std::collections::HashSet<EcoString>,

/// Configuration the user has set in their editor.
pub(crate) user_config: SharedConfig,
}

impl<'a, IO, Reporter> LanguageServerEngine<IO, Reporter>
Expand All @@ -95,6 +99,7 @@ where
progress_reporter: Reporter,
io: FileSystemProxy<IO>,
paths: ProjectPaths,
user_config: SharedConfig,
) -> Result<Self> {
let locker = io.inner().make_locker(&paths, config.target)?;

Expand Down Expand Up @@ -131,6 +136,7 @@ where
paths,
error: None,
hex_deps,
user_config,
})
}

Expand Down Expand Up @@ -462,6 +468,28 @@ where
})
}

pub fn inlay_hints(&mut self, params: lsp::InlayHintParams) -> Response<Vec<InlayHint>> {
self.respond(|this| {
let Ok(config) = this.user_config.read() else {
return Ok(vec![]);
};

if !config.inlay_hints.pipelines {
return Ok(vec![]);
}

let Some(module) = this.module_for_uri(&params.text_document.uri) else {
return Ok(vec![]);
};

let line_numbers = LineNumbers::new(&module.code);

let hints = inlay_hints::get_inlay_hints(module.ast.clone(), &line_numbers);

Ok(hints)
})
}

fn respond<T>(&mut self, handler: impl FnOnce(&mut Self) -> Result<T>) -> Response<T> {
let result = handler(self);
let warnings = self.take_warnings();
Expand Down
109 changes: 109 additions & 0 deletions compiler-core/src/language_server/inlay_hints.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
use lsp_types::{InlayHint, InlayHintKind, InlayHintLabel};

use crate::{
ast::{
visit::{self, Visit},
SrcSpan, TypedAssignment, TypedExpr, TypedModule,
},
line_numbers::LineNumbers,
type_::pretty::Printer,
};

use super::src_offset_to_lsp_position;

struct InlayHintsVisitor<'a> {
hints: Vec<InlayHint>,
line_numbers: &'a LineNumbers,
}

impl<'a> InlayHintsVisitor<'a> {
fn new(line_numbers: &'a LineNumbers) -> InlayHintsVisitor<'a> {
InlayHintsVisitor {
hints: vec![],
line_numbers,
}
}
}

fn default_inlay_hint(line_numbers: &LineNumbers, offset: u32, label: String) -> InlayHint {
let position = src_offset_to_lsp_position(offset, line_numbers);

InlayHint {
position,
label: InlayHintLabel::String(label),
kind: Some(InlayHintKind::TYPE),
text_edits: None,
tooltip: None,
padding_left: Some(true),
padding_right: None,
data: None,
}
}

impl<'ast> Visit<'ast> for InlayHintsVisitor<'_> {
fn visit_typed_expr_pipeline(
&mut self,
_location: &'ast SrcSpan,
assignments: &'ast [TypedAssignment],
finally: &'ast TypedExpr,
) {
let mut prev_hint: Option<(u32, Option<InlayHint>)> = None;
for assign in assignments {
let this_line: u32 = self
.line_numbers
.line_and_column_number(assign.location.end)
.line;

if let Some((prev_line, prev_hint)) = prev_hint {
if prev_line != this_line {
if let Some(prev_hint) = prev_hint {
self.hints.push(prev_hint);
}
}
};

let this_hint = default_inlay_hint(
self.line_numbers,
assign.location.end,
Printer::new().pretty_print(assign.type_().as_ref(), 0),
);

prev_hint = Some((
this_line,
if assign.value.is_simple_lit() {
None
} else {
Some(this_hint)
},
));

visit::visit_typed_expr(self, &assign.value);
}

if let Some((prev_line, prev_hint)) = prev_hint {
let this_line = self
.line_numbers
.line_and_column_number(finally.location().end)
.line;
if this_line != prev_line {
if let Some(prev_hint) = prev_hint {
self.hints.push(prev_hint);
}
let hint = default_inlay_hint(
self.line_numbers,
finally.location().end,
Printer::new().pretty_print(finally.type_().as_ref(), 0),
);
self.hints.push(hint);
}
}

visit::visit_typed_expr(self, finally);
}
}

pub fn get_inlay_hints(typed_module: TypedModule, line_numbers: &LineNumbers) -> Vec<InlayHint> {
let mut visitor = InlayHintsVisitor::new(line_numbers);
visitor.visit_typed_module(&typed_module);
visitor.hints
}
Loading

0 comments on commit 5d39782

Please sign in to comment.