Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .github/workflows/publish_npm_package.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ jobs:
node-version: 24
registry-url: https://registry.npmjs.org/

- name: Install toml
run: pip install toml

- name: Install wasm-pack
run: cargo install --locked wasm-pack

Expand Down
24 changes: 12 additions & 12 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

16 changes: 8 additions & 8 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[workspace.package]
version = "1.14.0"
version = "1.15.0"
authors = ["Victor M. Alvarez <vmalvarez@virustotal.com>"]
edition = "2024"
homepage = "https://virustotal.github.io/yara-x"
Expand Down Expand Up @@ -110,13 +110,13 @@ wasm-opt = "0.116.1"
wasmtime = { version = "40.0.4", default-features = false }
x509-parser = "0.18.0"
yansi = "1.0.1"
yara-x = { path = "lib", version = "1.14.0" }
yara-x-fmt = { path = "fmt", version = "1.14.0" }
yara-x-macros = { path = "macros", version = "1.14.0" }
yara-x-parser = { path = "parser", version = "1.14.0" }
yara-x-proto = { path = "proto", version = "1.14.0"}
yara-x-proto-yaml = { path = "proto-yaml", version = "1.14.0" }
yara-x-proto-json = { path = "proto-json", version = "1.14.0" }
yara-x = { path = "lib", version = "1.15.0" }
yara-x-fmt = { path = "fmt", version = "1.15.0" }
yara-x-macros = { path = "macros", version = "1.15.0" }
yara-x-parser = { path = "parser", version = "1.15.0" }
yara-x-proto = { path = "proto", version = "1.15.0"}
yara-x-proto-yaml = { path = "proto-yaml", version = "1.15.0" }
yara-x-proto-json = { path = "proto-json", version = "1.15.0" }
zip = { version = "8.2.0", default-features = false }
simd-adler32 = "0.3.8"
simd_cesu8 = "1.1.1"
Expand Down
25 changes: 22 additions & 3 deletions lib/src/modules/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -403,11 +403,30 @@ pub mod mods {
#[derive(Clone, Debug, PartialEq)]
pub struct FuncSignature {
/// The names and types of the function arguments.
pub args: Vec<(String, Type)>,
args: Vec<(String, Type)>,
/// The return type for the function.
pub ret: Type,
ret: Type,
/// Function's documentation.
pub doc: Option<Cow<'static, str>>,
doc: Option<Cow<'static, str>>,
}

impl FuncSignature {
/// The names and types of the function arguments.
pub fn args(
&self,
) -> impl ExactSizeIterator<Item = (&str, &Type)> {
self.args.iter().map(|(name, ty)| (name.as_str(), ty))
}

/// The return type for the function.
pub fn ret_type(&self) -> &Type {
&self.ret
}

/// Function's documentation.
pub fn doc(&self) -> Option<&str> {
self.doc.as_deref()
}
}

/// Describes a field within a structure or module.
Expand Down
17 changes: 7 additions & 10 deletions ls/src/features/completion.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ use crate::utils::cst_traversal::{
rule_containing_token, token_at_position,
};

use crate::utils::modules::{get_struct, ty_to_string};
use crate::utils::modules::{get_type, ty_to_string};

const PATTERN_MODS: &[(SyntaxKind, &[&str])] = &[
(
Expand Down Expand Up @@ -380,7 +380,7 @@ fn field_suggestions(token: &Token<Immutable>) -> Option<Vec<CompletionItem>> {
_ => None,
}?;

let current_struct = match get_struct(&token)? {
let current_struct = match get_type(&token)? {
Type::Struct(s) => s,
_ => return None,
};
Expand All @@ -399,14 +399,12 @@ fn field_suggestions(token: &Token<Immutable>) -> Option<Vec<CompletionItem>> {
.iter()
.map(|sig| {
let args = sig
.args
.iter()
.args()
.map(|(name, ty)| format!("{}: {}", name, ty_to_string(ty)))
.collect::<Vec<_>>();

let args_template = sig
.args
.iter()
.args()
.enumerate()
.map(|(n, (name, _))| {
format!("${{{}:{name}}}", n + 1)
Expand All @@ -430,19 +428,18 @@ fn field_suggestions(token: &Token<Immutable>) -> Option<Vec<CompletionItem>> {
description: Some(ty_to_string(&ty)),
..Default::default()
}),
documentation: sig.doc.as_ref().map(
documentation: sig.doc().map(
|docs| {
async_lsp::lsp_types::Documentation::MarkupContent(
async_lsp::lsp_types::MarkupContent {
kind: async_lsp::lsp_types::MarkupKind::Markdown,
value: format!(
"## `{}({}) -> {}`\n\n{}",
name,
sig.args
.iter()
sig.args()
.map(|(name, ty)| format!("{}: {}", name, ty_to_string(ty)))
.join(", "),
ty_to_string(&sig.ret),
ty_to_string(sig.ret_type()),
docs
),
},
Expand Down
103 changes: 68 additions & 35 deletions ls/src/features/hover.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,16 @@ use async_lsp::lsp_types::{
HoverContents, MarkupContent, MarkupKind, Position, Url,
};
use itertools::Itertools;

use yara_x::mods::reflect::Type;
use yara_x_parser::cst::{Immutable, Node, NodeOrToken, SyntaxKind, Utf8};

use crate::documents::storage::DocumentStorage;
use crate::utils::cst_traversal::{
find_declaration, pattern_from_ident, rule_containing_token,
token_at_position,
find_declaration, pattern_from_ident, prev_non_trivia_token,
rule_containing_token, token_at_position,
};

use crate::utils::modules::{get_struct, ty_to_string};
use crate::utils::modules::{get_type, ty_to_string};

/// Builder for hover Markdown representation of a rule.
struct RuleHoverBuilder {
Expand Down Expand Up @@ -106,38 +106,71 @@ pub fn hover(
}
// Other identifiers.
SyntaxKind::IDENT => {
if let Some(yara_x::mods::reflect::Type::Func(func)) =
get_struct(&token)
{
let documentation = func
.signatures
.iter()
.filter_map(|signature| {
signature.doc.as_ref().map(|doc| {
format!(
"### `{}({}) -> {}`\n\n***\n\n{}\n\n***\n\n",
token.text(),
signature
.args
.iter()
.map(|(name, ty)| format!(
"{}: {}",
name,
ty_to_string(ty)
))
.join(", "),
ty_to_string(&signature.ret),
doc
)
})
})
.join("\n");

return Some(HoverContents::Markup(MarkupContent {
kind: MarkupKind::Markdown,
value: documentation,
}));
let structure = prev_non_trivia_token(&token)
.filter(|token| token.kind() == SyntaxKind::DOT)
.and_then(|token| prev_non_trivia_token(&token))
.and_then(|token| get_type(&token))
.and_then(|ty| {
if let Type::Struct(s) = ty { Some(s) } else { None }
});

let field = structure
.as_ref()
.and_then(|s| s.fields().find(|f| f.name() == token.text()));

if let Some(field) = field {
match field.ty() {
Type::Func(func) => {
let documentation = func
.signatures
.iter()
.filter_map(|signature| {
signature.doc().map(|doc| {
format!(
"### `{}({}) -> {}`\n\n***\n\n{}\n\n***\n\n",
token.text(),
signature
.args()
.map(|(arg_name, arg_ty)| format!(
"{}: {}",
arg_name,
ty_to_string(arg_ty)
))
.join(", "),
ty_to_string(signature.ret_type()),
doc
)
})
})
.join("\n");

if !documentation.is_empty() {
return Some(HoverContents::Markup(
MarkupContent {
kind: MarkupKind::Markdown,
value: documentation,
},
));
}
}
ty => {
let mut value = format!(
"### `{}: {}`",
token.text(),
ty_to_string(&ty)
);
if let Some(d) = field.doc() {
value
.push_str(&format!("\n\n***\n\n{}\n\n***", d));
}
return Some(HoverContents::Markup(MarkupContent {
kind: MarkupKind::Markdown,
value,
}));
}
}
}

if let Some((_, n)) = find_declaration(&token) {
let text = n
.children_with_tokens()
Expand Down
8 changes: 5 additions & 3 deletions ls/src/features/inlay_hint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ pub fn inlay_hint(
&& let Some(new_ty) = func
.signatures
.first()
.map(|sign| &sign.ret)
.map(|sign| sign.ret_type())
{
new_ty.clone()
} else {
Expand Down Expand Up @@ -90,8 +90,10 @@ pub fn inlay_hint(
{
// Extract return type from function.
ty = if let Type::Func(func) = &ty
&& let Some(new_ty) =
func.signatures.first().map(|sign| &sign.ret)
&& let Some(new_ty) = func
.signatures
.first()
.map(|sign| sign.ret_type())
{
new_ty.clone()
} else {
Expand Down
Loading
Loading