From 7b8c44ef4ce7d6e9a09ea13064a84ce990722b46 Mon Sep 17 00:00:00 2001 From: Myriad-Dreamin Date: Sun, 16 Nov 2025 07:36:15 +0800 Subject: [PATCH] feat: detect battery mode --- Cargo.lock | 77 ++++- Cargo.toml | 1 + crates/tinymist-std/Cargo.toml | 3 +- crates/tinymist-std/src/battery.rs | 30 ++ crates/tinymist-std/src/lib.rs | 1 + crates/tinymist/src/config.rs | 7 +- locales/tinymist-rt.toml | 4 + tests/e2e/e2e/lsp.rs | 11 + .../vscode-syntax-only-1.87.2.json | 285 ++++++++++++++++++ 9 files changed, 412 insertions(+), 7 deletions(-) create mode 100644 crates/tinymist-std/src/battery.rs create mode 100644 tests/fixtures/initialization/vscode-syntax-only-1.87.2.json diff --git a/Cargo.lock b/Cargo.lock index b3cc72c3..6977e2cc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -221,6 +221,23 @@ version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" +[[package]] +name = "battery" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4b624268937c0e0a3edb7c27843f9e547c320d730c610d3b8e6e8e95b2026e4" +dependencies = [ + "cfg-if", + "core-foundation 0.7.0", + "lazycell", + "libc", + "mach", + "nix", + "num-traits", + "uom", + "winapi", +] + [[package]] name = "biblatex" version = "0.11.0" @@ -699,16 +716,32 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "core-foundation" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57d24c7a13c43e870e37c1556b74555437870a04514f7685f5b354e090567171" +dependencies = [ + "core-foundation-sys 0.7.0", + "libc", +] + [[package]] name = "core-foundation" version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b2a6cd9ae233e7f62ba4e9353e81a88df7fc8a5987b8d445b4d90c879bd156f6" dependencies = [ - "core-foundation-sys", + "core-foundation-sys 0.8.7", "libc", ] +[[package]] +name = "core-foundation-sys" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3a71ab494c0b5b860bdc8407ae08978052417070c2ced38573a9157ad75b8ac" + [[package]] name = "core-foundation-sys" version = "0.8.7" @@ -1828,7 +1861,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b0c919e5debc312ad217002b8048a17b7d83f80703865bbfcfebb0458b0b27d8" dependencies = [ "android_system_properties", - "core-foundation-sys", + "core-foundation-sys 0.8.7", "iana-time-zone-haiku", "js-sys", "log", @@ -2345,6 +2378,12 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" +[[package]] +name = "lazycell" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" + [[package]] name = "lexopt" version = "0.3.1" @@ -2449,6 +2488,15 @@ dependencies = [ "url", ] +[[package]] +name = "mach" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b823e83b2affd8f40a9ee8c29dbc56404c1e34cd2710921f2801e2cf29527afa" +dependencies = [ + "libc", +] + [[package]] name = "memchr" version = "2.7.5" @@ -2535,6 +2583,18 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "16cf681a23b4d0a43fc35024c176437f9dcd818db34e0f42ab456a0ee5ad497b" +[[package]] +name = "nix" +version = "0.19.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2ccba0cfe4fdf15982d1674c69b1fd80bad427d293849982668dfe454bd61f2" +dependencies = [ + "bitflags 1.3.2", + "cc", + "cfg-if", + "libc", +] + [[package]] name = "nohash-hasher" version = "0.2.0" @@ -4775,9 +4835,10 @@ version = "0.14.0" dependencies = [ "anyhow", "base64", + "battery", "bitvec", "comemo", - "core-foundation", + "core-foundation 0.10.1", "dashmap", "ecow", "fxhash", @@ -5767,6 +5828,16 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" +[[package]] +name = "uom" +version = "0.30.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e76503e636584f1e10b9b3b9498538279561adcef5412927ba00c2b32c4ce5ed" +dependencies = [ + "num-traits", + "typenum", +] + [[package]] name = "url" version = "2.5.2" diff --git a/Cargo.toml b/Cargo.toml index bf6ab164..6734b742 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,6 +34,7 @@ tokio = { version = "1.48.0", features = ["macros"] } tokio-util = { version = "0.7.16", features = ["compat"] } # System +battery = "0.7.8" temp-env = "0.3.6" open = { version = "5.3.2" } parking_lot = "0.12.1" diff --git a/crates/tinymist-std/Cargo.toml b/crates/tinymist-std/Cargo.toml index debc0d4c..c20b92d0 100644 --- a/crates/tinymist-std/Cargo.toml +++ b/crates/tinymist-std/Cargo.toml @@ -33,6 +33,7 @@ time = { workspace = true, features = ["formatting"] } lsp-types.workspace = true tempfile = { workspace = true, optional = true } same-file = { workspace = true, optional = true } +battery = { workspace = true, optional = true } # feature = "web" js-sys = { workspace = true, optional = true } @@ -78,7 +79,7 @@ rkyv-validation = ["rkyv/validation"] __web = ["wasm-bindgen", "js-sys", "web-time"] web = ["__web"] -system = ["tempfile", "same-file"] +system = ["tempfile", "same-file", "battery"] bi-hash = [] [lints] diff --git a/crates/tinymist-std/src/battery.rs b/crates/tinymist-std/src/battery.rs new file mode 100644 index 00000000..93822aa8 --- /dev/null +++ b/crates/tinymist-std/src/battery.rs @@ -0,0 +1,30 @@ +//! Battery-related utilities. + +#[cfg(feature = "system")] +mod system { + /// Detects if the system is currently in power saving mode. + pub fn is_power_saving() -> bool { + ::battery::Manager::new() + .ok() + .and_then(|manager| manager.batteries().ok()) + .map(|mut batteries| { + batteries.any(|battery| match battery { + Ok(bat) => matches!(bat.state(), ::battery::State::Discharging), + Err(_) => false, + }) + }) + .unwrap_or(false) + } +} +#[cfg(feature = "system")] +pub use system::*; + +#[cfg(not(feature = "system"))] +mod other { + /// Detects if the system is currently in power saving mode. + pub fn is_power_saving() -> bool { + false + } +} +#[cfg(not(feature = "system"))] +pub use other::*; diff --git a/crates/tinymist-std/src/lib.rs b/crates/tinymist-std/src/lib.rs index aac999b1..5eadf4fc 100644 --- a/crates/tinymist-std/src/lib.rs +++ b/crates/tinymist-std/src/lib.rs @@ -1,6 +1,7 @@ //! Additional functions wrapping Rust's standard library. pub mod adt; +pub mod battery; pub mod error; pub mod fs; pub mod hash; diff --git a/crates/tinymist/src/config.rs b/crates/tinymist/src/config.rs index b5a94501..216feea4 100644 --- a/crates/tinymist/src/config.rs +++ b/crates/tinymist/src/config.rs @@ -378,12 +378,13 @@ impl Config { } }; self.syntax_only = match try_(|| update.get("syntaxOnly")?.as_str()) { - Some("enable" | "onPowerSaving") => true, + Some("onPowerSaving") => tinymist_std::battery::is_power_saving(), + Some("enable") => true, Some("disable" | "auto") | None => false, Some(value) => { self.warnings.push(tinymist_l10n::t!( - "tinymist.config.badCompileStatus", - "compileStatus must be either `\"enable\"`, `\"disable\", `\"onPowerSaving\"`, or `\"auto\"`, got {value}", + "tinymist.config.badSyntaxOnly", + "syntaxOnly must be either `\"enable\"`, `\"disable\", `\"onPowerSaving\"`, or `\"auto\"`, got {value}", value = value.debug_l10n(), )); diff --git a/locales/tinymist-rt.toml b/locales/tinymist-rt.toml index 21c09de8..1d0dd38b 100644 --- a/locales/tinymist-rt.toml +++ b/locales/tinymist-rt.toml @@ -29,6 +29,10 @@ zh = "性能分析" en = "compileStatus must be either `\"enable\"`, `\"disable\", `\"onPowerSaving\"`, or `\"auto\"`, got {value}" zh = "compileStatus 必须是`\"enable\"`(打开)或 `\"disable\"`(关闭),得到 {value}" +[tinymist.config.badSyntaxOnly] +en = "syntaxOnly must be either `\"enable\"`, `\"disable\", `\"onPowerSaving\"`, or `\"auto\"`, got {value}" +zh = "syntaxOnly 必须是`\"enable\"`(打开)或 `\"disable\"`(关闭), `\"onPowerSaving\"`(省电模式下开启)或 `\"auto\"`(自动),得到 {value}" + [tinymist.config.badHoverPeriscope] en = "failed to parse hoverPeriscope: {err}" zh = "解析 hoverPeriscope 失败:{err}" diff --git a/tests/e2e/e2e/lsp.rs b/tests/e2e/e2e/lsp.rs index 249e1bb8..b58422ab 100644 --- a/tests/e2e/e2e/lsp.rs +++ b/tests/e2e/e2e/lsp.rs @@ -35,6 +35,17 @@ fn test_lsp() { let hash = replay_log(&root.join("vscode")); insta::assert_snapshot!(hash, @"siphash128_13:9a266bad2c9e8113b66eae3cfe83aab5"); } + + { + gen_smoke(SmokeArgs { + root: root.join("vscode"), + init: "initialization/vscode-syntax-only-1.87.2".to_owned(), + log: "tests/fixtures/editions/base.log".to_owned(), + }); + + let hash = replay_log(&root.join("vscode")); + insta::assert_snapshot!(hash, @"siphash128_13:9a266bad2c9e8113b66eae3cfe83aab5"); + } } fn handle_io(res: io::Result) -> T { diff --git a/tests/fixtures/initialization/vscode-syntax-only-1.87.2.json b/tests/fixtures/initialization/vscode-syntax-only-1.87.2.json new file mode 100644 index 00000000..0481cdd5 --- /dev/null +++ b/tests/fixtures/initialization/vscode-syntax-only-1.87.2.json @@ -0,0 +1,285 @@ +{ + "processId": 126420, + "clientInfo": { "name": "Visual Studio Code", "version": "1.87.2" }, + "locale": "en", + "capabilities": { + "workspace": { + "applyEdit": true, + "workspaceEdit": { + "documentChanges": true, + "resourceOperations": ["create", "rename", "delete"], + "failureHandling": "textOnlyTransactional", + "normalizesLineEndings": true, + "changeAnnotationSupport": { "groupsOnLabel": true } + }, + "configuration": true, + "didChangeWatchedFiles": { + "dynamicRegistration": true, + "relativePatternSupport": true + }, + "symbol": { + "dynamicRegistration": true, + "symbolKind": { + "valueSet": [ + 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 + ] + }, + "tagSupport": { "valueSet": [1] }, + "resolveSupport": { "properties": ["location.range"] } + }, + "codeLens": { "refreshSupport": true }, + "executeCommand": { "dynamicRegistration": true }, + "didChangeConfiguration": { "dynamicRegistration": true }, + "workspaceFolders": true, + "foldingRange": { "refreshSupport": true }, + "semanticTokens": { "refreshSupport": true }, + "fileOperations": { + "dynamicRegistration": true, + "didCreate": true, + "didRename": true, + "didDelete": true, + "willCreate": true, + "willRename": true, + "willDelete": true + }, + "inlineValue": { "refreshSupport": true }, + "inlayHint": { "refreshSupport": true }, + "diagnostics": { "refreshSupport": true } + }, + "textDocument": { + "publishDiagnostics": { + "relatedInformation": true, + "versionSupport": false, + "tagSupport": { "valueSet": [1, 2] }, + "codeDescriptionSupport": true, + "dataSupport": true + }, + "synchronization": { + "dynamicRegistration": true, + "willSave": true, + "willSaveWaitUntil": true, + "didSave": true + }, + "completion": { + "dynamicRegistration": true, + "contextSupport": true, + "completionItem": { + "snippetSupport": true, + "commitCharactersSupport": true, + "documentationFormat": ["markdown", "plaintext"], + "deprecatedSupport": true, + "preselectSupport": true, + "tagSupport": { "valueSet": [1] }, + "insertReplaceSupport": true, + "resolveSupport": { + "properties": ["documentation", "detail", "additionalTextEdits"] + }, + "insertTextModeSupport": { "valueSet": [1, 2] }, + "labelDetailsSupport": true + }, + "insertTextMode": 2, + "completionItemKind": { + "valueSet": [ + 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 + ] + }, + "completionList": { + "itemDefaults": [ + "commitCharacters", + "editRange", + "insertTextFormat", + "insertTextMode", + "data" + ] + } + }, + "hover": { + "dynamicRegistration": true, + "contentFormat": ["markdown", "plaintext"] + }, + "signatureHelp": { + "dynamicRegistration": true, + "signatureInformation": { + "documentationFormat": ["markdown", "plaintext"], + "parameterInformation": { "labelOffsetSupport": true }, + "activeParameterSupport": true + }, + "contextSupport": true + }, + "definition": { "dynamicRegistration": true, "linkSupport": true }, + "references": { "dynamicRegistration": true }, + "documentHighlight": { "dynamicRegistration": true }, + "documentSymbol": { + "dynamicRegistration": true, + "symbolKind": { + "valueSet": [ + 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 + ] + }, + "hierarchicalDocumentSymbolSupport": true, + "tagSupport": { "valueSet": [1] }, + "labelSupport": true + }, + "codeAction": { + "dynamicRegistration": true, + "isPreferredSupport": true, + "disabledSupport": true, + "dataSupport": true, + "resolveSupport": { "properties": ["edit"] }, + "codeActionLiteralSupport": { + "codeActionKind": { + "valueSet": [ + "", + "quickfix", + "refactor", + "refactor.extract", + "refactor.inline", + "refactor.rewrite", + "source", + "source.organizeImports" + ] + } + }, + "honorsChangeAnnotations": true + }, + "codeLens": { "dynamicRegistration": true }, + "formatting": { "dynamicRegistration": true }, + "rangeFormatting": { + "dynamicRegistration": true, + "rangesSupport": true + }, + "onTypeFormatting": { "dynamicRegistration": true }, + "rename": { + "dynamicRegistration": true, + "prepareSupport": true, + "prepareSupportDefaultBehavior": 1, + "honorsChangeAnnotations": true + }, + "documentLink": { "dynamicRegistration": true, "tooltipSupport": true }, + "typeDefinition": { "dynamicRegistration": true, "linkSupport": true }, + "implementation": { "dynamicRegistration": true, "linkSupport": true }, + "colorProvider": { "dynamicRegistration": true }, + "foldingRange": { + "dynamicRegistration": true, + "rangeLimit": 5000, + "lineFoldingOnly": true, + "foldingRangeKind": { "valueSet": ["comment", "imports", "region"] }, + "foldingRange": { "collapsedText": false } + }, + "declaration": { "dynamicRegistration": true, "linkSupport": true }, + "selectionRange": { "dynamicRegistration": true }, + "callHierarchy": { "dynamicRegistration": true }, + "semanticTokens": { + "dynamicRegistration": true, + "tokenTypes": [ + "namespace", + "type", + "class", + "enum", + "interface", + "struct", + "typeParameter", + "parameter", + "variable", + "property", + "enumMember", + "event", + "function", + "method", + "macro", + "keyword", + "modifier", + "comment", + "string", + "number", + "regexp", + "operator", + "decorator" + ], + "tokenModifiers": [ + "declaration", + "definition", + "readonly", + "static", + "deprecated", + "abstract", + "async", + "modification", + "documentation", + "defaultLibrary" + ], + "formats": ["relative"], + "requests": { "range": true, "full": { "delta": true } }, + "multilineTokenSupport": false, + "overlappingTokenSupport": false, + "serverCancelSupport": true, + "augmentsSyntaxTokens": true + }, + "linkedEditingRange": { "dynamicRegistration": true }, + "typeHierarchy": { "dynamicRegistration": true }, + "inlineValue": { "dynamicRegistration": true }, + "inlayHint": { + "dynamicRegistration": true, + "resolveSupport": { + "properties": [ + "tooltip", + "textEdits", + "label.tooltip", + "label.location", + "label.command" + ] + } + }, + "diagnostic": { + "dynamicRegistration": true, + "relatedDocumentSupport": false + } + }, + "window": { + "showMessage": { + "messageActionItem": { "additionalPropertiesSupport": true } + }, + "showDocument": { "support": true }, + "workDoneProgress": true + }, + "general": { + "staleRequestSupport": { + "cancel": true, + "retryOnContentModified": [ + "textDocument/semanticTokens/full", + "textDocument/semanticTokens/range", + "textDocument/semanticTokens/full/delta" + ] + }, + "regularExpressions": { "engine": "ECMAScript", "version": "ES2020" }, + "markdown": { "parser": "marked", "version": "1.1.0" }, + "positionEncodings": ["utf-16"] + }, + "notebookDocument": { + "synchronization": { + "dynamicRegistration": true, + "executionSummarySupport": true + } + } + }, + "initializationOptions": { + "outputPath": "$root/target/$dir/$name", + "exportPdf": "onSave", + "semanticTokens": "enable", + "noSystemFonts": null, + "typstExtraArgs": [], + "trace": { "server": "off" }, + "triggerSuggest": true, + "triggerParameterHints": true, + "triggerSuggestAndParameterHints": true, + "supportHtmlInMarkdown": true, + "supportExtendedCodeAction": true, + "syntaxOnly": "enable", + "customizedShowDocument": true, + "experimentalFormatterMode": "disable" + }, + "trace": "off" +}