mirror of
https://github.com/storytold/gridcraft.git
synced 2026-11-10 12:29:58 -06:00
#2225: copy quotes only cells with a tab or line break
Copy wrapped any cell that held a double quote in quotes (and doubled its quotes), so copying `say "hi"` into another app gave `"say ""hi"""`. Excel quotes only text with a tab or line break; a cell with just quotes goes on the clipboard as it is. Pasting text read a field that starts with a quote as quoted even when its closing quote doesn't end the field, so `"hi" there` (which Excel copies as it is) pasted as `hi there`. A field is now quoted only when its closing quote is followed by a tab, a line end or the end of the text; otherwise it is the text as written, as Excel pastes it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
178f3eb745
commit
fe965e7581
@@ -228,14 +228,23 @@ pub(crate) fn parse_tsv(text: &str) -> Vec<Vec<String>> {
|
||||
let mut chars = text.chars().peekable();
|
||||
let mut quoted = false;
|
||||
let mut at_start = true;
|
||||
// A quoted field's text as it stands in the input, for a field that turns out not to be one.
|
||||
let mut raw = String::new();
|
||||
while let Some(c) = chars.next() {
|
||||
if quoted {
|
||||
raw.push(c);
|
||||
if c == '"' {
|
||||
if chars.peek() == Some(&'"') {
|
||||
field.push('"');
|
||||
raw.push('"');
|
||||
chars.next();
|
||||
} else {
|
||||
quoted = false;
|
||||
// A field is quoted only when its closing quote ends it, as Excel reads it:
|
||||
// `"hi" there` is that text, quotes included.
|
||||
if !matches!(chars.peek(), None | Some('\t' | '\n' | '\r')) {
|
||||
field = std::mem::take(&mut raw);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
field.push(c);
|
||||
@@ -246,6 +255,7 @@ pub(crate) fn parse_tsv(text: &str) -> Vec<Vec<String>> {
|
||||
'"' if at_start => {
|
||||
quoted = true;
|
||||
at_start = false;
|
||||
raw = String::from('"');
|
||||
}
|
||||
'\t' => {
|
||||
row.push(std::mem::take(&mut field));
|
||||
|
||||
@@ -307,6 +307,9 @@ fn tsv() {
|
||||
assert_eq!(parse_tsv("a\tb\n1\t2\n"), vec![vec!["a", "b"], vec!["1", "2"]]);
|
||||
assert_eq!(parse_tsv("\"x\ty\"\tz"), vec![vec!["x\ty", "z"]]);
|
||||
assert_eq!(parse_tsv("\"he said \"\"hi\"\"\""), vec![vec!["he said \"hi\""]]);
|
||||
// #2225: a quote that doesn't close the field leaves it as written, as Excel pastes it.
|
||||
assert_eq!(parse_tsv("\"hi\" there\tx\n"), vec![vec!["\"hi\" there", "x"]]);
|
||||
assert_eq!(parse_tsv("say \"hi\"\n"), vec![vec!["say \"hi\""]]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -371,3 +374,19 @@ fn replace_case_insensitive_keeps_text_when_folding_moves_boundaries() {
|
||||
let sh = s.doc().unwrap().wb.active().unwrap();
|
||||
assert_eq!(sh.value(CellRef::new(0, 0)), Value::text(text));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn copy_quotes_only_cells_with_tabs_or_line_breaks() {
|
||||
// #2225: a cell holding a quote was wrapped in quotes on copy; Excel quotes only text with a
|
||||
// tab or line break, and pastes the result back as the same cells.
|
||||
let mut s = Session::new();
|
||||
s.new_workbook();
|
||||
for (cell, input) in [("A1", "say \"hi\""), ("A2", "plain"), ("A3", "\"hi\" there"), ("A4", "x \"q\"\ny"), ("A5", "tab\tinside")] {
|
||||
s.execute("cell.set", json!({"cell": cell, "input": input})).unwrap();
|
||||
}
|
||||
let text = s.execute("edit.copy", json!({"range": "A1:A5"})).unwrap()["text"].as_str().unwrap().to_string();
|
||||
assert_eq!(text, "say \"hi\"\nplain\n\"hi\" there\n\"x \"\"q\"\"\ny\"\n\"tab\tinside\"\n");
|
||||
let rows = parse_tsv(&text);
|
||||
let cells: Vec<&str> = rows.iter().map(|r| r[0].as_str()).collect();
|
||||
assert_eq!(cells, ["say \"hi\"", "plain", "\"hi\" there", "x \"q\"\ny", "tab\tinside"]);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user