rows/cols parameters: accept a single row or column

home.insertRows {"rows":"3"} and home.insertColumns {"cols":"B"}
failed with "not a range": only spans such as 3:3 or B:B parsed. The
same went for deleting, hiding, sizing and grouping rows and columns.
A single row number or column letter now means that one line.
This commit is contained in:
nihilist-narwhal
2026-10-13 06:54:31 -05:00
parent 84acf6293b
commit 9939819ee3
4 changed files with 23 additions and 4 deletions
+16 -2
View File
@@ -1127,10 +1127,10 @@ fn circle_invalid(s: &mut Session, _: &Json) -> Result<Json> {
// ---------------------------------------------------------------- outline
fn outline_lines(s: &Session, p: &Json) -> Result<(bool, u32, u32)> {
if let Some(r) = str_param(p, "rows").and_then(RangeRef::parse) {
if let Some(r) = str_param(p, "rows").and_then(parse_lines) {
return Ok((true, r.start.row, r.end.row));
}
if let Some(r) = str_param(p, "cols").and_then(RangeRef::parse) {
if let Some(r) = str_param(p, "cols").and_then(parse_lines) {
return Ok((false, r.start.col, r.end.col));
}
let r = s.doc()?.selection.current();
@@ -1493,6 +1493,20 @@ mod tests {
}
}
#[test]
fn single_row_and_column_parameters() {
let mut s = Session::new();
s.new_workbook();
s.execute("range.setValues", json!({"range": "A1", "values": [[1, 2], [3, 4]]})).unwrap();
s.execute("home.insertRows", json!({"rows": "2"})).unwrap();
s.execute("home.insertColumns", json!({"cols": "B"})).unwrap();
let v = |s: &mut Session, a: &str| s.execute("cell.get", json!({"cell": a})).unwrap()["value"].clone();
assert_eq!(v(&mut s, "C3"), json!(4.0));
s.execute("home.hideRows", json!({"rows": "1"})).unwrap();
s.execute("data.group", json!({"cols": "C"})).unwrap();
assert!(s.execute("home.deleteRows", json!({"rows": "0"})).is_err());
}
#[test]
fn split() {
assert_eq!(split_delimited("a,b,,c", &[','], false, '"'), ["a", "b", "", "c"]);
+1 -1
View File
@@ -940,7 +940,7 @@ fn line_targets(s: &Session, p: &Json, rows: bool) -> Result<Vec<(u32, u32)>> {
return t
.split(',')
.map(|part| {
let r = RangeRef::parse(part).ok_or_else(|| bad(key, format!("not a range: {part}")))?;
let r = parse_lines(part).ok_or_else(|| bad(key, format!("not a range: {part}")))?;
Ok(if rows { (r.start.row, r.end.row) } else { (r.start.col, r.end.col) })
})
.collect();
+5
View File
@@ -246,6 +246,11 @@ pub(crate) fn target_ranges(s: &Session, p: &Json) -> Result<Vec<RangeRef>> {
Ok(s.doc()?.selection.ranges.clone())
}
/// A `rows`/`cols` parameter: `3:5` and `B:C`, or a single `3` or `B`.
pub(crate) fn parse_lines(t: &str) -> Option<RangeRef> {
RangeRef::parse(t).or_else(|| if t.contains(':') { None } else { RangeRef::parse(&format!("{t}:{t}")) })
}
pub(crate) fn target_range(s: &Session, p: &Json) -> Result<RangeRef> {
Ok(target_ranges(s, p)?.first().copied().unwrap_or_default())
}
+1 -1
View File
@@ -63,7 +63,7 @@ pub fn specs() -> Vec<CommandSpec> {
fn lines(s: &Session, p: &Json, axis: Axis) -> Result<(u32, u32)> {
let key = if axis == Axis::Rows { "rows" } else { "cols" };
let r = match str_param(p, key).or_else(|| str_param(p, "range")) {
Some(t) => RangeRef::parse(t).ok_or_else(|| bad(key, format!("not a range: {t}")))?,
Some(t) => parse_lines(t).ok_or_else(|| bad(key, format!("not a range: {t}")))?,
None => s.doc()?.selection.current(),
};
let (a, b) = if axis == Axis::Rows { (r.start.row, r.end.row) } else { (r.start.col, r.end.col) };