Skip to content

Commit ae8720f

Browse files
committed
support for text wrapping
1 parent 9ff03fc commit ae8720f

File tree

9 files changed

+119
-14
lines changed

9 files changed

+119
-14
lines changed

book/src/language_reference/cell_and_row_options.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,13 @@ Bind a variable (specified by `VARIABLE_ID`) to reference this cell.
157157
`v = VARIABLE_ID`
158158

159159

160+
#### wrap = wrap | overflow | clip
161+
Whether to wrap the text in the cell. By default it is `wrap`
162+
163+
##### Alias
164+
`w = w | o | c`
165+
166+
160167
### Examples
161168

162169
* Align the second cell left, align the last cell to the center and make it bold and italicized:

docs/CHANGELOG.md

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,15 @@
11
## v0.8.0
22

3+
### Features
4+
5+
* Slashes are no longer needed to separate cell options
6+
- `[[text=bold halign=left]]` is now equivalent to `[[text=bold/halign=left]]`.
7+
* Support for multi-line cells
8+
* Support for text wrapping cell option
9+
* [csv++ User Guide](https://patrickomatic.github.io/csv-plus-plus/)
10+
* Switch to custom CSV parser (`csvp`)
11+
* Switch from CBOR to bincode for object file serialization
12+
313
### **Breaking Changes**
414

515
* Added enum variant `Error::CsvParseError`
@@ -17,15 +27,6 @@
1727
- `TextFormat`
1828
- `VerticalAlign`
1929

20-
### Features
21-
22-
* Slashes are no longer needed to separate cell options
23-
- `[[text=bold halign=left]]` is now equivalent to `[[text=bold/halign=left]]`.
24-
* Support for multi-line cells
25-
* [csv++ User Guide](https://patrickomatic.github.io/csv-plus-plus/)
26-
* Switch to custom CSV parser (`csvp`)
27-
* Switch from CBOR to bincode for object file serialization
28-
2930
### Deprecated Features
3031

3132
* You should no longer use `/` to separate cell options

src/cell/mod.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
//!
33
use crate::ast::Ast;
44
use crate::cell_options::{
5-
BorderSide, BorderStyle, DataValidation, HorizontalAlign, NumberFormat, TextFormat,
5+
BorderSide, BorderStyle, DataValidation, HorizontalAlign, NumberFormat, TextFormat, TextWrap,
66
VerticalAlign,
77
};
88
use crate::parser::ast_parser::AstParser;
@@ -31,6 +31,7 @@ pub struct Cell {
3131
pub note: Option<String>,
3232
pub number_format: Option<NumberFormat>,
3333
pub text_formats: collections::HashSet<TextFormat>,
34+
pub text_wrap: TextWrap,
3435
pub var: Option<String>,
3536
pub vertical_align: Option<VerticalAlign>,
3637
}
@@ -66,6 +67,7 @@ impl Cell {
6667
number_format: Option::default(),
6768
parsed_value: String::default(),
6869
text_formats: collections::HashSet::default(),
70+
text_wrap: TextWrap::default(),
6971
var: Option::default(),
7072
vertical_align: Option::default(),
7173
}
@@ -97,6 +99,7 @@ impl Cell {
9799
number_format: row.number_format,
98100
parsed_value: String::default(),
99101
text_formats: row.text_formats,
102+
text_wrap: row.text_wrap,
100103
var: None,
101104
vertical_align: row.vertical_align,
102105
}

src/cell_options/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ mod fill;
55
mod horizontal_align;
66
mod number_format;
77
mod text_format;
8+
mod text_wrap;
89
mod vertical_align;
910

1011
pub(crate) use border_side::BorderSide;
@@ -14,4 +15,5 @@ pub(crate) use fill::{Fill, ROW_MAX};
1415
pub(crate) use horizontal_align::HorizontalAlign;
1516
pub(crate) use number_format::NumberFormat;
1617
pub(crate) use text_format::TextFormat;
18+
pub(crate) use text_wrap::TextWrap;
1719
pub(crate) use vertical_align::VerticalAlign;

src/cell_options/text_wrap.rs

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
//! # `TextWrap`
2+
use crate::error::CellParseError;
3+
use crate::parser::cell_lexer::TokenMatch;
4+
5+
#[derive(Copy, Clone, Debug, Default, PartialEq, serde::Deserialize, serde::Serialize)]
6+
pub enum TextWrap {
7+
#[default]
8+
Wrap,
9+
Overflow,
10+
Clip,
11+
}
12+
13+
impl TryFrom<TokenMatch> for TextWrap {
14+
type Error = CellParseError;
15+
16+
fn try_from(input: TokenMatch) -> Result<Self, Self::Error> {
17+
match input.str_match.to_lowercase().as_str() {
18+
"c" | "clip" => Ok(Self::Clip),
19+
"o" | "overflow" => Ok(Self::Overflow),
20+
"w" | "wrap" => Ok(Self::Wrap),
21+
_ => Err(CellParseError::new(
22+
"wrap",
23+
input,
24+
&["clip (c)", "overflow (o)", "wrap (w)"],
25+
)),
26+
}
27+
}
28+
}
29+
30+
#[cfg(test)]
31+
mod tests {
32+
use super::*;
33+
use crate::test_utils::*;
34+
35+
#[test]
36+
fn try_from_clip() {
37+
assert_eq!(
38+
TextWrap::Clip,
39+
TextWrap::try_from(build_cell_token_match("c")).unwrap()
40+
);
41+
assert_eq!(
42+
TextWrap::Clip,
43+
TextWrap::try_from(build_cell_token_match("clip")).unwrap()
44+
);
45+
assert_eq!(
46+
TextWrap::Clip,
47+
TextWrap::try_from(build_cell_token_match("CLIP")).unwrap()
48+
);
49+
}
50+
51+
#[test]
52+
fn try_from_invalid() {
53+
assert!(TextWrap::try_from(build_cell_token_match("foo")).is_err());
54+
}
55+
}

src/parser/cell_parser/mod.rs

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@ mod validate;
44

55
use super::cell_lexer::{CellLexer, Token, TokenMatch};
66
use crate::cell_options::{
7-
BorderSide, BorderStyle, Fill, HorizontalAlign, NumberFormat, TextFormat, VerticalAlign,
7+
BorderSide, BorderStyle, Fill, HorizontalAlign, NumberFormat, TextFormat, TextWrap,
8+
VerticalAlign,
89
};
910
use crate::error::{BadInput, ParseResult, Result};
1011
use crate::{deprecated_feature, ArcSourceCode, Cell, Rgb, Row};
@@ -220,6 +221,16 @@ where
220221
})
221222
}
222223

224+
fn wrap_option(&mut self) -> ParseResult<()> {
225+
let value = TextWrap::try_from(self.lexer.take_option_right_side()?)?;
226+
if self.is_row_options {
227+
self.row.text_wrap = value;
228+
} else {
229+
self.cell.text_wrap = value;
230+
}
231+
Ok(())
232+
}
233+
223234
fn option(&mut self) -> ParseResult<()> {
224235
let option_name = self.lexer.take_token(Token::OptionName)?;
225236
match option_name.str_match.as_str() {
@@ -239,6 +250,7 @@ where
239250
"t" | "text" => self.text_option(),
240251
"v" | "var" => self.var_option(),
241252
"va" | "valign" => self.valign_option(),
253+
"w" | "wrap" => self.wrap_option(),
242254
_ => Err(option_name.into_parse_error("Expected a valid cell option")),
243255
}
244256
}

src/row/mod.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ mod display;
22

33
use crate::cell_options::{
44
BorderSide, BorderStyle, DataValidation, Fill, HorizontalAlign, NumberFormat, TextFormat,
5-
VerticalAlign,
5+
TextWrap, VerticalAlign,
66
};
77
use crate::{ArcSourceCode, Cell, Result, Rgb, Scope};
88
use csvp::Field;
@@ -25,6 +25,7 @@ pub struct Row {
2525
pub note: Option<String>,
2626
pub number_format: Option<NumberFormat>,
2727
pub text_formats: collections::HashSet<TextFormat>,
28+
pub text_wrap: TextWrap,
2829
pub var: Option<String>,
2930
pub vertical_align: Option<VerticalAlign>,
3031
}

src/target/excel/excel_cell.rs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
//! Converts between a Cell and an `umya_spreadsheet::Style` (which feature-wise actually happen
44
//! to map pretty nicely)
55
use crate::cell_options::{
6-
BorderSide, BorderStyle, HorizontalAlign, NumberFormat, TextFormat, VerticalAlign,
6+
BorderSide, BorderStyle, HorizontalAlign, NumberFormat, TextFormat, TextWrap, VerticalAlign,
77
};
88
use crate::{Cell, Rgb};
99

@@ -83,6 +83,15 @@ impl From<VerticalAlign> for umya_spreadsheet::VerticalAlignmentValues {
8383
}
8484
}
8585

86+
impl From<TextWrap> for bool {
87+
fn from(value: TextWrap) -> Self {
88+
match value {
89+
TextWrap::Wrap => true,
90+
_ => false,
91+
}
92+
}
93+
}
94+
8695
impl<'a> ExcelCell<'a> {
8796
pub(super) fn has_style(&self) -> bool {
8897
let cell = self.0;
@@ -113,6 +122,8 @@ impl<'a> ExcelCell<'a> {
113122
alignment.set_vertical(v.into());
114123
}
115124

125+
alignment.set_wrap_text(self.0.text_wrap.into());
126+
116127
s.set_alignment(alignment);
117128
}
118129

src/target/google_sheets/google_sheets_cell.rs

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
//! payloads should reflect only the things the user specified on the cell.
77
//!
88
use crate::cell_options::{
9-
BorderSide, BorderStyle, DataValidation, HorizontalAlign, NumberFormat, TextFormat,
9+
BorderSide, BorderStyle, DataValidation, HorizontalAlign, NumberFormat, TextFormat, TextWrap,
1010
VerticalAlign,
1111
};
1212
use crate::{Cell, Rgb};
@@ -68,6 +68,7 @@ impl<'a> GoogleSheetsCell<'a> {
6868
let number_format = self.number_format();
6969
let text_format = self.text_format();
7070
let vertical_alignment = self.vertical_alignment();
71+
let wrap_strategy = self.wrap_strategy();
7172

7273
if borders.is_none()
7374
&& background_color_style.is_none()
@@ -87,6 +88,9 @@ impl<'a> GoogleSheetsCell<'a> {
8788
number_format,
8889
text_format,
8990
vertical_alignment,
91+
// TODO: this doesn't jive with the caching strategy above, maybe it's time to give up
92+
// on that
93+
wrap_strategy: Some(wrap_strategy),
9094
..Default::default()
9195
})
9296
}
@@ -291,6 +295,15 @@ impl<'a> GoogleSheetsCell<'a> {
291295
.to_string()
292296
})
293297
}
298+
299+
fn wrap_strategy(&self) -> String {
300+
match self.0.text_wrap {
301+
TextWrap::Wrap => "WRAP",
302+
TextWrap::Overflow => "OVERFLOW",
303+
TextWrap::Clip => "CLIP",
304+
}
305+
.to_string()
306+
}
294307
}
295308

296309
#[cfg(test)]

0 commit comments

Comments
 (0)