use std::path::{Path, PathBuf};
use ktrs_editorconfig::property_type::TAB_WIDTH;
use ktrs_editorconfig::{
AnyPropertyType, ParseException, Property, PropertyTypeRegistry, ResourcePropertiesService,
};
use crate::editorconfig::{
CODE_STYLE_PROPERTY, END_OF_LINE_PROPERTY, EXPERIMENTAL_RULES_EXECUTION_PROPERTY, EditorConfig,
INDENT_SIZE_PROPERTY, PropertyRef, to_property_with_parsed_value,
};
use crate::engine::editor_config_cache::THREAD_SAFE_EDITOR_CONFIG_CACHE;
use crate::engine::editor_config_defaults::{EditorConfigDefaults, EditorConfigOverride};
use crate::engine::formatter_tags::{
FORMATTER_TAG_OFF_ENABLED_PROPERTY, FORMATTER_TAG_ON_ENABLED_PROPERTY,
FORMATTER_TAGS_ENABLED_PROPERTY,
};
pub const SUPPORTED_FILES: [&str; 2] = [".kt", ".kts"];
const TAB_WIDTH_PROPERTY_NAME: &str = "tab_width";
#[derive(Clone, Debug)]
pub struct EditorConfigLoaderEc4j {
registry: PropertyTypeRegistry,
}
impl EditorConfigLoaderEc4j {
pub fn new(property_types: &[&'static dyn AnyPropertyType]) -> EditorConfigLoaderEc4j {
EditorConfigLoaderEc4j {
registry: PropertyTypeRegistry::with_defaults(property_types.iter().copied()),
}
}
pub fn registry(&self) -> &PropertyTypeRegistry {
&self.registry
}
}
#[derive(Clone, Debug)]
pub struct EditorConfigLoader {
editor_config_loader_ec4j: EditorConfigLoaderEc4j,
editor_config_defaults: EditorConfigDefaults,
editor_config_override: EditorConfigOverride,
}
impl EditorConfigLoader {
pub fn new(
editor_config_loader_ec4j: EditorConfigLoaderEc4j,
editor_config_defaults: EditorConfigDefaults,
editor_config_override: EditorConfigOverride,
) -> EditorConfigLoader {
EditorConfigLoader {
editor_config_loader_ec4j,
editor_config_defaults,
editor_config_override,
}
}
pub fn load(&self, file_path: Option<&Path>) -> Result<EditorConfig, ParseException> {
let editor_config_path = match file_path {
Some(path) => to_absolute_path(path),
None => default_file_path(),
};
let mut properties = self
.create_resource_properties_service()
.query_properties(&editor_config_path)?
.into_properties();
let source = |properties: &[Property], name: &str| {
properties
.iter()
.find(|p| p.name() == name)
.map(|p| p.source_value().map(str::to_owned))
};
let indent_size_override = self
.editor_config_override
.get(&PropertyRef::from(&*INDENT_SIZE_PROPERTY));
if source(&properties, TAB_WIDTH_PROPERTY_NAME)
== source(&properties, &INDENT_SIZE_PROPERTY.name)
&& let Some(indent_size) = indent_size_override
{
put(
&mut properties,
Property::new(
TAB_WIDTH_PROPERTY_NAME,
Some(&TAB_WIDTH),
indent_size.source(),
),
);
}
for (key, value) in self.editor_config_override.properties() {
put(
&mut properties,
to_property_with_parsed_value(&**key, value.clone()),
);
}
Ok(
EditorConfig::new(properties).add_properties_with_default_value_if_missing(&[
PropertyRef::from(&*CODE_STYLE_PROPERTY),
PropertyRef::from(&*END_OF_LINE_PROPERTY),
PropertyRef::from(&*EXPERIMENTAL_RULES_EXECUTION_PROPERTY),
PropertyRef::from(&*FORMATTER_TAGS_ENABLED_PROPERTY),
PropertyRef::from(&*FORMATTER_TAG_OFF_ENABLED_PROPERTY),
PropertyRef::from(&*FORMATTER_TAG_ON_ENABLED_PROPERTY),
]),
)
}
fn create_resource_properties_service(&self) -> ResourcePropertiesService<'_> {
let defaults = &self.editor_config_defaults;
ResourcePropertiesService {
cache: &*THREAD_SAFE_EDITOR_CONFIG_CACHE,
config_file_name: ".editorconfig",
default_editor_configs: if *defaults == EditorConfigDefaults::empty() {
Vec::new()
} else {
vec![defaults.value.clone()]
},
keep_unset: true,
registry: self.editor_config_loader_ec4j.registry(),
root_directories: Vec::new(),
}
}
}
fn put(properties: &mut Vec<Property>, property: Property) {
match properties.iter_mut().find(|p| p.name() == property.name()) {
Some(existing) => *existing = property,
None => properties.push(property),
}
}
fn to_absolute_path(path: &Path) -> PathBuf {
if path.is_absolute() {
path.to_path_buf()
} else {
std::env::current_dir().unwrap_or_default().join(path)
}
}
fn default_file_path() -> PathBuf {
std::env::current_dir()
.unwrap_or_default()
.join(SUPPORTED_FILES[0])
}