use crate::schema::{ColorRole, DecorationKind, GutterDecorationKind, HoverPhase, Paint, Severity};
use std::rc::Rc;
pub const DECORATION_LEN: usize = 44;
pub const SYNTAX_SPAN_LEN: usize = 28;
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum RecordFieldType {
U32,
U64,
Role {
name: &'static str,
optional: bool,
},
Paint,
Flag,
Text,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct RecordField {
pub name: &'static str,
pub offset: u16,
pub ty: RecordFieldType,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct RecordSchema {
pub name: &'static str,
pub length: u16,
pub fields: &'static [RecordField],
}
const fn field(name: &'static str, offset: u16, ty: RecordFieldType) -> RecordField {
RecordField { name, offset, ty }
}
pub const DECORATION_RECORD: RecordSchema = RecordSchema {
name: "DecorationRecord",
length: DECORATION_LEN as u16,
fields: &[
field("version", 0, RecordFieldType::U32),
field(
"kind",
4,
RecordFieldType::Role {
name: "DecorationKind",
optional: false,
},
),
field(
"severity",
6,
RecordFieldType::Role {
name: "Severity",
optional: true,
},
),
field(
"color",
8,
RecordFieldType::Role {
name: "ColorRole",
optional: true,
},
),
field("startLine", 12, RecordFieldType::U32),
field("startColumn", 16, RecordFieldType::U32),
field("endLine", 20, RecordFieldType::U32),
field("endColumn", 24, RecordFieldType::U32),
field("id", 28, RecordFieldType::U64),
field("text", 36, RecordFieldType::Text),
],
};
pub const SYNTAX_SPAN_RECORD: RecordSchema = RecordSchema {
name: "SyntaxSpanRecord",
length: SYNTAX_SPAN_LEN as u16,
fields: &[
field("version", 0, RecordFieldType::U32),
field("startLine", 4, RecordFieldType::U32),
field("startColumn", 8, RecordFieldType::U32),
field("endLine", 12, RecordFieldType::U32),
field("endColumn", 16, RecordFieldType::U32),
field("paint", 20, RecordFieldType::Paint),
],
};
pub const FOLD_LEN: usize = 16;
pub const GUTTER_DECORATION_LEN: usize = 28;
pub const FOLD_RECORD: RecordSchema = RecordSchema {
name: "FoldRecord",
length: FOLD_LEN as u16,
fields: &[
field("version", 0, RecordFieldType::U32),
field("startLine", 4, RecordFieldType::U32),
field("endLine", 8, RecordFieldType::U32),
field("collapsed", 12, RecordFieldType::Flag),
],
};
pub const GUTTER_DECORATION_RECORD: RecordSchema = RecordSchema {
name: "GutterDecorationRecord",
length: GUTTER_DECORATION_LEN as u16,
fields: &[
field("version", 0, RecordFieldType::U32),
field("line", 4, RecordFieldType::U32),
field(
"kind",
8,
RecordFieldType::Role {
name: "GutterDecorationKind",
optional: false,
},
),
field(
"color",
10,
RecordFieldType::Role {
name: "ColorRole",
optional: true,
},
),
field("id", 12, RecordFieldType::U64),
field("glyph", 20, RecordFieldType::U32),
],
};
const fn hash_bytes(mut hash: u64, bytes: &[u8]) -> u64 {
let mut index = 0;
while index < bytes.len() {
hash ^= bytes[index] as u64;
hash = hash.wrapping_mul(0x0000_0100_0000_01b3);
index += 1;
}
hash
}
pub(crate) const fn hash_record_schema(mut hash: u64, schema: RecordSchema) -> u64 {
hash = hash_bytes(hash, schema.name.as_bytes());
hash = hash_bytes(hash, &schema.length.to_le_bytes());
let mut index = 0;
while index < schema.fields.len() {
let field = schema.fields[index];
hash = hash_bytes(hash, field.name.as_bytes());
hash = hash_bytes(hash, &field.offset.to_le_bytes());
hash = match field.ty {
RecordFieldType::U32 => hash_bytes(hash, &[1]),
RecordFieldType::U64 => hash_bytes(hash, &[2]),
RecordFieldType::Role { name, optional } => {
let hash = hash_bytes(hash, &[3, optional as u8]);
hash_bytes(hash, name.as_bytes())
}
RecordFieldType::Paint => hash_bytes(hash, &[4]),
RecordFieldType::Text => hash_bytes(hash, &[5]),
RecordFieldType::Flag => hash_bytes(hash, &[6]),
};
index += 1;
}
hash
}
#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct Position {
pub line: u32,
pub column: u32,
}
impl Position {
pub const fn new(line: u32, column: u32) -> Self {
Self { line, column }
}
}
#[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)]
pub struct CodeRange {
pub start: Position,
pub end: Position,
}
impl CodeRange {
pub const fn new(start: Position, end: Position) -> Self {
Self { start, end }
}
pub const fn at(position: Position) -> Self {
Self {
start: position,
end: position,
}
}
pub const fn of(start_line: u32, start_column: u32, end_line: u32, end_column: u32) -> Self {
Self {
start: Position::new(start_line, start_column),
end: Position::new(end_line, end_column),
}
}
pub fn is_empty(&self) -> bool {
self.start == self.end
}
}
#[derive(Clone, Debug, PartialEq)]
pub struct Decoration {
pub version: u32,
pub kind: DecorationKind,
pub severity: Option<Severity>,
pub color: Option<ColorRole>,
pub range: CodeRange,
pub id: u64,
pub text: String,
}
impl Decoration {
pub fn underline(version: u32, range: CodeRange, severity: Severity) -> Self {
Self {
version,
kind: DecorationKind::Underline,
severity: Some(severity),
color: None,
range,
id: 0,
text: String::new(),
}
}
pub fn code_lens(version: u32, line: u32, text: impl Into<String>, id: u64) -> Self {
Self {
version,
kind: DecorationKind::CodeLens,
severity: None,
color: None,
range: CodeRange::at(Position::new(line, 0)),
id,
text: text.into(),
}
}
pub fn hover_anchor(version: u32, range: CodeRange, id: u64) -> Self {
Self {
version,
kind: DecorationKind::HoverAnchor,
severity: None,
color: None,
range,
id,
text: String::new(),
}
}
pub fn ghost_text(version: u32, at: Position, text: impl Into<String>, id: u64) -> Self {
Self {
version,
kind: DecorationKind::GhostText,
severity: None,
color: None,
range: CodeRange::at(at),
id,
text: text.into(),
}
}
pub fn with_color(mut self, role: ColorRole) -> Self {
self.color = Some(role);
self
}
pub fn with_id(mut self, id: u64) -> Self {
self.id = id;
self
}
}
fn put_u16(out: &mut Vec<u8>, value: u16) {
out.extend_from_slice(&value.to_le_bytes());
}
fn put_u32(out: &mut Vec<u8>, value: u32) {
out.extend_from_slice(&value.to_le_bytes());
}
fn put_u64(out: &mut Vec<u8>, value: u64) {
out.extend_from_slice(&value.to_le_bytes());
}
fn put_range(out: &mut Vec<u8>, range: CodeRange) {
put_u32(out, range.start.line);
put_u32(out, range.start.column);
put_u32(out, range.end.line);
put_u32(out, range.end.column);
}
fn word(record: &[u8], at: usize) -> u32 {
u32::from_le_bytes([record[at], record[at + 1], record[at + 2], record[at + 3]])
}
fn half(record: &[u8], at: usize) -> u16 {
u16::from_le_bytes([record[at], record[at + 1]])
}
fn long(record: &[u8], at: usize) -> u64 {
u64::from(word(record, at)) | (u64::from(word(record, at + 4)) << 32)
}
fn range_at(record: &[u8], at: usize) -> CodeRange {
CodeRange::of(
word(record, at),
word(record, at + 4),
word(record, at + 8),
word(record, at + 12),
)
}
#[derive(Clone, Default, PartialEq)]
pub struct Decorations {
bytes: Option<Rc<[u8]>>,
}
impl std::fmt::Debug for Decorations {
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
formatter
.debug_struct("Decorations")
.field("decorations", &self.decorations().len())
.finish()
}
}
impl Decorations {
pub fn new(decorations: impl IntoIterator<Item = Decoration>) -> Self {
let decorations: Vec<Decoration> = decorations.into_iter().collect();
let records_len = decorations.len() * DECORATION_LEN;
let mut bytes = Vec::with_capacity(records_len);
let mut texts = Vec::new();
for decoration in &decorations {
put_u32(&mut bytes, decoration.version);
put_u16(&mut bytes, decoration.kind as u16);
put_u16(
&mut bytes,
decoration.severity.map_or(0, |value| value as u16),
);
put_u16(&mut bytes, decoration.color.map_or(0, |value| value as u16));
put_u16(&mut bytes, 0);
put_range(&mut bytes, decoration.range);
put_u64(&mut bytes, decoration.id);
put_u32(&mut bytes, (records_len + texts.len()) as u32);
put_u32(&mut bytes, decoration.text.len() as u32);
texts.extend_from_slice(decoration.text.as_bytes());
}
bytes.extend_from_slice(&texts);
Self::from_bytes(bytes)
}
pub fn from_bytes(bytes: impl Into<Rc<[u8]>>) -> Self {
let bytes = bytes.into();
Self {
bytes: (!bytes.is_empty()).then_some(bytes),
}
}
pub fn as_bytes(&self) -> &[u8] {
self.bytes.as_deref().unwrap_or(&[])
}
pub fn is_empty(&self) -> bool {
self.bytes.is_none()
}
pub fn decorations(&self) -> Vec<Decoration> {
let bytes = self.as_bytes();
let mut end = bytes.len();
let mut at = 0;
let mut found = Vec::new();
while at + DECORATION_LEN <= end {
let record = &bytes[at..at + DECORATION_LEN];
at += DECORATION_LEN;
let text_offset = word(record, 36) as usize;
let text_len = word(record, 40) as usize;
if text_len > 0 {
end = end.min(text_offset);
}
let Ok(kind) = DecorationKind::try_from(half(record, 4)) else {
continue;
};
let severity = match half(record, 6) {
0 => None,
tag => match Severity::try_from(tag) {
Ok(severity) => Some(severity),
Err(()) => continue,
},
};
let color = match half(record, 8) {
0 => None,
tag => match ColorRole::try_from(tag) {
Ok(role) => Some(role),
Err(()) => continue,
},
};
let text = if text_len == 0 {
String::new()
} else {
match bytes
.get(text_offset..text_offset + text_len)
.and_then(|run| std::str::from_utf8(run).ok())
{
Some(text) => text.to_owned(),
None => continue,
}
};
found.push(Decoration {
version: word(record, 0),
kind,
severity,
color,
range: range_at(record, 12),
id: long(record, 28),
text,
});
}
found
}
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct SyntaxSpan {
pub version: u32,
pub range: CodeRange,
pub paint: Paint,
}
impl SyntaxSpan {
pub const fn new(version: u32, range: CodeRange, paint: Paint) -> Self {
Self {
version,
range,
paint,
}
}
pub const fn role(version: u32, range: CodeRange, role: ColorRole) -> Self {
Self::new(version, range, Paint::Role(role))
}
}
#[derive(Clone, Default, PartialEq)]
pub struct SyntaxSpans {
bytes: Option<Rc<[u8]>>,
}
impl std::fmt::Debug for SyntaxSpans {
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
formatter
.debug_struct("SyntaxSpans")
.field("spans", &(self.as_bytes().len() / SYNTAX_SPAN_LEN))
.finish()
}
}
impl SyntaxSpans {
pub fn new(spans: impl IntoIterator<Item = SyntaxSpan>) -> Self {
let mut bytes = Vec::new();
for span in spans {
put_u32(&mut bytes, span.version);
put_range(&mut bytes, span.range);
put_u64(&mut bytes, span.paint.to_bits());
}
Self::from_bytes(bytes)
}
pub fn from_bytes(bytes: impl Into<Rc<[u8]>>) -> Self {
let bytes = bytes.into();
Self {
bytes: (!bytes.is_empty()).then_some(bytes),
}
}
pub fn as_bytes(&self) -> &[u8] {
self.bytes.as_deref().unwrap_or(&[])
}
pub fn is_empty(&self) -> bool {
self.bytes.is_none()
}
pub fn spans(&self) -> Vec<SyntaxSpan> {
self.as_bytes()
.chunks_exact(SYNTAX_SPAN_LEN)
.filter_map(|record| {
Some(SyntaxSpan {
version: word(record, 0),
range: range_at(record, 4),
paint: Paint::from_bits(long(record, 20))?,
})
})
.collect()
}
}
pub fn byte_offset(text: &str, position: Position) -> Option<usize> {
let bytes = text.as_bytes();
let mut start = 0;
for _ in 0..position.line {
let at = bytes[start..]
.iter()
.position(|byte| *byte == b'\n' || *byte == b'\r')?
+ start;
start = if bytes[at] == b'\r' && bytes.get(at + 1) == Some(&b'\n') {
at + 2
} else {
at + 1
};
}
let end = bytes[start..]
.iter()
.position(|byte| *byte == b'\n' || *byte == b'\r')
.map_or(bytes.len(), |offset| start + offset);
let mut units = 0_u32;
let mut offset = start;
for character in text[start..end].chars() {
if units == position.column {
return Some(offset);
}
units += character.len_utf16() as u32;
offset += character.len_utf8();
if units > position.column {
return None;
}
}
(units == position.column).then_some(offset)
}
#[derive(Clone, Debug, PartialEq)]
pub struct CodeChange {
pub version: u32,
pub range: CodeRange,
pub text: String,
}
impl CodeChange {
pub fn apply_to(&self, text: &mut String) -> bool {
let (Some(start), Some(end)) = (
byte_offset(text, self.range.start),
byte_offset(text, self.range.end),
) else {
return false;
};
if start > end {
return false;
}
text.replace_range(start..end, &self.text);
true
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct EditRejected {
pub request_id: u32,
pub base_version: u32,
pub current_version: u32,
pub range: CodeRange,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct CodeHover {
pub decoration: Option<u64>,
pub position: Position,
pub phase: HoverPhase,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct SaveRequest {
pub version: u32,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct Fold {
pub version: u32,
pub start_line: u32,
pub end_line: u32,
pub collapsed: bool,
}
impl Fold {
pub const fn new(version: u32, start_line: u32, end_line: u32) -> Self {
Self {
version,
start_line,
end_line,
collapsed: false,
}
}
pub const fn collapsed(mut self) -> Self {
self.collapsed = true;
self
}
}
#[derive(Clone, Default, PartialEq)]
pub struct Folds {
bytes: Option<Rc<[u8]>>,
}
impl std::fmt::Debug for Folds {
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
formatter
.debug_struct("Folds")
.field("folds", &(self.as_bytes().len() / FOLD_LEN))
.finish()
}
}
impl Folds {
pub fn new(folds: impl IntoIterator<Item = Fold>) -> Self {
let mut bytes = Vec::new();
for fold in folds {
put_u32(&mut bytes, fold.version);
put_u32(&mut bytes, fold.start_line);
put_u32(&mut bytes, fold.end_line);
put_u32(&mut bytes, u32::from(fold.collapsed));
}
Self::from_bytes(bytes)
}
pub fn from_bytes(bytes: impl Into<Rc<[u8]>>) -> Self {
let bytes = bytes.into();
Self {
bytes: (!bytes.is_empty()).then_some(bytes),
}
}
pub fn as_bytes(&self) -> &[u8] {
self.bytes.as_deref().unwrap_or(&[])
}
pub fn is_empty(&self) -> bool {
self.bytes.is_none()
}
pub fn folds(&self) -> Vec<Fold> {
self.as_bytes()
.chunks_exact(FOLD_LEN)
.filter_map(|record| {
Some(Fold {
version: word(record, 0),
start_line: word(record, 4),
end_line: word(record, 8),
collapsed: match word(record, 12) {
0 => false,
1 => true,
_ => return None,
},
})
})
.collect()
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct GutterDecoration {
pub version: u32,
pub line: u32,
pub kind: GutterDecorationKind,
pub color: Option<ColorRole>,
pub id: u64,
pub glyph: u32,
}
impl GutterDecoration {
pub const fn dot(version: u32, line: u32, id: u64) -> Self {
Self {
version,
line,
kind: GutterDecorationKind::Dot,
color: None,
id,
glyph: 0,
}
}
pub const fn bar(version: u32, line: u32, id: u64) -> Self {
Self {
version,
line,
kind: GutterDecorationKind::Bar,
color: None,
id,
glyph: 0,
}
}
pub const fn glyph(version: u32, line: u32, glyph: char, id: u64) -> Self {
Self {
version,
line,
kind: GutterDecorationKind::Glyph,
color: None,
id,
glyph: glyph as u32,
}
}
pub const fn with_color(mut self, role: ColorRole) -> Self {
self.color = Some(role);
self
}
pub const fn with_id(mut self, id: u64) -> Self {
self.id = id;
self
}
}
#[derive(Clone, Default, PartialEq)]
pub struct GutterDecorations {
bytes: Option<Rc<[u8]>>,
}
impl std::fmt::Debug for GutterDecorations {
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
formatter
.debug_struct("GutterDecorations")
.field(
"decorations",
&(self.as_bytes().len() / GUTTER_DECORATION_LEN),
)
.finish()
}
}
impl GutterDecorations {
pub fn new(decorations: impl IntoIterator<Item = GutterDecoration>) -> Self {
let mut bytes = Vec::new();
for decoration in decorations {
put_u32(&mut bytes, decoration.version);
put_u32(&mut bytes, decoration.line);
put_u16(&mut bytes, decoration.kind as u16);
put_u16(&mut bytes, decoration.color.map_or(0, |value| value as u16));
put_u64(&mut bytes, decoration.id);
put_u32(&mut bytes, decoration.glyph);
put_u32(&mut bytes, 0);
}
Self::from_bytes(bytes)
}
pub fn from_bytes(bytes: impl Into<Rc<[u8]>>) -> Self {
let bytes = bytes.into();
Self {
bytes: (!bytes.is_empty()).then_some(bytes),
}
}
pub fn as_bytes(&self) -> &[u8] {
self.bytes.as_deref().unwrap_or(&[])
}
pub fn is_empty(&self) -> bool {
self.bytes.is_none()
}
pub fn decorations(&self) -> Vec<GutterDecoration> {
self.as_bytes()
.chunks_exact(GUTTER_DECORATION_LEN)
.filter_map(|record| {
let kind = GutterDecorationKind::try_from(half(record, 8)).ok()?;
let color = match half(record, 10) {
0 => None,
tag => Some(ColorRole::try_from(tag).ok()?),
};
Some(GutterDecoration {
version: word(record, 0),
line: word(record, 4),
kind,
color,
id: long(record, 12),
glyph: word(record, 20),
})
})
.collect()
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct SelectionChange {
pub version: u32,
pub anchor: Position,
pub active: Position,
}
impl SelectionChange {
pub fn is_caret(&self) -> bool {
self.anchor == self.active
}
pub fn range(&self) -> CodeRange {
CodeRange::new(self.anchor.min(self.active), self.anchor.max(self.active))
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct VisibleRange {
pub version: u32,
pub first_line: u32,
pub last_line: u32,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct FoldChange {
pub start_line: u32,
pub collapsed: bool,
}