Initial public release of Warp.

Repo-Sync-Origin: warpdotdev/warp-internal@12af1d983b
This commit is contained in:
David Stern
2026-04-28 08:43:33 -05:00
commit 0dbd3d567a
4982 changed files with 1431549 additions and 0 deletions
@@ -0,0 +1,133 @@
use std::{iter, ops::Range};
use arborium::tree_sitter::{Node, Query, QueryCursor, TextProvider, Tree};
use rangemap::RangeMap;
use streaming_iterator::StreamingIterator;
use string_offset::{ByteOffset, CharOffset};
use warp_editor::content::{
buffer::{Buffer, ToBufferByteOffset, ToBufferCharOffset},
text::Bytes,
};
use warpui::color::ColorU;
/// Color mapping from parsed syntax token name to its corresponding highlighting color.
#[derive(Clone, Copy)]
pub struct ColorMap {
pub keyword_color: ColorU,
pub function_color: ColorU,
pub string_color: ColorU,
pub type_color: ColorU,
pub number_color: ColorU,
pub comment_color: ColorU,
pub property_color: ColorU,
pub tag_color: ColorU,
}
/// Query for retrieving syntax highlighting information on the tokens.
pub struct HighlightQuery {
highlight_map: Vec<Option<ColorU>>,
}
impl HighlightQuery {
pub fn new(query: &Query, color_map: ColorMap) -> Self {
let highlight_map = query
.capture_names()
.iter()
.map(|name| convert_capture_name_to_color(name, &color_map))
.collect();
Self { highlight_map }
}
/// Given the a character range, return its corresponding highlight colors.
pub fn get_highlighted_chunks(
&self,
range: Range<CharOffset>,
query: &Query,
buffer: &Buffer,
tree: &Tree,
) -> RangeMap<CharOffset, ColorU> {
let mut range_map = RangeMap::new();
let mut cursor = QueryCursor::new();
let byte_start = range.start.to_buffer_byte_offset(buffer).as_usize();
let byte_end = range.end.to_buffer_byte_offset(buffer).as_usize();
cursor.set_byte_range(byte_start..byte_end);
let mut captures = cursor.captures(query, tree.root_node(), TextBuffer(buffer));
while let Some(matches) = captures.next() {
for cap in matches.0.captures {
let insertion_range = cap.node.byte_range();
let color = self
.highlight_map
.get(cap.index as usize)
.and_then(|inner| *inner);
if let Some(color) = color {
let char_start =
ByteOffset::from(insertion_range.start).to_buffer_char_offset(buffer);
let char_end =
ByteOffset::from(insertion_range.end).to_buffer_char_offset(buffer);
if char_start < char_end {
range_map.insert(char_start..char_end, color);
}
}
}
}
range_map
}
}
fn convert_capture_name_to_color(name: &str, color_map: &ColorMap) -> Option<ColorU> {
match name.split('.').next() {
Some("keyword") => Some(color_map.keyword_color),
Some("function") => Some(color_map.function_color),
Some("string") => Some(color_map.string_color),
Some("type") => Some(color_map.type_color),
Some("number") => Some(color_map.number_color),
Some("comment") => Some(color_map.comment_color),
Some("property") => Some(color_map.property_color),
Some("tag") => Some(color_map.tag_color),
_ => None,
}
}
// The default tree-sitter implementation here is unsafe (since the cursor could query invalid ranges outside of content length).
// TODO(kevin): Once we migrate buffer to store ArrayStrings. We should implement the chunks API on buffer directly to avoid collecting
// into a String and then chunking them again for highlighting.
pub struct TextSlice<'a>(pub &'a [u8]);
impl TextSlice<'_> {
fn get(&self, range: Range<usize>) -> Self {
Self(self.0.get(range).unwrap_or_default())
}
}
impl AsRef<[u8]> for TextSlice<'_> {
fn as_ref(&self) -> &[u8] {
self.0
}
}
impl<'a> TextProvider<TextSlice<'a>> for TextSlice<'a> {
type I = iter::Once<TextSlice<'a>>;
fn text(&mut self, node: Node) -> Self::I {
iter::once(self.get(node.byte_range()))
}
}
pub struct TextBuffer<'a>(pub &'a Buffer);
impl<'a> TextProvider<&'a [u8]> for TextBuffer<'a> {
type I = Bytes<'a>;
fn text(&mut self, node: Node) -> Self::I {
let range = node.range();
self.0.bytes_in_range(
ByteOffset::from(range.start_byte),
ByteOffset::from(range.end_byte),
)
}
}