use crate::{ Event, ExtensionIndex, ExtensionIndexEntry, ExtensionIndexLanguageEntry, ExtensionIndexThemeEntry, ExtensionManifest, ExtensionSettings, ExtensionStore, GrammarManifestEntry, SchemaVersion, RELOAD_DEBOUNCE_DURATION,};use async_compression::futures::bufread::GzipEncoder;use collections::BTreeMap;use extension::ExtensionHostProxy;use fs::{FakeFs, Fs, RealFs};use futures::{io::BufReader, AsyncReadExt, StreamExt};use gpui::{AppContext as _, SemanticVersion, TestAppContext};use http_client::{FakeHttpClient, Response};use language::{LanguageMatcher, LanguageRegistry, LanguageServerBinaryStatus};use lsp::LanguageServerName;use node_runtime::NodeRuntime;use parking_lot::Mutex;use project::{Project, DEFAULT_COMPLETION_CONTEXT};use release_channel::AppVersion;use reqwest_client::ReqwestClient;use serde_json::json;use settings::{Settings as _, SettingsStore};use std::{ ffi::OsString, path::{Path, PathBuf}, sync::Arc,};use theme::ThemeRegistry;use util::test::TempTree; #[cfg(test)]#[ctor::ctor]fn init_logger() { if std::env::var("RUST_LOG").is_ok() { env_logger::init(); }} #[gpui::test]async fn test_extension_store(cx: &mut TestAppContext) { init_test(cx); let fs = FakeFs::new(cx.executor()); let http_client = FakeHttpClient::with_200_response(); fs.insert_tree( "/the-extension-dir", json!({ "installed": { "zed-monokai": { "extension.json": r#"{ "id": "zed-monokai", "name": "Zed Monokai", "version": "2.0.0", "themes": { "Monokai Dark": "themes/monokai.json", "Monokai Light": "themes/monokai.json", "Monokai Pro Dark": "themes/monokai-pro.json", "Monokai Pro Light": "themes/monokai-pro.json" } }"#, "themes": { "monokai.json": r#"{ "name": "Monokai", "author": "Someone", "themes": [ { "name": "Monokai Dark", "appearance": "dark", "style": {} }, { "name": "Monokai Light", "appearance": "light", "style": {} } ] }"#, "monokai-pro.json": r#"{ "name": "Monokai Pro", "author": "Someone", "themes": [ { "name": "Monokai Pro Dark", "appearance": "dark", "style": {} }, { "name": "Monokai Pro Light", "appearance": "light", "style": {} } ] }"#, } }, "zed-ruby": { "extension.json": r#"{ "id": "zed-ruby", "name": "Zed Ruby", "version": "1.0.0", "grammars": { "ruby": "grammars/ruby.wasm", "embedded_template": "grammars/embedded_template.wasm" }, "languages": { "ruby": "languages/ruby", "erb": "languages/erb" } }"#, "grammars": { "ruby.wasm": "", "embedded_template.wasm": "", }, "languages": { "ruby": { "config.toml": r#" name = "Ruby" grammar = "ruby" path_suffixes = ["rb"] "#, "highlights.scm": "", }, "erb": { "config.toml": r#" name = "ERB" grammar = "embedded_template" path_suffixes = ["erb"] "#, "highlights.scm": "", } }, } } }), ) .await; let mut expected_index = ExtensionIndex { extensions: [ ( "zed-ruby".into(), ExtensionIndexEntry { manifest: Arc::new(ExtensionManifest { id: "zed-ruby".into(), name: "Zed Ruby".into(), version: "1.0.0".into(), schema_version: SchemaVersion::ZERO, description: None, authors: Vec::new(), repository: None, themes: Default::default(), icon_themes: Vec::new(), lib: Default::default(), languages: vec!["languages/erb".into(), "languages/ruby".into()], grammars: [ ("embedded_template".into(), GrammarManifestEntry::default()), ("ruby".into(), GrammarManifestEntry::default()), ] .into_iter() .collect(), language_servers: BTreeMap::default(), context_servers: BTreeMap::default(), slash_commands: BTreeMap::default(), indexed_docs_providers: BTreeMap::default(), snippets: None, }), dev: false, }, ), ( "zed-monokai".into(), ExtensionIndexEntry { manifest: Arc::new(ExtensionManifest { id: "zed-monokai".into(), name: "Zed Monokai".into(), version: "2.0.0".into(), schema_version: SchemaVersion::ZERO, description: None, authors: vec![], repository: None, themes: vec![ "themes/monokai-pro.json".into(), "themes/monokai.json".into(), ], icon_themes: Vec::new(), lib: Default::default(), languages: Default::default(), grammars: BTreeMap::default(), language_servers: BTreeMap::default(), context_servers: BTreeMap::default(), slash_commands: BTreeMap::default(), indexed_docs_providers: BTreeMap::default(), snippets: None, }), dev: false, }, ), ] .into_iter() .collect(), languages: [ ( "ERB".into(), ExtensionIndexLanguageEntry { extension: "zed-ruby".into(), path: "languages/erb".into(), grammar: Some("embedded_template".into()), hidden: false, matcher: LanguageMatcher { path_suffixes: vec!["erb".into()], first_line_pattern: None, }, }, ), ( "Ruby".into(), ExtensionIndexLanguageEntry { extension: "zed-ruby".into(), path: "languages/ruby".into(), grammar: Some("ruby".into()), hidden: false, matcher: LanguageMatcher { path_suffixes: vec!["rb".into()], first_line_pattern: None, }, }, ), ] .into_iter() .collect(), themes: [ ( "Monokai Dark".into(), ExtensionIndexThemeEntry { extension: "zed-monokai".into(), path: "themes/monokai.json".into(), }, ), ( "Monokai Light".into(), ExtensionIndexThemeEntry { extension: "zed-monokai".into(), path: "themes/monokai.json".into(), }, ), ( "Monokai Pro Dark".into(), ExtensionIndexThemeEntry { extension: "zed-monokai".into(), path: "themes/monokai-pro.json".into(), }, ), ( "Monokai Pro Light".into(), ExtensionIndexThemeEntry { extension: "zed-monokai".into(), path: "themes/monokai-pro.json".into(), }, ), ] .into_iter() .collect(), icon_themes: BTreeMap::default(), }; let proxy = Arc::new(ExtensionHostProxy::new()); let theme_registry = Arc::new(ThemeRegistry::new(Box::new(()))); theme_extension::init(proxy.clone(), theme_registry.clone(), cx.executor()); let language_registry = Arc::new(LanguageRegistry::test(cx.executor())); language_extension::init(proxy.clone(), language_registry.clone()); let node_runtime = NodeRuntime::unavailable(); let store = cx.new(|cx| { ExtensionStore::new( PathBuf::from("/the-extension-dir"), None, proxy.clone(), fs.clone(), http_client.clone(), http_client.clone(), None, node_runtime.clone(), cx, ) }); cx.executor().advance_clock(RELOAD_DEBOUNCE_DURATION); store.read_with(cx, |store, _| { let index = &store.extension_index; assert_eq!(index.extensions, expected_index.extensions); assert_eq!(index.languages, expected_index.languages); assert_eq!(index.themes, expected_index.themes); assert_eq!( language_registry.language_names(), ["ERB", "Plain Text", "Ruby"] ); assert_eq!( theme_registry.list_names(), [ "Monokai Dark", "Monokai Light", "Monokai Pro Dark", "Monokai Pro Light", "One Dark", ] ); }); fs.insert_tree( "/the-extension-dir/installed/zed-gruvbox", json!({ "extension.json": r#"{ "id": "zed-gruvbox", "name": "Zed Gruvbox", "version": "1.0.0", "themes": { "Gruvbox": "themes/gruvbox.json" } }"#, "themes": { "gruvbox.json": r#"{ "name": "Gruvbox", "author": "Someone Else", "themes": [ { "name": "Gruvbox", "appearance": "dark", "style": {} } ] }"#, } }), ) .await; expected_index.extensions.insert( "zed-gruvbox".into(), ExtensionIndexEntry { manifest: Arc::new(ExtensionManifest { id: "zed-gruvbox".into(), name: "Zed Gruvbox".into(), version: "1.0.0".into(), schema_version: SchemaVersion::ZERO, description: None, authors: vec![], repository: None, themes: vec!["themes/gruvbox.json".into()], icon_themes: Vec::new(), lib: Default::default(), languages: Default::default(), grammars: BTreeMap::default(), language_servers: BTreeMap::default(), context_servers: BTreeMap::default(), slash_commands: BTreeMap::default(), indexed_docs_providers: BTreeMap::default(), snippets: None, }), dev: false, }, ); expected_index.themes.insert( "Gruvbox".into(), ExtensionIndexThemeEntry { extension: "zed-gruvbox".into(), path: "themes/gruvbox.json".into(), }, ); #[allow(clippy::let_underscore_future)] let _ = store.update(cx, |store, cx| store.reload(None, cx)); cx.executor().advance_clock(RELOAD_DEBOUNCE_DURATION); store.read_with(cx, |store, _| { let index = &store.extension_index; assert_eq!(index.extensions, expected_index.extensions); assert_eq!(index.languages, expected_index.languages); assert_eq!(index.themes, expected_index.themes); assert_eq!( theme_registry.list_names(), [ "Gruvbox", "Monokai Dark", "Monokai Light", "Monokai Pro Dark", "Monokai Pro Light", "One Dark", ] ); }); let prev_fs_metadata_call_count = fs.metadata_call_count(); let prev_fs_read_dir_call_count = fs.read_dir_call_count(); // Create new extension store, as if Zed were restarting. drop(store); let store = cx.new(|cx| { ExtensionStore::new( PathBuf::from("/the-extension-dir"), None, proxy, fs.clone(), http_client.clone(), http_client.clone(), None, node_runtime.clone(), cx, ) }); cx.executor().run_until_parked(); store.read_with(cx, |store, _| { assert_eq!(store.extension_index, expected_index); assert_eq!( language_registry.language_names(), ["ERB", "Plain Text", "Ruby"] ); assert_eq!( language_registry.grammar_names(), ["embedded_template".into(), "ruby".into()] ); assert_eq!( theme_registry.list_names(), [ "Gruvbox", "Monokai Dark", "Monokai Light", "Monokai Pro Dark", "Monokai Pro Light", "One Dark", ] ); // The on-disk manifest limits the number of FS calls that need to be made // on startup. assert_eq!(fs.read_dir_call_count(), prev_fs_read_dir_call_count); assert_eq!(fs.metadata_call_count(), prev_fs_metadata_call_count + 2); }); store.update(cx, |store, cx| { store.uninstall_extension("zed-ruby".into(), cx) }); cx.executor().advance_clock(RELOAD_DEBOUNCE_DURATION); expected_index.extensions.remove("zed-ruby"); expected_index.languages.remove("Ruby"); expected_index.languages.remove("ERB"); store.read_with(cx, |store, _| { assert_eq!(store.extension_index, expected_index); assert_eq!(language_registry.language_names(), ["Plain Text"]); assert_eq!(language_registry.grammar_names(), []); });} // todo(windows)// Disable this test on Windows for now. Because this test hangs at// `let fake_server = fake_servers.next().await.unwrap();`.// Reenable this test when we figure out why.#[gpui::test]#[cfg_attr(target_os = "windows", ignore)]async fn test_extension_store_with_test_extension(cx: &mut TestAppContext) { init_test(cx); cx.executor().allow_parking(); let root_dir = Path::new(env!("CARGO_MANIFEST_DIR")) .parent() .unwrap() .parent() .unwrap(); let cache_dir = root_dir.join("target"); let test_extension_id = "test-extension"; let test_extension_dir = root_dir.join("extensions").join(test_extension_id); let fs = Arc::new(RealFs::default()); let extensions_dir = TempTree::new(json!({ "installed": {}, "work": {} })); let project_dir = TempTree::new(json!({ "test.gleam": "" })); let extensions_dir = extensions_dir.path().canonicalize().unwrap(); let project_dir = project_dir.path().canonicalize().unwrap(); let project = Project::test(fs.clone(), [project_dir.as_path()], cx).await; let proxy = Arc::new(ExtensionHostProxy::new()); let theme_registry = Arc::new(ThemeRegistry::new(Box::new(()))); theme_extension::init(proxy.clone(), theme_registry.clone(), cx.executor()); let language_registry = project.read_with(cx, |project, _cx| project.languages().clone()); language_extension::init(proxy.clone(), language_registry.clone()); let node_runtime = NodeRuntime::unavailable(); let mut status_updates = language_registry.language_server_binary_statuses(); struct FakeLanguageServerVersion {Lines 1–500 of 785. Load more
use crate::{ Event, ExtensionIndex, ExtensionIndexEntry, ExtensionIndexLanguageEntry, ExtensionIndexThemeEntry, ExtensionManifest, ExtensionSettings, ExtensionStore, GrammarManifestEntry, SchemaVersion, RELOAD_DEBOUNCE_DURATION, }; use async_compression::futures::bufread::GzipEncoder; use collections::BTreeMap; use extension::ExtensionHostProxy; use fs::{FakeFs, Fs, RealFs}; use futures::{io::BufReader, AsyncReadExt, StreamExt}; use gpui::{AppContext as _, SemanticVersion, TestAppContext}; use http_client::{FakeHttpClient, Response}; use language::{LanguageMatcher, LanguageRegistry, LanguageServerBinaryStatus}; use lsp::LanguageServerName; use node_runtime::NodeRuntime; use parking_lot::Mutex; use project::{Project, DEFAULT_COMPLETION_CONTEXT}; use release_channel::AppVersion; use reqwest_client::ReqwestClient; use serde_json::json; use settings::{Settings as _, SettingsStore}; use std::{ ffi::OsString, path::{Path, PathBuf}, sync::Arc, }; use theme::ThemeRegistry; use util::test::TempTree; #[cfg(test)] #[ctor::ctor] fn init_logger() { if std::env::var("RUST_LOG").is_ok() { env_logger::init(); } } #[gpui::test] async fn test_extension_store(cx: &mut TestAppContext) { init_test(cx); let fs = FakeFs::new(cx.executor()); let http_client = FakeHttpClient::with_200_response(); fs.insert_tree( "/the-extension-dir", json!({ "installed": { "zed-monokai": { "extension.json": r#"{ "id": "zed-monokai", "name": "Zed Monokai", "version": "2.0.0", "themes": { "Monokai Dark": "themes/monokai.json", "Monokai Light": "themes/monokai.json", "Monokai Pro Dark": "themes/monokai-pro.json", "Monokai Pro Light": "themes/monokai-pro.json" } }"#, "themes": { "monokai.json": r#"{ "name": "Monokai", "author": "Someone", "themes": [ { "name": "Monokai Dark", "appearance": "dark", "style": {} }, { "name": "Monokai Light", "appearance": "light", "style": {} } ] }"#, "monokai-pro.json": r#"{ "name": "Monokai Pro", "author": "Someone", "themes": [ { "name": "Monokai Pro Dark", "appearance": "dark", "style": {} }, { "name": "Monokai Pro Light", "appearance": "light", "style": {} } ] }"#, } }, "zed-ruby": { "extension.json": r#"{ "id": "zed-ruby", "name": "Zed Ruby", "version": "1.0.0", "grammars": { "ruby": "grammars/ruby.wasm", "embedded_template": "grammars/embedded_template.wasm" }, "languages": { "ruby": "languages/ruby", "erb": "languages/erb" } }"#, "grammars": { "ruby.wasm": "", "embedded_template.wasm": "", }, "languages": { "ruby": { "config.toml": r#" name = "Ruby" grammar = "ruby" path_suffixes = ["rb"] "#, "highlights.scm": "", }, "erb": { "config.toml": r#" name = "ERB" grammar = "embedded_template" path_suffixes = ["erb"] "#, "highlights.scm": "", } }, } } }), ) .await; let mut expected_index = ExtensionIndex { extensions: [ ( "zed-ruby".into(), ExtensionIndexEntry { manifest: Arc::new(ExtensionManifest { id: "zed-ruby".into(), name: "Zed Ruby".into(), version: "1.0.0".into(), schema_version: SchemaVersion::ZERO, description: None, authors: Vec::new(), repository: None, themes: Default::default(), icon_themes: Vec::new(), lib: Default::default(), languages: vec!["languages/erb".into(), "languages/ruby".into()], grammars: [ ("embedded_template".into(), GrammarManifestEntry::default()), ("ruby".into(), GrammarManifestEntry::default()), ] .into_iter() .collect(), language_servers: BTreeMap::default(), context_servers: BTreeMap::default(), slash_commands: BTreeMap::default(), indexed_docs_providers: BTreeMap::default(), snippets: None, }), dev: false, }, ), ( "zed-monokai".into(), ExtensionIndexEntry { manifest: Arc::new(ExtensionManifest { id: "zed-monokai".into(), name: "Zed Monokai".into(), version: "2.0.0".into(), schema_version: SchemaVersion::ZERO, description: None, authors: vec![], repository: None, themes: vec![ "themes/monokai-pro.json".into(), "themes/monokai.json".into(), ], icon_themes: Vec::new(), lib: Default::default(), languages: Default::default(), grammars: BTreeMap::default(), language_servers: BTreeMap::default(), context_servers: BTreeMap::default(), slash_commands: BTreeMap::default(), indexed_docs_providers: BTreeMap::default(), snippets: None, }), dev: false, }, ), ] .into_iter() .collect(), languages: [ ( "ERB".into(), ExtensionIndexLanguageEntry { extension: "zed-ruby".into(), path: "languages/erb".into(), grammar: Some("embedded_template".into()), hidden: false, matcher: LanguageMatcher { path_suffixes: vec!["erb".into()], first_line_pattern: None, }, }, ), ( "Ruby".into(), ExtensionIndexLanguageEntry { extension: "zed-ruby".into(), path: "languages/ruby".into(), grammar: Some("ruby".into()), hidden: false, matcher: LanguageMatcher { path_suffixes: vec!["rb".into()], first_line_pattern: None, }, }, ), ] .into_iter() .collect(), themes: [ ( "Monokai Dark".into(), ExtensionIndexThemeEntry { extension: "zed-monokai".into(), path: "themes/monokai.json".into(), }, ), ( "Monokai Light".into(), ExtensionIndexThemeEntry { extension: "zed-monokai".into(), path: "themes/monokai.json".into(), }, ), ( "Monokai Pro Dark".into(), ExtensionIndexThemeEntry { extension: "zed-monokai".into(), path: "themes/monokai-pro.json".into(), }, ), ( "Monokai Pro Light".into(), ExtensionIndexThemeEntry { extension: "zed-monokai".into(), path: "themes/monokai-pro.json".into(), }, ), ] .into_iter() .collect(), icon_themes: BTreeMap::default(), }; let proxy = Arc::new(ExtensionHostProxy::new()); let theme_registry = Arc::new(ThemeRegistry::new(Box::new(()))); theme_extension::init(proxy.clone(), theme_registry.clone(), cx.executor()); let language_registry = Arc::new(LanguageRegistry::test(cx.executor())); language_extension::init(proxy.clone(), language_registry.clone()); let node_runtime = NodeRuntime::unavailable(); let store = cx.new(|cx| { ExtensionStore::new( PathBuf::from("/the-extension-dir"), None, proxy.clone(), fs.clone(), http_client.clone(), http_client.clone(), None, node_runtime.clone(), cx, ) }); cx.executor().advance_clock(RELOAD_DEBOUNCE_DURATION); store.read_with(cx, |store, _| { let index = &store.extension_index; assert_eq!(index.extensions, expected_index.extensions); assert_eq!(index.languages, expected_index.languages); assert_eq!(index.themes, expected_index.themes); assert_eq!( language_registry.language_names(), ["ERB", "Plain Text", "Ruby"] ); assert_eq!( theme_registry.list_names(), [ "Monokai Dark", "Monokai Light", "Monokai Pro Dark", "Monokai Pro Light", "One Dark", ] ); }); fs.insert_tree( "/the-extension-dir/installed/zed-gruvbox", json!({ "extension.json": r#"{ "id": "zed-gruvbox", "name": "Zed Gruvbox", "version": "1.0.0", "themes": { "Gruvbox": "themes/gruvbox.json" } }"#, "themes": { "gruvbox.json": r#"{ "name": "Gruvbox", "author": "Someone Else", "themes": [ { "name": "Gruvbox", "appearance": "dark", "style": {} } ] }"#, } }), ) .await; expected_index.extensions.insert( "zed-gruvbox".into(), ExtensionIndexEntry { manifest: Arc::new(ExtensionManifest { id: "zed-gruvbox".into(), name: "Zed Gruvbox".into(), version: "1.0.0".into(), schema_version: SchemaVersion::ZERO, description: None, authors: vec![], repository: None, themes: vec!["themes/gruvbox.json".into()], icon_themes: Vec::new(), lib: Default::default(), languages: Default::default(), grammars: BTreeMap::default(), language_servers: BTreeMap::default(), context_servers: BTreeMap::default(), slash_commands: BTreeMap::default(), indexed_docs_providers: BTreeMap::default(), snippets: None, }), dev: false, }, ); expected_index.themes.insert( "Gruvbox".into(), ExtensionIndexThemeEntry { extension: "zed-gruvbox".into(), path: "themes/gruvbox.json".into(), }, ); #[allow(clippy::let_underscore_future)] let _ = store.update(cx, |store, cx| store.reload(None, cx)); cx.executor().advance_clock(RELOAD_DEBOUNCE_DURATION); store.read_with(cx, |store, _| { let index = &store.extension_index; assert_eq!(index.extensions, expected_index.extensions); assert_eq!(index.languages, expected_index.languages); assert_eq!(index.themes, expected_index.themes); assert_eq!( theme_registry.list_names(), [ "Gruvbox", "Monokai Dark", "Monokai Light", "Monokai Pro Dark", "Monokai Pro Light", "One Dark", ] ); }); let prev_fs_metadata_call_count = fs.metadata_call_count(); let prev_fs_read_dir_call_count = fs.read_dir_call_count(); // Create new extension store, as if Zed were restarting. drop(store); let store = cx.new(|cx| { ExtensionStore::new( PathBuf::from("/the-extension-dir"), None, proxy, fs.clone(), http_client.clone(), http_client.clone(), None, node_runtime.clone(), cx, ) }); cx.executor().run_until_parked(); store.read_with(cx, |store, _| { assert_eq!(store.extension_index, expected_index); assert_eq!( language_registry.language_names(), ["ERB", "Plain Text", "Ruby"] ); assert_eq!( language_registry.grammar_names(), ["embedded_template".into(), "ruby".into()] ); assert_eq!( theme_registry.list_names(), [ "Gruvbox", "Monokai Dark", "Monokai Light", "Monokai Pro Dark", "Monokai Pro Light", "One Dark", ] ); // The on-disk manifest limits the number of FS calls that need to be made // on startup. assert_eq!(fs.read_dir_call_count(), prev_fs_read_dir_call_count); assert_eq!(fs.metadata_call_count(), prev_fs_metadata_call_count + 2); }); store.update(cx, |store, cx| { store.uninstall_extension("zed-ruby".into(), cx) }); cx.executor().advance_clock(RELOAD_DEBOUNCE_DURATION); expected_index.extensions.remove("zed-ruby"); expected_index.languages.remove("Ruby"); expected_index.languages.remove("ERB"); store.read_with(cx, |store, _| { assert_eq!(store.extension_index, expected_index); assert_eq!(language_registry.language_names(), ["Plain Text"]); assert_eq!(language_registry.grammar_names(), []); }); } // todo(windows) // Disable this test on Windows for now. Because this test hangs at // `let fake_server = fake_servers.next().await.unwrap();`. // Reenable this test when we figure out why. #[gpui::test] #[cfg_attr(target_os = "windows", ignore)] async fn test_extension_store_with_test_extension(cx: &mut TestAppContext) { init_test(cx); cx.executor().allow_parking(); let root_dir = Path::new(env!("CARGO_MANIFEST_DIR")) .parent() .unwrap() .parent() .unwrap(); let cache_dir = root_dir.join("target"); let test_extension_id = "test-extension"; let test_extension_dir = root_dir.join("extensions").join(test_extension_id); let fs = Arc::new(RealFs::default()); let extensions_dir = TempTree::new(json!({ "installed": {}, "work": {} })); let project_dir = TempTree::new(json!({ "test.gleam": "" })); let extensions_dir = extensions_dir.path().canonicalize().unwrap(); let project_dir = project_dir.path().canonicalize().unwrap(); let project = Project::test(fs.clone(), [project_dir.as_path()], cx).await; let proxy = Arc::new(ExtensionHostProxy::new()); let theme_registry = Arc::new(ThemeRegistry::new(Box::new(()))); theme_extension::init(proxy.clone(), theme_registry.clone(), cx.executor()); let language_registry = project.read_with(cx, |project, _cx| project.languages().clone()); language_extension::init(proxy.clone(), language_registry.clone()); let node_runtime = NodeRuntime::unavailable(); let mut status_updates = language_registry.language_server_binary_statuses(); struct FakeLanguageServerVersion {