@@ -6,7 +6,7 @@ use futures_util::FutureExt;
6use helix_core::auto_pairs::AutoPairs;
6use helix_core::auto_pairs::AutoPairs;
7use helix_core::doc_formatter::TextFormat;
7use helix_core::doc_formatter::TextFormat;
8use helix_core::syntax::Highlight;
8use helix_core::syntax::Highlight;
9−use helix_core::text_annotations::TextAnnotations;
9+use helix_core::text_annotations::{InlineAnnotation, TextAnnotations};
10use helix_core::Range;
10use helix_core::Range;
11use helix_vcs::{DiffHandle, DiffProviderRegistry};
11use helix_vcs::{DiffHandle, DiffProviderRegistry};
@@ -19,6 +19,7 @@ use std::collections::HashMap;
19use std::fmt::Display;
19use std::fmt::Display;
20use std::future::Future;
20use std::future::Future;
21use std::path::{Path, PathBuf};
21use std::path::{Path, PathBuf};
22use std::str::FromStr;
23use std::str::FromStr;
23use std::sync::{Arc, Weak};
24use std::sync::{Arc, Weak};
24use std::time::SystemTime;
25use std::time::SystemTime;
@@ -119,6 +120,14 @@ pub struct Document {
119 text: Rope,
120 text: Rope,
120 selections: HashMap<ViewId, Selection>,
121 selections: HashMap<ViewId, Selection>,
123+ /// Inlay hints annotations for the document, by view.
125+ /// To know if they're up-to-date, check the `id` field in `DocumentInlayHints`.
126+ pub(crate) inlay_hints: HashMap<ViewId, DocumentInlayHints>,
127+ /// Set to `true` when the document is updated, reset to `false` on the next inlay hints
128+ /// update from the LSP
129+ pub inlay_hints_oudated: bool,
122 path: Option<PathBuf>,
131 path: Option<PathBuf>,
123 encoding: &'static encoding::Encoding,
132 encoding: &'static encoding::Encoding,
@@ -162,6 +171,73 @@ pub struct Document {
162 version_control_head: Option<Arc<ArcSwap<Box<str>>>>,
171 version_control_head: Option<Arc<ArcSwap<Box<str>>>>,
174+/// Inlay hints for a single `(Document, View)` combo.
176+/// There are `*_inlay_hints` field for each kind of hints an LSP can send since we offer the
177+/// option to style theme differently in the theme according to the (currently supported) kinds
178+/// (`type`, `parameter` and the rest).
180+/// Inlay hints are always `InlineAnnotation`s, not overlays or line-ones: LSP may choose to place
181+/// them anywhere in the text and will sometime offer config options to move them where the user
182+/// wants them but it shouldn't be Helix who decides that so we use the most precise positioning.
184+/// The padding for inlay hints needs to be stored separately for before and after (the LSP spec
185+/// uses 'left' and 'right' but not all text is left to right so let's be correct) padding because
186+/// the 'before' padding must be added to a layer *before* the regular inlay hints and the 'after'
187+/// padding comes ... after.
188+#[derive(Debug, Clone)]
189+pub struct DocumentInlayHints {
190+ /// Identifier for the inlay hints stored in this structure. To be checked to know if they have
191+ /// to be recomputed on idle or not.
192+ pub id: DocumentInlayHintsId,
194+ /// Inlay hints of `TYPE` kind, if any.
195+ pub type_inlay_hints: Rc<[InlineAnnotation]>,
197+ /// Inlay hints of `PARAMETER` kind, if any.
198+ pub parameter_inlay_hints: Rc<[InlineAnnotation]>,
200+ /// Inlay hints that are neither `TYPE` nor `PARAMETER`.
202+ /// LSPs are not required to associate a kind to their inlay hints, for example Rust-Analyzer
203+ /// currently never does (February 2023) and the LSP spec may add new kinds in the future that
204+ /// we want to display even if we don't have some special highlighting for them.
205+ pub other_inlay_hints: Rc<[InlineAnnotation]>,
207+ /// Inlay hint padding. When creating the final `TextAnnotations`, the `before` padding must be
208+ /// added first, then the regular inlay hints, then the `after` padding.
209+ pub padding_before_inlay_hints: Rc<[InlineAnnotation]>,
210+ pub padding_after_inlay_hints: Rc<[InlineAnnotation]>,
213+impl DocumentInlayHints {
214+ /// Generate an empty list of inlay hints with the given ID.
215+ pub fn empty_with_id(id: DocumentInlayHintsId) -> Self {
218+ type_inlay_hints: Rc::new([]),
219+ parameter_inlay_hints: Rc::new([]),
220+ other_inlay_hints: Rc::new([]),
221+ padding_before_inlay_hints: Rc::new([]),
222+ padding_after_inlay_hints: Rc::new([]),
227+/// Associated with a [`Document`] and [`ViewId`], uniquely identifies the state of inlay hints for
228+/// for that document and view: if this changed since the last save, the inlay hints for the view
229+/// should be recomputed.
231+/// We can't store the `ViewOffset` instead of the first and last asked-for lines because if
232+/// softwrapping changes, the `ViewOffset` may not change while the displayed lines will.
233+#[derive(Copy, Clone, PartialEq, Eq)]
234+pub struct DocumentInlayHintsId {
235+ /// First line for which the inlay hints were requested.
236+ pub first_line: usize,
237+ /// Last line for which the inlay hints were requested.
238+ pub last_line: usize,
165use std::{fmt, mem};
241use std::{fmt, mem};
166impl fmt::Debug for Document {
242impl fmt::Debug for Document {
167 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
243 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
@@ -169,6 +245,8 @@ impl fmt::Debug for Document {
169 .field("id", &self.id)
245 .field("id", &self.id)
170 .field("text", &self.text)
246 .field("text", &self.text)
171 .field("selections", &self.selections)
247 .field("selections", &self.selections)
248+ .field("inlay_hints_oudated", &self.inlay_hints_oudated)
249+ .field("text_annotations", &self.inlay_hints)
172 .field("path", &self.path)
250 .field("path", &self.path)
173 .field("encoding", &self.encoding)
251 .field("encoding", &self.encoding)
174 .field("restore_cursor", &self.restore_cursor)
252 .field("restore_cursor", &self.restore_cursor)
@@ -187,6 +265,15 @@ impl fmt::Debug for Document {
268+impl fmt::Debug for DocumentInlayHintsId {
269+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
270+ // Much more agreable to read when debugging
271+ f.debug_struct("DocumentInlayHintsId")
272+ .field("lines", &(self.first_line..self.last_line))
190// The documentation and implementation of this function should be up-to-date with
277// The documentation and implementation of this function should be up-to-date with
191// its sibling function, `to_writer()`.
278// its sibling function, `to_writer()`.
@@ -389,6 +476,8 @@ impl Document {
389 encoding,
476 encoding,
391 selections: HashMap::default(),
478 selections: HashMap::default(),
479+ inlay_hints: HashMap::default(),
480+ inlay_hints_oudated: false,
392 indent_style: DEFAULT_INDENT,
481 indent_style: DEFAULT_INDENT,
393 line_ending: DEFAULT_LINE_ENDING,
482 line_ending: DEFAULT_LINE_ENDING,
394 restore_cursor: false,
483 restore_cursor: false,
@@ -819,13 +908,16 @@ impl Document {
822− /// Remove a view's selection from this document.
911+ /// Remove a view's selection and inlay hints from this document.
823 pub fn remove_view(&mut self, view_id: ViewId) {
912 pub fn remove_view(&mut self, view_id: ViewId) {
824 self.selections.remove(&view_id);
913 self.selections.remove(&view_id);
914+ self.inlay_hints.remove(&view_id);
827 /// Apply a [`Transaction`] to the [`Document`] to change its text.
917 /// Apply a [`Transaction`] to the [`Document`] to change its text.
828 fn apply_impl(&mut self, transaction: &Transaction, view_id: ViewId) -> bool {
918 fn apply_impl(&mut self, transaction: &Transaction, view_id: ViewId) -> bool {
919+ use helix_core::Assoc;
829 let old_doc = self.text().clone();
921 let old_doc = self.text().clone();
831 let success = transaction.changes().apply(&mut self.text);
923 let success = transaction.changes().apply(&mut self.text);
@@ -881,10 +973,10 @@ impl Document {
881 .unwrap();
973 .unwrap();
976+ let changes = transaction.changes();
884 // map state.diagnostics over changes::map_pos too
978 // map state.diagnostics over changes::map_pos too
885 for diagnostic in &mut self.diagnostics {
979 for diagnostic in &mut self.diagnostics {
886− use helix_core::Assoc;
887− let changes = transaction.changes();
888 diagnostic.range.start = changes.map_pos(diagnostic.range.start, Assoc::After);
980 diagnostic.range.start = changes.map_pos(diagnostic.range.start, Assoc::After);
889 diagnostic.range.end = changes.map_pos(diagnostic.range.end, Assoc::After);
981 diagnostic.range.end = changes.map_pos(diagnostic.range.end, Assoc::After);
890 diagnostic.line = self.text.char_to_line(diagnostic.range.start);
982 diagnostic.line = self.text.char_to_line(diagnostic.range.start);
@@ -892,13 +984,40 @@ impl Document {
892 self.diagnostics
984 self.diagnostics
893 .sort_unstable_by_key(|diagnostic| diagnostic.range);
985 .sort_unstable_by_key(|diagnostic| diagnostic.range);
987+ // Update the inlay hint annotations' positions, helping ensure they are displayed in the proper place
988+ let apply_inlay_hint_changes = |annotations: &mut Rc<[InlineAnnotation]>| {
989+ if let Some(data) = Rc::get_mut(annotations) {
990+ for inline in data.iter_mut() {
991+ inline.char_idx = changes.map_pos(inline.char_idx, Assoc::After);
996+ self.inlay_hints_oudated = true;
997+ for text_annotation in self.inlay_hints.values_mut() {
998+ let DocumentInlayHints {
1001+ parameter_inlay_hints,
1003+ padding_before_inlay_hints,
1004+ padding_after_inlay_hints,
1005+ } = text_annotation;
1007+ apply_inlay_hint_changes(padding_before_inlay_hints);
1008+ apply_inlay_hint_changes(type_inlay_hints);
1009+ apply_inlay_hint_changes(parameter_inlay_hints);
1010+ apply_inlay_hint_changes(other_inlay_hints);
1011+ apply_inlay_hint_changes(padding_after_inlay_hints);
895 // emit lsp notification
1014 // emit lsp notification
896 if let Some(language_server) = self.language_server() {
1015 if let Some(language_server) = self.language_server() {
897 let notify = language_server.text_document_did_change(
1016 let notify = language_server.text_document_did_change(
898 self.versioned_identifier(),
1017 self.versioned_identifier(),
899 &old_doc,
1018 &old_doc,
900 self.text(),
1019 self.text(),
901− transaction.changes(),
1020+ changes,
904 if let Some(notify) = notify {
1023 if let Some(notify) = notify {
@@ -1217,6 +1336,7 @@ impl Document {
1217 &self.selections[&view_id]
1336 &self.selections[&view_id]
1220 pub fn selections(&self) -> &HashMap<ViewId, Selection> {
1340 pub fn selections(&self) -> &HashMap<ViewId, Selection> {
1221 &self.selections
1341 &self.selections
@@ -1355,9 +1475,27 @@ impl Document {
1478+ /// Get the text annotations that apply to the whole document, those that do not apply to any
1358 pub fn text_annotations(&self, _theme: Option<&Theme>) -> TextAnnotations {
1480 pub fn text_annotations(&self, _theme: Option<&Theme>) -> TextAnnotations {
1359 TextAnnotations::default()
1481 TextAnnotations::default()
1484+ /// Set the inlay hints for this document and `view_id`.
1485+ pub fn set_inlay_hints(&mut self, view_id: ViewId, inlay_hints: DocumentInlayHints) {
1486+ self.inlay_hints.insert(view_id, inlay_hints);
1489+ /// Get the inlay hints for this document and `view_id`.
1490+ pub fn inlay_hints(&self, view_id: ViewId) -> Option<&DocumentInlayHints> {
1491+ self.inlay_hints.get(&view_id)
1494+ /// Completely removes all the inlay hints saved for the document, dropping them to free memory
1495+ /// (since it often means inlay hints have been fully deactivated).
1496+ pub fn reset_all_inlay_hints(&mut self) {
1497+ self.inlay_hints = Default::default();
1363#[derive(Clone, Debug)]
1501#[derive(Clone, Debug)]