Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
161 changes: 161 additions & 0 deletions etc/examples/create-context-demo-repos.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
#!/usr/bin/env bash
#
# Creates three small git repos demonstrating the syntax-highlighting context
# bug. In each repo, `git diff HEAD~1` produces a hunk that starts inside a
# multiline construct (docstring / block comment / template literal), so
# syntect misparses the closing delimiter.
#
# Usage:
# bash etc/examples/create-context-demo-repos.sh
#
# Then compare:
# cd /tmp/context-demo-python
# git diff HEAD~1 | delta # bug: return is string-colored
# git diff -U9999 HEAD~1 | delta -U 3 # fix: return is keyword-colored

set -euo pipefail

DIR="${1:-/tmp}"

# --- Python triple-quoted string ---
REPO="$DIR/context-demo-python"
rm -rf "$REPO"
mkdir -p "$REPO" && cd "$REPO" && git init -q

cat > example.py << 'PYEOF'
def foo():
"""
This is a docstring that spans
multiple lines.
It has lots of content.
More content here.
Even more explanation.
Still going on.
And yet more.
Final docstring line.
"""
x = 1
return x
PYEOF
git add example.py && git commit -q -m "initial"

cat > example.py << 'PYEOF'
def foo():
"""
This is a docstring that spans
multiple lines.
It has lots of content.
More content here.
Even more explanation.
Still going on.
And yet more.
Final docstring line.
"""
x = 2
return x + 1
PYEOF
git add example.py && git commit -q -m "change"

# --- Rust block comment ---
REPO="$DIR/context-demo-rust"
rm -rf "$REPO"
mkdir -p "$REPO" && cd "$REPO" && git init -q

cat > lib.rs << 'RSEOF'
/// Entry point for the library.
pub fn process(input: &str) -> Result<String, Error> {
/*
* Multi-line comment explaining
* the complex validation logic
* that follows below.
* It covers edge cases for:
* - empty input
* - unicode input
* - oversized input
* - malformed input
*/
let validated = validate(input)?;
let result = transform(validated);
Ok(result)
}
RSEOF
git add lib.rs && git commit -q -m "initial"

cat > lib.rs << 'RSEOF'
/// Entry point for the library.
pub fn process(input: &str) -> Result<String, Error> {
/*
* Multi-line comment explaining
* the complex validation logic
* that follows below.
* It covers edge cases for:
* - empty input
* - unicode input
* - oversized input
* - malformed input
*/
let validated = validate(input)?;
let trimmed = validated.trim();
let result = transform(trimmed);
Ok(result)
}
RSEOF
git add lib.rs && git commit -q -m "change"

# --- JavaScript template literal ---
REPO="$DIR/context-demo-js"
rm -rf "$REPO"
mkdir -p "$REPO" && cd "$REPO" && git init -q

cat > render.js << 'JSEOF'
function render(data) {
const html = `
<div class="container">
<header>
<h1>${data.title}</h1>
<nav>${data.breadcrumbs}</nav>
</header>
<main>
<p>${data.description}</p>
<ul>${data.items}</ul>
</main>
</div>
`;
const element = document.createElement("div");
element.innerHTML = html;
return element;
}
JSEOF
git add render.js && git commit -q -m "initial"

cat > render.js << 'JSEOF'
function render(data) {
const html = `
<div class="container">
<header>
<h1>${data.title}</h1>
<nav>${data.breadcrumbs}</nav>
</header>
<main>
<p>${data.description}</p>
<ul>${data.items}</ul>
</main>
</div>
`;
const wrapper = document.createElement("section");
wrapper.innerHTML = html;
wrapper.classList.add("rendered");
return wrapper;
}
JSEOF
git add render.js && git commit -q -m "change"

echo "Created repos:"
echo " $DIR/context-demo-python"
echo " $DIR/context-demo-rust"
echo " $DIR/context-demo-js"
echo
echo "Try:"
echo " cd $DIR/context-demo-python"
echo " git diff HEAD~1 | delta # bug"
echo " git diff -U9999 HEAD~1 | delta -U 3 # fix"
9 changes: 9 additions & 0 deletions src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,15 @@ pub struct Opt {
/// doesn't support it, then delta will fall back to `diff` instead of `git diff`.
pub diff_args: String,

#[arg(short = 'U', long = "context-lines", value_name = "N")]
/// Display at most N context lines around each change.
///
/// When the input diff has more context lines than N, delta will use all of them for syntax
/// highlighting but only display N. This can fix incorrect syntax highlighting around multiline
/// constructs (multiline strings, block comments, etc). Use this in conjunction with git's -U
/// option, e.g. `git diff -U9999`.
pub context_lines: Option<usize>,

#[arg(long = "diff-highlight")]
/// Emulate diff-highlight.
///
Expand Down
2 changes: 2 additions & 0 deletions src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ pub struct Config {
pub blame_timestamp_format: String,
pub blame_timestamp_output_format: Option<String>,
pub color_only: bool,
pub context_lines: Option<usize>,
pub commit_regex: Regex,
pub commit_style: Style,
pub cwd_of_delta_process: Option<PathBuf>,
Expand Down Expand Up @@ -295,6 +296,7 @@ impl From<cli::Opt> for Config {
blame_timestamp_output_format: opt.blame_timestamp_output_format,
commit_style: styles["commit-style"],
color_only: opt.color_only,
context_lines: opt.context_lines,
commit_regex,
cwd_of_delta_process,
cwd_of_user_shell_process,
Expand Down
1 change: 1 addition & 0 deletions src/delta.rs
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,7 @@ impl<'a> StateMachine<'a> {
self.handle_pending_line_with_diff_name()?;
self.painter.paint_buffered_minus_and_plus_lines();
self.painter.emit()?;
self.painter.flush_hunk_output()?;
Ok(())
}

Expand Down
14 changes: 14 additions & 0 deletions src/git_config/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,20 @@ impl GitConfigGet for usize {
}
}

impl GitConfigGet for Option<usize> {
fn git_config_get(key: &str, git_config: &GitConfig) -> Option<Self> {
if let Some(s) = git_config.config_from_env_var.get(key) {
if let Ok(n) = s.parse::<usize>() {
return Some(Some(n));
}
}
match git_config.config.get_i64(key) {
Ok(value) => Some(Some(value as usize)),
_ => None,
}
}
}

impl GitConfigGet for f64 {
fn git_config_get(key: &str, git_config: &GitConfig) -> Option<Self> {
if let Some(s) = git_config.config_from_env_var.get(key) {
Expand Down
1 change: 1 addition & 0 deletions src/handlers/hunk_header.rs
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,7 @@ impl StateMachine<'_> {
self.painter.paint_buffered_minus_and_plus_lines();
self.painter.set_highlighter();
self.painter.emit()?;
self.painter.flush_hunk_output()?;

let ParsedHunkHeader {
code_fragment,
Expand Down
1 change: 1 addition & 0 deletions src/options/get.rs
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@ pub trait GetOptionValue {
}

impl GetOptionValue for Option<String> {}
impl GetOptionValue for Option<usize> {}
impl GetOptionValue for String {}
impl GetOptionValue for bool {}
impl GetOptionValue for f64 {}
Expand Down
16 changes: 16 additions & 0 deletions src/options/option_value.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ pub enum OptionValue {
Boolean(bool),
Float(f64),
OptionString(Option<String>),
OptionInt(Option<usize>),
String(String),
Int(usize),
}
Expand Down Expand Up @@ -81,6 +82,21 @@ impl From<OptionValue> for String {
}
}

impl From<Option<usize>> for OptionValue {
fn from(value: Option<usize>) -> Self {
OptionValue::OptionInt(value)
}
}

impl From<OptionValue> for Option<usize> {
fn from(value: OptionValue) -> Self {
match value {
OptionValue::OptionInt(value) => value,
_ => delta_unreachable("Error converting OptionValue to Option<usize>."),
}
}
}

impl From<usize> for OptionValue {
fn from(value: usize) -> Self {
OptionValue::Int(value)
Expand Down
1 change: 1 addition & 0 deletions src/options/set.rs
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,7 @@ pub fn set_options(
commit_decoration_style,
commit_regex,
commit_style,
context_lines,
default_language,
diff_args,
diff_stat_align_width,
Expand Down
Loading
Loading