Skip to content

Commit b43b96c

Browse files
authored
Implement variable substitution for Windows machines (#281)
1 parent 11b9f82 commit b43b96c

File tree

2 files changed

+103
-2
lines changed

2 files changed

+103
-2
lines changed

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,8 @@ UNISIM.is_third_party = true
160160
```
161161

162162
Paths in the `vhdl_ls.toml` can contain glob patterns (i.e., `.../*/`).
163-
On Unix machines, they can also contain environment variables using the default `$NAME` or `${NAME}` syntax.
163+
On Unix machines, they can contain environment variables using the `$NAME` or `${NAME}` syntax.
164+
On Windows machines, use the `%NAME%` syntax to substitute environment variables.
164165

165166
## As an LSP-client developer how should I integrate VHDL-LS?
166167

vhdl_lang/src/config.rs

Lines changed: 101 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -289,12 +289,51 @@ where
289289
M::Value: AsRef<str>,
290290
{
291291
if cfg!(windows) {
292-
Ok(s.to_string())
292+
substitute_variables_windows(s, map)
293293
} else {
294294
subst::substitute(s, map).map_err(|err| err.to_string())
295295
}
296296
}
297297

298+
fn substitute_variables_windows<'a, M>(s: &str, map: &'a M) -> Result<String, String>
299+
where
300+
M: VariableMap<'a> + ?Sized,
301+
M::Value: AsRef<str>,
302+
{
303+
let mut output: Vec<char> = Vec::with_capacity(s.len());
304+
let mut var_buf: Vec<char> = Vec::new();
305+
306+
let mut var_found = false;
307+
308+
for ch in s.chars() {
309+
if ch == '%' {
310+
if var_found {
311+
let var_name = String::from_iter(var_buf);
312+
var_buf = Vec::new();
313+
match map.get(&var_name) {
314+
None => {
315+
return Err(format!("Variable '{var_name}' not found"));
316+
}
317+
Some(value) => {
318+
output.extend(value.as_ref().chars());
319+
}
320+
}
321+
}
322+
var_found = !var_found;
323+
} else if !var_found {
324+
output.push(ch);
325+
} else {
326+
var_buf.push(ch)
327+
}
328+
}
329+
330+
if var_found {
331+
Err("Unterminated variable".into())
332+
} else {
333+
Ok(String::from_iter(output))
334+
}
335+
}
336+
298337
/// Returns true if the pattern is a plain file name and not a glob pattern
299338
fn is_literal(pattern: &str) -> bool {
300339
for chr in pattern.chars() {
@@ -622,6 +661,67 @@ work.files = [
622661
assert!(substitute_environment_variables("$not_unicode", &map).is_err());
623662
}
624663

664+
#[test]
665+
fn windows_variable_names() {
666+
let mut map = HashMap::new();
667+
map.insert("A".to_owned(), "a".to_owned());
668+
map.insert("ABCD".to_owned(), "abcd".to_owned());
669+
map.insert("A_0".to_owned(), "a0".to_owned());
670+
map.insert("_".to_owned(), "u".to_owned());
671+
map.insert("PATH".to_owned(), r#"some\path"#.to_owned());
672+
673+
assert_eq!(Ok("".to_owned()), substitute_variables_windows("", &map));
674+
assert_eq!(
675+
Ok("test".to_owned()),
676+
substitute_variables_windows("test", &map)
677+
);
678+
assert_eq!(
679+
Ok("a".to_owned()),
680+
substitute_variables_windows("%A%", &map)
681+
);
682+
assert_eq!(
683+
Ok("abcd".to_owned()),
684+
substitute_variables_windows("%ABCD%", &map)
685+
);
686+
assert_eq!(
687+
Ok("a0".to_owned()),
688+
substitute_variables_windows("%A_0%", &map)
689+
);
690+
assert_eq!(
691+
Ok("u".to_owned()),
692+
substitute_variables_windows("%_%", &map)
693+
);
694+
assert_eq!(
695+
Ok(r#"some\path"#.to_owned()),
696+
substitute_variables_windows("%PATH%", &map)
697+
);
698+
699+
// embedded in longer string
700+
assert_eq!(
701+
Ok(r#"test\a\test"#.to_owned()),
702+
substitute_variables_windows(r#"test\%A%\test"#, &map)
703+
);
704+
assert_eq!(
705+
Ok(r#"test\a"#.to_owned()),
706+
substitute_variables_windows(r#"test\%A%"#, &map)
707+
);
708+
assert_eq!(
709+
Ok(r#"a\test"#.to_owned()),
710+
substitute_variables_windows(r#"%A%\test"#, &map)
711+
);
712+
assert_eq!(
713+
Ok(r#"C:\test\some\path\test"#.to_owned()),
714+
substitute_variables_windows(r#"C:\test\%PATH%\test"#, &map)
715+
);
716+
717+
// error cases
718+
assert_eq!(
719+
substitute_variables_windows("%not_present%", &map),
720+
Err("Variable 'not_present' not found".into())
721+
);
722+
assert!(substitute_variables_windows("%not_unicode%", &map).is_err());
723+
}
724+
625725
// Issue #278
626726
#[test]
627727
#[cfg(windows)]

0 commit comments

Comments
 (0)