Merge "Log caller when submit rules are evaluated and tracing is enabled"
diff --git a/Documentation/config-gerrit.txt b/Documentation/config-gerrit.txt
index 9e73f6f..76e1f82 100644
--- a/Documentation/config-gerrit.txt
+++ b/Documentation/config-gerrit.txt
@@ -5340,13 +5340,27 @@
[[tracing.traceid.requestUriPattern]]tracing.<trace-id>.requestUriPattern::
+
Regular expression to match request URIs for which request tracing
-should be always enabled. Request URIs are only available for REST
-requests. Request URIs never include the '/a' prefix.
+should be enabled except if they match
+link:tracing.traceid.excludedRequestUriPattern[excludedRequestUriPattern].
+Request URIs are only available for REST requests. Request URIs never include
+the '/a' prefix.
+
May be specified multiple times.
+
By default, unset (all request URIs are matched).
+[[tracing.traceid.excludedRequestUriPattern]]tracing.<trace-id>.excludedRequestUriPattern::
++
+Regular expression to match request URIs for which request tracing
+should not be enabled even if they match
+link:#tracing.traceid.requestUriPattern[requestUriPattern].
+Request URIs are only available for REST requests. Request URIs never include
+the '/a' prefix.
++
+May be specified multiple times.
++
+By default, unset (no request URIs are excluded).
+
[[tracing.traceid.account]]tracing.<trace-id>.account::
+
Account ID of an account for which request tracing should be always
@@ -5413,7 +5427,9 @@
[[deadline.id.requestUriPattern]]deadline.<id>.requestUriPattern::
+
-Regular expression to match request URIs to which the deadline applies. Request
+Regular expression to match request URIs to which the deadline applies except if
+they match
+link:#deadline.id.excludedRequestUriPattern[excludedRequestUriPattern]. Request
URIs are only available for REST requests. Request URIs never include the '/a'
prefix.
+
@@ -5421,6 +5437,17 @@
+
By default, unset (all request URIs are matched).
+[[deadline.id.excludedRequestUriPattern]]deadline.<id>.excludedRequestUriPattern::
++
+Regular expression to match request URIs to which the deadline should not be
+applied even if they match
+link:#deadline.id.requestUriPattern[requestUriPattern]. Request URIs are only
+available for REST requests. Request URIs never include the '/a' prefix.
++
+May be specified multiple times.
++
+By default, unset (no request URIs are excluded).
+
[[deadline.id.account]]deadline.<id>.account::
+
Account ID of an account to which the deadline applies.
diff --git a/Documentation/rest-api-accounts.txt b/Documentation/rest-api-accounts.txt
index a613c7e..ae0c0a6 100644
--- a/Documentation/rest-api-accounts.txt
+++ b/Documentation/rest-api-accounts.txt
@@ -1343,6 +1343,7 @@
"time_format": "HHMM_12",
"size_bar_in_change_table": true,
"disable_keyboard_shortcuts": true,
+ "disable_token_highlighting": true,
"diff_view": "SIDE_BY_SIDE",
"mute_common_path_prefixes": true,
"my": [
@@ -1394,6 +1395,7 @@
"diff_view": "SIDE_BY_SIDE",
"publish_comments_on_push": true,
"disable_keyboard_shortcuts": true,
+ "disable_token_highlighting": true,
"work_in_progress_by_default": true,
"mute_common_path_prefixes": true,
"my": [
@@ -2704,6 +2706,8 @@
Allowed values are `AUTO_MERGE` and `FIRST_PARENT`.
|`disable_keyboard_shortcuts` |not set if `false`|
Whether to disable all keyboard shortcuts.
+|`disable_token_highlighting` [not set if `false`]
+Whether to disable token highlighting on hover.
|`publish_comments_on_push` |not set if `false`|
Whether to link:user-upload.html#publish-comments[publish draft comments] on
push by default.
diff --git a/Documentation/user-search.txt b/Documentation/user-search.txt
index 444c9ee..3977278 100644
--- a/Documentation/user-search.txt
+++ b/Documentation/user-search.txt
@@ -737,6 +737,10 @@
to one of the fields in the
link:rest-api-changes.html#submit-record[SubmitRecord] REST API entity.
+`label:Code-Review\<=-1`::
++
+Matches changes with either a -1, -2, or any lower score.
+
`label:Code-Review=MAX`::
+
Matches changes with label voted with the highest possible score.
@@ -787,10 +791,6 @@
Matches changes with a +1 code review where the reviewer is in the
ldap/linux.workflow group.
-`label:Code-Review\<=-1`::
-+
-Matches changes with either a -1, -2, or any lower score.
-
`is:open label:Code-Review+2 label:Verified+1 NOT label:Verified-1 NOT label:Code-Review-2`::
`is:open label:Code-Review=ok label:Verified=ok`::
+
diff --git a/java/com/google/gerrit/extensions/client/GeneralPreferencesInfo.java b/java/com/google/gerrit/extensions/client/GeneralPreferencesInfo.java
index 21b319e..b26f435 100644
--- a/java/com/google/gerrit/extensions/client/GeneralPreferencesInfo.java
+++ b/java/com/google/gerrit/extensions/client/GeneralPreferencesInfo.java
@@ -148,6 +148,7 @@
public DefaultBase defaultBaseForMerges;
public Boolean publishCommentsOnPush;
public Boolean disableKeyboardShortcuts;
+ public Boolean disableTokenHighlighting;
public Boolean workInProgressByDefault;
public List<MenuItem> my;
public List<String> changeTable;
@@ -207,6 +208,7 @@
p.defaultBaseForMerges = DefaultBase.FIRST_PARENT;
p.publishCommentsOnPush = false;
p.disableKeyboardShortcuts = false;
+ p.disableTokenHighlighting = false;
p.workInProgressByDefault = false;
return p;
}
diff --git a/java/com/google/gerrit/pgm/ChangeExternalIdCaseSensitivity.java b/java/com/google/gerrit/pgm/ChangeExternalIdCaseSensitivity.java
index fbc6065..01c76c1 100644
--- a/java/com/google/gerrit/pgm/ChangeExternalIdCaseSensitivity.java
+++ b/java/com/google/gerrit/pgm/ChangeExternalIdCaseSensitivity.java
@@ -27,6 +27,7 @@
import com.google.gerrit.server.account.externalids.DisabledExternalIdCache;
import com.google.gerrit.server.account.externalids.ExternalId;
import com.google.gerrit.server.account.externalids.ExternalIdFactory;
+import com.google.gerrit.server.account.externalids.ExternalIdKeyFactory;
import com.google.gerrit.server.account.externalids.ExternalIdNotes;
import com.google.gerrit.server.account.externalids.ExternalIdUpsertPreprocessor;
import com.google.gerrit.server.account.externalids.ExternalIds;
@@ -157,8 +158,15 @@
private void recomputeExternalIdNoteId(ExternalIdNotes extIdNotes, ExternalId extId)
throws DuplicateKeyException, IOException {
if (extId.isScheme(SCHEME_GERRIT) || extId.isScheme(SCHEME_USERNAME)) {
- ExternalId.Key updatedKey =
- ExternalId.Key.create(extId.key().scheme(), extId.key().id(), !isUserNameCaseInsensitive);
+ ExternalIdKeyFactory keyFactory =
+ new ExternalIdKeyFactory(
+ new ExternalIdKeyFactory.Config() {
+ @Override
+ public boolean isUserNameCaseInsensitive() {
+ return !isUserNameCaseInsensitive;
+ }
+ });
+ ExternalId.Key updatedKey = keyFactory.create(extId.key().scheme(), extId.key().id());
if (!extId.key().sha1().getName().equals(updatedKey.sha1().getName())) {
logger.atInfo().log("Converting note name of external ID: %s", extId.key());
ExternalId updatedExtId =
diff --git a/java/com/google/gerrit/server/RequestConfig.java b/java/com/google/gerrit/server/RequestConfig.java
index 960907d..83cea5b 100644
--- a/java/com/google/gerrit/server/RequestConfig.java
+++ b/java/com/google/gerrit/server/RequestConfig.java
@@ -41,6 +41,7 @@
RequestConfig.Builder requestConfig = RequestConfig.builder(cfg, section, id);
requestConfig.requestTypes(parseRequestTypes(cfg, section, id));
requestConfig.requestUriPatterns(parseRequestUriPatterns(cfg, section, id));
+ requestConfig.excludedRequestUriPatterns(parseExcludedRequestUriPatterns(cfg, section, id));
requestConfig.accountIds(parseAccounts(cfg, section, id));
requestConfig.projectPatterns(parseProjectPatterns(cfg, section, id));
requestConfigs.add(requestConfig.build());
@@ -61,6 +62,11 @@
return parsePatterns(cfg, section, id, "requestUriPattern");
}
+ private static ImmutableSet<Pattern> parseExcludedRequestUriPatterns(
+ Config cfg, String section, String id) throws ConfigInvalidException {
+ return parsePatterns(cfg, section, id, "excludedRequestUriPattern");
+ }
+
private static ImmutableSet<Account.Id> parseAccounts(Config cfg, String section, String id)
throws ConfigInvalidException {
ImmutableSet.Builder<Account.Id> accountIds = ImmutableSet.builder();
@@ -115,6 +121,9 @@
/** pattern matching request URIs */
abstract ImmutableSet<Pattern> requestUriPatterns();
+ /** pattern matching request URIs to be excluded */
+ abstract ImmutableSet<Pattern> excludedRequestUriPatterns();
+
/** accounts IDs matching calling user */
abstract ImmutableSet<Account.Id> accountIds();
@@ -154,6 +163,13 @@
}
}
+ // If the request URI matches an excluded request URI pattern, then the request is not matched.
+ if (requestInfo.requestUri().isPresent()
+ && excludedRequestUriPatterns().stream()
+ .anyMatch(p -> p.matcher(requestInfo.requestUri().get()).matches())) {
+ return false;
+ }
+
// If in the request config accounts are set and none of them matches, then the request is not
// matched.
if (!accountIds().isEmpty()) {
@@ -200,6 +216,8 @@
abstract Builder requestUriPatterns(ImmutableSet<Pattern> requestUriPatterns);
+ abstract Builder excludedRequestUriPatterns(ImmutableSet<Pattern> excludedRequestUriPatterns);
+
abstract Builder accountIds(ImmutableSet<Account.Id> accountIds);
abstract Builder projectPatterns(ImmutableSet<Pattern> projectPatterns);
diff --git a/java/com/google/gerrit/server/account/externalids/ExternalIdFactory.java b/java/com/google/gerrit/server/account/externalids/ExternalIdFactory.java
index 0c96f58..ee42d67 100644
--- a/java/com/google/gerrit/server/account/externalids/ExternalIdFactory.java
+++ b/java/com/google/gerrit/server/account/externalids/ExternalIdFactory.java
@@ -24,9 +24,9 @@
import com.google.gerrit.entities.Account;
import com.google.gerrit.server.account.HashedPassword;
import com.google.gerrit.server.account.externalids.ExternalId.Key;
-import com.google.inject.Inject;
-import com.google.inject.Singleton;
import java.util.Set;
+import javax.inject.Inject;
+import javax.inject.Singleton;
import org.eclipse.jgit.errors.ConfigInvalidException;
import org.eclipse.jgit.lib.Config;
import org.eclipse.jgit.lib.ObjectId;
diff --git a/java/com/google/gerrit/server/account/externalids/ExternalIdKeyFactory.java b/java/com/google/gerrit/server/account/externalids/ExternalIdKeyFactory.java
index 8d56827..7d2f9de 100644
--- a/java/com/google/gerrit/server/account/externalids/ExternalIdKeyFactory.java
+++ b/java/com/google/gerrit/server/account/externalids/ExternalIdKeyFactory.java
@@ -18,8 +18,8 @@
import com.google.gerrit.common.Nullable;
import com.google.gerrit.server.config.AuthConfig;
import com.google.inject.ImplementedBy;
-import com.google.inject.Inject;
-import com.google.inject.Singleton;
+import javax.inject.Inject;
+import javax.inject.Singleton;
@Singleton
public class ExternalIdKeyFactory {
diff --git a/java/com/google/gerrit/server/query/approval/ListOfFilesUnchangedPredicate.java b/java/com/google/gerrit/server/query/approval/ListOfFilesUnchangedPredicate.java
index ec658ac..b7620e5 100644
--- a/java/com/google/gerrit/server/query/approval/ListOfFilesUnchangedPredicate.java
+++ b/java/com/google/gerrit/server/query/approval/ListOfFilesUnchangedPredicate.java
@@ -14,6 +14,7 @@
package com.google.gerrit.server.query.approval;
+import com.google.gerrit.entities.Patch;
import com.google.gerrit.entities.Patch.ChangeType;
import com.google.gerrit.entities.PatchSet;
import com.google.gerrit.entities.Project;
@@ -27,8 +28,10 @@
import com.google.inject.Singleton;
import java.io.IOException;
import java.util.Collection;
+import java.util.HashSet;
import java.util.Map;
import java.util.Objects;
+import java.util.Set;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevWalk;
@@ -77,13 +80,16 @@
*/
public boolean match(
Map<String, FileDiffOutput> modifiedFiles1, Map<String, FileDiffOutput> modifiedFiles2) {
- if (modifiedFiles1.size() != modifiedFiles2.size()) {
- return false;
- }
- for (String file : modifiedFiles1.keySet()) {
+ Set<String> allFiles = new HashSet<>();
+ allFiles.addAll(modifiedFiles1.keySet());
+ allFiles.addAll(modifiedFiles2.keySet());
+ for (String file : allFiles) {
+ if (Patch.isMagic(file)) {
+ continue;
+ }
FileDiffOutput fileDiffOutput1 = modifiedFiles1.get(file);
FileDiffOutput fileDiffOutput2 = modifiedFiles2.get(file);
- if (fileDiffOutput2 == null) {
+ if (fileDiffOutput1 == null || fileDiffOutput2 == null) {
return false;
}
if (!fileDiffOutput2.changeType().equals(fileDiffOutput1.changeType())) {
diff --git a/javatests/com/google/gerrit/acceptance/api/change/StickyApprovalsIT.java b/javatests/com/google/gerrit/acceptance/api/change/StickyApprovalsIT.java
index f966fc8..d502db2 100644
--- a/javatests/com/google/gerrit/acceptance/api/change/StickyApprovalsIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/change/StickyApprovalsIT.java
@@ -31,6 +31,7 @@
import com.google.common.cache.Cache;
import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
import com.google.gerrit.acceptance.AbstractDaemonTest;
import com.google.gerrit.acceptance.NoHttpd;
import com.google.gerrit.acceptance.PushOneCommit;
@@ -49,6 +50,7 @@
import com.google.gerrit.extensions.client.ChangeKind;
import com.google.gerrit.extensions.common.ApprovalInfo;
import com.google.gerrit.extensions.common.ChangeInfo;
+import com.google.gerrit.extensions.common.FileInfo;
import com.google.gerrit.server.change.ChangeKindCacheImpl;
import com.google.gerrit.server.project.testing.TestLabels;
import com.google.inject.Inject;
@@ -902,6 +904,52 @@
}
@Test
+ public void copyWithListOfFilesUnchangedButAddedMergeList() throws Exception {
+ try (ProjectConfigUpdate u = updateProject(project)) {
+ u.getConfig()
+ .updateLabelType(LabelId.CODE_REVIEW, b -> b.setCopyCondition("has:unchanged-files"));
+ u.save();
+ }
+ Change.Id parent1ChangeId = changeOperations.newChange().create();
+ Change.Id parent2ChangeId = changeOperations.newChange().create();
+ Change.Id dummyParentChangeId = changeOperations.newChange().create();
+ Change.Id changeId =
+ changeOperations
+ .newChange()
+ .mergeOf()
+ .change(parent1ChangeId)
+ .and()
+ .change(parent2ChangeId)
+ .create();
+
+ Map<String, FileInfo> changedFilesFirstPatchset =
+ gApi.changes().id(changeId.get()).current().files();
+
+ assertThat(changedFilesFirstPatchset.keySet()).containsExactly("/COMMIT_MSG", "/MERGE_LIST");
+
+ // Make a Code-Review vote that should be sticky.
+ gApi.changes().id(changeId.get()).current().review(ReviewInput.approve());
+
+ changeOperations
+ .change(changeId)
+ .newPatchset()
+ .parent()
+ .patchset(PatchSet.id(dummyParentChangeId, 1))
+ .create();
+
+ Map<String, FileInfo> changedFilesSecondPatchset =
+ gApi.changes().id(changeId.get()).current().files();
+
+ // Only "/MERGE_LIST" was removed.
+ assertThat(changedFilesSecondPatchset.keySet()).containsExactly("/COMMIT_MSG");
+ ApprovalInfo approvalInfo =
+ Iterables.getOnlyElement(
+ gApi.changes().id(changeId.get()).current().votes().get(LabelId.CODE_REVIEW));
+ assertThat(approvalInfo._accountId).isEqualTo(admin.id().get());
+ assertThat(approvalInfo.value).isEqualTo(2);
+ }
+
+ @Test
public void deleteStickyVote() throws Exception {
String label = LabelId.CODE_REVIEW;
try (ProjectConfigUpdate u = updateProject(project)) {
diff --git a/javatests/com/google/gerrit/acceptance/rest/CancellationIT.java b/javatests/com/google/gerrit/acceptance/rest/CancellationIT.java
index 9f294b3..c5ceea0 100644
--- a/javatests/com/google/gerrit/acceptance/rest/CancellationIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/CancellationIT.java
@@ -232,6 +232,32 @@
@Test
@GerritConfig(name = "deadline.default.timeout", value = "1ms")
+ @GerritConfig(
+ name = "deadline.default.excludedRequestUriPattern",
+ value = "/projects/non-matching")
+ public void abortIfServerDeadlineExceeded_excludedRequestUriPattern() throws Exception {
+ RestResponse response = adminRestSession.putWithHeaders("/projects/" + name("new"));
+ assertThat(response.getStatusCode()).isEqualTo(SC_REQUEST_TIMEOUT);
+ assertThat(response.getEntityContent())
+ .isEqualTo("Server Deadline Exceeded\n\ndefault.timeout=1ms");
+ }
+
+ @Test
+ @GerritConfig(name = "deadline.default.timeout", value = "1ms")
+ @GerritConfig(name = "deadline.default.requestUriPattern", value = "/projects/.*")
+ @GerritConfig(
+ name = "deadline.default.excludedRequestUriPattern",
+ value = "/projects/non-matching")
+ public void abortIfServerDeadlineExceeded_requestUriPatternAndExcludedRequestUriPattern()
+ throws Exception {
+ RestResponse response = adminRestSession.putWithHeaders("/projects/" + name("new"));
+ assertThat(response.getStatusCode()).isEqualTo(SC_REQUEST_TIMEOUT);
+ assertThat(response.getEntityContent())
+ .isEqualTo("Server Deadline Exceeded\n\ndefault.timeout=1ms");
+ }
+
+ @Test
+ @GerritConfig(name = "deadline.default.timeout", value = "1ms")
@GerritConfig(name = "deadline.default.projectPattern", value = ".*new.*")
public void abortIfServerDeadlineExceeded_projectPattern() throws Exception {
RestResponse response = adminRestSession.putWithHeaders("/projects/" + name("new"));
@@ -268,6 +294,24 @@
@Test
@GerritConfig(name = "deadline.default.timeout", value = "1ms")
+ @GerritConfig(name = "deadline.default.excludedRequestUriPattern", value = "/projects/.*")
+ public void nonMatchingServerDeadlineIsIgnored_excludedRequestUriPattern() throws Exception {
+ RestResponse response = adminRestSession.putWithHeaders("/projects/" + name("new"));
+ response.assertCreated();
+ }
+
+ @Test
+ @GerritConfig(name = "deadline.default.timeout", value = "1ms")
+ @GerritConfig(name = "deadline.default.requestUriPattern", value = "/projects/.*")
+ @GerritConfig(name = "deadline.default.excludedRequestUriPattern", value = "/projects/.*new")
+ public void nonMatchingServerDeadlineIsIgnored_requestUriPatternAndExcludedRequestUriPattern()
+ throws Exception {
+ RestResponse response = adminRestSession.putWithHeaders("/projects/" + name("new"));
+ response.assertCreated();
+ }
+
+ @Test
+ @GerritConfig(name = "deadline.default.timeout", value = "1ms")
@GerritConfig(name = "deadline.default.projectPattern", value = ".*foo.*")
public void nonMatchingServerDeadlineIsIgnored_projectPattern() throws Exception {
RestResponse response = adminRestSession.putWithHeaders("/projects/" + name("new"));
@@ -340,6 +384,14 @@
@Test
@GerritConfig(name = "deadline.default.timeout", value = "1ms")
+ @GerritConfig(name = "deadline.default.excludedRequestUriPattern", value = "][")
+ public void invalidServerDeadlineIsIgnored_invalidExcludedRequestUriPattern() throws Exception {
+ RestResponse response = adminRestSession.putWithHeaders("/projects/" + name("new"));
+ response.assertCreated();
+ }
+
+ @Test
+ @GerritConfig(name = "deadline.default.timeout", value = "1ms")
@GerritConfig(name = "deadline.default.projectPattern", value = "][")
public void invalidServerDeadlineIsIgnored_invalidProjectPattern() throws Exception {
RestResponse response = adminRestSession.putWithHeaders("/projects/" + name("new"));
diff --git a/javatests/com/google/gerrit/acceptance/rest/TraceIT.java b/javatests/com/google/gerrit/acceptance/rest/TraceIT.java
index 530f2ec..7e40b2b 100644
--- a/javatests/com/google/gerrit/acceptance/rest/TraceIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/TraceIT.java
@@ -711,6 +711,94 @@
}
@Test
+ @GerritConfig(name = "tracing.issue123.excludedRequestUriPattern", value = "/projects/.*")
+ public void traceExcludedRequestUriPattern() throws Exception {
+ TraceValidatingProjectCreationValidationListener projectCreationListener =
+ new TraceValidatingProjectCreationValidationListener();
+ try (Registration registration =
+ extensionRegistry.newRegistration().add(projectCreationListener)) {
+ RestResponse response = adminRestSession.put("/projects/xyz1");
+ assertThat(response.getStatusCode()).isEqualTo(SC_CREATED);
+ assertThat(response.getHeader(RestApiServlet.X_GERRIT_TRACE)).isNull();
+ assertThat(projectCreationListener.traceId).isNull();
+ assertThat(projectCreationListener.isLoggingForced).isFalse();
+
+ // The logging tag with the project name is also set if tracing is off.
+ assertThat(projectCreationListener.tags.get("project")).containsExactly("xyz1");
+ }
+ }
+
+ @Test
+ @GerritConfig(name = "tracing.issue123.excludedRequestUriPattern", value = "/projects/no-match")
+ public void traceExcludedRequestUriPatternNoMatch() throws Exception {
+ TraceValidatingProjectCreationValidationListener projectCreationListener =
+ new TraceValidatingProjectCreationValidationListener();
+ try (Registration registration =
+ extensionRegistry.newRegistration().add(projectCreationListener)) {
+ RestResponse response = adminRestSession.put("/projects/xyz3");
+ assertThat(response.getStatusCode()).isEqualTo(SC_CREATED);
+ assertThat(response.getHeader(RestApiServlet.X_GERRIT_TRACE)).isNull();
+ assertThat(projectCreationListener.traceId).isEqualTo("issue123");
+ assertThat(projectCreationListener.isLoggingForced).isTrue();
+ assertThat(projectCreationListener.tags.get("project")).containsExactly("xyz3");
+ }
+ }
+
+ @Test
+ @GerritConfig(name = "tracing.issue123.requestUriPattern", value = "/projects/.*")
+ @GerritConfig(name = "tracing.issue123.excludedRequestUriPattern", value = "/projects/xyz2")
+ public void traceRequestUriPatternAndExcludedRequestUriPattern() throws Exception {
+ TraceValidatingProjectCreationValidationListener projectCreationListener =
+ new TraceValidatingProjectCreationValidationListener();
+ try (Registration registration =
+ extensionRegistry.newRegistration().add(projectCreationListener)) {
+ RestResponse response = adminRestSession.put("/projects/xyz2");
+ assertThat(response.getStatusCode()).isEqualTo(SC_CREATED);
+ assertThat(response.getHeader(RestApiServlet.X_GERRIT_TRACE)).isNull();
+ assertThat(projectCreationListener.traceId).isNull();
+ assertThat(projectCreationListener.isLoggingForced).isFalse();
+
+ // The logging tag with the project name is also set if tracing is off.
+ assertThat(projectCreationListener.tags.get("project")).containsExactly("xyz2");
+ }
+ }
+
+ @Test
+ @GerritConfig(name = "tracing.issue123.requestUriPattern", value = "/projects/.*")
+ @GerritConfig(name = "tracing.issue123.excludedRequestUriPattern", value = "/projects/no-match")
+ public void traceRequestUriPatternAndExcludedRequestUriPatternNoMatch() throws Exception {
+ TraceValidatingProjectCreationValidationListener projectCreationListener =
+ new TraceValidatingProjectCreationValidationListener();
+ try (Registration registration =
+ extensionRegistry.newRegistration().add(projectCreationListener)) {
+ RestResponse response = adminRestSession.put("/projects/xyz3");
+ assertThat(response.getStatusCode()).isEqualTo(SC_CREATED);
+ assertThat(response.getHeader(RestApiServlet.X_GERRIT_TRACE)).isNull();
+ assertThat(projectCreationListener.traceId).isEqualTo("issue123");
+ assertThat(projectCreationListener.isLoggingForced).isTrue();
+ assertThat(projectCreationListener.tags.get("project")).containsExactly("xyz3");
+ }
+ }
+
+ @Test
+ @GerritConfig(name = "tracing.issue123.excludedRequestUriPattern", value = "][")
+ public void traceExcludedRequestUriInvalidRegEx() throws Exception {
+ TraceValidatingProjectCreationValidationListener projectCreationListener =
+ new TraceValidatingProjectCreationValidationListener();
+ try (Registration registration =
+ extensionRegistry.newRegistration().add(projectCreationListener)) {
+ RestResponse response = adminRestSession.put("/projects/xyz4");
+ assertThat(response.getStatusCode()).isEqualTo(SC_CREATED);
+ assertThat(response.getHeader(RestApiServlet.X_GERRIT_TRACE)).isNull();
+ assertThat(projectCreationListener.traceId).isNull();
+ assertThat(projectCreationListener.isLoggingForced).isFalse();
+
+ // The logging tag with the project name is also set if tracing is off.
+ assertThat(projectCreationListener.tags.get("project")).containsExactly("xyz4");
+ }
+ }
+
+ @Test
@GerritConfig(name = "retry.retryWithTraceOnFailure", value = "true")
public void noAutoRetryIfExceptionCausesNormalRetrying() throws Exception {
String changeId = createChange().getChangeId();
diff --git a/polygerrit-ui/app/api/diff.ts b/polygerrit-ui/app/api/diff.ts
index 26701e8..1453fd0 100644
--- a/polygerrit-ui/app/api/diff.ts
+++ b/polygerrit-ui/app/api/diff.ts
@@ -209,6 +209,18 @@
line_wrapping?: boolean;
}
+/**
+ * Listens to changes in token highlighting - when a new token starts or stopped being highlighted.
+ * Examples:
+ * - Token highlighted: ('myFunctionName', 12, [Element]).
+ * - Token unhighlighted: (undefined, 0, undefined).
+ */
+export type TokenHighlightedListener = (
+ newHighlight: string | undefined,
+ newLineNumber: number,
+ hoveredElement?: Element
+) => void;
+
export declare interface ImageDiffPreferences {
automatic_blink?: boolean;
}
diff --git a/polygerrit-ui/app/api/embed.ts b/polygerrit-ui/app/api/embed.ts
index ba378e2..fed724e 100644
--- a/polygerrit-ui/app/api/embed.ts
+++ b/polygerrit-ui/app/api/embed.ts
@@ -20,14 +20,24 @@
* limitations under the License.
*/
-import {DiffLayer, GrAnnotation, GrDiffCursor} from './diff';
+import {
+ DiffLayer,
+ GrAnnotation,
+ GrDiffCursor,
+ TokenHighlightedListener,
+} from './diff';
declare global {
interface Window {
grdiff: {
GrAnnotation: GrAnnotation;
GrDiffCursor: {new (): GrDiffCursor};
- TokenHighlightLayer: {new (container?: HTMLElement): DiffLayer};
+ TokenHighlightLayer: {
+ new (
+ container?: HTMLElement,
+ listener?: TokenHighlightedListener
+ ): DiffLayer;
+ };
};
}
}
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 9970dd5..fae518a 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
@@ -116,6 +116,13 @@
super();
this.addEventListener('next-page', () => this._handleNextPage());
this.addEventListener('previous-page', () => this._handlePreviousPage());
+ this.addEventListener('reload', () => {
+ this._loading = true;
+ this._getChanges().then(changes => {
+ this._changes = changes || [];
+ this._loading = false;
+ });
+ });
}
override connectedCallback() {
diff --git a/polygerrit-ui/app/elements/change/gr-reviewer-list/gr-reviewer-list_html.ts b/polygerrit-ui/app/elements/change/gr-reviewer-list/gr-reviewer-list_html.ts
index 058007b..b081be7 100644
--- a/polygerrit-ui/app/elements/change/gr-reviewer-list/gr-reviewer-list_html.ts
+++ b/polygerrit-ui/app/elements/change/gr-reviewer-list/gr-reviewer-list_html.ts
@@ -58,8 +58,8 @@
display: inline-block;
}
gr-vote-chip {
- --gr-vote-chip-width: 16px;
- --gr-vote-chip-height: 16px;
+ --gr-vote-chip-width: 14px;
+ --gr-vote-chip-height: 14px;
}
</style>
<div class="container">
diff --git a/polygerrit-ui/app/elements/change/gr-submit-requirements/gr-submit-requirements.ts b/polygerrit-ui/app/elements/change/gr-submit-requirements/gr-submit-requirements.ts
index 286f24a..724d803 100644
--- a/polygerrit-ui/app/elements/change/gr-submit-requirements/gr-submit-requirements.ts
+++ b/polygerrit-ui/app/elements/change/gr-submit-requirements/gr-submit-requirements.ts
@@ -30,6 +30,7 @@
extractAssociatedLabels,
iconForStatus,
} from '../../../utils/label-util';
+import {fontStyles} from '../../../styles/gr-font-styles';
@customElement('gr-submit-requirements')
export class GrSubmitRequirements extends LitElement {
@@ -44,43 +45,19 @@
static override get styles() {
return [
+ fontStyles,
css`
- :host {
- display: table;
- width: 100%;
- }
.metadata-title {
- font-size: 100%;
font-weight: var(--font-weight-bold);
color: var(--deemphasized-text-color);
padding-left: var(--metadata-horizontal-padding);
- }
- section {
- display: table-row;
- }
- .title {
- min-width: 10em;
- padding: var(--spacing-s) 0 0 0;
- }
- .value {
- padding: var(--spacing-s) 0 0 0;
- }
- .title,
- .status {
- display: table-cell;
- vertical-align: top;
- }
- .value {
- display: inline-flex;
- }
- .status {
- width: var(--line-height-small);
- padding: var(--spacing-s) var(--spacing-m) 0
- var(--requirements-horizontal-padding);
+ margin: 0 0 var(--spacing-s);
+ border-top: 1px solid var(--border-color);
+ padding-top: var(--spacing-s);
}
iron-icon {
- width: var(--line-height-small);
- height: var(--line-height-small);
+ width: var(--line-height-normal, 20px);
+ height: var(--line-height-normal, 20px);
}
iron-icon.check {
color: var(--success-foreground);
@@ -102,6 +79,20 @@
.testing:hover * {
visibility: visible;
}
+ .requirements,
+ section.votes {
+ margin-left: var(--spacing-l);
+ }
+ gr-limited-text.name {
+ font-weight: var(--font-weight-bold);
+ }
+ table {
+ border-collapse: collapse;
+ border-spacing: 0;
+ }
+ td {
+ padding: var(--spacing-s);
+ }
`,
];
}
@@ -110,25 +101,46 @@
const submit_requirements = (this.change?.submit_requirements ?? []).filter(
req => req.status !== SubmitRequirementStatus.NOT_APPLICABLE
);
- return html`<h3 class="metadata-title">Submit Requirements</h3>
+ return html` <h2
+ class="metadata-title heading-3"
+ id="submit-requirements-caption"
+ >
+ Submit Requirements
+ </h2>
+ <table class="requirements" aria-labelledby="submit-requirements-caption">
+ <thead hidden>
+ <tr>
+ <th>Status</th>
+ <th>Name</th>
+ <th>Votes</th>
+ </tr>
+ </thead>
+ <tbody>
+ ${submit_requirements.map(
+ requirement => html`<tr id="requirement-${requirement.name}">
+ <td>${this.renderStatus(requirement.status)}</td>
+ <td class="name">
+ <gr-limited-text
+ class="name"
+ limit="25"
+ .text="${requirement.name}"
+ ></gr-limited-text>
+ </td>
+ <td>${this.renderVotes(requirement)}</td>
+ </tr>`
+ )}
+ </tbody>
+ </table>
${submit_requirements.map(
- requirement => html`<section>
+ requirement => html`
<gr-submit-requirement-hovercard
+ for="requirement-${requirement.name}"
.requirement="${requirement}"
.change="${this.change}"
.account="${this.account}"
.mutable="${this.mutable}"
></gr-submit-requirement-hovercard>
- <div class="status">${this.renderStatus(requirement.status)}</div>
- <div class="title">
- <gr-limited-text
- class="name"
- limit="25"
- text="${requirement.name}"
- ></gr-limited-text>
- </div>
- <div class="value">${this.renderVotes(requirement)}</div>
- </section>`
+ `
)}
${this.renderTriggerVotes(
submit_requirements
@@ -140,6 +152,8 @@
return html`<iron-icon
class="${icon}"
icon="gr-icons:${icon}"
+ role="img"
+ aria-label="${status.toLowerCase()}"
></iron-icon>`;
}
@@ -183,17 +197,19 @@
label => !labelAssociatedWithSubmitReqs.includes(label)
);
if (!triggerVotes.length) return;
- return html`<h3 class="metadata-title">Trigger Votes</h3>
- ${triggerVotes.map(
- label => html`${label}:
- <gr-label-info
- .change="${this.change}"
- .account="${this.account}"
- .mutable="${this.mutable}"
- label="${label}"
- .labelInfo="${labels[label]}"
- ></gr-label-info>`
- )}`;
+ return html`<h3 class="metadata-title heading-3">Trigger Votes</h3>
+ <section class="votes">
+ ${triggerVotes.map(
+ label => html`${label}:
+ <gr-label-info
+ .change="${this.change}"
+ .account="${this.account}"
+ .mutable="${this.mutable}"
+ label="${label}"
+ .labelInfo="${labels[label]}"
+ ></gr-label-info>`
+ )}
+ </section>`;
}
renderFakeControls() {
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 4d97fec..6ed5a2c 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
@@ -102,9 +102,9 @@
items="[[getCommentsDropdownEntires(threads, loggedIn)]]"
>
</gr-dropdown-list>
- <template is="dom-if" if="[[threads.length]]">
+ <template is="dom-if" if="[[_displayedThreads.length]]">
<span class="author-text">From:</span>
- <template is="dom-repeat" items="[[getCommentAuthors(threads, account)]]">
+ <template is="dom-repeat" items="[[getCommentAuthors(_displayedThreads, account)]]">
<gr-account-label
account="[[item]]"
on-click="handleAccountClicked"
diff --git a/polygerrit-ui/app/elements/change/gr-thread-list/gr-thread-list_test.js b/polygerrit-ui/app/elements/change/gr-thread-list/gr-thread-list_test.js
index b478ef5..aab5cee 100644
--- a/polygerrit-ui/app/elements/change/gr-thread-list/gr-thread-list_test.js
+++ b/polygerrit-ui/app/elements/change/gr-thread-list/gr-thread-list_test.js
@@ -495,15 +495,18 @@
test('tapping single author chips', () => {
element.account = createAccountDetailWithId(1);
flush();
- const chips = queryAll(element, 'gr-account-label');
- const authors = Array.from(chips).map(
+ const chips = Array.from(queryAll(element, 'gr-account-label'));
+ const authors = chips.map(
chip => accountOrGroupKey(chip.account))
.sort();
assert.deepEqual(authors, [1, 1000000, 1000001, 1000002, 1000003]);
assert.equal(element.threads.length, 9);
assert.equal(element._displayedThreads.length, 9);
- tap(chips[0]); // accountId 1000001
+ // accountId 1000001
+ const chip = chips.find(chip => chip.account._account_id === 1000001);
+
+ tap(chip);
flush();
assert.equal(element.threads.length, 9);
@@ -511,7 +514,7 @@
assert.equal(element._displayedThreads[0].comments[0].author._account_id,
1000001);
- tap(chips[0]); // tapping again resets
+ tap(chip); // tapping again resets
flush();
assert.equal(element.threads.length, 9);
assert.equal(element._displayedThreads.length, 9);
@@ -520,10 +523,10 @@
test('tapping multiple author chips', () => {
element.account = createAccountDetailWithId(1);
flush();
- const chips = queryAll(element, 'gr-account-label');
+ const chips = Array.from(queryAll(element, 'gr-account-label'));
- tap(chips[0]); // accountId 1000001
- tap(chips[2]); // accountId 1000002
+ tap(chips.find(chip => chip.account._account_id === 1000001));
+ tap(chips.find(chip => chip.account._account_id === 1000002));
flush();
assert.equal(element.threads.length, 9);
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 6dd67e4..1c25755 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
@@ -38,7 +38,7 @@
import {OpenFixPreviewEvent} from '../../../types/events';
import {appContext} from '../../../services/app-context';
import {fireCloseFixPreview, fireEvent} from '../../../utils/event-util';
-import {ParsedChangeInfo} from '../../../types/types';
+import {DiffLayer, ParsedChangeInfo} from '../../../types/types';
import {GrButton} from '../../shared/gr-button/gr-button';
import {TokenHighlightLayer} from '../gr-diff-builder/token-highlight-layer';
import {KnownExperimentId} from '../../../services/flags/flags';
@@ -99,16 +99,25 @@
})
_disableApplyFixButton = false;
- layers = appContext.flagsService.isEnabled(
- KnownExperimentId.TOKEN_HIGHLIGHTING
- )
- ? [new TokenHighlightLayer(this)]
- : [];
+ @property({type: Array})
+ layers: DiffLayer[] = [];
private refitOverlay?: () => void;
private readonly restApiService = appContext.restApiService;
+ constructor() {
+ super();
+ this.restApiService.getPreferences().then(prefs => {
+ if (
+ !prefs?.disable_token_highlighting &&
+ appContext.flagsService.isEnabled(KnownExperimentId.TOKEN_HIGHLIGHTING)
+ ) {
+ this.layers = [new TokenHighlightLayer(this)];
+ }
+ });
+ }
+
/**
* Given robot comment CustomEvent object, fetch diffs associated
* with first robot comment suggested fix and open dialog.
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-builder/token-highlight-layer.ts b/polygerrit-ui/app/elements/diff/gr-diff-builder/token-highlight-layer.ts
index 56bb073..480e26c 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-builder/token-highlight-layer.ts
+++ b/polygerrit-ui/app/elements/diff/gr-diff-builder/token-highlight-layer.ts
@@ -15,7 +15,7 @@
* limitations under the License.
*/
import {DiffLayer, DiffLayerListener} from '../../../types/types';
-import {GrDiffLine, Side} from '../../../api/diff';
+import {GrDiffLine, Side, TokenHighlightedListener} from '../../../api/diff';
import {GrAnnotation} from '../gr-diff-highlight/gr-annotation';
import {debounce, DelayedTask} from '../../../utils/async-util';
import {
@@ -65,6 +65,9 @@
/** The currently highlighted token. */
private currentHighlight?: string;
+ /** Trigger when a new token starts or stoped being highlighted.*/
+ private readonly tokenHighlightedListener?: TokenHighlightedListener;
+
/**
* The line of the currently highlighted token. We store this in order to
* re-render only relevant lines of the diff. Only lines visible on the screen
@@ -95,7 +98,11 @@
private updateTokenTask?: DelayedTask;
- constructor(container: HTMLElement = document.documentElement) {
+ constructor(
+ container: HTMLElement = document.documentElement,
+ tokenHighlightedListener?: TokenHighlightedListener
+ ) {
+ this.tokenHighlightedListener = tokenHighlightedListener;
container.addEventListener('click', e => {
this.handleContainerClick(e);
});
@@ -188,7 +195,7 @@
this.updateTokenTask = debounce(
this.updateTokenTask,
() => {
- this.updateTokenHighlight(newHighlight, line);
+ this.updateTokenHighlight(newHighlight, line, element);
},
HOVER_DELAY_MS
);
@@ -203,7 +210,7 @@
if (element) return;
this.hoveredElement = undefined;
this.updateTokenTask?.cancel();
- this.updateTokenHighlight(undefined, 0);
+ this.updateTokenHighlight(undefined, 0, undefined);
}
private interferesWithSelection() {
@@ -241,7 +248,8 @@
private updateTokenHighlight(
newHighlight: string | undefined,
- newLineNumber: number
+ newLineNumber: number,
+ newHoveredElement: Element | undefined
) {
if (
this.currentHighlight === newHighlight &&
@@ -253,6 +261,13 @@
this.currentHighlight = newHighlight;
this.currentHighlightLineNumber = newLineNumber;
+ if (this.tokenHighlightedListener) {
+ this.tokenHighlightedListener(
+ newHighlight,
+ newLineNumber,
+ newHoveredElement
+ );
+ }
this.notifyForToken(oldHighlight, oldLineNumber);
this.notifyForToken(newHighlight, newLineNumber);
}
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-builder/token-highlight-layer_test.ts b/polygerrit-ui/app/elements/diff/gr-diff-builder/token-highlight-layer_test.ts
index 9fc69b5..2cb08d6 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-builder/token-highlight-layer_test.ts
+++ b/polygerrit-ui/app/elements/diff/gr-diff-builder/token-highlight-layer_test.ts
@@ -66,12 +66,22 @@
let container: HTMLElement;
let listener: MockListener;
let highlighter: TokenHighlightLayer;
+ let tokenHighlightingCalls: any[] = [];
+
+ function tokenHighlightedListener(
+ newHighlight: string | undefined,
+ newLineNumber: number,
+ hoveredElement?: Element
+ ) {
+ tokenHighlightingCalls.push({newHighlight, newLineNumber, hoveredElement});
+ }
setup(async () => {
listener = new MockListener();
+ tokenHighlightingCalls = [];
container = document.createElement('div');
document.body.appendChild(container);
- highlighter = new TokenHighlightLayer(container);
+ highlighter = new TokenHighlightLayer(container, tokenHighlightedListener);
highlighter.addListener((...args) => listener.notify(...args));
});
@@ -251,6 +261,37 @@
assert.equal(_testOnly_allTasks.size, 0);
});
+ test('triggers listener for applying and clearing highlighting', async () => {
+ const clock = sinon.useFakeTimers();
+ const line1 = createLine('two words');
+ annotate(line1);
+ const line2 = createLine('three words', 2);
+ annotate(line2, Side.RIGHT, 2);
+ const words1 = queryAndAssert(line1, '.tk-words');
+ assert.isTrue(words1.classList.contains('token'));
+ dispatchMouseEvent(
+ 'mouseover',
+ MockInteractions.middleOfNode(words1),
+ words1
+ );
+ assert.equal(tokenHighlightingCalls.length, 0);
+ clock.tick(HOVER_DELAY_MS);
+ assert.equal(tokenHighlightingCalls.length, 1);
+ assert.deepEqual(tokenHighlightingCalls[0], {
+ newHighlight: 'words',
+ newLineNumber: 1,
+ hoveredElement: words1,
+ });
+
+ MockInteractions.click(container);
+ assert.equal(tokenHighlightingCalls.length, 2);
+ assert.deepEqual(tokenHighlightingCalls[1], {
+ newHighlight: undefined,
+ newLineNumber: 0,
+ hoveredElement: undefined,
+ });
+ });
+
test('clicking clears highlight', async () => {
const clock = sinon.useFakeTimers();
const line1 = createLine('two words');
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 c4fed53..463163c 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
@@ -223,6 +223,9 @@
@property({type: Boolean})
_loggedIn = false;
+ @property({type: Boolean})
+ disableTokenHighlighting = false;
+
@property({type: String})
_errorMessage: string | null = null;
@@ -300,6 +303,11 @@
this.addEventListener('diff-context-expanded', event =>
this._handleDiffContextExpanded(event)
);
+ appContext.restApiService.getPreferences().then(prefs => {
+ if (prefs?.disable_token_highlighting) {
+ this.disableTokenHighlighting = prefs.disable_token_highlighting;
+ }
+ });
}
override ready() {
@@ -413,7 +421,8 @@
private _getLayers(path: string): DiffLayer[] {
const layers = [];
if (
- appContext.flagsService.isEnabled(KnownExperimentId.TOKEN_HIGHLIGHTING)
+ appContext.flagsService.isEnabled(KnownExperimentId.TOKEN_HIGHLIGHTING) &&
+ !this.disableTokenHighlighting
) {
layers.push(new TokenHighlightLayer(this));
}
diff --git a/polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-view.ts b/polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-view.ts
index 453bc3f..da4bb0a 100644
--- a/polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-view.ts
+++ b/polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-view.ts
@@ -85,6 +85,7 @@
'diff_view',
'publish_comments_on_push',
'disable_keyboard_shortcuts',
+ 'disable_token_highlighting',
'work_in_progress_by_default',
'default_base_for_merges',
'signed_off_by',
@@ -124,6 +125,7 @@
showSizeBarsInFileList: HTMLInputElement;
publishCommentsOnPush: HTMLInputElement;
disableKeyboardShortcuts: HTMLInputElement;
+ disableTokenHighlighting: HTMLInputElement;
relativeDateInChangeTable: HTMLInputElement;
changesPerPageSelect: HTMLInputElement;
dateTimeFormatSelect: HTMLInputElement;
@@ -408,6 +410,13 @@
);
}
+ _handleDisableTokenHighlightingChanged() {
+ this.set(
+ '_localPrefs.disable_token_highlighting',
+ this.$.disableTokenHighlighting.checked
+ );
+ }
+
_handleWorkInProgressByDefault() {
this.set(
'_localPrefs.work_in_progress_by_default',
diff --git a/polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-view_html.ts b/polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-view_html.ts
index 1ed0d57..78c4a62 100644
--- a/polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-view_html.ts
+++ b/polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-view_html.ts
@@ -309,6 +309,19 @@
</span>
</section>
<section>
+ <label for="disableTokenHighlighting" class="title"
+ >Disable token highlighting on hover</label
+ >
+ <span class="value">
+ <input
+ id="disableTokenHighlighting"
+ type="checkbox"
+ checked$="[[_localPrefs.disable_token_highlighting]]"
+ on-change="_handleDisableTokenHighlightingChanged"
+ />
+ </span>
+ </section>
+ <section>
<label for="insertSignedOff" class="title">
Insert Signed-off-by Footer For Inline Edit Changes
</label>
diff --git a/polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-view_test.ts b/polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-view_test.ts
index 26705ee..8194d5b 100644
--- a/polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-view_test.ts
+++ b/polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-view_test.ts
@@ -244,6 +244,13 @@
);
assert.equal(
(
+ valueOf('Disable token highlighting on hover', 'preferences')!
+ .firstElementChild as HTMLInputElement
+ ).checked,
+ false
+ );
+ assert.equal(
+ (
valueOf(
'Insert Signed-off-by Footer For Inline Edit Changes',
'preferences'
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 d6aae5c..82f6b46 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
@@ -204,6 +204,9 @@
@property({type: Object})
_selfAccount?: AccountDetailInfo;
+ @property({type: Boolean})
+ disableTokenHighlighting = false;
+
get keyBindings() {
return {
'e shift+e': '_handleEKey',
@@ -227,6 +230,11 @@
this.addEventListener('comment-update', e =>
this._handleCommentUpdate(e as CustomEvent)
);
+ appContext.restApiService.getPreferences().then(prefs => {
+ if (prefs?.disable_token_highlighting) {
+ this.disableTokenHighlighting = prefs.disable_token_highlighting;
+ }
+ });
}
override connectedCallback() {
@@ -360,7 +368,10 @@
_getLayers(diff?: DiffInfo) {
if (!diff) return [];
const layers = [];
- if (this.flagsService.isEnabled(KnownExperimentId.TOKEN_HIGHLIGHTING)) {
+ if (
+ this.flagsService.isEnabled(KnownExperimentId.TOKEN_HIGHLIGHTING) &&
+ !this.disableTokenHighlighting
+ ) {
layers.push(new TokenHighlightLayer(this));
}
layers.push(this.syntaxLayer);
diff --git a/polygerrit-ui/app/elements/shared/gr-vote-chip/gr-vote-chip.ts b/polygerrit-ui/app/elements/shared/gr-vote-chip/gr-vote-chip.ts
index 5b9ad5c..73ca99d 100644
--- a/polygerrit-ui/app/elements/shared/gr-vote-chip/gr-vote-chip.ts
+++ b/polygerrit-ui/app/elements/shared/gr-vote-chip/gr-vote-chip.ts
@@ -81,13 +81,13 @@
.vote-chip,
.chip-angle {
display: flex;
- width: var(--gr-vote-chip-width, 18px);
- height: var(--gr-vote-chip-height, 18px);
+ width: var(--gr-vote-chip-width, 16px);
+ height: var(--gr-vote-chip-height, 16px);
justify-content: center;
margin-right: var(--spacing-s);
padding: 1px;
border-radius: var(--border-radius);
- line-height: var(--gr-vote-chip-width, 18px);
+ line-height: var(--gr-vote-chip-width, 16px);
}
.vote-chip {
position: relative;
diff --git a/polygerrit-ui/app/services/flags/flags.ts b/polygerrit-ui/app/services/flags/flags.ts
index ef5fde2..2839874 100644
--- a/polygerrit-ui/app/services/flags/flags.ts
+++ b/polygerrit-ui/app/services/flags/flags.ts
@@ -27,6 +27,5 @@
NEW_IMAGE_DIFF_UI = 'UiFeature__new_image_diff_ui',
TOKEN_HIGHLIGHTING = 'UiFeature__token_highlighting',
CHECKS_DEVELOPER = 'UiFeature__checks_developer',
- NEW_REPLY_DIALOG = 'UiFeature__new_reply_dialog',
SUBMIT_REQUIREMENTS_UI = 'UiFeature__submit_requirements_ui',
}
diff --git a/polygerrit-ui/app/types/common.ts b/polygerrit-ui/app/types/common.ts
index 98c60d2..1617aa3 100644
--- a/polygerrit-ui/app/types/common.ts
+++ b/polygerrit-ui/app/types/common.ts
@@ -1143,6 +1143,7 @@
default_base_for_merges: DefaultBase;
publish_comments_on_push?: boolean;
disable_keyboard_shortcuts?: boolean;
+ disable_token_highlighting?: boolean;
work_in_progress_by_default?: boolean;
// The email_format doesn't mentioned in doc, but exists in Java class GeneralPreferencesInfo
email_format?: EmailFormat;