From 1db5b69233fda4a0acb4ca2af67c4e566a80b85d Mon Sep 17 00:00:00 2001
From: fidgetingbits <fidgetingbits@memeoid.cx>
Date: Fri, 3 Nov 2023 09:55:28 +0800
Subject: [PATCH 01/16] initial nix language support

---
 data/playground/nix/asserts.nix               |  25 +++
 data/playground/nix/attrsets.nix              |  19 ++
 data/playground/nix/comments.nix              |   8 +
 data/playground/nix/complex.nix               |  67 +++++++
 data/playground/nix/functions.nix             |  13 ++
 data/playground/nix/lists.nix                 |   7 +
 data/playground/nix/statements.nix            |  36 ++++
 data/playground/nix/strings.nix               |  13 ++
 packages/common/src/extensionDependencies.ts  |   1 +
 .../findSurroundingPairParseTreeBased.ts      |   5 +-
 .../findSurroundingPairTextBased.ts           |   5 +-
 .../{delimiterMaps.ts => getDelimiterMaps.ts} |  41 +++-
 .../getIndividualDelimiters.ts                |   4 +-
 .../modifiers/surroundingPair/index.ts        |   2 +-
 .../recorded/languages/nix/changeArg.yml      |  27 +++
 .../recorded/languages/nix/changeArg2.yml     |  27 +++
 .../recorded/languages/nix/changeBranch.yml   |  29 +++
 .../recorded/languages/nix/changeBranch3.yml  |  31 +++
 .../recorded/languages/nix/changeBranch4.yml  |  35 ++++
 .../recorded/languages/nix/changeCall.yml     |  29 +++
 .../recorded/languages/nix/changeCallee.yml   |  29 +++
 .../recorded/languages/nix/changeComment.yml  |  25 +++
 .../recorded/languages/nix/changeComment2.yml |  27 +++
 .../recorded/languages/nix/changeComment3.yml |  31 +++
 .../languages/nix/changeCondition.yml         |  35 ++++
 .../languages/nix/changeCondition2.yml        |  29 +++
 .../languages/nix/changeEveryIfState.yml      |  37 ++++
 .../recorded/languages/nix/changeEveryMap.yml |  56 ++++++
 .../languages/nix/changeEveryMap2.yml         |  62 ++++++
 .../languages/nix/changeEveryState.yml        |  43 ++++
 .../recorded/languages/nix/changeFunkName.yml |  29 +++
 .../recorded/languages/nix/changeIfState.yml  |  29 +++
 .../languages/nix/changeInsideFunk.yml        |  30 +++
 .../languages/nix/changeInsideLambda.yml      |  30 +++
 .../recorded/languages/nix/changeItem.yml     |  29 +++
 .../recorded/languages/nix/changeItem2.yml    |  35 ++++
 .../recorded/languages/nix/changeKey.yml      |  29 +++
 .../recorded/languages/nix/changeKeyPlex.yml  |  33 ++++
 .../recorded/languages/nix/changeList.yml     |  29 +++
 .../languages/nix/changeListGreenAir.yml      |  36 ++++
 .../recorded/languages/nix/changeMap.yml      |  37 ++++
 .../recorded/languages/nix/changeMap2.yml     |  34 ++++
 .../recorded/languages/nix/changeMap3.yml     |  31 +++
 .../recorded/languages/nix/changeMap4.yml     |  32 +++
 .../recorded/languages/nix/changeName.yml     |  43 ++++
 .../recorded/languages/nix/changeName2.yml    |  43 ++++
 .../recorded/languages/nix/changeRound.yml    |  29 +++
 .../recorded/languages/nix/changeRound2.yml   |  29 +++
 .../languages/nix/changeStateFine.yml         |  39 ++++
 .../recorded/languages/nix/changeStateOdd.yml |  45 +++++
 .../recorded/languages/nix/changeString.yml   |  29 +++
 .../recorded/languages/nix/changeString2.yml  |  29 +++
 .../recorded/languages/nix/changeString3.yml  |  31 +++
 .../recorded/languages/nix/changeValue.yml    |  37 ++++
 .../recorded/languages/nix/changeValue2.yml   |  43 ++++
 .../recorded/languages/nix/changeValue3.yml   |  37 ++++
 .../recorded/languages/nix/changeValue4.yml   |  43 ++++
 .../recorded/languages/nix/chuckArg.yml       |  29 +++
 .../recorded/languages/nix/chuckKey.yml       |  29 +++
 .../recorded/languages/nix/chuckValue.yml     |  37 ++++
 .../recorded/languages/nix/chuckValue2.yml    |  43 ++++
 queries/nix.scm                               | 187 ++++++++++++++++++
 62 files changed, 2038 insertions(+), 5 deletions(-)
 create mode 100644 data/playground/nix/asserts.nix
 create mode 100644 data/playground/nix/attrsets.nix
 create mode 100644 data/playground/nix/comments.nix
 create mode 100644 data/playground/nix/complex.nix
 create mode 100644 data/playground/nix/functions.nix
 create mode 100644 data/playground/nix/lists.nix
 create mode 100644 data/playground/nix/statements.nix
 create mode 100644 data/playground/nix/strings.nix
 rename packages/cursorless-engine/src/processTargets/modifiers/surroundingPair/{delimiterMaps.ts => getDelimiterMaps.ts} (54%)
 create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeArg.yml
 create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeArg2.yml
 create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeBranch.yml
 create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeBranch3.yml
 create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeBranch4.yml
 create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeCall.yml
 create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeCallee.yml
 create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeComment.yml
 create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeComment2.yml
 create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeComment3.yml
 create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeCondition.yml
 create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeCondition2.yml
 create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeEveryIfState.yml
 create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeEveryMap.yml
 create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeEveryMap2.yml
 create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeEveryState.yml
 create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeFunkName.yml
 create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeIfState.yml
 create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeInsideFunk.yml
 create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeInsideLambda.yml
 create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeItem.yml
 create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeItem2.yml
 create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeKey.yml
 create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeKeyPlex.yml
 create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeList.yml
 create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeListGreenAir.yml
 create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeMap.yml
 create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeMap2.yml
 create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeMap3.yml
 create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeMap4.yml
 create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeName.yml
 create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeName2.yml
 create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeRound.yml
 create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeRound2.yml
 create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeStateFine.yml
 create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeStateOdd.yml
 create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeString.yml
 create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeString2.yml
 create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeString3.yml
 create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeValue.yml
 create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeValue2.yml
 create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeValue3.yml
 create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeValue4.yml
 create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/chuckArg.yml
 create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/chuckKey.yml
 create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/chuckValue.yml
 create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/chuckValue2.yml
 create mode 100644 queries/nix.scm

diff --git a/data/playground/nix/asserts.nix b/data/playground/nix/asserts.nix
new file mode 100644
index 0000000000..f17323bfba
--- /dev/null
+++ b/data/playground/nix/asserts.nix
@@ -0,0 +1,25 @@
+# https://nixos.org/manual/nix/stable/language/constructs.html#assertions
+
+{ localServer ? false
+, httpServer ? false
+, sslSupport ? false
+, pythonBindings ? false
+, javaSwigBindings ? false
+, javahlBindings ? false
+, stdenv, fetchurl
+, openssl ? null, httpd ? null, db4 ? null, expat, swig ? null, j2sdk ? null
+}:
+
+assert localerver -> db4 != null;
+assert httpServer -> httpd != null && httpd.expat == expat;
+assert sslSupport -> openssl != null && (httpServer -> httpd.openssl == openssl);
+assert pythonBindings -> swig != null && swig.pythonSupport;
+assert javaSwigBindings -> swig != null && swig.javaSupport;
+assert javahlBindings -> j2sdk != null;
+
+stdenv.mkDerivation {
+  (name = "subversion-1.1.1";)
+  ...
+  openssl = if sslSupport then openssl else null;
+  ...
+}
diff --git a/data/playground/nix/attrsets.nix b/data/playground/nix/attrsets.nix
new file mode 100644
index 0000000000..dfd0115a58
--- /dev/null
+++ b/data/playground/nix/attrsets.nix
@@ -0,0 +1,19 @@
+{
+    a = { a = b; c = d; };
+    a = {
+        a = b;
+        c = d;
+    };
+
+    a = {
+        a = {
+            b = 1; };
+
+        c = { d = [ "1" 2 ] };
+    };
+
+    a = rec {
+        a = b;
+        b = a + 1;
+    };
+}
diff --git a/data/playground/nix/comments.nix b/data/playground/nix/comments.nix
new file mode 100644
index 0000000000..10c0d44c69
--- /dev/null
+++ b/data/playground/nix/comments.nix
@@ -0,0 +1,8 @@
+# Single-line comment
+/*
+ Multi-line comment
+*/
+{
+  a = b; # Inline comment (test)
+  b = c; # Inline comment 2
+}
diff --git a/data/playground/nix/complex.nix b/data/playground/nix/complex.nix
new file mode 100644
index 0000000000..68e2bfef6e
--- /dev/null
+++ b/data/playground/nix/complex.nix
@@ -0,0 +1,67 @@
+{
+  inputs = {
+    nixpkgs.url = "github:nixos/nixpkgs/nixos-23.05";
+    home-manager = {
+      url = "github:nix-community/home-manager/release-23.05";
+      inputs.nixpkgs.follows = "nixpkgs";
+    };
+  };
+
+  outputs = { self, nixpkgs, home-manager }:
+    let
+      mkHost = hostName: system:
+        (({ my-config, zfs-root, pkgs, system, ... }:
+          nixpkgs.lib.nixosSystem {
+            inherit system;
+            modules = [
+              # Module 0: zfs-root and my-config
+              ./modules
+
+              # Module 1: host-specific config, if exist
+              (if (builtins.pathExists
+                ./hosts/${hostName}/configuration.nix) then
+                (import ./hosts/${hostName}/configuration.nix { inherit pkgs; })
+              else
+                { })
+
+              # Module 2: entry point
+              (({ my-config, zfs-root, pkgs, lib, ... }: {
+                inherit my-config zfs-root;
+                system.configurationRevision = if (self ? rev) then
+                  self.rev
+                else
+                  throw "refuse to build: git tree is dirty";
+                system.stateVersion = "23.05";
+                imports = [
+                  "${nixpkgs}/nixos/modules/installer/scan/not-detected.nix"
+                  # "${nixpkgs}/nixos/modules/profiles/hardened.nix"
+                  # "${nixpkgs}/nixos/modules/profiles/qemu-guest.nix"
+                ];
+              }) {
+                inherit my-config zfs-root pkgs;
+                lib = nixpkgs.lib;
+              })
+
+              # Module 3: home-manager
+              home-manager.nixosModules.home-manager
+              {
+                home-manager.useGlobalPkgs = true;
+                home-manager.useUserPackages = true;
+              }
+
+              # Module 4: config shared by all hosts
+              (import ./configuration.nix { inherit pkgs; })
+            ];
+          })
+
+        # configuration input
+          (import ./hosts/${hostName} {
+            system = system;
+            pkgs = nixpkgs.legacyPackages.${system};
+          }));
+    in {
+      nixosConfigurations = {
+        exampleHost = mkHost "exampleHost" "x86_64-linux";
+      };
+    };
+}
diff --git a/data/playground/nix/functions.nix b/data/playground/nix/functions.nix
new file mode 100644
index 0000000000..3082051ec9
--- /dev/null
+++ b/data/playground/nix/functions.nix
@@ -0,0 +1,13 @@
+{
+  # Anonymous function
+  a = foo: (foo + 1);
+  x = a: b: a + b;
+
+  # Non-built-in function
+  ba = test( foo: bar: {
+    b = foo;
+  });
+
+  # Built-in function
+  x = map (x: y: x + x) [ 1 2 3 ];
+}
diff --git a/data/playground/nix/lists.nix b/data/playground/nix/lists.nix
new file mode 100644
index 0000000000..7b535c6ba4
--- /dev/null
+++ b/data/playground/nix/lists.nix
@@ -0,0 +1,7 @@
+{
+  foo = [ "a" a/b/c "b" ];
+  bar = [ A
+          B
+          C # foo
+          ];
+}
diff --git a/data/playground/nix/statements.nix b/data/playground/nix/statements.nix
new file mode 100644
index 0000000000..a04b68596d
--- /dev/null
+++ b/data/playground/nix/statements.nix
@@ -0,0 +1,36 @@
+{
+  key = if a then b else c;
+  key =
+    if a
+    then b
+    else c;
+
+  a = b; # Inline comment (test)
+  b = c; # Inline comment 2
+  a = 1 + 1;
+  a = a/b/c ? 0;
+  a = b: b + 1;
+
+  foo = let
+    a = b;
+    c = d;
+  in
+    {
+      output = b;
+    };
+
+  bar = let
+    a = 1;
+    b = 2;
+  in a + b;
+
+  bar =
+    with key;
+    let
+      a = key;
+    in
+      a;
+
+  a = x: x + 1;
+  b = x: y: x + y + 1;
+}
diff --git a/data/playground/nix/strings.nix b/data/playground/nix/strings.nix
new file mode 100644
index 0000000000..da8e2dcdab
--- /dev/null
+++ b/data/playground/nix/strings.nix
@@ -0,0 +1,13 @@
+{
+  a = "double quoted string";
+  b = ''two single quote string'';
+  c = ''
+    multi-line
+    quote string'';
+  d = "Interpolated ${value} string";
+  e = "Escaped \${string} ";
+  f = ''
+    "Nested"
+    string
+    '';
+}
diff --git a/packages/common/src/extensionDependencies.ts b/packages/common/src/extensionDependencies.ts
index 5139c3b3a7..3de42dd89c 100644
--- a/packages/common/src/extensionDependencies.ts
+++ b/packages/common/src/extensionDependencies.ts
@@ -6,6 +6,7 @@ export const extensionDependencies = [
   "scala-lang.scala", // scala
   "mrob95.vscode-talonscript", // talon
   "jrieken.vscode-tree-sitter-query", // scm
+  "bbenoist.nix", // nix
 
   // Necessary for the `drink cell` and `pour cell` tests
   "ms-toolsai.jupyter",
diff --git a/packages/cursorless-engine/src/processTargets/modifiers/surroundingPair/findSurroundingPairParseTreeBased.ts b/packages/cursorless-engine/src/processTargets/modifiers/surroundingPair/findSurroundingPairParseTreeBased.ts
index ccc756ea06..27e9ab3756 100644
--- a/packages/cursorless-engine/src/processTargets/modifiers/surroundingPair/findSurroundingPairParseTreeBased.ts
+++ b/packages/cursorless-engine/src/processTargets/modifiers/surroundingPair/findSurroundingPairParseTreeBased.ts
@@ -66,7 +66,10 @@ export function findSurroundingPairParseTreeBased(
 ) {
   const document: TextDocument = editor.document;
 
-  const individualDelimiters = getIndividualDelimiters(delimiters);
+  const individualDelimiters = getIndividualDelimiters(
+    document.languageId,
+    delimiters,
+  );
 
   const delimiterTextToDelimiterInfoMap = Object.fromEntries(
     individualDelimiters.map((individualDelimiter) => [
diff --git a/packages/cursorless-engine/src/processTargets/modifiers/surroundingPair/findSurroundingPairTextBased.ts b/packages/cursorless-engine/src/processTargets/modifiers/surroundingPair/findSurroundingPairTextBased.ts
index d0be1d0398..91bdb955d3 100644
--- a/packages/cursorless-engine/src/processTargets/modifiers/surroundingPair/findSurroundingPairTextBased.ts
+++ b/packages/cursorless-engine/src/processTargets/modifiers/surroundingPair/findSurroundingPairTextBased.ts
@@ -74,7 +74,10 @@ export function findSurroundingPairTextBased(
   const document: TextDocument = editor.document;
   const fullRange = allowableRange ?? document.range;
 
-  const individualDelimiters = getIndividualDelimiters(delimiters);
+  const individualDelimiters = getIndividualDelimiters(
+    editor.document.languageId,
+    delimiters,
+  );
 
   const delimiterTextToDelimiterInfoMap = Object.fromEntries(
     individualDelimiters.map((individualDelimiter) => [
diff --git a/packages/cursorless-engine/src/processTargets/modifiers/surroundingPair/delimiterMaps.ts b/packages/cursorless-engine/src/processTargets/modifiers/surroundingPair/getDelimiterMaps.ts
similarity index 54%
rename from packages/cursorless-engine/src/processTargets/modifiers/surroundingPair/delimiterMaps.ts
rename to packages/cursorless-engine/src/processTargets/modifiers/surroundingPair/getDelimiterMaps.ts
index b8e54be1fe..377b516628 100644
--- a/packages/cursorless-engine/src/processTargets/modifiers/surroundingPair/delimiterMaps.ts
+++ b/packages/cursorless-engine/src/processTargets/modifiers/surroundingPair/getDelimiterMaps.ts
@@ -5,7 +5,7 @@ import {
 
 type IndividualDelimiterText = string | string[];
 
-export const delimiterToText: Record<
+const delimiterToText: Record<
   SimpleSurroundingPairName,
   [IndividualDelimiterText, IndividualDelimiterText]
 > = {
@@ -25,6 +25,26 @@ export const delimiterToText: Record<
   squareBrackets: ["[", "]"],
 };
 
+const delimiterToTextNix: Record<
+  SimpleSurroundingPairName,
+  [IndividualDelimiterText, IndividualDelimiterText]
+> = {
+  angleBrackets: [
+    ["</", "<"],
+    [">", "/>"],
+  ],
+  backtickQuotes: ["`", "`"],
+  curlyBrackets: [["{", "${"], "}"],
+  doubleQuotes: ['"', '"'],
+  escapedDoubleQuotes: ['\\"', '\\"'],
+  escapedParentheses: ["\\(", "\\)"],
+  escapedSquareBrackets: ["\\[", "\\]"],
+  escapedSingleQuotes: ["\\'", "\\'"],
+  parentheses: [["(", "$("], ")"],
+  singleQuotes: ["''", "''"],
+  squareBrackets: ["[", "]"],
+};
+
 export const leftToRightMap: Record<string, string> = Object.fromEntries(
   Object.values(delimiterToText),
 );
@@ -46,3 +66,22 @@ export const complexDelimiterMap: Record<
     "angleBrackets",
   ],
 };
+
+/**
+ * Given a language id, returns a list of all possible delimiters
+ * for that language.
+ * @param languageId The language id
+ * @returns A list of all possible delimiters for that language
+ */
+export function getSimpleDelimiterMap(
+  languageId: string,
+): Record<
+  SimpleSurroundingPairName,
+  [IndividualDelimiterText, IndividualDelimiterText]
+> {
+  if (languageId == "nix") {
+    return delimiterToTextNix;
+  } else {
+    return delimiterToText;
+  }
+}
diff --git a/packages/cursorless-engine/src/processTargets/modifiers/surroundingPair/getIndividualDelimiters.ts b/packages/cursorless-engine/src/processTargets/modifiers/surroundingPair/getIndividualDelimiters.ts
index 16420cbf7a..f8f97a2ac8 100644
--- a/packages/cursorless-engine/src/processTargets/modifiers/surroundingPair/getIndividualDelimiters.ts
+++ b/packages/cursorless-engine/src/processTargets/modifiers/surroundingPair/getIndividualDelimiters.ts
@@ -1,6 +1,6 @@
 import { SimpleSurroundingPairName } from "@cursorless/common";
 import { IndividualDelimiter } from "./types";
-import { delimiterToText } from "./delimiterMaps";
+import { getSimpleDelimiterMap } from "./getDelimiterMaps";
 import { concat, uniq } from "lodash";
 import { isString } from "../../../util/type";
 
@@ -13,8 +13,10 @@ import { isString } from "../../../util/type";
  * @returns A list of information about all possible left / right delimiter instances
  */
 export function getIndividualDelimiters(
+  languageId: string,
   delimiters: SimpleSurroundingPairName[],
 ): IndividualDelimiter[] {
+  const delimiterToText = getSimpleDelimiterMap(languageId);
   return delimiters.flatMap((delimiter) => {
     const [leftDelimiter, rightDelimiter] = delimiterToText[delimiter];
 
diff --git a/packages/cursorless-engine/src/processTargets/modifiers/surroundingPair/index.ts b/packages/cursorless-engine/src/processTargets/modifiers/surroundingPair/index.ts
index 52898d6e88..92f4452b67 100644
--- a/packages/cursorless-engine/src/processTargets/modifiers/surroundingPair/index.ts
+++ b/packages/cursorless-engine/src/processTargets/modifiers/surroundingPair/index.ts
@@ -9,7 +9,7 @@ import getTextFragmentExtractor from "../../../languages/getTextFragmentExtracto
 import { Target } from "../../../typings/target.types";
 import { SurroundingPairTarget } from "../../targets";
 import { getContainingScopeTarget } from "../getContainingScopeTarget";
-import { complexDelimiterMap } from "./delimiterMaps";
+import { complexDelimiterMap } from "./getDelimiterMaps";
 import { SurroundingPairInfo } from "./extractSelectionFromSurroundingPairOffsets";
 import { findSurroundingPairParseTreeBased } from "./findSurroundingPairParseTreeBased";
 import { findSurroundingPairTextBased } from "./findSurroundingPairTextBased";
diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeArg.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeArg.yml
new file mode 100644
index 0000000000..c47e5037e2
--- /dev/null
+++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeArg.yml
@@ -0,0 +1,27 @@
+languageId: nix
+command:
+  version: 6
+  spokenForm: change arg
+  action:
+    name: clearAndSetSelection
+    target:
+      type: primitive
+      modifiers:
+        - type: containingScope
+          scopeType: {type: argumentOrParameter}
+  usePrePhraseSnapshot: true
+initialState:
+  documentContents: |-
+    { pkgs, ... }:
+    { a = 1; }
+  selections:
+    - anchor: {line: 0, character: 10}
+      active: {line: 0, character: 10}
+  marks: {}
+finalState:
+  documentContents: |-
+    :
+    { a = 1; }
+  selections:
+    - anchor: {line: 0, character: 0}
+      active: {line: 0, character: 0}
diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeArg2.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeArg2.yml
new file mode 100644
index 0000000000..f4d0656743
--- /dev/null
+++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeArg2.yml
@@ -0,0 +1,27 @@
+languageId: nix
+command:
+  version: 6
+  spokenForm: change arg
+  action:
+    name: clearAndSetSelection
+    target:
+      type: primitive
+      modifiers:
+        - type: containingScope
+          scopeType: {type: argumentOrParameter}
+  usePrePhraseSnapshot: true
+initialState:
+  documentContents: |-
+    { pkgs, ... }:
+    { a = 1; }
+  selections:
+    - anchor: {line: 1, character: 4}
+      active: {line: 1, character: 4}
+  marks: {}
+finalState:
+  documentContents: |-
+    :
+    { a = 1; }
+  selections:
+    - anchor: {line: 0, character: 0}
+      active: {line: 0, character: 0}
diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeBranch.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeBranch.yml
new file mode 100644
index 0000000000..f64258bb14
--- /dev/null
+++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeBranch.yml
@@ -0,0 +1,29 @@
+languageId: nix
+command:
+  version: 6
+  spokenForm: change branch
+  action:
+    name: clearAndSetSelection
+    target:
+      type: primitive
+      modifiers:
+        - type: containingScope
+          scopeType: {type: branch}
+  usePrePhraseSnapshot: true
+initialState:
+  documentContents: |-
+    {
+      foo = if a then b else c;
+    }
+  selections:
+    - anchor: {line: 1, character: 9}
+      active: {line: 1, character: 9}
+  marks: {}
+finalState:
+  documentContents: |-
+    {
+      foo =  else c;
+    }
+  selections:
+    - anchor: {line: 1, character: 8}
+      active: {line: 1, character: 8}
diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeBranch3.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeBranch3.yml
new file mode 100644
index 0000000000..3b3d7924f5
--- /dev/null
+++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeBranch3.yml
@@ -0,0 +1,31 @@
+languageId: nix
+command:
+  version: 6
+  spokenForm: change branch
+  action:
+    name: clearAndSetSelection
+    target:
+      type: primitive
+      modifiers:
+        - type: containingScope
+          scopeType: {type: branch}
+  usePrePhraseSnapshot: true
+initialState:
+  documentContents: |
+    {
+      key = if a then b else c;
+
+    }
+  selections:
+    - anchor: {line: 1, character: 22}
+      active: {line: 1, character: 22}
+  marks: {}
+finalState:
+  documentContents: |
+    {
+      key = if a then b ;
+
+    }
+  selections:
+    - anchor: {line: 1, character: 20}
+      active: {line: 1, character: 20}
diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeBranch4.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeBranch4.yml
new file mode 100644
index 0000000000..b6a4b19d1a
--- /dev/null
+++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeBranch4.yml
@@ -0,0 +1,35 @@
+languageId: nix
+command:
+  version: 6
+  spokenForm: change branch
+  action:
+    name: clearAndSetSelection
+    target:
+      type: primitive
+      modifiers:
+        - type: containingScope
+          scopeType: {type: branch}
+  usePrePhraseSnapshot: true
+initialState:
+  documentContents: |
+    {
+      key =
+        if a
+        then b
+        else c;
+    }
+  selections:
+    - anchor: {line: 3, character: 6}
+      active: {line: 3, character: 6}
+  marks: {}
+finalState:
+  documentContents: |
+    {
+      key =
+        if a
+        
+        else c;
+    }
+  selections:
+    - anchor: {line: 3, character: 4}
+      active: {line: 3, character: 4}
diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeCall.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeCall.yml
new file mode 100644
index 0000000000..e5da31531e
--- /dev/null
+++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeCall.yml
@@ -0,0 +1,29 @@
+languageId: nix
+command:
+  version: 6
+  spokenForm: change call
+  action:
+    name: clearAndSetSelection
+    target:
+      type: primitive
+      modifiers:
+        - type: containingScope
+          scopeType: {type: functionCall}
+  usePrePhraseSnapshot: true
+initialState:
+  documentContents: |
+    {
+      x = map (x: y: x + x) [ 1 2 3 ];
+    }
+  selections:
+    - anchor: {line: 1, character: 28}
+      active: {line: 1, character: 28}
+  marks: {}
+finalState:
+  documentContents: |
+    {
+      x = ;
+    }
+  selections:
+    - anchor: {line: 1, character: 6}
+      active: {line: 1, character: 6}
diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeCallee.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeCallee.yml
new file mode 100644
index 0000000000..31307cf239
--- /dev/null
+++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeCallee.yml
@@ -0,0 +1,29 @@
+languageId: nix
+command:
+  version: 6
+  spokenForm: change callee
+  action:
+    name: clearAndSetSelection
+    target:
+      type: primitive
+      modifiers:
+        - type: containingScope
+          scopeType: {type: functionCallee}
+  usePrePhraseSnapshot: true
+initialState:
+  documentContents: |
+    {
+      x = map (x: y: x + x) [ 1 2 3 ];
+    }
+  selections:
+    - anchor: {line: 1, character: 14}
+      active: {line: 1, character: 14}
+  marks: {}
+finalState:
+  documentContents: |
+    {
+      x =  (x: y: x + x) [ 1 2 3 ];
+    }
+  selections:
+    - anchor: {line: 1, character: 6}
+      active: {line: 1, character: 6}
diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeComment.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeComment.yml
new file mode 100644
index 0000000000..771680b2c8
--- /dev/null
+++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeComment.yml
@@ -0,0 +1,25 @@
+languageId: nix
+command:
+  version: 6
+  spokenForm: change comment
+  action:
+    name: clearAndSetSelection
+    target:
+      type: primitive
+      modifiers:
+        - type: containingScope
+          scopeType: {type: comment}
+  usePrePhraseSnapshot: true
+initialState:
+  documentContents: |
+    # Single-line comment
+  selections:
+    - anchor: {line: 0, character: 21}
+      active: {line: 0, character: 21}
+  marks: {}
+finalState:
+  documentContents: |+
+
+  selections:
+    - anchor: {line: 0, character: 0}
+      active: {line: 0, character: 0}
diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeComment2.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeComment2.yml
new file mode 100644
index 0000000000..06a6c95bc2
--- /dev/null
+++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeComment2.yml
@@ -0,0 +1,27 @@
+languageId: nix
+command:
+  version: 6
+  spokenForm: change comment
+  action:
+    name: clearAndSetSelection
+    target:
+      type: primitive
+      modifiers:
+        - type: containingScope
+          scopeType: {type: comment}
+  usePrePhraseSnapshot: true
+initialState:
+  documentContents: |
+    /*
+     Multi-line comment
+    */
+  selections:
+    - anchor: {line: 2, character: 0}
+      active: {line: 2, character: 0}
+  marks: {}
+finalState:
+  documentContents: |+
+
+  selections:
+    - anchor: {line: 0, character: 0}
+      active: {line: 0, character: 0}
diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeComment3.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeComment3.yml
new file mode 100644
index 0000000000..a93825c1cd
--- /dev/null
+++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeComment3.yml
@@ -0,0 +1,31 @@
+languageId: nix
+command:
+  version: 6
+  spokenForm: change comment
+  action:
+    name: clearAndSetSelection
+    target:
+      type: primitive
+      modifiers:
+        - type: containingScope
+          scopeType: {type: comment}
+  usePrePhraseSnapshot: true
+initialState:
+  documentContents: |
+    {
+      a = b; # Inline comment (test)
+      b = c; # Inline comment 2
+    }
+  selections:
+    - anchor: {line: 2, character: 13}
+      active: {line: 2, character: 13}
+  marks: {}
+finalState:
+  documentContents: |
+    {
+      a = b; # Inline comment (test)
+      b = c; 
+    }
+  selections:
+    - anchor: {line: 2, character: 9}
+      active: {line: 2, character: 9}
diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeCondition.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeCondition.yml
new file mode 100644
index 0000000000..0112c652b3
--- /dev/null
+++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeCondition.yml
@@ -0,0 +1,35 @@
+languageId: nix
+command:
+  version: 6
+  spokenForm: change condition
+  action:
+    name: clearAndSetSelection
+    target:
+      type: primitive
+      modifiers:
+        - type: containingScope
+          scopeType: {type: condition}
+  usePrePhraseSnapshot: true
+initialState:
+  documentContents: |
+    {
+      key =
+        if a > b
+        then b
+        else c;
+    }
+  selections:
+    - anchor: {line: 3, character: 9}
+      active: {line: 3, character: 9}
+  marks: {}
+finalState:
+  documentContents: |
+    {
+      key =
+        if 
+        then b
+        else c;
+    }
+  selections:
+    - anchor: {line: 2, character: 7}
+      active: {line: 2, character: 7}
diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeCondition2.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeCondition2.yml
new file mode 100644
index 0000000000..ee12a12a62
--- /dev/null
+++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeCondition2.yml
@@ -0,0 +1,29 @@
+languageId: nix
+command:
+  version: 6
+  spokenForm: change condition
+  action:
+    name: clearAndSetSelection
+    target:
+      type: primitive
+      modifiers:
+        - type: containingScope
+          scopeType: {type: condition}
+  usePrePhraseSnapshot: true
+initialState:
+  documentContents: |
+    {
+      key = assert a > b; "foo";
+    }
+  selections:
+    - anchor: {line: 1, character: 11}
+      active: {line: 1, character: 11}
+  marks: {}
+finalState:
+  documentContents: |
+    {
+      key = assert ; "foo";
+    }
+  selections:
+    - anchor: {line: 1, character: 15}
+      active: {line: 1, character: 15}
diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeEveryIfState.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeEveryIfState.yml
new file mode 100644
index 0000000000..a35c93253e
--- /dev/null
+++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeEveryIfState.yml
@@ -0,0 +1,37 @@
+languageId: nix
+command:
+  version: 6
+  spokenForm: change every if state
+  action:
+    name: clearAndSetSelection
+    target:
+      type: primitive
+      modifiers:
+        - type: everyScope
+          scopeType: {type: ifStatement}
+  usePrePhraseSnapshot: true
+initialState:
+  documentContents: |
+    {
+      key = if a then b else c;
+      key =
+        if a
+        then b
+        else c;
+    }
+  selections:
+    - anchor: {line: 5, character: 11}
+      active: {line: 5, character: 11}
+  marks: {}
+finalState:
+  documentContents: |
+    {
+      key = ;
+      key =
+        ;
+    }
+  selections:
+    - anchor: {line: 1, character: 8}
+      active: {line: 1, character: 8}
+    - anchor: {line: 3, character: 4}
+      active: {line: 3, character: 4}
diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeEveryMap.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeEveryMap.yml
new file mode 100644
index 0000000000..7dfd216810
--- /dev/null
+++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeEveryMap.yml
@@ -0,0 +1,56 @@
+languageId: nix
+command:
+  version: 6
+  spokenForm: change every map
+  action:
+    name: clearAndSetSelection
+    target:
+      type: primitive
+      modifiers:
+        - type: everyScope
+          scopeType: {type: map}
+  usePrePhraseSnapshot: true
+initialState:
+  documentContents: |
+    {
+        a = { a = b; c = d; };
+        a = {
+            a = b;
+            c = d;
+        };
+
+        a = {
+            a = {
+                b = 1; };
+
+            c = { d = [ "1" 2 ] };
+        };
+
+        a = rec {
+            a = b;
+            b = a + 1;
+        };
+    }
+  selections:
+    - anchor: {line: 13, character: 0}
+      active: {line: 13, character: 0}
+  marks: {}
+finalState:
+  documentContents: |
+    {
+        a = ;
+        a = ;
+
+        a = ;
+
+        a = ;
+    }
+  selections:
+    - anchor: {line: 1, character: 8}
+      active: {line: 1, character: 8}
+    - anchor: {line: 2, character: 8}
+      active: {line: 2, character: 8}
+    - anchor: {line: 4, character: 8}
+      active: {line: 4, character: 8}
+    - anchor: {line: 6, character: 8}
+      active: {line: 6, character: 8}
diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeEveryMap2.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeEveryMap2.yml
new file mode 100644
index 0000000000..8ee8cfa9d2
--- /dev/null
+++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeEveryMap2.yml
@@ -0,0 +1,62 @@
+languageId: nix
+command:
+  version: 6
+  spokenForm: change every map
+  action:
+    name: clearAndSetSelection
+    target:
+      type: primitive
+      modifiers:
+        - type: everyScope
+          scopeType: {type: map}
+  usePrePhraseSnapshot: true
+initialState:
+  documentContents: |
+    {
+        a = { a = b; c = d; };
+        a = {
+            a = b;
+            c = d;
+        };
+
+        a = {
+            a = {
+                b = 1; };
+
+            c = { d = [ "1" 2 ] };
+        };
+
+        a = rec {
+            a = b;
+            b = a + 1;
+        };
+    }
+  selections:
+    - anchor: {line: 10, character: 0}
+      active: {line: 10, character: 0}
+  marks: {}
+finalState:
+  documentContents: |
+    {
+        a = { a = b; c = d; };
+        a = {
+            a = b;
+            c = d;
+        };
+
+        a = {
+            a = ;
+
+            c = ;
+        };
+
+        a = rec {
+            a = b;
+            b = a + 1;
+        };
+    }
+  selections:
+    - anchor: {line: 8, character: 12}
+      active: {line: 8, character: 12}
+    - anchor: {line: 10, character: 12}
+      active: {line: 10, character: 12}
diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeEveryState.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeEveryState.yml
new file mode 100644
index 0000000000..7dff79a3a2
--- /dev/null
+++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeEveryState.yml
@@ -0,0 +1,43 @@
+languageId: nix
+command:
+  version: 6
+  spokenForm: change every state
+  action:
+    name: clearAndSetSelection
+    target:
+      type: primitive
+      modifiers:
+        - type: everyScope
+          scopeType: {type: statement}
+  usePrePhraseSnapshot: true
+initialState:
+  documentContents: |
+    {
+      foo = let
+        a = b;
+        c = d;
+      in
+        {
+          output = b;
+        };
+    }
+  selections:
+    - anchor: {line: 2, character: 10}
+      active: {line: 2, character: 10}
+  marks: {}
+finalState:
+  documentContents: |
+    {
+      foo = let
+        
+        
+      in
+        {
+          output = b;
+        };
+    }
+  selections:
+    - anchor: {line: 2, character: 4}
+      active: {line: 2, character: 4}
+    - anchor: {line: 3, character: 4}
+      active: {line: 3, character: 4}
diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeFunkName.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeFunkName.yml
new file mode 100644
index 0000000000..b261b56a97
--- /dev/null
+++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeFunkName.yml
@@ -0,0 +1,29 @@
+languageId: nix
+command:
+  version: 6
+  spokenForm: change funk name
+  action:
+    name: clearAndSetSelection
+    target:
+      type: primitive
+      modifiers:
+        - type: containingScope
+          scopeType: {type: functionName}
+  usePrePhraseSnapshot: true
+initialState:
+  documentContents: |
+    {
+      x = a: b: a + b;
+    }
+  selections:
+    - anchor: {line: 1, character: 16}
+      active: {line: 1, character: 16}
+  marks: {}
+finalState:
+  documentContents: |
+    {
+       = a: b: a + b;
+    }
+  selections:
+    - anchor: {line: 1, character: 2}
+      active: {line: 1, character: 2}
diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeIfState.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeIfState.yml
new file mode 100644
index 0000000000..98fee9d53c
--- /dev/null
+++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeIfState.yml
@@ -0,0 +1,29 @@
+languageId: nix
+command:
+  version: 6
+  spokenForm: change if state
+  action:
+    name: clearAndSetSelection
+    target:
+      type: primitive
+      modifiers:
+        - type: containingScope
+          scopeType: {type: ifStatement}
+  usePrePhraseSnapshot: true
+initialState:
+  documentContents: |
+    {
+      key = if a then b else c;
+    }
+  selections:
+    - anchor: {line: 1, character: 24}
+      active: {line: 1, character: 24}
+  marks: {}
+finalState:
+  documentContents: |
+    {
+      key = ;
+    }
+  selections:
+    - anchor: {line: 1, character: 8}
+      active: {line: 1, character: 8}
diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeInsideFunk.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeInsideFunk.yml
new file mode 100644
index 0000000000..e9bf3612a0
--- /dev/null
+++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeInsideFunk.yml
@@ -0,0 +1,30 @@
+languageId: nix
+command:
+  version: 6
+  spokenForm: change inside funk
+  action:
+    name: clearAndSetSelection
+    target:
+      type: primitive
+      modifiers:
+        - {type: interiorOnly}
+        - type: containingScope
+          scopeType: {type: namedFunction}
+  usePrePhraseSnapshot: true
+initialState:
+  documentContents: |
+    {
+      x = a: b: a + b;
+    }
+  selections:
+    - anchor: {line: 1, character: 16}
+      active: {line: 1, character: 16}
+  marks: {}
+finalState:
+  documentContents: |
+    {
+      x = ;
+    }
+  selections:
+    - anchor: {line: 1, character: 6}
+      active: {line: 1, character: 6}
diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeInsideLambda.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeInsideLambda.yml
new file mode 100644
index 0000000000..e9536e8c9b
--- /dev/null
+++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeInsideLambda.yml
@@ -0,0 +1,30 @@
+languageId: nix
+command:
+  version: 6
+  spokenForm: change inside lambda
+  action:
+    name: clearAndSetSelection
+    target:
+      type: primitive
+      modifiers:
+        - {type: interiorOnly}
+        - type: containingScope
+          scopeType: {type: anonymousFunction}
+  usePrePhraseSnapshot: true
+initialState:
+  documentContents: |-
+    {
+      x = a: b: a + b;
+    }
+  selections:
+    - anchor: {line: 1, character: 9}
+      active: {line: 1, character: 9}
+  marks: {}
+finalState:
+  documentContents: |-
+    {
+      x = a: b: ;
+    }
+  selections:
+    - anchor: {line: 1, character: 12}
+      active: {line: 1, character: 12}
diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeItem.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeItem.yml
new file mode 100644
index 0000000000..7704c4c9a1
--- /dev/null
+++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeItem.yml
@@ -0,0 +1,29 @@
+languageId: nix
+command:
+  version: 6
+  spokenForm: change item
+  action:
+    name: clearAndSetSelection
+    target:
+      type: primitive
+      modifiers:
+        - type: containingScope
+          scopeType: {type: collectionItem}
+  usePrePhraseSnapshot: true
+initialState:
+  documentContents: |
+    {
+      foo = [ "a" a/b/c "b" ];
+    }
+  selections:
+    - anchor: {line: 1, character: 17}
+      active: {line: 1, character: 17}
+  marks: {}
+finalState:
+  documentContents: |
+    {
+      foo = [ "a"  "b" ];
+    }
+  selections:
+    - anchor: {line: 1, character: 14}
+      active: {line: 1, character: 14}
diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeItem2.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeItem2.yml
new file mode 100644
index 0000000000..63833638ab
--- /dev/null
+++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeItem2.yml
@@ -0,0 +1,35 @@
+languageId: nix
+command:
+  version: 6
+  spokenForm: change item
+  action:
+    name: clearAndSetSelection
+    target:
+      type: primitive
+      modifiers:
+        - type: containingScope
+          scopeType: {type: collectionItem}
+  usePrePhraseSnapshot: true
+initialState:
+  documentContents: |
+    {
+      bar = [ A
+              B
+              C # foo
+              ];
+    }
+  selections:
+    - anchor: {line: 2, character: 10}
+      active: {line: 2, character: 10}
+  marks: {}
+finalState:
+  documentContents: |
+    {
+      bar = [ A
+              
+              C # foo
+              ];
+    }
+  selections:
+    - anchor: {line: 2, character: 10}
+      active: {line: 2, character: 10}
diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeKey.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeKey.yml
new file mode 100644
index 0000000000..68d31d581d
--- /dev/null
+++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeKey.yml
@@ -0,0 +1,29 @@
+languageId: nix
+command:
+  version: 6
+  spokenForm: change key
+  action:
+    name: clearAndSetSelection
+    target:
+      type: primitive
+      modifiers:
+        - type: containingScope
+          scopeType: {type: collectionKey}
+  usePrePhraseSnapshot: true
+initialState:
+  documentContents: |
+    {
+      foo = { x = 1; y = 2; };
+    }
+  selections:
+    - anchor: {line: 1, character: 14}
+      active: {line: 1, character: 14}
+  marks: {}
+finalState:
+  documentContents: |
+    {
+      foo = {  = 1; y = 2; };
+    }
+  selections:
+    - anchor: {line: 1, character: 10}
+      active: {line: 1, character: 10}
diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeKeyPlex.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeKeyPlex.yml
new file mode 100644
index 0000000000..2aa27a4ed0
--- /dev/null
+++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeKeyPlex.yml
@@ -0,0 +1,33 @@
+languageId: nix
+command:
+  version: 6
+  spokenForm: change key plex
+  action:
+    name: clearAndSetSelection
+    target:
+      type: primitive
+      mark: {type: decoratedSymbol, symbolColor: default, character: x}
+      modifiers:
+        - type: containingScope
+          scopeType: {type: collectionKey}
+  usePrePhraseSnapshot: true
+initialState:
+  documentContents: |
+    {
+      foo = { x = 1; y = 2; };
+    }
+  selections:
+    - anchor: {line: 1, character: 12}
+      active: {line: 1, character: 12}
+  marks:
+    default.x:
+      start: {line: 1, character: 10}
+      end: {line: 1, character: 11}
+finalState:
+  documentContents: |
+    {
+      foo = {  = 1; y = 2; };
+    }
+  selections:
+    - anchor: {line: 1, character: 10}
+      active: {line: 1, character: 10}
diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeList.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeList.yml
new file mode 100644
index 0000000000..61d901246c
--- /dev/null
+++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeList.yml
@@ -0,0 +1,29 @@
+languageId: nix
+command:
+  version: 6
+  spokenForm: change list
+  action:
+    name: clearAndSetSelection
+    target:
+      type: primitive
+      modifiers:
+        - type: containingScope
+          scopeType: {type: list}
+  usePrePhraseSnapshot: true
+initialState:
+  documentContents: |
+    {
+      foo = [ "a" a/b/c "b" ];
+    }
+  selections:
+    - anchor: {line: 1, character: 23}
+      active: {line: 1, character: 23}
+  marks: {}
+finalState:
+  documentContents: |
+    {
+      foo = ;
+    }
+  selections:
+    - anchor: {line: 1, character: 8}
+      active: {line: 1, character: 8}
diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeListGreenAir.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeListGreenAir.yml
new file mode 100644
index 0000000000..b8e3fa9302
--- /dev/null
+++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeListGreenAir.yml
@@ -0,0 +1,36 @@
+languageId: nix
+command:
+  version: 6
+  spokenForm: change list green air
+  action:
+    name: clearAndSetSelection
+    target:
+      type: primitive
+      mark: {type: decoratedSymbol, symbolColor: green, character: a}
+      modifiers:
+        - type: containingScope
+          scopeType: {type: list}
+  usePrePhraseSnapshot: true
+initialState:
+  documentContents: |
+    {
+      bar = [ A
+              B
+              C # foo
+              ];
+    }
+  selections:
+    - anchor: {line: 0, character: 1}
+      active: {line: 0, character: 1}
+  marks:
+    green.a:
+      start: {line: 1, character: 10}
+      end: {line: 1, character: 11}
+finalState:
+  documentContents: |
+    {
+      bar = ;
+    }
+  selections:
+    - anchor: {line: 1, character: 8}
+      active: {line: 1, character: 8}
diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeMap.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeMap.yml
new file mode 100644
index 0000000000..bec1c27961
--- /dev/null
+++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeMap.yml
@@ -0,0 +1,37 @@
+languageId: nix
+command:
+  version: 6
+  spokenForm: change map
+  action:
+    name: clearAndSetSelection
+    target:
+      type: primitive
+      modifiers:
+        - type: containingScope
+          scopeType: {type: map}
+  usePrePhraseSnapshot: true
+initialState:
+  documentContents: |
+    {
+        a = { a = b; c = d; };
+        a = {
+            a = b;
+            c = d;
+        };
+    }
+  selections:
+    - anchor: {line: 1, character: 12}
+      active: {line: 1, character: 12}
+  marks: {}
+finalState:
+  documentContents: |
+    {
+        a = ;
+        a = {
+            a = b;
+            c = d;
+        };
+    }
+  selections:
+    - anchor: {line: 1, character: 8}
+      active: {line: 1, character: 8}
diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeMap2.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeMap2.yml
new file mode 100644
index 0000000000..1b5f5998ae
--- /dev/null
+++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeMap2.yml
@@ -0,0 +1,34 @@
+languageId: nix
+command:
+  version: 6
+  spokenForm: change map
+  action:
+    name: clearAndSetSelection
+    target:
+      type: primitive
+      modifiers:
+        - type: containingScope
+          scopeType: {type: map}
+  usePrePhraseSnapshot: true
+initialState:
+  documentContents: |
+    {
+        a = { a = b; c = d; };
+        a = {
+            a = b;
+            c = d;
+        };
+    }
+  selections:
+    - anchor: {line: 3, character: 12}
+      active: {line: 3, character: 12}
+  marks: {}
+finalState:
+  documentContents: |
+    {
+        a = { a = b; c = d; };
+        a = ;
+    }
+  selections:
+    - anchor: {line: 2, character: 8}
+      active: {line: 2, character: 8}
diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeMap3.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeMap3.yml
new file mode 100644
index 0000000000..37abcceb61
--- /dev/null
+++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeMap3.yml
@@ -0,0 +1,31 @@
+languageId: nix
+command:
+  version: 6
+  spokenForm: change map
+  action:
+    name: clearAndSetSelection
+    target:
+      type: primitive
+      modifiers:
+        - type: containingScope
+          scopeType: {type: map}
+  usePrePhraseSnapshot: true
+initialState:
+  documentContents: |
+    {
+        a = { a = b; c = d; };
+        a = {
+            a = b;
+            c = d;
+        };
+    }
+  selections:
+    - anchor: {line: 6, character: 0}
+      active: {line: 6, character: 0}
+  marks: {}
+finalState:
+  documentContents: |+
+
+  selections:
+    - anchor: {line: 0, character: 0}
+      active: {line: 0, character: 0}
diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeMap4.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeMap4.yml
new file mode 100644
index 0000000000..5ecc01809e
--- /dev/null
+++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeMap4.yml
@@ -0,0 +1,32 @@
+languageId: nix
+command:
+  version: 6
+  spokenForm: change map
+  action:
+    name: clearAndSetSelection
+    target:
+      type: primitive
+      modifiers:
+        - type: containingScope
+          scopeType: {type: map}
+  usePrePhraseSnapshot: true
+initialState:
+  documentContents: |
+    {
+        a = rec {
+            a = b;
+            b = a + 1;
+        };
+    }
+  selections:
+    - anchor: {line: 2, character: 5}
+      active: {line: 2, character: 5}
+  marks: {}
+finalState:
+  documentContents: |
+    {
+        a = ;
+    }
+  selections:
+    - anchor: {line: 1, character: 8}
+      active: {line: 1, character: 8}
diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeName.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeName.yml
new file mode 100644
index 0000000000..09ce494977
--- /dev/null
+++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeName.yml
@@ -0,0 +1,43 @@
+languageId: nix
+command:
+  version: 6
+  spokenForm: change name
+  action:
+    name: clearAndSetSelection
+    target:
+      type: primitive
+      modifiers:
+        - type: containingScope
+          scopeType: {type: name}
+  usePrePhraseSnapshot: true
+initialState:
+  documentContents: |
+    {
+      foo = let
+        a = b;
+        c = d;
+      in
+        {
+          output = b;
+        };
+
+    }
+  selections:
+    - anchor: {line: 1, character: 11}
+      active: {line: 1, character: 11}
+  marks: {}
+finalState:
+  documentContents: |
+    {
+       = let
+        a = b;
+        c = d;
+      in
+        {
+          output = b;
+        };
+
+    }
+  selections:
+    - anchor: {line: 1, character: 2}
+      active: {line: 1, character: 2}
diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeName2.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeName2.yml
new file mode 100644
index 0000000000..d3180e4574
--- /dev/null
+++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeName2.yml
@@ -0,0 +1,43 @@
+languageId: nix
+command:
+  version: 6
+  spokenForm: change name
+  action:
+    name: clearAndSetSelection
+    target:
+      type: primitive
+      modifiers:
+        - type: containingScope
+          scopeType: {type: name}
+  usePrePhraseSnapshot: true
+initialState:
+  documentContents: |
+    {
+      foo = let
+        a = b;
+        c = d;
+      in
+        {
+          output = b;
+        };
+
+    }
+  selections:
+    - anchor: {line: 2, character: 10}
+      active: {line: 2, character: 10}
+  marks: {}
+finalState:
+  documentContents: |
+    {
+      foo = let
+         = b;
+        c = d;
+      in
+        {
+          output = b;
+        };
+
+    }
+  selections:
+    - anchor: {line: 2, character: 4}
+      active: {line: 2, character: 4}
diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeRound.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeRound.yml
new file mode 100644
index 0000000000..1b91d72438
--- /dev/null
+++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeRound.yml
@@ -0,0 +1,29 @@
+languageId: nix
+command:
+  version: 6
+  spokenForm: change round
+  action:
+    name: clearAndSetSelection
+    target:
+      type: primitive
+      modifiers:
+        - type: containingScope
+          scopeType: {type: surroundingPair, delimiter: parentheses}
+  usePrePhraseSnapshot: true
+initialState:
+  documentContents: |
+    {
+      foo = 1; # Test (round) 
+    }
+  selections:
+    - anchor: {line: 1, character: 23}
+      active: {line: 1, character: 23}
+  marks: {}
+finalState:
+  documentContents: |
+    {
+      foo = 1; # Test  
+    }
+  selections:
+    - anchor: {line: 1, character: 18}
+      active: {line: 1, character: 18}
diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeRound2.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeRound2.yml
new file mode 100644
index 0000000000..dc2fc91835
--- /dev/null
+++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeRound2.yml
@@ -0,0 +1,29 @@
+languageId: nix
+command:
+  version: 6
+  spokenForm: change round
+  action:
+    name: clearAndSetSelection
+    target:
+      type: primitive
+      modifiers:
+        - type: containingScope
+          scopeType: {type: surroundingPair, delimiter: parentheses}
+  usePrePhraseSnapshot: true
+initialState:
+  documentContents: |
+    {
+      b = ''two single (quote) string'';
+    }
+  selections:
+    - anchor: {line: 1, character: 22}
+      active: {line: 1, character: 22}
+  marks: {}
+finalState:
+  documentContents: |
+    {
+      b = ''two single  string'';
+    }
+  selections:
+    - anchor: {line: 1, character: 19}
+      active: {line: 1, character: 19}
diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeStateFine.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeStateFine.yml
new file mode 100644
index 0000000000..83f1f8bd71
--- /dev/null
+++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeStateFine.yml
@@ -0,0 +1,39 @@
+languageId: nix
+command:
+  version: 6
+  spokenForm: change state fine
+  action:
+    name: clearAndSetSelection
+    target:
+      type: primitive
+      mark: {type: decoratedSymbol, symbolColor: default, character: f}
+      modifiers:
+        - type: containingScope
+          scopeType: {type: statement}
+  usePrePhraseSnapshot: true
+initialState:
+  documentContents: |
+    {
+      foo = let
+        a = b;
+        c = d;
+      in
+        {
+          output = b;
+        };
+    }
+  selections:
+    - anchor: {line: 7, character: 6}
+      active: {line: 7, character: 6}
+  marks:
+    default.f:
+      start: {line: 1, character: 2}
+      end: {line: 1, character: 5}
+finalState:
+  documentContents: |
+    {
+      
+    }
+  selections:
+    - anchor: {line: 1, character: 2}
+      active: {line: 1, character: 2}
diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeStateOdd.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeStateOdd.yml
new file mode 100644
index 0000000000..1ea1ea9629
--- /dev/null
+++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeStateOdd.yml
@@ -0,0 +1,45 @@
+languageId: nix
+command:
+  version: 6
+  spokenForm: change state odd
+  action:
+    name: clearAndSetSelection
+    target:
+      type: primitive
+      mark: {type: decoratedSymbol, symbolColor: default, character: o}
+      modifiers:
+        - type: containingScope
+          scopeType: {type: statement}
+  usePrePhraseSnapshot: true
+initialState:
+  documentContents: |
+    {
+      foo = let
+        a = b;
+        c = d;
+      in
+        {
+          output = b;
+        };
+    }
+  selections:
+    - anchor: {line: 3, character: 10}
+      active: {line: 3, character: 10}
+  marks:
+    default.o:
+      start: {line: 6, character: 6}
+      end: {line: 6, character: 12}
+finalState:
+  documentContents: |
+    {
+      foo = let
+        a = b;
+        c = d;
+      in
+        {
+          
+        };
+    }
+  selections:
+    - anchor: {line: 6, character: 6}
+      active: {line: 6, character: 6}
diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeString.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeString.yml
new file mode 100644
index 0000000000..fdd53b2b5e
--- /dev/null
+++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeString.yml
@@ -0,0 +1,29 @@
+languageId: nix
+command:
+  version: 6
+  spokenForm: change string
+  action:
+    name: clearAndSetSelection
+    target:
+      type: primitive
+      modifiers:
+        - type: containingScope
+          scopeType: {type: surroundingPair, delimiter: string}
+  usePrePhraseSnapshot: true
+initialState:
+  documentContents: |
+    {
+      a = "double quoted string";
+    }
+  selections:
+    - anchor: {line: 1, character: 25}
+      active: {line: 1, character: 25}
+  marks: {}
+finalState:
+  documentContents: |
+    {
+      a = ;
+    }
+  selections:
+    - anchor: {line: 1, character: 6}
+      active: {line: 1, character: 6}
diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeString2.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeString2.yml
new file mode 100644
index 0000000000..1946648da9
--- /dev/null
+++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeString2.yml
@@ -0,0 +1,29 @@
+languageId: nix
+command:
+  version: 6
+  spokenForm: change string
+  action:
+    name: clearAndSetSelection
+    target:
+      type: primitive
+      modifiers:
+        - type: containingScope
+          scopeType: {type: surroundingPair, delimiter: string}
+  usePrePhraseSnapshot: true
+initialState:
+  documentContents: |
+    {
+      b = ''two single (quote) string'';
+    }
+  selections:
+    - anchor: {line: 1, character: 16}
+      active: {line: 1, character: 16}
+  marks: {}
+finalState:
+  documentContents: |
+    {
+      b = ;
+    }
+  selections:
+    - anchor: {line: 1, character: 6}
+      active: {line: 1, character: 6}
diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeString3.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeString3.yml
new file mode 100644
index 0000000000..6a8b624629
--- /dev/null
+++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeString3.yml
@@ -0,0 +1,31 @@
+languageId: nix
+command:
+  version: 6
+  spokenForm: change string
+  action:
+    name: clearAndSetSelection
+    target:
+      type: primitive
+      modifiers:
+        - type: containingScope
+          scopeType: {type: surroundingPair, delimiter: string}
+  usePrePhraseSnapshot: true
+initialState:
+  documentContents: |
+    {
+      c = ''
+        multi-line
+        quote string'';
+    }
+  selections:
+    - anchor: {line: 3, character: 4}
+      active: {line: 3, character: 4}
+  marks: {}
+finalState:
+  documentContents: |
+    {
+      c = ;
+    }
+  selections:
+    - anchor: {line: 1, character: 6}
+      active: {line: 1, character: 6}
diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeValue.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeValue.yml
new file mode 100644
index 0000000000..731bb106b9
--- /dev/null
+++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeValue.yml
@@ -0,0 +1,37 @@
+languageId: nix
+command:
+  version: 6
+  spokenForm: change value
+  action:
+    name: clearAndSetSelection
+    target:
+      type: primitive
+      modifiers:
+        - type: containingScope
+          scopeType: {type: value}
+  usePrePhraseSnapshot: true
+initialState:
+  documentContents: |
+    {
+      foo = let
+        a = b;
+        c = d;
+      in
+        {
+          output = b;
+        };
+
+    }
+  selections:
+    - anchor: {line: 1, character: 7}
+      active: {line: 1, character: 7}
+  marks: {}
+finalState:
+  documentContents: |
+    {
+      foo = ;
+
+    }
+  selections:
+    - anchor: {line: 1, character: 8}
+      active: {line: 1, character: 8}
diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeValue2.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeValue2.yml
new file mode 100644
index 0000000000..e9663d11c2
--- /dev/null
+++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeValue2.yml
@@ -0,0 +1,43 @@
+languageId: nix
+command:
+  version: 6
+  spokenForm: change value
+  action:
+    name: clearAndSetSelection
+    target:
+      type: primitive
+      modifiers:
+        - type: containingScope
+          scopeType: {type: value}
+  usePrePhraseSnapshot: true
+initialState:
+  documentContents: |
+    {
+      foo = let
+        a = b;
+        c = d;
+      in
+        {
+          output = b;
+        };
+
+    }
+  selections:
+    - anchor: {line: 2, character: 4}
+      active: {line: 2, character: 4}
+  marks: {}
+finalState:
+  documentContents: |
+    {
+      foo = let
+        a = ;
+        c = d;
+      in
+        {
+          output = b;
+        };
+
+    }
+  selections:
+    - anchor: {line: 2, character: 8}
+      active: {line: 2, character: 8}
diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeValue3.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeValue3.yml
new file mode 100644
index 0000000000..d0bcd459c5
--- /dev/null
+++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeValue3.yml
@@ -0,0 +1,37 @@
+languageId: nix
+command:
+  version: 6
+  spokenForm: change value
+  action:
+    name: clearAndSetSelection
+    target:
+      type: primitive
+      modifiers:
+        - type: containingScope
+          scopeType: {type: value}
+  usePrePhraseSnapshot: true
+initialState:
+  documentContents: |
+    {
+      foo = let
+        a = b;
+        c = d;
+      in
+        {
+          output = b;
+        };
+
+    }
+  selections:
+    - anchor: {line: 5, character: 5}
+      active: {line: 5, character: 5}
+  marks: {}
+finalState:
+  documentContents: |
+    {
+      foo = ;
+
+    }
+  selections:
+    - anchor: {line: 1, character: 8}
+      active: {line: 1, character: 8}
diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeValue4.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeValue4.yml
new file mode 100644
index 0000000000..96381cc208
--- /dev/null
+++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeValue4.yml
@@ -0,0 +1,43 @@
+languageId: nix
+command:
+  version: 6
+  spokenForm: change value
+  action:
+    name: clearAndSetSelection
+    target:
+      type: primitive
+      modifiers:
+        - type: containingScope
+          scopeType: {type: value}
+  usePrePhraseSnapshot: true
+initialState:
+  documentContents: |
+    {
+      foo = let
+        a = b;
+        c = d;
+      in
+        {
+          output = b;
+        };
+
+    }
+  selections:
+    - anchor: {line: 6, character: 13}
+      active: {line: 6, character: 13}
+  marks: {}
+finalState:
+  documentContents: |
+    {
+      foo = let
+        a = b;
+        c = d;
+      in
+        {
+          output = ;
+        };
+
+    }
+  selections:
+    - anchor: {line: 6, character: 15}
+      active: {line: 6, character: 15}
diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/chuckArg.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/chuckArg.yml
new file mode 100644
index 0000000000..8634cb326b
--- /dev/null
+++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/chuckArg.yml
@@ -0,0 +1,29 @@
+languageId: nix
+command:
+  version: 6
+  spokenForm: chuck arg
+  action:
+    name: remove
+    target:
+      type: primitive
+      modifiers:
+        - type: containingScope
+          scopeType: {type: argumentOrParameter}
+  usePrePhraseSnapshot: true
+initialState:
+  documentContents: |
+    {
+      d = "Interpolated ${value1} string ${value2}";
+    }
+  selections:
+    - anchor: {line: 1, character: 26}
+      active: {line: 1, character: 26}
+  marks: {}
+finalState:
+  documentContents: |
+    {
+      d = "Interpolated string ${value2}";
+    }
+  selections:
+    - anchor: {line: 1, character: 20}
+      active: {line: 1, character: 20}
diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/chuckKey.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/chuckKey.yml
new file mode 100644
index 0000000000..28fbc93192
--- /dev/null
+++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/chuckKey.yml
@@ -0,0 +1,29 @@
+languageId: nix
+command:
+  version: 6
+  spokenForm: chuck key
+  action:
+    name: remove
+    target:
+      type: primitive
+      modifiers:
+        - type: containingScope
+          scopeType: {type: collectionKey}
+  usePrePhraseSnapshot: true
+initialState:
+  documentContents: |
+    {
+      d = "Interpolated ${value1} string ${value2}";
+    }
+  selections:
+    - anchor: {line: 1, character: 6}
+      active: {line: 1, character: 6}
+  marks: {}
+finalState:
+  documentContents: |
+    {
+      "Interpolated ${value1} string ${value2}";
+    }
+  selections:
+    - anchor: {line: 1, character: 2}
+      active: {line: 1, character: 2}
diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/chuckValue.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/chuckValue.yml
new file mode 100644
index 0000000000..ee4902d806
--- /dev/null
+++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/chuckValue.yml
@@ -0,0 +1,37 @@
+languageId: nix
+command:
+  version: 6
+  spokenForm: chuck value
+  action:
+    name: remove
+    target:
+      type: primitive
+      modifiers:
+        - type: containingScope
+          scopeType: {type: value}
+  usePrePhraseSnapshot: true
+initialState:
+  documentContents: |
+    {
+      foo = let
+        a = b;
+        c = d;
+      in
+        {
+          output = b;
+        };
+
+    }
+  selections:
+    - anchor: {line: 1, character: 7}
+      active: {line: 1, character: 7}
+  marks: {}
+finalState:
+  documentContents: |
+    {
+      foo;
+
+    }
+  selections:
+    - anchor: {line: 1, character: 5}
+      active: {line: 1, character: 5}
diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/chuckValue2.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/chuckValue2.yml
new file mode 100644
index 0000000000..03c0e75286
--- /dev/null
+++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/chuckValue2.yml
@@ -0,0 +1,43 @@
+languageId: nix
+command:
+  version: 6
+  spokenForm: chuck value
+  action:
+    name: remove
+    target:
+      type: primitive
+      modifiers:
+        - type: containingScope
+          scopeType: {type: value}
+  usePrePhraseSnapshot: true
+initialState:
+  documentContents: |
+    {
+      foo = let
+        a = b;
+        c = d;
+      in
+        {
+          output = b;
+        };
+
+    }
+  selections:
+    - anchor: {line: 2, character: 4}
+      active: {line: 2, character: 4}
+  marks: {}
+finalState:
+  documentContents: |
+    {
+      foo = let
+        a;
+        c = d;
+      in
+        {
+          output = b;
+        };
+
+    }
+  selections:
+    - anchor: {line: 2, character: 4}
+      active: {line: 2, character: 4}
diff --git a/queries/nix.scm b/queries/nix.scm
new file mode 100644
index 0000000000..17d362a8f3
--- /dev/null
+++ b/queries/nix.scm
@@ -0,0 +1,187 @@
+;; Statements
+
+;; Any top-level expression that isn't a comment is a statement
+(
+    (source_code
+        (_) @statement
+    ) @_.iteration
+    (#not-type? @statement comment)
+)
+(source_code) @comment.iteration
+
+;; Any foo = <expression> anywhere is a statement
+[
+    (inherit)
+    (binding)
+] @statement
+(binding_set) @map.iteration @list.iteration @ifStatement.iteration
+
+;; This matches assert statements as stand-alone statements. This
+;; is because it's common to have a series of assert statements in a file, which
+;; would otherwise all match as a single statement. See the playground/nix/asserts.nix
+;; file for an example.
+(
+    (assert_expression
+        "assert" @statement.start
+        condition: (_)
+        ";" @statement.end
+        body: (_)
+    ) @_.domain
+    (#not-parent-type? @_.domain binding)
+)
+(assert_expression
+    "assert" @functionCallee @functionCall.start
+    condition: (_) @condition
+    body: (_) @branch @functionCall.end.startOf
+) @_.domain
+
+(
+    (with_expression) @statement
+    (#not-parent-type? @statement binding)
+)
+
+;;!! let
+;;!!   a = 1;
+;;!   <******
+;;!!   b = 2;
+;;!    *****>
+;;!! in a + b
+(let_expression
+    "let"
+    (_) @statement.iteration @value.iteration @name.iteration
+)
+
+;; Conditionals
+
+;;!! key = if a then b else c;
+;;!        ^^^^^^^^^^^^^^^^^^
+(if_expression) @ifStatement @branch.iteration @condition.iteration
+;;!! key = if a then b else c;
+;;!        ^^^^^^^^^^^
+;;!        xxxxxxxxxxxx
+(if_expression
+    "if" @branch.start
+    condition: (_)
+    "then"
+    consequence: (_) @branch.end
+)
+;;!! key = if a then b else c;
+;;!                    ^^^^^^
+;;!                   xxxxxxx
+(if_expression
+    "else" @branch.start
+    alternative: (_) @branch.end
+)
+
+;;!! key = if a > 10 then b else c;
+;;!           ^^^^^^
+(if_expression
+    condition: (_) @condition
+) @_.domain
+
+;; Lists and maps
+
+;;!! foo = [ a b c ];
+;;!        ^^^^^^^^^
+(list_expression
+    element: (_) @collectionItem
+) @list
+
+;;!! foo = { x = 1; y = 2; };
+;;!        ^^^^^^^^^^^^^^^^^
+[
+    (attrset_expression)
+    (rec_attrset_expression)
+] @map @statement.iteration @value.iteration @name.iteration
+
+;;!! foo = { x = 1; y = 2; };
+;;!          ^
+;;!          xxxx
+;;!          -----
+(binding
+    attrpath: (_) @collectionKey @_.trailing.start.endOf
+    expression: (_) @_.trailing.end.startOf
+) @_.domain
+
+;; Strings
+
+;;!! # foo
+;;!  ^^^^^
+(comment) @comment @textFragment
+
+[
+    (string_expression)
+    (indented_string_expression)
+] @string
+(string_fragment) @textFragment
+
+;;!! d = "foo ${bar} baz"
+;;!           ^^^^^^
+;;!      <**************>
+(string_expression
+    (interpolation) @argumentOrParameter
+) @_.iteration
+
+;; Functions
+
+;; Note for this part of the function, we identify is as lambda only
+;;!! x = a: b: a + b;
+;;!         ^^^^^^^^ Func1 due to currying
+;;!      ^^^^^^^^^^^ Func2 due to currying
+(function_expression
+    [
+        (
+            ;; Match foo@{ a, b }: style
+            (identifier) @argumentOrParameter.start
+            "@"
+            (formals) @argumentOrParameter.end
+        )
+        ;; Match a: style arg
+        (identifier) @argumentOrParameter
+
+        ;; Match { a, b }: style
+        (formals) @argumentOrParameter
+    ]
+    .
+    body: (_) @anonymousFunction.interior
+) @_.domain @anonymousFunction @argumentOrParameter.iteration
+
+;; We define the named function as the full assignment of the lambda
+;; This means funk name becomes the variable holding the lambda
+(binding
+    (_) @functionName
+    "="
+    expression: (function_expression) @namedFunction.interior
+) @namedFunction @functionName.domain
+
+(apply_expression
+    function: (_) @functionCallee
+    argument: (_) @argumentOrParameter
+) @_.domain
+
+(apply_expression) @functionCall
+(function_expression
+    (formals) @argumentOrParameter
+) @_.domain
+
+;; Names and Values
+
+;;!! a = 25;
+;;!      ^^
+;;!   xxxxx
+;;!  -------
+(binding
+    (_) @_.leading.start.endOf
+    .
+    expression: (_) @value @_.leading.end.startOf
+) @_.domain
+
+;;!! a = 25;
+;;!  ^
+;;!  xxxx
+;;!  -------
+(binding
+    (_) @name @_.leading.end.startOf @_.trailing.start.endOf
+    .
+    expression: (_) @_.trailing.end.startOf
+) @_.domain

From 16f9ff3e3dff5a6c96911df3e7025bbe43273e2b Mon Sep 17 00:00:00 2001
From: fidgetingbits <fidgetingbits@memeoid.cx>
Date: Fri, 3 Nov 2023 09:56:23 +0800
Subject: [PATCH 02/16] Fix callee and args, and add inherit keyword specialc
 ase

---
 queries/nix.scm | 118 +++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 116 insertions(+), 2 deletions(-)

diff --git a/queries/nix.scm b/queries/nix.scm
index 17d362a8f3..671a3f8557 100644
--- a/queries/nix.scm
+++ b/queries/nix.scm
@@ -1,4 +1,6 @@
+;;
 ;; Statements
+;;
 
 ;; Any top-level expression that isn't a comment is a statement
 (
@@ -51,7 +53,9 @@
     (_) @statement.iteration @value.iteration @name.iteration
 )
 
+;;
 ;; Conditionals
+;;
 
 ;;!! key = if a then b else c;
 ;;!        ^^^^^^^^^^^^^^^^^^
@@ -79,7 +83,9 @@
     condition: (_) @condition
 ) @_.domain
 
+;;
 ;; Lists and maps
+;;
 
 ;;!! foo = [ a b c ];
 ;;!        ^^^^^^^^^
@@ -103,7 +109,9 @@
     expression: (_) @_.trailing.end.startOf
 ) @_.domain
 
+;;
 ;; Strings
+;;
 
 ;;!! # foo
 ;;!  ^^^^^
@@ -122,7 +130,9 @@
     (interpolation) @argumentOrParameter
 ) @_.iteration
 
+;;
 ;; Functions
+;;
 
 ;; Note for this part of the function, we identify is as lambda only
 ;;!! x = a: b: a + b;
@@ -154,17 +164,121 @@
     expression: (function_expression) @namedFunction.interior
 ) @namedFunction @functionName.domain
 
+;; Calls to functions are a bit finicky, because of everything being curried lambdas
+(
+    (apply_expression) @dummy @functionCall @argumentOrParameter.iteration
+    (#not-parent-type? @functionCall apply_expression)
+)
+
+;; This is gross, but not sure how to do fuzzy matching against an unknown number of
+;; nested child nodes. Arbitrarily stopping at 5 args for now, as that ought to be enough
+;; arguments for anyone
+;; Args:
+;;!! mkHost a b c d e
+;;!           ^
+;;!           xx
+;;!        <*********>
+;;! Callee:
+;;!! mkHost a b c d e
+;;!  ^^^^^^
+;;!  xxxxxxx
+;;!  ----------------
+;; The mkHost node looks like this:
+;; #   (binding_set
+;; #    binding: (binding
+;; #     expression: (attrset_expression
+;; #      (binding_set
+;; #       binding: (binding
+;; #        expression: (apply_expression
+;; #         function: (apply_expression
+;; #          function: (apply_expression
+;; #           function: (variable_expression
+;; #            name: (identifier)
+;; #           )
+(apply_expression
+    [
+        (apply_expression
+            function: (variable_expression
+                name: (identifier) @functionCallee
+            )
+        )
+        (apply_expression
+            [
+                (apply_expression
+                    function: (variable_expression
+                        name: (identifier) @functionCallee
+                    )
+                )
+                (apply_expression
+                    [
+                        (apply_expression
+                            function: (variable_expression
+                                name: (identifier) @functionCallee
+                            )
+                        )
+                        (apply_expression
+                            (apply_expression
+                                function: (variable_expression
+                                    name: (identifier) @functionCallee
+                                )
+                            )
+                        )
+                    ]
+                )
+            ]
+        )
+    ]
+) @_.domain
+
+;; Args:
+;;!! mkHost a
+;;!         ^
+;;!         x
+;;!  --------
+;;! Callee:
+;;!! mkHost a
+;;!  ^^^^^^
+;;!  xxxxxx
+;;!  --------
 (apply_expression
-    function: (_) @functionCallee
+    function: (variable_expression
+        name: (identifier) @functionCallee
+    )
     argument: (_) @argumentOrParameter
 ) @_.domain
 
-(apply_expression) @functionCall
+(apply_expression
+    argument: (_) @argumentOrParameter
+)
+
 (function_expression
     (formals) @argumentOrParameter
 ) @_.domain
 
+;; inherit is a built-in keyword, but is close enough to a function...
+;; Callee:
+;;!! inherit pkgs input output;
+;;!  ^^^^^^^
+;;!  xxxxxxxx
+;;!  -------------------------
+;; Args:
+;;!! inherit pkgs input output;
+;;!          ^^^^
+;;!          xxxxx
+;;!         <*****************>
+(inherit
+    "inherit" @functionCallee
+    (
+        (inherited_attrs) @argumentOrParameter.iteration
+    )
+) @functionCall @_.domain
+(inherited_attrs
+    attr: (_) @argumentOrParameter
+)
+
+;;
 ;; Names and Values
+;;
 
 ;;!! a = 25;
 ;;!      ^^

From 85e0775f6c59f966e04ff013c4aa721dd4b36b22 Mon Sep 17 00:00:00 2001
From: fidgetingbits <fidgetingbits@memeoid.cx>
Date: Fri, 3 Nov 2023 10:15:47 +0800
Subject: [PATCH 03/16] Fix branch test now that we match ts behavior

---
 .../{changeBranch4.yml => changeBranch2.yml}  | 21 ++++++++-----------
 1 file changed, 9 insertions(+), 12 deletions(-)
 rename packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/{changeBranch4.yml => changeBranch2.yml} (60%)

diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeBranch4.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeBranch2.yml
similarity index 60%
rename from packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeBranch4.yml
rename to packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeBranch2.yml
index b6a4b19d1a..d3105012d4 100644
--- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeBranch4.yml
+++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeBranch2.yml
@@ -13,23 +13,20 @@ command:
 initialState:
   documentContents: |
     {
-      key =
-        if a
-        then b
-        else c;
+      foo = if a 
+      then b
+      else c;
     }
   selections:
-    - anchor: {line: 3, character: 6}
-      active: {line: 3, character: 6}
+    - anchor: {line: 1, character: 9}
+      active: {line: 1, character: 9}
   marks: {}
 finalState:
   documentContents: |
     {
-      key =
-        if a
-        
-        else c;
+      foo = 
+      else c;
     }
   selections:
-    - anchor: {line: 3, character: 4}
-      active: {line: 3, character: 4}
+    - anchor: {line: 1, character: 8}
+      active: {line: 1, character: 8}

From 7c5e12a65689383286245ff83f020d9da613489c Mon Sep 17 00:00:00 2001
From: fidgetingbits <fidgetingbits@memeoid.cx>
Date: Fri, 3 Nov 2023 10:23:46 +0800
Subject: [PATCH 04/16] Add inside support for maps and lists

---
 .../languages/nix/changeInsideList.yml        | 33 +++++++++++++++++++
 .../recorded/languages/nix/chuckInsideMap.yml | 32 ++++++++++++++++++
 queries/nix.scm                               | 17 ++++++++--
 3 files changed, 79 insertions(+), 3 deletions(-)
 create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeInsideList.yml
 create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/chuckInsideMap.yml

diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeInsideList.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeInsideList.yml
new file mode 100644
index 0000000000..33a8c0b2ae
--- /dev/null
+++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeInsideList.yml
@@ -0,0 +1,33 @@
+languageId: nix
+command:
+  version: 6
+  spokenForm: change inside list
+  action:
+    name: clearAndSetSelection
+    target:
+      type: primitive
+      modifiers:
+        - {type: interiorOnly}
+        - type: containingScope
+          scopeType: {type: list}
+  usePrePhraseSnapshot: true
+initialState:
+  documentContents: |
+    {
+      foo = [ 1 2 3 ];
+    }
+  selections:
+    - anchor: {line: 1, character: 11}
+      active: {line: 1, character: 11}
+  marks: {}
+finalState:
+  documentContents: |
+    {
+      foo = [  ];
+    }
+  selections:
+    - anchor: {line: 1, character: 10}
+      active: {line: 1, character: 10}
+ide:
+  messages:
+    - {type: error, id: TreeSitterQuery.checkCaptures.duplicate}
diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/chuckInsideMap.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/chuckInsideMap.yml
new file mode 100644
index 0000000000..09a4a8a806
--- /dev/null
+++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/chuckInsideMap.yml
@@ -0,0 +1,32 @@
+languageId: nix
+command:
+  version: 6
+  spokenForm: chuck inside map
+  action:
+    name: remove
+    target:
+      type: primitive
+      modifiers:
+        - {type: interiorOnly}
+        - type: containingScope
+          scopeType: {type: map}
+  usePrePhraseSnapshot: true
+initialState:
+  documentContents: |
+    {
+      foo = if a 
+      then b
+      else c;
+    }
+  selections:
+    - anchor: {line: 3, character: 9}
+      active: {line: 3, character: 9}
+  marks: {}
+finalState:
+  documentContents: |
+    {
+      
+    }
+  selections:
+    - anchor: {line: 1, character: 2}
+      active: {line: 1, character: 2}
diff --git a/queries/nix.scm b/queries/nix.scm
index 671a3f8557..bfe29d1dc0 100644
--- a/queries/nix.scm
+++ b/queries/nix.scm
@@ -88,16 +88,27 @@
 ;;
 
 ;;!! foo = [ a b c ];
-;;!        ^^^^^^^^^
+;;!          ^
+;;!          xx
 (list_expression
     element: (_) @collectionItem
+)
+
+;;!! foo = [ a b c ];
+;;!        ^^^^^^^^^
+(list_expression
+    element: (_)+ @list.interior
 ) @list
 
 ;;!! foo = { x = 1; y = 2; };
 ;;!        ^^^^^^^^^^^^^^^^^
 [
-    (attrset_expression)
-    (rec_attrset_expression)
+    (attrset_expression
+        (_) @map.interior
+    )
+    (rec_attrset_expression
+        (_) @map.interior
+    )
 ] @map @statement.iteration @value.iteration @name.iteration
 
 ;;!! foo = { x = 1; y = 2; };

From e3a148039d6d0317112c27d8174ed1ac47fa3fab Mon Sep 17 00:00:00 2001
From: fidgetingbits <fidgetingbits@memeoid.cx>
Date: Fri, 3 Nov 2023 19:06:08 +0800
Subject: [PATCH 05/16] Add branch interior

---
 queries/nix.scm | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/queries/nix.scm b/queries/nix.scm
index bfe29d1dc0..251c21d3d6 100644
--- a/queries/nix.scm
+++ b/queries/nix.scm
@@ -67,14 +67,14 @@
     "if" @branch.start
     condition: (_)
     "then"
-    consequence: (_) @branch.end
+    consequence: (_) @branch.end @branch.interior
 )
 ;;!! key = if a then b else c;
 ;;!                    ^^^^^^
 ;;!                   xxxxxxx
 (if_expression
     "else" @branch.start
-    alternative: (_) @branch.end
+    alternative: (_) @branch.end @branch.interior
 )
 
 ;;!! key = if a > 10 then b else c;

From 7472aabffcdb3bd57b22b47e9f3419a5e28d2ae7 Mon Sep 17 00:00:00 2001
From: fidgetingbits <fidgetingbits@memeoid.cx>
Date: Mon, 13 Nov 2023 09:37:22 +0800
Subject: [PATCH 06/16] Disable statements most places, add item for maps, fix
 list error

---
 .../languages/nix/changeEveryState.yml        | 43 ------------------
 .../languages/nix/changeInsideList.yml        | 21 +++------
 .../languages/nix/changeStateFine.yml         | 39 ----------------
 .../recorded/languages/nix/changeStateOdd.yml | 45 -------------------
 queries/nix.scm                               | 39 +++++++++++-----
 5 files changed, 34 insertions(+), 153 deletions(-)
 delete mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeEveryState.yml
 delete mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeStateFine.yml
 delete mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeStateOdd.yml

diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeEveryState.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeEveryState.yml
deleted file mode 100644
index 7dff79a3a2..0000000000
--- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeEveryState.yml
+++ /dev/null
@@ -1,43 +0,0 @@
-languageId: nix
-command:
-  version: 6
-  spokenForm: change every state
-  action:
-    name: clearAndSetSelection
-    target:
-      type: primitive
-      modifiers:
-        - type: everyScope
-          scopeType: {type: statement}
-  usePrePhraseSnapshot: true
-initialState:
-  documentContents: |
-    {
-      foo = let
-        a = b;
-        c = d;
-      in
-        {
-          output = b;
-        };
-    }
-  selections:
-    - anchor: {line: 2, character: 10}
-      active: {line: 2, character: 10}
-  marks: {}
-finalState:
-  documentContents: |
-    {
-      foo = let
-        
-        
-      in
-        {
-          output = b;
-        };
-    }
-  selections:
-    - anchor: {line: 2, character: 4}
-      active: {line: 2, character: 4}
-    - anchor: {line: 3, character: 4}
-      active: {line: 3, character: 4}
diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeInsideList.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeInsideList.yml
index 33a8c0b2ae..b2f60ffa97 100644
--- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeInsideList.yml
+++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeInsideList.yml
@@ -12,22 +12,13 @@ command:
           scopeType: {type: list}
   usePrePhraseSnapshot: true
 initialState:
-  documentContents: |
-    {
-      foo = [ 1 2 3 ];
-    }
+  documentContents: "{ d = [ \"1\" 2 ]; }"
   selections:
-    - anchor: {line: 1, character: 11}
-      active: {line: 1, character: 11}
+    - anchor: {line: 0, character: 10}
+      active: {line: 0, character: 10}
   marks: {}
 finalState:
-  documentContents: |
-    {
-      foo = [  ];
-    }
+  documentContents: "{ d = [  ]; }"
   selections:
-    - anchor: {line: 1, character: 10}
-      active: {line: 1, character: 10}
-ide:
-  messages:
-    - {type: error, id: TreeSitterQuery.checkCaptures.duplicate}
+    - anchor: {line: 0, character: 8}
+      active: {line: 0, character: 8}
diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeStateFine.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeStateFine.yml
deleted file mode 100644
index 83f1f8bd71..0000000000
--- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeStateFine.yml
+++ /dev/null
@@ -1,39 +0,0 @@
-languageId: nix
-command:
-  version: 6
-  spokenForm: change state fine
-  action:
-    name: clearAndSetSelection
-    target:
-      type: primitive
-      mark: {type: decoratedSymbol, symbolColor: default, character: f}
-      modifiers:
-        - type: containingScope
-          scopeType: {type: statement}
-  usePrePhraseSnapshot: true
-initialState:
-  documentContents: |
-    {
-      foo = let
-        a = b;
-        c = d;
-      in
-        {
-          output = b;
-        };
-    }
-  selections:
-    - anchor: {line: 7, character: 6}
-      active: {line: 7, character: 6}
-  marks:
-    default.f:
-      start: {line: 1, character: 2}
-      end: {line: 1, character: 5}
-finalState:
-  documentContents: |
-    {
-      
-    }
-  selections:
-    - anchor: {line: 1, character: 2}
-      active: {line: 1, character: 2}
diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeStateOdd.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeStateOdd.yml
deleted file mode 100644
index 1ea1ea9629..0000000000
--- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeStateOdd.yml
+++ /dev/null
@@ -1,45 +0,0 @@
-languageId: nix
-command:
-  version: 6
-  spokenForm: change state odd
-  action:
-    name: clearAndSetSelection
-    target:
-      type: primitive
-      mark: {type: decoratedSymbol, symbolColor: default, character: o}
-      modifiers:
-        - type: containingScope
-          scopeType: {type: statement}
-  usePrePhraseSnapshot: true
-initialState:
-  documentContents: |
-    {
-      foo = let
-        a = b;
-        c = d;
-      in
-        {
-          output = b;
-        };
-    }
-  selections:
-    - anchor: {line: 3, character: 10}
-      active: {line: 3, character: 10}
-  marks:
-    default.o:
-      start: {line: 6, character: 6}
-      end: {line: 6, character: 12}
-finalState:
-  documentContents: |
-    {
-      foo = let
-        a = b;
-        c = d;
-      in
-        {
-          
-        };
-    }
-  selections:
-    - anchor: {line: 6, character: 6}
-      active: {line: 6, character: 6}
diff --git a/queries/nix.scm b/queries/nix.scm
index 251c21d3d6..0a4f032b5a 100644
--- a/queries/nix.scm
+++ b/queries/nix.scm
@@ -3,19 +3,19 @@
 ;;
 
 ;; Any top-level expression that isn't a comment is a statement
-(
-    (source_code
-        (_) @statement
-    ) @_.iteration
-    (#not-type? @statement comment)
-)
+;; (
+;;     (source_code
+;;         (_) @statement
+;;     ) @_.iteration
+;;     (#not-type? @statement comment)
+;; )
 (source_code) @comment.iteration
 
 ;; Any foo = <expression> anywhere is a statement
-[
-    (inherit)
-    (binding)
-] @statement
+;; [
+;;     (inherit)
+;;     (binding)
+;; ] @statement
 (binding_set) @map.iteration @list.iteration @ifStatement.iteration
 
 ;; This matches assert statements as stand-alone statements. This
@@ -97,7 +97,8 @@
 ;;!! foo = [ a b c ];
 ;;!        ^^^^^^^^^
 (list_expression
-    element: (_)+ @list.interior
+    "[" @list.interior.start.endOf
+    "]" @list.interior.end.startOf
 ) @list
 
 ;;!! foo = { x = 1; y = 2; };
@@ -111,6 +112,22 @@
     )
 ] @map @statement.iteration @value.iteration @name.iteration
 
+;;!! foo = { x = 1; };
+;;!          ^^^^^^
+(attrset_expression
+    (binding_set
+        binding: (_) @collectionItem
+    )
+) @_.iteration
+
+;;!! foo = rec { x = 1; };
+;;!              ^^^^^^
+(rec_attrset_expression
+    (binding_set
+        binding: (_) @collectionItem
+    )
+) @_.iteration
+
 ;;!! foo = { x = 1; y = 2; };
 ;;!          ^
 ;;!          xxxx

From 7199519f709185e455bc3ae5369d45280f702a77 Mon Sep 17 00:00:00 2001
From: fidgetingbits <fidgetingbits@memeoid.cx>
Date: Tue, 14 Nov 2023 09:41:55 +0800
Subject: [PATCH 07/16] editorconfing lint and a bit of removals

---
 queries/nix.scm | 233 +++++++++++++++++++++---------------------------
 1 file changed, 100 insertions(+), 133 deletions(-)

diff --git a/queries/nix.scm b/queries/nix.scm
index 0a4f032b5a..3482be6be1 100644
--- a/queries/nix.scm
+++ b/queries/nix.scm
@@ -23,23 +23,22 @@
 ;; would otherwise all match as a single statement. See the playground/nix/asserts.nix
 ;; file for an example.
 (
-    (assert_expression
-        "assert" @statement.start
-        condition: (_)
-        ";" @statement.end
-        body: (_)
-    ) @_.domain
-    (#not-parent-type? @_.domain binding)
+  (assert_expression
+    "assert" @statement.start
+    condition: (_)
+    ";" @statement.end
+    body: (_)
+  ) @_.domain
+  (#not-parent-type? @_.domain binding)
 )
 (assert_expression
-    "assert" @functionCallee @functionCall.start
-    condition: (_) @condition
-    body: (_) @branch @functionCall.end.startOf
+  condition: (_) @condition
+  body: (_) @branch
 ) @_.domain
 
 (
-    (with_expression) @statement
-    (#not-parent-type? @statement binding)
+  (with_expression) @statement
+  (#not-parent-type? @statement binding)
 )
 
 ;;!! let
@@ -49,8 +48,9 @@
 ;;!    *****>
 ;;!! in a + b
 (let_expression
-    "let"
-    (_) @statement.iteration @value.iteration @name.iteration
+  "let"
+  .
+  (_) @statement.iteration @value.iteration @name.iteration
 )
 
 ;;
@@ -64,23 +64,23 @@
 ;;!        ^^^^^^^^^^^
 ;;!        xxxxxxxxxxxx
 (if_expression
-    "if" @branch.start
-    condition: (_)
-    "then"
-    consequence: (_) @branch.end @branch.interior
+  "if" @branch.start
+  condition: (_)
+  "then"
+  consequence: (_) @branch.end @branch.interior
 )
 ;;!! key = if a then b else c;
 ;;!                    ^^^^^^
 ;;!                   xxxxxxx
 (if_expression
-    "else" @branch.start
-    alternative: (_) @branch.end @branch.interior
+  "else" @branch.start
+  alternative: (_) @branch.end @branch.interior
 )
 
 ;;!! key = if a > 10 then b else c;
 ;;!           ^^^^^^
 (if_expression
-    condition: (_) @condition
+  condition: (_) @condition
 ) @_.domain
 
 ;;
@@ -91,41 +91,41 @@
 ;;!          ^
 ;;!          xx
 (list_expression
-    element: (_) @collectionItem
+  element: (_) @collectionItem
 )
 
 ;;!! foo = [ a b c ];
 ;;!        ^^^^^^^^^
 (list_expression
-    "[" @list.interior.start.endOf
-    "]" @list.interior.end.startOf
+  "[" @list.interior.start.endOf
+  "]" @list.interior.end.startOf
 ) @list
 
 ;;!! foo = { x = 1; y = 2; };
 ;;!        ^^^^^^^^^^^^^^^^^
 [
-    (attrset_expression
-        (_) @map.interior
-    )
-    (rec_attrset_expression
-        (_) @map.interior
-    )
+  (attrset_expression
+    (_) @map.interior
+  )
+  (rec_attrset_expression
+    (_) @map.interior
+  )
 ] @map @statement.iteration @value.iteration @name.iteration
 
 ;;!! foo = { x = 1; };
 ;;!          ^^^^^^
 (attrset_expression
-    (binding_set
-        binding: (_) @collectionItem
-    )
+  (binding_set
+    binding: (_) @collectionItem
+  )
 ) @_.iteration
 
 ;;!! foo = rec { x = 1; };
 ;;!              ^^^^^^
 (rec_attrset_expression
-    (binding_set
-        binding: (_) @collectionItem
-    )
+  (binding_set
+    binding: (_) @collectionItem
+  )
 ) @_.iteration
 
 ;;!! foo = { x = 1; y = 2; };
@@ -133,8 +133,8 @@
 ;;!          xxxx
 ;;!          -----
 (binding
-    attrpath: (_) @collectionKey @_.trailing.start.endOf
-    expression: (_) @_.trailing.end.startOf
+  attrpath: (_) @collectionKey @_.trailing.start.endOf
+  expression: (_) @_.trailing.end.startOf
 ) @_.domain
 
 ;;
@@ -146,8 +146,8 @@
 (comment) @comment @textFragment
 
 [
-    (string_expression)
-    (indented_string_expression)
+  (string_expression)
+  (indented_string_expression)
 ] @string
 (string_fragment) @textFragment
 
@@ -155,7 +155,7 @@
 ;;!           ^^^^^^
 ;;!      <**************>
 (string_expression
-    (interpolation) @argumentOrParameter
+  (interpolation) @argumentOrParameter
 ) @_.iteration
 
 ;;
@@ -167,35 +167,35 @@
 ;;!         ^^^^^^^^ Func1 due to currying
 ;;!      ^^^^^^^^^^^ Func2 due to currying
 (function_expression
-    [
-        (
-            ;; Match foo@{ a, b }: style
-            (identifier) @argumentOrParameter.start
-            "@"
-            (formals) @argumentOrParameter.end
-        )
-        ;; Match a: style arg
-        (identifier) @argumentOrParameter
+  [
+    (
+      ;; Match foo@{ a, b }: style
+      (identifier) @argumentOrParameter.start
+      "@"
+      (formals) @argumentOrParameter.end
+    )
+    ;; Match a: style arg
+    (identifier) @argumentOrParameter
 
-        ;; Match { a, b }: style
-        (formals) @argumentOrParameter
-    ]
-    .
-    body: (_) @anonymousFunction.interior
-) @_.domain @anonymousFunction @argumentOrParameter.iteration
+    ;; Match { a, b }: style
+    (formals) @argumentOrParameter
+  ]
+  .
+  body: (_) @anonymousFunction.interior
+) @anonymousFunction @argumentOrParameter.iteration
 
 ;; We define the named function as the full assignment of the lambda
 ;; This means funk name becomes the variable holding the lambda
 (binding
-    (_) @functionName
-    "="
-    expression: (function_expression) @namedFunction.interior
+  (_) @functionName
+  "="
+  expression: (function_expression) @namedFunction.interior
 ) @namedFunction @functionName.domain
 
 ;; Calls to functions are a bit finicky, because of everything being curried lambdas
 (
-    (apply_expression) @dummy @functionCall @argumentOrParameter.iteration
-    (#not-parent-type? @functionCall apply_expression)
+  (apply_expression) @dummy @functionCall @argumentOrParameter.iteration
+  (#not-parent-type? @functionCall apply_expression)
 )
 
 ;; This is gross, but not sure how to do fuzzy matching against an unknown number of
@@ -211,51 +211,39 @@
 ;;!  ^^^^^^
 ;;!  xxxxxxx
 ;;!  ----------------
-;; The mkHost node looks like this:
-;; #   (binding_set
-;; #    binding: (binding
-;; #     expression: (attrset_expression
-;; #      (binding_set
-;; #       binding: (binding
-;; #        expression: (apply_expression
-;; #         function: (apply_expression
-;; #          function: (apply_expression
-;; #           function: (variable_expression
-;; #            name: (identifier)
-;; #           )
 (apply_expression
-    [
+  [
+    (apply_expression
+      function: (variable_expression
+        name: (identifier) @functionCallee
+      )
+    )
+    (apply_expression
+      [
         (apply_expression
-            function: (variable_expression
-                name: (identifier) @functionCallee
-            )
+          function: (variable_expression
+            name: (identifier) @functionCallee
+          )
         )
         (apply_expression
-            [
-                (apply_expression
-                    function: (variable_expression
-                        name: (identifier) @functionCallee
-                    )
-                )
-                (apply_expression
-                    [
-                        (apply_expression
-                            function: (variable_expression
-                                name: (identifier) @functionCallee
-                            )
-                        )
-                        (apply_expression
-                            (apply_expression
-                                function: (variable_expression
-                                    name: (identifier) @functionCallee
-                                )
-                            )
-                        )
-                    ]
+          [
+            (apply_expression
+              function: (variable_expression
+                name: (identifier) @functionCallee
+              )
+            )
+            (apply_expression
+              (apply_expression
+                function: (variable_expression
+                  name: (identifier) @functionCallee
                 )
-            ]
+              )
+            )
+          ]
         )
-    ]
+      ]
+    )
+  ]
 ) @_.domain
 
 ;; Args:
@@ -269,39 +257,18 @@
 ;;!  xxxxxx
 ;;!  --------
 (apply_expression
-    function: (variable_expression
-        name: (identifier) @functionCallee
-    )
-    argument: (_) @argumentOrParameter
+  function: (variable_expression
+    name: (identifier) @functionCallee
+  )
+  argument: (_) @argumentOrParameter
 ) @_.domain
 
 (apply_expression
-    argument: (_) @argumentOrParameter
+  argument: (_) @argumentOrParameter
 )
 
 (function_expression
-    (formals) @argumentOrParameter
-) @_.domain
-
-;; inherit is a built-in keyword, but is close enough to a function...
-;; Callee:
-;;!! inherit pkgs input output;
-;;!  ^^^^^^^
-;;!  xxxxxxxx
-;;!  -------------------------
-;; Args:
-;;!! inherit pkgs input output;
-;;!          ^^^^
-;;!          xxxxx
-;;!         <*****************>
-(inherit
-    "inherit" @functionCallee
-    (
-        (inherited_attrs) @argumentOrParameter.iteration
-    )
-) @functionCall @_.domain
-(inherited_attrs
-    attr: (_) @argumentOrParameter
+  (formals) @argumentOrParameter
 )
 
 ;;
@@ -313,9 +280,9 @@
 ;;!   xxxxx
 ;;!  -------
 (binding
-    (_) @_.leading.start.endOf
-    .
-    expression: (_) @value @_.leading.end.startOf
+  (_) @_.leading.start.endOf
+  .
+  expression: (_) @value @_.leading.end.startOf
 ) @_.domain
 
 ;;!! a = 25;
@@ -323,7 +290,7 @@
 ;;!  xxxx
 ;;!  -------
 (binding
-    (_) @name @_.leading.end.startOf @_.trailing.start.endOf
-    .
-    expression: (_) @_.trailing.end.startOf
+  (_) @name @_.leading.end.startOf @_.trailing.start.endOf
+  .
+  expression: (_) @_.trailing.end.startOf
 ) @_.domain

From 68638f482aa8479c6373bdab4553f5c7a28ad1a3 Mon Sep 17 00:00:00 2001
From: fidgetingbits <fidgetingbits@memeoid.cx>
Date: Tue, 14 Nov 2023 10:43:29 +0800
Subject: [PATCH 08/16] Fix call/callee in a bunch of cases

---
 queries/nix.scm | 60 ++++++++++++++++++++++++++++++++++++-------------
 1 file changed, 45 insertions(+), 15 deletions(-)

diff --git a/queries/nix.scm b/queries/nix.scm
index 3482be6be1..edd9bccce0 100644
--- a/queries/nix.scm
+++ b/queries/nix.scm
@@ -2,13 +2,13 @@
 ;; Statements
 ;;
 
-;; Any top-level expression that isn't a comment is a statement
-;; (
-;;     (source_code
-;;         (_) @statement
-;;     ) @_.iteration
-;;     (#not-type? @statement comment)
-;; )
+(let_expression
+  (binding_set
+    (_) @statement @_.domain
+  )
+  "in" @statement.iteration.end.startOf
+)
+
 (source_code) @comment.iteration
 
 ;; Any foo = <expression> anywhere is a statement
@@ -110,7 +110,7 @@
   (rec_attrset_expression
     (_) @map.interior
   )
-] @map @statement.iteration @value.iteration @name.iteration
+] @map @value.iteration @name.iteration
 
 ;;!! foo = { x = 1; };
 ;;!          ^^^^^^
@@ -192,12 +192,6 @@
   expression: (function_expression) @namedFunction.interior
 ) @namedFunction @functionName.domain
 
-;; Calls to functions are a bit finicky, because of everything being curried lambdas
-(
-  (apply_expression) @dummy @functionCall @argumentOrParameter.iteration
-  (#not-parent-type? @functionCall apply_expression)
-)
-
 ;; This is gross, but not sure how to do fuzzy matching against an unknown number of
 ;; nested child nodes. Arbitrarily stopping at 5 args for now, as that ought to be enough
 ;; arguments for anyone
@@ -244,7 +238,43 @@
       ]
     )
   ]
-) @_.domain
+) @functionCall @_.domain
+
+;; Similar to above, but sometimes the function calls are in select_expression
+(apply_expression
+  [
+    (select_expression
+      expression: (variable_expression
+        name: (identifier)
+      )
+    ) @functionCallee
+    (apply_expression
+      [
+        (select_expression
+          expression: (variable_expression
+            name: (identifier)
+          )
+        ) @functionCallee
+        (apply_expression
+          [
+            (select_expression
+              expression: (variable_expression
+                name: (identifier)
+              )
+            ) @functionCallee
+            (apply_expression
+              (select_expression
+                expression: (variable_expression
+                  name: (identifier)
+                )
+              ) @functionCallee
+            )
+          ]
+        )
+      ]
+    )
+  ]
+) @functionCall @_.domain
 
 ;; Args:
 ;;!! mkHost a

From c3d1ae7a4a4884cc595cab6461e0a6cec020907b Mon Sep 17 00:00:00 2001
From: fidgetingbits <fidgetingbits@memeoid.cx>
Date: Tue, 14 Nov 2023 17:17:09 +0800
Subject: [PATCH 09/16] Support inside comment

---
 .../languages/nix/changeInsideComment.yml     | 26 +++++++++++++++++++
 queries/nix.scm                               |  5 +++-
 2 files changed, 30 insertions(+), 1 deletion(-)
 create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeInsideComment.yml

diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeInsideComment.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeInsideComment.yml
new file mode 100644
index 0000000000..8601889825
--- /dev/null
+++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeInsideComment.yml
@@ -0,0 +1,26 @@
+languageId: nix
+command:
+  version: 6
+  spokenForm: change inside comment
+  action:
+    name: clearAndSetSelection
+    target:
+      type: primitive
+      modifiers:
+        - {type: interiorOnly}
+        - type: containingScope
+          scopeType: {type: comment}
+  usePrePhraseSnapshot: true
+initialState:
+  documentContents: |
+    # I am a comment
+  selections:
+    - anchor: {line: 0, character: 16}
+      active: {line: 0, character: 16}
+  marks: {}
+finalState:
+  documentContents: |
+    # 
+  selections:
+    - anchor: {line: 0, character: 2}
+      active: {line: 0, character: 2}
diff --git a/queries/nix.scm b/queries/nix.scm
index edd9bccce0..90d839f282 100644
--- a/queries/nix.scm
+++ b/queries/nix.scm
@@ -143,7 +143,10 @@
 
 ;;!! # foo
 ;;!  ^^^^^
-(comment) @comment @textFragment
+(
+  (comment) @comment @textFragment @comment.interior
+  (#shrink-to-match! @comment.interior "# ?(?<keep>.*)$")
+)
 
 [
   (string_expression)

From 7abb7a301bf1a8b04766e6e299da7673bc72fdab Mon Sep 17 00:00:00 2001
From: fidgetingbits <fidgetingbits@memeoid.cx>
Date: Tue, 14 Nov 2023 17:51:26 +0800
Subject: [PATCH 10/16] Support other variation of @ assignment for args

---
 .../recorded/languages/nix/changeArg3.yml     | 31 +++++++++++++++++++
 .../recorded/languages/nix/changeArg4.yml     | 31 +++++++++++++++++++
 queries/nix.scm                               | 25 ++++++++-------
 3 files changed, 75 insertions(+), 12 deletions(-)
 create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeArg3.yml
 create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeArg4.yml

diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeArg3.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeArg3.yml
new file mode 100644
index 0000000000..b70276f6ec
--- /dev/null
+++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeArg3.yml
@@ -0,0 +1,31 @@
+languageId: nix
+command:
+  version: 6
+  spokenForm: change arg
+  action:
+    name: clearAndSetSelection
+    target:
+      type: primitive
+      modifiers:
+        - type: containingScope
+          scopeType: {type: argumentOrParameter}
+  usePrePhraseSnapshot: true
+initialState:
+  documentContents: |-
+    {
+      outputs = { self, nix-darwin, nixpkgs, home-manager, sops-nix, ... }@inputs:
+        {};
+    }
+  selections:
+    - anchor: {line: 1, character: 21}
+      active: {line: 1, character: 21}
+  marks: {}
+finalState:
+  documentContents: |-
+    {
+      outputs = :
+        {};
+    }
+  selections:
+    - anchor: {line: 1, character: 12}
+      active: {line: 1, character: 12}
diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeArg4.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeArg4.yml
new file mode 100644
index 0000000000..760f9c91dd
--- /dev/null
+++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeArg4.yml
@@ -0,0 +1,31 @@
+languageId: nix
+command:
+  version: 6
+  spokenForm: change arg
+  action:
+    name: clearAndSetSelection
+    target:
+      type: primitive
+      modifiers:
+        - type: containingScope
+          scopeType: {type: argumentOrParameter}
+  usePrePhraseSnapshot: true
+initialState:
+  documentContents: |-
+    {
+      outputs = inputs@{ self, nix-darwin, nixpkgs, home-manager, sops-nix, ... }:
+        {};
+    }
+  selections:
+    - anchor: {line: 1, character: 16}
+      active: {line: 1, character: 16}
+  marks: {}
+finalState:
+  documentContents: |-
+    {
+      outputs = :
+        {};
+    }
+  selections:
+    - anchor: {line: 1, character: 12}
+      active: {line: 1, character: 12}
diff --git a/queries/nix.scm b/queries/nix.scm
index 90d839f282..73d9aa612e 100644
--- a/queries/nix.scm
+++ b/queries/nix.scm
@@ -172,16 +172,21 @@
 (function_expression
   [
     (
-      ;; Match foo@{ a, b }: style
-      (identifier) @argumentOrParameter.start
-      "@"
+      ;; Match { a, b } and foo@{ a, b }: style
+      (identifier)? @argumentOrParameter.start
+      .
+      "@"?
+      .
       (formals) @argumentOrParameter.end
     )
-    ;; Match a: style arg
-    (identifier) @argumentOrParameter
-
-    ;; Match { a, b }: style
-    (formals) @argumentOrParameter
+    (
+      ;; Match a: and { a, b }@foo: styles
+      (formals)? @argumentOrParameter.start
+      .
+      "@"?
+      .
+      (identifier) @argumentOrParameter.end
+    )
   ]
   .
   body: (_) @anonymousFunction.interior
@@ -300,10 +305,6 @@
   argument: (_) @argumentOrParameter
 )
 
-(function_expression
-  (formals) @argumentOrParameter
-)
-
 ;;
 ;; Names and Values
 ;;

From 2e217bec34b149f84564b77d8d7da890064f2b55 Mon Sep 17 00:00:00 2001
From: fidgetingbits <fidgetingbits@memeoid.cx>
Date: Tue, 14 Nov 2023 18:19:54 +0800
Subject: [PATCH 11/16] Fix mixing functionCall case

---
 .../recorded/languages/nix/changeCall2.yml    | 31 +++++++++++++++++++
 queries/nix.scm                               |  2 +-
 2 files changed, 32 insertions(+), 1 deletion(-)
 create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeCall2.yml

diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeCall2.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeCall2.yml
new file mode 100644
index 0000000000..725922dfa6
--- /dev/null
+++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeCall2.yml
@@ -0,0 +1,31 @@
+languageId: nix
+command:
+  version: 6
+  spokenForm: change call
+  action:
+    name: clearAndSetSelection
+    target:
+      type: primitive
+      modifiers:
+        - type: containingScope
+          scopeType: {type: functionCall}
+  usePrePhraseSnapshot: true
+initialState:
+  documentContents: |-
+    {
+        devShells = forEachSupportedSystem ({ pkgs }: {
+            default = pkgs.mkShell {};
+        });
+    }
+  selections:
+    - anchor: {line: 1, character: 24}
+      active: {line: 1, character: 24}
+  marks: {}
+finalState:
+  documentContents: |-
+    {
+        devShells = ;
+    }
+  selections:
+    - anchor: {line: 1, character: 16}
+      active: {line: 1, character: 16}
diff --git a/queries/nix.scm b/queries/nix.scm
index 73d9aa612e..7de1060619 100644
--- a/queries/nix.scm
+++ b/queries/nix.scm
@@ -299,7 +299,7 @@
     name: (identifier) @functionCallee
   )
   argument: (_) @argumentOrParameter
-) @_.domain
+) @functionCall @_.domain
 
 (apply_expression
   argument: (_) @argumentOrParameter

From 250a68b1f23c91137df3b96a687a09b3270e74e9 Mon Sep 17 00:00:00 2001
From: fidgetingbits <fidgetingbits@memeoid.cx>
Date: Wed, 15 Nov 2023 09:47:55 +0800
Subject: [PATCH 12/16] assert tests and other tweaks

---
 .../recorded/languages/nix/changeArg3.yml     | 16 +++----
 .../recorded/languages/nix/changeArg4.yml     | 16 +++----
 .../languages/nix/changeCondition3.yml        | 31 +++++++++++++
 .../languages/nix/changeCondition4.yml        | 31 +++++++++++++
 .../recorded/languages/nix/changeState.yml    | 29 +++++++++++++
 .../recorded/languages/nix/changeState2.yml   | 31 +++++++++++++
 .../recorded/languages/nix/changeState3.yml   | 21 +++++++++
 queries/nix.scm                               | 43 +++++++++++++------
 8 files changed, 189 insertions(+), 29 deletions(-)
 create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeCondition3.yml
 create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeCondition4.yml
 create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeState.yml
 create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeState2.yml
 create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeState3.yml

diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeArg3.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeArg3.yml
index b70276f6ec..0f69812efe 100644
--- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeArg3.yml
+++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeArg3.yml
@@ -11,21 +11,21 @@ command:
           scopeType: {type: argumentOrParameter}
   usePrePhraseSnapshot: true
 initialState:
-  documentContents: |-
+  documentContents: |
     {
-      outputs = { self, nix-darwin, nixpkgs, home-manager, sops-nix, ... }@inputs:
+        outputs = { self, nix-darwin, nixpkgs, home-manager, sops-nix, ... }@inputs:
         {};
     }
   selections:
-    - anchor: {line: 1, character: 21}
-      active: {line: 1, character: 21}
+    - anchor: {line: 1, character: 39}
+      active: {line: 1, character: 39}
   marks: {}
 finalState:
-  documentContents: |-
+  documentContents: |
     {
-      outputs = :
+        outputs = :
         {};
     }
   selections:
-    - anchor: {line: 1, character: 12}
-      active: {line: 1, character: 12}
+    - anchor: {line: 1, character: 14}
+      active: {line: 1, character: 14}
diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeArg4.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeArg4.yml
index 760f9c91dd..81dee8178c 100644
--- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeArg4.yml
+++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeArg4.yml
@@ -11,21 +11,21 @@ command:
           scopeType: {type: argumentOrParameter}
   usePrePhraseSnapshot: true
 initialState:
-  documentContents: |-
+  documentContents: |
     {
-      outputs = inputs@{ self, nix-darwin, nixpkgs, home-manager, sops-nix, ... }:
+        outputs = inputs@{ self, nix-darwin, nixpkgs, home-manager, sops-nix, ... }:
         {};
     }
   selections:
-    - anchor: {line: 1, character: 16}
-      active: {line: 1, character: 16}
+    - anchor: {line: 1, character: 44}
+      active: {line: 1, character: 44}
   marks: {}
 finalState:
-  documentContents: |-
+  documentContents: |
     {
-      outputs = :
+        outputs = :
         {};
     }
   selections:
-    - anchor: {line: 1, character: 12}
-      active: {line: 1, character: 12}
+    - anchor: {line: 1, character: 14}
+      active: {line: 1, character: 14}
diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeCondition3.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeCondition3.yml
new file mode 100644
index 0000000000..c525a9c109
--- /dev/null
+++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeCondition3.yml
@@ -0,0 +1,31 @@
+languageId: nix
+command:
+  version: 6
+  spokenForm: change condition
+  action:
+    name: clearAndSetSelection
+    target:
+      type: primitive
+      modifiers:
+        - type: containingScope
+          scopeType: {type: condition}
+  usePrePhraseSnapshot: true
+initialState:
+  documentContents: |-
+    { a ? false, b ? false , c ? null }:
+        assert a -> c != null;
+        assert b -> c != null;
+        {foo = a;}
+  selections:
+    - anchor: {line: 1, character: 13}
+      active: {line: 1, character: 13}
+  marks: {}
+finalState:
+  documentContents: |-
+    { a ? false, b ? false , c ? null }:
+        assert ;
+        assert b -> c != null;
+        {foo = a;}
+  selections:
+    - anchor: {line: 1, character: 11}
+      active: {line: 1, character: 11}
diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeCondition4.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeCondition4.yml
new file mode 100644
index 0000000000..ef925d7604
--- /dev/null
+++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeCondition4.yml
@@ -0,0 +1,31 @@
+languageId: nix
+command:
+  version: 6
+  spokenForm: change condition
+  action:
+    name: clearAndSetSelection
+    target:
+      type: primitive
+      modifiers:
+        - type: containingScope
+          scopeType: {type: condition}
+  usePrePhraseSnapshot: true
+initialState:
+  documentContents: |-
+    { a ? false, b ? false , c ? null }:
+        assert a -> c != null;
+        assert b -> c != null;
+        {foo = a;}
+  selections:
+    - anchor: {line: 3, character: 9}
+      active: {line: 3, character: 9}
+  marks: {}
+finalState:
+  documentContents: |-
+    { a ? false, b ? false , c ? null }:
+        assert a -> c != null;
+        assert ;
+        {foo = a;}
+  selections:
+    - anchor: {line: 2, character: 11}
+      active: {line: 2, character: 11}
diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeState.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeState.yml
new file mode 100644
index 0000000000..75a78b12b5
--- /dev/null
+++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeState.yml
@@ -0,0 +1,29 @@
+languageId: nix
+command:
+  version: 6
+  spokenForm: change state
+  action:
+    name: clearAndSetSelection
+    target:
+      type: primitive
+      modifiers:
+        - type: containingScope
+          scopeType: {type: statement}
+  usePrePhraseSnapshot: true
+initialState:
+  documentContents: |
+    { config, lib, pkgs, ... }:
+    with lib;
+    { foo = libthing; }
+  selections:
+    - anchor: {line: 1, character: 4}
+      active: {line: 1, character: 4}
+  marks: {}
+finalState:
+  documentContents: |
+    { config, lib, pkgs, ... }:
+
+    { foo = libthing; }
+  selections:
+    - anchor: {line: 1, character: 0}
+      active: {line: 1, character: 0}
diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeState2.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeState2.yml
new file mode 100644
index 0000000000..19b895ef50
--- /dev/null
+++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeState2.yml
@@ -0,0 +1,31 @@
+languageId: nix
+command:
+  version: 6
+  spokenForm: change state
+  action:
+    name: clearAndSetSelection
+    target:
+      type: primitive
+      modifiers:
+        - type: containingScope
+          scopeType: {type: statement}
+  usePrePhraseSnapshot: true
+initialState:
+  documentContents: |-
+    { a ? false, b ? false , c ? null }:
+        assert a -> c != null;
+        assert b -> c != null;
+        {foo = a;}
+  selections:
+    - anchor: {line: 1, character: 9}
+      active: {line: 1, character: 9}
+  marks: {}
+finalState:
+  documentContents: |-
+    { a ? false, b ? false , c ? null }:
+        
+        assert b -> c != null;
+        {foo = a;}
+  selections:
+    - anchor: {line: 1, character: 4}
+      active: {line: 1, character: 4}
diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeState3.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeState3.yml
new file mode 100644
index 0000000000..3dadba90a8
--- /dev/null
+++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeState3.yml
@@ -0,0 +1,21 @@
+languageId: nix
+command:
+  version: 6
+  spokenForm: change state
+  action:
+    name: clearAndSetSelection
+    target:
+      type: primitive
+      modifiers:
+        - type: containingScope
+          scopeType: {type: statement}
+  usePrePhraseSnapshot: true
+initialState:
+  documentContents: |-
+    { a ? false, b ? false , c ? null }:
+        {foo = assert b != false; a;}
+  selections:
+    - anchor: {line: 1, character: 19}
+      active: {line: 1, character: 19}
+  marks: {}
+thrownError: {name: NoContainingScopeError}
diff --git a/queries/nix.scm b/queries/nix.scm
index 7de1060619..ffa6291daf 100644
--- a/queries/nix.scm
+++ b/queries/nix.scm
@@ -24,23 +24,36 @@
 ;; file for an example.
 (
   (assert_expression
-    "assert" @statement.start
-    condition: (_)
+    "assert" @statement.start @statement.domain.start
+    condition: (_) @condition
     ";" @statement.end
-    body: (_)
-  ) @_.domain
-  (#not-parent-type? @_.domain binding)
+    body: (_) @statement.domain.end.startOf
+  ) @condition.domain
+  (#not-parent-type? @condition.domain binding)
 )
-(assert_expression
-  condition: (_) @condition
-  body: (_) @branch
-) @_.domain
 
+;; FIXME: Branch only makes sense probably for single line assert, but may not keep it.
+;; also don't know how to match on single line only
+;; (assert_expression
+;;   condition: (_) @condition
+;;   body: (_) @branch
+;; ) @_.domain
+
+;; Match with when it's part of an expression like:
+;; with lib;
+;; let ... in ...;
 (
-  (with_expression) @statement
-  (#not-parent-type? @statement binding)
+  (with_expression
+    "with" @statement.start
+    .
+    environment: (_)
+    ";" @statement.end
+  ) @dummy
+  (#not-parent-type? @dummy binding)
 )
 
+;; FIXME: Need something like above for inherit, but only if it is on a single line.
+
 ;;!! let
 ;;!!   a = 1;
 ;;!   <******
@@ -141,8 +154,12 @@
 ;; Strings
 ;;
 
+;; Comment
 ;;!! # foo
 ;;!  ^^^^^
+;; Inside comment:
+;;!! # foo
+;;!    ^^^
 (
   (comment) @comment @textFragment @comment.interior
   (#shrink-to-match! @comment.interior "# ?(?<keep>.*)$")
@@ -201,8 +218,8 @@
 ) @namedFunction @functionName.domain
 
 ;; This is gross, but not sure how to do fuzzy matching against an unknown number of
-;; nested child nodes. Arbitrarily stopping at 5 args for now, as that ought to be enough
-;; arguments for anyone
+;; nested child nodes. Possiblyl should be a new predicate. Arbitrarily stopping at
+;; 5 args for now, as that ought to be enough arguments for anyone
 ;; Args:
 ;;!! mkHost a b c d e
 ;;!           ^

From 5b4cc8df9e026b19736c7e4497b604c6b46b966d Mon Sep 17 00:00:00 2001
From: fidgetingbits <fidgetingbits@memeoid.cx>
Date: Wed, 15 Nov 2023 10:22:53 +0800
Subject: [PATCH 13/16] Remove inside comment stuff for now

---
 .../recorded/languages/nix/changeArg2.yml     |  8 +--
 .../languages/nix/changeInsideComment.yml     | 26 ---------
 queries/nix.scm                               | 57 +++++++++++++------
 3 files changed, 45 insertions(+), 46 deletions(-)
 delete mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeInsideComment.yml

diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeArg2.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeArg2.yml
index f4d0656743..9e0d416073 100644
--- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeArg2.yml
+++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeArg2.yml
@@ -11,15 +11,15 @@ command:
           scopeType: {type: argumentOrParameter}
   usePrePhraseSnapshot: true
 initialState:
-  documentContents: |-
+  documentContents: |
     { pkgs, ... }:
     { a = 1; }
   selections:
-    - anchor: {line: 1, character: 4}
-      active: {line: 1, character: 4}
+    - anchor: {line: 0, character: 3}
+      active: {line: 0, character: 3}
   marks: {}
 finalState:
-  documentContents: |-
+  documentContents: |
     :
     { a = 1; }
   selections:
diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeInsideComment.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeInsideComment.yml
deleted file mode 100644
index 8601889825..0000000000
--- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeInsideComment.yml
+++ /dev/null
@@ -1,26 +0,0 @@
-languageId: nix
-command:
-  version: 6
-  spokenForm: change inside comment
-  action:
-    name: clearAndSetSelection
-    target:
-      type: primitive
-      modifiers:
-        - {type: interiorOnly}
-        - type: containingScope
-          scopeType: {type: comment}
-  usePrePhraseSnapshot: true
-initialState:
-  documentContents: |
-    # I am a comment
-  selections:
-    - anchor: {line: 0, character: 16}
-      active: {line: 0, character: 16}
-  marks: {}
-finalState:
-  documentContents: |
-    # 
-  selections:
-    - anchor: {line: 0, character: 2}
-      active: {line: 0, character: 2}
diff --git a/queries/nix.scm b/queries/nix.scm
index ffa6291daf..b3d8013b6b 100644
--- a/queries/nix.scm
+++ b/queries/nix.scm
@@ -25,19 +25,19 @@
 (
   (assert_expression
     "assert" @statement.start @statement.domain.start
-    condition: (_) @condition
+    condition: (_)
     ";" @statement.end
     body: (_) @statement.domain.end.startOf
-  ) @condition.domain
-  (#not-parent-type? @condition.domain binding)
+  ) @dummy
+  (#not-parent-type? @dummy binding)
 )
 
 ;; FIXME: Branch only makes sense probably for single line assert, but may not keep it.
 ;; also don't know how to match on single line only
-;; (assert_expression
-;;   condition: (_) @condition
-;;   body: (_) @branch
-;; ) @_.domain
+(assert_expression
+  condition: (_) @condition
+  ;;   body: (_) @branch
+) @_.domain
 
 ;; Match with when it's part of an expression like:
 ;; with lib;
@@ -160,10 +160,22 @@
 ;; Inside comment:
 ;;!! # foo
 ;;!    ^^^
-(
-  (comment) @comment @textFragment @comment.interior
-  (#shrink-to-match! @comment.interior "# ?(?<keep>.*)$")
-)
+(comment) @comment @textFragment
+
+;; FIXME: This works for inside comment on #, but breaks multi-line /* */
+;; Not sure how to match different interiors depending on contents. Almost
+;; need a #regex predicate? Can't use shrink-to-match in [] without errors
+;; [
+;;   (
+;;     (comment) @comment.interior
+;;     (#shrink-to-match! @comment.interior "# ?(?<keep>.*)$")
+;;   )
+;;   (
+;;     (comment) @comment.interior
+;;     ;; This would need to be a multi-line match, which I'm not sure shrink-to-match can do
+;;     ;; (#shrink-to-match! @comment.interior "/\*(?<keep>.*)\*/$")
+;;   )
+;; ] @comment
 
 [
   (string_expression)
@@ -187,23 +199,36 @@
 ;;!         ^^^^^^^^ Func1 due to currying
 ;;!      ^^^^^^^^^^^ Func2 due to currying
 (function_expression
+  .
   [
     (
-      ;; Match { a, b } and foo@{ a, b }: style
+      ;; Match foo@{ a, b }:
       (identifier)? @argumentOrParameter.start
       .
-      "@"?
+      "@"
       .
       (formals) @argumentOrParameter.end
     )
     (
-      ;; Match a: and { a, b }@foo: styles
-      (formals)? @argumentOrParameter.start
+      ;; Match{ a, b }@foo:
+      (formals) @argumentOrParameter.start
       .
-      "@"?
+      "@"
       .
       (identifier) @argumentOrParameter.end
     )
+    ;; Match { a, b }:
+    (
+      (formals) @argumentOrParameter
+      .
+      ":"
+    )
+    ;; Match a:
+    (
+      (identifier) @argumentOrParameter
+      .
+      ":"
+    )
   ]
   .
   body: (_) @anonymousFunction.interior

From db69df5c4082216cab0adf719a345ed8ff059ea1 Mon Sep 17 00:00:00 2001
From: fidgetingbits <fidgetingbits@memeoid.cx>
Date: Thu, 1 Feb 2024 09:47:37 +0800
Subject: [PATCH 14/16] add collectionItem iteration and domain to lists

---
 .../languages/nix/changeEveryItem.yml         | 34 +++++++++++++++++++
 queries/nix.scm                               |  2 +-
 2 files changed, 35 insertions(+), 1 deletion(-)
 create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeEveryItem.yml

diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeEveryItem.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeEveryItem.yml
new file mode 100644
index 0000000000..d5025ff1a0
--- /dev/null
+++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeEveryItem.yml
@@ -0,0 +1,34 @@
+languageId: nix
+command:
+  version: 6
+  spokenForm: change every item
+  action:
+    name: clearAndSetSelection
+    target:
+      type: primitive
+      modifiers:
+        - type: everyScope
+          scopeType: {type: collectionItem}
+  usePrePhraseSnapshot: true
+initialState:
+  documentContents: |+
+    foo = [ "a" a/b/c "b" ];
+
+  selections:
+    - anchor: {line: 0, character: 9}
+      active: {line: 0, character: 9}
+  marks: {}
+finalState:
+  documentContents: |+
+    foo = [    ];
+
+  selections:
+    - anchor: {line: 0, character: 8}
+      active: {line: 0, character: 8}
+    - anchor: {line: 0, character: 9}
+      active: {line: 0, character: 9}
+    - anchor: {line: 0, character: 10}
+      active: {line: 0, character: 10}
+ide:
+  messages:
+    - {type: error, id: BaseTreeSitterScopeHandler.allow-multiple}
diff --git a/queries/nix.scm b/queries/nix.scm
index b3d8013b6b..e7ce720616 100644
--- a/queries/nix.scm
+++ b/queries/nix.scm
@@ -105,7 +105,7 @@
 ;;!          xx
 (list_expression
   element: (_) @collectionItem
-)
+) @_.iteration @_.domain
 
 ;;!! foo = [ a b c ];
 ;;!        ^^^^^^^^^

From bf7df7efe1131c6b224ad4abc38bc223aa91b032 Mon Sep 17 00:00:00 2001
From: "pre-commit-ci-lite[bot]"
 <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com>
Date: Mon, 17 Jun 2024 04:26:13 +0000
Subject: [PATCH 15/16] [pre-commit.ci lite] apply automatic fixes

---
 .../getIndividualDelimiters.ts                |  4 ++--
 .../recorded/languages/nix/changeArg.yml      | 10 ++++-----
 .../recorded/languages/nix/changeArg2.yml     | 10 ++++-----
 .../recorded/languages/nix/changeArg3.yml     | 10 ++++-----
 .../recorded/languages/nix/changeArg4.yml     | 10 ++++-----
 .../recorded/languages/nix/changeBranch.yml   | 10 ++++-----
 .../recorded/languages/nix/changeBranch2.yml  | 14 ++++++------
 .../recorded/languages/nix/changeBranch3.yml  | 10 ++++-----
 .../recorded/languages/nix/changeCall.yml     | 10 ++++-----
 .../recorded/languages/nix/changeCall2.yml    | 10 ++++-----
 .../recorded/languages/nix/changeCallee.yml   | 10 ++++-----
 .../recorded/languages/nix/changeComment.yml  | 10 ++++-----
 .../recorded/languages/nix/changeComment2.yml | 10 ++++-----
 .../recorded/languages/nix/changeComment3.yml | 12 +++++-----
 .../languages/nix/changeCondition.yml         | 12 +++++-----
 .../languages/nix/changeCondition2.yml        | 10 ++++-----
 .../languages/nix/changeCondition3.yml        | 10 ++++-----
 .../languages/nix/changeCondition4.yml        | 10 ++++-----
 .../languages/nix/changeEveryIfState.yml      | 14 ++++++------
 .../languages/nix/changeEveryItem.yml         | 20 ++++++++---------
 .../recorded/languages/nix/changeEveryMap.yml | 22 +++++++++----------
 .../languages/nix/changeEveryMap2.yml         | 14 ++++++------
 .../recorded/languages/nix/changeFunkName.yml | 10 ++++-----
 .../recorded/languages/nix/changeIfState.yml  | 10 ++++-----
 .../languages/nix/changeInsideFunk.yml        | 12 +++++-----
 .../languages/nix/changeInsideLambda.yml      | 12 +++++-----
 .../languages/nix/changeInsideList.yml        | 14 ++++++------
 .../recorded/languages/nix/changeItem.yml     | 10 ++++-----
 .../recorded/languages/nix/changeItem2.yml    | 12 +++++-----
 .../recorded/languages/nix/changeKey.yml      | 10 ++++-----
 .../recorded/languages/nix/changeKeyPlex.yml  | 16 +++++++-------
 .../recorded/languages/nix/changeList.yml     | 10 ++++-----
 .../languages/nix/changeListGreenAir.yml      | 16 +++++++-------
 .../recorded/languages/nix/changeMap.yml      | 10 ++++-----
 .../recorded/languages/nix/changeMap2.yml     | 10 ++++-----
 .../recorded/languages/nix/changeMap3.yml     | 10 ++++-----
 .../recorded/languages/nix/changeMap4.yml     | 10 ++++-----
 .../recorded/languages/nix/changeName.yml     | 10 ++++-----
 .../recorded/languages/nix/changeName2.yml    | 10 ++++-----
 .../recorded/languages/nix/changeRound.yml    | 14 ++++++------
 .../recorded/languages/nix/changeRound2.yml   | 10 ++++-----
 .../recorded/languages/nix/changeState.yml    | 10 ++++-----
 .../recorded/languages/nix/changeState2.yml   | 12 +++++-----
 .../recorded/languages/nix/changeState3.yml   |  8 +++----
 .../recorded/languages/nix/changeString.yml   | 10 ++++-----
 .../recorded/languages/nix/changeString2.yml  | 10 ++++-----
 .../recorded/languages/nix/changeString3.yml  | 10 ++++-----
 .../recorded/languages/nix/changeValue.yml    | 10 ++++-----
 .../recorded/languages/nix/changeValue2.yml   | 10 ++++-----
 .../recorded/languages/nix/changeValue3.yml   | 10 ++++-----
 .../recorded/languages/nix/changeValue4.yml   | 10 ++++-----
 .../recorded/languages/nix/chuckArg.yml       | 10 ++++-----
 .../recorded/languages/nix/chuckInsideMap.yml | 16 +++++++-------
 .../recorded/languages/nix/chuckKey.yml       | 10 ++++-----
 .../recorded/languages/nix/chuckValue.yml     | 10 ++++-----
 .../recorded/languages/nix/chuckValue2.yml    | 10 ++++-----
 56 files changed, 312 insertions(+), 312 deletions(-)

diff --git a/packages/cursorless-engine/src/processTargets/modifiers/surroundingPair/getIndividualDelimiters.ts b/packages/cursorless-engine/src/processTargets/modifiers/surroundingPair/getIndividualDelimiters.ts
index 2b7af28481..e63a8a8d69 100644
--- a/packages/cursorless-engine/src/processTargets/modifiers/surroundingPair/getIndividualDelimiters.ts
+++ b/packages/cursorless-engine/src/processTargets/modifiers/surroundingPair/getIndividualDelimiters.ts
@@ -44,8 +44,8 @@ export function getIndividualDelimiters(
           isLeft && !isRight
             ? "left"
             : isRight && !isLeft
-            ? "right"
-            : "unknown",
+              ? "right"
+              : "unknown",
         delimiter,
       };
     });
diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeArg.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeArg.yml
index c47e5037e2..8efcae9e8c 100644
--- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeArg.yml
+++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeArg.yml
@@ -8,20 +8,20 @@ command:
       type: primitive
       modifiers:
         - type: containingScope
-          scopeType: {type: argumentOrParameter}
+          scopeType: { type: argumentOrParameter }
   usePrePhraseSnapshot: true
 initialState:
   documentContents: |-
     { pkgs, ... }:
     { a = 1; }
   selections:
-    - anchor: {line: 0, character: 10}
-      active: {line: 0, character: 10}
+    - anchor: { line: 0, character: 10 }
+      active: { line: 0, character: 10 }
   marks: {}
 finalState:
   documentContents: |-
     :
     { a = 1; }
   selections:
-    - anchor: {line: 0, character: 0}
-      active: {line: 0, character: 0}
+    - anchor: { line: 0, character: 0 }
+      active: { line: 0, character: 0 }
diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeArg2.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeArg2.yml
index 9e0d416073..629adbd188 100644
--- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeArg2.yml
+++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeArg2.yml
@@ -8,20 +8,20 @@ command:
       type: primitive
       modifiers:
         - type: containingScope
-          scopeType: {type: argumentOrParameter}
+          scopeType: { type: argumentOrParameter }
   usePrePhraseSnapshot: true
 initialState:
   documentContents: |
     { pkgs, ... }:
     { a = 1; }
   selections:
-    - anchor: {line: 0, character: 3}
-      active: {line: 0, character: 3}
+    - anchor: { line: 0, character: 3 }
+      active: { line: 0, character: 3 }
   marks: {}
 finalState:
   documentContents: |
     :
     { a = 1; }
   selections:
-    - anchor: {line: 0, character: 0}
-      active: {line: 0, character: 0}
+    - anchor: { line: 0, character: 0 }
+      active: { line: 0, character: 0 }
diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeArg3.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeArg3.yml
index 0f69812efe..4d77988db4 100644
--- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeArg3.yml
+++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeArg3.yml
@@ -8,7 +8,7 @@ command:
       type: primitive
       modifiers:
         - type: containingScope
-          scopeType: {type: argumentOrParameter}
+          scopeType: { type: argumentOrParameter }
   usePrePhraseSnapshot: true
 initialState:
   documentContents: |
@@ -17,8 +17,8 @@ initialState:
         {};
     }
   selections:
-    - anchor: {line: 1, character: 39}
-      active: {line: 1, character: 39}
+    - anchor: { line: 1, character: 39 }
+      active: { line: 1, character: 39 }
   marks: {}
 finalState:
   documentContents: |
@@ -27,5 +27,5 @@ finalState:
         {};
     }
   selections:
-    - anchor: {line: 1, character: 14}
-      active: {line: 1, character: 14}
+    - anchor: { line: 1, character: 14 }
+      active: { line: 1, character: 14 }
diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeArg4.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeArg4.yml
index 81dee8178c..ecfadb77f1 100644
--- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeArg4.yml
+++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeArg4.yml
@@ -8,7 +8,7 @@ command:
       type: primitive
       modifiers:
         - type: containingScope
-          scopeType: {type: argumentOrParameter}
+          scopeType: { type: argumentOrParameter }
   usePrePhraseSnapshot: true
 initialState:
   documentContents: |
@@ -17,8 +17,8 @@ initialState:
         {};
     }
   selections:
-    - anchor: {line: 1, character: 44}
-      active: {line: 1, character: 44}
+    - anchor: { line: 1, character: 44 }
+      active: { line: 1, character: 44 }
   marks: {}
 finalState:
   documentContents: |
@@ -27,5 +27,5 @@ finalState:
         {};
     }
   selections:
-    - anchor: {line: 1, character: 14}
-      active: {line: 1, character: 14}
+    - anchor: { line: 1, character: 14 }
+      active: { line: 1, character: 14 }
diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeBranch.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeBranch.yml
index f64258bb14..512aae2282 100644
--- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeBranch.yml
+++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeBranch.yml
@@ -8,7 +8,7 @@ command:
       type: primitive
       modifiers:
         - type: containingScope
-          scopeType: {type: branch}
+          scopeType: { type: branch }
   usePrePhraseSnapshot: true
 initialState:
   documentContents: |-
@@ -16,8 +16,8 @@ initialState:
       foo = if a then b else c;
     }
   selections:
-    - anchor: {line: 1, character: 9}
-      active: {line: 1, character: 9}
+    - anchor: { line: 1, character: 9 }
+      active: { line: 1, character: 9 }
   marks: {}
 finalState:
   documentContents: |-
@@ -25,5 +25,5 @@ finalState:
       foo =  else c;
     }
   selections:
-    - anchor: {line: 1, character: 8}
-      active: {line: 1, character: 8}
+    - anchor: { line: 1, character: 8 }
+      active: { line: 1, character: 8 }
diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeBranch2.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeBranch2.yml
index d3105012d4..b8274bb944 100644
--- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeBranch2.yml
+++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeBranch2.yml
@@ -8,25 +8,25 @@ command:
       type: primitive
       modifiers:
         - type: containingScope
-          scopeType: {type: branch}
+          scopeType: { type: branch }
   usePrePhraseSnapshot: true
 initialState:
   documentContents: |
     {
-      foo = if a 
+      foo = if a
       then b
       else c;
     }
   selections:
-    - anchor: {line: 1, character: 9}
-      active: {line: 1, character: 9}
+    - anchor: { line: 1, character: 9 }
+      active: { line: 1, character: 9 }
   marks: {}
 finalState:
   documentContents: |
     {
-      foo = 
+      foo =
       else c;
     }
   selections:
-    - anchor: {line: 1, character: 8}
-      active: {line: 1, character: 8}
+    - anchor: { line: 1, character: 8 }
+      active: { line: 1, character: 8 }
diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeBranch3.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeBranch3.yml
index 3b3d7924f5..c97883b27d 100644
--- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeBranch3.yml
+++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeBranch3.yml
@@ -8,7 +8,7 @@ command:
       type: primitive
       modifiers:
         - type: containingScope
-          scopeType: {type: branch}
+          scopeType: { type: branch }
   usePrePhraseSnapshot: true
 initialState:
   documentContents: |
@@ -17,8 +17,8 @@ initialState:
 
     }
   selections:
-    - anchor: {line: 1, character: 22}
-      active: {line: 1, character: 22}
+    - anchor: { line: 1, character: 22 }
+      active: { line: 1, character: 22 }
   marks: {}
 finalState:
   documentContents: |
@@ -27,5 +27,5 @@ finalState:
 
     }
   selections:
-    - anchor: {line: 1, character: 20}
-      active: {line: 1, character: 20}
+    - anchor: { line: 1, character: 20 }
+      active: { line: 1, character: 20 }
diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeCall.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeCall.yml
index e5da31531e..e61fd37cf2 100644
--- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeCall.yml
+++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeCall.yml
@@ -8,7 +8,7 @@ command:
       type: primitive
       modifiers:
         - type: containingScope
-          scopeType: {type: functionCall}
+          scopeType: { type: functionCall }
   usePrePhraseSnapshot: true
 initialState:
   documentContents: |
@@ -16,8 +16,8 @@ initialState:
       x = map (x: y: x + x) [ 1 2 3 ];
     }
   selections:
-    - anchor: {line: 1, character: 28}
-      active: {line: 1, character: 28}
+    - anchor: { line: 1, character: 28 }
+      active: { line: 1, character: 28 }
   marks: {}
 finalState:
   documentContents: |
@@ -25,5 +25,5 @@ finalState:
       x = ;
     }
   selections:
-    - anchor: {line: 1, character: 6}
-      active: {line: 1, character: 6}
+    - anchor: { line: 1, character: 6 }
+      active: { line: 1, character: 6 }
diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeCall2.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeCall2.yml
index 725922dfa6..88cc263789 100644
--- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeCall2.yml
+++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeCall2.yml
@@ -8,7 +8,7 @@ command:
       type: primitive
       modifiers:
         - type: containingScope
-          scopeType: {type: functionCall}
+          scopeType: { type: functionCall }
   usePrePhraseSnapshot: true
 initialState:
   documentContents: |-
@@ -18,8 +18,8 @@ initialState:
         });
     }
   selections:
-    - anchor: {line: 1, character: 24}
-      active: {line: 1, character: 24}
+    - anchor: { line: 1, character: 24 }
+      active: { line: 1, character: 24 }
   marks: {}
 finalState:
   documentContents: |-
@@ -27,5 +27,5 @@ finalState:
         devShells = ;
     }
   selections:
-    - anchor: {line: 1, character: 16}
-      active: {line: 1, character: 16}
+    - anchor: { line: 1, character: 16 }
+      active: { line: 1, character: 16 }
diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeCallee.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeCallee.yml
index 31307cf239..2eca997028 100644
--- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeCallee.yml
+++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeCallee.yml
@@ -8,7 +8,7 @@ command:
       type: primitive
       modifiers:
         - type: containingScope
-          scopeType: {type: functionCallee}
+          scopeType: { type: functionCallee }
   usePrePhraseSnapshot: true
 initialState:
   documentContents: |
@@ -16,8 +16,8 @@ initialState:
       x = map (x: y: x + x) [ 1 2 3 ];
     }
   selections:
-    - anchor: {line: 1, character: 14}
-      active: {line: 1, character: 14}
+    - anchor: { line: 1, character: 14 }
+      active: { line: 1, character: 14 }
   marks: {}
 finalState:
   documentContents: |
@@ -25,5 +25,5 @@ finalState:
       x =  (x: y: x + x) [ 1 2 3 ];
     }
   selections:
-    - anchor: {line: 1, character: 6}
-      active: {line: 1, character: 6}
+    - anchor: { line: 1, character: 6 }
+      active: { line: 1, character: 6 }
diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeComment.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeComment.yml
index 771680b2c8..dbbce401be 100644
--- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeComment.yml
+++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeComment.yml
@@ -8,18 +8,18 @@ command:
       type: primitive
       modifiers:
         - type: containingScope
-          scopeType: {type: comment}
+          scopeType: { type: comment }
   usePrePhraseSnapshot: true
 initialState:
   documentContents: |
     # Single-line comment
   selections:
-    - anchor: {line: 0, character: 21}
-      active: {line: 0, character: 21}
+    - anchor: { line: 0, character: 21 }
+      active: { line: 0, character: 21 }
   marks: {}
 finalState:
   documentContents: |+
 
   selections:
-    - anchor: {line: 0, character: 0}
-      active: {line: 0, character: 0}
+    - anchor: { line: 0, character: 0 }
+      active: { line: 0, character: 0 }
diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeComment2.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeComment2.yml
index 06a6c95bc2..0ed589ec9b 100644
--- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeComment2.yml
+++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeComment2.yml
@@ -8,7 +8,7 @@ command:
       type: primitive
       modifiers:
         - type: containingScope
-          scopeType: {type: comment}
+          scopeType: { type: comment }
   usePrePhraseSnapshot: true
 initialState:
   documentContents: |
@@ -16,12 +16,12 @@ initialState:
      Multi-line comment
     */
   selections:
-    - anchor: {line: 2, character: 0}
-      active: {line: 2, character: 0}
+    - anchor: { line: 2, character: 0 }
+      active: { line: 2, character: 0 }
   marks: {}
 finalState:
   documentContents: |+
 
   selections:
-    - anchor: {line: 0, character: 0}
-      active: {line: 0, character: 0}
+    - anchor: { line: 0, character: 0 }
+      active: { line: 0, character: 0 }
diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeComment3.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeComment3.yml
index a93825c1cd..7d2b5be8a3 100644
--- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeComment3.yml
+++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeComment3.yml
@@ -8,7 +8,7 @@ command:
       type: primitive
       modifiers:
         - type: containingScope
-          scopeType: {type: comment}
+          scopeType: { type: comment }
   usePrePhraseSnapshot: true
 initialState:
   documentContents: |
@@ -17,15 +17,15 @@ initialState:
       b = c; # Inline comment 2
     }
   selections:
-    - anchor: {line: 2, character: 13}
-      active: {line: 2, character: 13}
+    - anchor: { line: 2, character: 13 }
+      active: { line: 2, character: 13 }
   marks: {}
 finalState:
   documentContents: |
     {
       a = b; # Inline comment (test)
-      b = c; 
+      b = c;
     }
   selections:
-    - anchor: {line: 2, character: 9}
-      active: {line: 2, character: 9}
+    - anchor: { line: 2, character: 9 }
+      active: { line: 2, character: 9 }
diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeCondition.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeCondition.yml
index 0112c652b3..ec236c8bbb 100644
--- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeCondition.yml
+++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeCondition.yml
@@ -8,7 +8,7 @@ command:
       type: primitive
       modifiers:
         - type: containingScope
-          scopeType: {type: condition}
+          scopeType: { type: condition }
   usePrePhraseSnapshot: true
 initialState:
   documentContents: |
@@ -19,17 +19,17 @@ initialState:
         else c;
     }
   selections:
-    - anchor: {line: 3, character: 9}
-      active: {line: 3, character: 9}
+    - anchor: { line: 3, character: 9 }
+      active: { line: 3, character: 9 }
   marks: {}
 finalState:
   documentContents: |
     {
       key =
-        if 
+        if
         then b
         else c;
     }
   selections:
-    - anchor: {line: 2, character: 7}
-      active: {line: 2, character: 7}
+    - anchor: { line: 2, character: 7 }
+      active: { line: 2, character: 7 }
diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeCondition2.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeCondition2.yml
index ee12a12a62..3a66a10983 100644
--- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeCondition2.yml
+++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeCondition2.yml
@@ -8,7 +8,7 @@ command:
       type: primitive
       modifiers:
         - type: containingScope
-          scopeType: {type: condition}
+          scopeType: { type: condition }
   usePrePhraseSnapshot: true
 initialState:
   documentContents: |
@@ -16,8 +16,8 @@ initialState:
       key = assert a > b; "foo";
     }
   selections:
-    - anchor: {line: 1, character: 11}
-      active: {line: 1, character: 11}
+    - anchor: { line: 1, character: 11 }
+      active: { line: 1, character: 11 }
   marks: {}
 finalState:
   documentContents: |
@@ -25,5 +25,5 @@ finalState:
       key = assert ; "foo";
     }
   selections:
-    - anchor: {line: 1, character: 15}
-      active: {line: 1, character: 15}
+    - anchor: { line: 1, character: 15 }
+      active: { line: 1, character: 15 }
diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeCondition3.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeCondition3.yml
index c525a9c109..d4c5a31f51 100644
--- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeCondition3.yml
+++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeCondition3.yml
@@ -8,7 +8,7 @@ command:
       type: primitive
       modifiers:
         - type: containingScope
-          scopeType: {type: condition}
+          scopeType: { type: condition }
   usePrePhraseSnapshot: true
 initialState:
   documentContents: |-
@@ -17,8 +17,8 @@ initialState:
         assert b -> c != null;
         {foo = a;}
   selections:
-    - anchor: {line: 1, character: 13}
-      active: {line: 1, character: 13}
+    - anchor: { line: 1, character: 13 }
+      active: { line: 1, character: 13 }
   marks: {}
 finalState:
   documentContents: |-
@@ -27,5 +27,5 @@ finalState:
         assert b -> c != null;
         {foo = a;}
   selections:
-    - anchor: {line: 1, character: 11}
-      active: {line: 1, character: 11}
+    - anchor: { line: 1, character: 11 }
+      active: { line: 1, character: 11 }
diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeCondition4.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeCondition4.yml
index ef925d7604..7fa2b87bed 100644
--- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeCondition4.yml
+++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeCondition4.yml
@@ -8,7 +8,7 @@ command:
       type: primitive
       modifiers:
         - type: containingScope
-          scopeType: {type: condition}
+          scopeType: { type: condition }
   usePrePhraseSnapshot: true
 initialState:
   documentContents: |-
@@ -17,8 +17,8 @@ initialState:
         assert b -> c != null;
         {foo = a;}
   selections:
-    - anchor: {line: 3, character: 9}
-      active: {line: 3, character: 9}
+    - anchor: { line: 3, character: 9 }
+      active: { line: 3, character: 9 }
   marks: {}
 finalState:
   documentContents: |-
@@ -27,5 +27,5 @@ finalState:
         assert ;
         {foo = a;}
   selections:
-    - anchor: {line: 2, character: 11}
-      active: {line: 2, character: 11}
+    - anchor: { line: 2, character: 11 }
+      active: { line: 2, character: 11 }
diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeEveryIfState.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeEveryIfState.yml
index a35c93253e..5b53c43839 100644
--- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeEveryIfState.yml
+++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeEveryIfState.yml
@@ -8,7 +8,7 @@ command:
       type: primitive
       modifiers:
         - type: everyScope
-          scopeType: {type: ifStatement}
+          scopeType: { type: ifStatement }
   usePrePhraseSnapshot: true
 initialState:
   documentContents: |
@@ -20,8 +20,8 @@ initialState:
         else c;
     }
   selections:
-    - anchor: {line: 5, character: 11}
-      active: {line: 5, character: 11}
+    - anchor: { line: 5, character: 11 }
+      active: { line: 5, character: 11 }
   marks: {}
 finalState:
   documentContents: |
@@ -31,7 +31,7 @@ finalState:
         ;
     }
   selections:
-    - anchor: {line: 1, character: 8}
-      active: {line: 1, character: 8}
-    - anchor: {line: 3, character: 4}
-      active: {line: 3, character: 4}
+    - anchor: { line: 1, character: 8 }
+      active: { line: 1, character: 8 }
+    - anchor: { line: 3, character: 4 }
+      active: { line: 3, character: 4 }
diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeEveryItem.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeEveryItem.yml
index d5025ff1a0..3954444201 100644
--- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeEveryItem.yml
+++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeEveryItem.yml
@@ -8,27 +8,27 @@ command:
       type: primitive
       modifiers:
         - type: everyScope
-          scopeType: {type: collectionItem}
+          scopeType: { type: collectionItem }
   usePrePhraseSnapshot: true
 initialState:
   documentContents: |+
     foo = [ "a" a/b/c "b" ];
 
   selections:
-    - anchor: {line: 0, character: 9}
-      active: {line: 0, character: 9}
+    - anchor: { line: 0, character: 9 }
+      active: { line: 0, character: 9 }
   marks: {}
 finalState:
   documentContents: |+
     foo = [    ];
 
   selections:
-    - anchor: {line: 0, character: 8}
-      active: {line: 0, character: 8}
-    - anchor: {line: 0, character: 9}
-      active: {line: 0, character: 9}
-    - anchor: {line: 0, character: 10}
-      active: {line: 0, character: 10}
+    - anchor: { line: 0, character: 8 }
+      active: { line: 0, character: 8 }
+    - anchor: { line: 0, character: 9 }
+      active: { line: 0, character: 9 }
+    - anchor: { line: 0, character: 10 }
+      active: { line: 0, character: 10 }
 ide:
   messages:
-    - {type: error, id: BaseTreeSitterScopeHandler.allow-multiple}
+    - { type: error, id: BaseTreeSitterScopeHandler.allow-multiple }
diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeEveryMap.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeEveryMap.yml
index 7dfd216810..c38a981c75 100644
--- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeEveryMap.yml
+++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeEveryMap.yml
@@ -8,7 +8,7 @@ command:
       type: primitive
       modifiers:
         - type: everyScope
-          scopeType: {type: map}
+          scopeType: { type: map }
   usePrePhraseSnapshot: true
 initialState:
   documentContents: |
@@ -32,8 +32,8 @@ initialState:
         };
     }
   selections:
-    - anchor: {line: 13, character: 0}
-      active: {line: 13, character: 0}
+    - anchor: { line: 13, character: 0 }
+      active: { line: 13, character: 0 }
   marks: {}
 finalState:
   documentContents: |
@@ -46,11 +46,11 @@ finalState:
         a = ;
     }
   selections:
-    - anchor: {line: 1, character: 8}
-      active: {line: 1, character: 8}
-    - anchor: {line: 2, character: 8}
-      active: {line: 2, character: 8}
-    - anchor: {line: 4, character: 8}
-      active: {line: 4, character: 8}
-    - anchor: {line: 6, character: 8}
-      active: {line: 6, character: 8}
+    - anchor: { line: 1, character: 8 }
+      active: { line: 1, character: 8 }
+    - anchor: { line: 2, character: 8 }
+      active: { line: 2, character: 8 }
+    - anchor: { line: 4, character: 8 }
+      active: { line: 4, character: 8 }
+    - anchor: { line: 6, character: 8 }
+      active: { line: 6, character: 8 }
diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeEveryMap2.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeEveryMap2.yml
index 8ee8cfa9d2..ffcf9f8b7f 100644
--- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeEveryMap2.yml
+++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeEveryMap2.yml
@@ -8,7 +8,7 @@ command:
       type: primitive
       modifiers:
         - type: everyScope
-          scopeType: {type: map}
+          scopeType: { type: map }
   usePrePhraseSnapshot: true
 initialState:
   documentContents: |
@@ -32,8 +32,8 @@ initialState:
         };
     }
   selections:
-    - anchor: {line: 10, character: 0}
-      active: {line: 10, character: 0}
+    - anchor: { line: 10, character: 0 }
+      active: { line: 10, character: 0 }
   marks: {}
 finalState:
   documentContents: |
@@ -56,7 +56,7 @@ finalState:
         };
     }
   selections:
-    - anchor: {line: 8, character: 12}
-      active: {line: 8, character: 12}
-    - anchor: {line: 10, character: 12}
-      active: {line: 10, character: 12}
+    - anchor: { line: 8, character: 12 }
+      active: { line: 8, character: 12 }
+    - anchor: { line: 10, character: 12 }
+      active: { line: 10, character: 12 }
diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeFunkName.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeFunkName.yml
index b261b56a97..89c65b849d 100644
--- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeFunkName.yml
+++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeFunkName.yml
@@ -8,7 +8,7 @@ command:
       type: primitive
       modifiers:
         - type: containingScope
-          scopeType: {type: functionName}
+          scopeType: { type: functionName }
   usePrePhraseSnapshot: true
 initialState:
   documentContents: |
@@ -16,8 +16,8 @@ initialState:
       x = a: b: a + b;
     }
   selections:
-    - anchor: {line: 1, character: 16}
-      active: {line: 1, character: 16}
+    - anchor: { line: 1, character: 16 }
+      active: { line: 1, character: 16 }
   marks: {}
 finalState:
   documentContents: |
@@ -25,5 +25,5 @@ finalState:
        = a: b: a + b;
     }
   selections:
-    - anchor: {line: 1, character: 2}
-      active: {line: 1, character: 2}
+    - anchor: { line: 1, character: 2 }
+      active: { line: 1, character: 2 }
diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeIfState.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeIfState.yml
index 98fee9d53c..a42cbbd38f 100644
--- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeIfState.yml
+++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeIfState.yml
@@ -8,7 +8,7 @@ command:
       type: primitive
       modifiers:
         - type: containingScope
-          scopeType: {type: ifStatement}
+          scopeType: { type: ifStatement }
   usePrePhraseSnapshot: true
 initialState:
   documentContents: |
@@ -16,8 +16,8 @@ initialState:
       key = if a then b else c;
     }
   selections:
-    - anchor: {line: 1, character: 24}
-      active: {line: 1, character: 24}
+    - anchor: { line: 1, character: 24 }
+      active: { line: 1, character: 24 }
   marks: {}
 finalState:
   documentContents: |
@@ -25,5 +25,5 @@ finalState:
       key = ;
     }
   selections:
-    - anchor: {line: 1, character: 8}
-      active: {line: 1, character: 8}
+    - anchor: { line: 1, character: 8 }
+      active: { line: 1, character: 8 }
diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeInsideFunk.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeInsideFunk.yml
index e9bf3612a0..761054fc81 100644
--- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeInsideFunk.yml
+++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeInsideFunk.yml
@@ -7,9 +7,9 @@ command:
     target:
       type: primitive
       modifiers:
-        - {type: interiorOnly}
+        - { type: interiorOnly }
         - type: containingScope
-          scopeType: {type: namedFunction}
+          scopeType: { type: namedFunction }
   usePrePhraseSnapshot: true
 initialState:
   documentContents: |
@@ -17,8 +17,8 @@ initialState:
       x = a: b: a + b;
     }
   selections:
-    - anchor: {line: 1, character: 16}
-      active: {line: 1, character: 16}
+    - anchor: { line: 1, character: 16 }
+      active: { line: 1, character: 16 }
   marks: {}
 finalState:
   documentContents: |
@@ -26,5 +26,5 @@ finalState:
       x = ;
     }
   selections:
-    - anchor: {line: 1, character: 6}
-      active: {line: 1, character: 6}
+    - anchor: { line: 1, character: 6 }
+      active: { line: 1, character: 6 }
diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeInsideLambda.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeInsideLambda.yml
index e9536e8c9b..f7ff318bb6 100644
--- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeInsideLambda.yml
+++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeInsideLambda.yml
@@ -7,9 +7,9 @@ command:
     target:
       type: primitive
       modifiers:
-        - {type: interiorOnly}
+        - { type: interiorOnly }
         - type: containingScope
-          scopeType: {type: anonymousFunction}
+          scopeType: { type: anonymousFunction }
   usePrePhraseSnapshot: true
 initialState:
   documentContents: |-
@@ -17,8 +17,8 @@ initialState:
       x = a: b: a + b;
     }
   selections:
-    - anchor: {line: 1, character: 9}
-      active: {line: 1, character: 9}
+    - anchor: { line: 1, character: 9 }
+      active: { line: 1, character: 9 }
   marks: {}
 finalState:
   documentContents: |-
@@ -26,5 +26,5 @@ finalState:
       x = a: b: ;
     }
   selections:
-    - anchor: {line: 1, character: 12}
-      active: {line: 1, character: 12}
+    - anchor: { line: 1, character: 12 }
+      active: { line: 1, character: 12 }
diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeInsideList.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeInsideList.yml
index b2f60ffa97..e244bfe1a1 100644
--- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeInsideList.yml
+++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeInsideList.yml
@@ -7,18 +7,18 @@ command:
     target:
       type: primitive
       modifiers:
-        - {type: interiorOnly}
+        - { type: interiorOnly }
         - type: containingScope
-          scopeType: {type: list}
+          scopeType: { type: list }
   usePrePhraseSnapshot: true
 initialState:
-  documentContents: "{ d = [ \"1\" 2 ]; }"
+  documentContents: '{ d = [ "1" 2 ]; }'
   selections:
-    - anchor: {line: 0, character: 10}
-      active: {line: 0, character: 10}
+    - anchor: { line: 0, character: 10 }
+      active: { line: 0, character: 10 }
   marks: {}
 finalState:
   documentContents: "{ d = [  ]; }"
   selections:
-    - anchor: {line: 0, character: 8}
-      active: {line: 0, character: 8}
+    - anchor: { line: 0, character: 8 }
+      active: { line: 0, character: 8 }
diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeItem.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeItem.yml
index 7704c4c9a1..d99c7625c8 100644
--- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeItem.yml
+++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeItem.yml
@@ -8,7 +8,7 @@ command:
       type: primitive
       modifiers:
         - type: containingScope
-          scopeType: {type: collectionItem}
+          scopeType: { type: collectionItem }
   usePrePhraseSnapshot: true
 initialState:
   documentContents: |
@@ -16,8 +16,8 @@ initialState:
       foo = [ "a" a/b/c "b" ];
     }
   selections:
-    - anchor: {line: 1, character: 17}
-      active: {line: 1, character: 17}
+    - anchor: { line: 1, character: 17 }
+      active: { line: 1, character: 17 }
   marks: {}
 finalState:
   documentContents: |
@@ -25,5 +25,5 @@ finalState:
       foo = [ "a"  "b" ];
     }
   selections:
-    - anchor: {line: 1, character: 14}
-      active: {line: 1, character: 14}
+    - anchor: { line: 1, character: 14 }
+      active: { line: 1, character: 14 }
diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeItem2.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeItem2.yml
index 63833638ab..2824b94622 100644
--- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeItem2.yml
+++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeItem2.yml
@@ -8,7 +8,7 @@ command:
       type: primitive
       modifiers:
         - type: containingScope
-          scopeType: {type: collectionItem}
+          scopeType: { type: collectionItem }
   usePrePhraseSnapshot: true
 initialState:
   documentContents: |
@@ -19,17 +19,17 @@ initialState:
               ];
     }
   selections:
-    - anchor: {line: 2, character: 10}
-      active: {line: 2, character: 10}
+    - anchor: { line: 2, character: 10 }
+      active: { line: 2, character: 10 }
   marks: {}
 finalState:
   documentContents: |
     {
       bar = [ A
-              
+
               C # foo
               ];
     }
   selections:
-    - anchor: {line: 2, character: 10}
-      active: {line: 2, character: 10}
+    - anchor: { line: 2, character: 10 }
+      active: { line: 2, character: 10 }
diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeKey.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeKey.yml
index 68d31d581d..b1002acb83 100644
--- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeKey.yml
+++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeKey.yml
@@ -8,7 +8,7 @@ command:
       type: primitive
       modifiers:
         - type: containingScope
-          scopeType: {type: collectionKey}
+          scopeType: { type: collectionKey }
   usePrePhraseSnapshot: true
 initialState:
   documentContents: |
@@ -16,8 +16,8 @@ initialState:
       foo = { x = 1; y = 2; };
     }
   selections:
-    - anchor: {line: 1, character: 14}
-      active: {line: 1, character: 14}
+    - anchor: { line: 1, character: 14 }
+      active: { line: 1, character: 14 }
   marks: {}
 finalState:
   documentContents: |
@@ -25,5 +25,5 @@ finalState:
       foo = {  = 1; y = 2; };
     }
   selections:
-    - anchor: {line: 1, character: 10}
-      active: {line: 1, character: 10}
+    - anchor: { line: 1, character: 10 }
+      active: { line: 1, character: 10 }
diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeKeyPlex.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeKeyPlex.yml
index 2aa27a4ed0..422d25d675 100644
--- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeKeyPlex.yml
+++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeKeyPlex.yml
@@ -6,10 +6,10 @@ command:
     name: clearAndSetSelection
     target:
       type: primitive
-      mark: {type: decoratedSymbol, symbolColor: default, character: x}
+      mark: { type: decoratedSymbol, symbolColor: default, character: x }
       modifiers:
         - type: containingScope
-          scopeType: {type: collectionKey}
+          scopeType: { type: collectionKey }
   usePrePhraseSnapshot: true
 initialState:
   documentContents: |
@@ -17,17 +17,17 @@ initialState:
       foo = { x = 1; y = 2; };
     }
   selections:
-    - anchor: {line: 1, character: 12}
-      active: {line: 1, character: 12}
+    - anchor: { line: 1, character: 12 }
+      active: { line: 1, character: 12 }
   marks:
     default.x:
-      start: {line: 1, character: 10}
-      end: {line: 1, character: 11}
+      start: { line: 1, character: 10 }
+      end: { line: 1, character: 11 }
 finalState:
   documentContents: |
     {
       foo = {  = 1; y = 2; };
     }
   selections:
-    - anchor: {line: 1, character: 10}
-      active: {line: 1, character: 10}
+    - anchor: { line: 1, character: 10 }
+      active: { line: 1, character: 10 }
diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeList.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeList.yml
index 61d901246c..e72bc2c433 100644
--- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeList.yml
+++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeList.yml
@@ -8,7 +8,7 @@ command:
       type: primitive
       modifiers:
         - type: containingScope
-          scopeType: {type: list}
+          scopeType: { type: list }
   usePrePhraseSnapshot: true
 initialState:
   documentContents: |
@@ -16,8 +16,8 @@ initialState:
       foo = [ "a" a/b/c "b" ];
     }
   selections:
-    - anchor: {line: 1, character: 23}
-      active: {line: 1, character: 23}
+    - anchor: { line: 1, character: 23 }
+      active: { line: 1, character: 23 }
   marks: {}
 finalState:
   documentContents: |
@@ -25,5 +25,5 @@ finalState:
       foo = ;
     }
   selections:
-    - anchor: {line: 1, character: 8}
-      active: {line: 1, character: 8}
+    - anchor: { line: 1, character: 8 }
+      active: { line: 1, character: 8 }
diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeListGreenAir.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeListGreenAir.yml
index b8e3fa9302..dc58c1c6fb 100644
--- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeListGreenAir.yml
+++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeListGreenAir.yml
@@ -6,10 +6,10 @@ command:
     name: clearAndSetSelection
     target:
       type: primitive
-      mark: {type: decoratedSymbol, symbolColor: green, character: a}
+      mark: { type: decoratedSymbol, symbolColor: green, character: a }
       modifiers:
         - type: containingScope
-          scopeType: {type: list}
+          scopeType: { type: list }
   usePrePhraseSnapshot: true
 initialState:
   documentContents: |
@@ -20,17 +20,17 @@ initialState:
               ];
     }
   selections:
-    - anchor: {line: 0, character: 1}
-      active: {line: 0, character: 1}
+    - anchor: { line: 0, character: 1 }
+      active: { line: 0, character: 1 }
   marks:
     green.a:
-      start: {line: 1, character: 10}
-      end: {line: 1, character: 11}
+      start: { line: 1, character: 10 }
+      end: { line: 1, character: 11 }
 finalState:
   documentContents: |
     {
       bar = ;
     }
   selections:
-    - anchor: {line: 1, character: 8}
-      active: {line: 1, character: 8}
+    - anchor: { line: 1, character: 8 }
+      active: { line: 1, character: 8 }
diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeMap.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeMap.yml
index bec1c27961..0c059d1c3f 100644
--- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeMap.yml
+++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeMap.yml
@@ -8,7 +8,7 @@ command:
       type: primitive
       modifiers:
         - type: containingScope
-          scopeType: {type: map}
+          scopeType: { type: map }
   usePrePhraseSnapshot: true
 initialState:
   documentContents: |
@@ -20,8 +20,8 @@ initialState:
         };
     }
   selections:
-    - anchor: {line: 1, character: 12}
-      active: {line: 1, character: 12}
+    - anchor: { line: 1, character: 12 }
+      active: { line: 1, character: 12 }
   marks: {}
 finalState:
   documentContents: |
@@ -33,5 +33,5 @@ finalState:
         };
     }
   selections:
-    - anchor: {line: 1, character: 8}
-      active: {line: 1, character: 8}
+    - anchor: { line: 1, character: 8 }
+      active: { line: 1, character: 8 }
diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeMap2.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeMap2.yml
index 1b5f5998ae..f7517b2711 100644
--- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeMap2.yml
+++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeMap2.yml
@@ -8,7 +8,7 @@ command:
       type: primitive
       modifiers:
         - type: containingScope
-          scopeType: {type: map}
+          scopeType: { type: map }
   usePrePhraseSnapshot: true
 initialState:
   documentContents: |
@@ -20,8 +20,8 @@ initialState:
         };
     }
   selections:
-    - anchor: {line: 3, character: 12}
-      active: {line: 3, character: 12}
+    - anchor: { line: 3, character: 12 }
+      active: { line: 3, character: 12 }
   marks: {}
 finalState:
   documentContents: |
@@ -30,5 +30,5 @@ finalState:
         a = ;
     }
   selections:
-    - anchor: {line: 2, character: 8}
-      active: {line: 2, character: 8}
+    - anchor: { line: 2, character: 8 }
+      active: { line: 2, character: 8 }
diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeMap3.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeMap3.yml
index 37abcceb61..5c51e136a7 100644
--- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeMap3.yml
+++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeMap3.yml
@@ -8,7 +8,7 @@ command:
       type: primitive
       modifiers:
         - type: containingScope
-          scopeType: {type: map}
+          scopeType: { type: map }
   usePrePhraseSnapshot: true
 initialState:
   documentContents: |
@@ -20,12 +20,12 @@ initialState:
         };
     }
   selections:
-    - anchor: {line: 6, character: 0}
-      active: {line: 6, character: 0}
+    - anchor: { line: 6, character: 0 }
+      active: { line: 6, character: 0 }
   marks: {}
 finalState:
   documentContents: |+
 
   selections:
-    - anchor: {line: 0, character: 0}
-      active: {line: 0, character: 0}
+    - anchor: { line: 0, character: 0 }
+      active: { line: 0, character: 0 }
diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeMap4.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeMap4.yml
index 5ecc01809e..c7f7bef354 100644
--- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeMap4.yml
+++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeMap4.yml
@@ -8,7 +8,7 @@ command:
       type: primitive
       modifiers:
         - type: containingScope
-          scopeType: {type: map}
+          scopeType: { type: map }
   usePrePhraseSnapshot: true
 initialState:
   documentContents: |
@@ -19,8 +19,8 @@ initialState:
         };
     }
   selections:
-    - anchor: {line: 2, character: 5}
-      active: {line: 2, character: 5}
+    - anchor: { line: 2, character: 5 }
+      active: { line: 2, character: 5 }
   marks: {}
 finalState:
   documentContents: |
@@ -28,5 +28,5 @@ finalState:
         a = ;
     }
   selections:
-    - anchor: {line: 1, character: 8}
-      active: {line: 1, character: 8}
+    - anchor: { line: 1, character: 8 }
+      active: { line: 1, character: 8 }
diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeName.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeName.yml
index 09ce494977..3b5d465cd9 100644
--- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeName.yml
+++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeName.yml
@@ -8,7 +8,7 @@ command:
       type: primitive
       modifiers:
         - type: containingScope
-          scopeType: {type: name}
+          scopeType: { type: name }
   usePrePhraseSnapshot: true
 initialState:
   documentContents: |
@@ -23,8 +23,8 @@ initialState:
 
     }
   selections:
-    - anchor: {line: 1, character: 11}
-      active: {line: 1, character: 11}
+    - anchor: { line: 1, character: 11 }
+      active: { line: 1, character: 11 }
   marks: {}
 finalState:
   documentContents: |
@@ -39,5 +39,5 @@ finalState:
 
     }
   selections:
-    - anchor: {line: 1, character: 2}
-      active: {line: 1, character: 2}
+    - anchor: { line: 1, character: 2 }
+      active: { line: 1, character: 2 }
diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeName2.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeName2.yml
index d3180e4574..654c92b3c7 100644
--- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeName2.yml
+++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeName2.yml
@@ -8,7 +8,7 @@ command:
       type: primitive
       modifiers:
         - type: containingScope
-          scopeType: {type: name}
+          scopeType: { type: name }
   usePrePhraseSnapshot: true
 initialState:
   documentContents: |
@@ -23,8 +23,8 @@ initialState:
 
     }
   selections:
-    - anchor: {line: 2, character: 10}
-      active: {line: 2, character: 10}
+    - anchor: { line: 2, character: 10 }
+      active: { line: 2, character: 10 }
   marks: {}
 finalState:
   documentContents: |
@@ -39,5 +39,5 @@ finalState:
 
     }
   selections:
-    - anchor: {line: 2, character: 4}
-      active: {line: 2, character: 4}
+    - anchor: { line: 2, character: 4 }
+      active: { line: 2, character: 4 }
diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeRound.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeRound.yml
index 1b91d72438..f59b1831af 100644
--- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeRound.yml
+++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeRound.yml
@@ -8,22 +8,22 @@ command:
       type: primitive
       modifiers:
         - type: containingScope
-          scopeType: {type: surroundingPair, delimiter: parentheses}
+          scopeType: { type: surroundingPair, delimiter: parentheses }
   usePrePhraseSnapshot: true
 initialState:
   documentContents: |
     {
-      foo = 1; # Test (round) 
+      foo = 1; # Test (round)
     }
   selections:
-    - anchor: {line: 1, character: 23}
-      active: {line: 1, character: 23}
+    - anchor: { line: 1, character: 23 }
+      active: { line: 1, character: 23 }
   marks: {}
 finalState:
   documentContents: |
     {
-      foo = 1; # Test  
+      foo = 1; # Test
     }
   selections:
-    - anchor: {line: 1, character: 18}
-      active: {line: 1, character: 18}
+    - anchor: { line: 1, character: 18 }
+      active: { line: 1, character: 18 }
diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeRound2.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeRound2.yml
index dc2fc91835..5dfdd92c9a 100644
--- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeRound2.yml
+++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeRound2.yml
@@ -8,7 +8,7 @@ command:
       type: primitive
       modifiers:
         - type: containingScope
-          scopeType: {type: surroundingPair, delimiter: parentheses}
+          scopeType: { type: surroundingPair, delimiter: parentheses }
   usePrePhraseSnapshot: true
 initialState:
   documentContents: |
@@ -16,8 +16,8 @@ initialState:
       b = ''two single (quote) string'';
     }
   selections:
-    - anchor: {line: 1, character: 22}
-      active: {line: 1, character: 22}
+    - anchor: { line: 1, character: 22 }
+      active: { line: 1, character: 22 }
   marks: {}
 finalState:
   documentContents: |
@@ -25,5 +25,5 @@ finalState:
       b = ''two single  string'';
     }
   selections:
-    - anchor: {line: 1, character: 19}
-      active: {line: 1, character: 19}
+    - anchor: { line: 1, character: 19 }
+      active: { line: 1, character: 19 }
diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeState.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeState.yml
index 75a78b12b5..699cd3ad72 100644
--- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeState.yml
+++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeState.yml
@@ -8,7 +8,7 @@ command:
       type: primitive
       modifiers:
         - type: containingScope
-          scopeType: {type: statement}
+          scopeType: { type: statement }
   usePrePhraseSnapshot: true
 initialState:
   documentContents: |
@@ -16,8 +16,8 @@ initialState:
     with lib;
     { foo = libthing; }
   selections:
-    - anchor: {line: 1, character: 4}
-      active: {line: 1, character: 4}
+    - anchor: { line: 1, character: 4 }
+      active: { line: 1, character: 4 }
   marks: {}
 finalState:
   documentContents: |
@@ -25,5 +25,5 @@ finalState:
 
     { foo = libthing; }
   selections:
-    - anchor: {line: 1, character: 0}
-      active: {line: 1, character: 0}
+    - anchor: { line: 1, character: 0 }
+      active: { line: 1, character: 0 }
diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeState2.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeState2.yml
index 19b895ef50..7985542b63 100644
--- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeState2.yml
+++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeState2.yml
@@ -8,7 +8,7 @@ command:
       type: primitive
       modifiers:
         - type: containingScope
-          scopeType: {type: statement}
+          scopeType: { type: statement }
   usePrePhraseSnapshot: true
 initialState:
   documentContents: |-
@@ -17,15 +17,15 @@ initialState:
         assert b -> c != null;
         {foo = a;}
   selections:
-    - anchor: {line: 1, character: 9}
-      active: {line: 1, character: 9}
+    - anchor: { line: 1, character: 9 }
+      active: { line: 1, character: 9 }
   marks: {}
 finalState:
   documentContents: |-
     { a ? false, b ? false , c ? null }:
-        
+
         assert b -> c != null;
         {foo = a;}
   selections:
-    - anchor: {line: 1, character: 4}
-      active: {line: 1, character: 4}
+    - anchor: { line: 1, character: 4 }
+      active: { line: 1, character: 4 }
diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeState3.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeState3.yml
index 3dadba90a8..252b7a6aa2 100644
--- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeState3.yml
+++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeState3.yml
@@ -8,14 +8,14 @@ command:
       type: primitive
       modifiers:
         - type: containingScope
-          scopeType: {type: statement}
+          scopeType: { type: statement }
   usePrePhraseSnapshot: true
 initialState:
   documentContents: |-
     { a ? false, b ? false , c ? null }:
         {foo = assert b != false; a;}
   selections:
-    - anchor: {line: 1, character: 19}
-      active: {line: 1, character: 19}
+    - anchor: { line: 1, character: 19 }
+      active: { line: 1, character: 19 }
   marks: {}
-thrownError: {name: NoContainingScopeError}
+thrownError: { name: NoContainingScopeError }
diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeString.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeString.yml
index fdd53b2b5e..6618232fa1 100644
--- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeString.yml
+++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeString.yml
@@ -8,7 +8,7 @@ command:
       type: primitive
       modifiers:
         - type: containingScope
-          scopeType: {type: surroundingPair, delimiter: string}
+          scopeType: { type: surroundingPair, delimiter: string }
   usePrePhraseSnapshot: true
 initialState:
   documentContents: |
@@ -16,8 +16,8 @@ initialState:
       a = "double quoted string";
     }
   selections:
-    - anchor: {line: 1, character: 25}
-      active: {line: 1, character: 25}
+    - anchor: { line: 1, character: 25 }
+      active: { line: 1, character: 25 }
   marks: {}
 finalState:
   documentContents: |
@@ -25,5 +25,5 @@ finalState:
       a = ;
     }
   selections:
-    - anchor: {line: 1, character: 6}
-      active: {line: 1, character: 6}
+    - anchor: { line: 1, character: 6 }
+      active: { line: 1, character: 6 }
diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeString2.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeString2.yml
index 1946648da9..e46e50d4d1 100644
--- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeString2.yml
+++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeString2.yml
@@ -8,7 +8,7 @@ command:
       type: primitive
       modifiers:
         - type: containingScope
-          scopeType: {type: surroundingPair, delimiter: string}
+          scopeType: { type: surroundingPair, delimiter: string }
   usePrePhraseSnapshot: true
 initialState:
   documentContents: |
@@ -16,8 +16,8 @@ initialState:
       b = ''two single (quote) string'';
     }
   selections:
-    - anchor: {line: 1, character: 16}
-      active: {line: 1, character: 16}
+    - anchor: { line: 1, character: 16 }
+      active: { line: 1, character: 16 }
   marks: {}
 finalState:
   documentContents: |
@@ -25,5 +25,5 @@ finalState:
       b = ;
     }
   selections:
-    - anchor: {line: 1, character: 6}
-      active: {line: 1, character: 6}
+    - anchor: { line: 1, character: 6 }
+      active: { line: 1, character: 6 }
diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeString3.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeString3.yml
index 6a8b624629..d77a3530b3 100644
--- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeString3.yml
+++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeString3.yml
@@ -8,7 +8,7 @@ command:
       type: primitive
       modifiers:
         - type: containingScope
-          scopeType: {type: surroundingPair, delimiter: string}
+          scopeType: { type: surroundingPair, delimiter: string }
   usePrePhraseSnapshot: true
 initialState:
   documentContents: |
@@ -18,8 +18,8 @@ initialState:
         quote string'';
     }
   selections:
-    - anchor: {line: 3, character: 4}
-      active: {line: 3, character: 4}
+    - anchor: { line: 3, character: 4 }
+      active: { line: 3, character: 4 }
   marks: {}
 finalState:
   documentContents: |
@@ -27,5 +27,5 @@ finalState:
       c = ;
     }
   selections:
-    - anchor: {line: 1, character: 6}
-      active: {line: 1, character: 6}
+    - anchor: { line: 1, character: 6 }
+      active: { line: 1, character: 6 }
diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeValue.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeValue.yml
index 731bb106b9..442671bd42 100644
--- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeValue.yml
+++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeValue.yml
@@ -8,7 +8,7 @@ command:
       type: primitive
       modifiers:
         - type: containingScope
-          scopeType: {type: value}
+          scopeType: { type: value }
   usePrePhraseSnapshot: true
 initialState:
   documentContents: |
@@ -23,8 +23,8 @@ initialState:
 
     }
   selections:
-    - anchor: {line: 1, character: 7}
-      active: {line: 1, character: 7}
+    - anchor: { line: 1, character: 7 }
+      active: { line: 1, character: 7 }
   marks: {}
 finalState:
   documentContents: |
@@ -33,5 +33,5 @@ finalState:
 
     }
   selections:
-    - anchor: {line: 1, character: 8}
-      active: {line: 1, character: 8}
+    - anchor: { line: 1, character: 8 }
+      active: { line: 1, character: 8 }
diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeValue2.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeValue2.yml
index e9663d11c2..f124b2cfd1 100644
--- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeValue2.yml
+++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeValue2.yml
@@ -8,7 +8,7 @@ command:
       type: primitive
       modifiers:
         - type: containingScope
-          scopeType: {type: value}
+          scopeType: { type: value }
   usePrePhraseSnapshot: true
 initialState:
   documentContents: |
@@ -23,8 +23,8 @@ initialState:
 
     }
   selections:
-    - anchor: {line: 2, character: 4}
-      active: {line: 2, character: 4}
+    - anchor: { line: 2, character: 4 }
+      active: { line: 2, character: 4 }
   marks: {}
 finalState:
   documentContents: |
@@ -39,5 +39,5 @@ finalState:
 
     }
   selections:
-    - anchor: {line: 2, character: 8}
-      active: {line: 2, character: 8}
+    - anchor: { line: 2, character: 8 }
+      active: { line: 2, character: 8 }
diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeValue3.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeValue3.yml
index d0bcd459c5..459baf7855 100644
--- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeValue3.yml
+++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeValue3.yml
@@ -8,7 +8,7 @@ command:
       type: primitive
       modifiers:
         - type: containingScope
-          scopeType: {type: value}
+          scopeType: { type: value }
   usePrePhraseSnapshot: true
 initialState:
   documentContents: |
@@ -23,8 +23,8 @@ initialState:
 
     }
   selections:
-    - anchor: {line: 5, character: 5}
-      active: {line: 5, character: 5}
+    - anchor: { line: 5, character: 5 }
+      active: { line: 5, character: 5 }
   marks: {}
 finalState:
   documentContents: |
@@ -33,5 +33,5 @@ finalState:
 
     }
   selections:
-    - anchor: {line: 1, character: 8}
-      active: {line: 1, character: 8}
+    - anchor: { line: 1, character: 8 }
+      active: { line: 1, character: 8 }
diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeValue4.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeValue4.yml
index 96381cc208..3b32ab9205 100644
--- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeValue4.yml
+++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/changeValue4.yml
@@ -8,7 +8,7 @@ command:
       type: primitive
       modifiers:
         - type: containingScope
-          scopeType: {type: value}
+          scopeType: { type: value }
   usePrePhraseSnapshot: true
 initialState:
   documentContents: |
@@ -23,8 +23,8 @@ initialState:
 
     }
   selections:
-    - anchor: {line: 6, character: 13}
-      active: {line: 6, character: 13}
+    - anchor: { line: 6, character: 13 }
+      active: { line: 6, character: 13 }
   marks: {}
 finalState:
   documentContents: |
@@ -39,5 +39,5 @@ finalState:
 
     }
   selections:
-    - anchor: {line: 6, character: 15}
-      active: {line: 6, character: 15}
+    - anchor: { line: 6, character: 15 }
+      active: { line: 6, character: 15 }
diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/chuckArg.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/chuckArg.yml
index 8634cb326b..f2ec633b6d 100644
--- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/chuckArg.yml
+++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/chuckArg.yml
@@ -8,7 +8,7 @@ command:
       type: primitive
       modifiers:
         - type: containingScope
-          scopeType: {type: argumentOrParameter}
+          scopeType: { type: argumentOrParameter }
   usePrePhraseSnapshot: true
 initialState:
   documentContents: |
@@ -16,8 +16,8 @@ initialState:
       d = "Interpolated ${value1} string ${value2}";
     }
   selections:
-    - anchor: {line: 1, character: 26}
-      active: {line: 1, character: 26}
+    - anchor: { line: 1, character: 26 }
+      active: { line: 1, character: 26 }
   marks: {}
 finalState:
   documentContents: |
@@ -25,5 +25,5 @@ finalState:
       d = "Interpolated string ${value2}";
     }
   selections:
-    - anchor: {line: 1, character: 20}
-      active: {line: 1, character: 20}
+    - anchor: { line: 1, character: 20 }
+      active: { line: 1, character: 20 }
diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/chuckInsideMap.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/chuckInsideMap.yml
index 09a4a8a806..64a10e11f7 100644
--- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/chuckInsideMap.yml
+++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/chuckInsideMap.yml
@@ -7,26 +7,26 @@ command:
     target:
       type: primitive
       modifiers:
-        - {type: interiorOnly}
+        - { type: interiorOnly }
         - type: containingScope
-          scopeType: {type: map}
+          scopeType: { type: map }
   usePrePhraseSnapshot: true
 initialState:
   documentContents: |
     {
-      foo = if a 
+      foo = if a
       then b
       else c;
     }
   selections:
-    - anchor: {line: 3, character: 9}
-      active: {line: 3, character: 9}
+    - anchor: { line: 3, character: 9 }
+      active: { line: 3, character: 9 }
   marks: {}
 finalState:
   documentContents: |
     {
-      
+
     }
   selections:
-    - anchor: {line: 1, character: 2}
-      active: {line: 1, character: 2}
+    - anchor: { line: 1, character: 2 }
+      active: { line: 1, character: 2 }
diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/chuckKey.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/chuckKey.yml
index 28fbc93192..14aeb31640 100644
--- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/chuckKey.yml
+++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/chuckKey.yml
@@ -8,7 +8,7 @@ command:
       type: primitive
       modifiers:
         - type: containingScope
-          scopeType: {type: collectionKey}
+          scopeType: { type: collectionKey }
   usePrePhraseSnapshot: true
 initialState:
   documentContents: |
@@ -16,8 +16,8 @@ initialState:
       d = "Interpolated ${value1} string ${value2}";
     }
   selections:
-    - anchor: {line: 1, character: 6}
-      active: {line: 1, character: 6}
+    - anchor: { line: 1, character: 6 }
+      active: { line: 1, character: 6 }
   marks: {}
 finalState:
   documentContents: |
@@ -25,5 +25,5 @@ finalState:
       "Interpolated ${value1} string ${value2}";
     }
   selections:
-    - anchor: {line: 1, character: 2}
-      active: {line: 1, character: 2}
+    - anchor: { line: 1, character: 2 }
+      active: { line: 1, character: 2 }
diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/chuckValue.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/chuckValue.yml
index ee4902d806..8916e8198b 100644
--- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/chuckValue.yml
+++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/chuckValue.yml
@@ -8,7 +8,7 @@ command:
       type: primitive
       modifiers:
         - type: containingScope
-          scopeType: {type: value}
+          scopeType: { type: value }
   usePrePhraseSnapshot: true
 initialState:
   documentContents: |
@@ -23,8 +23,8 @@ initialState:
 
     }
   selections:
-    - anchor: {line: 1, character: 7}
-      active: {line: 1, character: 7}
+    - anchor: { line: 1, character: 7 }
+      active: { line: 1, character: 7 }
   marks: {}
 finalState:
   documentContents: |
@@ -33,5 +33,5 @@ finalState:
 
     }
   selections:
-    - anchor: {line: 1, character: 5}
-      active: {line: 1, character: 5}
+    - anchor: { line: 1, character: 5 }
+      active: { line: 1, character: 5 }
diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/chuckValue2.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/chuckValue2.yml
index 03c0e75286..6a07d078b4 100644
--- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/chuckValue2.yml
+++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/nix/chuckValue2.yml
@@ -8,7 +8,7 @@ command:
       type: primitive
       modifiers:
         - type: containingScope
-          scopeType: {type: value}
+          scopeType: { type: value }
   usePrePhraseSnapshot: true
 initialState:
   documentContents: |
@@ -23,8 +23,8 @@ initialState:
 
     }
   selections:
-    - anchor: {line: 2, character: 4}
-      active: {line: 2, character: 4}
+    - anchor: { line: 2, character: 4 }
+      active: { line: 2, character: 4 }
   marks: {}
 finalState:
   documentContents: |
@@ -39,5 +39,5 @@ finalState:
 
     }
   selections:
-    - anchor: {line: 2, character: 4}
-      active: {line: 2, character: 4}
+    - anchor: { line: 2, character: 4 }
+      active: { line: 2, character: 4 }

From f402bd4472f1fc4c27a80a0abf65b8b1282d45e4 Mon Sep 17 00:00:00 2001
From: fidgetingbits <fidgetingbits@users.noreply.github.com>
Date: Mon, 17 Jun 2024 12:33:08 +0800
Subject: [PATCH 16/16] fix merge screwup

---
 .../surroundingPair/{getDelimiterMaps.ts => delimiterMaps.ts}   | 0
 .../src/processTargets/modifiers/surroundingPair/index.ts       | 2 +-
 2 files changed, 1 insertion(+), 1 deletion(-)
 rename packages/cursorless-engine/src/processTargets/modifiers/surroundingPair/{getDelimiterMaps.ts => delimiterMaps.ts} (100%)

diff --git a/packages/cursorless-engine/src/processTargets/modifiers/surroundingPair/getDelimiterMaps.ts b/packages/cursorless-engine/src/processTargets/modifiers/surroundingPair/delimiterMaps.ts
similarity index 100%
rename from packages/cursorless-engine/src/processTargets/modifiers/surroundingPair/getDelimiterMaps.ts
rename to packages/cursorless-engine/src/processTargets/modifiers/surroundingPair/delimiterMaps.ts
diff --git a/packages/cursorless-engine/src/processTargets/modifiers/surroundingPair/index.ts b/packages/cursorless-engine/src/processTargets/modifiers/surroundingPair/index.ts
index d92c16848f..f81ef8ebab 100644
--- a/packages/cursorless-engine/src/processTargets/modifiers/surroundingPair/index.ts
+++ b/packages/cursorless-engine/src/processTargets/modifiers/surroundingPair/index.ts
@@ -7,7 +7,7 @@ import { LanguageDefinitions } from "../../../languages/LanguageDefinitions";
 import { Target } from "../../../typings/target.types";
 import { SurroundingPairTarget } from "../../targets";
 import { getContainingScopeTarget } from "../getContainingScopeTarget";
-import { complexDelimiterMap } from "./getDelimiterMaps";
+import { complexDelimiterMap } from "./delimiterMaps";
 import { SurroundingPairInfo } from "./extractSelectionFromSurroundingPairOffsets";
 import { findSurroundingPairParseTreeBased } from "./findSurroundingPairParseTreeBased";
 import { findSurroundingPairTextBased } from "./findSurroundingPairTextBased";