Add conceal support

This commit is contained in:
ef3d0c3e 2024-12-05 09:40:00 +01:00
parent de983979c4
commit 0fddb56b03
7 changed files with 214 additions and 6 deletions

View file

@ -23,6 +23,7 @@ use crate::parser::source::Cursor;
use crate::parser::source::Token; use crate::parser::source::Token;
use crate::parser::source::VirtualSource; use crate::parser::source::VirtualSource;
use crate::parser::util; use crate::parser::util;
use lsp::conceal::Conceals;
use lsp::hints::Hints; use lsp::hints::Hints;
use parser::util::escape_source; use parser::util::escape_source;
use regex::Regex; use regex::Regex;
@ -330,6 +331,7 @@ impl Rule for ListRule {
offset.unwrap_or(usize::MAX), offset.unwrap_or(usize::MAX),
); );
// Semantic
if let Some((sems, tokens)) = if let Some((sems, tokens)) =
Semantics::from_source(cursor.source.clone(), &state.shared.lsp) Semantics::from_source(cursor.source.clone(), &state.shared.lsp)
{ {
@ -340,12 +342,30 @@ impl Rule for ListRule {
} }
} }
if let Some(hints) = if let Some(conceals) =
Hints::from_source(cursor.source.clone(), &state.shared.lsp) Conceals::from_source(cursor.source.clone(), &state.shared.lsp)
{ {
let mut i = captures.get(1).unwrap().start();
for (numbered, _) in &depth {
conceals.add(
i..i + 1,
lsp::conceal::ConcealTarget::Highlight {
text: if *numbered {
"".into()
} else {
"⦿".into()
},
highlight_group: "Function".into(),
},
);
i += 1;
}
}
// Hints
if let Some(hints) = Hints::from_source(cursor.source.clone(), &state.shared.lsp) {
let mut label = String::new(); let mut label = String::new();
for (_, id) in &depth for (_, id) in &depth {
{
if !label.is_empty() { if !label.is_empty() {
label.push('.'); label.push('.');
} }

138
src/lsp/conceal.rs Normal file
View file

@ -0,0 +1,138 @@
use std::cell::Ref;
use std::cell::RefCell;
use std::ops::Range;
use std::rc::Rc;
use serde::Deserialize;
use serde::Serialize;
use tower_lsp::jsonrpc::Request;
use tower_lsp::jsonrpc::{self};
use tower_lsp::lsp_types::Position;
use crate::parser::source::LineCursor;
use crate::parser::source::Source;
use crate::parser::source::SourceFile;
use crate::parser::source::SourcePosition;
use crate::parser::source::VirtualSource;
use super::data::LSPData;
#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ConcealParams {
pub text_document: tower_lsp::lsp_types::TextDocumentIdentifier,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct ConcealInfo {
pub range: tower_lsp::lsp_types::Range,
pub conceal_text: ConcealTarget,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub enum ConcealTarget {
Text(String),
Highlight {
text: String,
highlight_group: String,
},
}
/// Per file conceals
#[derive(Debug)]
pub struct ConcealsData {
/// The current cursor
cursor: RefCell<LineCursor>,
/// The conceals
pub conceals: RefCell<Vec<ConcealInfo>>,
}
impl ConcealsData {
pub fn new(source: Rc<dyn Source>) -> Self {
Self {
cursor: RefCell::new(LineCursor::new(source)),
conceals: RefCell::new(vec![]),
}
}
}
/// Temporary data returned by [`Self::from_source_impl`]
#[derive(Debug)]
pub struct Conceals<'a> {
pub(self) conceals: Ref<'a, ConcealsData>,
// The source used when resolving the parent source
pub(self) original_source: Rc<dyn Source>,
/// The resolved parent source
pub(self) source: Rc<dyn Source>,
}
impl<'a> Conceals<'a> {
fn from_source_impl(
source: Rc<dyn Source>,
lsp: &'a Option<RefCell<LSPData>>,
original_source: Rc<dyn Source>,
) -> Option<Self> {
if (source.name().starts_with(":LUA:") || source.name().starts_with(":VAR:"))
&& source.downcast_ref::<VirtualSource>().is_some()
{
return None;
}
if let Some(location) = source
.clone()
.downcast_rc::<VirtualSource>()
.ok()
.as_ref()
.map(|parent| parent.location())
.unwrap_or(None)
{
return Self::from_source_impl(location.source(), lsp, original_source);
} else if let Ok(source) = source.clone().downcast_rc::<SourceFile>() {
return Ref::filter_map(lsp.as_ref().unwrap().borrow(), |lsp: &LSPData| {
lsp.conceals.get(&(source.clone() as Rc<dyn Source>))
})
.ok()
.map(|conceals| Self {
conceals,
source,
original_source,
});
}
None
}
pub fn from_source(source: Rc<dyn Source>, lsp: &'a Option<RefCell<LSPData>>) -> Option<Self> {
if lsp.is_none() {
return None;
}
Self::from_source_impl(source.clone(), lsp, source)
}
pub fn add(&self, range: Range<usize>, text: ConcealTarget) {
let range = self.original_source.original_range(range).1;
let mut cursor = self.conceals.cursor.borrow_mut();
cursor.move_to(range.start);
let line = cursor.line;
let start_char = cursor.line_pos;
cursor.move_to(range.end);
assert_eq!(line, cursor.line);
let end_char = cursor.line_pos;
self.conceals.conceals.borrow_mut().push(ConcealInfo {
range: tower_lsp::lsp_types::Range {
start: Position {
line: line as u32,
character: start_char as u32,
},
end: Position {
line: line as u32,
character: end_char as u32,
},
},
conceal_text: text,
})
}
}

View file

@ -3,6 +3,7 @@ use std::rc::Rc;
use crate::parser::source::Source; use crate::parser::source::Source;
use super::conceal::ConcealsData;
use super::definition::DefinitionData; use super::definition::DefinitionData;
use super::hints::HintsData; use super::hints::HintsData;
use super::semantic::SemanticsData; use super::semantic::SemanticsData;
@ -14,6 +15,7 @@ pub struct LSPData {
pub semantic_data: HashMap<Rc<dyn Source>, SemanticsData>, pub semantic_data: HashMap<Rc<dyn Source>, SemanticsData>,
pub inlay_hints: HashMap<Rc<dyn Source>, HintsData>, pub inlay_hints: HashMap<Rc<dyn Source>, HintsData>,
pub definitions: HashMap<Rc<dyn Source>, DefinitionData>, pub definitions: HashMap<Rc<dyn Source>, DefinitionData>,
pub conceals: HashMap<Rc<dyn Source>, ConcealsData>,
} }
impl LSPData { impl LSPData {
@ -23,6 +25,7 @@ impl LSPData {
semantic_data: HashMap::new(), semantic_data: HashMap::new(),
inlay_hints: HashMap::new(), inlay_hints: HashMap::new(),
definitions: HashMap::new(), definitions: HashMap::new(),
conceals: HashMap::new(),
} }
} }
@ -40,5 +43,9 @@ impl LSPData {
self.definitions self.definitions
.insert(source.clone(), DefinitionData::new()); .insert(source.clone(), DefinitionData::new());
} }
if !self.conceals.contains_key(&source) {
self.conceals
.insert(source.clone(), ConcealsData::new(source.clone()));
}
} }
} }

View file

@ -31,6 +31,7 @@ impl HintsData {
} }
} }
/// Temporary data returned by [`Self::from_source_impl`]
#[derive(Debug)] #[derive(Debug)]
pub struct Hints<'a> { pub struct Hints<'a> {
pub(self) hints: Ref<'a, HintsData>, pub(self) hints: Ref<'a, HintsData>,

View file

@ -2,3 +2,4 @@ pub mod data;
pub mod definition; pub mod definition;
pub mod hints; pub mod hints;
pub mod semantic; pub mod semantic;
pub mod conceal;

View file

@ -305,6 +305,7 @@ impl SemanticsData {
} }
} }
/// Temporary data returned by [`Self::from_source_impl`]
#[derive(Debug)] #[derive(Debug)]
pub struct Semantics<'a> { pub struct Semantics<'a> {
pub(self) sems: Ref<'a, SemanticsData>, pub(self) sems: Ref<'a, SemanticsData>,

View file

@ -9,12 +9,17 @@ mod parser;
use std::rc::Rc; use std::rc::Rc;
use dashmap::DashMap; use dashmap::DashMap;
use downcast_rs::Downcast;
use lsp::conceal::ConcealInfo;
use lsp::conceal::ConcealParams;
use lsp::conceal::ConcealTarget;
use parser::langparser::LangParser; use parser::langparser::LangParser;
use parser::parser::ParseMode; use parser::parser::ParseMode;
use parser::parser::Parser; use parser::parser::Parser;
use parser::parser::ParserState; use parser::parser::ParserState;
use parser::reports::Report; use parser::reports::Report;
use parser::source::SourceFile; use parser::source::SourceFile;
use tower_lsp::jsonrpc;
use tower_lsp::lsp_types::*; use tower_lsp::lsp_types::*;
use tower_lsp::Client; use tower_lsp::Client;
use tower_lsp::LanguageServer; use tower_lsp::LanguageServer;
@ -29,6 +34,7 @@ struct Backend {
semantic_token_map: DashMap<String, Vec<SemanticToken>>, semantic_token_map: DashMap<String, Vec<SemanticToken>>,
diagnostic_map: DashMap<String, Vec<Diagnostic>>, diagnostic_map: DashMap<String, Vec<Diagnostic>>,
hints_map: DashMap<String, Vec<InlayHint>>, hints_map: DashMap<String, Vec<InlayHint>>,
conceals_map: DashMap<String, Vec<ConcealInfo>>,
} }
#[derive(Debug)] #[derive(Debug)]
@ -112,6 +118,36 @@ impl Backend {
} }
} }
} }
// Conceals
if let Some(lsp) = state.shared.lsp.as_ref() {
let borrow = lsp.borrow();
for (source, conceals) in &borrow.conceals {
if let Some(path) = source
.clone()
.downcast_rc::<SourceFile>()
.ok()
.map(|source| source.path().to_owned())
{
self.conceals_map
.insert(path, conceals.conceals.replace(vec![]));
}
}
}
}
async fn handle_conceal_request(
&self,
params: ConcealParams,
) -> jsonrpc::Result<Vec<ConcealInfo>> {
eprintln!("HERE {:#?}", self.conceals_map);
if let Some(conceals) = self.conceals_map.get(params.text_document.uri.as_str()) {
let (_, data) = conceals.pair();
eprintln!("HERE2");
return Ok(data.to_vec());
}
Ok(vec![])
} }
} }
@ -313,13 +349,17 @@ async fn main() {
let stdin = tokio::io::stdin(); let stdin = tokio::io::stdin();
let stdout = tokio::io::stdout(); let stdout = tokio::io::stdout();
let (service, socket) = LspService::new(|client| Backend { let (service, socket) = LspService::build(|client| Backend {
client, client,
document_map: DashMap::new(), document_map: DashMap::new(),
definition_map: DashMap::new(), definition_map: DashMap::new(),
semantic_token_map: DashMap::new(), semantic_token_map: DashMap::new(),
diagnostic_map: DashMap::new(), diagnostic_map: DashMap::new(),
hints_map: DashMap::new(), hints_map: DashMap::new(),
}); conceals_map: DashMap::new(),
})
.custom_method("textDocument/conceal", Backend::handle_conceal_request)
.finish();
Server::new(stdin, stdout, socket).serve(service).await; Server::new(stdin, stdout, socket).serve(service).await;
} }