1use std::collections::HashMap;
16use std::error::Error;
17use std::fmt;
18use std::fs;
19use std::io::{self, Read};
20use std::path::{Path, PathBuf};
21use std::sync::Arc;
22
23use serde::de::{self, Deserialize};
24use serde_json::{self, Value};
25use toml;
26
27use crate::syntax::{LanguageId, Languages};
28use crate::tabs::{BufferId, ViewId};
29
30fn load_base_config() -> Table {
32 fn load(default: &str) -> Table {
33 table_from_toml_str(default).expect("default configs must load")
34 }
35
36 fn platform_overrides() -> Option<Table> {
37 if cfg!(test) {
38 None
41 } else if cfg!(windows) {
42 let toml = include_str!("../assets/windows.toml");
43 Some(load(toml))
44 } else {
45 None
47 }
48 }
49
50 let base_toml: &str = include_str!("../assets/defaults.toml");
51 let mut base = load(base_toml);
52 if let Some(overrides) = platform_overrides() {
53 for (k, v) in overrides.iter() {
54 base.insert(k.to_owned(), v.to_owned());
55 }
56 }
57 base
58}
59
60pub type Table = serde_json::Map<String, Value>;
62
63#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
65#[serde(rename_all = "snake_case")]
66pub enum ConfigDomain {
67 General,
69 Language(LanguageId),
71 UserOverride(BufferId),
73 #[serde(skip_deserializing)]
75 SysOverride(BufferId),
76}
77
78#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
81#[serde(rename_all = "snake_case")]
82pub enum ConfigDomainExternal {
83 General,
84 Syntax(LanguageId),
86 Language(LanguageId),
87 UserOverride(ViewId),
88}
89
90#[derive(Debug)]
92pub enum ConfigError {
93 UnknownDomain(String),
95 Parse(PathBuf, toml::de::Error),
97 UnexpectedItem(serde_json::Error),
99 Io(io::Error),
101}
102
103#[derive(Debug)]
106pub struct ConfigPair {
107 base: Option<Arc<Table>>,
109 user: Option<Arc<Table>>,
112 cache: Arc<Table>,
114}
115
116#[derive(Debug, Clone)]
119struct LanguageTag {
120 detected: LanguageId,
121 user: Option<LanguageId>,
122}
123
124#[derive(Debug)]
125pub struct ConfigManager {
126 configs: HashMap<ConfigDomain, ConfigPair>,
128 languages: Languages,
130 buffer_tags: HashMap<BufferId, LanguageTag>,
132 buffer_configs: HashMap<BufferId, BufferConfig>,
134 config_dir: Option<PathBuf>,
137 extras_dir: Option<PathBuf>,
140}
141
142#[derive(Debug, Clone, Default)]
145struct TableStack(Vec<Arc<Table>>);
146
147#[derive(Debug, Clone, Serialize, Deserialize)]
149pub struct Config<T> {
150 #[serde(skip)]
153 source: TableStack,
154 pub items: T,
156}
157
158fn deserialize_tab_size<'de, D>(deserializer: D) -> Result<usize, D::Error>
159where
160 D: serde::Deserializer<'de>,
161{
162 let tab_size = usize::deserialize(deserializer)?;
163 if tab_size == 0 {
164 Err(de::Error::invalid_value(
165 de::Unexpected::Unsigned(tab_size as u64),
166 &"tab_size must be at least 1",
167 ))
168 } else {
169 Ok(tab_size)
170 }
171}
172
173#[derive(Debug, Clone, Deserialize, Serialize, PartialEq)]
175pub struct BufferItems {
176 pub line_ending: String,
177 #[serde(deserialize_with = "deserialize_tab_size")]
178 pub tab_size: usize,
179 pub translate_tabs_to_spaces: bool,
180 pub use_tab_stops: bool,
181 pub font_face: String,
182 pub font_size: f32,
183 pub auto_indent: bool,
184 pub scroll_past_end: bool,
185 pub wrap_width: usize,
186 pub word_wrap: bool,
187 pub autodetect_whitespace: bool,
188 pub surrounding_pairs: Vec<(String, String)>,
189 pub save_with_newline: bool,
190}
191
192pub type BufferConfig = Config<BufferItems>;
193
194impl ConfigPair {
195 fn with_base<T: Into<Option<Table>>>(table: T) -> Self {
197 let base = table.into().map(Arc::new);
198 let cache = base.clone().unwrap_or_default();
199 ConfigPair { base, cache, user: None }
200 }
201
202 fn new_with_base<T: Into<Option<Table>>>(&self, table: T) -> Self {
205 let mut new_self = ConfigPair::with_base(table);
206 new_self.user = self.user.clone();
207 new_self.rebuild();
208 new_self
209 }
210
211 fn set_table(&mut self, user: Table) {
212 self.user = Some(Arc::new(user));
213 self.rebuild();
214 }
215
216 fn table_for_update(&self, user: Table) -> Table {
219 let mut new_user: Table =
220 self.user.as_ref().map(|arc| arc.as_ref().clone()).unwrap_or_default();
221 for (k, v) in user {
222 if v.is_null() {
223 new_user.remove(&k);
224 } else {
225 new_user.insert(k, v);
226 }
227 }
228 new_user
229 }
230
231 fn rebuild(&mut self) {
232 let mut cache = self.base.clone().unwrap_or_default();
233 if let Some(ref user) = self.user {
234 for (k, v) in user.iter() {
235 Arc::make_mut(&mut cache).insert(k.to_owned(), v.clone());
236 }
237 }
238 self.cache = cache;
239 }
240}
241
242impl ConfigManager {
243 pub fn new(config_dir: Option<PathBuf>, extras_dir: Option<PathBuf>) -> Self {
244 let base = load_base_config();
245 let mut defaults = HashMap::new();
246 defaults.insert(ConfigDomain::General, ConfigPair::with_base(base));
247 ConfigManager {
248 configs: defaults,
249 buffer_tags: HashMap::new(),
250 buffer_configs: HashMap::new(),
251 languages: Languages::default(),
252 config_dir,
253 extras_dir,
254 }
255 }
256
257 pub(crate) fn base_config_file_path(&self) -> Option<PathBuf> {
259 let config_file = self.config_dir.as_ref().map(|p| p.join("preferences.xiconfig"));
260 let exists = config_file.as_ref().map(|p| p.exists()).unwrap_or(false);
261 if exists {
262 config_file
263 } else {
264 None
265 }
266 }
267
268 pub(crate) fn get_plugin_paths(&self) -> Vec<PathBuf> {
269 let config_dir = self.config_dir.as_ref().map(|p| p.join("plugins"));
270 [self.extras_dir.as_ref(), config_dir.as_ref()]
271 .iter()
272 .flat_map(|p| p.map(|p| p.to_owned()))
273 .filter(|p| p.exists())
274 .collect()
275 }
276
277 pub(crate) fn add_buffer(&mut self, id: BufferId, path: Option<&Path>) -> Table {
288 let lang = path.and_then(|p| self.language_for_path(p)).unwrap_or_default();
289 let lang_tag = LanguageTag::new(lang);
290 assert!(self.buffer_tags.insert(id, lang_tag).is_none());
291 self.update_buffer_config(id).expect("new buffer must always have config")
292 }
293
294 pub(crate) fn update_buffer_path(&mut self, id: BufferId, path: &Path) -> Option<Table> {
300 assert!(self.buffer_tags.contains_key(&id));
301 let lang = self.language_for_path(path).unwrap_or_default();
302 let has_changed = self.buffer_tags.get_mut(&id).map(|tag| tag.set_detected(lang)).unwrap();
303
304 if has_changed {
305 self.update_buffer_config(id)
306 } else {
307 None
308 }
309 }
310
311 pub(crate) fn remove_buffer(&mut self, id: BufferId) {
317 self.buffer_tags.remove(&id).expect("remove key must exist");
318 self.buffer_configs.remove(&id);
319 }
321
322 pub(crate) fn override_language(
325 &mut self,
326 id: BufferId,
327 new_lang: LanguageId,
328 ) -> Option<Table> {
329 let has_changed = self
330 .buffer_tags
331 .get_mut(&id)
332 .map(|tag| tag.set_user(Some(new_lang)))
333 .expect("buffer must exist");
334 if has_changed {
335 self.update_buffer_config(id)
336 } else {
337 None
338 }
339 }
340
341 fn update_buffer_config(&mut self, id: BufferId) -> Option<Table> {
342 let new_config = self.generate_buffer_config(id);
343 let changes = new_config.changes_from(self.buffer_configs.get(&id));
344 self.buffer_configs.insert(id, new_config);
345 changes
346 }
347
348 fn update_all_buffer_configs(&mut self) -> Vec<(BufferId, Table)> {
349 self.buffer_configs
350 .keys()
351 .cloned()
352 .collect::<Vec<_>>()
353 .into_iter()
354 .flat_map(|k| self.update_buffer_config(k).map(|c| (k, c)))
355 .collect::<Vec<_>>()
356 }
357
358 fn generate_buffer_config(&mut self, id: BufferId) -> BufferConfig {
359 let lang = self
361 .buffer_tags
362 .get(&id)
363 .map(LanguageTag::resolve)
364 .and_then(|name| self.languages.language_for_name(name))
365 .map(|l| l.name.clone());
366 let mut configs = Vec::new();
367
368 configs.push(self.configs.get(&ConfigDomain::General));
369 if let Some(s) = lang {
370 configs.push(self.configs.get(&s.into()))
371 };
372 configs.push(self.configs.get(&ConfigDomain::SysOverride(id)));
373 configs.push(self.configs.get(&ConfigDomain::UserOverride(id)));
374
375 let configs = configs
376 .iter()
377 .flat_map(Option::iter)
378 .map(|c| c.cache.clone())
379 .rev()
380 .collect::<Vec<_>>();
381
382 let stack = TableStack(configs);
383 stack.into_config()
384 }
385
386 pub(crate) fn get_buffer_config(&self, id: BufferId) -> &BufferConfig {
393 self.buffer_configs.get(&id).unwrap()
394 }
395
396 pub(crate) fn get_buffer_language(&self, id: BufferId) -> LanguageId {
402 self.buffer_tags.get(&id).map(LanguageTag::resolve).unwrap()
403 }
404
405 pub fn set_languages(&mut self, languages: Languages) {
407 self.languages.difference(&languages).iter().for_each(|lang| {
409 let domain: ConfigDomain = lang.name.clone().into();
410 if let Some(pair) = self.configs.get_mut(&domain) {
411 *pair = pair.new_with_base(None);
412 }
413 });
414
415 for language in languages.iter() {
416 let lang_id = language.name.clone();
417 let domain: ConfigDomain = lang_id.into();
418 let default_config = language.default_config.clone();
419 self.configs
420 .entry(domain.clone())
421 .and_modify(|c| *c = c.new_with_base(default_config.clone()))
422 .or_insert_with(|| ConfigPair::with_base(default_config));
423 if let Some(table) = self.load_user_config_file(&domain) {
424 let _ = self.set_user_config(domain, table);
427 }
428 }
429 self.languages = languages;
432 self.update_all_buffer_configs();
433 }
434
435 fn load_user_config_file(&self, domain: &ConfigDomain) -> Option<Table> {
436 let path = self
437 .config_dir
438 .as_ref()
439 .map(|p| p.join(domain.file_stem()).with_extension("xiconfig"))?;
440
441 if !path.exists() {
442 return None;
443 }
444
445 match try_load_from_file(&path) {
446 Ok(t) => Some(t),
447 Err(e) => {
448 error!("Error loading config: {:?}", e);
449 None
450 }
451 }
452 }
453
454 pub fn language_for_path(&self, path: &Path) -> Option<LanguageId> {
455 self.languages.language_for_path(path).map(|lang| lang.name.clone())
456 }
457
458 pub fn set_user_config(
462 &mut self,
463 domain: ConfigDomain,
464 config: Table,
465 ) -> Result<Vec<(BufferId, Table)>, ConfigError> {
466 self.check_table(&config)?;
467 self.configs
468 .entry(domain.clone())
469 .or_insert_with(|| ConfigPair::with_base(None))
470 .set_table(config);
471 Ok(self.update_all_buffer_configs())
472 }
473
474 pub(crate) fn table_for_update(&mut self, domain: ConfigDomain, changes: Table) -> Table {
494 self.configs
495 .entry(domain.clone())
496 .or_insert_with(|| ConfigPair::with_base(None))
497 .table_for_update(changes)
498 }
499
500 pub fn domain_for_path(&self, path: &Path) -> Option<ConfigDomain> {
502 if path.extension().map(|e| e != "xiconfig").unwrap_or(true) {
503 return None;
504 }
505 match path.file_stem().and_then(|s| s.to_str()) {
506 Some("preferences") => Some(ConfigDomain::General),
507 Some(name) if self.languages.language_for_name(&name).is_some() => {
508 let lang =
509 self.languages.language_for_name(&name).map(|lang| lang.name.clone()).unwrap();
510 Some(ConfigDomain::Language(lang))
511 }
512 _ => None,
514 }
515 }
516
517 fn check_table(&self, table: &Table) -> Result<(), ConfigError> {
518 let defaults = self
519 .configs
520 .get(&ConfigDomain::General)
521 .and_then(|pair| pair.base.clone())
522 .expect("general domain must have defaults");
523 let mut defaults: Table = defaults.as_ref().clone();
524 for (k, v) in table.iter() {
525 if v.is_null() {
527 continue;
528 }
529 defaults.insert(k.to_owned(), v.to_owned());
530 }
531 let _: BufferItems = serde_json::from_value(defaults.into())?;
532 Ok(())
533 }
534
535 pub(crate) fn get_themes_dir(&self) -> Option<PathBuf> {
538 let themes_dir = self.config_dir.as_ref().map(|p| p.join("themes"));
539
540 if let Some(p) = themes_dir {
541 if p.exists() {
542 return Some(p);
543 }
544 if fs::DirBuilder::new().create(&p).is_ok() {
545 return Some(p);
546 }
547 }
548 None
549 }
550
551 pub(crate) fn get_plugins_dir(&self) -> Option<PathBuf> {
554 let plugins_dir = self.config_dir.as_ref().map(|p| p.join("plugins"));
555
556 if let Some(p) = plugins_dir {
557 if p.exists() {
558 return Some(p);
559 }
560 if fs::DirBuilder::new().create(&p).is_ok() {
561 return Some(p);
562 }
563 }
564 None
565 }
566}
567
568impl TableStack {
569 fn collate(&self) -> Table {
571 let mut out = Table::new();
574 for table in &self.0 {
575 for (k, v) in table.iter() {
576 if !out.contains_key(k) {
577 out.insert(k.to_owned(), v.to_owned());
580 }
581 }
582 }
583 out
584 }
585
586 fn into_config<T>(self) -> Config<T>
588 where
589 for<'de> T: Deserialize<'de>,
590 {
591 let out = self.collate();
592 let items: T = serde_json::from_value(out.into()).unwrap();
593 let source = self;
594 Config { source, items }
595 }
596
597 fn get<S: AsRef<str>>(&self, key: S) -> Option<&Value> {
600 for table in &self.0 {
601 if let Some(v) = table.get(key.as_ref()) {
602 return Some(v);
603 }
604 }
605 None
606 }
607
608 fn diff(&self, other: &TableStack) -> Option<Table> {
611 let mut out: Option<Table> = None;
612 let this = self.collate();
613 for (k, v) in this.iter() {
614 if other.get(k) != Some(v) {
615 let out: &mut Table = out.get_or_insert(Table::new());
616 out.insert(k.to_owned(), v.to_owned());
617 }
618 }
619 out
620 }
621}
622
623impl<T> Config<T> {
624 pub fn to_table(&self) -> Table {
625 self.source.collate()
626 }
627}
628
629impl<'de, T: Deserialize<'de>> Config<T> {
630 pub fn changes_from(&self, other: Option<&Config<T>>) -> Option<Table> {
633 match other {
634 Some(other) => self.source.diff(&other.source),
635 None => self.source.collate().into(),
636 }
637 }
638}
639
640impl ConfigDomain {
641 fn file_stem(&self) -> &str {
642 match self {
643 ConfigDomain::General => "preferences",
644 ConfigDomain::Language(lang) => lang.as_ref(),
645 ConfigDomain::UserOverride(_) | ConfigDomain::SysOverride(_) => "we don't have files",
646 }
647 }
648}
649
650impl LanguageTag {
651 fn new(detected: LanguageId) -> Self {
652 LanguageTag { detected, user: None }
653 }
654
655 fn resolve(&self) -> LanguageId {
656 self.user.as_ref().unwrap_or(&self.detected).clone()
657 }
658
659 fn set_detected(&mut self, detected: LanguageId) -> bool {
662 let before = self.resolve();
663 self.detected = detected;
664 before != self.resolve()
665 }
666
667 #[allow(dead_code)]
670 fn set_user(&mut self, new_lang: Option<LanguageId>) -> bool {
671 let has_changed = self.user != new_lang;
672 self.user = new_lang;
673 has_changed
674 }
675}
676
677impl<T: PartialEq> PartialEq for Config<T> {
678 fn eq(&self, other: &Config<T>) -> bool {
679 self.items == other.items
680 }
681}
682
683impl From<LanguageId> for ConfigDomain {
684 fn from(src: LanguageId) -> ConfigDomain {
685 ConfigDomain::Language(src)
686 }
687}
688
689impl From<BufferId> for ConfigDomain {
690 fn from(src: BufferId) -> ConfigDomain {
691 ConfigDomain::UserOverride(src)
692 }
693}
694
695impl fmt::Display for ConfigError {
696 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
697 use self::ConfigError::*;
698 match *self {
699 UnknownDomain(ref s) => write!(f, "{}: {}", self.description(), s),
700 Parse(ref p, ref e) => write!(f, "{} ({:?}), {:?}", self.description(), p, e),
701 Io(ref e) => write!(f, "error loading config: {:?}", e),
702 UnexpectedItem(ref e) => write!(f, "{}", e),
703 }
704 }
705}
706
707impl Error for ConfigError {
708 fn description(&self) -> &str {
709 use self::ConfigError::*;
710 match *self {
711 UnknownDomain(..) => "unknown domain",
712 Parse(_, ref e) => e.description(),
713 Io(ref e) => e.description(),
714 UnexpectedItem(ref e) => e.description(),
715 }
716 }
717}
718
719impl From<io::Error> for ConfigError {
720 fn from(src: io::Error) -> ConfigError {
721 ConfigError::Io(src)
722 }
723}
724
725impl From<serde_json::Error> for ConfigError {
726 fn from(src: serde_json::Error) -> ConfigError {
727 ConfigError::UnexpectedItem(src)
728 }
729}
730
731pub(crate) fn init_config_dir(dir: &Path) -> io::Result<()> {
733 let builder = fs::DirBuilder::new();
734 builder.create(dir)?;
735 builder.create(dir.join("plugins"))?;
736 Ok(())
737}
738
739pub(crate) fn try_load_from_file(path: &Path) -> Result<Table, ConfigError> {
742 let mut file = fs::File::open(&path)?;
743 let mut contents = String::new();
744 file.read_to_string(&mut contents)?;
745 table_from_toml_str(&contents).map_err(|e| ConfigError::Parse(path.to_owned(), e))
746}
747
748pub(crate) fn table_from_toml_str(s: &str) -> Result<Table, toml::de::Error> {
749 let table = toml::from_str(&s)?;
750 let table = from_toml_value(table).as_object().unwrap().to_owned();
751 Ok(table)
752}
753
754fn from_toml_value(value: toml::Value) -> Value {
758 match value {
759 toml::Value::String(value) => value.to_owned().into(),
760 toml::Value::Float(value) => value.into(),
761 toml::Value::Integer(value) => value.into(),
762 toml::Value::Boolean(value) => value.into(),
763 toml::Value::Datetime(value) => value.to_string().into(),
764
765 toml::Value::Table(table) => {
766 let mut m = Table::new();
767 for (key, value) in table {
768 m.insert(key.clone(), from_toml_value(value));
769 }
770 m.into()
771 }
772
773 toml::Value::Array(array) => {
774 let mut l = Vec::new();
775 for value in array {
776 l.push(from_toml_value(value));
777 }
778 l.into()
779 }
780 }
781}
782
783#[cfg(test)]
784mod tests {
785 use super::*;
786 use crate::syntax::LanguageDefinition;
787
788 #[test]
789 fn test_overrides() {
790 let user_config = table_from_toml_str(r#"tab_size = 42"#).unwrap();
791 let rust_config = table_from_toml_str(r#"tab_size = 31"#).unwrap();
792
793 let lang_def = rust_lang_def(None);
794 let rust_id: LanguageId = "Rust".into();
795
796 let buf_id_1 = BufferId(1); let buf_id_2 = BufferId(2); let buf_id_3 = BufferId(3); let mut manager = ConfigManager::new(None, None);
801 manager.set_languages(Languages::new(&[lang_def]));
802 manager.set_user_config(rust_id.clone().into(), rust_config).unwrap();
803 manager.set_user_config(ConfigDomain::General, user_config).unwrap();
804
805 let changes = json!({"tab_size": 67}).as_object().unwrap().to_owned();
806 manager.set_user_config(ConfigDomain::SysOverride(buf_id_3), changes).unwrap();
807
808 manager.add_buffer(buf_id_1, None);
809 manager.add_buffer(buf_id_2, Some(Path::new("file.rs")));
810 manager.add_buffer(buf_id_3, Some(Path::new("file2.rs")));
811
812 let config = manager.get_buffer_config(buf_id_1).to_owned();
814 assert_eq!(config.source.0.len(), 1);
815 assert_eq!(config.items.tab_size, 42);
816 let config = manager.get_buffer_config(buf_id_2).to_owned();
817 assert_eq!(config.items.tab_size, 31);
818 let config = manager.get_buffer_config(buf_id_3).to_owned();
819 assert_eq!(config.items.tab_size, 67);
820
821 let changes = json!({"tab_size": 85}).as_object().unwrap().to_owned();
823 manager.set_user_config(ConfigDomain::UserOverride(buf_id_3), changes).unwrap();
824 let config = manager.get_buffer_config(buf_id_3);
825 assert_eq!(config.items.tab_size, 85);
826 }
827
828 #[test]
829 fn test_config_domain_serde() {
830 assert_eq!(serde_json::to_string(&ConfigDomain::General).unwrap(), "\"general\"");
831 let d = ConfigDomainExternal::UserOverride(ViewId(1));
832 assert_eq!(serde_json::to_string(&d).unwrap(), "{\"user_override\":\"view-id-1\"}");
833 let d = ConfigDomain::Language("Swift".into());
834 assert_eq!(serde_json::to_string(&d).unwrap(), "{\"language\":\"Swift\"}");
835 }
836
837 #[test]
838 fn test_diff() {
839 let conf1 = r#"
840tab_size = 42
841translate_tabs_to_spaces = true
842"#;
843 let conf1 = table_from_toml_str(conf1).unwrap();
844
845 let conf2 = r#"
846tab_size = 6
847translate_tabs_to_spaces = true
848"#;
849 let conf2 = table_from_toml_str(conf2).unwrap();
850
851 let stack1 = TableStack(vec![Arc::new(conf1)]);
852 let stack2 = TableStack(vec![Arc::new(conf2)]);
853 let diff = stack1.diff(&stack2).unwrap();
854 assert!(diff.len() == 1);
855 assert_eq!(diff.get("tab_size"), Some(&42.into()));
856 }
857
858 #[test]
859 fn test_updating_in_place() {
860 let mut manager = ConfigManager::new(None, None);
861 let buf_id = BufferId(1);
862 manager.add_buffer(buf_id, None);
863 assert_eq!(manager.get_buffer_config(buf_id).items.font_size, 14.);
864 let changes = json!({"font_size": 69, "font_face": "nice"}).as_object().unwrap().to_owned();
865 let table = manager.table_for_update(ConfigDomain::General, changes);
866 manager.set_user_config(ConfigDomain::General, table).unwrap();
867 assert_eq!(manager.get_buffer_config(buf_id).items.font_size, 69.);
868
869 let changes = json!({ "font_size": Value::Null }).as_object().unwrap().to_owned();
871 let table = manager.table_for_update(ConfigDomain::General, changes);
872 manager.set_user_config(ConfigDomain::General, table).unwrap();
873 assert_eq!(manager.get_buffer_config(buf_id).items.font_size, 14.);
874 assert_eq!(manager.get_buffer_config(buf_id).items.font_face, "nice");
875 }
876
877 #[test]
878 fn lang_overrides() {
879 let mut manager = ConfigManager::new(None, None);
880 let lang_defaults = json!({"font_size": 69, "font_face": "nice"});
881 let lang_overrides = json!({"font_size": 420, "font_face": "cool"});
882 let lang_def = rust_lang_def(lang_defaults.as_object().map(Table::clone));
883 let lang_id: LanguageId = "Rust".into();
884 let domain: ConfigDomain = lang_id.clone().into();
885
886 manager.set_languages(Languages::new(&[lang_def.clone()]));
887 assert_eq!(manager.languages.iter().count(), 1);
888
889 let buf_id = BufferId(1);
890 manager.add_buffer(buf_id, Some(Path::new("file.rs")));
891
892 let config = manager.get_buffer_config(buf_id).to_owned();
893 assert_eq!(config.source.0.len(), 2);
894 assert_eq!(config.items.font_size, 69.);
895
896 manager.set_languages(Languages::new(&[]));
898 assert_eq!(manager.languages.iter().count(), 0);
899
900 let config = manager.get_buffer_config(buf_id).to_owned();
901 assert_eq!(config.source.0.len(), 1);
902 assert_eq!(config.items.font_size, 14.);
903
904 manager
905 .set_user_config(domain.clone(), lang_overrides.as_object().map(Table::clone).unwrap())
906 .unwrap();
907
908 let config = manager.get_buffer_config(buf_id).to_owned();
910 assert_eq!(config.items.font_size, 14.);
911
912 manager.set_languages(Languages::new(&[lang_def.clone()]));
914 let config = manager.get_buffer_config(buf_id).to_owned();
915 assert_eq!(config.items.font_size, 420.);
916
917 let changes = json!({ "font_size": Value::Null }).as_object().unwrap().to_owned();
918
919 let table = manager.table_for_update(domain.clone(), changes);
921 manager.set_user_config(domain.clone(), table).unwrap();
922 let config = manager.get_buffer_config(buf_id).to_owned();
923 assert_eq!(config.items.font_size, 69.);
924
925 manager.set_languages(Languages::new(&[]));
926 let config = manager.get_buffer_config(buf_id);
927 assert_eq!(config.items.font_size, 14.);
928 }
929
930 fn rust_lang_def<T: Into<Option<Table>>>(defaults: T) -> LanguageDefinition {
931 LanguageDefinition::simple("Rust", &["rs"], "source.rust", defaults.into())
932 }
933}