@ -4,45 +4,97 @@ use helix_loader::merge_toml_values;
use helix_view ::document ::Mode ;
use serde ::Deserialize ;
use std ::collections ::HashMap ;
use std ::path ::PathBuf ;
use std ::fmt ::Display ;
use std ::fs ;
use std ::io ::Error as IOError ;
use toml ::de ::Error as TomlError ;
#[ derive(Debug, Clone, PartialEq) ]
pub struct Config {
pub theme : Option < String > ,
pub keys : HashMap < Mode , KeyTrie > ,
pub editor : helix_view ::editor ::Config ,
// Config loading error
#[ derive(Debug) ]
pub enum ConfigLoadError {
BadConfig ( TomlError ) ,
Error ( IOError ) ,
}
impl Default for ConfigLoadError {
fn default ( ) -> Self {
ConfigLoadError ::Error ( IOError ::new ( std ::io ::ErrorKind ::NotFound , "place holder" ) )
}
}
#[ derive(Debug, Default, Clone, PartialEq, Deserialize) ]
#[ serde(rename_all = " kebab-case " ) ]
pub enum LoadWorkspaceConfig {
#[ default ]
Never ,
Always ,
}
// Deserializable raw config struct
#[ derive(Debug, Clone, PartialEq, Deserialize) ]
#[ serde(deny_unknown_fields) ]
#[ serde( rename_all = " kebab-case " , deny_unknown_fields)]
pub struct ConfigRaw {
pub load_workspace_config : Option < LoadWorkspaceConfig > ,
pub theme : Option < String > ,
pub keys : Option < HashMap < Mode , KeyTrie > > ,
pub editor : Option < toml ::Value > ,
}
impl Default for Config {
fn default ( ) -> Config {
Config {
impl Default for ConfigRaw {
fn default ( ) -> ConfigRaw {
Self {
load_workspace_config : Some ( LoadWorkspaceConfig ::default ( ) ) ,
theme : None ,
keys : keymap ::default ( ) ,
editor : helix_view ::editor ::Config ::default ( ) ,
keys : Some ( keymap ::default ( ) ) ,
editor : None ,
}
}
}
#[ derive(Debug) ]
pub enum ConfigLoadError {
BadConfig ( TomlError ) ,
Error ( IOError ) ,
impl ConfigRaw {
fn load ( file : PathBuf ) -> Result < Self , ConfigLoadError > {
let source = fs ::read_to_string ( file ) . map_err ( ConfigLoadError ::Error ) ? ;
toml ::from_str ( & source ) . map_err ( ConfigLoadError ::BadConfig )
}
fn merge ( self , other : ConfigRaw , trust : bool ) -> Self {
ConfigRaw {
load_workspace_config : match trust {
true = > other . load_workspace_config . or ( self . load_workspace_config ) ,
false = > self . load_workspace_config ,
} ,
theme : other . theme . or ( self . theme ) ,
keys : match ( self . keys , other . keys ) {
( Some ( a ) , Some ( b ) ) = > Some ( merge_keys ( a , b ) ) ,
( opt_a , opt_b ) = > opt_a . or ( opt_b ) ,
} ,
editor : match ( self . editor , other . editor ) {
( Some ( a ) , Some ( b ) ) = > Some ( merge_toml_values ( a , b , 3 ) ) ,
( opt_a , opt_b ) = > opt_a . or ( opt_b ) ,
}
}
}
}
impl Default for ConfigLoadError {
fn default ( ) -> Self {
ConfigLoadError ::Error ( IOError ::new ( std ::io ::ErrorKind ::NotFound , "place holder" ) )
// Final config struct
#[ derive(Debug, Clone, PartialEq) ]
pub struct Config {
pub load_workspace_config : LoadWorkspaceConfig ,
pub theme : Option < String > ,
pub keys : HashMap < Mode , KeyTrie > ,
pub editor : helix_view ::editor ::Config ,
}
impl Default for Config {
fn default ( ) -> Config {
let raw = ConfigRaw ::default ( ) ;
Self {
load_workspace_config : raw . load_workspace_config . unwrap_or_default ( ) ,
theme : raw . theme ,
keys : raw . keys . unwrap_or_else ( | | keymap ::default ( ) ) ,
editor : helix_view ::editor ::Config ::default ( ) ,
}
}
}
@ -55,74 +107,37 @@ impl Display for ConfigLoadError {
}
}
impl Config {
pub fn load (
global : Result < String , ConfigLoadError > ,
local : Result < String , ConfigLoadError > ,
) -> Result < Config , ConfigLoadError > {
let global_config : Result < ConfigRaw , ConfigLoadError > =
global . and_then ( | file | toml ::from_str ( & file ) . map_err ( ConfigLoadError ::BadConfig ) ) ;
let local_config : Result < ConfigRaw , ConfigLoadError > =
local . and_then ( | file | toml ::from_str ( & file ) . map_err ( ConfigLoadError ::BadConfig ) ) ;
let res = match ( global_config , local_config ) {
( Ok ( global ) , Ok ( local ) ) = > {
let mut keys = keymap ::default ( ) ;
if let Some ( global_keys ) = global . keys {
merge_keys ( & mut keys , global_keys )
}
if let Some ( local_keys ) = local . keys {
merge_keys ( & mut keys , local_keys )
}
let editor = match ( global . editor , local . editor ) {
( None , None ) = > helix_view ::editor ::Config ::default ( ) ,
( None , Some ( val ) ) | ( Some ( val ) , None ) = > {
val . try_into ( ) . map_err ( ConfigLoadError ::BadConfig ) ?
}
( Some ( global ) , Some ( local ) ) = > merge_toml_values ( global , local , 3 )
. try_into ( )
. map_err ( ConfigLoadError ::BadConfig ) ? ,
} ;
Config {
theme : local . theme . or ( global . theme ) ,
keys ,
editor ,
}
}
// if any configs are invalid return that first
( _ , Err ( ConfigLoadError ::BadConfig ( err ) ) )
| ( Err ( ConfigLoadError ::BadConfig ( err ) ) , _ ) = > {
return Err ( ConfigLoadError ::BadConfig ( err ) )
}
( Ok ( config ) , Err ( _ ) ) | ( Err ( _ ) , Ok ( config ) ) = > {
let mut keys = keymap ::default ( ) ;
if let Some ( keymap ) = config . keys {
merge_keys ( & mut keys , keymap ) ;
}
Config {
theme : config . theme ,
keys ,
editor : config . editor . map_or_else (
| | Ok ( helix_view ::editor ::Config ::default ( ) ) ,
| val | val . try_into ( ) . map_err ( ConfigLoadError ::BadConfig ) ,
) ? ,
}
}
// these are just two io errors return the one for the global config
( Err ( err ) , Err ( _ ) ) = > return Err ( err ) ,
} ;
Ok ( res )
impl TryFrom < ConfigRaw > for Config {
type Error = ConfigLoadError ;
fn try_from ( config : ConfigRaw ) -> Result < Self , Self ::Error > {
Ok ( Self {
load_workspace_config : config . load_workspace_config . unwrap_or_default ( ) ,
theme : config . theme ,
keys : config . keys . unwrap_or_else ( | | keymap ::default ( ) ) ,
editor : config . editor
. map ( | e | e . try_into ( ) ) . transpose ( )
. map_err ( ConfigLoadError ::BadConfig ) ?
. unwrap_or_default ( ) ,
} )
}
}
pub fn load_default ( ) -> Result < Config , ConfigLoadError > {
let global_config =
fs ::read_to_string ( helix_loader ::config_file ( ) ) . map_err ( ConfigLoadError ::Error ) ;
let local_config = fs ::read_to_string ( helix_loader ::workspace_config_file ( ) )
. map_err ( ConfigLoadError ::Error ) ;
Config ::load ( global_config , local_config )
impl Config {
pub fn load ( ) -> Result < Config , ConfigLoadError > {
let default = ConfigRaw ::default ( ) ;
let global = default . merge ( ConfigRaw ::load ( helix_loader ::config_file ( ) ) ? , true ) ;
match global . load_workspace_config {
Some ( LoadWorkspaceConfig ::Always ) = > {
match ConfigRaw ::load ( helix_loader ::workspace_config_file ( ) ) {
Ok ( workspace ) = > Ok ( global . merge ( workspace , false ) ) ,
Err ( ConfigLoadError ::Error ( _ ) ) = > Ok ( global ) ,
error = > error ,
} ?
} ,
_ = > global ,
} . try_into ( )
}
}
@ -131,8 +146,9 @@ mod tests {
use super ::* ;
impl Config {
fn load_test ( config : & str ) -> Config {
Config ::load ( Ok ( config . to_owned ( ) ) , Err ( ConfigLoadError ::default ( ) ) ) . unwrap ( )
fn load_test ( file : & str ) -> Config {
let raw : ConfigRaw = toml ::from_str ( file ) . unwrap ( ) ;
ConfigRaw ::default ( ) . merge ( raw , true ) . try_into ( ) . unwrap ( )
}
}
@ -151,9 +167,8 @@ mod tests {
A - F12 = "move_next_word_end"
" #;
let mut keys = keymap ::default ( ) ;
merge_keys (
& mut keys ,
let keys = merge_keys (
keymap ::default ( ) ,
hashmap ! {
Mode ::Insert = > keymap ! ( { "Insert mode"
"y" = > move_line_down ,