aboutsummaryrefslogtreecommitdiffstats
path: root/src/parse/mod.rs
diff options
context:
space:
mode:
authorJonas Maier <jonas@x77.dev>2026-05-22 21:26:41 +0200
committerJonas Maier <jonas@x77.dev>2026-05-22 21:26:41 +0200
commiteeb267c46340d5d47f41cc2440f0b281f9ae9261 (patch)
treeabcbc6624e0903cc1c7cd919d15a42ebb970692a /src/parse/mod.rs
parent07daff9331dbdc607584edbf1a8fb3e415c338ea (diff)
downloadpish-eeb267c46340d5d47f41cc2440f0b281f9ae9261.tar.gz
basic syntax highlighting
Diffstat (limited to 'src/parse/mod.rs')
-rw-r--r--src/parse/mod.rs84
1 files changed, 80 insertions, 4 deletions
diff --git a/src/parse/mod.rs b/src/parse/mod.rs
index 102b334..97a6e4a 100644
--- a/src/parse/mod.rs
+++ b/src/parse/mod.rs
@@ -3,6 +3,8 @@ use crate::{BString, PushAll, bstr};
#[cfg(test)]
mod test;
+mod span;
+
pub trait Stage: PartialEq {
type Str: std::fmt::Debug + Clone + PartialEq;
}
@@ -908,6 +910,7 @@ impl Parse for ExpString {
let mut last_delim = StringDelimiter::None;
'outer: loop {
+ let begin = b.loc();
let Some(delim) = StringDelimiter::try_begin(b) else {
break;
};
@@ -1034,6 +1037,14 @@ impl Parse for ExpString {
add_char(p, x);
}
}
+
+ if !delim.is_none() {
+ let end = b.loc_u32();
+ b.highlights.push(Highlight {
+ span: begin.to(end),
+ kind: HighlightKind::String,
+ });
+ }
}
if already_parsed {
@@ -1285,6 +1296,18 @@ pub enum ParseMode {
Completion,
}
+#[derive(Copy, Clone)]
+pub enum HighlightKind {
+ Keyword(Keyword),
+ String,
+ None,
+}
+
+pub struct Highlight {
+ pub span: span::Span,
+ pub kind: HighlightKind,
+}
+
pub struct Cursor<'a> {
buf: &'a [u8],
mode: ParseMode,
@@ -1293,6 +1316,13 @@ pub struct Cursor<'a> {
spaced: bool,
pub backtrace: bool,
+
+ pub highlights: Vec<Highlight>,
+
+ file: span::FileId,
+
+ buf_start: u64,
+ buf_len: u32,
}
#[derive(Default)]
@@ -1305,11 +1335,20 @@ struct SpaceStats {
impl<'a> Cursor<'a> {
pub fn new(buf: &'a [u8], mode: ParseMode) -> Self {
+ assert!(
+ buf.len() < u32::MAX as usize,
+ "cannot support larger parse buffers for now - what are you even doing."
+ );
+
Self {
buf,
mode,
spaced: false,
backtrace: false,
+ highlights: Vec::new(),
+ file: span::FileId::new(),
+ buf_start: buf.as_ptr() as u64,
+ buf_len: buf.len() as u32,
}
}
@@ -1377,6 +1416,18 @@ impl<'a> Cursor<'a> {
}
}
+ fn loc_u32(&self) -> u32 {
+ let now_loc = self.buf.as_ptr() as u64;
+ assert!(now_loc >= self.buf_start, "not the original buffer");
+ let relative_loc = (now_loc - self.buf_start) as u32;
+ assert!(relative_loc <= self.buf_len, "not the original buffer");
+ relative_loc
+ }
+
+ fn loc(&self) -> span::SpanFrom {
+ self.file.from(self.loc_u32())
+ }
+
fn spaces_stats(&mut self) -> SpaceStats {
let mut stats = SpaceStats::default();
while self.has() && b" \t\n\r".contains(&self.buf[0]) {
@@ -1418,7 +1469,8 @@ impl<'a> Cursor<'a> {
}
self.spaces();
- if self.buf.starts_with(bytes) {
+ let span = self.loc().with_len(bytes.len() as u32);
+ let result = if self.buf.starts_with(bytes) {
if kw.requires_space() {
if self.buf.len() > bytes.len() && self.buf[bytes.len()].is_ascii_whitespace() {
self.buf = &self.buf[bytes.len() + 1..];
@@ -1439,11 +1491,20 @@ impl<'a> Cursor<'a> {
}
} else {
Err(ParseError::ExpectedKeyword(kw))
+ };
+
+ if result.is_ok() {
+ self.highlights.push(Highlight {
+ span,
+ kind: HighlightKind::Keyword(kw),
+ })
}
+
+ result
}
}
-#[derive(Debug, PartialEq)]
+#[derive(Copy, Clone, Debug, PartialEq)]
pub enum Keyword {
If,
While,
@@ -1546,8 +1607,8 @@ impl Parse for While {
}
}
-impl Parse for Ast<PreExpansion> {
- fn parse(b: &mut Cursor<'_>) -> Result<Self> {
+impl Ast<PreExpansion> {
+ fn parse_inner(b: &mut Cursor<'_>) -> Result<Self> {
b.spaces();
let orig_len = b.buf.len();
@@ -1586,6 +1647,21 @@ impl Parse for Ast<PreExpansion> {
}
}
+impl Parse for Ast<PreExpansion> {
+ fn parse(b: &mut Cursor<'_>) -> Result<Self> {
+ let begin = b.loc();
+ let result = Ast::parse_inner(b);
+ let span = begin.to(b.loc_u32());
+ if result.is_ok() {
+ b.highlights.push(Highlight {
+ span,
+ kind: HighlightKind::None,
+ });
+ }
+ result
+ }
+}
+
impl Parse for Command<PreExpansion> {
fn parse(b: &mut Cursor<'_>) -> Result<Self> {
let path: ExpString = b.parse()?;