cat / 012
clone

a spreadsheet for the terminal: lotus 1-2-3 looks, google sheets keys and formulas, one go binary

Export formulas naming a missing sheet to XLSX as their values, with a note

Dami committed 97caaa7
commit
97caaa7353423a0942e07d05227e1f05837def93
parent
cb58592dcc47269d9ee3e5416fa8b3aa695a8dd1
author
Dami <dami.etoile@gmail.com>
5 files changed, +109 -15
Mdocs/files.md2+-
Minternal/fileio/snapshot.go2+
Minternal/fileio/xlsx.go71++++++++++++++++++++++++------
Minternal/fileio/xlsx_test.go29++++++++++++
Minternal/sheet/refs.go20++++++++
M docs/files.md +1 -1
@@ -10,7 +10,7 @@ bar; Esc cancels. Saving an imported sheet asks whether to save it as a
1010 | Format | Import | Download |1111 |---|---|---|1212 | CSV, TSV | Delimiter (`,` `;` tab `\|`), UTF-8 BOM, UTF-16 and Windows-1252 detected; entries become numbers, dates, currency and percentages as if typed; formulas stay text | Values as shown, as Sheets' Download does |13-| Excel `.xlsx` | Every sheet, opening on the one Excel showed: values, formulas (references between sheets too), workbook named ranges, number formats, bold, italic, underline, strikethrough, alignment, column widths. Formulas 012 can't read (unknown functions) keep their values; Excel pivot tables come in as the values they showed | Every sheet, the same, with formulas in Excel's syntax and their results cached. JEV functions and `#AND#` save as values, and so do pivot tables: Excel gets the results, not a pivot |13+| Excel `.xlsx` | Every sheet, opening on the one Excel showed: values, formulas (references between sheets too), workbook named ranges, number formats, bold, italic, underline, strikethrough, alignment, column widths. Formulas 012 can't read (unknown functions) keep their values; Excel pivot tables come in as the values they showed | Every sheet, the same, with formulas in Excel's syntax and their results cached. JEV functions and `#AND#` save as values, and so do formulas naming a sheet that doesn't exist (their `#REF!`; Excel would refuse the reference) and pivot tables: Excel gets the results, not a pivot. The download's result counts the formulas saved as values, with an example |1414 | SQLite | Pick a table or view, or type a query; a header row names the columns | The sheet shown or the selection as a table, first row as column names; a table of that name is replaced |1515 | Parquet | Every column, with dates and timestamps; lists joined with commas | |1616 | Lotus 1-2-3 `.wk1`, `.wks` | Numbers, labels with their alignment, formats, column widths, formulas translated (references, operators, `@SUM`, `@AVG`, `@IF`, `@ROUND`, `@PMT` and 60 more) or kept as values | |
M internal/fileio/snapshot.go +2 -0
M internal/fileio/xlsx.go +57 -14
@@ -10,6 +10,7 @@ import (
1010 1111 "github.com/xuri/excelize/v2"1212 13+ "github.com/FelineStateMachine/012/internal/formula"1314 "github.com/FelineStateMachine/012/internal/sheet"1415 )1516
@@ -230,7 +231,10 @@ func exportXLSX(_ context.Context, name string, snap *Snapshot, _ ExportOptions)
230231 if len(sheets) == 0 {231232 sheets = []*Snapshot{snap}232233 }233- w := &xlsxWriter{x: x, styleIDs: map[xlsxStyle]int{}, multi: len(sheets) > 1}234+ w := &xlsxWriter{x: x, styleIDs: map[xlsxStyle]int{}, multi: len(sheets) > 1, known: map[string]bool{}}235+ for _, sn := range sheets {236+ w.known[formula.SheetKey(sn.Name)] = true237+ }234238 res := &ExportResult{}235239 for i, sn := range sheets {236240 ws := sheetName(sn.Name)
@@ -261,9 +265,13 @@ func exportXLSX(_ context.Context, name string, snap *Snapshot, _ ExportOptions)
261265 if err := x.SetCalcProps(&excelize.CalcPropsOptions{FullCalcOnLoad: &yes}); err != nil {262266 return nil, err263267 }264- if w.values > 0 {268+ if w.values.n > 0 {265269 res.Notes = append(res.Notes, fmt.Sprintf("%s with no Excel equivalent saved as values, e.g. %s",266- count(w.values, "formula", "formulas"), w.example))270+ count(w.values.n, "formula", "formulas"), w.values.example))271+ }272+ if w.missing.n > 0 {273+ res.Notes = append(res.Notes, fmt.Sprintf("%s naming a sheet that doesn't exist saved as values, e.g. %s (%s)",274+ count(w.missing.n, "formula", "formulas"), w.missing.example, sheet.QuoteSheet(w.missingSheet)))267275 }268276 return res, writeFile(name, func(w io.Writer) error { return x.Write(w) })269277 }
@@ -272,9 +280,42 @@ func exportXLSX(_ context.Context, name string, snap *Snapshot, _ ExportOptions)
272280 type xlsxWriter struct {273281 x *excelize.File274282 styleIDs map[xlsxStyle]int275- values int // formulas written as values276- example string // the first of them277- multi bool // examples name their sheet283+ multi bool // examples name their sheet284+285+ // Formulas are written as their values when Excel has no equivalent,286+ // or when they name a sheet the file doesn't have, which Excel would287+ // refuse.288+ values valueCount289+ missing valueCount290+ missingSheet string // the sheet missing's example names291+ known map[string]bool // keys of the sheets written292+}293+294+// valueCount counts formulas written as values, keeping the first one's295+// address as an example.296+type valueCount struct {297+ n int298+ example string299+}300+301+func (v *valueCount) add(multi bool, ws string, a sheet.Addr) {302+ v.n++303+ if v.example == "" {304+ v.example = a.String()305+ if multi {306+ v.example = sheet.QuoteSheet(ws) + "!" + a.String()307+ }308+ }309+}310+311+// unknownSheet returns a sheet c's formula names that isn't written.312+func (w *xlsxWriter) unknownSheet(c SnapCell) (string, bool) {313+ for _, name := range c.Sheets {314+ if !w.known[formula.SheetKey(name)] {315+ return name, true316+ }317+ }318+ return "", false278319 }279320 280321 func (w *xlsxWriter) styleFor(f sheet.Format, st sheet.Style) (int, error) {
@@ -335,7 +376,8 @@ func (w *xlsxWriter) sheet(ws string, snap *Snapshot) (int, error) {
335376 }336377 337378 // cell is the snapshot cell c at a as excelize writes it. A formula with338-// no Excel equivalent is written as its value, and counted.379+// no Excel equivalent, or naming a sheet that isn't written, is written380+// as its value, and counted.339381 func (w *xlsxWriter) cell(ws string, a sheet.Addr, c SnapCell) (excelize.Cell, error) {340382 // A formula's inferred format is written too, so its result shows341383 // the same in Excel.
@@ -350,17 +392,18 @@ func (w *xlsxWriter) cell(ws string, a sheet.Addr, c SnapCell) (excelize.Cell, e
350392 if !c.Formula {351393 return xc, nil352394 }395+ if name, ok := w.unknownSheet(c); ok {396+ if w.missing.n == 0 {397+ w.missingSheet = name398+ }399+ w.missing.add(w.multi, ws, a)400+ return xc, nil401+ }353402 if fx, ok := toExcelFormula(c.Input); ok {354403 xc.Formula = fx355404 return xc, nil356405 }357- w.values++358- if w.example == "" {359- w.example = a.String()360- if w.multi {361- w.example = sheet.QuoteSheet(ws) + "!" + a.String()362- }363- }406+ w.values.add(w.multi, ws, a)364407 return xc, nil365408 }366409
M internal/fileio/xlsx_test.go +29 -0
M internal/sheet/refs.go +20 -0