A simple CPU rendered GUI IDE experience.
feat: hov
bendn 4 months ago
parent 861504f · commit 94d92d6
-rw-r--r--.gitignore3
-rw-r--r--Cargo.toml7
-rw-r--r--src/hov.rs580
-rw-r--r--src/lsp.rs4
-rw-r--r--src/main.rs261
-rw-r--r--src/text.rs52
-rw-r--r--src/vec.md258
7 files changed, 731 insertions, 434 deletions
diff --git a/.gitignore b/.gitignore
index ffa3bbd..b354aec 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1 +1,2 @@
-Cargo.lock \ No newline at end of file
+Cargo.lock
+target/ \ No newline at end of file
diff --git a/Cargo.toml b/Cargo.toml
index ae21f93..732dbd1 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -49,7 +49,6 @@ arc-swap = "1.7.1"
tokio = { version = "1.47.1", features = ["rt-multi-thread"] }
regex-cursor = "0.1.5"
papaya = "0.2.3"
-minimad = { git = "https://github.com/Canop/minimad", branch = "keep-code-fence", version = "0.13.1" }
markdown = "1.0.0"
itertools = "0.14.0"
@@ -60,3 +59,9 @@ cc = "*"
debug = 2
# overflow-checks = true
# debug-assertions = true
+
+[profile.dev]
+incremental = false
+
+[patch.'https://github.com/bend-n/fimg']
+fimg = { "path" = "../fimg" }
diff --git a/src/hov.rs b/src/hov.rs
index 869ff75..7152069 100644
--- a/src/hov.rs
+++ b/src/hov.rs
@@ -1,4 +1,5 @@
-use std::iter::{once, repeat};
+use std::iter::{empty, once, repeat_n};
+use std::ops::Range;
use std::pin::pin;
use std::time::Instant;
use std::vec::Vec;
@@ -7,375 +8,274 @@ use dsb::cell::Style;
use dsb::{Cell, F};
use fimg::Image;
use itertools::Itertools;
-use minimad::*;
+use markdown::mdast::{self, Node};
use ropey::Rope;
+const D: Cell =
+ Cell { letter: None, style: Style { bg: BG, color: FG, flags: 0 } };
+use crate::{FG, text};
-use crate::{BG, FG, M, text};
-fn f(x: &Compound) -> u8 {
- let mut s = 0;
- if x.bold {
- s |= Style::BOLD;
- }
- if x.italic {
- s |= Style::ITALIC;
+struct Builder {
+ c: usize,
+ to: Vec<Cell>,
+ scratch: Vec<Cell>,
+}
+enum Action {
+ Put(Vec<Cell>),
+ Just(Vec<Cell>),
+ Finish,
+}
+impl Builder {
+ fn finish(&mut self) {
+ assert!(self.scratch.len() < self.c);
+ self.to.extend(&self.scratch);
+ self.to.extend(repeat_n(D, self.c - self.scratch.len()));
+ self.scratch.clear();
+ assert!(self.to.len() % self.c == 0);
}
- if x.strikeout {
- s |= Style::STRIKETHROUGH
+ fn put_wrapping(
+ &mut self,
+ mut c: impl Iterator<Item = Cell>,
+ mut l: usize,
+ ) {
+ self.to.extend(self.scratch.drain(..));
+ let fill = self.c - (self.to.len() % self.c);
+ self.to.extend(c.by_ref().take(fill));
+ l -= fill;
+ let n = l - (l % self.c);
+ self.to.extend(c.by_ref().take(n));
+ c.collect_into(&mut self.scratch);
+ assert!(self.to.len() % self.c == 0);
+ assert!(self.scratch.len() < self.c);
}
- s
-}
-fn markdown(c: usize, x: &str) -> Vec<Cell> {
- let mut cells = vec![];
- // println!(
- // "{:#?}",
- // markdown::to_mdast(
- // include_str!("../vec.md"),
- // &markdown::ParseOptions::default()
- // )
- // .unwrap()
- // );
- use Default::default;
- // std::fs::write(
- // "vec.ast",
- // format!(
- // "{:#?}",
- // minimad::parse_text(
- // include_str!("../vec.md"),
- // Options { keep_code_fences: true, ..default() },
- // )
- // .lines
- // ),
- // )
- // .unwrap();
- const D: Cell = Cell {
- letter: None,
- style: Style { bg: BG, color: FG, flags: 0 },
- };
- let mut l = minimad::parse_text(
- x,
- Options { keep_code_fences: true, ..default() },
- )
- .lines
- .into_iter()
- .peekable();
- 'l: while let Some(line) = l.next() {
- match line {
- minimad::Line::Normal(Composite { style, compounds }) => {
- let mut compounded = l
- .by_ref()
- .peeking_take_while(|x| matches!(x, Line::Normal(Composite{style, compounds}) if !matches!(style, CompositeStyle::Header(_))))
- .flat_map(|x| match x {
- Line::Normal(Composite { style, compounds }) =>
- compounds.into_iter().flat_map(move |x| {
- x.as_str().chars().zip(repeat(f(&x)).map(
- move |flags| match style {
- CompositeStyle::Paragraph =>
- Style {
- color: FG,
- bg: BG,
- flags,
- },
- CompositeStyle::Header(x) =>
- Style {
- color: [255; 3],
- bg: BG,
- flags: flags | Style::BOLD,
- },
- CompositeStyle::ListItem(x) =>
- Style {
- color: FG,
- bg: BG,
- flags,
- },
- CompositeStyle::Code => Style {
- color: [244,244,244],
- bg: [5,5,5],
- flags,
- },
- CompositeStyle::Quote => Style {
- color: [128; 3],
- bg: [0; 3],
- flags,
- },
- },
- ))
- }).chain(once((' ', Style { color: FG, bg: BG, flags : 0 }))),
- _ => panic!(),
- })
- .chunk_by(|x| x.0.is_whitespace());
- let mut compounded = compounded.into_iter();
- let mut out = vec![];
-
- // let mut compounds = compounds.iter();
- while let Some((x, word)) = compounded.next() {
- if x {
- continue;
- }
- if out.len() > c {
- panic!()
- }
- let word = word.collect::<Vec<_>>();
- if word.len() > c {
- let mut w = word.iter().map(|&(x, style)| Cell {
- letter: Some(x),
- style,
- });
- out.extend(w);
- cells.extend(
- out.drain(..out.len() - out.len() % c),
- );
- // 'out: loop {
- // while out.len() != c {
- // let Some(x) = w.next() else {
- // break 'out;
- // };
- // out.push(x);
- // }
- // cells.extend(&out);
- // out.clear();
- // }
- continue;
- // cells.extend(&out);
- // out.clear();
- // cells.extend(
- // w.by_ref()
- // .take(c.saturating_sub(out.len() + 1)),
- // );
- // cells.push(Cell::basic('-'));
- // cells.extend(w);
- }
+ fn put(&mut self, c: impl Iterator<Item = Cell> + Clone) {
+ let mut c = c.map(|x| match x.letter {
+ Some('\n') => x.style.empty(),
+ _ => x,
+ });
+ assert!(self.scratch.len() < self.c);
+ loop {
+ let n = c
+ .clone()
+ .take_while_inclusive(|x| {
+ !x.letter.is_none_or(char::is_whitespace)
+ })
+ .count();
+ let next = c.by_ref().take_while_inclusive(|x| {
+ !x.letter.is_none_or(char::is_whitespace)
+ });
- if out.len() + word.len() > c {
- assert_eq!(cells.len() % c, 0);
- cells.extend(&out);
- cells.extend(std::iter::repeat_n(
- D,
- c.saturating_sub(out.len()),
- ));
- dbg!(out.len(), c);
- out.clear();
- assert_eq!(cells.len() % c, 0);
- }
+ if n == 0 {
+ break;
+ }
- out.extend(word.iter().map(|&(x, style)| Cell {
- letter: Some(x),
- style,
- }));
- if out.len() != c {
- out.push(D);
- } else {
- cells.extend(&out);
- out.clear();
- }
- dbg!(out.len());
+ if n + self.scratch.len() < self.c {
+ self.scratch.extend(next);
+ } else if n < self.c {
+ self.finish();
+ self.scratch.extend(next);
+ } else {
+ self.put_wrapping(next.into_iter(), n);
+ }
+ }
+ // assert!(self.scratch.len() == 0);
+ // }
+ assert!(self.scratch.len() < self.c);
- // }
- }
- // if out.len() > c {
- // panic!();
- // }
- // if out.len() + next.char_length() > c {
- // cells.extend(&out);
- // cells.extend(std::iter::repeat_n(
- // Cell::default(),
- // c.saturating_sub(out.len()),
- // ));
- // out.clear();
- // assert_eq!(cells.len() % c, 0);
- // }
- // out.extend(
- // next.as_str()
- // .chars()
- // .map(|x| Cell { letter: Some(x), style }),
- // );
- // }
- if !out.is_empty() {
- assert!(out.len() <= c, "{} < c", out.len());
- cells.extend(&out);
- cells.extend(std::iter::repeat_n(
- D,
- c.saturating_sub(out.len()),
- ));
- assert_eq!(cells.len() % c, 0);
- }
- continue 'l;
+ // self.scratch.extend(after);
+ // self.scratch.extend(c);
+ assert!(self.to.len() % self.c == 0);
+ assert!(self.scratch.len() < self.c);
+ }
+ fn extend_(
+ &self,
+ n: &mdast::Node,
+ i: impl IntoIterator<Item = Action>,
+ inherit: Style,
+ ) -> Vec<Action> {
+ n.children()
+ .iter()
+ .flat_map(|i| *i)
+ .flat_map(|x| self.run(x, inherit))
+ .chain(i)
+ .collect()
+ }
+ fn extend(&self, n: &Node, inherit: Style) -> Vec<Action> {
+ self.extend_(n, empty(), inherit)
+ }
+ fn run(&self, node: &mdast::Node, mut inherit: Style) -> Vec<Action> {
+ match node {
+ Node::Break(_) => vec![Action::Finish],
+ Node::InlineCode(x) => {
+ inherit.bg = [21, 24, 30];
+ vec![Action::Put(
+ x.value.chars().map(|x| inherit.basic(x)).collect(),
+ )]
+ }
+ // Node::Html(Html { value: "code", .. }) => {}
+ Node::Delete(_) => todo!(),
+ Node::Emphasis(_) => {
+ inherit.flags |= Style::ITALIC;
+ self.extend(node, inherit | Style::ITALIC)
+ }
+ Node::Link(_) => {
+ inherit.flags |= Style::UNDERLINE;
+ inherit.color = [244, 196, 98];
+ self.extend(node, inherit)
+ }
+ Node::LinkReference(_) => {
+ inherit.flags |= Style::UNDERLINE;
+ inherit.color = [244, 196, 98];
+ self.extend(node, inherit)
+ }
+ Node::Heading(_) => {
+ inherit.flags |= Style::BOLD;
+ inherit.color = [255, 255, 255];
+ self.extend_(node, [Action::Finish], inherit)
}
- minimad::Line::TableRow(table_row) => todo!(),
- minimad::Line::TableRule(table_rule) => todo!(),
- minimad::Line::HorizontalRule => vec![Cell::basic('-')],
- minimad::Line::CodeFence(Composite {
- compounds: [Compound { src: lang, .. }],
- ..
- }) => {
- let mut r = Rope::new();
- while let Some(x) = l.next() {
- match x {
- Line::CodeFence(Composite {
- compounds: [],
- ..
- }) => {
- break;
- }
- Line::Normal(Composite {
- compounds: [Compound { src, .. }],
- ..
- }) => {
- r.insert(r.len_chars(), src);
- r.insert_char(r.len_chars(), '\n');
- }
- _ => {}
- }
- }
- let mut cell = vec![D; c * r.len_lines()];
+
+ Node::Strong(_) => self.extend(node, inherit | Style::BOLD),
+ Node::Text(text) => vec![Action::Put(
+ text.value
+ .chars()
+ .map(|x| if x == '\n' { ' ' } else { x })
+ .map(|x| inherit.basic(x))
+ .collect(),
+ )],
+ Node::Code(code) => {
+ let r = Rope::from_str(&code.value);
+ let mut cell = vec![D; self.c * r.len_lines()];
for (l, y) in r.lines().zip(0..) {
- for (e, x) in l.chars().take(c).zip(0..) {
+ for (e, x) in l.chars().take(self.c).zip(0..) {
if e != '\n' {
- cell[y * c + x].letter = Some(e);
+ cell[y * self.c + x].letter = Some(e);
}
}
}
for ((x1, y1), (x2, y2), s, txt) in
std::iter::from_coroutine(pin!(text::hl(
- text::LOADER.language_for_name(lang).unwrap_or(
- text::LOADER
- .language_for_name("rust")
- .unwrap()
- ),
+ text::LOADER
+ .language_for_name(
+ code.lang.as_deref().unwrap_or("rust")
+ )
+ .unwrap_or(
+ text::LOADER
+ .language_for_name("rust")
+ .unwrap()
+ ),
&r,
..,
- c,
+ self.c,
)))
{
- cell.get_mut(y1 * c + x1..y2 * c + x2).map(|x| {
- x.iter_mut().zip(txt.chars()).for_each(|(x, c)| {
- x.style |= s;
- x.letter = Some(c);
- })
- });
+ cell.get_mut(y1 * self.c + x1..y2 * self.c + x2).map(
+ |x| {
+ x.iter_mut().zip(txt.chars()).for_each(
+ |(x, c)| {
+ x.style |= s;
+ x.letter = Some(c);
+ },
+ )
+ },
+ );
}
- cells.extend(cell);
- assert_eq!(cells.len() % c, 0);
- continue 'l;
+ assert_eq!(self.to.len() % self.c, 0);
+ vec![Action::Finish, Action::Just(cell)]
}
- _ => panic!(),
- };
- // if out.len() > c {
- // out.drain(c - 1..);
- // out.push(Cell::basic('…'));
- // } else {
- // out.extend(std::iter::repeat_n(
- // Cell::default(),
- // c.saturating_sub(out.len()),
- // ));
- // }
- // assert_eq!(out.len(), c);
- // cells.extend(out);
+ Node::ThematicBreak(_) => {
+ vec![Action::Finish, Action::Finish]
+ }
+
+ Node::Paragraph(paragraph) => paragraph
+ .children
+ .iter()
+ .flat_map(|c| self.run(&c, inherit))
+ .chain([Action::Finish, Action::Finish])
+ .collect(),
+ n => self.extend(n, inherit),
+ }
}
- assert_eq!(cells.len() % c, 0);
- cells
}
-
+pub fn p(x: &str) -> Option<Node> {
+ markdown::to_mdast(x, &markdown::ParseOptions::gfm()).ok()
+}
+fn len(node: &mdast::Node) -> Vec<usize> {
+ match node {
+ Node::Break(_) => vec![usize::MAX],
+ Node::InlineCode(x) => vec![x.value.chars().count()],
+ Node::Code(x) => once(usize::MAX)
+ .chain(
+ Iterator::intersperse(
+ x.value.lines().map(|l| l.chars().count()),
+ usize::MAX,
+ )
+ .chain([usize::MAX]),
+ )
+ .collect(),
+ // Node::Html(Html { value: "code", .. }) => {}
+ Node::Text(text) => vec![text.value.chars().count()],
+ Node::Paragraph(x) =>
+ x.children.iter().flat_map(len).chain([usize::MAX]).collect(),
+ n => n.children().iter().flat_map(|x| *x).flat_map(len).collect(),
+ }
+}
+pub fn l(node: &Node) -> Vec<usize> {
+ len(node)
+ .into_iter()
+ .chunk_by(|&x| x != usize::MAX)
+ .into_iter()
+ .filter_map(|x| x.0.then(|| x.1.sum::<usize>()))
+ .collect::<Vec<_>>()
+}
+#[implicit_fn::implicit_fn]
+pub fn markdown2(c: usize, x: &Node) -> Vec<Cell> {
+ let mut r = Builder { c, to: vec![], scratch: vec![] };
+ for (is_put, act) in r
+ .run(&x, Style { bg: BG, color: FG, flags: 0 })
+ .into_iter()
+ .chunk_by(|x| matches!(x, Action::Put(_)))
+ .into_iter()
+ {
+ if is_put {
+ r.put(
+ act.flat_map(|x| match x {
+ Action::Put(x) => x,
+ _ => panic!(),
+ })
+ .collect::<Vec<_>>()
+ .into_iter(),
+ );
+ } else {
+ for act in act {
+ match act {
+ Action::Put(cells) => r.put(cells.into_iter()),
+ Action::Just(cells) => r.to.extend(cells),
+ Action::Finish => r.finish(),
+ }
+ }
+ }
+ }
+ if r.to.iter().take(c).all(_.letter.is_none()) {
+ r.to.drain(..c);
+ }
+ if r.to.iter().rev().take(c).all(_.letter.is_none()) {
+ r.to.drain(r.to.len() - c..);
+ }
+ r.to
+}
+pub const BG: [u8; 3] = text::color(b"191E27");
#[test]
fn t() {
let ppem = 18.0;
let lh = 10.0;
- let (w, h) = (800, 8000);
+ let (w, h) = (400, 8000);
let (c, r) = dsb::fit(&crate::FONT, ppem, lh, (w, h));
- std::fs::write(
- "mdast2",
- format!(
- "{:#?}",
- markdown::to_mdast(
- include_str!("../vec.md"),
- &markdown::ParseOptions::gfm()
- )
- .unwrap()
- ),
- );
- let mut cells = markdown(c, include_str!("../vec.md"));
- // use Default::default;
- // for line in dbg!(minimad::parse_text(
- // include_str!("../vec.md"),
- // Options { keep_code_fences: true, ..default() },
- // ))
- // .lines
- // {
- // fn f(x: &Compound) -> u8 {
- // let mut s = 0;
- // if x.bold {
- // s |= Style::BOLD;
- // }
- // if x.italic {
- // s |= Style::ITALIC;
- // }
- // if x.strikeout {
- // s |= Style::STRIKETHROUGH
- // }
- // s
- // }
- // let mut out = match line {
- // minimad::Line::Normal(Composite { style, compounds }) =>
- // compounds
- // .iter()
- // .flat_map(|c| {
- // c.as_str().chars().map(move |x| {
- // let flags = f(&c);
- // let style = match style {
- // CompositeStyle::Paragraph => Style {
- // color: [128; 3],
- // bg: [0; 3],
- // flags,
- // },
- // CompositeStyle::Header(x) => Style {
- // color: [255; 3],
- // bg: [0; 3],
- // flags: flags | Style::BOLD,
- // },
- // CompositeStyle::ListItem(x) => Style {
- // color: [128; 3],
- // bg: [0; 3],
- // flags,
- // },
- // CompositeStyle::Code => Style {
- // color: [100; 3],
- // bg: [0; 3],
- // flags,
- // },
- // CompositeStyle::Quote => Style {
- // color: [128; 3],
- // bg: [0; 3],
- // flags,
- // },
- // };
- // Cell { letter: Some(x), style }
- // })
- // })
- // .collect::<Vec<_>>(),
- // minimad::Line::TableRow(table_row) => todo!(),
- // minimad::Line::TableRule(table_rule) => todo!(),
- // minimad::Line::HorizontalRule => vec![Cell::basic('-')],
- // minimad::Line::CodeFence(composite) => {
- // vec![]
- // }
- // };
- // if out.len() > c {
- // out.drain(c - 1..);
- // out.push(Cell::basic('…'));
- // } else {
- // out.extend(std::iter::repeat_n(
- // Cell::default(),
- // c - out.len(),
- // ));
- // }
- // assert_eq!(out.len(), c);
- // cells.extend(out);
- // }
+
+ let cells = markdown2(c, &p(include_str!("vec.md")).unwrap());
+ dbg!(l(&p(include_str!("vec.md")).unwrap()));
dbg!(cells.len() / c);
- // let (fw, fh) = dsb::dims(&FONT, ppem);
dbg!(w, h);
dbg!(c, r);
- // panic!();
let mut fonts = dsb::Fonts::new(
F::FontRef(*crate::FONT, &[(2003265652, 550.0)]),
@@ -401,3 +301,7 @@ fn t() {
println!("{:?}", now.elapsed());
x.as_ref().save("x");
}
+pub struct Hovr {
+ pub(crate) span: Option<Range<usize>>,
+ pub(crate) item: crate::text::CellBuffer,
+}
diff --git a/src/lsp.rs b/src/lsp.rs
index e5c8ae4..39a0e0e 100644
--- a/src/lsp.rs
+++ b/src/lsp.rs
@@ -117,6 +117,10 @@ impl Client {
}
pub fn rq_semantic_tokens(&self, f: &Path) -> anyhow::Result<()> {
debug!("requested semantic tokens");
+ let Some(b"rs") = f.extension().map(|x| x.as_encoded_bytes())
+ else {
+ return Ok(());
+ };
let mut p = self.semantic_tokens.1.lock();
if let Some((h, task)) = &*p {
if !h.is_finished() {
diff --git a/src/main.rs b/src/main.rs
index e0f5289..4f04d36 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,13 +1,17 @@
// this looks pretty good though
#![feature(tuple_trait, unboxed_closures, fn_traits)]
#![feature(
+ iter_intersperse,
stmt_expr_attributes,
+ new_range_api,
+ iter_collect_into,
mpmc_channel,
const_cmp,
- // generator_trait,
gen_blocks,
const_default,
- coroutines,iter_from_coroutine,coroutine_trait,
+ coroutines,
+ iter_from_coroutine,
+ coroutine_trait,
cell_get_cloned,
import_trait_associated_functions,
if_let_guard,
@@ -18,33 +22,34 @@
portable_simd
)]
#![allow(incomplete_features, redundant_semicolons)]
-use std::convert::identity;
+use std::borrow::Cow;
use std::io::BufReader;
-use std::mem::forget;
use std::num::NonZeroU32;
use std::path::{Path, PathBuf};
use std::process::{Command, Stdio};
-use std::rc::Rc;
use std::sync::{Arc, LazyLock, OnceLock};
use std::thread;
use std::time::Instant;
use Default::default;
use NamedKey::*;
+use atools::prelude::AASAdd;
use diff_match_patch_rs::PatchInput;
use dsb::cell::Style;
use dsb::{Cell, F};
-use fimg::Image;
+use fimg::{Image, OverlayAt};
use lsp_types::request::HoverRequest;
use lsp_types::{
- Hover, HoverParams, Position, SemanticTokensOptions, SemanticTokensServerCapabilities, ServerCapabilities, TextDocumentIdentifier, TextDocumentPositionParams, WorkspaceFolder
+ Hover, HoverParams, MarkedString, Position, SemanticTokensOptions,
+ SemanticTokensServerCapabilities, ServerCapabilities,
+ TextDocumentIdentifier, TextDocumentPositionParams, WorkspaceFolder,
};
-use minimad::{Composite, CompositeStyle};
-use parking_lot::RwLock;
+use parking_lot::Mutex;
use regex::Regex;
use ropey::Rope;
use rust_fsm::StateMachineImpl;
use swash::{FontRef, Instance};
+use tokio::task::spawn_blocking;
use url::Url;
use winit::event::{
ElementState, Event, MouseButton, MouseScrollDelta, WindowEvent,
@@ -55,12 +60,13 @@ use winit::platform::wayland::WindowAttributesExtWayland;
use winit::window::{Icon, Window};
use crate::bar::Bar;
+use crate::hov::Hovr;
use crate::text::{Diff, TextArea};
mod bar;
+pub mod hov;
mod lsp;
mod text;
mod winit_app;
-mod hov;
fn main() {
env_logger::init();
// lsp::x();
@@ -185,38 +191,44 @@ pub(crate) fn entry(event_loop: EventLoop<()>) {
.as_ref()
.and_then(|x| rooter(&x.parent().unwrap()))
.and_then(|x| x.canonicalize().ok());
- let c = workspace.zip(origin.clone()).map(|(workspace, origin)| {
- let mut c = Command::new("rust-analyzer")
- .stdin(Stdio::piped())
- .stdout(Stdio::piped())
- .stderr(Stdio::inherit())
- .spawn()
- .unwrap();
+ let c = workspace.as_ref().zip(origin.clone()).map(
+ |(workspace, origin)| {
+ let mut c = Command::new("rust-analyzer")
+ .stdin(Stdio::piped())
+ .stdout(Stdio::piped())
+ .stderr(Stdio::inherit())
+ .spawn()
+ .unwrap();
- let (c, t, t2, changed) = lsp::run(
- lsp_server::stdio::stdio_transport(
- BufReader::new(c.stdout.take().unwrap()),
- c.stdin.take().unwrap(),
- ),
- WorkspaceFolder {
- uri: Url::from_file_path(&workspace).unwrap(),
- name: workspace
- .file_name()
- .unwrap()
- .to_string_lossy()
- .into_owned(),
- },
- );
- c.open(&origin, std::fs::read_to_string(&origin).unwrap())
- .unwrap();
- ((c, origin), (t, t2), changed)
- });
- let (lsp, t, ch) = match c {
- Some((a,b,c)) => {
- (Some(a), Some(b), Some(c))
+ let (c, t, t2, changed) = lsp::run(
+ lsp_server::stdio::stdio_transport(
+ BufReader::new(c.stdout.take().unwrap()),
+ c.stdin.take().unwrap(),
+ ),
+ WorkspaceFolder {
+ uri: Url::from_file_path(&workspace).unwrap(),
+ name: workspace
+ .file_name()
+ .unwrap()
+ .to_string_lossy()
+ .into_owned(),
+ },
+ );
+ c.open(&origin, std::fs::read_to_string(&origin).unwrap())
+ .unwrap();
+ (c, (t, t2), changed)
},
- None => { (None, None, None) }
- };
+ );
+ let (lsp, t, ch) = match c {
+ Some((a, b, c)) => (Some(a), Some(b), Some(c)),
+ None => (None, None, None),
+ };
+ macro_rules! lsp {
+ () => {
+ lsp.as_ref().zip(origin.as_deref())
+ };
+ }
+ let hovering = &*Box::leak(Box::new(Mutex::new(None::<hov::Hovr>)));
// let mut hl_result = None;
@@ -236,13 +248,13 @@ pub(crate) fn entry(event_loop: EventLoop<()>) {
}
macro_rules! change {
() => {
- lsp.as_ref().map(|(x, origin)| {
+ lsp!().map(|(x, origin)| {
x.edit(&origin, text.rope.to_string()).unwrap();
x.rq_semantic_tokens(origin).unwrap();
});
};
}
- lsp.as_ref().map(|(x, origin)| x.rq_semantic_tokens(origin).unwrap());
+ lsp!().map(|(x, origin)| x.rq_semantic_tokens(origin).unwrap());
let mut mtime = modify!();
macro_rules! save {
() => {{
@@ -341,6 +353,9 @@ pub(crate) fn entry(event_loop: EventLoop<()>) {
if size.height != 0 && size.width != 0 {
let now = Instant::now();
+ if c*r!=cells.len(){
+ return;
+ }
cells.fill(Cell {
style: Style { color: BG, bg: BG, flags: 0 },
letter: None,
@@ -392,7 +407,7 @@ pub(crate) fn entry(event_loop: EventLoop<()>) {
}
},
origin.as_deref(),
- lsp.as_ref().and_then(|(x, _)| { match &x.initialized {
+ lsp!().and_then(|(x, _)| { match &x.initialized {
Some(lsp_types::InitializeResult {
capabilities: ServerCapabilities {
semantic_tokens_provider:
@@ -411,11 +426,11 @@ pub(crate) fn entry(event_loop: EventLoop<()>) {
r - 1,
origin
.as_ref()
- .map(|x| x.to_str().unwrap())
+ .map(|x| workspace.as_ref().and_then(|w| x.strip_prefix(w).ok()).unwrap_or(&x).to_str().unwrap())
.unwrap_or("new buffer"),
&state,
&text,
- lsp.as_ref().map(|x| &x.0)
+ lsp.as_ref()
);
unsafe {
dsb::render(
@@ -427,13 +442,52 @@ pub(crate) fn entry(event_loop: EventLoop<()>) {
ls,
true,
i.as_mut(),
- )
+ )
};
+ hovering.lock().as_ref().map(|x| x.span.clone().map(|sp| {
+ let met = FONT.metrics(&[]);
+ let fac = ppem / met.units_per_em as f32;
+ let [(_x, _y), (_x2, _)] = text.position(sp);
+ let [_x, _x2] = [_x, _x2].add(text.line_number_offset()+1);
+ let _y = _y - text.vo;
+ if !(cursor_position.1 == _y && (_x..=_x2).contains(&cursor_position.0)) {
+ return;
+ }
+ let position = (
+ ((_x) as f32 * fw).round() as usize,
+ ((_y as f32 as f32) * (fh + ls * fac)).round() as usize,
+ );
+
+ let ppem = 18.0;
+ let ls = 10.0;
+ let r = x.item.l().min(15);
+ let c = x.item.displayable(r);
+ let (w, h) = dsb::size(&fonts.regular, ppem, ls, (x.item.c, r));
+ let top = position.1.checked_sub(h).unwrap_or((((_y + 1) as f32) * (fh + ls * fac)).round() as usize,);
+ let left =
+ if position.0 + w as usize > window.inner_size().width as usize {
+ window.inner_size().width as usize- w as usize
+ } else { position.0 };
+
+ let mut i2 = Image::build(w as _, h as _).fill(BG);
+ unsafe{ dsb::render(
+ &c,
+ (x.item.c, 0),
+ ppem,
+ hov::BG,
+ &mut fonts,
+ ls,
+ true,
+ i2.as_mut(),
+ )};
+ // dbg!(w, h, i2.width(), i2.height(), window.inner_size(), i.width(),i.height());
+ unsafe { i.overlay_at(&i2.as_ref(), left as u32, top as u32) };
+ i.r#box((left .saturating_sub(1) as _, top.saturating_sub(1) as _), i2.width(), i2.height(), [0;3]);
+ }));
let met = FONT.metrics(&[]);
let fac = ppem / met.units_per_em as f32;
// if x.view_o == Some(x.cells.row) || x.view_o.is_none() {
- use fimg::OverlayAt;
let (fw, fh) = dsb::dims(&FONT, ppem);
let cursor =
Image::<_, 4>::build(3, (fh).ceil() as u32)
@@ -509,41 +563,76 @@ pub(crate) fn entry(event_loop: EventLoop<()>) {
text.cursor = x;
*state.sel() = x..x;
}
- Some(Do::Hover) => {
- let hover = text.index_at(cursor_position);
+ Some(Do::Hover) if let Some(hover) = text.raw_index_at(cursor_position) => {
+ assert_eq!(hover, text.index_at(cursor_position));
let (x, y) =text.xy(hover);
- let s = lsp.as_ref().map(|(c, o)| {
- let (rx, id) = c.request::<HoverRequest>(&HoverParams {
- text_document_position_params: TextDocumentPositionParams { text_document: TextDocumentIdentifier::new(Url::from_file_path(o).unwrap()), position: Position {
- line: y as _, character: x as _,
- }},
- work_done_progress_params:default() }).unwrap();
- c.runtime.spawn(async {
- let x = rx.await?.load::<Hover>()?;
- // dbg!(&x);
- match x.contents {
- lsp_types::HoverContents::Scalar(marked_string) => {
- println!("{marked_string:?}");
- },
- lsp_types::HoverContents::Array(marked_strings) => {
- println!("{marked_strings:?}");
-
- },
- lsp_types::HoverContents::Markup(markup_content) => {
- println!("{}", markup_content.value);
- // dbg!(minimad::Text::from(&*markup_content.value));
- },
- }
- anyhow::Ok(())
- })
+ let text = text.clone();
+ {
+ let mut l = hovering.lock();
+ if let Some(Hovr{ span: Some(span),..}) = &*l {
+ let [(_x, _y), (_x2, _)] = text.position(span.clone());
+ let [_x, _x2] = [_x, _x2].add(text.line_number_offset()+1);
+ let _y = _y - text.vo;
+ if cursor_position.1 == _y && (_x.._x2).contains(&cursor_position.0) {
+ return
+ } else {
+ *l = None;
+ window.request_redraw();
+ }
+ }
+ }
+ let hovering = hovering;
+ lsp!().map(|(cl, o)| {
+let window = window.clone();
+static RUNNING: LazyLock< papaya::HashSet<usize, >>= LazyLock::new(||papaya::HashSet::new());
+if !RUNNING.insert(hover, &RUNNING.guard()) {return}
+let (rx, _) = cl.request::<HoverRequest>(&HoverParams {
+text_document_position_params: TextDocumentPositionParams { text_document: TextDocumentIdentifier::new(Url::from_file_path(o).unwrap()), position: Position {
+ line: y as _, character: x as _,
+}},
+work_done_progress_params:default() }).unwrap();
+cl.runtime.spawn(async move {
+ let x = rx.await?.load::<Hover>()?;
+ let (w, cells) = spawn_blocking(move || {
+ let x = match &x.contents {
+ lsp_types::HoverContents::Scalar(marked_string) => {
+ match marked_string{
+ MarkedString::LanguageString(x) =>Cow::Borrowed(&*x.value),
+ MarkedString::String(x) => Cow::Borrowed(&**x),
+ }
+ },
+ lsp_types::HoverContents::Array(marked_strings) => {
+ Cow::Owned(marked_strings.iter().map(|x| match x{
+ MarkedString::LanguageString(x) => &*x.value,
+ MarkedString::String(x) => &*x,
+ }).collect::<String>())
+ },
+ lsp_types::HoverContents::Markup(markup_content) => {
+ Cow::Borrowed(&*markup_content.value)
+ },
+ };
+ let x = hov::p(&x).unwrap();
+ let m = hov::l(&x).into_iter().max().map(_+2).unwrap_or(usize::MAX).min(c-10);
+ (m, hov::markdown2(m, &x))
+}).await.unwrap();
+RUNNING.remove(&hover,&RUNNING.guard());
+ let span = x.range.and_then(|x| {
+ Some(text.l_position(x.start).ok()?..text.l_position(x.end).ok()?)
+ });
+ *hovering.lock()= Some( hov::Hovr { span, item: text::CellBuffer { c: w, vo: 0, cells: cells.into() }}.into());
+ window.request_redraw();
+ anyhow::Ok(())
+});
});
-
+ }
+ Some(Do::Hover) => {
+ *hovering.lock() = None;
+ window.request_redraw();
}
None => {}
x => unreachable!("{x:?}"),
}
}
-
Event::WindowEvent {
event:
WindowEvent::MouseInput { state: bt, button, .. },
@@ -796,12 +885,17 @@ pub(crate) fn entry(event_loop: EventLoop<()>) {
_ => {}
};
},
- );
- ch.map(|ch| thread::Builder::new().name("redrawer".into()).spawn(move || {
- for () in ch {
- PUT.get().map(|x| x.request_redraw());
- }
- }));
+ );
+ ch.map(|ch| {
+ thread::Builder::new().name("redrawer".into()).spawn(move || {
+ for () in ch {
+ PUT.get().map(|x| {
+ x.request_redraw();
+ println!("rq redraw");
+ });
+ }
+ })
+ });
winit_app::run_app(event_loop, app);
}
@@ -886,7 +980,7 @@ impl State {
}
}
-use std::ops::Range;
+use std::ops::{Not, Range};
rust_fsm::state_machine! {
#[derive(Clone, Debug)]
@@ -907,8 +1001,7 @@ Default => {
M(MouseButton => MouseButton::Left) => _ [MoveCursor],
C(((usize, usize)) => .. if unsafe { CLICKING }) => Selection(0..0) [StartSelection],
Changed => RequestBoolean(BoolRequest => BoolRequest::ReloadFile),
- C(_ if false) => _ [Hover],
- C(_) => _,
+ C(_) => _ [Hover],
K(_) => _ [Edit],
M(_) => _,
},
diff --git a/src/text.rs b/src/text.rs
index f35539f..cd22f64 100644
--- a/src/text.rs
+++ b/src/text.rs
@@ -2,7 +2,7 @@ use std::cmp::min;
use std::fmt::{Debug, Display};
use std::ops::{Deref, Not as _, Range, RangeBounds};
use std::path::Path;
-use std::pin::{Pin, pin};
+use std::pin::pin;
use std::sync::{Arc, LazyLock};
use std::vec::Vec;
@@ -11,17 +11,14 @@ use diff_match_patch_rs::{DiffMatchPatch, Patches};
use dsb::Cell;
use dsb::cell::Style;
use helix_core::Syntax;
-use helix_core::syntax::{HighlightEvent, Loader, reconfigure_highlights};
+use helix_core::syntax::{HighlightEvent, Loader};
use implicit_fn::implicit_fn;
use log::error;
-use lsp_types::{
- SemanticToken, SemanticTokensLegend, SemanticTokensServerCapabilities,
-};
+use lsp_types::{Position, SemanticToken, SemanticTokensLegend};
use ropey::{Rope, RopeSlice};
-use tree_house::{Language, fixtures};
+use tree_house::Language;
use winit::keyboard::{NamedKey, SmolStr};
-use crate::MODIFIERS;
use crate::text::semantic::{MCOLORS, MODIFIED, MSTYLE};
macro_rules! theme {
($n:literal $($x:literal $color:literal $($style:expr)?),+ $(,)?) => {
@@ -144,7 +141,7 @@ const fn of(x: &'static str) -> usize {
panic!()
}
-const fn color(x: &[u8; 6]) -> [u8; 3] {
+pub const fn color(x: &[u8; 6]) -> [u8; 3] {
car::map!(
car::map!(x, |b| (b & 0xF) + 9 * (b >> 6)).chunked::<2>(),
|[a, b]| a * 16 + b
@@ -202,6 +199,29 @@ pub struct TextArea {
pub r: usize,
pub c: usize,
}
+
+pub struct CellBuffer {
+ pub c: usize,
+ pub vo: usize,
+ pub cells: Box<[Cell]>,
+}
+impl Deref for CellBuffer {
+ type Target = [Cell];
+
+ fn deref(&self) -> &Self::Target {
+ &self.cells
+ }
+}
+
+impl CellBuffer {
+ pub fn displayable(&self, r: usize) -> &[Cell] {
+ &self[self.vo * self.c..((self.vo + r) * self.c).min(self.len())]
+ }
+ pub fn l(&self) -> usize {
+ self.len() / self.c
+ }
+}
+
impl Debug for TextArea {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("TextArea")
@@ -262,6 +282,13 @@ impl TextArea {
.min(self.rope.len_chars())
}
+ pub fn raw_index_at(&self, (x, y): (usize, usize)) -> Option<usize> {
+ let x = x.checked_sub(self.line_number_offset() + 1)?;
+ Some(self.vo + y)
+ .filter(|&l| self.rope.line(l).len_chars() > x)
+ .and_then(|l| Some(self.rope.try_line_to_char(l).ok()? + x))
+ }
+
pub fn insert_(&mut self, c: SmolStr) {
self.rope.insert(self.cursor, &c);
self.cursor += c.chars().count();
@@ -574,6 +601,12 @@ impl TextArea {
&mut cell[y1 * c + x1..y2 * c + x2]
}
+ pub fn l_position(&self, p: Position) -> Result<usize, ropey::Error> {
+ Ok(self.rope.try_line_to_char(p.line as _)?
+ + (p.character as usize)
+ .min(self.rope.line(p.line as _).len_chars()))
+ }
+
#[implicit_fn]
pub fn write_to<'lsp>(
&mut self,
@@ -629,7 +662,7 @@ impl TextArea {
+ ch as usize
+ t.length as usize,
)? - self.rope.try_line_to_char(ln as _)?;
- (x1, x2)
+ (x1.min(self.c), x2.min(self.c))
};
let Ok((x1, x2)) = x else {
continue;
@@ -1036,7 +1069,6 @@ pub fn hl(
// );
#[coroutine]
static move || {
- println!("`\n{text}\n`");
let syntax = Syntax::new(text.slice(..), lang, &LOADER).unwrap();
let mut h = syntax.highlighter(text.slice(..), &LOADER, r);
let mut at = 0;
diff --git a/src/vec.md b/src/vec.md
new file mode 100644
index 0000000..7554be7
--- /dev/null
+++ b/src/vec.md
@@ -0,0 +1,258 @@
+```rust
+alloc::vec
+```
+
+```rust
+pub struct Vec<T, A = Global>
+where
+ A: Allocator,
+{
+ buf: RawVec<T, A>,
+ len: usize,
+}
+```
+
+---
+
+A contiguous growable array type, written as `Vec<T>`, short for 'vector'.
+
+# Examples
+
+```rust
+let mut vec = Vec::new();
+vec.push(1);
+vec.push(2);
+
+assert_eq!(vec.len(), 2);
+assert_eq!(vec[0], 1);
+
+assert_eq!(vec.pop(), Some(2));
+assert_eq!(vec.len(), 1);
+
+vec[0] = 7;
+assert_eq!(vec[0], 7);
+
+vec.extend([1, 2, 3]);
+
+for x in &vec {
+ println!("{x}");
+}
+assert_eq!(vec, [7, 1, 2, 3]);
+```
+
+The [`vec`](https://doc.rust-lang.org/nightly/alloc/macros/macro.vec.html) macro is provided for convenient initialization:
+
+```rust
+let mut vec1 = vec![1, 2, 3];
+vec1.push(4);
+let vec2 = Vec::from([1, 2, 3, 4]);
+assert_eq!(vec1, vec2);
+```
+
+It can also initialize each element of a `Vec<T>` with a given value.
+This may be more efficient than performing allocation and initialization
+in separate steps, especially when initializing a vector of zeros:
+
+```rust
+let vec = vec![0; 5];
+assert_eq!(vec, [0, 0, 0, 0, 0]);
+
+// The following is equivalent, but potentially slower:
+let mut vec = Vec::with_capacity(5);
+vec.resize(5, 0);
+assert_eq!(vec, [0, 0, 0, 0, 0]);
+```
+
+For more information, see
+[Capacity and Reallocation](https://doc.rust-lang.org/nightly/alloc/vec/struct.Vec.html#capacity-and-reallocation).
+
+Use a `Vec<T>` as an efficient stack:
+
+```rust
+let mut stack = Vec::new();
+
+stack.push(1);
+stack.push(2);
+stack.push(3);
+
+while let Some(top) = stack.pop() {
+ // Prints 3, 2, 1
+ println!("{top}");
+}
+```
+
+# Indexing
+
+The `Vec` type allows access to values by index, because it implements the
+[`Index`](https://doc.rust-lang.org/nightly/core/ops/index/trait.Index.html) trait. An example will be more explicit:
+
+```rust
+let v = vec![0, 2, 4, 6];
+println!("{}", v[1]); // it will display '2'
+```
+
+However be careful: if you try to access an index which isn't in the `Vec`,
+your software will panic! You cannot do this:
+
+```rust
+let v = vec![0, 2, 4, 6];
+println!("{}", v[6]); // it will panic!
+```
+
+Use [`get`] and [`get_mut`] if you want to check whether the index is in
+the `Vec`.
+
+# Slicing
+
+A `Vec` can be mutable. On the other hand, slices are read-only objects.
+To get a [slice](https://doc.rust-lang.org/nightly/core/slice/index.html), use [`&`](`&`). Example:
+
+```rust
+fn read_slice(slice: &[usize]) {
+ // ...
+}
+
+let v = vec![0, 1];
+read_slice(&v);
+
+// ... and that's all!
+// you can also do it like this:
+let u: &[usize] = &v;
+// or like this:
+let u: &[_] = &v;
+```
+
+In Rust, it's more common to pass slices as arguments rather than vectors
+when you just want to provide read access. The same goes for [`String`] and
+[`&str`].
+
+# Capacity and reallocation
+
+The capacity of a vector is the amount of space allocated for any future
+elements that will be added onto the vector. This is not to be confused with
+the _length_ of a vector, which specifies the number of actual elements
+within the vector. If a vector's length exceeds its capacity, its capacity
+will automatically be increased, but its elements will have to be
+reallocated.
+
+For example, a vector with capacity 10 and length 0 would be an empty vector
+with space for 10 more elements. Pushing 10 or fewer elements onto the
+vector will not change its capacity or cause reallocation to occur. However,
+if the vector's length is increased to 11, it will have to reallocate, which
+can be slow. For this reason, it is recommended to use [`Vec::with_capacity`](https://doc.rust-lang.org/nightly/alloc/vec/struct.Vec.html#method.with_capacity)
+whenever possible to specify how big the vector is expected to get.
+
+# Guarantees
+
+Due to its incredibly fundamental nature, `Vec` makes a lot of guarantees
+about its design. This ensures that it's as low-overhead as possible in
+the general case, and can be correctly manipulated in primitive ways
+by unsafe code. Note that these guarantees refer to an unqualified `Vec<T>`.
+If additional type parameters are added (e.g., to support custom allocators),
+overriding their defaults may change the behavior.
+
+Most fundamentally, `Vec` is and always will be a (pointer, capacity, length)
+triplet. No more, no less. The order of these fields is completely
+unspecified, and you should use the appropriate methods to modify these.
+The pointer will never be null, so this type is null-pointer-optimized.
+
+However, the pointer might not actually point to allocated memory. In particular,
+if you construct a `Vec` with capacity 0 via [`Vec::new`](https://doc.rust-lang.org/nightly/alloc/vec/struct.Vec.html#method.new), [`vec![]`](https://doc.rust-lang.org/nightly/alloc/macros/macro.vec.html),
+[`Vec::with_capacity(0)`](https://doc.rust-lang.org/nightly/alloc/vec/struct.Vec.html#method.with_capacity), or by calling [`shrink_to_fit`]
+on an empty Vec, it will not allocate memory. Similarly, if you store zero-sized
+types inside a `Vec`, it will not allocate space for them. _Note that in this case
+the `Vec` might not report a [`capacity`] of 0_. `Vec` will allocate if and only
+if <code>
+[size_of::\<T>](https://doc.rust-lang.org/nightly/core/mem/fn.size_of.html)() \* [capacity]() > 0</code>. In general, `Vec`'s allocation
+details are very subtle --- if you intend to allocate memory using a `Vec`
+and use it for something else (either to pass to unsafe code, or to build your
+own memory-backed collection), be sure to deallocate this memory by using
+`from_raw_parts` to recover the `Vec` and then dropping it.
+
+If a `Vec` _has_ allocated memory, then the memory it points to is on the heap
+(as defined by the allocator Rust is configured to use by default), and its
+pointer points to [`len`] initialized, contiguous elements in order (what
+you would see if you coerced it to a slice), followed by <code>
+[capacity] - [len]</code>
+logically uninitialized, contiguous elements.
+
+A vector containing the elements `'a'` and `'b'` with capacity 4 can be
+visualized as below. The top part is the `Vec` struct, it contains a
+pointer to the head of the allocation in the heap, length and capacity.
+The bottom part is the allocation on the heap, a contiguous memory block.
+
+```text
+ ptr len capacity
+ +--------+--------+--------+
+ | 0x0123 | 2 | 4 |
+ +--------+--------+--------+
+ |
+ v
+Heap +--------+--------+--------+--------+
+ | 'a' | 'b' | uninit | uninit |
+ +--------+--------+--------+--------+
+```
+
+- **uninit** represents memory that is not initialized, see [`MaybeUninit`].
+- Note: the ABI is not stable and `Vec` makes no guarantees about its memory
+ layout (including the order of fields).
+
+`Vec` will never perform a "small optimization" where elements are actually
+stored on the stack for two reasons:
+
+- It would make it more difficult for unsafe code to correctly manipulate
+ a `Vec`. The contents of a `Vec` wouldn't have a stable address if it were
+ only moved, and it would be more difficult to determine if a `Vec` had
+ actually allocated memory.
+
+- It would penalize the general case, incurring an additional branch
+ on every access.
+
+`Vec` will never automatically shrink itself, even if completely empty. This
+ensures no unnecessary allocations or deallocations occur. Emptying a `Vec`
+and then filling it back up to the same [`len`] should incur no calls to
+the allocator. If you wish to free up unused memory, use
+[`shrink_to_fit`] or [`shrink_to`].
+
+[`push`] and [`insert`] will never (re)allocate if the reported capacity is
+sufficient. [`push`] and [`insert`] _will_ (re)allocate if
+<code>
+[len] == [capacity]</code>. That is, the reported capacity is completely
+accurate, and can be relied on. It can even be used to manually free the memory
+allocated by a `Vec` if desired. Bulk insertion methods _may_ reallocate, even
+when not necessary.
+
+`Vec` does not guarantee any particular growth strategy when reallocating
+when full, nor when [`reserve`] is called. The current strategy is basic
+and it may prove desirable to use a non-constant growth factor. Whatever
+strategy is used will of course guarantee _O_(1) amortized [`push`].
+
+It is guaranteed, in order to respect the intentions of the programmer, that
+all of `vec![e_1, e_2, ..., e_n]`, `vec![x; n]`, and [`Vec::with_capacity(n)`] produce a `Vec`
+that requests an allocation of the exact size needed for precisely `n` elements from the allocator,
+and no other size (such as, for example: a size rounded up to the nearest power of 2).
+The allocator will return an allocation that is at least as large as requested, but it may be larger.
+
+It is guaranteed that the [`Vec::capacity`] method returns a value that is at least the requested capacity
+and not more than the allocated capacity.
+
+The method [`Vec::shrink_to_fit`](https://doc.rust-lang.org/nightly/alloc/vec/struct.Vec.html#method.shrink_to_fit) will attempt to discard excess capacity an allocator has given to a `Vec`.
+If <code>
+[len] == [capacity]</code>, then a `Vec<T>` can be converted
+to and from a [`Box<[T]>`](https://doc.rust-lang.org/nightly/alloc/boxed/struct.Box.html) without reallocating or moving the elements.
+`Vec` exploits this fact as much as reasonable when implementing common conversions
+such as [`into_boxed_slice`].
+
+`Vec` will not specifically overwrite any data that is removed from it,
+but also won't specifically preserve it. Its uninitialized memory is
+scratch space that it may use however it wants. It will generally just do
+whatever is most efficient or otherwise easy to implement. Do not rely on
+removed data to be erased for security purposes. Even if you drop a `Vec`, its
+buffer may simply be reused by another allocation. Even if you zero a `Vec`'s memory
+first, that might not actually happen because the optimizer does not consider
+this a side-effect that must be preserved. There is one case which we will
+not break, however: using `unsafe` code to write to the excess capacity,
+and then increasing the length to match, is always valid.
+
+Currently, `Vec` does not guarantee the order in which elements are dropped.
+The order has changed in the past and may change again.