1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
use std::io::Write;
use std::{
cell::RefCell,
process::{Command, Stdio},
};
use log::info;
use lsp_types::{TextEdit, Url};
use shader_sense::position::ShaderRange;
use shader_sense::{shader::ShadingLanguage, shader_error::ShaderError};
use crate::server::common::{shader_range_to_lsp_range, ServerLanguageError};
use crate::server::ServerLanguage;
impl ServerLanguage {
fn clang_format_path() -> &'static str {
"clang-format"
}
pub fn is_clang_format_available() -> bool {
if cfg!(target_os = "wasi") {
false // Cannot spawn an exe in wasi.
} else {
match Command::new(Self::clang_format_path())
.arg("--version")
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.spawn()
{
Ok(child) => match child.wait_with_output() {
Ok(output) => {
info!(
"Using {} for formatting",
String::from_utf8(output.stdout)
.unwrap_or("clang-format version unknown".into())
.trim()
);
true
}
Err(_) => {
info!("clang-format failed to get version.");
false
}
},
Err(e) => {
if let std::io::ErrorKind::NotFound = e.kind() {
info!("clang-format not found, disabled formatting.");
false
} else {
info!("clang-format failed, disabled formatting.");
false
}
}
}
}
}
pub fn recolt_formatting(
&self,
uri: &Url,
range: Option<ShaderRange>,
) -> Result<Vec<TextEdit>, ServerLanguageError> {
let cached_file = self.get_cachable_file(&uri)?;
match &cached_file.shading_language {
ShadingLanguage::Wgsl => {
// TODO: Find a formatter for wgsl.
// naga does not provide much for this...
// wgsl analyzer does its own parsing https://github.com/wgsl-analyzer/wgsl-analyzer/blob/main/crates/wgsl_formatter/src/lib.rs
Ok(vec![])
}
// HLSL & GLSL can rely on clang-format.
ShadingLanguage::Hlsl | ShadingLanguage::Glsl => {
let shader_module = RefCell::borrow(&cached_file.shader_module);
let (offset, length) = match &range {
Some(range) => {
let byte_offset_start =
range.start.to_byte_offset(&shader_module.content)?;
let byte_offset_end = range.end.to_byte_offset(&shader_module.content)?;
let byte_length = byte_offset_end - byte_offset_start;
assert!(byte_length <= shader_module.content.len());
(byte_offset_start, byte_length)
}
None => (0, shader_module.content.len()),
};
info!(
"Offset {} and length {} for content {}",
offset,
length,
shader_module.content.len()
);
let mut child = Command::new("clang-format")
// Required for finding .clang-format
.arg(format!(
"--assume-filename={}",
shader_module.file_path.display()
))
.arg(format!("--offset={}", offset))
.arg(format!("--length={}", length))
//.arg("--style")
//.arg("file") // need a .clang-format file for style
//.arg("--fallback-style")
//.arg("")
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.spawn()?;
// Note we place inside a scope to ensure that stdin is closed
{
let mut stdin = child.stdin.take().expect("no stdin handle");
write!(stdin, "{}", &shader_module.content)?;
}
// Wait for the output and mark it as big edit chunk.
let output = child.wait_with_output()?;
if output.status.success() {
let formatted_code = String::from_utf8(output.stdout)
.map_err(|e| ShaderError::InternalErr(e.utf8_error().to_string()))?;
Ok(vec![TextEdit {
range: shader_range_to_lsp_range(&ShaderRange::whole(
&shader_module.content,
)),
new_text: formatted_code,
}])
} else {
Err(ServerLanguageError::InternalError(
"Failed to parse file with clang-format".into(),
))
}
}
}
}
}