Move multi_buffer to editor crate
This commit is contained in:
parent
6caf016df9
commit
c8b43e3078
16 changed files with 44 additions and 47 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -1527,6 +1527,7 @@ dependencies = [
|
||||||
"aho-corasick",
|
"aho-corasick",
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"clock",
|
"clock",
|
||||||
|
"collections",
|
||||||
"ctor",
|
"ctor",
|
||||||
"env_logger",
|
"env_logger",
|
||||||
"gpui",
|
"gpui",
|
||||||
|
|
|
@ -8,6 +8,7 @@ path = "src/editor.rs"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
test-support = [
|
test-support = [
|
||||||
|
"rand",
|
||||||
"text/test-support",
|
"text/test-support",
|
||||||
"language/test-support",
|
"language/test-support",
|
||||||
"gpui/test-support",
|
"gpui/test-support",
|
||||||
|
@ -17,6 +18,7 @@ test-support = [
|
||||||
[dependencies]
|
[dependencies]
|
||||||
text = { path = "../text" }
|
text = { path = "../text" }
|
||||||
clock = { path = "../clock" }
|
clock = { path = "../clock" }
|
||||||
|
collections = { path = "../collections" }
|
||||||
gpui = { path = "../gpui" }
|
gpui = { path = "../gpui" }
|
||||||
language = { path = "../language" }
|
language = { path = "../language" }
|
||||||
project = { path = "../project" }
|
project = { path = "../project" }
|
||||||
|
@ -30,6 +32,7 @@ lazy_static = "1.4"
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
parking_lot = "0.11"
|
parking_lot = "0.11"
|
||||||
postage = { version = "0.4", features = ["futures-traits"] }
|
postage = { version = "0.4", features = ["futures-traits"] }
|
||||||
|
rand = { version = "0.8.3", optional = true }
|
||||||
serde = { version = "1", features = ["derive", "rc"] }
|
serde = { version = "1", features = ["derive", "rc"] }
|
||||||
smallvec = { version = "1.6", features = ["union"] }
|
smallvec = { version = "1.6", features = ["union"] }
|
||||||
smol = "1.2"
|
smol = "1.2"
|
||||||
|
|
|
@ -3,13 +3,11 @@ mod fold_map;
|
||||||
mod tab_map;
|
mod tab_map;
|
||||||
mod wrap_map;
|
mod wrap_map;
|
||||||
|
|
||||||
|
use crate::{Anchor, MultiBuffer, MultiBufferSnapshot, ToOffset, ToPoint};
|
||||||
use block_map::{BlockMap, BlockPoint};
|
use block_map::{BlockMap, BlockPoint};
|
||||||
use fold_map::{FoldMap, ToFoldPoint as _};
|
use fold_map::{FoldMap, ToFoldPoint as _};
|
||||||
use gpui::{fonts::FontId, ElementBox, Entity, ModelContext, ModelHandle};
|
use gpui::{fonts::FontId, ElementBox, Entity, ModelContext, ModelHandle};
|
||||||
use language::{
|
use language::{Point, Subscription as BufferSubscription};
|
||||||
multi_buffer::{Anchor, MultiBuffer, MultiBufferSnapshot, ToOffset, ToPoint},
|
|
||||||
Point, Subscription as BufferSubscription,
|
|
||||||
};
|
|
||||||
use std::{
|
use std::{
|
||||||
collections::{HashMap, HashSet},
|
collections::{HashMap, HashSet},
|
||||||
ops::Range,
|
ops::Range,
|
||||||
|
|
|
@ -1,9 +1,7 @@
|
||||||
use super::wrap_map::{self, WrapEdit, WrapPoint, WrapSnapshot};
|
use super::wrap_map::{self, WrapEdit, WrapPoint, WrapSnapshot};
|
||||||
|
use crate::{Anchor, ToOffset, ToPoint as _};
|
||||||
use gpui::{AppContext, ElementBox};
|
use gpui::{AppContext, ElementBox};
|
||||||
use language::{
|
use language::Chunk;
|
||||||
multi_buffer::{Anchor, ToOffset, ToPoint as _},
|
|
||||||
Chunk,
|
|
||||||
};
|
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
use std::{
|
use std::{
|
||||||
cmp::{self, Ordering},
|
cmp::{self, Ordering},
|
||||||
|
@ -866,8 +864,8 @@ fn offset_for_row(s: &str, target: u32) -> (u32, usize) {
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::display_map::{fold_map::FoldMap, tab_map::TabMap, wrap_map::WrapMap};
|
use crate::display_map::{fold_map::FoldMap, tab_map::TabMap, wrap_map::WrapMap};
|
||||||
|
use crate::multi_buffer::MultiBuffer;
|
||||||
use gpui::{elements::Empty, Element};
|
use gpui::{elements::Empty, Element};
|
||||||
use language::multi_buffer::MultiBuffer;
|
|
||||||
use rand::prelude::*;
|
use rand::prelude::*;
|
||||||
use std::env;
|
use std::env;
|
||||||
use text::RandomCharIter;
|
use text::RandomCharIter;
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
use language::{
|
use crate::{Anchor, AnchorRangeExt, MultiBufferChunks, MultiBufferSnapshot, ToOffset};
|
||||||
multi_buffer::{Anchor, AnchorRangeExt, MultiBufferChunks, MultiBufferSnapshot, ToOffset},
|
use language::{Chunk, Edit, Point, PointUtf16, TextSummary};
|
||||||
Chunk, Edit, Point, PointUtf16, TextSummary,
|
|
||||||
};
|
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
use std::{
|
use std::{
|
||||||
cmp::{self, Ordering},
|
cmp::{self, Ordering},
|
||||||
|
@ -1057,8 +1055,7 @@ pub type FoldEdit = Edit<FoldOffset>;
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::ToPoint;
|
use crate::{MultiBuffer, ToPoint};
|
||||||
use language::multi_buffer::MultiBuffer;
|
|
||||||
use rand::prelude::*;
|
use rand::prelude::*;
|
||||||
use std::{env, mem};
|
use std::{env, mem};
|
||||||
use text::RandomCharIter;
|
use text::RandomCharIter;
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
use super::fold_map::{self, FoldEdit, FoldPoint, FoldSnapshot, ToFoldPoint};
|
use super::fold_map::{self, FoldEdit, FoldPoint, FoldSnapshot, ToFoldPoint};
|
||||||
use language::{multi_buffer::MultiBufferSnapshot, rope, Chunk};
|
use crate::MultiBufferSnapshot;
|
||||||
|
use language::{rope, Chunk};
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
use std::{cmp, mem, ops::Range};
|
use std::{cmp, mem, ops::Range};
|
||||||
use sum_tree::Bias;
|
use sum_tree::Bias;
|
||||||
|
@ -438,8 +439,7 @@ impl<'a> Iterator for TabChunks<'a> {
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::display_map::fold_map::FoldMap;
|
use crate::{display_map::fold_map::FoldMap, MultiBuffer};
|
||||||
use language::multi_buffer::MultiBuffer;
|
|
||||||
use rand::{prelude::StdRng, Rng};
|
use rand::{prelude::StdRng, Rng};
|
||||||
use text::{RandomCharIter, Rope};
|
use text::{RandomCharIter, Rope};
|
||||||
|
|
||||||
|
|
|
@ -2,11 +2,12 @@ use super::{
|
||||||
fold_map,
|
fold_map,
|
||||||
tab_map::{self, TabEdit, TabPoint, TabSnapshot},
|
tab_map::{self, TabEdit, TabPoint, TabSnapshot},
|
||||||
};
|
};
|
||||||
|
use crate::{MultiBufferSnapshot, Point};
|
||||||
use gpui::{
|
use gpui::{
|
||||||
fonts::FontId, text_layout::LineWrapper, Entity, ModelContext, ModelHandle, MutableAppContext,
|
fonts::FontId, text_layout::LineWrapper, Entity, ModelContext, ModelHandle, MutableAppContext,
|
||||||
Task,
|
Task,
|
||||||
};
|
};
|
||||||
use language::{multi_buffer::MultiBufferSnapshot, Chunk, Point};
|
use language::Chunk;
|
||||||
use lazy_static::lazy_static;
|
use lazy_static::lazy_static;
|
||||||
use smol::future::yield_now;
|
use smol::future::yield_now;
|
||||||
use std::{collections::VecDeque, mem, ops::Range, time::Duration};
|
use std::{collections::VecDeque, mem, ops::Range, time::Duration};
|
||||||
|
@ -977,8 +978,9 @@ mod tests {
|
||||||
use crate::{
|
use crate::{
|
||||||
display_map::{fold_map::FoldMap, tab_map::TabMap},
|
display_map::{fold_map::FoldMap, tab_map::TabMap},
|
||||||
test::Observer,
|
test::Observer,
|
||||||
|
MultiBuffer,
|
||||||
};
|
};
|
||||||
use language::{multi_buffer::MultiBuffer, RandomCharIter};
|
use language::RandomCharIter;
|
||||||
use rand::prelude::*;
|
use rand::prelude::*;
|
||||||
use std::{cmp, env};
|
use std::{cmp, env};
|
||||||
use text::Rope;
|
use text::Rope;
|
||||||
|
|
|
@ -2,6 +2,7 @@ pub mod display_map;
|
||||||
mod element;
|
mod element;
|
||||||
pub mod items;
|
pub mod items;
|
||||||
pub mod movement;
|
pub mod movement;
|
||||||
|
mod multi_buffer;
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test;
|
mod test;
|
||||||
|
@ -21,12 +22,14 @@ use gpui::{
|
||||||
};
|
};
|
||||||
use items::BufferItemHandle;
|
use items::BufferItemHandle;
|
||||||
use language::{
|
use language::{
|
||||||
multi_buffer::{
|
|
||||||
Anchor, AnchorRangeExt, MultiBuffer, MultiBufferSnapshot, SelectionSet, ToOffset, ToPoint,
|
|
||||||
},
|
|
||||||
BracketPair, Buffer, Diagnostic, DiagnosticSeverity, Language, Point, Selection, SelectionGoal,
|
BracketPair, Buffer, Diagnostic, DiagnosticSeverity, Language, Point, Selection, SelectionGoal,
|
||||||
SelectionSetId,
|
SelectionSetId,
|
||||||
};
|
};
|
||||||
|
use multi_buffer::{
|
||||||
|
Anchor, AnchorRangeExt, MultiBufferChunks, MultiBufferSnapshot,
|
||||||
|
SelectionSet, ToOffset, ToPoint,
|
||||||
|
};
|
||||||
|
pub use multi_buffer::MultiBuffer;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
use smol::Timer;
|
use smol::Timer;
|
||||||
|
|
|
@ -4,6 +4,7 @@ use super::{
|
||||||
DisplayPoint, Editor, EditorMode, EditorSettings, EditorSnapshot, EditorStyle, Input, Scroll,
|
DisplayPoint, Editor, EditorMode, EditorSettings, EditorSnapshot, EditorStyle, Input, Scroll,
|
||||||
Select, SelectPhase, SoftWrap, MAX_LINE_LEN,
|
Select, SelectPhase, SoftWrap, MAX_LINE_LEN,
|
||||||
};
|
};
|
||||||
|
use crate::ToPoint;
|
||||||
use clock::ReplicaId;
|
use clock::ReplicaId;
|
||||||
use gpui::{
|
use gpui::{
|
||||||
color::Color,
|
color::Color,
|
||||||
|
@ -19,7 +20,7 @@ use gpui::{
|
||||||
MutableAppContext, PaintContext, Quad, Scene, SizeConstraint, ViewContext, WeakViewHandle,
|
MutableAppContext, PaintContext, Quad, Scene, SizeConstraint, ViewContext, WeakViewHandle,
|
||||||
};
|
};
|
||||||
use json::json;
|
use json::json;
|
||||||
use language::{multi_buffer::ToPoint, Chunk};
|
use language::Chunk;
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
use std::{
|
use std::{
|
||||||
cmp::{self, Ordering},
|
cmp::{self, Ordering},
|
||||||
|
@ -1162,8 +1163,7 @@ fn scale_horizontal_mouse_autoscroll_delta(delta: f32) -> f32 {
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::{Editor, EditorSettings};
|
use crate::{Editor, EditorSettings, MultiBuffer};
|
||||||
use language::{MultiBuffer};
|
|
||||||
use util::test::sample_text;
|
use util::test::sample_text;
|
||||||
|
|
||||||
#[gpui::test]
|
#[gpui::test]
|
||||||
|
|
|
@ -1,14 +1,12 @@
|
||||||
use crate::{Editor, EditorSettings, Event};
|
use crate::{Editor, EditorSettings, Event};
|
||||||
|
use crate::{MultiBuffer, ToPoint as _};
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use gpui::{
|
use gpui::{
|
||||||
elements::*, fonts::TextStyle, AppContext, Entity, ModelContext, ModelHandle,
|
elements::*, fonts::TextStyle, AppContext, Entity, ModelContext, ModelHandle,
|
||||||
MutableAppContext, RenderContext, Subscription, Task, View, ViewContext, ViewHandle,
|
MutableAppContext, RenderContext, Subscription, Task, View, ViewContext, ViewHandle,
|
||||||
WeakModelHandle,
|
WeakModelHandle,
|
||||||
};
|
};
|
||||||
use language::{
|
use language::{Diagnostic, File as _};
|
||||||
multi_buffer::{MultiBuffer, ToPoint as _},
|
|
||||||
Diagnostic, File as _,
|
|
||||||
};
|
|
||||||
use postage::watch;
|
use postage::watch;
|
||||||
use project::{ProjectPath, Worktree};
|
use project::{ProjectPath, Worktree};
|
||||||
use std::fmt::Write;
|
use std::fmt::Write;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use super::{Bias, DisplayPoint, DisplaySnapshot, SelectionGoal, ToDisplayPoint};
|
use super::{Bias, DisplayPoint, DisplaySnapshot, SelectionGoal, ToDisplayPoint};
|
||||||
|
use crate::ToPoint;
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use language::multi_buffer::ToPoint;
|
|
||||||
use std::{cmp, ops::Range};
|
use std::{cmp, ops::Range};
|
||||||
|
|
||||||
pub fn left(map: &DisplaySnapshot, mut point: DisplayPoint) -> Result<DisplayPoint> {
|
pub fn left(map: &DisplaySnapshot, mut point: DisplayPoint) -> Result<DisplayPoint> {
|
||||||
|
@ -244,8 +244,7 @@ fn char_kind(c: char) -> CharKind {
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::display_map::DisplayMap;
|
use crate::{DisplayMap, MultiBuffer};
|
||||||
use language::MultiBuffer;
|
|
||||||
|
|
||||||
#[gpui::test]
|
#[gpui::test]
|
||||||
fn test_prev_next_word_boundary_multibyte(cx: &mut gpui::MutableAppContext) {
|
fn test_prev_next_word_boundary_multibyte(cx: &mut gpui::MutableAppContext) {
|
||||||
|
|
|
@ -1,15 +1,15 @@
|
||||||
mod anchor;
|
mod anchor;
|
||||||
mod selection;
|
mod selection;
|
||||||
|
|
||||||
use crate::{
|
|
||||||
buffer::{self, Buffer, Chunk, ToOffset as _, ToPoint as _},
|
|
||||||
BufferSnapshot, DiagnosticEntry, Event, File, Language,
|
|
||||||
};
|
|
||||||
pub use anchor::{Anchor, AnchorRangeExt};
|
pub use anchor::{Anchor, AnchorRangeExt};
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use clock::ReplicaId;
|
use clock::ReplicaId;
|
||||||
use collections::HashMap;
|
use collections::HashMap;
|
||||||
use gpui::{AppContext, Entity, ModelContext, ModelHandle, MutableAppContext, Task};
|
use gpui::{AppContext, Entity, ModelContext, ModelHandle, MutableAppContext, Task};
|
||||||
|
use language::{
|
||||||
|
Buffer, BufferChunks, BufferSnapshot, Chunk, DiagnosticEntry, Event, File, Language,
|
||||||
|
ToOffset as _, ToPoint as _,
|
||||||
|
};
|
||||||
pub use selection::SelectionSet;
|
pub use selection::SelectionSet;
|
||||||
use std::{
|
use std::{
|
||||||
cell::{Ref, RefCell},
|
cell::{Ref, RefCell},
|
||||||
|
@ -72,7 +72,7 @@ pub struct ExcerptProperties<'a, T> {
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
struct Excerpt {
|
struct Excerpt {
|
||||||
id: ExcerptId,
|
id: ExcerptId,
|
||||||
buffer: buffer::BufferSnapshot,
|
buffer: BufferSnapshot,
|
||||||
range: Range<text::Anchor>,
|
range: Range<text::Anchor>,
|
||||||
text_summary: TextSummary,
|
text_summary: TextSummary,
|
||||||
header_height: u8,
|
header_height: u8,
|
||||||
|
@ -90,7 +90,7 @@ pub struct MultiBufferChunks<'a> {
|
||||||
cursor: Cursor<'a, Excerpt, usize>,
|
cursor: Cursor<'a, Excerpt, usize>,
|
||||||
header_height: u8,
|
header_height: u8,
|
||||||
has_trailing_newline: bool,
|
has_trailing_newline: bool,
|
||||||
excerpt_chunks: Option<buffer::BufferChunks<'a>>,
|
excerpt_chunks: Option<BufferChunks<'a>>,
|
||||||
theme: Option<&'a SyntaxTheme>,
|
theme: Option<&'a SyntaxTheme>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -394,7 +394,7 @@ impl MultiBuffer {
|
||||||
cx.subscribe(buffer, Self::on_buffer_event).detach();
|
cx.subscribe(buffer, Self::on_buffer_event).detach();
|
||||||
|
|
||||||
let buffer = props.buffer.read(cx);
|
let buffer = props.buffer.read(cx);
|
||||||
let range = buffer.anchor_before(props.range.start)..buffer.anchor_after(props.range.end);
|
let range = buffer.anchor_before(&props.range.start)..buffer.anchor_after(&props.range.end);
|
||||||
let mut snapshot = self.snapshot.borrow_mut();
|
let mut snapshot = self.snapshot.borrow_mut();
|
||||||
let prev_id = snapshot.excerpts.last().map(|e| &e.id);
|
let prev_id = snapshot.excerpts.last().map(|e| &e.id);
|
||||||
let id = ExcerptId::between(prev_id.unwrap_or(&ExcerptId::min()), &ExcerptId::max());
|
let id = ExcerptId::between(prev_id.unwrap_or(&ExcerptId::min()), &ExcerptId::max());
|
||||||
|
@ -565,7 +565,7 @@ impl MultiBuffer {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Entity for MultiBuffer {
|
impl Entity for MultiBuffer {
|
||||||
type Event = super::Event;
|
type Event = language::Event;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MultiBufferSnapshot {
|
impl MultiBufferSnapshot {
|
||||||
|
@ -1099,7 +1099,7 @@ impl MultiBufferSnapshot {
|
||||||
impl Excerpt {
|
impl Excerpt {
|
||||||
fn new(
|
fn new(
|
||||||
id: ExcerptId,
|
id: ExcerptId,
|
||||||
buffer: buffer::BufferSnapshot,
|
buffer: BufferSnapshot,
|
||||||
range: Range<text::Anchor>,
|
range: Range<text::Anchor>,
|
||||||
header_height: u8,
|
header_height: u8,
|
||||||
has_trailing_newline: bool,
|
has_trailing_newline: bool,
|
||||||
|
@ -1357,8 +1357,8 @@ impl ToPoint for Point {
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::buffer::Buffer;
|
|
||||||
use gpui::MutableAppContext;
|
use gpui::MutableAppContext;
|
||||||
|
use language::Buffer;
|
||||||
use rand::prelude::*;
|
use rand::prelude::*;
|
||||||
use std::env;
|
use std::env;
|
||||||
use text::{Point, RandomCharIter};
|
use text::{Point, RandomCharIter};
|
|
@ -1,7 +1,6 @@
|
||||||
mod buffer;
|
mod buffer;
|
||||||
mod diagnostic_set;
|
mod diagnostic_set;
|
||||||
mod highlight_map;
|
mod highlight_map;
|
||||||
pub mod multi_buffer;
|
|
||||||
pub mod proto;
|
pub mod proto;
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests;
|
mod tests;
|
||||||
|
@ -14,7 +13,6 @@ use gpui::{executor::Background, AppContext};
|
||||||
use highlight_map::HighlightMap;
|
use highlight_map::HighlightMap;
|
||||||
use lazy_static::lazy_static;
|
use lazy_static::lazy_static;
|
||||||
use lsp::LanguageServer;
|
use lsp::LanguageServer;
|
||||||
pub use multi_buffer::MultiBuffer;
|
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use std::{collections::HashSet, path::Path, str, sync::Arc};
|
use std::{collections::HashSet, path::Path, str, sync::Arc};
|
||||||
|
|
|
@ -944,11 +944,11 @@ mod tests {
|
||||||
EstablishConnectionError, UserStore,
|
EstablishConnectionError, UserStore,
|
||||||
},
|
},
|
||||||
contacts_panel::JoinWorktree,
|
contacts_panel::JoinWorktree,
|
||||||
editor::{Editor, EditorSettings, Input},
|
editor::{Editor, EditorSettings, Input, MultiBuffer},
|
||||||
fs::{FakeFs, Fs as _},
|
fs::{FakeFs, Fs as _},
|
||||||
language::{
|
language::{
|
||||||
tree_sitter_rust, Diagnostic, DiagnosticEntry, Language, LanguageConfig,
|
tree_sitter_rust, Diagnostic, DiagnosticEntry, Language, LanguageConfig,
|
||||||
LanguageRegistry, LanguageServerConfig, MultiBuffer, Point,
|
LanguageRegistry, LanguageServerConfig, Point,
|
||||||
},
|
},
|
||||||
lsp,
|
lsp,
|
||||||
project::{ProjectPath, Worktree},
|
project::{ProjectPath, Worktree},
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue