Merge "ChangeData: add Javadoc"
diff --git a/Documentation/config-gerrit.txt b/Documentation/config-gerrit.txt
index 618621b..91fe24b 100644
--- a/Documentation/config-gerrit.txt
+++ b/Documentation/config-gerrit.txt
@@ -3397,7 +3397,7 @@
 config is backwards compatible with what the default was before the config
 was added.
 
-[[event.comment-added.publishPatchSetLevelComment]][event.comment-added.publishPatchSetLevelComment::
+[[event.comment-added.publishPatchSetLevelComment]]event.comment-added.publishPatchSetLevelComment::
 +
 Add patch set level comment as event comment. Without this option, patch set
 level comment will not be included in the event comment attribute. Given that
diff --git a/Documentation/dev-bazel.txt b/Documentation/dev-bazel.txt
index 0ae038a..9d29980 100644
--- a/Documentation/dev-bazel.txt
+++ b/Documentation/dev-bazel.txt
@@ -376,6 +376,11 @@
 * server
 * ssh
 
+Bazel itself supports a multitude of ways to
+link:https://docs.bazel.build/versions/master/guide.html#specifying-targets-to-build[specify targets,role=external,window=_blank]
+for fine-grained test selection that can be combined with many of the examples
+above.
+
 [[elasticsearch]]
 === Elasticsearch
 
diff --git a/Documentation/dev-crafting-changes.txt b/Documentation/dev-crafting-changes.txt
index 4e248dd..7935f30 100644
--- a/Documentation/dev-crafting-changes.txt
+++ b/Documentation/dev-crafting-changes.txt
@@ -147,7 +147,7 @@
 link:https://github.com/google/google-java-format[`google-java-format`,role=external,window=_blank]
 tool (version 1.7), and to format Bazel BUILD, WORKSPACE and .bzl files the
 link:https://github.com/bazelbuild/buildtools/tree/master/buildifier[`buildifier`,role=external,window=_blank]
-tool (version 3.5.0). Unused dependencies are found and removed using the
+tool (version 4.0.0). Unused dependencies are found and removed using the
 link:https://github.com/bazelbuild/buildtools/tree/master/unused_deps[`unused_deps`,role=external,window=_blank]
 build tool, a sibling of `buildifier`.
 
@@ -160,6 +160,9 @@
 wrapper script. If you run your own copy, please use the same version,
 as there may be slight differences between versions.
 
+[[code-rules]]
+== Code Rules
+=== Final
 When to use `final` modifier and when not (in new code):
 
 Always:
@@ -181,6 +184,12 @@
   be removed
   * method parameters: similar to local variables
 
+=== Optional / Nullable
+Recommended:
+
+  * Optionals in arguments are discouraged (use @Nullable instead)
+  * Return types should be objects or Optionals of objects, but not null/nullable
+
 [[code-organization]]
 == Code Organization
 
diff --git a/Documentation/rest-api-changes.txt b/Documentation/rest-api-changes.txt
index 096b068..3a5fb96 100644
--- a/Documentation/rest-api-changes.txt
+++ b/Documentation/rest-api-changes.txt
@@ -564,6 +564,12 @@
   }
 ----
 
+Historical state of the change can be retrieved by specifying the
+`meta=SHA1` parameter. This will use a historical NoteDb snapshot to
+populate ChangeInfo. If the SHA1 is not reachable as a NoteDb state,
+status code 412 is returned.
+
+
 [[get-change-detail]]
 === Get Change Detail
 --
@@ -6467,6 +6473,8 @@
 Only set if link:#current-revision[the current revision] is requested
 (in which case it will only contain a key for the current revision) or
 if link:#all-revisions[all revisions] are requested.
+|`meta_rev_id`           |optional|
+The SHA1 of the NoteDb meta ref.
 |`tracking_ids`       |optional|
 A list of link:#tracking-id-info[TrackingIdInfo] entities describing
 references to external tracking systems. Only set if
diff --git a/Documentation/rest-api-projects.txt b/Documentation/rest-api-projects.txt
index 677fc6d..49bc7e5 100644
--- a/Documentation/rest-api-projects.txt
+++ b/Documentation/rest-api-projects.txt
@@ -4211,8 +4211,10 @@
 |`branches`                  |optional|
 A list of branches that should be initially created. +
 For the branch names the `refs/heads/` prefix can be omitted. +
-The first entry of the list will be the default branch (ie. the target +
-of the `HEAD` symbolic ref).
+The first entry of the list will be the default branch (ie. the target
+of the `HEAD` symbolic ref). +
+Branches in the Gerrit internal ref space are not allowed, such as
+refs/groups/, refs/changes/, etc...
 |`owners`                    |optional|
 A list of groups that should be assigned as project owner. +
 Each group in the list must be specified as
diff --git a/Documentation/user-porting-comments.txt b/Documentation/user-porting-comments.txt
index b85e592..8b6c005 100644
--- a/Documentation/user-porting-comments.txt
+++ b/Documentation/user-porting-comments.txt
@@ -4,7 +4,7 @@
 
 Comments in Gerrit are associated with a patchset. When a new patchset is uploaded, the comments are lost since they are not associated with the newer patchset.
 
-image::images/user-porting-comment-original-comment.png["Comment left on Patchset 15", align="center"]
+image::images/user-porting-comments-original-comment.png["Comment left on Patchset 15", align="center"]
 
 To solve this issue, Gerrit now has “Ported Comments”. These are comments that were left on an older patchset displayed on all the newer patchsets uploaded. For example, a comment left on Patchset 6 will be ported over to Patchset 7, 8 and all subsequent patchsets that are uploaded, not just the latest patchset.
 
@@ -18,7 +18,7 @@
 
 Resolved comments are not ported over.
 
-image::images/user-porting-comment-ported-comment.png["Comment ported over to patchset 16", align="center"]
+image::images/user-porting-comments-ported-comment.png["Comment ported over to patchset 16", align="center"]
 
 ## Interaction
 
diff --git a/WORKSPACE b/WORKSPACE
index 0767907..c24d4f9 100644
--- a/WORKSPACE
+++ b/WORKSPACE
@@ -34,11 +34,11 @@
 
 http_archive(
     name = "bazel_toolchains",
-    sha256 = "726b5423e1c7a3866a3a6d68e7123b4a955e9fcbe912a51e0f737e6dab1d0af2",
-    strip_prefix = "bazel-toolchains-3.1.0",
+    sha256 = "1adf7a8e9901287c644dcf9ca08dd8d67a69df94bedbd57a841490a84dc1e9ed",
+    strip_prefix = "bazel-toolchains-5.0.0",
     urls = [
-        "https://mirror.bazel.build/github.com/bazelbuild/bazel-toolchains/releases/download/3.1.0/bazel-toolchains-3.1.0.tar.gz",
-        "https://github.com/bazelbuild/bazel-toolchains/releases/download/3.1.0/bazel-toolchains-3.1.0.tar.gz",
+        "https://mirror.bazel.build/github.com/bazelbuild/bazel-toolchains/archive/v5.0.0.tar.gz",
+        "https://github.com/bazelbuild/bazel-toolchains/archive/v5.0.0.tar.gz",
     ],
 )
 
@@ -842,55 +842,55 @@
     sha1 = "7e060dd5b19431e6d198e91ff670644372f60fbd",
 )
 
-JETTY_VERS = "9.4.35.v20201120"
+JETTY_VERS = "9.4.36.v20210114"
 
 maven_jar(
     name = "jetty-servlet",
     artifact = "org.eclipse.jetty:jetty-servlet:" + JETTY_VERS,
-    sha1 = "3e61bcb471e1bfc545ce866cbbe33c3aedeec9b1",
+    sha1 = "b189e52a5ee55ae172e4e99e29c5c314f5daf4b9",
 )
 
 maven_jar(
     name = "jetty-security",
     artifact = "org.eclipse.jetty:jetty-security:" + JETTY_VERS,
-    sha1 = "80dc2f422789c78315de76d289b7a5b36c3232d5",
+    sha1 = "42030d6ed7dfc0f75818cde0adcf738efc477574",
 )
 
 maven_jar(
     name = "jetty-server",
     artifact = "org.eclipse.jetty:jetty-server:" + JETTY_VERS,
-    sha1 = "513502352fd689d4730b2935421b990ada8cc818",
+    sha1 = "88a7d342974aadca658e7386e8d0fcc5c0788f41",
 )
 
 maven_jar(
     name = "jetty-jmx",
     artifact = "org.eclipse.jetty:jetty-jmx:" + JETTY_VERS,
-    sha1 = "38812031940a466d626ab5d9bbbd9d5d39e9f735",
+    sha1 = "bb3847eabe085832aeaedd30e872b40931632e54",
 )
 
 maven_jar(
     name = "jetty-http",
     artifact = "org.eclipse.jetty:jetty-http:" + JETTY_VERS,
-    sha1 = "45d35131a35a1e76991682174421e8cdf765fb9f",
+    sha1 = "1eee89a55e04ff94df0f85d95200fc48acb43d86",
 )
 
 maven_jar(
     name = "jetty-io",
     artifact = "org.eclipse.jetty:jetty-io:" + JETTY_VERS,
-    sha1 = "eb9460700b99b71ecd82a53697f5ff99f69b9e1c",
+    sha1 = "84a8faf9031eb45a5a2ddb7681e22c483d81ab3a",
 )
 
 maven_jar(
     name = "jetty-util",
     artifact = "org.eclipse.jetty:jetty-util:" + JETTY_VERS,
-    sha1 = "ef61b83f9715c3b5355b633d9f01d2834f908ece",
+    sha1 = "925257fbcca6b501a25252c7447dbedb021f7404",
 )
 
 maven_jar(
     name = "jetty-util-ajax",
     artifact = "org.eclipse.jetty:jetty-util-ajax:" + JETTY_VERS,
-    sha1 = "ebbb43912c6423bedb3458e44aee28eeb4d66f27",
-    src_sha1 = "b3acea974a17493afb125a9dfbe783870ce1d2f9",
+    sha1 = "2f478130c21787073facb64d7242e06f94980c60",
+    src_sha1 = "7153d7ca38878d971fd90992c303bb7719ba7a21",
 )
 
 maven_jar(
diff --git a/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/CreateChange-body.json b/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/CreateChange-body.json
index 23bf26c..8babac8 100644
--- a/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/CreateChange-body.json
+++ b/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/CreateChange-body.json
@@ -1,5 +1,5 @@
 {
   "project": "${project}",
-  "branch": "master",
+  "branch": "${branch}",
   "subject": "Change"
 }
diff --git a/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/SubmitChangeInBranch.json b/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/SubmitChangeInBranch.json
new file mode 100644
index 0000000..301c65b
--- /dev/null
+++ b/e2e-tests/src/test/resources/data/com/google/gerrit/scenarios/SubmitChangeInBranch.json
@@ -0,0 +1,5 @@
+[
+  {
+    "url": "HTTP_SCHEME://HOSTNAME:HTTP_PORT/a/changes/"
+  }
+]
diff --git a/e2e-tests/src/test/scala/com/google/gerrit/scenarios/ApproveChange.scala b/e2e-tests/src/test/scala/com/google/gerrit/scenarios/ApproveChange.scala
index 8ae69d7..5e4f671 100644
--- a/e2e-tests/src/test/scala/com/google/gerrit/scenarios/ApproveChange.scala
+++ b/e2e-tests/src/test/scala/com/google/gerrit/scenarios/ApproveChange.scala
@@ -19,10 +19,15 @@
 import io.gatling.core.structure.ScenarioBuilder
 import io.gatling.http.Predef.http
 
+import scala.collection.mutable
+
 class ApproveChange extends GerritSimulation {
-  private val data: FeederBuilder = jsonFile(resource).convert(keys).queue
+  private val data: FeederBuilder = jsonFile(resource).convert(keys).circular
+  private var numbersCopy: mutable.Queue[Int] = mutable.Queue[Int]()
   private var createChange: Option[CreateChange] = None
 
+  override def relativeRuntimeWeight = 10
+
   def this(createChange: CreateChange) {
     this()
     this.createChange = Some(createChange)
@@ -32,7 +37,10 @@
       .feed(data)
       .exec(session => {
         if (createChange.nonEmpty) {
-          session.set("number", createChange.get.number)
+          if (numbersCopy.isEmpty) {
+            numbersCopy = createChange.get.numbers.clone()
+          }
+          session.set("number", numbersCopy.dequeue())
         } else {
           session
         }
diff --git a/e2e-tests/src/test/scala/com/google/gerrit/scenarios/CreateBranch.scala b/e2e-tests/src/test/scala/com/google/gerrit/scenarios/CreateBranch.scala
index ccde633..3630a7a 100644
--- a/e2e-tests/src/test/scala/com/google/gerrit/scenarios/CreateBranch.scala
+++ b/e2e-tests/src/test/scala/com/google/gerrit/scenarios/CreateBranch.scala
@@ -19,18 +19,27 @@
 import io.gatling.core.structure.ScenarioBuilder
 import io.gatling.http.Predef._
 
+import scala.collection.mutable
 import scala.concurrent.duration._
 
 class CreateBranch extends ProjectSimulation {
   private val data: FeederBuilder = jsonFile(resource).convert(keys).circular
   private val branchIdKey = "branchId"
   private var counter = 0
+  var branches: mutable.Queue[String] = mutable.Queue[String]()
 
-  private val test: ScenarioBuilder = scenario(uniqueName)
+  def this(projectName: String) {
+    this()
+    this.projectName = projectName
+  }
+
+  val test: ScenarioBuilder = scenario(uniqueName)
       .feed(data)
       .exec(session => {
         counter += 1
-        session.set(branchIdKey, "branch-" + counter)
+        val branchId = "branch-" + counter
+        branches += branchId
+        session.set(branchIdKey, branchId)
       })
       .exec(http(uniqueName)
           .post("${url}${" + branchIdKey + "}")
diff --git a/e2e-tests/src/test/scala/com/google/gerrit/scenarios/CreateChange.scala b/e2e-tests/src/test/scala/com/google/gerrit/scenarios/CreateChange.scala
index b0063cb..b28edb5 100644
--- a/e2e-tests/src/test/scala/com/google/gerrit/scenarios/CreateChange.scala
+++ b/e2e-tests/src/test/scala/com/google/gerrit/scenarios/CreateChange.scala
@@ -25,18 +25,37 @@
 class CreateChange extends ProjectSimulation {
   private val data: FeederBuilder = jsonFile(resource).convert(keys).circular
   private val numberKey = "_number"
+  private val weightPerUser = 0.1
+  private var createBranch: Option[CreateBranch] = None
+  private var branchesCopy: mutable.Queue[String] = mutable.Queue[String]()
   var number = 0
   var numbers: mutable.Queue[Int] = mutable.Queue[Int]()
 
-  override def relativeRuntimeWeight = 2
+  override def relativeRuntimeWeight: Int = 2 + (numberOfUsers * weightPerUser).toInt
 
   def this(projectName: String) {
     this()
     this.projectName = projectName
   }
 
+  def this(projectName: String, createBranch: CreateBranch) {
+    this()
+    this.projectName = projectName
+    this.createBranch = Some(createBranch)
+  }
+
   val test: ScenarioBuilder = scenario(uniqueName)
       .feed(data)
+      .exec(session => {
+        var branchId = "master"
+        if (createBranch.nonEmpty) {
+          if (branchesCopy.isEmpty) {
+            branchesCopy = createBranch.get.branches.clone()
+          }
+          branchId = branchesCopy.dequeue()
+        }
+        session.set("branch", branchId)
+      })
       .exec(httpRequest
           .body(ElFileBody(body)).asJson
           .check(regex("\"" + numberKey + "\":(\\d+),").saveAs(numberKey)))
diff --git a/e2e-tests/src/test/scala/com/google/gerrit/scenarios/SubmitChangeInBranch.scala b/e2e-tests/src/test/scala/com/google/gerrit/scenarios/SubmitChangeInBranch.scala
new file mode 100644
index 0000000..1b88503
--- /dev/null
+++ b/e2e-tests/src/test/scala/com/google/gerrit/scenarios/SubmitChangeInBranch.scala
@@ -0,0 +1,74 @@
+// Copyright (C) 2021 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.scenarios
+
+import io.gatling.core.Predef.{atOnceUsers, _}
+import io.gatling.core.feeder.FeederBuilder
+import io.gatling.core.structure.ScenarioBuilder
+import io.gatling.http.Predef.http
+
+import scala.collection.mutable
+import scala.concurrent.duration._
+
+class SubmitChangeInBranch extends GerritSimulation {
+  private val data: FeederBuilder = jsonFile(resource).convert(keys).circular
+  private var changesCopy: mutable.Queue[Int] = mutable.Queue[Int]()
+  private val projectName = className
+
+  override def relativeRuntimeWeight = 10
+
+  private val test: ScenarioBuilder = scenario(uniqueName)
+      .feed(data)
+      .exec(session => {
+        if (changesCopy.isEmpty) {
+          changesCopy = createChange.numbers.clone()
+        }
+        session.set("number", changesCopy.dequeue())
+      })
+      .exec(http(uniqueName).post("${url}${number}/submit"))
+
+  private val createProject = new CreateProject(projectName)
+  private val createBranch = new CreateBranch(projectName)
+  private val createChange = new CreateChange(projectName, createBranch)
+  private val approveChange = new ApproveChange(createChange)
+  private val deleteProject = new DeleteProject(projectName)
+
+  setUp(
+    createProject.test.inject(
+      nothingFor(stepWaitTime(createProject) seconds),
+      atOnceUsers(single)
+    ),
+    createBranch.test.inject(
+      nothingFor(stepWaitTime(createBranch) seconds),
+      atOnceUsers(numberOfUsers)
+    ),
+    createChange.test.inject(
+      nothingFor(stepWaitTime(createChange) seconds),
+      atOnceUsers(numberOfUsers)
+    ),
+    approveChange.test.inject(
+      nothingFor(stepWaitTime(approveChange) seconds),
+      atOnceUsers(numberOfUsers)
+    ),
+    test.inject(
+      nothingFor(stepWaitTime(this) seconds),
+      atOnceUsers(numberOfUsers)
+    ),
+    deleteProject.test.inject(
+      nothingFor(stepWaitTime(deleteProject) seconds),
+      atOnceUsers(single)
+    ),
+  ).protocols(httpProtocol)
+}
diff --git a/java/com/google/gerrit/auth/BUILD b/java/com/google/gerrit/auth/BUILD
index a390e14..609ec8a 100644
--- a/java/com/google/gerrit/auth/BUILD
+++ b/java/com/google/gerrit/auth/BUILD
@@ -20,8 +20,6 @@
         "//java/com/google/gerrit/common:server",
         "//java/com/google/gerrit/entities",
         "//java/com/google/gerrit/extensions:api",
-        "//java/com/google/gerrit/git",
-        "//java/com/google/gerrit/jgit",
         "//java/com/google/gerrit/proto",
         "//java/com/google/gerrit/server",
         "//java/com/google/gerrit/server/cache/serialize",
diff --git a/java/com/google/gerrit/common/data/testing/BUILD b/java/com/google/gerrit/common/data/testing/BUILD
index b9ec30b..d39d05c 100644
--- a/java/com/google/gerrit/common/data/testing/BUILD
+++ b/java/com/google/gerrit/common/data/testing/BUILD
@@ -6,7 +6,6 @@
     srcs = glob(["**/*.java"]),
     visibility = ["//visibility:public"],
     deps = [
-        "//java/com/google/gerrit/common:server",
         "//java/com/google/gerrit/entities",
         "//lib/truth",
     ],
diff --git a/java/com/google/gerrit/elasticsearch/ElasticChangeIndex.java b/java/com/google/gerrit/elasticsearch/ElasticChangeIndex.java
index 969ffa5..162654d 100644
--- a/java/com/google/gerrit/elasticsearch/ElasticChangeIndex.java
+++ b/java/com/google/gerrit/elasticsearch/ElasticChangeIndex.java
@@ -37,6 +37,7 @@
 import com.google.gerrit.exceptions.StorageException;
 import com.google.gerrit.index.FieldDef;
 import com.google.gerrit.index.QueryOptions;
+import com.google.gerrit.index.RefState;
 import com.google.gerrit.index.Schema;
 import com.google.gerrit.index.query.DataSource;
 import com.google.gerrit.index.query.Predicate;
@@ -354,7 +355,7 @@
 
     // Ref-state.
     if (fields.contains(ChangeField.REF_STATE.getName())) {
-      cd.setRefStates(getByteArray(source, ChangeField.REF_STATE.getName()));
+      cd.setRefStates(RefState.parseStates(getByteArray(source, ChangeField.REF_STATE.getName())));
     }
 
     // Ref-state-pattern.
diff --git a/java/com/google/gerrit/elasticsearch/ElasticVersion.java b/java/com/google/gerrit/elasticsearch/ElasticVersion.java
index bba1577..5e72780 100644
--- a/java/com/google/gerrit/elasticsearch/ElasticVersion.java
+++ b/java/com/google/gerrit/elasticsearch/ElasticVersion.java
@@ -18,8 +18,6 @@
 import java.util.regex.Pattern;
 
 public enum ElasticVersion {
-  V7_2("7.2.*"),
-  V7_3("7.3.*"),
   V7_4("7.4.*"),
   V7_5("7.5.*"),
   V7_6("7.6.*"),
diff --git a/java/com/google/gerrit/extensions/common/ChangeInfo.java b/java/com/google/gerrit/extensions/common/ChangeInfo.java
index 7ed2f95..528efe3 100644
--- a/java/com/google/gerrit/extensions/common/ChangeInfo.java
+++ b/java/com/google/gerrit/extensions/common/ChangeInfo.java
@@ -70,6 +70,7 @@
   public String submissionId;
   public Integer cherryPickOfChange;
   public Integer cherryPickOfPatchSet;
+  public String metaRevId;
 
   /**
    * Whether the change contains conflicts.
diff --git a/java/com/google/gerrit/extensions/common/FileInfo.java b/java/com/google/gerrit/extensions/common/FileInfo.java
index 32c5bd5..510c2ad 100644
--- a/java/com/google/gerrit/extensions/common/FileInfo.java
+++ b/java/com/google/gerrit/extensions/common/FileInfo.java
@@ -34,8 +34,8 @@
           && Objects.equals(oldPath, fileInfo.oldPath)
           && Objects.equals(linesInserted, fileInfo.linesInserted)
           && Objects.equals(linesDeleted, fileInfo.linesDeleted)
-          && Objects.equals(sizeDelta, fileInfo.sizeDelta)
-          && Objects.equals(size, fileInfo.size);
+          && sizeDelta == fileInfo.sizeDelta
+          && size == fileInfo.size;
     }
     return false;
   }
diff --git a/java/com/google/gerrit/extensions/restapi/NeedsParams.java b/java/com/google/gerrit/extensions/restapi/NeedsParams.java
index c6e2151..bb4294f 100644
--- a/java/com/google/gerrit/extensions/restapi/NeedsParams.java
+++ b/java/com/google/gerrit/extensions/restapi/NeedsParams.java
@@ -19,7 +19,9 @@
 /**
  * Optional interface for {@link RestCollection}.
  *
- * <p>Collections that implement this interface can get to know about the request parameters.
+ * <p>Collections that implement this interface can get to know about the request parameters. The
+ * request parameters are passed only if the collection is the endpoint, e.g. {@code
+ * /changes/?q=abc} would trigger, but {@code /changes/100/?q=abc} does not.
  */
 public interface NeedsParams {
   /**
diff --git a/java/com/google/gerrit/index/RefState.java b/java/com/google/gerrit/index/RefState.java
index 956dcab..ed38de9 100644
--- a/java/com/google/gerrit/index/RefState.java
+++ b/java/com/google/gerrit/index/RefState.java
@@ -20,8 +20,7 @@
 
 import com.google.auto.value.AutoValue;
 import com.google.common.base.Splitter;
-import com.google.common.collect.MultimapBuilder;
-import com.google.common.collect.SetMultimap;
+import com.google.common.collect.ImmutableSetMultimap;
 import com.google.gerrit.common.Nullable;
 import com.google.gerrit.entities.Project;
 import com.google.gerrit.git.ObjectIds;
@@ -33,10 +32,10 @@
 
 @AutoValue
 public abstract class RefState {
-  public static SetMultimap<Project.NameKey, RefState> parseStates(Iterable<byte[]> states) {
+  public static ImmutableSetMultimap<Project.NameKey, RefState> parseStates(
+      Iterable<byte[]> states) {
     RefState.check(states != null, null);
-    SetMultimap<Project.NameKey, RefState> result =
-        MultimapBuilder.hashKeys().hashSetValues().build();
+    ImmutableSetMultimap.Builder<Project.NameKey, RefState> result = ImmutableSetMultimap.builder();
     for (byte[] b : states) {
       RefState.check(b != null, null);
       String s = new String(b, UTF_8);
@@ -44,7 +43,7 @@
       RefState.check(parts.size() == 3 && !parts.get(0).isEmpty() && !parts.get(1).isEmpty(), s);
       result.put(Project.nameKey(parts.get(0)), RefState.create(parts.get(1), parts.get(2)));
     }
-    return result;
+    return result.build();
   }
 
   public static RefState create(String ref, String sha) {
diff --git a/java/com/google/gerrit/lucene/LuceneChangeIndex.java b/java/com/google/gerrit/lucene/LuceneChangeIndex.java
index f4b9e69..c3d4440 100644
--- a/java/com/google/gerrit/lucene/LuceneChangeIndex.java
+++ b/java/com/google/gerrit/lucene/LuceneChangeIndex.java
@@ -49,6 +49,7 @@
 import com.google.gerrit.exceptions.StorageException;
 import com.google.gerrit.index.FieldDef;
 import com.google.gerrit.index.QueryOptions;
+import com.google.gerrit.index.RefState;
 import com.google.gerrit.index.Schema;
 import com.google.gerrit.index.query.FieldBundle;
 import com.google.gerrit.index.query.Predicate;
@@ -702,7 +703,7 @@
   }
 
   private void decodeRefStates(ListMultimap<String, IndexableField> doc, ChangeData cd) {
-    cd.setRefStates(copyAsBytes(doc.get(REF_STATE_FIELD)));
+    cd.setRefStates(RefState.parseStates(copyAsBytes(doc.get(REF_STATE_FIELD))));
   }
 
   private void decodeRefStatePatterns(ListMultimap<String, IndexableField> doc, ChangeData cd) {
diff --git a/java/com/google/gerrit/pgm/util/BUILD b/java/com/google/gerrit/pgm/util/BUILD
index f7c2b75..cfdd383 100644
--- a/java/com/google/gerrit/pgm/util/BUILD
+++ b/java/com/google/gerrit/pgm/util/BUILD
@@ -12,7 +12,6 @@
         "//java/com/google/gerrit/metrics",
         "//java/com/google/gerrit/metrics/dropwizard",
         "//java/com/google/gerrit/server",
-        "//java/com/google/gerrit/server/cache/h2",
         "//java/com/google/gerrit/server/cache/mem",
         "//java/com/google/gerrit/server/restapi",
         "//java/com/google/gerrit/server/schema",
diff --git a/java/com/google/gerrit/server/BUILD b/java/com/google/gerrit/server/BUILD
index 69e398d..9fa7456 100644
--- a/java/com/google/gerrit/server/BUILD
+++ b/java/com/google/gerrit/server/BUILD
@@ -62,7 +62,6 @@
         "//java/com/google/gerrit/server/util/git",
         "//java/com/google/gerrit/server/util/time",
         "//java/com/google/gerrit/util/cli",
-        "//java/com/google/gerrit/util/ssl",
         "//java/org/apache/commons/net",
         "//lib:args4j",
         "//lib:autolink",
@@ -144,7 +143,6 @@
     visibility = ["//visibility:public"],
     deps = [
         ":server",
-        "//java/com/google/gerrit/auth",
         "//java/com/google/gerrit/extensions:api",
         "//java/com/google/gerrit/server/git/receive",
         "//java/com/google/gerrit/server/logging",
diff --git a/java/com/google/gerrit/server/cache/serialize/entities/BUILD b/java/com/google/gerrit/server/cache/serialize/entities/BUILD
index cb8c4ae..55080e8 100644
--- a/java/com/google/gerrit/server/cache/serialize/entities/BUILD
+++ b/java/com/google/gerrit/server/cache/serialize/entities/BUILD
@@ -5,12 +5,8 @@
     srcs = glob(["*.java"]),
     visibility = ["//visibility:public"],
     deps = [
-        "//java/com/google/gerrit/common:annotations",
-        "//java/com/google/gerrit/common:server",
         "//java/com/google/gerrit/entities",
         "//java/com/google/gerrit/extensions:api",
-        "//java/com/google/gerrit/git",
-        "//java/com/google/gerrit/proto",
         "//java/com/google/gerrit/server/cache/serialize",
         "//lib:guava",
         "//lib:jgit",
diff --git a/java/com/google/gerrit/server/change/ChangeJson.java b/java/com/google/gerrit/server/change/ChangeJson.java
index f292245..c7aedb6 100644
--- a/java/com/google/gerrit/server/change/ChangeJson.java
+++ b/java/com/google/gerrit/server/change/ChangeJson.java
@@ -47,6 +47,7 @@
 import com.google.common.collect.Maps;
 import com.google.common.collect.Sets;
 import com.google.common.flogger.FluentLogger;
+import com.google.gerrit.common.Nullable;
 import com.google.gerrit.entities.Account;
 import com.google.gerrit.entities.Address;
 import com.google.gerrit.entities.Change;
@@ -54,6 +55,7 @@
 import com.google.gerrit.entities.PatchSet;
 import com.google.gerrit.entities.PatchSetApproval;
 import com.google.gerrit.entities.Project;
+import com.google.gerrit.entities.RefNames;
 import com.google.gerrit.entities.SubmitRecord;
 import com.google.gerrit.entities.SubmitRecord.Status;
 import com.google.gerrit.entities.SubmitRequirement;
@@ -75,6 +77,7 @@
 import com.google.gerrit.extensions.common.SubmitRequirementInfo;
 import com.google.gerrit.extensions.common.TrackingIdInfo;
 import com.google.gerrit.extensions.restapi.Url;
+import com.google.gerrit.index.RefState;
 import com.google.gerrit.index.query.QueryResult;
 import com.google.gerrit.metrics.Description;
 import com.google.gerrit.metrics.Description.Units;
@@ -119,6 +122,7 @@
 import java.util.Set;
 import java.util.stream.Collectors;
 import org.eclipse.jgit.lib.Config;
+import org.eclipse.jgit.lib.ObjectId;
 
 /**
  * Produces {@link ChangeInfo} (which is serialized to JSON afterwards) from {@link ChangeData}.
@@ -281,6 +285,11 @@
     return format(changeDataFactory.create(change));
   }
 
+  public ChangeInfo format(Change change, @Nullable ObjectId metaRevId) {
+    ChangeNotes notes = notesFactory.createChecked(change.getProject(), change.getId(), metaRevId);
+    return format(changeDataFactory.create(notes));
+  }
+
   public ChangeInfo format(ChangeData cd) {
     return format(cd, Optional.empty(), true, getPluginInfos(cd));
   }
@@ -323,9 +332,13 @@
   }
 
   public ChangeInfo format(Project.NameKey project, Change.Id id) {
+    return format(project, id, null);
+  }
+
+  public ChangeInfo format(Project.NameKey project, Change.Id id, @Nullable ObjectId metaRevId) {
     ChangeNotes notes;
     try {
-      notes = notesFactory.createChecked(project, id);
+      notes = notesFactory.createChecked(project, id, metaRevId);
     } catch (StorageException e) {
       if (!has(CHECK)) {
         throw e;
@@ -570,6 +583,15 @@
     out.totalCommentCount = cd.totalCommentCount();
     out.unresolvedCommentCount = cd.unresolvedCommentCount();
 
+    if (cd.getRefStates() != null) {
+      String metaName = RefNames.changeMetaRef(cd.getId());
+      Optional<RefState> metaState =
+          cd.getRefStates().values().stream().filter(r -> r.ref().equals(metaName)).findAny();
+
+      // metaState should always be there, but it doesn't hurt to be extra careful.
+      metaState.ifPresent(rs -> out.metaRevId = rs.id().getName());
+    }
+
     if (user.isIdentifiedUser()) {
       Collection<String> stars = cd.stars(user.getAccountId());
       out.starred = stars.contains(StarredChangesUtil.DEFAULT_LABEL) ? true : null;
diff --git a/java/com/google/gerrit/server/comment/CommentContextLoader.java b/java/com/google/gerrit/server/comment/CommentContextLoader.java
index 63bb8d0..c93f4b1 100644
--- a/java/com/google/gerrit/server/comment/CommentContextLoader.java
+++ b/java/com/google/gerrit/server/comment/CommentContextLoader.java
@@ -26,7 +26,6 @@
 import com.google.gerrit.entities.Comment;
 import com.google.gerrit.entities.CommentContext;
 import com.google.gerrit.entities.Project;
-import com.google.gerrit.exceptions.StorageException;
 import com.google.gerrit.extensions.common.ContextLineInfo;
 import com.google.gerrit.server.git.GitRepositoryManager;
 import com.google.gerrit.server.patch.ComparisonType;
@@ -158,12 +157,10 @@
 
   private static CommentContext createContext(Text src, Range commentRange, int contextPadding) {
     if (commentRange.start() < 1 || commentRange.end() - 1 > src.size()) {
-      throw new StorageException(
-          "Invalid comment range "
-              + commentRange
-              + ". Text only contains "
-              + src.size()
-              + " lines.");
+      // TODO(ghareeb): We should throw an exception in this case. See
+      // https://bugs.chromium.org/p/gerrit/issues/detail?id=14102 which is an example where the
+      // diff contains an extra line not in the original file.
+      return CommentContext.empty();
     }
     commentRange = adjustRange(commentRange, contextPadding, src.size());
     ImmutableMap.Builder<Integer, String> context =
diff --git a/java/com/google/gerrit/server/data/BUILD b/java/com/google/gerrit/server/data/BUILD
index c3dc672..1aaab96 100644
--- a/java/com/google/gerrit/server/data/BUILD
+++ b/java/com/google/gerrit/server/data/BUILD
@@ -9,7 +9,6 @@
     deps = [
         "//java/com/google/gerrit/entities",
         "//java/com/google/gerrit/extensions:api",
-        "//java/org/apache/commons/net",
         "//lib:gson",
     ],
 )
diff --git a/java/com/google/gerrit/server/git/TagSet.java b/java/com/google/gerrit/server/git/TagSet.java
index 43483bf..d6220a2 100644
--- a/java/com/google/gerrit/server/git/TagSet.java
+++ b/java/com/google/gerrit/server/git/TagSet.java
@@ -16,9 +16,9 @@
 
 import com.google.common.annotations.VisibleForTesting;
 import com.google.common.base.MoreObjects;
+import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Maps;
 import com.google.common.flogger.FluentLogger;
-import com.google.gerrit.entities.PatchSet;
 import com.google.gerrit.entities.Project;
 import com.google.gerrit.entities.RefNames;
 import com.google.gerrit.server.cache.proto.Cache.TagSetHolderProto.TagSetProto;
@@ -37,6 +37,7 @@
 import org.eclipse.jgit.lib.ObjectId;
 import org.eclipse.jgit.lib.ObjectIdOwnerMap;
 import org.eclipse.jgit.lib.Ref;
+import org.eclipse.jgit.lib.RefDatabase;
 import org.eclipse.jgit.lib.Repository;
 import org.eclipse.jgit.revwalk.RevCommit;
 import org.eclipse.jgit.revwalk.RevSort;
@@ -44,6 +45,12 @@
 
 class TagSet {
   private static final FluentLogger logger = FluentLogger.forEnclosingClass();
+  private static final ImmutableSet<String> SKIPPABLE_REF_PREFIXES =
+      ImmutableSet.of(
+          RefNames.REFS_CHANGES,
+          RefNames.REFS_CACHE_AUTOMERGE,
+          RefNames.REFS_DRAFT_COMMENTS,
+          RefNames.REFS_STARRED_CHANGES);
 
   private final Project.NameKey projectName;
   private final Map<String, CachedRef> refs;
@@ -179,7 +186,9 @@
 
     try (TagWalk rw = new TagWalk(git)) {
       rw.setRetainBody(false);
-      for (Ref ref : git.getRefDatabase().getRefs()) {
+      for (Ref ref :
+          git.getRefDatabase()
+              .getRefsByPrefixWithExclusions(RefDatabase.ALL, SKIPPABLE_REF_PREFIXES)) {
         if (skip(ref)) {
           continue;
 
@@ -365,9 +374,7 @@
   static boolean skip(Ref ref) {
     return ref.isSymbolic()
         || ref.getObjectId() == null
-        || PatchSet.isChangeRef(ref.getName())
-        || RefNames.isNoteDbMetaRef(ref.getName())
-        || ref.getName().startsWith(RefNames.REFS_CACHE_AUTOMERGE);
+        || SKIPPABLE_REF_PREFIXES.stream().anyMatch(p -> ref.getName().startsWith(p));
   }
 
   private static boolean isTag(Ref ref) {
diff --git a/java/com/google/gerrit/server/group/testing/BUILD b/java/com/google/gerrit/server/group/testing/BUILD
index fd61dff..77bb777 100644
--- a/java/com/google/gerrit/server/group/testing/BUILD
+++ b/java/com/google/gerrit/server/group/testing/BUILD
@@ -7,7 +7,6 @@
     testonly = True,
     srcs = glob(["*.java"]),
     deps = [
-        "//java/com/google/gerrit/common:server",
         "//java/com/google/gerrit/entities",
         "//java/com/google/gerrit/server",
         "//lib:guava",
diff --git a/java/com/google/gerrit/server/index/change/ChangeField.java b/java/com/google/gerrit/server/index/change/ChangeField.java
index 5f2525e..59600e0 100644
--- a/java/com/google/gerrit/server/index/change/ChangeField.java
+++ b/java/com/google/gerrit/server/index/change/ChangeField.java
@@ -57,7 +57,6 @@
 import com.google.gerrit.entities.converter.PatchSetProtoConverter;
 import com.google.gerrit.entities.converter.ProtoConverter;
 import com.google.gerrit.index.FieldDef;
-import com.google.gerrit.index.RefState;
 import com.google.gerrit.index.SchemaUtil;
 import com.google.gerrit.json.OutputFormat;
 import com.google.gerrit.proto.Protos;
@@ -66,9 +65,7 @@
 import com.google.gerrit.server.StarredChangesUtil;
 import com.google.gerrit.server.config.AllUsersName;
 import com.google.gerrit.server.index.change.StalenessChecker.RefStatePattern;
-import com.google.gerrit.server.notedb.ChangeNotes;
 import com.google.gerrit.server.notedb.ReviewerStateInternal;
-import com.google.gerrit.server.notedb.RobotCommentNotes;
 import com.google.gerrit.server.project.SubmitRuleOptions;
 import com.google.gerrit.server.query.change.ChangeData;
 import com.google.gerrit.server.query.change.ChangeQueryBuilder;
@@ -982,27 +979,9 @@
           .buildRepeatable(
               cd -> {
                 List<byte[]> result = new ArrayList<>();
-                Project.NameKey project = cd.change().getProject();
-
-                cd.editRefs()
-                    .values()
-                    .forEach(r -> result.add(RefState.of(r).toByteArray(project)));
-                cd.starRefs()
-                    .values()
-                    .forEach(r -> result.add(RefState.of(r.ref()).toByteArray(allUsers(cd))));
-
-                ChangeNotes notes = cd.notes();
-                result.add(
-                    RefState.create(notes.getRefName(), notes.getMetaId()).toByteArray(project));
-                notes.getRobotComments(); // Force loading robot comments.
-                RobotCommentNotes robotNotes = notes.getRobotCommentNotes();
-                result.add(
-                    RefState.create(robotNotes.getRefName(), robotNotes.getMetaId())
-                        .toByteArray(project));
-                cd.draftRefs()
-                    .values()
-                    .forEach(r -> result.add(RefState.of(r).toByteArray(allUsers(cd))));
-
+                cd.getRefStates()
+                    .entries()
+                    .forEach(e -> result.add(e.getValue().toByteArray(e.getKey())));
                 return result;
               });
 
diff --git a/java/com/google/gerrit/server/index/change/StalenessChecker.java b/java/com/google/gerrit/server/index/change/StalenessChecker.java
index 7e50104..ad5cc2b 100644
--- a/java/com/google/gerrit/server/index/change/StalenessChecker.java
+++ b/java/com/google/gerrit/server/index/change/StalenessChecker.java
@@ -93,7 +93,7 @@
       return StalenessCheckResult.stale("Document %s missing from index", id);
     }
     ChangeData cd = result.get();
-    return check(repoManager, id, parseStates(cd), parsePatterns(cd));
+    return check(repoManager, id, cd.getRefStates(), parsePatterns(cd));
   }
 
   /**
@@ -127,10 +127,6 @@
     return StalenessCheckResult.notStale();
   }
 
-  private SetMultimap<Project.NameKey, RefState> parseStates(ChangeData cd) {
-    return RefState.parseStates(cd.getRefStates());
-  }
-
   private ListMultimap<Project.NameKey, RefStatePattern> parsePatterns(ChangeData cd) {
     return parsePatterns(cd.getRefStatePatterns());
   }
diff --git a/java/com/google/gerrit/server/mail/send/AddKeySender.java b/java/com/google/gerrit/server/mail/send/AddKeySender.java
index 0d447ca..652766a 100644
--- a/java/com/google/gerrit/server/mail/send/AddKeySender.java
+++ b/java/com/google/gerrit/server/mail/send/AddKeySender.java
@@ -15,7 +15,6 @@
 package com.google.gerrit.server.mail.send;
 
 import com.google.common.base.Joiner;
-import com.google.gerrit.entities.Address;
 import com.google.gerrit.exceptions.EmailException;
 import com.google.gerrit.extensions.api.changes.RecipientType;
 import com.google.gerrit.server.IdentifiedUser;
@@ -68,7 +67,7 @@
     super.init();
     setHeader("Subject", String.format("[Gerrit Code Review] New %s Keys Added", getKeyType()));
     setMessageId(messageIdGenerator.fromAccountUpdate(user.getAccountId()));
-    add(RecipientType.TO, Address.create(getEmail()));
+    add(RecipientType.TO, user.getAccountId());
   }
 
   @Override
diff --git a/java/com/google/gerrit/server/mail/send/DeleteKeySender.java b/java/com/google/gerrit/server/mail/send/DeleteKeySender.java
index 46e7fd8..d6d306c 100644
--- a/java/com/google/gerrit/server/mail/send/DeleteKeySender.java
+++ b/java/com/google/gerrit/server/mail/send/DeleteKeySender.java
@@ -15,7 +15,6 @@
 package com.google.gerrit.server.mail.send;
 
 import com.google.common.base.Joiner;
-import com.google.gerrit.entities.Address;
 import com.google.gerrit.exceptions.EmailException;
 import com.google.gerrit.extensions.api.changes.RecipientType;
 import com.google.gerrit.server.IdentifiedUser;
@@ -71,7 +70,7 @@
     super.init();
     setHeader("Subject", String.format("[Gerrit Code Review] %s Keys Deleted", getKeyType()));
     setMessageId(messageIdGenerator.fromAccountUpdate(user.getAccountId()));
-    add(RecipientType.TO, Address.create(getEmail()));
+    add(RecipientType.TO, user.getAccountId());
   }
 
   @Override
diff --git a/java/com/google/gerrit/server/mail/send/HttpPasswordUpdateSender.java b/java/com/google/gerrit/server/mail/send/HttpPasswordUpdateSender.java
index c1c2f31..045c6a4 100644
--- a/java/com/google/gerrit/server/mail/send/HttpPasswordUpdateSender.java
+++ b/java/com/google/gerrit/server/mail/send/HttpPasswordUpdateSender.java
@@ -14,7 +14,6 @@
 
 package com.google.gerrit.server.mail.send;
 
-import com.google.gerrit.entities.Address;
 import com.google.gerrit.exceptions.EmailException;
 import com.google.gerrit.extensions.api.changes.RecipientType;
 import com.google.gerrit.server.IdentifiedUser;
@@ -51,7 +50,7 @@
     setMessageId(
         messageIdGenerator.fromReasonAccountIdAndTimestamp(
             "HTTP_password_change", user.getAccountId(), TimeUtil.now()));
-    add(RecipientType.TO, Address.create(getEmail()));
+    add(RecipientType.TO, user.getAccountId());
   }
 
   @Override
diff --git a/java/com/google/gerrit/server/mail/send/MailSoySauceProvider.java b/java/com/google/gerrit/server/mail/send/MailSoySauceProvider.java
index 1b58057..aade30f 100644
--- a/java/com/google/gerrit/server/mail/send/MailSoySauceProvider.java
+++ b/java/com/google/gerrit/server/mail/send/MailSoySauceProvider.java
@@ -113,6 +113,11 @@
 
   private void addTemplate(SoyFileSet.Builder builder, String resourcePath, String name)
       throws ProvisionException {
+    if (!resourcePath.endsWith("/")) {
+      resourcePath += "/";
+    }
+    String logicalPath = resourcePath + name;
+
     // Load as a file in the mail templates directory if present.
     Path tmpl = site.mail_dir.resolve(name);
     if (Files.isRegularFile(tmpl)) {
@@ -125,14 +130,11 @@
         throw new ProvisionException(
             "Failed to read template file " + tmpl.toAbsolutePath().toString(), err);
       }
-      builder.add(content, tmpl.toAbsolutePath().toString());
+      builder.add(content, logicalPath);
       return;
     }
 
     // Otherwise load the template as a resource.
-    if (!resourcePath.endsWith("/")) {
-      resourcePath += "/";
-    }
-    builder.add(Resources.getResource(resourcePath + name));
+    builder.add(Resources.getResource(logicalPath), logicalPath);
   }
 }
diff --git a/java/com/google/gerrit/server/notedb/AbstractChangeNotes.java b/java/com/google/gerrit/server/notedb/AbstractChangeNotes.java
index 9b5b4d4..a7c7757 100644
--- a/java/com/google/gerrit/server/notedb/AbstractChangeNotes.java
+++ b/java/com/google/gerrit/server/notedb/AbstractChangeNotes.java
@@ -37,6 +37,8 @@
 import org.eclipse.jgit.lib.ObjectId;
 import org.eclipse.jgit.lib.Ref;
 import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.jgit.revwalk.RevWalk;
 
 /** View of contents at a single ref related to some change. * */
 public abstract class AbstractChangeNotes<T> {
@@ -118,9 +120,14 @@
   private ObjectId revision;
   private boolean loaded;
 
-  protected AbstractChangeNotes(Args args, Change.Id changeId) {
+  protected AbstractChangeNotes(Args args, Change.Id changeId, @Nullable ObjectId metaSha1) {
     this.args = requireNonNull(args);
     this.changeId = requireNonNull(changeId);
+    this.revision = metaSha1;
+  }
+
+  protected AbstractChangeNotes(Args args, Change.Id changeId) {
+    this(args, changeId, null);
   }
 
   public Change.Id getChangeId() {
@@ -144,7 +151,7 @@
         Repository repo = args.repoManager.openRepository(getProjectName());
         // Call openHandle even if reading is disabled, to trigger
         // auto-rebuilding before this object may get passed to a ChangeUpdate.
-        LoadHandle handle = openHandle(repo)) {
+        LoadHandle handle = openHandle(repo, revision)) {
       revision = handle.id();
       onLoad(handle);
       loaded = true;
@@ -168,13 +175,17 @@
    * @param repo open repository.
    * @return handle for reading the entity.
    * @throws NoSuchChangeException change does not exist.
+   * @throws MissingMetaObjectException specified SHA1 isn't reachable from meta branch.
    * @throws IOException a repo-level error occurred.
    */
-  protected LoadHandle openHandle(Repository repo) throws NoSuchChangeException, IOException {
-    return openHandle(repo, readRef(repo));
-  }
+  protected LoadHandle openHandle(Repository repo, @Nullable ObjectId id)
+      throws NoSuchChangeException, IOException, MissingMetaObjectException {
+    if (id == null) {
+      id = readRef(repo);
+    } else {
+      verifyMetaId(repo, id);
+    }
 
-  protected LoadHandle openHandle(Repository repo, ObjectId id) {
     return new LoadHandle(repo, id);
   }
 
@@ -215,4 +226,20 @@
   protected final T self() {
     return (T) this;
   }
+
+  private void verifyMetaId(Repository repo, ObjectId id)
+      throws IOException, MissingMetaObjectException {
+    try (RevWalk rw = new RevWalk(repo)) {
+      Ref ref = repo.getRefDatabase().exactRef(getRefName());
+      RevCommit tip = rw.parseCommit(ref.getObjectId());
+      rw.markStart(tip);
+      for (RevCommit rev : rw) {
+        if (id.equals(rev)) {
+          return;
+        }
+      }
+    }
+
+    throw new MissingMetaObjectException(id.getName() + " not reachable from " + getRefName());
+  }
 }
diff --git a/java/com/google/gerrit/server/notedb/ChangeNotes.java b/java/com/google/gerrit/server/notedb/ChangeNotes.java
index cf09ff3..77d7bc0 100644
--- a/java/com/google/gerrit/server/notedb/ChangeNotes.java
+++ b/java/com/google/gerrit/server/notedb/ChangeNotes.java
@@ -114,9 +114,14 @@
       return createChecked(c.getProject(), c.getId());
     }
 
-    public ChangeNotes createChecked(Project.NameKey project, Change.Id changeId) {
+    public ChangeNotes createChecked(
+        Project.NameKey project, Change.Id changeId, @Nullable ObjectId metaRevId) {
       Change change = newChange(project, changeId);
-      return new ChangeNotes(args, change, true, null).load();
+      return new ChangeNotes(args, change, true, null, metaRevId).load();
+    }
+
+    public ChangeNotes createChecked(Project.NameKey project, Change.Id changeId) {
+      return createChecked(project, changeId, null);
     }
 
     public static Change newChange(Project.NameKey project, Change.Id changeId) {
@@ -333,14 +338,23 @@
   private ImmutableListMultimap<PatchSet.Id, PatchSetApproval> approvals;
   private ImmutableSet<Comment.Key> commentKeys;
 
-  @VisibleForTesting
-  public ChangeNotes(Args args, Change change, boolean shouldExist, @Nullable RefCache refs) {
-    super(args, change.getId());
+  public ChangeNotes(
+      Args args,
+      Change change,
+      boolean shouldExist,
+      @Nullable RefCache refs,
+      @Nullable ObjectId metaSha1) {
+    super(args, change.getId(), metaSha1);
     this.change = new Change(change);
     this.shouldExist = shouldExist;
     this.refs = refs;
   }
 
+  @VisibleForTesting
+  public ChangeNotes(Args args, Change change, boolean shouldExist, @Nullable RefCache refs) {
+    this(args, change, shouldExist, refs, null);
+  }
+
   public Change getChange() {
     return change;
   }
diff --git a/java/com/google/gerrit/server/notedb/MissingMetaObjectException.java b/java/com/google/gerrit/server/notedb/MissingMetaObjectException.java
new file mode 100644
index 0000000..ffe9fa8
--- /dev/null
+++ b/java/com/google/gerrit/server/notedb/MissingMetaObjectException.java
@@ -0,0 +1,22 @@
+// Copyright (C) 2021 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.server.notedb;
+
+/** Separate exception type to throw if requested meta SHA1 is not available. */
+public class MissingMetaObjectException extends RuntimeException {
+  MissingMetaObjectException(String msg) {
+    super(msg);
+  }
+}
diff --git a/java/com/google/gerrit/server/project/testing/BUILD b/java/com/google/gerrit/server/project/testing/BUILD
index 3112b5a..ab75ec7 100644
--- a/java/com/google/gerrit/server/project/testing/BUILD
+++ b/java/com/google/gerrit/server/project/testing/BUILD
@@ -5,8 +5,5 @@
     testonly = True,
     srcs = glob(["*.java"]),
     visibility = ["//visibility:public"],
-    deps = [
-        "//java/com/google/gerrit/common:server",
-        "//java/com/google/gerrit/entities",
-    ],
+    deps = ["//java/com/google/gerrit/entities"],
 )
diff --git a/java/com/google/gerrit/server/query/change/ChangeData.java b/java/com/google/gerrit/server/query/change/ChangeData.java
index 3c877e5..f047543 100644
--- a/java/com/google/gerrit/server/query/change/ChangeData.java
+++ b/java/com/google/gerrit/server/query/change/ChangeData.java
@@ -26,13 +26,16 @@
 import com.google.common.collect.ImmutableListMultimap;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.ImmutableSetMultimap;
 import com.google.common.collect.ImmutableSortedSet;
 import com.google.common.collect.Iterables;
 import com.google.common.collect.ListMultimap;
 import com.google.common.collect.Lists;
 import com.google.common.collect.Maps;
+import com.google.common.collect.SetMultimap;
 import com.google.common.primitives.Ints;
 import com.google.gerrit.common.Nullable;
+import com.google.gerrit.common.UsedAt;
 import com.google.gerrit.entities.Account;
 import com.google.gerrit.entities.AttentionSetUpdate;
 import com.google.gerrit.entities.Change;
@@ -43,6 +46,7 @@
 import com.google.gerrit.entities.PatchSet;
 import com.google.gerrit.entities.PatchSetApproval;
 import com.google.gerrit.entities.Project;
+import com.google.gerrit.entities.Project.NameKey;
 import com.google.gerrit.entities.RefNames;
 import com.google.gerrit.entities.RobotComment;
 import com.google.gerrit.entities.SubmitRecord;
@@ -50,6 +54,7 @@
 import com.google.gerrit.exceptions.StorageException;
 import com.google.gerrit.extensions.restapi.BadRequestException;
 import com.google.gerrit.extensions.restapi.ResourceConflictException;
+import com.google.gerrit.index.RefState;
 import com.google.gerrit.server.ApprovalsUtil;
 import com.google.gerrit.server.ChangeMessagesUtil;
 import com.google.gerrit.server.CommentsUtil;
@@ -69,6 +74,7 @@
 import com.google.gerrit.server.git.GitRepositoryManager;
 import com.google.gerrit.server.git.MergeUtil;
 import com.google.gerrit.server.notedb.ChangeNotes;
+import com.google.gerrit.server.notedb.RobotCommentNotes;
 import com.google.gerrit.server.patch.DiffSummary;
 import com.google.gerrit.server.patch.DiffSummaryKey;
 import com.google.gerrit.server.patch.PatchListCache;
@@ -315,7 +321,7 @@
   private Integer totalCommentCount;
   private LabelTypes labelTypes;
   private Optional<Timestamp> mergedOn;
-  private ImmutableList<byte[]> refStates;
+  private ImmutableSetMultimap<NameKey, RefState> refStates;
   private ImmutableList<byte[]> refStatePatterns;
 
   @Inject
@@ -601,6 +607,7 @@
       author = c.getAuthorIdent();
       committer = c.getCommitterIdent();
       parentCount = c.getParentCount();
+      merge = parentCount > 0;
     } catch (IOException e) {
       throw new StorageException(
           String.format(
@@ -962,7 +969,7 @@
         return null;
       }
     }
-    return parentCount > 1;
+    return merge;
   }
 
   public Set<Account.Id> editsByUser() {
@@ -1176,12 +1183,38 @@
     }
   }
 
-  public ImmutableList<byte[]> getRefStates() {
+  public SetMultimap<NameKey, RefState> getRefStates() {
+    if (refStates == null) {
+      if (!lazyLoad) {
+        return ImmutableSetMultimap.of();
+      }
+
+      ImmutableSetMultimap.Builder<NameKey, RefState> result = ImmutableSetMultimap.builder();
+      editRefs().values().forEach(r -> result.put(project, RefState.of(r)));
+      starRefs().values().forEach(r -> result.put(allUsersName, RefState.of(r.ref())));
+
+      // TODO: instantiating the notes is too much. We don't want to parse NoteDb, we just want the
+      // refs.
+      result.put(project, RefState.create(notes().getRefName(), notes().getMetaId()));
+      notes().getRobotComments(); // Force loading robot comments.
+      RobotCommentNotes robotNotes = notes().getRobotCommentNotes();
+      result.put(project, RefState.create(robotNotes.getRefName(), robotNotes.getMetaId()));
+      draftRefs().values().forEach(r -> result.put(allUsersName, RefState.of(r)));
+
+      refStates = result.build();
+    }
+
     return refStates;
   }
 
+  @UsedAt(UsedAt.Project.GOOGLE)
   public void setRefStates(Iterable<byte[]> refStates) {
-    this.refStates = ImmutableList.copyOf(refStates);
+    // TODO(hanwen): remove Google use, and drop this method.
+    setRefStates(RefState.parseStates(refStates));
+  }
+
+  public void setRefStates(ImmutableSetMultimap<Project.NameKey, RefState> refStates) {
+    this.refStates = refStates;
   }
 
   public ImmutableList<byte[]> getRefStatePatterns() {
diff --git a/java/com/google/gerrit/server/restapi/BUILD b/java/com/google/gerrit/server/restapi/BUILD
index 3cb0796..6d3e222 100644
--- a/java/com/google/gerrit/server/restapi/BUILD
+++ b/java/com/google/gerrit/server/restapi/BUILD
@@ -8,7 +8,6 @@
     name = "restapi",
     srcs = glob(["**/*.java"]),
     deps = [
-        "//java/com/google/gerrit/auth",
         "//java/com/google/gerrit/common:annotations",
         "//java/com/google/gerrit/common:server",
         "//java/com/google/gerrit/entities",
@@ -19,13 +18,11 @@
         "//java/com/google/gerrit/index:query_exception",
         "//java/com/google/gerrit/index/project",
         "//java/com/google/gerrit/json",
-        "//java/com/google/gerrit/mail",
         "//java/com/google/gerrit/metrics",
         "//java/com/google/gerrit/server",
         "//java/com/google/gerrit/server/ioutil",
         "//java/com/google/gerrit/server/logging",
         "//java/com/google/gerrit/server/util/time",
-        "//java/com/google/gerrit/util/cli",
         "//lib:args4j",
         "//lib:blame-cache",
         "//lib:gson",
diff --git a/java/com/google/gerrit/server/restapi/change/DeleteAssignee.java b/java/com/google/gerrit/server/restapi/change/DeleteAssignee.java
index 20fd675..842ed2a 100644
--- a/java/com/google/gerrit/server/restapi/change/DeleteAssignee.java
+++ b/java/com/google/gerrit/server/restapi/change/DeleteAssignee.java
@@ -93,10 +93,7 @@
       }
       IdentifiedUser deletedAssigneeUser = userFactory.create(currentAssigneeId);
       deletedAssignee = deletedAssigneeUser.state();
-      // noteDb
       update.removeAssignee();
-      // reviewDb
-      change.setAssignee(null);
       addMessage(ctx, update, deletedAssigneeUser);
       return true;
     }
diff --git a/java/com/google/gerrit/server/restapi/change/GetChange.java b/java/com/google/gerrit/server/restapi/change/GetChange.java
index f28d2b3..2f1c61e 100644
--- a/java/com/google/gerrit/server/restapi/change/GetChange.java
+++ b/java/com/google/gerrit/server/restapi/change/GetChange.java
@@ -16,12 +16,15 @@
 
 import com.google.common.collect.ImmutableListMultimap;
 import com.google.common.collect.Streams;
+import com.google.gerrit.common.Nullable;
 import com.google.gerrit.entities.Change;
 import com.google.gerrit.extensions.client.ListChangesOption;
 import com.google.gerrit.extensions.client.ListOption;
 import com.google.gerrit.extensions.common.ChangeInfo;
 import com.google.gerrit.extensions.common.PluginDefinedInfo;
 import com.google.gerrit.extensions.registration.DynamicSet;
+import com.google.gerrit.extensions.restapi.BadRequestException;
+import com.google.gerrit.extensions.restapi.PreconditionFailedException;
 import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestReadView;
 import com.google.gerrit.server.DynamicOptions;
@@ -31,12 +34,15 @@
 import com.google.gerrit.server.change.ChangeResource;
 import com.google.gerrit.server.change.PluginDefinedAttributesFactories;
 import com.google.gerrit.server.change.RevisionResource;
+import com.google.gerrit.server.notedb.MissingMetaObjectException;
 import com.google.gerrit.server.query.change.ChangeData;
 import com.google.inject.Inject;
 import java.util.Collection;
 import java.util.EnumSet;
 import java.util.HashMap;
 import java.util.Map;
+import org.eclipse.jgit.errors.InvalidObjectIdException;
+import org.eclipse.jgit.lib.ObjectId;
 import org.kohsuke.args4j.Option;
 
 public class GetChange
@@ -53,6 +59,9 @@
     options.add(o);
   }
 
+  @Option(name = "--meta", usage = "NoteDb meta SHA1")
+  String metaRevId = "";
+
   @Option(name = "-O", usage = "Output option flags, in hex")
   void setOptionFlagsHex(String hex) {
     options.addAll(ListOption.fromBits(ListChangesOption.class, Integer.parseInt(hex, 16)));
@@ -75,14 +84,35 @@
   }
 
   @Override
-  public Response<ChangeInfo> apply(ChangeResource rsrc) {
-    return Response.withMustRevalidate(newChangeJson().format(rsrc));
+  public Response<ChangeInfo> apply(ChangeResource rsrc)
+      throws BadRequestException, PreconditionFailedException {
+    try {
+      return Response.withMustRevalidate(newChangeJson().format(rsrc.getChange(), getMetaRevId()));
+    } catch (MissingMetaObjectException e) {
+      throw new PreconditionFailedException(e.getMessage());
+    }
   }
 
   Response<ChangeInfo> apply(RevisionResource rsrc) {
     return Response.withMustRevalidate(newChangeJson().format(rsrc));
   }
 
+  @Nullable
+  private ObjectId getMetaRevId() throws BadRequestException {
+    if (metaRevId.isEmpty()) {
+      return null;
+    }
+
+    // It might be interesting to also allow {SHA1}^^, so callers can walk back into history
+    // without having to fetch the entire /meta ref. If we do so, we have to be careful that
+    // the error messages can't be abused to fetch hidden data.
+    try {
+      return ObjectId.fromString(metaRevId);
+    } catch (InvalidObjectIdException e) {
+      throw new BadRequestException("invalid meta SHA1: " + metaRevId, e);
+    }
+  }
+
   private ChangeJson newChangeJson() {
     return json.create(options, this::createPluginDefinedInfos);
   }
diff --git a/java/com/google/gerrit/server/restapi/change/GetDetail.java b/java/com/google/gerrit/server/restapi/change/GetDetail.java
index e31d84b..15362d5 100644
--- a/java/com/google/gerrit/server/restapi/change/GetDetail.java
+++ b/java/com/google/gerrit/server/restapi/change/GetDetail.java
@@ -16,6 +16,8 @@
 
 import com.google.gerrit.extensions.client.ListChangesOption;
 import com.google.gerrit.extensions.common.ChangeInfo;
+import com.google.gerrit.extensions.restapi.BadRequestException;
+import com.google.gerrit.extensions.restapi.PreconditionFailedException;
 import com.google.gerrit.extensions.restapi.Response;
 import com.google.gerrit.extensions.restapi.RestReadView;
 import com.google.gerrit.server.DynamicOptions;
@@ -58,7 +60,8 @@
   }
 
   @Override
-  public Response<ChangeInfo> apply(ChangeResource rsrc) {
+  public Response<ChangeInfo> apply(ChangeResource rsrc)
+      throws BadRequestException, PreconditionFailedException {
     return delegate.apply(rsrc);
   }
 }
diff --git a/java/com/google/gerrit/server/restapi/change/PostReview.java b/java/com/google/gerrit/server/restapi/change/PostReview.java
index a360510..562bdf8 100644
--- a/java/com/google/gerrit/server/restapi/change/PostReview.java
+++ b/java/com/google/gerrit/server/restapi/change/PostReview.java
@@ -1272,28 +1272,27 @@
             del.add(c);
             update.putApproval(normName, (short) 0);
           }
-        } else if (c != null) {
-          // Check if the label exists in the request input (the user voted again). If the user
-          // hadn't voted again, there is no need to re-apply the vote.
-          if (inLabels.keySet().contains(c.label())) {
-            PatchSetApproval.Builder b =
-                c.toBuilder()
-                    .value(ent.getValue())
-                    .granted(ctx.getWhen())
-                    .tag(Optional.ofNullable(in.tag));
-            ctx.getUser().updateRealAccountId(b::realAccountId);
-            c = b.build();
-            ups.add(c);
-            addLabelDelta(normName, c.value());
-            oldApprovals.put(normName, previous.get(normName));
-            approvals.put(normName, c.value());
-            update.putApproval(normName, ent.getValue());
-          } else {
-            current.put(normName, c);
-            oldApprovals.put(normName, null);
-            approvals.put(normName, c.value());
-          }
-        } else {
+          // Only allow voting again if the vote is copied over from a past patch-set, or the
+          // values are different.
+        } else if (c != null
+            && (c.value() != ent.getValue() || isApprovalCopiedOver(c, ctx.getNotes()))) {
+          PatchSetApproval.Builder b =
+              c.toBuilder()
+                  .value(ent.getValue())
+                  .granted(ctx.getWhen())
+                  .tag(Optional.ofNullable(in.tag));
+          ctx.getUser().updateRealAccountId(b::realAccountId);
+          c = b.build();
+          ups.add(c);
+          addLabelDelta(normName, c.value());
+          oldApprovals.put(normName, previous.get(normName));
+          approvals.put(normName, c.value());
+          update.putApproval(normName, ent.getValue());
+        } else if (c != null && c.value() == ent.getValue()) {
+          current.put(normName, c);
+          oldApprovals.put(normName, null);
+          approvals.put(normName, c.value());
+        } else if (c == null) {
           c =
               ApprovalsUtil.newApproval(psId, user, lt.getLabelId(), ent.getValue(), ctx.getWhen())
                   .tag(Optional.ofNullable(in.tag))
@@ -1319,6 +1318,17 @@
       return !del.isEmpty() || !ups.isEmpty();
     }
 
+    /**
+     * Approval is copied over if it doesn't exist in the approvals of the current patch-set
+     * according to change notes (which means it was computed in {@link
+     * com.google.gerrit.server.ApprovalInference})
+     */
+    private boolean isApprovalCopiedOver(
+        PatchSetApproval patchSetApproval, ChangeNotes changeNotes) {
+      return !changeNotes.getApprovals().get(changeNotes.getChange().currentPatchSetId()).stream()
+          .anyMatch(p -> p.equals(patchSetApproval));
+    }
+
     private void validatePostSubmitLabels(
         ChangeContext ctx,
         LabelTypes labelTypes,
diff --git a/java/com/google/gerrit/server/restapi/project/CreateProject.java b/java/com/google/gerrit/server/restapi/project/CreateProject.java
index a5a0034..faab241 100644
--- a/java/com/google/gerrit/server/restapi/project/CreateProject.java
+++ b/java/com/google/gerrit/server/restapi/project/CreateProject.java
@@ -208,4 +208,18 @@
     }
     return normalizedBranches;
   }
+
+  static class ValidBranchListener implements ProjectCreationValidationListener {
+    @Override
+    public void validateNewProject(CreateProjectArgs args) throws ValidationException {
+      for (String branch : args.branch) {
+        if (RefNames.isGerritRef(branch)) {
+          throw new ValidationException(
+              String.format(
+                  "Cannot create a project with branch %s. Branches in the Gerrit internal refs namespace are not allowed",
+                  branch));
+        }
+      }
+    }
+  }
 }
diff --git a/java/com/google/gerrit/server/restapi/project/Module.java b/java/com/google/gerrit/server/restapi/project/Module.java
index 517b888..9217077 100644
--- a/java/com/google/gerrit/server/restapi/project/Module.java
+++ b/java/com/google/gerrit/server/restapi/project/Module.java
@@ -29,6 +29,7 @@
 import com.google.gerrit.server.config.GerritConfigListener;
 import com.google.gerrit.server.project.RefValidationHelper;
 import com.google.gerrit.server.restapi.change.CherryPickCommit;
+import com.google.gerrit.server.validators.ProjectCreationValidationListener;
 
 public class Module extends RestApiModule {
 
@@ -47,6 +48,8 @@
     DynamicMap.mapOf(binder(), LABEL_KIND);
 
     DynamicSet.bind(binder(), GerritConfigListener.class).to(SetParent.class);
+    DynamicSet.bind(binder(), ProjectCreationValidationListener.class)
+        .to(CreateProject.ValidBranchListener.class);
 
     create(PROJECT_KIND).to(CreateProject.class);
     put(PROJECT_KIND).to(PutProject.class);
diff --git a/java/com/google/gerrit/server/submit/MergeOp.java b/java/com/google/gerrit/server/submit/MergeOp.java
index 7f434ca..871d8d2 100644
--- a/java/com/google/gerrit/server/submit/MergeOp.java
+++ b/java/com/google/gerrit/server/submit/MergeOp.java
@@ -35,6 +35,7 @@
 import com.google.gerrit.common.Nullable;
 import com.google.gerrit.entities.BranchNameKey;
 import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.Change.Status;
 import com.google.gerrit.entities.ChangeMessage;
 import com.google.gerrit.entities.PatchSet;
 import com.google.gerrit.entities.Project;
@@ -493,12 +494,29 @@
         logger.atFine().log("Calculated to merge %s", indexBackedChangeSet);
 
         // Reload ChangeSet so that we don't rely on (potentially) stale index data for merging
-        ChangeSet cs = reloadChanges(indexBackedChangeSet);
+        ChangeSet noteDbChangeSet = reloadChanges(indexBackedChangeSet);
+
+        // At this point, any change that isn't new can be filtered out since they were only here
+        // in the first place due to stale index.
+        List<ChangeData> filteredChanges = new ArrayList<>();
+        for (ChangeData changeData : noteDbChangeSet.changes()) {
+          if (!changeData.change().getStatus().equals(Status.NEW)) {
+            logger.atFine().log(
+                "Change %s has status %s due to stale index, so it is skipped during submit",
+                changeData.getId().toString(), changeData.change().getStatus().name());
+            continue;
+          }
+          filteredChanges.add(changeData);
+        }
+
+        // There are no hidden changes (or else we would have thrown AuthException above).
+        ChangeSet filteredNoteDbChangeSet =
+            new ChangeSet(filteredChanges, /* hiddenChanges= */ ImmutableList.of());
 
         // Count cross-project submissions outside of the retry loop. The chance of a single project
         // failing increases with the number of projects, so the failure count would be inflated if
         // this metric were incremented inside of integrateIntoHistory.
-        int projects = cs.projects().size();
+        int projects = filteredNoteDbChangeSet.projects().size();
         if (projects > 1) {
           topicMetrics.topicSubmissions.increment();
         }
@@ -517,22 +535,22 @@
                     this.ts = TimeUtil.nowTs();
                     openRepoManager();
                   }
-                  this.commitStatus = new CommitStatus(cs, isRetry);
+                  this.commitStatus = new CommitStatus(filteredNoteDbChangeSet, isRetry);
                   if (checkSubmitRules) {
                     logger.atFine().log("Checking submit rules and state");
-                    checkSubmitRulesAndState(cs, isRetry);
+                    checkSubmitRulesAndState(filteredNoteDbChangeSet, isRetry);
                   } else {
                     logger.atFine().log("Bypassing submit rules");
-                    bypassSubmitRules(cs, isRetry);
+                    bypassSubmitRules(filteredNoteDbChangeSet, isRetry);
                   }
-                  integrateIntoHistory(cs, submissionExecutor);
+                  integrateIntoHistory(filteredNoteDbChangeSet, submissionExecutor);
                   return null;
                 })
             .listener(retryTracker)
             // Up to the entire submit operation is retried, including possibly many projects.
             // Multiply the timeout by the number of projects we're actually attempting to
             // submit.
-            .defaultTimeoutMultiplier(cs.projects().size())
+            .defaultTimeoutMultiplier(filteredNoteDbChangeSet.projects().size())
             // By default, we only retry lock failures. Here it's better to also retry unexpected
             // runtime exceptions.
             .retryOn(t -> t instanceof RuntimeException)
diff --git a/java/gerrit/BUILD b/java/gerrit/BUILD
index 7dbf751..db831b7 100644
--- a/java/gerrit/BUILD
+++ b/java/gerrit/BUILD
@@ -5,7 +5,6 @@
     srcs = glob(["**/*.java"]),
     visibility = ["//visibility:public"],
     deps = [
-        "//java/com/google/gerrit/common:server",
         "//java/com/google/gerrit/entities",
         "//java/com/google/gerrit/extensions:api",
         "//java/com/google/gerrit/server",
diff --git a/javatests/com/google/gerrit/acceptance/api/change/PostReviewIT.java b/javatests/com/google/gerrit/acceptance/api/change/PostReviewIT.java
index 0b2e0f7..c8b1715 100644
--- a/javatests/com/google/gerrit/acceptance/api/change/PostReviewIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/change/PostReviewIT.java
@@ -63,7 +63,6 @@
 import com.google.gerrit.server.restapi.change.OnPostReview;
 import com.google.gerrit.server.restapi.change.PostReview;
 import com.google.gerrit.server.update.CommentsRejectedException;
-import com.google.gerrit.testing.FakeEmailSender;
 import com.google.gerrit.testing.TestCommentHelper;
 import com.google.inject.Inject;
 import com.google.inject.Module;
@@ -597,7 +596,7 @@
       input = new ReviewInput().label(LabelId.CODE_REVIEW, 2);
       gApi.changes().id(r.getChangeId()).current().review(input);
       testOnPostReview.assertApproval(
-          LabelId.CODE_REVIEW, /* expectedOldValue= */ 2, /* expectedNewValue= */ 2);
+          LabelId.CODE_REVIEW, /* expectedOldValue= */ null, /* expectedNewValue= */ 2);
 
       // Delete the vote.
       input = new ReviewInput().label(LabelId.CODE_REVIEW, 0);
@@ -627,21 +626,19 @@
     assertThat(r.getChange().approvals().values()).hasSize(1);
     List<ChangeMessageInfo> changeMessages = gApi.changes().id(r.getChangeId()).messages();
 
-    // The two latest change messages are both about Code-Review+2
+    // Only the last change message is about Code-Review+2
     assertThat(Iterables.getLast(changeMessages).message).isEqualTo("Patch Set 1: Code-Review+2");
     changeMessages.remove(changeMessages.size() - 1);
-    assertThat(Iterables.getLast(changeMessages).message).isEqualTo("Patch Set 1: Code-Review+2");
+    assertThat(Iterables.getLast(changeMessages).message)
+        .isNotEqualTo("Patch Set 1: Code-Review+2");
 
-    // The two latest emails are about Code-Review +2.
-    List<FakeEmailSender.Message> messages = sender.getMessages();
-    assertThat(messages).hasSize(2);
-    for (FakeEmailSender.Message message : messages) {
-      assertThat(message.body()).contains("Patch Set 1: Code-Review+2");
-    }
+    // Only one email is about Code-Review +2 was sent.
+    assertThat(Iterables.getOnlyElement(sender.getMessages()).body())
+        .contains("Patch Set 1: Code-Review+2");
   }
 
   @Test
-  public void votingTheSameVoteSecondTimeExtendsOnPostReview() throws Exception {
+  public void votingTheSameVoteSecondTimeExtendsOnPostReviewWithOldNullValue() throws Exception {
     PushOneCommit.Result r = createChange();
 
     // Add a new vote.
@@ -656,12 +653,12 @@
       gApi.changes().id(r.getChangeId()).current().review(input);
 
       testOnPostReview.assertApproval(
-          LabelId.CODE_REVIEW, /* expectedOldValue= */ 2, /* expectedNewValue= */ 2);
+          LabelId.CODE_REVIEW, /* expectedOldValue= */ null, /* expectedNewValue= */ 2);
     }
   }
 
   @Test
-  public void votingTheSameVoteSecondTimeFiresOnCommentAdded() throws Exception {
+  public void votingTheSameVoteSecondTimeDoesNotFireOnCommentAdded() throws Exception {
     PushOneCommit.Result r = createChange();
 
     // Add a new vote.
@@ -675,8 +672,8 @@
       input = new ReviewInput().label(LabelId.CODE_REVIEW, 2);
       gApi.changes().id(r.getChangeId()).current().review(input);
 
-      assertThat(testListener.lastCommentAddedEvent.getComment())
-          .isEqualTo("Patch Set 1: Code-Review+2");
+      // Event not fired.
+      assertThat(testListener.lastCommentAddedEvent).isNull();
     }
   }
 
diff --git a/javatests/com/google/gerrit/acceptance/api/revision/RevisionIT.java b/javatests/com/google/gerrit/acceptance/api/revision/RevisionIT.java
index 67e62dd..abfd7896 100644
--- a/javatests/com/google/gerrit/acceptance/api/revision/RevisionIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/revision/RevisionIT.java
@@ -176,12 +176,12 @@
     assertThat(approval.postSubmit).isNull();
     assertPermitted(gApi.changes().id(changeId).get(DETAILED_LABELS), LabelId.CODE_REVIEW, 1, 2);
 
-    // Repeating the current label is allowed. Flips the postSubmit since technically this is a
-    // new vote.
+    // Repeating the current label is allowed. Does not flip the postSubmit bit due to
+    // deduplication codepath.
     gApi.changes().id(changeId).current().review(ReviewInput.recommend());
     approval = getApproval(changeId, label);
     assertThat(approval.value).isEqualTo(1);
-    assertThat(approval.postSubmit).isTrue();
+    assertThat(approval.postSubmit).isNull();
 
     // Reducing vote is not allowed.
     ResourceConflictException thrown =
@@ -193,7 +193,7 @@
         .isEqualTo("Cannot reduce vote on labels for closed change: Code-Review");
     approval = getApproval(changeId, label);
     assertThat(approval.value).isEqualTo(1);
-    assertThat(approval.postSubmit).isTrue();
+    assertThat(approval.postSubmit).isNull();
 
     // Increasing vote is allowed.
     gApi.changes().id(changeId).current().review(ReviewInput.approve());
diff --git a/javatests/com/google/gerrit/acceptance/git/BUILD b/javatests/com/google/gerrit/acceptance/git/BUILD
index ef54c92..13311e3 100644
--- a/javatests/com/google/gerrit/acceptance/git/BUILD
+++ b/javatests/com/google/gerrit/acceptance/git/BUILD
@@ -21,7 +21,6 @@
     deps = [
         "//java/com/google/gerrit/acceptance:lib",
         "//java/com/google/gerrit/git",
-        "//java/com/google/gerrit/mail",
     ],
 )
 
diff --git a/javatests/com/google/gerrit/acceptance/rest/change/ChangeMetaIT.java b/javatests/com/google/gerrit/acceptance/rest/change/ChangeMetaIT.java
new file mode 100644
index 0000000..e025c52
--- /dev/null
+++ b/javatests/com/google/gerrit/acceptance/rest/change/ChangeMetaIT.java
@@ -0,0 +1,150 @@
+// Copyright (C) 2017 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.acceptance.rest.change;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.gerrit.entities.RefNames.changeMetaRef;
+
+import com.google.common.collect.Iterables;
+import com.google.gerrit.acceptance.AbstractDaemonTest;
+import com.google.gerrit.acceptance.PushOneCommit;
+import com.google.gerrit.acceptance.RestResponse;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.extensions.common.ChangeInfo;
+import com.google.gerrit.extensions.common.PluginDefinedInfo;
+import com.google.gerrit.extensions.registration.DynamicSet;
+import com.google.gerrit.server.change.ChangePluginDefinedInfoFactory;
+import com.google.gerrit.server.query.change.ChangeData;
+import com.google.gson.reflect.TypeToken;
+import com.google.gson.stream.JsonReader;
+import com.google.inject.AbstractModule;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import org.eclipse.jgit.lib.Repository;
+import org.junit.Test;
+
+/** Test handling of the NoteDb commit hash in the GetChange endpoint */
+public class ChangeMetaIT extends AbstractDaemonTest {
+  @Test
+  public void metaSha1_fromIndex() throws Exception {
+    PushOneCommit.Result result = createChange();
+    String changeId = result.getChangeId();
+
+    try (AutoCloseable ignored = disableNoteDb()) {
+      ChangeInfo change =
+          Iterables.getOnlyElement(gApi.changes().query().withQuery("change:" + changeId).get());
+
+      try (Repository repo = repoManager.openRepository(project)) {
+        assertThat(change.metaRevId)
+            .isEqualTo(
+                repo.exactRef(changeMetaRef(Change.id(change._number))).getObjectId().getName());
+      }
+    }
+  }
+
+  @Test
+  public void metaSha1_fromNoteDb() throws Exception {
+    PushOneCommit.Result result = createChange();
+    String changeId = result.getChangeId();
+    ChangeInfo before = gApi.changes().id(changeId).get();
+    try (Repository repo = repoManager.openRepository(project)) {
+      assertThat(before.metaRevId)
+          .isEqualTo(
+              repo.exactRef(changeMetaRef(Change.id(before._number))).getObjectId().getName());
+    }
+  }
+
+  @Test
+  public void ChangeInfo_metaSha1_parameter() throws Exception {
+    PushOneCommit.Result result = createChange();
+    String changeId = result.getChangeId();
+    gApi.changes().id(changeId).setMessage("before\n\n" + "Change-Id: " + result.getChangeId());
+    ChangeInfo before = gApi.changes().id(changeId).get();
+    gApi.changes().id(changeId).setMessage("after\n\n" + "Change-Id: " + result.getChangeId());
+    ChangeInfo after = gApi.changes().id(changeId).get();
+    assertThat(after.metaRevId).isNotEqualTo(before.metaRevId);
+
+    RestResponse resp = adminRestSession.get("/changes/" + changeId + "/?meta=" + before.metaRevId);
+    resp.assertOK();
+
+    ChangeInfo got;
+    try (JsonReader jsonReader = new JsonReader(resp.getReader())) {
+      jsonReader.setLenient(true);
+      got = newGson().fromJson(jsonReader, ChangeInfo.class);
+    }
+    assertThat(got.subject).isEqualTo(before.subject);
+  }
+
+  @Test
+  public void metaUnreachableSha1() throws Exception {
+    PushOneCommit.Result ch1 = createChange();
+    PushOneCommit.Result ch2 = createChange();
+
+    ChangeInfo info2 = gApi.changes().id(ch2.getChangeId()).get();
+
+    RestResponse resp =
+        adminRestSession.get("/changes/" + ch1.getChangeId() + "/?meta=" + info2.metaRevId);
+
+    resp.assertStatus(412);
+  }
+
+  protected static class PluginDefinedSimpleAttributeModule extends AbstractModule {
+    static class MyMetaHash extends PluginDefinedInfo {
+      String myMetaRef;
+    };
+
+    static PluginDefinedInfo newMyMetaHash(ChangeData cd) {
+      MyMetaHash mmh = new MyMetaHash();
+      mmh.myMetaRef = cd.notes().getMetaId().name();
+      return mmh;
+    }
+
+    @Override
+    public void configure() {
+      DynamicSet.bind(binder(), ChangePluginDefinedInfoFactory.class)
+          .toInstance(
+              (cds, bp, p) -> {
+                Map<Change.Id, PluginDefinedInfo> out = new HashMap<>();
+                cds.forEach(cd -> out.put(cd.getId(), newMyMetaHash(cd)));
+                return out;
+              });
+    }
+  }
+
+  @Test
+  public void pluginDefinedAttribute() throws Exception {
+    try (AutoCloseable ignored =
+        installPlugin("my-plugin", PluginDefinedSimpleAttributeModule.class)) {
+      PushOneCommit.Result result = createChange();
+      String changeId = result.getChangeId();
+      gApi.changes().id(changeId).setMessage("before\n\n" + "Change-Id: " + result.getChangeId());
+      ChangeInfo before = gApi.changes().id(changeId).get();
+      gApi.changes().id(changeId).setMessage("after\n\n" + "Change-Id: " + result.getChangeId());
+      ChangeInfo after = gApi.changes().id(changeId).get();
+
+      RestResponse resp =
+          adminRestSession.get("/changes/" + changeId + "/?meta=" + before.metaRevId);
+      resp.assertOK();
+
+      Map<String, Object> changeInfo =
+          newGson().fromJson(resp.getReader(), new TypeToken<Map<String, Object>>() {}.getType());
+      List<Object> plugins = (List<Object>) changeInfo.get("plugins");
+      Map<String, Object> myplugin = (Map<String, Object>) plugins.get(0);
+
+      assertThat(myplugin.get("my_meta_ref")).isEqualTo(before.metaRevId);
+    }
+  }
+}
diff --git a/javatests/com/google/gerrit/acceptance/rest/project/BUILD b/javatests/com/google/gerrit/acceptance/rest/project/BUILD
index 5e1fc83..edcb1f9 100644
--- a/javatests/com/google/gerrit/acceptance/rest/project/BUILD
+++ b/javatests/com/google/gerrit/acceptance/rest/project/BUILD
@@ -20,7 +20,6 @@
         "LabelAssert.java",
     ],
     deps = [
-        "//java/com/google/gerrit/common:server",
         "//java/com/google/gerrit/entities",
         "//java/com/google/gerrit/extensions:api",
         "//java/com/google/gerrit/server",
diff --git a/javatests/com/google/gerrit/acceptance/rest/project/CreateProjectIT.java b/javatests/com/google/gerrit/acceptance/rest/project/CreateProjectIT.java
index 10fd65f..e5c5952 100644
--- a/javatests/com/google/gerrit/acceptance/rest/project/CreateProjectIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/project/CreateProjectIT.java
@@ -53,6 +53,7 @@
 import com.google.gerrit.extensions.restapi.Url;
 import com.google.gerrit.server.group.SystemGroupBackend;
 import com.google.gerrit.server.project.ProjectState;
+import com.google.gerrit.server.validators.ValidationException;
 import com.google.inject.Inject;
 import java.util.Collections;
 import java.util.Optional;
@@ -328,6 +329,21 @@
   }
 
   @Test
+  public void createProjectWithInvalidBranch() throws Exception {
+    String newProjectName = name("newProject");
+    ProjectInput in = new ProjectInput();
+    in.name = newProjectName;
+    in.createEmptyCommit = true;
+    in.branches = ImmutableList.of("refs/heads/test", "refs/changes/34/1234");
+    Throwable thrown =
+        assertThrows(ResourceConflictException.class, () -> gApi.projects().create(in));
+    assertThat(thrown).hasCauseThat().isInstanceOf(ValidationException.class);
+    assertThat(thrown)
+        .hasMessageThat()
+        .contains("Cannot create a project with branch refs/changes/34/1234");
+  }
+
+  @Test
   public void createProjectWithCapability() throws Exception {
     projectOperations
         .allProjectsForUpdate()
diff --git a/javatests/com/google/gerrit/acceptance/server/change/BUILD b/javatests/com/google/gerrit/acceptance/server/change/BUILD
index e41178f..19ca946 100644
--- a/javatests/com/google/gerrit/acceptance/server/change/BUILD
+++ b/javatests/com/google/gerrit/acceptance/server/change/BUILD
@@ -18,8 +18,6 @@
         "//java/com/google/gerrit/acceptance:lib",
         "//java/com/google/gerrit/entities",
         "//java/com/google/gerrit/extensions:api",
-        "//java/com/google/gerrit/server/logging",
-        "//java/com/google/gerrit/server/util/time",
         "@guava//jar",
     ],
 )
diff --git a/javatests/com/google/gerrit/acceptance/server/change/CommentContextIT.java b/javatests/com/google/gerrit/acceptance/server/change/CommentContextIT.java
index adfe56d..548e3fe 100644
--- a/javatests/com/google/gerrit/acceptance/server/change/CommentContextIT.java
+++ b/javatests/com/google/gerrit/acceptance/server/change/CommentContextIT.java
@@ -19,11 +19,9 @@
 import static com.google.gerrit.entities.Patch.COMMIT_MSG;
 import static com.google.gerrit.entities.Patch.MERGE_LIST;
 import static com.google.gerrit.entities.Patch.PATCHSET_LEVEL;
-import static com.google.gerrit.testing.GerritJUnit.assertThrows;
 
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.MoreCollectors;
-import com.google.common.util.concurrent.UncheckedExecutionException;
 import com.google.gerrit.acceptance.AbstractDaemonTest;
 import com.google.gerrit.acceptance.NoHttpd;
 import com.google.gerrit.acceptance.PushOneCommit;
@@ -148,12 +146,10 @@
         CommentsUtil.newComment(COMMIT_MSG, Side.REVISION, 100, "comment", false);
     CommentsUtil.addComments(gApi, changeId, ps1, comment);
 
-    Throwable thrown =
-        assertThrows(
-            UncheckedExecutionException.class,
-            () -> gApi.changes().id(changeId).commentsRequest().withContext(true).getAsList());
-
-    assertThat(thrown).hasCauseThat().hasMessageThat().contains("Invalid comment range");
+    List<CommentInfo> comments =
+        gApi.changes().id(changeId).commentsRequest().withContext(true).getAsList();
+    assertThat(comments).hasSize(1);
+    assertThat(comments.get(0).contextLines).isEmpty();
   }
 
   @Test
diff --git a/javatests/com/google/gerrit/auth/BUILD b/javatests/com/google/gerrit/auth/BUILD
index d4bb16d..6a41d01 100644
--- a/javatests/com/google/gerrit/auth/BUILD
+++ b/javatests/com/google/gerrit/auth/BUILD
@@ -17,16 +17,13 @@
         "//prolog:gerrit-prolog-common",
     ],
     deps = [
-        "//java/com/google/gerrit/acceptance/testsuite/project",
         "//java/com/google/gerrit/auth",
         "//java/com/google/gerrit/entities",
         "//java/com/google/gerrit/extensions:api",
         "//java/com/google/gerrit/proto/testing",
         "//java/com/google/gerrit/server",
         "//java/com/google/gerrit/server/cache/serialize",
-        "//java/com/google/gerrit/server/cache/testing",
         "//java/com/google/gerrit/testing:gerrit-test-util",
-        "//java/com/google/gerrit/truth",
         "//lib:guava",
         "//lib:guava-retrying",
         "//lib:jgit",
diff --git a/javatests/com/google/gerrit/elasticsearch/ElasticContainer.java b/javatests/com/google/gerrit/elasticsearch/ElasticContainer.java
index e8cf3e9..8cd09e6 100644
--- a/javatests/com/google/gerrit/elasticsearch/ElasticContainer.java
+++ b/javatests/com/google/gerrit/elasticsearch/ElasticContainer.java
@@ -39,10 +39,6 @@
 
   private static String getImageName(ElasticVersion version) {
     switch (version) {
-      case V7_2:
-        return "blacktop/elasticsearch:7.2.1";
-      case V7_3:
-        return "blacktop/elasticsearch:7.3.2";
       case V7_4:
         return "blacktop/elasticsearch:7.4.2";
       case V7_5:
diff --git a/javatests/com/google/gerrit/elasticsearch/ElasticVersionTest.java b/javatests/com/google/gerrit/elasticsearch/ElasticVersionTest.java
index 1ec8a5d..508dc84 100644
--- a/javatests/com/google/gerrit/elasticsearch/ElasticVersionTest.java
+++ b/javatests/com/google/gerrit/elasticsearch/ElasticVersionTest.java
@@ -22,12 +22,6 @@
 public class ElasticVersionTest {
   @Test
   public void supportedVersion() throws Exception {
-    assertThat(ElasticVersion.forVersion("7.2.0")).isEqualTo(ElasticVersion.V7_2);
-    assertThat(ElasticVersion.forVersion("7.2.1")).isEqualTo(ElasticVersion.V7_2);
-
-    assertThat(ElasticVersion.forVersion("7.3.0")).isEqualTo(ElasticVersion.V7_3);
-    assertThat(ElasticVersion.forVersion("7.3.1")).isEqualTo(ElasticVersion.V7_3);
-
     assertThat(ElasticVersion.forVersion("7.4.0")).isEqualTo(ElasticVersion.V7_4);
     assertThat(ElasticVersion.forVersion("7.4.1")).isEqualTo(ElasticVersion.V7_4);
 
diff --git a/javatests/com/google/gerrit/server/BUILD b/javatests/com/google/gerrit/server/BUILD
index 9a6b82b..7ab7ae9 100644
--- a/javatests/com/google/gerrit/server/BUILD
+++ b/javatests/com/google/gerrit/server/BUILD
@@ -74,6 +74,7 @@
         "//lib:jgit",
         "//lib:jgit-junit",
         "//lib:protobuf",
+        "//lib:soy",
         "//lib/auto:auto-value",
         "//lib/auto:auto-value-annotations",
         "//lib/flogger:api",
diff --git a/javatests/com/google/gerrit/server/cache/serialize/BUILD b/javatests/com/google/gerrit/server/cache/serialize/BUILD
index 6976d19..fa6a717 100644
--- a/javatests/com/google/gerrit/server/cache/serialize/BUILD
+++ b/javatests/com/google/gerrit/server/cache/serialize/BUILD
@@ -5,7 +5,6 @@
     srcs = glob(["*.java"]),
     deps = [
         "//java/com/google/gerrit/entities",
-        "//java/com/google/gerrit/extensions:api",
         "//java/com/google/gerrit/server",
         "//java/com/google/gerrit/server/cache/serialize",
         "//java/com/google/gerrit/server/cache/testing",
diff --git a/javatests/com/google/gerrit/server/cache/serialize/entities/BUILD b/javatests/com/google/gerrit/server/cache/serialize/entities/BUILD
index b84febb..a8158fc 100644
--- a/javatests/com/google/gerrit/server/cache/serialize/entities/BUILD
+++ b/javatests/com/google/gerrit/server/cache/serialize/entities/BUILD
@@ -4,14 +4,10 @@
     name = "tests",
     srcs = glob(["*.java"]),
     deps = [
-        "//java/com/google/gerrit/common:server",
         "//java/com/google/gerrit/entities",
         "//java/com/google/gerrit/extensions:api",
         "//java/com/google/gerrit/server",
-        "//java/com/google/gerrit/server/cache/serialize",
         "//java/com/google/gerrit/server/cache/serialize/entities",
-        "//java/com/google/gerrit/server/cache/testing",
-        "//java/com/google/gerrit/testing:gerrit-test-util",
         "//lib:guava",
         "//lib:jgit",
         "//lib:protobuf",
diff --git a/javatests/com/google/gerrit/server/group/db/BUILD b/javatests/com/google/gerrit/server/group/db/BUILD
index 3303338..9f9f459 100644
--- a/javatests/com/google/gerrit/server/group/db/BUILD
+++ b/javatests/com/google/gerrit/server/group/db/BUILD
@@ -6,7 +6,6 @@
     srcs = glob(["*.java"]),
     deps = [
         "//java/com/google/gerrit/common:annotations",
-        "//java/com/google/gerrit/common:server",
         "//java/com/google/gerrit/common/data/testing:common-data-test-util",
         "//java/com/google/gerrit/entities",
         "//java/com/google/gerrit/exceptions",
diff --git a/javatests/com/google/gerrit/server/mail/send/MailSoySauceProviderTest.java b/javatests/com/google/gerrit/server/mail/send/MailSoySauceProviderTest.java
new file mode 100644
index 0000000..2ec5e4d
--- /dev/null
+++ b/javatests/com/google/gerrit/server/mail/send/MailSoySauceProviderTest.java
@@ -0,0 +1,48 @@
+// Copyright (C) 2021 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.server.mail.send;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import com.google.gerrit.extensions.registration.DynamicSet;
+import com.google.gerrit.server.config.SitePaths;
+import com.google.gerrit.server.plugincontext.PluginContext.PluginMetrics;
+import com.google.gerrit.server.plugincontext.PluginSetContext;
+import com.google.template.soy.shared.SoyAstCache;
+import java.nio.file.Paths;
+import org.junit.Before;
+import org.junit.Test;
+
+public class MailSoySauceProviderTest {
+
+  private SitePaths sitePaths;
+  private DynamicSet<MailSoyTemplateProvider> set;
+
+  @Before
+  public void setUp() throws Exception {
+    sitePaths = new SitePaths(Paths.get("."));
+    set = new DynamicSet<>();
+  }
+
+  @Test
+  public void soyCompilation() {
+    MailSoySauceProvider provider =
+        new MailSoySauceProvider(
+            sitePaths,
+            new SoyAstCache(),
+            new PluginSetContext<>(set, PluginMetrics.DISABLED_INSTANCE));
+    assertThat(provider.get()).isNotNull(); // should not throw
+  }
+}
diff --git a/javatests/com/google/gerrit/server/notedb/ChangeNotesStateTest.java b/javatests/com/google/gerrit/server/notedb/ChangeNotesStateTest.java
index 67181b7..78804a7 100644
--- a/javatests/com/google/gerrit/server/notedb/ChangeNotesStateTest.java
+++ b/javatests/com/google/gerrit/server/notedb/ChangeNotesStateTest.java
@@ -1013,60 +1013,6 @@
                 .build());
   }
 
-  /* Transitional test. Remove once follow-up change is live without accidents. */
-  @Test
-  public void binaryCompatibility() throws Exception {
-    ChangeNotesState.Builder builder = newBuilder();
-    PatchSet ps1 =
-        PatchSet.builder()
-            .id(PatchSet.id(ID, 1))
-            .commitId(ObjectId.fromString("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"))
-            .uploader(Account.id(2000))
-            .createdOn(cols.createdOn())
-            .build();
-    PatchSetApproval a1 =
-        PatchSetApproval.builder()
-            .key(
-                PatchSetApproval.key(
-                    ps1.id(), Account.id(2001), LabelId.create(LabelId.CODE_REVIEW)))
-            .value(1)
-            .granted(new Timestamp(1212L))
-            .build();
-
-    ChangeMessage m1 =
-        new ChangeMessage(
-            ChangeMessage.key(ID, "uuid1"),
-            Account.id(1000),
-            new Timestamp(1212L),
-            PatchSet.id(ID, 1));
-    ChangeNotesState state =
-        builder
-            .approvals(ImmutableMap.of(PatchSet.id(ID, 1), a1).entrySet())
-            .patchSets(ImmutableMap.of(ps1.id(), ps1).entrySet())
-            .changeMessages(ImmutableList.of(m1))
-            .build();
-
-    byte got[] = ChangeNotesState.Serializer.INSTANCE.serialize(state);
-    byte want[] =
-        new byte[] {
-          10, 20, 18, 52, 86, 120, 18, 52, 86, 120, 18, 52, 86, 120, 18, 52, 86, 120, 18, 52, 86,
-          120, 16, 123, 26, 89, 10, 41, 73, 97, 98, 99, 100, 97, 98, 99, 100, 97, 98, 99, 100, 97,
-          98, 99, 100, 97, 98, 99, 100, 97, 98, 99, 100, 97, 98, 99, 100, 97, 98, 99, 100, 97, 98,
-          99, 100, 97, 98, 99, 100, 16, -64, -60, 7, 24, -57, -88, 14, 32, -24, 7, 42, 17, 114, 101,
-          102, 115, 47, 104, 101, 97, 100, 115, 47, 109, 97, 115, 116, 101, 114, 66, 11, 84, 101,
-          115, 116, 32, 99, 104, 97, 110, 103, 101, -88, 1, 1, 50, 66, 10, 6, 10, 2, 8, 123, 16, 1,
-          18, 42, 10, 40, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97,
-          97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97,
-          26, 3, 8, -48, 15, 33, 64, -30, 1, 0, 0, 0, 0, 0, 58, 43, 10, 28, 10, 6, 10, 2, 8, 123,
-          16, 1, 18, 3, 8, -47, 15, 26, 13, 10, 11, 67, 111, 100, 101, 45, 82, 101, 118, 105, 101,
-          119, 16, 1, 25, -68, 4, 0, 0, 0, 0, 0, 0, 64, 0, 122, 35, 10, 11, 10, 2, 8, 123, 18, 5,
-          117, 117, 105, 100, 49, 18, 3, 8, -24, 7, 25, -68, 4, 0, 0, 0, 0, 0, 0, 42, 6, 10, 2, 8,
-          123, 16, 1
-        };
-
-    assertThat(got).isEqualTo(want);
-  }
-
   @Test
   public void commentFields() throws Exception {
     assertThatSerializedClass(Comment.Key.class)
diff --git a/javatests/com/google/gerrit/server/query/change/BUILD b/javatests/com/google/gerrit/server/query/change/BUILD
index 0258e5d..43b9690 100644
--- a/javatests/com/google/gerrit/server/query/change/BUILD
+++ b/javatests/com/google/gerrit/server/query/change/BUILD
@@ -19,7 +19,6 @@
         "//java/com/google/gerrit/acceptance/config",
         "//java/com/google/gerrit/acceptance/testsuite/project",
         "//java/com/google/gerrit/common:annotations",
-        "//java/com/google/gerrit/common:server",
         "//java/com/google/gerrit/entities",
         "//java/com/google/gerrit/extensions:api",
         "//java/com/google/gerrit/httpd",
diff --git a/javatests/com/google/gerrit/server/rules/BUILD b/javatests/com/google/gerrit/server/rules/BUILD
index 250b0ce..5c57ede 100644
--- a/javatests/com/google/gerrit/server/rules/BUILD
+++ b/javatests/com/google/gerrit/server/rules/BUILD
@@ -7,7 +7,6 @@
     resources = ["//prologtests:gerrit_common_test"],
     runtime_deps = ["//prolog:gerrit-prolog-common"],
     deps = [
-        "//java/com/google/gerrit/common:server",
         "//java/com/google/gerrit/entities",
         "//java/com/google/gerrit/server",
         "//java/com/google/gerrit/server/project/testing:project-test-util",
diff --git a/javatests/com/google/gerrit/server/update/BUILD b/javatests/com/google/gerrit/server/update/BUILD
index e175b95..4fe4ab04 100644
--- a/javatests/com/google/gerrit/server/update/BUILD
+++ b/javatests/com/google/gerrit/server/update/BUILD
@@ -10,7 +10,6 @@
     ],
     deps = [
         "//java/com/google/gerrit/common:annotations",
-        "//java/com/google/gerrit/common:server",
         "//java/com/google/gerrit/entities",
         "//java/com/google/gerrit/extensions:api",
         "//java/com/google/gerrit/server",
diff --git a/lib/highlightjs/highlight.min.js b/lib/highlightjs/highlight.min.js
index 7f5e3ef..38fa024 100644
--- a/lib/highlightjs/highlight.min.js
+++ b/lib/highlightjs/highlight.min.js
@@ -1,5 +1,5 @@
 /*
-  Highlight.js 10.5.0 (e67c3e06)
+  Highlight.js 10.6.0 (eb122d3b)
   License: BSD-3-Clause
   Copyright (c) 2006-2020, Ivan Sagalaev
 */
@@ -44,16 +44,16 @@
 },v=(e,t,n={})=>{const s=a({className:"comment",begin:e,end:t,contains:[]},n)
 ;return s.contains.push(E),s.contains.push({className:"doctag",
 begin:"(?:TODO|FIXME|NOTE|BUG|OPTIMIZE|HACK|XXX):",relevance:0}),s
-},N=v("//","$"),w=v("/\\*","\\*/"),R=v("#","$");var y=Object.freeze({
-__proto__:null,IDENT_RE:g,UNDERSCORE_IDENT_RE:d,NUMBER_RE:h,C_NUMBER_RE:f,
-BINARY_NUMBER_RE:p,
+},w=v("//","$"),N=v("/\\*","\\*/"),y=v("#","$");var R=Object.freeze({
+__proto__:null,MATCH_NOTHING_RE:/\b\B/,IDENT_RE:g,UNDERSCORE_IDENT_RE:d,
+NUMBER_RE:h,C_NUMBER_RE:f,BINARY_NUMBER_RE:p,
 RE_STARTERS_RE:"!|!=|!==|%|%=|&|&&|&=|\\*|\\*=|\\+|\\+=|,|-|-=|/=|/|:|;|<<|<<=|<=|<|===|==|=|>>>=|>>=|>=|>>>|>>|>|\\?|\\[|\\{|\\(|\\^|\\^=|\\||\\|=|\\|\\||~",
 SHEBANG:(e={})=>{const t=/^#![ ]*\//
 ;return e.binary&&(e.begin=((...e)=>e.map((e=>u(e))).join(""))(t,/.*\b/,e.binary,/\b.*/)),
 a({className:"meta",begin:t,end:/$/,relevance:0,"on:begin":(e,t)=>{
 0!==e.index&&t.ignoreMatch()}},e)},BACKSLASH_ESCAPE:m,APOS_STRING_MODE:b,
-QUOTE_STRING_MODE:x,PHRASAL_WORDS_MODE:E,COMMENT:v,C_LINE_COMMENT_MODE:N,
-C_BLOCK_COMMENT_MODE:w,HASH_COMMENT_MODE:R,NUMBER_MODE:{className:"number",
+QUOTE_STRING_MODE:x,PHRASAL_WORDS_MODE:E,COMMENT:v,C_LINE_COMMENT_MODE:w,
+C_BLOCK_COMMENT_MODE:N,HASH_COMMENT_MODE:y,NUMBER_MODE:{className:"number",
 begin:h,relevance:0},C_NUMBER_MODE:{className:"number",begin:f,relevance:0},
 BINARY_NUMBER_MODE:{className:"number",begin:p,relevance:0},CSS_NUMBER_MODE:{
 className:"number",
@@ -67,16 +67,21 @@
 t.data._beginMatch!==e[1]&&t.ignoreMatch()}})});function _(e,t){
 "."===e.input[e.index-1]&&t.ignoreMatch()}function k(e,t){
 t&&e.beginKeywords&&(e.begin="\\b("+e.beginKeywords.split(" ").join("|")+")(?!\\.)(?=\\b|\\s)",
-e.__beforeBegin=_,e.keywords=e.keywords||e.beginKeywords,delete e.beginKeywords)
-}function M(e,t){
+e.__beforeBegin=_,e.keywords=e.keywords||e.beginKeywords,delete e.beginKeywords,
+void 0===e.relevance&&(e.relevance=0))}function O(e,t){
 Array.isArray(e.illegal)&&(e.illegal=((...e)=>"("+e.map((e=>u(e))).join("|")+")")(...e.illegal))
-}function O(e,t){if(e.match){
+}function M(e,t){if(e.match){
 if(e.begin||e.end)throw Error("begin & end are not supported with match")
 ;e.begin=e.match,delete e.match}}function A(e,t){
 void 0===e.relevance&&(e.relevance=1)}
 const L=["of","and","for","in","not","or","if","then","parent","list","value"]
-;function B(e,t){return t?Number(t):(e=>L.includes(e.toLowerCase()))(e)?0:1}
-function I(e,{plugins:t}){function n(t,n){
+;function B(e,t,n="keyword"){const s={}
+;return"string"==typeof e?r(n,e.split(" ")):Array.isArray(e)?r(n,e):Object.keys(e).forEach((n=>{
+Object.assign(s,B(e[n],t,n))})),s;function r(e,n){
+t&&(n=n.map((e=>e.toLowerCase()))),n.forEach((t=>{const n=t.split("|")
+;s[n[0]]=[e,I(n[0],n[1])]}))}}function I(e,t){
+return t?Number(t):(e=>L.includes(e.toLowerCase()))(e)?0:1}
+function T(e,{plugins:t}){function n(t,n){
 return RegExp(u(t),"m"+(e.case_insensitive?"i":"")+(n?"g":""))}class s{
 constructor(){
 this.matchIndexes={},this.regexes=[],this.matchAt=1,this.position=0}
@@ -112,14 +117,11 @@
 e.contains&&e.contains.includes("self"))throw Error("ERR: contains `self` is not supported at the top-level of a language.  See documentation.")
 ;return e.classNameAliases=a(e.classNameAliases||{}),function t(s,i){const o=s
 ;if(s.compiled)return o
-;[O].forEach((e=>e(s,i))),e.compilerExtensions.forEach((e=>e(s,i))),
-s.__beforeBegin=null,[k,M,A].forEach((e=>e(s,i))),s.compiled=!0;let l=null
+;[M].forEach((e=>e(s,i))),e.compilerExtensions.forEach((e=>e(s,i))),
+s.__beforeBegin=null,[k,O,A].forEach((e=>e(s,i))),s.compiled=!0;let l=null
 ;if("object"==typeof s.keywords&&(l=s.keywords.$pattern,
-delete s.keywords.$pattern),s.keywords&&(s.keywords=((e,t)=>{const n={}
-;return"string"==typeof e?s("keyword",e):Object.keys(e).forEach((t=>{s(t,e[t])
-})),n;function s(e,s){t&&(s=s.toLowerCase()),s.split(" ").forEach((t=>{
-const s=t.split("|");n[s[0]]=[e,B(s[0],s[1])]}))}
-})(s.keywords,e.case_insensitive)),
+delete s.keywords.$pattern),
+s.keywords&&(s.keywords=B(s.keywords,e.case_insensitive)),
 s.lexemes&&l)throw Error("ERR: Prefer `keywords.$pattern` to `mode.lexemes`, BOTH are not allowed. (see mode reference) ")
 ;return l=l||s.lexemes||/\w+/,
 o.keywordPatternRe=n(l,!0),i&&(s.begin||(s.begin=/\B|\b/),
@@ -129,14 +131,14 @@
 s.endsWithParent&&i.terminatorEnd&&(o.terminatorEnd+=(s.end?"|":"")+i.terminatorEnd)),
 s.illegal&&(o.illegalRe=n(s.illegal)),
 s.contains||(s.contains=[]),s.contains=[].concat(...s.contains.map((e=>(e=>(e.variants&&!e.cachedVariants&&(e.cachedVariants=e.variants.map((t=>a(e,{
-variants:null},t)))),e.cachedVariants?e.cachedVariants:T(e)?a(e,{
+variants:null},t)))),e.cachedVariants?e.cachedVariants:j(e)?a(e,{
 starts:e.starts?a(e.starts):null
 }):Object.isFrozen(e)?a(e):e))("self"===e?s:e)))),s.contains.forEach((e=>{t(e,o)
 })),s.starts&&t(s.starts,i),o.matcher=(e=>{const t=new r
 ;return e.contains.forEach((e=>t.addRule(e.begin,{rule:e,type:"begin"
 }))),e.terminatorEnd&&t.addRule(e.terminatorEnd,{type:"end"
-}),e.illegal&&t.addRule(e.illegal,{type:"illegal"}),t})(o),o}(e)}function T(e){
-return!!e&&(e.endsWithParent||T(e.starts))}function j(e){const t={
+}),e.illegal&&t.addRule(e.illegal,{type:"illegal"}),t})(o),o}(e)}function j(e){
+return!!e&&(e.endsWithParent||j(e.starts))}function S(e){const t={
 props:["language","code","autodetect"],data:()=>({detectedLanguage:"",
 unknownLanguage:!1}),computed:{className(){
 return this.unknownLanguage?"":"hljs "+this.detectedLanguage},highlighted(){
@@ -148,26 +150,26 @@
 return!(this.language&&(e=this.autodetect,!e&&""!==e));var e},
 ignoreIllegals:()=>!0},render(e){return e("pre",{},[e("code",{
 class:this.className,domProps:{innerHTML:this.highlighted}})])}};return{
-Component:t,VuePlugin:{install(e){e.component("highlightjs",t)}}}}const S={
-"after:highlightBlock":({block:e,result:t,text:n})=>{const s=D(e)
+Component:t,VuePlugin:{install(e){e.component("highlightjs",t)}}}}const P={
+"after:highlightBlock":({block:e,result:t,text:n})=>{const s=C(e)
 ;if(!s.length)return;const a=document.createElement("div")
 ;a.innerHTML=t.value,t.value=((e,t,n)=>{let s=0,a="";const i=[];function o(){
 return e.length&&t.length?e[0].offset!==t[0].offset?e[0].offset<t[0].offset?e:t:"start"===t[0].event?e:t:e.length?e:t
-}function l(e){a+="<"+P(e)+[].map.call(e.attributes,(function(e){
+}function l(e){a+="<"+D(e)+[].map.call(e.attributes,(function(e){
 return" "+e.nodeName+'="'+r(e.value)+'"'})).join("")+">"}function c(e){
-a+="</"+P(e)+">"}function u(e){("start"===e.event?l:c)(e.node)}
+a+="</"+D(e)+">"}function u(e){("start"===e.event?l:c)(e.node)}
 for(;e.length||t.length;){let t=o()
 ;if(a+=r(n.substring(s,t[0].offset)),s=t[0].offset,t===e){i.reverse().forEach(c)
 ;do{u(t.splice(0,1)[0]),t=o()}while(t===e&&t.length&&t[0].offset===s)
 ;i.reverse().forEach(l)
 }else"start"===t[0].event?i.push(t[0].node):i.pop(),u(t.splice(0,1)[0])}
-return a+r(n.substr(s))})(s,D(a),n)}};function P(e){
-return e.nodeName.toLowerCase()}function D(e){const t=[];return function e(n,s){
+return a+r(n.substr(s))})(s,C(a),n)}};function D(e){
+return e.nodeName.toLowerCase()}function C(e){const t=[];return function e(n,s){
 for(let r=n.firstChild;r;r=r.nextSibling)3===r.nodeType?s+=r.nodeValue.length:1===r.nodeType&&(t.push({
-event:"start",offset:s,node:r}),s=e(r,s),P(r).match(/br|hr|img|input/)||t.push({
-event:"stop",offset:s,node:r}));return s}(e,0),t}const C=e=>{console.error(e)
-},H=(e,...t)=>{console.log("WARN: "+e,...t)},$=(e,t)=>{
-console.log(`Deprecated as of ${e}. ${t}`)},U=r,z=a,K=Symbol("nomatch")
+event:"start",offset:s,node:r}),s=e(r,s),D(r).match(/br|hr|img|input/)||t.push({
+event:"stop",offset:s,node:r}));return s}(e,0),t}const H=e=>{console.error(e)
+},U=(e,...t)=>{console.log("WARN: "+e,...t)},$=(e,t)=>{
+console.log(`Deprecated as of ${e}. ${t}`)},z=r,K=a,G=Symbol("nomatch")
 ;return(e=>{const n=Object.create(null),r=Object.create(null),a=[];let i=!0
 ;const o=/(^(<[^>]+>|\t|)+|\n)/gm,l="Could not find the language '{}', did you forget to load/include a language module?",u={
 disableAutodetect:!0,name:"Plain text",contains:[]};let g={
@@ -175,71 +177,71 @@
 languageDetectRe:/\blang(?:uage)?-([\w-]+)\b/i,classPrefix:"hljs-",
 tabReplace:null,useBR:!1,languages:null,__emitter:c};function d(e){
 return g.noHighlightRe.test(e)}function h(e,t,n,s){const r={code:t,language:e}
-;_("before:highlight",r);const a=r.result?r.result:f(r.language,r.code,n,s)
-;return a.code=r.code,_("after:highlight",a),a}function f(e,t,r,o){const c=t
+;M("before:highlight",r);const a=r.result?r.result:f(r.language,r.code,n,s)
+;return a.code=r.code,M("after:highlight",a),a}function f(e,t,r,o){const c=t
 ;function u(e,t){const n=w.case_insensitive?t[0].toLowerCase():t[0]
 ;return Object.prototype.hasOwnProperty.call(e.keywords,n)&&e.keywords[n]}
-function d(){null!=_.subLanguage?(()=>{if(""===O)return;let e=null
-;if("string"==typeof _.subLanguage){
-if(!n[_.subLanguage])return void M.addText(O)
-;e=f(_.subLanguage,O,!0,k[_.subLanguage]),k[_.subLanguage]=e.top
-}else e=p(O,_.subLanguage.length?_.subLanguage:null)
-;_.relevance>0&&(A+=e.relevance),M.addSublanguage(e.emitter,e.language)
-})():(()=>{if(!_.keywords)return void M.addText(O);let e=0
-;_.keywordPatternRe.lastIndex=0;let t=_.keywordPatternRe.exec(O),n="";for(;t;){
-n+=O.substring(e,t.index);const s=u(_,t);if(s){const[e,r]=s
-;M.addText(n),n="",A+=r;const a=w.classNameAliases[e]||e;M.addKeyword(t[0],a)
-}else n+=t[0];e=_.keywordPatternRe.lastIndex,t=_.keywordPatternRe.exec(O)}
-n+=O.substr(e),M.addText(n)})(),O=""}function h(e){
-return e.className&&M.openNode(w.classNameAliases[e.className]||e.className),
-_=Object.create(e,{parent:{value:_}}),_}function m(e,t,n){let r=((e,t)=>{
+function d(){null!=R.subLanguage?(()=>{if(""===M)return;let e=null
+;if("string"==typeof R.subLanguage){
+if(!n[R.subLanguage])return void O.addText(M)
+;e=f(R.subLanguage,M,!0,k[R.subLanguage]),k[R.subLanguage]=e.top
+}else e=p(M,R.subLanguage.length?R.subLanguage:null)
+;R.relevance>0&&(A+=e.relevance),O.addSublanguage(e.emitter,e.language)
+})():(()=>{if(!R.keywords)return void O.addText(M);let e=0
+;R.keywordPatternRe.lastIndex=0;let t=R.keywordPatternRe.exec(M),n="";for(;t;){
+n+=M.substring(e,t.index);const s=u(R,t);if(s){const[e,r]=s
+;O.addText(n),n="",A+=r;const a=w.classNameAliases[e]||e;O.addKeyword(t[0],a)
+}else n+=t[0];e=R.keywordPatternRe.lastIndex,t=R.keywordPatternRe.exec(M)}
+n+=M.substr(e),O.addText(n)})(),M=""}function h(e){
+return e.className&&O.openNode(w.classNameAliases[e.className]||e.className),
+R=Object.create(e,{parent:{value:R}}),R}function m(e,t,n){let r=((e,t)=>{
 const n=e&&e.exec(t);return n&&0===n.index})(e.endRe,n);if(r){if(e["on:end"]){
 const n=new s(e);e["on:end"](t,n),n.ignore&&(r=!1)}if(r){
 for(;e.endsParent&&e.parent;)e=e.parent;return e}}
 if(e.endsWithParent)return m(e.parent,t,n)}function b(e){
-return 0===_.matcher.regexIndex?(O+=e[0],1):(T=!0,0)}function x(e){
-const t=e[0],n=c.substr(e.index),s=m(_,e,n);if(!s)return K;const r=_
-;r.skip?O+=t:(r.returnEnd||r.excludeEnd||(O+=t),d(),r.excludeEnd&&(O=t));do{
-_.className&&M.closeNode(),_.skip||_.subLanguage||(A+=_.relevance),_=_.parent
-}while(_!==s.parent)
+return 0===R.matcher.regexIndex?(M+=e[0],1):(I=!0,0)}function x(e){
+const t=e[0],n=c.substr(e.index),s=m(R,e,n);if(!s)return G;const r=R
+;r.skip?M+=t:(r.returnEnd||r.excludeEnd||(M+=t),d(),r.excludeEnd&&(M=t));do{
+R.className&&O.closeNode(),R.skip||R.subLanguage||(A+=R.relevance),R=R.parent
+}while(R!==s.parent)
 ;return s.starts&&(s.endSameAsBegin&&(s.starts.endRe=s.endRe),
 h(s.starts)),r.returnEnd?0:t.length}let E={};function v(t,n){const a=n&&n[0]
-;if(O+=t,null==a)return d(),0
+;if(M+=t,null==a)return d(),0
 ;if("begin"===E.type&&"end"===n.type&&E.index===n.index&&""===a){
-if(O+=c.slice(n.index,n.index+1),!i){const t=Error("0 width match regex")
+if(M+=c.slice(n.index,n.index+1),!i){const t=Error("0 width match regex")
 ;throw t.languageName=e,t.badRule=E.rule,t}return 1}
 if(E=n,"begin"===n.type)return function(e){
 const t=e[0],n=e.rule,r=new s(n),a=[n.__beforeBegin,n["on:begin"]]
 ;for(const n of a)if(n&&(n(e,r),r.ignore))return b(t)
 ;return n&&n.endSameAsBegin&&(n.endRe=RegExp(t.replace(/[-/\\^$*+?.()|[\]{}]/g,"\\$&"),"m")),
-n.skip?O+=t:(n.excludeBegin&&(O+=t),
-d(),n.returnBegin||n.excludeBegin||(O=t)),h(n),n.returnBegin?0:t.length}(n)
+n.skip?M+=t:(n.excludeBegin&&(M+=t),
+d(),n.returnBegin||n.excludeBegin||(M=t)),h(n),n.returnBegin?0:t.length}(n)
 ;if("illegal"===n.type&&!r){
-const e=Error('Illegal lexeme "'+a+'" for mode "'+(_.className||"<unnamed>")+'"')
-;throw e.mode=_,e}if("end"===n.type){const e=x(n);if(e!==K)return e}
+const e=Error('Illegal lexeme "'+a+'" for mode "'+(R.className||"<unnamed>")+'"')
+;throw e.mode=R,e}if("end"===n.type){const e=x(n);if(e!==G)return e}
 if("illegal"===n.type&&""===a)return 1
 ;if(B>1e5&&B>3*n.index)throw Error("potential infinite loop, way more iterations than matches")
-;return O+=a,a.length}const w=N(e)
-;if(!w)throw C(l.replace("{}",e)),Error('Unknown language: "'+e+'"')
-;const R=I(w,{plugins:a});let y="",_=o||R;const k={},M=new g.__emitter(g);(()=>{
-const e=[];for(let t=_;t!==w;t=t.parent)t.className&&e.unshift(t.className)
-;e.forEach((e=>M.openNode(e)))})();let O="",A=0,L=0,B=0,T=!1;try{
-for(_.matcher.considerAll();;){
-B++,T?T=!1:_.matcher.considerAll(),_.matcher.lastIndex=L
-;const e=_.matcher.exec(c);if(!e)break;const t=v(c.substring(L,e.index),e)
-;L=e.index+t}return v(c.substr(L)),M.closeAllNodes(),M.finalize(),y=M.toHTML(),{
-relevance:A,value:y,language:e,illegal:!1,emitter:M,top:_}}catch(t){
+;return M+=a,a.length}const w=_(e)
+;if(!w)throw H(l.replace("{}",e)),Error('Unknown language: "'+e+'"')
+;const N=T(w,{plugins:a});let y="",R=o||N;const k={},O=new g.__emitter(g);(()=>{
+const e=[];for(let t=R;t!==w;t=t.parent)t.className&&e.unshift(t.className)
+;e.forEach((e=>O.openNode(e)))})();let M="",A=0,L=0,B=0,I=!1;try{
+for(R.matcher.considerAll();;){
+B++,I?I=!1:R.matcher.considerAll(),R.matcher.lastIndex=L
+;const e=R.matcher.exec(c);if(!e)break;const t=v(c.substring(L,e.index),e)
+;L=e.index+t}return v(c.substr(L)),O.closeAllNodes(),O.finalize(),y=O.toHTML(),{
+relevance:Math.floor(A),value:y,language:e,illegal:!1,emitter:O,top:R}}catch(t){
 if(t.message&&t.message.includes("Illegal"))return{illegal:!0,illegalBy:{
 msg:t.message,context:c.slice(L-100,L+100),mode:t.mode},sofar:y,relevance:0,
-value:U(c),emitter:M};if(i)return{illegal:!1,relevance:0,value:U(c),emitter:M,
-language:e,top:_,errorRaised:t};throw t}}function p(e,t){
+value:z(c),emitter:O};if(i)return{illegal:!1,relevance:0,value:z(c),emitter:O,
+language:e,top:R,errorRaised:t};throw t}}function p(e,t){
 t=t||g.languages||Object.keys(n);const s=(e=>{const t={relevance:0,
-emitter:new g.__emitter(g),value:U(e),illegal:!1,top:u}
-;return t.emitter.addText(e),t})(e),r=t.filter(N).filter(R).map((t=>f(t,e,!1)))
+emitter:new g.__emitter(g),value:z(e),illegal:!1,top:u}
+;return t.emitter.addText(e),t})(e),r=t.filter(_).filter(O).map((t=>f(t,e,!1)))
 ;r.unshift(s);const a=r.sort(((e,t)=>{
 if(e.relevance!==t.relevance)return t.relevance-e.relevance
-;if(e.language&&t.language){if(N(e.language).supersetOf===t.language)return 1
-;if(N(t.language).supersetOf===e.language)return-1}return 0})),[i,o]=a,l=i
+;if(e.language&&t.language){if(_(e.language).supersetOf===t.language)return 1
+;if(_(t.language).supersetOf===e.language)return-1}return 0})),[i,o]=a,l=i
 ;return l.second_best=o,l}const m={"before:highlightBlock":({block:e})=>{
 g.useBR&&(e.innerHTML=e.innerHTML.replace(/\n/g,"").replace(/<br[ /]*>/g,"\n"))
 },"after:highlightBlock":({result:e})=>{
@@ -248,45 +250,49 @@
 g.tabReplace&&(e.value=e.value.replace(b,(e=>e.replace(/\t/g,g.tabReplace))))}}
 ;function E(e){let t=null;const n=(e=>{let t=e.className+" "
 ;t+=e.parentNode?e.parentNode.className:"";const n=g.languageDetectRe.exec(t)
-;if(n){const t=N(n[1])
-;return t||(H(l.replace("{}",n[1])),H("Falling back to no-highlight mode for this block.",e)),
-t?n[1]:"no-highlight"}return t.split(/\s+/).find((e=>d(e)||N(e)))})(e)
-;if(d(n))return;_("before:highlightBlock",{block:e,language:n}),t=e
-;const s=t.textContent,a=n?h(n,s,!0):p(s);_("after:highlightBlock",{block:e,
+;if(n){const t=_(n[1])
+;return t||(U(l.replace("{}",n[1])),U("Falling back to no-highlight mode for this block.",e)),
+t?n[1]:"no-highlight"}return t.split(/\s+/).find((e=>d(e)||_(e)))})(e)
+;if(d(n))return;M("before:highlightBlock",{block:e,language:n}),t=e
+;const s=t.textContent,a=n?h(n,s,!0):p(s);M("after:highlightBlock",{block:e,
 result:a,text:s}),e.innerHTML=a.value,((e,t,n)=>{const s=t?r[t]:n
 ;e.classList.add("hljs"),s&&e.classList.add(s)})(e,n,a.language),e.result={
 language:a.language,re:a.relevance,relavance:a.relevance
 },a.second_best&&(e.second_best={language:a.second_best.language,
 re:a.second_best.relevance,relavance:a.second_best.relevance})}const v=()=>{
-v.called||(v.called=!0,document.querySelectorAll("pre code").forEach(E))}
-;function N(e){return e=(e||"").toLowerCase(),n[e]||n[r[e]]}
-function w(e,{languageName:t}){"string"==typeof e&&(e=[e]),e.forEach((e=>{r[e]=t
-}))}function R(e){const t=N(e);return t&&!t.disableAutodetect}function _(e,t){
-const n=e;a.forEach((e=>{e[n]&&e[n](t)}))}Object.assign(e,{highlight:h,
-highlightAuto:p,fixMarkup:e=>{
-return $("10.2.0","fixMarkup will be removed entirely in v11.0"),
-$("10.2.0","Please see https://github.com/highlightjs/highlight.js/issues/2534"),
+v.called||(v.called=!0,
+$("10.6.0","initHighlighting() is deprecated.  Use highlightAll() instead."),
+document.querySelectorAll("pre code").forEach(E))};let w=!1,N=!1;function y(){
+N?document.querySelectorAll("pre code").forEach(E):w=!0}function _(e){
+return e=(e||"").toLowerCase(),n[e]||n[r[e]]}function k(e,{languageName:t}){
+"string"==typeof e&&(e=[e]),e.forEach((e=>{r[e]=t}))}function O(e){const t=_(e)
+;return t&&!t.disableAutodetect}function M(e,t){const n=e;a.forEach((e=>{
+e[n]&&e[n](t)}))}
+"undefined"!=typeof window&&window.addEventListener&&window.addEventListener("DOMContentLoaded",(()=>{
+N=!0,w&&y()}),!1),Object.assign(e,{highlight:h,highlightAuto:p,highlightAll:y,
+fixMarkup:e=>{
+return $("10.2.0","fixMarkup will be removed entirely in v11.0"),$("10.2.0","Please see https://github.com/highlightjs/highlight.js/issues/2534"),
 t=e,
 g.tabReplace||g.useBR?t.replace(o,(e=>"\n"===e?g.useBR?"<br>":e:g.tabReplace?e.replace(/\t/g,g.tabReplace):e)):t
 ;var t},highlightBlock:E,configure:e=>{
 e.useBR&&($("10.3.0","'useBR' will be removed entirely in v11.0"),
 $("10.3.0","Please see https://github.com/highlightjs/highlight.js/issues/2559")),
-g=z(g,e)},initHighlighting:v,initHighlightingOnLoad:()=>{
-window.addEventListener("DOMContentLoaded",v,!1)},registerLanguage:(t,s)=>{
-let r=null;try{r=s(e)}catch(e){
-if(C("Language definition for '{}' could not be registered.".replace("{}",t)),
-!i)throw e;C(e),r=u}
-r.name||(r.name=t),n[t]=r,r.rawDefinition=s.bind(null,e),r.aliases&&w(r.aliases,{
-languageName:t})},listLanguages:()=>Object.keys(n),getLanguage:N,
-registerAliases:w,requireLanguage:e=>{
+g=K(g,e)},initHighlighting:v,initHighlightingOnLoad:()=>{
+$("10.6.0","initHighlightingOnLoad() is deprecated.  Use highlightAll() instead."),
+w=!0},registerLanguage:(t,s)=>{let r=null;try{r=s(e)}catch(e){
+if(H("Language definition for '{}' could not be registered.".replace("{}",t)),
+!i)throw e;H(e),r=u}
+r.name||(r.name=t),n[t]=r,r.rawDefinition=s.bind(null,e),r.aliases&&k(r.aliases,{
+languageName:t})},listLanguages:()=>Object.keys(n),getLanguage:_,
+registerAliases:k,requireLanguage:e=>{
 $("10.4.0","requireLanguage will be removed entirely in v11."),
 $("10.4.0","Please see https://github.com/highlightjs/highlight.js/pull/2844")
-;const t=N(e);if(t)return t
+;const t=_(e);if(t)return t
 ;throw Error("The '{}' language is required, but not loaded.".replace("{}",e))},
-autoDetection:R,inherit:z,addPlugin:e=>{a.push(e)},vuePlugin:j(e).VuePlugin
-}),e.debugMode=()=>{i=!1},e.safeMode=()=>{i=!0},e.versionString="10.5.0"
-;for(const e in y)"object"==typeof y[e]&&t(y[e])
-;return Object.assign(e,y),e.addPlugin(m),e.addPlugin(S),e.addPlugin(x),e})({})
+autoDetection:O,inherit:K,addPlugin:e=>{a.push(e)},vuePlugin:S(e).VuePlugin
+}),e.debugMode=()=>{i=!1},e.safeMode=()=>{i=!0},e.versionString="10.6.0"
+;for(const e in R)"object"==typeof R[e]&&t(R[e])
+;return Object.assign(e,R),e.addPlugin(m),e.addPlugin(P),e.addPlugin(x),e})({})
 }();"object"==typeof exports&&"undefined"!=typeof module&&(module.exports=hljs);
 hljs.registerLanguage("1c",(()=>{"use strict";return s=>{
 var x="[A-Za-z\u0410-\u042f\u0430-\u044f\u0451\u0401_][A-Za-z\u0410-\u042f\u0430-\u044f\u0451\u0401_0-9]+",n="\u0434\u0430\u043b\u0435\u0435 \u0432\u043e\u0437\u0432\u0440\u0430\u0442 \u0432\u044b\u0437\u0432\u0430\u0442\u044c\u0438\u0441\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435 \u0432\u044b\u043f\u043e\u043b\u043d\u0438\u0442\u044c \u0434\u043b\u044f \u0435\u0441\u043b\u0438 \u0438 \u0438\u0437 \u0438\u043b\u0438 \u0438\u043d\u0430\u0447\u0435 \u0438\u043d\u0430\u0447\u0435\u0435\u0441\u043b\u0438 \u0438\u0441\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435 \u043a\u0430\u0436\u0434\u043e\u0433\u043e \u043a\u043e\u043d\u0435\u0446\u0435\u0441\u043b\u0438 \u043a\u043e\u043d\u0435\u0446\u043f\u043e\u043f\u044b\u0442\u043a\u0438 \u043a\u043e\u043d\u0435\u0446\u0446\u0438\u043a\u043b\u0430 \u043d\u0435 \u043d\u043e\u0432\u044b\u0439 \u043f\u0435\u0440\u0435\u0439\u0442\u0438 \u043f\u0435\u0440\u0435\u043c \u043f\u043e \u043f\u043e\u043a\u0430 \u043f\u043e\u043f\u044b\u0442\u043a\u0430 \u043f\u0440\u0435\u0440\u0432\u0430\u0442\u044c \u043f\u0440\u043e\u0434\u043e\u043b\u0436\u0438\u0442\u044c \u0442\u043e\u0433\u0434\u0430 \u0446\u0438\u043a\u043b \u044d\u043a\u0441\u043f\u043e\u0440\u0442 ",e="null \u0438\u0441\u0442\u0438\u043d\u0430 \u043b\u043e\u0436\u044c \u043d\u0435\u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u043e",o=s.inherit(s.NUMBER_MODE),t={
@@ -317,7 +323,7 @@
 unexpectedChars:/[!@#$^&',?+~`|:]/},n=a.COMMENT(/;/,/$/),r={
 className:"attribute",begin:e(s.ruleDeclaration,/(?=\s*=)/)};return{
 name:"Augmented Backus-Naur Form",illegal:s.unexpectedChars,
-keywords:"ALPHA BIT CHAR CR CRLF CTL DIGIT DQUOTE HEXDIG HTAB LF LWSP OCTET SP VCHAR WSP",
+keywords:["ALPHA","BIT","CHAR","CR","CRLF","CTL","DIGIT","DQUOTE","HEXDIG","HTAB","LF","LWSP","OCTET","SP","VCHAR","WSP"],
 contains:[r,n,{className:"symbol",begin:/%b[0-1]+(-[0-1]+|(\.[0-1]+)+){0,1}/},{
 className:"symbol",begin:/%d[0-9]+(-[0-9]+|(\.[0-9]+)+){0,1}/},{
 className:"symbol",begin:/%x[0-9A-F]+(-[0-9A-F]+|(\.[0-9A-F]+)+){0,1}/},{
@@ -330,8 +336,8 @@
 ;return{name:"Apache Access Log",contains:[{className:"number",
 begin:/^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}(:\d{1,5})?\b/,relevance:5},{
 className:"number",begin:/\b\d+\b/,relevance:0},{className:"string",
-begin:n(/"/,a(...l)),end:/"/,keywords:l.join(" "),illegal:/\n/,relevance:5,
-contains:[{begin:/HTTP\/[12]\.\d'/,relevance:5}]},{className:"string",
+begin:n(/"/,a(...l)),end:/"/,keywords:l,illegal:/\n/,relevance:5,contains:[{
+begin:/HTTP\/[12]\.\d'/,relevance:5}]},{className:"string",
 begin:/\[\d[^\]\n]{8,}\]/,illegal:/\n/,relevance:1},{className:"string",
 begin:/\[/,end:/\]/,illegal:/\n/,relevance:0},{className:"string",
 begin:/"Mozilla\/\d\.\d \(/,end:/"/,illegal:/\n/,relevance:3},{
@@ -459,8 +465,7 @@
 begin:/\$[(.]/}],illegal:/#(?!!)/}}})());
 hljs.registerLanguage("arduino",(()=>{"use strict";function e(e){
 return((...e)=>e.map((e=>(e=>e?"string"==typeof e?e:e.source:null)(e))).join(""))("(",e,")?")
-}return t=>{const r=function(t){const r=(t=>{const r=t.COMMENT("//","$",{
-contains:[{begin:/\\\n/}]
+}return t=>{const r=(t=>{const r=t.COMMENT("//","$",{contains:[{begin:/\\\n/}]
 }),n="[a-zA-Z_]\\w*::",i="(decltype\\(auto\\)|"+e(n)+"[a-zA-Z_]\\w*"+e("<[^<>]+>")+")",a={
 className:"keyword",begin:"\\b[a-z\\d_]*_t\\b"},s={className:"string",
 variants:[{begin:'(u8?|U|L)?"',end:'"',illegal:"\\n",
@@ -491,16 +496,14 @@
 end:/\)/,keywords:u,relevance:0,contains:[r,t.C_BLOCK_COMMENT_MODE,s,o,a,{
 begin:/\(/,end:/\)/,keywords:u,relevance:0,
 contains:["self",r,t.C_BLOCK_COMMENT_MODE,s,o,a]}]
-},a,r,t.C_BLOCK_COMMENT_MODE,l]};return{
-aliases:["c","cc","h","c++","h++","hpp","hh","hxx","cxx"],keywords:u,
-disableAutodetect:!0,illegal:"</",contains:[].concat(p,g,m,[l,{
+},a,r,t.C_BLOCK_COMMENT_MODE,l]};return{name:"C++",
+aliases:["cc","c++","h++","hpp","hh","hxx","cxx"],keywords:u,illegal:"</",
+contains:[].concat(p,g,m,[l,{
 begin:"\\b(deque|list|queue|priority_queue|pair|stack|vector|map|set|bitset|multiset|multimap|unordered_map|unordered_set|unordered_multiset|unordered_multimap|array)\\s*<",
 end:">",keywords:u,contains:["self",a]},{begin:t.IDENT_RE+"::",keywords:u},{
 className:"class",beginKeywords:"enum class struct union",end:/[{;:<>=]/,
 contains:[{beginKeywords:"final class struct"},t.TITLE_MODE]}]),exports:{
-preprocessor:l,strings:s,keywords:u}}})(t)
-;return r.disableAutodetect=!1,r.name="C++",
-r.aliases=["cc","c++","h++","hpp","hh","hxx","cxx"],r}(t),n=r.keywords
+preprocessor:l,strings:s,keywords:u}}})(t),n=r.keywords
 ;return n.keyword+=" boolean byte word String",
 n.literal+=" DIGITAL_MESSAGE FIRMATA_STRING ANALOG_MESSAGE REPORT_DIGITAL REPORT_ANALOG INPUT_PULLUP SET_PIN_MODE INTERNAL2V56 SYSTEM_RESET LED_BUILTIN INTERNAL1V1 SYSEX_START INTERNAL EXTERNAL DEFAULT OUTPUT INPUT HIGH LOW",
 n.built_in+=" setup loop KeyboardController MouseController SoftwareSerial EthernetServer EthernetClient LiquidCrystal RobotControl GSMVoiceCall EthernetUDP EsploraTFT HttpClient RobotMotor WiFiClient GSMScanner FileSystem Scheduler GSMServer YunClient YunServer IPAddress GSMClient GSMModem Keyboard Ethernet Console GSMBand Esplora Stepper Process WiFiUDP GSM_SMS Mailbox USBHost Firmata PImage Client Server GSMPIN FileIO Bridge Serial EEPROM Stream Mouse Audio Servo File Task GPRS WiFi Wire TFT GSM SPI SD runShellCommandAsynchronously analogWriteResolution retrieveCallingNumber printFirmwareVersion analogReadResolution sendDigitalPortPair noListenOnLocalhost readJoystickButton setFirmwareVersion readJoystickSwitch scrollDisplayRight getVoiceCallStatus scrollDisplayLeft writeMicroseconds delayMicroseconds beginTransmission getSignalStrength runAsynchronously getAsynchronously listenOnLocalhost getCurrentCarrier readAccelerometer messageAvailable sendDigitalPorts lineFollowConfig countryNameWrite runShellCommand readStringUntil rewindDirectory readTemperature setClockDivider readLightSensor endTransmission analogReference detachInterrupt countryNameRead attachInterrupt encryptionType readBytesUntil robotNameWrite readMicrophone robotNameRead cityNameWrite userNameWrite readJoystickY readJoystickX mouseReleased openNextFile scanNetworks noInterrupts digitalWrite beginSpeaker mousePressed isActionDone mouseDragged displayLogos noAutoscroll addParameter remoteNumber getModifiers keyboardRead userNameRead waitContinue processInput parseCommand printVersion readNetworks writeMessage blinkVersion cityNameRead readMessage setDataMode parsePacket isListening setBitOrder beginPacket isDirectory motorsWrite drawCompass digitalRead clearScreen serialEvent rightToLeft setTextSize leftToRight requestFrom keyReleased compassRead analogWrite interrupts WiFiServer disconnect playMelody parseFloat autoscroll getPINUsed setPINUsed setTimeout sendAnalog readSlider analogRead beginWrite createChar motorsStop keyPressed tempoWrite readButton subnetMask debugPrint macAddress writeGreen randomSeed attachGPRS readString sendString remotePort releaseAll mouseMoved background getXChange getYChange answerCall getResult voiceCall endPacket constrain getSocket writeJSON getButton available connected findUntil readBytes exitValue readGreen writeBlue startLoop IPAddress isPressed sendSysex pauseMode gatewayIP setCursor getOemKey tuneWrite noDisplay loadImage switchPIN onRequest onReceive changePIN playFile noBuffer parseInt overflow checkPIN knobRead beginTFT bitClear updateIR bitWrite position writeRGB highByte writeRed setSpeed readBlue noStroke remoteIP transfer shutdown hangCall beginSMS endWrite attached maintain noCursor checkReg checkPUK shiftOut isValid shiftIn pulseIn connect println localIP pinMode getIMEI display noBlink process getBand running beginSD drawBMP lowByte setBand release bitRead prepare pointTo readRed setMode noFill remove listen stroke detach attach noTone exists buffer height bitSet circle config cursor random IRread setDNS endSMS getKey micros millis begin print write ready flush width isPIN blink clear press mkdir rmdir close point yield image BSSID click delay read text move peek beep rect line open seek fill size turn stop home find step tone sqrt RSSI SSID end bit tan cos sin pow map abs max min get run put",
@@ -524,7 +527,7 @@
 return e?"string"==typeof e?e:e.source:null}function n(e){return a("(?=",e,")")}
 function a(...n){return n.map((n=>e(n))).join("")}function s(...n){
 return"("+n.map((n=>e(n))).join("|")+")"}return e=>{
-const t=a(/[A-Z_]/,a("(",/[A-Z0-9_.-]+:/,")?"),/[A-Z0-9_.-]*/),i={
+const t=a(/[A-Z_]/,a("(",/[A-Z0-9_.-]*:/,")?"),/[A-Z0-9_.-]*/),i={
 className:"symbol",begin:/&[a-z]+;|&#[0-9]+;|&#x[a-f0-9]+;/},r={begin:/\s/,
 contains:[{className:"meta-keyword",begin:/#?[a-z_][a-z1-9_-]+/,illegal:/\n/}]
 },c=e.inherit(r,{begin:/\(/,end:/\)/}),l=e.inherit(e.APOS_STRING_MODE,{
@@ -671,9 +674,9 @@
 },e.REGEXP_MODE,e.HASH_COMMENT_MODE,e.NUMBER_MODE]})})());
 hljs.registerLanguage("axapta",(()=>{"use strict";return e=>({name:"X++",
 aliases:["x++"],keywords:{
-keyword:"abstract as asc avg break breakpoint by byref case catch changecompany class client client common const continue count crosscompany delegate delete_from desc display div do edit else eventhandler exists extends final finally firstfast firstonly firstonly1 firstonly10 firstonly100 firstonly1000 flush for forceliterals forcenestedloop forceplaceholders forceselectorder forupdate from generateonly group hint if implements in index insert_recordset interface internal is join like maxof minof mod namespace new next nofetch notexists optimisticlock order outer pessimisticlock print private protected public readonly repeatableread retry return reverse select server setting static sum super switch this throw try ttsabort ttsbegin ttscommit unchecked update_recordset using validtimestate void where while",
-built_in:"anytype boolean byte char container date double enum guid int int64 long real short str utcdatetime var",
-literal:"default false null true"},
+keyword:["abstract","as","asc","avg","break","breakpoint","by","byref","case","catch","changecompany","class","client","client","common","const","continue","count","crosscompany","delegate","delete_from","desc","display","div","do","edit","else","eventhandler","exists","extends","final","finally","firstfast","firstonly","firstonly1","firstonly10","firstonly100","firstonly1000","flush","for","forceliterals","forcenestedloop","forceplaceholders","forceselectorder","forupdate","from","generateonly","group","hint","if","implements","in","index","insert_recordset","interface","internal","is","join","like","maxof","minof","mod","namespace","new","next","nofetch","notexists","optimisticlock","order","outer","pessimisticlock","print","private","protected","public","readonly","repeatableread","retry","return","reverse","select","server","setting","static","sum","super","switch","this","throw","try","ttsabort","ttsbegin","ttscommit","unchecked","update_recordset","using","validtimestate","void","where","while"],
+built_in:["anytype","boolean","byte","char","container","date","double","enum","guid","int","int64","long","real","short","str","utcdatetime","var"],
+literal:["default","false","null","true"]},
 contains:[e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,e.C_NUMBER_MODE,{
 className:"meta",begin:"#",end:"$"},{className:"class",
 beginKeywords:"class interface",end:/\{/,excludeEnd:!0,illegal:":",contains:[{
@@ -719,6 +722,51 @@
 },n]}}})());
 hljs.registerLanguage("c-like",(()=>{"use strict";function e(e){
 return((...e)=>e.map((e=>(e=>e?"string"==typeof e?e:e.source:null)(e))).join(""))("(",e,")?")
+}return t=>{const n=(t=>{const n=t.COMMENT("//","$",{contains:[{begin:/\\\n/}]
+}),a="[a-zA-Z_]\\w*::",r="(decltype\\(auto\\)|"+e(a)+"[a-zA-Z_]\\w*"+e("<[^<>]+>")+")",s={
+className:"keyword",begin:"\\b[a-z\\d_]*_t\\b"},i={className:"string",
+variants:[{begin:'(u8?|U|L)?"',end:'"',illegal:"\\n",
+contains:[t.BACKSLASH_ESCAPE]},{
+begin:"(u8?|U|L)?'(\\\\(x[0-9A-Fa-f]{2}|u[0-9A-Fa-f]{4,8}|[0-7]{3}|\\S)|.)",
+end:"'",illegal:"."},t.END_SAME_AS_BEGIN({
+begin:/(?:u8?|U|L)?R"([^()\\ ]{0,16})\(/,end:/\)([^()\\ ]{0,16})"/})]},c={
+className:"number",variants:[{begin:"\\b(0b[01']+)"},{
+begin:"(-?)\\b([\\d']+(\\.[\\d']*)?|\\.[\\d']+)((ll|LL|l|L)(u|U)?|(u|U)(ll|LL|l|L)?|f|F|b|B)"
+},{
+begin:"(-?)(\\b0[xX][a-fA-F0-9']+|(\\b[\\d']+(\\.[\\d']*)?|\\.[\\d']+)([eE][-+]?[\\d']+)?)"
+}],relevance:0},o={className:"meta",begin:/#\s*[a-z]+\b/,end:/$/,keywords:{
+"meta-keyword":"if else elif endif define undef warning error line pragma _Pragma ifdef ifndef include"
+},contains:[{begin:/\\\n/,relevance:0},t.inherit(i,{className:"meta-string"}),{
+className:"meta-string",begin:/<.*?>/,end:/$/,illegal:"\\n"
+},n,t.C_BLOCK_COMMENT_MODE]},l={className:"title",begin:e(a)+t.IDENT_RE,
+relevance:0},d=e(a)+t.IDENT_RE+"\\s*\\(",u={
+keyword:"int float while private char char8_t char16_t char32_t catch import module export virtual operator sizeof dynamic_cast|10 typedef const_cast|10 const for static_cast|10 union namespace unsigned long volatile static protected bool template mutable if public friend do goto auto void enum else break extern using asm case typeid wchar_t short reinterpret_cast|10 default double register explicit signed typename try this switch continue inline delete alignas alignof constexpr consteval constinit decltype concept co_await co_return co_yield requires noexcept static_assert thread_local restrict final override atomic_bool atomic_char atomic_schar atomic_uchar atomic_short atomic_ushort atomic_int atomic_uint atomic_long atomic_ulong atomic_llong atomic_ullong new throw return and and_eq bitand bitor compl not not_eq or or_eq xor xor_eq",
+built_in:"std string wstring cin cout cerr clog stdin stdout stderr stringstream istringstream ostringstream auto_ptr deque list queue stack vector map set pair bitset multiset multimap unordered_set unordered_map unordered_multiset unordered_multimap priority_queue make_pair array shared_ptr abort terminate abs acos asin atan2 atan calloc ceil cosh cos exit exp fabs floor fmod fprintf fputs free frexp fscanf future isalnum isalpha iscntrl isdigit isgraph islower isprint ispunct isspace isupper isxdigit tolower toupper labs ldexp log10 log malloc realloc memchr memcmp memcpy memset modf pow printf putchar puts scanf sinh sin snprintf sprintf sqrt sscanf strcat strchr strcmp strcpy strcspn strlen strncat strncmp strncpy strpbrk strrchr strspn strstr tanh tan vfprintf vprintf vsprintf endl initializer_list unique_ptr _Bool complex _Complex imaginary _Imaginary",
+literal:"true false nullptr NULL"},p=[o,s,n,t.C_BLOCK_COMMENT_MODE,c,i],m={
+variants:[{begin:/=/,end:/;/},{begin:/\(/,end:/\)/},{
+beginKeywords:"new throw return else",end:/;/}],keywords:u,contains:p.concat([{
+begin:/\(/,end:/\)/,keywords:u,contains:p.concat(["self"]),relevance:0}]),
+relevance:0},_={className:"function",begin:"("+r+"[\\*&\\s]+)+"+d,
+returnBegin:!0,end:/[{;=]/,excludeEnd:!0,keywords:u,illegal:/[^\w\s\*&:<>.]/,
+contains:[{begin:"decltype\\(auto\\)",keywords:u,relevance:0},{begin:d,
+returnBegin:!0,contains:[l],relevance:0},{className:"params",begin:/\(/,
+end:/\)/,keywords:u,relevance:0,contains:[n,t.C_BLOCK_COMMENT_MODE,i,c,s,{
+begin:/\(/,end:/\)/,keywords:u,relevance:0,
+contains:["self",n,t.C_BLOCK_COMMENT_MODE,i,c,s]}]
+},s,n,t.C_BLOCK_COMMENT_MODE,o]};return{name:"C++",
+aliases:["cc","c++","h++","hpp","hh","hxx","cxx"],keywords:u,illegal:"</",
+contains:[].concat(m,_,p,[o,{
+begin:"\\b(deque|list|queue|priority_queue|pair|stack|vector|map|set|bitset|multiset|multimap|unordered_map|unordered_set|unordered_multiset|unordered_multimap|array)\\s*<",
+end:">",keywords:u,contains:["self",s]},{begin:t.IDENT_RE+"::",keywords:u},{
+className:"class",beginKeywords:"enum class struct union",end:/[{;:<>=]/,
+contains:[{beginKeywords:"final class struct"},t.TITLE_MODE]}]),exports:{
+preprocessor:o,strings:i,keywords:u}}})(t)
+;return n.disableAutodetect=!0,n.aliases=[],
+t.getLanguage("c")||n.aliases.push("c","h"),
+t.getLanguage("cpp")||n.aliases.push("cc","c++","h++","hpp","hh","hxx","cxx"),n}
+})());
+hljs.registerLanguage("c",(()=>{"use strict";function e(e){
+return((...e)=>e.map((e=>(e=>e?"string"==typeof e?e:e.source:null)(e))).join(""))("(",e,")?")
 }return t=>{const n=t.COMMENT("//","$",{contains:[{begin:/\\\n/}]
 }),r="[a-zA-Z_]\\w*::",a="(decltype\\(auto\\)|"+e(r)+"[a-zA-Z_]\\w*"+e("<[^<>]+>")+")",i={
 className:"keyword",begin:"\\b[a-z\\d_]*_t\\b"},s={className:"string",
@@ -750,56 +798,13 @@
 end:/\)/,keywords:u,relevance:0,contains:[n,t.C_BLOCK_COMMENT_MODE,s,o,i,{
 begin:/\(/,end:/\)/,keywords:u,relevance:0,
 contains:["self",n,t.C_BLOCK_COMMENT_MODE,s,o,i]}]
-},i,n,t.C_BLOCK_COMMENT_MODE,c]};return{
-aliases:["c","cc","h","c++","h++","hpp","hh","hxx","cxx"],keywords:u,
+},i,n,t.C_BLOCK_COMMENT_MODE,c]};return{name:"C",aliases:["c","h"],keywords:u,
 disableAutodetect:!0,illegal:"</",contains:[].concat(p,_,m,[c,{
 begin:"\\b(deque|list|queue|priority_queue|pair|stack|vector|map|set|bitset|multiset|multimap|unordered_map|unordered_set|unordered_multiset|unordered_multimap|array)\\s*<",
 end:">",keywords:u,contains:["self",i]},{begin:t.IDENT_RE+"::",keywords:u},{
 className:"class",beginKeywords:"enum class struct union",end:/[{;:<>=]/,
 contains:[{beginKeywords:"final class struct"},t.TITLE_MODE]}]),exports:{
 preprocessor:c,strings:s,keywords:u}}}})());
-hljs.registerLanguage("c",(()=>{"use strict";function e(e){
-return((...e)=>e.map((e=>(e=>e?"string"==typeof e?e:e.source:null)(e))).join(""))("(",e,")?")
-}return t=>{const n=(t=>{const n=t.COMMENT("//","$",{contains:[{begin:/\\\n/}]
-}),r="[a-zA-Z_]\\w*::",a="(decltype\\(auto\\)|"+e(r)+"[a-zA-Z_]\\w*"+e("<[^<>]+>")+")",s={
-className:"keyword",begin:"\\b[a-z\\d_]*_t\\b"},i={className:"string",
-variants:[{begin:'(u8?|U|L)?"',end:'"',illegal:"\\n",
-contains:[t.BACKSLASH_ESCAPE]},{
-begin:"(u8?|U|L)?'(\\\\(x[0-9A-Fa-f]{2}|u[0-9A-Fa-f]{4,8}|[0-7]{3}|\\S)|.)",
-end:"'",illegal:"."},t.END_SAME_AS_BEGIN({
-begin:/(?:u8?|U|L)?R"([^()\\ ]{0,16})\(/,end:/\)([^()\\ ]{0,16})"/})]},o={
-className:"number",variants:[{begin:"\\b(0b[01']+)"},{
-begin:"(-?)\\b([\\d']+(\\.[\\d']*)?|\\.[\\d']+)((ll|LL|l|L)(u|U)?|(u|U)(ll|LL|l|L)?|f|F|b|B)"
-},{
-begin:"(-?)(\\b0[xX][a-fA-F0-9']+|(\\b[\\d']+(\\.[\\d']*)?|\\.[\\d']+)([eE][-+]?[\\d']+)?)"
-}],relevance:0},c={className:"meta",begin:/#\s*[a-z]+\b/,end:/$/,keywords:{
-"meta-keyword":"if else elif endif define undef warning error line pragma _Pragma ifdef ifndef include"
-},contains:[{begin:/\\\n/,relevance:0},t.inherit(i,{className:"meta-string"}),{
-className:"meta-string",begin:/<.*?>/,end:/$/,illegal:"\\n"
-},n,t.C_BLOCK_COMMENT_MODE]},l={className:"title",begin:e(r)+t.IDENT_RE,
-relevance:0},d=e(r)+t.IDENT_RE+"\\s*\\(",u={
-keyword:"int float while private char char8_t char16_t char32_t catch import module export virtual operator sizeof dynamic_cast|10 typedef const_cast|10 const for static_cast|10 union namespace unsigned long volatile static protected bool template mutable if public friend do goto auto void enum else break extern using asm case typeid wchar_t short reinterpret_cast|10 default double register explicit signed typename try this switch continue inline delete alignas alignof constexpr consteval constinit decltype concept co_await co_return co_yield requires noexcept static_assert thread_local restrict final override atomic_bool atomic_char atomic_schar atomic_uchar atomic_short atomic_ushort atomic_int atomic_uint atomic_long atomic_ulong atomic_llong atomic_ullong new throw return and and_eq bitand bitor compl not not_eq or or_eq xor xor_eq",
-built_in:"std string wstring cin cout cerr clog stdin stdout stderr stringstream istringstream ostringstream auto_ptr deque list queue stack vector map set pair bitset multiset multimap unordered_set unordered_map unordered_multiset unordered_multimap priority_queue make_pair array shared_ptr abort terminate abs acos asin atan2 atan calloc ceil cosh cos exit exp fabs floor fmod fprintf fputs free frexp fscanf future isalnum isalpha iscntrl isdigit isgraph islower isprint ispunct isspace isupper isxdigit tolower toupper labs ldexp log10 log malloc realloc memchr memcmp memcpy memset modf pow printf putchar puts scanf sinh sin snprintf sprintf sqrt sscanf strcat strchr strcmp strcpy strcspn strlen strncat strncmp strncpy strpbrk strrchr strspn strstr tanh tan vfprintf vprintf vsprintf endl initializer_list unique_ptr _Bool complex _Complex imaginary _Imaginary",
-literal:"true false nullptr NULL"},m=[c,s,n,t.C_BLOCK_COMMENT_MODE,o,i],p={
-variants:[{begin:/=/,end:/;/},{begin:/\(/,end:/\)/},{
-beginKeywords:"new throw return else",end:/;/}],keywords:u,contains:m.concat([{
-begin:/\(/,end:/\)/,keywords:u,contains:m.concat(["self"]),relevance:0}]),
-relevance:0},_={className:"function",begin:"("+a+"[\\*&\\s]+)+"+d,
-returnBegin:!0,end:/[{;=]/,excludeEnd:!0,keywords:u,illegal:/[^\w\s\*&:<>.]/,
-contains:[{begin:"decltype\\(auto\\)",keywords:u,relevance:0},{begin:d,
-returnBegin:!0,contains:[l],relevance:0},{className:"params",begin:/\(/,
-end:/\)/,keywords:u,relevance:0,contains:[n,t.C_BLOCK_COMMENT_MODE,i,o,s,{
-begin:/\(/,end:/\)/,keywords:u,relevance:0,
-contains:["self",n,t.C_BLOCK_COMMENT_MODE,i,o,s]}]
-},s,n,t.C_BLOCK_COMMENT_MODE,c]};return{
-aliases:["c","cc","h","c++","h++","hpp","hh","hxx","cxx"],keywords:u,
-disableAutodetect:!0,illegal:"</",contains:[].concat(p,_,m,[c,{
-begin:"\\b(deque|list|queue|priority_queue|pair|stack|vector|map|set|bitset|multiset|multimap|unordered_map|unordered_set|unordered_multiset|unordered_multimap|array)\\s*<",
-end:">",keywords:u,contains:["self",s]},{begin:t.IDENT_RE+"::",keywords:u},{
-className:"class",beginKeywords:"enum class struct union",end:/[{;:<>=]/,
-contains:[{beginKeywords:"final class struct"},t.TITLE_MODE]}]),exports:{
-preprocessor:c,strings:i,keywords:u}}})(t)
-;return n.name="C",n.aliases=["c","h"],n}})());
 hljs.registerLanguage("cal",(()=>{"use strict";return e=>{
 const n="div mod in and or not xor asserterror begin case do downto else end exit for if of repeat then to until while with var",a=[e.C_LINE_COMMENT_MODE,e.COMMENT(/\{/,/\}/,{
 relevance:0}),e.COMMENT(/\(\*/,/\*\)/,{relevance:10})],r={className:"string",
@@ -843,7 +848,7 @@
 contains:[e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,e.C_NUMBER_MODE,{
 begin:"->|<-[|:]?|#!?|>>=|\\{\\||\\|\\}|:==|=:|<>"}]})})());
 hljs.registerLanguage("clojure",(()=>{"use strict";return e=>{
-var t="a-zA-Z_\\-!.?+*=<>&#'",n="["+t+"]["+t+"0-9/;:]*",r="def defonce defprotocol defstruct defmulti defmethod defn- defn defmacro deftype defrecord",a={
+const t="a-zA-Z_\\-!.?+*=<>&#'",n="["+t+"]["+t+"0-9/;:]*",r="def defonce defprotocol defstruct defmulti defmethod defn- defn defmacro deftype defrecord",a={
 $pattern:n,
 "builtin-name":r+" cond apply if-not if-let if not not= =|0 <|0 >|0 <=|0 >=|0 ==|0 +|0 /|0 *|0 -|0 rem quot neg? pos? delay? symbol? keyword? true? false? integer? empty? coll? list? set? ifn? fn? associative? sequential? sorted? counted? reversible? number? decimal? class? distinct? isa? float? rational? reduced? ratio? odd? even? char? seq? vector? string? map? nil? contains? zero? instance? not-every? not-any? libspec? -> ->> .. . inc compare do dotimes mapcat take remove take-while drop letfn drop-last take-last drop-while while intern condp case reduced cycle split-at split-with repeat replicate iterate range merge zipmap declare line-seq sort comparator sort-by dorun doall nthnext nthrest partition eval doseq await await-for let agent atom send send-off release-pending-sends add-watch mapv filterv remove-watch agent-error restart-agent set-error-handler error-handler set-error-mode! error-mode shutdown-agents quote var fn loop recur throw try monitor-enter monitor-exit macroexpand macroexpand-1 for dosync and or when when-not when-let comp juxt partial sequence memoize constantly complement identity assert peek pop doto proxy first rest cons cast coll last butlast sigs reify second ffirst fnext nfirst nnext meta with-meta ns in-ns create-ns import refer keys select-keys vals key val rseq name namespace promise into transient persistent! conj! assoc! dissoc! pop! disj! use class type num float double short byte boolean bigint biginteger bigdec print-method print-dup throw-if printf format load compile get-in update-in pr pr-on newline flush read slurp read-line subvec with-open memfn time re-find re-groups rand-int rand mod locking assert-valid-fdecl alias resolve ref deref refset swap! reset! set-validator! compare-and-set! alter-meta! reset-meta! commute get-validator alter ref-set ref-history-count ref-min-history ref-max-history ensure sync io! new next conj set! to-array future future-call into-array aset gen-class reduce map filter find empty hash-map hash-set sorted-map sorted-map-by sorted-set sorted-set-by vec vector seq flatten reverse assoc dissoc list disj get union difference intersection extend extend-type extend-protocol int nth delay count concat chunk chunk-buffer chunk-append chunk-first chunk-rest max min dec unchecked-inc-int unchecked-inc unchecked-dec-inc unchecked-dec unchecked-negate unchecked-add-int unchecked-add unchecked-subtract-int unchecked-subtract chunk-next chunk-cons chunked-seq? prn vary-meta lazy-seq spread list* str find-keyword keyword symbol gensym force rationalize"
 },s={begin:n,relevance:0},o={className:"number",begin:"[-+]?\\d+(\\.\\d+)?",
@@ -871,15 +876,13 @@
 ;const e=["as","in","of","if","for","while","finally","var","new","function","do","return","void","else","break","catch","instanceof","with","throw","case","default","try","switch","continue","typeof","delete","let","yield","const","class","debugger","async","await","static","import","from","export","extends"],n=["true","false","null","undefined","NaN","Infinity"],a=[].concat(["setInterval","setTimeout","clearInterval","clearTimeout","require","exports","eval","isFinite","isNaN","parseFloat","parseInt","decodeURI","decodeURIComponent","encodeURI","encodeURIComponent","escape","unescape"],["arguments","this","super","console","window","document","localStorage","module","global"],["Intl","DataView","Number","Math","Date","String","RegExp","Object","Function","Boolean","Error","Symbol","Set","Map","WeakSet","WeakMap","Proxy","Reflect","JSON","Promise","Float64Array","Int16Array","Int32Array","Int8Array","Uint16Array","Uint32Array","Float32Array","Array","Uint8Array","Uint8ClampedArray","ArrayBuffer"],["EvalError","InternalError","RangeError","ReferenceError","SyntaxError","TypeError","URIError"])
 ;return r=>{const t={
 keyword:e.concat(["then","unless","until","loop","by","when","and","or","is","isnt","not"]).filter((i=["var","const","let","function","static"],
-e=>!i.includes(e))).join(" "),
-literal:n.concat(["yes","no","on","off"]).join(" "),
-built_in:a.concat(["npm","print"]).join(" ")};var i
-;const s="[A-Za-z$_][0-9A-Za-z$_]*",o={className:"subst",begin:/#\{/,end:/\}/,
-keywords:t},c=[r.BINARY_NUMBER_MODE,r.inherit(r.C_NUMBER_MODE,{starts:{
-end:"(\\s*/)?",relevance:0}}),{className:"string",variants:[{begin:/'''/,
-end:/'''/,contains:[r.BACKSLASH_ESCAPE]},{begin:/'/,end:/'/,
-contains:[r.BACKSLASH_ESCAPE]},{begin:/"""/,end:/"""/,
-contains:[r.BACKSLASH_ESCAPE,o]},{begin:/"/,end:/"/,
+e=>!i.includes(e))),literal:n.concat(["yes","no","on","off"]),
+built_in:a.concat(["npm","print"])};var i;const s="[A-Za-z$_][0-9A-Za-z$_]*",o={
+className:"subst",begin:/#\{/,end:/\}/,keywords:t
+},c=[r.BINARY_NUMBER_MODE,r.inherit(r.C_NUMBER_MODE,{starts:{end:"(\\s*/)?",
+relevance:0}}),{className:"string",variants:[{begin:/'''/,end:/'''/,
+contains:[r.BACKSLASH_ESCAPE]},{begin:/'/,end:/'/,contains:[r.BACKSLASH_ESCAPE]
+},{begin:/"""/,end:/"""/,contains:[r.BACKSLASH_ESCAPE,o]},{begin:/"/,end:/"/,
 contains:[r.BACKSLASH_ESCAPE,o]}]},{className:"regexp",variants:[{begin:"///",
 end:"///",contains:[o,r.HASH_COMMENT_MODE]},{begin:"//[gim]{0,3}(?=\\W)",
 relevance:0},{begin:/\/(?![ *]).*?(?![\\]).\/[gim]{0,3}(?=\W)/}]},{begin:"@"+s
@@ -918,47 +921,45 @@
 end:/>\s*>/,subLanguage:"xml"}]})})());
 hljs.registerLanguage("cpp",(()=>{"use strict";function e(e){
 return((...e)=>e.map((e=>(e=>e?"string"==typeof e?e:e.source:null)(e))).join(""))("(",e,")?")
-}return t=>{const n=(t=>{const n=t.COMMENT("//","$",{contains:[{begin:/\\\n/}]
-}),r="[a-zA-Z_]\\w*::",a="(decltype\\(auto\\)|"+e(r)+"[a-zA-Z_]\\w*"+e("<[^<>]+>")+")",s={
-className:"keyword",begin:"\\b[a-z\\d_]*_t\\b"},i={className:"string",
+}return t=>{const n=t.COMMENT("//","$",{contains:[{begin:/\\\n/}]
+}),r="[a-zA-Z_]\\w*::",a="(decltype\\(auto\\)|"+e(r)+"[a-zA-Z_]\\w*"+e("<[^<>]+>")+")",i={
+className:"keyword",begin:"\\b[a-z\\d_]*_t\\b"},s={className:"string",
 variants:[{begin:'(u8?|U|L)?"',end:'"',illegal:"\\n",
 contains:[t.BACKSLASH_ESCAPE]},{
 begin:"(u8?|U|L)?'(\\\\(x[0-9A-Fa-f]{2}|u[0-9A-Fa-f]{4,8}|[0-7]{3}|\\S)|.)",
 end:"'",illegal:"."},t.END_SAME_AS_BEGIN({
-begin:/(?:u8?|U|L)?R"([^()\\ ]{0,16})\(/,end:/\)([^()\\ ]{0,16})"/})]},c={
+begin:/(?:u8?|U|L)?R"([^()\\ ]{0,16})\(/,end:/\)([^()\\ ]{0,16})"/})]},o={
 className:"number",variants:[{begin:"\\b(0b[01']+)"},{
 begin:"(-?)\\b([\\d']+(\\.[\\d']*)?|\\.[\\d']+)((ll|LL|l|L)(u|U)?|(u|U)(ll|LL|l|L)?|f|F|b|B)"
 },{
 begin:"(-?)(\\b0[xX][a-fA-F0-9']+|(\\b[\\d']+(\\.[\\d']*)?|\\.[\\d']+)([eE][-+]?[\\d']+)?)"
-}],relevance:0},o={className:"meta",begin:/#\s*[a-z]+\b/,end:/$/,keywords:{
+}],relevance:0},c={className:"meta",begin:/#\s*[a-z]+\b/,end:/$/,keywords:{
 "meta-keyword":"if else elif endif define undef warning error line pragma _Pragma ifdef ifndef include"
-},contains:[{begin:/\\\n/,relevance:0},t.inherit(i,{className:"meta-string"}),{
+},contains:[{begin:/\\\n/,relevance:0},t.inherit(s,{className:"meta-string"}),{
 className:"meta-string",begin:/<.*?>/,end:/$/,illegal:"\\n"
 },n,t.C_BLOCK_COMMENT_MODE]},l={className:"title",begin:e(r)+t.IDENT_RE,
 relevance:0},d=e(r)+t.IDENT_RE+"\\s*\\(",u={
 keyword:"int float while private char char8_t char16_t char32_t catch import module export virtual operator sizeof dynamic_cast|10 typedef const_cast|10 const for static_cast|10 union namespace unsigned long volatile static protected bool template mutable if public friend do goto auto void enum else break extern using asm case typeid wchar_t short reinterpret_cast|10 default double register explicit signed typename try this switch continue inline delete alignas alignof constexpr consteval constinit decltype concept co_await co_return co_yield requires noexcept static_assert thread_local restrict final override atomic_bool atomic_char atomic_schar atomic_uchar atomic_short atomic_ushort atomic_int atomic_uint atomic_long atomic_ulong atomic_llong atomic_ullong new throw return and and_eq bitand bitor compl not not_eq or or_eq xor xor_eq",
 built_in:"std string wstring cin cout cerr clog stdin stdout stderr stringstream istringstream ostringstream auto_ptr deque list queue stack vector map set pair bitset multiset multimap unordered_set unordered_map unordered_multiset unordered_multimap priority_queue make_pair array shared_ptr abort terminate abs acos asin atan2 atan calloc ceil cosh cos exit exp fabs floor fmod fprintf fputs free frexp fscanf future isalnum isalpha iscntrl isdigit isgraph islower isprint ispunct isspace isupper isxdigit tolower toupper labs ldexp log10 log malloc realloc memchr memcmp memcpy memset modf pow printf putchar puts scanf sinh sin snprintf sprintf sqrt sscanf strcat strchr strcmp strcpy strcspn strlen strncat strncmp strncpy strpbrk strrchr strspn strstr tanh tan vfprintf vprintf vsprintf endl initializer_list unique_ptr _Bool complex _Complex imaginary _Imaginary",
-literal:"true false nullptr NULL"},p=[o,s,n,t.C_BLOCK_COMMENT_MODE,c,i],m={
+literal:"true false nullptr NULL"},m=[c,i,n,t.C_BLOCK_COMMENT_MODE,o,s],p={
 variants:[{begin:/=/,end:/;/},{begin:/\(/,end:/\)/},{
-beginKeywords:"new throw return else",end:/;/}],keywords:u,contains:p.concat([{
-begin:/\(/,end:/\)/,keywords:u,contains:p.concat(["self"]),relevance:0}]),
+beginKeywords:"new throw return else",end:/;/}],keywords:u,contains:m.concat([{
+begin:/\(/,end:/\)/,keywords:u,contains:m.concat(["self"]),relevance:0}]),
 relevance:0},_={className:"function",begin:"("+a+"[\\*&\\s]+)+"+d,
 returnBegin:!0,end:/[{;=]/,excludeEnd:!0,keywords:u,illegal:/[^\w\s\*&:<>.]/,
 contains:[{begin:"decltype\\(auto\\)",keywords:u,relevance:0},{begin:d,
 returnBegin:!0,contains:[l],relevance:0},{className:"params",begin:/\(/,
-end:/\)/,keywords:u,relevance:0,contains:[n,t.C_BLOCK_COMMENT_MODE,i,c,s,{
+end:/\)/,keywords:u,relevance:0,contains:[n,t.C_BLOCK_COMMENT_MODE,s,o,i,{
 begin:/\(/,end:/\)/,keywords:u,relevance:0,
-contains:["self",n,t.C_BLOCK_COMMENT_MODE,i,c,s]}]
-},s,n,t.C_BLOCK_COMMENT_MODE,o]};return{
-aliases:["c","cc","h","c++","h++","hpp","hh","hxx","cxx"],keywords:u,
-disableAutodetect:!0,illegal:"</",contains:[].concat(m,_,p,[o,{
+contains:["self",n,t.C_BLOCK_COMMENT_MODE,s,o,i]}]
+},i,n,t.C_BLOCK_COMMENT_MODE,c]};return{name:"C++",
+aliases:["cc","c++","h++","hpp","hh","hxx","cxx"],keywords:u,illegal:"</",
+contains:[].concat(p,_,m,[c,{
 begin:"\\b(deque|list|queue|priority_queue|pair|stack|vector|map|set|bitset|multiset|multimap|unordered_map|unordered_set|unordered_multiset|unordered_multimap|array)\\s*<",
-end:">",keywords:u,contains:["self",s]},{begin:t.IDENT_RE+"::",keywords:u},{
+end:">",keywords:u,contains:["self",i]},{begin:t.IDENT_RE+"::",keywords:u},{
 className:"class",beginKeywords:"enum class struct union",end:/[{;:<>=]/,
 contains:[{beginKeywords:"final class struct"},t.TITLE_MODE]}]),exports:{
-preprocessor:o,strings:i,keywords:u}}})(t)
-;return n.disableAutodetect=!1,n.name="C++",
-n.aliases=["cc","c++","h++","hpp","hh","hxx","cxx"],n}})());
+preprocessor:c,strings:s,keywords:u}}}})());
 hljs.registerLanguage("crmsh",(()=>{"use strict";return e=>{
 const t="group clone ms master location colocation order fencing_topology rsc_ticket acl_target acl_group user role tag xml"
 ;return{name:"crmsh",aliases:["crm","pcmk"],case_insensitive:!0,keywords:{
@@ -1023,9 +1024,9 @@
 ;return t.contains=g,c.contains=g.slice(1),{name:"Crystal",aliases:["cr"],
 keywords:a,contains:g}}})());
 hljs.registerLanguage("csharp",(()=>{"use strict";return e=>{var n={
-keyword:["abstract","as","base","break","case","class","const","continue","do","else","event","explicit","extern","finally","fixed","for","foreach","goto","if","implicit","in","interface","internal","is","lock","namespace","new","operator","out","override","params","private","protected","public","readonly","record","ref","return","sealed","sizeof","stackalloc","static","struct","switch","this","throw","try","typeof","unchecked","unsafe","using","virtual","void","volatile","while"].concat(["add","alias","and","ascending","async","await","by","descending","equals","from","get","global","group","init","into","join","let","nameof","not","notnull","on","or","orderby","partial","remove","select","set","unmanaged","value|0","var","when","where","with","yield"]).join(" "),
-built_in:"bool byte char decimal delegate double dynamic enum float int long nint nuint object sbyte short string ulong unit ushort",
-literal:"default false null true"},a=e.inherit(e.TITLE_MODE,{
+keyword:["abstract","as","base","break","case","class","const","continue","do","else","event","explicit","extern","finally","fixed","for","foreach","goto","if","implicit","in","interface","internal","is","lock","namespace","new","operator","out","override","params","private","protected","public","readonly","record","ref","return","sealed","sizeof","stackalloc","static","struct","switch","this","throw","try","typeof","unchecked","unsafe","using","virtual","void","volatile","while"].concat(["add","alias","and","ascending","async","await","by","descending","equals","from","get","global","group","init","into","join","let","nameof","not","notnull","on","or","orderby","partial","remove","select","set","unmanaged","value|0","var","when","where","with","yield"]),
+built_in:["bool","byte","char","decimal","delegate","double","dynamic","enum","float","int","long","nint","nuint","object","sbyte","short","string","ulong","unit","ushort"],
+literal:["default","false","null","true"]},a=e.inherit(e.TITLE_MODE,{
 begin:"[a-zA-Z](\\.?\\w)*"}),i={className:"number",variants:[{
 begin:"\\b(0b[01']+)"},{
 begin:"(-?)\\b([\\d']+(\\.[\\d']*)?|\\.[\\d']+)(u|U|l|L|ul|UL|f|F|b|B)"},{
@@ -1072,29 +1073,32 @@
 keyword:"base-uri child-src connect-src default-src font-src form-action frame-ancestors frame-src img-src media-src object-src plugin-types report-uri sandbox script-src style-src"
 },contains:[{className:"string",begin:"'",end:"'"},{className:"attribute",
 begin:"^Content",end:":",excludeEnd:!0}]})})());
-hljs.registerLanguage("css",(()=>{"use strict";return e=>{
-var n="[a-zA-Z-][a-zA-Z0-9_-]*",a={
-begin:/([*]\s?)?(?:[A-Z_.\-\\]+|--[a-zA-Z0-9_-]+)\s*(\/\*\*\/)?:/,
-returnBegin:!0,end:";",endsWithParent:!0,contains:[{className:"attribute",
-begin:/\S/,end:":",excludeEnd:!0,starts:{endsWithParent:!0,excludeEnd:!0,
-contains:[{begin:/[\w-]+\(/,returnBegin:!0,contains:[{className:"built_in",
-begin:/[\w-]+/},{begin:/\(/,end:/\)/,
-contains:[e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,e.CSS_NUMBER_MODE]}]
-},e.CSS_NUMBER_MODE,e.QUOTE_STRING_MODE,e.APOS_STRING_MODE,e.C_BLOCK_COMMENT_MODE,{
-className:"number",begin:"#[0-9A-Fa-f]+"},{className:"meta",begin:"!important"}]
-}}]};return{name:"CSS",case_insensitive:!0,illegal:/[=|'\$]/,
-contains:[e.C_BLOCK_COMMENT_MODE,{className:"selector-id",
-begin:/#[A-Za-z0-9_-]+/},{className:"selector-class",begin:"\\."+n},{
-className:"selector-attr",begin:/\[/,end:/\]/,illegal:"$",
-contains:[e.APOS_STRING_MODE,e.QUOTE_STRING_MODE]},{className:"selector-pseudo",
-begin:/:(:)?[a-zA-Z0-9_+()"'.-]+/},{begin:"@(page|font-face)",
-lexemes:"@[a-z-]+",keywords:"@page @font-face"},{begin:"@",end:"[{;]",
-illegal:/:/,returnBegin:!0,contains:[{className:"keyword",
+hljs.registerLanguage("css",(()=>{"use strict"
+;const e=["a","abbr","address","article","aside","audio","b","blockquote","body","button","canvas","caption","cite","code","dd","del","details","dfn","div","dl","dt","em","fieldset","figcaption","figure","footer","form","h1","h2","h3","h4","h5","h6","header","hgroup","html","i","iframe","img","input","ins","kbd","label","legend","li","main","mark","menu","nav","object","ol","p","q","quote","samp","section","span","strong","summary","sup","table","tbody","td","textarea","tfoot","th","thead","time","tr","ul","var","video"],t=["any-hover","any-pointer","aspect-ratio","color","color-gamut","color-index","device-aspect-ratio","device-height","device-width","display-mode","forced-colors","grid","height","hover","inverted-colors","monochrome","orientation","overflow-block","overflow-inline","pointer","prefers-color-scheme","prefers-contrast","prefers-reduced-motion","prefers-reduced-transparency","resolution","scan","scripting","update","width","min-width","max-width","min-height","max-height"],i=["active","any-link","blank","checked","current","default","defined","dir","disabled","drop","empty","enabled","first","first-child","first-of-type","fullscreen","future","focus","focus-visible","focus-within","has","host","host-context","hover","indeterminate","in-range","invalid","is","lang","last-child","last-of-type","left","link","local-link","not","nth-child","nth-col","nth-last-child","nth-last-col","nth-last-of-type","nth-of-type","only-child","only-of-type","optional","out-of-range","past","placeholder-shown","read-only","read-write","required","right","root","scope","target","target-within","user-invalid","valid","visited","where"],o=["after","backdrop","before","cue","cue-region","first-letter","first-line","grammar-error","marker","part","placeholder","selection","slotted","spelling-error"],r=["align-content","align-items","align-self","animation","animation-delay","animation-direction","animation-duration","animation-fill-mode","animation-iteration-count","animation-name","animation-play-state","animation-timing-function","auto","backface-visibility","background","background-attachment","background-clip","background-color","background-image","background-origin","background-position","background-repeat","background-size","border","border-bottom","border-bottom-color","border-bottom-left-radius","border-bottom-right-radius","border-bottom-style","border-bottom-width","border-collapse","border-color","border-image","border-image-outset","border-image-repeat","border-image-slice","border-image-source","border-image-width","border-left","border-left-color","border-left-style","border-left-width","border-radius","border-right","border-right-color","border-right-style","border-right-width","border-spacing","border-style","border-top","border-top-color","border-top-left-radius","border-top-right-radius","border-top-style","border-top-width","border-width","bottom","box-decoration-break","box-shadow","box-sizing","break-after","break-before","break-inside","caption-side","clear","clip","clip-path","color","column-count","column-fill","column-gap","column-rule","column-rule-color","column-rule-style","column-rule-width","column-span","column-width","columns","content","counter-increment","counter-reset","cursor","direction","display","empty-cells","filter","flex","flex-basis","flex-direction","flex-flow","flex-grow","flex-shrink","flex-wrap","float","font","font-display","font-family","font-feature-settings","font-kerning","font-language-override","font-size","font-size-adjust","font-stretch","font-style","font-variant","font-variant-ligatures","font-variation-settings","font-weight","height","hyphens","icon","image-orientation","image-rendering","image-resolution","ime-mode","inherit","initial","justify-content","left","letter-spacing","line-height","list-style","list-style-image","list-style-position","list-style-type","margin","margin-bottom","margin-left","margin-right","margin-top","marks","mask","max-height","max-width","min-height","min-width","nav-down","nav-index","nav-left","nav-right","nav-up","none","normal","object-fit","object-position","opacity","order","orphans","outline","outline-color","outline-offset","outline-style","outline-width","overflow","overflow-wrap","overflow-x","overflow-y","padding","padding-bottom","padding-left","padding-right","padding-top","page-break-after","page-break-before","page-break-inside","perspective","perspective-origin","pointer-events","position","quotes","resize","right","src","tab-size","table-layout","text-align","text-align-last","text-decoration","text-decoration-color","text-decoration-line","text-decoration-style","text-indent","text-overflow","text-rendering","text-shadow","text-transform","text-underline-position","top","transform","transform-origin","transform-style","transition","transition-delay","transition-duration","transition-property","transition-timing-function","unicode-bidi","vertical-align","visibility","white-space","widows","width","word-break","word-spacing","word-wrap","z-index"].reverse()
+;return n=>{const a=(e=>({IMPORTANT:{className:"meta",begin:"!important"},
+HEXCOLOR:{className:"number",begin:"#([a-fA-F0-9]{6}|[a-fA-F0-9]{3})"},
+ATTRIBUTE_SELECTOR_MODE:{className:"selector-attr",begin:/\[/,end:/\]/,
+illegal:"$",contains:[e.APOS_STRING_MODE,e.QUOTE_STRING_MODE]}
+}))(n),l=[n.APOS_STRING_MODE,n.QUOTE_STRING_MODE];return{name:"CSS",
+case_insensitive:!0,illegal:/[=|'\$]/,keywords:{keyframePosition:"from to"},
+classNameAliases:{keyframePosition:"selector-tag"},
+contains:[n.C_BLOCK_COMMENT_MODE,{begin:/-(webkit|moz|ms|o)-(?=[a-z])/
+},n.CSS_NUMBER_MODE,{className:"selector-id",begin:/#[A-Za-z0-9_-]+/,relevance:0
+},{className:"selector-class",begin:"\\.[a-zA-Z-][a-zA-Z0-9_-]*",relevance:0
+},a.ATTRIBUTE_SELECTOR_MODE,{className:"selector-pseudo",variants:[{
+begin:":("+i.join("|")+")"},{begin:"::("+o.join("|")+")"}]},{
+className:"attribute",begin:"\\b("+r.join("|")+")\\b"},{begin:":",end:"[;}]",
+contains:[a.HEXCOLOR,a.IMPORTANT,n.CSS_NUMBER_MODE,...l,{
+begin:/(url|data-uri)\(/,end:/\)/,relevance:0,keywords:{built_in:"url data-uri"
+},contains:[{className:"string",begin:/[^)]/,endsWithParent:!0,excludeEnd:!0}]
+},{className:"built_in",begin:/[\w-]+(?=\()/}]},{
+begin:(s=/@/,((...e)=>e.map((e=>(e=>e?"string"==typeof e?e:e.source:null)(e))).join(""))("(?=",s,")")),
+end:"[{;]",relevance:0,illegal:/:/,contains:[{className:"keyword",
 begin:/@-?\w[\w]*(-\w+)*/},{begin:/\s/,endsWithParent:!0,excludeEnd:!0,
-relevance:0,keywords:"and or not only",contains:[{begin:/[a-z-]+:/,
-className:"attribute"},e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,e.CSS_NUMBER_MODE]
-}]},{className:"selector-tag",begin:n,relevance:0},{begin:/\{/,end:/\}/,
-illegal:/\S/,contains:[e.C_BLOCK_COMMENT_MODE,{begin:/;/},a]}]}}})());
+relevance:0,keywords:{$pattern:/[a-z-]+/,keyword:"and or not only",
+attribute:t.join(" ")},contains:[{begin:/[a-z-]+(?=:)/,className:"attribute"
+},...l,n.CSS_NUMBER_MODE]}]},{className:"selector-tag",
+begin:"\\b("+e.join("|")+")\\b"}]};var s}})());
 hljs.registerLanguage("d",(()=>{"use strict";return e=>{const a={
 $pattern:e.UNDERSCORE_IDENT_RE,
 keyword:"abstract alias align asm assert auto body break byte case cast catch class const continue debug default delete deprecated do else enum export extern final finally for foreach foreach_reverse|10 goto if immutable import in inout int interface invariant is lazy macro mixin module new nothrow out override package pragma private protected public pure ref return scope shared static struct super switch synchronized template this throw try typedef typeid typeof union unittest version void volatile while with __FILE__ __LINE__ __gshared|10 __thread __traits __DATE__ __EOF__ __TIME__ __TIMESTAMP__ __VENDOR__ __VERSION__",
@@ -1155,7 +1159,7 @@
 ;const i=["Comparable","DateTime","Duration","Function","Iterable","Iterator","List","Map","Match","Object","Pattern","RegExp","Set","Stopwatch","String","StringBuffer","StringSink","Symbol","Type","Uri","bool","double","int","num","Element","ElementList"],r=i.map((e=>e+"?"))
 ;return{name:"Dart",keywords:{
 keyword:"abstract as assert async await break case catch class const continue covariant default deferred do dynamic else enum export extends extension external factory false final finally for Function get hide if implements import in inferface is late library mixin new null on operator part required rethrow return set show static super switch sync this throw true try typedef var void while with yield",
-built_in:i.concat(r).concat(["Never","Null","dynamic","print","document","querySelector","querySelectorAll","window"]).join(" "),
+built_in:i.concat(r).concat(["Never","Null","dynamic","print","document","querySelector","querySelectorAll","window"]),
 $pattern:/[A-Za-z][A-Za-z0-9_]*\??/},
 contains:[t,e.COMMENT(/\/\*\*(?!\/)/,/\*\//,{subLanguage:"markdown",relevance:0
 }),e.COMMENT(/\/{3,} ?/,/$/,{contains:[{subLanguage:"markdown",begin:".",
@@ -1317,48 +1321,51 @@
 hljs.registerLanguage("ruby",(()=>{"use strict";function e(...e){
 return e.map((e=>{return(n=e)?"string"==typeof n?n:n.source:null;var n
 })).join("")}return n=>{
-var a,i="([a-zA-Z_]\\w*[!?=]?|[-+~]@|<<|>>|=~|===?|<=>|[<>]=?|\\*\\*|[-/+%^&*~`|]|\\[\\]=?)",s={
+const a="([a-zA-Z_]\\w*[!?=]?|[-+~]@|<<|>>|=~|===?|<=>|[<>]=?|\\*\\*|[-/+%^&*~`|]|\\[\\]=?)",i={
 keyword:"and then defined module in return redo if BEGIN retry end for self when next until do begin unless END rescue else break undef not super class case require yield alias while ensure elsif or include attr_reader attr_writer attr_accessor __FILE__",
-built_in:"proc lambda",literal:"true false nil"},r={className:"doctag",
-begin:"@[A-Za-z]+"},b={begin:"#<",end:">"},t=[n.COMMENT("#","$",{contains:[r]
-}),n.COMMENT("^=begin","^=end",{contains:[r],relevance:10
+built_in:"proc lambda",literal:"true false nil"},s={className:"doctag",
+begin:"@[A-Za-z]+"},r={begin:"#<",end:">"},b=[n.COMMENT("#","$",{contains:[s]
+}),n.COMMENT("^=begin","^=end",{contains:[s],relevance:10
 }),n.COMMENT("^__END__","\\n$")],c={className:"subst",begin:/#\{/,end:/\}/,
-keywords:s},d={className:"string",contains:[n.BACKSLASH_ESCAPE,c],variants:[{
+keywords:i},t={className:"string",contains:[n.BACKSLASH_ESCAPE,c],variants:[{
 begin:/'/,end:/'/},{begin:/"/,end:/"/},{begin:/`/,end:/`/},{begin:/%[qQwWx]?\(/,
 end:/\)/},{begin:/%[qQwWx]?\[/,end:/\]/},{begin:/%[qQwWx]?\{/,end:/\}/},{
 begin:/%[qQwWx]?</,end:/>/},{begin:/%[qQwWx]?\//,end:/\//},{begin:/%[qQwWx]?%/,
 end:/%/},{begin:/%[qQwWx]?-/,end:/-/},{begin:/%[qQwWx]?\|/,end:/\|/},{
-begin:/\B\?(\\\d{1,3}|\\x[A-Fa-f0-9]{1,2}|\\u[A-Fa-f0-9]{4}|\\?\S)\b/},{
+begin:/\B\?(\\\d{1,3})/},{begin:/\B\?(\\x[A-Fa-f0-9]{1,2})/},{
+begin:/\B\?(\\u\{?[A-Fa-f0-9]{1,6}\}?)/},{
+begin:/\B\?(\\M-\\C-|\\M-\\c|\\c\\M-|\\M-|\\C-\\M-)[\x20-\x7e]/},{
+begin:/\B\?\\(c|C-)[\x20-\x7e]/},{begin:/\B\?\\?\S/},{
 begin:/<<[-~]?'?(\w+)\n(?:[^\n]*\n)*?\s*\1\b/,returnBegin:!0,contains:[{
 begin:/<<[-~]?'?/},n.END_SAME_AS_BEGIN({begin:/(\w+)/,end:/(\w+)/,
-contains:[n.BACKSLASH_ESCAPE,c]})]}]},g="[0-9](_?[0-9])*",l={className:"number",
+contains:[n.BACKSLASH_ESCAPE,c]})]}]},g="[0-9](_?[0-9])*",d={className:"number",
 relevance:0,variants:[{
 begin:`\\b([1-9](_?[0-9])*|0)(\\.(${g}))?([eE][+-]?(${g})|r)?i?\\b`},{
 begin:"\\b0[dD][0-9](_?[0-9])*r?i?\\b"},{begin:"\\b0[bB][0-1](_?[0-1])*r?i?\\b"
 },{begin:"\\b0[oO][0-7](_?[0-7])*r?i?\\b"},{
 begin:"\\b0[xX][0-9a-fA-F](_?[0-9a-fA-F])*r?i?\\b"},{
-begin:"\\b0(_?[0-7])+r?i?\\b"}]},o={className:"params",begin:"\\(",end:"\\)",
-endsParent:!0,keywords:s},_=[d,{className:"class",beginKeywords:"class module",
+begin:"\\b0(_?[0-7])+r?i?\\b"}]},l={className:"params",begin:"\\(",end:"\\)",
+endsParent:!0,keywords:i},o=[t,{className:"class",beginKeywords:"class module",
 end:"$|;",illegal:/=/,contains:[n.inherit(n.TITLE_MODE,{
 begin:"[A-Za-z_]\\w*(::\\w+)*(\\?|!)?"}),{begin:"<\\s*",contains:[{
-begin:"("+n.IDENT_RE+"::)?"+n.IDENT_RE}]}].concat(t)},{className:"function",
-begin:e(/def\s*/,(a=i+"\\s*(\\(|;|$)",e("(?=",a,")"))),keywords:"def",end:"$|;",
-contains:[n.inherit(n.TITLE_MODE,{begin:i}),o].concat(t)},{begin:n.IDENT_RE+"::"
-},{className:"symbol",begin:n.UNDERSCORE_IDENT_RE+"(!|\\?)?:",relevance:0},{
-className:"symbol",begin:":(?!\\s)",contains:[d,{begin:i}],relevance:0},l,{
-className:"variable",
+begin:"("+n.IDENT_RE+"::)?"+n.IDENT_RE,relevance:0}]}].concat(b)},{
+className:"function",begin:e(/def\s*/,(_=a+"\\s*(\\(|;|$)",e("(?=",_,")"))),
+relevance:0,keywords:"def",end:"$|;",contains:[n.inherit(n.TITLE_MODE,{begin:a
+}),l].concat(b)},{begin:n.IDENT_RE+"::"},{className:"symbol",
+begin:n.UNDERSCORE_IDENT_RE+"(!|\\?)?:",relevance:0},{className:"symbol",
+begin:":(?!\\s)",contains:[t,{begin:a}],relevance:0},d,{className:"variable",
 begin:"(\\$\\W)|((\\$|@@?)(\\w+))(?=[^@$?])(?![A-Za-z])(?![@$?'])"},{
-className:"params",begin:/\|/,end:/\|/,relevance:0,keywords:s},{
+className:"params",begin:/\|/,end:/\|/,relevance:0,keywords:i},{
 begin:"("+n.RE_STARTERS_RE+"|unless)\\s*",keywords:"unless",contains:[{
 className:"regexp",contains:[n.BACKSLASH_ESCAPE,c],illegal:/\n/,variants:[{
 begin:"/",end:"/[a-z]*"},{begin:/%r\{/,end:/\}[a-z]*/},{begin:"%r\\(",
 end:"\\)[a-z]*"},{begin:"%r!",end:"![a-z]*"},{begin:"%r\\[",end:"\\][a-z]*"}]
-}].concat(b,t),relevance:0}].concat(b,t);c.contains=_,o.contains=_;var E=[{
-begin:/^\s*=>/,starts:{end:"$",contains:_}},{className:"meta",
+}].concat(r,b),relevance:0}].concat(r,b);var _;c.contains=o,l.contains=o
+;const E=[{begin:/^\s*=>/,starts:{end:"$",contains:o}},{className:"meta",
 begin:"^([>?]>|[\\w#]+\\(\\w+\\):\\d+:\\d+>|(\\w+-)?\\d+\\.\\d+\\.\\d+(p\\d+)?[^\\d][^>]+>)(?=[ ])",
-starts:{end:"$",contains:_}}];return t.unshift(b),{name:"Ruby",
-aliases:["rb","gemspec","podspec","thor","irb"],keywords:s,illegal:/\/\*/,
-contains:[n.SHEBANG({binary:"ruby"})].concat(E).concat(t).concat(_)}}})());
+starts:{end:"$",contains:o}}];return b.unshift(r),{name:"Ruby",
+aliases:["rb","gemspec","podspec","thor","irb"],keywords:i,illegal:/\/\*/,
+contains:[n.SHEBANG({binary:"ruby"})].concat(E).concat(b).concat(o)}}})());
 hljs.registerLanguage("erb",(()=>{"use strict";return e=>({name:"ERB",
 subLanguage:"xml",contains:[e.COMMENT("<%#","%>"),{begin:"<%[%=-]?",
 end:"[%-]?%>",subLanguage:"ruby",excludeBegin:!0,excludeEnd:!0}]})})());
@@ -1398,7 +1405,7 @@
 illegal:"\\(|#|//|/\\*|\\\\|:|;",contains:[u,e.inherit(e.TITLE_MODE,{begin:n})],
 starts:{end:";|\\.",keywords:a,contains:E}},i,{begin:"^-",end:"\\.",relevance:0,
 excludeEnd:!0,returnBegin:!0,keywords:{$pattern:"-"+e.IDENT_RE,
-keyword:"-module -record -undef -export -ifdef -ifndef -author -copyright -doc -vsn -import -include -include_lib -compile -define -else -endif -file -behaviour -behavior -spec"
+keyword:["-module","-record","-undef","-export","-ifdef","-ifndef","-author","-copyright","-doc","-vsn","-import","-include","-include_lib","-compile","-define","-else","-endif","-file","-behaviour","-behavior","-spec"].map((e=>e+"|1.5")).join(" ")
 },contains:[u]},s,e.QUOTE_STRING_MODE,b,o,l,d,{begin:/\.$/}]}}})());
 hljs.registerLanguage("excel",(()=>{"use strict";return E=>({
 name:"Excel formulae",aliases:["xlsx","xls"],case_insensitive:!0,keywords:{
@@ -1422,7 +1429,7 @@
 },contains:[e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,{className:"string",
 begin:/'(.|\\[xXuU][a-zA-Z0-9]+)'/},{className:"string",variants:[{begin:'"',
 end:'"'}]},{className:"function",beginKeywords:"def",end:/[:={\[(\n;]/,
-excludeEnd:!0,contains:[{className:"title",
+excludeEnd:!0,contains:[{className:"title",relevance:0,
 begin:/[^0-9\n\t "'(),.`{}\[\]:;][^\n\t "'(),.`{}\[\]:;]+|[^0-9\n\t "'(),.`{}\[\]:;=]/
 }]},e.C_NUMBER_MODE]})})());
 hljs.registerLanguage("fortran",(()=>{"use strict";function e(...e){
@@ -1554,7 +1561,7 @@
 keyword:"begin end if then else while do for break continue with until repeat exit and or xor not return mod div switch case default var globalvar enum #macro #region #endregion",
 built_in:"is_real is_string is_array is_undefined is_int32 is_int64 is_ptr is_vec3 is_vec4 is_matrix is_bool typeof variable_global_exists variable_global_get variable_global_set variable_instance_exists variable_instance_get variable_instance_set variable_instance_get_names array_length_1d array_length_2d array_height_2d array_equals array_create array_copy random random_range irandom irandom_range random_set_seed random_get_seed randomize randomise choose abs round floor ceil sign frac sqrt sqr exp ln log2 log10 sin cos tan arcsin arccos arctan arctan2 dsin dcos dtan darcsin darccos darctan darctan2 degtorad radtodeg power logn min max mean median clamp lerp dot_product dot_product_3d dot_product_normalised dot_product_3d_normalised dot_product_normalized dot_product_3d_normalized math_set_epsilon math_get_epsilon angle_difference point_distance_3d point_distance point_direction lengthdir_x lengthdir_y real string int64 ptr string_format chr ansi_char ord string_length string_byte_length string_pos string_copy string_char_at string_ord_at string_byte_at string_set_byte_at string_delete string_insert string_lower string_upper string_repeat string_letters string_digits string_lettersdigits string_replace string_replace_all string_count string_hash_to_newline clipboard_has_text clipboard_set_text clipboard_get_text date_current_datetime date_create_datetime date_valid_datetime date_inc_year date_inc_month date_inc_week date_inc_day date_inc_hour date_inc_minute date_inc_second date_get_year date_get_month date_get_week date_get_day date_get_hour date_get_minute date_get_second date_get_weekday date_get_day_of_year date_get_hour_of_year date_get_minute_of_year date_get_second_of_year date_year_span date_month_span date_week_span date_day_span date_hour_span date_minute_span date_second_span date_compare_datetime date_compare_date date_compare_time date_date_of date_time_of date_datetime_string date_date_string date_time_string date_days_in_month date_days_in_year date_leap_year date_is_today date_set_timezone date_get_timezone game_set_speed game_get_speed motion_set motion_add place_free place_empty place_meeting place_snapped move_random move_snap move_towards_point move_contact_solid move_contact_all move_outside_solid move_outside_all move_bounce_solid move_bounce_all move_wrap distance_to_point distance_to_object position_empty position_meeting path_start path_end mp_linear_step mp_potential_step mp_linear_step_object mp_potential_step_object mp_potential_settings mp_linear_path mp_potential_path mp_linear_path_object mp_potential_path_object mp_grid_create mp_grid_destroy mp_grid_clear_all mp_grid_clear_cell mp_grid_clear_rectangle mp_grid_add_cell mp_grid_get_cell mp_grid_add_rectangle mp_grid_add_instances mp_grid_path mp_grid_draw mp_grid_to_ds_grid collision_point collision_rectangle collision_circle collision_ellipse collision_line collision_point_list collision_rectangle_list collision_circle_list collision_ellipse_list collision_line_list instance_position_list instance_place_list point_in_rectangle point_in_triangle point_in_circle rectangle_in_rectangle rectangle_in_triangle rectangle_in_circle instance_find instance_exists instance_number instance_position instance_nearest instance_furthest instance_place instance_create_depth instance_create_layer instance_copy instance_change instance_destroy position_destroy position_change instance_id_get instance_deactivate_all instance_deactivate_object instance_deactivate_region instance_activate_all instance_activate_object instance_activate_region room_goto room_goto_previous room_goto_next room_previous room_next room_restart game_end game_restart game_load game_save game_save_buffer game_load_buffer event_perform event_user event_perform_object event_inherited show_debug_message show_debug_overlay debug_event debug_get_callstack alarm_get alarm_set font_texture_page_size keyboard_set_map keyboard_get_map keyboard_unset_map keyboard_check keyboard_check_pressed keyboard_check_released keyboard_check_direct keyboard_get_numlock keyboard_set_numlock keyboard_key_press keyboard_key_release keyboard_clear io_clear mouse_check_button mouse_check_button_pressed mouse_check_button_released mouse_wheel_up mouse_wheel_down mouse_clear draw_self draw_sprite draw_sprite_pos draw_sprite_ext draw_sprite_stretched draw_sprite_stretched_ext draw_sprite_tiled draw_sprite_tiled_ext draw_sprite_part draw_sprite_part_ext draw_sprite_general draw_clear draw_clear_alpha draw_point draw_line draw_line_width draw_rectangle draw_roundrect draw_roundrect_ext draw_triangle draw_circle draw_ellipse draw_set_circle_precision draw_arrow draw_button draw_path draw_healthbar draw_getpixel draw_getpixel_ext draw_set_colour draw_set_color draw_set_alpha draw_get_colour draw_get_color draw_get_alpha merge_colour make_colour_rgb make_colour_hsv colour_get_red colour_get_green colour_get_blue colour_get_hue colour_get_saturation colour_get_value merge_color make_color_rgb make_color_hsv color_get_red color_get_green color_get_blue color_get_hue color_get_saturation color_get_value merge_color screen_save screen_save_part draw_set_font draw_set_halign draw_set_valign draw_text draw_text_ext string_width string_height string_width_ext string_height_ext draw_text_transformed draw_text_ext_transformed draw_text_colour draw_text_ext_colour draw_text_transformed_colour draw_text_ext_transformed_colour draw_text_color draw_text_ext_color draw_text_transformed_color draw_text_ext_transformed_color draw_point_colour draw_line_colour draw_line_width_colour draw_rectangle_colour draw_roundrect_colour draw_roundrect_colour_ext draw_triangle_colour draw_circle_colour draw_ellipse_colour draw_point_color draw_line_color draw_line_width_color draw_rectangle_color draw_roundrect_color draw_roundrect_color_ext draw_triangle_color draw_circle_color draw_ellipse_color draw_primitive_begin draw_vertex draw_vertex_colour draw_vertex_color draw_primitive_end sprite_get_uvs font_get_uvs sprite_get_texture font_get_texture texture_get_width texture_get_height texture_get_uvs draw_primitive_begin_texture draw_vertex_texture draw_vertex_texture_colour draw_vertex_texture_color texture_global_scale surface_create surface_create_ext surface_resize surface_free surface_exists surface_get_width surface_get_height surface_get_texture surface_set_target surface_set_target_ext surface_reset_target surface_depth_disable surface_get_depth_disable draw_surface draw_surface_stretched draw_surface_tiled draw_surface_part draw_surface_ext draw_surface_stretched_ext draw_surface_tiled_ext draw_surface_part_ext draw_surface_general surface_getpixel surface_getpixel_ext surface_save surface_save_part surface_copy surface_copy_part application_surface_draw_enable application_get_position application_surface_enable application_surface_is_enabled display_get_width display_get_height display_get_orientation display_get_gui_width display_get_gui_height display_reset display_mouse_get_x display_mouse_get_y display_mouse_set display_set_ui_visibility window_set_fullscreen window_get_fullscreen window_set_caption window_set_min_width window_set_max_width window_set_min_height window_set_max_height window_get_visible_rects window_get_caption window_set_cursor window_get_cursor window_set_colour window_get_colour window_set_color window_get_color window_set_position window_set_size window_set_rectangle window_center window_get_x window_get_y window_get_width window_get_height window_mouse_get_x window_mouse_get_y window_mouse_set window_view_mouse_get_x window_view_mouse_get_y window_views_mouse_get_x window_views_mouse_get_y audio_listener_position audio_listener_velocity audio_listener_orientation audio_emitter_position audio_emitter_create audio_emitter_free audio_emitter_exists audio_emitter_pitch audio_emitter_velocity audio_emitter_falloff audio_emitter_gain audio_play_sound audio_play_sound_on audio_play_sound_at audio_stop_sound audio_resume_music audio_music_is_playing audio_resume_sound audio_pause_sound audio_pause_music audio_channel_num audio_sound_length audio_get_type audio_falloff_set_model audio_play_music audio_stop_music audio_master_gain audio_music_gain audio_sound_gain audio_sound_pitch audio_stop_all audio_resume_all audio_pause_all audio_is_playing audio_is_paused audio_exists audio_sound_set_track_position audio_sound_get_track_position audio_emitter_get_gain audio_emitter_get_pitch audio_emitter_get_x audio_emitter_get_y audio_emitter_get_z audio_emitter_get_vx audio_emitter_get_vy audio_emitter_get_vz audio_listener_set_position audio_listener_set_velocity audio_listener_set_orientation audio_listener_get_data audio_set_master_gain audio_get_master_gain audio_sound_get_gain audio_sound_get_pitch audio_get_name audio_sound_set_track_position audio_sound_get_track_position audio_create_stream audio_destroy_stream audio_create_sync_group audio_destroy_sync_group audio_play_in_sync_group audio_start_sync_group audio_stop_sync_group audio_pause_sync_group audio_resume_sync_group audio_sync_group_get_track_pos audio_sync_group_debug audio_sync_group_is_playing audio_debug audio_group_load audio_group_unload audio_group_is_loaded audio_group_load_progress audio_group_name audio_group_stop_all audio_group_set_gain audio_create_buffer_sound audio_free_buffer_sound audio_create_play_queue audio_free_play_queue audio_queue_sound audio_get_recorder_count audio_get_recorder_info audio_start_recording audio_stop_recording audio_sound_get_listener_mask audio_emitter_get_listener_mask audio_get_listener_mask audio_sound_set_listener_mask audio_emitter_set_listener_mask audio_set_listener_mask audio_get_listener_count audio_get_listener_info audio_system show_message show_message_async clickable_add clickable_add_ext clickable_change clickable_change_ext clickable_delete clickable_exists clickable_set_style show_question show_question_async get_integer get_string get_integer_async get_string_async get_login_async get_open_filename get_save_filename get_open_filename_ext get_save_filename_ext show_error highscore_clear highscore_add highscore_value highscore_name draw_highscore sprite_exists sprite_get_name sprite_get_number sprite_get_width sprite_get_height sprite_get_xoffset sprite_get_yoffset sprite_get_bbox_left sprite_get_bbox_right sprite_get_bbox_top sprite_get_bbox_bottom sprite_save sprite_save_strip sprite_set_cache_size sprite_set_cache_size_ext sprite_get_tpe sprite_prefetch sprite_prefetch_multi sprite_flush sprite_flush_multi sprite_set_speed sprite_get_speed_type sprite_get_speed font_exists font_get_name font_get_fontname font_get_bold font_get_italic font_get_first font_get_last font_get_size font_set_cache_size path_exists path_get_name path_get_length path_get_time path_get_kind path_get_closed path_get_precision path_get_number path_get_point_x path_get_point_y path_get_point_speed path_get_x path_get_y path_get_speed script_exists script_get_name timeline_add timeline_delete timeline_clear timeline_exists timeline_get_name timeline_moment_clear timeline_moment_add_script timeline_size timeline_max_moment object_exists object_get_name object_get_sprite object_get_solid object_get_visible object_get_persistent object_get_mask object_get_parent object_get_physics object_is_ancestor room_exists room_get_name sprite_set_offset sprite_duplicate sprite_assign sprite_merge sprite_add sprite_replace sprite_create_from_surface sprite_add_from_surface sprite_delete sprite_set_alpha_from_sprite sprite_collision_mask font_add_enable_aa font_add_get_enable_aa font_add font_add_sprite font_add_sprite_ext font_replace font_replace_sprite font_replace_sprite_ext font_delete path_set_kind path_set_closed path_set_precision path_add path_assign path_duplicate path_append path_delete path_add_point path_insert_point path_change_point path_delete_point path_clear_points path_reverse path_mirror path_flip path_rotate path_rescale path_shift script_execute object_set_sprite object_set_solid object_set_visible object_set_persistent object_set_mask room_set_width room_set_height room_set_persistent room_set_background_colour room_set_background_color room_set_view room_set_viewport room_get_viewport room_set_view_enabled room_add room_duplicate room_assign room_instance_add room_instance_clear room_get_camera room_set_camera asset_get_index asset_get_type file_text_open_from_string file_text_open_read file_text_open_write file_text_open_append file_text_close file_text_write_string file_text_write_real file_text_writeln file_text_read_string file_text_read_real file_text_readln file_text_eof file_text_eoln file_exists file_delete file_rename file_copy directory_exists directory_create directory_destroy file_find_first file_find_next file_find_close file_attributes filename_name filename_path filename_dir filename_drive filename_ext filename_change_ext file_bin_open file_bin_rewrite file_bin_close file_bin_position file_bin_size file_bin_seek file_bin_write_byte file_bin_read_byte parameter_count parameter_string environment_get_variable ini_open_from_string ini_open ini_close ini_read_string ini_read_real ini_write_string ini_write_real ini_key_exists ini_section_exists ini_key_delete ini_section_delete ds_set_precision ds_exists ds_stack_create ds_stack_destroy ds_stack_clear ds_stack_copy ds_stack_size ds_stack_empty ds_stack_push ds_stack_pop ds_stack_top ds_stack_write ds_stack_read ds_queue_create ds_queue_destroy ds_queue_clear ds_queue_copy ds_queue_size ds_queue_empty ds_queue_enqueue ds_queue_dequeue ds_queue_head ds_queue_tail ds_queue_write ds_queue_read ds_list_create ds_list_destroy ds_list_clear ds_list_copy ds_list_size ds_list_empty ds_list_add ds_list_insert ds_list_replace ds_list_delete ds_list_find_index ds_list_find_value ds_list_mark_as_list ds_list_mark_as_map ds_list_sort ds_list_shuffle ds_list_write ds_list_read ds_list_set ds_map_create ds_map_destroy ds_map_clear ds_map_copy ds_map_size ds_map_empty ds_map_add ds_map_add_list ds_map_add_map ds_map_replace ds_map_replace_map ds_map_replace_list ds_map_delete ds_map_exists ds_map_find_value ds_map_find_previous ds_map_find_next ds_map_find_first ds_map_find_last ds_map_write ds_map_read ds_map_secure_save ds_map_secure_load ds_map_secure_load_buffer ds_map_secure_save_buffer ds_map_set ds_priority_create ds_priority_destroy ds_priority_clear ds_priority_copy ds_priority_size ds_priority_empty ds_priority_add ds_priority_change_priority ds_priority_find_priority ds_priority_delete_value ds_priority_delete_min ds_priority_find_min ds_priority_delete_max ds_priority_find_max ds_priority_write ds_priority_read ds_grid_create ds_grid_destroy ds_grid_copy ds_grid_resize ds_grid_width ds_grid_height ds_grid_clear ds_grid_set ds_grid_add ds_grid_multiply ds_grid_set_region ds_grid_add_region ds_grid_multiply_region ds_grid_set_disk ds_grid_add_disk ds_grid_multiply_disk ds_grid_set_grid_region ds_grid_add_grid_region ds_grid_multiply_grid_region ds_grid_get ds_grid_get_sum ds_grid_get_max ds_grid_get_min ds_grid_get_mean ds_grid_get_disk_sum ds_grid_get_disk_min ds_grid_get_disk_max ds_grid_get_disk_mean ds_grid_value_exists ds_grid_value_x ds_grid_value_y ds_grid_value_disk_exists ds_grid_value_disk_x ds_grid_value_disk_y ds_grid_shuffle ds_grid_write ds_grid_read ds_grid_sort ds_grid_set ds_grid_get effect_create_below effect_create_above effect_clear part_type_create part_type_destroy part_type_exists part_type_clear part_type_shape part_type_sprite part_type_size part_type_scale part_type_orientation part_type_life part_type_step part_type_death part_type_speed part_type_direction part_type_gravity part_type_colour1 part_type_colour2 part_type_colour3 part_type_colour_mix part_type_colour_rgb part_type_colour_hsv part_type_color1 part_type_color2 part_type_color3 part_type_color_mix part_type_color_rgb part_type_color_hsv part_type_alpha1 part_type_alpha2 part_type_alpha3 part_type_blend part_system_create part_system_create_layer part_system_destroy part_system_exists part_system_clear part_system_draw_order part_system_depth part_system_position part_system_automatic_update part_system_automatic_draw part_system_update part_system_drawit part_system_get_layer part_system_layer part_particles_create part_particles_create_colour part_particles_create_color part_particles_clear part_particles_count part_emitter_create part_emitter_destroy part_emitter_destroy_all part_emitter_exists part_emitter_clear part_emitter_region part_emitter_burst part_emitter_stream external_call external_define external_free window_handle window_device matrix_get matrix_set matrix_build_identity matrix_build matrix_build_lookat matrix_build_projection_ortho matrix_build_projection_perspective matrix_build_projection_perspective_fov matrix_multiply matrix_transform_vertex matrix_stack_push matrix_stack_pop matrix_stack_multiply matrix_stack_set matrix_stack_clear matrix_stack_top matrix_stack_is_empty browser_input_capture os_get_config os_get_info os_get_language os_get_region os_lock_orientation display_get_dpi_x display_get_dpi_y display_set_gui_size display_set_gui_maximise display_set_gui_maximize device_mouse_dbclick_enable display_set_timing_method display_get_timing_method display_set_sleep_margin display_get_sleep_margin virtual_key_add virtual_key_hide virtual_key_delete virtual_key_show draw_enable_drawevent draw_enable_swf_aa draw_set_swf_aa_level draw_get_swf_aa_level draw_texture_flush draw_flush gpu_set_blendenable gpu_set_ztestenable gpu_set_zfunc gpu_set_zwriteenable gpu_set_lightingenable gpu_set_fog gpu_set_cullmode gpu_set_blendmode gpu_set_blendmode_ext gpu_set_blendmode_ext_sepalpha gpu_set_colorwriteenable gpu_set_colourwriteenable gpu_set_alphatestenable gpu_set_alphatestref gpu_set_alphatestfunc gpu_set_texfilter gpu_set_texfilter_ext gpu_set_texrepeat gpu_set_texrepeat_ext gpu_set_tex_filter gpu_set_tex_filter_ext gpu_set_tex_repeat gpu_set_tex_repeat_ext gpu_set_tex_mip_filter gpu_set_tex_mip_filter_ext gpu_set_tex_mip_bias gpu_set_tex_mip_bias_ext gpu_set_tex_min_mip gpu_set_tex_min_mip_ext gpu_set_tex_max_mip gpu_set_tex_max_mip_ext gpu_set_tex_max_aniso gpu_set_tex_max_aniso_ext gpu_set_tex_mip_enable gpu_set_tex_mip_enable_ext gpu_get_blendenable gpu_get_ztestenable gpu_get_zfunc gpu_get_zwriteenable gpu_get_lightingenable gpu_get_fog gpu_get_cullmode gpu_get_blendmode gpu_get_blendmode_ext gpu_get_blendmode_ext_sepalpha gpu_get_blendmode_src gpu_get_blendmode_dest gpu_get_blendmode_srcalpha gpu_get_blendmode_destalpha gpu_get_colorwriteenable gpu_get_colourwriteenable gpu_get_alphatestenable gpu_get_alphatestref gpu_get_alphatestfunc gpu_get_texfilter gpu_get_texfilter_ext gpu_get_texrepeat gpu_get_texrepeat_ext gpu_get_tex_filter gpu_get_tex_filter_ext gpu_get_tex_repeat gpu_get_tex_repeat_ext gpu_get_tex_mip_filter gpu_get_tex_mip_filter_ext gpu_get_tex_mip_bias gpu_get_tex_mip_bias_ext gpu_get_tex_min_mip gpu_get_tex_min_mip_ext gpu_get_tex_max_mip gpu_get_tex_max_mip_ext gpu_get_tex_max_aniso gpu_get_tex_max_aniso_ext gpu_get_tex_mip_enable gpu_get_tex_mip_enable_ext gpu_push_state gpu_pop_state gpu_get_state gpu_set_state draw_light_define_ambient draw_light_define_direction draw_light_define_point draw_light_enable draw_set_lighting draw_light_get_ambient draw_light_get draw_get_lighting shop_leave_rating url_get_domain url_open url_open_ext url_open_full get_timer achievement_login achievement_logout achievement_post achievement_increment achievement_post_score achievement_available achievement_show_achievements achievement_show_leaderboards achievement_load_friends achievement_load_leaderboard achievement_send_challenge achievement_load_progress achievement_reset achievement_login_status achievement_get_pic achievement_show_challenge_notifications achievement_get_challenges achievement_event achievement_show achievement_get_info cloud_file_save cloud_string_save cloud_synchronise ads_enable ads_disable ads_setup ads_engagement_launch ads_engagement_available ads_engagement_active ads_event ads_event_preload ads_set_reward_callback ads_get_display_height ads_get_display_width ads_move ads_interstitial_available ads_interstitial_display device_get_tilt_x device_get_tilt_y device_get_tilt_z device_is_keypad_open device_mouse_check_button device_mouse_check_button_pressed device_mouse_check_button_released device_mouse_x device_mouse_y device_mouse_raw_x device_mouse_raw_y device_mouse_x_to_gui device_mouse_y_to_gui iap_activate iap_status iap_enumerate_products iap_restore_all iap_acquire iap_consume iap_product_details iap_purchase_details facebook_init facebook_login facebook_status facebook_graph_request facebook_dialog facebook_logout facebook_launch_offerwall facebook_post_message facebook_send_invite facebook_user_id facebook_accesstoken facebook_check_permission facebook_request_read_permissions facebook_request_publish_permissions gamepad_is_supported gamepad_get_device_count gamepad_is_connected gamepad_get_description gamepad_get_button_threshold gamepad_set_button_threshold gamepad_get_axis_deadzone gamepad_set_axis_deadzone gamepad_button_count gamepad_button_check gamepad_button_check_pressed gamepad_button_check_released gamepad_button_value gamepad_axis_count gamepad_axis_value gamepad_set_vibration gamepad_set_colour gamepad_set_color os_is_paused window_has_focus code_is_compiled http_get http_get_file http_post_string http_request json_encode json_decode zip_unzip load_csv base64_encode base64_decode md5_string_unicode md5_string_utf8 md5_file os_is_network_connected sha1_string_unicode sha1_string_utf8 sha1_file os_powersave_enable analytics_event analytics_event_ext win8_livetile_tile_notification win8_livetile_tile_clear win8_livetile_badge_notification win8_livetile_badge_clear win8_livetile_queue_enable win8_secondarytile_pin win8_secondarytile_badge_notification win8_secondarytile_delete win8_livetile_notification_begin win8_livetile_notification_secondary_begin win8_livetile_notification_expiry win8_livetile_notification_tag win8_livetile_notification_text_add win8_livetile_notification_image_add win8_livetile_notification_end win8_appbar_enable win8_appbar_add_element win8_appbar_remove_element win8_settingscharm_add_entry win8_settingscharm_add_html_entry win8_settingscharm_add_xaml_entry win8_settingscharm_set_xaml_property win8_settingscharm_get_xaml_property win8_settingscharm_remove_entry win8_share_image win8_share_screenshot win8_share_file win8_share_url win8_share_text win8_search_enable win8_search_disable win8_search_add_suggestions win8_device_touchscreen_available win8_license_initialize_sandbox win8_license_trial_version winphone_license_trial_version winphone_tile_title winphone_tile_count winphone_tile_back_title winphone_tile_back_content winphone_tile_back_content_wide winphone_tile_front_image winphone_tile_front_image_small winphone_tile_front_image_wide winphone_tile_back_image winphone_tile_back_image_wide winphone_tile_background_colour winphone_tile_background_color winphone_tile_icon_image winphone_tile_small_icon_image winphone_tile_wide_content winphone_tile_cycle_images winphone_tile_small_background_image physics_world_create physics_world_gravity physics_world_update_speed physics_world_update_iterations physics_world_draw_debug physics_pause_enable physics_fixture_create physics_fixture_set_kinematic physics_fixture_set_density physics_fixture_set_awake physics_fixture_set_restitution physics_fixture_set_friction physics_fixture_set_collision_group physics_fixture_set_sensor physics_fixture_set_linear_damping physics_fixture_set_angular_damping physics_fixture_set_circle_shape physics_fixture_set_box_shape physics_fixture_set_edge_shape physics_fixture_set_polygon_shape physics_fixture_set_chain_shape physics_fixture_add_point physics_fixture_bind physics_fixture_bind_ext physics_fixture_delete physics_apply_force physics_apply_impulse physics_apply_angular_impulse physics_apply_local_force physics_apply_local_impulse physics_apply_torque physics_mass_properties physics_draw_debug physics_test_overlap physics_remove_fixture physics_set_friction physics_set_density physics_set_restitution physics_get_friction physics_get_density physics_get_restitution physics_joint_distance_create physics_joint_rope_create physics_joint_revolute_create physics_joint_prismatic_create physics_joint_pulley_create physics_joint_wheel_create physics_joint_weld_create physics_joint_friction_create physics_joint_gear_create physics_joint_enable_motor physics_joint_get_value physics_joint_set_value physics_joint_delete physics_particle_create physics_particle_delete physics_particle_delete_region_circle physics_particle_delete_region_box physics_particle_delete_region_poly physics_particle_set_flags physics_particle_set_category_flags physics_particle_draw physics_particle_draw_ext physics_particle_count physics_particle_get_data physics_particle_get_data_particle physics_particle_group_begin physics_particle_group_circle physics_particle_group_box physics_particle_group_polygon physics_particle_group_add_point physics_particle_group_end physics_particle_group_join physics_particle_group_delete physics_particle_group_count physics_particle_group_get_data physics_particle_group_get_mass physics_particle_group_get_inertia physics_particle_group_get_centre_x physics_particle_group_get_centre_y physics_particle_group_get_vel_x physics_particle_group_get_vel_y physics_particle_group_get_ang_vel physics_particle_group_get_x physics_particle_group_get_y physics_particle_group_get_angle physics_particle_set_group_flags physics_particle_get_group_flags physics_particle_get_max_count physics_particle_get_radius physics_particle_get_density physics_particle_get_damping physics_particle_get_gravity_scale physics_particle_set_max_count physics_particle_set_radius physics_particle_set_density physics_particle_set_damping physics_particle_set_gravity_scale network_create_socket network_create_socket_ext network_create_server network_create_server_raw network_connect network_connect_raw network_send_packet network_send_raw network_send_broadcast network_send_udp network_send_udp_raw network_set_timeout network_set_config network_resolve network_destroy buffer_create buffer_write buffer_read buffer_seek buffer_get_surface buffer_set_surface buffer_delete buffer_exists buffer_get_type buffer_get_alignment buffer_poke buffer_peek buffer_save buffer_save_ext buffer_load buffer_load_ext buffer_load_partial buffer_copy buffer_fill buffer_get_size buffer_tell buffer_resize buffer_md5 buffer_sha1 buffer_base64_encode buffer_base64_decode buffer_base64_decode_ext buffer_sizeof buffer_get_address buffer_create_from_vertex_buffer buffer_create_from_vertex_buffer_ext buffer_copy_from_vertex_buffer buffer_async_group_begin buffer_async_group_option buffer_async_group_end buffer_load_async buffer_save_async gml_release_mode gml_pragma steam_activate_overlay steam_is_overlay_enabled steam_is_overlay_activated steam_get_persona_name steam_initialised steam_is_cloud_enabled_for_app steam_is_cloud_enabled_for_account steam_file_persisted steam_get_quota_total steam_get_quota_free steam_file_write steam_file_write_file steam_file_read steam_file_delete steam_file_exists steam_file_size steam_file_share steam_is_screenshot_requested steam_send_screenshot steam_is_user_logged_on steam_get_user_steam_id steam_user_owns_dlc steam_user_installed_dlc steam_set_achievement steam_get_achievement steam_clear_achievement steam_set_stat_int steam_set_stat_float steam_set_stat_avg_rate steam_get_stat_int steam_get_stat_float steam_get_stat_avg_rate steam_reset_all_stats steam_reset_all_stats_achievements steam_stats_ready steam_create_leaderboard steam_upload_score steam_upload_score_ext steam_download_scores_around_user steam_download_scores steam_download_friends_scores steam_upload_score_buffer steam_upload_score_buffer_ext steam_current_game_language steam_available_languages steam_activate_overlay_browser steam_activate_overlay_user steam_activate_overlay_store steam_get_user_persona_name steam_get_app_id steam_get_user_account_id steam_ugc_download steam_ugc_create_item steam_ugc_start_item_update steam_ugc_set_item_title steam_ugc_set_item_description steam_ugc_set_item_visibility steam_ugc_set_item_tags steam_ugc_set_item_content steam_ugc_set_item_preview steam_ugc_submit_item_update steam_ugc_get_item_update_progress steam_ugc_subscribe_item steam_ugc_unsubscribe_item steam_ugc_num_subscribed_items steam_ugc_get_subscribed_items steam_ugc_get_item_install_info steam_ugc_get_item_update_info steam_ugc_request_item_details steam_ugc_create_query_user steam_ugc_create_query_user_ex steam_ugc_create_query_all steam_ugc_create_query_all_ex steam_ugc_query_set_cloud_filename_filter steam_ugc_query_set_match_any_tag steam_ugc_query_set_search_text steam_ugc_query_set_ranked_by_trend_days steam_ugc_query_add_required_tag steam_ugc_query_add_excluded_tag steam_ugc_query_set_return_long_description steam_ugc_query_set_return_total_only steam_ugc_query_set_allow_cached_response steam_ugc_send_query shader_set shader_get_name shader_reset shader_current shader_is_compiled shader_get_sampler_index shader_get_uniform shader_set_uniform_i shader_set_uniform_i_array shader_set_uniform_f shader_set_uniform_f_array shader_set_uniform_matrix shader_set_uniform_matrix_array shader_enable_corner_id texture_set_stage texture_get_texel_width texture_get_texel_height shaders_are_supported vertex_format_begin vertex_format_end vertex_format_delete vertex_format_add_position vertex_format_add_position_3d vertex_format_add_colour vertex_format_add_color vertex_format_add_normal vertex_format_add_texcoord vertex_format_add_textcoord vertex_format_add_custom vertex_create_buffer vertex_create_buffer_ext vertex_delete_buffer vertex_begin vertex_end vertex_position vertex_position_3d vertex_colour vertex_color vertex_argb vertex_texcoord vertex_normal vertex_float1 vertex_float2 vertex_float3 vertex_float4 vertex_ubyte4 vertex_submit vertex_freeze vertex_get_number vertex_get_buffer_size vertex_create_buffer_from_buffer vertex_create_buffer_from_buffer_ext push_local_notification push_get_first_local_notification push_get_next_local_notification push_cancel_local_notification skeleton_animation_set skeleton_animation_get skeleton_animation_mix skeleton_animation_set_ext skeleton_animation_get_ext skeleton_animation_get_duration skeleton_animation_get_frames skeleton_animation_clear skeleton_skin_set skeleton_skin_get skeleton_attachment_set skeleton_attachment_get skeleton_attachment_create skeleton_collision_draw_set skeleton_bone_data_get skeleton_bone_data_set skeleton_bone_state_get skeleton_bone_state_set skeleton_get_minmax skeleton_get_num_bounds skeleton_get_bounds skeleton_animation_get_frame skeleton_animation_set_frame draw_skeleton draw_skeleton_time draw_skeleton_instance draw_skeleton_collision skeleton_animation_list skeleton_skin_list skeleton_slot_data layer_get_id layer_get_id_at_depth layer_get_depth layer_create layer_destroy layer_destroy_instances layer_add_instance layer_has_instance layer_set_visible layer_get_visible layer_exists layer_x layer_y layer_get_x layer_get_y layer_hspeed layer_vspeed layer_get_hspeed layer_get_vspeed layer_script_begin layer_script_end layer_shader layer_get_script_begin layer_get_script_end layer_get_shader layer_set_target_room layer_get_target_room layer_reset_target_room layer_get_all layer_get_all_elements layer_get_name layer_depth layer_get_element_layer layer_get_element_type layer_element_move layer_force_draw_depth layer_is_draw_depth_forced layer_get_forced_depth layer_background_get_id layer_background_exists layer_background_create layer_background_destroy layer_background_visible layer_background_change layer_background_sprite layer_background_htiled layer_background_vtiled layer_background_stretch layer_background_yscale layer_background_xscale layer_background_blend layer_background_alpha layer_background_index layer_background_speed layer_background_get_visible layer_background_get_sprite layer_background_get_htiled layer_background_get_vtiled layer_background_get_stretch layer_background_get_yscale layer_background_get_xscale layer_background_get_blend layer_background_get_alpha layer_background_get_index layer_background_get_speed layer_sprite_get_id layer_sprite_exists layer_sprite_create layer_sprite_destroy layer_sprite_change layer_sprite_index layer_sprite_speed layer_sprite_xscale layer_sprite_yscale layer_sprite_angle layer_sprite_blend layer_sprite_alpha layer_sprite_x layer_sprite_y layer_sprite_get_sprite layer_sprite_get_index layer_sprite_get_speed layer_sprite_get_xscale layer_sprite_get_yscale layer_sprite_get_angle layer_sprite_get_blend layer_sprite_get_alpha layer_sprite_get_x layer_sprite_get_y layer_tilemap_get_id layer_tilemap_exists layer_tilemap_create layer_tilemap_destroy tilemap_tileset tilemap_x tilemap_y tilemap_set tilemap_set_at_pixel tilemap_get_tileset tilemap_get_tile_width tilemap_get_tile_height tilemap_get_width tilemap_get_height tilemap_get_x tilemap_get_y tilemap_get tilemap_get_at_pixel tilemap_get_cell_x_at_pixel tilemap_get_cell_y_at_pixel tilemap_clear draw_tilemap draw_tile tilemap_set_global_mask tilemap_get_global_mask tilemap_set_mask tilemap_get_mask tilemap_get_frame tile_set_empty tile_set_index tile_set_flip tile_set_mirror tile_set_rotate tile_get_empty tile_get_index tile_get_flip tile_get_mirror tile_get_rotate layer_tile_exists layer_tile_create layer_tile_destroy layer_tile_change layer_tile_xscale layer_tile_yscale layer_tile_blend layer_tile_alpha layer_tile_x layer_tile_y layer_tile_region layer_tile_visible layer_tile_get_sprite layer_tile_get_xscale layer_tile_get_yscale layer_tile_get_blend layer_tile_get_alpha layer_tile_get_x layer_tile_get_y layer_tile_get_region layer_tile_get_visible layer_instance_get_instance instance_activate_layer instance_deactivate_layer camera_create camera_create_view camera_destroy camera_apply camera_get_active camera_get_default camera_set_default camera_set_view_mat camera_set_proj_mat camera_set_update_script camera_set_begin_script camera_set_end_script camera_set_view_pos camera_set_view_size camera_set_view_speed camera_set_view_border camera_set_view_angle camera_set_view_target camera_get_view_mat camera_get_proj_mat camera_get_update_script camera_get_begin_script camera_get_end_script camera_get_view_x camera_get_view_y camera_get_view_width camera_get_view_height camera_get_view_speed_x camera_get_view_speed_y camera_get_view_border_x camera_get_view_border_y camera_get_view_angle camera_get_view_target view_get_camera view_get_visible view_get_xport view_get_yport view_get_wport view_get_hport view_get_surface_id view_set_camera view_set_visible view_set_xport view_set_yport view_set_wport view_set_hport view_set_surface_id gesture_drag_time gesture_drag_distance gesture_flick_speed gesture_double_tap_time gesture_double_tap_distance gesture_pinch_distance gesture_pinch_angle_towards gesture_pinch_angle_away gesture_rotate_time gesture_rotate_angle gesture_tap_count gesture_get_drag_time gesture_get_drag_distance gesture_get_flick_speed gesture_get_double_tap_time gesture_get_double_tap_distance gesture_get_pinch_distance gesture_get_pinch_angle_towards gesture_get_pinch_angle_away gesture_get_rotate_time gesture_get_rotate_angle gesture_get_tap_count keyboard_virtual_show keyboard_virtual_hide keyboard_virtual_status keyboard_virtual_height",
 literal:"self other all noone global local undefined pointer_invalid pointer_null path_action_stop path_action_restart path_action_continue path_action_reverse true false pi GM_build_date GM_version GM_runtime_version  timezone_local timezone_utc gamespeed_fps gamespeed_microseconds  ev_create ev_destroy ev_step ev_alarm ev_keyboard ev_mouse ev_collision ev_other ev_draw ev_draw_begin ev_draw_end ev_draw_pre ev_draw_post ev_keypress ev_keyrelease ev_trigger ev_left_button ev_right_button ev_middle_button ev_no_button ev_left_press ev_right_press ev_middle_press ev_left_release ev_right_release ev_middle_release ev_mouse_enter ev_mouse_leave ev_mouse_wheel_up ev_mouse_wheel_down ev_global_left_button ev_global_right_button ev_global_middle_button ev_global_left_press ev_global_right_press ev_global_middle_press ev_global_left_release ev_global_right_release ev_global_middle_release ev_joystick1_left ev_joystick1_right ev_joystick1_up ev_joystick1_down ev_joystick1_button1 ev_joystick1_button2 ev_joystick1_button3 ev_joystick1_button4 ev_joystick1_button5 ev_joystick1_button6 ev_joystick1_button7 ev_joystick1_button8 ev_joystick2_left ev_joystick2_right ev_joystick2_up ev_joystick2_down ev_joystick2_button1 ev_joystick2_button2 ev_joystick2_button3 ev_joystick2_button4 ev_joystick2_button5 ev_joystick2_button6 ev_joystick2_button7 ev_joystick2_button8 ev_outside ev_boundary ev_game_start ev_game_end ev_room_start ev_room_end ev_no_more_lives ev_animation_end ev_end_of_path ev_no_more_health ev_close_button ev_user0 ev_user1 ev_user2 ev_user3 ev_user4 ev_user5 ev_user6 ev_user7 ev_user8 ev_user9 ev_user10 ev_user11 ev_user12 ev_user13 ev_user14 ev_user15 ev_step_normal ev_step_begin ev_step_end ev_gui ev_gui_begin ev_gui_end ev_cleanup ev_gesture ev_gesture_tap ev_gesture_double_tap ev_gesture_drag_start ev_gesture_dragging ev_gesture_drag_end ev_gesture_flick ev_gesture_pinch_start ev_gesture_pinch_in ev_gesture_pinch_out ev_gesture_pinch_end ev_gesture_rotate_start ev_gesture_rotating ev_gesture_rotate_end ev_global_gesture_tap ev_global_gesture_double_tap ev_global_gesture_drag_start ev_global_gesture_dragging ev_global_gesture_drag_end ev_global_gesture_flick ev_global_gesture_pinch_start ev_global_gesture_pinch_in ev_global_gesture_pinch_out ev_global_gesture_pinch_end ev_global_gesture_rotate_start ev_global_gesture_rotating ev_global_gesture_rotate_end vk_nokey vk_anykey vk_enter vk_return vk_shift vk_control vk_alt vk_escape vk_space vk_backspace vk_tab vk_pause vk_printscreen vk_left vk_right vk_up vk_down vk_home vk_end vk_delete vk_insert vk_pageup vk_pagedown vk_f1 vk_f2 vk_f3 vk_f4 vk_f5 vk_f6 vk_f7 vk_f8 vk_f9 vk_f10 vk_f11 vk_f12 vk_numpad0 vk_numpad1 vk_numpad2 vk_numpad3 vk_numpad4 vk_numpad5 vk_numpad6 vk_numpad7 vk_numpad8 vk_numpad9 vk_divide vk_multiply vk_subtract vk_add vk_decimal vk_lshift vk_lcontrol vk_lalt vk_rshift vk_rcontrol vk_ralt  mb_any mb_none mb_left mb_right mb_middle c_aqua c_black c_blue c_dkgray c_fuchsia c_gray c_green c_lime c_ltgray c_maroon c_navy c_olive c_purple c_red c_silver c_teal c_white c_yellow c_orange fa_left fa_center fa_right fa_top fa_middle fa_bottom pr_pointlist pr_linelist pr_linestrip pr_trianglelist pr_trianglestrip pr_trianglefan bm_complex bm_normal bm_add bm_max bm_subtract bm_zero bm_one bm_src_colour bm_inv_src_colour bm_src_color bm_inv_src_color bm_src_alpha bm_inv_src_alpha bm_dest_alpha bm_inv_dest_alpha bm_dest_colour bm_inv_dest_colour bm_dest_color bm_inv_dest_color bm_src_alpha_sat tf_point tf_linear tf_anisotropic mip_off mip_on mip_markedonly audio_falloff_none audio_falloff_inverse_distance audio_falloff_inverse_distance_clamped audio_falloff_linear_distance audio_falloff_linear_distance_clamped audio_falloff_exponent_distance audio_falloff_exponent_distance_clamped audio_old_system audio_new_system audio_mono audio_stereo audio_3d cr_default cr_none cr_arrow cr_cross cr_beam cr_size_nesw cr_size_ns cr_size_nwse cr_size_we cr_uparrow cr_hourglass cr_drag cr_appstart cr_handpoint cr_size_all spritespeed_framespersecond spritespeed_framespergameframe asset_object asset_unknown asset_sprite asset_sound asset_room asset_path asset_script asset_font asset_timeline asset_tiles asset_shader fa_readonly fa_hidden fa_sysfile fa_volumeid fa_directory fa_archive  ds_type_map ds_type_list ds_type_stack ds_type_queue ds_type_grid ds_type_priority ef_explosion ef_ring ef_ellipse ef_firework ef_smoke ef_smokeup ef_star ef_spark ef_flare ef_cloud ef_rain ef_snow pt_shape_pixel pt_shape_disk pt_shape_square pt_shape_line pt_shape_star pt_shape_circle pt_shape_ring pt_shape_sphere pt_shape_flare pt_shape_spark pt_shape_explosion pt_shape_cloud pt_shape_smoke pt_shape_snow ps_distr_linear ps_distr_gaussian ps_distr_invgaussian ps_shape_rectangle ps_shape_ellipse ps_shape_diamond ps_shape_line ty_real ty_string dll_cdecl dll_stdcall matrix_view matrix_projection matrix_world os_win32 os_windows os_macosx os_ios os_android os_symbian os_linux os_unknown os_winphone os_tizen os_win8native os_wiiu os_3ds  os_psvita os_bb10 os_ps4 os_xboxone os_ps3 os_xbox360 os_uwp os_tvos os_switch browser_not_a_browser browser_unknown browser_ie browser_firefox browser_chrome browser_safari browser_safari_mobile browser_opera browser_tizen browser_edge browser_windows_store browser_ie_mobile  device_ios_unknown device_ios_iphone device_ios_iphone_retina device_ios_ipad device_ios_ipad_retina device_ios_iphone5 device_ios_iphone6 device_ios_iphone6plus device_emulator device_tablet display_landscape display_landscape_flipped display_portrait display_portrait_flipped tm_sleep tm_countvsyncs of_challenge_win of_challen ge_lose of_challenge_tie leaderboard_type_number leaderboard_type_time_mins_secs cmpfunc_never cmpfunc_less cmpfunc_equal cmpfunc_lessequal cmpfunc_greater cmpfunc_notequal cmpfunc_greaterequal cmpfunc_always cull_noculling cull_clockwise cull_counterclockwise lighttype_dir lighttype_point iap_ev_storeload iap_ev_product iap_ev_purchase iap_ev_consume iap_ev_restore iap_storeload_ok iap_storeload_failed iap_status_uninitialised iap_status_unavailable iap_status_loading iap_status_available iap_status_processing iap_status_restoring iap_failed iap_unavailable iap_available iap_purchased iap_canceled iap_refunded fb_login_default fb_login_fallback_to_webview fb_login_no_fallback_to_webview fb_login_forcing_webview fb_login_use_system_account fb_login_forcing_safari  phy_joint_anchor_1_x phy_joint_anchor_1_y phy_joint_anchor_2_x phy_joint_anchor_2_y phy_joint_reaction_force_x phy_joint_reaction_force_y phy_joint_reaction_torque phy_joint_motor_speed phy_joint_angle phy_joint_motor_torque phy_joint_max_motor_torque phy_joint_translation phy_joint_speed phy_joint_motor_force phy_joint_max_motor_force phy_joint_length_1 phy_joint_length_2 phy_joint_damping_ratio phy_joint_frequency phy_joint_lower_angle_limit phy_joint_upper_angle_limit phy_joint_angle_limits phy_joint_max_length phy_joint_max_torque phy_joint_max_force phy_debug_render_aabb phy_debug_render_collision_pairs phy_debug_render_coms phy_debug_render_core_shapes phy_debug_render_joints phy_debug_render_obb phy_debug_render_shapes  phy_particle_flag_water phy_particle_flag_zombie phy_particle_flag_wall phy_particle_flag_spring phy_particle_flag_elastic phy_particle_flag_viscous phy_particle_flag_powder phy_particle_flag_tensile phy_particle_flag_colourmixing phy_particle_flag_colormixing phy_particle_group_flag_solid phy_particle_group_flag_rigid phy_particle_data_flag_typeflags phy_particle_data_flag_position phy_particle_data_flag_velocity phy_particle_data_flag_colour phy_particle_data_flag_color phy_particle_data_flag_category  achievement_our_info achievement_friends_info achievement_leaderboard_info achievement_achievement_info achievement_filter_all_players achievement_filter_friends_only achievement_filter_favorites_only achievement_type_achievement_challenge achievement_type_score_challenge achievement_pic_loaded  achievement_show_ui achievement_show_profile achievement_show_leaderboard achievement_show_achievement achievement_show_bank achievement_show_friend_picker achievement_show_purchase_prompt network_socket_tcp network_socket_udp network_socket_bluetooth network_type_connect network_type_disconnect network_type_data network_type_non_blocking_connect network_config_connect_timeout network_config_use_non_blocking_socket network_config_enable_reliable_udp network_config_disable_reliable_udp buffer_fixed buffer_grow buffer_wrap buffer_fast buffer_vbuffer buffer_network buffer_u8 buffer_s8 buffer_u16 buffer_s16 buffer_u32 buffer_s32 buffer_u64 buffer_f16 buffer_f32 buffer_f64 buffer_bool buffer_text buffer_string buffer_surface_copy buffer_seek_start buffer_seek_relative buffer_seek_end buffer_generalerror buffer_outofspace buffer_outofbounds buffer_invalidtype  text_type button_type input_type ANSI_CHARSET DEFAULT_CHARSET EASTEUROPE_CHARSET RUSSIAN_CHARSET SYMBOL_CHARSET SHIFTJIS_CHARSET HANGEUL_CHARSET GB2312_CHARSET CHINESEBIG5_CHARSET JOHAB_CHARSET HEBREW_CHARSET ARABIC_CHARSET GREEK_CHARSET TURKISH_CHARSET VIETNAMESE_CHARSET THAI_CHARSET MAC_CHARSET BALTIC_CHARSET OEM_CHARSET  gp_face1 gp_face2 gp_face3 gp_face4 gp_shoulderl gp_shoulderr gp_shoulderlb gp_shoulderrb gp_select gp_start gp_stickl gp_stickr gp_padu gp_padd gp_padl gp_padr gp_axislh gp_axislv gp_axisrh gp_axisrv ov_friends ov_community ov_players ov_settings ov_gamegroup ov_achievements lb_sort_none lb_sort_ascending lb_sort_descending lb_disp_none lb_disp_numeric lb_disp_time_sec lb_disp_time_ms ugc_result_success ugc_filetype_community ugc_filetype_microtrans ugc_visibility_public ugc_visibility_friends_only ugc_visibility_private ugc_query_RankedByVote ugc_query_RankedByPublicationDate ugc_query_AcceptedForGameRankedByAcceptanceDate ugc_query_RankedByTrend ugc_query_FavoritedByFriendsRankedByPublicationDate ugc_query_CreatedByFriendsRankedByPublicationDate ugc_query_RankedByNumTimesReported ugc_query_CreatedByFollowedUsersRankedByPublicationDate ugc_query_NotYetRated ugc_query_RankedByTotalVotesAsc ugc_query_RankedByVotesUp ugc_query_RankedByTextSearch ugc_sortorder_CreationOrderDesc ugc_sortorder_CreationOrderAsc ugc_sortorder_TitleAsc ugc_sortorder_LastUpdatedDesc ugc_sortorder_SubscriptionDateDesc ugc_sortorder_VoteScoreDesc ugc_sortorder_ForModeration ugc_list_Published ugc_list_VotedOn ugc_list_VotedUp ugc_list_VotedDown ugc_list_WillVoteLater ugc_list_Favorited ugc_list_Subscribed ugc_list_UsedOrPlayed ugc_list_Followed ugc_match_Items ugc_match_Items_Mtx ugc_match_Items_ReadyToUse ugc_match_Collections ugc_match_Artwork ugc_match_Videos ugc_match_Screenshots ugc_match_AllGuides ugc_match_WebGuides ugc_match_IntegratedGuides ugc_match_UsableInGame ugc_match_ControllerBindings  vertex_usage_position vertex_usage_colour vertex_usage_color vertex_usage_normal vertex_usage_texcoord vertex_usage_textcoord vertex_usage_blendweight vertex_usage_blendindices vertex_usage_psize vertex_usage_tangent vertex_usage_binormal vertex_usage_fog vertex_usage_depth vertex_usage_sample vertex_type_float1 vertex_type_float2 vertex_type_float3 vertex_type_float4 vertex_type_colour vertex_type_color vertex_type_ubyte4 layerelementtype_undefined layerelementtype_background layerelementtype_instance layerelementtype_oldtilemap layerelementtype_sprite layerelementtype_tilemap layerelementtype_particlesystem layerelementtype_tile tile_rotate tile_flip tile_mirror tile_index_mask kbv_type_default kbv_type_ascii kbv_type_url kbv_type_email kbv_type_numbers kbv_type_phone kbv_type_phone_name kbv_returnkey_default kbv_returnkey_go kbv_returnkey_google kbv_returnkey_join kbv_returnkey_next kbv_returnkey_route kbv_returnkey_search kbv_returnkey_send kbv_returnkey_yahoo kbv_returnkey_done kbv_returnkey_continue kbv_returnkey_emergency kbv_autocapitalize_none kbv_autocapitalize_words kbv_autocapitalize_sentences kbv_autocapitalize_characters",
-symbol:"argument_relative argument argument0 argument1 argument2 argument3 argument4 argument5 argument6 argument7 argument8 argument9 argument10 argument11 argument12 argument13 argument14 argument15 argument_count x y xprevious yprevious xstart ystart hspeed vspeed direction speed friction gravity gravity_direction path_index path_position path_positionprevious path_speed path_scale path_orientation path_endaction object_index id solid persistent mask_index instance_count instance_id room_speed fps fps_real current_time current_year current_month current_day current_weekday current_hour current_minute current_second alarm timeline_index timeline_position timeline_speed timeline_running timeline_loop room room_first room_last room_width room_height room_caption room_persistent score lives health show_score show_lives show_health caption_score caption_lives caption_health event_type event_number event_object event_action application_surface gamemaker_pro gamemaker_registered gamemaker_version error_occurred error_last debug_mode keyboard_key keyboard_lastkey keyboard_lastchar keyboard_string mouse_x mouse_y mouse_button mouse_lastbutton cursor_sprite visible sprite_index sprite_width sprite_height sprite_xoffset sprite_yoffset image_number image_index image_speed depth image_xscale image_yscale image_angle image_alpha image_blend bbox_left bbox_right bbox_top bbox_bottom layer background_colour  background_showcolour background_color background_showcolor view_enabled view_current view_visible view_xview view_yview view_wview view_hview view_xport view_yport view_wport view_hport view_angle view_hborder view_vborder view_hspeed view_vspeed view_object view_surface_id view_camera game_id game_display_name game_project_name game_save_id working_directory temp_directory program_directory browser_width browser_height os_type os_device os_browser os_version display_aa async_load delta_time webgl_enabled event_data iap_data phy_rotation phy_position_x phy_position_y phy_angular_velocity phy_linear_velocity_x phy_linear_velocity_y phy_speed_x phy_speed_y phy_speed phy_angular_damping phy_linear_damping phy_bullet phy_fixed_rotation phy_active phy_mass phy_inertia phy_com_x phy_com_y phy_dynamic phy_kinematic phy_sleeping phy_collision_points phy_collision_x phy_collision_y phy_col_normal_x phy_col_normal_y phy_position_xprevious phy_position_yprevious"
+symbol:"argument_relative argument argument0 argument1 argument2 argument3 argument4 argument5 argument6 argument7 argument8 argument9 argument10 argument11 argument12 argument13 argument14 argument15 argument_count x|0 y|0 xprevious yprevious xstart ystart hspeed vspeed direction speed friction gravity gravity_direction path_index path_position path_positionprevious path_speed path_scale path_orientation path_endaction object_index id solid persistent mask_index instance_count instance_id room_speed fps fps_real current_time current_year current_month current_day current_weekday current_hour current_minute current_second alarm timeline_index timeline_position timeline_speed timeline_running timeline_loop room room_first room_last room_width room_height room_caption room_persistent score lives health show_score show_lives show_health caption_score caption_lives caption_health event_type event_number event_object event_action application_surface gamemaker_pro gamemaker_registered gamemaker_version error_occurred error_last debug_mode keyboard_key keyboard_lastkey keyboard_lastchar keyboard_string mouse_x mouse_y mouse_button mouse_lastbutton cursor_sprite visible sprite_index sprite_width sprite_height sprite_xoffset sprite_yoffset image_number image_index image_speed depth image_xscale image_yscale image_angle image_alpha image_blend bbox_left bbox_right bbox_top bbox_bottom layer background_colour  background_showcolour background_color background_showcolor view_enabled view_current view_visible view_xview view_yview view_wview view_hview view_xport view_yport view_wport view_hport view_angle view_hborder view_vborder view_hspeed view_vspeed view_object view_surface_id view_camera game_id game_display_name game_project_name game_save_id working_directory temp_directory program_directory browser_width browser_height os_type os_device os_browser os_version display_aa async_load delta_time webgl_enabled event_data iap_data phy_rotation phy_position_x phy_position_y phy_angular_velocity phy_linear_velocity_x phy_linear_velocity_y phy_speed_x phy_speed_y phy_speed phy_angular_damping phy_linear_damping phy_bullet phy_fixed_rotation phy_active phy_mass phy_inertia phy_com_x phy_com_y phy_dynamic phy_kinematic phy_sleeping phy_collision_points phy_collision_x phy_collision_y phy_col_normal_x phy_col_normal_y phy_position_xprevious phy_position_yprevious"
 },
 contains:[e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,e.C_NUMBER_MODE]
 })})());
@@ -1596,7 +1603,7 @@
 className:"class",beginKeywords:"class interface trait enum",end:/\{/,
 illegal:":",contains:[{beginKeywords:"extends implements"
 },n.UNDERSCORE_TITLE_MODE]},{className:"meta",begin:"@[A-Za-z]+",relevance:0},{
-className:"attr",begin:a+"[ \t]*:"},{begin:/\?/,end:/:/,relevance:0,
+className:"attr",begin:a+"[ \t]*:",relevance:0},{begin:/\?/,end:/:/,relevance:0,
 contains:[t,r,s,i,"self"]},{className:"symbol",
 begin:"^[ \t]*"+(l=a+":",((...e)=>e.map((e=>(e=>e?"string"==typeof e?e:e.source:null)(e))).join(""))("(?=",l,")")),
 excludeBegin:!0,end:a+":",relevance:0}],illegal:/#|<\//};var l}})());
@@ -1618,19 +1625,20 @@
 hljs.registerLanguage("handlebars",(()=>{"use strict";function e(e){
 return e?"string"==typeof e?e:e.source:null}function n(...n){
 return n.map((n=>e(n))).join("")}return a=>{const t={
-"builtin-name":"action bindattr collection component concat debugger each each-in get hash if in input link-to loc log lookup mut outlet partial query-params render template textarea unbound unless view with yield"
+"builtin-name":["action","bindattr","collection","component","concat","debugger","each","each-in","get","hash","if","in","input","link-to","loc","log","lookup","mut","outlet","partial","query-params","render","template","textarea","unbound","unless","view","with","yield"]
 },s=/\[\]|\[[^\]]+\]/,i=/[^\s!"#%&'()*+,.\/;<=>@\[\\\]^`{|}~]+/,r=((...n)=>"("+n.map((n=>e(n))).join("|")+")")(/""|"[^"]+"/,/''|'[^']+'/,s,i),l=n(n("(",/\.|\.\/|\//,")?"),r,(h=n(/(\.|\/)/,r),
 n("(",h,")*"))),c=n("(",s,"|",i,")(?==)"),o={begin:l,lexemes:/[\w.\/]+/
-},m=a.inherit(o,{keywords:{literal:"true false undefined null"}}),d={begin:/\(/,
-end:/\)/},g={className:"attr",begin:c,relevance:0,starts:{begin:/=/,end:/=/,
-starts:{contains:[a.NUMBER_MODE,a.QUOTE_STRING_MODE,a.APOS_STRING_MODE,m,d]}}
-},b={contains:[a.NUMBER_MODE,a.QUOTE_STRING_MODE,a.APOS_STRING_MODE,{
-begin:/as\s+\|/,keywords:{keyword:"as"},end:/\|/,contains:[{begin:/\w+/}]
-},g,m,d],returnEnd:!0},u=a.inherit(o,{className:"name",keywords:t,
-starts:a.inherit(b,{end:/\)/})});var h;d.contains=[u];const N=a.inherit(o,{
-keywords:t,className:"name",starts:a.inherit(b,{end:/\}\}/})}),p=a.inherit(o,{
-keywords:t,className:"name"}),E=a.inherit(o,{className:"name",keywords:t,
-starts:a.inherit(b,{end:/\}\}/})});return{name:"Handlebars",
+},m=a.inherit(o,{keywords:{literal:["true","false","undefined","null"]}}),d={
+begin:/\(/,end:/\)/},g={className:"attr",begin:c,relevance:0,starts:{begin:/=/,
+end:/=/,starts:{
+contains:[a.NUMBER_MODE,a.QUOTE_STRING_MODE,a.APOS_STRING_MODE,m,d]}}},b={
+contains:[a.NUMBER_MODE,a.QUOTE_STRING_MODE,a.APOS_STRING_MODE,{begin:/as\s+\|/,
+keywords:{keyword:"as"},end:/\|/,contains:[{begin:/\w+/}]},g,m,d],returnEnd:!0
+},u=a.inherit(o,{className:"name",keywords:t,starts:a.inherit(b,{end:/\)/})})
+;var h;d.contains=[u];const N=a.inherit(o,{keywords:t,className:"name",
+starts:a.inherit(b,{end:/\}\}/})}),p=a.inherit(o,{keywords:t,className:"name"
+}),E=a.inherit(o,{className:"name",keywords:t,starts:a.inherit(b,{end:/\}\}/})})
+;return{name:"Handlebars",
 aliases:["hbs","html.hbs","html.handlebars","htmlbars"],case_insensitive:!0,
 subLanguage:"xml",contains:[{begin:/\\\{\{/,skip:!0},{begin:/\\\\(?=\{\{)/,
 skip:!0},a.COMMENT(/\{\{!--/,/--\}\}/),a.COMMENT(/\{\{!/,/\}\}/),{
@@ -1707,12 +1715,12 @@
 hljs.registerLanguage("htmlbars",(()=>{"use strict";function e(e){
 return e?"string"==typeof e?e:e.source:null}function n(...n){
 return n.map((n=>e(n))).join("")}return a=>{const t=function(a){const t={
-"builtin-name":"action bindattr collection component concat debugger each each-in get hash if in input link-to loc log lookup mut outlet partial query-params render template textarea unbound unless view with yield"
+"builtin-name":["action","bindattr","collection","component","concat","debugger","each","each-in","get","hash","if","in","input","link-to","loc","log","lookup","mut","outlet","partial","query-params","render","template","textarea","unbound","unless","view","with","yield"]
 },s=/\[\]|\[[^\]]+\]/,i=/[^\s!"#%&'()*+,.\/;<=>@\[\\\]^`{|}~]+/,r=((...n)=>"("+n.map((n=>e(n))).join("|")+")")(/""|"[^"]+"/,/''|'[^']+'/,s,i),l=n(n("(",/\.|\.\/|\//,")?"),r,(c=n(/(\.|\/)/,r),
 n("(",c,")*")));var c;const o=n("(",s,"|",i,")(?==)"),m={begin:l,
-lexemes:/[\w.\/]+/},d=a.inherit(m,{keywords:{literal:"true false undefined null"
-}}),g={begin:/\(/,end:/\)/},b={className:"attr",begin:o,relevance:0,starts:{
-begin:/=/,end:/=/,starts:{
+lexemes:/[\w.\/]+/},d=a.inherit(m,{keywords:{
+literal:["true","false","undefined","null"]}}),g={begin:/\(/,end:/\)/},b={
+className:"attr",begin:o,relevance:0,starts:{begin:/=/,end:/=/,starts:{
 contains:[a.NUMBER_MODE,a.QUOTE_STRING_MODE,a.APOS_STRING_MODE,d,g]}}},u={
 contains:[a.NUMBER_MODE,a.QUOTE_STRING_MODE,a.APOS_STRING_MODE,{begin:/as\s+\|/,
 keywords:{keyword:"as"},end:/\|/,contains:[{begin:/\w+/}]},b,d,g],returnEnd:!0
@@ -1844,8 +1852,8 @@
 begin:/import java\.[a-z]+\./,keywords:"import",relevance:2
 },e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,{
 className:"class",beginKeywords:"class interface enum",end:/[{;=]/,
-excludeEnd:!0,keywords:"class interface enum",illegal:/[:"\[\]]/,contains:[{
-beginKeywords:"extends implements"},e.UNDERSCORE_TITLE_MODE]},{
+excludeEnd:!0,relevance:1,keywords:"class interface enum",illegal:/[:"\[\]]/,
+contains:[{beginKeywords:"extends implements"},e.UNDERSCORE_TITLE_MODE]},{
 beginKeywords:"new throw return else",relevance:0},{className:"class",
 begin:"record\\s+"+e.UNDERSCORE_IDENT_RE+"\\s*\\(",returnBegin:!0,excludeEnd:!0,
 end:/[{;=]/,keywords:n,contains:[{beginKeywords:"record"},{
@@ -1862,15 +1870,14 @@
 },e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]},r,s]}}})());
 hljs.registerLanguage("javascript",(()=>{"use strict"
 ;const e="[A-Za-z$_][0-9A-Za-z$_]*",n=["as","in","of","if","for","while","finally","var","new","function","do","return","void","else","break","catch","instanceof","with","throw","case","default","try","switch","continue","typeof","delete","let","yield","const","class","debugger","async","await","static","import","from","export","extends"],a=["true","false","null","undefined","NaN","Infinity"],s=[].concat(["setInterval","setTimeout","clearInterval","clearTimeout","require","exports","eval","isFinite","isNaN","parseFloat","parseInt","decodeURI","decodeURIComponent","encodeURI","encodeURIComponent","escape","unescape"],["arguments","this","super","console","window","document","localStorage","module","global"],["Intl","DataView","Number","Math","Date","String","RegExp","Object","Function","Boolean","Error","Symbol","Set","Map","WeakSet","WeakMap","Proxy","Reflect","JSON","Promise","Float64Array","Int16Array","Int32Array","Int8Array","Uint16Array","Uint32Array","Float32Array","Array","Uint8Array","Uint8ClampedArray","ArrayBuffer"],["EvalError","InternalError","RangeError","ReferenceError","SyntaxError","TypeError","URIError"])
-;function r(e){return i("(?=",e,")")}function i(...e){return e.map((e=>{
-return(n=e)?"string"==typeof n?n:n.source:null;var n})).join("")}return t=>{
+;function r(e){return t("(?=",e,")")}function t(...e){return e.map((e=>{
+return(n=e)?"string"==typeof n?n:n.source:null;var n})).join("")}return i=>{
 const c=e,o={begin:/<[A-Za-z0-9\\._:-]+/,end:/\/[A-Za-z0-9\\._:-]+>|\/>/,
 isTrulyOpeningTag:(e,n)=>{const a=e[0].length+e.index,s=e.input[a]
 ;"<"!==s?">"===s&&(((e,{after:n})=>{const a="</"+e[0].slice(1)
 ;return-1!==e.input.indexOf(a,n)})(e,{after:a
-})||n.ignoreMatch()):n.ignoreMatch()}},l={$pattern:e,keyword:n.join(" "),
-literal:a.join(" "),built_in:s.join(" ")
-},b="\\.([0-9](_?[0-9])*)",g="0|[1-9](_?[0-9])*|0[0-7]*[89][0-9]*",d={
+})||n.ignoreMatch()):n.ignoreMatch()}},l={$pattern:e,keyword:n,literal:a,
+built_in:s},b="\\.([0-9](_?[0-9])*)",g="0|[1-9](_?[0-9])*|0[0-7]*[89][0-9]*",d={
 className:"number",variants:[{
 begin:`(\\b(${g})((${b})|\\.)?|(${b}))[eE][+-]?([0-9](_?[0-9])*)\\b`},{
 begin:`\\b(${g})\\b((${b})\\b|\\.)?|(${b})\\b`},{
@@ -1879,48 +1886,48 @@
 begin:"\\b0[bB][0-1](_?[0-1])*n?\\b"},{begin:"\\b0[oO][0-7](_?[0-7])*n?\\b"},{
 begin:"\\b0[0-7]+n?\\b"}],relevance:0},E={className:"subst",begin:"\\$\\{",
 end:"\\}",keywords:l,contains:[]},u={begin:"html`",end:"",starts:{end:"`",
-returnEnd:!1,contains:[t.BACKSLASH_ESCAPE,E],subLanguage:"xml"}},_={
+returnEnd:!1,contains:[i.BACKSLASH_ESCAPE,E],subLanguage:"xml"}},_={
 begin:"css`",end:"",starts:{end:"`",returnEnd:!1,
-contains:[t.BACKSLASH_ESCAPE,E],subLanguage:"css"}},m={className:"string",
-begin:"`",end:"`",contains:[t.BACKSLASH_ESCAPE,E]},N={className:"comment",
-variants:[t.COMMENT(/\/\*\*(?!\/)/,"\\*/",{relevance:0,contains:[{
+contains:[i.BACKSLASH_ESCAPE,E],subLanguage:"css"}},m={className:"string",
+begin:"`",end:"`",contains:[i.BACKSLASH_ESCAPE,E]},N={className:"comment",
+variants:[i.COMMENT(/\/\*\*(?!\/)/,"\\*/",{relevance:0,contains:[{
 className:"doctag",begin:"@[A-Za-z]+",contains:[{className:"type",begin:"\\{",
 end:"\\}",relevance:0},{className:"variable",begin:c+"(?=\\s*(-)|$)",
 endsParent:!0,relevance:0},{begin:/(?=[^\n])\s/,relevance:0}]}]
-}),t.C_BLOCK_COMMENT_MODE,t.C_LINE_COMMENT_MODE]
-},y=[t.APOS_STRING_MODE,t.QUOTE_STRING_MODE,u,_,m,d,t.REGEXP_MODE]
+}),i.C_BLOCK_COMMENT_MODE,i.C_LINE_COMMENT_MODE]
+},y=[i.APOS_STRING_MODE,i.QUOTE_STRING_MODE,u,_,m,d,i.REGEXP_MODE]
 ;E.contains=y.concat({begin:/\{/,end:/\}/,keywords:l,contains:["self"].concat(y)
 });const f=[].concat(N,E.contains),A=f.concat([{begin:/\(/,end:/\)/,keywords:l,
 contains:["self"].concat(f)}]),p={className:"params",begin:/\(/,end:/\)/,
 excludeBegin:!0,excludeEnd:!0,keywords:l,contains:A};return{name:"Javascript",
 aliases:["js","jsx","mjs","cjs"],keywords:l,exports:{PARAMS_CONTAINS:A},
-illegal:/#(?![$_A-z])/,contains:[t.SHEBANG({label:"shebang",binary:"node",
+illegal:/#(?![$_A-z])/,contains:[i.SHEBANG({label:"shebang",binary:"node",
 relevance:5}),{label:"use_strict",className:"meta",relevance:10,
 begin:/^\s*['"]use (strict|asm)['"]/
-},t.APOS_STRING_MODE,t.QUOTE_STRING_MODE,u,_,m,N,d,{
-begin:i(/[{,\n]\s*/,r(i(/(((\/\/.*$)|(\/\*(\*[^/]|[^*])*\*\/))\s*)*/,c+"\\s*:"))),
+},i.APOS_STRING_MODE,i.QUOTE_STRING_MODE,u,_,m,N,d,{
+begin:t(/[{,\n]\s*/,r(t(/(((\/\/.*$)|(\/\*(\*[^/]|[^*])*\*\/))\s*)*/,c+"\\s*:"))),
 relevance:0,contains:[{className:"attr",begin:c+r("\\s*:"),relevance:0}]},{
-begin:"("+t.RE_STARTERS_RE+"|\\b(case|return|throw)\\b)\\s*",
-keywords:"return throw case",contains:[N,t.REGEXP_MODE,{className:"function",
-begin:"(\\([^()]*(\\([^()]*(\\([^()]*\\)[^()]*)*\\)[^()]*)*\\)|"+t.UNDERSCORE_IDENT_RE+")\\s*=>",
+begin:"("+i.RE_STARTERS_RE+"|\\b(case|return|throw)\\b)\\s*",
+keywords:"return throw case",contains:[N,i.REGEXP_MODE,{className:"function",
+begin:"(\\([^()]*(\\([^()]*(\\([^()]*\\)[^()]*)*\\)[^()]*)*\\)|"+i.UNDERSCORE_IDENT_RE+")\\s*=>",
 returnBegin:!0,end:"\\s*=>",contains:[{className:"params",variants:[{
-begin:t.UNDERSCORE_IDENT_RE,relevance:0},{className:null,begin:/\(\s*\)/,skip:!0
+begin:i.UNDERSCORE_IDENT_RE,relevance:0},{className:null,begin:/\(\s*\)/,skip:!0
 },{begin:/\(/,end:/\)/,excludeBegin:!0,excludeEnd:!0,keywords:l,contains:A}]}]
 },{begin:/,/,relevance:0},{className:"",begin:/\s/,end:/\s*/,skip:!0},{
 variants:[{begin:"<>",end:"</>"},{begin:o.begin,"on:begin":o.isTrulyOpeningTag,
 end:o.end}],subLanguage:"xml",contains:[{begin:o.begin,end:o.end,skip:!0,
 contains:["self"]}]}],relevance:0},{className:"function",
 beginKeywords:"function",end:/[{;]/,excludeEnd:!0,keywords:l,
-contains:["self",t.inherit(t.TITLE_MODE,{begin:c}),p],illegal:/%/},{
+contains:["self",i.inherit(i.TITLE_MODE,{begin:c}),p],illegal:/%/},{
 beginKeywords:"while if switch catch for"},{className:"function",
-begin:t.UNDERSCORE_IDENT_RE+"\\([^()]*(\\([^()]*(\\([^()]*\\)[^()]*)*\\)[^()]*)*\\)\\s*\\{",
-returnBegin:!0,contains:[p,t.inherit(t.TITLE_MODE,{begin:c})]},{variants:[{
+begin:i.UNDERSCORE_IDENT_RE+"\\([^()]*(\\([^()]*(\\([^()]*\\)[^()]*)*\\)[^()]*)*\\)\\s*\\{",
+returnBegin:!0,contains:[p,i.inherit(i.TITLE_MODE,{begin:c})]},{variants:[{
 begin:"\\."+c},{begin:"\\$"+c}],relevance:0},{className:"class",
 beginKeywords:"class",end:/[{;=]/,excludeEnd:!0,illegal:/[:"[\]]/,contains:[{
-beginKeywords:"extends"},t.UNDERSCORE_TITLE_MODE]},{begin:/\b(?=constructor)/,
-end:/[{;]/,excludeEnd:!0,contains:[t.inherit(t.TITLE_MODE,{begin:c}),"self",p]
+beginKeywords:"extends"},i.UNDERSCORE_TITLE_MODE]},{begin:/\b(?=constructor)/,
+end:/[{;]/,excludeEnd:!0,contains:[i.inherit(i.TITLE_MODE,{begin:c}),"self",p]
 },{begin:"(get|set)\\s+(?="+c+"\\()",end:/\{/,keywords:"get set",
-contains:[t.inherit(t.TITLE_MODE,{begin:c}),{begin:/\(\)/},p]},{begin:/\$[(.]/}]
+contains:[i.inherit(i.TITLE_MODE,{begin:c}),{begin:/\(\)/},p]},{begin:/\$[(.]/}]
 }}})());
 hljs.registerLanguage("jboss-cli",(()=>{"use strict";return e=>({
 name:"JBoss CLI",aliases:["wildfly-cli"],keywords:{$pattern:"[a-z-]+",
@@ -1942,9 +1949,9 @@
 contains:a,keywords:e,illegal:"\\S"}}})());
 hljs.registerLanguage("julia",(()=>{"use strict";return e=>{
 var r="[A-Za-z_\\u00A1-\\uFFFF][A-Za-z_0-9\\u00A1-\\uFFFF]*",t={$pattern:r,
-keyword:"baremodule begin break catch ccall const continue do else elseif end export false finally for function global if import in isa let local macro module quote return true try using where while",
-literal:"ARGS C_NULL DEPOT_PATH ENDIAN_BOM ENV Inf Inf16 Inf32 Inf64 InsertionSort LOAD_PATH MergeSort NaN NaN16 NaN32 NaN64 PROGRAM_FILE QuickSort RoundDown RoundFromZero RoundNearest RoundNearestTiesAway RoundNearestTiesUp RoundToZero RoundUp VERSION|0 devnull false im missing nothing pi stderr stdin stdout true undef \u03c0 \u212f",
-built_in:"AbstractArray AbstractChannel AbstractChar AbstractDict AbstractDisplay AbstractFloat AbstractIrrational AbstractMatrix AbstractRange AbstractSet AbstractString AbstractUnitRange AbstractVecOrMat AbstractVector Any ArgumentError Array AssertionError BigFloat BigInt BitArray BitMatrix BitSet BitVector Bool BoundsError CapturedException CartesianIndex CartesianIndices Cchar Cdouble Cfloat Channel Char Cint Cintmax_t Clong Clonglong Cmd Colon Complex ComplexF16 ComplexF32 ComplexF64 CompositeException Condition Cptrdiff_t Cshort Csize_t Cssize_t Cstring Cuchar Cuint Cuintmax_t Culong Culonglong Cushort Cvoid Cwchar_t Cwstring DataType DenseArray DenseMatrix DenseVecOrMat DenseVector Dict DimensionMismatch Dims DivideError DomainError EOFError Enum ErrorException Exception ExponentialBackOff Expr Float16 Float32 Float64 Function GlobalRef HTML IO IOBuffer IOContext IOStream IdDict IndexCartesian IndexLinear IndexStyle InexactError InitError Int Int128 Int16 Int32 Int64 Int8 Integer InterruptException InvalidStateException Irrational KeyError LinRange LineNumberNode LinearIndices LoadError MIME Matrix Method MethodError Missing MissingException Module NTuple NamedTuple Nothing Number OrdinalRange OutOfMemoryError OverflowError Pair PartialQuickSort PermutedDimsArray Pipe ProcessFailedException Ptr QuoteNode Rational RawFD ReadOnlyMemoryError Real ReentrantLock Ref Regex RegexMatch RoundingMode SegmentationFault Set Signed Some StackOverflowError StepRange StepRangeLen StridedArray StridedMatrix StridedVecOrMat StridedVector String StringIndexError SubArray SubString SubstitutionString Symbol SystemError Task TaskFailedException Text TextDisplay Timer Tuple Type TypeError TypeVar UInt UInt128 UInt16 UInt32 UInt64 UInt8 UndefInitializer UndefKeywordError UndefRefError UndefVarError Union UnionAll UnitRange Unsigned Val Vararg VecElement VecOrMat Vector VersionNumber WeakKeyDict WeakRef"
+keyword:["baremodule","begin","break","catch","ccall","const","continue","do","else","elseif","end","export","false","finally","for","function","global","if","import","in","isa","let","local","macro","module","quote","return","true","try","using","where","while"],
+literal:["ARGS","C_NULL","DEPOT_PATH","ENDIAN_BOM","ENV","Inf","Inf16","Inf32","Inf64","InsertionSort","LOAD_PATH","MergeSort","NaN","NaN16","NaN32","NaN64","PROGRAM_FILE","QuickSort","RoundDown","RoundFromZero","RoundNearest","RoundNearestTiesAway","RoundNearestTiesUp","RoundToZero","RoundUp","VERSION|0","devnull","false","im","missing","nothing","pi","stderr","stdin","stdout","true","undef","\u03c0","\u212f"],
+built_in:["AbstractArray","AbstractChannel","AbstractChar","AbstractDict","AbstractDisplay","AbstractFloat","AbstractIrrational","AbstractMatrix","AbstractRange","AbstractSet","AbstractString","AbstractUnitRange","AbstractVecOrMat","AbstractVector","Any","ArgumentError","Array","AssertionError","BigFloat","BigInt","BitArray","BitMatrix","BitSet","BitVector","Bool","BoundsError","CapturedException","CartesianIndex","CartesianIndices","Cchar","Cdouble","Cfloat","Channel","Char","Cint","Cintmax_t","Clong","Clonglong","Cmd","Colon","Complex","ComplexF16","ComplexF32","ComplexF64","CompositeException","Condition","Cptrdiff_t","Cshort","Csize_t","Cssize_t","Cstring","Cuchar","Cuint","Cuintmax_t","Culong","Culonglong","Cushort","Cvoid","Cwchar_t","Cwstring","DataType","DenseArray","DenseMatrix","DenseVecOrMat","DenseVector","Dict","DimensionMismatch","Dims","DivideError","DomainError","EOFError","Enum","ErrorException","Exception","ExponentialBackOff","Expr","Float16","Float32","Float64","Function","GlobalRef","HTML","IO","IOBuffer","IOContext","IOStream","IdDict","IndexCartesian","IndexLinear","IndexStyle","InexactError","InitError","Int","Int128","Int16","Int32","Int64","Int8","Integer","InterruptException","InvalidStateException","Irrational","KeyError","LinRange","LineNumberNode","LinearIndices","LoadError","MIME","Matrix","Method","MethodError","Missing","MissingException","Module","NTuple","NamedTuple","Nothing","Number","OrdinalRange","OutOfMemoryError","OverflowError","Pair","PartialQuickSort","PermutedDimsArray","Pipe","ProcessFailedException","Ptr","QuoteNode","Rational","RawFD","ReadOnlyMemoryError","Real","ReentrantLock","Ref","Regex","RegexMatch","RoundingMode","SegmentationFault","Set","Signed","Some","StackOverflowError","StepRange","StepRangeLen","StridedArray","StridedMatrix","StridedVecOrMat","StridedVector","String","StringIndexError","SubArray","SubString","SubstitutionString","Symbol","SystemError","Task","TaskFailedException","Text","TextDisplay","Timer","Tuple","Type","TypeError","TypeVar","UInt","UInt128","UInt16","UInt32","UInt64","UInt8","UndefInitializer","UndefKeywordError","UndefRefError","UndefVarError","Union","UnionAll","UnitRange","Unsigned","Val","Vararg","VecElement","VecOrMat","Vector","VersionNumber","WeakKeyDict","WeakRef"]
 },n={keywords:t,illegal:/<\//},a={className:"subst",begin:/\$\(/,end:/\)/,
 keywords:t},i={className:"variable",begin:"\\$"+r},o={className:"string",
 contains:[e.BACKSLASH_ESCAPE,a,i],variants:[{begin:/\w*"""/,end:/"""\w*/,
@@ -2075,33 +2082,42 @@
 className:"title",begin:"[A-Za-z_][A-Za-z_0-9]*"},{className:"params",
 begin:"\\(",end:"\\)",endsParent:!0,contains:[{className:"string",begin:'"',
 end:'"'},{className:"variable",begin:"[A-Za-z_][A-Za-z_0-9]*"}]}]}]})})());
-hljs.registerLanguage("less",(()=>{"use strict";return e=>{
-var n="([\\w-]+|@\\{[\\w-]+\\})",a=[],s=[],t=e=>({className:"string",
-begin:"~?"+e+".*?"+e}),r=(e,n,a)=>({className:e,begin:n,relevance:a}),i={
-begin:"\\(",end:"\\)",contains:s,relevance:0}
-;s.push(e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,t("'"),t('"'),e.CSS_NUMBER_MODE,{
+hljs.registerLanguage("less",(()=>{"use strict"
+;const e=["a","abbr","address","article","aside","audio","b","blockquote","body","button","canvas","caption","cite","code","dd","del","details","dfn","div","dl","dt","em","fieldset","figcaption","figure","footer","form","h1","h2","h3","h4","h5","h6","header","hgroup","html","i","iframe","img","input","ins","kbd","label","legend","li","main","mark","menu","nav","object","ol","p","q","quote","samp","section","span","strong","summary","sup","table","tbody","td","textarea","tfoot","th","thead","time","tr","ul","var","video"],t=["any-hover","any-pointer","aspect-ratio","color","color-gamut","color-index","device-aspect-ratio","device-height","device-width","display-mode","forced-colors","grid","height","hover","inverted-colors","monochrome","orientation","overflow-block","overflow-inline","pointer","prefers-color-scheme","prefers-contrast","prefers-reduced-motion","prefers-reduced-transparency","resolution","scan","scripting","update","width","min-width","max-width","min-height","max-height"],i=["active","any-link","blank","checked","current","default","defined","dir","disabled","drop","empty","enabled","first","first-child","first-of-type","fullscreen","future","focus","focus-visible","focus-within","has","host","host-context","hover","indeterminate","in-range","invalid","is","lang","last-child","last-of-type","left","link","local-link","not","nth-child","nth-col","nth-last-child","nth-last-col","nth-last-of-type","nth-of-type","only-child","only-of-type","optional","out-of-range","past","placeholder-shown","read-only","read-write","required","right","root","scope","target","target-within","user-invalid","valid","visited","where"],o=["after","backdrop","before","cue","cue-region","first-letter","first-line","grammar-error","marker","part","placeholder","selection","slotted","spelling-error"],n=["align-content","align-items","align-self","animation","animation-delay","animation-direction","animation-duration","animation-fill-mode","animation-iteration-count","animation-name","animation-play-state","animation-timing-function","auto","backface-visibility","background","background-attachment","background-clip","background-color","background-image","background-origin","background-position","background-repeat","background-size","border","border-bottom","border-bottom-color","border-bottom-left-radius","border-bottom-right-radius","border-bottom-style","border-bottom-width","border-collapse","border-color","border-image","border-image-outset","border-image-repeat","border-image-slice","border-image-source","border-image-width","border-left","border-left-color","border-left-style","border-left-width","border-radius","border-right","border-right-color","border-right-style","border-right-width","border-spacing","border-style","border-top","border-top-color","border-top-left-radius","border-top-right-radius","border-top-style","border-top-width","border-width","bottom","box-decoration-break","box-shadow","box-sizing","break-after","break-before","break-inside","caption-side","clear","clip","clip-path","color","column-count","column-fill","column-gap","column-rule","column-rule-color","column-rule-style","column-rule-width","column-span","column-width","columns","content","counter-increment","counter-reset","cursor","direction","display","empty-cells","filter","flex","flex-basis","flex-direction","flex-flow","flex-grow","flex-shrink","flex-wrap","float","font","font-display","font-family","font-feature-settings","font-kerning","font-language-override","font-size","font-size-adjust","font-stretch","font-style","font-variant","font-variant-ligatures","font-variation-settings","font-weight","height","hyphens","icon","image-orientation","image-rendering","image-resolution","ime-mode","inherit","initial","justify-content","left","letter-spacing","line-height","list-style","list-style-image","list-style-position","list-style-type","margin","margin-bottom","margin-left","margin-right","margin-top","marks","mask","max-height","max-width","min-height","min-width","nav-down","nav-index","nav-left","nav-right","nav-up","none","normal","object-fit","object-position","opacity","order","orphans","outline","outline-color","outline-offset","outline-style","outline-width","overflow","overflow-wrap","overflow-x","overflow-y","padding","padding-bottom","padding-left","padding-right","padding-top","page-break-after","page-break-before","page-break-inside","perspective","perspective-origin","pointer-events","position","quotes","resize","right","src","tab-size","table-layout","text-align","text-align-last","text-decoration","text-decoration-color","text-decoration-line","text-decoration-style","text-indent","text-overflow","text-rendering","text-shadow","text-transform","text-underline-position","top","transform","transform-origin","transform-style","transition","transition-delay","transition-duration","transition-property","transition-timing-function","unicode-bidi","vertical-align","visibility","white-space","widows","width","word-break","word-spacing","word-wrap","z-index"].reverse(),r=i.concat(o)
+;return a=>{const s=(e=>({IMPORTANT:{className:"meta",begin:"!important"},
+HEXCOLOR:{className:"number",begin:"#([a-fA-F0-9]{6}|[a-fA-F0-9]{3})"},
+ATTRIBUTE_SELECTOR_MODE:{className:"selector-attr",begin:/\[/,end:/\]/,
+illegal:"$",contains:[e.APOS_STRING_MODE,e.QUOTE_STRING_MODE]}
+}))(a),l=r,d="([\\w-]+|@\\{[\\w-]+\\})",c=[],g=[],b=e=>({className:"string",
+begin:"~?"+e+".*?"+e}),m=(e,t,i)=>({className:e,begin:t,relevance:i}),u={
+$pattern:/[a-z-]+/,keyword:"and or not only",attribute:t.join(" ")},p={
+begin:"\\(",end:"\\)",contains:g,keywords:u,relevance:0}
+;g.push(a.C_LINE_COMMENT_MODE,a.C_BLOCK_COMMENT_MODE,b("'"),b('"'),a.CSS_NUMBER_MODE,{
 begin:"(url|data-uri)\\(",starts:{className:"string",end:"[\\)\\n]",
 excludeEnd:!0}
-},r("number","#[0-9A-Fa-f]+\\b"),i,r("variable","@@?[\\w-]+",10),r("variable","@\\{[\\w-]+\\}"),r("built_in","~?`[^`]*?`"),{
+},s.HEXCOLOR,p,m("variable","@@?[\\w-]+",10),m("variable","@\\{[\\w-]+\\}"),m("built_in","~?`[^`]*?`"),{
 className:"attribute",begin:"[\\w-]+\\s*:",end:":",returnBegin:!0,excludeEnd:!0
-},{className:"meta",begin:"!important"});var c=s.concat({begin:/\{/,end:/\}/,
-contains:a}),l={beginKeywords:"when",endsWithParent:!0,contains:[{
-beginKeywords:"and not"}].concat(s)},g={begin:n+"\\s*:",returnBegin:!0,
-end:"[;}]",relevance:0,contains:[{className:"attribute",begin:n,end:":",
-excludeEnd:!0,starts:{endsWithParent:!0,illegal:"[<=$]",relevance:0,contains:s}
-}]},d={className:"keyword",
+},s.IMPORTANT);const f=g.concat({begin:/\{/,end:/\}/,contains:c}),h={
+beginKeywords:"when",endsWithParent:!0,contains:[{beginKeywords:"and not"
+}].concat(g)},w={begin:d+"\\s*:",returnBegin:!0,end:/[;}]/,relevance:0,
+contains:[{begin:/-(webkit|moz|ms|o)-/},{className:"attribute",
+begin:"\\b("+n.join("|")+")\\b",end:/(?=:)/,starts:{endsWithParent:!0,
+illegal:"[<=$]",relevance:0,contains:g}}]},v={className:"keyword",
 begin:"@(import|media|charset|font-face|(-[a-z]+-)?keyframes|supports|document|namespace|page|viewport|host)\\b",
-starts:{end:"[;{}]",returnEnd:!0,contains:s,relevance:0}},o={
+starts:{end:"[;{}]",keywords:u,returnEnd:!0,contains:g,relevance:0}},y={
 className:"variable",variants:[{begin:"@[\\w-]+\\s*:",relevance:15},{
-begin:"@[\\w-]+"}],starts:{end:"[;}]",returnEnd:!0,contains:c}},b={variants:[{
-begin:"[\\.#:&\\[>]",end:"[;{}]"},{begin:n,end:/\{/}],returnBegin:!0,
+begin:"@[\\w-]+"}],starts:{end:"[;}]",returnEnd:!0,contains:f}},k={variants:[{
+begin:"[\\.#:&\\[>]",end:"[;{}]"},{begin:d,end:/\{/}],returnBegin:!0,
 returnEnd:!0,illegal:"[<='$\"]",relevance:0,
-contains:[e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,l,r("keyword","all\\b"),r("variable","@\\{[\\w-]+\\}"),r("selector-tag",n+"%?",0),r("selector-id","#"+n),r("selector-class","\\."+n,0),r("selector-tag","&",0),{
-className:"selector-attr",begin:"\\[",end:"\\]"},{className:"selector-pseudo",
-begin:/:(:)?[a-zA-Z0-9_\-+()"'.]+/},{begin:"\\(",end:"\\)",contains:c},{
-begin:"!important"}]}
-;return a.push(e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,d,o,g,b),{
-name:"Less",case_insensitive:!0,illegal:"[=>'/<($\"]",contains:a}}})());
+contains:[a.C_LINE_COMMENT_MODE,a.C_BLOCK_COMMENT_MODE,h,m("keyword","all\\b"),m("variable","@\\{[\\w-]+\\}"),{
+begin:"\\b("+e.join("|")+")\\b",className:"selector-tag"
+},m("selector-tag",d+"%?",0),m("selector-id","#"+d),m("selector-class","\\."+d,0),m("selector-tag","&",0),s.ATTRIBUTE_SELECTOR_MODE,{
+className:"selector-pseudo",begin:":("+i.join("|")+")"},{
+className:"selector-pseudo",begin:"::("+o.join("|")+")"},{begin:"\\(",end:"\\)",
+contains:f},{begin:"!important"}]},E={begin:`[\\w-]+:(:)?(${l.join("|")})`,
+returnBegin:!0,contains:[k]}
+;return c.push(a.C_LINE_COMMENT_MODE,a.C_BLOCK_COMMENT_MODE,v,y,E,w,k),{
+name:"Less",case_insensitive:!0,illegal:"[=>'/<($\"]",contains:c}}})());
 hljs.registerLanguage("lisp",(()=>{"use strict";return e=>{
 var n="[a-zA-Z_\\-+\\*\\/<=>&#][a-zA-Z0-9_\\-+*\\/<=>&#!]*",a="\\|[^]*?\\|",i="(-|\\+)?\\d+(\\.\\d+|\\/\\d+)?((d|e|f|l|s|D|E|F|L|S)(\\+|-)?\\d+)?",s={
 className:"literal",begin:"\\b(t{1}|nil)\\b"},l={className:"number",variants:[{
@@ -2141,11 +2157,11 @@
 hljs.registerLanguage("livescript",(()=>{"use strict"
 ;const e=["as","in","of","if","for","while","finally","var","new","function","do","return","void","else","break","catch","instanceof","with","throw","case","default","try","switch","continue","typeof","delete","let","yield","const","class","debugger","async","await","static","import","from","export","extends"],n=["true","false","null","undefined","NaN","Infinity"],a=[].concat(["setInterval","setTimeout","clearInterval","clearTimeout","require","exports","eval","isFinite","isNaN","parseFloat","parseInt","decodeURI","decodeURIComponent","encodeURI","encodeURIComponent","escape","unescape"],["arguments","this","super","console","window","document","localStorage","module","global"],["Intl","DataView","Number","Math","Date","String","RegExp","Object","Function","Boolean","Error","Symbol","Set","Map","WeakSet","WeakMap","Proxy","Reflect","JSON","Promise","Float64Array","Int16Array","Int32Array","Int8Array","Uint16Array","Uint32Array","Float32Array","Array","Uint8Array","Uint8ClampedArray","ArrayBuffer"],["EvalError","InternalError","RangeError","ReferenceError","SyntaxError","TypeError","URIError"])
 ;return t=>{const r={
-keyword:e.concat(["then","unless","until","loop","of","by","when","and","or","is","isnt","not","it","that","otherwise","from","to","til","fallthrough","case","enum","native","list","map","__hasProp","__extends","__slice","__bind","__indexOf"]).join(" "),
-literal:n.concat(["yes","no","on","off","it","that","void"]).join(" "),
-built_in:a.concat(["npm","print"]).join(" ")
-},i="[A-Za-z$_](?:-[0-9A-Za-z$_]|[0-9A-Za-z$_])*",s=t.inherit(t.TITLE_MODE,{
-begin:i}),o={className:"subst",begin:/#\{/,end:/\}/,keywords:r},c={
+keyword:e.concat(["then","unless","until","loop","of","by","when","and","or","is","isnt","not","it","that","otherwise","from","to","til","fallthrough","case","enum","native","list","map","__hasProp","__extends","__slice","__bind","__indexOf"]),
+literal:n.concat(["yes","no","on","off","it","that","void"]),
+built_in:a.concat(["npm","print"])
+},s="[A-Za-z$_](?:-[0-9A-Za-z$_]|[0-9A-Za-z$_])*",i=t.inherit(t.TITLE_MODE,{
+begin:s}),o={className:"subst",begin:/#\{/,end:/\}/,keywords:r},c={
 className:"subst",begin:/#[A-Za-z$_]/,end:/(?:-[0-9A-Za-z$_]|[0-9A-Za-z$_])*/,
 keywords:r},l=[t.BINARY_NUMBER_MODE,{className:"number",
 begin:"(\\b0[xX][a-fA-F0-9_]+)|(\\b\\d(\\d|_\\d)*(\\.(\\d(\\d|_\\d)*)?)?(_*[eE]([-+]\\d(_\\d|\\d)*)?)?[_a-z]*)",
@@ -2156,21 +2172,21 @@
 contains:[t.BACKSLASH_ESCAPE,o,c]},{begin:/\\/,end:/(\s|$)/,excludeEnd:!0}]},{
 className:"regexp",variants:[{begin:"//",end:"//[gim]*",
 contains:[o,t.HASH_COMMENT_MODE]},{
-begin:/\/(?![ *])(\\.|[^\\\n])*?\/[gim]*(?=\W)/}]},{begin:"@"+i},{begin:"``",
+begin:/\/(?![ *])(\\.|[^\\\n])*?\/[gim]*(?=\W)/}]},{begin:"@"+s},{begin:"``",
 end:"``",excludeBegin:!0,excludeEnd:!0,subLanguage:"javascript"}];o.contains=l
 ;const d={className:"params",begin:"\\(",returnBegin:!0,contains:[{begin:/\(/,
 end:/\)/,keywords:r,contains:["self"].concat(l)}]};return{name:"LiveScript",
 aliases:["ls"],keywords:r,illegal:/\/\*/,
 contains:l.concat([t.COMMENT("\\/\\*","\\*\\/"),t.HASH_COMMENT_MODE,{
-begin:"(#=>|=>|\\|>>|-?->|!->)"},{className:"function",contains:[s,d],
+begin:"(#=>|=>|\\|>>|-?->|!->)"},{className:"function",contains:[i,d],
 returnBegin:!0,variants:[{
-begin:"("+i+"\\s*(?:=|:=)\\s*)?(\\(.*\\)\\s*)?\\B->\\*?",end:"->\\*?"},{
-begin:"("+i+"\\s*(?:=|:=)\\s*)?!?(\\(.*\\)\\s*)?\\B[-~]{1,2}>\\*?",
+begin:"("+s+"\\s*(?:=|:=)\\s*)?(\\(.*\\)\\s*)?\\B->\\*?",end:"->\\*?"},{
+begin:"("+s+"\\s*(?:=|:=)\\s*)?!?(\\(.*\\)\\s*)?\\B[-~]{1,2}>\\*?",
 end:"[-~]{1,2}>\\*?"},{
-begin:"("+i+"\\s*(?:=|:=)\\s*)?(\\(.*\\)\\s*)?\\B!?[-~]{1,2}>\\*?",
+begin:"("+s+"\\s*(?:=|:=)\\s*)?(\\(.*\\)\\s*)?\\B!?[-~]{1,2}>\\*?",
 end:"!?[-~]{1,2}>\\*?"}]},{className:"class",beginKeywords:"class",end:"$",
 illegal:/[:="\[\]]/,contains:[{beginKeywords:"extends",endsWithParent:!0,
-illegal:/[:="\[\]]/,contains:[s]},s]},{begin:i+":",end:":",returnBegin:!0,
+illegal:/[:="\[\]]/,contains:[i]},i]},{begin:s+":",end:":",returnBegin:!0,
 returnEnd:!0,relevance:0}])}}})());
 hljs.registerLanguage("llvm",(()=>{"use strict";function e(...e){
 return e.map((e=>{return(n=e)?"string"==typeof n?n:n.source:null;var n
@@ -2314,36 +2330,44 @@
 hljs.registerLanguage("mizar",(()=>{"use strict";return e=>({name:"Mizar",
 keywords:"environ vocabularies notations constructors definitions registrations theorems schemes requirements begin end definition registration cluster existence pred func defpred deffunc theorem proof let take assume then thus hence ex for st holds consider reconsider such that and in provided of as from be being by means equals implies iff redefine define now not or attr is mode suppose per cases set thesis contradiction scheme reserve struct correctness compatibility coherence symmetry assymetry reflexivity irreflexivity connectedness uniqueness commutativity idempotence involutiveness projectivity",
 contains:[e.COMMENT("::","$")]})})());
-hljs.registerLanguage("perl",(()=>{"use strict";function e(...e){
-return e.map((e=>{return(n=e)?"string"==typeof n?n:n.source:null;var n
-})).join("")}return n=>{const t=/[dualxmsipn]{0,12}/,s={$pattern:/[\w.]+/,
-keyword:"getpwent getservent quotemeta msgrcv scalar kill dbmclose undef lc ma syswrite tr send umask sysopen shmwrite vec qx utime local oct semctl localtime readpipe do return format read sprintf dbmopen pop getpgrp not getpwnam rewinddir qq fileno qw endprotoent wait sethostent bless s|0 opendir continue each sleep endgrent shutdown dump chomp connect getsockname die socketpair close flock exists index shmget sub for endpwent redo lstat msgctl setpgrp abs exit select print ref gethostbyaddr unshift fcntl syscall goto getnetbyaddr join gmtime symlink semget splice x|0 getpeername recv log setsockopt cos last reverse gethostbyname getgrnam study formline endhostent times chop length gethostent getnetent pack getprotoent getservbyname rand mkdir pos chmod y|0 substr endnetent printf next open msgsnd readdir use unlink getsockopt getpriority rindex wantarray hex system getservbyport endservent int chr untie rmdir prototype tell listen fork shmread ucfirst setprotoent else sysseek link getgrgid shmctl waitpid unpack getnetbyname reset chdir grep split require caller lcfirst until warn while values shift telldir getpwuid my getprotobynumber delete and sort uc defined srand accept package seekdir getprotobyname semop our rename seek if q|0 chroot sysread setpwent no crypt getc chown sqrt write setnetent setpriority foreach tie sin msgget map stat getlogin unless elsif truncate exec keys glob tied closedir ioctl socket readlink eval xor readline binmode setservent eof ord bind alarm pipe atan2 getgrent exp time push setgrent gt lt or ne m|0 break given say state when"
-},r={className:"subst",begin:"[$@]\\{",end:"\\}",keywords:s},i={begin:/->\{/,
-end:/\}/},a={variants:[{begin:/\$\d/},{
-begin:e(/[$%@](\^\w\b|#\w+(::\w+)*|\{\w+\}|\w+(::\w*)*)/,"(?![A-Za-z])(?![@$%])")
+hljs.registerLanguage("perl",(()=>{"use strict";function e(e){
+return e?"string"==typeof e?e:e.source:null}function n(...n){
+return n.map((n=>e(n))).join("")}function t(...n){
+return"("+n.map((n=>e(n))).join("|")+")"}return e=>{
+const r=/[dualxmsipngr]{0,12}/,s={$pattern:/[\w.]+/,
+keyword:"abs accept alarm and atan2 bind binmode bless break caller chdir chmod chomp chop chown chr chroot close closedir connect continue cos crypt dbmclose dbmopen defined delete die do dump each else elsif endgrent endhostent endnetent endprotoent endpwent endservent eof eval exec exists exit exp fcntl fileno flock for foreach fork format formline getc getgrent getgrgid getgrnam gethostbyaddr gethostbyname gethostent getlogin getnetbyaddr getnetbyname getnetent getpeername getpgrp getpriority getprotobyname getprotobynumber getprotoent getpwent getpwnam getpwuid getservbyname getservbyport getservent getsockname getsockopt given glob gmtime goto grep gt hex if index int ioctl join keys kill last lc lcfirst length link listen local localtime log lstat lt ma map mkdir msgctl msgget msgrcv msgsnd my ne next no not oct open opendir or ord our pack package pipe pop pos print printf prototype push q|0 qq quotemeta qw qx rand read readdir readline readlink readpipe recv redo ref rename require reset return reverse rewinddir rindex rmdir say scalar seek seekdir select semctl semget semop send setgrent sethostent setnetent setpgrp setpriority setprotoent setpwent setservent setsockopt shift shmctl shmget shmread shmwrite shutdown sin sleep socket socketpair sort splice split sprintf sqrt srand stat state study sub substr symlink syscall sysopen sysread sysseek system syswrite tell telldir tie tied time times tr truncate uc ucfirst umask undef unless unlink unpack unshift untie until use utime values vec wait waitpid wantarray warn when while write x|0 xor y|0"
+},i={className:"subst",begin:"[$@]\\{",end:"\\}",keywords:s},a={begin:/->\{/,
+end:/\}/},o={variants:[{begin:/\$\d/},{
+begin:n(/[$%@](\^\w\b|#\w+(::\w+)*|\{\w+\}|\w+(::\w*)*)/,"(?![A-Za-z])(?![@$%])")
 },{begin:/[$%@][^\s\w{]/,relevance:0}]
-},o=[n.BACKSLASH_ESCAPE,r,a],c=[a,n.HASH_COMMENT_MODE,n.COMMENT(/^=\w/,/=cut/,{
-endsWithParent:!0}),i,{className:"string",contains:o,variants:[{
+},c=[e.BACKSLASH_ESCAPE,i,o],g=[/!/,/\//,/\|/,/\?/,/'/,/"/,/#/],l=(e,t,s="\\1")=>{
+const i="\\1"===s?s:n(s,t)
+;return n(n("(?:",e,")"),t,/(?:\\.|[^\\\/])*?/,i,/(?:\\.|[^\\\/])*?/,s,r)
+},d=(e,t,s)=>n(n("(?:",e,")"),t,/(?:\\.|[^\\\/])*?/,s,r),p=[o,e.HASH_COMMENT_MODE,e.COMMENT(/^=\w/,/=cut/,{
+endsWithParent:!0}),a,{className:"string",contains:c,variants:[{
 begin:"q[qwxr]?\\s*\\(",end:"\\)",relevance:5},{begin:"q[qwxr]?\\s*\\[",
 end:"\\]",relevance:5},{begin:"q[qwxr]?\\s*\\{",end:"\\}",relevance:5},{
 begin:"q[qwxr]?\\s*\\|",end:"\\|",relevance:5},{begin:"q[qwxr]?\\s*<",end:">",
 relevance:5},{begin:"qw\\s+q",end:"q",relevance:5},{begin:"'",end:"'",
-contains:[n.BACKSLASH_ESCAPE]},{begin:'"',end:'"'},{begin:"`",end:"`",
-contains:[n.BACKSLASH_ESCAPE]},{begin:/\{\w+\}/,contains:[],relevance:0},{
-begin:"-?\\w+\\s*=>",contains:[],relevance:0}]},{className:"number",
+contains:[e.BACKSLASH_ESCAPE]},{begin:'"',end:'"'},{begin:"`",end:"`",
+contains:[e.BACKSLASH_ESCAPE]},{begin:/\{\w+\}/,relevance:0},{
+begin:"-?\\w+\\s*=>",relevance:0}]},{className:"number",
 begin:"(\\b0[0-7_]+)|(\\b0x[0-9a-fA-F_]+)|(\\b[1-9][0-9_]*(\\.[0-9_]+)?)|[0_]\\b",
 relevance:0},{
-begin:"(\\/\\/|"+n.RE_STARTERS_RE+"|\\b(split|return|print|reverse|grep)\\b)\\s*",
+begin:"(\\/\\/|"+e.RE_STARTERS_RE+"|\\b(split|return|print|reverse|grep)\\b)\\s*",
 keywords:"split return print reverse grep",relevance:0,
-contains:[n.HASH_COMMENT_MODE,{className:"regexp",
-begin:e(/(s|tr|y)/,/\//,/(\\.|[^\\\/])*/,/\//,/(\\.|[^\\\/])*/,/\//,t),
-relevance:10},{className:"regexp",begin:/(m|qr)?\//,end:e(/\//,t),
-contains:[n.BACKSLASH_ESCAPE],relevance:0}]},{className:"function",
-beginKeywords:"sub",end:"(\\s*\\(.*?\\))?[;{]",excludeEnd:!0,relevance:5,
-contains:[n.TITLE_MODE]},{begin:"-\\w\\b",relevance:0},{begin:"^__DATA__$",
-end:"^__END__$",subLanguage:"mojolicious",contains:[{begin:"^@@.*",end:"$",
-className:"comment"}]}];return r.contains=c,i.contains=c,{name:"Perl",
-aliases:["pl","pm"],keywords:s,contains:c}}})());
+contains:[e.HASH_COMMENT_MODE,{className:"regexp",variants:[{
+begin:l("s|tr|y",t(...g))},{begin:l("s|tr|y","\\(","\\)")},{
+begin:l("s|tr|y","\\[","\\]")},{begin:l("s|tr|y","\\{","\\}")}],relevance:2},{
+className:"regexp",variants:[{begin:/(m|qr)\/\//,relevance:0},{
+begin:d("(?:m|qr)?",/\//,/\//)},{begin:d("m|qr",t(...g),/\1/)},{
+begin:d("m|qr",/\(/,/\)/)},{begin:d("m|qr",/\[/,/\]/)},{
+begin:d("m|qr",/\{/,/\}/)}]}]},{className:"function",beginKeywords:"sub",
+end:"(\\s*\\(.*?\\))?[;{]",excludeEnd:!0,relevance:5,contains:[e.TITLE_MODE]},{
+begin:"-\\w\\b",relevance:0},{begin:"^__DATA__$",end:"^__END__$",
+subLanguage:"mojolicious",contains:[{begin:"^@@.*",end:"$",className:"comment"}]
+}];return i.contains=p,a.contains=p,{name:"Perl",aliases:["pl","pm"],keywords:s,
+contains:p}}})());
 hljs.registerLanguage("mojolicious",(()=>{"use strict";return e=>({
 name:"Mojolicious",subLanguage:"xml",contains:[{className:"meta",
 begin:"^__(END|DATA)__$"},{begin:"^\\s*%{1,2}={0,2}",end:"$",subLanguage:"perl"
@@ -2690,7 +2714,7 @@
 begin:"(".concat(n.keyword.toString().replace(/\s/g,"|"),")\\b"),endsParent:!0,
 relevance:0},e.inherit(e.TITLE_MODE,{endsParent:!0})]
 },g=[p,r,s,e.NUMBER_MODE,a,t,{className:"built_in",variants:[{
-begin:"(Add|Clear|Close|Copy|Enter|Exit|Find|Format|Get|Hide|Join|Lock|Move|New|Open|Optimize|Pop|Push|Redo|Remove|Rename|Reset|Resize|Search|Select|Set|Show|Skip|Split|Step|Switch|Undo|Unlock|Watch|Backup|Checkpoint|Compare|Compress|Convert|ConvertFrom|ConvertTo|Dismount|Edit|Expand|Export|Group|Import|Initialize|Limit|Merge|Out|Publish|Restore|Save|Sync|Unpublish|Update|Approve|Assert|Complete|Confirm|Deny|Disable|Enable|Install|Invoke|Register|Request|Restart|Resume|Start|Stop|Submit|Suspend|Uninstall|Unregister|Wait|Debug|Measure|Ping|Repair|Resolve|Test|Trace|Connect|Disconnect|Read|Receive|Send|Write|Block|Grant|Protect|Revoke|Unblock|Unprotect|Use|ForEach|Sort|Tee|Where)+(-)[\\w\\d]+"
+begin:"(Add|Clear|Close|Copy|Enter|Exit|Find|Format|Get|Hide|Join|Lock|Move|New|Open|Optimize|Pop|Push|Redo|Remove|Rename|Reset|Resize|Search|Select|Set|Show|Skip|Split|Step|Switch|Undo|Unlock|Watch|Backup|Checkpoint|Compare|Compress|Convert|ConvertFrom|ConvertTo|Dismount|Edit|Expand|Export|Group|Import|Initialize|Limit|Merge|Mount|Out|Publish|Restore|Save|Sync|Unpublish|Update|Approve|Assert|Build|Complete|Confirm|Deny|Deploy|Disable|Enable|Install|Invoke|Register|Request|Restart|Resume|Start|Stop|Submit|Suspend|Uninstall|Unregister|Wait|Debug|Measure|Ping|Repair|Resolve|Test|Trace|Connect|Disconnect|Read|Receive|Send|Write|Block|Grant|Protect|Revoke|Unblock|Unprotect|Use|ForEach|Sort|Tee|Where)+(-)[\\w\\d]+"
 }]},i,{className:"literal",begin:/\$(null|true|false)\b/},{
 className:"selector-tag",begin:/@\B/,relevance:0}],m={begin:/\[/,end:/\]/,
 excludeBegin:!0,excludeEnd:!0,relevance:0,contains:[].concat("self",g,{
@@ -2773,9 +2797,9 @@
 begin:"\\.\\w*"},e.UNDERSCORE_TITLE_MODE]},{className:"string",begin:'(~)?"',
 end:'"',illegal:"\\n"},{className:"symbol",begin:"#[a-zA-Z_]\\w*\\$?"}]})})());
 hljs.registerLanguage("python",(()=>{"use strict";return e=>{const n={
-keyword:"and as assert async await break class continue def del elif else except finally for  from global if import in is lambda nonlocal|10 not or pass raise return try while with yield",
-built_in:"__import__ abs all any ascii bin bool breakpoint bytearray bytes callable chr classmethod compile complex delattr dict dir divmod enumerate eval exec filter float format frozenset getattr globals hasattr hash help hex id input int isinstance issubclass iter len list locals map max memoryview min next object oct open ord pow print property range repr reversed round set setattr slice sorted staticmethod str sum super tuple type vars zip",
-literal:"__debug__ Ellipsis False None NotImplemented True"},a={
+keyword:["and","as","assert","async","await","break","class","continue","def","del","elif","else","except","finally","for","","from","global","if","import","in","is","lambda","nonlocal|10","not","or","pass","raise","return","try","while","with","yield"],
+built_in:["__import__","abs","all","any","ascii","bin","bool","breakpoint","bytearray","bytes","callable","chr","classmethod","compile","complex","delattr","dict","dir","divmod","enumerate","eval","exec","filter","float","format","frozenset","getattr","globals","hasattr","hash","help","hex","id","input","int","isinstance","issubclass","iter","len","list","locals","map","max","memoryview","min","next","object","oct","open","ord","pow","print","property","range","repr","reversed","round","set","setattr","slice","sorted","staticmethod","str","sum","super","tuple","type","vars","zip"],
+literal:["__debug__","Ellipsis","False","None","NotImplemented","True"]},a={
 className:"meta",begin:/^(>>>|\.\.\.) /},s={className:"subst",begin:/\{/,
 end:/\}/,keywords:n,illegal:/#/},i={begin:/\{\{/,relevance:0},r={
 className:"string",contains:[e.BACKSLASH_ESCAPE],variants:[{
@@ -2945,7 +2969,7 @@
 keyword:r+" :"+r.split(" ").join(" :")+" :"+"global local beep delay put len typeof pick log time set find environment terminal error execute parse resolve toarray tobool toid toip toip6 tonum tostr totime".split(" ").join(" :")
 },contains:[{variants:[{begin:/\/\*/,end:/\*\//},{begin:/\/\//,end:/$/},{
 begin:/<\//,end:/>/}],illegal:/./},e.COMMENT("^#","$"),s,t,i,{
-begin:/[\w-]+=([^\s{}[\]()]+)/,relevance:0,returnBegin:!0,contains:[{
+begin:/[\w-]+=([^\s{}[\]()>]+)/,relevance:0,returnBegin:!0,contains:[{
 className:"attribute",begin:/[^=]+/},{begin:/=/,endsWithParent:!0,relevance:0,
 contains:[s,t,i,{className:"literal",begin:"\\b("+n.split(" ").join("|")+")\\b"
 },{begin:/("[^"]*"|[^\s{}[\]]+)/}]}]},{className:"number",begin:/\*[0-9a-fA-F]+/
@@ -3008,12 +3032,11 @@
 },e.COMMENT("\\*",";"),e.C_BLOCK_COMMENT_MODE]})})());
 hljs.registerLanguage("scala",(()=>{"use strict";return e=>{const n={
 className:"subst",variants:[{begin:"\\$[A-Za-z0-9_]+"},{begin:/\$\{/,end:/\}/}]
-},a={className:"string",variants:[{begin:'"',end:'"',illegal:"\\n",
-contains:[e.BACKSLASH_ESCAPE]},{begin:'"""',end:'"""',relevance:10},{
-begin:'[a-z]+"',end:'"',illegal:"\\n",contains:[e.BACKSLASH_ESCAPE,n]},{
-className:"string",begin:'[a-z]+"""',end:'"""',contains:[n],relevance:10}]},s={
-className:"type",begin:"\\b[A-Z][A-Za-z0-9_]*",relevance:0},t={
-className:"title",
+},a={className:"string",variants:[{begin:'"""',end:'"""'},{begin:'"',end:'"',
+illegal:"\\n",contains:[e.BACKSLASH_ESCAPE]},{begin:'[a-z]+"',end:'"',
+illegal:"\\n",contains:[e.BACKSLASH_ESCAPE,n]},{className:"string",
+begin:'[a-z]+"""',end:'"""',contains:[n],relevance:10}]},s={className:"type",
+begin:"\\b[A-Z][A-Za-z0-9_]*",relevance:0},t={className:"title",
 begin:/[^0-9\n\t "'(),.`{}\[\]:;][^\n\t "'(),.`{}\[\]:;]+|[^0-9\n\t "'(),.`{}\[\]:;=]/,
 relevance:0},i={className:"class",beginKeywords:"class object trait type",
 end:/[:={\[\n;]/,excludeEnd:!0,
@@ -3056,31 +3079,31 @@
 className:"params",begin:"\\(",end:"\\)"}]},{
 begin:"[a-zA-Z_][a-zA-Z_0-9]*[\\.']+",relevance:0},{begin:"\\[",
 end:"\\][\\.']*",relevance:0,contains:n},e.COMMENT("//","$")].concat(n)}}})());
-hljs.registerLanguage("scss",(()=>{"use strict";return e=>{var t="@[a-z-]+",i={
-className:"variable",begin:"(\\$[a-zA-Z-][a-zA-Z0-9_-]*)\\b"},r={
-className:"number",begin:"#[0-9A-Fa-f]+"}
-;return e.CSS_NUMBER_MODE,e.QUOTE_STRING_MODE,
-e.APOS_STRING_MODE,e.C_BLOCK_COMMENT_MODE,{name:"SCSS",case_insensitive:!0,
-illegal:"[=/|']",contains:[e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,{
+hljs.registerLanguage("scss",(()=>{"use strict"
+;const e=["a","abbr","address","article","aside","audio","b","blockquote","body","button","canvas","caption","cite","code","dd","del","details","dfn","div","dl","dt","em","fieldset","figcaption","figure","footer","form","h1","h2","h3","h4","h5","h6","header","hgroup","html","i","iframe","img","input","ins","kbd","label","legend","li","main","mark","menu","nav","object","ol","p","q","quote","samp","section","span","strong","summary","sup","table","tbody","td","textarea","tfoot","th","thead","time","tr","ul","var","video"],t=["any-hover","any-pointer","aspect-ratio","color","color-gamut","color-index","device-aspect-ratio","device-height","device-width","display-mode","forced-colors","grid","height","hover","inverted-colors","monochrome","orientation","overflow-block","overflow-inline","pointer","prefers-color-scheme","prefers-contrast","prefers-reduced-motion","prefers-reduced-transparency","resolution","scan","scripting","update","width","min-width","max-width","min-height","max-height"],i=["active","any-link","blank","checked","current","default","defined","dir","disabled","drop","empty","enabled","first","first-child","first-of-type","fullscreen","future","focus","focus-visible","focus-within","has","host","host-context","hover","indeterminate","in-range","invalid","is","lang","last-child","last-of-type","left","link","local-link","not","nth-child","nth-col","nth-last-child","nth-last-col","nth-last-of-type","nth-of-type","only-child","only-of-type","optional","out-of-range","past","placeholder-shown","read-only","read-write","required","right","root","scope","target","target-within","user-invalid","valid","visited","where"],r=["after","backdrop","before","cue","cue-region","first-letter","first-line","grammar-error","marker","part","placeholder","selection","slotted","spelling-error"],o=["align-content","align-items","align-self","animation","animation-delay","animation-direction","animation-duration","animation-fill-mode","animation-iteration-count","animation-name","animation-play-state","animation-timing-function","auto","backface-visibility","background","background-attachment","background-clip","background-color","background-image","background-origin","background-position","background-repeat","background-size","border","border-bottom","border-bottom-color","border-bottom-left-radius","border-bottom-right-radius","border-bottom-style","border-bottom-width","border-collapse","border-color","border-image","border-image-outset","border-image-repeat","border-image-slice","border-image-source","border-image-width","border-left","border-left-color","border-left-style","border-left-width","border-radius","border-right","border-right-color","border-right-style","border-right-width","border-spacing","border-style","border-top","border-top-color","border-top-left-radius","border-top-right-radius","border-top-style","border-top-width","border-width","bottom","box-decoration-break","box-shadow","box-sizing","break-after","break-before","break-inside","caption-side","clear","clip","clip-path","color","column-count","column-fill","column-gap","column-rule","column-rule-color","column-rule-style","column-rule-width","column-span","column-width","columns","content","counter-increment","counter-reset","cursor","direction","display","empty-cells","filter","flex","flex-basis","flex-direction","flex-flow","flex-grow","flex-shrink","flex-wrap","float","font","font-display","font-family","font-feature-settings","font-kerning","font-language-override","font-size","font-size-adjust","font-stretch","font-style","font-variant","font-variant-ligatures","font-variation-settings","font-weight","height","hyphens","icon","image-orientation","image-rendering","image-resolution","ime-mode","inherit","initial","justify-content","left","letter-spacing","line-height","list-style","list-style-image","list-style-position","list-style-type","margin","margin-bottom","margin-left","margin-right","margin-top","marks","mask","max-height","max-width","min-height","min-width","nav-down","nav-index","nav-left","nav-right","nav-up","none","normal","object-fit","object-position","opacity","order","orphans","outline","outline-color","outline-offset","outline-style","outline-width","overflow","overflow-wrap","overflow-x","overflow-y","padding","padding-bottom","padding-left","padding-right","padding-top","page-break-after","page-break-before","page-break-inside","perspective","perspective-origin","pointer-events","position","quotes","resize","right","src","tab-size","table-layout","text-align","text-align-last","text-decoration","text-decoration-color","text-decoration-line","text-decoration-style","text-indent","text-overflow","text-rendering","text-shadow","text-transform","text-underline-position","top","transform","transform-origin","transform-style","transition","transition-delay","transition-duration","transition-property","transition-timing-function","unicode-bidi","vertical-align","visibility","white-space","widows","width","word-break","word-spacing","word-wrap","z-index"].reverse()
+;return a=>{const n=(e=>({IMPORTANT:{className:"meta",begin:"!important"},
+HEXCOLOR:{className:"number",begin:"#([a-fA-F0-9]{6}|[a-fA-F0-9]{3})"},
+ATTRIBUTE_SELECTOR_MODE:{className:"selector-attr",begin:/\[/,end:/\]/,
+illegal:"$",contains:[e.APOS_STRING_MODE,e.QUOTE_STRING_MODE]}
+}))(a),l=r,s=i,d="@[a-z-]+",c={className:"variable",
+begin:"(\\$[a-zA-Z-][a-zA-Z0-9_-]*)\\b"};return{name:"SCSS",case_insensitive:!0,
+illegal:"[=/|']",contains:[a.C_LINE_COMMENT_MODE,a.C_BLOCK_COMMENT_MODE,{
 className:"selector-id",begin:"#[A-Za-z0-9_-]+",relevance:0},{
-className:"selector-class",begin:"\\.[A-Za-z0-9_-]+",relevance:0},{
-className:"selector-attr",begin:"\\[",end:"\\]",illegal:"$"},{
-className:"selector-tag",
-begin:"\\b(a|abbr|acronym|address|area|article|aside|audio|b|base|big|blockquote|body|br|button|canvas|caption|cite|code|col|colgroup|command|datalist|dd|del|details|dfn|div|dl|dt|em|embed|fieldset|figcaption|figure|footer|form|frame|frameset|(h[1-6])|head|header|hgroup|hr|html|i|iframe|img|input|ins|kbd|keygen|label|legend|li|link|map|mark|meta|meter|nav|noframes|noscript|object|ol|optgroup|option|output|p|param|pre|progress|q|rp|rt|ruby|samp|script|section|select|small|span|strike|strong|style|sub|sup|table|tbody|td|textarea|tfoot|th|thead|time|title|tr|tt|ul|var|video)\\b",
-relevance:0},{className:"selector-pseudo",
-begin:":(visited|valid|root|right|required|read-write|read-only|out-range|optional|only-of-type|only-child|nth-of-type|nth-last-of-type|nth-last-child|nth-child|not|link|left|last-of-type|last-child|lang|invalid|indeterminate|in-range|hover|focus|first-of-type|first-line|first-letter|first-child|first|enabled|empty|disabled|default|checked|before|after|active)"
-},{className:"selector-pseudo",
-begin:"::(after|before|choices|first-letter|first-line|repeat-index|repeat-item|selection|value)"
-},i,{className:"attribute",
-begin:"\\b(src|z-index|word-wrap|word-spacing|word-break|width|widows|white-space|visibility|vertical-align|unicode-bidi|transition-timing-function|transition-property|transition-duration|transition-delay|transition|transform-style|transform-origin|transform|top|text-underline-position|text-transform|text-shadow|text-rendering|text-overflow|text-indent|text-decoration-style|text-decoration-line|text-decoration-color|text-decoration|text-align-last|text-align|tab-size|table-layout|right|resize|quotes|position|pointer-events|perspective-origin|perspective|page-break-inside|page-break-before|page-break-after|padding-top|padding-right|padding-left|padding-bottom|padding|overflow-y|overflow-x|overflow-wrap|overflow|outline-width|outline-style|outline-offset|outline-color|outline|orphans|order|opacity|object-position|object-fit|normal|none|nav-up|nav-right|nav-left|nav-index|nav-down|min-width|min-height|max-width|max-height|mask|marks|margin-top|margin-right|margin-left|margin-bottom|margin|list-style-type|list-style-position|list-style-image|list-style|line-height|letter-spacing|left|justify-content|initial|inherit|ime-mode|image-orientation|image-resolution|image-rendering|icon|hyphens|height|font-weight|font-variant-ligatures|font-variant|font-style|font-stretch|font-size-adjust|font-size|font-language-override|font-kerning|font-feature-settings|font-family|font|float|flex-wrap|flex-shrink|flex-grow|flex-flow|flex-direction|flex-basis|flex|filter|empty-cells|display|direction|cursor|counter-reset|counter-increment|content|column-width|column-span|column-rule-width|column-rule-style|column-rule-color|column-rule|column-gap|column-fill|column-count|columns|color|clip-path|clip|clear|caption-side|break-inside|break-before|break-after|box-sizing|box-shadow|box-decoration-break|bottom|border-width|border-top-width|border-top-style|border-top-right-radius|border-top-left-radius|border-top-color|border-top|border-style|border-spacing|border-right-width|border-right-style|border-right-color|border-right|border-radius|border-left-width|border-left-style|border-left-color|border-left|border-image-width|border-image-source|border-image-slice|border-image-repeat|border-image-outset|border-image|border-color|border-collapse|border-bottom-width|border-bottom-style|border-bottom-right-radius|border-bottom-left-radius|border-bottom-color|border-bottom|border|background-size|background-repeat|background-position|background-origin|background-image|background-color|background-clip|background-attachment|background-blend-mode|background|backface-visibility|auto|animation-timing-function|animation-play-state|animation-name|animation-iteration-count|animation-fill-mode|animation-duration|animation-direction|animation-delay|animation|align-self|align-items|align-content)\\b",
-illegal:"[^\\s]"},{
+className:"selector-class",begin:"\\.[A-Za-z0-9_-]+",relevance:0
+},n.ATTRIBUTE_SELECTOR_MODE,{className:"selector-tag",
+begin:"\\b("+e.join("|")+")\\b",relevance:0},{className:"selector-pseudo",
+begin:":("+s.join("|")+")"},{className:"selector-pseudo",
+begin:"::("+l.join("|")+")"},c,{begin:/\(/,end:/\)/,contains:[a.CSS_NUMBER_MODE]
+},{className:"attribute",begin:"\\b("+o.join("|")+")\\b"},{
 begin:"\\b(whitespace|wait|w-resize|visible|vertical-text|vertical-ideographic|uppercase|upper-roman|upper-alpha|underline|transparent|top|thin|thick|text|text-top|text-bottom|tb-rl|table-header-group|table-footer-group|sw-resize|super|strict|static|square|solid|small-caps|separate|se-resize|scroll|s-resize|rtl|row-resize|ridge|right|repeat|repeat-y|repeat-x|relative|progress|pointer|overline|outside|outset|oblique|nowrap|not-allowed|normal|none|nw-resize|no-repeat|no-drop|newspaper|ne-resize|n-resize|move|middle|medium|ltr|lr-tb|lowercase|lower-roman|lower-alpha|loose|list-item|line|line-through|line-edge|lighter|left|keep-all|justify|italic|inter-word|inter-ideograph|inside|inset|inline|inline-block|inherit|inactive|ideograph-space|ideograph-parenthesis|ideograph-numeric|ideograph-alpha|horizontal|hidden|help|hand|groove|fixed|ellipsis|e-resize|double|dotted|distribute|distribute-space|distribute-letter|distribute-all-lines|disc|disabled|default|decimal|dashed|crosshair|collapse|col-resize|circle|char|center|capitalize|break-word|break-all|bottom|both|bolder|bold|block|bidi-override|below|baseline|auto|always|all-scroll|absolute|table|table-cell)\\b"
 },{begin:":",end:";",
-contains:[i,r,e.CSS_NUMBER_MODE,e.QUOTE_STRING_MODE,e.APOS_STRING_MODE,{
-className:"meta",begin:"!important"}]},{begin:"@(page|font-face)",lexemes:t,
-keywords:"@page @font-face"},{begin:"@",end:"[{;]",returnBegin:!0,
-keywords:"and or not only",contains:[{begin:t,className:"keyword"
-},i,e.QUOTE_STRING_MODE,e.APOS_STRING_MODE,r,e.CSS_NUMBER_MODE]}]}}})());
+contains:[c,n.HEXCOLOR,a.CSS_NUMBER_MODE,a.QUOTE_STRING_MODE,a.APOS_STRING_MODE,n.IMPORTANT]
+},{begin:"@(page|font-face)",lexemes:d,keywords:"@page @font-face"},{begin:"@",
+end:"[{;]",returnBegin:!0,keywords:{$pattern:/[a-z-]+/,
+keyword:"and or not only",attribute:t.join(" ")},contains:[{begin:d,
+className:"keyword"},{begin:/[a-z-]+(?=:)/,className:"attribute"
+},c,a.QUOTE_STRING_MODE,a.APOS_STRING_MODE,n.HEXCOLOR,a.CSS_NUMBER_MODE]}]}}
+})());
 hljs.registerLanguage("shell",(()=>{"use strict";return s=>({
 name:"Shell Session",aliases:["console"],contains:[{className:"meta",
 begin:/^\s{0,3}[/~\w\d[\]()@-]*[>%$#]/,starts:{end:/[^\\](?=\s*$)/,
@@ -3165,15 +3188,14 @@
 return r.map((r=>e(r))).join("")}function t(...r){
 return"("+r.map((r=>e(r))).join("|")+")"}return e=>{
 const n=e.COMMENT("--","$"),a=["true","false","unknown"],i=["bigint","binary","blob","boolean","char","character","clob","date","dec","decfloat","decimal","float","int","integer","interval","nchar","nclob","national","numeric","real","row","smallint","time","timestamp","varchar","varying","varbinary"],s=["abs","acos","array_agg","asin","atan","avg","cast","ceil","ceiling","coalesce","corr","cos","cosh","count","covar_pop","covar_samp","cume_dist","dense_rank","deref","element","exp","extract","first_value","floor","json_array","json_arrayagg","json_exists","json_object","json_objectagg","json_query","json_table","json_table_primitive","json_value","lag","last_value","lead","listagg","ln","log","log10","lower","max","min","mod","nth_value","ntile","nullif","percent_rank","percentile_cont","percentile_disc","position","position_regex","power","rank","regr_avgx","regr_avgy","regr_count","regr_intercept","regr_r2","regr_slope","regr_sxx","regr_sxy","regr_syy","row_number","sin","sinh","sqrt","stddev_pop","stddev_samp","substring","substring_regex","sum","tan","tanh","translate","translate_regex","treat","trim","trim_array","unnest","upper","value_of","var_pop","var_samp","width_bucket"],o=["create table","insert into","primary key","foreign key","not null","alter table","add constraint","grouping sets","on overflow","character set","respect nulls","ignore nulls","nulls first","nulls last","depth first","breadth first"],c=s,l=["abs","acos","all","allocate","alter","and","any","are","array","array_agg","array_max_cardinality","as","asensitive","asin","asymmetric","at","atan","atomic","authorization","avg","begin","begin_frame","begin_partition","between","bigint","binary","blob","boolean","both","by","call","called","cardinality","cascaded","case","cast","ceil","ceiling","char","char_length","character","character_length","check","classifier","clob","close","coalesce","collate","collect","column","commit","condition","connect","constraint","contains","convert","copy","corr","corresponding","cos","cosh","count","covar_pop","covar_samp","create","cross","cube","cume_dist","current","current_catalog","current_date","current_default_transform_group","current_path","current_role","current_row","current_schema","current_time","current_timestamp","current_path","current_role","current_transform_group_for_type","current_user","cursor","cycle","date","day","deallocate","dec","decimal","decfloat","declare","default","define","delete","dense_rank","deref","describe","deterministic","disconnect","distinct","double","drop","dynamic","each","element","else","empty","end","end_frame","end_partition","end-exec","equals","escape","every","except","exec","execute","exists","exp","external","extract","false","fetch","filter","first_value","float","floor","for","foreign","frame_row","free","from","full","function","fusion","get","global","grant","group","grouping","groups","having","hold","hour","identity","in","indicator","initial","inner","inout","insensitive","insert","int","integer","intersect","intersection","interval","into","is","join","json_array","json_arrayagg","json_exists","json_object","json_objectagg","json_query","json_table","json_table_primitive","json_value","lag","language","large","last_value","lateral","lead","leading","left","like","like_regex","listagg","ln","local","localtime","localtimestamp","log","log10","lower","match","match_number","match_recognize","matches","max","member","merge","method","min","minute","mod","modifies","module","month","multiset","national","natural","nchar","nclob","new","no","none","normalize","not","nth_value","ntile","null","nullif","numeric","octet_length","occurrences_regex","of","offset","old","omit","on","one","only","open","or","order","out","outer","over","overlaps","overlay","parameter","partition","pattern","per","percent","percent_rank","percentile_cont","percentile_disc","period","portion","position","position_regex","power","precedes","precision","prepare","primary","procedure","ptf","range","rank","reads","real","recursive","ref","references","referencing","regr_avgx","regr_avgy","regr_count","regr_intercept","regr_r2","regr_slope","regr_sxx","regr_sxy","regr_syy","release","result","return","returns","revoke","right","rollback","rollup","row","row_number","rows","running","savepoint","scope","scroll","search","second","seek","select","sensitive","session_user","set","show","similar","sin","sinh","skip","smallint","some","specific","specifictype","sql","sqlexception","sqlstate","sqlwarning","sqrt","start","static","stddev_pop","stddev_samp","submultiset","subset","substring","substring_regex","succeeds","sum","symmetric","system","system_time","system_user","table","tablesample","tan","tanh","then","time","timestamp","timezone_hour","timezone_minute","to","trailing","translate","translate_regex","translation","treat","trigger","trim","trim_array","true","truncate","uescape","union","unique","unknown","unnest","update   ","upper","user","using","value","values","value_of","var_pop","var_samp","varbinary","varchar","varying","versioning","when","whenever","where","width_bucket","window","with","within","without","year","add","asc","collation","desc","final","first","last","view"].filter((e=>!s.includes(e))),u={
-begin:r(/\b/,t(...c),/\s*\(/),keywords:{built_in:c.join(" ")}};return{
-name:"SQL",case_insensitive:!0,illegal:/[{}]|<\//,keywords:{
-$pattern:/\b[\w\.]+/,keyword:((e,{exceptions:r,when:t}={})=>{const n=t
+begin:r(/\b/,t(...c),/\s*\(/),keywords:{built_in:c}};return{name:"SQL",
+case_insensitive:!0,illegal:/[{}]|<\//,keywords:{$pattern:/\b[\w\.]+/,
+keyword:((e,{exceptions:r,when:t}={})=>{const n=t
 ;return r=r||[],e.map((e=>e.match(/\|\d+$/)||r.includes(e)?e:n(e)?e+"|0":e))
-})(l,{when:e=>e.length<3}).join(" "),literal:a.join(" "),type:i.join(" "),
-built_in:"current_catalog current_date current_default_transform_group current_path current_role current_schema current_transform_group_for_type current_user session_user system_time system_user current_time localtime current_timestamp localtimestamp"
-},contains:[{begin:t(...o),keywords:{$pattern:/[\w\.]+/,
-keyword:l.concat(o).join(" "),literal:a.join(" "),type:i.join(" ")}},{
-className:"type",
+})(l,{when:e=>e.length<3}),literal:a,type:i,
+built_in:["current_catalog","current_date","current_default_transform_group","current_path","current_role","current_schema","current_transform_group_for_type","current_user","session_user","system_time","system_user","current_time","localtime","current_timestamp","localtimestamp"]
+},contains:[{begin:t(...o),keywords:{$pattern:/[\w\.]+/,keyword:l.concat(o),
+literal:a,type:i}},{className:"type",
 begin:t("double precision","large object","with timezone","without timezone")
 },u,{className:"variable",begin:/@[a-z0-9]+/},{className:"string",variants:[{
 begin:/'/,end:/'/,contains:[{begin:/''/}]}]},{begin:/"/,end:/"/,contains:[{
@@ -3181,16 +3203,16 @@
 begin:/[-+*/=%^~]|&&?|\|\|?|!=?|<(?:=>?|<|>)?|>[>=]?/,relevance:0}]}}})());
 hljs.registerLanguage("stan",(()=>{"use strict";return _=>({name:"Stan",
 aliases:["stanfuncs"],keywords:{$pattern:_.IDENT_RE,
-title:"functions model data parameters quantities transformed generated",
-keyword:["for","in","if","else","while","break","continue","return"].concat(["int","real","vector","ordered","positive_ordered","simplex","unit_vector","row_vector","matrix","cholesky_factor_corr|10","cholesky_factor_cov|10","corr_matrix|10","cov_matrix|10","void"]).concat(["print","reject","increment_log_prob|10","integrate_ode|10","integrate_ode_rk45|10","integrate_ode_bdf|10","algebra_solver"]).join(" "),
-built_in:"Phi Phi_approx abs acos acosh algebra_solver append_array append_col append_row asin asinh atan atan2 atanh bernoulli_cdf bernoulli_lccdf bernoulli_lcdf bernoulli_logit_lpmf bernoulli_logit_rng bernoulli_lpmf bernoulli_rng bessel_first_kind bessel_second_kind beta_binomial_cdf beta_binomial_lccdf beta_binomial_lcdf beta_binomial_lpmf beta_binomial_rng beta_cdf beta_lccdf beta_lcdf beta_lpdf beta_rng binary_log_loss binomial_cdf binomial_coefficient_log binomial_lccdf binomial_lcdf binomial_logit_lpmf binomial_lpmf binomial_rng block categorical_logit_lpmf categorical_logit_rng categorical_lpmf categorical_rng cauchy_cdf cauchy_lccdf cauchy_lcdf cauchy_lpdf cauchy_rng cbrt ceil chi_square_cdf chi_square_lccdf chi_square_lcdf chi_square_lpdf chi_square_rng cholesky_decompose choose col cols columns_dot_product columns_dot_self cos cosh cov_exp_quad crossprod csr_extract_u csr_extract_v csr_extract_w csr_matrix_times_vector csr_to_dense_matrix cumulative_sum determinant diag_matrix diag_post_multiply diag_pre_multiply diagonal digamma dims dirichlet_lpdf dirichlet_rng distance dot_product dot_self double_exponential_cdf double_exponential_lccdf double_exponential_lcdf double_exponential_lpdf double_exponential_rng e eigenvalues_sym eigenvectors_sym erf erfc exp exp2 exp_mod_normal_cdf exp_mod_normal_lccdf exp_mod_normal_lcdf exp_mod_normal_lpdf exp_mod_normal_rng expm1 exponential_cdf exponential_lccdf exponential_lcdf exponential_lpdf exponential_rng fabs falling_factorial fdim floor fma fmax fmin fmod frechet_cdf frechet_lccdf frechet_lcdf frechet_lpdf frechet_rng gamma_cdf gamma_lccdf gamma_lcdf gamma_lpdf gamma_p gamma_q gamma_rng gaussian_dlm_obs_lpdf get_lp gumbel_cdf gumbel_lccdf gumbel_lcdf gumbel_lpdf gumbel_rng head hypergeometric_lpmf hypergeometric_rng hypot inc_beta int_step integrate_ode integrate_ode_bdf integrate_ode_rk45 inv inv_Phi inv_chi_square_cdf inv_chi_square_lccdf inv_chi_square_lcdf inv_chi_square_lpdf inv_chi_square_rng inv_cloglog inv_gamma_cdf inv_gamma_lccdf inv_gamma_lcdf inv_gamma_lpdf inv_gamma_rng inv_logit inv_sqrt inv_square inv_wishart_lpdf inv_wishart_rng inverse inverse_spd is_inf is_nan lbeta lchoose lgamma lkj_corr_cholesky_lpdf lkj_corr_cholesky_rng lkj_corr_lpdf lkj_corr_rng lmgamma lmultiply log log10 log1m log1m_exp log1m_inv_logit log1p log1p_exp log2 log_determinant log_diff_exp log_falling_factorial log_inv_logit log_mix log_rising_factorial log_softmax log_sum_exp logistic_cdf logistic_lccdf logistic_lcdf logistic_lpdf logistic_rng logit lognormal_cdf lognormal_lccdf lognormal_lcdf lognormal_lpdf lognormal_rng machine_precision matrix_exp max mdivide_left_spd mdivide_left_tri_low mdivide_right_spd mdivide_right_tri_low mean min modified_bessel_first_kind modified_bessel_second_kind multi_gp_cholesky_lpdf multi_gp_lpdf multi_normal_cholesky_lpdf multi_normal_cholesky_rng multi_normal_lpdf multi_normal_prec_lpdf multi_normal_rng multi_student_t_lpdf multi_student_t_rng multinomial_lpmf multinomial_rng multiply_log multiply_lower_tri_self_transpose neg_binomial_2_cdf neg_binomial_2_lccdf neg_binomial_2_lcdf neg_binomial_2_log_lpmf neg_binomial_2_log_rng neg_binomial_2_lpmf neg_binomial_2_rng neg_binomial_cdf neg_binomial_lccdf neg_binomial_lcdf neg_binomial_lpmf neg_binomial_rng negative_infinity normal_cdf normal_lccdf normal_lcdf normal_lpdf normal_rng not_a_number num_elements ordered_logistic_lpmf ordered_logistic_rng owens_t pareto_cdf pareto_lccdf pareto_lcdf pareto_lpdf pareto_rng pareto_type_2_cdf pareto_type_2_lccdf pareto_type_2_lcdf pareto_type_2_lpdf pareto_type_2_rng pi poisson_cdf poisson_lccdf poisson_lcdf poisson_log_lpmf poisson_log_rng poisson_lpmf poisson_rng positive_infinity pow print prod qr_Q qr_R quad_form quad_form_diag quad_form_sym rank rayleigh_cdf rayleigh_lccdf rayleigh_lcdf rayleigh_lpdf rayleigh_rng reject rep_array rep_matrix rep_row_vector rep_vector rising_factorial round row rows rows_dot_product rows_dot_self scaled_inv_chi_square_cdf scaled_inv_chi_square_lccdf scaled_inv_chi_square_lcdf scaled_inv_chi_square_lpdf scaled_inv_chi_square_rng sd segment sin singular_values sinh size skew_normal_cdf skew_normal_lccdf skew_normal_lcdf skew_normal_lpdf skew_normal_rng softmax sort_asc sort_desc sort_indices_asc sort_indices_desc sqrt sqrt2 square squared_distance step student_t_cdf student_t_lccdf student_t_lcdf student_t_lpdf student_t_rng sub_col sub_row sum tail tan tanh target tcrossprod tgamma to_array_1d to_array_2d to_matrix to_row_vector to_vector trace trace_gen_quad_form trace_quad_form trigamma trunc uniform_cdf uniform_lccdf uniform_lcdf uniform_lpdf uniform_rng variance von_mises_lpdf von_mises_rng weibull_cdf weibull_lccdf weibull_lcdf weibull_lpdf weibull_rng wiener_lpdf wishart_lpdf wishart_rng"
+title:["functions","model","data","parameters","quantities","transformed","generated"],
+keyword:["for","in","if","else","while","break","continue","return"].concat(["int","real","vector","ordered","positive_ordered","simplex","unit_vector","row_vector","matrix","cholesky_factor_corr|10","cholesky_factor_cov|10","corr_matrix|10","cov_matrix|10","void"]).concat(["print","reject","increment_log_prob|10","integrate_ode|10","integrate_ode_rk45|10","integrate_ode_bdf|10","algebra_solver"]),
+built_in:["Phi","Phi_approx","abs","acos","acosh","algebra_solver","append_array","append_col","append_row","asin","asinh","atan","atan2","atanh","bernoulli_cdf","bernoulli_lccdf","bernoulli_lcdf","bernoulli_logit_lpmf","bernoulli_logit_rng","bernoulli_lpmf","bernoulli_rng","bessel_first_kind","bessel_second_kind","beta_binomial_cdf","beta_binomial_lccdf","beta_binomial_lcdf","beta_binomial_lpmf","beta_binomial_rng","beta_cdf","beta_lccdf","beta_lcdf","beta_lpdf","beta_rng","binary_log_loss","binomial_cdf","binomial_coefficient_log","binomial_lccdf","binomial_lcdf","binomial_logit_lpmf","binomial_lpmf","binomial_rng","block","categorical_logit_lpmf","categorical_logit_rng","categorical_lpmf","categorical_rng","cauchy_cdf","cauchy_lccdf","cauchy_lcdf","cauchy_lpdf","cauchy_rng","cbrt","ceil","chi_square_cdf","chi_square_lccdf","chi_square_lcdf","chi_square_lpdf","chi_square_rng","cholesky_decompose","choose","col","cols","columns_dot_product","columns_dot_self","cos","cosh","cov_exp_quad","crossprod","csr_extract_u","csr_extract_v","csr_extract_w","csr_matrix_times_vector","csr_to_dense_matrix","cumulative_sum","determinant","diag_matrix","diag_post_multiply","diag_pre_multiply","diagonal","digamma","dims","dirichlet_lpdf","dirichlet_rng","distance","dot_product","dot_self","double_exponential_cdf","double_exponential_lccdf","double_exponential_lcdf","double_exponential_lpdf","double_exponential_rng","e","eigenvalues_sym","eigenvectors_sym","erf","erfc","exp","exp2","exp_mod_normal_cdf","exp_mod_normal_lccdf","exp_mod_normal_lcdf","exp_mod_normal_lpdf","exp_mod_normal_rng","expm1","exponential_cdf","exponential_lccdf","exponential_lcdf","exponential_lpdf","exponential_rng","fabs","falling_factorial","fdim","floor","fma","fmax","fmin","fmod","frechet_cdf","frechet_lccdf","frechet_lcdf","frechet_lpdf","frechet_rng","gamma_cdf","gamma_lccdf","gamma_lcdf","gamma_lpdf","gamma_p","gamma_q","gamma_rng","gaussian_dlm_obs_lpdf","get_lp","gumbel_cdf","gumbel_lccdf","gumbel_lcdf","gumbel_lpdf","gumbel_rng","head","hypergeometric_lpmf","hypergeometric_rng","hypot","inc_beta","int_step","integrate_ode","integrate_ode_bdf","integrate_ode_rk45","inv","inv_Phi","inv_chi_square_cdf","inv_chi_square_lccdf","inv_chi_square_lcdf","inv_chi_square_lpdf","inv_chi_square_rng","inv_cloglog","inv_gamma_cdf","inv_gamma_lccdf","inv_gamma_lcdf","inv_gamma_lpdf","inv_gamma_rng","inv_logit","inv_sqrt","inv_square","inv_wishart_lpdf","inv_wishart_rng","inverse","inverse_spd","is_inf","is_nan","lbeta","lchoose","lgamma","lkj_corr_cholesky_lpdf","lkj_corr_cholesky_rng","lkj_corr_lpdf","lkj_corr_rng","lmgamma","lmultiply","log","log10","log1m","log1m_exp","log1m_inv_logit","log1p","log1p_exp","log2","log_determinant","log_diff_exp","log_falling_factorial","log_inv_logit","log_mix","log_rising_factorial","log_softmax","log_sum_exp","logistic_cdf","logistic_lccdf","logistic_lcdf","logistic_lpdf","logistic_rng","logit","lognormal_cdf","lognormal_lccdf","lognormal_lcdf","lognormal_lpdf","lognormal_rng","machine_precision","matrix_exp","max","mdivide_left_spd","mdivide_left_tri_low","mdivide_right_spd","mdivide_right_tri_low","mean","min","modified_bessel_first_kind","modified_bessel_second_kind","multi_gp_cholesky_lpdf","multi_gp_lpdf","multi_normal_cholesky_lpdf","multi_normal_cholesky_rng","multi_normal_lpdf","multi_normal_prec_lpdf","multi_normal_rng","multi_student_t_lpdf","multi_student_t_rng","multinomial_lpmf","multinomial_rng","multiply_log","multiply_lower_tri_self_transpose","neg_binomial_2_cdf","neg_binomial_2_lccdf","neg_binomial_2_lcdf","neg_binomial_2_log_lpmf","neg_binomial_2_log_rng","neg_binomial_2_lpmf","neg_binomial_2_rng","neg_binomial_cdf","neg_binomial_lccdf","neg_binomial_lcdf","neg_binomial_lpmf","neg_binomial_rng","negative_infinity","normal_cdf","normal_lccdf","normal_lcdf","normal_lpdf","normal_rng","not_a_number","num_elements","ordered_logistic_lpmf","ordered_logistic_rng","owens_t","pareto_cdf","pareto_lccdf","pareto_lcdf","pareto_lpdf","pareto_rng","pareto_type_2_cdf","pareto_type_2_lccdf","pareto_type_2_lcdf","pareto_type_2_lpdf","pareto_type_2_rng","pi","poisson_cdf","poisson_lccdf","poisson_lcdf","poisson_log_lpmf","poisson_log_rng","poisson_lpmf","poisson_rng","positive_infinity","pow","print","prod","qr_Q","qr_R","quad_form","quad_form_diag","quad_form_sym","rank","rayleigh_cdf","rayleigh_lccdf","rayleigh_lcdf","rayleigh_lpdf","rayleigh_rng","reject","rep_array","rep_matrix","rep_row_vector","rep_vector","rising_factorial","round","row","rows","rows_dot_product","rows_dot_self","scaled_inv_chi_square_cdf","scaled_inv_chi_square_lccdf","scaled_inv_chi_square_lcdf","scaled_inv_chi_square_lpdf","scaled_inv_chi_square_rng","sd","segment","sin","singular_values","sinh","size","skew_normal_cdf","skew_normal_lccdf","skew_normal_lcdf","skew_normal_lpdf","skew_normal_rng","softmax","sort_asc","sort_desc","sort_indices_asc","sort_indices_desc","sqrt","sqrt2","square","squared_distance","step","student_t_cdf","student_t_lccdf","student_t_lcdf","student_t_lpdf","student_t_rng","sub_col","sub_row","sum","tail","tan","tanh","target","tcrossprod","tgamma","to_array_1d","to_array_2d","to_matrix","to_row_vector","to_vector","trace","trace_gen_quad_form","trace_quad_form","trigamma","trunc","uniform_cdf","uniform_lccdf","uniform_lcdf","uniform_lpdf","uniform_rng","variance","von_mises_lpdf","von_mises_rng","weibull_cdf","weibull_lccdf","weibull_lcdf","weibull_lpdf","weibull_rng","wiener_lpdf","wishart_lpdf","wishart_rng"]
 },contains:[_.C_LINE_COMMENT_MODE,_.COMMENT(/#/,/$/,{relevance:0,keywords:{
 "meta-keyword":"include"}}),_.COMMENT(/\/\*/,/\*\//,{relevance:0,contains:[{
 className:"doctag",begin:/@(return|param)/}]}),{begin:/<\s*lower\s*=/,
 keywords:"lower"},{begin:/[<,]\s*upper\s*=/,keywords:"upper"},{
 className:"keyword",begin:/\btarget\s*\+=/,relevance:10},{
 begin:"~\\s*("+_.IDENT_RE+")\\s*\\(",
-keywords:"bernoulli bernoulli_logit beta beta_binomial binomial binomial_logit categorical categorical_logit cauchy chi_square dirichlet double_exponential exp_mod_normal exponential frechet gamma gaussian_dlm_obs gumbel hypergeometric inv_chi_square inv_gamma inv_wishart lkj_corr lkj_corr_cholesky logistic lognormal multi_gp multi_gp_cholesky multi_normal multi_normal_cholesky multi_normal_prec multi_student_t multinomial neg_binomial neg_binomial_2 neg_binomial_2_log normal ordered_logistic pareto pareto_type_2 poisson poisson_log rayleigh scaled_inv_chi_square skew_normal student_t uniform von_mises weibull wiener wishart"
+keywords:["bernoulli","bernoulli_logit","beta","beta_binomial","binomial","binomial_logit","categorical","categorical_logit","cauchy","chi_square","dirichlet","double_exponential","exp_mod_normal","exponential","frechet","gamma","gaussian_dlm_obs","gumbel","hypergeometric","inv_chi_square","inv_gamma","inv_wishart","lkj_corr","lkj_corr_cholesky","logistic","lognormal","multi_gp","multi_gp_cholesky","multi_normal","multi_normal_cholesky","multi_normal_prec","multi_student_t","multinomial","neg_binomial","neg_binomial_2","neg_binomial_2_log","normal","ordered_logistic","pareto","pareto_type_2","poisson","poisson_log","rayleigh","scaled_inv_chi_square","skew_normal","student_t","uniform","von_mises","weibull","wiener","wishart"]
 },{className:"number",variants:[{begin:/\b\d+(?:\.\d*)?(?:[eE][+-]?\d+)?/},{
 begin:/\.\d+(?:[eE][+-]?\d+)?\b/}],relevance:0},{className:"string",begin:'"',
 end:'"',relevance:0}]})})());
@@ -3213,28 +3235,32 @@
 illegal:null}),e.inherit(e.QUOTE_STRING_MODE,{illegal:null}),{
 className:"string",begin:"'",end:"'"},{className:"symbol",variants:[{begin:"#",
 end:"\\d+",illegal:"\\W"}]}]})})());
-hljs.registerLanguage("stylus",(()=>{"use strict";return e=>{var t={
-className:"variable",begin:"\\$"+e.IDENT_RE},i={className:"number",
-begin:"#([a-fA-F0-9]{6}|[a-fA-F0-9]{3})"};return{name:"Stylus",aliases:["styl"],
-case_insensitive:!1,keywords:"if else for in",
+hljs.registerLanguage("stylus",(()=>{"use strict"
+;const e=["a","abbr","address","article","aside","audio","b","blockquote","body","button","canvas","caption","cite","code","dd","del","details","dfn","div","dl","dt","em","fieldset","figcaption","figure","footer","form","h1","h2","h3","h4","h5","h6","header","hgroup","html","i","iframe","img","input","ins","kbd","label","legend","li","main","mark","menu","nav","object","ol","p","q","quote","samp","section","span","strong","summary","sup","table","tbody","td","textarea","tfoot","th","thead","time","tr","ul","var","video"],t=["any-hover","any-pointer","aspect-ratio","color","color-gamut","color-index","device-aspect-ratio","device-height","device-width","display-mode","forced-colors","grid","height","hover","inverted-colors","monochrome","orientation","overflow-block","overflow-inline","pointer","prefers-color-scheme","prefers-contrast","prefers-reduced-motion","prefers-reduced-transparency","resolution","scan","scripting","update","width","min-width","max-width","min-height","max-height"],i=["active","any-link","blank","checked","current","default","defined","dir","disabled","drop","empty","enabled","first","first-child","first-of-type","fullscreen","future","focus","focus-visible","focus-within","has","host","host-context","hover","indeterminate","in-range","invalid","is","lang","last-child","last-of-type","left","link","local-link","not","nth-child","nth-col","nth-last-child","nth-last-col","nth-last-of-type","nth-of-type","only-child","only-of-type","optional","out-of-range","past","placeholder-shown","read-only","read-write","required","right","root","scope","target","target-within","user-invalid","valid","visited","where"],o=["after","backdrop","before","cue","cue-region","first-letter","first-line","grammar-error","marker","part","placeholder","selection","slotted","spelling-error"],r=["align-content","align-items","align-self","animation","animation-delay","animation-direction","animation-duration","animation-fill-mode","animation-iteration-count","animation-name","animation-play-state","animation-timing-function","auto","backface-visibility","background","background-attachment","background-clip","background-color","background-image","background-origin","background-position","background-repeat","background-size","border","border-bottom","border-bottom-color","border-bottom-left-radius","border-bottom-right-radius","border-bottom-style","border-bottom-width","border-collapse","border-color","border-image","border-image-outset","border-image-repeat","border-image-slice","border-image-source","border-image-width","border-left","border-left-color","border-left-style","border-left-width","border-radius","border-right","border-right-color","border-right-style","border-right-width","border-spacing","border-style","border-top","border-top-color","border-top-left-radius","border-top-right-radius","border-top-style","border-top-width","border-width","bottom","box-decoration-break","box-shadow","box-sizing","break-after","break-before","break-inside","caption-side","clear","clip","clip-path","color","column-count","column-fill","column-gap","column-rule","column-rule-color","column-rule-style","column-rule-width","column-span","column-width","columns","content","counter-increment","counter-reset","cursor","direction","display","empty-cells","filter","flex","flex-basis","flex-direction","flex-flow","flex-grow","flex-shrink","flex-wrap","float","font","font-display","font-family","font-feature-settings","font-kerning","font-language-override","font-size","font-size-adjust","font-stretch","font-style","font-variant","font-variant-ligatures","font-variation-settings","font-weight","height","hyphens","icon","image-orientation","image-rendering","image-resolution","ime-mode","inherit","initial","justify-content","left","letter-spacing","line-height","list-style","list-style-image","list-style-position","list-style-type","margin","margin-bottom","margin-left","margin-right","margin-top","marks","mask","max-height","max-width","min-height","min-width","nav-down","nav-index","nav-left","nav-right","nav-up","none","normal","object-fit","object-position","opacity","order","orphans","outline","outline-color","outline-offset","outline-style","outline-width","overflow","overflow-wrap","overflow-x","overflow-y","padding","padding-bottom","padding-left","padding-right","padding-top","page-break-after","page-break-before","page-break-inside","perspective","perspective-origin","pointer-events","position","quotes","resize","right","src","tab-size","table-layout","text-align","text-align-last","text-decoration","text-decoration-color","text-decoration-line","text-decoration-style","text-indent","text-overflow","text-rendering","text-shadow","text-transform","text-underline-position","top","transform","transform-origin","transform-style","transition","transition-delay","transition-duration","transition-property","transition-timing-function","unicode-bidi","vertical-align","visibility","white-space","widows","width","word-break","word-spacing","word-wrap","z-index"].reverse()
+;return n=>{const a=(e=>({IMPORTANT:{className:"meta",begin:"!important"},
+HEXCOLOR:{className:"number",begin:"#([a-fA-F0-9]{6}|[a-fA-F0-9]{3})"},
+ATTRIBUTE_SELECTOR_MODE:{className:"selector-attr",begin:/\[/,end:/\]/,
+illegal:"$",contains:[e.APOS_STRING_MODE,e.QUOTE_STRING_MODE]}}))(n),s={
+className:"variable",begin:"\\$"+n.IDENT_RE},l="(?=[.\\s\\n[:,(])";return{
+name:"Stylus",aliases:["styl"],case_insensitive:!1,keywords:"if else for in",
 illegal:"(\\?|(\\bReturn\\b)|(\\bEnd\\b)|(\\bend\\b)|(\\bdef\\b)|;|#\\s|\\*\\s|===\\s|\\||%)",
-contains:[e.QUOTE_STRING_MODE,e.APOS_STRING_MODE,e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,i,{
-begin:"\\.[a-zA-Z][a-zA-Z0-9_-]*(?=[.\\s\\n[:,])",className:"selector-class"},{
-begin:"#[a-zA-Z][a-zA-Z0-9_-]*(?=[.\\s\\n[:,])",className:"selector-id"},{
-begin:"\\b(a|abbr|address|article|aside|audio|b|blockquote|body|button|canvas|caption|cite|code|dd|del|details|dfn|div|dl|dt|em|fieldset|figcaption|figure|footer|form|h1|h2|h3|h4|h5|h6|header|hgroup|html|i|iframe|img|input|ins|kbd|label|legend|li|mark|menu|nav|object|ol|p|q|quote|samp|section|span|strong|summary|sup|table|tbody|td|textarea|tfoot|th|thead|time|tr|ul|var|video)(?=[.\\s\\n[:,])",
-className:"selector-tag"},{
-begin:"&?:?:\\b(after|before|first-letter|first-line|active|first-child|focus|hover|lang|link|visited)(?=[.\\s\\n[:,])"
-},{
-begin:"@(charset|css|debug|extend|font-face|for|import|include|media|mixin|page|warn|while)\\b"
-},t,e.CSS_NUMBER_MODE,e.NUMBER_MODE,{className:"function",
+contains:[n.QUOTE_STRING_MODE,n.APOS_STRING_MODE,n.C_LINE_COMMENT_MODE,n.C_BLOCK_COMMENT_MODE,a.HEXCOLOR,{
+begin:"\\.[a-zA-Z][a-zA-Z0-9_-]*(?=[.\\s\\n[:,(])",className:"selector-class"},{
+begin:"#[a-zA-Z][a-zA-Z0-9_-]*(?=[.\\s\\n[:,(])",className:"selector-id"},{
+begin:"\\b("+e.join("|")+")"+l,className:"selector-tag"},{
+className:"selector-pseudo",begin:"&?:("+i.join("|")+")"+l},{
+className:"selector-pseudo",begin:"&?::("+o.join("|")+")"+l
+},a.ATTRIBUTE_SELECTOR_MODE,{className:"keyword",begin:/@media/,starts:{
+end:/[{;}]/,keywords:{$pattern:/[a-z-]+/,keyword:"and or not only",
+attribute:t.join(" ")},contains:[n.CSS_NUMBER_MODE]}},{className:"keyword",
+begin:"@((-(o|moz|ms|webkit)-)?(charset|css|debug|extend|font-face|for|import|include|keyframes|media|mixin|page|warn|while))\\b"
+},s,n.CSS_NUMBER_MODE,{className:"function",
 begin:"^[a-zA-Z][a-zA-Z0-9_-]*\\(.*\\)",illegal:"[\\n]",returnBegin:!0,
 contains:[{className:"title",begin:"\\b[a-zA-Z][a-zA-Z0-9_-]*"},{
 className:"params",begin:/\(/,end:/\)/,
-contains:[i,t,e.APOS_STRING_MODE,e.CSS_NUMBER_MODE,e.NUMBER_MODE,e.QUOTE_STRING_MODE]
-}]},{className:"attribute",
-begin:"\\b("+["align-content","align-items","align-self","animation","animation-delay","animation-direction","animation-duration","animation-fill-mode","animation-iteration-count","animation-name","animation-play-state","animation-timing-function","auto","backface-visibility","background","background-attachment","background-clip","background-color","background-image","background-origin","background-position","background-repeat","background-size","border","border-bottom","border-bottom-color","border-bottom-left-radius","border-bottom-right-radius","border-bottom-style","border-bottom-width","border-collapse","border-color","border-image","border-image-outset","border-image-repeat","border-image-slice","border-image-source","border-image-width","border-left","border-left-color","border-left-style","border-left-width","border-radius","border-right","border-right-color","border-right-style","border-right-width","border-spacing","border-style","border-top","border-top-color","border-top-left-radius","border-top-right-radius","border-top-style","border-top-width","border-width","bottom","box-decoration-break","box-shadow","box-sizing","break-after","break-before","break-inside","caption-side","clear","clip","clip-path","color","column-count","column-fill","column-gap","column-rule","column-rule-color","column-rule-style","column-rule-width","column-span","column-width","columns","content","counter-increment","counter-reset","cursor","direction","display","empty-cells","filter","flex","flex-basis","flex-direction","flex-flow","flex-grow","flex-shrink","flex-wrap","float","font","font-family","font-feature-settings","font-kerning","font-language-override","font-size","font-size-adjust","font-stretch","font-style","font-variant","font-variant-ligatures","font-weight","height","hyphens","icon","image-orientation","image-rendering","image-resolution","ime-mode","inherit","initial","justify-content","left","letter-spacing","line-height","list-style","list-style-image","list-style-position","list-style-type","margin","margin-bottom","margin-left","margin-right","margin-top","marks","mask","max-height","max-width","min-height","min-width","nav-down","nav-index","nav-left","nav-right","nav-up","none","normal","object-fit","object-position","opacity","order","orphans","outline","outline-color","outline-offset","outline-style","outline-width","overflow","overflow-wrap","overflow-x","overflow-y","padding","padding-bottom","padding-left","padding-right","padding-top","page-break-after","page-break-before","page-break-inside","perspective","perspective-origin","pointer-events","position","quotes","resize","right","tab-size","table-layout","text-align","text-align-last","text-decoration","text-decoration-color","text-decoration-line","text-decoration-style","text-indent","text-overflow","text-rendering","text-shadow","text-transform","text-underline-position","top","transform","transform-origin","transform-style","transition","transition-delay","transition-duration","transition-property","transition-timing-function","unicode-bidi","vertical-align","visibility","white-space","widows","width","word-break","word-spacing","word-wrap","z-index"].reverse().join("|")+")\\b",
-starts:{end:/;|$/,
-contains:[i,t,e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,e.CSS_NUMBER_MODE,e.NUMBER_MODE,e.C_BLOCK_COMMENT_MODE],
+contains:[a.HEXCOLOR,s,n.APOS_STRING_MODE,n.CSS_NUMBER_MODE,n.QUOTE_STRING_MODE]
+}]},{className:"attribute",begin:"\\b("+r.join("|")+")\\b",starts:{end:/;|$/,
+contains:[a.HEXCOLOR,s,n.APOS_STRING_MODE,n.QUOTE_STRING_MODE,n.CSS_NUMBER_MODE,n.C_BLOCK_COMMENT_MODE,a.IMPORTANT],
 illegal:/\./,relevance:0}}]}}})());
 hljs.registerLanguage("subunit",(()=>{"use strict";return s=>({name:"SubUnit",
 case_insensitive:!0,contains:[{className:"string",begin:"\\[\n(multipart)?",
@@ -3246,56 +3272,66 @@
 },{begin:"^progress(:?)(\\s+)?(pop|push)?"},{begin:"^tags:"},{begin:"^time:"}]}]
 })})());
 hljs.registerLanguage("swift",(()=>{"use strict";function e(e){
-return e?"string"==typeof e?e:e.source:null}function n(e){return i("(?=",e,")")}
-function i(...n){return n.map((n=>e(n))).join("")}function a(...n){
+return e?"string"==typeof e?e:e.source:null}function n(e){return a("(?=",e,")")}
+function a(...n){return n.map((n=>e(n))).join("")}function t(...n){
 return"("+n.map((n=>e(n))).join("|")+")"}
-const t=e=>i(/\b/,e,/\w$/.test(e)?/\b/:/\B/),u=["Protocol","Type"].map(t),s=["init","self"].map(t),r=["Any","Self"],o=["associatedtype",/as\?/,/as!/,"as","break","case","catch","class","continue","convenience","default","defer","deinit","didSet","do","dynamic","else","enum","extension","fallthrough","fileprivate(set)","fileprivate","final","for","func","get","guard","if","import","indirect","infix",/init\?/,/init!/,"inout","internal(set)","internal","in","is","lazy","let","mutating","nonmutating","open(set)","open","operator","optional","override","postfix","precedencegroup","prefix","private(set)","private","protocol","public(set)","public","repeat","required","rethrows","return","set","some","static","struct","subscript","super","switch","throws","throw",/try\?/,/try!/,"try","typealias","unowned(safe)","unowned(unsafe)","unowned","var","weak","where","while","willSet"],l=["false","nil","true"],c=["#colorLiteral","#column","#dsohandle","#else","#elseif","#endif","#error","#file","#fileID","#fileLiteral","#filePath","#function","#if","#imageLiteral","#keyPath","#line","#selector","#sourceLocation","#warn_unqualified_access","#warning"],b=["abs","all","any","assert","assertionFailure","debugPrint","dump","fatalError","getVaList","isKnownUniquelyReferenced","max","min","numericCast","pointwiseMax","pointwiseMin","precondition","preconditionFailure","print","readLine","repeatElement","sequence","stride","swap","swift_unboxFromSwiftValueWithType","transcode","type","unsafeBitCast","unsafeDowncast","withExtendedLifetime","withUnsafeMutablePointer","withUnsafePointer","withVaList","withoutActuallyEscaping","zip"],p=a(/[/=\-+!*%<>&|^~?]/,/[\u00A1-\u00A7]/,/[\u00A9\u00AB]/,/[\u00AC\u00AE]/,/[\u00B0\u00B1]/,/[\u00B6\u00BB\u00BF\u00D7\u00F7]/,/[\u2016-\u2017]/,/[\u2020-\u2027]/,/[\u2030-\u203E]/,/[\u2041-\u2053]/,/[\u2055-\u205E]/,/[\u2190-\u23FF]/,/[\u2500-\u2775]/,/[\u2794-\u2BFF]/,/[\u2E00-\u2E7F]/,/[\u3001-\u3003]/,/[\u3008-\u3020]/,/[\u3030]/),F=a(p,/[\u0300-\u036F]/,/[\u1DC0-\u1DFF]/,/[\u20D0-\u20FF]/,/[\uFE00-\uFE0F]/,/[\uFE20-\uFE2F]/),d=i(p,F,"*"),g=a(/[a-zA-Z_]/,/[\u00A8\u00AA\u00AD\u00AF\u00B2-\u00B5\u00B7-\u00BA]/,/[\u00BC-\u00BE\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u00FF]/,/[\u0100-\u02FF\u0370-\u167F\u1681-\u180D\u180F-\u1DBF]/,/[\u1E00-\u1FFF]/,/[\u200B-\u200D\u202A-\u202E\u203F-\u2040\u2054\u2060-\u206F]/,/[\u2070-\u20CF\u2100-\u218F\u2460-\u24FF\u2776-\u2793]/,/[\u2C00-\u2DFF\u2E80-\u2FFF]/,/[\u3004-\u3007\u3021-\u302F\u3031-\u303F\u3040-\uD7FF]/,/[\uF900-\uFD3D\uFD40-\uFDCF\uFDF0-\uFE1F\uFE30-\uFE44]/,/[\uFE47-\uFFFD]/),f=a(g,/\d/,/[\u0300-\u036F\u1DC0-\u1DFF\u20D0-\u20FF\uFE20-\uFE2F]/),m=i(g,f,"*"),w=i(/[A-Z]/,f,"*"),E=["autoclosure",i(/convention\(/,a("swift","block","c"),/\)/),"discardableResult","dynamicCallable","dynamicMemberLookup","escaping","frozen","GKInspectable","IBAction","IBDesignable","IBInspectable","IBOutlet","IBSegueAction","inlinable","main","nonobjc","NSApplicationMain","NSCopying","NSManaged",i(/objc\(/,m,/\)/),"objc","objcMembers","propertyWrapper","requires_stored_property_inits","testable","UIApplicationMain","unknown","usableFromInline"],y=["iOS","iOSApplicationExtension","macOS","macOSApplicationExtension","macCatalyst","macCatalystApplicationExtension","watchOS","watchOSApplicationExtension","tvOS","tvOSApplicationExtension","swift"]
-;return e=>{const p=e.COMMENT("/\\*","\\*/",{contains:["self"]}),g={
-className:"keyword",begin:i(/\./,n(a(...u,...s))),end:a(...u,...s),
-excludeBegin:!0},A={begin:i(/\./,a(...o)),relevance:0
-},C=o.filter((e=>"string"==typeof e)).concat(["_|0"]),v={variants:[{
+const i=e=>a(/\b/,e,/\w$/.test(e)?/\b/:/\B/),s=["Protocol","Type"].map(i),u=["init","self"].map(i),c=["Any","Self"],r=["associatedtype",/as\?/,/as!/,"as","break","case","catch","class","continue","convenience","default","defer","deinit","didSet","do","dynamic","else","enum","extension","fallthrough",/fileprivate\(set\)/,"fileprivate","final","for","func","get","guard","if","import","indirect","infix",/init\?/,/init!/,"inout",/internal\(set\)/,"internal","in","is","lazy","let","mutating","nonmutating",/open\(set\)/,"open","operator","optional","override","postfix","precedencegroup","prefix",/private\(set\)/,"private","protocol",/public\(set\)/,"public","repeat","required","rethrows","return","set","some","static","struct","subscript","super","switch","throws","throw",/try\?/,/try!/,"try","typealias",/unowned\(safe\)/,/unowned\(unsafe\)/,"unowned","var","weak","where","while","willSet"],o=["false","nil","true"],l=["assignment","associativity","higherThan","left","lowerThan","none","right"],m=["#colorLiteral","#column","#dsohandle","#else","#elseif","#endif","#error","#file","#fileID","#fileLiteral","#filePath","#function","#if","#imageLiteral","#keyPath","#line","#selector","#sourceLocation","#warn_unqualified_access","#warning"],d=["abs","all","any","assert","assertionFailure","debugPrint","dump","fatalError","getVaList","isKnownUniquelyReferenced","max","min","numericCast","pointwiseMax","pointwiseMin","precondition","preconditionFailure","print","readLine","repeatElement","sequence","stride","swap","swift_unboxFromSwiftValueWithType","transcode","type","unsafeBitCast","unsafeDowncast","withExtendedLifetime","withUnsafeMutablePointer","withUnsafePointer","withVaList","withoutActuallyEscaping","zip"],p=t(/[/=\-+!*%<>&|^~?]/,/[\u00A1-\u00A7]/,/[\u00A9\u00AB]/,/[\u00AC\u00AE]/,/[\u00B0\u00B1]/,/[\u00B6\u00BB\u00BF\u00D7\u00F7]/,/[\u2016-\u2017]/,/[\u2020-\u2027]/,/[\u2030-\u203E]/,/[\u2041-\u2053]/,/[\u2055-\u205E]/,/[\u2190-\u23FF]/,/[\u2500-\u2775]/,/[\u2794-\u2BFF]/,/[\u2E00-\u2E7F]/,/[\u3001-\u3003]/,/[\u3008-\u3020]/,/[\u3030]/),F=t(p,/[\u0300-\u036F]/,/[\u1DC0-\u1DFF]/,/[\u20D0-\u20FF]/,/[\uFE00-\uFE0F]/,/[\uFE20-\uFE2F]/),b=a(p,F,"*"),h=t(/[a-zA-Z_]/,/[\u00A8\u00AA\u00AD\u00AF\u00B2-\u00B5\u00B7-\u00BA]/,/[\u00BC-\u00BE\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u00FF]/,/[\u0100-\u02FF\u0370-\u167F\u1681-\u180D\u180F-\u1DBF]/,/[\u1E00-\u1FFF]/,/[\u200B-\u200D\u202A-\u202E\u203F-\u2040\u2054\u2060-\u206F]/,/[\u2070-\u20CF\u2100-\u218F\u2460-\u24FF\u2776-\u2793]/,/[\u2C00-\u2DFF\u2E80-\u2FFF]/,/[\u3004-\u3007\u3021-\u302F\u3031-\u303F\u3040-\uD7FF]/,/[\uF900-\uFD3D\uFD40-\uFDCF\uFDF0-\uFE1F\uFE30-\uFE44]/,/[\uFE47-\uFEFE\uFF00-\uFFFD]/),f=t(h,/\d/,/[\u0300-\u036F\u1DC0-\u1DFF\u20D0-\u20FF\uFE20-\uFE2F]/),w=a(h,f,"*"),y=a(/[A-Z]/,f,"*"),g=["autoclosure",a(/convention\(/,t("swift","block","c"),/\)/),"discardableResult","dynamicCallable","dynamicMemberLookup","escaping","frozen","GKInspectable","IBAction","IBDesignable","IBInspectable","IBOutlet","IBSegueAction","inlinable","main","nonobjc","NSApplicationMain","NSCopying","NSManaged",a(/objc\(/,w,/\)/),"objc","objcMembers","propertyWrapper","requires_stored_property_inits","testable","UIApplicationMain","unknown","usableFromInline"],E=["iOS","iOSApplicationExtension","macOS","macOSApplicationExtension","macCatalyst","macCatalystApplicationExtension","watchOS","watchOSApplicationExtension","tvOS","tvOSApplicationExtension","swift"]
+;return e=>{const p={match:/\s+/,relevance:0},h=e.COMMENT("/\\*","\\*/",{
+contains:["self"]}),v=[e.C_LINE_COMMENT_MODE,h],N={className:"keyword",
+begin:a(/\./,n(t(...s,...u))),end:t(...s,...u),excludeBegin:!0},A={
+match:a(/\./,t(...r)),relevance:0
+},C=r.filter((e=>"string"==typeof e)).concat(["_|0"]),_={variants:[{
 className:"keyword",
-begin:a(...o.filter((e=>"string"!=typeof e)).concat(r).map(t),...s)}]},_={
-$pattern:a(/\b\w+(\(\w+\))?/,/#\w+/),keyword:C.concat(c).join(" "),
-literal:l.join(" ")},N=[g,A,v],D=[{begin:i(/\./,a(...b)),relevance:0},{
-className:"built_in",begin:i(/\b/,a(...b),/(?=\()/)}],B={begin:/->/,relevance:0
-},M=[B,{className:"operator",relevance:0,variants:[{begin:d},{
-begin:`\\.(\\.|${F})+`}]}],h="([0-9a-fA-F]_*)+",S={className:"number",
-relevance:0,variants:[{
-begin:"\\b(([0-9]_*)+)(\\.(([0-9]_*)+))?([eE][+-]?(([0-9]_*)+))?\\b"},{
-begin:`\\b0x(${h})(\\.(${h}))?([pP][+-]?(([0-9]_*)+))?\\b`},{
-begin:/\b0o([0-7]_*)+\b/},{begin:/\b0b([01]_*)+\b/}]},O=(e="")=>({
-className:"subst",variants:[{begin:i(/\\/,e,/[0\\tnr"']/)},{
-begin:i(/\\/,e,/u\{[0-9a-fA-F]{1,8}\}/)}]}),x=(e="")=>({className:"subst",
-begin:i(/\\/,e,/[\t ]*(?:[\r\n]|\r\n)/)}),k=(e="")=>({className:"subst",
-label:"interpol",begin:i(/\\/,e,/\(/),end:/\)/}),L=(e="")=>({begin:i(e,/"""/),
-end:i(/"""/,e),contains:[O(e),x(e),k(e)]}),I=(e="")=>({begin:i(e,/"/),
-end:i(/"/,e),contains:[O(e),k(e)]}),$={className:"string",
-variants:[L(),L("#"),L("##"),L("###"),I(),I("#"),I("##"),I("###")]},T=[{
-begin:i(/`/,m,/`/)},{className:"variable",begin:/\$\d+/},{className:"variable",
-begin:`\\$${f}+`}],j=[{begin:/(@|#)available\(/,end:/\)/,keywords:{
-$pattern:/[@#]?\w+/,keyword:y.concat(["@available","#available"]).join(" ")},
-contains:[...M,S,$]},{className:"keyword",begin:i(/@/,a(...E))},{
-className:"meta",begin:i(/@/,m)}],K={begin:n(/\b[A-Z]/),relevance:0,contains:[{
+match:t(...r.filter((e=>"string"!=typeof e)).concat(c).map(i),...u)}]},D={
+$pattern:t(/\b\w+/,/#\w+/),keyword:C.concat(m),literal:o},B=[N,A,_],k=[{
+match:a(/\./,t(...d)),relevance:0},{className:"built_in",
+match:a(/\b/,t(...d),/(?=\()/)}],M={match:/->/,relevance:0},S=[M,{
+className:"operator",relevance:0,variants:[{match:b},{match:`\\.(\\.|${F})+`}]
+}],x="([0-9a-fA-F]_*)+",I={className:"number",relevance:0,variants:[{
+match:"\\b(([0-9]_*)+)(\\.(([0-9]_*)+))?([eE][+-]?(([0-9]_*)+))?\\b"},{
+match:`\\b0x(${x})(\\.(${x}))?([pP][+-]?(([0-9]_*)+))?\\b`},{
+match:/\b0o([0-7]_*)+\b/},{match:/\b0b([01]_*)+\b/}]},O=(e="")=>({
+className:"subst",variants:[{match:a(/\\/,e,/[0\\tnr"']/)},{
+match:a(/\\/,e,/u\{[0-9a-fA-F]{1,8}\}/)}]}),T=(e="")=>({className:"subst",
+match:a(/\\/,e,/[\t ]*(?:[\r\n]|\r\n)/)}),L=(e="")=>({className:"subst",
+label:"interpol",begin:a(/\\/,e,/\(/),end:/\)/}),P=(e="")=>({begin:a(e,/"""/),
+end:a(/"""/,e),contains:[O(e),T(e),L(e)]}),$=(e="")=>({begin:a(e,/"/),
+end:a(/"/,e),contains:[O(e),L(e)]}),K={className:"string",
+variants:[P(),P("#"),P("##"),P("###"),$(),$("#"),$("##"),$("###")]},j={
+match:a(/`/,w,/`/)},z=[j,{className:"variable",match:/\$\d+/},{
+className:"variable",match:`\\$${f}+`}],q=[{match:/(@|#)available/,
+className:"keyword",starts:{contains:[{begin:/\(/,end:/\)/,keywords:E,
+contains:[...S,I,K]}]}},{className:"keyword",match:a(/@/,t(...g))},{
+className:"meta",match:a(/@/,w)}],U={match:n(/\b[A-Z]/),relevance:0,contains:[{
 className:"type",
-begin:i(/(AV|CA|CF|CG|CI|CL|CM|CN|CT|MK|MP|MTK|MTL|NS|SCN|SK|UI|WK|XC)/,f,"+")
-},{className:"type",begin:w,relevance:0},{begin:/[?!]+/,relevance:0},{
-begin:/\.\.\./,relevance:0},{begin:i(/\s+&\s+/,n(w)),relevance:0}]},P={
-begin:/</,end:/>/,keywords:_,contains:[...N,...j,B,K]};K.contains.push(P)
-;for(const e of $.variants){const n=e.contains.find((e=>"interpol"===e.label))
-;n.keywords=_;const i=[...N,...D,...M,S,$,...T];n.contains=[...i,{begin:/\(/,
-end:/\)/,contains:["self",...i]}]}return{name:"Swift",keywords:_,
-contains:[e.C_LINE_COMMENT_MODE,p,{className:"function",beginKeywords:"func",
-end:/\{/,excludeEnd:!0,contains:[e.inherit(e.TITLE_MODE,{
-begin:/[A-Za-z$_][0-9A-Za-z$_]*/}),{begin:/</,end:/>/},{className:"params",
-begin:/\(/,end:/\)/,endsParent:!0,keywords:_,
-contains:["self",...N,S,$,e.C_BLOCK_COMMENT_MODE,{begin:":"}],illegal:/["']/}],
-illegal:/\[|%/},{className:"class",
-beginKeywords:"struct protocol class extension enum",end:"\\{",excludeEnd:!0,
-keywords:_,contains:[e.inherit(e.TITLE_MODE,{
-begin:/[A-Za-z$_][\u00C0-\u02B80-9A-Za-z$_]*/}),...N]},{beginKeywords:"import",
-end:/$/,contains:[e.C_LINE_COMMENT_MODE,p],relevance:0
-},...N,...D,...M,S,$,...T,...j,K]}}})());
+match:a(/(AV|CA|CF|CG|CI|CL|CM|CN|CT|MK|MP|MTK|MTL|NS|SCN|SK|UI|WK|XC)/,f,"+")
+},{className:"type",match:y,relevance:0},{match:/[?!]+/,relevance:0},{
+match:/\.\.\./,relevance:0},{match:a(/\s+&\s+/,n(y)),relevance:0}]},Z={
+begin:/</,end:/>/,keywords:D,contains:[...v,...B,...q,M,U]};U.contains.push(Z)
+;const G={begin:/\(/,end:/\)/,relevance:0,keywords:D,contains:["self",{
+match:a(w,/\s*:/),keywords:"_|0",relevance:0
+},...v,...B,...k,...S,I,K,...z,...q,U]},H={beginKeywords:"func",contains:[{
+className:"title",match:t(j.match,w,b),endsParent:!0,relevance:0},p]},R={
+begin:/</,end:/>/,contains:[...v,U]},V={begin:/\(/,end:/\)/,keywords:D,
+contains:[{begin:t(n(a(w,/\s*:/)),n(a(w,/\s+/,w,/\s*:/))),end:/:/,relevance:0,
+contains:[{className:"keyword",match:/\b_\b/},{className:"params",match:w}]
+},...v,...B,...S,I,K,...q,U,G],endsParent:!0,illegal:/["']/},W={
+className:"function",match:n(/\bfunc\b/),contains:[H,R,V,p],illegal:[/\[/,/%/]
+},X={className:"function",match:/\b(subscript|init[?!]?)\s*(?=[<(])/,keywords:{
+keyword:"subscript init init? init!",$pattern:/\w+[?!]?/},contains:[R,V,p],
+illegal:/\[|%/},J={beginKeywords:"operator",end:e.MATCH_NOTHING_RE,contains:[{
+className:"title",match:b,endsParent:!0,relevance:0}]},Q={
+beginKeywords:"precedencegroup",end:e.MATCH_NOTHING_RE,contains:[{
+className:"title",match:y,relevance:0},{begin:/{/,end:/}/,relevance:0,
+endsParent:!0,keywords:[...l,...o],contains:[U]}]};for(const e of K.variants){
+const n=e.contains.find((e=>"interpol"===e.label));n.keywords=D
+;const a=[...B,...k,...S,I,K,...z];n.contains=[...a,{begin:/\(/,end:/\)/,
+contains:["self",...a]}]}return{name:"Swift",keywords:D,contains:[...v,W,X,{
+className:"class",beginKeywords:"struct protocol class extension enum",
+end:"\\{",excludeEnd:!0,keywords:D,contains:[e.inherit(e.TITLE_MODE,{
+begin:/[A-Za-z$_][\u00C0-\u02B80-9A-Za-z$_]*/}),...B]},J,Q,{
+beginKeywords:"import",end:/$/,contains:[...v],relevance:0
+},...B,...k,...S,I,K,...z,...q,U,G]}}})());
 hljs.registerLanguage("taggerscript",(()=>{"use strict";return e=>({
 name:"Tagger Script",contains:[{className:"comment",begin:/\$noop\(/,end:/\)/,
 contains:[{begin:/\(/,end:/\)/,contains:["self",{begin:/\\./}]}],relevance:10},{
@@ -3308,12 +3344,12 @@
 },{begin:/\S+/}],contains:[e.BACKSLASH_ESCAPE,{className:"template-variable",
 variants:[{begin:/\{\{/,end:/\}\}/},{begin:/%\{/,end:/\}/}]}]},i=e.inherit(s,{
 variants:[{begin:/'/,end:/'/},{begin:/"/,end:/"/},{begin:/[^\s,{}[\]]+/}]}),l={
-end:",",endsWithParent:!0,excludeEnd:!0,contains:[],keywords:n,relevance:0},t={
-begin:/\{/,end:/\}/,contains:[l],illegal:"\\n",relevance:0},g={begin:"\\[",
-end:"\\]",contains:[l],illegal:"\\n",relevance:0},b=[{className:"attr",
-variants:[{begin:"\\w[\\w :\\/.-]*:(?=[ \t]|$)"},{
-begin:'"\\w[\\w :\\/.-]*":(?=[ \t]|$)'},{begin:"'\\w[\\w :\\/.-]*':(?=[ \t]|$)"
-}]},{className:"meta",begin:"^---\\s*$",relevance:10},{className:"string",
+end:",",endsWithParent:!0,excludeEnd:!0,keywords:n,relevance:0},t={begin:/\{/,
+end:/\}/,contains:[l],illegal:"\\n",relevance:0},g={begin:"\\[",end:"\\]",
+contains:[l],illegal:"\\n",relevance:0},b=[{className:"attr",variants:[{
+begin:"\\w[\\w :\\/.-]*:(?=[ \t]|$)"},{begin:'"\\w[\\w :\\/.-]*":(?=[ \t]|$)'},{
+begin:"'\\w[\\w :\\/.-]*':(?=[ \t]|$)"}]},{className:"meta",begin:"^---\\s*$",
+relevance:10},{className:"string",
 begin:"[\\|>]([1-9]?[+-])?[ ]*\\n( +)[^ ][^\\n]*\\n(\\2[^\\n]+\\n?)*"},{
 begin:"<%[%=-]?",end:"[%-]?%>",subLanguage:"ruby",excludeBegin:!0,excludeEnd:!0,
 relevance:0},{className:"type",begin:"!\\w+!"+a},{className:"type",
@@ -3389,22 +3425,21 @@
 begin:/\{\{/,end:/\}\}/,contains:["self",t,n]}]}}})());
 hljs.registerLanguage("typescript",(()=>{"use strict"
 ;const e="[A-Za-z$_][0-9A-Za-z$_]*",n=["as","in","of","if","for","while","finally","var","new","function","do","return","void","else","break","catch","instanceof","with","throw","case","default","try","switch","continue","typeof","delete","let","yield","const","class","debugger","async","await","static","import","from","export","extends"],a=["true","false","null","undefined","NaN","Infinity"],s=[].concat(["setInterval","setTimeout","clearInterval","clearTimeout","require","exports","eval","isFinite","isNaN","parseFloat","parseInt","decodeURI","decodeURIComponent","encodeURI","encodeURIComponent","escape","unescape"],["arguments","this","super","console","window","document","localStorage","module","global"],["Intl","DataView","Number","Math","Date","String","RegExp","Object","Function","Boolean","Error","Symbol","Set","Map","WeakSet","WeakMap","Proxy","Reflect","JSON","Promise","Float64Array","Int16Array","Int32Array","Int8Array","Uint16Array","Uint32Array","Float32Array","Array","Uint8Array","Uint8ClampedArray","ArrayBuffer"],["EvalError","InternalError","RangeError","ReferenceError","SyntaxError","TypeError","URIError"])
-;function t(e){return i("(?=",e,")")}function i(...e){return e.map((e=>{
-return(n=e)?"string"==typeof n?n:n.source:null;var n})).join("")}return r=>{
+;function t(e){return r("(?=",e,")")}function r(...e){return e.map((e=>{
+return(n=e)?"string"==typeof n?n:n.source:null;var n})).join("")}return i=>{
 const c={$pattern:e,
-keyword:n.concat(["type","namespace","typedef","interface","public","private","protected","implements","declare","abstract","readonly"]).join(" "),
-literal:a.join(" "),
-built_in:s.concat(["any","void","number","boolean","string","object","never","enum"]).join(" ")
+keyword:n.concat(["type","namespace","typedef","interface","public","private","protected","implements","declare","abstract","readonly"]),
+literal:a,
+built_in:s.concat(["any","void","number","boolean","string","object","never","enum"])
 },o={className:"meta",begin:"@[A-Za-z$_][0-9A-Za-z$_]*"},l=(e,n,a)=>{
 const s=e.contains.findIndex((e=>e.label===n))
 ;if(-1===s)throw Error("can not find mode to replace");e.contains.splice(s,1,a)
-},b=(r=>{const c=e,o={begin:/<[A-Za-z0-9\\._:-]+/,
+},b=(i=>{const c=e,o={begin:/<[A-Za-z0-9\\._:-]+/,
 end:/\/[A-Za-z0-9\\._:-]+>|\/>/,isTrulyOpeningTag:(e,n)=>{
 const a=e[0].length+e.index,s=e.input[a];"<"!==s?">"===s&&(((e,{after:n})=>{
 const a="</"+e[0].slice(1);return-1!==e.input.indexOf(a,n)})(e,{after:a
-})||n.ignoreMatch()):n.ignoreMatch()}},l={$pattern:e,keyword:n.join(" "),
-literal:a.join(" "),built_in:s.join(" ")
-},b="\\.([0-9](_?[0-9])*)",d="0|[1-9](_?[0-9])*|0[0-7]*[89][0-9]*",g={
+})||n.ignoreMatch()):n.ignoreMatch()}},l={$pattern:e,keyword:n,literal:a,
+built_in:s},b="\\.([0-9](_?[0-9])*)",d="0|[1-9](_?[0-9])*|0[0-7]*[89][0-9]*",g={
 className:"number",variants:[{
 begin:`(\\b(${d})((${b})|\\.)?|(${b}))[eE][+-]?([0-9](_?[0-9])*)\\b`},{
 begin:`\\b(${d})\\b((${b})\\b|\\.)?|(${b})\\b`},{
@@ -3413,53 +3448,53 @@
 begin:"\\b0[bB][0-1](_?[0-1])*n?\\b"},{begin:"\\b0[oO][0-7](_?[0-7])*n?\\b"},{
 begin:"\\b0[0-7]+n?\\b"}],relevance:0},u={className:"subst",begin:"\\$\\{",
 end:"\\}",keywords:l,contains:[]},E={begin:"html`",end:"",starts:{end:"`",
-returnEnd:!1,contains:[r.BACKSLASH_ESCAPE,u],subLanguage:"xml"}},m={
+returnEnd:!1,contains:[i.BACKSLASH_ESCAPE,u],subLanguage:"xml"}},m={
 begin:"css`",end:"",starts:{end:"`",returnEnd:!1,
-contains:[r.BACKSLASH_ESCAPE,u],subLanguage:"css"}},_={className:"string",
-begin:"`",end:"`",contains:[r.BACKSLASH_ESCAPE,u]},y={className:"comment",
-variants:[r.COMMENT(/\/\*\*(?!\/)/,"\\*/",{relevance:0,contains:[{
+contains:[i.BACKSLASH_ESCAPE,u],subLanguage:"css"}},_={className:"string",
+begin:"`",end:"`",contains:[i.BACKSLASH_ESCAPE,u]},y={className:"comment",
+variants:[i.COMMENT(/\/\*\*(?!\/)/,"\\*/",{relevance:0,contains:[{
 className:"doctag",begin:"@[A-Za-z]+",contains:[{className:"type",begin:"\\{",
 end:"\\}",relevance:0},{className:"variable",begin:c+"(?=\\s*(-)|$)",
 endsParent:!0,relevance:0},{begin:/(?=[^\n])\s/,relevance:0}]}]
-}),r.C_BLOCK_COMMENT_MODE,r.C_LINE_COMMENT_MODE]
-},p=[r.APOS_STRING_MODE,r.QUOTE_STRING_MODE,E,m,_,g,r.REGEXP_MODE]
+}),i.C_BLOCK_COMMENT_MODE,i.C_LINE_COMMENT_MODE]
+},p=[i.APOS_STRING_MODE,i.QUOTE_STRING_MODE,E,m,_,g,i.REGEXP_MODE]
 ;u.contains=p.concat({begin:/\{/,end:/\}/,keywords:l,contains:["self"].concat(p)
 });const N=[].concat(y,u.contains),f=N.concat([{begin:/\(/,end:/\)/,keywords:l,
 contains:["self"].concat(N)}]),A={className:"params",begin:/\(/,end:/\)/,
 excludeBegin:!0,excludeEnd:!0,keywords:l,contains:f};return{name:"Javascript",
 aliases:["js","jsx","mjs","cjs"],keywords:l,exports:{PARAMS_CONTAINS:f},
-illegal:/#(?![$_A-z])/,contains:[r.SHEBANG({label:"shebang",binary:"node",
+illegal:/#(?![$_A-z])/,contains:[i.SHEBANG({label:"shebang",binary:"node",
 relevance:5}),{label:"use_strict",className:"meta",relevance:10,
 begin:/^\s*['"]use (strict|asm)['"]/
-},r.APOS_STRING_MODE,r.QUOTE_STRING_MODE,E,m,_,y,g,{
-begin:i(/[{,\n]\s*/,t(i(/(((\/\/.*$)|(\/\*(\*[^/]|[^*])*\*\/))\s*)*/,c+"\\s*:"))),
+},i.APOS_STRING_MODE,i.QUOTE_STRING_MODE,E,m,_,y,g,{
+begin:r(/[{,\n]\s*/,t(r(/(((\/\/.*$)|(\/\*(\*[^/]|[^*])*\*\/))\s*)*/,c+"\\s*:"))),
 relevance:0,contains:[{className:"attr",begin:c+t("\\s*:"),relevance:0}]},{
-begin:"("+r.RE_STARTERS_RE+"|\\b(case|return|throw)\\b)\\s*",
-keywords:"return throw case",contains:[y,r.REGEXP_MODE,{className:"function",
-begin:"(\\([^()]*(\\([^()]*(\\([^()]*\\)[^()]*)*\\)[^()]*)*\\)|"+r.UNDERSCORE_IDENT_RE+")\\s*=>",
+begin:"("+i.RE_STARTERS_RE+"|\\b(case|return|throw)\\b)\\s*",
+keywords:"return throw case",contains:[y,i.REGEXP_MODE,{className:"function",
+begin:"(\\([^()]*(\\([^()]*(\\([^()]*\\)[^()]*)*\\)[^()]*)*\\)|"+i.UNDERSCORE_IDENT_RE+")\\s*=>",
 returnBegin:!0,end:"\\s*=>",contains:[{className:"params",variants:[{
-begin:r.UNDERSCORE_IDENT_RE,relevance:0},{className:null,begin:/\(\s*\)/,skip:!0
+begin:i.UNDERSCORE_IDENT_RE,relevance:0},{className:null,begin:/\(\s*\)/,skip:!0
 },{begin:/\(/,end:/\)/,excludeBegin:!0,excludeEnd:!0,keywords:l,contains:f}]}]
 },{begin:/,/,relevance:0},{className:"",begin:/\s/,end:/\s*/,skip:!0},{
 variants:[{begin:"<>",end:"</>"},{begin:o.begin,"on:begin":o.isTrulyOpeningTag,
 end:o.end}],subLanguage:"xml",contains:[{begin:o.begin,end:o.end,skip:!0,
 contains:["self"]}]}],relevance:0},{className:"function",
 beginKeywords:"function",end:/[{;]/,excludeEnd:!0,keywords:l,
-contains:["self",r.inherit(r.TITLE_MODE,{begin:c}),A],illegal:/%/},{
+contains:["self",i.inherit(i.TITLE_MODE,{begin:c}),A],illegal:/%/},{
 beginKeywords:"while if switch catch for"},{className:"function",
-begin:r.UNDERSCORE_IDENT_RE+"\\([^()]*(\\([^()]*(\\([^()]*\\)[^()]*)*\\)[^()]*)*\\)\\s*\\{",
-returnBegin:!0,contains:[A,r.inherit(r.TITLE_MODE,{begin:c})]},{variants:[{
+begin:i.UNDERSCORE_IDENT_RE+"\\([^()]*(\\([^()]*(\\([^()]*\\)[^()]*)*\\)[^()]*)*\\)\\s*\\{",
+returnBegin:!0,contains:[A,i.inherit(i.TITLE_MODE,{begin:c})]},{variants:[{
 begin:"\\."+c},{begin:"\\$"+c}],relevance:0},{className:"class",
 beginKeywords:"class",end:/[{;=]/,excludeEnd:!0,illegal:/[:"[\]]/,contains:[{
-beginKeywords:"extends"},r.UNDERSCORE_TITLE_MODE]},{begin:/\b(?=constructor)/,
-end:/[{;]/,excludeEnd:!0,contains:[r.inherit(r.TITLE_MODE,{begin:c}),"self",A]
+beginKeywords:"extends"},i.UNDERSCORE_TITLE_MODE]},{begin:/\b(?=constructor)/,
+end:/[{;]/,excludeEnd:!0,contains:[i.inherit(i.TITLE_MODE,{begin:c}),"self",A]
 },{begin:"(get|set)\\s+(?="+c+"\\()",end:/\{/,keywords:"get set",
-contains:[r.inherit(r.TITLE_MODE,{begin:c}),{begin:/\(\)/},A]},{begin:/\$[(.]/}]
-}})(r)
+contains:[i.inherit(i.TITLE_MODE,{begin:c}),{begin:/\(\)/},A]},{begin:/\$[(.]/}]
+}})(i)
 ;return Object.assign(b.keywords,c),b.exports.PARAMS_CONTAINS.push(o),b.contains=b.contains.concat([o,{
 beginKeywords:"namespace",end:/\{/,excludeEnd:!0},{beginKeywords:"interface",
 end:/\{/,excludeEnd:!0,keywords:"interface extends"
-}]),l(b,"shebang",r.SHEBANG()),l(b,"use_strict",{className:"meta",relevance:10,
+}]),l(b,"shebang",i.SHEBANG()),l(b,"use_strict",{className:"meta",relevance:10,
 begin:/^\s*['"]use strict['"]/
 }),b.contains.find((e=>"function"===e.className)).relevance=0,Object.assign(b,{
 name:"TypeScript",aliases:["ts"]}),b}})());
@@ -3505,9 +3540,9 @@
 const i="lcase month vartype instrrev ubound setlocale getobject rgb getref string weekdayname rnd dateadd monthname now day minute isarray cbool round formatcurrency conversions csng timevalue second year space abs clng timeserial fixs len asc isempty maths dateserial atn timer isobject filter weekday datevalue ccur isdate instr datediff formatdatetime replace isnull right sgn array snumeric log cdbl hex chr lbound msgbox ucase getlocale cos cdate cbyte rtrim join hour oct typename trim strcomp int createobject loadpicture tan formatnumber mid split  cint sin datepart ltrim sqr time derived eval date formatpercent exp inputbox left ascw chrw regexp cstr err".split(" ")
 ;return{name:"VBScript",aliases:["vbs"],case_insensitive:!0,keywords:{
 keyword:"call class const dim do loop erase execute executeglobal exit for each next function if then else on error option explicit new private property let get public randomize redim rem select case set stop sub while wend with end to elseif is or xor and not class_initialize class_terminate default preserve in me byval byref step resume goto",
-built_in:"server response request scriptengine scriptenginebuildversion scriptengineminorversion scriptenginemajorversion",
+built_in:["server","response","request","scriptengine","scriptenginebuildversion","scriptengineminorversion","scriptenginemajorversion"],
 literal:"true false null nothing empty"},illegal:"//",contains:[{
-begin:t(r(...i),"\\s*\\("),relevance:0,keywords:{built_in:i.join(" ")}
+begin:t(r(...i),"\\s*\\("),relevance:0,keywords:{built_in:i}
 },e.inherit(e.QUOTE_STRING_MODE,{contains:[{begin:'""'}]}),e.COMMENT(/'/,/$/,{
 relevance:0}),e.C_NUMBER_MODE]}}})());
 hljs.registerLanguage("vbscript-html",(()=>{"use strict";return e=>({
diff --git a/plugins/singleusergroup b/plugins/singleusergroup
index 3548ec8..09623b9 160000
--- a/plugins/singleusergroup
+++ b/plugins/singleusergroup
@@ -1 +1 @@
-Subproject commit 3548ec83c0c271a8768a6b03b0c28711521ed6cf
+Subproject commit 09623b9432d360060f88ae48fb3386e374ca29c0
diff --git a/polygerrit-ui/app/api/annotation.ts b/polygerrit-ui/app/api/annotation.ts
index c046b4f..e58bdd5 100644
--- a/polygerrit-ui/app/api/annotation.ts
+++ b/polygerrit-ui/app/api/annotation.ts
@@ -14,18 +14,14 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import {CoverageRange, Side} from './diff';
+import {CoverageRange, GrDiffLine, Side} from './diff';
 import {StyleObject} from './styles';
 
-export type AddLayerFunc = (ctx: AnnotationContext) => void;
-
-export type NotifyFunc = (
-  path: string,
-  start: number,
-  end: number,
-  side: Side
-) => void;
-
+/**
+ * This is the callback object that Gerrit calls once for each diff. Gerrit
+ * is then responsible for styling the diff according the returned array of
+ * CoverageRanges.
+ */
 export type CoverageProvider = (
   changeNum: number,
   path: string,
@@ -34,14 +30,35 @@
   /**
    * This is a ChangeInfo object as defined here:
    * https://gerrit-review.googlesource.com/Documentation/rest-api-changes.html#change-info
-   * We neither want to repeat it nor add a dependency on it here.
+   * At the moment we neither want to repeat it nor add a dependency on it here.
+   * TODO: Create a dedicated smaller object for exposing a change in the plugin
+   * API. Or allow the plugin API to depend on the entire rest API.
    */
   change?: unknown
 ) => Promise<Array<CoverageRange>>;
 
+export type AnnotationCallback = (ctx: AnnotationContext) => void;
+
+/**
+ * This object is passed to the plugin from Gerrit for each line of a diff that
+ * is being rendered. The plugin can then call annotateRange() or
+ * annotateLineNumber() to apply additional styles to the diff.
+ */
 export interface AnnotationContext {
+  /** Set by Gerrit and consumed by the plugin provided AddLayerFunc. */
+  readonly changeNum: number;
+  /** Set by Gerrit and consumed by the plugin provided AddLayerFunc. */
+  readonly path: string;
+  /** Set by Gerrit and consumed by the plugin provided AddLayerFunc. */
+  readonly line: GrDiffLine;
+  /** Set by Gerrit and consumed by the plugin provided AddLayerFunc. */
+  readonly contentEl: HTMLElement;
+  /** Set by Gerrit and consumed by the plugin provided AddLayerFunc. */
+  readonly lineNumberEl: HTMLElement;
+
   /**
-   * Method to add annotations to a content line.
+   * Can be called by the plugin to style a part of the given line of the
+   * context.
    *
    * @param offset The char offset where the update starts.
    * @param length The number of chars that the update covers.
@@ -56,7 +73,8 @@
   ): void;
 
   /**
-   * Method to add a CSS class to the line number TD element.
+   * Can be called by the plugin to style a part of the given line of the
+   * context.
    *
    * @param styleObject The style object for the range.
    * @param side The side of the update. ('left' or 'right')
@@ -66,23 +84,12 @@
 
 export interface AnnotationPluginApi {
   /**
-   * Register a function to call to apply annotations. Plugins should use
-   * GrAnnotationActionsContext.annotateRange and
-   * GrAnnotationActionsContext.annotateLineNumber to apply a CSS class to the
-   * line content or the line number.
-   *
-   * @param addLayerFunc The function
-   * that will be called when the AnnotationLayer is ready to annotate.
+   * Registers a callback for applying annotations. Gerrit will call the
+   * callback for every line of every file that is rendered and pass the
+   * information about the file and line as an AnnotationContext, which also
+   * provides methods for the plugin to style the content.
    */
-  addLayer(addLayerFunc: AddLayerFunc): AnnotationPluginApi;
-
-  /**
-   * The specified function will be called with a notify function for the plugin
-   * to call when it has all required data for annotation. Optional.
-   *
-   * @param notifyFunc See doc of the notify function below to see what it does.
-   */
-  addNotifier(notifyFunc: (n: NotifyFunc) => void): AnnotationPluginApi;
+  setLayer(callback: AnnotationCallback): AnnotationPluginApi;
 
   /**
    * The specified function will be called when a gr-diff component is built,
@@ -117,9 +124,10 @@
   ): AnnotationPluginApi;
 
   /**
-   * The notify function will call the listeners of all required annotation
-   * layers. Intended to be called by the plugin when all required data for
-   * annotation is available.
+   * For plugins notifying Gerrit about new annotations being ready to be
+   * applied for a certain range. Gerrit will then re-render the relevant lines
+   * of the diff and call back to the layer annotation function that was
+   * registered in addLayer().
    *
    * @param path The file path whose listeners should be notified.
    * @param start The line where the update starts.
diff --git a/polygerrit-ui/app/api/checks.ts b/polygerrit-ui/app/api/checks.ts
index 143fbd1..799d1f7 100644
--- a/polygerrit-ui/app/api/checks.ts
+++ b/polygerrit-ui/app/api/checks.ts
@@ -43,6 +43,12 @@
   fetchPollingIntervalSeconds: number;
 }
 
+export interface ChangeData {
+  changeNumber: number;
+  patchsetNumber: number;
+  repo: string;
+}
+
 export interface ChecksProvider {
   /**
    * Gerrit calls this method when ...
@@ -51,7 +57,7 @@
    * - ... while the tab is visible in a regular polling interval, see
    *       ChecksApiConfig.
    */
-  fetch(change: number, patchset: number): Promise<FetchResponse>;
+  fetch(change: ChangeData): Promise<FetchResponse>;
 }
 
 export interface FetchResponse {
@@ -224,10 +230,21 @@
 export type ActionCallback = (
   change: number,
   patchset: number,
+  /**
+   * Identical to 'attempt' property of CheckRun. Not set for top-level
+   * actions.
+   */
   attempt: number | undefined,
+  /**
+   * Identical to 'externalId' property of CheckRun. Not set for top-level
+   * actions.
+   */
   externalId: string | undefined,
-  /** Identical to 'checkName' property of CheckRun. */
-  checkName: string,
+  /**
+   * Identical to 'checkName' property of CheckRun. Not set for top-level
+   * actions.
+   */
+  checkName: string | undefined,
   /** Identical to 'name' property of Action entity. */
   actionName: string
 ) => Promise<ActionResult>;
diff --git a/polygerrit-ui/app/api/diff.ts b/polygerrit-ui/app/api/diff.ts
index 5d7125c..bd110c8 100644
--- a/polygerrit-ui/app/api/diff.ts
+++ b/polygerrit-ui/app/api/diff.ts
@@ -162,20 +162,20 @@
 export declare interface DiffPreferencesInfo {
   context: number;
   ignore_whitespace: IgnoreWhitespaceType;
-  intraline_difference?: boolean;
   line_length: number;
   show_line_endings?: boolean;
   show_tabs?: boolean;
   show_whitespace_errors?: boolean;
-  skip_uncommented?: boolean;
   syntax_highlighting?: boolean;
-  auto_hide_diff_table_header?: boolean;
   tab_size: number;
   font_size: number;
   // TODO: Missing documentation
   show_file_comment_button?: boolean;
-  // TODO: Missing documentation
-  theme?: string;
+}
+
+export declare interface RenderPreferences {
+  hide_left_side?: boolean;
+  disable_context_control_buttons?: boolean;
 }
 
 /**
diff --git a/polygerrit-ui/app/constants/constants.ts b/polygerrit-ui/app/constants/constants.ts
index 03d5000..be502f7 100644
--- a/polygerrit-ui/app/constants/constants.ts
+++ b/polygerrit-ui/app/constants/constants.ts
@@ -403,12 +403,10 @@
 // (Render mode being at least one of them).
 export function createDefaultDiffPrefs(): DiffPreferencesInfo {
   return {
-    auto_hide_diff_table_header: true,
     context: 10,
     cursor_blink_rate: 0,
     font_size: 12,
     ignore_whitespace: 'IGNORE_NONE',
-    intraline_difference: true,
     line_length: 100,
     line_wrapping: false,
     show_line_endings: true,
@@ -416,7 +414,6 @@
     show_whitespace_errors: true,
     syntax_highlighting: true,
     tab_size: 8,
-    theme: 'DEFAULT',
   };
 }
 
diff --git a/polygerrit-ui/app/elements/admin/gr-admin-group-list/gr-admin-group-list.ts b/polygerrit-ui/app/elements/admin/gr-admin-group-list/gr-admin-group-list.ts
index e77a5e8..4fa84eb 100644
--- a/polygerrit-ui/app/elements/admin/gr-admin-group-list/gr-admin-group-list.ts
+++ b/polygerrit-ui/app/elements/admin/gr-admin-group-list/gr-admin-group-list.ts
@@ -95,7 +95,7 @@
   @property({type: String})
   _filter = '';
 
-  private restApiService = appContext.restApiService;
+  private readonly restApiService = appContext.restApiService;
 
   /** @override */
   attached() {
diff --git a/polygerrit-ui/app/elements/admin/gr-admin-view/gr-admin-view.ts b/polygerrit-ui/app/elements/admin/gr-admin-view/gr-admin-view.ts
index 8a3e47f..bd2781f 100644
--- a/polygerrit-ui/app/elements/admin/gr-admin-view/gr-admin-view.ts
+++ b/polygerrit-ui/app/elements/admin/gr-admin-view/gr-admin-view.ts
@@ -173,7 +173,7 @@
   @property({type: Boolean})
   _showPluginList?: boolean;
 
-  private restApiService = appContext.restApiService;
+  private readonly restApiService = appContext.restApiService;
 
   private readonly jsAPI = appContext.jsApiService;
 
diff --git a/polygerrit-ui/app/elements/admin/gr-create-group-dialog/gr-create-group-dialog.ts b/polygerrit-ui/app/elements/admin/gr-create-group-dialog/gr-create-group-dialog.ts
index b68f720..e68f6c9 100644
--- a/polygerrit-ui/app/elements/admin/gr-create-group-dialog/gr-create-group-dialog.ts
+++ b/polygerrit-ui/app/elements/admin/gr-create-group-dialog/gr-create-group-dialog.ts
@@ -44,7 +44,7 @@
   @property({type: Boolean})
   _groupCreated = false;
 
-  private restApiService = appContext.restApiService;
+  private readonly restApiService = appContext.restApiService;
 
   _computeGroupUrl(groupId: string) {
     return getBaseUrl() + '/admin/groups/' + encodeURL(groupId, true);
diff --git a/polygerrit-ui/app/elements/admin/gr-create-pointer-dialog/gr-create-pointer-dialog.ts b/polygerrit-ui/app/elements/admin/gr-create-pointer-dialog/gr-create-pointer-dialog.ts
index d50d7c5..6334670 100644
--- a/polygerrit-ui/app/elements/admin/gr-create-pointer-dialog/gr-create-pointer-dialog.ts
+++ b/polygerrit-ui/app/elements/admin/gr-create-pointer-dialog/gr-create-pointer-dialog.ts
@@ -68,7 +68,7 @@
     this.hasNewItemName = !!name;
   }
 
-  private restApiService = appContext.restApiService;
+  private readonly restApiService = appContext.restApiService;
 
   handleCreateItem() {
     if (!this.repoName) {
diff --git a/polygerrit-ui/app/elements/admin/gr-create-repo-dialog/gr-create-repo-dialog.ts b/polygerrit-ui/app/elements/admin/gr-create-repo-dialog/gr-create-repo-dialog.ts
index c9fd241..f708485 100644
--- a/polygerrit-ui/app/elements/admin/gr-create-repo-dialog/gr-create-repo-dialog.ts
+++ b/polygerrit-ui/app/elements/admin/gr-create-repo-dialog/gr-create-repo-dialog.ts
@@ -70,7 +70,7 @@
   @property({type: Object})
   _queryGroups: AutocompleteQuery;
 
-  private restApiService = appContext.restApiService;
+  private readonly restApiService = appContext.restApiService;
 
   constructor() {
     super();
diff --git a/polygerrit-ui/app/elements/admin/gr-group-audit-log/gr-group-audit-log.ts b/polygerrit-ui/app/elements/admin/gr-group-audit-log/gr-group-audit-log.ts
index bc4750f..201b340 100644
--- a/polygerrit-ui/app/elements/admin/gr-group-audit-log/gr-group-audit-log.ts
+++ b/polygerrit-ui/app/elements/admin/gr-group-audit-log/gr-group-audit-log.ts
@@ -55,7 +55,7 @@
   @property({type: Boolean})
   _loading = true;
 
-  private restApiService = appContext.restApiService;
+  private readonly restApiService = appContext.restApiService;
 
   /** @override */
   attached() {
diff --git a/polygerrit-ui/app/elements/admin/gr-group-members/gr-group-members.ts b/polygerrit-ui/app/elements/admin/gr-group-members/gr-group-members.ts
index 451139c..54f58c2 100644
--- a/polygerrit-ui/app/elements/admin/gr-group-members/gr-group-members.ts
+++ b/polygerrit-ui/app/elements/admin/gr-group-members/gr-group-members.ts
@@ -117,7 +117,7 @@
 
   _itemId?: AccountId | GroupId;
 
-  private restApiService = appContext.restApiService;
+  private readonly restApiService = appContext.restApiService;
 
   constructor() {
     super();
diff --git a/polygerrit-ui/app/elements/admin/gr-group/gr-group.ts b/polygerrit-ui/app/elements/admin/gr-group/gr-group.ts
index 6f00445..84daef8 100644
--- a/polygerrit-ui/app/elements/admin/gr-group/gr-group.ts
+++ b/polygerrit-ui/app/elements/admin/gr-group/gr-group.ts
@@ -126,7 +126,7 @@
   @property({type: Boolean})
   _isAdmin = false;
 
-  private restApiService = appContext.restApiService;
+  private readonly restApiService = appContext.restApiService;
 
   constructor() {
     super();
diff --git a/polygerrit-ui/app/elements/admin/gr-permission/gr-permission.ts b/polygerrit-ui/app/elements/admin/gr-permission/gr-permission.ts
index 5702bfb..85ba052 100644
--- a/polygerrit-ui/app/elements/admin/gr-permission/gr-permission.ts
+++ b/polygerrit-ui/app/elements/admin/gr-permission/gr-permission.ts
@@ -140,7 +140,7 @@
   @property({type: Boolean})
   _originalExclusiveValue?: boolean;
 
-  private restApiService = appContext.restApiService;
+  private readonly restApiService = appContext.restApiService;
 
   constructor() {
     super();
diff --git a/polygerrit-ui/app/elements/admin/gr-plugin-list/gr-plugin-list.ts b/polygerrit-ui/app/elements/admin/gr-plugin-list/gr-plugin-list.ts
index fc1ceee..f5e9a92 100644
--- a/polygerrit-ui/app/elements/admin/gr-plugin-list/gr-plugin-list.ts
+++ b/polygerrit-ui/app/elements/admin/gr-plugin-list/gr-plugin-list.ts
@@ -77,7 +77,7 @@
   @property({type: String})
   _filter = '';
 
-  private restApiService = appContext.restApiService;
+  private readonly restApiService = appContext.restApiService;
 
   /** @override */
   attached() {
diff --git a/polygerrit-ui/app/elements/admin/gr-repo-access/gr-repo-access.ts b/polygerrit-ui/app/elements/admin/gr-repo-access/gr-repo-access.ts
index 935d091..26ff799 100644
--- a/polygerrit-ui/app/elements/admin/gr-repo-access/gr-repo-access.ts
+++ b/polygerrit-ui/app/elements/admin/gr-repo-access/gr-repo-access.ts
@@ -120,7 +120,7 @@
 
   private _originalInheritsFrom?: ProjectInfo | null;
 
-  private restApiService = appContext.restApiService;
+  private readonly restApiService = appContext.restApiService;
 
   constructor() {
     super();
diff --git a/polygerrit-ui/app/elements/admin/gr-repo-commands/gr-repo-commands.ts b/polygerrit-ui/app/elements/admin/gr-repo-commands/gr-repo-commands.ts
index 14cf234..f209729 100644
--- a/polygerrit-ui/app/elements/admin/gr-repo-commands/gr-repo-commands.ts
+++ b/polygerrit-ui/app/elements/admin/gr-repo-commands/gr-repo-commands.ts
@@ -91,7 +91,7 @@
   @property({type: Boolean})
   _runningGC = false;
 
-  private restApiService = appContext.restApiService;
+  private readonly restApiService = appContext.restApiService;
 
   /** @override */
   attached() {
diff --git a/polygerrit-ui/app/elements/admin/gr-repo-dashboards/gr-repo-dashboards.ts b/polygerrit-ui/app/elements/admin/gr-repo-dashboards/gr-repo-dashboards.ts
index 5f6cd29..7b3c7fb 100644
--- a/polygerrit-ui/app/elements/admin/gr-repo-dashboards/gr-repo-dashboards.ts
+++ b/polygerrit-ui/app/elements/admin/gr-repo-dashboards/gr-repo-dashboards.ts
@@ -50,7 +50,7 @@
   @property({type: Array})
   _dashboards?: DashboardRef[];
 
-  private restApiService = appContext.restApiService;
+  private readonly restApiService = appContext.restApiService;
 
   _repoChanged(repo?: RepoName) {
     this._loading = true;
diff --git a/polygerrit-ui/app/elements/admin/gr-repo-list/gr-repo-list.ts b/polygerrit-ui/app/elements/admin/gr-repo-list/gr-repo-list.ts
index c1c8475..d6aa0e6 100644
--- a/polygerrit-ui/app/elements/admin/gr-repo-list/gr-repo-list.ts
+++ b/polygerrit-ui/app/elements/admin/gr-repo-list/gr-repo-list.ts
@@ -88,7 +88,7 @@
     return this.computeShownItems(this._repos);
   }
 
-  private restApiService = appContext.restApiService;
+  private readonly restApiService = appContext.restApiService;
 
   /** @override */
   attached() {
diff --git a/polygerrit-ui/app/elements/admin/gr-repo/gr-repo.ts b/polygerrit-ui/app/elements/admin/gr-repo/gr-repo.ts
index bcc6039..b6881ff 100644
--- a/polygerrit-ui/app/elements/admin/gr-repo/gr-repo.ts
+++ b/polygerrit-ui/app/elements/admin/gr-repo/gr-repo.ts
@@ -141,7 +141,7 @@
   @property({type: Array})
   weblinks: WebLinkInfo[] = [];
 
-  private restApiService = appContext.restApiService;
+  private readonly restApiService = appContext.restApiService;
 
   /** @override */
   attached() {
diff --git a/polygerrit-ui/app/elements/change-list/gr-change-list-view/gr-change-list-view.ts b/polygerrit-ui/app/elements/change-list/gr-change-list-view/gr-change-list-view.ts
index 441d514..42741fa 100644
--- a/polygerrit-ui/app/elements/change-list/gr-change-list-view/gr-change-list-view.ts
+++ b/polygerrit-ui/app/elements/change-list/gr-change-list-view/gr-change-list-view.ts
@@ -111,7 +111,7 @@
   @property({type: String})
   _repo: string | null = null;
 
-  private restApiService = appContext.restApiService;
+  private readonly restApiService = appContext.restApiService;
 
   /** @override */
   created() {
diff --git a/polygerrit-ui/app/elements/change-list/gr-change-list/gr-change-list.ts b/polygerrit-ui/app/elements/change-list/gr-change-list/gr-change-list.ts
index f36df84..f26cd46 100644
--- a/polygerrit-ui/app/elements/change-list/gr-change-list/gr-change-list.ts
+++ b/polygerrit-ui/app/elements/change-list/gr-change-list/gr-change-list.ts
@@ -146,7 +146,7 @@
 
   flagsService = appContext.flagsService;
 
-  private restApiService = appContext.restApiService;
+  private readonly restApiService = appContext.restApiService;
 
   keyboardShortcuts() {
     return {
diff --git a/polygerrit-ui/app/elements/change-list/gr-dashboard-view/gr-dashboard-view.ts b/polygerrit-ui/app/elements/change-list/gr-dashboard-view/gr-dashboard-view.ts
index 841ee6e..9240905 100644
--- a/polygerrit-ui/app/elements/change-list/gr-dashboard-view/gr-dashboard-view.ts
+++ b/polygerrit-ui/app/elements/change-list/gr-dashboard-view/gr-dashboard-view.ts
@@ -121,7 +121,7 @@
 
   private reporting = appContext.reportingService;
 
-  private restApiService = appContext.restApiService;
+  private readonly restApiService = appContext.restApiService;
 
   private lastVisibleTimestampMs = 0;
 
diff --git a/polygerrit-ui/app/elements/change-list/gr-user-header/gr-user-header.ts b/polygerrit-ui/app/elements/change-list/gr-user-header/gr-user-header.ts
index 10f65a5..cfee0cd 100644
--- a/polygerrit-ui/app/elements/change-list/gr-user-header/gr-user-header.ts
+++ b/polygerrit-ui/app/elements/change-list/gr-user-header/gr-user-header.ts
@@ -54,7 +54,7 @@
   @property({type: String})
   _status = '';
 
-  private restApiService = appContext.restApiService;
+  private readonly restApiService = appContext.restApiService;
 
   _accountChanged(userId?: AccountId) {
     if (!userId) {
diff --git a/polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions.ts b/polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions.ts
index 89ca4ef..c3858d8 100644
--- a/polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions.ts
+++ b/polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions.ts
@@ -553,7 +553,7 @@
   @property({type: Object})
   _config?: ServerInfo;
 
-  private restApiService = appContext.restApiService;
+  private readonly restApiService = appContext.restApiService;
 
   /** @override */
   created() {
diff --git a/polygerrit-ui/app/elements/change/gr-change-metadata/gr-change-metadata_html.ts b/polygerrit-ui/app/elements/change/gr-change-metadata/gr-change-metadata_html.ts
index cd17271..c5c73c5 100644
--- a/polygerrit-ui/app/elements/change/gr-change-metadata/gr-change-metadata_html.ts
+++ b/polygerrit-ui/app/elements/change/gr-change-metadata/gr-change-metadata_html.ts
@@ -115,6 +115,11 @@
          commit message box. Their top border should be on the same line. */
       margin-bottom: var(--spacing-s);
     }
+    .show-all-button iron-icon {
+      color: inherit;
+      --iron-icon-height: 18px;
+      --iron-icon-width: 18px;
+    }
   </style>
   <gr-external-style id="externalStyle" name="change-metadata">
     <template is="dom-if" if="[[_isNewChangeSummaryUiEnabled]]">
diff --git a/polygerrit-ui/app/elements/change/gr-change-summary/gr-change-summary.ts b/polygerrit-ui/app/elements/change/gr-change-summary/gr-change-summary.ts
index 021d7c4..4af51a9 100644
--- a/polygerrit-ui/app/elements/change/gr-change-summary/gr-change-summary.ts
+++ b/polygerrit-ui/app/elements/change/gr-change-summary/gr-change-summary.ts
@@ -40,6 +40,7 @@
   iconForCategory,
   iconForStatus,
   isRunning,
+  isRunningOrHasCompleted,
 } from '../../../services/checks/checks-util';
 import {ChangeComments} from '../../diff/gr-comment-api/gr-comment-api';
 import {
@@ -55,6 +56,7 @@
 import {notUndefined} from '../../../types/types';
 import {uniqueDefinedAvatar} from '../../../utils/account-util';
 import {PrimaryTab} from '../../../constants/constants';
+import {CommentTabState} from '../../../types/events';
 
 export enum SummaryChipStyles {
   INFO = 'info',
@@ -71,6 +73,9 @@
   @property()
   styleType = SummaryChipStyles.UNDEFINED;
 
+  @property()
+  category?: CommentTabState;
+
   static get styles() {
     return [
       sharedStyles,
@@ -132,7 +137,9 @@
   private handleClick(e: MouseEvent) {
     e.stopPropagation();
     e.preventDefault();
-    fireShowPrimaryTab(this, PrimaryTab.COMMENT_THREADS);
+    fireShowPrimaryTab(this, PrimaryTab.COMMENT_THREADS, true, {
+      commentTab: this.category,
+    });
   }
 }
 
@@ -282,6 +289,9 @@
         :host.new-change-summary-true {
           margin-bottom: var(--spacing-m);
         }
+        .zeroState {
+          color: var(--primary-text-color);
+        }
         td.key {
           padding-right: var(--spacing-l);
           padding-bottom: var(--spacing-m);
@@ -306,6 +316,11 @@
     ];
   }
 
+  renderChecksZeroState() {
+    if (this.runs.some(isRunningOrHasCompleted)) return;
+    return html`<span class="font-small zeroState">No results</span>`;
+  }
+
   renderChecksChipForCategory(category: Category) {
     const icon = iconForCategory(category);
     const runs = this.runs.filter(run => hasResultsOf(run, category));
@@ -389,7 +404,7 @@
           <tr ?hidden=${!this.showChecksSummary}>
             <td class="key">Checks</td>
             <td class="value">
-              ${this.renderChecksChipForCategory(
+              ${this.renderChecksZeroState()}${this.renderChecksChipForCategory(
                 Category.ERROR
               )}${this.renderChecksChipForCategory(
                 Category.WARNING
@@ -404,21 +419,23 @@
           <tr ?hidden=${!this.newChangeSummaryUiEnabled}>
             <td class="key">Comments</td>
             <td class="value">
-              <gr-summary-chip
-                styleType=${SummaryChipStyles.INFO}
+              <span
+                class="font-small zeroState"
                 ?hidden=${!!countResolvedComments ||
                 !!draftCount ||
                 !!countUnresolvedComments}
               >
-                No Comments</gr-summary-chip
+                No Comments</span
               ><gr-summary-chip
                 styleType=${SummaryChipStyles.WARNING}
+                category=${CommentTabState.DRAFTS}
                 icon="edit"
                 ?hidden=${!draftCount}
               >
                 ${pluralize(draftCount, 'draft')}</gr-summary-chip
               ><gr-summary-chip
                 styleType=${SummaryChipStyles.WARNING}
+                category=${CommentTabState.UNRESOLVED}
                 icon="message"
                 ?hidden=${!countUnresolvedComments}
               >
@@ -433,6 +450,7 @@
                 ${countUnresolvedComments} unresolved</gr-summary-chip
               ><gr-summary-chip
                 styleType=${SummaryChipStyles.CHECK}
+                category=${CommentTabState.SHOW_ALL}
                 icon="markChatRead"
                 ?hidden=${!countResolvedComments}
                 >${countResolvedComments} resolved</gr-summary-chip
diff --git a/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.ts b/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.ts
index 61006f9..304939c 100644
--- a/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.ts
+++ b/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.ts
@@ -108,6 +108,9 @@
   QuickLabelInfo,
   ApprovalInfo,
   ElementPropertyDeepChange,
+  ChangeId,
+  RelatedChangeAndCommitInfo,
+  RelatedChangesInfo,
 } from '../../../types/common';
 import {DiffPreferencesInfo} from '../../../types/diff';
 import {GrReplyDialog, FocusTarget} from '../gr-reply-dialog/gr-reply-dialog';
@@ -118,7 +121,7 @@
   GrCommentApi,
   ChangeComments,
 } from '../../diff/gr-comment-api/gr-comment-api';
-import {hasOwnProperty} from '../../../utils/common-util';
+import {assertIsDefined, hasOwnProperty} from '../../../utils/common-util';
 import {GrEditControls} from '../../edit/gr-edit-controls/gr-edit-controls';
 import {
   CommentThread,
@@ -152,11 +155,13 @@
   ShowAlertEventDetail,
   SwitchTabEvent,
   ThreadListModifiedEvent,
+  TabState,
 } from '../../../types/events';
 import {GrButton} from '../../shared/gr-button/gr-button';
 import {GrMessagesList} from '../gr-messages-list/gr-messages-list';
 import {GrThreadList} from '../gr-thread-list/gr-thread-list';
 import {
+  EventType,
   fireAlert,
   fireEvent,
   firePageError,
@@ -555,6 +560,9 @@
   @property({type: Boolean})
   _isNewChangeSummaryUiEnabled = false;
 
+  @property({type: String})
+  _tabState?: TabState;
+
   restApiService = appContext.restApiService;
 
   checksService = appContext.checksService;
@@ -571,7 +579,6 @@
       [Shortcut.UP_TO_DASHBOARD]: '_handleUpToDashboard',
       [Shortcut.EXPAND_ALL_MESSAGES]: '_handleExpandAllMessages',
       [Shortcut.COLLAPSE_ALL_MESSAGES]: '_handleCollapseAllMessages',
-      [Shortcut.EXPAND_ALL_DIFF_CONTEXT]: '_expandAllDiffs',
       [Shortcut.OPEN_DIFF_PREFS]: '_handleOpenDiffPrefsShortcut',
       [Shortcut.EDIT_TOPIC]: '_handleEditTopic',
       [Shortcut.DIFF_AGAINST_BASE]: '_handleDiffAgainstBase',
@@ -694,7 +701,7 @@
     this.listen(window, 'scroll', '_handleScroll');
     this.listen(document, 'visibilitychange', '_handleVisibilityChange');
 
-    this.addEventListener('show-primary-tab', e =>
+    this.addEventListener(EventType.SHOW_PRIMARY_TAB, e =>
       this._setActivePrimaryTab(e)
     );
     this.addEventListener('show-secondary-tab', e =>
@@ -853,6 +860,7 @@
         this._selectedTabPluginHeader = '';
       }
     }
+    this._tabState = e.detail.tabState;
   }
 
   /**
@@ -878,7 +886,7 @@
   }
 
   _handleCommitMessageSave(e: EditableContentSaveEvent) {
-    if (!this._change) throw new Error('missing required change property');
+    assertIsDefined(this._change, '_change');
     if (!this._changeNum)
       throw new Error('missing required changeNum property');
     // Trim trailing whitespace from each line.
@@ -1419,7 +1427,7 @@
   }
 
   _handleMessageAnchorTap(e: CustomEvent<{id: string}>) {
-    if (!this._change) throw new Error('missing required change property');
+    assertIsDefined(this._change, '_change');
     if (!this._patchRange)
       throw new Error('missing required _patchRange property');
     const hash = MSG_PREFIX + e.detail.id;
@@ -1701,7 +1709,7 @@
     if (this.shouldSuppressKeyboardShortcut(e)) {
       return;
     }
-    if (!this._change) throw new Error('missing required change property');
+    assertIsDefined(this._change, '_change');
     if (!this._patchRange)
       throw new Error('missing required _patchRange property');
     if (this._patchRange.basePatchNum === ParentPatchSetNum) {
@@ -1715,7 +1723,7 @@
     if (this.shouldSuppressKeyboardShortcut(e)) {
       return;
     }
-    if (!this._change) throw new Error('missing required change property');
+    assertIsDefined(this._change, '_change');
     if (!this._patchRange)
       throw new Error('missing required _patchRange property');
     if (this._patchRange.basePatchNum === ParentPatchSetNum) {
@@ -1729,7 +1737,7 @@
     if (this.shouldSuppressKeyboardShortcut(e)) {
       return;
     }
-    if (!this._change) throw new Error('missing required change property');
+    assertIsDefined(this._change, '_change');
     if (!this._patchRange)
       throw new Error('missing required _patchRange property');
     const latestPatchNum = computeLatestPatchNum(this._allPatchSets);
@@ -1748,7 +1756,7 @@
     if (this.shouldSuppressKeyboardShortcut(e)) {
       return;
     }
-    if (!this._change) throw new Error('missing required change property');
+    assertIsDefined(this._change, '_change');
     const latestPatchNum = computeLatestPatchNum(this._allPatchSets);
     if (!this._patchRange)
       throw new Error('missing required _patchRange property');
@@ -1767,7 +1775,7 @@
     if (this.shouldSuppressKeyboardShortcut(e)) {
       return;
     }
-    if (!this._change) throw new Error('missing required change property');
+    assertIsDefined(this._change, '_change');
     if (!this._patchRange)
       throw new Error('missing required _patchRange property');
     const latestPatchNum = computeLatestPatchNum(this._allPatchSets);
@@ -1913,7 +1921,7 @@
   }
 
   _getProjectConfig() {
-    if (!this._change) throw new Error('missing required change property');
+    assertIsDefined(this._change, '_change');
     return this.restApiService
       .getProjectConfig(this._change.project)
       .then(config => {
@@ -2288,7 +2296,26 @@
       const relatedChangesLoaded = coreDataPromise.then(() => {
         this.getRelatedChangesList()?.reload();
         if (this._isNewChangeSummaryUiEnabled) {
-          this.getRelatedChangesListExperimental()?.reload();
+          let relatedChangesPromise:
+            | Promise<RelatedChangesInfo | undefined>
+            | undefined;
+          const patchNum = this._computeLatestPatchNum(this._allPatchSets);
+          if (this._change && patchNum) {
+            relatedChangesPromise = this.restApiService
+              .getRelatedChanges(this._change._number, patchNum)
+              .then(response => {
+                if (this._change && response) {
+                  this.hasParent = this._calculateHasParent(
+                    this._change.change_id,
+                    response.changes
+                  );
+                }
+                return response;
+              });
+          }
+          this.getRelatedChangesListExperimental()?.reload(
+            relatedChangesPromise
+          );
         }
       });
       allDataPromises.push(relatedChangesLoaded);
@@ -2305,11 +2332,26 @@
   }
 
   /**
+   * Determines whether or not the given change has a parent change. If there
+   * is a relation chain, and the change id is not the last item of the
+   * relation chain, there is a parent.
+   */
+  _calculateHasParent(
+    currentChangeId: ChangeId,
+    relatedChanges: RelatedChangeAndCommitInfo[]
+  ) {
+    return (
+      relatedChanges.length > 0 &&
+      relatedChanges[relatedChanges.length - 1].change_id !== currentChangeId
+    );
+  }
+
+  /**
    * Kicks off requests for resources that rely on the patch range
    * (`this._patchRange`) being defined.
    */
   _reloadPatchNumDependentResources(rightPatchNumChanged?: boolean) {
-    if (!this._changeNum) throw new Error('missing changeNum');
+    assertIsDefined(this._changeNum, '_changeNum');
     if (!this._patchRange?.patchNum) throw new Error('missing patchNum');
     const promises = [this._getCommitInfo(), this.$.fileList.reload()];
     if (rightPatchNumChanged)
@@ -2549,7 +2591,7 @@
     }
 
     this._updateCheckTimerHandle = this.async(() => {
-      if (!this._change) throw new Error('missing required change property');
+      assertIsDefined(this._change, '_change');
       const change = this._change;
       fetchChangeUpdates(change, this.restApiService).then(result => {
         let toastMessage = null;
@@ -2657,7 +2699,7 @@
       GrEditControls
     >('#editControls');
     if (!controls) throw new Error('Missing edit controls');
-    if (!this._change) throw new Error('missing required change property');
+    assertIsDefined(this._change, '_change');
     if (!this._patchRange)
       throw new Error('missing required _patchRange property');
     const path = e.detail.path;
@@ -2692,7 +2734,7 @@
     if (!this._selectedRevision) {
       return;
     }
-    if (!this._change) throw new Error('missing required change property');
+    assertIsDefined(this._change, '_change');
 
     let patchNum: PatchSetNum;
     if (patchNumStr === 'edit') {
@@ -2740,7 +2782,7 @@
   }
 
   _handleStopEditTap() {
-    if (!this._change) throw new Error('missing required change property');
+    assertIsDefined(this._change, '_change');
     if (!this._patchRange)
       throw new Error('missing required _patchRange property');
     GerritNav.navigateToChange(this._change, this._patchRange.patchNum);
@@ -2775,7 +2817,7 @@
   /**
    * Wrapper for using in the element template and computed properties
    */
-  _computeLatestPatchNum(allPatchSets: PatchSet[]) {
+  _computeLatestPatchNum(allPatchSets?: PatchSet[]) {
     return computeLatestPatchNum(allPatchSets);
   }
 
diff --git a/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view_html.ts b/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view_html.ts
index 81aff90..f3fb860 100644
--- a/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view_html.ts
+++ b/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view_html.ts
@@ -105,7 +105,7 @@
       font-size: var(--font-size-mono);
       line-height: var(--line-height-mono);
       margin-right: var(--spacing-l);
-      margin-bottom: var(--spacing-l);
+      margin-bottom: var(--spacing-m);
       /* Account for border and padding and rounding errors. */
       max-width: calc(72ch + 2px + 2 * var(--spacing-m) + 0.4px);
     }
@@ -548,6 +548,7 @@
                 <gr-related-changes-list-experimental
                   change="[[_change]]"
                   id="relatedChangesExperimental"
+                  patch-num="[[_computeLatestPatchNum(_allPatchSets)]]"
                 ></gr-related-changes-list-experimental>
               </template>
               <template is="dom-if" if="[[!_isNewChangeSummaryUiEnabled]]">
@@ -679,8 +680,10 @@
           change="[[_change]]"
           change-num="[[_changeNum]]"
           logged-in="[[_loggedIn]]"
+          comment-tab-state="[[_tabState.commentTab]]"
           only-show-robot-comments-with-human-reply=""
           unresolved-only
+          show-comment-context
         ></gr-thread-list>
       </template>
       <template
diff --git a/polygerrit-ui/app/elements/change/gr-confirm-cherrypick-dialog/gr-confirm-cherrypick-dialog.ts b/polygerrit-ui/app/elements/change/gr-confirm-cherrypick-dialog/gr-confirm-cherrypick-dialog.ts
index 10cffba..225e3e9 100644
--- a/polygerrit-ui/app/elements/change/gr-confirm-cherrypick-dialog/gr-confirm-cherrypick-dialog.ts
+++ b/polygerrit-ui/app/elements/change/gr-confirm-cherrypick-dialog/gr-confirm-cherrypick-dialog.ts
@@ -144,7 +144,7 @@
 
   private selectedChangeIds = new Set<ChangeInfoId>();
 
-  private restApiService = appContext.restApiService;
+  private readonly restApiService = appContext.restApiService;
 
   constructor() {
     super();
diff --git a/polygerrit-ui/app/elements/change/gr-confirm-move-dialog/gr-confirm-move-dialog.ts b/polygerrit-ui/app/elements/change/gr-confirm-move-dialog/gr-confirm-move-dialog.ts
index ff61de9..5e95e66 100644
--- a/polygerrit-ui/app/elements/change/gr-confirm-move-dialog/gr-confirm-move-dialog.ts
+++ b/polygerrit-ui/app/elements/change/gr-confirm-move-dialog/gr-confirm-move-dialog.ts
@@ -67,7 +67,7 @@
     };
   }
 
-  private restApiService = appContext.restApiService;
+  private readonly restApiService = appContext.restApiService;
 
   constructor() {
     super();
diff --git a/polygerrit-ui/app/elements/change/gr-confirm-rebase-dialog/gr-confirm-rebase-dialog.ts b/polygerrit-ui/app/elements/change/gr-confirm-rebase-dialog/gr-confirm-rebase-dialog.ts
index 9dcd849..b2b6e61 100644
--- a/polygerrit-ui/app/elements/change/gr-confirm-rebase-dialog/gr-confirm-rebase-dialog.ts
+++ b/polygerrit-ui/app/elements/change/gr-confirm-rebase-dialog/gr-confirm-rebase-dialog.ts
@@ -89,7 +89,7 @@
   @property({type: Array})
   _recentChanges?: RebaseChange[];
 
-  private restApiService = appContext.restApiService;
+  private readonly restApiService = appContext.restApiService;
 
   constructor() {
     super();
diff --git a/polygerrit-ui/app/elements/change/gr-confirm-submit-dialog/gr-confirm-submit-dialog_html.ts b/polygerrit-ui/app/elements/change/gr-confirm-submit-dialog/gr-confirm-submit-dialog_html.ts
index fae920d..99094d2 100644
--- a/polygerrit-ui/app/elements/change/gr-confirm-submit-dialog/gr-confirm-submit-dialog_html.ts
+++ b/polygerrit-ui/app/elements/change/gr-confirm-submit-dialog/gr-confirm-submit-dialog_html.ts
@@ -53,7 +53,7 @@
           <template is="dom-if" if="[[change.is_private]]">
             <p>
               <iron-icon
-                icon="gr-icons:error"
+                icon="gr-icons:warning"
                 class="warningBeforeSubmit"
               ></iron-icon>
               <strong>Heads Up!</strong>
@@ -63,7 +63,7 @@
           <template is="dom-if" if="[[change.unresolved_comment_count]]">
             <p>
               <iron-icon
-                icon="gr-icons:error"
+                icon="gr-icons:warning"
                 class="warningBeforeSubmit"
               ></iron-icon>
               [[_computeUnresolvedCommentsWarning(change)]]
@@ -80,7 +80,7 @@
           </template>
           <template is="dom-if" if="[[_computeHasChangeEdit(change)]]">
             <iron-icon
-              icon="gr-icons:error"
+              icon="gr-icons:warning"
               class="warningBeforeSubmit"
             ></iron-icon>
             Your unpublished edit will not be submitted. Did you forget to click
diff --git a/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list_test.js b/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list_test.js
index 2d38dcc..285b73f 100644
--- a/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list_test.js
+++ b/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list_test.js
@@ -1374,12 +1374,10 @@
         line_length: 100,
         cursor_blink_rate: 0,
         line_wrapping: false,
-        intraline_difference: true,
         show_line_endings: true,
         show_tabs: true,
         show_whitespace_errors: true,
         syntax_highlighting: true,
-        auto_hide_diff_table_header: true,
         theme: 'DEFAULT',
         ignore_whitespace: 'IGNORE_NONE',
       };
diff --git a/polygerrit-ui/app/elements/change/gr-label-scores/gr-label-scores.ts b/polygerrit-ui/app/elements/change/gr-label-scores/gr-label-scores.ts
index 7af9d66..a966186 100644
--- a/polygerrit-ui/app/elements/change/gr-label-scores/gr-label-scores.ts
+++ b/polygerrit-ui/app/elements/change/gr-label-scores/gr-label-scores.ts
@@ -95,9 +95,11 @@
     numberValue?: number
   ): string {
     const detailedInfo = labels[labelName] as DetailedLabelInfo;
-    for (const labelValue of Object.keys(detailedInfo.values)) {
-      if (Number(labelValue) === numberValue) {
-        return labelValue;
+    if (detailedInfo.values) {
+      for (const labelValue of Object.keys(detailedInfo.values)) {
+        if (Number(labelValue) === numberValue) {
+          return labelValue;
+        }
       }
     }
     const stringVal = `${numberValue}`;
diff --git a/polygerrit-ui/app/elements/change/gr-message/gr-message.ts b/polygerrit-ui/app/elements/change/gr-message/gr-message.ts
index 39cdf22..2fe409a 100644
--- a/polygerrit-ui/app/elements/change/gr-message/gr-message.ts
+++ b/polygerrit-ui/app/elements/change/gr-message/gr-message.ts
@@ -40,6 +40,7 @@
   NumericChangeId,
   ChangeMessageId,
   PatchSetNum,
+  AccountInfo,
 } from '../../../types/common';
 import {CommentThread} from '../../../utils/comment-util';
 import {hasOwnProperty} from '../../../utils/common-util';
@@ -51,6 +52,7 @@
   computeLatestPatchNum,
   computePredecessor,
 } from '../../../utils/patch-set-util';
+import {isServiceUser} from '../../../utils/account-util';
 
 const PATCH_SET_PREFIX_PATTERN = /^(?:Uploaded\s*)?(?:P|p)atch (?:S|s)et \d+:\s*(.*)/;
 const LABEL_TITLE_SCORE_PATTERN = /^(-?)([A-Za-z0-9-]+?)([+-]\d+)?[.]?$/;
@@ -472,9 +474,10 @@
     return classes.join(' ');
   }
 
-  _computeClass(expanded: boolean) {
+  _computeClass(expanded?: boolean, author?: AccountInfo) {
     const classes = [];
     classes.push(expanded ? 'expanded' : 'collapsed');
+    if (isServiceUser(author)) classes.push('serviceUser');
     return classes.join(' ');
   }
 
diff --git a/polygerrit-ui/app/elements/change/gr-message/gr-message_html.ts b/polygerrit-ui/app/elements/change/gr-message/gr-message_html.ts
index 6515af0..b93040b 100644
--- a/polygerrit-ui/app/elements/change/gr-message/gr-message_html.ts
+++ b/polygerrit-ui/app/elements/change/gr-message/gr-message_html.ts
@@ -39,11 +39,24 @@
     .contentContainer {
       padding: var(--spacing-m) var(--spacing-l);
     }
+    .expanded .contentContainer {
+      background-color: var(--background-color-secondary);
+    }
     .collapsed .contentContainer {
-      /* For expanded state we inherit the alternating background color
-           that is set in gr-messages-list. */
       background-color: var(--background-color-primary);
     }
+    div.serviceUser.expanded div.contentContainer {
+      background-color: var(
+        --background-color-service-user,
+        var(--background-color-secondary)
+      );
+    }
+    div.serviceUser.collapsed div.contentContainer {
+      background-color: var(
+        --background-color-service-user,
+        var(--background-color-primary)
+      );
+    }
     .name {
       font-weight: var(--font-weight-bold);
     }
@@ -188,7 +201,7 @@
       }
     }
   </style>
-  <div class$="[[_computeClass(_expanded)]]">
+  <div class$="[[_computeClass(_expanded, author)]]">
     <div class="contentContainer">
       <div class="author" on-click="_handleAuthorClick">
         <span hidden$="[[!showOnBehalfOf]]">
@@ -259,6 +272,7 @@
               change-num="[[changeNum]]"
               logged-in="[[_loggedIn]]"
               hide-toggle-buttons
+              show-comment-context
             >
             </gr-thread-list>
           </template>
diff --git a/polygerrit-ui/app/elements/change/gr-messages-list/gr-messages-list_html.ts b/polygerrit-ui/app/elements/change/gr-messages-list/gr-messages-list_html.ts
index e1ef3f8..6c786ef 100644
--- a/polygerrit-ui/app/elements/change/gr-messages-list/gr-messages-list_html.ts
+++ b/polygerrit-ui/app/elements/change/gr-messages-list/gr-messages-list_html.ts
@@ -50,9 +50,6 @@
     gr-message:not(:last-of-type) {
       border-bottom: 1px solid var(--border-color);
     }
-    gr-message {
-      background-color: var(--background-color-secondary);
-    }
   </style>
   <div class="header">
     <div id="showAllActivityToggleContainer" class="container">
diff --git a/polygerrit-ui/app/elements/change/gr-related-changes-list-experimental/gr-related-change.ts b/polygerrit-ui/app/elements/change/gr-related-changes-list-experimental/gr-related-change.ts
new file mode 100644
index 0000000..3ed545e
--- /dev/null
+++ b/polygerrit-ui/app/elements/change/gr-related-changes-list-experimental/gr-related-change.ts
@@ -0,0 +1,197 @@
+/**
+ * @license
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import {html} from 'lit-html';
+import {GrLitElement} from '../../lit/gr-lit-element';
+import {customElement, property, css} from 'lit-element';
+import {sharedStyles} from '../../../styles/shared-styles';
+import {
+  ChangeInfo,
+  RelatedChangeAndCommitInfo,
+  CommitId,
+} from '../../../types/common';
+import {ChangeStatus} from '../../../constants/constants';
+import {isChangeInfo} from '../../../utils/change-util';
+
+@customElement('gr-related-change')
+export class GrRelatedChange extends GrLitElement {
+  @property()
+  change?: ChangeInfo | RelatedChangeAndCommitInfo;
+
+  @property()
+  href?: string;
+
+  @property()
+  isCurrentChange = false;
+
+  @property()
+  showSubmittableCheck = false;
+
+  @property()
+  showChangeStatus = false;
+
+  /*
+   * Needed for calculation if change is direct or indirect ancestor/descendant
+   * to current change.
+   */
+  @property()
+  connectedRevisions?: CommitId[];
+
+  static get styles() {
+    return [
+      sharedStyles,
+      css`
+        a {
+          display: block;
+        }
+        .changeContainer,
+        a {
+          max-width: 100%;
+          overflow: hidden;
+          text-overflow: ellipsis;
+          white-space: nowrap;
+        }
+        .changeContainer {
+          display: flex;
+        }
+        .strikethrough {
+          color: var(--deemphasized-text-color);
+          text-decoration: line-through;
+        }
+        .status {
+          color: var(--deemphasized-text-color);
+          font-weight: var(--font-weight-bold);
+          margin-left: var(--spacing-xs);
+        }
+        .notCurrent {
+          color: #e65100;
+        }
+        .indirectAncestor {
+          color: #33691e;
+        }
+        .submittableCheck {
+          padding-left: var(--spacing-s);
+          color: var(--positive-green-text-color);
+          display: none;
+        }
+        .submittableCheck.submittable {
+          display: inline;
+        }
+        .hidden,
+        .mobile {
+          display: none;
+        }
+        .submittableCheck {
+          padding-left: var(--spacing-s);
+          color: var(--positive-green-text-color);
+          display: none;
+        }
+        .submittableCheck.submittable {
+          display: inline;
+        }
+        .arrowToCurrentChange {
+          position: absolute;
+        }
+      `,
+    ];
+  }
+
+  render() {
+    const change = this.change;
+    if (!change) throw new Error('Missing change');
+    const linkClass = this._computeLinkClass(change);
+    return html`<span
+        role="img"
+        class="arrowToCurrentChange"
+        aria-label="Arrow marking current change"
+        ?hidden=${!this.isCurrentChange}
+        >âž”</span
+      >
+      <div class="changeContainer">
+        <a href="${this.href}" class="${linkClass}"><slot></slot></a>
+        ${this.showSubmittableCheck
+          ? html`<span
+              tabindex="-1"
+              title="Submittable"
+              class="submittableCheck ${linkClass}"
+              role="img"
+              aria-label="Submittable"
+              >✓</span
+            >`
+          : ''}
+        ${this.showChangeStatus && !isChangeInfo(change)
+          ? html`<span class="${this._computeChangeStatusClass(change)}">
+              (${this._computeChangeStatus(change)})
+            </span>`
+          : ''}
+      </div> `;
+  }
+
+  _computeLinkClass(change: ChangeInfo | RelatedChangeAndCommitInfo) {
+    const statuses = [];
+    if (change.status === ChangeStatus.ABANDONED) {
+      statuses.push('strikethrough');
+    }
+    if (change.submittable) {
+      statuses.push('submittable');
+    }
+    return statuses.join(' ');
+  }
+
+  _computeChangeStatusClass(change: RelatedChangeAndCommitInfo) {
+    const classes = ['status'];
+    if (change._revision_number !== change._current_revision_number) {
+      classes.push('notCurrent');
+    } else if (this._isIndirectAncestor(change)) {
+      classes.push('indirectAncestor');
+    } else if (change.submittable) {
+      classes.push('submittable');
+    } else if (change.status === ChangeStatus.NEW) {
+      classes.push('hidden');
+    }
+    return classes.join(' ');
+  }
+
+  _computeChangeStatus(change: RelatedChangeAndCommitInfo) {
+    switch (change.status) {
+      case ChangeStatus.MERGED:
+        return 'Merged';
+      case ChangeStatus.ABANDONED:
+        return 'Abandoned';
+    }
+    if (change._revision_number !== change._current_revision_number) {
+      return 'Not current';
+    } else if (this._isIndirectAncestor(change)) {
+      return 'Indirect ancestor';
+    } else if (change.submittable) {
+      return 'Submittable';
+    }
+    return '';
+  }
+
+  _isIndirectAncestor(change: RelatedChangeAndCommitInfo) {
+    return (
+      this.connectedRevisions &&
+      !this.connectedRevisions.includes(change.commit.commit)
+    );
+  }
+}
+
+declare global {
+  interface HTMLElementTagNameMap {
+    'gr-related-change': GrRelatedChange;
+  }
+}
diff --git a/polygerrit-ui/app/elements/change/gr-related-changes-list-experimental/gr-related-changes-list-experimental.ts b/polygerrit-ui/app/elements/change/gr-related-changes-list-experimental/gr-related-changes-list-experimental.ts
index 5134b72..11f0fd3 100644
--- a/polygerrit-ui/app/elements/change/gr-related-changes-list-experimental/gr-related-changes-list-experimental.ts
+++ b/polygerrit-ui/app/elements/change/gr-related-changes-list-experimental/gr-related-changes-list-experimental.ts
@@ -15,6 +15,7 @@
  * limitations under the License.
  */
 import {html, nothing} from 'lit-html';
+import './gr-related-change';
 import {classMap} from 'lit-html/directives/class-map';
 import {GrLitElement} from '../../lit/gr-lit-element';
 import {customElement, property, css} from 'lit-element';
@@ -23,18 +24,15 @@
   SubmittedTogetherInfo,
   ChangeInfo,
   RelatedChangeAndCommitInfo,
+  RelatedChangesInfo,
+  PatchSetNum,
+  CommitId,
 } from '../../../types/common';
 import {appContext} from '../../../services/app-context';
 import {ParsedChangeInfo} from '../../../types/types';
 import {GerritNav} from '../../core/gr-navigation/gr-navigation';
 import {pluralize} from '../../../utils/string-util';
-import {ChangeStatus} from '../../../constants/constants';
-
-function isChangeInfo(
-  x: ChangeInfo | RelatedChangeAndCommitInfo | ParsedChangeInfo
-): x is ChangeInfo | ParsedChangeInfo {
-  return (x as ChangeInfo)._number !== undefined;
-}
+import {getRevisionKey, isChangeInfo} from '../../../utils/change-util';
 
 /** What is the maximum number of shown changes in collapsed list? */
 const MAX_CHANGES_WHEN_COLLAPSED = 3;
@@ -44,74 +42,120 @@
   @property()
   change?: ParsedChangeInfo;
 
+  @property({type: String})
+  patchNum?: PatchSetNum;
+
   @property()
   _submittedTogether?: SubmittedTogetherInfo = {
     changes: [],
     non_visible_changes: 0,
   };
 
+  @property()
+  _relatedResponse?: RelatedChangesInfo = {changes: []};
+
   private readonly restApiService = appContext.restApiService;
 
   static get styles() {
     return [
       sharedStyles,
       css`
-        .title {
-          font-weight: var(--font-weight-bold);
-          color: var(--deemphasized-text-color);
-          padding-left: var(--metadata-horizontal-padding);
-        }
-        h4 {
-          display: flex;
-        }
-        /* This is a hacky solution from old gr-related-change-list
-         * TODO(milutin): find layout without needing it
-         */
-        h4:before,
-        gr-related-change:before {
-          content: ' ';
-          flex-shrink: 0;
-          width: 1.2em;
-        }
         .note {
           color: var(--error-text-color);
         }
+        section {
+          margin-bottom: var(--spacing-m);
+        }
       `,
     ];
   }
 
   render() {
+    const relatedChanges = this._relatedResponse?.changes ?? [];
+    let showWhenCollapsedPredicate = this.showWhenCollapsedPredicateFactory(
+      relatedChanges.length,
+      relatedChanges.findIndex(relatedChange =>
+        this._changesEqual(relatedChange, this.change)
+      )
+    );
+    const connectedRevisions = this._computeConnectedRevisions(
+      this.change,
+      this.patchNum,
+      relatedChanges
+    );
+    const relatedChangeSection = html` <section
+      class="relatedChanges"
+      ?hidden=${!relatedChanges.length}
+    >
+      <gr-related-collapse
+        title="Relation chain"
+        .length=${relatedChanges.length}
+      >
+        ${relatedChanges.map(
+          (change, index) =>
+            html`<gr-related-change
+              class="${classMap({
+                ['show-when-collapsed']: showWhenCollapsedPredicate(index),
+              })}"
+              .isCurrentChange="${this._changesEqual(change, this.change)}"
+              .change="${change}"
+              .connectedRevisions="${connectedRevisions}"
+              .href="${change?._change_number
+                ? GerritNav.getUrlForChangeById(
+                    change._change_number,
+                    change.project,
+                    change._revision_number as PatchSetNum
+                  )
+                : ''}"
+              .showChangeStatus=${true}
+              >${change.commit.subject}</gr-related-change
+            >`
+        )}
+      </gr-related-collapse>
+    </section>`;
+
     const submittedTogetherChanges = this._submittedTogether?.changes ?? [];
     const countNonVisibleChanges =
       this._submittedTogether?.non_visible_changes ?? 0;
-    const showWhenCollapsedPredicate = this.showWhenCollapsedPredicateFactory(
+    showWhenCollapsedPredicate = this.showWhenCollapsedPredicateFactory(
       submittedTogetherChanges.length,
       submittedTogetherChanges.findIndex(relatedChange =>
         this._changesEqual(relatedChange, this.change)
       )
     );
-    return html` <section
+    const submittedTogetherSection = html`<section
       id="submittedTogether"
       ?hidden=${!submittedTogetherChanges?.length &&
       !this._submittedTogether?.non_visible_changes}
     >
-      <h4 class="title">Submitted together</h4>
-      <gr-related-collapse .length=${submittedTogetherChanges.length}>
+      <gr-related-collapse
+        title="Submitted together"
+        .length=${submittedTogetherChanges.length}
+      >
         ${submittedTogetherChanges.map(
-          (relatedChange, index) =>
+          (change, index) =>
             html`<gr-related-change
               class="${classMap({
                 ['show-when-collapsed']: showWhenCollapsedPredicate(index),
               })}"
-              .currentChange="${this._changesEqual(relatedChange, this.change)}"
-              .change="${relatedChange}"
-            ></gr-related-change>`
+              .isCurrentChange="${this._changesEqual(change, this.change)}"
+              .change="${change}"
+              .href="${GerritNav.getUrlForChangeById(
+                change._number,
+                change.project
+              )}"
+              .showSubmittableCheck=${true}
+              >${change.project}: ${change.branch}:
+              ${change.subject}</gr-related-change
+            >`
         )}
       </gr-related-collapse>
       <div class="note" ?hidden=${!countNonVisibleChanges}>
         (+ ${pluralize(countNonVisibleChanges, 'non-visible change')})
       </div>
     </section>`;
+
+    return html`${relatedChangeSection}${submittedTogetherSection}`;
   }
 
   showWhenCollapsedPredicateFactory(length: number, highlightIndex: number) {
@@ -126,13 +170,27 @@
     };
   }
 
-  reload() {
+  reload(getRelatedChanges?: Promise<RelatedChangesInfo | undefined>) {
     if (!this.change) return Promise.reject(new Error('change missing'));
-    return this.restApiService
-      .getChangesSubmittedTogether(this.change._number)
-      .then(response => {
-        this._submittedTogether = response;
-      });
+    const promises: Array<Promise<void>> = [
+      this.restApiService
+        .getChangesSubmittedTogether(this.change._number)
+        .then(response => {
+          this._submittedTogether = response;
+        }),
+    ];
+    if (getRelatedChanges) {
+      promises.push(
+        getRelatedChanges.then(response => {
+          if (!response) {
+            throw new Error('getRelatedChanges returned undefined response');
+          }
+          this._relatedResponse = response;
+        })
+      );
+    }
+
+    return Promise.all(promises);
   }
 
   /**
@@ -165,11 +223,54 @@
     }
     return change._change_number;
   }
+
+  /*
+   * A list of commit ids connected to change to understand if other change
+   * is direct or indirect ancestor / descendant.
+   */
+  _computeConnectedRevisions(
+    change?: ParsedChangeInfo,
+    patchNum?: PatchSetNum,
+    relatedChanges?: RelatedChangeAndCommitInfo[]
+  ) {
+    if (!patchNum || !relatedChanges || !change) {
+      return [];
+    }
+
+    const connected: CommitId[] = [];
+    const changeRevision = getRevisionKey(change, patchNum);
+    const commits = relatedChanges.map(c => c.commit);
+    let pos = commits.length - 1;
+
+    while (pos >= 0) {
+      const commit: CommitId = commits[pos].commit;
+      connected.push(commit);
+      // TODO(TS): Ensure that both (commit and changeRevision) are string and use === instead
+      // eslint-disable-next-line eqeqeq
+      if (commit == changeRevision) {
+        break;
+      }
+      pos--;
+    }
+    while (pos >= 0) {
+      for (let i = 0; i < commits[pos].parents.length; i++) {
+        if (connected.includes(commits[pos].parents[i].commit)) {
+          connected.push(commits[pos].commit);
+          break;
+        }
+      }
+      --pos;
+    }
+    return connected;
+  }
 }
 
 @customElement('gr-related-collapse')
 export class GrRelatedCollapse extends GrLitElement {
   @property()
+  title = '';
+
+  @property()
   showAll = false;
 
   @property()
@@ -179,10 +280,24 @@
     return [
       sharedStyles,
       css`
+        .title {
+          font-weight: var(--font-weight-bold);
+          color: var(--deemphasized-text-color);
+          padding-left: var(--metadata-horizontal-padding);
+        }
+        h4 {
+          display: flex;
+          align-self: flex-end;
+        }
         gr-button {
           display: flex;
         }
-        gr-button:before {
+        /* This is a hacky solution from old gr-related-change-list
+         * TODO(milutin): find layout without needing it
+         */
+        h4:before,
+        gr-button:before,
+        ::slotted(gr-related-change):before {
           content: ' ';
           flex-shrink: 0;
           width: 1.2em;
@@ -196,31 +311,46 @@
         ::slotted(gr-related-change) {
           display: flex;
         }
+        gr-button iron-icon {
+          color: inherit;
+          --iron-icon-height: 18px;
+          --iron-icon-width: 18px;
+        }
+        .container {
+          justify-content: space-between;
+          display: flex;
+          margin-bottom: var(--spacing-s);
+        }
       `,
     ];
   }
 
   render() {
+    const title = html`<h4 class="title">${this.title}</h4>`;
+
     const collapsible = this.length > MAX_CHANGES_WHEN_COLLAPSED;
     const items = html` <div
       class="${!this.showAll && collapsible ? 'collapsed' : ''}"
     >
       <slot></slot>
     </div>`;
+
     let button = nothing;
     if (collapsible) {
       if (this.showAll) {
         button = html`<gr-button link="" @click="${this.toggle}"
-          >Show less</gr-button
-        >`;
+          >Show less<iron-icon icon="gr-icons:expand-less"></iron-icon
+        ></gr-button>`;
       } else {
         button = html`<gr-button link="" @click="${this.toggle}"
-          >+ ${this.length - MAX_CHANGES_WHEN_COLLAPSED} more</gr-button
-        >`;
+          >Show all (${this.length})
+          <iron-icon icon="gr-icons:expand-more"></iron-icon
+        ></gr-button>`;
       }
     }
 
-    return html`${items}${button}`;
+    return html`<div class="container">${title}${button}</div>
+      ${items}`;
   }
 
   private toggle(e: MouseEvent) {
@@ -229,97 +359,9 @@
   }
 }
 
-@customElement('gr-related-change')
-export class GrRelatedChange extends GrLitElement {
-  @property()
-  change?: ChangeInfo;
-
-  @property()
-  currentChange = false;
-
-  static get styles() {
-    return [
-      sharedStyles,
-      css`
-        a {
-          display: block;
-        }
-        .changeContainer,
-        a {
-          max-width: 100%;
-          overflow: hidden;
-          text-overflow: ellipsis;
-          white-space: nowrap;
-        }
-        .changeContainer {
-          display: flex;
-        }
-        .strikethrough {
-          color: var(--deemphasized-text-color);
-          text-decoration: line-through;
-        }
-        .submittableCheck {
-          padding-left: var(--spacing-s);
-          color: var(--positive-green-text-color);
-          display: none;
-        }
-        .submittableCheck.submittable {
-          display: inline;
-        }
-        .arrowToCurrentChange {
-          position: absolute;
-        }
-      `,
-    ];
-  }
-
-  render() {
-    const change = this.change;
-    if (!change) throw new Error('Missing change');
-    const linkClass = this._computeLinkClass(change);
-    return html`<span
-        role="img"
-        class="arrowToCurrentChange"
-        aria-label="Arrow marking current change"
-        ?hidden=${!this.currentChange}
-        >âž”</span
-      >
-      <div class="changeContainer">
-        <a
-          href="${GerritNav.getUrlForChangeById(
-            change._number,
-            change.project
-          )}"
-          class="${linkClass}"
-          >${change.project}: ${change.branch}: ${change.subject}</a
-        >
-        <span
-          tabindex="-1"
-          title="Submittable"
-          class="submittableCheck ${linkClass}"
-          role="img"
-          aria-label="Submittable"
-          >✓</span
-        >
-      </div> `;
-  }
-
-  _computeLinkClass(change: ChangeInfo) {
-    const statuses = [];
-    if (change.status === ChangeStatus.ABANDONED) {
-      statuses.push('strikethrough');
-    }
-    if (change.submittable) {
-      statuses.push('submittable');
-    }
-    return statuses.join(' ');
-  }
-}
-
 declare global {
   interface HTMLElementTagNameMap {
     'gr-related-changes-list-experimental': GrRelatedChangesListExperimental;
     'gr-related-collapse': GrRelatedCollapse;
-    'gr-related-change': GrRelatedChange;
   }
 }
diff --git a/polygerrit-ui/app/elements/change/gr-related-changes-list/gr-related-changes-list.ts b/polygerrit-ui/app/elements/change/gr-related-changes-list/gr-related-changes-list.ts
index 4ad99d5..5705c4f 100644
--- a/polygerrit-ui/app/elements/change/gr-related-changes-list/gr-related-changes-list.ts
+++ b/polygerrit-ui/app/elements/change/gr-related-changes-list/gr-related-changes-list.ts
@@ -26,7 +26,11 @@
 import {GerritNav} from '../../core/gr-navigation/gr-navigation';
 import {ChangeStatus} from '../../../constants/constants';
 
-import {changeIsOpen, getRevisionKey} from '../../../utils/change-util';
+import {
+  changeIsOpen,
+  getRevisionKey,
+  isChangeInfo,
+} from '../../../utils/change-util';
 import {getPluginEndpoints} from '../../shared/gr-js-api-interface/gr-plugin-endpoints';
 import {customElement, observe, property} from '@polymer/decorators';
 import {
@@ -48,12 +52,6 @@
   return {changes: [], non_visible_changes: 0};
 }
 
-function isChangeInfo(
-  x: ChangeInfo | RelatedChangeAndCommitInfo
-): x is ChangeInfo {
-  return (x as ChangeInfo)._number !== undefined;
-}
-
 @customElement('gr-related-changes-list')
 export class GrRelatedChangesList extends GestureEventListeners(
   LegacyElementMixin(PolymerElement)
@@ -113,6 +111,8 @@
 
   private readonly restApiService = appContext.restApiService;
 
+  private readonly reportingService = appContext.reportingService;
+
   clear() {
     this.loading = true;
     this.hidden = true;
@@ -440,6 +440,27 @@
   _computeNonVisibleChangesNote(n: number) {
     return `(+ ${pluralize(n, 'non-visible change')})`;
   }
+
+  // TODO(milutin): Temporary for data collection, remove when data collected
+  _reportClick(e: Event) {
+    const target = e.target as HTMLAnchorElement | undefined;
+    const section = target?.parentElement?.parentElement;
+    const sectionName = section?.getElementsByTagName('h4')[0]?.innerText;
+    const sectionLinks = [...(section?.getElementsByTagName('a') ?? [])];
+    const currentChange = section
+      ?.getElementsByClassName('arrowToCurrentChange')[0]
+      ?.nextElementSibling?.nextElementSibling?.getElementsByTagName('a')[0];
+
+    if (!target) return;
+    this.reportingService.reportInteraction('related-change-click', {
+      sectionName,
+      index: sectionLinks.indexOf(target) + 1,
+      countChanges: sectionLinks.length,
+      currentChangeIndex: !currentChange
+        ? undefined
+        : sectionLinks.indexOf(currentChange) + 1,
+    });
+  }
 }
 
 declare global {
diff --git a/polygerrit-ui/app/elements/change/gr-related-changes-list/gr-related-changes-list_html.ts b/polygerrit-ui/app/elements/change/gr-related-changes-list/gr-related-changes-list_html.ts
index 0f42028..8cb0638 100644
--- a/polygerrit-ui/app/elements/change/gr-related-changes-list/gr-related-changes-list_html.ts
+++ b/polygerrit-ui/app/elements/change/gr-related-changes-list/gr-related-changes-list_html.ts
@@ -117,6 +117,7 @@
               href$="[[_computeChangeURL(related._change_number, related.project, related._revision_number)]]"
               class$="[[_computeLinkClass(related)]]"
               title$="[[related.commit.subject]]"
+              on-click="_reportClick"
             >
               [[related.commit.subject]]
             </a>
@@ -149,6 +150,7 @@
               href$="[[_computeChangeURL(related._number, related.project)]]"
               class$="[[_computeLinkClass(related)]]"
               title$="[[related.project]]: [[related.branch]]: [[related.subject]]"
+              on-click="_reportClick"
             >
               [[related.project]]: [[related.branch]]: [[related.subject]]
             </a>
@@ -176,6 +178,7 @@
               href$="[[_computeChangeURL(change._number, change.project)]]"
               class$="[[_computeLinkClass(change)]]"
               title$="[[change.project]]: [[change.branch]]: [[change.subject]]"
+              on-click="_reportClick"
             >
               [[change.project]]: [[change.branch]]: [[change.subject]]
             </a>
@@ -204,6 +207,7 @@
               href$="[[_computeChangeURL(change._number, change.project)]]"
               class$="[[_computeLinkClass(change)]]"
               title$="[[change.branch]]: [[change.subject]]"
+              on-click="_reportClick"
             >
               [[change.branch]]: [[change.subject]]
             </a>
diff --git a/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog.ts b/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog.ts
index 36764f3..705a402 100644
--- a/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog.ts
+++ b/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog.ts
@@ -92,6 +92,7 @@
 } from '@polymer/polymer/interfaces';
 import {
   areSetsEqual,
+  assertIsDefined,
   assertNever,
   containsAll,
 } from '../../../utils/common-util';
@@ -433,7 +434,7 @@
   }
 
   open(focusTarget?: FocusTarget) {
-    if (!this.change) throw new Error('missing required change property');
+    assertIsDefined(this.change, 'change');
     this.knownLatestState = LatestPatchState.CHECKING;
     fetchChangeUpdates(this.change, this.restApiService).then(result => {
       this.knownLatestState = result.isLatest
@@ -605,7 +606,7 @@
     account: AccountInfoInput | GroupInfoInput,
     type: ReviewerType
   ) {
-    if (!this.change) throw new Error('missing required change property');
+    assertIsDefined(this.change, 'change');
     if (account._pendingAdd || !isAccount(account)) {
       return;
     }
@@ -1213,7 +1214,7 @@
   }
 
   cancel() {
-    if (!this.change) throw new Error('missing required change property');
+    assertIsDefined(this.change, 'change');
     if (!this._owner) throw new Error('missing required _owner property');
     this.dispatchEvent(
       new CustomEvent('cancel', {
@@ -1269,8 +1270,8 @@
   }
 
   _saveReview(review: ReviewInput, errFn?: ErrorCallback) {
-    if (!this.change) throw new Error('missing required change property');
-    if (!this.patchNum) throw new Error('missing required patchNum property');
+    assertIsDefined(this.change, 'change');
+    assertIsDefined(this.patchNum, 'patchNum');
     return this.restApiService.saveChangeReview(
       this.change._number,
       this.patchNum,
@@ -1316,7 +1317,7 @@
   }
 
   _getStorageLocation(): StorageLocation {
-    if (!this.change) throw new Error('missing required change property');
+    assertIsDefined(this.change, 'change');
     return {
       changeNum: this.change._number,
       patchNum: '@change',
diff --git a/polygerrit-ui/app/elements/change/gr-thread-list/gr-thread-list.ts b/polygerrit-ui/app/elements/change/gr-thread-list/gr-thread-list.ts
index 22b2e43..f0ec7cd 100644
--- a/polygerrit-ui/app/elements/change/gr-thread-list/gr-thread-list.ts
+++ b/polygerrit-ui/app/elements/change/gr-thread-list/gr-thread-list.ts
@@ -43,6 +43,8 @@
 import {fireThreadListModifiedEvent} from '../../../utils/event-util';
 import {KnownExperimentId} from '../../../services/flags/flags';
 import {appContext} from '../../../services/app-context';
+import {assertNever} from '../../../utils/common-util';
+import {CommentTabState} from '../../../types/events';
 
 interface CommentThreadWithInfo {
   thread: CommentThread;
@@ -77,6 +79,9 @@
   @property({type: Array})
   _sortedThreads: CommentThread[] = [];
 
+  @property({type: Boolean})
+  showCommentContext = false;
+
   @property({
     computed:
       '_computeDisplayedThreads(_sortedThreads.*, unresolvedOnly, ' +
@@ -103,6 +108,9 @@
   @property({type: Boolean})
   _isNewChangeSummaryUiEnabled = false;
 
+  @property({type: Object, observer: '_commentTabStateChange'})
+  commentTabState?: CommentTabState;
+
   flagsService = appContext.flagsService;
 
   /** @override */
@@ -498,6 +506,26 @@
   filterRobotThreadsWithoutHumanReply(threads?: CommentThread[]) {
     return threads?.filter(t => !isRobotThread(t) || hasHumanReply(t));
   }
+
+  _commentTabStateChange(
+    newValue?: CommentTabState,
+    oldValue?: CommentTabState
+  ) {
+    if (!newValue || newValue === oldValue) return;
+    switch (newValue) {
+      case CommentTabState.UNRESOLVED:
+        this._handleOnlyUnresolved();
+        break;
+      case CommentTabState.DRAFTS:
+        this._handleOnlyDrafts();
+        break;
+      case CommentTabState.SHOW_ALL:
+        this._handleAllComments();
+        break;
+      default:
+        assertNever(newValue, 'Unsupported preferred state');
+    }
+  }
 }
 
 declare global {
diff --git a/polygerrit-ui/app/elements/change/gr-thread-list/gr-thread-list_html.ts b/polygerrit-ui/app/elements/change/gr-thread-list/gr-thread-list_html.ts
index 8a52555..8b1ba25 100644
--- a/polygerrit-ui/app/elements/change/gr-thread-list/gr-thread-list_html.ts
+++ b/polygerrit-ui/app/elements/change/gr-thread-list/gr-thread-list_html.ts
@@ -25,7 +25,6 @@
     gr-comment-thread {
       display: block;
       margin-bottom: var(--spacing-m);
-      max-width: 80ch;
     }
     .header {
       align-items: center;
@@ -102,7 +101,7 @@
             name="filterComments"
             type="radio"
             on-click="_handleOnlyUnresolved"
-            checked$="[[unresolvedOnly]]"
+            checked="[[unresolvedOnly]]"
           />
           <label for="unresolvedRadio">
             Unresolved ([[_countUnresolved(threads)]])
@@ -113,7 +112,7 @@
             name="filterComments"
             type="radio"
             on-click="_handleOnlyDrafts"
-            checked$="[[_draftsOnly]]"
+            checked="[[_draftsOnly]]"
           />
           <label for="draftsRadio">
             Drafts ([[_countDrafts(threads)]])
@@ -124,7 +123,7 @@
             name="filterComments"
             type="radio"
             on-click="_handleAllComments"
-            checked$="[[_showAllComments(_draftsOnly, unresolvedOnly)]]"
+            checked="[[_showAllComments(_draftsOnly, unresolvedOnly)]]"
           />
           <label for="all">
             All ([[_countAllThreads(threads)]])
@@ -172,6 +171,7 @@
       <gr-comment-thread
         show-file-path=""
         show-ported-comment="[[thread.ported]]"
+        show-comment-context="[[showCommentContext]]"
         change-num="[[changeNum]]"
         comments="[[thread.comments]]"
         diff-side="[[thread.diffSide]]"
diff --git a/polygerrit-ui/app/elements/checks/gr-checks-results.ts b/polygerrit-ui/app/elements/checks/gr-checks-results.ts
index 68ff67b..34945a9 100644
--- a/polygerrit-ui/app/elements/checks/gr-checks-results.ts
+++ b/polygerrit-ui/app/elements/checks/gr-checks-results.ts
@@ -21,8 +21,10 @@
 import {sharedStyles} from '../../styles/shared-styles';
 import {RunResult} from '../../services/checks/checks-model';
 import {
+  hasCompleted,
   hasCompletedWithoutResults,
   iconForCategory,
+  isRunning,
 } from '../../services/checks/checks-util';
 
 @customElement('gr-result-row')
@@ -79,16 +81,16 @@
         }
         td .summary-cell {
           display: flex;
-          max-width: calc(100vw - 579px);
+          max-width: calc(100vw - 700px);
         }
         td .summary-cell .summary {
           font-weight: var(--font-weight-bold);
           flex-shrink: 1;
           overflow: hidden;
           text-overflow: ellipsis;
+          margin-right: var(--spacing-s);
         }
         td .summary-cell .message {
-          margin-left: var(--spacing-s);
           flex-grow: 1;
           /* Looks a bit stupid, but the idea is that .message shrinks first,
              and only when that has shrunken to 0, then .summary should also
@@ -119,7 +121,7 @@
 
   update(changedProperties: PropertyValues) {
     if (changedProperties.has('result')) {
-      this.isExpandable = !!this.result?.message;
+      this.isExpandable = !!this.result?.summary && !!this.result?.message;
     }
     super.update(changedProperties);
   }
@@ -136,10 +138,8 @@
         </td>
         <td class="summaryCol">
           <div class="summary-cell">
-            ${(this.result.links ?? []).map(this.renderLink)}
-            <!-- The &nbsp; is for being able to shrink a tiny amount without
-                 the text itself getting shrunk with an ellipsis. -->
-            <div class="summary">${this.result.summary}&nbsp;</div>
+            ${(this.result.links?.slice(0, 5) ?? []).map(this.renderLink)}
+            ${this.renderSummary(this.result.summary)}
             <div class="message">
               ${this.isExpanded ? '' : this.result.message}
             </div>
@@ -163,7 +163,6 @@
             aria-label="${this.isExpanded
               ? 'Collapse result row'
               : 'Expand result row'}"
-            @click="${this.toggleExpanded}"
             @keydown="${this.toggleExpanded}"
           >
             <iron-icon
@@ -182,6 +181,15 @@
     this.isExpanded = !this.isExpanded;
   }
 
+  renderSummary(text?: string) {
+    if (!text) return;
+    return html`
+      <!-- The &nbsp; is for being able to shrink a tiny amount without
+       the text itself getting shrunk with an ellipsis. -->
+      <div class="summary">${text}&nbsp;</div>
+    `;
+  }
+
   renderLink(link: Link) {
     return html`
       <a href="${link.url}" target="_blank">
@@ -229,9 +237,19 @@
   render() {
     if (!this.result) return '';
     return html`
-      <div class="message">
-        ${this.result.message}
-      </div>
+      <gr-endpoint-decorator name="check-result-expanded">
+        <gr-endpoint-param
+          name="run"
+          value="${this.result}"
+        ></gr-endpoint-param>
+        <gr-endpoint-param
+          name="result"
+          value="${this.result}"
+        ></gr-endpoint-param>
+        <div class="message">
+          ${this.result.message}
+        </div>
+      </gr-endpoint-decorator>
     `;
   }
 }
@@ -250,7 +268,7 @@
           padding: var(--spacing-xl);
         }
         .categoryHeader {
-          margin-top: var(--spacing-xxl);
+          margin-top: var(--spacing-l);
           margin-left: var(--spacing-l);
           text-transform: capitalize;
         }
@@ -270,6 +288,9 @@
         .categoryHeader iron-icon.success {
           color: var(--success-foreground);
         }
+        .noCompleted {
+          margin-top: var(--spacing-l);
+        }
         table.resultsTable {
           width: 100%;
           max-width: 1280px;
@@ -289,12 +310,21 @@
   render() {
     return html`
       <div><h2 class="heading-2">Results</h2></div>
-      ${this.renderSection(Category.ERROR)}
+      ${this.renderNoCompleted()} ${this.renderSection(Category.ERROR)}
       ${this.renderSection(Category.WARNING)}
       ${this.renderSection(Category.INFO)} ${this.renderSuccess()}
     `;
   }
 
+  renderNoCompleted() {
+    if (this.runs.some(hasCompleted)) return;
+    let text = 'No results';
+    if (this.runs.some(isRunning)) {
+      text = 'Checks are running ...';
+    }
+    return html`<div class="noCompleted">${text}</div>`;
+  }
+
   renderSection(category: Category) {
     const catString = category.toString().toLowerCase();
     const runs = this.runs.filter(r =>
@@ -360,7 +390,8 @@
   renderSuccessfulRun(run: CheckRun) {
     const adaptedRun: RunResult = {
       category: Category.INFO, // will not be used, but is required
-      summary: run.statusDescription ?? 'Completed without results.',
+      summary: run.statusDescription ?? '',
+      message: 'Completed without results.',
       ...run,
     };
     return html`<gr-result-row .result="${adaptedRun}"></gr-result-row>`;
diff --git a/polygerrit-ui/app/elements/checks/gr-checks-runs.ts b/polygerrit-ui/app/elements/checks/gr-checks-runs.ts
index 09a2319..5b7428b 100644
--- a/polygerrit-ui/app/elements/checks/gr-checks-runs.ts
+++ b/polygerrit-ui/app/elements/checks/gr-checks-runs.ts
@@ -22,6 +22,7 @@
 import {sharedStyles} from '../../styles/shared-styles';
 import {
   compareByWorstCategory,
+  fireActionTriggered,
   iconForRun,
   primaryRunAction,
 } from '../../services/checks/checks-util';
@@ -60,33 +61,6 @@
   );
 }
 
-export interface ActionTriggeredEventDetail {
-  action: Action;
-  run: CheckRun;
-}
-
-export type ActionTriggeredEvent = CustomEvent<ActionTriggeredEventDetail>;
-
-declare global {
-  interface HTMLElementEventMap {
-    'action-triggered': ActionTriggeredEvent;
-  }
-}
-
-function fireActionTriggered(
-  target: EventTarget,
-  action: Action,
-  run: CheckRun
-) {
-  target.dispatchEvent(
-    new CustomEvent('action-triggered', {
-      detail: {action, run},
-      composed: true,
-      bubbles: true,
-    })
-  );
-}
-
 @customElement('gr-checks-run')
 export class GrChecksRun extends GrLitElement {
   static get styles() {
@@ -104,7 +78,12 @@
           border-radius: var(--border-radius);
           padding: var(--spacing-s) var(--spacing-m);
           margin-top: var(--spacing-s);
-          cursor: default;
+          cursor: pointer;
+        }
+        .left {
+          overflow: hidden;
+          white-space: nowrap;
+          text-overflow: ellipsis;
         }
         .name {
           font-weight: var(--font-weight-bold);
diff --git a/polygerrit-ui/app/elements/checks/gr-checks-tab.ts b/polygerrit-ui/app/elements/checks/gr-checks-tab.ts
index d31ec53..61f8bd1 100644
--- a/polygerrit-ui/app/elements/checks/gr-checks-tab.ts
+++ b/polygerrit-ui/app/elements/checks/gr-checks-tab.ts
@@ -18,13 +18,21 @@
 import {css, customElement, property} from 'lit-element';
 import {GrLitElement} from '../lit/gr-lit-element';
 import {Action, CheckResult, CheckRun} from '../../api/checks';
-import {allResults$, allRuns$} from '../../services/checks/checks-model';
+import {
+  allActions$,
+  allResults$,
+  allRuns$,
+} from '../../services/checks/checks-model';
 import './gr-checks-runs';
 import './gr-checks-results';
 import {sharedStyles} from '../../styles/shared-styles';
 import {changeNum$, currentPatchNum$} from '../../services/change/change-model';
 import {NumericChangeId, PatchSetNum} from '../../types/common';
-import {ActionTriggeredEvent} from './gr-checks-runs';
+import {
+  ActionTriggeredEvent,
+  fireActionTriggered,
+} from '../../services/checks/checks-util';
+import {checkRequiredProperty} from '../../utils/common-util';
 
 /**
  * The "Checks" tab on the Gerrit change page. Gets its data from plugins that
@@ -37,6 +45,8 @@
 
   results: CheckResult[] = [];
 
+  actions: Action[] = [];
+
   @property()
   currentPatchNum: PatchSetNum | undefined = undefined;
 
@@ -46,6 +56,7 @@
   constructor() {
     super();
     this.subscribe('runs', allRuns$);
+    this.subscribe('actions', allActions$);
     this.subscribe('results', allResults$);
     this.subscribe('currentPatchNum', currentPatchNum$);
     this.subscribe('changeNum', changeNum$);
@@ -63,17 +74,13 @@
           display: block;
         }
         .header {
-          display: block;
+          display: flex;
+          justify-content: space-between;
           padding: var(--spacing-m) var(--spacing-l);
           border-bottom: 1px solid var(--border-color);
         }
-        .header span {
-          display: inline-block;
-          color: var(--link-color);
-          padding: var(--spacing-s) var(--spacing-m);
-          margin-right: var(--spacing-l);
-          border: 1px solid var(--border-color);
-          border-radius: var(--border-radius);
+        .action {
+          margin-left: var(--spacing-m);
         }
         .container {
           display: flex;
@@ -95,15 +102,20 @@
     const ps = `Patchset ${this.currentPatchNum} (Latest)`;
     return html`
       <div class="header">
-        <gr-dropdown-list
-          value="${ps}"
-          .items="${[
-            {
-              value: `${ps}`,
-              text: `${ps}`,
-            },
-          ]}"
-        ></gr-dropdown-list>
+        <div class="left">
+          <gr-dropdown-list
+            value="${ps}"
+            .items="${[
+              {
+                value: `${ps}`,
+                text: `${ps}`,
+              },
+            ]}"
+          ></gr-dropdown-list>
+        </div>
+        <div class="right">
+          ${this.actions.map(this.renderAction)}
+        </div>
       </div>
       <div class="container">
         <gr-checks-runs class="runs" .runs="${this.runs}"></gr-checks-runs>
@@ -115,7 +127,13 @@
     `;
   }
 
-  private handleActionTriggered(action: Action, run: CheckRun) {
+  renderAction(action: Action) {
+    return html`<gr-checks-top-level-action
+      .action="${action}"
+    ></gr-checks-top-level-action>`;
+  }
+
+  handleActionTriggered(action: Action, run?: CheckRun) {
     if (!this.changeNum) return;
     if (!this.currentPatchNum) return;
     // TODO(brohlfs): The callback is supposed to be returning a promise.
@@ -124,16 +142,40 @@
     action.callback(
       this.changeNum,
       this.currentPatchNum as number,
-      run.attempt,
-      run.externalId,
-      run.checkName,
+      run?.attempt,
+      run?.externalId,
+      run?.checkName,
       action.name
     );
   }
 }
 
+@customElement('gr-checks-top-level-action')
+export class GrChecksTopLevelAction extends GrLitElement {
+  @property()
+  action!: Action;
+
+  connectedCallback() {
+    super.connectedCallback();
+    checkRequiredProperty(this.action, 'action');
+  }
+
+  render() {
+    return html`
+      <gr-button link class="action" @click="${this.handleClick}"
+        >${this.action.name}</gr-button
+      >
+    `;
+  }
+
+  handleClick() {
+    fireActionTriggered(this, this.action);
+  }
+}
+
 declare global {
   interface HTMLElementTagNameMap {
     'gr-checks-tab': GrChecksTab;
+    'gr-checks-top-level-action': GrChecksTopLevelAction;
   }
 }
diff --git a/polygerrit-ui/app/elements/diff/gr-apply-fix-dialog/gr-apply-fix-dialog.ts b/polygerrit-ui/app/elements/diff/gr-apply-fix-dialog/gr-apply-fix-dialog.ts
index 468bb97..e381213 100644
--- a/polygerrit-ui/app/elements/diff/gr-apply-fix-dialog/gr-apply-fix-dialog.ts
+++ b/polygerrit-ui/app/elements/diff/gr-apply-fix-dialog/gr-apply-fix-dialog.ts
@@ -100,7 +100,7 @@
 
   private refitOverlay?: () => void;
 
-  private restApiService = appContext.restApiService;
+  private readonly restApiService = appContext.restApiService;
 
   /**
    * Given robot comment CustomEvent object, fetch diffs associated
diff --git a/polygerrit-ui/app/elements/diff/gr-comment-api/gr-comment-api.ts b/polygerrit-ui/app/elements/diff/gr-comment-api/gr-comment-api.ts
index 468c8ca..19f43c2 100644
--- a/polygerrit-ui/app/elements/diff/gr-comment-api/gr-comment-api.ts
+++ b/polygerrit-ui/app/elements/diff/gr-comment-api/gr-comment-api.ts
@@ -594,6 +594,8 @@
 export const _testOnly_findCommentById =
   ChangeComments.prototype.findCommentById;
 
+export const _testOnly_getCommentsForPath =
+  ChangeComments.prototype.getCommentsForPath;
 @customElement('gr-comment-api')
 export class GrCommentApi extends GestureEventListeners(
   LegacyElementMixin(PolymerElement)
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-cursor/gr-diff-cursor.ts b/polygerrit-ui/app/elements/diff/gr-diff-cursor/gr-diff-cursor.ts
index 035c1a3..5ac37dc 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-cursor/gr-diff-cursor.ts
+++ b/polygerrit-ui/app/elements/diff/gr-diff-cursor/gr-diff-cursor.ts
@@ -235,7 +235,11 @@
     return result;
   }
 
-  moveToNextCommentThread(): CursorMoveResult {
+  moveToNextCommentThread(): CursorMoveResult | undefined {
+    if (this.isAtEnd()) {
+      fireEvent(this, 'navigate-to-next-file-with-comments');
+      return;
+    }
     const result = this.$.cursorManager.next({
       filter: (row: HTMLElement) => this._rowHasThread(row),
     });
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-host/gr-diff-host.ts b/polygerrit-ui/app/elements/diff/gr-diff-host/gr-diff-host.ts
index ae1e438..47b4a1f 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-host/gr-diff-host.ts
+++ b/polygerrit-ui/app/elements/diff/gr-diff-host/gr-diff-host.ts
@@ -79,6 +79,7 @@
   fireEvent,
 } from '../../../utils/event-util';
 import {getPluginLoader} from '../../shared/gr-js-api-interface/gr-plugin-loader';
+import {assertIsDefined} from '../../../utils/common-util';
 
 const MSG_EMPTY_BLAME = 'No blame information for this diff.';
 
@@ -324,8 +325,8 @@
     return getPluginLoader()
       .awaitPluginsLoaded()
       .then(() => {
-        if (!this.path) throw new Error('Missing required "path" property.');
-        if (!this.changeNum) throw new Error('Missing required "changeNum".');
+        assertIsDefined(this.path, 'path');
+        assertIsDefined(this.changeNum, 'changeNum');
         this._layers = this._getLayers(this.path, this.changeNum);
         this._coverageRanges = [];
         // We kick off fetching the data here, but we don't return the promise,
@@ -341,8 +342,8 @@
    */
   async reload(shouldReportMetric?: boolean) {
     this.clear();
-    if (!this.path) throw new Error('Missing required "path" property.');
-    if (!this.changeNum) throw new Error('Missing required "changeNum" prop.');
+    assertIsDefined(this.path, 'path');
+    assertIsDefined(this.changeNum, 'changeNum');
     this.diff = undefined;
     this._errorMessage = null;
     const whitespaceLevel = this._getIgnoreWhitespace();
@@ -420,10 +421,10 @@
   }
 
   _getCoverageData() {
-    if (!this.changeNum) throw new Error('Missing required "changeNum" prop.');
-    if (!this.change) throw new Error('Missing required "change" prop.');
-    if (!this.path) throw new Error('Missing required "path" prop.');
-    if (!this.patchRange) throw new Error('Missing required "patchRange".');
+    assertIsDefined(this.changeNum, 'changeNum');
+    assertIsDefined(this.change, 'change');
+    assertIsDefined(this.path, 'path');
+    assertIsDefined(this.patchRange, 'patchRange');
     const changeNum = this.changeNum;
     const change = this.change;
     const path = this.path;
@@ -442,7 +443,7 @@
           if (!provider) return;
           provider(changeNum, path, basePatchNum, patchNum, change)
             .then(coverageRanges => {
-              if (!this.patchRange) throw new Error('Missing "patchRange".');
+              assertIsDefined(this.patchRange, 'patchRange');
               if (
                 !coverageRanges ||
                 changeNum !== this.changeNum ||
@@ -532,9 +533,9 @@
    * Load and display blame information for the base of the diff.
    */
   loadBlame(): Promise<BlameInfo[]> {
-    if (!this.changeNum) throw new Error('Missing required "changeNum" prop.');
-    if (!this.patchRange) throw new Error('Missing required "patchRange".');
-    if (!this.path) throw new Error('Missing required "path" property.');
+    assertIsDefined(this.changeNum, 'changeNum');
+    assertIsDefined(this.patchRange, 'patchRange');
+    assertIsDefined(this.path, 'path');
     return this.restApiService
       .getBlame(this.changeNum, this.patchRange.patchNum, this.path, true)
       .then(blame => {
@@ -564,8 +565,8 @@
     this.$.diff.clearDiffContent();
   }
 
-  expandAllContext() {
-    this.$.diff.expandAllContext();
+  toggleAllContext() {
+    this.$.diff.toggleAllContext();
   }
 
   _getLoggedIn() {
@@ -599,9 +600,9 @@
     // Wrap the diff request in a new promise so that the error handler
     // rejects the promise, allowing the error to be handled in the .catch.
     return new Promise((resolve, reject) => {
-      if (!this.changeNum) throw new Error('Missing required "changeNum".');
-      if (!this.patchRange) throw new Error('Missing required "patchRange".');
-      if (!this.path) throw new Error('Missing required "path" property.');
+      assertIsDefined(this.changeNum, 'changeNum');
+      assertIsDefined(this.patchRange, 'patchRange');
+      assertIsDefined(this.path, 'path');
       this.restApiService
         .getDiff(
           this.changeNum,
@@ -669,7 +670,7 @@
 
     // Report the due_to_rebase percentage in the "diff" category when
     // applicable.
-    if (!this.patchRange) throw new Error('Missing required "patchRange".');
+    assertIsDefined(this.patchRange, 'patchRange');
     if (this.patchRange.basePatchNum === 'PARENT') {
       this.reporting.reportInteraction(EVENT_AGAINST_PARENT);
     } else if (percentRebaseDelta === 0) {
@@ -726,8 +727,8 @@
   }
 
   _getImages(diff: DiffInfo) {
-    if (!this.changeNum) throw new Error('Missing required "changeNum" prop.');
-    if (!this.patchRange) throw new Error('Missing required "patchRange".');
+    assertIsDefined(this.changeNum, 'changeNum');
+    assertIsDefined(this.patchRange, 'patchRange');
     return this.restApiService.getImagesForDiff(
       this.changeNum,
       diff,
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-host/gr-diff-host_test.js b/polygerrit-ui/app/elements/diff/gr-diff-host/gr-diff-host_test.js
index 99486ad..01c78f0 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-host/gr-diff-host_test.js
+++ b/polygerrit-ui/app/elements/diff/gr-diff-host/gr-diff-host_test.js
@@ -796,9 +796,9 @@
     assert.equal(stub.lastCall.args.length, 0);
   });
 
-  test('delegates expandAllContext()', () => {
-    const stub = sinon.stub(element.$.diff, 'expandAllContext');
-    element.expandAllContext();
+  test('delegates toggleAllContext()', () => {
+    const stub = sinon.stub(element.$.diff, 'toggleAllContext');
+    element.toggleAllContext();
     assert.isTrue(stub.calledOnce);
     assert.equal(stub.lastCall.args.length, 0);
   });
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view.ts b/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view.ts
index 62b34e9..088d9cf 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view.ts
+++ b/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view.ts
@@ -97,6 +97,7 @@
 import {CustomKeyboardEvent, OpenFixPreviewEvent} from '../../../types/events';
 import {fireAlert, fireTitleChange} from '../../../utils/event-util';
 import {GerritView} from '../../../services/router/router-model';
+import {assertIsDefined} from '../../../utils/common-util';
 const ERR_REVIEW_STATUS = 'Couldn’t change file review status.';
 const MSG_LOADING_BLAME = 'Loading blame...';
 const MSG_LOADED_BLAME = 'Blame loaded';
@@ -291,7 +292,7 @@
       [Shortcut.OPEN_DIFF_PREFS]: '_handleCommaKey',
       [Shortcut.TOGGLE_DIFF_MODE]: '_handleToggleDiffMode',
       [Shortcut.TOGGLE_FILE_REVIEWED]: '_throttledToggleFileReviewed',
-      [Shortcut.EXPAND_ALL_DIFF_CONTEXT]: '_handleExpandAllDiffContext',
+      [Shortcut.TOGGLE_ALL_DIFF_CONTEXT]: '_handleToggleAllDiffContext',
       [Shortcut.NEXT_UNREVIEWED_FILE]: '_handleNextUnreviewedFile',
       [Shortcut.TOGGLE_BLAME]: '_handleToggleBlame',
       [Shortcut.TOGGLE_HIDE_ALL_COMMENT_THREADS]:
@@ -370,7 +371,7 @@
   }
 
   _getChangeEdit() {
-    if (!this._changeNum) throw new Error('Missing this._changeNum');
+    assertIsDefined(this._changeNum, '_changeNum');
     return this.restApiService.getChangeEdit(this._changeNum);
   }
 
@@ -711,7 +712,12 @@
     );
   }
 
-  _navToFile(path: string, fileList: string[], direction: -1 | 1) {
+  _navToFile(
+    path: string,
+    fileList: string[],
+    direction: -1 | 1,
+    navigateToFirstComment?: boolean
+  ) {
     const newPath = this._getNavLinkPath(path, fileList, direction);
     if (!newPath) return;
     if (!this._change) return;
@@ -727,11 +733,18 @@
     }
 
     if (!newPath.path) return;
+    let lineNum;
+    if (navigateToFirstComment)
+      lineNum = this._changeComments?.getCommentsForPath(
+        newPath.path,
+        this._patchRange
+      )?.[0].line;
     GerritNav.navigateToDiff(
       this._change,
       newPath.path,
       this._patchRange.patchNum,
-      this._patchRange.basePatchNum
+      this._patchRange.basePatchNum,
+      lineNum
     );
   }
 
@@ -968,7 +981,7 @@
         leftSide = !!this.params.leftSide;
       }
     }
-    if (!this._patchRange) throw new Error('Failed to initialize patchRange.');
+    assertIsDefined(this._patchRange, '_patchRange');
     this._initLineOfInterestAndCursor(leftSide);
 
     if (this.params?.commentId) {
@@ -1040,10 +1053,10 @@
         this._initPatchRange();
         this._initCommitRange();
 
-        if (!this._path) throw new Error('path must be defined');
+        assertIsDefined(this._path, '_path');
         if (!this._changeComments)
           throw new Error('change comments must be defined');
-        if (!this._patchRange) throw new Error('patch range must be defined');
+        assertIsDefined(this._patchRange, '_patchRange');
 
         // TODO(dhruvsri): check if basePath should be set here
         this.$.diffHost.threads = this._changeComments.getThreadsBySideForFile(
@@ -1070,9 +1083,9 @@
         if (!this._diff) throw new Error('Missing this._diff');
         const fileUnchanged = this._isFileUnchanged(this._diff);
         if (fileUnchanged && value.commentLink) {
-          if (!this._change) throw new Error('Missing this._change');
-          if (!this._path) throw new Error('Missing this._path');
-          if (!this._patchRange) throw new Error('Missing this._patchRange');
+          assertIsDefined(this._change, '_change');
+          assertIsDefined(this._path, '_path');
+          assertIsDefined(this._patchRange, '_patchRange');
 
           if (this._patchRange.basePatchNum === ParentPatchSetNum) {
             // file is unchanged between Base vs X
@@ -1481,7 +1494,7 @@
   }
 
   _loadComments(patchSet?: PatchSetNum) {
-    if (!this._changeNum) throw new Error('Missing this._changeNum');
+    assertIsDefined(this._changeNum, '_changeNum');
     return this.$.commentAPI
       .loadAll(this._changeNum, patchSet)
       .then(comments => {
@@ -1517,7 +1530,7 @@
   }
 
   _getDiffDrafts() {
-    if (!this._changeNum) throw new Error('Missing this._changeNum');
+    assertIsDefined(this._changeNum, '_changeNum');
 
     return this.restApiService.getDiffDrafts(this._changeNum);
   }
@@ -1732,10 +1745,10 @@
     return '';
   }
 
-  _handleExpandAllDiffContext(e: CustomKeyboardEvent) {
+  _handleToggleAllDiffContext(e: CustomKeyboardEvent) {
     if (this.shouldSuppressKeyboardShortcut(e)) return;
 
-    this.$.diffHost.expandAllContext();
+    this.$.diffHost.toggleAllContext();
   }
 
   _computeDiffPrefsDisabled(disableDiffPrefs?: boolean, loggedIn?: boolean) {
@@ -1757,6 +1770,23 @@
     this._navToFile(this._path, unreviewedFiles, 1);
   }
 
+  _navigateToNextFileWithCommentThread() {
+    if (!this._path) return;
+    if (!this._fileList) return;
+    if (!this._patchRange) return;
+    if (!this._change) return;
+    const hasComment = (path: string) => {
+      return (
+        this._changeComments?.getCommentsForPath(path, this._patchRange!)
+          ?.length ?? 0 > 0
+      );
+    };
+    const filesWithComments = this._fileList.filter(
+      file => file === this._path || hasComment(file)
+    );
+    this._navToFile(this._path, filesWithComments, 1, true);
+  }
+
   _handleReloadingDiffPreference() {
     this._getDiffPreferences();
   }
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view_html.ts b/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view_html.ts
index a00927c..bfb0cd8 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view_html.ts
+++ b/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view_html.ts
@@ -428,6 +428,7 @@
   <gr-diff-cursor
     id="cursor"
     on-navigate-to-next-unreviewed-file="_handleNextUnreviewedFile"
+    on-navigate-to-next-file-with-comments="_navigateToNextFileWithCommentThread"
   ></gr-diff-cursor>
   <gr-comment-api id="commentAPI"></gr-comment-api>
 `;
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view_test.js b/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view_test.js
index 34e74b6..b5473a3 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view_test.js
+++ b/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view_test.js
@@ -21,7 +21,7 @@
 import {ChangeStatus} from '../../../constants/constants.js';
 import {TestKeyboardShortcutBinder} from '../../../test/test-utils.js';
 import {Shortcut} from '../../../mixins/keyboard-shortcut-mixin/keyboard-shortcut-mixin.js';
-import {_testOnly_findCommentById} from '../gr-comment-api/gr-comment-api.js';
+import {ChangeComments, _testOnly_findCommentById, _testOnly_getCommentsForPath} from '../gr-comment-api/gr-comment-api.js';
 import {GerritView} from '../../../services/router/router-model.js';
 import {
   createChange,
@@ -63,7 +63,7 @@
       kb.bindShortcut(Shortcut.OPEN_DIFF_PREFS, ',');
       kb.bindShortcut(Shortcut.TOGGLE_DIFF_MODE, 'm');
       kb.bindShortcut(Shortcut.TOGGLE_FILE_REVIEWED, 'r');
-      kb.bindShortcut(Shortcut.EXPAND_ALL_DIFF_CONTEXT, 'shift+x');
+      kb.bindShortcut(Shortcut.TOGGLE_ALL_DIFF_CONTEXT, 'shift+x');
       kb.bindShortcut(Shortcut.EXPAND_ALL_COMMENT_THREADS, 'e');
       kb.bindShortcut(Shortcut.TOGGLE_HIDE_ALL_COMMENT_THREADS, 'h');
       kb.bindShortcut(Shortcut.COLLAPSE_ALL_COMMENT_THREADS, 'shift+e');
@@ -137,6 +137,7 @@
         computeUnresolvedNum: () => {},
         getPaths: () => {},
         getThreadsBySideForFile: () => [],
+        getCommentsForPath: _testOnly_getCommentsForPath,
         findCommentById: _testOnly_findCommentById,
 
       }));
@@ -465,11 +466,53 @@
       assert.equal(element._setReviewed.lastCall.args[0], true);
     });
 
-    test('shift+x shortcut expands all diff context', () => {
-      const expandStub = sinon.stub(element.$.diffHost, 'expandAllContext');
+    test('moveToNextCommentThread navigates to next file', () => {
+      const diffNavStub = sinon.stub(GerritNav, 'navigateToDiff');
+      const diffChangeStub = sinon.stub(element, '_navigateToChange');
+      sinon.stub(element.$.cursor, 'isAtEnd').returns(true);
+      element._changeNum = '42';
+      const comment = {
+        'wheatley.md': [{
+          ...createComment(),
+          patch_set: 10,
+          line: 21,
+        }],
+      };
+      element._changeComments = new ChangeComments(comment);
+      element._patchRange = {
+        basePatchNum: PARENT,
+        patchNum: 10,
+      };
+      element._change = {
+        _number: 42,
+        revisions: {
+          a: {_number: 10, commit: {parents: []}},
+        },
+      };
+      element._files = getFilesFromFileList(
+          ['chell.go', 'glados.txt', 'wheatley.md']);
+      element._path = 'glados.txt';
+      element.changeViewState.selectedFileIndex = 1;
+      element._loggedIn = true;
+
+      MockInteractions.pressAndReleaseKeyOn(element, 78, 'shift', 'n');
+      flush();
+      assert.isTrue(diffNavStub.calledWithExactly(
+          element._change, 'wheatley.md', 10, PARENT, 21));
+
+      element._path = 'wheatley.md'; // navigated to next file
+
+      MockInteractions.pressAndReleaseKeyOn(element, 78, 'shift', 'n');
+      flush();
+
+      assert.isTrue(diffChangeStub.called);
+    });
+
+    test('shift+x shortcut toggles all diff context', () => {
+      const toggleStub = sinon.stub(element.$.diffHost, 'toggleAllContext');
       MockInteractions.pressAndReleaseKeyOn(element, 88, 'shift', 'x');
       flush();
-      assert.isTrue(expandStub.called);
+      assert.isTrue(toggleStub.called);
     });
 
     test('diff against base', () => {
@@ -621,14 +664,14 @@
       MockInteractions.pressAndReleaseKeyOn(element, 221, null, ']');
       assert.isTrue(element._loading);
       assert(diffNavStub.lastCall.calledWithExactly(element._change,
-          'wheatley.md', 10, 5),
+          'wheatley.md', 10, 5, undefined),
       'Should navigate to /c/42/5..10/wheatley.md');
       element._path = 'wheatley.md';
 
       MockInteractions.pressAndReleaseKeyOn(element, 219, null, '[');
       assert.isTrue(element._loading);
       assert(diffNavStub.lastCall.calledWithExactly(element._change,
-          'glados.txt', 10, 5),
+          'glados.txt', 10, 5, undefined),
       'Should navigate to /c/42/5..10/glados.txt');
       element._path = 'glados.txt';
 
@@ -638,7 +681,8 @@
           element._change,
           'chell.go',
           10,
-          5),
+          5,
+          undefined),
       'Should navigate to /c/42/5..10/chell.go');
       element._path = 'chell.go';
 
@@ -692,13 +736,13 @@
 
       MockInteractions.pressAndReleaseKeyOn(element, 221, null, ']');
       assert(diffNavStub.lastCall.calledWithExactly(element._change,
-          'wheatley.md', 1, PARENT),
+          'wheatley.md', 1, PARENT, undefined),
       'Should navigate to /c/42/1/wheatley.md');
       element._path = 'wheatley.md';
 
       MockInteractions.pressAndReleaseKeyOn(element, 219, null, '[');
       assert(diffNavStub.lastCall.calledWithExactly(element._change,
-          'glados.txt', 1, PARENT),
+          'glados.txt', 1, PARENT, undefined),
       'Should navigate to /c/42/1/glados.txt');
       element._path = 'glados.txt';
 
@@ -707,7 +751,8 @@
           element._change,
           'chell.go',
           1,
-          PARENT), 'Should navigate to /c/42/1/chell.go');
+          PARENT,
+          undefined), 'Should navigate to /c/42/1/chell.go');
       element._path = 'chell.go';
 
       changeNavStub.reset();
diff --git a/polygerrit-ui/app/elements/diff/gr-diff/gr-diff.ts b/polygerrit-ui/app/elements/diff/gr-diff/gr-diff.ts
index 7946061..6974a76 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff/gr-diff.ts
+++ b/polygerrit-ui/app/elements/diff/gr-diff/gr-diff.ts
@@ -55,7 +55,11 @@
   PolymerDomWrapper,
 } from '../../../types/types';
 import {CommentRangeLayer} from '../gr-ranged-comment-layer/gr-ranged-comment-layer';
-import {DiffViewMode, Side} from '../../../constants/constants';
+import {
+  createDefaultDiffPrefs,
+  DiffViewMode,
+  Side,
+} from '../../../constants/constants';
 import {KeyLocations} from '../gr-diff-processor/gr-diff-processor';
 import {FlattenedNodesObserver} from '@polymer/polymer/lib/utils/flattened-nodes-observer';
 import {PolymerDeepPropertyChange} from '@polymer/polymer/interfaces';
@@ -66,15 +70,18 @@
 // @ts-ignore
 import * as shadow from 'shadow-selection-polyfill/shadow.js';
 
-import {CreateCommentEventDetail as CreateCommentEventDetailApi} from '../../../api/diff';
+import {
+  CreateCommentEventDetail as CreateCommentEventDetailApi,
+  RenderPreferences,
+} from '../../../api/diff';
 import {isSafari} from '../../../utils/dom-util';
+import {assertIsDefined} from '../../../utils/common-util';
 
 const NO_NEWLINE_BASE = 'No newline at end of base file.';
 const NO_NEWLINE_REVISION = 'No newline at end of revision file.';
 
 const LARGE_DIFF_THRESHOLD_LINES = 10000;
 const FULL_CONTEXT = -1;
-const LIMITED_CONTEXT = 10;
 
 const COMMIT_MSG_PATH = '/COMMIT_MSG';
 /**
@@ -158,6 +165,9 @@
   @property({type: Object, observer: '_prefsObserver'})
   prefs?: DiffPreferencesInfo;
 
+  @property({type: Object, observer: '_renderPrefsChanged'})
+  renderPrefs?: RenderPreferences;
+
   @property({type: Boolean})
   displayLine = false;
 
@@ -173,6 +183,10 @@
   @property({type: Array})
   _commentRanges: CommentRangeLayer[] = [];
 
+  // explicitly highlight a range if it is not associated with any comment
+  @property({type: Object})
+  highlightRange?: CommentRange;
+
   @property({type: Array})
   coverageRanges: CoverageRange[] = [];
 
@@ -413,6 +427,14 @@
     if (addedCommentRanges && addedCommentRanges.length) {
       this.push('_commentRanges', ...addedCommentRanges);
     }
+    if (this.highlightRange) {
+      this.push('_commentRanges', {
+        side: Side.RIGHT,
+        range: this.highlightRange,
+        hovering: true,
+        rootId: '',
+      });
+    }
   }
 
   /**
@@ -608,7 +630,7 @@
     const contentEl = this.$.diffBuilder.getContentTdByLineEl(lineEl);
     if (!contentEl) throw new Error('content el not found for line el');
     side = side ?? this._getCommentSideByLineAndContent(lineEl, contentEl);
-    if (!this.path) throw new Error('must have a path to create comments');
+    assertIsDefined(this.path, 'path');
     this.dispatchEvent(
       new CustomEvent<CreateCommentEventDetail>('create-comment', {
         bubbles: true,
@@ -727,6 +749,16 @@
     }
   }
 
+  _renderPrefsChanged(renderPrefs?: RenderPreferences) {
+    if (!renderPrefs) return;
+    if (renderPrefs.hide_left_side) {
+      this.classList.add('no-left');
+    }
+    if (renderPrefs.disable_context_control_buttons) {
+      this.updateStyles({'--context-control-display': 'none'});
+    }
+  }
+
   _diffChanged(newValue?: DiffInfo) {
     this._setLoading(true);
     this._cleanup();
@@ -940,8 +972,12 @@
     this._debounceRenderDiffTable();
   }
 
-  _handleLimitedBypass() {
-    this._safetyBypass = LIMITED_CONTEXT;
+  _collapseContext() {
+    // Uses the default context amount if the preference is for the entire file.
+    this._safetyBypass =
+      this.prefs?.context && this.prefs.context >= 0
+        ? null
+        : createDefaultDiffPrefs().context;
     this._debounceRenderDiffTable();
   }
 
@@ -953,8 +989,15 @@
     return errorMessage ? 'showError' : '';
   }
 
-  expandAllContext() {
-    this._handleFullBypass();
+  toggleAllContext() {
+    if (!this.prefs) {
+      return;
+    }
+    if (this._getBypassPrefs(this.prefs).context < 0) {
+      this._collapseContext();
+    } else {
+      this._handleFullBypass();
+    }
   }
 
   _computeNewlineWarning(warnLeft: boolean, warnRight: boolean) {
diff --git a/polygerrit-ui/app/elements/diff/gr-diff/gr-diff_html.ts b/polygerrit-ui/app/elements/diff/gr-diff/gr-diff_html.ts
index b0f48ce..4d0e566 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff/gr-diff_html.ts
+++ b/polygerrit-ui/app/elements/diff/gr-diff/gr-diff_html.ts
@@ -272,6 +272,7 @@
 
     /* Context controls */
     .contextControl {
+      display: var(--context-control-display, table-row-group);
       background-color: transparent;
       border: none;
       --divider-height: var(--spacing-s);
@@ -622,7 +623,7 @@
       Prevented render because "Whole file" is enabled and this diff is very
       large (about [[_diffLength]] lines).
     </p>
-    <gr-button on-click="_handleLimitedBypass">
+    <gr-button on-click="_collapseContext">
       Render with limited context
     </gr-button>
     <gr-button on-click="_handleFullBypass">
diff --git a/polygerrit-ui/app/elements/diff/gr-diff/gr-diff_test.js b/polygerrit-ui/app/elements/diff/gr-diff/gr-diff_test.js
index ec44f92..6fef223 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff/gr-diff_test.js
+++ b/polygerrit-ui/app/elements/diff/gr-diff/gr-diff_test.js
@@ -161,12 +161,10 @@
         element.patchRange = {basePatchNum: 'PARENT', patchNum: 1};
         element.isImageDiff = true;
         element.prefs = {
-          auto_hide_diff_table_header: true,
           context: 10,
           cursor_blink_rate: 0,
           font_size: 12,
           ignore_whitespace: 'IGNORE_NONE',
-          intraline_difference: true,
           line_length: 100,
           line_wrapping: false,
           show_line_endings: true,
@@ -497,12 +495,11 @@
           line_length: 100,
           cursor_blink_rate: 0,
           line_wrapping: false,
-          intraline_difference: true,
+
           show_line_endings: true,
           show_tabs: true,
           show_whitespace_errors: true,
           syntax_highlighting: true,
-          auto_hide_diff_table_header: true,
           theme: 'DEFAULT',
           ignore_whitespace: 'IGNORE_NONE',
         };
@@ -797,6 +794,43 @@
       element.addEventListener('render', rendered);
       element._renderDiffTable();
     });
+
+    test('toggles expand context using bypass', async () => {
+      element.prefs = {...MINIMAL_PREFS, context: 3};
+
+      element.toggleAllContext();
+      element._renderDiffTable();
+      await flush();
+
+      assert.equal(element.prefs.context, 3);
+      assert.equal(element._safetyBypass, -1);
+      assert.equal(renderStub.firstCall.lastArg.context, -1);
+    });
+
+    test('toggles collapse context from bypass', async () => {
+      element.prefs = {...MINIMAL_PREFS, context: 3};
+      element._safetyBypass = -1;
+
+      element.toggleAllContext();
+      element._renderDiffTable();
+      await flush();
+
+      assert.equal(element.prefs.context, 3);
+      assert.isNull(element._safetyBypass);
+      assert.equal(renderStub.firstCall.lastArg.context, 3);
+    });
+
+    test('toggles collapse context from pref using default', async () => {
+      element.prefs = {...MINIMAL_PREFS, context: -1};
+
+      element.toggleAllContext();
+      element._renderDiffTable();
+      await flush();
+
+      assert.equal(element.prefs.context, -1);
+      assert.equal(element._safetyBypass, 10);
+      assert.equal(renderStub.firstCall.lastArg.context, 10);
+    });
   });
 
   suite('blame', () => {
@@ -959,11 +993,10 @@
     element = basicFixture.instantiate();
     element.prefs = {
       ignore_whitespace: ignore_whitespace || 'IGNORE_ALL',
-      auto_hide_diff_table_header: true,
       context: 10,
       cursor_blink_rate: 0,
       font_size: 12,
-      intraline_difference: true,
+
       line_length: 100,
       line_wrapping: false,
       show_line_endings: true,
diff --git a/polygerrit-ui/app/elements/edit/gr-editor-view/gr-editor-view.ts b/polygerrit-ui/app/elements/edit/gr-editor-view/gr-editor-view.ts
index f6f4395..1864598 100644
--- a/polygerrit-ui/app/elements/edit/gr-editor-view/gr-editor-view.ts
+++ b/polygerrit-ui/app/elements/edit/gr-editor-view/gr-editor-view.ts
@@ -45,6 +45,7 @@
 import {fireAlert, fireTitleChange} from '../../../utils/event-util';
 import {appContext} from '../../../services/app-context';
 import {ErrorCallback} from '../../../api/rest';
+import {assertIsDefined} from '../../../utils/common-util';
 
 const RESTORED_MESSAGE = 'Content restored from a previous edit.';
 const SAVING_MESSAGE = 'Saving changes...';
@@ -326,7 +327,7 @@
   }
 
   _handlePublishTap() {
-    if (!this._changeNum) throw new Error('missing changeNum');
+    assertIsDefined(this._changeNum, '_changeNum');
 
     const changeNum = this._changeNum;
     this._saveEdit().then(() => {
@@ -347,7 +348,7 @@
           handleError
         )
         .then(() => {
-          if (!this._change) throw new Error('missing change');
+          assertIsDefined(this._change, '_change');
           GerritNav.navigateToChange(this._change);
         });
     });
diff --git a/polygerrit-ui/app/elements/gr-app-element.ts b/polygerrit-ui/app/elements/gr-app-element.ts
index cdc9f98..ec3bd0f 100644
--- a/polygerrit-ui/app/elements/gr-app-element.ts
+++ b/polygerrit-ui/app/elements/gr-app-element.ts
@@ -206,7 +206,7 @@
 
   private reporting = appContext.reportingService;
 
-  private restApiService = appContext.restApiService;
+  private readonly restApiService = appContext.restApiService;
 
   keyboardShortcuts() {
     return {
@@ -392,7 +392,7 @@
     }
     this.bindShortcut(Shortcut.NEXT_CHUNK, 'n');
     this.bindShortcut(Shortcut.PREV_CHUNK, 'p');
-    this.bindShortcut(Shortcut.EXPAND_ALL_DIFF_CONTEXT, 'shift+x');
+    this.bindShortcut(Shortcut.TOGGLE_ALL_DIFF_CONTEXT, 'shift+x');
     this.bindShortcut(Shortcut.NEXT_COMMENT_THREAD, 'shift+n');
     this.bindShortcut(Shortcut.PREV_COMMENT_THREAD, 'shift+p');
     this.bindShortcut(
diff --git a/polygerrit-ui/app/elements/shared/gr-change-status/gr-change-status.ts b/polygerrit-ui/app/elements/shared/gr-change-status/gr-change-status.ts
index d83ca49..7cf9bb1 100644
--- a/polygerrit-ui/app/elements/shared/gr-change-status/gr-change-status.ts
+++ b/polygerrit-ui/app/elements/shared/gr-change-status/gr-change-status.ts
@@ -35,9 +35,9 @@
   "It will not appear on dashboards unless you are CC'ed or assigned, " +
   'and email notifications will be silenced until the review is started.';
 
-const MERGE_CONFLICT_TOOLTIP =
+export const MERGE_CONFLICT_TOOLTIP =
   'This change has merge conflicts. ' +
-  'Download the patch and run "git rebase master". ' +
+  'Download the patch and run "git rebase". ' +
   'Upload a new patchset after resolving all merge conflicts.';
 
 const PRIVATE_TOOLTIP =
diff --git a/polygerrit-ui/app/elements/shared/gr-change-status/gr-change-status_test.js b/polygerrit-ui/app/elements/shared/gr-change-status/gr-change-status_test.js
index 770a21c..16fc664 100644
--- a/polygerrit-ui/app/elements/shared/gr-change-status/gr-change-status_test.js
+++ b/polygerrit-ui/app/elements/shared/gr-change-status/gr-change-status_test.js
@@ -17,6 +17,7 @@
 
 import '../../../test/common-test-setup-karma.js';
 import './gr-change-status.js';
+import {MERGE_CONFLICT_TOOLTIP} from './gr-change-status.js';
 
 const basicFixture = fixtureFromElement('gr-change-status');
 
@@ -24,10 +25,6 @@
     'It will not appear on dashboards unless you are CC\'ed or assigned, ' +
     'and email notifications will be silenced until the review is started.';
 
-const MERGE_CONFLICT_TOOLTIP = 'This change has merge conflicts. ' +
-  'Download the patch and run "git rebase master". ' +
-  'Upload a new patchset after resolving all merge conflicts.';
-
 const PRIVATE_TOOLTIP = 'This change is only visible to its owner and ' +
     'current reviewers (or anyone with "View Private Changes" permission).';
 
diff --git a/polygerrit-ui/app/elements/shared/gr-comment-thread/gr-comment-thread.ts b/polygerrit-ui/app/elements/shared/gr-comment-thread/gr-comment-thread.ts
index 6ee82c9..a5b7df7 100644
--- a/polygerrit-ui/app/elements/shared/gr-comment-thread/gr-comment-thread.ts
+++ b/polygerrit-ui/app/elements/shared/gr-comment-thread/gr-comment-thread.ts
@@ -17,6 +17,7 @@
 import '../../../styles/shared-styles';
 import '../gr-storage/gr-storage';
 import '../gr-comment/gr-comment';
+import '../../diff/gr-diff/gr-diff';
 import {dom, EventApi} from '@polymer/polymer/lib/legacy/polymer.dom';
 import {GestureEventListeners} from '@polymer/polymer/lib/mixins/gesture-event-listeners';
 import {LegacyElementMixin} from '@polymer/polymer/lib/legacy/legacy-element-mixin';
@@ -24,6 +25,7 @@
 import {htmlTemplate} from './gr-comment-thread_html';
 import {KeyboardShortcutMixin} from '../../../mixins/keyboard-shortcut-mixin/keyboard-shortcut-mixin';
 import {
+  computeDiffFromContext,
   isDraft,
   isRobot,
   sortComments,
@@ -33,9 +35,14 @@
 } from '../../../utils/comment-util';
 import {GerritNav} from '../../core/gr-navigation/gr-navigation';
 import {appContext} from '../../../services/app-context';
-import {CommentSide, Side, SpecialFilePath} from '../../../constants/constants';
+import {
+  CommentSide,
+  createDefaultDiffPrefs,
+  Side,
+  SpecialFilePath,
+} from '../../../constants/constants';
 import {computeDisplayPath} from '../../../utils/path-list-util';
-import {customElement, observe, property} from '@polymer/decorators';
+import {computed, customElement, observe, property} from '@polymer/decorators';
 import {
   CommentRange,
   ConfigInfo,
@@ -50,6 +57,10 @@
 import {CustomKeyboardEvent} from '../../../types/events';
 import {LineNumber, FILE} from '../../diff/gr-diff/gr-diff-line';
 import {GrButton} from '../gr-button/gr-button';
+import {KnownExperimentId} from '../../../services/flags/flags';
+import {DiffInfo, DiffPreferencesInfo} from '../../../types/diff';
+import {RenderPreferences} from '../../../api/diff';
+import {check, assertIsDefined} from '../../../utils/common-util';
 
 const UNRESOLVED_EXPAND_COUNT = 5;
 const NEWLINE_PATTERN = /\n/g;
@@ -122,7 +133,7 @@
   patchNum?: PatchSetNum;
 
   @property({type: String})
-  path?: string;
+  path: string | undefined;
 
   @property({type: String, observer: '_projectNameChanged'})
   projectName?: RepoName;
@@ -164,6 +175,15 @@
   @property({type: Object})
   _projectConfig?: ConfigInfo;
 
+  @property({type: Object})
+  _prefs: DiffPreferencesInfo = createDefaultDiffPrefs();
+
+  @property({type: Object})
+  _renderPrefs: RenderPreferences = {
+    hide_left_side: true,
+    disable_context_control_buttons: true,
+  };
+
   @property({type: Boolean, reflectToAttribute: true})
   isRobotComment = false;
 
@@ -176,6 +196,9 @@
   @property({type: Boolean})
   showPatchset = true;
 
+  @property({type: Boolean})
+  showCommentContext = false;
+
   get keyBindings() {
     return {
       'e shift+e': '_handleEKey',
@@ -188,6 +211,10 @@
 
   readonly storage = new GrStorage();
 
+  private isCommentContextExperimentEnabled = this.flagsService.isEnabled(
+    KnownExperimentId.COMMENT_CONTEXT
+  );
+
   readonly restApiService = appContext.restApiService;
 
   /** @override */
@@ -204,9 +231,34 @@
     this._getLoggedIn().then(loggedIn => {
       this._showActions = loggedIn;
     });
+    this.restApiService.getDiffPreferences().then(prefs => {
+      if (!prefs) return;
+      this._prefs = {
+        ...prefs,
+        show_file_comment_button: false,
+        // override explicitly so that diff doesn't take too much width
+        // compared to the context
+        line_wrapping: false,
+      };
+    });
     this._setInitialExpandedState();
   }
 
+  @computed('comments', 'path')
+  get _diff() {
+    if (this.comments === undefined || this.path === undefined) return;
+    if (!this.comments[0]?.context_lines?.length) return;
+    return computeDiffFromContext(this.comments[0].context_lines, this.path);
+  }
+
+  _shouldShowCommentContext(diff?: DiffInfo) {
+    return (
+      this.isCommentContextExperimentEnabled &&
+      this.showCommentContext &&
+      !!diff
+    );
+  }
+
   addOrEditDraft(lineNum?: LineNumber, rangeParam?: CommentRange) {
     const lastComment = this.comments[this.comments.length - 1] || {};
     if (isDraft(lastComment)) {
@@ -266,6 +318,32 @@
     return GerritNav.getUrlForComment(changeNum, projectName, id);
   }
 
+  getHighlightRange() {
+    const comment = this.comments?.[0];
+    if (!comment) return undefined;
+    if (comment.range) return comment.range;
+    if (comment.line) {
+      return {
+        start_line: comment.line,
+        start_character: 0,
+        end_line: comment.line,
+        end_character: 0,
+      };
+    }
+    return undefined;
+  }
+
+  _getUrlForViewDiff(comments: UIComment[]) {
+    assertIsDefined(this.changeNum, 'changeNum');
+    assertIsDefined(this.projectName, 'projectName');
+    check(comments.length > 0, 'comment not found');
+    return GerritNav.getUrlForComment(
+      this.changeNum,
+      this.projectName,
+      comments[0].id!
+    );
+  }
+
   _getDiffUrlForComment(
     projectName?: RepoName,
     changeNum?: NumericChangeId,
@@ -555,8 +633,8 @@
   }
 
   _handleCommentDiscard(e: Event) {
-    if (!this.changeNum) throw new Error('changeNum is missing');
-    if (!this.patchNum) throw new Error('patchNum is missing');
+    assertIsDefined(this.changeNum, 'changeNum');
+    assertIsDefined(this.patchNum, 'patchNum');
     const diffCommentEl = (dom(e) as EventApi).rootTarget as GrComment;
     const comment = diffCommentEl.comment;
     const idx = this._indexOf(comment, this.comments);
diff --git a/polygerrit-ui/app/elements/shared/gr-comment-thread/gr-comment-thread_html.ts b/polygerrit-ui/app/elements/shared/gr-comment-thread/gr-comment-thread_html.ts
index 1a89a79..76bbd67 100644
--- a/polygerrit-ui/app/elements/shared/gr-comment-thread/gr-comment-thread_html.ts
+++ b/polygerrit-ui/app/elements/shared/gr-comment-thread/gr-comment-thread_html.ts
@@ -23,6 +23,12 @@
       font-size: var(--font-size-normal);
       font-weight: var(--font-weight-normal);
       line-height: var(--line-height-normal);
+      /* Explicitly set the background color of the diff to be white. We
+       * cannot use the diff content type ab because of the skip chunk preceding
+       * it, diff processor assumes the chunk of type skip/ab can be collapsed
+       * and hides our diff behind context control buttons.
+       *  */
+      --dark-add-highlight-color: white;
     }
     gr-button {
       margin-left: var(--spacing-m);
@@ -34,24 +40,29 @@
       margin-left: auto;
       padding: var(--spacing-s) var(--spacing-m);
     }
-    #container {
+    .comment-box {
+      width: 80ch;
+      max-width: 100%;
       background-color: var(--comment-background-color);
       color: var(--comment-text-color);
-      display: var(--gr-comment-thread-display, block);
-      margin: 0 var(--spacing-s) var(--spacing-s);
-      white-space: normal;
       box-shadow: var(--elevation-level-2);
       border-radius: var(--border-radius);
+    }
+    #container {
+      display: var(--gr-comment-thread-display, flex);
+      align-items: flex-start;
+      margin: 0 var(--spacing-s) var(--spacing-s);
+      white-space: normal;
       /** This is required for firefox to continue the inheritance */
       -webkit-user-select: inherit;
       -moz-user-select: inherit;
       -ms-user-select: inherit;
       user-select: inherit;
     }
-    #container.unresolved {
+    .comment-box.unresolved {
       background-color: var(--unresolved-comment-background-color);
     }
-    #container.robotComment {
+    .comment-box.robotComment {
       background-color: var(--robot-comment-background-color);
     }
     #commentInfoContainer {
@@ -75,7 +86,18 @@
     .fileName {
       padding: var(--spacing-m) var(--spacing-s) var(--spacing-m);
     }
+    .diff-container {
+      margin-left: var(--spacing-l);
+      border: 1px solid var(--border-color);
+    }
+    .view-diff-container {
+      text-align: end;
+    }
+    .view-diff-button {
+      margin: var(--spacing-m);
+    }
   </style>
+
   <template is="dom-if" if="[[showFilePath]]">
     <template is="dom-if" if="[[showFileName]]">
       <div class="fileName">
@@ -100,71 +122,91 @@
       </template>
     </div>
   </template>
-  <div
-    id="container"
-    class$="[[_computeHostClass(unresolved, isRobotComment)]]"
-  >
-    <template
-      id="commentList"
-      is="dom-repeat"
-      items="[[_orderedComments]]"
-      as="comment"
-    >
-      <gr-comment
-        comment="{{comment}}"
-        comments="{{comments}}"
-        robot-button-disabled="[[_shouldDisableAction(_showActions, _lastComment)]]"
-        change-num="[[changeNum]]"
-        project-name="[[projectName]]"
-        patch-num="[[patchNum]]"
-        draft="[[_isDraft(comment)]]"
-        show-actions="[[_showActions]]"
-        show-patchset="[[showPatchset]]"
-        show-ported-comment="[[_computeShowPortedComment(comment)]]"
-        side="[[comment.side]]"
-        project-config="[[_projectConfig]]"
-        on-create-fix-comment="_handleCommentFix"
-        on-comment-discard="_handleCommentDiscard"
-        on-comment-save="_handleCommentSavedOrDiscarded"
-      ></gr-comment>
-    </template>
-    <div
-      id="commentInfoContainer"
-      hidden$="[[_hideActions(_showActions, _lastComment)]]"
-    >
-      <span id="unresolvedLabel">[[_getUnresolvedLabel(unresolved)]]</span>
-      <div id="actions">
-        <gr-button
-          id="replyBtn"
-          link=""
-          class="action reply"
-          on-click="_handleCommentReply"
-          >Reply</gr-button
-        >
-        <gr-button
-          id="quoteBtn"
-          link=""
-          class="action quote"
-          on-click="_handleCommentQuote"
-          >Quote</gr-button
-        >
-        <template is="dom-if" if="[[unresolved]]">
+  <div id="container">
+    <div class$="[[_computeHostClass(unresolved, isRobotComment)]] comment-box">
+      <template
+        id="commentList"
+        is="dom-repeat"
+        items="[[_orderedComments]]"
+        as="comment"
+      >
+        <gr-comment
+          comment="{{comment}}"
+          comments="{{comments}}"
+          robot-button-disabled="[[_shouldDisableAction(_showActions, _lastComment)]]"
+          change-num="[[changeNum]]"
+          project-name="[[projectName]]"
+          patch-num="[[patchNum]]"
+          draft="[[_isDraft(comment)]]"
+          show-actions="[[_showActions]]"
+          show-patchset="[[showPatchset]]"
+          show-ported-comment="[[_computeShowPortedComment(comment)]]"
+          side="[[comment.side]]"
+          project-config="[[_projectConfig]]"
+          on-create-fix-comment="_handleCommentFix"
+          on-comment-discard="_handleCommentDiscard"
+          on-comment-save="_handleCommentSavedOrDiscarded"
+        ></gr-comment>
+      </template>
+      <div
+        id="commentInfoContainer"
+        hidden$="[[_hideActions(_showActions, _lastComment)]]"
+      >
+        <span id="unresolvedLabel">[[_getUnresolvedLabel(unresolved)]]</span>
+        <div id="actions">
           <gr-button
-            id="ackBtn"
+            id="replyBtn"
             link=""
-            class="action ack"
-            on-click="_handleCommentAck"
-            >Ack</gr-button
+            class="action reply"
+            on-click="_handleCommentReply"
+            >Reply</gr-button
           >
           <gr-button
-            id="doneBtn"
+            id="quoteBtn"
             link=""
-            class="action done"
-            on-click="_handleCommentDone"
-            >Done</gr-button
+            class="action quote"
+            on-click="_handleCommentQuote"
+            >Quote</gr-button
           >
-        </template>
+          <template is="dom-if" if="[[unresolved]]">
+            <gr-button
+              id="ackBtn"
+              link=""
+              class="action ack"
+              on-click="_handleCommentAck"
+              >Ack</gr-button
+            >
+            <gr-button
+              id="doneBtn"
+              link=""
+              class="action done"
+              on-click="_handleCommentDone"
+              >Done</gr-button
+            >
+          </template>
+        </div>
       </div>
     </div>
+    <template is="dom-if" if="[[_shouldShowCommentContext(_diff)]]">
+      <div class="diff-container">
+        <gr-diff
+          id="diff"
+          change-num="[[changeNum]]"
+          diff="[[_diff]]"
+          path="[[path]]"
+          prefs="[[_prefs]]"
+          render-prefs="[[_renderPrefs]]"
+          highlight-range="[[getHighlightRange(comments)]]"
+        >
+        </gr-diff>
+        <div class="view-diff-container">
+          <a href="[[_getUrlForViewDiff(comments)]]">
+            <gr-button link class="view-diff-button" on-click="_handleViewDiff">
+              View Diff
+            </gr-button>
+          </a>
+        </div>
+      </div>
+    </template>
   </div>
 `;
diff --git a/polygerrit-ui/app/elements/shared/gr-comment/gr-comment.ts b/polygerrit-ui/app/elements/shared/gr-comment/gr-comment.ts
index 898aff3..bf376f6 100644
--- a/polygerrit-ui/app/elements/shared/gr-comment/gr-comment.ts
+++ b/polygerrit-ui/app/elements/shared/gr-comment/gr-comment.ts
@@ -61,6 +61,7 @@
 import {OpenFixPreviewEventDetail} from '../../../types/events';
 import {fireAlert} from '../../../utils/event-util';
 import {pluralize} from '../../../utils/string-util';
+import {assertIsDefined} from '../../../utils/common-util';
 
 const STORAGE_DEBOUNCE_INTERVAL = 400;
 const TOAST_DEBOUNCE_INTERVAL = 200;
@@ -322,7 +323,7 @@
   }
 
   _handlePortedMessageClick() {
-    if (!this.comment) throw new Error('comment not set');
+    assertIsDefined(this.comment, 'comment');
     this.reporting.reportInteraction('navigate-to-original-comment', {
       line: this.comment.line,
       range: this.comment.range,
@@ -496,10 +497,8 @@
     // prior to it being saved.
     this.cancelDebouncer(DEBOUNCER_STORE);
 
-    if (!this.comment?.path) throw new Error('Cannot erase Draft Comment');
-    if (this.changeNum === undefined) {
-      throw new Error('undefined changeNum');
-    }
+    assertIsDefined(this.comment?.path, 'comment.path');
+    assertIsDefined(this.changeNum, 'changeNum');
     this.storage.eraseDraftComment({
       changeNum: this.changeNum,
       patchNum: this._getPatchNum(),
diff --git a/polygerrit-ui/app/elements/shared/gr-hovercard-account/gr-hovercard-account.ts b/polygerrit-ui/app/elements/shared/gr-hovercard-account/gr-hovercard-account.ts
index 592f7903..ddae8ea 100644
--- a/polygerrit-ui/app/elements/shared/gr-hovercard-account/gr-hovercard-account.ts
+++ b/polygerrit-ui/app/elements/shared/gr-hovercard-account/gr-hovercard-account.ts
@@ -45,6 +45,7 @@
 import {ReviewerState} from '../../../constants/constants';
 import {CURRENT} from '../../../utils/patch-set-util';
 import {isInvolved, isRemovableReviewer} from '../../../utils/change-util';
+import {assertIsDefined} from '../../../utils/common-util';
 
 @customElement('gr-hovercard-account')
 export class GrHovercardAccount extends GestureEventListeners(
@@ -163,7 +164,7 @@
   }
 
   _handleChangeReviewerOrCCStatus() {
-    if (!this.change) throw new Error('expected change object to be present');
+    assertIsDefined(this.change, 'change');
     // accountKey() throws an error if _account_id & email is not found, which
     // we want to check before showing reloading toast
     const _accountKey = accountKey(this.account);
diff --git a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-annotation-actions-context.ts b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-annotation-actions-context.ts
index 6a4da7b..9f9ba6a 100644
--- a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-annotation-actions-context.ts
+++ b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-annotation-actions-context.ts
@@ -29,14 +29,13 @@
  * @param lineNumberEl The TD element of the line number to
  * apply the annotation to using annotateLineNumber.
  * @param line The line object.
- * @param path The file path (eg: /COMMIT_MSG').
+ * @param path The file path (eg: '/COMMIT_MSG').
  * @param changeNum The Gerrit change number.
- * @param patchNum The Gerrit patch number.
  */
 export class GrAnnotationActionsContext implements AnnotationContext {
-  private _contentEl: HTMLElement;
+  contentEl: HTMLElement;
 
-  private _lineNumberEl: HTMLElement;
+  lineNumberEl: HTMLElement;
 
   line: GrDiffLine;
 
@@ -53,9 +52,8 @@
     path: string,
     changeNum: string | number
   ) {
-    this._contentEl = contentEl;
-    this._lineNumberEl = lineNumberEl;
-
+    this.contentEl = contentEl;
+    this.lineNumberEl = lineNumberEl;
     this.line = line;
     this.path = path;
     this.changeNum = Number(changeNum);
@@ -80,12 +78,12 @@
     styleObject: GrStyleObject,
     side: string
   ) {
-    if (this._contentEl?.getAttribute('data-side') === side) {
+    if (this.contentEl?.getAttribute('data-side') === side) {
       GrAnnotation.annotateElement(
-        this._contentEl,
+        this.contentEl,
         offset,
         length,
-        styleObject.getClassName(this._contentEl)
+        styleObject.getClassName(this.contentEl)
       );
     }
   }
@@ -97,8 +95,8 @@
    * @param side The side of the update. ('left' or 'right')
    */
   annotateLineNumber(styleObject: GrStyleObject, side: string) {
-    if (this._lineNumberEl?.classList.contains(side)) {
-      styleObject.apply(this._lineNumberEl);
+    if (this.lineNumberEl?.classList.contains(side)) {
+      styleObject.apply(this.lineNumberEl);
     }
   }
 }
diff --git a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-annotation-actions-js-api.ts b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-annotation-actions-js-api.ts
index 4abc6e1..95252cf 100644
--- a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-annotation-actions-js-api.ts
+++ b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-annotation-actions-js-api.ts
@@ -21,65 +21,37 @@
 import {EventType, PluginApi} from '../../../api/plugin';
 import {appContext} from '../../../services/app-context';
 import {
-  AddLayerFunc,
+  AnnotationCallback,
   AnnotationPluginApi,
   CoverageProvider,
-  NotifyFunc,
 } from '../../../api/annotation';
 
 export class GrAnnotationActionsInterface implements AnnotationPluginApi {
-  // Collect all annotation layers instantiated by getLayer. Will be used when
-  // notifying their listeners in the notify function.
+  /**
+   * Collect all annotation layers instantiated by createLayer. This is only
+   * used for being able to look up the appropriate layer when notify() is
+   * being called by plugins.
+   */
   private annotationLayers: AnnotationLayer[] = [];
 
-  private coverageProvider: CoverageProvider | null = null;
+  private coverageProvider?: CoverageProvider;
 
-  // Default impl is a no-op.
-  private addLayerFunc: AddLayerFunc = () => {};
+  private annotationCallback?: AnnotationCallback;
 
-  reporting = appContext.reportingService;
+  private readonly reporting = appContext.reportingService;
 
   constructor(private readonly plugin: PluginApi) {
-    // Return this instance when there is an annotatediff event.
     plugin.on(EventType.ANNOTATE_DIFF, this);
   }
 
-  /**
-   * Register a function to call to apply annotations. Plugins should use
-   * GrAnnotationActionsContext.annotateRange and
-   * GrAnnotationActionsContext.annotateLineNumber to apply a CSS class to the
-   * line content or the line number.
-   *
-   * @param addLayerFunc The function
-   * that will be called when the AnnotationLayer is ready to annotate.
-   */
-  addLayer(addLayerFunc: AddLayerFunc) {
-    this.addLayerFunc = addLayerFunc;
+  setLayer(annotationCallback: AnnotationCallback) {
+    if (this.annotationCallback) {
+      console.warn('Overwriting an existing plugin annotation layer.');
+    }
+    this.annotationCallback = annotationCallback;
     return this;
   }
 
-  /**
-   * The specified function will be called with a notify function for the plugin
-   * to call when it has all required data for annotation. Optional.
-   *
-   * @param notifyFunc See doc of the notify function below to see what it does.
-   */
-  addNotifier(notifyFunc: (n: NotifyFunc) => void) {
-    notifyFunc(
-      (path: string, startRange: number, endRange: number, side: Side) =>
-        this.notify(path, startRange, endRange, side)
-    );
-    return this;
-  }
-
-  /**
-   * The specified function will be called when a gr-diff component is built,
-   * and feeds the returned coverage data into the diff. Optional.
-   *
-   * Be sure to call this only once and only from one plugin. Multiple coverage
-   * providers are not supported. A second call will just overwrite the
-   * provider of the first call.
-   */
   setCoverageProvider(
     coverageProvider: CoverageProvider
   ): GrAnnotationActionsInterface {
@@ -98,23 +70,6 @@
     return this.coverageProvider;
   }
 
-  /**
-   * Returns a checkbox HTMLElement that can be used to toggle annotations
-   * on/off. The checkbox will be initially disabled. Plugins should enable it
-   * when data is ready and should add a click handler to toggle CSS on/off.
-   *
-   * Note1: Calling this method from multiple plugins will only work for the
-   * 1st call. It will print an error message for all subsequent calls
-   * and will not invoke their onAttached functions.
-   * Note2: This method will be deprecated and eventually removed when
-   * https://bugs.chromium.org/p/gerrit/issues/detail?id=8077 is
-   * implemented.
-   *
-   * @param checkboxLabel Will be used as the label for the checkbox.
-   * Optional. "Enable" is used if this is not specified.
-   * @param onAttached The function that will be called
-   * when the checkbox is attached to the page.
-   */
   enableToggleCheckbox(
     checkboxLabel: string,
     onAttached: (checkboxEl: Element | null) => void
@@ -148,16 +103,6 @@
     return this;
   }
 
-  /**
-   * The notify function will call the listeners of all required annotation
-   * layers. Intended to be called by the plugin when all required data for
-   * annotation is available.
-   *
-   * @param path The file path whose listeners should be notified.
-   * @param start The line where the update starts.
-   * @param end The line where the update ends.
-   * @param side The side of the update ('left' or 'right').
-   */
   notify(path: string, start: number, end: number, side: Side) {
     for (const annotationLayer of this.annotationLayers) {
       // Notify only the annotation layer that is associated with the specified
@@ -169,24 +114,25 @@
   }
 
   /**
-   * Should be called to register annotation layers by the framework. Not
-   * intended to be called by plugins.
+   * Factory method called by Gerrit for creating a DiffLayer for each diff that
+   * is rendered.
    *
-   * Don't forget to dispose layer.
-   *
-   * @param path The file path (eg: /COMMIT_MSG').
-   * @param changeNum The Gerrit change number.
+   * Don't forget to also call disposeLayer().
    */
-  getLayer(path: string, changeNum: number) {
+  createLayer(path: string, changeNum: number) {
+    if (!this.annotationCallback) return undefined;
     const annotationLayer = new AnnotationLayer(
       path,
       changeNum,
-      this.addLayerFunc
+      this.annotationCallback
     );
     this.annotationLayers.push(annotationLayer);
     return annotationLayer;
   }
 
+  /**
+   * Called by Gerrit for each diff renderer that had called createLayer().
+   */
   disposeLayer(path: string) {
     this.annotationLayers = this.annotationLayers.filter(
       annotationLayer => annotationLayer.path !== path
@@ -194,6 +140,10 @@
   }
 }
 
+/**
+ * An AnnotationLayer exists for each file that is being rendered. This class is
+ * not exposed to plugins, but being used by Gerrit's diff rendering.
+ */
 export class AnnotationLayer implements DiffLayer {
   private listeners: DiffLayerListener[] = [];
 
@@ -202,13 +152,13 @@
    *
    * @param path The file path (eg: /COMMIT_MSG').
    * @param changeNum The Gerrit change number.
-   * @param addLayerFunc The function
+   * @param annotationCallback The function
    * that will be called when the AnnotationLayer is ready to annotate.
    */
   constructor(
     readonly path: string,
     private readonly changeNum: number,
-    private readonly addLayerFunc: AddLayerFunc
+    private readonly annotationCallback: AnnotationCallback
   ) {
     this.listeners = [];
   }
@@ -230,7 +180,8 @@
   }
 
   /**
-   * Layer method to add annotations to a line.
+   * Called by Gerrit during diff rendering for each line. Delegates to the
+   * plugin provided callback for potentially annotating this line.
    *
    * @param contentEl The DIV.contentText element of the line
    * content to apply the annotation to using annotateRange.
@@ -243,18 +194,20 @@
     lineNumberEl: HTMLElement,
     line: GrDiffLine
   ) {
-    const annotationActionsContext = new GrAnnotationActionsContext(
+    const context = new GrAnnotationActionsContext(
       contentEl,
       lineNumberEl,
       line,
       this.path,
       this.changeNum
     );
-    this.addLayerFunc(annotationActionsContext);
+    this.annotationCallback(context);
   }
 
   /**
-   * Notify Layer listeners of changes to annotations.
+   * Notify layer listeners (which typically is just Gerrit's diff renderer) of
+   * changes to annotations after the diff rendering had already completed. This
+   * is indirectly called by plugins using the AnnotationPluginApi.notify().
    *
    * @param start The line where the update starts.
    * @param end The line where the update ends.
diff --git a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-annotation-actions-js-api_test.js b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-annotation-actions-js-api_test.js
index 7ae34cf..9811f99 100644
--- a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-annotation-actions-js-api_test.js
+++ b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-annotation-actions-js-api_test.js
@@ -59,9 +59,9 @@
       assert.equal(context.line, line);
       assert.equal(context.changeNum, changeNum);
     };
-    annotationActions.addLayer(testLayerFunc);
+    annotationActions.setLayer(testLayerFunc);
 
-    const annotationLayer = annotationActions.getLayer(
+    const annotationLayer = annotationActions.createLayer(
         '/dummy/path', changeNum);
 
     const lineNumberEl = document.createElement('td');
@@ -72,27 +72,19 @@
   test('add notifier', () => {
     const path1 = '/dummy/path1';
     const path2 = '/dummy/path2';
-    const annotationLayer1 = annotationActions.getLayer(path1, 1);
-    const annotationLayer2 = annotationActions.getLayer(path2, 1);
+    annotationActions.setLayer(context => {});
+    const annotationLayer1 = annotationActions.createLayer(path1, 1);
+    const annotationLayer2 = annotationActions.createLayer(path2, 1);
     const layer1Spy = sinon.spy(annotationLayer1, 'notifyListeners');
     const layer2Spy = sinon.spy(annotationLayer2, 'notifyListeners');
 
-    let notify;
-    let notifyFuncCalled;
-    const notifyFunc = n => {
-      notifyFuncCalled = true;
-      notify = n;
-    };
-    annotationActions.addNotifier(notifyFunc);
-    assert.isTrue(notifyFuncCalled);
-
     // Assert that no layers are invoked with a different path.
-    notify('/dummy/path3', 0, 10, 'right');
+    annotationActions.notify('/dummy/path3', 0, 10, 'right');
     assert.isFalse(layer1Spy.called);
     assert.isFalse(layer2Spy.called);
 
     // Assert that only the 1st layer is invoked with path1.
-    notify(path1, 0, 10, 'right');
+    annotationActions.notify(path1, 0, 10, 'right');
     assert.isTrue(layer1Spy.called);
     assert.isFalse(layer2Spy.called);
 
@@ -101,7 +93,7 @@
     layer2Spy.resetHistory();
 
     // Assert that only the 2nd layer is invoked with path2.
-    notify(path2, 0, 20, 'left');
+    annotationActions.notify(path2, 0, 20, 'left');
     assert.isFalse(layer1Spy.called);
     assert.isTrue(layer2Spy.called);
   });
@@ -143,7 +135,8 @@
   });
 
   test('layer notify listeners', () => {
-    const annotationLayer = annotationActions.getLayer('/dummy/path', 1);
+    annotationActions.setLayer(context => {});
+    const annotationLayer = annotationActions.createLayer('/dummy/path', 1);
     let listenerCalledTimes = 0;
     const startRange = 10;
     const endRange = 20;
diff --git a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-js-api-interface-element.ts b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-js-api-interface-element.ts
index 830fb92..8689ad2 100644
--- a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-js-api-interface-element.ts
+++ b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-js-api-interface-element.ts
@@ -252,8 +252,8 @@
     for (const cb of this._getEventCallbacks(EventType.ANNOTATE_DIFF)) {
       const annotationApi = (cb as unknown) as GrAnnotationActionsInterface;
       try {
-        const layer = annotationApi.getLayer(path, changeNum);
-        layers.push(layer);
+        const layer = annotationApi.createLayer(path, changeNum);
+        if (layer) layers.push(layer);
       } catch (err) {
         this.reporting.error(err);
       }
diff --git a/polygerrit-ui/app/elements/shared/gr-label-info/gr-label-info.ts b/polygerrit-ui/app/elements/shared/gr-label-info/gr-label-info.ts
index 70f68ae..60a07a4 100644
--- a/polygerrit-ui/app/elements/shared/gr-label-info/gr-label-info.ts
+++ b/polygerrit-ui/app/elements/shared/gr-label-info/gr-label-info.ts
@@ -223,7 +223,7 @@
     if (
       !labelInfo ||
       !isDetailedLabelInfo(labelInfo) ||
-      !labelInfo.values[score]
+      !labelInfo.values?.[score]
     ) {
       return '';
     }
diff --git a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface.ts b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface.ts
index 6e17c75..ec59ddc 100644
--- a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface.ts
+++ b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface.ts
@@ -2232,11 +2232,15 @@
     path?: string
   ) {
     if (!basePatchNum && !patchNum && !path) {
-      return this._getDiffComments(changeNum, '/comments');
+      return this._getDiffComments(changeNum, '/comments', {
+        'enable-context': true,
+        'context-padding': 3,
+      });
     }
     return this._getDiffComments(
       changeNum,
       '/comments',
+      {'enable-context': true, 'context-padding': 3},
       basePatchNum,
       patchNum,
       path
@@ -2267,6 +2271,7 @@
     return this._getDiffComments(
       changeNum,
       '/robotcomments',
+      undefined,
       basePatchNum,
       patchNum,
       path
@@ -2305,6 +2310,7 @@
       return this._getDiffComments(
         changeNum,
         '/drafts',
+        undefined,
         basePatchNum,
         patchNum,
         path
@@ -2337,7 +2343,8 @@
 
   _getDiffComments(
     changeNum: NumericChangeId,
-    endpoint: '/comments' | '/drafts'
+    endpoint: '/comments' | '/drafts',
+    params?: FetchParams
   ): Promise<PathToCommentsInfoMap | undefined>;
 
   _getDiffComments(
@@ -2348,6 +2355,7 @@
   _getDiffComments(
     changeNum: NumericChangeId,
     endpoint: '/comments' | '/drafts',
+    params?: FetchParams,
     basePatchNum?: PatchSetNum,
     patchNum?: PatchSetNum,
     path?: string
@@ -2356,6 +2364,7 @@
   _getDiffComments(
     changeNum: NumericChangeId,
     endpoint: '/robotcomments',
+    params?: FetchParams,
     basePatchNum?: PatchSetNum,
     patchNum?: PatchSetNum,
     path?: string
@@ -2364,6 +2373,7 @@
   _getDiffComments(
     changeNum: NumericChangeId,
     endpoint: string,
+    params?: FetchParams,
     basePatchNum?: PatchSetNum,
     patchNum?: PatchSetNum,
     path?: string
@@ -2388,6 +2398,7 @@
           endpoint,
           revision: patchNum,
           reportEndpointAsIs: true,
+          params,
         },
         noAcceptHeader
       ) as Promise<
diff --git a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface_test.js b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface_test.js
index 6db2bff..7cbcaef 100644
--- a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface_test.js
+++ b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface_test.js
@@ -75,7 +75,8 @@
             },
           ],
         }));
-    return element._getDiffComments('42', '', 'PARENT', 1, 'sieve.go').then(
+    return element._getDiffComments('42', '', undefined, 'PARENT', 1,
+        'sieve.go').then(
         obj => {
           assert.equal(obj.baseComments.length, 1);
           assert.deepEqual(obj.baseComments[0], {
@@ -239,7 +240,7 @@
         });
       }
     });
-    return element._getDiffComments('42', '', 1, 2, 'sieve.go').then(
+    return element._getDiffComments('42', '', undefined, 1, 2, 'sieve.go').then(
         obj => {
           assert.equal(obj.baseComments.length, 1);
           assert.deepEqual(obj.baseComments[0], {
@@ -395,12 +396,10 @@
     sinon.stub(element, 'getLoggedIn').callsFake(() => Promise.resolve(false));
 
     return element.getDiffPreferences().then(obj => {
-      assert.equal(obj.auto_hide_diff_table_header, true);
       assert.equal(obj.context, 10);
       assert.equal(obj.cursor_blink_rate, 0);
       assert.equal(obj.font_size, 12);
       assert.equal(obj.ignore_whitespace, 'IGNORE_NONE');
-      assert.equal(obj.intraline_difference, true);
       assert.equal(obj.line_length, 100);
       assert.equal(obj.line_wrapping, false);
       assert.equal(obj.show_line_endings, true);
@@ -408,7 +407,6 @@
       assert.equal(obj.show_whitespace_errors, true);
       assert.equal(obj.syntax_highlighting, true);
       assert.equal(obj.tab_size, 8);
-      assert.equal(obj.theme, 'DEFAULT');
     });
   });
 
diff --git a/polygerrit-ui/app/mixins/keyboard-shortcut-mixin/keyboard-shortcut-mixin.ts b/polygerrit-ui/app/mixins/keyboard-shortcut-mixin/keyboard-shortcut-mixin.ts
index 881e102..63a05aa 100644
--- a/polygerrit-ui/app/mixins/keyboard-shortcut-mixin/keyboard-shortcut-mixin.ts
+++ b/polygerrit-ui/app/mixins/keyboard-shortcut-mixin/keyboard-shortcut-mixin.ts
@@ -175,7 +175,7 @@
   VISIBLE_LINE = 'VISIBLE_LINE',
   NEXT_CHUNK = 'NEXT_CHUNK',
   PREV_CHUNK = 'PREV_CHUNK',
-  EXPAND_ALL_DIFF_CONTEXT = 'EXPAND_ALL_DIFF_CONTEXT',
+  TOGGLE_ALL_DIFF_CONTEXT = 'TOGGLE_ALL_DIFF_CONTEXT',
   NEXT_COMMENT_THREAD = 'NEXT_COMMENT_THREAD',
   PREV_COMMENT_THREAD = 'PREV_COMMENT_THREAD',
   EXPAND_ALL_COMMENT_THREADS = 'EXPAND_ALL_COMMENT_THREADS',
@@ -403,9 +403,9 @@
   'Go to previous diff chunk'
 );
 _describe(
-  Shortcut.EXPAND_ALL_DIFF_CONTEXT,
+  Shortcut.TOGGLE_ALL_DIFF_CONTEXT,
   ShortcutSection.DIFFS,
-  'Expand all diff context'
+  'Toggle all diff context'
 );
 _describe(
   Shortcut.NEXT_COMMENT_THREAD,
diff --git a/polygerrit-ui/app/samples/coverage-plugin.js b/polygerrit-ui/app/samples/coverage-plugin.js
index 9b2b687..8d321c7 100644
--- a/polygerrit-ui/app/samples/coverage-plugin.js
+++ b/polygerrit-ui/app/samples/coverage-plugin.js
@@ -41,7 +41,7 @@
   const coverageStyle = styleApi.css('background-color: #EF9B9B !important');
   const emptyStyle = styleApi.css('');
 
-  annotationApi.addLayer(context => {
+  annotationApi.setLayer(context => {
     if (Object.keys(coverageData).length === 0) {
       // Coverage data is not ready yet.
       return;
@@ -64,19 +64,13 @@
       }
     }
   }).enableToggleCheckbox('Display Coverage', checkbox => {
-    // Checkbox is attached so now add the notifier that will be controlled
-    // by the checkbox.
-    // Checkbox will only be added to the file diff page, in the top right
-    // section near the "Diff view".
-    annotationApi.addNotifier(notifyFunc => {
-      populateWithDummyData(coverageData);
-      checkbox.disabled = false;
-      checkbox.onclick = e => {
-        displayCoverage = e.target.checked;
-        Object.keys(coverageData).forEach(file => {
-          notifyFunc(file, 0, coverageData[file].totalLines, 'right');
-        });
-      };
-    });
+    populateWithDummyData(coverageData);
+    checkbox.disabled = false;
+    checkbox.onclick = e => {
+      displayCoverage = e.target.checked;
+      Object.keys(coverageData).forEach(file => {
+        annotationApi.notify(file, 0, coverageData[file].totalLines, 'right');
+      });
+    };
   });
-});
\ No newline at end of file
+});
diff --git a/polygerrit-ui/app/services/checks/checks-model.ts b/polygerrit-ui/app/services/checks/checks-model.ts
index 9546d0b..6705a85 100644
--- a/polygerrit-ui/app/services/checks/checks-model.ts
+++ b/polygerrit-ui/app/services/checks/checks-model.ts
@@ -17,6 +17,7 @@
 
 import {BehaviorSubject, Observable} from 'rxjs';
 import {
+  Action,
   Category,
   CheckResult,
   CheckRun,
@@ -35,6 +36,7 @@
   pluginName: string;
   config?: ChecksApiConfig;
   runs: CheckRun[];
+  actions: Action[];
 }
 
 interface ChecksState {
@@ -53,6 +55,18 @@
   distinctUntilChanged()
 );
 
+export const allActions$ = checksState$.pipe(
+  map(state => {
+    return Object.values(state).reduce(
+      (allActions: Action[], providerState: ChecksProviderState) => [
+        ...allActions,
+        ...providerState.actions,
+      ],
+      []
+    );
+  })
+);
+
 export const allRuns$ = checksState$.pipe(
   map(state => {
     return Object.values(state).reduce(
@@ -94,6 +108,7 @@
     pluginName,
     config,
     runs: [],
+    actions: [],
   };
   privateState$.next(nextState);
 }
@@ -162,11 +177,16 @@
   status: RunStatus.COMPLETED,
 };
 
-export function updateStateSetResults(pluginName: string, runs: CheckRun[]) {
+export function updateStateSetResults(
+  pluginName: string,
+  runs: CheckRun[],
+  actions: Action[] = []
+) {
   const nextState = {...privateState$.getValue()};
   nextState[pluginName] = {
     ...nextState[pluginName],
     runs: [...runs],
+    actions: [...actions],
   };
   privateState$.next(nextState);
 }
diff --git a/polygerrit-ui/app/services/checks/checks-service.ts b/polygerrit-ui/app/services/checks/checks-service.ts
index a8dd8b8..2e63f98 100644
--- a/polygerrit-ui/app/services/checks/checks-service.ts
+++ b/polygerrit-ui/app/services/checks/checks-service.ts
@@ -23,6 +23,7 @@
   withLatestFrom,
 } from 'rxjs/operators';
 import {
+  ChangeData,
   ChecksApiConfig,
   ChecksProvider,
   FetchResponse,
@@ -77,13 +78,20 @@
                 runs: [],
               });
             }
-            return from(
-              this.providers[pluginName].fetch(change._number, patchNum)
-            );
+            const data: ChangeData = {
+              changeNumber: change._number,
+              patchsetNumber: patchNum,
+              repo: change.project,
+            };
+            return from(this.providers[pluginName].fetch(data));
           }
         ),
         tap(response => {
-          updateStateSetResults(pluginName, response.runs ?? []);
+          updateStateSetResults(
+            pluginName,
+            response.runs ?? [],
+            response.actions
+          );
         })
       )
       .subscribe(() => {});
diff --git a/polygerrit-ui/app/services/checks/checks-util.ts b/polygerrit-ui/app/services/checks/checks-util.ts
index 30b82b6..176464f 100644
--- a/polygerrit-ui/app/services/checks/checks-util.ts
+++ b/polygerrit-ui/app/services/checks/checks-util.ts
@@ -135,3 +135,30 @@
       return 2;
   }
 }
+
+export interface ActionTriggeredEventDetail {
+  action: Action;
+  run?: CheckRun;
+}
+
+export type ActionTriggeredEvent = CustomEvent<ActionTriggeredEventDetail>;
+
+declare global {
+  interface HTMLElementEventMap {
+    'action-triggered': ActionTriggeredEvent;
+  }
+}
+
+export function fireActionTriggered(
+  target: EventTarget,
+  action: Action,
+  run?: CheckRun
+) {
+  target.dispatchEvent(
+    new CustomEvent('action-triggered', {
+      detail: {action, run},
+      composed: true,
+      bubbles: true,
+    })
+  );
+}
diff --git a/polygerrit-ui/app/services/flags/flags.ts b/polygerrit-ui/app/services/flags/flags.ts
index f96afaa..878b8f7 100644
--- a/polygerrit-ui/app/services/flags/flags.ts
+++ b/polygerrit-ui/app/services/flags/flags.ts
@@ -31,4 +31,5 @@
   NEW_CHANGE_SUMMARY_UI = 'UiFeature__new_change_summary_ui',
   PORTING_COMMENTS = 'UiFeature__porting_comments',
   NEW_IMAGE_DIFF_UI = 'UiFeature__new_image_diff_ui',
+  COMMENT_CONTEXT = 'UiFeature__comment_context',
 }
diff --git a/polygerrit-ui/app/test/common-test-setup.ts b/polygerrit-ui/app/test/common-test-setup.ts
index 0f394be..4e1662c 100644
--- a/polygerrit-ui/app/test/common-test-setup.ts
+++ b/polygerrit-ui/app/test/common-test-setup.ts
@@ -90,8 +90,10 @@
 }
 
 window.fixture = fixtureImpl;
+let testSetupTimestampMs = 0;
 
 setup(() => {
+  testSetupTimestampMs = new Date().getTime();
   window.Gerrit = {};
   initGlobalVariables();
   addIronOverlayBackdropStyleEl();
@@ -201,4 +203,9 @@
   // `this.debounce()`. For those please be careful and cancel them using
   // `this.cancelDebouncer()` in the `detached()` lifecycle hook.
   flushDebouncers();
+  const testTeardownTimestampMs = new Date().getTime();
+  const elapsedMs = testTeardownTimestampMs - testSetupTimestampMs;
+  if (elapsedMs > 1000) {
+    console.warn(`ATTENTION! Test took longer than 1 second: ${elapsedMs} ms`);
+  }
 });
diff --git a/polygerrit-ui/app/types/common.ts b/polygerrit-ui/app/types/common.ts
index 5f196b1..95801e7 100644
--- a/polygerrit-ui/app/types/common.ts
+++ b/polygerrit-ui/app/types/common.ts
@@ -198,7 +198,7 @@
   // This is not set when the change has no reviewers.
   all?: ApprovalInfo[];
   // Docs claim that 'values' is optional, but it is actually always set.
-  values: LabelValueToDescriptionMap; // A map of all values that are allowed for this label
+  values?: LabelValueToDescriptionMap; // A map of all values that are allowed for this label
   default_value?: number;
 }
 
@@ -1173,11 +1173,21 @@
   unresolved?: boolean;
   change_message_id?: string;
   commit_id?: string;
+  context_lines?: ContextLine[];
 }
 
 export type PathToCommentsInfoMap = {[path: string]: CommentInfo[]};
 
 /**
+ * The ContextLine entity contains the line number and line text of a single line of the source file content..
+ * https://gerrit-review.googlesource.com/Documentation/rest-api-changes.html#context-line
+ */
+export interface ContextLine {
+  line_number: number;
+  context_line: string;
+}
+
+/**
  * The ProjectInfo entity contains information about a project
  * https://gerrit-review.googlesource.com/Documentation/rest-api-projects.html#project-info
  */
@@ -1670,7 +1680,6 @@
   context?: number;
   expand_all_comments?: boolean;
   ignore_whitespace: IgnoreWhitespaceType;
-  intraline_difference?: boolean;
   line_length?: number;
   manual_review?: boolean;
   retain_header?: boolean;
@@ -1678,10 +1687,8 @@
   show_tabs?: boolean;
   show_whitespace_errors?: boolean;
   skip_deleted?: boolean;
-  skip_uncommented?: boolean;
   syntax_highlighting?: boolean;
   hide_top_menu?: boolean;
-  auto_hide_diff_table_header?: boolean;
   hide_line_numbers?: boolean;
   tab_size?: number;
   font_size?: number;
diff --git a/polygerrit-ui/app/types/events.ts b/polygerrit-ui/app/types/events.ts
index 163c760..6b05fad 100644
--- a/polygerrit-ui/app/types/events.ts
+++ b/polygerrit-ui/app/types/events.ts
@@ -146,6 +146,18 @@
   value?: number;
   // scroll into the tab afterwards, from custom event
   scrollIntoView?: boolean;
+  // define state of tab after opening
+  tabState?: TabState;
+}
+
+export interface TabState {
+  commentTab?: CommentTabState;
+}
+
+export enum CommentTabState {
+  UNRESOLVED = 'unresolved',
+  DRAFTS = 'drafts',
+  SHOW_ALL = 'show all',
 }
 
 export type SwitchTabEvent = CustomEvent<SwitchTabEventDetail>;
diff --git a/polygerrit-ui/app/utils/change-util.ts b/polygerrit-ui/app/utils/change-util.ts
index 8839be1..1228863 100644
--- a/polygerrit-ui/app/utils/change-util.ts
+++ b/polygerrit-ui/app/utils/change-util.ts
@@ -21,6 +21,7 @@
   PatchSetNum,
   ChangeInfo,
   AccountInfo,
+  RelatedChangeAndCommitInfo,
 } from '../types/common';
 import {ParsedChangeInfo} from '../types/types';
 
@@ -225,3 +226,9 @@
       (!reviewer._account_id && account.email === reviewer.email)
   );
 }
+
+export function isChangeInfo(
+  x: ChangeInfo | RelatedChangeAndCommitInfo | ParsedChangeInfo
+): x is ChangeInfo | ParsedChangeInfo {
+  return (x as ChangeInfo)._number !== undefined;
+}
diff --git a/polygerrit-ui/app/utils/comment-util.ts b/polygerrit-ui/app/utils/comment-util.ts
index 0c4a3c6..de12a2a 100644
--- a/polygerrit-ui/app/utils/comment-util.ts
+++ b/polygerrit-ui/app/utils/comment-util.ts
@@ -24,12 +24,14 @@
   CommentRange,
   PatchRange,
   ParentPatchSetNum,
+  ContextLine,
 } from '../types/common';
 import {CommentSide, Side} from '../constants/constants';
 import {parseDate} from './date-util';
 import {LineNumber} from '../elements/diff/gr-diff/gr-diff-line';
 import {CommentIdToCommentThreadMap} from '../elements/diff/gr-comment-api/gr-comment-api';
 import {isMergeParent, getParentIndex} from './patch-set-util';
+import {DiffInfo} from '../types/diff';
 
 export interface DraftCommentProps {
   __draft?: boolean;
@@ -285,3 +287,34 @@
     };
   }
 }
+
+export function computeDiffFromContext(context: ContextLine[], path: string) {
+  // do not render more than 20 lines of context
+  context = context.slice(0, 20);
+  const diff: DiffInfo = {
+    meta_a: {
+      name: '',
+      content_type: '',
+      lines: 0,
+      web_links: [],
+    },
+    meta_b: {
+      name: path,
+      content_type: '',
+      lines: context.length + context?.[0].line_number,
+      web_links: [],
+    },
+    change_type: 'MODIFIED',
+    intraline_status: 'OK',
+    diff_header: [],
+    content: [
+      {
+        skip: context[0].line_number - 1,
+      },
+      {
+        b: context.map(line => line.context_line),
+      },
+    ],
+  };
+  return diff;
+}
diff --git a/polygerrit-ui/app/utils/common-util.ts b/polygerrit-ui/app/utils/common-util.ts
index ad76b79..2246251 100644
--- a/polygerrit-ui/app/utils/common-util.ts
+++ b/polygerrit-ui/app/utils/common-util.ts
@@ -67,6 +67,30 @@
 }
 
 /**
+ * Throws an error if the property is not defined.
+ */
+export function checkRequiredProperty<T>(
+  property: T,
+  propertyName: string
+): asserts property is NonNullable<T> {
+  if (property === undefined || property === null) {
+    throw new Error(`Required property '${propertyName}' not set.`);
+  }
+}
+
+/**
+ * Throws an error if the property is not defined.
+ */
+export function assertIsDefined<T>(
+  val: T,
+  variableName = 'variable'
+): asserts val is NonNullable<T> {
+  if (val === undefined || val === null) {
+    throw new Error(`${variableName} is not defined`);
+  }
+}
+
+/**
  * Returns true, if both sets contain the same members.
  */
 export function areSetsEqual<T>(a: Set<T>, b: Set<T>): boolean {
diff --git a/polygerrit-ui/app/utils/event-util.ts b/polygerrit-ui/app/utils/event-util.ts
index ee3c833..cf590e0 100644
--- a/polygerrit-ui/app/utils/event-util.ts
+++ b/polygerrit-ui/app/utils/event-util.ts
@@ -17,7 +17,7 @@
 
 import {UrlEncodedCommentId} from '../types/common';
 import {FetchRequest} from '../types/types';
-import {DialogChangeEventDetail} from '../types/events';
+import {DialogChangeEventDetail, TabState} from '../types/events';
 
 export enum EventType {
   SHOW_ALERT = 'show-alert',
@@ -27,6 +27,7 @@
   TITLE_CHANGE = 'title-change',
   THREAD_LIST_MODIFIED = 'thread-list-modified',
   DIALOG_CHANGE = 'dialog-change',
+  SHOW_PRIMARY_TAB = 'show-primary-tab',
 }
 
 export function fireEvent(target: EventTarget, type: string) {
@@ -117,10 +118,15 @@
   );
 }
 
-export function fireShowPrimaryTab(target: EventTarget, tab: string) {
+export function fireShowPrimaryTab(
+  target: EventTarget,
+  tab: string,
+  scrollIntoView?: boolean,
+  tabState?: TabState
+) {
   target.dispatchEvent(
-    new CustomEvent('show-primary-tab', {
-      detail: {tab},
+    new CustomEvent(EventType.SHOW_PRIMARY_TAB, {
+      detail: {tab, scrollIntoView, tabState},
       composed: true,
       bubbles: true,
     })
diff --git a/polygerrit-ui/app/utils/label-util.ts b/polygerrit-ui/app/utils/label-util.ts
index fc83d77..60ac4d8 100644
--- a/polygerrit-ui/app/utils/label-util.ts
+++ b/polygerrit-ui/app/utils/label-util.ts
@@ -27,7 +27,7 @@
 export const CODE_REVIEW = 'Code-Review';
 
 export function getVotingRange(label?: LabelInfo): VotingRangeInfo | undefined {
-  if (!label || !isDetailedLabelInfo(label)) return undefined;
+  if (!label || !isDetailedLabelInfo(label) || !label.values) return undefined;
   const values = Object.keys(label.values).map(v => Number(v));
   values.sort((a, b) => a - b);
   if (!values.length) return undefined;
diff --git a/resources/com/google/gerrit/httpd/raw/PolyGerritIndexHtml.soy b/resources/com/google/gerrit/httpd/raw/PolyGerritIndexHtml.soy
index 31ea7d2..93584c6 100644
--- a/resources/com/google/gerrit/httpd/raw/PolyGerritIndexHtml.soy
+++ b/resources/com/google/gerrit/httpd/raw/PolyGerritIndexHtml.soy
@@ -16,7 +16,7 @@
 
 {namespace com.google.gerrit.httpd.raw}
 
-{template .Index}
+{template Index}
   {@param canonicalPath: ?}
   {@param staticResourcePath: ?}
   {@param gerritInitialData: /** {string} map of REST endpoint to response for startup. */ ?}
diff --git a/resources/com/google/gerrit/server/mail/Abandoned.soy b/resources/com/google/gerrit/server/mail/Abandoned.soy
index d5aac0e..b57f00a 100644
--- a/resources/com/google/gerrit/server/mail/Abandoned.soy
+++ b/resources/com/google/gerrit/server/mail/Abandoned.soy
@@ -20,7 +20,7 @@
  * The .Abandoned template will determine the contents of the email related to a
  * change being abandoned.
  */
-{template .Abandoned kind="text"}
+{template Abandoned kind="text"}
   {@param change: ?}
   {@param coverLetter: ?}
   {@param email: ?}
diff --git a/resources/com/google/gerrit/server/mail/AbandonedHtml.soy b/resources/com/google/gerrit/server/mail/AbandonedHtml.soy
index 9ad996e..9f80241 100644
--- a/resources/com/google/gerrit/server/mail/AbandonedHtml.soy
+++ b/resources/com/google/gerrit/server/mail/AbandonedHtml.soy
@@ -16,7 +16,9 @@
 
 {namespace com.google.gerrit.server.mail.template}
 
-{template .AbandonedHtml}
+import * as mailTemplate from 'com/google/gerrit/server/mail/Private.soy';
+
+{template AbandonedHtml}
   {@param coverLetter: ?}
   {@param email: ?}
   {@param fromName: ?}
@@ -26,7 +28,7 @@
 
   {if $email.changeUrl}
     <p>
-      {call .ViewChangeButton data="all" /}
+      {call mailTemplate.ViewChangeButton data="all" /}
     </p>
   {/if}
 
diff --git a/resources/com/google/gerrit/server/mail/AddKey.soy b/resources/com/google/gerrit/server/mail/AddKey.soy
index 8b609cf..c77ab56 100644
--- a/resources/com/google/gerrit/server/mail/AddKey.soy
+++ b/resources/com/google/gerrit/server/mail/AddKey.soy
@@ -16,11 +16,13 @@
 
 {namespace com.google.gerrit.server.mail.template}
 
+import * as mailTemplate from 'com/google/gerrit/server/mail/NoReplyFooter.soy';
+
 /**
  * The .AddKey template will determine the contents of the email related to
  * adding a new SSH or GPG key to an account.
  */
-{template .AddKey kind="text"}
+{template AddKey kind="text"}
   {@param email: ?}
   One or more new {$email.keyType} keys have been added to Gerrit Code Review at
   {sp}{$email.gerritHost}:
@@ -64,5 +66,5 @@
   browser window instead.
 
   {\n}
-  {call .NoReplyFooter /}
+  {call mailTemplate.NoReplyFooter /}
 {/template}
diff --git a/resources/com/google/gerrit/server/mail/AddKeyHtml.soy b/resources/com/google/gerrit/server/mail/AddKeyHtml.soy
index ed4f435..3987684 100644
--- a/resources/com/google/gerrit/server/mail/AddKeyHtml.soy
+++ b/resources/com/google/gerrit/server/mail/AddKeyHtml.soy
@@ -16,7 +16,9 @@
 
 {namespace com.google.gerrit.server.mail.template}
 
-{template .AddKeyHtml}
+import * as mailTemplate from 'com/google/gerrit/server/mail/NoReplyFooterHtml.soy';
+
+{template AddKeyHtml}
   {@param email: ?}
   <p>
     One or more new {$email.keyType} keys have been added to Gerrit Code Review
@@ -57,5 +59,5 @@
     {/if}.
   </p>
 
-  {call .NoReplyFooterHtml /}
+  {call mailTemplate.NoReplyFooterHtml /}
 {/template}
diff --git a/resources/com/google/gerrit/server/mail/AddToAttentionSet.soy b/resources/com/google/gerrit/server/mail/AddToAttentionSet.soy
index 5ea41b2..64c1ad3 100644
--- a/resources/com/google/gerrit/server/mail/AddToAttentionSet.soy
+++ b/resources/com/google/gerrit/server/mail/AddToAttentionSet.soy
@@ -20,7 +20,7 @@
  * The .AddToAttentionSet template will determine the contents of the email related to a
  * user being added to the attention set.
  */
-{template .AddToAttentionSet kind="text"}
+{template AddToAttentionSet kind="text"}
   {@param change: ?}
   {@param coverLetter: ?}
   {@param email: ?}
diff --git a/resources/com/google/gerrit/server/mail/AddToAttentionSetHtml.soy b/resources/com/google/gerrit/server/mail/AddToAttentionSetHtml.soy
index bac180a..04d759e 100644
--- a/resources/com/google/gerrit/server/mail/AddToAttentionSetHtml.soy
+++ b/resources/com/google/gerrit/server/mail/AddToAttentionSetHtml.soy
@@ -16,7 +16,9 @@
 
 {namespace com.google.gerrit.server.mail.template}
 
-{template .AddToAttentionSetHtml}
+import * as mailTemplate from 'com/google/gerrit/server/mail/Private.soy';
+
+{template AddToAttentionSetHtml}
   {@param coverLetter: ?}
   {@param email: ?}
   {@param fromName: ?}
@@ -33,11 +35,11 @@
 
   {if $email.changeUrl}
     <p>
-      {call .ViewChangeButton data="all" /}
+      {call mailTemplate.ViewChangeButton data="all" /}
     </p>
   {/if}
 
   {if $coverLetter}
     <div style="white-space:pre-wrap">{$coverLetter}</div>
   {/if}
-{/template}
\ No newline at end of file
+{/template}
diff --git a/resources/com/google/gerrit/server/mail/ChangeFooter.soy b/resources/com/google/gerrit/server/mail/ChangeFooter.soy
index a8170ca..236b171 100644
--- a/resources/com/google/gerrit/server/mail/ChangeFooter.soy
+++ b/resources/com/google/gerrit/server/mail/ChangeFooter.soy
@@ -20,7 +20,7 @@
  * The .ChangeFooter template will determine the contents of the footer text
  * that will be appended to ALL emails related to changes.
  */
-{template .ChangeFooter kind="text"}
+{template ChangeFooter kind="text"}
   {@param email: ?}
   --{sp}
   {\n}
diff --git a/resources/com/google/gerrit/server/mail/ChangeFooterHtml.soy b/resources/com/google/gerrit/server/mail/ChangeFooterHtml.soy
index b619c53..28442ee 100644
--- a/resources/com/google/gerrit/server/mail/ChangeFooterHtml.soy
+++ b/resources/com/google/gerrit/server/mail/ChangeFooterHtml.soy
@@ -16,7 +16,7 @@
 
 {namespace com.google.gerrit.server.mail.template}
 
-{template .ChangeFooterHtml}
+{template ChangeFooterHtml}
   {@param change: ?}
   {@param email: ?}
   {if $email.changeUrl or $email.settingsUrl}
diff --git a/resources/com/google/gerrit/server/mail/ChangeHeader.soy b/resources/com/google/gerrit/server/mail/ChangeHeader.soy
index fde69f1..7a2da65 100644
--- a/resources/com/google/gerrit/server/mail/ChangeHeader.soy
+++ b/resources/com/google/gerrit/server/mail/ChangeHeader.soy
@@ -16,7 +16,7 @@
 
 {namespace com.google.gerrit.server.mail.template}
 
-{template .ChangeHeader kind="text"}
+{template ChangeHeader kind="text"}
   {@param attentionSet: ?}
   {if $attentionSet}
     Attention is currently required from:{sp}
diff --git a/resources/com/google/gerrit/server/mail/ChangeHeaderHtml.soy b/resources/com/google/gerrit/server/mail/ChangeHeaderHtml.soy
index ea12455..a1bcd8f 100644
--- a/resources/com/google/gerrit/server/mail/ChangeHeaderHtml.soy
+++ b/resources/com/google/gerrit/server/mail/ChangeHeaderHtml.soy
@@ -17,7 +17,7 @@
 
 {namespace com.google.gerrit.server.mail.template}
 
-{template .ChangeHeaderHtml}
+{template ChangeHeaderHtml}
   {@param attentionSet: ?}
   {if $attentionSet}
     <p> Attention is currently required from:{sp}
diff --git a/resources/com/google/gerrit/server/mail/ChangeSubject.soy b/resources/com/google/gerrit/server/mail/ChangeSubject.soy
index 7fcd213..12422ed 100644
--- a/resources/com/google/gerrit/server/mail/ChangeSubject.soy
+++ b/resources/com/google/gerrit/server/mail/ChangeSubject.soy
@@ -20,7 +20,7 @@
  * The .ChangeSubject template will determine the contents of the email subject
  * line for ALL emails related to changes.
  */
-{template .ChangeSubject kind="text"}
+{template ChangeSubject kind="text"}
   {@param branch: ?}
   {@param change: ?}
   {@param shortProjectName: ?}
diff --git a/resources/com/google/gerrit/server/mail/Comment.soy b/resources/com/google/gerrit/server/mail/Comment.soy
index 893ef6f..973b369 100644
--- a/resources/com/google/gerrit/server/mail/Comment.soy
+++ b/resources/com/google/gerrit/server/mail/Comment.soy
@@ -20,7 +20,7 @@
  * The .Comment template will determine the contents of the email related to a
  * user submitting comments on changes.
  */
-{template .Comment kind="text"}
+{template Comment kind="text"}
   {@param change: ?}
   {@param coverLetter: ?}
   {@param email: ?}
diff --git a/resources/com/google/gerrit/server/mail/CommentFooter.soy b/resources/com/google/gerrit/server/mail/CommentFooter.soy
index 3998438..3c111f7 100644
--- a/resources/com/google/gerrit/server/mail/CommentFooter.soy
+++ b/resources/com/google/gerrit/server/mail/CommentFooter.soy
@@ -21,5 +21,5 @@
  * that will be appended to emails related to a user submitting comments on
  * changes.
  */
-{template .CommentFooter kind="text"}
+{template CommentFooter kind="text"}
 {/template}
diff --git a/resources/com/google/gerrit/server/mail/CommentFooterHtml.soy b/resources/com/google/gerrit/server/mail/CommentFooterHtml.soy
index 033c1b1..ce8a933 100644
--- a/resources/com/google/gerrit/server/mail/CommentFooterHtml.soy
+++ b/resources/com/google/gerrit/server/mail/CommentFooterHtml.soy
@@ -16,5 +16,5 @@
 
 {namespace com.google.gerrit.server.mail.template}
 
-{template .CommentFooterHtml}
+{template CommentFooterHtml}
 {/template}
diff --git a/resources/com/google/gerrit/server/mail/CommentHtml.soy b/resources/com/google/gerrit/server/mail/CommentHtml.soy
index 21fee18..b3924c3 100644
--- a/resources/com/google/gerrit/server/mail/CommentHtml.soy
+++ b/resources/com/google/gerrit/server/mail/CommentHtml.soy
@@ -16,7 +16,9 @@
 
 {namespace com.google.gerrit.server.mail.template}
 
-{template .CommentHtml}
+import * as mailTemplate from 'com/google/gerrit/server/mail/Private.soy';
+
+{template CommentHtml}
   {@param commentFiles: ?}
   {@param commentCount: ?}
   {@param email: ?}
@@ -71,7 +73,9 @@
   {/let}
 
   {if $patchSetCommentBlocks}
-    {call .WikiFormat}{param content: $patchSetCommentBlocks /}{/call}
+    {call mailTemplate.WikiFormat}
+      {param content: $patchSetCommentBlocks /}
+    {/call}
   {/if}
 
   {if length($labels) > 0}
@@ -97,7 +101,7 @@
 
   {if $email.changeUrl}
     <p>
-      {call .ViewChangeButton data="all" /}
+      {call mailTemplate.ViewChangeButton data="all" /}
     </p>
   {/if}
 
@@ -146,11 +150,13 @@
               {if length($comment.lines) > 1}
                 <p>
                   <blockquote style="{$blockquoteStyle}">
-                    {call .Pre}{param content kind="html"}
-                      {for $line in $comment.lines}
-                        {$line}{\n}
-                      {/for}
-                    {/param}{/call}
+                    {call mailTemplate.Pre}
+                      {param content kind="html"}
+                        {for $line in $comment.lines}
+                          {$line}{\n}
+                        {/for}
+                      {/param}
+                    {/call}
                   </blockquote>
                 </p>
               {/if}
@@ -163,7 +169,9 @@
                 </p>
               {/if}
 
-              {call .WikiFormat}{param content: $comment.messageBlocks /}{/call}
+              {call mailTemplate.WikiFormat}
+                {param content: $comment.messageBlocks /}
+              {/call}
             </li>
           {/for}
         </ul>
diff --git a/resources/com/google/gerrit/server/mail/DeleteKey.soy b/resources/com/google/gerrit/server/mail/DeleteKey.soy
index 30548c8..ffc12dc 100644
--- a/resources/com/google/gerrit/server/mail/DeleteKey.soy
+++ b/resources/com/google/gerrit/server/mail/DeleteKey.soy
@@ -20,7 +20,7 @@
  * The .DeleteKey template will determine the contents of the email related to
  * deleting a SSH or GPG key.
  */
-{template .DeleteKey kind="text"}
+{template DeleteKey kind="text"}
   {@param email: ?}
   One or more {$email.keyType} keys have been deleted on Gerrit Code Review at
   {sp}{$email.gerritHost}:
diff --git a/resources/com/google/gerrit/server/mail/DeleteKeyHtml.soy b/resources/com/google/gerrit/server/mail/DeleteKeyHtml.soy
index 1ab3955..4ce5246 100644
--- a/resources/com/google/gerrit/server/mail/DeleteKeyHtml.soy
+++ b/resources/com/google/gerrit/server/mail/DeleteKeyHtml.soy
@@ -16,7 +16,7 @@
 
 {namespace com.google.gerrit.server.mail.template}
 
-{template .DeleteKeyHtml}
+{template DeleteKeyHtml}
   {@param email: ?}
   <p>
     One or more {$email.keyType} keys have been deleted on Gerrit Code Review
diff --git a/resources/com/google/gerrit/server/mail/DeleteReviewer.soy b/resources/com/google/gerrit/server/mail/DeleteReviewer.soy
index 3310249..a9ba802 100644
--- a/resources/com/google/gerrit/server/mail/DeleteReviewer.soy
+++ b/resources/com/google/gerrit/server/mail/DeleteReviewer.soy
@@ -20,7 +20,7 @@
  * The .DeleteReviewer template will determine the contents of the email related
  * to removal of a reviewer (and the reviewer's votes) from reviews.
  */
-{template .DeleteReviewer kind="text"}
+{template DeleteReviewer kind="text"}
   {@param change: ?}
   {@param coverLetter: ?}
   {@param email: ?}
diff --git a/resources/com/google/gerrit/server/mail/DeleteReviewerHtml.soy b/resources/com/google/gerrit/server/mail/DeleteReviewerHtml.soy
index 54720fe..685ca4c 100644
--- a/resources/com/google/gerrit/server/mail/DeleteReviewerHtml.soy
+++ b/resources/com/google/gerrit/server/mail/DeleteReviewerHtml.soy
@@ -16,7 +16,9 @@
 
 {namespace com.google.gerrit.server.mail.template}
 
-{template .DeleteReviewerHtml}
+import * as mailTemplate from 'com/google/gerrit/server/mail/Private.soy';
+
+{template DeleteReviewerHtml}
   {@param email: ?}
   {@param fromName: ?}
   <p>
@@ -35,7 +37,7 @@
 
   {if $email.changeUrl}
     <p>
-      {call .ViewChangeButton data="all" /}
+      {call mailTemplate.ViewChangeButton data="all" /}
     </p>
   {/if}
 {/template}
diff --git a/resources/com/google/gerrit/server/mail/DeleteVote.soy b/resources/com/google/gerrit/server/mail/DeleteVote.soy
index 0ee5454..74790f7 100644
--- a/resources/com/google/gerrit/server/mail/DeleteVote.soy
+++ b/resources/com/google/gerrit/server/mail/DeleteVote.soy
@@ -20,7 +20,7 @@
  * The .DeleteVote template will determine the contents of the email related
  * to removing votes on changes.
  */
-{template .DeleteVote kind="text"}
+{template DeleteVote kind="text"}
   {@param change: ?}
   {@param coverLetter: ?}
   {@param email: ?}
diff --git a/resources/com/google/gerrit/server/mail/DeleteVoteHtml.soy b/resources/com/google/gerrit/server/mail/DeleteVoteHtml.soy
index 3a82927..dd3b423 100644
--- a/resources/com/google/gerrit/server/mail/DeleteVoteHtml.soy
+++ b/resources/com/google/gerrit/server/mail/DeleteVoteHtml.soy
@@ -16,7 +16,9 @@
 
 {namespace com.google.gerrit.server.mail.template}
 
-{template .DeleteVoteHtml}
+import * as mailTemplate from 'com/google/gerrit/server/mail/Private.soy';
+
+{template DeleteVoteHtml}
   {@param coverLetter: ?}
   {@param email: ?}
   {@param fromName: ?}
@@ -26,7 +28,7 @@
 
   {if $email.changeUrl}
     <p>
-      {call .ViewChangeButton data="all" /}
+      {call mailTemplate.ViewChangeButton data="all" /}
     </p>
   {/if}
 
diff --git a/resources/com/google/gerrit/server/mail/Footer.soy b/resources/com/google/gerrit/server/mail/Footer.soy
index 7483cd9..6ce0d3b 100644
--- a/resources/com/google/gerrit/server/mail/Footer.soy
+++ b/resources/com/google/gerrit/server/mail/Footer.soy
@@ -21,7 +21,7 @@
  * appended to the end of all outgoing emails after the ChangeFooter and
  * CommentFooter.
  */
-{template .Footer kind="text"}
+{template Footer kind="text"}
   {@param footers: ?}
   {for $footer in $footers}
     {$footer}{\n}
diff --git a/resources/com/google/gerrit/server/mail/FooterHtml.soy b/resources/com/google/gerrit/server/mail/FooterHtml.soy
index ce934d3..c89dea7 100644
--- a/resources/com/google/gerrit/server/mail/FooterHtml.soy
+++ b/resources/com/google/gerrit/server/mail/FooterHtml.soy
@@ -16,7 +16,7 @@
 
 {namespace com.google.gerrit.server.mail.template}
 
-{template .FooterHtml}
+{template FooterHtml}
   {@param footers: ?}
   {\n}
   {\n}
diff --git a/resources/com/google/gerrit/server/mail/HttpPasswordUpdate.soy b/resources/com/google/gerrit/server/mail/HttpPasswordUpdate.soy
index 38e679e..08daa932 100644
--- a/resources/com/google/gerrit/server/mail/HttpPasswordUpdate.soy
+++ b/resources/com/google/gerrit/server/mail/HttpPasswordUpdate.soy
@@ -20,7 +20,7 @@
  * The .HttpPasswordUpdate template will determine the contents of the email related to
  * adding, changing or deleting the HTTP password.
  */
-{template .HttpPasswordUpdate kind="text"}
+{template HttpPasswordUpdate kind="text"}
   {@param email: ?}
   The HTTP password was {$email.operation} on Gerrit Code Review at
   {sp}{$email.gerritHost}.
diff --git a/resources/com/google/gerrit/server/mail/HttpPasswordUpdateHtml.soy b/resources/com/google/gerrit/server/mail/HttpPasswordUpdateHtml.soy
index 3c4594c..e28aaaa 100644
--- a/resources/com/google/gerrit/server/mail/HttpPasswordUpdateHtml.soy
+++ b/resources/com/google/gerrit/server/mail/HttpPasswordUpdateHtml.soy
@@ -16,7 +16,7 @@
 
 {namespace com.google.gerrit.server.mail.template}
 
-{template .HttpPasswordUpdateHtml}
+{template HttpPasswordUpdateHtml}
   {@param email: ?}
   <p>
     The HTTP password was {$email.operation} on Gerrit Code Review
diff --git a/resources/com/google/gerrit/server/mail/InboundEmailRejection.soy b/resources/com/google/gerrit/server/mail/InboundEmailRejection.soy
index 16c5c8d..378785a 100644
--- a/resources/com/google/gerrit/server/mail/InboundEmailRejection.soy
+++ b/resources/com/google/gerrit/server/mail/InboundEmailRejection.soy
@@ -16,7 +16,9 @@
 
 {namespace com.google.gerrit.server.mail.template}
 
-{template .InboundEmailRejectionFooter kind="text"}
+import * as noReplyFooter from 'com/google/gerrit/server/mail/NoReplyFooter.soy';
+
+{template InboundEmailRejectionFooter kind="text"}
   {\n}
   {\n}
   Thus, no actions were taken by Gerrit in response to this email,
@@ -24,7 +26,7 @@
   {\n}
   This email was sent in response to an email coming from this address.
   In case you did not send Gerrit an email, feel free to ignore this.
-  {call .NoReplyFooter /}
+  {call noReplyFooter.NoReplyFooter /}
 {/template}
 
 /**
@@ -32,39 +34,39 @@
  * to warning users of error in inbound emails
  */
 
-{template .InboundEmailRejection_PARSING_ERROR kind="text"}
+{template InboundEmailRejection_PARSING_ERROR kind="text"}
   Gerrit Code Review was unable to parse your email.{\n}
   This might be because your email did not quote Gerrit's email,
   because you are using an unsupported email client,
   or because of a bug.
-  {call .InboundEmailRejectionFooter /}
+  {call InboundEmailRejectionFooter /}
 {/template}
 
-{template .InboundEmailRejection_UNKNOWN_ACCOUNT kind="text"}
+{template InboundEmailRejection_UNKNOWN_ACCOUNT kind="text"}
   Gerrit Code Review was unable to match your email to an account.{\n}
   This may happen if several accounts are linked to this email address.
-  {call .InboundEmailRejectionFooter /}
+  {call InboundEmailRejectionFooter /}
 {/template}
 
-{template .InboundEmailRejection_INACTIVE_ACCOUNT kind="text"}
+{template InboundEmailRejection_INACTIVE_ACCOUNT kind="text"}
   Your account on this Gerrit Code Review instance is marked as inactive,
   so your email has been ignored. {\n}
   If you think this is an error, please contact your Gerrit instance administrator.
   {\n}{\n}
   This email was sent in response to an email coming from this address.
   In case you did not send Gerrit an email, feel free to ignore this.
-  {call .NoReplyFooter /}
+  {call noReplyFooter.NoReplyFooter /}
 {/template}
 
-{template .InboundEmailRejection_INTERNAL_EXCEPTION kind="text"}
+{template InboundEmailRejection_INTERNAL_EXCEPTION kind="text"}
   Gerrit Code Review encountered an internal exception and was unable to fulfil your request.
   {\n}
   This might be caused by an ongoing maintenance or a data corruption.
-  {call .InboundEmailRejectionFooter /}
+  {call InboundEmailRejectionFooter /}
 {/template}
 
-{template .InboundEmailRejection_COMMENT_REJECTED kind="text"}
+{template InboundEmailRejection_COMMENT_REJECTED kind="text"}
   Gerrit Code Review rejected one or more comments because they did not pass validation, or
   because the maximum number of comments per change would be exceeded.
-  {call .InboundEmailRejectionFooter /}
+  {call InboundEmailRejectionFooter /}
 {/template}
diff --git a/resources/com/google/gerrit/server/mail/InboundEmailRejectionHtml.soy b/resources/com/google/gerrit/server/mail/InboundEmailRejectionHtml.soy
index 8762e10..f0b18d2 100644
--- a/resources/com/google/gerrit/server/mail/InboundEmailRejectionHtml.soy
+++ b/resources/com/google/gerrit/server/mail/InboundEmailRejectionHtml.soy
@@ -16,8 +16,11 @@
 
 {namespace com.google.gerrit.server.mail.template}
 
+import * as noReplyFooter from 'com/google/gerrit/server/mail/NoReplyFooter.soy';
+import * as noReplyFooterHtml from 'com/google/gerrit/server/mail/NoReplyFooterHtml.soy';
 
-{template .InboundEmailRejectionFooterHtml}
+
+{template InboundEmailRejectionFooterHtml}
   <p>
     Thus, no actions were taken by Gerrit in response to this email,
     and you should use the Gerrit website to continue.
@@ -25,7 +28,7 @@
   <p>
     In case you did not send Gerrit an email, feel free to ignore this.
   </p>
-  {call .NoReplyFooterHtml /}
+  {call noReplyFooterHtml.NoReplyFooterHtml /}
 {/template}
 
 /**
@@ -33,7 +36,7 @@
  * to warning users of error in inbound emails
  */
 
-{template .InboundEmailRejectionHtml_PARSING_ERROR}
+{template InboundEmailRejectionHtml_PARSING_ERROR}
   <p>
     Gerrit Code Review was unable to parse your email.
   </p>
@@ -42,20 +45,20 @@
     because you are using an unsupported email client,
     or because of a bug.
   </p>
-  {call .InboundEmailRejectionFooterHtml /}
+  {call InboundEmailRejectionFooterHtml /}
 {/template}
 
-{template .InboundEmailRejectionHtml_UNKNOWN_ACCOUNT}
+{template InboundEmailRejectionHtml_UNKNOWN_ACCOUNT}
   <p>
     Gerrit Code Review was unable to match your email to an account.
   </p>
   <p>
     This may happen if several accounts are linked to this email address.
   </p>
-  {call .InboundEmailRejectionFooterHtml /}
+  {call InboundEmailRejectionFooterHtml /}
 {/template}
 
-{template .InboundEmailRejectionHtml_INACTIVE_ACCOUNT}
+{template InboundEmailRejectionHtml_INACTIVE_ACCOUNT}
   <p>
     Your account on this Gerrit Code Review instance is marked as inactive,
     so your email has been ignored.
@@ -66,23 +69,23 @@
   <p>
     In case you did not send Gerrit an email, feel free to ignore this.
   </p>
-  {call .NoReplyFooter /}
+  {call noReplyFooter.NoReplyFooter /}
 {/template}
 
-{template .InboundEmailRejectionHtml_INTERNAL_EXCEPTION}
+{template InboundEmailRejectionHtml_INTERNAL_EXCEPTION}
   <p>
     Gerrit Code Review encountered an internal exception and was unable to fulfil your request.
   </p>
   <p>
     This might be caused by an ongoing maintenance or a data corruption.
   <p>
-  {call .InboundEmailRejectionFooterHtml /}
+  {call InboundEmailRejectionFooterHtml /}
 {/template}
 
-{template .InboundEmailRejectionHtml_COMMENT_REJECTED}
+{template InboundEmailRejectionHtml_COMMENT_REJECTED}
   <p>
     Gerrit Code Review rejected one or more comments because they did not pass validation, or
     because the maximum number of comments per change would be exceeded.
   </p>
-  {call .InboundEmailRejectionFooterHtml /}
+  {call InboundEmailRejectionFooterHtml /}
 {/template}
diff --git a/resources/com/google/gerrit/server/mail/Merged.soy b/resources/com/google/gerrit/server/mail/Merged.soy
index 59e73eb..b586851 100644
--- a/resources/com/google/gerrit/server/mail/Merged.soy
+++ b/resources/com/google/gerrit/server/mail/Merged.soy
@@ -21,7 +21,7 @@
  * The .Merged template will determine the contents of the email related to
  * a change successfully merged to the head.
  */
-{template .Merged kind="text"}
+{template Merged kind="text"}
   {@param change: ?}
   {@param email: ?}
   {@param fromName: ?}
diff --git a/resources/com/google/gerrit/server/mail/MergedHtml.soy b/resources/com/google/gerrit/server/mail/MergedHtml.soy
index ca6451e..53c1645 100644
--- a/resources/com/google/gerrit/server/mail/MergedHtml.soy
+++ b/resources/com/google/gerrit/server/mail/MergedHtml.soy
@@ -16,7 +16,9 @@
 
 {namespace com.google.gerrit.server.mail.template}
 
-{template .MergedHtml}
+import * as mailTemplate from 'com/google/gerrit/server/mail/Private.soy';
+
+{template MergedHtml}
   {@param diffLines: ?}
   {@param email: ?}
   {@param fromName: ?}
@@ -26,16 +28,20 @@
 
   {if $email.changeUrl}
     <p>
-      {call .ViewChangeButton data="all" /}
+      {call mailTemplate.ViewChangeButton data="all" /}
     </p>
   {/if}
 
   <div style="white-space:pre-wrap">{$email.approvals}</div>
 
-  {call .Pre}{param content: $email.changeDetail /}{/call}
+  {call mailTemplate.Pre}
+    {param content: $email.changeDetail /}
+  {/call}
 
   {if $email.includeDiff}
-    {call .UnifiedDiff}{param diffLines: $diffLines /}{/call}
+    {call mailTemplate.UnifiedDiff}
+      {param diffLines: $diffLines /}
+    {/call}
   {/if}
   <div style="white-space:pre-wrap">{$email.stickyApprovalDiff}</div>
 {/template}
diff --git a/resources/com/google/gerrit/server/mail/NewChange.soy b/resources/com/google/gerrit/server/mail/NewChange.soy
index fa447e9..fe99ba4 100644
--- a/resources/com/google/gerrit/server/mail/NewChange.soy
+++ b/resources/com/google/gerrit/server/mail/NewChange.soy
@@ -20,7 +20,7 @@
  * The .NewChange template will determine the contents of the email related to a
  * user submitting a new change for review.
  */
-{template .NewChange kind="text"}
+{template NewChange kind="text"}
   {@param change: ?}
   {@param email: ?}
   {@param ownerName: ?}
diff --git a/resources/com/google/gerrit/server/mail/NewChangeHtml.soy b/resources/com/google/gerrit/server/mail/NewChangeHtml.soy
index e16b213..756d6ce 100644
--- a/resources/com/google/gerrit/server/mail/NewChangeHtml.soy
+++ b/resources/com/google/gerrit/server/mail/NewChangeHtml.soy
@@ -16,7 +16,9 @@
 
 {namespace com.google.gerrit.server.mail.template}
 
-{template .NewChangeHtml}
+import * as mailTemplate from 'com/google/gerrit/server/mail/Private.soy';
+
+{template NewChangeHtml}
   {@param diffLines: ?}
   {@param email: ?}
   {@param fromName: ?}
@@ -41,20 +43,26 @@
 
   {if $email.changeUrl}
     <p>
-      {call .ViewChangeButton data="all" /}
+      {call mailTemplate.ViewChangeButton data="all" /}
     </p>
   {/if}
 
-  {call .Pre}{param content: $email.changeDetail /}{/call}
+  {call mailTemplate.Pre}
+    {param content: $email.changeDetail /}
+  {/call}
 
   {if $email.sshHost}
-    {call .Pre}{param content kind="html"}
-      git pull ssh:{print '//'}{$email.sshHost}/{$projectName}
-          {sp}{$patchSet.refName}
-    {/param}{/call}
+    {call mailTemplate.Pre}
+      {param content kind="html"}
+        git pull ssh:{print '//'}{$email.sshHost}/{$projectName}
+        {sp}{$patchSet.refName}
+      {/param}
+    {/call}
   {/if}
 
   {if $email.includeDiff}
-    {call .UnifiedDiff}{param diffLines: $diffLines /}{/call}
+    {call mailTemplate.UnifiedDiff}
+      {param diffLines: $diffLines /}
+    {/call}
   {/if}
 {/template}
diff --git a/resources/com/google/gerrit/server/mail/NoReplyFooter.soy b/resources/com/google/gerrit/server/mail/NoReplyFooter.soy
index 1443100..d309e90 100644
--- a/resources/com/google/gerrit/server/mail/NoReplyFooter.soy
+++ b/resources/com/google/gerrit/server/mail/NoReplyFooter.soy
@@ -16,7 +16,7 @@
 
 {namespace com.google.gerrit.server.mail.template}
 
-{template .NoReplyFooter kind="text"}
+{template NoReplyFooter kind="text"}
   {\n}
   This is a send-only email address.  Replies to this message will not be read
   or answered.
diff --git a/resources/com/google/gerrit/server/mail/NoReplyFooterHtml.soy b/resources/com/google/gerrit/server/mail/NoReplyFooterHtml.soy
index 93df527..1baf5ab 100644
--- a/resources/com/google/gerrit/server/mail/NoReplyFooterHtml.soy
+++ b/resources/com/google/gerrit/server/mail/NoReplyFooterHtml.soy
@@ -16,7 +16,7 @@
 
 {namespace com.google.gerrit.server.mail.template}
 
-{template .NoReplyFooterHtml}
+{template NoReplyFooterHtml}
   <p>
     This is a send-only email address.  Replies to this message will not be read
     or answered.
diff --git a/resources/com/google/gerrit/server/mail/Private.soy b/resources/com/google/gerrit/server/mail/Private.soy
index 510f15e..7920c21 100644
--- a/resources/com/google/gerrit/server/mail/Private.soy
+++ b/resources/com/google/gerrit/server/mail/Private.soy
@@ -23,7 +23,7 @@
 /**
  * Private template to generate "View Change" buttons.
  */
-{template .ViewChangeButton}
+{template ViewChangeButton}
   {@param email: ?}
   <a href="{$email.changeUrl}">View Change</a>
 {/template}
@@ -31,7 +31,7 @@
 /**
  * Private template to render PRE block with consistent font-sizing.
  */
-{template .Pre}
+{template Pre}
   {@param content: ?}
   {let $preStyle kind="css"}
     font-family: monospace,monospace; // Use this to avoid browsers scaling down
@@ -54,7 +54,7 @@
  * This mechanism encodes as little structure as possible in order to depend on
  * the Soy autoescape mechanism for all of the content.
  */
-{template .WikiFormat}
+{template WikiFormat}
   {@param content: ?}
   {let $blockquoteStyle kind="css"}
     border-left: 1px solid #aaa;
@@ -72,10 +72,10 @@
       <p style="{$pStyle}">{$block.text|changeNewlineToBr}</p>
     {elseif $block.type == 'quote'}
       <blockquote style="{$blockquoteStyle}">
-        {call .WikiFormat}{param content: $block.quotedBlocks /}{/call}
+        {call WikiFormat}{param content: $block.quotedBlocks /}{/call}
       </blockquote>
     {elseif $block.type == 'pre'}
-      {call .Pre}{param content: $block.text /}{/call}
+      {call Pre}{param content: $block.text /}{/call}
     {elseif $block.type == 'list'}
       <ul>
         {for $item in $block.items}
@@ -86,7 +86,7 @@
   {/for}
 {/template}
 
-{template .UnifiedDiff}
+{template UnifiedDiff}
   {@param diffLines: ?}
   {let $addStyle kind="css"}
     color: hsl(120, 100%, 40%);
diff --git a/resources/com/google/gerrit/server/mail/RegisterNewEmail.soy b/resources/com/google/gerrit/server/mail/RegisterNewEmail.soy
index ee03de0..bde8152 100644
--- a/resources/com/google/gerrit/server/mail/RegisterNewEmail.soy
+++ b/resources/com/google/gerrit/server/mail/RegisterNewEmail.soy
@@ -20,7 +20,7 @@
  * The .RegisterNewEmail template will determine the contents of the email
  * related to registering new email accounts.
  */
-{template .RegisterNewEmail kind="text"}
+{template RegisterNewEmail kind="text"}
   {@param email: ?}
   Welcome to Gerrit Code Review at {$email.gerritHost}.{\n}
 
diff --git a/resources/com/google/gerrit/server/mail/RegisterNewEmailHtml.soy b/resources/com/google/gerrit/server/mail/RegisterNewEmailHtml.soy
index 033d145..e3ec3a5 100644
--- a/resources/com/google/gerrit/server/mail/RegisterNewEmailHtml.soy
+++ b/resources/com/google/gerrit/server/mail/RegisterNewEmailHtml.soy
@@ -16,7 +16,7 @@
 
 {namespace com.google.gerrit.server.mail.template}
 
-{template .RegisterNewEmailHtml}
+{template RegisterNewEmailHtml}
   {@param email: ?}
   <p>
     Welcome to Gerrit Code Review at {$email.gerritHost}.
diff --git a/resources/com/google/gerrit/server/mail/RemoveFromAttentionSet.soy b/resources/com/google/gerrit/server/mail/RemoveFromAttentionSet.soy
index f116adb..a329f7b0 100644
--- a/resources/com/google/gerrit/server/mail/RemoveFromAttentionSet.soy
+++ b/resources/com/google/gerrit/server/mail/RemoveFromAttentionSet.soy
@@ -20,7 +20,7 @@
  * The .RemoveFromAttentionSet template will determine the contents of the email related to a
  * user being added to the attention set.
  */
-{template .RemoveFromAttentionSet kind="text"}
+{template RemoveFromAttentionSet kind="text"}
   {@param change: ?}
   {@param coverLetter: ?}
   {@param email: ?}
diff --git a/resources/com/google/gerrit/server/mail/RemoveFromAttentionSetHtml.soy b/resources/com/google/gerrit/server/mail/RemoveFromAttentionSetHtml.soy
index 55eef13..c1c6185 100644
--- a/resources/com/google/gerrit/server/mail/RemoveFromAttentionSetHtml.soy
+++ b/resources/com/google/gerrit/server/mail/RemoveFromAttentionSetHtml.soy
@@ -16,7 +16,9 @@
 
 {namespace com.google.gerrit.server.mail.template}
 
-{template .RemoveFromAttentionSetHtml}
+import * as mailTemplate from 'com/google/gerrit/server/mail/Private.soy';
+
+{template RemoveFromAttentionSetHtml}
   {@param coverLetter: ?}
   {@param email: ?}
   {@param fromName: ?}
@@ -33,11 +35,11 @@
 
   {if $email.changeUrl}
     <p>
-      {call .ViewChangeButton data="all" /}
+      {call mailTemplate.ViewChangeButton data="all" /}
     </p>
   {/if}
 
   {if $coverLetter}
     <div style="white-space:pre-wrap">{$coverLetter}</div>
   {/if}
-{/template}
\ No newline at end of file
+{/template}
diff --git a/resources/com/google/gerrit/server/mail/ReplacePatchSet.soy b/resources/com/google/gerrit/server/mail/ReplacePatchSet.soy
index bb84cf1..6dffa6b 100644
--- a/resources/com/google/gerrit/server/mail/ReplacePatchSet.soy
+++ b/resources/com/google/gerrit/server/mail/ReplacePatchSet.soy
@@ -20,7 +20,7 @@
  * The .ReplacePatchSet template will determine the contents of the email
  * related to a user submitting a new patchset for a change.
  */
-{template .ReplacePatchSet kind="text"}
+{template ReplacePatchSet kind="text"}
   {@param change: ?}
   {@param email: ?}
   {@param fromEmail: ?}
diff --git a/resources/com/google/gerrit/server/mail/ReplacePatchSetHtml.soy b/resources/com/google/gerrit/server/mail/ReplacePatchSetHtml.soy
index 96cba5f..57c6db6 100644
--- a/resources/com/google/gerrit/server/mail/ReplacePatchSetHtml.soy
+++ b/resources/com/google/gerrit/server/mail/ReplacePatchSetHtml.soy
@@ -16,7 +16,9 @@
 
 {namespace com.google.gerrit.server.mail.template}
 
-{template .ReplacePatchSetHtml}
+import * as mailTemplate from 'com/google/gerrit/server/mail/Private.soy';
+
+{template ReplacePatchSetHtml}
   {@param change: ?}
   {@param email: ?}
   {@param fromName: ?}
@@ -35,16 +37,20 @@
 
   {if $email.changeUrl}
     <p>
-      {call .ViewChangeButton data="all" /}
+      {call mailTemplate.ViewChangeButton data="all" /}
     </p>
   {/if}
 
-  {call .Pre}{param content: $email.changeDetail /}{/call}
+  {call mailTemplate.Pre}
+    {param content: $email.changeDetail /}
+  {/call}
 
   {if $email.sshHost}
-    {call .Pre}{param content kind="html"}
-      git pull ssh:{print '//'}{$email.sshHost}/{$projectName}{sp}
-          {$patchSet.refName}
-    {/param}{/call}
+    {call mailTemplate.Pre}
+      {param content kind="html"}
+        git pull ssh:{print '//'}{$email.sshHost}/{$projectName}{sp}
+        {$patchSet.refName}
+      {/param}
+    {/call}
   {/if}
 {/template}
diff --git a/resources/com/google/gerrit/server/mail/Restored.soy b/resources/com/google/gerrit/server/mail/Restored.soy
index 0ec65b30..e09f95f 100644
--- a/resources/com/google/gerrit/server/mail/Restored.soy
+++ b/resources/com/google/gerrit/server/mail/Restored.soy
@@ -20,7 +20,7 @@
  * The .Restored template will determine the contents of the email related to a
  * change being restored.
  */
-{template .Restored kind="text"}
+{template Restored kind="text"}
   {@param change: ?}
   {@param coverLetter: ?}
   {@param email: ?}
diff --git a/resources/com/google/gerrit/server/mail/RestoredHtml.soy b/resources/com/google/gerrit/server/mail/RestoredHtml.soy
index bcd358f..19c4b99 100644
--- a/resources/com/google/gerrit/server/mail/RestoredHtml.soy
+++ b/resources/com/google/gerrit/server/mail/RestoredHtml.soy
@@ -16,7 +16,9 @@
 
 {namespace com.google.gerrit.server.mail.template}
 
-{template .RestoredHtml}
+import * as mailTemplate from 'com/google/gerrit/server/mail/Private.soy';
+
+{template RestoredHtml}
   {@param email: ?}
   {@param fromName: ?}
   <p>
@@ -25,7 +27,7 @@
 
   {if $email.changeUrl}
     <p>
-      {call .ViewChangeButton data="all" /}
+      {call mailTemplate.ViewChangeButton data="all" /}
     </p>
   {/if}
 {/template}
diff --git a/resources/com/google/gerrit/server/mail/Reverted.soy b/resources/com/google/gerrit/server/mail/Reverted.soy
index 32a65c6..bdfd0ad 100644
--- a/resources/com/google/gerrit/server/mail/Reverted.soy
+++ b/resources/com/google/gerrit/server/mail/Reverted.soy
@@ -20,7 +20,7 @@
  * The .Reverted template will determine the contents of the email related
  * to a change being reverted.
  */
-{template .Reverted kind="text"}
+{template Reverted kind="text"}
   {@param change: ?}
   {@param coverLetter: ?}
   {@param email: ?}
diff --git a/resources/com/google/gerrit/server/mail/RevertedHtml.soy b/resources/com/google/gerrit/server/mail/RevertedHtml.soy
index 69260ad..d7b60df 100644
--- a/resources/com/google/gerrit/server/mail/RevertedHtml.soy
+++ b/resources/com/google/gerrit/server/mail/RevertedHtml.soy
@@ -16,7 +16,9 @@
 
 {namespace com.google.gerrit.server.mail.template}
 
-{template .RevertedHtml}
+import * as mailTemplate from 'com/google/gerrit/server/mail/Private.soy';
+
+{template RevertedHtml}
   {@param email: ?}
   {@param fromName: ?}
   <p>
@@ -25,7 +27,7 @@
 
   {if $email.changeUrl}
     <p>
-      {call .ViewChangeButton data="all" /}
+      {call mailTemplate.ViewChangeButton data="all" /}
     </p>
   {/if}
 {/template}
diff --git a/resources/com/google/gerrit/server/mail/SetAssignee.soy b/resources/com/google/gerrit/server/mail/SetAssignee.soy
index 1fdf690..5e83cfb 100644
--- a/resources/com/google/gerrit/server/mail/SetAssignee.soy
+++ b/resources/com/google/gerrit/server/mail/SetAssignee.soy
@@ -20,7 +20,7 @@
  * The .SetAssignee template will determine the contents of the email related
  * to a user being assigned to a change.
  */
-{template .SetAssignee kind="text"}
+{template SetAssignee kind="text"}
   {@param change: ?}
   {@param email: ?}
   {@param fromName: ?}
diff --git a/resources/com/google/gerrit/server/mail/SetAssigneeHtml.soy b/resources/com/google/gerrit/server/mail/SetAssigneeHtml.soy
index 1826314..4ff6cc1 100644
--- a/resources/com/google/gerrit/server/mail/SetAssigneeHtml.soy
+++ b/resources/com/google/gerrit/server/mail/SetAssigneeHtml.soy
@@ -16,7 +16,9 @@
 
 {namespace com.google.gerrit.server.mail.template}
 
-{template .SetAssigneeHtml}
+import * as mailTemplate from 'com/google/gerrit/server/mail/Private.soy';
+
+{template SetAssigneeHtml}
   {@param diffLines: ?}
   {@param email: ?}
   {@param fromName: ?}
@@ -29,20 +31,26 @@
 
   {if $email.changeUrl}
     <p>
-      {call .ViewChangeButton data="all" /}
+      {call mailTemplate.ViewChangeButton data="all" /}
     </p>
   {/if}
 
-  {call .Pre}{param content: $email.changeDetail /}{/call}
+  {call mailTemplate.Pre}
+    {param content: $email.changeDetail /}
+  {/call}
 
   {if $email.sshHost}
-    {call .Pre}{param content kind="html"}
-      git pull ssh:{print '//'}{$email.sshHost}/{$projectName}
-          {sp}{$patchSet.refName}
-    {/param}{/call}
+    {call mailTemplate.Pre}
+      {param content kind="html"}
+        git pull ssh:{print '//'}{$email.sshHost}/{$projectName}
+        {sp}{$patchSet.refName}
+      {/param}
+    {/call}
   {/if}
 
   {if $email.includeDiff}
-    {call .UnifiedDiff}{param diffLines: $diffLines /}{/call}
+    {call mailTemplate.UnifiedDiff}
+      {param diffLines: $diffLines /}
+    {/call}
   {/if}
 {/template}
diff --git a/tools/maven/gerrit-acceptance-framework_pom.xml b/tools/maven/gerrit-acceptance-framework_pom.xml
index f6d1e3b..718f8d0 100644
--- a/tools/maven/gerrit-acceptance-framework_pom.xml
+++ b/tools/maven/gerrit-acceptance-framework_pom.xml
@@ -41,6 +41,9 @@
       <name>Edwin Kempin</name>
     </developer>
     <developer>
+      <name>Gal Paikin</name>
+    </developer>
+    <developer>
       <name>Han-Wen Nienhuys</name>
     </developer>
     <developer>
@@ -50,6 +53,9 @@
       <name>Jacek Centkowski</name>
     </developer>
     <developer>
+      <name>Joerg Zieren</name>
+    </developer>
+    <developer>
       <name>Luca Milanesio</name>
     </developer>
     <developer>
@@ -62,6 +68,9 @@
       <name>Matthias Sohn</name>
     </developer>
     <developer>
+      <name>Milutin Kristofic</name>
+    </developer>
+    <developer>
       <name>Nasser Grainawi</name>
     </developer>
     <developer>
diff --git a/tools/maven/gerrit-extension-api_pom.xml b/tools/maven/gerrit-extension-api_pom.xml
index daf8089..a415f24 100644
--- a/tools/maven/gerrit-extension-api_pom.xml
+++ b/tools/maven/gerrit-extension-api_pom.xml
@@ -41,6 +41,9 @@
       <name>Edwin Kempin</name>
     </developer>
     <developer>
+      <name>Gal Paikin</name>
+    </developer>
+    <developer>
       <name>Han-Wen Nienhuys</name>
     </developer>
     <developer>
@@ -50,6 +53,9 @@
       <name>Jacek Centkowski</name>
     </developer>
     <developer>
+      <name>Joerg Zieren</name>
+    </developer>
+    <developer>
       <name>Luca Milanesio</name>
     </developer>
     <developer>
@@ -62,6 +68,9 @@
       <name>Matthias Sohn</name>
     </developer>
     <developer>
+      <name>Milutin Kristofic</name>
+    </developer>
+    <developer>
       <name>Nasser Grainawi</name>
     </developer>
     <developer>
diff --git a/tools/maven/gerrit-plugin-api_pom.xml b/tools/maven/gerrit-plugin-api_pom.xml
index 07895a4..5e58fdd 100644
--- a/tools/maven/gerrit-plugin-api_pom.xml
+++ b/tools/maven/gerrit-plugin-api_pom.xml
@@ -41,6 +41,9 @@
       <name>Edwin Kempin</name>
     </developer>
     <developer>
+      <name>Gal Paikin</name>
+    </developer>
+    <developer>
       <name>Han-Wen Nienhuys</name>
     </developer>
     <developer>
@@ -50,6 +53,9 @@
       <name>Jacek Centkowski</name>
     </developer>
     <developer>
+      <name>Joerg Zieren</name>
+    </developer>
+    <developer>
       <name>Luca Milanesio</name>
     </developer>
     <developer>
@@ -62,6 +68,9 @@
       <name>Matthias Sohn</name>
     </developer>
     <developer>
+      <name>Milutin Kristofic</name>
+    </developer>
+    <developer>
       <name>Nasser Grainawi</name>
     </developer>
     <developer>
diff --git a/tools/maven/gerrit-war_pom.xml b/tools/maven/gerrit-war_pom.xml
index a28adea..3b3a055 100644
--- a/tools/maven/gerrit-war_pom.xml
+++ b/tools/maven/gerrit-war_pom.xml
@@ -41,6 +41,9 @@
       <name>Edwin Kempin</name>
     </developer>
     <developer>
+      <name>Gal Paikin</name>
+    </developer>
+    <developer>
       <name>Han-Wen Nienhuys</name>
     </developer>
     <developer>
@@ -50,6 +53,9 @@
       <name>Jacek Centkowski</name>
     </developer>
     <developer>
+      <name>Joerg Zieren</name>
+    </developer>
+    <developer>
       <name>Luca Milanesio</name>
     </developer>
     <developer>
@@ -62,6 +68,9 @@
       <name>Matthias Sohn</name>
     </developer>
     <developer>
+      <name>Milutin Kristofic</name>
+    </developer>
+    <developer>
       <name>Nasser Grainawi</name>
     </developer>
     <developer>
diff --git a/tools/nongoogle.bzl b/tools/nongoogle.bzl
index e8f12c8..3f5ef20 100644
--- a/tools/nongoogle.bzl
+++ b/tools/nongoogle.bzl
@@ -164,8 +164,8 @@
     # Keep this version of Soy synchronized with the version used in Gitiles.
     maven_jar(
         name = "soy",
-        artifact = "com.google.template:soy:2019-10-08",
-        sha1 = "4518bf8bac2dbbed684849bc209c39c4cb546237",
+        artifact = "com.google.template:soy:2021-02-01",
+        sha1 = "8e833744832ba88059205a1e30e0898f925d8cb5",
     )
 
     # Test-only dependencies below.
diff --git a/tools/remote-bazelrc b/tools/remote-bazelrc
index 0aeb8d5..de7d0df 100644
--- a/tools/remote-bazelrc
+++ b/tools/remote-bazelrc
@@ -25,7 +25,7 @@
 # this higher can make builds faster by allowing more jobs to run in parallel.
 # Setting it too high can result in jobs that timeout, however, while waiting
 # for a remote machine to execute them.
-build:remote --jobs=50
+build:remote --jobs=100
 build:remote --disk_cache=
 
 # Set several flags related to specifying the platform, toolchain and java