Skip to content

Commit ef6cdea

Browse files
committed
integrate new syntax highlighting
1 parent 0cb2add commit ef6cdea

9 files changed

Lines changed: 235 additions & 97 deletions

File tree

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,15 @@
11
from __future__ import annotations
22

3-
def syntax_highlight(s: str, language: str) -> list[tuple[str, str]]:
3+
from typing import Literal
4+
5+
6+
def highlight(text: str, language: Literal["xml", "yaml", "error", "none"]) -> list[tuple[str, str]]:
47
pass
58

6-
def all_tags(language: str) -> list[str]:
9+
def tags() -> list[str]:
710
pass
811

9-
__all__ = ["syntax_highlight", "all_tags"]
12+
__all__ = [
13+
"highlight",
14+
"tags",
15+
]

mitmproxy-rs/src/contentview.rs

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use mitmproxy::contentviews::{Metadata, Prettify, Reencode, SyntaxHighlight};
1+
use mitmproxy::contentviews::{Metadata, Prettify, Reencode};
22
use pyo3::{exceptions::PyValueError, prelude::*};
33

44
struct PythonMetadata(PyObject);
@@ -52,11 +52,8 @@ impl Contentview {
5252

5353
/// Optional syntax highlighting that should be applied to the prettified output.
5454
#[getter]
55-
pub fn syntax_highlight(&self) -> &str {
56-
match self.0.syntax_highlight() {
57-
SyntaxHighlight::None => "none",
58-
SyntaxHighlight::Yaml => "yaml",
59-
}
55+
pub fn syntax_highlight(&self) -> String {
56+
self.0.syntax_highlight().to_string()
6057
}
6158

6259
fn __lt__(&self, py: Python<'_>, other: PyObject) -> PyResult<bool> {

mitmproxy-rs/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,7 @@ mod mitmproxy_rs {
133133
#[pymodule_export]
134134
use crate::syntax_highlight::highlight;
135135
#[pymodule_export]
136-
use crate::syntax_highlight::all_tags;
136+
use crate::syntax_highlight::tags;
137137
}
138138
}
139139

Lines changed: 28 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,39 @@
11
#[allow(unused_imports)]
22
use anyhow::{anyhow, Result};
3+
use std::str::FromStr;
34

5+
use mitmproxy::syntax_highlight::{Language, Tag};
46
use pyo3::{exceptions::PyValueError, prelude::*};
57

6-
fn str_to_language(s: &str) -> PyResult<mitmproxy::syntax_highlight::Language> {
7-
match s {
8-
"xml" => Ok(mitmproxy::syntax_highlight::Language::Xml),
9-
"yaml" => Ok(mitmproxy::syntax_highlight::Language::Yaml),
10-
other => Err(PyErr::new::<PyValueError, _>(format!(
11-
"Unsupported language: {other}"
12-
))),
13-
}
14-
}
15-
16-
/// Transform a text into tagged chunks for text.
8+
/// Transform text into a list of tagged chunks.
9+
///
10+
/// Example:
11+
///
12+
/// ```python
13+
/// from mitmproxy_rs.syntax_highlight import highlight
14+
/// highlighted = highlight("key: 42", "yaml")
15+
/// print(highlighted) # [('property', 'key'), ('', ': '), ('number', '42')]
16+
/// ```
1717
#[pyfunction]
18-
pub fn highlight(s: String, language: &str) -> PyResult<Vec<(&'static str, String)>> {
19-
let language = str_to_language(language)?;
20-
language.highlight(s.as_bytes())
18+
pub fn highlight(text: String, language: &str) -> PyResult<Vec<(&'static str, String)>> {
19+
let language = Language::from_str(language)?;
20+
language
21+
.highlight(text.as_bytes())
22+
.map(|chunks| {
23+
chunks
24+
.into_iter()
25+
.map(|(tag, text)| (tag.to_str(), text))
26+
.collect()
27+
})
2128
.map_err(|e| PyValueError::new_err(e.to_string()))
2229
}
2330

24-
/// Return the list of all possible tags for a given language.
31+
/// Return the list of all possible tag names for a given language.
2532
#[pyfunction]
26-
pub fn all_tags(language: &str) -> PyResult<&[&str]> {
27-
let language = str_to_language(language)?;
28-
Ok(language.all_tags())
33+
pub fn tags() -> PyResult<Vec<&'static str>> {
34+
Ok(Tag::VALUES
35+
.iter()
36+
.map(|tag| tag.to_str())
37+
.filter(|&x| !x.is_empty())
38+
.collect())
2939
}

src/contentviews/mod.rs

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,12 @@ mod protobuf;
55

66
use anyhow::Result;
77

8+
use crate::syntax_highlight;
89
pub use hex_dump::HexDump;
910
pub use hex_stream::HexStream;
1011
pub use msgpack::MsgPack;
1112
pub use protobuf::Protobuf;
1213

13-
pub enum SyntaxHighlight {
14-
None,
15-
Yaml,
16-
}
17-
1814
pub trait Metadata {
1915
fn content_type(&self) -> Option<String>;
2016
}
@@ -32,8 +28,8 @@ pub trait Prettify: Send + Sync {
3228
0.0
3329
}
3430

35-
fn syntax_highlight(&self) -> SyntaxHighlight {
36-
SyntaxHighlight::None
31+
fn syntax_highlight(&self) -> syntax_highlight::Language {
32+
syntax_highlight::Language::None
3733
}
3834
}
3935

src/syntax_highlight/common.rs

Lines changed: 52 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,60 +1,59 @@
1-
use super::Chunk;
1+
use super::{Chunk, Tag};
22
use anyhow::{Context, Result};
33
use tree_sitter_highlight::{HighlightConfiguration, HighlightEvent, Highlighter};
44

55
pub fn highlight(
66
language: tree_sitter::Language,
77
highlights_query: &str,
8-
tags: &[&'static str],
8+
names: &[&str],
9+
tags: &[Tag],
910
input: &[u8],
1011
) -> Result<Vec<Chunk>> {
1112
let mut highlighter = Highlighter::new();
1213
let mut config = HighlightConfiguration::new(language, "", highlights_query, "", "")
1314
.context("failed to create highlight configuration")?;
14-
config.configure(tags);
15+
config.configure(names);
1516

1617
let highlights = highlighter
1718
.highlight(&config, input, None, |_| None)
1819
.context("failed to highlight")?;
1920

2021
let mut chunks: Vec<Chunk> = Vec::new();
21-
let mut tag: Option<&'static str> = None;
22+
let mut tag: Tag = Tag::Text;
2223

2324
for event in highlights {
2425
let event = event.context("highlighter failure")?;
2526
match event {
2627
HighlightEvent::Source { start, end } => {
27-
let contents = &input[start..end];
28-
let tag_str = tag.unwrap_or("");
29-
28+
let contents = String::from_utf8_lossy(&input[start..end]);
3029
match chunks.last_mut() {
31-
Some(x) if x.0 == tag_str => {
32-
x.1.push_str(&String::from_utf8_lossy(contents));
30+
Some(x) if x.0 == tag || contents.trim_ascii().is_empty() => {
31+
x.1.push_str(&contents);
3332
}
34-
_ => chunks.push(
35-
(tag_str, String::from_utf8_lossy(contents).to_string())
36-
),
33+
_ => chunks.push((tag, contents.to_string())),
3734
}
3835
}
3936
HighlightEvent::HighlightStart(s) => {
40-
tag = Some(tags[s.0]);
37+
tag = tags[s.0];
4138
}
4239
HighlightEvent::HighlightEnd => {
43-
tag = None;
40+
tag = Tag::Text;
4441
}
4542
}
4643
}
4744
Ok(chunks)
4845
}
4946

5047
#[cfg(test)]
51-
pub(super) fn test_tags_ok(
48+
pub(super) fn test_names_ok(
5249
language: tree_sitter::Language,
5350
highlights_query: &str,
54-
tags: &[&'static str],
51+
names: &[&str],
52+
tags: &[Tag],
5553
) {
54+
assert_eq!(names.len(), tags.len());
5655
let config = HighlightConfiguration::new(language, "", highlights_query, "", "").unwrap();
57-
for &tag in tags {
56+
for &tag in names {
5857
assert!(
5958
config.names().iter().any(|name| name.contains(tag)),
6059
"Invalid tag: {},\nAllowed tags: {:?}",
@@ -63,3 +62,39 @@ pub(super) fn test_tags_ok(
6362
);
6463
}
6564
}
65+
66+
#[allow(unused)]
67+
#[cfg(test)]
68+
pub(super) fn debug(language: tree_sitter::Language, highlights_query: &str, input: &[u8]) {
69+
let mut highlighter = Highlighter::new();
70+
let mut config = HighlightConfiguration::new(language, "", highlights_query, "", "").unwrap();
71+
let names = config
72+
.names()
73+
.iter()
74+
.map(|name| name.to_string())
75+
.collect::<Vec<_>>();
76+
config.configure(&names);
77+
let highlights = highlighter
78+
.highlight(&config, input, None, |_| None)
79+
.unwrap();
80+
81+
let mut tag: &str = "";
82+
for event in highlights {
83+
match event.unwrap() {
84+
HighlightEvent::Source { start, end } => {
85+
let contents = &input[start..end];
86+
println!(
87+
"{}: {:?}",
88+
tag,
89+
String::from_utf8_lossy(contents).to_string().as_str()
90+
);
91+
}
92+
HighlightEvent::HighlightStart(s) => {
93+
tag = &names[s.0];
94+
}
95+
HighlightEvent::HighlightEnd => {
96+
tag = "";
97+
}
98+
}
99+
}
100+
}

src/syntax_highlight/mod.rs

Lines changed: 77 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,98 @@
1+
use anyhow::bail;
2+
use std::fmt;
3+
use std::fmt::Formatter;
4+
use std::str::FromStr;
5+
16
mod common;
27
mod xml;
38
mod yaml;
49

5-
pub type Chunk = (&'static str, String);
10+
pub type Chunk = (Tag, String);
611

712
pub enum Language {
813
Xml,
914
Yaml,
15+
Error,
16+
None,
1017
}
1118

1219
impl Language {
1320
pub fn highlight(&self, input: &[u8]) -> anyhow::Result<Vec<Chunk>> {
1421
match self {
1522
Language::Yaml => yaml::highlight_yaml(input),
1623
Language::Xml => xml::highlight_xml(input),
24+
Language::None => Ok(vec![(
25+
Tag::Text,
26+
String::from_utf8_lossy(input).to_string(),
27+
)]),
28+
Language::Error => Ok(vec![(
29+
Tag::Error,
30+
String::from_utf8_lossy(input).to_string(),
31+
)]),
1732
}
1833
}
19-
20-
pub fn all_tags(&self) -> &'static [&'static str] {
34+
}
35+
36+
impl FromStr for Language {
37+
type Err = anyhow::Error;
38+
39+
fn from_str(s: &str) -> Result<Self, Self::Err> {
40+
Ok(match s {
41+
"xml" => Language::Xml,
42+
"yaml" => Language::Yaml,
43+
"none" => Language::None,
44+
"error" => Language::Error,
45+
other => bail!("Unsupported language: {other}"),
46+
})
47+
}
48+
}
49+
50+
impl fmt::Display for Language {
51+
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
52+
write!(
53+
f,
54+
"{}",
55+
match self {
56+
Language::Xml => "xml",
57+
Language::Yaml => "yaml",
58+
Language::Error => "error",
59+
Language::None => "none",
60+
}
61+
)
62+
}
63+
}
64+
65+
#[derive(Debug, Eq, PartialEq, Copy, Clone)]
66+
pub enum Tag {
67+
Text, // Text that shouldn't be emphasized.
68+
Name, // A tag, such as an HTML tag or a YAML key.
69+
String, // A string value.
70+
Number, // A number value.
71+
Boolean, // A boolean value.
72+
Comment, // A comment.
73+
Error, // An error value.
74+
}
75+
76+
impl Tag {
77+
pub const VALUES: [Self; 7] = [
78+
Self::Text,
79+
Self::Name,
80+
Self::String,
81+
Self::Number,
82+
Self::Boolean,
83+
Self::Comment,
84+
Self::Error,
85+
];
86+
87+
pub fn to_str(self) -> &'static str {
2188
match self {
22-
Language::Xml => xml::XML_TAGS,
23-
Language::Yaml => yaml::YAML_TAGS,
89+
Tag::Text => "",
90+
Tag::Name => "name",
91+
Tag::String => "string",
92+
Tag::Number => "number",
93+
Tag::Boolean => "boolean",
94+
Tag::Comment => "comment",
95+
Tag::Error => "error",
2496
}
2597
}
2698
}

0 commit comments

Comments
 (0)