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.16"
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;
};
// ToDo: this works for full func names, would need work to support FUNC(x) macroed code
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/common/src/config/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ pub use pdrive::PDriveOption;
pub use project::{
ProjectConfig,
hemtt::{RuntimeArguments, launch::LaunchOptions},
inspector::InspectorOptions,
lint::{LintConfig, LintConfigOverride, LintEnabled},
preprocessor::PreprocessorOptions,
};
Expand Down
152 changes: 152 additions & 0 deletions libs/common/src/config/project/inspector.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
use serde::{Deserialize, Serialize};

#[allow(clippy::module_name_repetitions)]
#[derive(PartialEq, Eq, Debug, Clone, Default)]
/// Configuration for inspector options
pub struct InspectorOptions {
/// variable names to ignore when checking for undefined variables
vars_to_ignore: Option<Vec<String>>,
/// function prefixes to check for when calling
check_function_calls: Vec<String>,
/// project function prefixes to export to `.hemtt/functions`
export_functions: Vec<String>,
/// header regex
header_regex: String,
/// header regex for a line
header_line_regex: String,
}

impl InspectorOptions {
#[must_use]
pub fn vars_to_ignore(&self) -> Option<&[String]> {
self.vars_to_ignore.as_deref()
}
#[must_use]
pub fn with_vars_to_ignore(mut self, value: Option<Vec<String>>) -> Self {
self.vars_to_ignore = value;
self
}
#[must_use]
pub fn check_function_calls(&self) -> &[String] {
&self.check_function_calls
}
#[must_use]
pub fn with_check_function_calls(mut self, value: Vec<String>) -> Self {
self.check_function_calls = value;
self
}
#[must_use]
pub fn export_functions(&self) -> &[String] {
&self.export_functions
}
#[must_use]
pub fn with_export_functions(mut self, value: Vec<String>) -> Self {
self.export_functions = value;
self
}
#[must_use]
pub fn header_regex(&self) -> &str {
&self.header_regex
}
#[must_use]
pub fn with_header_regex(mut self, value: String) -> Self {
self.header_regex = value;
self
}
#[must_use]
pub fn header_line_regex(&self) -> &str {
&self.header_line_regex
}
#[must_use]
pub fn with_header_line_regex(mut self, value: String) -> Self {
self.header_line_regex = value;
self
}
}

#[derive(PartialEq, Eq, Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
enum VectorOrBoolWildcard {
Vec(Vec<String>),
Bool(bool),
}
impl VectorOrBoolWildcard {
/// false becomes empty vec, true becomes vec with wildcard "*"
fn to_vec(&self) -> Vec<String> {
match self {
Self::Vec(v) => v.iter().map(|s| s.to_lowercase()).collect(),
Self::Bool(b) => {
if *b {
vec!["*".to_string()]
} else {
vec![]
}
}
}
}
}

#[allow(clippy::module_name_repetitions)]
#[derive(PartialEq, Eq, Debug, Default, Clone, Serialize, Deserialize)]
pub struct InspectorOptionsFile {
#[serde(default)]
vars_to_ignore: Option<Vec<String>>,
#[serde(default)]
check_function_calls: Option<VectorOrBoolWildcard>,
#[serde(default)]
export_functions: Option<VectorOrBoolWildcard>,
#[serde(default)]
header_regex: Option<String>,
#[serde(default)]
header_line_regex: Option<String>,
}

impl From<InspectorOptionsFile> for InspectorOptions {
fn from(file: InspectorOptionsFile) -> Self {
Self {
vars_to_ignore: file.vars_to_ignore, // default is None, which will load the CfgFunction vars (_fnc_scriptName...)
check_function_calls: file
.check_function_calls
.unwrap_or(VectorOrBoolWildcard::Bool(false)) // opt-in to check func calls
.to_vec(),
export_functions: file
.export_functions
.unwrap_or(VectorOrBoolWildcard::Bool(true)) // default all
.to_vec(),
header_regex: file.header_regex.unwrap_or_default(), // opt-in to header parsing, either preset or actual regex
header_line_regex: file.header_line_regex.unwrap_or_default(),
}
}
}

#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_inspector_options_file_1() {
let toml: &'static str = r"";
let file: InspectorOptionsFile = toml::from_str(toml).expect("Failed to parse TOML");
let options: InspectorOptions = file.into();
assert!(options.vars_to_ignore().is_none());
assert!(options.check_function_calls().is_empty());
assert_eq!(options.export_functions(), &vec!["*".to_string()]);
assert_eq!(options.header_regex, "");
assert_eq!(options.header_line_regex, "");
}
#[test]
fn test_inspector_options_file_2() {
let toml: &'static str = r#"
export_functions = ["abe_berry"]
check_function_calls = true
vars_to_ignore = []
header_regex = "ace"
"#;
let file: InspectorOptionsFile = toml::from_str(toml).expect("Failed to parse TOML");
let options: InspectorOptions = file.into();
assert!(options.vars_to_ignore().expect("some").is_empty());
assert_eq!(options.check_function_calls(), &vec!["*".to_string()]);
assert_eq!(options.export_functions(), &vec!["abe_berry".to_string()]);
assert_eq!(options.header_regex, "ace");
assert_eq!(options.header_line_regex, "");
}
}
17 changes: 16 additions & 1 deletion libs/common/src/config/project/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ use super::deprecated;

pub mod files;
pub mod hemtt;
pub mod inspector;
pub mod lint;
pub mod preprocessor;
pub mod signing;
Expand Down Expand Up @@ -48,6 +49,9 @@ pub struct ProjectConfig {
// Preprocessor options
preprocessor: preprocessor::PreprocessorOptions,

/// Inspector specific configuration
inspector: inspector::InspectorOptions,

/// Signing specific configuration
signing: signing::SigningConfig,

Expand Down Expand Up @@ -119,6 +123,12 @@ impl ProjectConfig {
&self.preprocessor
}

#[must_use]
/// Inspector specific configuration
pub const fn inspector(&self) -> &inspector::InspectorOptions {
&self.inspector
}

#[must_use]
/// Signing specific configuration
pub const fn signing(&self) -> &signing::SigningConfig {
Expand Down Expand Up @@ -199,6 +209,9 @@ pub struct ProjectFile {
#[serde(default)]
preprocessor: preprocessor::PreprocessorOptionsFile,

#[serde(default)]
inspector: inspector::InspectorOptionsFile,

#[serde(default)]
signing: signing::SigningSectionFile,

Expand Down Expand Up @@ -257,6 +270,7 @@ impl TryFrom<ProjectFile> for ProjectConfig {
files: file.files.into(),
lints: file.lints.into(),
preprocessor: file.preprocessor.into(),
inspector: file.inspector.into(),
signing: file.signing.into(),
runtime: RuntimeArguments::default(),
expected_path,
Expand Down Expand Up @@ -290,7 +304,7 @@ impl TryFrom<ProjectFile> for ProjectConfig {
mod test_helper {
use std::collections::HashMap;

use super::{files, hemtt, lint, preprocessor, signing, version};
use super::{files, hemtt, inspector, lint, preprocessor, signing, version};

impl super::ProjectConfig {
#[must_use]
Expand All @@ -310,6 +324,7 @@ mod test_helper {
lints: lint::LintSectionFile::default(),
hemtt: hemtt::HemttSectionFile::default(),
preprocessor: preprocessor::PreprocessorOptionsFile::default(),
inspector: inspector::InspectorOptionsFile::default(),
signing: signing::SigningSectionFile::default(),
meta_path: std::path::PathBuf::default(),
}
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
Loading
Loading