New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
A simpler way to input tables and grids content #4071
Comments
This feels way too different from the rest of Typst's syntax and the '&' having a special meaning outside of math mode would create a lot of problems. You would have to give it a special meaning only when used inside a content block passed to the table function. |
I think it would be better to add #table(
// ...
table.row[][],
// ...
) |
Alright, maybe another separator could do the job. |
I don't think a future syntax sugar for simple tables is necessarily a bad idea, but it would require a lot of consideration. |
The thing to look for though is how well that would work with colspan and rowspan (cells that spans multiple rows or columns). Not sure that wouldn't break things short of having a separate simple table mode. |
The cell separator could be a constant, e.g. #sep, that is non-op (or error) outside a table/grid, or other available symbol or even a two-symbol operator. |
I'd be ok with either this or a With that said, I'm not sure alignment points in markup mode would be used for tables, but rather perhaps for actual alignment points if the layout system is ever made flexible enough for that. |
why \ would not be good enough to end a row? If a newline is needed inside a cell, which is not very frequent, a Edit: I think I got it. |
At least now (v0.11.0), I don't think your proposed style really simplifies the syntax. And actually, in my view, your style is not consistent with typst syntax style. If let me choose between them, I would like the current table syntax absolutely. |
Thank you, @Coekjan, for sharing your perspective. I'd like to present my thoughts and respectfully express my disagreement. The first line of Typst's description on the GitHub page reads, "Typst is a new markup-based typesetting system," followed by, "Built-in markup for the most common formatting tasks". Thus, once most commands are set up in the beginning of a document, we can easily use markup for titles, lists, terms, bold text, etc, ensuring clear content. In my opinion, the current syntax for tables and grids contradicts this philosophy. While matrices and vectors employ a form of markup, tables and grids lack a similar approach that would ensure clarity and conciseness. Instead, they rely on a plethora of braces and lack the ability to declare new rows. |
Please refer to my previous post. I believe that using markup aligns well with Typst's syntax. Additionally, it seems that the current table and grid commands are the only ones that require numerous content arguments. |
Different from heading, list, bold-text, in my view, table is a complex thing. Features like cell-merge (colspan/rowspan), cell-alignment and cell-rotation seem difficult to be expressed in markup. This is, I believe, one of the reasons why typst intro the
Yes, I believe typst team will consider to add some support to declare a new row. I think it is possible to support declaring new rows within current |
Thank you @Coekjan for your clarifications.
(a different separator can be used) While I believe tablem could be helpful for some users, it's not suitable for most use cases. Furthermore, I think that the table syntax in Typst should aim to be clearer than its LaTeX counterpart, not the other way around. |
Tried to borrow some ideas from Kotlin's type-safe builder and I came up with this: #table-dsl(inset: 5pt, column => {
column("id", row => {
row("1")
row("2")
row("3")
})
column("name", row => {
row("ana")
row("marcus")
row("joana")
})
column("age", row => {
row(18)
row(32)
row(26)
})
}) The code behind it isn't that fancy, but I couldn't make it work without a #let table-dsl(inset: 10pt, builder) = {
let table-state = state("table", ())
let row-builders = (column) => (builder) => {
table-state.update(table => {
let pos = table.position(c => c.name == column)
let col = table.at(pos)
col.rows.push(builder)
table.at(pos) = col
table
})
}
let columns = (name, column-builder) => {
table-state.update(table => {
table.push((name: name, rows: ()))
table
})
column-builder(row-builders(name))
}
builder(columns)
context {
let t = table-state.get()
let row-size = t.map(v => v.rows.len()).sorted().last()
let values = ()
for row-index in range(0, row-size) {
for column in t {
values.push(column.rows.at(row-index, default: []))
}
}
let headers = t.map(c => c.name)
table-state.update(s => ())
table(
columns: headers.len(),
inset: inset,
align: horizon,
table.header(..headers),
..values.map(r => [#r])
)
}
} Anyway, I have no problem with the current |
I also believe that to target Typst as a markup-based typesetting language, tables in markdown syntax, like tablem does, should be the default, without having to use external packages. In the same way that This lowers the barrier of entry of people that are already familiar with markdown significantly, writing tables is a fairly common task. |
I am absolutely agree. |
Here's a way to do this purely in code (without relying on Code
// MIT-0 licensed (feel free to use!)
#let table-dsl(col-callback, ..args) = {
assert(args.pos().len() == 0, message: "'table-dsl' does not take additional positional arguments, only the 'columns => ...' callback")
let make-row(cell) = ([#cell],) // simply wrap in an array
let make-column(header-cell, row-callback) = {
// row() row() row() will join the cells
// into a single array, which we flatten here
// into a single array of column cells.
// We wrap that array into a larger array
// in order to produce an array of column arrays
// when joining column() column().
((header-cell, ..row-callback(make-row)),)
}
// array of columns
// each column is an array of cells
let columns = col-callback(make-column)
// transpose into array of rows
// due to row-major order
let rows = array.zip(..columns)
let header = table.header(..rows.remove(0))
// flatten the rest into a single array of cells
let cells = rows.join()
table(columns: columns.len(), ..args.named(), header, ..cells)
}
// Example
#table-dsl(inset: 5pt, column => {
column("id", row => {
row("1")
row("2")
row("3")
})
column("name", row => {
row("ana")
row("marcus")
row("joana")
})
column("age", row => {
row(18)
row(32)
row(26)
})
}) |
Regarding a dedicated syntax for tables: Personally, I think Markdown tables in particular are a bad fit for Typst, because they may require a good amount of fiddling to get right. In particular, since you also input the table's lines, you need to use the correct amount of While I can see how a dedicated syntax for tables could make sense, it would have to account for large tables and be easy to use and modify. The current syntax achieves this well because you can split a function call across multiple lines, and you can lay out the parameters visually in any way you want. With that said, it is also possible to lay out the parameters in a confusing manner, but never in a way that you can't "recover" (follow the parentheses and check the amount of columns). Additionally, editing the contents of a cell is not a problem at all, since each cell is delimited by commas with any amount of whitespace inbetween. Regardless, I think packages are a great way to experiment with other possible approaches. On our side, at the compiler, we could consider adding support for an optional Either way, I'm interested to see other suggestions for possible syntaxes, as I'm sure the discussion could benefit from some more diversity 😄 |
That's really cool! Based on your example, I guess I was able to improve it further, I'm just not so sure about the possible limitations tho: Basically, I was able to remove the callbacks, which I think makes it a bit easier:#let row(cell) = ([#cell],)
#let column(name, rows) = ((name, ..rows),)
#let table-dsl(all-rows, ..args) = {
assert(
args.pos().len() == 0,
message: "'table-dsl' does not take additional positional arguments"
)
let rows = array.zip(..all-rows)
let header = table.header(..rows.remove(0))
let cells = rows.join()
table(columns: header.children.len(), ..args.named(), header, ..cells)
}
#table-dsl(inset: 5pt, {
column("id", {
row("1")
row("2")
row("3")
})
column("name", {
row("ana")
row("marcus")
row("joana")
})
column("age", {
row(18)
row(32)
row(26)
})
}) I'll try it in some projects, if I don't find any issues, I'll submit a new package. Thanks for the tip. 😄 |
Description
FR: A simpler way to input cells in tables and grids
Current Issues:
Bracketed cell entries: The current requirement to use brackets for each cell is cumbersome and often confusing, particularly in large tables.
No explicit row separator: Without a specific row separator, cells are placed based on the previously defined number of columns, making it difficult to accurately differentiate between rows. This issue becomes particularly problematic when cells are removed or replaced, often resulting in a disorganized table structure during editing. As a result, quick compilation loses its effectiveness because cells shift to different positions, causing confusion during the editing process.
Furthermore, when rows have empty cells at the end, multiple empty brackets are required, adding unnecessary clutter. This issue could be resolved with a dedicated newline command, simplifying table input.
Proposed solution:
One content field with cell separator: Implement a single content field where the ampersand (&) symbol acts as a separator between cells. This minimizes the need for multiple brackets and commas. The & symbol would hold special significance, similar to this and other symbols used for specific formatting purposes: - for itemization, + for enumerators, \ for lists, and & for equation alignment.
Row Separator: Utilize the backslash \ as a row separator to mark the start of a new row. This symbol would have a dedicated meaning, much like it does in lists where each item is separated by a backslash.
Use Case
Consider the following example, which simplifies the input format:
Currently:
Proposed:
The proposed format seeks to streamline the input process, making table and grid creation more intuitive and less error-prone. Additionally, its resemblance to LaTeX could help make the transition smoother for newcomers, providing a familiar structure and easing the learning curve. Furthermore, the simplicity of the proposed format aligns more closely to a markdown syntax.
The text was updated successfully, but these errors were encountered: