Skip to content

Commit 7fd20da

Browse files
committed
Merge commit 'baa3cb05b4738c0fe2275d11b2eb2be3cfe7fc40' into dev/lhecker/syntax-highlighting
2 parents 1ef1dbf + baa3cb0 commit 7fd20da

49 files changed

Lines changed: 1837 additions & 1022 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.cargo/release-nightly.toml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,3 @@ rustflags = [
1313
[unstable]
1414
panic-immediate-abort = true
1515
build-std = ["std", "panic_abort"]
16-
build-std-features = ["default", "optimize_for_size"]

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ resolver = "2"
77
edition = "2024"
88
license = "MIT"
99
repository = "https://github.com/microsoft/edit"
10-
rust-version = "1.92"
10+
rust-version = "1.93"
1111

1212
# We use `opt-level = "s"` as it significantly reduces binary size.
1313
# We could then use the `#[optimize(speed)]` attribute for spot optimizations.

crates/edit/benches/lib.rs

Lines changed: 15 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
// Copyright (c) Microsoft Corporation.
22
// Licensed under the MIT License.
33

4-
#![feature(allocator_api)]
5-
64
use std::hint::black_box;
75
use std::io::Cursor;
86
use std::ops::Range;
@@ -12,20 +10,22 @@ use std::{mem, vec};
1210
use criterion::{BenchmarkId, Criterion, Throughput, criterion_group, criterion_main};
1311
use edit::helpers::*;
1412
use edit::simd::MemsetSafe;
15-
use edit::{buffer, hash, json, lsh, oklab, simd, unicode};
16-
use stdext::arena::{Arena, scratch_arena};
13+
use edit::{buffer, glob, hash, json, lsh, oklab, simd, unicode};
14+
use stdext::arena::{self, Arena, scratch_arena};
15+
use stdext::collections::BVec;
16+
use stdext::unicode::Utf8Chars;
1717
use stdext::{arena, glob, varint};
1818

1919
struct EditingTracePatch<'a>(usize, usize, &'a str);
2020

2121
struct EditingTraceTransaction<'a> {
22-
patches: Vec<EditingTracePatch<'a>, &'a Arena>,
22+
patches: BVec<'a, EditingTracePatch<'a>>,
2323
}
2424

2525
struct EditingTraceData<'a> {
2626
start_content: &'a str,
2727
end_content: &'a str,
28-
txns: Vec<EditingTraceTransaction<'a>, &'a Arena>,
28+
txns: BVec<'a, EditingTraceTransaction<'a>>,
2929
}
3030

3131
fn bench_buffer(c: &mut Criterion) {
@@ -42,24 +42,25 @@ fn bench_buffer(c: &mut Criterion) {
4242
let mut res = EditingTraceData {
4343
start_content: root.get_str("startContent").unwrap(),
4444
end_content: root.get_str("endContent").unwrap(),
45-
txns: Vec::with_capacity_in(txns.len(), &scratch),
45+
txns: BVec::empty(),
4646
};
47+
res.txns.reserve(&*scratch, txns.len());
4748

4849
for txn in txns {
4950
let txn = txn.as_object().unwrap();
5051
let patches = txn.get_array("patches").unwrap();
51-
let mut txn =
52-
EditingTraceTransaction { patches: Vec::with_capacity_in(patches.len(), &scratch) };
52+
let mut txn = EditingTraceTransaction { patches: BVec::empty() };
53+
txn.patches.reserve(&*scratch, patches.len());
5354

5455
for patch in patches {
5556
let patch = patch.as_array().unwrap();
5657
let offset = patch[0].as_number().unwrap() as usize;
5758
let del_len = patch[1].as_number().unwrap() as usize;
5859
let ins_str = patch[2].as_str().unwrap();
59-
txn.patches.push(EditingTracePatch(offset, del_len, ins_str));
60+
txn.patches.push(&*scratch, EditingTracePatch(offset, del_len, ins_str));
6061
}
6162

62-
res.txns.push(txn);
63+
res.txns.push(&*scratch, txn);
6364
}
6465

6566
res
@@ -257,7 +258,7 @@ fn bench_simd_memchr2(c: &mut Criterion) {
257258
}
258259
}
259260

260-
fn bench_simd_memset<T: MemsetSafe + Copy + Default>(c: &mut Criterion) {
261+
fn bench_simd_memset<T: Copy + Default>(c: &mut Criterion) {
261262
let mut group = c.benchmark_group("simd");
262263
let name = format!("memset<{}>", std::any::type_name::<T>());
263264
let size = mem::size_of::<T>();
@@ -272,7 +273,7 @@ fn bench_simd_memset<T: MemsetSafe + Copy + Default>(c: &mut Criterion) {
272273
&bytes,
273274
|b, &bytes| {
274275
let slice = unsafe { buf.get_unchecked_mut(..bytes / size) };
275-
b.iter(|| simd::memset(black_box(slice), Default::default()));
276+
b.iter(|| stdext::simd::memset(black_box(slice), Default::default()));
276277
},
277278
);
278279
}
@@ -304,9 +305,7 @@ fn bench_unicode(c: &mut Criterion) {
304305
c.benchmark_group("unicode::Utf8Chars")
305306
.throughput(Throughput::Bytes(bytes.len() as u64))
306307
.bench_function("next", |b| {
307-
b.iter(|| {
308-
unicode::Utf8Chars::new(bytes, 0).fold(0u32, |acc, ch| acc.wrapping_add(ch as u32))
309-
})
308+
b.iter(|| Utf8Chars::new(bytes, 0).fold(0u32, |acc, ch| acc.wrapping_add(ch as u32)))
310309
});
311310
}
312311

crates/edit/src/base64.rs

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@
33

44
//! Base64 facilities.
55
6-
use stdext::arena::ArenaString;
6+
use stdext::arena::Arena;
7+
use stdext::collections::BString;
78

89
const CHARSET: [u8; 64] = *b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
910

@@ -15,15 +16,15 @@ pub fn encode_len(src_len: usize) -> usize {
1516
}
1617

1718
/// Encodes the given bytes as base64 and appends them to the destination string.
18-
pub fn encode(dst: &mut ArenaString, src: &[u8]) {
19+
pub fn encode<'a>(arena: &'a Arena, dst: &mut BString<'a>, src: &[u8]) {
1920
unsafe {
2021
let mut inp = src.as_ptr();
2122
let mut remaining = src.len();
2223
let dst = dst.as_mut_vec();
2324

2425
let out_len = encode_len(src.len());
2526
// ... we can then use this fact to reserve space all at once.
26-
dst.reserve(out_len);
27+
dst.reserve(arena, out_len);
2728

2829
// SAFETY: Getting a pointer to the reserved space is only safe
2930
// *after* calling `reserve()` as it may change the pointer.
@@ -79,16 +80,17 @@ pub fn encode(dst: &mut ArenaString, src: &[u8]) {
7980

8081
#[cfg(test)]
8182
mod tests {
82-
use stdext::arena::{Arena, ArenaString};
83+
use stdext::arena::scratch_arena;
84+
use stdext::collections::BString;
8385

8486
use super::encode;
8587

8688
#[test]
8789
fn test_basic() {
88-
let arena = Arena::new(4 * 1024).unwrap();
90+
let scratch = scratch_arena(None);
8991
let enc = |s: &[u8]| {
90-
let mut dst = ArenaString::new_in(&arena);
91-
encode(&mut dst, s);
92+
let mut dst = BString::empty();
93+
encode(&scratch, &mut dst, s);
9294
dst
9395
};
9496
assert_eq!(enc(b""), "");

crates/edit/src/bin/edit/documents.rs

Lines changed: 37 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
// Copyright (c) Microsoft Corporation.
22
// Licensed under the MIT License.
33

4-
use std::collections::LinkedList;
54
use std::ffi::OsStr;
65
use std::fs::File;
76
use std::path::{Path, PathBuf};
@@ -110,7 +109,7 @@ impl Document {
110109

111110
#[derive(Default)]
112111
pub struct DocumentManager {
113-
list: LinkedList<Document>,
112+
list: Vec<Document>,
114113
}
115114

116115
impl DocumentManager {
@@ -121,30 +120,48 @@ impl DocumentManager {
121120

122121
#[inline]
123122
pub fn active(&self) -> Option<&Document> {
124-
self.list.front()
123+
self.list.last()
125124
}
126125

127126
#[inline]
128127
pub fn active_mut(&mut self) -> Option<&mut Document> {
129-
self.list.front_mut()
128+
self.list.last_mut()
130129
}
131130

132-
#[inline]
133131
pub fn update_active<F: FnMut(&Document) -> bool>(&mut self, mut func: F) -> bool {
134-
let mut cursor = self.list.cursor_front_mut();
135-
while let Some(doc) = cursor.current() {
136-
if func(doc) {
137-
let list = cursor.remove_current_as_list().unwrap();
138-
self.list.cursor_front_mut().splice_before(list);
139-
return true;
140-
}
141-
cursor.move_next();
132+
let Some(idx) = self.list.iter().rposition(&mut func) else {
133+
return false;
134+
};
135+
136+
// Already active (= last) document matched? Nothing to do.
137+
if idx == self.list.len() - 1 {
138+
return false;
142139
}
143-
false
140+
141+
// Otherwise, move the matched document to the end of the list so it becomes active.
142+
// Uses unsafe, because `rotate_left()` is horrendously bad with -Copt-level=s
143+
// (it's really almost comical) and I just don't tolerate that.
144+
// If I'm dead and you're looking to rewrite this use `list.push(list.remove(idx))`.
145+
unsafe {
146+
let beg = self.list.as_mut_ptr();
147+
let doc = beg.add(idx);
148+
let last = beg.add(self.list.len() - 1);
149+
let amount = self.list.len() - idx - 1;
150+
let mut temp = std::mem::MaybeUninit::<Document>::uninit();
151+
152+
// Make a backup of the document
153+
std::ptr::copy_nonoverlapping(doc, temp.as_mut_ptr(), 1);
154+
// Shift the rest to the front
155+
std::ptr::copy(doc.add(1), doc, amount);
156+
// Move the backup to the end
157+
std::ptr::copy_nonoverlapping(temp.as_ptr(), last, 1);
158+
}
159+
160+
true
144161
}
145162

146163
pub fn remove_active(&mut self) {
147-
self.list.pop_front();
164+
self.list.pop();
148165
}
149166

150167
pub fn add_untitled(&mut self) -> apperr::Result<&mut Document> {
@@ -160,8 +177,9 @@ impl DocumentManager {
160177
};
161178
self.gen_untitled_name(&mut doc);
162179

163-
self.list.push_front(doc);
164-
Ok(self.list.front_mut().unwrap())
180+
// In the future this could use push_mut, but it's unstable right now. As usual.
181+
self.list.push(doc);
182+
Ok(self.list.last_mut().unwrap())
165183
}
166184

167185
pub fn gen_untitled_name(&self, doc: &mut Document) {
@@ -231,8 +249,8 @@ impl DocumentManager {
231249
self.remove_active();
232250
}
233251

234-
self.list.push_front(doc);
235-
Ok(self.list.front_mut().unwrap())
252+
self.list.push(doc);
253+
Ok(self.list.last_mut().unwrap())
236254
}
237255

238256
pub fn reflow_all(&self) {

crates/edit/src/bin/edit/draw_editor.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ use edit::helpers::*;
88
use edit::icu;
99
use edit::input::{kbmod, vk};
1010
use edit::tui::*;
11+
use stdext::string_from_utf8_lossy_owned;
1112

1213
use crate::localization::*;
1314
use crate::state::*;
@@ -58,7 +59,7 @@ fn draw_search(ctx: &mut Context, state: &mut State) {
5859
// If the selection is empty, focus the search input field.
5960
// Otherwise, focus the replace input field, if it exists.
6061
if let Some(selection) = doc.buffer.borrow_mut().extract_user_selection(false) {
61-
state.search_needle = String::from_utf8_lossy_owned(selection);
62+
state.search_needle = string_from_utf8_lossy_owned(selection);
6263
focus = state.wants_search.kind;
6364
}
6465
}

crates/edit/src/bin/edit/draw_filepicker.rs

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ use edit::input::{kbmod, vk};
1111
use edit::tui::*;
1212
use edit::{icu, path};
1313
use stdext::arena::scratch_arena;
14+
use stdext::collections::BVec;
1415

1516
use crate::localization::*;
1617
use crate::state::*;
@@ -344,14 +345,7 @@ fn draw_dialog_saveas_refresh_files(state: &mut State) {
344345
entries.sort_unstable_by(|a, b| {
345346
let a = a.as_bytes();
346347
let b = b.as_bytes();
347-
348-
let a_is_dir = a.last() == Some(&b'/');
349-
let b_is_dir = b.last() == Some(&b'/');
350-
351-
match b_is_dir.cmp(&a_is_dir) {
352-
Ordering::Equal => icu::compare_strings(a, b),
353-
other => other,
354-
}
348+
icu::compare_strings(a, b)
355349
});
356350
}
357351

@@ -376,9 +370,10 @@ fn update_autocomplete_suggestions(state: &mut State) {
376370
// The problem is finding the upper bound. Here I'm using a trick:
377371
// By appending U+10FFFF (the highest possible Unicode code point)
378372
// we create a needle that naturally yields an upper bound.
379-
let mut needle_upper_bound = Vec::with_capacity_in(needle.len() + 4, &*scratch);
380-
needle_upper_bound.extend_from_slice(needle);
381-
needle_upper_bound.extend_from_slice(b"\xf4\x8f\xbf\xbf");
373+
let mut needle_upper_bound = BVec::empty();
374+
needle_upper_bound.reserve(&*scratch, needle.len() + 4);
375+
needle_upper_bound.extend_from_slice(&*scratch, needle);
376+
needle_upper_bound.extend_from_slice(&*scratch, b"\xf4\x8f\xbf\xbf");
382377

383378
if let Some(dirs_files) = &state.file_picker_entries {
384379
'outer: for entries in &dirs_files[1..] {

crates/edit/src/bin/edit/draw_menubar.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,7 @@ pub fn draw_dialog_about(ctx: &mut Context, state: &mut State) {
166166
ctx.attr_overflow(Overflow::TruncateHead);
167167
ctx.attr_position(Position::Center);
168168

169-
ctx.label("copyright", "Copyright (c) Microsoft Corp 2025");
169+
ctx.label("copyright", "Copyright (c) Microsoft Corporation");
170170
ctx.attr_overflow(Overflow::TruncateTail);
171171
ctx.attr_position(Position::Center);
172172

crates/edit/src/bin/edit/draw_statusbar.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ use edit::lsh::LANGUAGES;
1010
use edit::tui::*;
1111
use stdext::arena::scratch_arena;
1212
use stdext::arena_format;
13+
use stdext::collections::BVec;
1314

1415
use crate::localization::*;
1516
use crate::state::*;
@@ -347,14 +348,14 @@ fn encoding_picker_update_list(state: &mut State) {
347348

348349
let encodings = icu::get_available_encodings();
349350
let scratch = scratch_arena(None);
350-
let mut matches = Vec::new_in(&*scratch);
351+
let mut matches = BVec::empty();
351352

352353
for enc in encodings.all {
353354
let local_scratch = scratch_arena(Some(&scratch));
354355
let (score, _) = score_fuzzy(&local_scratch, enc.label, needle, true);
355356

356357
if score > 0 {
357-
matches.push((score, *enc));
358+
matches.push(&*scratch, (score, *enc));
358359
}
359360
}
360361

0 commit comments

Comments
 (0)