|
|
|
@ -41,833 +41,13 @@ use anyhow::{anyhow, bail, Error};
|
|
|
|
|
|
|
|
|
|
pub use helix_core::diagnostic::Severity;
|
|
|
|
|
use helix_core::{
|
|
|
|
|
auto_pairs::AutoPairs,
|
|
|
|
|
syntax::{self, AutoPairConfig, IndentationHeuristic, LanguageServerFeature, SoftWrap},
|
|
|
|
|
Change, LineEnding, Position, Selection, NATIVE_LINE_ENDING,
|
|
|
|
|
syntax::{self, LanguageServerFeature},
|
|
|
|
|
Change, Position, Selection,
|
|
|
|
|
};
|
|
|
|
|
use helix_dap as dap;
|
|
|
|
|
use helix_lsp::lsp;
|
|
|
|
|
|
|
|
|
|
use serde::{ser::SerializeMap, Deserialize, Deserializer, Serialize, Serializer};
|
|
|
|
|
|
|
|
|
|
use arc_swap::access::{DynAccess, DynGuard};
|
|
|
|
|
|
|
|
|
|
fn deserialize_duration_millis<'de, D>(deserializer: D) -> Result<Duration, D::Error>
|
|
|
|
|
where
|
|
|
|
|
D: serde::Deserializer<'de>,
|
|
|
|
|
{
|
|
|
|
|
let millis = u64::deserialize(deserializer)?;
|
|
|
|
|
Ok(Duration::from_millis(millis))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn serialize_duration_millis<S>(duration: &Duration, serializer: S) -> Result<S::Ok, S::Error>
|
|
|
|
|
where
|
|
|
|
|
S: Serializer,
|
|
|
|
|
{
|
|
|
|
|
serializer.serialize_u64(
|
|
|
|
|
duration
|
|
|
|
|
.as_millis()
|
|
|
|
|
.try_into()
|
|
|
|
|
.map_err(|_| serde::ser::Error::custom("duration value overflowed u64"))?,
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
|
|
|
|
#[serde(rename_all = "kebab-case", default, deny_unknown_fields)]
|
|
|
|
|
pub struct GutterConfig {
|
|
|
|
|
/// Gutter Layout
|
|
|
|
|
pub layout: Vec<GutterType>,
|
|
|
|
|
/// Options specific to the "line-numbers" gutter
|
|
|
|
|
pub line_numbers: GutterLineNumbersConfig,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Default for GutterConfig {
|
|
|
|
|
fn default() -> Self {
|
|
|
|
|
Self {
|
|
|
|
|
layout: vec![
|
|
|
|
|
GutterType::Diagnostics,
|
|
|
|
|
GutterType::Spacer,
|
|
|
|
|
GutterType::LineNumbers,
|
|
|
|
|
GutterType::Spacer,
|
|
|
|
|
GutterType::Diff,
|
|
|
|
|
],
|
|
|
|
|
line_numbers: GutterLineNumbersConfig::default(),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl From<Vec<GutterType>> for GutterConfig {
|
|
|
|
|
fn from(x: Vec<GutterType>) -> Self {
|
|
|
|
|
GutterConfig {
|
|
|
|
|
layout: x,
|
|
|
|
|
..Default::default()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn deserialize_gutter_seq_or_struct<'de, D>(deserializer: D) -> Result<GutterConfig, D::Error>
|
|
|
|
|
where
|
|
|
|
|
D: Deserializer<'de>,
|
|
|
|
|
{
|
|
|
|
|
struct GutterVisitor;
|
|
|
|
|
|
|
|
|
|
impl<'de> serde::de::Visitor<'de> for GutterVisitor {
|
|
|
|
|
type Value = GutterConfig;
|
|
|
|
|
|
|
|
|
|
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
|
|
|
|
|
write!(
|
|
|
|
|
formatter,
|
|
|
|
|
"an array of gutter names or a detailed gutter configuration"
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn visit_seq<S>(self, mut seq: S) -> Result<Self::Value, S::Error>
|
|
|
|
|
where
|
|
|
|
|
S: serde::de::SeqAccess<'de>,
|
|
|
|
|
{
|
|
|
|
|
let mut gutters = Vec::new();
|
|
|
|
|
while let Some(gutter) = seq.next_element::<String>()? {
|
|
|
|
|
gutters.push(
|
|
|
|
|
gutter
|
|
|
|
|
.parse::<GutterType>()
|
|
|
|
|
.map_err(serde::de::Error::custom)?,
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Ok(gutters.into())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn visit_map<M>(self, map: M) -> Result<Self::Value, M::Error>
|
|
|
|
|
where
|
|
|
|
|
M: serde::de::MapAccess<'de>,
|
|
|
|
|
{
|
|
|
|
|
let deserializer = serde::de::value::MapAccessDeserializer::new(map);
|
|
|
|
|
Deserialize::deserialize(deserializer)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
deserializer.deserialize_any(GutterVisitor)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
|
|
|
|
|
#[serde(rename_all = "kebab-case", default, deny_unknown_fields)]
|
|
|
|
|
pub struct GutterLineNumbersConfig {
|
|
|
|
|
/// Minimum number of characters to use for line number gutter. Defaults to 3.
|
|
|
|
|
pub min_width: usize,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Default for GutterLineNumbersConfig {
|
|
|
|
|
fn default() -> Self {
|
|
|
|
|
Self { min_width: 3 }
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
|
|
|
|
#[serde(rename_all = "kebab-case", default, deny_unknown_fields)]
|
|
|
|
|
pub struct FilePickerConfig {
|
|
|
|
|
/// IgnoreOptions
|
|
|
|
|
/// Enables ignoring hidden files.
|
|
|
|
|
/// Whether to hide hidden files in file picker and global search results. Defaults to true.
|
|
|
|
|
pub hidden: bool,
|
|
|
|
|
/// Enables following symlinks.
|
|
|
|
|
/// Whether to follow symbolic links in file picker and file or directory completions. Defaults to true.
|
|
|
|
|
pub follow_symlinks: bool,
|
|
|
|
|
/// Hides symlinks that point into the current directory. Defaults to true.
|
|
|
|
|
pub deduplicate_links: bool,
|
|
|
|
|
/// Enables reading ignore files from parent directories. Defaults to true.
|
|
|
|
|
pub parents: bool,
|
|
|
|
|
/// Enables reading `.ignore` files.
|
|
|
|
|
/// Whether to hide files listed in .ignore in file picker and global search results. Defaults to true.
|
|
|
|
|
pub ignore: bool,
|
|
|
|
|
/// Enables reading `.gitignore` files.
|
|
|
|
|
/// Whether to hide files listed in .gitignore in file picker and global search results. Defaults to true.
|
|
|
|
|
pub git_ignore: bool,
|
|
|
|
|
/// Enables reading global .gitignore, whose path is specified in git's config: `core.excludefile` option.
|
|
|
|
|
/// Whether to hide files listed in global .gitignore in file picker and global search results. Defaults to true.
|
|
|
|
|
pub git_global: bool,
|
|
|
|
|
/// Enables reading `.git/info/exclude` files.
|
|
|
|
|
/// Whether to hide files listed in .git/info/exclude in file picker and global search results. Defaults to true.
|
|
|
|
|
pub git_exclude: bool,
|
|
|
|
|
/// WalkBuilder options
|
|
|
|
|
/// Maximum Depth to recurse directories in file picker and global search. Defaults to `None`.
|
|
|
|
|
pub max_depth: Option<usize>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Default for FilePickerConfig {
|
|
|
|
|
fn default() -> Self {
|
|
|
|
|
Self {
|
|
|
|
|
hidden: true,
|
|
|
|
|
follow_symlinks: true,
|
|
|
|
|
deduplicate_links: true,
|
|
|
|
|
parents: true,
|
|
|
|
|
ignore: true,
|
|
|
|
|
git_ignore: true,
|
|
|
|
|
git_global: true,
|
|
|
|
|
git_exclude: true,
|
|
|
|
|
max_depth: None,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
|
|
|
|
#[serde(rename_all = "kebab-case", default, deny_unknown_fields)]
|
|
|
|
|
pub struct Config {
|
|
|
|
|
/// Padding to keep between the edge of the screen and the cursor when scrolling. Defaults to 5.
|
|
|
|
|
pub scrolloff: usize,
|
|
|
|
|
/// Number of lines to scroll at once. Defaults to 3
|
|
|
|
|
pub scroll_lines: isize,
|
|
|
|
|
/// Mouse support. Defaults to true.
|
|
|
|
|
pub mouse: bool,
|
|
|
|
|
/// Shell to use for shell commands. Defaults to ["cmd", "/C"] on Windows and ["sh", "-c"] otherwise.
|
|
|
|
|
pub shell: Vec<String>,
|
|
|
|
|
/// Line number mode.
|
|
|
|
|
pub line_number: LineNumber,
|
|
|
|
|
/// Highlight the lines cursors are currently on. Defaults to false.
|
|
|
|
|
pub cursorline: bool,
|
|
|
|
|
/// Highlight the columns cursors are currently on. Defaults to false.
|
|
|
|
|
pub cursorcolumn: bool,
|
|
|
|
|
#[serde(deserialize_with = "deserialize_gutter_seq_or_struct")]
|
|
|
|
|
pub gutters: GutterConfig,
|
|
|
|
|
/// Middle click paste support. Defaults to true.
|
|
|
|
|
pub middle_click_paste: bool,
|
|
|
|
|
/// Automatic insertion of pairs to parentheses, brackets,
|
|
|
|
|
/// etc. Optionally, this can be a list of 2-tuples to specify a
|
|
|
|
|
/// global list of characters to pair. Defaults to true.
|
|
|
|
|
pub auto_pairs: AutoPairConfig,
|
|
|
|
|
/// Automatic auto-completion, automatically pop up without user trigger. Defaults to true.
|
|
|
|
|
pub auto_completion: bool,
|
|
|
|
|
/// Automatic formatting on save. Defaults to true.
|
|
|
|
|
pub auto_format: bool,
|
|
|
|
|
/// Automatic save on focus lost. Defaults to false.
|
|
|
|
|
pub auto_save: bool,
|
|
|
|
|
/// Set a global text_width
|
|
|
|
|
pub text_width: usize,
|
|
|
|
|
/// Time in milliseconds since last keypress before idle timers trigger.
|
|
|
|
|
/// Used for autocompletion, set to 0 for instant. Defaults to 250ms.
|
|
|
|
|
#[serde(
|
|
|
|
|
serialize_with = "serialize_duration_millis",
|
|
|
|
|
deserialize_with = "deserialize_duration_millis"
|
|
|
|
|
)]
|
|
|
|
|
pub idle_timeout: Duration,
|
|
|
|
|
/// Whether to insert the completion suggestion on hover. Defaults to true.
|
|
|
|
|
pub preview_completion_insert: bool,
|
|
|
|
|
pub completion_trigger_len: u8,
|
|
|
|
|
/// Whether to instruct the LSP to replace the entire word when applying a completion
|
|
|
|
|
/// or to only insert new text
|
|
|
|
|
pub completion_replace: bool,
|
|
|
|
|
/// Whether to display infoboxes. Defaults to true.
|
|
|
|
|
pub auto_info: bool,
|
|
|
|
|
pub file_picker: FilePickerConfig,
|
|
|
|
|
/// Configuration of the statusline elements
|
|
|
|
|
pub statusline: StatusLineConfig,
|
|
|
|
|
/// Shape for cursor in each mode
|
|
|
|
|
pub cursor_shape: CursorShapeConfig,
|
|
|
|
|
/// Set to `true` to override automatic detection of terminal truecolor support in the event of a false negative. Defaults to `false`.
|
|
|
|
|
pub true_color: bool,
|
|
|
|
|
/// Set to `true` to override automatic detection of terminal undercurl support in the event of a false negative. Defaults to `false`.
|
|
|
|
|
pub undercurl: bool,
|
|
|
|
|
/// Search configuration.
|
|
|
|
|
#[serde(default)]
|
|
|
|
|
pub search: SearchConfig,
|
|
|
|
|
pub lsp: LspConfig,
|
|
|
|
|
pub terminal: Option<TerminalConfig>,
|
|
|
|
|
/// Column numbers at which to draw the rulers. Defaults to `[]`, meaning no rulers.
|
|
|
|
|
pub rulers: Vec<u16>,
|
|
|
|
|
#[serde(default)]
|
|
|
|
|
pub whitespace: WhitespaceConfig,
|
|
|
|
|
/// Persistently display open buffers along the top
|
|
|
|
|
pub bufferline: BufferLine,
|
|
|
|
|
/// Vertical indent width guides.
|
|
|
|
|
pub indent_guides: IndentGuidesConfig,
|
|
|
|
|
/// Whether to color modes with different colors. Defaults to `false`.
|
|
|
|
|
pub color_modes: bool,
|
|
|
|
|
pub soft_wrap: SoftWrap,
|
|
|
|
|
/// Workspace specific lsp ceiling dirs
|
|
|
|
|
pub workspace_lsp_roots: Vec<PathBuf>,
|
|
|
|
|
/// Which line ending to choose for new documents. Defaults to `native`. i.e. `crlf` on Windows, otherwise `lf`.
|
|
|
|
|
pub default_line_ending: LineEndingConfig,
|
|
|
|
|
/// Whether to automatically insert a trailing line-ending on write if missing. Defaults to `true`.
|
|
|
|
|
pub insert_final_newline: bool,
|
|
|
|
|
/// Enables smart tab
|
|
|
|
|
pub smart_tab: Option<SmartTabConfig>,
|
|
|
|
|
/// Draw border around popups.
|
|
|
|
|
pub popup_border: PopupBorderConfig,
|
|
|
|
|
/// Which indent heuristic to use when a new line is inserted
|
|
|
|
|
#[serde(default)]
|
|
|
|
|
pub indent_heuristic: IndentationHeuristic,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, Eq, PartialOrd, Ord)]
|
|
|
|
|
#[serde(rename_all = "kebab-case", default)]
|
|
|
|
|
pub struct SmartTabConfig {
|
|
|
|
|
pub enable: bool,
|
|
|
|
|
pub supersede_menu: bool,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Default for SmartTabConfig {
|
|
|
|
|
fn default() -> Self {
|
|
|
|
|
SmartTabConfig {
|
|
|
|
|
enable: true,
|
|
|
|
|
supersede_menu: false,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
|
|
|
|
#[serde(default, rename_all = "kebab-case", deny_unknown_fields)]
|
|
|
|
|
pub struct TerminalConfig {
|
|
|
|
|
pub command: String,
|
|
|
|
|
#[serde(default)]
|
|
|
|
|
#[serde(skip_serializing_if = "Vec::is_empty")]
|
|
|
|
|
pub args: Vec<String>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[cfg(windows)]
|
|
|
|
|
pub fn get_terminal_provider() -> Option<TerminalConfig> {
|
|
|
|
|
use crate::env::binary_exists;
|
|
|
|
|
|
|
|
|
|
if binary_exists("wt") {
|
|
|
|
|
return Some(TerminalConfig {
|
|
|
|
|
command: "wt".to_string(),
|
|
|
|
|
args: vec![
|
|
|
|
|
"new-tab".to_string(),
|
|
|
|
|
"--title".to_string(),
|
|
|
|
|
"DEBUG".to_string(),
|
|
|
|
|
"cmd".to_string(),
|
|
|
|
|
"/C".to_string(),
|
|
|
|
|
],
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Some(TerminalConfig {
|
|
|
|
|
command: "conhost".to_string(),
|
|
|
|
|
args: vec!["cmd".to_string(), "/C".to_string()],
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[cfg(not(any(windows, target_os = "wasm32")))]
|
|
|
|
|
pub fn get_terminal_provider() -> Option<TerminalConfig> {
|
|
|
|
|
use crate::env::{binary_exists, env_var_is_set};
|
|
|
|
|
|
|
|
|
|
if env_var_is_set("TMUX") && binary_exists("tmux") {
|
|
|
|
|
return Some(TerminalConfig {
|
|
|
|
|
command: "tmux".to_string(),
|
|
|
|
|
args: vec!["split-window".to_string()],
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if env_var_is_set("WEZTERM_UNIX_SOCKET") && binary_exists("wezterm") {
|
|
|
|
|
return Some(TerminalConfig {
|
|
|
|
|
command: "wezterm".to_string(),
|
|
|
|
|
args: vec!["cli".to_string(), "split-pane".to_string()],
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
None
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
|
|
|
|
#[serde(default, rename_all = "kebab-case", deny_unknown_fields)]
|
|
|
|
|
pub struct LspConfig {
|
|
|
|
|
/// Enables LSP
|
|
|
|
|
pub enable: bool,
|
|
|
|
|
/// Display LSP progress messages below statusline
|
|
|
|
|
pub display_messages: bool,
|
|
|
|
|
/// Enable automatic pop up of signature help (parameter hints)
|
|
|
|
|
pub auto_signature_help: bool,
|
|
|
|
|
/// Display docs under signature help popup
|
|
|
|
|
pub display_signature_help_docs: bool,
|
|
|
|
|
/// Display inlay hints
|
|
|
|
|
pub display_inlay_hints: bool,
|
|
|
|
|
/// Whether to enable snippet support
|
|
|
|
|
pub snippets: bool,
|
|
|
|
|
/// Whether to include declaration in the goto reference query
|
|
|
|
|
pub goto_reference_include_declaration: bool,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Default for LspConfig {
|
|
|
|
|
fn default() -> Self {
|
|
|
|
|
Self {
|
|
|
|
|
enable: true,
|
|
|
|
|
display_messages: false,
|
|
|
|
|
auto_signature_help: true,
|
|
|
|
|
display_signature_help_docs: true,
|
|
|
|
|
display_inlay_hints: false,
|
|
|
|
|
snippets: true,
|
|
|
|
|
goto_reference_include_declaration: true,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
|
|
|
|
#[serde(rename_all = "kebab-case", default, deny_unknown_fields)]
|
|
|
|
|
pub struct SearchConfig {
|
|
|
|
|
/// Smart case: Case insensitive searching unless pattern contains upper case characters. Defaults to true.
|
|
|
|
|
pub smart_case: bool,
|
|
|
|
|
/// Whether the search should wrap after depleting the matches. Default to true.
|
|
|
|
|
pub wrap_around: bool,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
|
|
|
|
#[serde(rename_all = "kebab-case", default, deny_unknown_fields)]
|
|
|
|
|
pub struct StatusLineConfig {
|
|
|
|
|
pub left: Vec<StatusLineElement>,
|
|
|
|
|
pub center: Vec<StatusLineElement>,
|
|
|
|
|
pub right: Vec<StatusLineElement>,
|
|
|
|
|
pub separator: String,
|
|
|
|
|
pub mode: ModeConfig,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Default for StatusLineConfig {
|
|
|
|
|
fn default() -> Self {
|
|
|
|
|
use StatusLineElement as E;
|
|
|
|
|
|
|
|
|
|
Self {
|
|
|
|
|
left: vec![
|
|
|
|
|
E::Mode,
|
|
|
|
|
E::Spinner,
|
|
|
|
|
E::FileName,
|
|
|
|
|
E::ReadOnlyIndicator,
|
|
|
|
|
E::FileModificationIndicator,
|
|
|
|
|
],
|
|
|
|
|
center: vec![],
|
|
|
|
|
right: vec![
|
|
|
|
|
E::Diagnostics,
|
|
|
|
|
E::Selections,
|
|
|
|
|
E::Register,
|
|
|
|
|
E::Position,
|
|
|
|
|
E::FileEncoding,
|
|
|
|
|
],
|
|
|
|
|
separator: String::from("│"),
|
|
|
|
|
mode: ModeConfig::default(),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
|
|
|
|
#[serde(rename_all = "kebab-case", default, deny_unknown_fields)]
|
|
|
|
|
pub struct ModeConfig {
|
|
|
|
|
pub normal: String,
|
|
|
|
|
pub insert: String,
|
|
|
|
|
pub select: String,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Default for ModeConfig {
|
|
|
|
|
fn default() -> Self {
|
|
|
|
|
Self {
|
|
|
|
|
normal: String::from("NOR"),
|
|
|
|
|
insert: String::from("INS"),
|
|
|
|
|
select: String::from("SEL"),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
|
|
|
|
#[serde(rename_all = "kebab-case")]
|
|
|
|
|
pub enum StatusLineElement {
|
|
|
|
|
/// The editor mode (Normal, Insert, Visual/Selection)
|
|
|
|
|
Mode,
|
|
|
|
|
|
|
|
|
|
/// The LSP activity spinner
|
|
|
|
|
Spinner,
|
|
|
|
|
|
|
|
|
|
/// The file basename (the leaf of the open file's path)
|
|
|
|
|
FileBaseName,
|
|
|
|
|
|
|
|
|
|
/// The relative file path
|
|
|
|
|
FileName,
|
|
|
|
|
|
|
|
|
|
// The file modification indicator
|
|
|
|
|
FileModificationIndicator,
|
|
|
|
|
|
|
|
|
|
/// An indicator that shows `"[readonly]"` when a file cannot be written
|
|
|
|
|
ReadOnlyIndicator,
|
|
|
|
|
|
|
|
|
|
/// The file encoding
|
|
|
|
|
FileEncoding,
|
|
|
|
|
|
|
|
|
|
/// The file line endings (CRLF or LF)
|
|
|
|
|
FileLineEnding,
|
|
|
|
|
|
|
|
|
|
/// The file type (language ID or "text")
|
|
|
|
|
FileType,
|
|
|
|
|
|
|
|
|
|
/// A summary of the number of errors and warnings
|
|
|
|
|
Diagnostics,
|
|
|
|
|
|
|
|
|
|
/// A summary of the number of errors and warnings on file and workspace
|
|
|
|
|
WorkspaceDiagnostics,
|
|
|
|
|
|
|
|
|
|
/// The number of selections (cursors)
|
|
|
|
|
Selections,
|
|
|
|
|
|
|
|
|
|
/// The number of characters currently in primary selection
|
|
|
|
|
PrimarySelectionLength,
|
|
|
|
|
|
|
|
|
|
/// The cursor position
|
|
|
|
|
Position,
|
|
|
|
|
|
|
|
|
|
/// The separator string
|
|
|
|
|
Separator,
|
|
|
|
|
|
|
|
|
|
/// The cursor position as a percent of the total file
|
|
|
|
|
PositionPercentage,
|
|
|
|
|
|
|
|
|
|
/// The total line numbers of the current file
|
|
|
|
|
TotalLineNumbers,
|
|
|
|
|
|
|
|
|
|
/// A single space
|
|
|
|
|
Spacer,
|
|
|
|
|
|
|
|
|
|
/// Current version control information
|
|
|
|
|
VersionControl,
|
|
|
|
|
|
|
|
|
|
/// Indicator for selected register
|
|
|
|
|
Register,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Cursor shape is read and used on every rendered frame and so needs
|
|
|
|
|
// to be fast. Therefore we avoid a hashmap and use an enum indexed array.
|
|
|
|
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
|
|
|
|
pub struct CursorShapeConfig([CursorKind; 3]);
|
|
|
|
|
|
|
|
|
|
impl CursorShapeConfig {
|
|
|
|
|
pub fn from_mode(&self, mode: Mode) -> CursorKind {
|
|
|
|
|
self.get(mode as usize).copied().unwrap_or_default()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl<'de> Deserialize<'de> for CursorShapeConfig {
|
|
|
|
|
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
|
|
|
|
where
|
|
|
|
|
D: Deserializer<'de>,
|
|
|
|
|
{
|
|
|
|
|
let m = HashMap::<Mode, CursorKind>::deserialize(deserializer)?;
|
|
|
|
|
let into_cursor = |mode: Mode| m.get(&mode).copied().unwrap_or_default();
|
|
|
|
|
Ok(CursorShapeConfig([
|
|
|
|
|
into_cursor(Mode::Normal),
|
|
|
|
|
into_cursor(Mode::Select),
|
|
|
|
|
into_cursor(Mode::Insert),
|
|
|
|
|
]))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Serialize for CursorShapeConfig {
|
|
|
|
|
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
|
|
|
|
where
|
|
|
|
|
S: serde::Serializer,
|
|
|
|
|
{
|
|
|
|
|
let mut map = serializer.serialize_map(Some(self.len()))?;
|
|
|
|
|
let modes = [Mode::Normal, Mode::Select, Mode::Insert];
|
|
|
|
|
for mode in modes {
|
|
|
|
|
map.serialize_entry(&mode, &self.from_mode(mode))?;
|
|
|
|
|
}
|
|
|
|
|
map.end()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl std::ops::Deref for CursorShapeConfig {
|
|
|
|
|
type Target = [CursorKind; 3];
|
|
|
|
|
|
|
|
|
|
fn deref(&self) -> &Self::Target {
|
|
|
|
|
&self.0
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Default for CursorShapeConfig {
|
|
|
|
|
fn default() -> Self {
|
|
|
|
|
Self([CursorKind::Block; 3])
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// bufferline render modes
|
|
|
|
|
#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
|
|
|
|
#[serde(rename_all = "kebab-case")]
|
|
|
|
|
pub enum BufferLine {
|
|
|
|
|
/// Don't render bufferline
|
|
|
|
|
#[default]
|
|
|
|
|
Never,
|
|
|
|
|
/// Always render
|
|
|
|
|
Always,
|
|
|
|
|
/// Only if multiple buffers are open
|
|
|
|
|
Multiple,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
|
|
|
|
#[serde(rename_all = "kebab-case")]
|
|
|
|
|
pub enum LineNumber {
|
|
|
|
|
/// Show absolute line number
|
|
|
|
|
Absolute,
|
|
|
|
|
|
|
|
|
|
/// If focused and in normal/select mode, show relative line number to the primary cursor.
|
|
|
|
|
/// If unfocused or in insert mode, show absolute line number.
|
|
|
|
|
Relative,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl std::str::FromStr for LineNumber {
|
|
|
|
|
type Err = anyhow::Error;
|
|
|
|
|
|
|
|
|
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
|
|
|
|
match s.to_lowercase().as_str() {
|
|
|
|
|
"absolute" | "abs" => Ok(Self::Absolute),
|
|
|
|
|
"relative" | "rel" => Ok(Self::Relative),
|
|
|
|
|
_ => anyhow::bail!("Line number can only be `absolute` or `relative`."),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
|
|
|
|
#[serde(rename_all = "kebab-case")]
|
|
|
|
|
pub enum GutterType {
|
|
|
|
|
/// Show diagnostics and other features like breakpoints
|
|
|
|
|
Diagnostics,
|
|
|
|
|
/// Show line numbers
|
|
|
|
|
LineNumbers,
|
|
|
|
|
/// Show one blank space
|
|
|
|
|
Spacer,
|
|
|
|
|
/// Highlight local changes
|
|
|
|
|
Diff,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl std::str::FromStr for GutterType {
|
|
|
|
|
type Err = anyhow::Error;
|
|
|
|
|
|
|
|
|
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
|
|
|
|
match s.to_lowercase().as_str() {
|
|
|
|
|
"diagnostics" => Ok(Self::Diagnostics),
|
|
|
|
|
"spacer" => Ok(Self::Spacer),
|
|
|
|
|
"line-numbers" => Ok(Self::LineNumbers),
|
|
|
|
|
"diff" => Ok(Self::Diff),
|
|
|
|
|
_ => anyhow::bail!(
|
|
|
|
|
"Gutter type can only be `diagnostics`, `spacer`, `line-numbers` or `diff`."
|
|
|
|
|
),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
|
|
|
|
#[serde(default)]
|
|
|
|
|
pub struct WhitespaceConfig {
|
|
|
|
|
pub render: WhitespaceRender,
|
|
|
|
|
pub characters: WhitespaceCharacters,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Default for WhitespaceConfig {
|
|
|
|
|
fn default() -> Self {
|
|
|
|
|
Self {
|
|
|
|
|
render: WhitespaceRender::Basic(WhitespaceRenderValue::None),
|
|
|
|
|
characters: WhitespaceCharacters::default(),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
|
|
|
|
#[serde(untagged, rename_all = "kebab-case")]
|
|
|
|
|
pub enum WhitespaceRender {
|
|
|
|
|
Basic(WhitespaceRenderValue),
|
|
|
|
|
Specific {
|
|
|
|
|
default: Option<WhitespaceRenderValue>,
|
|
|
|
|
space: Option<WhitespaceRenderValue>,
|
|
|
|
|
nbsp: Option<WhitespaceRenderValue>,
|
|
|
|
|
tab: Option<WhitespaceRenderValue>,
|
|
|
|
|
newline: Option<WhitespaceRenderValue>,
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
|
|
|
|
#[serde(rename_all = "kebab-case")]
|
|
|
|
|
pub enum WhitespaceRenderValue {
|
|
|
|
|
None,
|
|
|
|
|
// TODO
|
|
|
|
|
// Selection,
|
|
|
|
|
All,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl WhitespaceRender {
|
|
|
|
|
pub fn space(&self) -> WhitespaceRenderValue {
|
|
|
|
|
match *self {
|
|
|
|
|
Self::Basic(val) => val,
|
|
|
|
|
Self::Specific { default, space, .. } => {
|
|
|
|
|
space.or(default).unwrap_or(WhitespaceRenderValue::None)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
pub fn nbsp(&self) -> WhitespaceRenderValue {
|
|
|
|
|
match *self {
|
|
|
|
|
Self::Basic(val) => val,
|
|
|
|
|
Self::Specific { default, nbsp, .. } => {
|
|
|
|
|
nbsp.or(default).unwrap_or(WhitespaceRenderValue::None)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
pub fn tab(&self) -> WhitespaceRenderValue {
|
|
|
|
|
match *self {
|
|
|
|
|
Self::Basic(val) => val,
|
|
|
|
|
Self::Specific { default, tab, .. } => {
|
|
|
|
|
tab.or(default).unwrap_or(WhitespaceRenderValue::None)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
pub fn newline(&self) -> WhitespaceRenderValue {
|
|
|
|
|
match *self {
|
|
|
|
|
Self::Basic(val) => val,
|
|
|
|
|
Self::Specific {
|
|
|
|
|
default, newline, ..
|
|
|
|
|
} => newline.or(default).unwrap_or(WhitespaceRenderValue::None),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
|
|
|
|
#[serde(default)]
|
|
|
|
|
pub struct WhitespaceCharacters {
|
|
|
|
|
pub space: char,
|
|
|
|
|
pub nbsp: char,
|
|
|
|
|
pub tab: char,
|
|
|
|
|
pub tabpad: char,
|
|
|
|
|
pub newline: char,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Default for WhitespaceCharacters {
|
|
|
|
|
fn default() -> Self {
|
|
|
|
|
Self {
|
|
|
|
|
space: '·', // U+00B7
|
|
|
|
|
nbsp: '⍽', // U+237D
|
|
|
|
|
tab: '→', // U+2192
|
|
|
|
|
newline: '⏎', // U+23CE
|
|
|
|
|
tabpad: ' ',
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
|
|
|
|
#[serde(default, rename_all = "kebab-case")]
|
|
|
|
|
pub struct IndentGuidesConfig {
|
|
|
|
|
pub render: bool,
|
|
|
|
|
pub character: char,
|
|
|
|
|
pub skip_levels: u8,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Default for IndentGuidesConfig {
|
|
|
|
|
fn default() -> Self {
|
|
|
|
|
Self {
|
|
|
|
|
skip_levels: 0,
|
|
|
|
|
render: false,
|
|
|
|
|
character: '│',
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Line ending configuration.
|
|
|
|
|
#[derive(Default, Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
|
|
|
|
#[serde(rename_all = "lowercase")]
|
|
|
|
|
pub enum LineEndingConfig {
|
|
|
|
|
/// The platform's native line ending.
|
|
|
|
|
///
|
|
|
|
|
/// `crlf` on Windows, otherwise `lf`.
|
|
|
|
|
#[default]
|
|
|
|
|
Native,
|
|
|
|
|
/// Line feed.
|
|
|
|
|
LF,
|
|
|
|
|
/// Carriage return followed by line feed.
|
|
|
|
|
Crlf,
|
|
|
|
|
/// Form feed.
|
|
|
|
|
#[cfg(feature = "unicode-lines")]
|
|
|
|
|
FF,
|
|
|
|
|
/// Carriage return.
|
|
|
|
|
#[cfg(feature = "unicode-lines")]
|
|
|
|
|
CR,
|
|
|
|
|
/// Next line.
|
|
|
|
|
#[cfg(feature = "unicode-lines")]
|
|
|
|
|
Nel,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl From<LineEndingConfig> for LineEnding {
|
|
|
|
|
fn from(line_ending: LineEndingConfig) -> Self {
|
|
|
|
|
match line_ending {
|
|
|
|
|
LineEndingConfig::Native => NATIVE_LINE_ENDING,
|
|
|
|
|
LineEndingConfig::LF => LineEnding::LF,
|
|
|
|
|
LineEndingConfig::Crlf => LineEnding::Crlf,
|
|
|
|
|
#[cfg(feature = "unicode-lines")]
|
|
|
|
|
LineEndingConfig::FF => LineEnding::FF,
|
|
|
|
|
#[cfg(feature = "unicode-lines")]
|
|
|
|
|
LineEndingConfig::CR => LineEnding::CR,
|
|
|
|
|
#[cfg(feature = "unicode-lines")]
|
|
|
|
|
LineEndingConfig::Nel => LineEnding::Nel,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
|
|
|
|
#[serde(rename_all = "kebab-case")]
|
|
|
|
|
pub enum PopupBorderConfig {
|
|
|
|
|
None,
|
|
|
|
|
All,
|
|
|
|
|
Popup,
|
|
|
|
|
Menu,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Default for Config {
|
|
|
|
|
fn default() -> Self {
|
|
|
|
|
Self {
|
|
|
|
|
scrolloff: 5,
|
|
|
|
|
scroll_lines: 3,
|
|
|
|
|
mouse: true,
|
|
|
|
|
shell: if cfg!(windows) {
|
|
|
|
|
vec!["cmd".to_owned(), "/C".to_owned()]
|
|
|
|
|
} else {
|
|
|
|
|
vec!["sh".to_owned(), "-c".to_owned()]
|
|
|
|
|
},
|
|
|
|
|
line_number: LineNumber::Absolute,
|
|
|
|
|
cursorline: false,
|
|
|
|
|
cursorcolumn: false,
|
|
|
|
|
gutters: GutterConfig::default(),
|
|
|
|
|
middle_click_paste: true,
|
|
|
|
|
auto_pairs: AutoPairConfig::default(),
|
|
|
|
|
auto_completion: true,
|
|
|
|
|
auto_format: true,
|
|
|
|
|
auto_save: false,
|
|
|
|
|
idle_timeout: Duration::from_millis(250),
|
|
|
|
|
preview_completion_insert: true,
|
|
|
|
|
completion_trigger_len: 2,
|
|
|
|
|
auto_info: true,
|
|
|
|
|
file_picker: FilePickerConfig::default(),
|
|
|
|
|
statusline: StatusLineConfig::default(),
|
|
|
|
|
cursor_shape: CursorShapeConfig::default(),
|
|
|
|
|
true_color: false,
|
|
|
|
|
undercurl: false,
|
|
|
|
|
search: SearchConfig::default(),
|
|
|
|
|
lsp: LspConfig::default(),
|
|
|
|
|
terminal: get_terminal_provider(),
|
|
|
|
|
rulers: Vec::new(),
|
|
|
|
|
whitespace: WhitespaceConfig::default(),
|
|
|
|
|
bufferline: BufferLine::default(),
|
|
|
|
|
indent_guides: IndentGuidesConfig::default(),
|
|
|
|
|
color_modes: false,
|
|
|
|
|
soft_wrap: SoftWrap {
|
|
|
|
|
enable: Some(false),
|
|
|
|
|
..SoftWrap::default()
|
|
|
|
|
},
|
|
|
|
|
text_width: 80,
|
|
|
|
|
completion_replace: false,
|
|
|
|
|
workspace_lsp_roots: Vec::new(),
|
|
|
|
|
default_line_ending: LineEndingConfig::default(),
|
|
|
|
|
insert_final_newline: true,
|
|
|
|
|
smart_tab: Some(SmartTabConfig::default()),
|
|
|
|
|
popup_border: PopupBorderConfig::None,
|
|
|
|
|
indent_heuristic: IndentationHeuristic::default(),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Default for SearchConfig {
|
|
|
|
|
fn default() -> Self {
|
|
|
|
|
Self {
|
|
|
|
|
wrap_around: true,
|
|
|
|
|
smart_case: true,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
use arc_swap::access::DynGuard;
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, Clone, Default)]
|
|
|
|
|
pub struct Breakpoint {
|
|
|
|
|