mirror of
https://github.com/helix-editor/helix.git
synced 2024-11-25 19:03:30 +04:00
Merge branch 'helix-editor:master' into pull-diagnostics
This commit is contained in:
commit
c9074d196b
8
.github/workflows/build.yml
vendored
8
.github/workflows/build.yml
vendored
@ -6,7 +6,7 @@ on:
|
|||||||
- master
|
- master
|
||||||
merge_group:
|
merge_group:
|
||||||
schedule:
|
schedule:
|
||||||
- cron: '00 01 * * *'
|
- cron: "00 01 * * *"
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
check:
|
check:
|
||||||
@ -17,10 +17,7 @@ jobs:
|
|||||||
- name: Checkout sources
|
- name: Checkout sources
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
- name: Install stable toolchain
|
- name: Install stable toolchain
|
||||||
uses: helix-editor/rust-toolchain@v1
|
uses: dtolnay/rust-toolchain@1.70
|
||||||
with:
|
|
||||||
profile: minimal
|
|
||||||
override: true
|
|
||||||
|
|
||||||
- uses: Swatinem/rust-cache@v2
|
- uses: Swatinem/rust-cache@v2
|
||||||
with:
|
with:
|
||||||
@ -119,4 +116,3 @@ jobs:
|
|||||||
git diff-files --quiet \
|
git diff-files --quiet \
|
||||||
|| (echo "Run 'cargo xtask docgen', commit the changes and push again" \
|
|| (echo "Run 'cargo xtask docgen', commit the changes and push again" \
|
||||||
&& exit 1)
|
&& exit 1)
|
||||||
|
|
||||||
|
28
Cargo.lock
generated
28
Cargo.lock
generated
@ -136,9 +136,9 @@ checksum = "df8670b8c7b9dae1793364eafadf7239c40d669904660c5960d74cfd80b46a53"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cc"
|
name = "cc"
|
||||||
version = "1.1.5"
|
version = "1.1.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "324c74f2155653c90b04f25b2a47a8a631360cb908f92a772695f430c7e31052"
|
checksum = "2aba8f4e9906c7ce3c73463f62a7f0c65183ada1a2d47e397cc8810827f9694f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cfg-if"
|
name = "cfg-if"
|
||||||
@ -1681,9 +1681,9 @@ checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libloading"
|
name = "libloading"
|
||||||
version = "0.8.4"
|
version = "0.8.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e310b3a6b5907f99202fcdb4960ff45b93735d7c7d96b760fcff8db2dc0e103d"
|
checksum = "4979f22fdb869068da03c9f7528f8297c6fd2606bc3a4affe42e6a823fdb8da4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"windows-targets 0.52.0",
|
"windows-targets 0.52.0",
|
||||||
@ -2345,18 +2345,18 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "thiserror"
|
name = "thiserror"
|
||||||
version = "1.0.62"
|
version = "1.0.63"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f2675633b1499176c2dff06b0856a27976a8f9d436737b4cf4f312d4d91d8bbb"
|
checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"thiserror-impl",
|
"thiserror-impl",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "thiserror-impl"
|
name = "thiserror-impl"
|
||||||
version = "1.0.62"
|
version = "1.0.63"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d20468752b09f49e909e55a5d338caa8bedf615594e9d80bc4c565d30faf798c"
|
checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@ -2422,9 +2422,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tokio"
|
name = "tokio"
|
||||||
version = "1.38.0"
|
version = "1.38.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ba4f4a02a7a80d6f274636f0aa95c7e383b912d41fe721a31f29e29698585a4a"
|
checksum = "eb2caba9f80616f438e09748d5acda951967e1ea58508ef53d9c6402485a46df"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"backtrace",
|
"backtrace",
|
||||||
"bytes",
|
"bytes",
|
||||||
@ -2463,9 +2463,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "toml"
|
name = "toml"
|
||||||
version = "0.8.14"
|
version = "0.8.15"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6f49eb2ab21d2f26bd6db7bf383edc527a7ebaee412d17af4d40fdccd442f335"
|
checksum = "ac2caab0bf757388c6c0ae23b3293fdb463fee59434529014f85e3263b995c28"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
"serde_spanned",
|
"serde_spanned",
|
||||||
@ -2484,9 +2484,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "toml_edit"
|
name = "toml_edit"
|
||||||
version = "0.22.14"
|
version = "0.22.16"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f21c7aaf97f1bd9ca9d4f9e73b0a6c74bd5afef56f2bc931943a6e1c37e04e38"
|
checksum = "278f3d518e152219c994ce877758516bca5e118eaed6996192a774fb9fbf0788"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"indexmap",
|
"indexmap",
|
||||||
"serde",
|
"serde",
|
||||||
|
@ -85,6 +85,6 @@
|
|||||||
| `:reset-diff-change`, `:diffget`, `:diffg` | Reset the diff change at the cursor position. |
|
| `:reset-diff-change`, `:diffget`, `:diffg` | Reset the diff change at the cursor position. |
|
||||||
| `:clear-register` | Clear given register. If no argument is provided, clear all registers. |
|
| `:clear-register` | Clear given register. If no argument is provided, clear all registers. |
|
||||||
| `:redraw` | Clear and re-render the whole UI |
|
| `:redraw` | Clear and re-render the whole UI |
|
||||||
| `:move` | Move the current buffer and its corresponding file to a different path |
|
| `:move`, `:mv` | Move the current buffer and its corresponding file to a different path |
|
||||||
| `:yank-diagnostic` | Yank diagnostic(s) under primary cursor to register, or clipboard by default |
|
| `:yank-diagnostic` | Yank diagnostic(s) under primary cursor to register, or clipboard by default |
|
||||||
| `:read`, `:r` | Load a file into buffer |
|
| `:read`, `:r` | Load a file into buffer |
|
||||||
|
25
contrib/completion/hx.nu
Normal file
25
contrib/completion/hx.nu
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
# Completions for Helix: <https://github.com/helix-editor/helix>
|
||||||
|
#
|
||||||
|
# NOTE: the `+N` syntax is not supported in Nushell (https://github.com/nushell/nushell/issues/13418)
|
||||||
|
# so it has not been specified here and will not be proposed in the autocompletion of Nushell.
|
||||||
|
# The help message won't be overriden though, so it will still be present here
|
||||||
|
|
||||||
|
def health_categories [] { ["all", "clipboard", "languages"] }
|
||||||
|
|
||||||
|
def grammar_categories [] { ["fetch", "build"] }
|
||||||
|
|
||||||
|
# A post-modern text editor.
|
||||||
|
export extern hx [
|
||||||
|
--help(-h), # Prints help information
|
||||||
|
--tutor, # Loads the tutorial
|
||||||
|
--health: string@health_categories = "all", # Checks for potential errors in editor setup
|
||||||
|
--grammar(-g): string@grammar_categories, # Fetches or builds tree-sitter grammars listed in `languages.toml`
|
||||||
|
--config(-c): glob, # Specifies a file to use for configuration
|
||||||
|
-v, # Increases logging verbosity each use for up to 3 times
|
||||||
|
--log: glob, # Specifies a file to use for logging
|
||||||
|
--version(-V), # Prints version information
|
||||||
|
--vsplit, # Splits all given files vertically into different windows
|
||||||
|
--hsplit, # Splits all given files horizontally into different windows
|
||||||
|
--working-dir(-w): glob, # Specify an initial working directory
|
||||||
|
...files: glob, # Sets the input file to use, position can also be specified via file[:row[:col]]
|
||||||
|
]
|
@ -395,9 +395,9 @@ pub fn handle_config_events(&mut self, config_event: ConfigEvent) {
|
|||||||
|
|
||||||
// reset view position in case softwrap was enabled/disabled
|
// reset view position in case softwrap was enabled/disabled
|
||||||
let scrolloff = self.editor.config().scrolloff;
|
let scrolloff = self.editor.config().scrolloff;
|
||||||
for (view, _) in self.editor.tree.views_mut() {
|
for (view, _) in self.editor.tree.views() {
|
||||||
let doc = &self.editor.documents[&view.doc];
|
let doc = doc_mut!(self.editor, &view.doc);
|
||||||
view.ensure_cursor_in_view(doc, scrolloff)
|
view.ensure_cursor_in_view(doc, scrolloff);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1032,6 +1032,7 @@ fn goto_window(cx: &mut Context, align: Align) {
|
|||||||
let count = cx.count() - 1;
|
let count = cx.count() - 1;
|
||||||
let config = cx.editor.config();
|
let config = cx.editor.config();
|
||||||
let (view, doc) = current!(cx.editor);
|
let (view, doc) = current!(cx.editor);
|
||||||
|
let view_offset = doc.view_offset(view.id);
|
||||||
|
|
||||||
let height = view.inner_height();
|
let height = view.inner_height();
|
||||||
|
|
||||||
@ -1044,15 +1045,15 @@ fn goto_window(cx: &mut Context, align: Align) {
|
|||||||
let last_visual_line = view.last_visual_line(doc);
|
let last_visual_line = view.last_visual_line(doc);
|
||||||
|
|
||||||
let visual_line = match align {
|
let visual_line = match align {
|
||||||
Align::Top => view.offset.vertical_offset + scrolloff + count,
|
Align::Top => view_offset.vertical_offset + scrolloff + count,
|
||||||
Align::Center => view.offset.vertical_offset + (last_visual_line / 2),
|
Align::Center => view_offset.vertical_offset + (last_visual_line / 2),
|
||||||
Align::Bottom => {
|
Align::Bottom => {
|
||||||
view.offset.vertical_offset + last_visual_line.saturating_sub(scrolloff + count)
|
view_offset.vertical_offset + last_visual_line.saturating_sub(scrolloff + count)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let visual_line = visual_line
|
let visual_line = visual_line
|
||||||
.max(view.offset.vertical_offset + scrolloff)
|
.max(view_offset.vertical_offset + scrolloff)
|
||||||
.min(view.offset.vertical_offset + last_visual_line.saturating_sub(scrolloff));
|
.min(view_offset.vertical_offset + last_visual_line.saturating_sub(scrolloff));
|
||||||
|
|
||||||
let pos = view
|
let pos = view
|
||||||
.pos_at_visual_coords(doc, visual_line as u16, 0, false)
|
.pos_at_visual_coords(doc, visual_line as u16, 0, false)
|
||||||
@ -1665,6 +1666,7 @@ pub fn scroll(cx: &mut Context, offset: usize, direction: Direction, sync_cursor
|
|||||||
use Direction::*;
|
use Direction::*;
|
||||||
let config = cx.editor.config();
|
let config = cx.editor.config();
|
||||||
let (view, doc) = current!(cx.editor);
|
let (view, doc) = current!(cx.editor);
|
||||||
|
let mut view_offset = doc.view_offset(view.id);
|
||||||
|
|
||||||
let range = doc.selection(view.id).primary();
|
let range = doc.selection(view.id).primary();
|
||||||
let text = doc.text().slice(..);
|
let text = doc.text().slice(..);
|
||||||
@ -1681,15 +1683,19 @@ pub fn scroll(cx: &mut Context, offset: usize, direction: Direction, sync_cursor
|
|||||||
let doc_text = doc.text().slice(..);
|
let doc_text = doc.text().slice(..);
|
||||||
let viewport = view.inner_area(doc);
|
let viewport = view.inner_area(doc);
|
||||||
let text_fmt = doc.text_format(viewport.width, None);
|
let text_fmt = doc.text_format(viewport.width, None);
|
||||||
let mut annotations = view.text_annotations(&*doc, None);
|
(view_offset.anchor, view_offset.vertical_offset) = char_idx_at_visual_offset(
|
||||||
(view.offset.anchor, view.offset.vertical_offset) = char_idx_at_visual_offset(
|
|
||||||
doc_text,
|
doc_text,
|
||||||
view.offset.anchor,
|
view_offset.anchor,
|
||||||
view.offset.vertical_offset as isize + offset,
|
view_offset.vertical_offset as isize + offset,
|
||||||
0,
|
0,
|
||||||
&text_fmt,
|
&text_fmt,
|
||||||
&annotations,
|
// &annotations,
|
||||||
|
&view.text_annotations(&*doc, None),
|
||||||
);
|
);
|
||||||
|
doc.set_view_offset(view.id, view_offset);
|
||||||
|
|
||||||
|
let doc_text = doc.text().slice(..);
|
||||||
|
let mut annotations = view.text_annotations(&*doc, None);
|
||||||
|
|
||||||
if sync_cursor {
|
if sync_cursor {
|
||||||
let movement = match cx.editor.mode {
|
let movement = match cx.editor.mode {
|
||||||
@ -1716,14 +1722,16 @@ pub fn scroll(cx: &mut Context, offset: usize, direction: Direction, sync_cursor
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let view_offset = doc.view_offset(view.id);
|
||||||
|
|
||||||
let mut head;
|
let mut head;
|
||||||
match direction {
|
match direction {
|
||||||
Forward => {
|
Forward => {
|
||||||
let off;
|
let off;
|
||||||
(head, off) = char_idx_at_visual_offset(
|
(head, off) = char_idx_at_visual_offset(
|
||||||
doc_text,
|
doc_text,
|
||||||
view.offset.anchor,
|
view_offset.anchor,
|
||||||
(view.offset.vertical_offset + scrolloff) as isize,
|
(view_offset.vertical_offset + scrolloff) as isize,
|
||||||
0,
|
0,
|
||||||
&text_fmt,
|
&text_fmt,
|
||||||
&annotations,
|
&annotations,
|
||||||
@ -1736,8 +1744,8 @@ pub fn scroll(cx: &mut Context, offset: usize, direction: Direction, sync_cursor
|
|||||||
Backward => {
|
Backward => {
|
||||||
head = char_idx_at_visual_offset(
|
head = char_idx_at_visual_offset(
|
||||||
doc_text,
|
doc_text,
|
||||||
view.offset.anchor,
|
view_offset.anchor,
|
||||||
(view.offset.vertical_offset + height - scrolloff - 1) as isize,
|
(view_offset.vertical_offset + height - scrolloff - 1) as isize,
|
||||||
0,
|
0,
|
||||||
&text_fmt,
|
&text_fmt,
|
||||||
&annotations,
|
&annotations,
|
||||||
@ -5124,7 +5132,7 @@ fn split(editor: &mut Editor, action: Action) {
|
|||||||
let (view, doc) = current!(editor);
|
let (view, doc) = current!(editor);
|
||||||
let id = doc.id();
|
let id = doc.id();
|
||||||
let selection = doc.selection(view.id).clone();
|
let selection = doc.selection(view.id).clone();
|
||||||
let offset = view.offset;
|
let offset = doc.view_offset(view.id);
|
||||||
|
|
||||||
editor.switch(id, action);
|
editor.switch(id, action);
|
||||||
|
|
||||||
@ -5133,7 +5141,7 @@ fn split(editor: &mut Editor, action: Action) {
|
|||||||
doc.set_selection(view.id, selection);
|
doc.set_selection(view.id, selection);
|
||||||
// match the view scroll offset (switch doesn't handle this fully
|
// match the view scroll offset (switch doesn't handle this fully
|
||||||
// since the selection is only matched after the split)
|
// since the selection is only matched after the split)
|
||||||
view.offset = offset;
|
doc.set_view_offset(view.id, offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn hsplit(cx: &mut Context) {
|
fn hsplit(cx: &mut Context) {
|
||||||
@ -5228,14 +5236,21 @@ fn align_view_middle(cx: &mut Context) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let doc_text = doc.text().slice(..);
|
let doc_text = doc.text().slice(..);
|
||||||
let annotations = view.text_annotations(doc, None);
|
|
||||||
let pos = doc.selection(view.id).primary().cursor(doc_text);
|
let pos = doc.selection(view.id).primary().cursor(doc_text);
|
||||||
let pos =
|
let pos = visual_offset_from_block(
|
||||||
visual_offset_from_block(doc_text, view.offset.anchor, pos, &text_fmt, &annotations).0;
|
doc_text,
|
||||||
|
doc.view_offset(view.id).anchor,
|
||||||
|
pos,
|
||||||
|
&text_fmt,
|
||||||
|
&view.text_annotations(doc, None),
|
||||||
|
)
|
||||||
|
.0;
|
||||||
|
|
||||||
view.offset.horizontal_offset = pos
|
let mut offset = doc.view_offset(view.id);
|
||||||
|
offset.horizontal_offset = pos
|
||||||
.col
|
.col
|
||||||
.saturating_sub((view.inner_area(doc).width as usize) / 2);
|
.saturating_sub((view.inner_area(doc).width as usize) / 2);
|
||||||
|
doc.set_view_offset(view.id, offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn scroll_up(cx: &mut Context) {
|
fn scroll_up(cx: &mut Context) {
|
||||||
@ -6117,7 +6132,7 @@ fn jump_to_word(cx: &mut Context, behaviour: Movement) {
|
|||||||
|
|
||||||
// This is not necessarily exact if there is virtual text like soft wrap.
|
// This is not necessarily exact if there is virtual text like soft wrap.
|
||||||
// It's ok though because the extra jump labels will not be rendered.
|
// It's ok though because the extra jump labels will not be rendered.
|
||||||
let start = text.line_to_char(text.char_to_line(view.offset.anchor));
|
let start = text.line_to_char(text.char_to_line(doc.view_offset(view.id).anchor));
|
||||||
let end = text.line_to_char(view.estimate_last_doc_line(doc) + 1);
|
let end = text.line_to_char(view.estimate_last_doc_line(doc) + 1);
|
||||||
|
|
||||||
let primary_selection = doc.selection(view.id).primary();
|
let primary_selection = doc.selection(view.id).primary();
|
||||||
|
@ -1299,7 +1299,8 @@ fn compute_inlay_hints_for_view(
|
|||||||
// than computing all the hints for the full file (which could be dozens of time
|
// than computing all the hints for the full file (which could be dozens of time
|
||||||
// longer than the view is).
|
// longer than the view is).
|
||||||
let view_height = view.inner_height();
|
let view_height = view.inner_height();
|
||||||
let first_visible_line = doc_text.char_to_line(view.offset.anchor.min(doc_text.len_chars()));
|
let first_visible_line =
|
||||||
|
doc_text.char_to_line(doc.view_offset(view_id).anchor.min(doc_text.len_chars()));
|
||||||
let first_line = first_visible_line.saturating_sub(view_height);
|
let first_line = first_visible_line.saturating_sub(view_height);
|
||||||
let last_line = first_visible_line
|
let last_line = first_visible_line
|
||||||
.saturating_add(view_height.saturating_mul(2))
|
.saturating_add(view_height.saturating_mul(2))
|
||||||
|
@ -1587,7 +1587,7 @@ fn find_highlight_at_cursor(
|
|||||||
// Query the same range as the one used in syntax highlighting.
|
// Query the same range as the one used in syntax highlighting.
|
||||||
let range = {
|
let range = {
|
||||||
// Calculate viewport byte ranges:
|
// Calculate viewport byte ranges:
|
||||||
let row = text.char_to_line(view.offset.anchor.min(text.len_chars()));
|
let row = text.char_to_line(doc.view_offset(view.id).anchor.min(text.len_chars()));
|
||||||
// Saturating subs to make it inclusive zero indexing.
|
// Saturating subs to make it inclusive zero indexing.
|
||||||
let last_line = text.len_lines().saturating_sub(1);
|
let last_line = text.len_lines().saturating_sub(1);
|
||||||
let height = view.inner_area(doc).height;
|
let height = view.inner_area(doc).height;
|
||||||
@ -3122,7 +3122,7 @@ fn read(cx: &mut compositor::Context, args: &[Cow<str>], event: PromptEvent) ->
|
|||||||
},
|
},
|
||||||
TypableCommand {
|
TypableCommand {
|
||||||
name: "move",
|
name: "move",
|
||||||
aliases: &[],
|
aliases: &["mv"],
|
||||||
doc: "Move the current buffer and its corresponding file to a different path",
|
doc: "Move the current buffer and its corresponding file to a different path",
|
||||||
fun: move_buffer,
|
fun: move_buffer,
|
||||||
signature: CommandSignature::positional(&[completers::filename]),
|
signature: CommandSignature::positional(&[completers::filename]),
|
||||||
|
@ -93,6 +93,8 @@ pub fn render_view(
|
|||||||
let theme = &editor.theme;
|
let theme = &editor.theme;
|
||||||
let config = editor.config();
|
let config = editor.config();
|
||||||
|
|
||||||
|
let view_offset = doc.view_offset(view.id);
|
||||||
|
|
||||||
let text_annotations = view.text_annotations(doc, Some(theme));
|
let text_annotations = view.text_annotations(doc, Some(theme));
|
||||||
let mut decorations = DecorationManager::default();
|
let mut decorations = DecorationManager::default();
|
||||||
|
|
||||||
@ -119,13 +121,13 @@ pub fn render_view(
|
|||||||
}
|
}
|
||||||
|
|
||||||
let syntax_highlights =
|
let syntax_highlights =
|
||||||
Self::doc_syntax_highlights(doc, view.offset.anchor, inner.height, theme);
|
Self::doc_syntax_highlights(doc, view_offset.anchor, inner.height, theme);
|
||||||
|
|
||||||
let mut overlay_highlights =
|
let mut overlay_highlights =
|
||||||
Self::empty_highlight_iter(doc, view.offset.anchor, inner.height);
|
Self::empty_highlight_iter(doc, view_offset.anchor, inner.height);
|
||||||
let overlay_syntax_highlights = Self::overlay_syntax_highlights(
|
let overlay_syntax_highlights = Self::overlay_syntax_highlights(
|
||||||
doc,
|
doc,
|
||||||
view.offset.anchor,
|
view_offset.anchor,
|
||||||
inner.height,
|
inner.height,
|
||||||
&text_annotations,
|
&text_annotations,
|
||||||
);
|
);
|
||||||
@ -203,7 +205,7 @@ pub fn render_view(
|
|||||||
surface,
|
surface,
|
||||||
inner,
|
inner,
|
||||||
doc,
|
doc,
|
||||||
view.offset,
|
view_offset,
|
||||||
&text_annotations,
|
&text_annotations,
|
||||||
syntax_highlights,
|
syntax_highlights,
|
||||||
overlay_highlights,
|
overlay_highlights,
|
||||||
@ -259,11 +261,13 @@ pub fn render_rulers(
|
|||||||
.and_then(|config| config.rulers.as_ref())
|
.and_then(|config| config.rulers.as_ref())
|
||||||
.unwrap_or(editor_rulers);
|
.unwrap_or(editor_rulers);
|
||||||
|
|
||||||
|
let view_offset = doc.view_offset(view.id);
|
||||||
|
|
||||||
rulers
|
rulers
|
||||||
.iter()
|
.iter()
|
||||||
// View might be horizontally scrolled, convert from absolute distance
|
// View might be horizontally scrolled, convert from absolute distance
|
||||||
// from the 1st column to relative distance from left of viewport
|
// from the 1st column to relative distance from left of viewport
|
||||||
.filter_map(|ruler| ruler.checked_sub(1 + view.offset.horizontal_offset as u16))
|
.filter_map(|ruler| ruler.checked_sub(1 + view_offset.horizontal_offset as u16))
|
||||||
.filter(|ruler| ruler < &viewport.width)
|
.filter(|ruler| ruler < &viewport.width)
|
||||||
.map(|ruler| viewport.clip_left(ruler).with_width(1))
|
.map(|ruler| viewport.clip_left(ruler).with_width(1))
|
||||||
.for_each(|area| surface.set_style(area, ruler_theme))
|
.for_each(|area| surface.set_style(area, ruler_theme))
|
||||||
@ -825,6 +829,7 @@ pub fn highlight_cursorcolumn(
|
|||||||
let inner_area = view.inner_area(doc);
|
let inner_area = view.inner_area(doc);
|
||||||
|
|
||||||
let selection = doc.selection(view.id);
|
let selection = doc.selection(view.id);
|
||||||
|
let view_offset = doc.view_offset(view.id);
|
||||||
let primary = selection.primary();
|
let primary = selection.primary();
|
||||||
let text_format = doc.text_format(viewport.width, None);
|
let text_format = doc.text_format(viewport.width, None);
|
||||||
for range in selection.iter() {
|
for range in selection.iter() {
|
||||||
@ -835,11 +840,11 @@ pub fn highlight_cursorcolumn(
|
|||||||
visual_offset_from_block(text, cursor, cursor, &text_format, text_annotations).0;
|
visual_offset_from_block(text, cursor, cursor, &text_format, text_annotations).0;
|
||||||
|
|
||||||
// if the cursor is horizontally in the view
|
// if the cursor is horizontally in the view
|
||||||
if col >= view.offset.horizontal_offset
|
if col >= view_offset.horizontal_offset
|
||||||
&& inner_area.width > (col - view.offset.horizontal_offset) as u16
|
&& inner_area.width > (col - view_offset.horizontal_offset) as u16
|
||||||
{
|
{
|
||||||
let area = Rect::new(
|
let area = Rect::new(
|
||||||
inner_area.x + (col - view.offset.horizontal_offset) as u16,
|
inner_area.x + (col - view_offset.horizontal_offset) as u16,
|
||||||
view.area.y,
|
view.area.y,
|
||||||
1,
|
1,
|
||||||
view.area.height,
|
view.area.height,
|
||||||
|
@ -83,7 +83,7 @@ pub fn raw_regex_prompt(
|
|||||||
let (view, doc) = current!(cx.editor);
|
let (view, doc) = current!(cx.editor);
|
||||||
let doc_id = view.doc;
|
let doc_id = view.doc;
|
||||||
let snapshot = doc.selection(view.id).clone();
|
let snapshot = doc.selection(view.id).clone();
|
||||||
let offset_snapshot = view.offset;
|
let offset_snapshot = doc.view_offset(view.id);
|
||||||
let config = cx.editor.config();
|
let config = cx.editor.config();
|
||||||
|
|
||||||
let mut prompt = Prompt::new(
|
let mut prompt = Prompt::new(
|
||||||
@ -95,7 +95,7 @@ pub fn raw_regex_prompt(
|
|||||||
PromptEvent::Abort => {
|
PromptEvent::Abort => {
|
||||||
let (view, doc) = current!(cx.editor);
|
let (view, doc) = current!(cx.editor);
|
||||||
doc.set_selection(view.id, snapshot.clone());
|
doc.set_selection(view.id, snapshot.clone());
|
||||||
view.offset = offset_snapshot;
|
doc.set_view_offset(view.id, offset_snapshot);
|
||||||
}
|
}
|
||||||
PromptEvent::Update | PromptEvent::Validate => {
|
PromptEvent::Update | PromptEvent::Validate => {
|
||||||
// skip empty input
|
// skip empty input
|
||||||
@ -136,7 +136,7 @@ pub fn raw_regex_prompt(
|
|||||||
Err(err) => {
|
Err(err) => {
|
||||||
let (view, doc) = current!(cx.editor);
|
let (view, doc) = current!(cx.editor);
|
||||||
doc.set_selection(view.id, snapshot.clone());
|
doc.set_selection(view.id, snapshot.clone());
|
||||||
view.offset = offset_snapshot;
|
doc.set_view_offset(view.id, offset_snapshot);
|
||||||
|
|
||||||
if event == PromptEvent::Validate {
|
if event == PromptEvent::Validate {
|
||||||
let callback = async move {
|
let callback = async move {
|
||||||
|
@ -37,9 +37,12 @@
|
|||||||
ChangeSet, Diagnostic, LineEnding, Range, Rope, RopeBuilder, Selection, Syntax, Transaction,
|
ChangeSet, Diagnostic, LineEnding, Range, Rope, RopeBuilder, Selection, Syntax, Transaction,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::editor::Config;
|
use crate::{
|
||||||
use crate::events::{DocumentDidChange, SelectionDidChange};
|
editor::Config,
|
||||||
use crate::{DocumentId, Editor, Theme, View, ViewId};
|
events::{DocumentDidChange, SelectionDidChange},
|
||||||
|
view::ViewPosition,
|
||||||
|
DocumentId, Editor, Theme, View, ViewId,
|
||||||
|
};
|
||||||
|
|
||||||
/// 8kB of buffer space for encoding and decoding `Rope`s.
|
/// 8kB of buffer space for encoding and decoding `Rope`s.
|
||||||
const BUF_SIZE: usize = 8192;
|
const BUF_SIZE: usize = 8192;
|
||||||
@ -130,6 +133,7 @@ pub struct Document {
|
|||||||
pub(crate) id: DocumentId,
|
pub(crate) id: DocumentId,
|
||||||
text: Rope,
|
text: Rope,
|
||||||
selections: HashMap<ViewId, Selection>,
|
selections: HashMap<ViewId, Selection>,
|
||||||
|
view_data: HashMap<ViewId, ViewData>,
|
||||||
|
|
||||||
/// Inlay hints annotations for the document, by view.
|
/// Inlay hints annotations for the document, by view.
|
||||||
///
|
///
|
||||||
@ -267,6 +271,7 @@ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|||||||
.field("selections", &self.selections)
|
.field("selections", &self.selections)
|
||||||
.field("inlay_hints_oudated", &self.inlay_hints_oudated)
|
.field("inlay_hints_oudated", &self.inlay_hints_oudated)
|
||||||
.field("text_annotations", &self.inlay_hints)
|
.field("text_annotations", &self.inlay_hints)
|
||||||
|
.field("view_data", &self.view_data)
|
||||||
.field("path", &self.path)
|
.field("path", &self.path)
|
||||||
.field("encoding", &self.encoding)
|
.field("encoding", &self.encoding)
|
||||||
.field("restore_cursor", &self.restore_cursor)
|
.field("restore_cursor", &self.restore_cursor)
|
||||||
@ -658,6 +663,7 @@ pub fn from(
|
|||||||
selections: HashMap::default(),
|
selections: HashMap::default(),
|
||||||
inlay_hints: HashMap::default(),
|
inlay_hints: HashMap::default(),
|
||||||
inlay_hints_oudated: false,
|
inlay_hints_oudated: false,
|
||||||
|
view_data: Default::default(),
|
||||||
indent_style: DEFAULT_INDENT,
|
indent_style: DEFAULT_INDENT,
|
||||||
line_ending,
|
line_ending,
|
||||||
restore_cursor: false,
|
restore_cursor: false,
|
||||||
@ -1187,12 +1193,14 @@ pub fn reset_selection(&mut self, view_id: ViewId) {
|
|||||||
self.set_selection(view_id, Selection::single(origin.anchor, origin.head));
|
self.set_selection(view_id, Selection::single(origin.anchor, origin.head));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Initializes a new selection for the given view if it does not
|
/// Initializes a new selection and view_data for the given view
|
||||||
/// already have one.
|
/// if it does not already have them.
|
||||||
pub fn ensure_view_init(&mut self, view_id: ViewId) {
|
pub fn ensure_view_init(&mut self, view_id: ViewId) {
|
||||||
if self.selections.get(&view_id).is_none() {
|
if self.selections.get(&view_id).is_none() {
|
||||||
self.reset_selection(view_id);
|
self.reset_selection(view_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.view_data_mut(view_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Mark document as recent used for MRU sorting
|
/// Mark document as recent used for MRU sorting
|
||||||
@ -1238,6 +1246,12 @@ fn apply_impl(
|
|||||||
.ensure_invariants(self.text.slice(..));
|
.ensure_invariants(self.text.slice(..));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for view_data in self.view_data.values_mut() {
|
||||||
|
view_data.view_position.anchor = transaction
|
||||||
|
.changes()
|
||||||
|
.map_pos(view_data.view_position.anchor, Assoc::Before);
|
||||||
|
}
|
||||||
|
|
||||||
// if specified, the current selection should instead be replaced by transaction.selection
|
// if specified, the current selection should instead be replaced by transaction.selection
|
||||||
if let Some(selection) = transaction.selection() {
|
if let Some(selection) = transaction.selection() {
|
||||||
self.selections.insert(
|
self.selections.insert(
|
||||||
@ -1762,6 +1776,28 @@ pub fn selections(&self) -> &HashMap<ViewId, Selection> {
|
|||||||
&self.selections
|
&self.selections
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn view_data(&self, view_id: ViewId) -> &ViewData {
|
||||||
|
self.view_data
|
||||||
|
.get(&view_id)
|
||||||
|
.expect("This should only be called after ensure_view_init")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn view_data_mut(&mut self, view_id: ViewId) -> &mut ViewData {
|
||||||
|
self.view_data.entry(view_id).or_default()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn get_view_offset(&self, view_id: ViewId) -> Option<ViewPosition> {
|
||||||
|
Some(self.view_data.get(&view_id)?.view_position)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn view_offset(&self, view_id: ViewId) -> ViewPosition {
|
||||||
|
self.view_data(view_id).view_position
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_view_offset(&mut self, view_id: ViewId, new_offset: ViewPosition) {
|
||||||
|
self.view_data_mut(view_id).view_position = new_offset;
|
||||||
|
}
|
||||||
|
|
||||||
pub fn relative_path(&self) -> Option<Cow<Path>> {
|
pub fn relative_path(&self) -> Option<Cow<Path>> {
|
||||||
self.path
|
self.path
|
||||||
.as_deref()
|
.as_deref()
|
||||||
@ -2037,6 +2073,11 @@ pub fn reset_all_inlay_hints(&mut self) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Default)]
|
||||||
|
pub struct ViewData {
|
||||||
|
view_position: ViewPosition,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub enum FormatterError {
|
pub enum FormatterError {
|
||||||
SpawningFailed {
|
SpawningFailed {
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
align_view,
|
|
||||||
annotations::diagnostics::{DiagnosticFilter, InlineDiagnosticsConfig},
|
annotations::diagnostics::{DiagnosticFilter, InlineDiagnosticsConfig},
|
||||||
document::{
|
document::{
|
||||||
DocumentOpenError, DocumentSavedEventFuture, DocumentSavedEventResult, Mode, SavePoint,
|
DocumentOpenError, DocumentSavedEventFuture, DocumentSavedEventResult, Mode, SavePoint,
|
||||||
@ -11,8 +10,7 @@
|
|||||||
register::Registers,
|
register::Registers,
|
||||||
theme::{self, Theme},
|
theme::{self, Theme},
|
||||||
tree::{self, Tree},
|
tree::{self, Tree},
|
||||||
view::ViewPosition,
|
Document, DocumentId, View, ViewId,
|
||||||
Align, Document, DocumentId, View, ViewId,
|
|
||||||
};
|
};
|
||||||
use dap::StackFrame;
|
use dap::StackFrame;
|
||||||
use helix_vcs::DiffProviderRegistry;
|
use helix_vcs::DiffProviderRegistry;
|
||||||
@ -1530,16 +1528,17 @@ fn _refresh(&mut self) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn replace_document_in_view(&mut self, current_view: ViewId, doc_id: DocumentId) {
|
fn replace_document_in_view(&mut self, current_view: ViewId, doc_id: DocumentId) {
|
||||||
|
let scrolloff = self.config().scrolloff;
|
||||||
let view = self.tree.get_mut(current_view);
|
let view = self.tree.get_mut(current_view);
|
||||||
view.doc = doc_id;
|
|
||||||
view.offset = ViewPosition::default();
|
|
||||||
|
|
||||||
|
view.doc = doc_id;
|
||||||
let doc = doc_mut!(self, &doc_id);
|
let doc = doc_mut!(self, &doc_id);
|
||||||
|
|
||||||
doc.ensure_view_init(view.id);
|
doc.ensure_view_init(view.id);
|
||||||
view.sync_changes(doc);
|
view.sync_changes(doc);
|
||||||
doc.mark_as_focused();
|
doc.mark_as_focused();
|
||||||
|
|
||||||
align_view(doc, view, Align::Center);
|
view.ensure_cursor_in_view(doc, scrolloff)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn switch(&mut self, id: DocumentId, action: Action) {
|
pub fn switch(&mut self, id: DocumentId, action: Action) {
|
||||||
@ -1899,8 +1898,8 @@ pub fn should_close(&self) -> bool {
|
|||||||
|
|
||||||
pub fn ensure_cursor_in_view(&mut self, id: ViewId) {
|
pub fn ensure_cursor_in_view(&mut self, id: ViewId) {
|
||||||
let config = self.config();
|
let config = self.config();
|
||||||
let view = self.tree.get_mut(id);
|
let view = self.tree.get(id);
|
||||||
let doc = &self.documents[&view.doc];
|
let doc = doc_mut!(self, &view.doc);
|
||||||
view.ensure_cursor_in_view(doc, config.scrolloff)
|
view.ensure_cursor_in_view(doc, config.scrolloff)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -47,11 +47,12 @@ pub enum Align {
|
|||||||
Bottom,
|
Bottom,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn align_view(doc: &Document, view: &mut View, align: Align) {
|
pub fn align_view(doc: &mut Document, view: &View, align: Align) {
|
||||||
let doc_text = doc.text().slice(..);
|
let doc_text = doc.text().slice(..);
|
||||||
let cursor = doc.selection(view.id).primary().cursor(doc_text);
|
let cursor = doc.selection(view.id).primary().cursor(doc_text);
|
||||||
let viewport = view.inner_area(doc);
|
let viewport = view.inner_area(doc);
|
||||||
let last_line_height = viewport.height.saturating_sub(1);
|
let last_line_height = viewport.height.saturating_sub(1);
|
||||||
|
let mut view_offset = doc.view_offset(view.id);
|
||||||
|
|
||||||
let relative = match align {
|
let relative = match align {
|
||||||
Align::Center => last_line_height / 2,
|
Align::Center => last_line_height / 2,
|
||||||
@ -60,15 +61,15 @@ pub fn align_view(doc: &Document, view: &mut View, align: Align) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let text_fmt = doc.text_format(viewport.width, None);
|
let text_fmt = doc.text_format(viewport.width, None);
|
||||||
let annotations = view.text_annotations(doc, None);
|
(view_offset.anchor, view_offset.vertical_offset) = char_idx_at_visual_offset(
|
||||||
(view.offset.anchor, view.offset.vertical_offset) = char_idx_at_visual_offset(
|
|
||||||
doc_text,
|
doc_text,
|
||||||
cursor,
|
cursor,
|
||||||
-(relative as isize),
|
-(relative as isize),
|
||||||
0,
|
0,
|
||||||
&text_fmt,
|
&text_fmt,
|
||||||
&annotations,
|
&view.text_annotations(doc, None),
|
||||||
);
|
);
|
||||||
|
doc.set_view_offset(view.id, view_offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub use document::Document;
|
pub use document::Document;
|
||||||
|
@ -5,7 +5,6 @@
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
clipboard::{get_clipboard_provider, ClipboardProvider, ClipboardType},
|
clipboard::{get_clipboard_provider, ClipboardProvider, ClipboardType},
|
||||||
document::SCRATCH_BUFFER_NAME,
|
|
||||||
Editor,
|
Editor,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -61,14 +60,7 @@ pub fn read<'a>(&'a self, name: char, editor: &'a Editor) -> Option<RegisterValu
|
|||||||
Some(RegisterValues::new(doc.selection(view.id).fragments(text)))
|
Some(RegisterValues::new(doc.selection(view.id).fragments(text)))
|
||||||
}
|
}
|
||||||
'%' => {
|
'%' => {
|
||||||
let doc = doc!(editor);
|
let path = doc!(editor).display_name();
|
||||||
|
|
||||||
let path = doc
|
|
||||||
.path()
|
|
||||||
.as_ref()
|
|
||||||
.map(|p| p.to_string_lossy())
|
|
||||||
.unwrap_or_else(|| SCRATCH_BUFFER_NAME.into());
|
|
||||||
|
|
||||||
Some(RegisterValues::new(iter::once(path)))
|
Some(RegisterValues::new(iter::once(path)))
|
||||||
}
|
}
|
||||||
'*' | '+' => Some(read_from_clipboard(
|
'*' | '+' => Some(read_from_clipboard(
|
||||||
|
@ -128,7 +128,6 @@ pub struct ViewPosition {
|
|||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct View {
|
pub struct View {
|
||||||
pub id: ViewId,
|
pub id: ViewId,
|
||||||
pub offset: ViewPosition,
|
|
||||||
pub area: Rect,
|
pub area: Rect,
|
||||||
pub doc: DocumentId,
|
pub doc: DocumentId,
|
||||||
pub jumps: JumpList,
|
pub jumps: JumpList,
|
||||||
@ -173,11 +172,6 @@ pub fn new(doc: DocumentId, gutters: GutterConfig) -> Self {
|
|||||||
Self {
|
Self {
|
||||||
id: ViewId::default(),
|
id: ViewId::default(),
|
||||||
doc,
|
doc,
|
||||||
offset: ViewPosition {
|
|
||||||
anchor: 0,
|
|
||||||
horizontal_offset: 0,
|
|
||||||
vertical_offset: 0,
|
|
||||||
},
|
|
||||||
area: Rect::default(), // will get calculated upon inserting into tree
|
area: Rect::default(), // will get calculated upon inserting into tree
|
||||||
jumps: JumpList::new((doc, Selection::point(0))), // TODO: use actual sel
|
jumps: JumpList::new((doc, Selection::point(0))), // TODO: use actual sel
|
||||||
docs_access_history: Vec::new(),
|
docs_access_history: Vec::new(),
|
||||||
@ -240,9 +234,10 @@ pub fn offset_coords_to_in_view_center<const CENTERING: bool>(
|
|||||||
doc: &Document,
|
doc: &Document,
|
||||||
scrolloff: usize,
|
scrolloff: usize,
|
||||||
) -> Option<ViewPosition> {
|
) -> Option<ViewPosition> {
|
||||||
|
let view_offset = doc.get_view_offset(self.id)?;
|
||||||
let doc_text = doc.text().slice(..);
|
let doc_text = doc.text().slice(..);
|
||||||
let viewport = self.inner_area(doc);
|
let viewport = self.inner_area(doc);
|
||||||
let vertical_viewport_end = self.offset.vertical_offset + viewport.height as usize;
|
let vertical_viewport_end = view_offset.vertical_offset + viewport.height as usize;
|
||||||
let text_fmt = doc.text_format(viewport.width, None);
|
let text_fmt = doc.text_format(viewport.width, None);
|
||||||
let annotations = self.text_annotations(doc, None);
|
let annotations = self.text_annotations(doc, None);
|
||||||
|
|
||||||
@ -256,7 +251,7 @@ pub fn offset_coords_to_in_view_center<const CENTERING: bool>(
|
|||||||
};
|
};
|
||||||
|
|
||||||
let cursor = doc.selection(self.id).primary().cursor(doc_text);
|
let cursor = doc.selection(self.id).primary().cursor(doc_text);
|
||||||
let mut offset = self.offset;
|
let mut offset = view_offset;
|
||||||
let off = visual_offset_from_anchor(
|
let off = visual_offset_from_anchor(
|
||||||
doc_text,
|
doc_text,
|
||||||
offset.anchor,
|
offset.anchor,
|
||||||
@ -321,22 +316,22 @@ pub fn offset_coords_to_in_view_center<const CENTERING: bool>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// if we are not centering return None if view position is unchanged
|
// if we are not centering return None if view position is unchanged
|
||||||
if !CENTERING && offset == self.offset {
|
if !CENTERING && offset == view_offset {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
Some(offset)
|
Some(offset)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn ensure_cursor_in_view(&mut self, doc: &Document, scrolloff: usize) {
|
pub fn ensure_cursor_in_view(&self, doc: &mut Document, scrolloff: usize) {
|
||||||
if let Some(offset) = self.offset_coords_to_in_view_center::<false>(doc, scrolloff) {
|
if let Some(offset) = self.offset_coords_to_in_view_center::<false>(doc, scrolloff) {
|
||||||
self.offset = offset;
|
doc.set_view_offset(self.id, offset);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn ensure_cursor_in_view_center(&mut self, doc: &Document, scrolloff: usize) {
|
pub fn ensure_cursor_in_view_center(&self, doc: &mut Document, scrolloff: usize) {
|
||||||
if let Some(offset) = self.offset_coords_to_in_view_center::<true>(doc, scrolloff) {
|
if let Some(offset) = self.offset_coords_to_in_view_center::<true>(doc, scrolloff) {
|
||||||
self.offset = offset;
|
doc.set_view_offset(self.id, offset);
|
||||||
} else {
|
} else {
|
||||||
align_view(doc, self, Align::Center);
|
align_view(doc, self, Align::Center);
|
||||||
}
|
}
|
||||||
@ -354,7 +349,7 @@ pub fn is_cursor_in_view(&mut self, doc: &Document, scrolloff: usize) -> bool {
|
|||||||
#[inline]
|
#[inline]
|
||||||
pub fn estimate_last_doc_line(&self, doc: &Document) -> usize {
|
pub fn estimate_last_doc_line(&self, doc: &Document) -> usize {
|
||||||
let doc_text = doc.text().slice(..);
|
let doc_text = doc.text().slice(..);
|
||||||
let line = doc_text.char_to_line(self.offset.anchor.min(doc_text.len_chars()));
|
let line = doc_text.char_to_line(doc.view_offset(self.id).anchor.min(doc_text.len_chars()));
|
||||||
// Saturating subs to make it inclusive zero indexing.
|
// Saturating subs to make it inclusive zero indexing.
|
||||||
(line + self.inner_height())
|
(line + self.inner_height())
|
||||||
.min(doc_text.len_lines())
|
.min(doc_text.len_lines())
|
||||||
@ -368,9 +363,10 @@ pub fn last_visual_line(&self, doc: &Document) -> usize {
|
|||||||
let viewport = self.inner_area(doc);
|
let viewport = self.inner_area(doc);
|
||||||
let text_fmt = doc.text_format(viewport.width, None);
|
let text_fmt = doc.text_format(viewport.width, None);
|
||||||
let annotations = self.text_annotations(doc, None);
|
let annotations = self.text_annotations(doc, None);
|
||||||
|
let view_offset = doc.view_offset(self.id);
|
||||||
|
|
||||||
// last visual line in view is trivial to compute
|
// last visual line in view is trivial to compute
|
||||||
let visual_height = self.offset.vertical_offset + viewport.height as usize;
|
let visual_height = doc.view_offset(self.id).vertical_offset + viewport.height as usize;
|
||||||
|
|
||||||
// fast path when the EOF is not visible on the screen,
|
// fast path when the EOF is not visible on the screen,
|
||||||
if self.estimate_last_doc_line(doc) < doc_text.len_lines() - 1 {
|
if self.estimate_last_doc_line(doc) < doc_text.len_lines() - 1 {
|
||||||
@ -380,7 +376,7 @@ pub fn last_visual_line(&self, doc: &Document) -> usize {
|
|||||||
// translate to document line
|
// translate to document line
|
||||||
let pos = visual_offset_from_anchor(
|
let pos = visual_offset_from_anchor(
|
||||||
doc_text,
|
doc_text,
|
||||||
self.offset.anchor,
|
view_offset.anchor,
|
||||||
usize::MAX,
|
usize::MAX,
|
||||||
&text_fmt,
|
&text_fmt,
|
||||||
&annotations,
|
&annotations,
|
||||||
@ -388,7 +384,7 @@ pub fn last_visual_line(&self, doc: &Document) -> usize {
|
|||||||
);
|
);
|
||||||
|
|
||||||
match pos {
|
match pos {
|
||||||
Ok((Position { row, .. }, _)) => row.saturating_sub(self.offset.vertical_offset),
|
Ok((Position { row, .. }, _)) => row.saturating_sub(view_offset.vertical_offset),
|
||||||
Err(PosAfterMaxRow) => visual_height.saturating_sub(1),
|
Err(PosAfterMaxRow) => visual_height.saturating_sub(1),
|
||||||
Err(PosBeforeAnchorRow) => 0,
|
Err(PosBeforeAnchorRow) => 0,
|
||||||
}
|
}
|
||||||
@ -403,13 +399,15 @@ pub fn screen_coords_at_pos(
|
|||||||
text: RopeSlice,
|
text: RopeSlice,
|
||||||
pos: usize,
|
pos: usize,
|
||||||
) -> Option<Position> {
|
) -> Option<Position> {
|
||||||
|
let view_offset = doc.view_offset(self.id);
|
||||||
|
|
||||||
let viewport = self.inner_area(doc);
|
let viewport = self.inner_area(doc);
|
||||||
let text_fmt = doc.text_format(viewport.width, None);
|
let text_fmt = doc.text_format(viewport.width, None);
|
||||||
let annotations = self.text_annotations(doc, None);
|
let annotations = self.text_annotations(doc, None);
|
||||||
|
|
||||||
let mut pos = visual_offset_from_anchor(
|
let mut pos = visual_offset_from_anchor(
|
||||||
text,
|
text,
|
||||||
self.offset.anchor,
|
view_offset.anchor,
|
||||||
pos,
|
pos,
|
||||||
&text_fmt,
|
&text_fmt,
|
||||||
&annotations,
|
&annotations,
|
||||||
@ -417,14 +415,14 @@ pub fn screen_coords_at_pos(
|
|||||||
)
|
)
|
||||||
.ok()?
|
.ok()?
|
||||||
.0;
|
.0;
|
||||||
if pos.row < self.offset.vertical_offset {
|
if pos.row < view_offset.vertical_offset {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
pos.row -= self.offset.vertical_offset;
|
pos.row -= view_offset.vertical_offset;
|
||||||
if pos.row >= viewport.height as usize {
|
if pos.row >= viewport.height as usize {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
pos.col = pos.col.saturating_sub(self.offset.horizontal_offset);
|
pos.col = pos.col.saturating_sub(view_offset.horizontal_offset);
|
||||||
|
|
||||||
Some(pos)
|
Some(pos)
|
||||||
}
|
}
|
||||||
@ -488,7 +486,7 @@ pub fn text_annotations<'a>(
|
|||||||
doc,
|
doc,
|
||||||
cursor,
|
cursor,
|
||||||
width,
|
width,
|
||||||
self.offset.horizontal_offset,
|
doc.view_offset(self.id).horizontal_offset,
|
||||||
config,
|
config,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
@ -535,13 +533,14 @@ pub fn text_pos_at_visual_coords(
|
|||||||
ignore_virtual_text: bool,
|
ignore_virtual_text: bool,
|
||||||
) -> Option<usize> {
|
) -> Option<usize> {
|
||||||
let text = doc.text().slice(..);
|
let text = doc.text().slice(..);
|
||||||
|
let view_offset = doc.view_offset(self.id);
|
||||||
|
|
||||||
let text_row = row as usize + self.offset.vertical_offset;
|
let text_row = row as usize + view_offset.vertical_offset;
|
||||||
let text_col = column as usize + self.offset.horizontal_offset;
|
let text_col = column as usize + view_offset.horizontal_offset;
|
||||||
|
|
||||||
let (char_idx, virt_lines) = char_idx_at_visual_offset(
|
let (char_idx, virt_lines) = char_idx_at_visual_offset(
|
||||||
text,
|
text,
|
||||||
self.offset.anchor,
|
view_offset.anchor,
|
||||||
text_row as isize,
|
text_row as isize,
|
||||||
text_col,
|
text_col,
|
||||||
&text_fmt,
|
&text_fmt,
|
||||||
@ -689,11 +688,12 @@ fn test_text_pos_at_screen_coords() {
|
|||||||
let mut view = View::new(DocumentId::default(), GutterConfig::default());
|
let mut view = View::new(DocumentId::default(), GutterConfig::default());
|
||||||
view.area = Rect::new(40, 40, 40, 40);
|
view.area = Rect::new(40, 40, 40, 40);
|
||||||
let rope = Rope::from_str("abc\n\tdef");
|
let rope = Rope::from_str("abc\n\tdef");
|
||||||
let doc = Document::from(
|
let mut doc = Document::from(
|
||||||
rope,
|
rope,
|
||||||
None,
|
None,
|
||||||
Arc::new(ArcSwap::new(Arc::new(Config::default()))),
|
Arc::new(ArcSwap::new(Arc::new(Config::default()))),
|
||||||
);
|
);
|
||||||
|
doc.ensure_view_init(view.id);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
view.text_pos_at_screen_coords(
|
view.text_pos_at_screen_coords(
|
||||||
@ -863,11 +863,12 @@ fn test_text_pos_at_screen_coords_without_line_numbers_gutter() {
|
|||||||
);
|
);
|
||||||
view.area = Rect::new(40, 40, 40, 40);
|
view.area = Rect::new(40, 40, 40, 40);
|
||||||
let rope = Rope::from_str("abc\n\tdef");
|
let rope = Rope::from_str("abc\n\tdef");
|
||||||
let doc = Document::from(
|
let mut doc = Document::from(
|
||||||
rope,
|
rope,
|
||||||
None,
|
None,
|
||||||
Arc::new(ArcSwap::new(Arc::new(Config::default()))),
|
Arc::new(ArcSwap::new(Arc::new(Config::default()))),
|
||||||
);
|
);
|
||||||
|
doc.ensure_view_init(view.id);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
view.text_pos_at_screen_coords(
|
view.text_pos_at_screen_coords(
|
||||||
&doc,
|
&doc,
|
||||||
@ -892,11 +893,12 @@ fn test_text_pos_at_screen_coords_without_any_gutters() {
|
|||||||
);
|
);
|
||||||
view.area = Rect::new(40, 40, 40, 40);
|
view.area = Rect::new(40, 40, 40, 40);
|
||||||
let rope = Rope::from_str("abc\n\tdef");
|
let rope = Rope::from_str("abc\n\tdef");
|
||||||
let doc = Document::from(
|
let mut doc = Document::from(
|
||||||
rope,
|
rope,
|
||||||
None,
|
None,
|
||||||
Arc::new(ArcSwap::new(Arc::new(Config::default()))),
|
Arc::new(ArcSwap::new(Arc::new(Config::default()))),
|
||||||
);
|
);
|
||||||
|
doc.ensure_view_init(view.id);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
view.text_pos_at_screen_coords(
|
view.text_pos_at_screen_coords(
|
||||||
&doc,
|
&doc,
|
||||||
@ -915,11 +917,12 @@ fn test_text_pos_at_screen_coords_cjk() {
|
|||||||
let mut view = View::new(DocumentId::default(), GutterConfig::default());
|
let mut view = View::new(DocumentId::default(), GutterConfig::default());
|
||||||
view.area = Rect::new(40, 40, 40, 40);
|
view.area = Rect::new(40, 40, 40, 40);
|
||||||
let rope = Rope::from_str("Hi! こんにちは皆さん");
|
let rope = Rope::from_str("Hi! こんにちは皆さん");
|
||||||
let doc = Document::from(
|
let mut doc = Document::from(
|
||||||
rope,
|
rope,
|
||||||
None,
|
None,
|
||||||
Arc::new(ArcSwap::new(Arc::new(Config::default()))),
|
Arc::new(ArcSwap::new(Arc::new(Config::default()))),
|
||||||
);
|
);
|
||||||
|
doc.ensure_view_init(view.id);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
view.text_pos_at_screen_coords(
|
view.text_pos_at_screen_coords(
|
||||||
@ -998,11 +1001,12 @@ fn test_text_pos_at_screen_coords_graphemes() {
|
|||||||
let mut view = View::new(DocumentId::default(), GutterConfig::default());
|
let mut view = View::new(DocumentId::default(), GutterConfig::default());
|
||||||
view.area = Rect::new(40, 40, 40, 40);
|
view.area = Rect::new(40, 40, 40, 40);
|
||||||
let rope = Rope::from_str("Hèl̀l̀ò world!");
|
let rope = Rope::from_str("Hèl̀l̀ò world!");
|
||||||
let doc = Document::from(
|
let mut doc = Document::from(
|
||||||
rope,
|
rope,
|
||||||
None,
|
None,
|
||||||
Arc::new(ArcSwap::new(Arc::new(Config::default()))),
|
Arc::new(ArcSwap::new(Arc::new(Config::default()))),
|
||||||
);
|
);
|
||||||
|
doc.ensure_view_init(view.id);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
view.text_pos_at_screen_coords(
|
view.text_pos_at_screen_coords(
|
||||||
|
@ -64,12 +64,9 @@ info = "dragonBlue"
|
|||||||
hint = "waveAqua1"
|
hint = "waveAqua1"
|
||||||
|
|
||||||
## Diff
|
## Diff
|
||||||
"diff.plus" = "winterGreen"
|
"diff.plus" = "autumnGreen"
|
||||||
"diff.plus.gutter" = "autumnGreen"
|
"diff.minus" = "autumnRed"
|
||||||
"diff.minus" = "winterRed"
|
"diff.delta" = "autumnYellow"
|
||||||
"diff.minus.gutter" = "autumnRed"
|
|
||||||
"diff.delta" = "winterBlue"
|
|
||||||
"diff.delta.gutter" = "autumnYellow"
|
|
||||||
|
|
||||||
## Syntax highlighting
|
## Syntax highlighting
|
||||||
"attribute" = "waveRed"
|
"attribute" = "waveRed"
|
||||||
|
Loading…
Reference in New Issue
Block a user