mirror of
https://github.com/Myriad-Dreamin/tinymist.git
synced 2025-08-03 17:58:17 +00:00
feat: add code action to wrap content blocks (#881)
This commit is contained in:
parent
3207f57a78
commit
94ea230645
1 changed files with 46 additions and 5 deletions
|
@ -3,7 +3,11 @@ use once_cell::sync::{Lazy, OnceCell};
|
|||
use regex::Regex;
|
||||
use typst_shim::syntax::LinkedNodeExt;
|
||||
|
||||
use crate::{prelude::*, SemanticRequest};
|
||||
use crate::{
|
||||
prelude::*,
|
||||
syntax::{interpret_mode_at, InterpretMode},
|
||||
SemanticRequest,
|
||||
};
|
||||
|
||||
/// The [`textDocument/codeAction`] request is sent from the client to the
|
||||
/// server to compute commands for a given text document and range. These
|
||||
|
@ -77,12 +81,10 @@ impl SemanticRequest for CodeActionRequest {
|
|||
fn request(self, ctx: &mut LocalContext) -> Option<Self::Response> {
|
||||
let source = ctx.source_by_path(&self.path).ok()?;
|
||||
let range = ctx.to_typst_range(self.range, &source)?;
|
||||
let cursor = (range.start + 1).min(source.text().len());
|
||||
// todo: don't ignore the range end
|
||||
|
||||
let root = LinkedNode::new(source.root());
|
||||
let mut worker = CodeActionWorker::new(ctx, source.clone());
|
||||
worker.work(root, cursor);
|
||||
worker.work(root, range);
|
||||
|
||||
let res = worker.actions;
|
||||
(!res.is_empty()).then_some(res)
|
||||
|
@ -112,6 +114,7 @@ impl<'a> CodeActionWorker<'a> {
|
|||
.as_ref()
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
fn local_edits(&self, edits: Vec<TextEdit>) -> Option<WorkspaceEdit> {
|
||||
Some(WorkspaceEdit {
|
||||
changes: Some(HashMap::from_iter([(self.local_url()?.clone(), edits)])),
|
||||
|
@ -119,10 +122,45 @@ impl<'a> CodeActionWorker<'a> {
|
|||
})
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
fn local_edit(&self, edit: TextEdit) -> Option<WorkspaceEdit> {
|
||||
self.local_edits(vec![edit])
|
||||
}
|
||||
|
||||
fn wrap_actions(&mut self, node: &LinkedNode, range: Range<usize>) -> Option<()> {
|
||||
if range.is_empty() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let start_mode = interpret_mode_at(Some(node));
|
||||
if !matches!(start_mode, InterpretMode::Markup | InterpretMode::Math) {
|
||||
return None;
|
||||
}
|
||||
|
||||
let edit = self.local_edits(vec![
|
||||
TextEdit {
|
||||
range: self
|
||||
.ctx
|
||||
.to_lsp_range(range.start..range.start, &self.current),
|
||||
new_text: "#[".into(),
|
||||
},
|
||||
TextEdit {
|
||||
range: self.ctx.to_lsp_range(range.end..range.end, &self.current),
|
||||
new_text: "]".into(),
|
||||
},
|
||||
])?;
|
||||
|
||||
let action = CodeActionOrCommand::CodeAction(CodeAction {
|
||||
title: "Wrap with content block".to_string(),
|
||||
kind: Some(CodeActionKind::REFACTOR_REWRITE),
|
||||
edit: Some(edit),
|
||||
..CodeAction::default()
|
||||
});
|
||||
self.actions.push(action);
|
||||
|
||||
Some(())
|
||||
}
|
||||
|
||||
fn heading_actions(&mut self, node: &LinkedNode) -> Option<()> {
|
||||
let h = node.cast::<ast::Heading>()?;
|
||||
let depth = h.depth().get();
|
||||
|
@ -269,13 +307,16 @@ impl<'a> CodeActionWorker<'a> {
|
|||
Some(())
|
||||
}
|
||||
|
||||
fn work(&mut self, root: LinkedNode, cursor: usize) -> Option<()> {
|
||||
fn work(&mut self, root: LinkedNode, range: Range<usize>) -> Option<()> {
|
||||
let cursor = (range.start + 1).min(self.current.text().len());
|
||||
let node = root.leaf_at_compat(cursor)?;
|
||||
let mut node = &node;
|
||||
|
||||
let mut heading_resolved = false;
|
||||
let mut equation_resolved = false;
|
||||
|
||||
self.wrap_actions(node, range);
|
||||
|
||||
loop {
|
||||
match node.kind() {
|
||||
// Only the deepest heading is considered
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue