mirror of
https://github.com/Myriad-Dreamin/tinymist.git
synced 2025-11-22 20:35:20 +00:00
feat: make configuration items null-safe (#1988)
When passing configuration items with null values, the default configurations are used. Note: I don't ensure this to be always true, some configuration items may have different non-default behaviors when accepting a null value now or in future. The `deserialize_null_default` is taken from https://github.com/serde-rs/serde/issues/1098. Configuration parsing changes: + some configurations only accepting boolean now coerce null to `false` (default). + some configurations only accepting an object now coerce null to default. + The `tinymist.preview.invertColors` now now coerces null to `"never"` (default).
This commit is contained in:
parent
f33f612f43
commit
fb1c8b3b35
2 changed files with 87 additions and 32 deletions
|
|
@ -70,17 +70,17 @@ type LspCompletion = CompletionItem;
|
|||
#[serde(rename_all = "camelCase")]
|
||||
pub struct CompletionFeat {
|
||||
/// Whether to trigger completions on arguments (placeholders) of snippets.
|
||||
#[serde(default)]
|
||||
#[serde(default, deserialize_with = "deserialize_null_default")]
|
||||
pub trigger_on_snippet_placeholders: bool,
|
||||
/// Whether supports trigger suggest completion, a.k.a. auto-completion.
|
||||
#[serde(default)]
|
||||
#[serde(default, deserialize_with = "deserialize_null_default")]
|
||||
pub trigger_suggest: bool,
|
||||
/// Whether supports trigger parameter hint, a.k.a. signature help.
|
||||
#[serde(default)]
|
||||
#[serde(default, deserialize_with = "deserialize_null_default")]
|
||||
pub trigger_parameter_hints: bool,
|
||||
/// Whether supports trigger the command combining suggest and parameter
|
||||
/// hints.
|
||||
#[serde(default)]
|
||||
#[serde(default, deserialize_with = "deserialize_null_default")]
|
||||
pub trigger_suggest_and_parameter_hints: bool,
|
||||
|
||||
/// The Way to complete symbols.
|
||||
|
|
@ -995,6 +995,17 @@ fn is_arg_like_context(mut matching: &LinkedNode) -> bool {
|
|||
// ctx.completions.push(compl);
|
||||
// }
|
||||
|
||||
fn deserialize_null_default<'de, D, T>(deserializer: D) -> Result<T, D::Error>
|
||||
where
|
||||
T: Default + Deserialize<'de>,
|
||||
D: serde::Deserializer<'de>,
|
||||
{
|
||||
let opt = Option::deserialize(deserializer)?;
|
||||
Ok(opt.unwrap_or_default())
|
||||
}
|
||||
|
||||
// todo: doesn't complete parameter now, which is not good.
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::slice_at;
|
||||
|
|
@ -1009,5 +1020,3 @@ mod tests {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
// todo: doesn't complete parameter now, which is not good.
|
||||
|
|
|
|||
|
|
@ -862,19 +862,19 @@ pub enum SemanticTokensMode {
|
|||
#[serde(rename_all = "camelCase")]
|
||||
pub struct PreviewFeat {
|
||||
/// The browsing preview options.
|
||||
#[serde(default)]
|
||||
#[serde(default, deserialize_with = "deserialize_null_default")]
|
||||
pub browsing: BrowsingPreviewOpts,
|
||||
/// The background preview options.
|
||||
#[serde(default)]
|
||||
#[serde(default, deserialize_with = "deserialize_null_default")]
|
||||
pub background: BackgroundPreviewOpts,
|
||||
/// When to refresh the preview.
|
||||
#[serde(default)]
|
||||
pub refresh: Option<TaskWhen>,
|
||||
/// Whether to enable partial rendering.
|
||||
#[serde(default)]
|
||||
#[serde(default, deserialize_with = "deserialize_null_default")]
|
||||
pub partial_rendering: bool,
|
||||
/// Invert colors for the preview.
|
||||
#[serde(default)]
|
||||
#[serde(default, deserialize_with = "deserialize_null_default")]
|
||||
pub invert_colors: PreviewInvertColors,
|
||||
}
|
||||
|
||||
|
|
@ -911,6 +911,7 @@ pub struct BrowsingPreviewOpts {
|
|||
#[serde(rename_all = "camelCase")]
|
||||
pub struct BackgroundPreviewOpts {
|
||||
/// Whether to run the preview in the background.
|
||||
#[serde(default, deserialize_with = "deserialize_null_default")]
|
||||
pub enabled: bool,
|
||||
/// The arguments for the background preview.
|
||||
pub args: Option<Vec<String>>,
|
||||
|
|
@ -957,6 +958,15 @@ pub(crate) fn get_semantic_tokens_options() -> SemanticTokensOptions {
|
|||
}
|
||||
}
|
||||
|
||||
fn deserialize_null_default<'de, D, T>(deserializer: D) -> Result<T, D::Error>
|
||||
where
|
||||
T: Default + Deserialize<'de>,
|
||||
D: serde::Deserializer<'de>,
|
||||
{
|
||||
let opt = Option::deserialize(deserializer)?;
|
||||
Ok(opt.unwrap_or_default())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
|
@ -1106,33 +1116,69 @@ mod tests {
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn test_null_completion() {
|
||||
let mut config = Config::default();
|
||||
let update = json!({
|
||||
"completion": null
|
||||
});
|
||||
|
||||
good_config(&mut config, &update);
|
||||
fn test_null_args() {
|
||||
fn test_good_config(path: &str) -> Config {
|
||||
let mut obj = json!(null);
|
||||
let path = path.split('.').collect::<Vec<_>>();
|
||||
for p in path.iter().rev() {
|
||||
obj = json!({ *p: obj });
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_null_root() {
|
||||
let mut config = Config::default();
|
||||
let update = json!({
|
||||
"root": null
|
||||
});
|
||||
|
||||
good_config(&mut config, &update);
|
||||
let mut c = Config::default();
|
||||
good_config(&mut c, &obj);
|
||||
c
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_null_extra_args() {
|
||||
let mut config = Config::default();
|
||||
let update = json!({
|
||||
"typstExtraArgs": null
|
||||
});
|
||||
test_good_config("root");
|
||||
test_good_config("rootPath");
|
||||
test_good_config("colorTheme");
|
||||
test_good_config("lint");
|
||||
test_good_config("customizedShowDocument");
|
||||
test_good_config("projectResolution");
|
||||
test_good_config("exportPdf");
|
||||
test_good_config("exportTarget");
|
||||
test_good_config("fontPaths");
|
||||
test_good_config("formatterMode");
|
||||
test_good_config("formatterPrintWidth");
|
||||
test_good_config("formatterIndentSize");
|
||||
test_good_config("formatterProseWrap");
|
||||
test_good_config("outputPath");
|
||||
test_good_config("semanticTokens");
|
||||
test_good_config("delegateFsRequests");
|
||||
test_good_config("supportHtmlInMarkdown");
|
||||
test_good_config("supportExtendedCodeAction");
|
||||
test_good_config("development");
|
||||
test_good_config("systemFonts");
|
||||
|
||||
good_config(&mut config, &update);
|
||||
test_good_config("completion");
|
||||
test_good_config("completion.triggerSuggest");
|
||||
test_good_config("completion.triggerParameterHints");
|
||||
test_good_config("completion.triggerSuggestAndParameterHints");
|
||||
test_good_config("completion.triggerOnSnippetPlaceholders");
|
||||
test_good_config("completion.symbol");
|
||||
test_good_config("completion.postfix");
|
||||
test_good_config("completion.postfixUfcs");
|
||||
test_good_config("completion.postfixUfcsLeft");
|
||||
test_good_config("completion.postfixUfcsRight");
|
||||
test_good_config("completion.postfixSnippets");
|
||||
|
||||
test_good_config("lint");
|
||||
test_good_config("lint.enabled");
|
||||
test_good_config("lint.when");
|
||||
|
||||
test_good_config("preview");
|
||||
test_good_config("preview.browsing");
|
||||
test_good_config("preview.browsing.args");
|
||||
test_good_config("preview.background");
|
||||
test_good_config("preview.background.enabled");
|
||||
test_good_config("preview.background.args");
|
||||
test_good_config("preview.refresh");
|
||||
test_good_config("preview.partialRendering");
|
||||
let c = test_good_config("preview.invertColors");
|
||||
assert_eq!(
|
||||
c.preview.invert_colors,
|
||||
PreviewInvertColors::Enum(PreviewInvertColor::Never)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue