# Revamping the libipuz edit APIs **Status:** Reviewed *federico@gnome.org* | partially-implemented ## Summary Currently, all editing is split between `xword-state.c` and libipuz. We propose a structured way to handle editing to make it easier to reason about, and to put the majority of the complexity in libipuz. Currently, the `fix()` API is implemented, but the linters are not. ## Problem Statement There currently exists a mixture of API levels used to mutate puzzles through the editor. For example, it's possible to change an individual cell within the puzzle by calling `ipuz_cell_set_*`, or `ipuz_style_set_*`. Similarly, you can change a clue. However, all these functions bring some challenges with them, that can lead to an internally inconsistent state: * Changing the cell_type can leave the numbering and clue cells inconsistent. * Changing the symmetry can be super destructive * Even clues are super-tricky: We need to make sure every cell knows which clue it's part, and vice-versa. Additionally, there are some conventions we want to enforce that aren't part of the ipuz spec. In particular: * For crosswords, there are only across/down clues, and every cell is part of at least one clue. * We are correctly numbered. Clue numbers increase by 1, and there are no gaps * We store the clue cells within the puzzle, and they don't have discontinuities We tried to work around this in calls from `xword-state.c` by introducing the `ipuz_crossword_set_cell_type()` function, as well as storing the symmetry within the puzzle. This is always immediatedly followed but a call to `ipuz_crossword_calculate_clues()`. This level of granularity isn't sufficient for what we need to do and hasn't aged well. ## Challenges ### Subtypes Arrowword, Filippine, and Barred puzzles inherit from Crossword, and Acrostics don't inherit from Crosswords at all. They don't share the same conventions that Crossword has, though there are some commonalitites. All of these will need their own adjustment to editing. For example, Filippine puzzles only have across clues, and have a vertical line that's highlighted with the answer. Barred puzzles don't have blocks or nulls, and expect "T", "L", and "TL" styles to exist. Arrowwords need to have the arrow updated. Symmetry makes almost no sense for Acrostics. **NOTE:** In addition, both Filippine and Acrostic puzzles will benefit from a locked-down custom editor that doesn't even let you change the grid directly. They might want to bypass XwordState's editing system entirely. Given that we called it `XwordState`, the introduction of an `AcrosticState` wouldn't be surprising, but this may be substantial work and sources of errors. ### Raw Mode Some puzzles do need to break the rules. There are fun puzzles with diagonal clues, or 'J' hooks that don't reveal themselves immediately. Maybe you do want a barred puzzle with blocks and/or discontinuities. We may want to consider adding a *raw* mode to the editor that just uses the `_set()` functions and lets you produce valid-but-unusual puzzles. This mode would require people to automatically set the range of cells of clues, for example. ## Proposal ### libipuz changes * We will have three different APIs for libipuz. First the existing one demarked by the ipuz `_set()` functions. These will let you manipulate the puzzle however you see fit. Example: `ipuz_cell_set_label()` * We add a set of linting APIs demarked by `_check()` functions. This will return a list of potentially broken attributes that might need fixing. We will have to define a set of types for each check type. Example: `ipuz_crossword_check_cell_labels()`. * We add a correction API, demarked by `_fix()` functions * `_fix()` functions may need arguments. For example, `fix_symmetry()` will need to take in a list of modified cells to update. Otherwise, we won't know which direction to change things. * As a convenience, we add an `ipuz_crossword_fix_all()` function to call the fix functions in the correct order. * We remove symmetry as a tracked feature of crosswords. Here's a diagram of this behavior. ![libipuz editing changes overview](ipuz-edit-interface-graph.jpg)
_sketch of editing changes_ **NOTE:** We will make both the `_check()` and `_fix()` function class methods, so that child types can override or chain to them. Most notably, `fix_all()` also needs to be a class_method. `fix_all()` will also need to take in arguments, so the class method will require kwargs. ### XwordState changes * We move any settings to give us hints about the type of fixes to the Quirks object. * We will introduce a *mangaged_mode*/*raw_mode* enum to the Quirks object to track whether we want to fix things up automatically or not. * As another example, *symmetry* is another quirk setting * All current mutators within xword_state will fix up puzzle based on that mode. ### Editor Changes / Raw Mode * There's a (currently unwritten) style tab in the editor that will let you customize the label / style of grids. We will have to prevent the user from breaking assumptions * The current `edit-` widgets were written to expect the puzzle type and base puzzle to change. We will make it so that `PUZZLE_KIND` is a construct-only property. `raw_mode` should be a property that can change, and the widgets have to adjust themselves to adapt.