Skip to content
C.W.K.
Lesson 02 of 05 · published

Bindings Complete Themselves — and Respect the Names People Write

~12 min · complete-bindings, rename, delete, generated-names

Level 0Idle Hands
0 XP0/39 lessons0/12 achievements
0/100 XP to next level100 XP to go0% complete
"A command still carrying the name Tinker generated from the old preset name follows a rename; a name someone wrote is kept." — the doc comment on Configuration.upsert

A New Preset Arrives With Its Commands

When Dad (or Pippa, through preset.save) adds a preset called "Recording", he does not have to create its commands. On save, completeBindings sees a preset with no toggle, apply or restore command and appends them: record.toggle named "Toggle Recording", record.apply named "Apply Recording", record.restore named "Restore Recording". Each appears in the shortcut list with an empty recorder and its own tinker:// URL, ready to bind.

Generated Names Versus Written Names

Those generated names are a guess. The seeded editing preset shows what a person writes instead: "Toggle Final Cut editing", "Prepare for Final Cut", "Editing done". Now rename the preset. A naive implementation either leaves every command name stale ("Toggle Recording" for a preset now called "Screencast") or overwrites all of them, destroying "Editing done". Tinker's upsert does neither. For each of the three operations it finds the command, and renames it only if its current name is exactly what Tinker would have generated from the old preset name. A generated name follows the rename; a written one is kept.

Deleting a Preset Deletes Only What Can Only Address It

removePreset removes the preset and every command whose operation is a toggle, apply or restore of that preset ID — commands that would otherwise point at nothing and fail validation. It leaves capability commands and the release command alone. In Settings, Delete is an armed two-click control. And because saving is refused while a changed preset is active, a preset that is currently applied cannot be deleted at all; the app door's preset.delete describes itself accordingly as deleting an inactive preset.

Completion Never Removes

Completion only adds. It never deletes a command Dad made, never renames one, and never re-adds a chord he cleared. It also always ensures that "Stop and restore everything" exists — so even a settings file someone trimmed by hand cannot lose the emergency exit. The design rule behind all three behaviours is the same: Tinker fills in what is missing and treats anything a person wrote as authoritative.

Code

Rename follows generated names only·swift
static func generatedName(_ verb: String, _ presetName: String) -> String {
  verb.capitalized + " " + presetName
}

public mutating func upsert(_ preset: Preset) {
  guard let index = presets.firstIndex(where: { $0.id == preset.id }) else {
    presets.append(preset); return
  }
  let previous = presets[index]
  presets[index] = preset
  guard previous.name != preset.name else { return }
  for (verb, operation) in Self.presetOperations(preset.id) {
    guard let i = commands.firstIndex(where: { $0.operation == operation }),
          commands[i].name == Self.generatedName(verb, previous.name) else { continue }
    commands[i].name = Self.generatedName(verb, preset.name)
  }
}
Delete removes only the commands that can only address the preset·swift
public mutating func removePreset(_ id: String) {
  presets.removeAll { $0.id == id }
  commands.removeAll { command in
    switch command.operation {
    case .toggle(let preset), .apply(let preset), .restore(let preset): preset == id
    default: false
    }
  }
}
Renaming 'Final Cut editing' to 'Video edit'·text
command         before                       after
--------------  ---------------------------  ---------------------------
edit.toggle     Toggle Final Cut editing     Toggle Video edit   (generated -> follows)
edit.apply      Prepare for Final Cut        Prepare for Final Cut  (written -> kept)
edit.restore    Editing done                 Editing done           (written -> kept)

External links

Exercise

The seeded edit.toggle is named 'Toggle Final Cut editing', which happens to equal generatedName("toggle", "Final Cut editing"). Explain why the table above shows it following the rename, and decide whether that is the behaviour you want. If not, what would you change — in the data or in the rule?
Hint
The rule can only compare strings, so a written name that happens to equal the generated one is indistinguishable from a generated one and follows the rename. Here that is harmless and probably desirable. If you needed to pin it, the cheapest change is in the data — write a name that differs from the generated form — rather than adding a 'was generated' flag to every command.

Progress

Progress is local-only — sign in to sync across devices.
Spotted a bug or have feedback on this page?Report an Issue

Comments 0

🔔 Reply notifications (sign in)
Sign in — Please sign in to comment.

No comments yet — be the first.