diff --git a/.gitmodules b/.gitmodules index edfe3c391..d5bd61c9e 100644 --- a/.gitmodules +++ b/.gitmodules @@ -165,8 +165,12 @@ [submodule "helix-syntax/languages/tree-sitter-dockerfile"] path = helix-syntax/languages/tree-sitter-dockerfile url = https://github.com/camdencheek/tree-sitter-dockerfile.git - shallow = true + shallow = true [submodule "helix-syntax/languages/tree-sitter-fish"] path = helix-syntax/languages/tree-sitter-fish url = https://github.com/ram02z/tree-sitter-fish shallow = true +[submodule "helix-syntax/languages/tree-sitter-git-commit"] + path = helix-syntax/languages/tree-sitter-git-commit + url = https://github.com/the-mikedavis/tree-sitter-git-commit.git + shallow = true diff --git a/Cargo.lock b/Cargo.lock index 27268feb0..40afe4e50 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -370,6 +370,7 @@ version = "0.5.0" dependencies = [ "arc-swap", "chrono", + "encoding_rs", "etcetera", "helix-syntax", "log", @@ -471,7 +472,6 @@ dependencies = [ "chardetng", "clipboard-win", "crossterm", - "encoding_rs", "futures-util", "helix-core", "helix-lsp", diff --git a/base16_theme.toml b/base16_theme.toml index 5ec74bcc6..bb60a3ea5 100644 --- a/base16_theme.toml +++ b/base16_theme.toml @@ -29,6 +29,10 @@ "namespace" = "magenta" "ui.help" = { fg = "white", bg = "black" } +"diff.plus" = "green" +"diff.delta" = "yellow" +"diff.minus" = "red" + "diagnostic" = { modifiers = ["underlined"] } "ui.gutter" = { bg = "black" } "info" = "blue" diff --git a/book/src/generated/lang-support.md b/book/src/generated/lang-support.md index c70542011..9c42005bb 100644 --- a/book/src/generated/lang-support.md +++ b/book/src/generated/lang-support.md @@ -3,7 +3,7 @@ | bash | ✓ | | | `bash-language-server` | | c | ✓ | | | `clangd` | | c-sharp | ✓ | | | | -| cmake | ✓ | | | `cmake-language-server` | +| cmake | ✓ | ✓ | ✓ | `cmake-language-server` | | comment | ✓ | | | | | cpp | ✓ | | | `clangd` | | css | ✓ | | | | @@ -11,6 +11,7 @@ | dockerfile | ✓ | | | `docker-langserver` | | elixir | ✓ | | | `elixir-ls` | | fish | ✓ | ✓ | ✓ | | +| git-commit | ✓ | | | | | glsl | ✓ | | ✓ | | | go | ✓ | ✓ | ✓ | `gopls` | | html | ✓ | | | | diff --git a/book/src/generated/typable-cmd.md b/book/src/generated/typable-cmd.md index 763c5795d..37572f911 100644 --- a/book/src/generated/typable-cmd.md +++ b/book/src/generated/typable-cmd.md @@ -41,4 +41,5 @@ | `:hsplit`, `:hs`, `:sp` | Open the file in a horizontal split. | | `:tutor` | Open the tutorial. | | `:goto`, `:g` | Go to line number. | +| `:set-option`, `:set` | Set a config option at runtime | | `:help`, `:h` | Open documentation for a command or keybind. | diff --git a/book/src/guides/adding_languages.md b/book/src/guides/adding_languages.md index 987ad088c..5844a48ee 100644 --- a/book/src/guides/adding_languages.md +++ b/book/src/guides/adding_languages.md @@ -27,18 +27,19 @@ directory](../configuration.md). These are the available keys and descriptions for the file. -| Key | Description | -| ---- | ----------- | -| name | The name of the language | -| scope | A string like `source.js` that identifies the language. Currently, we strive to match the scope names used by popular TextMate grammars and by the Linguist library. Usually `source.` or `text.` in case of markup languages | -| injection-regex | regex pattern that will be tested against a language name in order to determine whether this language should be used for a potential [language injection][treesitter-language-injection] site. | -| file-types | The filetypes of the language, for example `["yml", "yaml"]` | -| shebangs | The interpreters from the shebang line, for example `["sh", "bash"]` | -| roots | A set of marker files to look for when trying to find the workspace root. For example `Cargo.lock`, `yarn.lock` | -| auto-format | Whether to autoformat this language when saving | -| comment-token | The token to use as a comment-token | -| indent | The indent to use. Has sub keys `tab-width` and `unit` | -| config | Language server configuration | +| Key | Description | +| ---- | ----------- | +| name | The name of the language | +| scope | A string like `source.js` that identifies the language. Currently, we strive to match the scope names used by popular TextMate grammars and by the Linguist library. Usually `source.` or `text.` in case of markup languages | +| injection-regex | regex pattern that will be tested against a language name in order to determine whether this language should be used for a potential [language injection][treesitter-language-injection] site. | +| file-types | The filetypes of the language, for example `["yml", "yaml"]` | +| shebangs | The interpreters from the shebang line, for example `["sh", "bash"]` | +| roots | A set of marker files to look for when trying to find the workspace root. For example `Cargo.lock`, `yarn.lock` | +| auto-format | Whether to autoformat this language when saving | +| diagnostic-severity | Minimal severity of diagnostic for it to be displayed. (Allowed values: `Error`, `Warning`, `Info`, `Hint`) | +| comment-token | The token to use as a comment-token | +| indent | The indent to use. Has sub keys `tab-width` and `unit` | +| config | Language server configuration | ## Queries diff --git a/book/src/keymap.md b/book/src/keymap.md index d84b17b8e..581e70e93 100644 --- a/book/src/keymap.md +++ b/book/src/keymap.md @@ -46,39 +46,39 @@ ### Changes -| Key | Description | Command | -| ----- | ----------- | ------- | -| `r` | Replace with a character | `replace` | -| `R` | Replace with yanked text | `replace_with_yanked` | -| `~` | Switch case of the selected text | `switch_case` | -| `` ` `` | Set the selected text to lower case | `switch_to_lowercase` | -| `` Alt-` `` | Set the selected text to upper case | `switch_to_uppercase` | -| `i` | Insert before selection | `insert_mode` | -| `a` | Insert after selection (append) | `append_mode` | -| `I` | Insert at the start of the line | `prepend_to_line` | -| `A` | Insert at the end of the line | `append_to_line` | -| `o` | Open new line below selection | `open_below` | -| `O` | Open new line above selection | `open_above` | -| `.` | Repeat last change | N/A | -| `u` | Undo change | `undo` | -| `U` | Redo change | `redo` | -| `Alt-u` | Move backward in history | `earlier` | -| `Alt-U` | Move forward in history | `later` | -| `y` | Yank selection | `yank` | -| `p` | Paste after selection | `paste_after` | -| `P` | Paste before selection | `paste_before` | -| `"` `` | Select a register to yank to or paste from | `select_register` | -| `>` | Indent selection | `indent` | -| `<` | Unindent selection | `unindent` | -| `=` | Format selection (currently nonfunctional/disabled) (**LSP**) | `format_selections` | -| `d` | Delete selection | `delete_selection` | -| `Alt-d` | Delete selection, without yanking | `delete_selection_noyank` | -| `c` | Change selection (delete and enter insert mode) | `change_selection` | -| `Alt-c` | Change selection (delete and enter insert mode, without yanking) | `change_selection_noyank` | -| `Ctrl-a` | Increment object (number) under cursor | `increment` | -| `Ctrl-x` | Decrement object (number) under cursor | `decrement` | -| `q` | Start/stop macro recording to the selected register | `record_macro` | -| `Q` | Play back a recorded macro from the selected register | `play_macro` | +| Key | Description | Command | +| ----- | ----------- | ------- | +| `r` | Replace with a character | `replace` | +| `R` | Replace with yanked text | `replace_with_yanked` | +| `~` | Switch case of the selected text | `switch_case` | +| `` ` `` | Set the selected text to lower case | `switch_to_lowercase` | +| `` Alt-` `` | Set the selected text to upper case | `switch_to_uppercase` | +| `i` | Insert before selection | `insert_mode` | +| `a` | Insert after selection (append) | `append_mode` | +| `I` | Insert at the start of the line | `prepend_to_line` | +| `A` | Insert at the end of the line | `append_to_line` | +| `o` | Open new line below selection | `open_below` | +| `O` | Open new line above selection | `open_above` | +| `.` | Repeat last change | N/A | +| `u` | Undo change | `undo` | +| `U` | Redo change | `redo` | +| `Alt-u` | Move backward in history | `earlier` | +| `Alt-U` | Move forward in history | `later` | +| `y` | Yank selection | `yank` | +| `p` | Paste after selection | `paste_after` | +| `P` | Paste before selection | `paste_before` | +| `"` `` | Select a register to yank to or paste from | `select_register` | +| `>` | Indent selection | `indent` | +| `<` | Unindent selection | `unindent` | +| `=` | Format selection (currently nonfunctional/disabled) (**LSP**) | `format_selections` | +| `d` | Delete selection | `delete_selection` | +| `Alt-d` | Delete selection, without yanking | `delete_selection_noyank` | +| `c` | Change selection (delete and enter insert mode) | `change_selection` | +| `Alt-c` | Change selection (delete and enter insert mode, without yanking) | `change_selection_noyank` | +| `Ctrl-a` | Increment object (number) under cursor | `increment` | +| `Ctrl-x` | Decrement object (number) under cursor | `decrement` | +| `Q` | Start/stop macro recording to the selected register (experimental) | `record_macro` | +| `q` | Play back a recorded macro from the selected register (experimental) | `replay_macro` | #### Shell diff --git a/book/src/languages.md b/book/src/languages.md index cef61501f..4c4dc326d 100644 --- a/book/src/languages.md +++ b/book/src/languages.md @@ -11,4 +11,3 @@ Changes made to the `languages.toml` file in a user's [configuration directory]( name = "rust" auto-format = false ``` - diff --git a/book/src/themes.md b/book/src/themes.md index fce2c0ca4..8eee334b9 100644 --- a/book/src/themes.md +++ b/book/src/themes.md @@ -178,6 +178,12 @@ We use a similar set of scopes as - `inline` - `block` +- `diff` - version control changes + - `plus` - additions + - `minus` - deletions + - `delta` - modifications + - `moved` - renamed or moved files/changes + #### Interface These scopes are used for theming the editor interface. diff --git a/helix-core/Cargo.toml b/helix-core/Cargo.toml index 839b07ac1..3d7fe8662 100644 --- a/helix-core/Cargo.toml +++ b/helix-core/Cargo.toml @@ -35,6 +35,7 @@ toml = "0.5" similar = "2.1" etcetera = "0.3" +encoding_rs = "0.8" chrono = { version = "0.4", default-features = false, features = ["alloc", "std"] } diff --git a/helix-core/src/diagnostic.rs b/helix-core/src/diagnostic.rs index 4fcf51c9c..210ad6391 100644 --- a/helix-core/src/diagnostic.rs +++ b/helix-core/src/diagnostic.rs @@ -1,12 +1,19 @@ //! LSP diagnostic utility types. +use serde::{Deserialize, Serialize}; /// Describes the severity level of a [`Diagnostic`]. -#[derive(Debug, Clone, Copy, Eq, PartialEq)] +#[derive(Debug, Clone, Copy, Eq, PartialEq, PartialOrd, Ord, Deserialize, Serialize)] pub enum Severity { - Error, - Warning, - Info, Hint, + Info, + Warning, + Error, +} + +impl Default for Severity { + fn default() -> Self { + Self::Hint + } } /// A range of `char`s within the text. diff --git a/helix-core/src/indent.rs b/helix-core/src/indent.rs index b6f5081ac..c2baf3ccc 100644 --- a/helix-core/src/indent.rs +++ b/helix-core/src/indent.rs @@ -442,6 +442,7 @@ where ); let doc = Rope::from(doc); + use crate::diagnostic::Severity; use crate::syntax::{ Configuration, IndentationConfiguration, LanguageConfiguration, Loader, }; @@ -459,6 +460,7 @@ where roots: vec![], comment_token: None, auto_format: false, + diagnostic_severity: Severity::Warning, language_server: None, indent: Some(IndentationConfiguration { tab_width: 4, diff --git a/helix-core/src/lib.rs b/helix-core/src/lib.rs index 92a59f31e..1c78e7c0b 100644 --- a/helix-core/src/lib.rs +++ b/helix-core/src/lib.rs @@ -1,3 +1,5 @@ +pub use encoding_rs as encoding; + pub mod auto_pairs; pub mod chars; pub mod comment; diff --git a/helix-core/src/syntax.rs b/helix-core/src/syntax.rs index ef35fc756..cdae02103 100644 --- a/helix-core/src/syntax.rs +++ b/helix-core/src/syntax.rs @@ -1,5 +1,6 @@ use crate::{ chars::char_is_line_ending, + diagnostic::Severity, regex::Regex, transaction::{ChangeSet, Operation}, Rope, RopeSlice, Tendril, @@ -63,6 +64,8 @@ pub struct LanguageConfiguration { #[serde(default)] pub auto_format: bool, + #[serde(default)] + pub diagnostic_severity: Severity, // content_regex #[serde(default, skip_serializing, deserialize_with = "deserialize_regex")] diff --git a/helix-lsp/src/client.rs b/helix-lsp/src/client.rs index f1de87524..43804daa9 100644 --- a/helix-lsp/src/client.rs +++ b/helix-lsp/src/client.rs @@ -556,6 +556,14 @@ impl Client { self.call::(params) } + pub async fn resolve_completion_item( + &self, + completion_item: lsp::CompletionItem, + ) -> Result { + self.request::(completion_item) + .await + } + pub fn text_document_signature_help( &self, text_document: lsp::TextDocumentIdentifier, diff --git a/helix-syntax/languages/tree-sitter-git-commit b/helix-syntax/languages/tree-sitter-git-commit new file mode 160000 index 000000000..5cd4776c8 --- /dev/null +++ b/helix-syntax/languages/tree-sitter-git-commit @@ -0,0 +1 @@ +Subproject commit 5cd4776c86c82d9d6afdc8c73a47a08057aef618 diff --git a/helix-term/src/application.rs b/helix-term/src/application.rs index 9a46a7fe6..c7202feba 100644 --- a/helix-term/src/application.rs +++ b/helix-term/src/application.rs @@ -378,6 +378,7 @@ impl Application { let doc = self.editor.document_by_path_mut(&path); if let Some(doc) = doc { + let lang_conf = doc.language_config(); let text = doc.text(); let diagnostics = params @@ -415,19 +416,31 @@ impl Application { return None; }; + let severity = + diagnostic.severity.map(|severity| match severity { + DiagnosticSeverity::ERROR => Error, + DiagnosticSeverity::WARNING => Warning, + DiagnosticSeverity::INFORMATION => Info, + DiagnosticSeverity::HINT => Hint, + severity => unreachable!( + "unrecognized diagnostic severity: {:?}", + severity + ), + }); + + if let Some(lang_conf) = lang_conf { + if let Some(severity) = severity { + if severity < lang_conf.diagnostic_severity { + return None; + } + } + }; + Some(Diagnostic { range: Range { start, end }, line: diagnostic.range.start.line as usize, message: diagnostic.message, - severity: diagnostic.severity.map( - |severity| match severity { - DiagnosticSeverity::ERROR => Error, - DiagnosticSeverity::WARNING => Warning, - DiagnosticSeverity::INFORMATION => Info, - DiagnosticSeverity::HINT => Hint, - severity => unimplemented!("{:?}", severity), - }, - ), + severity, // code // source }) diff --git a/helix-term/src/commands.rs b/helix-term/src/commands.rs index c3f107bef..e34cec477 100644 --- a/helix-term/src/commands.rs +++ b/helix-term/src/commands.rs @@ -396,7 +396,7 @@ impl MappableCommand { increment, "Increment", decrement, "Decrement", record_macro, "Record macro", - play_macro, "Play macro", + replay_macro, "Replay macro", ); } @@ -2637,6 +2637,36 @@ pub mod cmd { let (view, doc) = current!(cx.editor); view.ensure_cursor_in_view(doc, line); + Ok(()) + } + + fn setting( + cx: &mut compositor::Context, + args: &[Cow], + _event: PromptEvent, + ) -> anyhow::Result<()> { + let runtime_config = &mut cx.editor.config; + + if args.len() != 2 { + anyhow::bail!("Bad arguments. Usage: `:set key field`"); + } + + let (key, arg) = (&args[0].to_lowercase(), &args[1]); + + match key.as_ref() { + "scrolloff" => runtime_config.scrolloff = arg.parse()?, + "scroll-lines" => runtime_config.scroll_lines = arg.parse()?, + "mouse" => runtime_config.mouse = arg.parse()?, + "line-number" => runtime_config.line_number = arg.parse()?, + "middle-click_paste" => runtime_config.middle_click_paste = arg.parse()?, + "smart-case" => runtime_config.smart_case = arg.parse()?, + "auto-pairs" => runtime_config.auto_pairs = arg.parse()?, + "auto-completion" => runtime_config.auto_completion = arg.parse()?, + "completion-trigger-len" => runtime_config.completion_trigger_len = arg.parse()?, + "auto-info" => runtime_config.auto_info = arg.parse()?, + "true-color" => runtime_config.true_color = arg.parse()?, + _ => anyhow::bail!("Unknown key `{}`.", args[0]), + } Ok(()) } @@ -2965,6 +2995,13 @@ pub mod cmd { fun: goto_line_number, completer: None, }, + TypableCommand { + name: "set-option", + aliases: &["set"], + doc: "Set a config option at runtime", + fun: setting, + completer: Some(completers::setting), + }, TypableCommand { name: "help", aliases: &["h"], @@ -6021,42 +6058,42 @@ fn record_macro(cx: &mut Context) { keys.pop(); let s = keys .into_iter() - .map(|key| format!("{}", key)) - .collect::>() - .join(" "); + .map(|key| { + let s = key.to_string(); + if s.chars().count() == 1 { + s + } else { + format!("<{}>", s) + } + }) + .collect::(); cx.editor.registers.get_mut(reg).write(vec![s]); cx.editor - .set_status(format!("Recorded to register {}", reg)); + .set_status(format!("Recorded to register [{}]", reg)); } else { let reg = cx.register.take().unwrap_or('@'); cx.editor.macro_recording = Some((reg, Vec::new())); cx.editor - .set_status(format!("Recording to register {}", reg)); + .set_status(format!("Recording to register [{}]", reg)); } } -fn play_macro(cx: &mut Context) { +fn replay_macro(cx: &mut Context) { let reg = cx.register.unwrap_or('@'); - let keys = match cx - .editor - .registers - .get(reg) - .and_then(|reg| reg.read().get(0)) - .context("Register empty") - .and_then(|s| { - s.split_whitespace() - .map(str::parse::) - .collect::, _>>() - .context("Failed to parse macro") - }) { - Ok(keys) => keys, - Err(e) => { - cx.editor.set_error(format!("{}", e)); - return; + let keys: Vec = if let Some([keys_str]) = cx.editor.registers.read(reg) { + match helix_view::input::parse_macro(keys_str) { + Ok(keys) => keys, + Err(err) => { + cx.editor.set_error(format!("Invalid macro: {}", err)); + return; + } } + } else { + cx.editor.set_error(format!("Register [{}] empty", reg)); + return; }; - let count = cx.count(); + let count = cx.count(); cx.callback = Some(Box::new( move |compositor: &mut Compositor, cx: &mut compositor::Context| { for _ in 0..count { diff --git a/helix-term/src/keymap.rs b/helix-term/src/keymap.rs index 257d5f296..08c8032bb 100644 --- a/helix-term/src/keymap.rs +++ b/helix-term/src/keymap.rs @@ -593,8 +593,8 @@ impl Default for Keymaps { // paste_all "P" => paste_before, - "q" => record_macro, - "Q" => play_macro, + "Q" => record_macro, + "q" => replay_macro, ">" => indent, "<" => unindent, diff --git a/helix-term/src/ui/completion.rs b/helix-term/src/ui/completion.rs index a55201ff2..274330c0e 100644 --- a/helix-term/src/ui/completion.rs +++ b/helix-term/src/ui/completion.rs @@ -154,8 +154,19 @@ impl Completion { ); doc.apply(&transaction, view.id); - if let Some(additional_edits) = &item.additional_text_edits { - // gopls uses this to add extra imports + // apply additional edits, mostly used to auto import unqualified types + let resolved_additional_text_edits = if item.additional_text_edits.is_some() { + None + } else { + Completion::resolve_completion_item(doc, item.clone()) + .and_then(|item| item.additional_text_edits) + }; + + if let Some(additional_edits) = item + .additional_text_edits + .as_ref() + .or_else(|| resolved_additional_text_edits.as_ref()) + { if !additional_edits.is_empty() { let transaction = util::generate_transaction_from_edits( doc.text(), @@ -181,6 +192,31 @@ impl Completion { completion } + fn resolve_completion_item( + doc: &Document, + completion_item: lsp::CompletionItem, + ) -> Option { + let language_server = doc.language_server()?; + let completion_resolve_provider = language_server + .capabilities() + .completion_provider + .as_ref()? + .resolve_provider; + if completion_resolve_provider != Some(true) { + return None; + } + + let future = language_server.resolve_completion_item(completion_item); + let response = helix_lsp::block_on(future); + match response { + Ok(completion_item) => Some(completion_item), + Err(err) => { + log::error!("execute LSP command: {}", err); + None + } + } + } + pub fn recompute_filter(&mut self, editor: &Editor) { // recompute menu based on matches let menu = self.popup.contents_mut(); diff --git a/helix-term/src/ui/editor.rs b/helix-term/src/ui/editor.rs index c5b438986..dd0503981 100644 --- a/helix-term/src/ui/editor.rs +++ b/helix-term/src/ui/editor.rs @@ -7,7 +7,7 @@ use crate::{ }; use helix_core::{ - coords_at_pos, + coords_at_pos, encoding, graphemes::{ensure_grapheme_boundary_next, next_grapheme_boundary, prev_grapheme_boundary}, movement::Direction, syntax::{self, HighlightEvent}, @@ -548,21 +548,6 @@ impl EditorView { } surface.set_string(viewport.x + 5, viewport.y, progress, base_style); - let rel_path = doc.relative_path(); - let path = rel_path - .as_ref() - .map(|p| p.to_string_lossy()) - .unwrap_or_else(|| SCRATCH_BUFFER_NAME.into()); - - let title = format!("{}{}", path, if doc.is_modified() { "[+]" } else { "" }); - surface.set_stringn( - viewport.x + 8, - viewport.y, - title, - viewport.width.saturating_sub(6) as usize, - base_style, - ); - //------------------------------- // Right side of the status line. //------------------------------- @@ -636,6 +621,13 @@ impl EditorView { base_style, )); + let enc = doc.encoding(); + if enc != encoding::UTF_8 { + right_side_text + .0 + .push(Span::styled(format!(" {} ", enc.name()), base_style)); + } + // Render to the statusline. surface.set_spans( viewport.x @@ -646,6 +638,31 @@ impl EditorView { &right_side_text, right_side_text.width() as u16, ); + + //------------------------------- + // Middle / File path / Title + //------------------------------- + let title = { + let rel_path = doc.relative_path(); + let path = rel_path + .as_ref() + .map(|p| p.to_string_lossy()) + .unwrap_or_else(|| SCRATCH_BUFFER_NAME.into()); + format!("{}{}", path, if doc.is_modified() { "[+]" } else { "" }) + }; + + surface.set_string_truncated( + viewport.x + 8, // 8: 1 space + 3 char mode string + 1 space + 1 spinner + 1 space + viewport.y, + title, + viewport + .width + .saturating_sub(6) + .saturating_sub(right_side_text.width() as u16 + 1) as usize, // "+ 1": a space between the title and the selection info + base_style, + true, + true, + ); } /// Handle events by looking them up in `self.keymaps`. Returns None diff --git a/helix-term/src/ui/mod.rs b/helix-term/src/ui/mod.rs index a19798bad..2db71e787 100644 --- a/helix-term/src/ui/mod.rs +++ b/helix-term/src/ui/mod.rs @@ -174,7 +174,9 @@ pub mod completers { use crate::ui::prompt::Completion; use fuzzy_matcher::skim::SkimMatcherV2 as Matcher; use fuzzy_matcher::FuzzyMatcher; + use helix_view::editor::Config; use helix_view::theme; + use once_cell::sync::Lazy; use std::borrow::Cow; use std::cmp::Reverse; @@ -208,6 +210,31 @@ pub mod completers { names } + pub fn setting(input: &str) -> Vec { + static KEYS: Lazy> = Lazy::new(|| { + serde_json::to_value(Config::default()) + .unwrap() + .as_object() + .unwrap() + .keys() + .cloned() + .collect() + }); + + let matcher = Matcher::default(); + + let mut matches: Vec<_> = KEYS + .iter() + .filter_map(|name| matcher.fuzzy_match(name, input).map(|score| (name, score))) + .collect(); + + matches.sort_unstable_by_key(|(_file, score)| Reverse(*score)); + matches + .into_iter() + .map(|(name, _)| ((0..), name.into())) + .collect() + } + pub fn filename(input: &str) -> Vec { filename_impl(input, |entry| { let is_dir = entry.file_type().map_or(false, |entry| entry.is_dir()); diff --git a/helix-view/Cargo.toml b/helix-view/Cargo.toml index 9c3a83f81..121a518c3 100644 --- a/helix-view/Cargo.toml +++ b/helix-view/Cargo.toml @@ -29,7 +29,6 @@ futures-util = { version = "0.3", features = ["std", "async-await"], default-fea slotmap = "1" -encoding_rs = "0.8" chardetng = "0.1" serde = { version = "1.0", features = ["derive"] } diff --git a/helix-view/src/document.rs b/helix-view/src/document.rs index c71d1850a..9652d7b3c 100644 --- a/helix-view/src/document.rs +++ b/helix-view/src/document.rs @@ -9,6 +9,7 @@ use std::str::FromStr; use std::sync::Arc; use helix_core::{ + encoding, history::History, indent::{auto_detect_indent_style, IndentStyle}, line_ending::auto_detect_line_ending, @@ -74,7 +75,7 @@ pub struct Document { pub(crate) selections: HashMap, path: Option, - encoding: &'static encoding_rs::Encoding, + encoding: &'static encoding::Encoding, /// Current editing mode. pub mode: Mode, @@ -143,8 +144,8 @@ impl fmt::Debug for Document { /// be used to override encoding auto-detection. pub fn from_reader( reader: &mut R, - encoding: Option<&'static encoding_rs::Encoding>, -) -> Result<(Rope, &'static encoding_rs::Encoding), Error> { + encoding: Option<&'static encoding::Encoding>, +) -> Result<(Rope, &'static encoding::Encoding), Error> { // These two buffers are 8192 bytes in size each and are used as // intermediaries during the decoding process. Text read into `buf` // from `reader` is decoded into `buf_out` as UTF-8. Once either @@ -212,11 +213,11 @@ pub fn from_reader( total_read += read; total_written += written; match result { - encoding_rs::CoderResult::InputEmpty => { + encoding::CoderResult::InputEmpty => { debug_assert_eq!(slice.len(), total_read); break; } - encoding_rs::CoderResult::OutputFull => { + encoding::CoderResult::OutputFull => { debug_assert!(slice.len() > total_read); builder.append(&buf_str[..total_written]); total_written = 0; @@ -251,7 +252,7 @@ pub fn from_reader( /// replacement characters may appear in the encoded text. pub async fn to_writer<'a, W: tokio::io::AsyncWriteExt + Unpin + ?Sized>( writer: &'a mut W, - encoding: &'static encoding_rs::Encoding, + encoding: &'static encoding::Encoding, rope: &'a Rope, ) -> Result<(), Error> { // Text inside a `Rope` is stored as non-contiguous blocks of data called @@ -286,12 +287,12 @@ pub async fn to_writer<'a, W: tokio::io::AsyncWriteExt + Unpin + ?Sized>( total_read += read; total_written += written; match result { - encoding_rs::CoderResult::InputEmpty => { + encoding::CoderResult::InputEmpty => { debug_assert_eq!(chunk.len(), total_read); debug_assert!(buf.len() >= total_written); break; } - encoding_rs::CoderResult::OutputFull => { + encoding::CoderResult::OutputFull => { debug_assert!(chunk.len() > total_read); writer.write_all(&buf[..total_written]).await?; total_written = 0; @@ -322,8 +323,8 @@ use helix_lsp::lsp; use url::Url; impl Document { - pub fn from(text: Rope, encoding: Option<&'static encoding_rs::Encoding>) -> Self { - let encoding = encoding.unwrap_or(encoding_rs::UTF_8); + pub fn from(text: Rope, encoding: Option<&'static encoding::Encoding>) -> Self { + let encoding = encoding.unwrap_or(encoding::UTF_8); let changes = ChangeSet::new(&text); let old_state = None; @@ -356,7 +357,7 @@ impl Document { /// overwritten with the `encoding` parameter. pub fn open( path: &Path, - encoding: Option<&'static encoding_rs::Encoding>, + encoding: Option<&'static encoding::Encoding>, theme: Option<&Theme>, config_loader: Option<&syntax::Loader>, ) -> Result { @@ -366,7 +367,7 @@ impl Document { std::fs::File::open(path).context(format!("unable to open {:?}", path))?; from_reader(&mut file, encoding)? } else { - let encoding = encoding.unwrap_or(encoding_rs::UTF_8); + let encoding = encoding.unwrap_or(encoding::UTF_8); (Rope::from(DEFAULT_LINE_ENDING.as_str()), encoding) }; @@ -548,7 +549,7 @@ impl Document { /// Sets the [`Document`]'s encoding with the encoding correspondent to `label`. pub fn set_encoding(&mut self, label: &str) -> Result<(), Error> { - match encoding_rs::Encoding::for_label(label.as_bytes()) { + match encoding::Encoding::for_label(label.as_bytes()) { Some(encoding) => self.encoding = encoding, None => return Err(anyhow::anyhow!("unknown encoding")), } @@ -556,7 +557,7 @@ impl Document { } /// Returns the [`Document`]'s current encoding. - pub fn encoding(&self) -> &'static encoding_rs::Encoding { + pub fn encoding(&self) -> &'static encoding::Encoding { self.encoding } @@ -1123,7 +1124,7 @@ mod test { macro_rules! test_decode { ($label:expr, $label_override:expr) => { - let encoding = encoding_rs::Encoding::for_label($label_override.as_bytes()).unwrap(); + let encoding = encoding::Encoding::for_label($label_override.as_bytes()).unwrap(); let base_path = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("tests/encoding"); let path = base_path.join(format!("{}_in.txt", $label)); let ref_path = base_path.join(format!("{}_in_ref.txt", $label)); @@ -1142,7 +1143,7 @@ mod test { macro_rules! test_encode { ($label:expr, $label_override:expr) => { - let encoding = encoding_rs::Encoding::for_label($label_override.as_bytes()).unwrap(); + let encoding = encoding::Encoding::for_label($label_override.as_bytes()).unwrap(); let base_path = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("tests/encoding"); let path = base_path.join(format!("{}_out.txt", $label)); let ref_path = base_path.join(format!("{}_out_ref.txt", $label)); diff --git a/helix-view/src/editor.rs b/helix-view/src/editor.rs index fd6eb4d57..f4b0f73e7 100644 --- a/helix-view/src/editor.rs +++ b/helix-view/src/editor.rs @@ -27,7 +27,7 @@ pub use helix_core::register::Registers; use helix_core::syntax; use helix_core::{Position, Selection}; -use serde::Deserialize; +use serde::{Deserialize, Serialize}; fn deserialize_duration_millis<'de, D>(deserializer: D) -> Result where @@ -37,7 +37,7 @@ where Ok(Duration::from_millis(millis)) } -#[derive(Debug, Clone, PartialEq, Deserialize)] +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] #[serde(rename_all = "kebab-case", default, deny_unknown_fields)] pub struct FilePickerConfig { /// IgnoreOptions @@ -77,7 +77,7 @@ impl Default for FilePickerConfig { } } -#[derive(Debug, Clone, PartialEq, Deserialize)] +#[derive(Debug, Clone, PartialEq, 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. @@ -109,7 +109,7 @@ pub struct Config { pub true_color: bool, } -#[derive(Debug, Copy, Clone, PartialEq, Eq, Deserialize)] +#[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "kebab-case")] pub enum LineNumber { /// Show absolute line number @@ -119,6 +119,18 @@ pub enum LineNumber { Relative, } +impl std::str::FromStr for LineNumber { + type Err = anyhow::Error; + + fn from_str(s: &str) -> Result { + 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`."), + } + } +} + impl Default for Config { fn default() -> Self { Self { diff --git a/helix-view/src/input.rs b/helix-view/src/input.rs index 92caa5176..14dadc3b9 100644 --- a/helix-view/src/input.rs +++ b/helix-view/src/input.rs @@ -254,6 +254,43 @@ impl From for crossterm::event::KeyEvent { } } +pub fn parse_macro(keys_str: &str) -> anyhow::Result> { + use anyhow::Context; + let mut keys_res: anyhow::Result<_> = Ok(Vec::new()); + let mut i = 0; + while let Ok(keys) = &mut keys_res { + if i >= keys_str.len() { + break; + } + if !keys_str.is_char_boundary(i) { + i += 1; + continue; + } + + let s = &keys_str[i..]; + let mut end_i = 1; + while !s.is_char_boundary(end_i) { + end_i += 1; + } + let c = &s[..end_i]; + if c == ">" { + keys_res = Err(anyhow!("Unmatched '>'")); + } else if c != "<" { + keys.push(c); + i += end_i; + } else { + match s.find('>').context("'>' expected") { + Ok(end_i) => { + keys.push(&s[1..end_i]); + i += end_i + 1; + } + Err(err) => keys_res = Err(err), + } + } + } + keys_res.and_then(|keys| keys.into_iter().map(str::parse).collect()) +} + #[cfg(test)] mod test { use super::*; @@ -339,4 +376,120 @@ mod test { assert!(str::parse::("123").is_err()); assert!(str::parse::("S--").is_err()); } + + #[test] + fn parsing_valid_macros() { + assert_eq!( + parse_macro("xdo").ok(), + Some(vec![ + KeyEvent { + code: KeyCode::Char('x'), + modifiers: KeyModifiers::NONE, + }, + KeyEvent { + code: KeyCode::Char('d'), + modifiers: KeyModifiers::NONE, + }, + KeyEvent { + code: KeyCode::Char('o'), + modifiers: KeyModifiers::NONE, + }, + ]), + ); + + assert_eq!( + parse_macro("vhxx").ok(), + Some(vec![ + KeyEvent { + code: KeyCode::Char('w'), + modifiers: KeyModifiers::CONTROL, + }, + KeyEvent { + code: KeyCode::Char('v'), + modifiers: KeyModifiers::NONE, + }, + KeyEvent { + code: KeyCode::Char('w'), + modifiers: KeyModifiers::CONTROL, + }, + KeyEvent { + code: KeyCode::Char('h'), + modifiers: KeyModifiers::NONE, + }, + KeyEvent { + code: KeyCode::Char('o'), + modifiers: KeyModifiers::CONTROL, + }, + KeyEvent { + code: KeyCode::Char('x'), + modifiers: KeyModifiers::NONE, + }, + KeyEvent { + code: KeyCode::Char('x'), + modifiers: KeyModifiers::NONE, + }, + KeyEvent { + code: KeyCode::Char('s'), + modifiers: KeyModifiers::ALT, + }, + ]) + ); + + assert_eq!( + parse_macro(":o foo.bar").ok(), + Some(vec![ + KeyEvent { + code: KeyCode::Char(':'), + modifiers: KeyModifiers::NONE, + }, + KeyEvent { + code: KeyCode::Char('o'), + modifiers: KeyModifiers::NONE, + }, + KeyEvent { + code: KeyCode::Char(' '), + modifiers: KeyModifiers::NONE, + }, + KeyEvent { + code: KeyCode::Char('f'), + modifiers: KeyModifiers::NONE, + }, + KeyEvent { + code: KeyCode::Char('o'), + modifiers: KeyModifiers::NONE, + }, + KeyEvent { + code: KeyCode::Char('o'), + modifiers: KeyModifiers::NONE, + }, + KeyEvent { + code: KeyCode::Char('.'), + modifiers: KeyModifiers::NONE, + }, + KeyEvent { + code: KeyCode::Char('b'), + modifiers: KeyModifiers::NONE, + }, + KeyEvent { + code: KeyCode::Char('a'), + modifiers: KeyModifiers::NONE, + }, + KeyEvent { + code: KeyCode::Char('r'), + modifiers: KeyModifiers::NONE, + }, + KeyEvent { + code: KeyCode::Enter, + modifiers: KeyModifiers::NONE, + }, + ]) + ); + } + + #[test] + fn parsing_invalid_macros_fails() { + assert!(parse_macro("abc123").is_err()); + assert!(parse_macro("wd").is_err()); + } } diff --git a/languages.toml b/languages.toml index dd18fa190..616ef234e 100644 --- a/languages.toml +++ b/languages.toml @@ -473,3 +473,11 @@ file-types = ["Dockerfile", "dockerfile"] comment-token = "#" indent = { tab-width = 2, unit = " " } language-server = { command = "docker-langserver", args = ["--stdio"] } + +[[language]] +name = "git-commit" +scope = "git.commitmsg" +roots = [] +file-types = ["COMMIT_EDITMSG"] +comment-token = "#" +indent = { tab-width = 2, unit = " " } diff --git a/runtime/queries/cmake/indents.toml b/runtime/queries/cmake/indents.toml new file mode 100644 index 000000000..8b886a4fb --- /dev/null +++ b/runtime/queries/cmake/indents.toml @@ -0,0 +1,12 @@ +indent = [ + "if_condition", + "foreach_loop", + "while_loop", + "function_def", + "macro_def", + "normal_command", +] + +outdent = [ + ")" +] diff --git a/runtime/queries/cmake/textobjects.scm b/runtime/queries/cmake/textobjects.scm new file mode 100644 index 000000000..b0d1b1083 --- /dev/null +++ b/runtime/queries/cmake/textobjects.scm @@ -0,0 +1,3 @@ +(macro_def) @function.around + +(argument) @parameter.inside diff --git a/runtime/queries/git-commit/highlights.scm b/runtime/queries/git-commit/highlights.scm new file mode 100644 index 000000000..a74bb3817 --- /dev/null +++ b/runtime/queries/git-commit/highlights.scm @@ -0,0 +1,19 @@ +(subject) @markup.heading +(path) @string.special.path +(branch) @string.special.symbol +(commit) @constant +(item) @markup.link.url +(header) @tag +(rebase_command) @markup.raw + +(change kind: "new file" @diff.plus) +(change kind: "deleted" @diff.minus) +(change kind: "modified" @diff.delta) +(change kind: "renamed" @diff.delta.moved) + +[":" "->"] @punctuation.delimeter +(comment) @comment + +; once we have diff injections, @comment should become @none +((comment (scissors)) + (message)+ @comment) diff --git a/runtime/queries/git-commit/injections.scm b/runtime/queries/git-commit/injections.scm new file mode 100644 index 000000000..2837a5865 --- /dev/null +++ b/runtime/queries/git-commit/injections.scm @@ -0,0 +1,15 @@ +; once a diff grammar is available, we can inject diff highlighting into the +; trailer after scissors (git commit --verbose) +; see https://github.com/helix-editor/helix/pull/1338#issuecomment-1000013539 +; +; ((comment (scissors)) +; (message) @injection.content +; (#set! injection.language "diff")) + +; --- + +; once a rebase grammar is available, we can inject rebase highlighting into +; interactive rebase summary sections like so: +; +; ((rebase_command) @injection.content +; (#set! injection.language "git-rebase")) diff --git a/runtime/themes/base16_default_dark.toml b/runtime/themes/base16_default_dark.toml index fd4b0fea3..33ff87fae 100644 --- a/runtime/themes/base16_default_dark.toml +++ b/runtime/themes/base16_default_dark.toml @@ -33,6 +33,10 @@ "namespace" = "base0E" "ui.help" = { fg = "base06", bg = "base01" } +"diff.plus" = "base0B" +"diff.delta" = "base09" +"diff.minus" = "base08" + "diagnostic" = { modifiers = ["underlined"] } "ui.gutter" = { bg = "base01" } "info" = "base0D" diff --git a/runtime/themes/base16_default_light.toml b/runtime/themes/base16_default_light.toml index 596990dad..3f4de2654 100644 --- a/runtime/themes/base16_default_light.toml +++ b/runtime/themes/base16_default_light.toml @@ -33,6 +33,10 @@ "namespace" = "base0E" "ui.help" = { fg = "base06", bg = "base01" } +"diff.plus" = "base0B" +"diff.delta" = "base09" +"diff.minus" = "base08" + "diagnostic" = { modifiers = ["underlined"] } "ui.gutter" = { bg = "base01" } "info" = "base0D" diff --git a/runtime/themes/base16_terminal.toml b/runtime/themes/base16_terminal.toml index 123ceaea6..cbbfbf241 100644 --- a/runtime/themes/base16_terminal.toml +++ b/runtime/themes/base16_terminal.toml @@ -30,6 +30,10 @@ "namespace" = "light-magenta" "ui.help" = { fg = "white", bg = "black" } +"diff.plus" = "light-green" +"diff.delta" = "yellow" +"diff.minus" = "light-red" + "diagnostic" = { modifiers = ["underlined"] } "ui.gutter" = { bg = "black" } "info" = "light-blue" diff --git a/runtime/themes/bogster.toml b/runtime/themes/bogster.toml index 86a6c34bf..ea6844f2c 100644 --- a/runtime/themes/bogster.toml +++ b/runtime/themes/bogster.toml @@ -28,6 +28,10 @@ "module" = "#d32c5d" +"diff.plus" = "#59dcb7" +"diff.delta" = "#dc7759" +"diff.minus" = "#dc597f" + "ui.background" = { bg = "#161c23" } "ui.linenr" = { fg = "#415367" } "ui.linenr.selected" = { fg = "#e5ded6" } # TODO diff --git a/runtime/themes/dark_plus.toml b/runtime/themes/dark_plus.toml index 0554f827f..784376493 100644 --- a/runtime/themes/dark_plus.toml +++ b/runtime/themes/dark_plus.toml @@ -39,6 +39,10 @@ "constant.numeric" = { fg = "pale_green" } "constant.character.escape" = { fg = "gold" } +"diff.plus" = { fg = "pale_green" } +"diff.delta" = { fg = "gold" } +"diff.minus" = { fg = "red" } + "ui.background" = { fg = "light_gray", bg = "dark_gray2" } "ui.window" = { bg = "widget" } diff --git a/runtime/themes/dracula.toml b/runtime/themes/dracula.toml index 956fd0f5d..f01c73230 100644 --- a/runtime/themes/dracula.toml +++ b/runtime/themes/dracula.toml @@ -15,6 +15,10 @@ "variable.builtin" = { fg = "cyan", modifiers = ["italic"] } "variable.parameter" = { fg ="orange", modifiers = ["italic"] } +"diff.plus" = { fg = "green" } +"diff.delta" = { fg = "orange" } +"diff.minus" = { fg = "red" } + "ui.background" = { fg = "foreground", bg = "background" } "ui.cursor" = { fg = "background", bg = "orange", modifiers = ["dim"] } "ui.cursor.match" = { fg = "green", modifiers = ["underlined"] } diff --git a/runtime/themes/everforest_dark.toml b/runtime/themes/everforest_dark.toml index bbd005e6a..3be9f1f0e 100644 --- a/runtime/themes/everforest_dark.toml +++ b/runtime/themes/everforest_dark.toml @@ -34,6 +34,10 @@ "module" = "blue" "special" = "orange" +"diff.plus" = "green" +"diff.delta" = "orange" +"diff.minus" = "red" + "ui.background" = { bg = "bg0" } "ui.cursor" = { fg = "bg0", bg = "fg" } "ui.cursor.match" = { fg = "orange", bg = "bg_yellow" } diff --git a/runtime/themes/gruvbox.toml b/runtime/themes/gruvbox.toml index 0ff039eab..9eae09152 100644 --- a/runtime/themes/gruvbox.toml +++ b/runtime/themes/gruvbox.toml @@ -28,6 +28,10 @@ "label" = "aqua1" "module" = "aqua1" +"diff.plus" = "green1" +"diff.delta" = "orange1" +"diff.minus" = "red1" + "warning" = { fg = "orange1", bg = "bg1" } "error" = { fg = "red1", bg = "bg1" } "info" = { fg = "aqua1", bg = "bg1" } diff --git a/runtime/themes/ingrid.toml b/runtime/themes/ingrid.toml index 308294759..df8a922fd 100644 --- a/runtime/themes/ingrid.toml +++ b/runtime/themes/ingrid.toml @@ -28,6 +28,10 @@ "module" = "#839A53" +"diff.plus" = "#839A53" +"diff.delta" = "#D4A520" +"diff.minus" = "#D74E50" + "ui.background" = { bg = "#FFFCFD" } "ui.linenr" = { fg = "#bbbbbb" } "ui.linenr.selected" = { fg = "#F3EAE9" } # TODO diff --git a/runtime/themes/monokai.toml b/runtime/themes/monokai.toml index 38f9f1707..c8bf8ebd8 100644 --- a/runtime/themes/monokai.toml +++ b/runtime/themes/monokai.toml @@ -39,6 +39,10 @@ "constant.numeric" = { fg = "#ae81ff" } "constant.character.escape" = { fg = "#ae81ff" } +"diff.plus" = { fg = "#a6e22e" } +"diff.delta" = { fg = "#fd971f" } +"diff.minus" = { fg = "#f92672" } + "ui.background" = { fg = "text", bg = "background" } "ui.window" = { bg = "widget" } @@ -65,7 +69,7 @@ "warning" = { fg = "#cca700" } "error" = { fg = "#f48771" } "info" = { fg = "#75beff" } -"hint" = { fg = "#eeeeeeb3" } +"hint" = { fg = "#eeeeeb3" } diagnostic = { modifiers = ["underlined"] } diff --git a/runtime/themes/monokai_pro.toml b/runtime/themes/monokai_pro.toml index bf8a4a84e..5daeaf6cf 100644 --- a/runtime/themes/monokai_pro.toml +++ b/runtime/themes/monokai_pro.toml @@ -77,6 +77,11 @@ # integer, floating point "constant.numeric" = "purple" +# vcs +"diff.plus" = "green" +"diff.delta" = "orange" +"diff.minus" = "red" + # make diagnostic underlined, to distinguish with selection text. diagnostic = { modifiers = ["underlined"] } diff --git a/runtime/themes/monokai_pro_machine.toml b/runtime/themes/monokai_pro_machine.toml index d8a701f11..0763a5fb6 100644 --- a/runtime/themes/monokai_pro_machine.toml +++ b/runtime/themes/monokai_pro_machine.toml @@ -77,6 +77,11 @@ # integer, floating point "constant.numeric" = "purple" +# vcs +"diff.plus" = "green" +"diff.delta" = "orange" +"diff.minus" = "red" + # make diagnostic underlined, to distinguish with selection text. diagnostic = { modifiers = ["underlined"] } diff --git a/runtime/themes/monokai_pro_octagon.toml b/runtime/themes/monokai_pro_octagon.toml index 74459472a..6a74a8d0a 100644 --- a/runtime/themes/monokai_pro_octagon.toml +++ b/runtime/themes/monokai_pro_octagon.toml @@ -77,6 +77,11 @@ # integer, floating point "constant.numeric" = "purple" +# vcs +"diff.plus" = "green" +"diff.delta" = "orange" +"diff.minus" = "red" + # make diagnostic underlined, to distinguish with selection text. diagnostic = { modifiers = ["underlined"] } diff --git a/runtime/themes/monokai_pro_ristretto.toml b/runtime/themes/monokai_pro_ristretto.toml index a9cf4b34a..1a1a32ffa 100644 --- a/runtime/themes/monokai_pro_ristretto.toml +++ b/runtime/themes/monokai_pro_ristretto.toml @@ -77,6 +77,11 @@ # integer, floating point "constant.numeric" = "purple" +# vcs +"diff.plus" = "green" +"diff.delta" = "orange" +"diff.minus" = "red" + # make diagnostic underlined, to distinguish with selection text. diagnostic = { modifiers = ["underlined"] } diff --git a/runtime/themes/monokai_pro_spectrum.toml b/runtime/themes/monokai_pro_spectrum.toml index 232adfbdf..366304939 100644 --- a/runtime/themes/monokai_pro_spectrum.toml +++ b/runtime/themes/monokai_pro_spectrum.toml @@ -77,6 +77,11 @@ # integer, floating point "constant.numeric" = "purple" +# vcs +"diff.plus" = "green" +"diff.delta" = "orange" +"diff.minus" = "red" + # make diagnostic underlined, to distinguish with selection text. diagnostic = { modifiers = ["underlined"] } diff --git a/runtime/themes/nord.toml b/runtime/themes/nord.toml index a619f902e..ae1ea29be 100644 --- a/runtime/themes/nord.toml +++ b/runtime/themes/nord.toml @@ -84,6 +84,11 @@ # nord15 - integer, floating point "constant.numeric" = "nord15" +# vcs +"diff.plus" = "nord14" +"diff.delta" = "nord12" +"diff.minus" = "nord11" + [palette] nord0 = "#2e3440" nord1 = "#3b4252" diff --git a/runtime/themes/onedark.toml b/runtime/themes/onedark.toml index 4ab1dd95b..dfeadbb29 100644 --- a/runtime/themes/onedark.toml +++ b/runtime/themes/onedark.toml @@ -36,6 +36,10 @@ "markup.link.url" = { fg = "cyan", modifiers = ["underlined"]} "markup.link.label" = { fg = "purple" } +"diff.plus" = "green" +"diff.delta" = "gold" +"diff.minus" = "red" + diagnostic = { modifiers = ["underlined"] } "info" = { fg = "blue", modifiers = ["bold"] } "hint" = { fg = "green", modifiers = ["bold"] } diff --git a/runtime/themes/rose_pine.toml b/runtime/themes/rose_pine.toml index 256b33c8d..bf3a040ba 100644 --- a/runtime/themes/rose_pine.toml +++ b/runtime/themes/rose_pine.toml @@ -1,4 +1,5 @@ # Author: RayGervais +# Author: ChrisHa "ui.background" = { bg = "base" } "ui.menu" = "surface" @@ -37,6 +38,9 @@ "ui.window" = { bg = "base" } "ui.help" = { bg = "overlay", fg = "foam" } "text" = "text" +"diff.plus" = "foam" +"diff.delta" = "rose" +"diff.minus" = "love" "info" = "gold" "hint" = "gold" @@ -44,6 +48,15 @@ "diagnostic" = "rose" "error" = "love" +"markup.heading" = { fg = "rose" } +"markup.raw.inline" = { fg = "foam" } +"markup.bold" = { fg = "gold", modifiers = ["bold"] } +"markup.italic" = { fg = "iris", modifiers = ["italic"] } +"markup.list" = { fg = "love" } +"markup.quote" = { fg = "rose" } +"markup.link.url" = { fg = "pine", modifiers = ["underlined"]} +"markup.link.label" = { fg = "foam" } + [palette] base = "#191724" surface = "#1f1d2e" diff --git a/runtime/themes/rose_pine_dawn.toml b/runtime/themes/rose_pine_dawn.toml index 43ba24ed0..cc6d287e0 100644 --- a/runtime/themes/rose_pine_dawn.toml +++ b/runtime/themes/rose_pine_dawn.toml @@ -1,8 +1,8 @@ -# Author: ChrisHa # Author: RayGervais +# Author: ChrisHa -"ui.background" = { bg = "base" } -"ui.menu" = "surface" +"ui.background" = { bg = "surface" } +"ui.menu" = "base" "ui.menu.selected" = { fg = "iris", bg = "surface" } "ui.linenr" = {fg = "subtle" } "ui.popup" = { bg = "overlay" } @@ -38,6 +38,9 @@ "ui.window" = { bg = "base" } "ui.help" = { bg = "overlay", fg = "foam" } "text" = "text" +"diff.plus" = "foam" +"diff.delta" = "rose" +"diff.minus" = "love" "info" = "gold" "hint" = "gold" @@ -45,6 +48,15 @@ "diagnostic" = "rose" "error" = "love" +"markup.heading" = { fg = "rose" } +"markup.raw.inline" = { fg = "foam" } +"markup.bold" = { fg = "gold", modifiers = ["bold"] } +"markup.italic" = { fg = "iris", modifiers = ["italic"] } +"markup.list" = { fg = "love" } +"markup.quote" = { fg = "rose" } +"markup.link.url" = { fg = "pine", modifiers = ["underlined"]} +"markup.link.label" = { fg = "foam" } + [palette] base = "#faf4ed" surface = "#fffaf3" diff --git a/runtime/themes/solarized_dark.toml b/runtime/themes/solarized_dark.toml index 979fdaac7..ab345c8db 100644 --- a/runtime/themes/solarized_dark.toml +++ b/runtime/themes/solarized_dark.toml @@ -22,6 +22,10 @@ "module" = { fg = "violet" } "tag" = { fg = "magenta" } +"diff.plus" = { fg = "green" } +"diff.delta" = { fg = "orange" } +"diff.minus" = { fg = "red" } + # 背景 "ui.background" = { bg = "base03" } diff --git a/runtime/themes/solarized_light.toml b/runtime/themes/solarized_light.toml index ded90cc48..6b0137fb7 100644 --- a/runtime/themes/solarized_light.toml +++ b/runtime/themes/solarized_light.toml @@ -22,6 +22,10 @@ "module" = { fg = "violet" } "tag" = { fg = "magenta" } +"diff.plus" = { fg = "green" } +"diff.delta" = { fg = "orange" } +"diff.minus" = { fg = "red" } + # 背景 "ui.background" = { bg = "base03" } diff --git a/runtime/themes/spacebones_light.toml b/runtime/themes/spacebones_light.toml index 92f116ab9..fcbe7b371 100644 --- a/runtime/themes/spacebones_light.toml +++ b/runtime/themes/spacebones_light.toml @@ -30,6 +30,10 @@ "label" = "#b1951d" "module" = "#b1951d" +"diff.plus" = "#2d9574" +"diff.delta" = "#715ab1" +"diff.minus" = "#ba2f59" + "warning" = { fg = "#da8b55" } "error" = { fg = "#e0211d" } "info" = { fg = "#b1951d" } diff --git a/theme.toml b/theme.toml index b316e8142..ca0b28057 100644 --- a/theme.toml +++ b/theme.toml @@ -34,6 +34,10 @@ label = "honey" "markup.link.url" = { fg = "silver", modifiers = ["underlined"] } "markup.raw" = "almond" +"diff.plus" = "#35bf86" +"diff.minus" = "#f22c86" +"diff.delta" = "#6f44f0" + # TODO: diferentiate doc comment # concat (ERROR) @error.syntax and "MISSING ;" selectors for errors