merge: epoch-1.10.0 d'upstream

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Lionel DARNIS 2026-10-11 07:00:05 +02:00
commit 79f6777d08
17 changed files with 704 additions and 438 deletions

View file

@ -1,8 +1,10 @@
- [ ] I have disclosed use of any AI generated code in my commit messages. Replace this text with a description of your changes, and why these changes are important. Read and acknowledge the list below. PRs without a completed checkbox will be closed.
- If you are using an LLM, and do not fully understand the changes it is making to the code base, do not create a PR.
- In our experience, AI generated code often results in overly complex code that lacks enough context for a proper fix or feature inclusion. This results in considerably longer code reviews. Due to this, AI authored or partially authored PRs may be closed without comment.
- [ ] I understand these changes in full and will be able to respond to review comments.
- [ ] My change is accurately described in the commit message.
- [ ] My contribution is tested and working as described.
- [ ] I have read the [Developer Certificate of Origin](https://developercertificate.org/) and certify my contribution under its conditions.
---
- I have not included any LLM (also known as AI) generated content in this PR, including code, comments, and descriptions.
- I understand these changes in full and will be able to respond to review comments.
- My change is accurately described in the commit message.
- My contribution is tested and working as described.
- I have read the [Developer Certificate of Origin](https://developercertificate.org/) and certify my contribution under its conditions.
- [ ] I have read and followed all of the above requirements. For more information, see our [contributor guide](https://github.com/pop-os/pop/blob/master/CONTRIBUTING.md).

47
.github/workflows/ci.yml vendored Normal file
View file

@ -0,0 +1,47 @@
name: Continuous integration
on:
push:
branches: [master]
pull_request:
env:
CARGO_TERM_COLOR: always
DEBIAN_FRONTEND: noninteractive
jobs:
fmt:
name: Rustfmt
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v5
- name: install toolchain
run: rustup toolchain install nightly --component rustfmt
- name: fmt
run: cargo +nightly fmt --all --check
clippy:
name: Clippy
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v5
- name: install system dependencies
run: sudo apt-get update && sudo apt-get install -y pkg-config libglib2.0-dev libxkbcommon-dev libwayland-dev
- name: install toolchain
run: rustup show
- uses: Swatinem/rust-cache@v2
- name: clippy
run: cargo clippy --all-features --all-targets -- -D warnings
test:
name: Test
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v5
- name: install system dependencies
run: sudo apt-get update && sudo apt-get install -y pkg-config libglib2.0-dev libxkbcommon-dev libwayland-dev
- name: install toolchain
run: rustup show
- uses: Swatinem/rust-cache@v2
- name: test
run: cargo test --all-features

14
.github/workflows/require-checklist.yml vendored Normal file
View file

@ -0,0 +1,14 @@
# From https://github.com/mheap/require-checklist-action, license MIT
name: Require Checklist
on:
pull_request:
types: [opened, edited, synchronize]
jobs:
job1:
runs-on: ubuntu-latest
steps:
- uses: mheap/require-checklist-action@v2
with:
requireChecklist: true

632
Cargo.lock generated

File diff suppressed because it is too large Load diff

View file

@ -1,6 +1,6 @@
[package] [package]
name = "cosmic-edit" name = "cosmic-edit"
version = "1.9.0" version = "1.10.0"
authors = ["Jeremy Soller <jeremy@system76.com>"] authors = ["Jeremy Soller <jeremy@system76.com>"]
edition = "2024" edition = "2024"
license = "GPL-3.0-only" license = "GPL-3.0-only"

70
cargo.just Normal file
View file

@ -0,0 +1,70 @@
cargo-target-dir := env('CARGO_TARGET_DIR', 'target')
# Compile with debug profile
[no-cd]
build-debug *args:
cargo build {{args}}
# Compile with release profile
[no-cd]
build-release *args: (build-debug '--release' args)
# Compile with a vendored tarball
[no-cd]
build-vendored *args: vendor-extract (build-release '--frozen --offline' args)
# Check for errors and linter warnings
[no-cd]
check *args:
cargo clippy --all-features {{args}} -- -W clippy::pedantic
# Runs a check with JSON message format for IDE integration
[no-cd]
check-json: (check '--message-format=json')
# Remove Cargo build artifacts
[no-cd]
clean:
cargo clean
# Also remove .cargo and vendored dependencies
[no-cd]
clean-dist: clean
rm -rf .cargo vendor vendor.tar target
# Run the application for testing purposes
[no-cd]
run *args:
cargo run {{args}}
# Run `cargo test`
[no-cd]
test *args:
cargo test {{args}}
# Vendor Cargo dependencies locally
[no-cd]
vendor:
#!/usr/bin/env bash
mkdir -p .cargo
cargo vendor --locked | head -n -1 > .cargo/config.toml
echo 'directory = "vendor"' >> .cargo/config.toml
echo >> .cargo/config.toml
echo '[env]' >> .cargo/config.toml
if [ -n "${SOURCE_DATE_EPOCH}" ]
then
source_date="$(date -d "@${SOURCE_DATE_EPOCH}" "+%Y-%m-%d")"
echo "VERGEN_GIT_COMMIT_DATE = \"${source_date}\"" >> .cargo/config.toml
fi
if [ -n "${SOURCE_GIT_HASH}" ]
then
echo "VERGEN_GIT_SHA = \"${SOURCE_GIT_HASH}\"" >> .cargo/config.toml
fi
tar pcf vendor.tar .cargo vendor
rm -rf .cargo vendor
# Extracts vendored dependencies
[no-cd]
vendor-extract:
rm -rf vendor
tar pxf vendor.tar

6
debian/changelog vendored
View file

@ -1,3 +1,9 @@
cosmic-edit (1.10.0) noble; urgency=medium
* Epoch 1.10.0 version update
-- Michael Murphy <michael@mmurphy.dev> Mon, 05 Oct 2026 21:19:33 +0200
cosmic-edit (1.9.0) noble; urgency=medium cosmic-edit (1.9.0) noble; urgency=medium
* Epoch 1.9.0 version update * Epoch 1.9.0 version update

View file

@ -53,7 +53,7 @@ dark = Sötét
light = Világos light = Világos
syntax-dark = Sötét szintaxis syntax-dark = Sötét szintaxis
syntax-light = Világos szintaxis syntax-light = Világos szintaxis
default-font = Alapértelmezett betűtípus default-font = Alapértelmezett betűkészlet
default-font-size = Alapértelmezett betűméret default-font-size = Alapértelmezett betűméret
default-zoom-step = Nagyítási mérték default-zoom-step = Nagyítási mérték

3
rust-toolchain.toml Normal file
View file

@ -0,0 +1,3 @@
[toolchain]
channel = "1.93"
components = ["clippy", "rustfmt"]

View file

@ -112,17 +112,8 @@ impl Config {
} }
} }
#[derive(Clone, CosmicConfigEntry, Debug, Deserialize, Eq, PartialEq, Serialize)] #[derive(Clone, CosmicConfigEntry, Debug, Deserialize, Eq, PartialEq, Serialize, Default)]
pub struct ConfigState { pub struct ConfigState {
pub recent_files: VecDeque<PathBuf>, pub recent_files: VecDeque<PathBuf>,
pub recent_projects: VecDeque<PathBuf>, pub recent_projects: VecDeque<PathBuf>,
} }
impl Default for ConfigState {
fn default() -> Self {
Self {
recent_files: VecDeque::new(),
recent_projects: VecDeque::new(),
}
}
}

View file

@ -124,7 +124,7 @@ impl GitRepository {
msg.push_str("\nstderr> "); msg.push_str("\nstderr> ");
msg.push_str(line); msg.push_str(line);
} }
Err(io::Error::new(io::ErrorKind::Other, msg)) Err(io::Error::other(msg))
} }
} }

View file

@ -328,6 +328,7 @@ impl PartialEq for WatcherWrapper {
} }
} }
#[allow(clippy::large_enum_variant)]
enum NewTab { enum NewTab {
Tab(EditorTab), Tab(EditorTab),
Exists(Entity), Exists(Entity),
@ -529,7 +530,7 @@ impl App {
continue; continue;
} }
let entry_path = entry.path(); let entry_path = entry.path();
let node = match ProjectNode::new(&entry_path) { let node = match ProjectNode::new(entry_path) {
Ok(ok) => ok, Ok(ok) => ok,
Err(err) => { Err(err) => {
log::error!( log::error!(
@ -682,13 +683,13 @@ impl App {
//TODO: allow files to be open multiple times //TODO: allow files to be open multiple times
let mut activate_opt = None; let mut activate_opt = None;
for entity in self.tab_model.iter() { for entity in self.tab_model.iter() {
if let Some(Tab::Editor(tab)) = self.tab_model.data::<Tab>(entity) { if let Some(Tab::Editor(tab)) = self.tab_model.data::<Tab>(entity)
if tab.path_opt.as_ref() == Some(&canonical) { && tab.path_opt.as_ref() == Some(&canonical)
{
activate_opt = Some(entity); activate_opt = Some(entity);
break; break;
} }
} }
}
if let Some(entity) = activate_opt { if let Some(entity) = activate_opt {
self.tab_model.activate(entity); self.tab_model.activate(entity);
return Some(NewTab::Exists(entity)); return Some(NewTab::Exists(entity));
@ -734,13 +735,13 @@ impl App {
} }
let entities: Vec<_> = self.tab_model.iter().collect(); let entities: Vec<_> = self.tab_model.iter().collect();
for entity in entities { for entity in entities {
if self.tab_model.is_active(entity) { if self.tab_model.is_active(entity)
if let Some(Tab::Editor(tab)) = self.tab_model.data_mut::<Tab>(entity) { && let Some(Tab::Editor(tab)) = self.tab_model.data_mut::<Tab>(entity)
{
tab.set_config(&self.config); tab.set_config(&self.config);
} }
} }
} }
}
Task::none() Task::none()
} }
@ -754,12 +755,12 @@ impl App {
} }
fn save_config_state(&mut self) { fn save_config_state(&mut self) {
if let Some(ref config_state_handler) = self.config_state_handler { if let Some(ref config_state_handler) = self.config_state_handler
if let Err(err) = self.config_state.write_entry(config_state_handler) { && let Err(err) = self.config_state.write_entry(config_state_handler)
{
log::error!("failed to save config_state: {}", err); log::error!("failed to save config_state: {}", err);
} }
} }
}
fn update_dialogs(&mut self) -> Task<Message> { fn update_dialogs(&mut self) -> Task<Message> {
match self.dialog_page_opt { match self.dialog_page_opt {
@ -777,12 +778,12 @@ impl App {
Some(DialogPage::PromptSaveQuit(ref _entities)) => { Some(DialogPage::PromptSaveQuit(ref _entities)) => {
let mut unsaved = Vec::new(); let mut unsaved = Vec::new();
for entity in self.tab_model.iter() { for entity in self.tab_model.iter() {
if let Some(Tab::Editor(tab)) = self.tab_model.data::<Tab>(entity) { if let Some(Tab::Editor(tab)) = self.tab_model.data::<Tab>(entity)
if tab.changed() { && tab.changed()
{
unsaved.push(entity); unsaved.push(entity);
} }
} }
}
if unsaved.is_empty() { if unsaved.is_empty() {
// All tabs have been saved, we can exit // All tabs have been saved, we can exit
return self.update(Message::QuitForce); return self.update(Message::QuitForce);
@ -919,10 +920,11 @@ impl App {
} }
'tabs: for entity in self.tab_model.iter() { 'tabs: for entity in self.tab_model.iter() {
if let Some(Tab::Editor(tab)) = self.tab_model.data::<Tab>(entity) { if let Some(Tab::Editor(tab)) = self.tab_model.data::<Tab>(entity)
if let Some(path) = &tab.path_opt { && let Some(path) = &tab.path_opt
{
for (_, project_path) in self.projects.iter() { for (_, project_path) in self.projects.iter() {
if path.starts_with(&project_path) { if path.starts_with(project_path) {
// Do not watch tabs inside of already watched projects // Do not watch tabs inside of already watched projects
continue 'tabs; continue 'tabs;
} }
@ -930,7 +932,6 @@ impl App {
new_paths.insert((path.to_path_buf(), RecursiveMode::NonRecursive)); new_paths.insert((path.to_path_buf(), RecursiveMode::NonRecursive));
} }
} }
}
// Unwatch paths no longer used // Unwatch paths no longer used
for path_mode in old_paths.iter() { for path_mode in old_paths.iter() {
@ -1443,7 +1444,7 @@ impl Application for App {
let mut theme_names = let mut theme_names =
Vec::with_capacity(SYNTAX_SYSTEM.get().unwrap().theme_set.themes.len()); Vec::with_capacity(SYNTAX_SYSTEM.get().unwrap().theme_set.themes.len());
for (theme_name, _theme) in SYNTAX_SYSTEM.get().unwrap().theme_set.themes.iter() { for theme_name in SYNTAX_SYSTEM.get().unwrap().theme_set.themes.keys() {
theme_names.push(theme_name.to_string()); theme_names.push(theme_name.to_string());
} }
@ -1462,7 +1463,7 @@ impl Application for App {
.comments(fl!("comment")) .comments(fl!("comment"))
.license("GPL-3.0-only") .license("GPL-3.0-only")
.license_url("https://spdx.org/licenses/GPL-3.0-only") .license_url("https://spdx.org/licenses/GPL-3.0-only")
.developers([("Jeremy Soller", "jeremy@system76.com")]) .developers([("System76", "info@system76.com")])
.links([ .links([
(fl!("repository"), "https://github.com/pop-os/cosmic-edit"), (fl!("repository"), "https://github.com/pop-os/cosmic-edit"),
( (
@ -1652,9 +1653,7 @@ impl Application for App {
} }
fn dialog(&self) -> Option<Element<'_, Self::Message>> { fn dialog(&self) -> Option<Element<'_, Self::Message>> {
let Some(ref dialog) = self.dialog_page_opt else { let dialog = self.dialog_page_opt.as_ref()?;
return None;
};
let cosmic_theme::Spacing { space_xxs, .. } = self.core().system_theme().cosmic().spacing; let cosmic_theme::Spacing { space_xxs, .. } = self.core().system_theme().cosmic().spacing;
@ -1937,8 +1936,9 @@ impl Application for App {
return self.update_config(); return self.update_config();
} }
Message::FindNext => { Message::FindNext => {
if !self.find_search_value.is_empty() { if !self.find_search_value.is_empty()
if let Some(Tab::Editor(tab)) = self.active_tab() { && let Some(Tab::Editor(tab)) = self.active_tab()
{
//TODO: do not compile find regex on every search? //TODO: do not compile find regex on every search?
match self.config.find_regex(&self.find_search_value) { match self.config.find_regex(&self.find_search_value) {
Ok(regex) => { Ok(regex) => {
@ -1954,14 +1954,14 @@ impl Application for App {
} }
} }
} }
}
// Focus correct input // Focus correct input
return self.update_focus(); return self.update_focus();
} }
Message::FindPrevious => { Message::FindPrevious => {
if !self.find_search_value.is_empty() { if !self.find_search_value.is_empty()
if let Some(Tab::Editor(tab)) = self.active_tab() { && let Some(Tab::Editor(tab)) = self.active_tab()
{
//TODO: do not compile find regex on every search? //TODO: do not compile find regex on every search?
match self.config.find_regex(&self.find_search_value) { match self.config.find_regex(&self.find_search_value) {
Ok(regex) => { Ok(regex) => {
@ -1977,14 +1977,14 @@ impl Application for App {
} }
} }
} }
}
// Focus correct input // Focus correct input
return self.update_focus(); return self.update_focus();
} }
Message::FindReplace => { Message::FindReplace => {
if !self.find_search_value.is_empty() { if !self.find_search_value.is_empty()
if let Some(Tab::Editor(tab)) = self.active_tab() { && let Some(Tab::Editor(tab)) = self.active_tab()
{
//TODO: do not compile find regex on every search? //TODO: do not compile find regex on every search?
match self.config.find_regex(&self.find_search_value) { match self.config.find_regex(&self.find_search_value) {
Ok(regex) => { Ok(regex) => {
@ -2006,14 +2006,14 @@ impl Application for App {
} }
} }
} }
}
// Focus correct input // Focus correct input
return self.update_focus(); return self.update_focus();
} }
Message::FindReplaceAll => { Message::FindReplaceAll => {
if !self.find_search_value.is_empty() { if !self.find_search_value.is_empty()
if let Some(Tab::Editor(tab)) = self.active_tab() { && let Some(Tab::Editor(tab)) = self.active_tab()
{
//TODO: do not compile find regex on every search? //TODO: do not compile find regex on every search?
match self.config.find_regex(&self.find_search_value) { match self.config.find_regex(&self.find_search_value) {
Ok(regex) => { Ok(regex) => {
@ -2035,7 +2035,6 @@ impl Application for App {
} }
} }
} }
}
// Focus correct input // Focus correct input
return self.update_focus(); return self.update_focus();
@ -2172,21 +2171,17 @@ impl Application for App {
// Reload tabs that changed // Reload tabs that changed
let mut tab_reload = Vec::new(); let mut tab_reload = Vec::new();
for entity in self.tab_model.iter() { for entity in self.tab_model.iter() {
if let Some(Tab::Editor(tab)) = self.tab_model.data::<Tab>(entity) { if let Some(Tab::Editor(tab)) = self.tab_model.data::<Tab>(entity)
if let Some(path) = &tab.path_opt { && let Some(path) = &tab.path_opt
if event.paths.contains(path) { && event.paths.contains(path)
{
if tab.changed() { if tab.changed() {
log::warn!( log::warn!("file changed externally before being saved: {:?}", path);
"file changed externally before being saved: {:?}",
path
);
} else { } else {
tab_reload.push(entity); tab_reload.push(entity);
} }
} }
} }
}
}
for entity in tab_reload { for entity in tab_reload {
match self.tab_model.data_mut::<Tab>(entity) { match self.tab_model.data_mut::<Tab>(entity) {
Some(Tab::Editor(tab)) => { Some(Tab::Editor(tab)) => {
@ -2281,7 +2276,7 @@ impl Application for App {
{ {
for (_, project_path) in self.projects.iter() { for (_, project_path) in self.projects.iter() {
for path in event.paths.iter() { for path in event.paths.iter() {
if let Ok(prefix) = path.strip_prefix(&project_path) { if let Ok(prefix) = path.strip_prefix(project_path) {
// Manually ignore project .git folders // Manually ignore project .git folders
//TODO: use logic from ignore crate somehow? //TODO: use logic from ignore crate somehow?
if prefix.starts_with(".git") { if prefix.starts_with(".git") {
@ -2351,12 +2346,12 @@ impl Application for App {
{ {
let mut close = Vec::new(); let mut close = Vec::new();
for entity in self.tab_model.iter() { for entity in self.tab_model.iter() {
if let Some(Tab::GitDiff(other_tab)) = self.tab_model.data::<Tab>(entity) { if let Some(Tab::GitDiff(other_tab)) = self.tab_model.data::<Tab>(entity)
if other_tab.diff.path == diff.path { && other_tab.diff.path == diff.path
{
close.push(entity); close.push(entity);
} }
} }
}
for entity in close { for entity in close {
self.tab_model.remove(entity); self.tab_model.remove(entity);
} }
@ -2672,12 +2667,12 @@ impl Application for App {
tab.path_opt = Some(paths.remove(0)); tab.path_opt = Some(paths.remove(0));
title_opt = Some(tab.title()); title_opt = Some(tab.title());
tab.save(); tab.save();
if let Some(path) = tab.path_opt.clone() { if let Some(path) = tab.path_opt.clone()
if let Ok(canonical) = fs::canonicalize(&path) { && let Ok(canonical) = fs::canonicalize(&path)
{
self.add_to_recents(&canonical); self.add_to_recents(&canonical);
} }
} }
}
if let Some(title) = title_opt { if let Some(title) = title_opt {
self.tab_model.text_set(entity, title); self.tab_model.text_set(entity, title);
} }
@ -2851,13 +2846,7 @@ impl Application for App {
.tab_model .tab_model
.position(self.tab_model.active()) .position(self.tab_model.active())
.and_then(|i| (i as usize).checked_sub(1)) .and_then(|i| (i as usize).checked_sub(1))
.unwrap_or_else(|| { .unwrap_or_else(|| self.tab_model.iter().count().saturating_sub(1));
self.tab_model
.iter()
.count()
.checked_sub(1)
.unwrap_or_default()
});
let entity = self.tab_model.iter().nth(pos); let entity = self.tab_model.iter().nth(pos);
if let Some(entity) = entity { if let Some(entity) = entity {

View file

@ -12,7 +12,11 @@ use cosmic::{
responsive_menu_bar, segmented_button, responsive_menu_bar, segmented_button,
}, },
}; };
use std::{collections::HashMap, path::PathBuf, sync::LazyLock}; use std::{
collections::HashMap,
path::{Path, PathBuf},
sync::LazyLock,
};
use crate::{Action, Config, ConfigState, Message, fl}; use crate::{Action, Config, ConfigState, Message, fl};
@ -60,7 +64,7 @@ fn truncate_middle(value: &str, max_chars: usize) -> String {
) )
} }
fn format_recent_menu_path(path: &PathBuf, home_dir_opt: Option<&PathBuf>) -> String { fn format_recent_menu_path(path: &Path, home_dir_opt: Option<&PathBuf>) -> String {
const ELLIPSIS: &str = "..."; const ELLIPSIS: &str = "...";
let display = if let Some(home_dir) = home_dir_opt { let display = if let Some(home_dir) = home_dir_opt {
@ -163,7 +167,7 @@ pub fn menu_bar<'a>(
config: &Config, config: &Config,
config_state: &ConfigState, config_state: &ConfigState,
key_binds: &HashMap<KeyBind, Action>, key_binds: &HashMap<KeyBind, Action>,
projects: &Vec<(String, PathBuf)>, projects: &[(String, PathBuf)],
has_selection: bool, has_selection: bool,
) -> Element<'a, Message> { ) -> Element<'a, Message> {
//TODO: port to libcosmic //TODO: port to libcosmic

View file

@ -29,15 +29,15 @@ impl ProjectNode {
let path = fs::canonicalize(path)?; let path = fs::canonicalize(path)?;
let name = path let name = path
.file_name() .file_name()
.ok_or(io::Error::new( .ok_or(io::Error::other(format!(
io::ErrorKind::Other, "path {:?} has no file name",
format!("path {:?} has no file name", path), path
))? )))?
.to_str() .to_str()
.ok_or(io::Error::new( .ok_or(io::Error::other(format!(
io::ErrorKind::Other, "path {:?} is not valid UTF-8",
format!("path {:?} is not valid UTF-8", path), path
))? )))?
.to_string(); .to_string();
Ok(if path.is_dir() { Ok(if path.is_dir() {
Self::Folder { Self::Folder {

View file

@ -54,11 +54,11 @@ impl ProjectSearchResult {
} }
}; };
if let Some(file_type) = entry.file_type() { if let Some(file_type) = entry.file_type()
if file_type.is_dir() { && file_type.is_dir()
{
continue; continue;
} }
}
let entry_path = entry.path(); let entry_path = entry.path();

View file

@ -25,6 +25,7 @@ fn editor_text(editor: &ViEditor<'static, 'static>) -> String {
}) })
} }
#[allow(clippy::large_enum_variant)]
pub enum Tab { pub enum Tab {
Editor(EditorTab), Editor(EditorTab),
GitDiff(GitDiffTab), GitDiff(GitDiffTab),
@ -313,7 +314,7 @@ impl EditorTab {
.filter_map(|m| { .filter_map(|m| {
if cursor.line != start_line if cursor.line != start_line
|| m.start() >= cursor.index || m.start() >= cursor.index
|| m.start() < cursor.index && wrapped == true || m.start() < cursor.index && wrapped
{ {
Some((m.start(), m.len())) Some((m.start(), m.len()))
} else { } else {
@ -390,7 +391,7 @@ impl EditorTab {
if cursor.line != start_line if cursor.line != start_line
|| m.start() > cursor.index || m.start() > cursor.index
|| m.start() == cursor.index && current_selection == Selection::None || m.start() == cursor.index && current_selection == Selection::None
|| m.start() < cursor.index && wrapped == true || m.start() < cursor.index && wrapped
{ {
Some((m.start(), m.end())) Some((m.start(), m.end()))
} else { } else {
@ -433,7 +434,7 @@ impl EditorTab {
if cursor.line != start_line if cursor.line != start_line
|| m.start() < cursor.index || m.start() < cursor.index
|| m.start() == cursor.index && current_selection == Selection::None || m.start() == cursor.index && current_selection == Selection::None
|| m.start() > cursor.index && wrapped == true || m.start() > cursor.index && wrapped
{ {
Some((m.start(), m.end())) Some((m.start(), m.end()))
} else { } else {

View file

@ -317,9 +317,8 @@ where
editor.with_buffer(|buffer| { editor.with_buffer(|buffer| {
let mut layout_lines = 0; let mut layout_lines = 0;
for line in buffer.lines.iter() { for line in buffer.lines.iter() {
match line.layout_opt() { if let Some(layout) = line.layout_opt() {
Some(layout) => layout_lines += layout.len(), layout_lines += layout.len()
None => (),
} }
} }
@ -755,7 +754,7 @@ where
match editor.buffer_ref() { match editor.buffer_ref() {
cosmic_text::BufferRef::Arc(buffer) => { cosmic_text::BufferRef::Arc(buffer) => {
renderer.fill_raw(Raw { renderer.fill_raw(Raw {
buffer: Arc::downgrade(&buffer), buffer: Arc::downgrade(buffer),
position: pos, position: pos,
color: Color::from_rgba(1.0, 1.0, 1.0, 1.0), color: Color::from_rgba(1.0, 1.0, 1.0, 1.0),
clip_bounds, clip_bounds,
@ -1095,8 +1094,7 @@ where
editor.set_redraw(true); editor.set_redraw(true);
shell.capture_event(); shell.capture_event();
} }
Named::Tab => { Named::Tab if !modifiers.control() && !modifiers.alt() => {
if !modifiers.control() && !modifiers.alt() {
if modifiers.shift() { if modifiers.shift() {
editor.action(Action::Unindent); editor.action(Action::Unindent);
} else { } else {
@ -1104,7 +1102,6 @@ where
} }
shell.capture_event(); shell.capture_event();
} }
}
_ => (), _ => (),
}, },
Event::Keyboard(KeyEvent::KeyPressed { text, .. }) if state.is_focused => { Event::Keyboard(KeyEvent::KeyPressed { text, .. }) if state.is_focused => {
@ -1152,7 +1149,7 @@ where
InputMethodEvent::Commit(text) => { InputMethodEvent::Commit(text) => {
if state.is_focused { if state.is_focused {
editor.start_change(); editor.start_change();
editor.insert_string(&text, None); editor.insert_string(text, None);
editor.finish_change(); editor.finish_change();
shell.capture_event(); shell.capture_event();
} }
@ -1174,14 +1171,14 @@ where
let y = y_logical * scale_factor; let y = y_logical * scale_factor;
// Do this first as the horizontal scrollbar is on top of the buffer // Do this first as the horizontal scrollbar is on top of the buffer
if let Some(scrollbar_h_rect) = state.scrollbar_h_rect.get() { if let Some(scrollbar_h_rect) = state.scrollbar_h_rect.get()
if scrollbar_h_rect.contains(Point::new(x_logical, y_logical)) { && scrollbar_h_rect.contains(Point::new(x_logical, y_logical))
{
state.dragging = Some(Dragging::ScrollbarH { state.dragging = Some(Dragging::ScrollbarH {
start_x: x, start_x: x,
start_scroll: editor.with_buffer(|buffer| buffer.scroll()), start_scroll: editor.with_buffer(|buffer| buffer.scroll()),
}); });
} }
}
if matches!(state.dragging, Some(Dragging::ScrollbarH { .. })) { if matches!(state.dragging, Some(Dragging::ScrollbarH { .. })) {
// The horizontal scrollbar is on top of the buffer, // The horizontal scrollbar is on top of the buffer,
@ -1207,7 +1204,7 @@ where
ClickKind::Single ClickKind::Single
}; };
let maybe_anchor = if state.modifiers.shift() { let maybe_anchor = if state.modifiers.shift() {
state.shift_anchor.lock().unwrap().clone() *state.shift_anchor.lock().unwrap()
} else { } else {
None None
}; };
@ -1388,12 +1385,10 @@ where
shell.capture_event(); shell.capture_event();
} }
} }
Event::Window(WindowEvent::RedrawRequested(_now)) => { Event::Window(WindowEvent::RedrawRequested(_now)) if state.is_focused => {
if state.is_focused {
state.caret_position = editor.cursor_position().unwrap_or(state.caret_position); state.caret_position = editor.cursor_position().unwrap_or(state.caret_position);
shell.request_input_method(&self.input_method(state, scale_factor, layout)); shell.request_input_method(&self.input_method(state, scale_factor, layout));
} }
}
_ => (), _ => (),
} }