Skip to content
Draft
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
41 changes: 34 additions & 7 deletions Cargo.lock

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

3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,8 @@ future_incompatible = "warn"
nonstandard_style = "warn"

[workspace.dependencies]
arma3-wiki = "0.4.5"
# debug until merge # arma3-wiki = "0.4.4"
arma3-wiki = { path = "../arma3-wiki/clients/rust" }
automod = "1.0.15"
byteorder = "1.5.0"
chumsky = "0.9.3"
Expand Down
55 changes: 55 additions & 0 deletions hls/src/sqf/hover.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,11 +47,66 @@ impl SqfAnalyzer {
let Symbol::Word(word) = token.symbol() else {
return None;
};
println!("Hover word: {word}");
if let Some(func) = database.external_functions_get(&word.to_lowercase()) {
return Some(hover_func(func));
}
database.wiki().commands().get(word)?;
Some(hover(word, &database))
}
}

// WIP
fn hover_func(func: &arma3_wiki::model::Function) -> Hover {
Hover {
contents: HoverContents::Array({
let mut contents = Vec::new();
contents.push(MarkedString::String(format!(
"## {}",
func.name().unwrap_or(&String::new())
)));
{
let mut string = String::new();
for arg in func.params() {
writeln!(
string,
"- `{}`: {}{}",
arg.name(),
{
let typ = arg.typ().to_string();
if typ == "Unknown" {
typ
} else {
format!(
"[{}](https://community.bistudio.com/wiki/{})",
typ,
typ.replace(' ', "_")
)
}
},
{ arg.description().unwrap_or("?") }
)
.expect("Failed to write to string");
}
contents.push(MarkedString::String(format!("### Syntax\n{string}")));
}
if let Some(ret) = func.ret() {
contents.push(MarkedString::String(format!(
"### Return Type\n- [{}](https://community.bistudio.com/wiki/{})",
ret,
ret.to_string().replace(' ', "_")
)));
}
let example = func.example();
if !example.is_empty() {
contents.push(MarkedString::String(format!("### Example\n{example}")));
}
contents
}),
range: None,
}
}

fn hover(command: &str, database: &Database) -> Hover {
database.wiki().commands().get(command).map_or_else(
|| Hover {
Expand Down
1 change: 1 addition & 0 deletions libs/sqf/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ thiserror = { workspace = true }
toml = { workspace = true }
tracing = { workspace = true }
regex = { workspace = true }
fs-err = { workspace = true }

[features]
default = ["compiler", "parser"]
Expand Down
86 changes: 72 additions & 14 deletions libs/sqf/src/analyze/inspector/commands.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,31 +60,50 @@ impl Inspector {
IndexSet::new()
}
#[must_use]
/// # Panics
pub fn cmd_generic_params(
&mut self,
rhs: &IndexSet<GameValue>,
debug_type: &str,
source: &Range<usize>,
unary: bool,
) -> IndexSet<GameValue> {
let mut error_type = None;
// get header for type defaults
let header = if unary
&& self.in_primary_scope()
&& let Some(self_header) = &self.function_info
{
Some(self_header.clone())
} else {
None
};

for possible in rhs {
let GameValue::Array(Some(gv_array), _) = possible else {
continue;
};

for gv_index in gv_array {
for (i, gv_index) in gv_array.iter().enumerate() {
let set_from_header = header
.as_ref()
.and_then(|h| h.params().get(i))
.map(|a| GameValue::from_wiki_value(a.typ(), NilSource::Generic));
for (element, element_span) in gv_index {
match element {
GameValue::Anything | GameValue::Array(None, _) => {}
GameValue::String(_) => {
if let Some(error) = self.cmd_generic_params_element(
&[vec![(element.clone(), element_span.clone())]], // put it in a dummy array
&[vec![(element.clone(), element_span.clone())]], // put it in a dummy
set_from_header.as_ref(),
) {
error_type = Some(error);
}
}
GameValue::Array(Some(arg_array), _) => {
if let Some(error) = self.cmd_generic_params_element(arg_array) {
if let Some(error) =
self.cmd_generic_params_element(arg_array, set_from_header.as_ref())
{
error_type = Some(error);
}
}
Expand Down Expand Up @@ -116,7 +135,32 @@ impl Inspector {
pub fn cmd_generic_params_element(
&mut self,
element: &[Vec<(GameValue, Range<usize>)>],
header_defaults: Option<&IndexSet<GameValue>>,
) -> Option<InvalidArgs> {
/// get generic params and optionally check that they match header
fn match_defaults_to_header(
var_types: &mut IndexSet<GameValue>,
input: &[(GameValue, Range<usize>)],
header_defaults: Option<&IndexSet<GameValue>>,
) -> Option<InvalidArgs> {
let mut error_type = None;
for (v, v_span) in input {
let vg = v.make_generic();
if let Some(header_defaults) = header_defaults
&& !(header_defaults
.iter()
.any(|hd| GameValue::match_values(&vg, hd)))
{
error_type = Some(InvalidArgs::ExpectedDifferentTypeHeader {
expected: header_defaults.iter().cloned().collect(),
found: vec![vg.clone()],
span: v_span.clone(),
});
}
var_types.insert(vg);
}
error_type
}
if element.is_empty() || element[0].is_empty() {
return None;
}
Expand All @@ -134,7 +178,13 @@ impl Inspector {
match type_p {
GameValue::Array(Some(type_array), _) => {
for type_i in type_array {
var_types.extend(type_i.iter().map(|(v, _)| v.make_generic()));
if let Some(type_error) = match_defaults_to_header(
&mut var_types,
type_i,
header_defaults,
) {
error_type = Some(type_error);
}
}
}
GameValue::Array(None, _) | GameValue::Anything => {}
Expand All @@ -148,6 +198,9 @@ impl Inspector {
}
}
}
if let Some(header_defaults) = header_defaults {
var_types.extend(header_defaults.iter().cloned());
}
if var_types.is_empty() {
var_types.insert(GameValue::Anything);
}
Expand All @@ -167,18 +220,23 @@ impl Inspector {
expected: var_types.iter().cloned().collect(),
found: vec![default_value.clone()],
span: element[1][0].1.clone(),
default: Some(
(element[2]
.first()
.map(|(_, s)| s.clone())
.unwrap_or_default()
.start)
..(element[2]
.last()
// element[2] could be missing if expected are from header
default: if element.len() > 2 && !element[2].is_empty() {
Some(
(element[2]
.first()
.map(|(_, s)| s.clone())
.unwrap_or_default()
.end),
),
.start)
..(element[2]
.last()
.map(|(_, s)| s.clone())
.unwrap_or_default()
.end),
)
} else {
None
},
});
}
var_types.insert(default_value);
Expand Down
Loading
Loading