shebang hover (#2290)
* add a test Signed-off-by: Jess Frazelle <github@jessfraz.com> * plugoin Signed-off-by: Jess Frazelle <github@jessfraz.com> --------- Signed-off-by: Jess Frazelle <github@jessfraz.com>
This commit is contained in:
@ -167,6 +167,7 @@ export class LanguageServerPlugin implements PluginValue {
|
|||||||
if (pos === null) return null
|
if (pos === null) return null
|
||||||
const dom = document.createElement('div')
|
const dom = document.createElement('div')
|
||||||
dom.classList.add('documentation')
|
dom.classList.add('documentation')
|
||||||
|
dom.style.zIndex = '99999999'
|
||||||
if (this.allowHTMLContent) dom.innerHTML = formatContents(contents)
|
if (this.allowHTMLContent) dom.innerHTML = formatContents(contents)
|
||||||
else dom.textContent = formatContents(contents)
|
else dom.textContent = formatContents(contents)
|
||||||
return { pos, end, create: (view) => ({ dom }), above: true }
|
return { pos, end, create: (view) => ({ dom }), above: true }
|
||||||
|
@ -36,6 +36,28 @@ pub struct Program {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Program {
|
impl Program {
|
||||||
|
pub fn get_hover_value_for_position(&self, pos: usize, code: &str) -> Option<Hover> {
|
||||||
|
// Check if we are in the non code meta.
|
||||||
|
if let Some(meta) = self.get_non_code_meta_for_position(pos) {
|
||||||
|
for node in &meta.start {
|
||||||
|
if node.contains(pos) {
|
||||||
|
// We only care about the shebang.
|
||||||
|
if let NonCodeValue::Shebang { value: _ } = &node.value {
|
||||||
|
let source_range: SourceRange = node.into();
|
||||||
|
return Some(Hover::Comment {
|
||||||
|
value: r#"The `#!` at the start of a script, known as a shebang, specifies the path to the interpreter that should execute the script. This line is not necessary for your `kcl` to run in the modeling-app. You can safely delete it. If you wish to learn more about what you _can_ do with a shebang, read this doc: [zoo.dev/docs/faq/shebang](https://zoo.dev/docs/faq/shebang)."#.to_string(),
|
||||||
|
range: source_range.to_lsp_range(code),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let value = self.get_value_for_position(pos)?;
|
||||||
|
|
||||||
|
value.get_hover_value_for_position(pos, code)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn recast(&self, options: &FormatOptions, indentation_level: usize) -> String {
|
pub fn recast(&self, options: &FormatOptions, indentation_level: usize) -> String {
|
||||||
let indentation = options.get_indentation(indentation_level);
|
let indentation = options.get_indentation(indentation_level);
|
||||||
let result = self
|
let result = self
|
||||||
@ -814,6 +836,18 @@ pub struct NonCodeNode {
|
|||||||
pub value: NonCodeValue,
|
pub value: NonCodeValue,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<NonCodeNode> for SourceRange {
|
||||||
|
fn from(value: NonCodeNode) -> Self {
|
||||||
|
Self([value.start, value.end])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<&NonCodeNode> for SourceRange {
|
||||||
|
fn from(value: &NonCodeNode) -> Self {
|
||||||
|
Self([value.start, value.end])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl NonCodeNode {
|
impl NonCodeNode {
|
||||||
pub fn contains(&self, pos: usize) -> bool {
|
pub fn contains(&self, pos: usize) -> bool {
|
||||||
self.start <= pos && pos <= self.end
|
self.start <= pos && pos <= self.end
|
||||||
@ -2987,6 +3021,10 @@ pub enum Hover {
|
|||||||
parameter_index: u32,
|
parameter_index: u32,
|
||||||
range: LspRange,
|
range: LspRange,
|
||||||
},
|
},
|
||||||
|
Comment {
|
||||||
|
value: String,
|
||||||
|
range: LspRange,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Format options.
|
/// Format options.
|
||||||
|
@ -795,11 +795,7 @@ impl LanguageServer for Backend {
|
|||||||
return Ok(None);
|
return Ok(None);
|
||||||
};
|
};
|
||||||
|
|
||||||
let Some(value) = ast.get_value_for_position(pos) else {
|
let Some(hover) = ast.get_hover_value_for_position(pos, current_code) else {
|
||||||
return Ok(None);
|
|
||||||
};
|
|
||||||
|
|
||||||
let Some(hover) = value.get_hover_value_for_position(pos, current_code) else {
|
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -836,6 +832,13 @@ impl LanguageServer for Backend {
|
|||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
crate::ast::types::Hover::Signature { .. } => Ok(None),
|
crate::ast::types::Hover::Signature { .. } => Ok(None),
|
||||||
|
crate::ast::types::Hover::Comment { value, range } => Ok(Some(Hover {
|
||||||
|
contents: HoverContents::Markup(MarkupContent {
|
||||||
|
kind: MarkupKind::Markdown,
|
||||||
|
value,
|
||||||
|
}),
|
||||||
|
range: Some(range),
|
||||||
|
})),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -944,6 +947,9 @@ impl LanguageServer for Backend {
|
|||||||
|
|
||||||
Ok(Some(signature.clone()))
|
Ok(Some(signature.clone()))
|
||||||
}
|
}
|
||||||
|
crate::ast::types::Hover::Comment { value: _, range: _ } => {
|
||||||
|
return Ok(None);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -884,6 +884,53 @@ async fn test_kcl_lsp_on_hover() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tokio::test(flavor = "multi_thread")]
|
||||||
|
async fn test_kcl_lsp_on_hover_shebang() {
|
||||||
|
let server = kcl_lsp_server(false).await.unwrap();
|
||||||
|
|
||||||
|
// Send open file.
|
||||||
|
server
|
||||||
|
.did_open(tower_lsp::lsp_types::DidOpenTextDocumentParams {
|
||||||
|
text_document: tower_lsp::lsp_types::TextDocumentItem {
|
||||||
|
uri: "file:///test.kcl".try_into().unwrap(),
|
||||||
|
language_id: "kcl".to_string(),
|
||||||
|
version: 1,
|
||||||
|
text: r#"#!/usr/bin/env zoo kcl view
|
||||||
|
startSketchOn()"#
|
||||||
|
.to_string(),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.await;
|
||||||
|
server.wait_on_handle().await;
|
||||||
|
|
||||||
|
// Send hover request.
|
||||||
|
let hover = server
|
||||||
|
.hover(tower_lsp::lsp_types::HoverParams {
|
||||||
|
text_document_position_params: tower_lsp::lsp_types::TextDocumentPositionParams {
|
||||||
|
text_document: tower_lsp::lsp_types::TextDocumentIdentifier {
|
||||||
|
uri: "file:///test.kcl".try_into().unwrap(),
|
||||||
|
},
|
||||||
|
position: tower_lsp::lsp_types::Position { line: 0, character: 2 },
|
||||||
|
},
|
||||||
|
work_done_progress_params: Default::default(),
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
// Check the hover.
|
||||||
|
if let Some(hover) = hover {
|
||||||
|
assert_eq!(
|
||||||
|
hover.contents,
|
||||||
|
tower_lsp::lsp_types::HoverContents::Markup(tower_lsp::lsp_types::MarkupContent {
|
||||||
|
kind: tower_lsp::lsp_types::MarkupKind::Markdown,
|
||||||
|
value: "The `#!` at the start of a script, known as a shebang, specifies the path to the interpreter that should execute the script. This line is not necessary for your `kcl` to run in the modeling-app. You can safely delete it. If you wish to learn more about what you _can_ do with a shebang, read this doc: [zoo.dev/docs/faq/shebang](https://zoo.dev/docs/faq/shebang).".to_string()
|
||||||
|
})
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
panic!("Expected hover");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[tokio::test(flavor = "multi_thread")]
|
#[tokio::test(flavor = "multi_thread")]
|
||||||
async fn test_kcl_lsp_signature_help() {
|
async fn test_kcl_lsp_signature_help() {
|
||||||
let server = kcl_lsp_server(false).await.unwrap();
|
let server = kcl_lsp_server(false).await.unwrap();
|
||||||
|
Reference in New Issue
Block a user