Merge "Fix OperatorPrecedence bug pattern flagged by error prone"
diff --git a/Documentation/dev-bazel.txt b/Documentation/dev-bazel.txt
index 514a4c9..ce4cef4 100644
--- a/Documentation/dev-bazel.txt
+++ b/Documentation/dev-bazel.txt
@@ -35,8 +35,14 @@
link:https://github.com/bazelbuild/bazelisk[Bazelisk,role=external,window=_blank] is a version
manager for link:https://bazel.build/[Bazel,role=external,window=_blank], similar to how `nvm`
manages `npm` versions. It takes care of downloading and installing Bazel itself, so you don't have
-to worry about using the correct version of Bazel. Bazelisk can be installed in different
-ways: link:https://docs.bazel.build/install-bazelisk.html[Install,role=external,window=_blank]
+to worry about using the correct version of Bazel. One particular advantage to
+using Bazelisk is that you can jump between different versions of Gerrit and not
+worry about which version of Bazel you need.
+
+Bazelisk can be installed in different ways:
+link:https://docs.bazel.build/install-bazelisk.html[Bazelisk Installation,role=external,window=_blank].
+To execute the correct version of Bazel using Bazelisk you simply replace
+the `bazel` command with `bazelisk`.
[[java]]
=== Java
@@ -54,7 +60,7 @@
To build Gerrit with Java 11 language level, run:
```
- $ bazel build --java_toolchain=//tools:error_prone_warnings_toolchain_java11 :release
+ $ bazelisk build --java_toolchain=//tools:error_prone_warnings_toolchain_java11 :release
```
[[java-17]]
@@ -63,13 +69,13 @@
Java 17 is supported. To build Gerrit with Java 17, run:
```
- $ bazel build --config=java17 :release
+ $ bazelisk build --config=java17 :release
```
To run the tests with Java 17, run:
```
- $ bazel test --config=java17 //...
+ $ bazelisk test --config=java17 //...
```
=== Node.js and npm packages
@@ -83,7 +89,7 @@
To build the Gerrit web application:
----
- bazel build gerrit
+ bazelisk build gerrit
----
The output executable WAR will be placed in:
@@ -99,7 +105,7 @@
core plugins and documentation:
----
- bazel build release
+ bazelisk build release
----
The output executable WAR will be placed in:
@@ -113,7 +119,7 @@
To build Gerrit in headless mode, i.e. without the Gerrit UI:
----
- bazel build headless
+ bazelisk build headless
----
The output executable WAR will be placed in:
@@ -127,7 +133,7 @@
To build the extension, plugin and acceptance-framework JAR files:
----
- bazel build api
+ bazelisk build api
----
The output archive that contains Java binaries, Java sources and
@@ -153,7 +159,7 @@
=== Plugins
----
- bazel build plugins:core
+ bazelisk build plugins:core
----
The output JAR files for individual plugins will be placed in:
@@ -171,7 +177,7 @@
To build a specific plugin:
----
- bazel build plugins/<name>
+ bazelisk build plugins/<name>
----
The output JAR file will be be placed in:
@@ -216,7 +222,7 @@
To build only the documentation for testing or static hosting:
----
- bazel build Documentation:searchfree
+ bazelisk build Documentation:searchfree
----
The html files will be bundled into `searchfree.zip` in this location:
@@ -228,7 +234,7 @@
To generate HTML files skipping the zip archiving:
----
- bazel build Documentation
+ bazelisk build Documentation
----
And open `bazel-bin/Documentation/index.html`.
@@ -236,7 +242,7 @@
To build the Gerrit executable WAR with the documentation included:
----
- bazel build withdocs
+ bazelisk build withdocs
----
The WAR file will be placed in:
@@ -248,7 +254,7 @@
Alternatively, one can generate the documentation as flat files:
----
- bazel build Documentation:Documentation
+ bazelisk build Documentation:Documentation
----
The html, css, js files are placed in:
@@ -260,64 +266,23 @@
[[tests]]
== Running Unit Tests
-----
- bazel test --build_tests_only //...
-----
-
-Debugging tests:
+Bazel BUILD files define test targets for Gerrit. You can run all declared
+test targets with:
----
- bazel test --test_output=streamed --test_filter=com.gerrit.TestClass.testMethod testTarget
+ bazelisk test --build_tests_only //...
----
-Debug test example:
+[[testgroups]]
+=== Running Test Groups
+
+To run one or more specific labeled groups of tests:
----
- bazel test --test_output=streamed --test_filter=com.google.gerrit.acceptance.api.change.ChangeIT.getAmbiguous //javatests/com/google/gerrit/acceptance/api/change:api_change
+ bazelisk test --test_tag_filters=api,git //...
----
-To run a specific test group, e.g. the rest-account test group:
-
-----
- bazel test //javatests/com/google/gerrit/acceptance/rest/account:rest_account
-----
-
-To run only tests that do not use SSH:
-
-----
- bazel test --test_env=GERRIT_USE_SSH=NO //...
-----
-
-To exclude tests that have been marked as flaky:
-
-----
- bazel test --test_tag_filters=-flaky //...
-----
-
-To exclude tests that require very recent git client version:
-
-----
- bazel test --test_tag_filters=-git-protocol-v2 //...
-----
-
-To ignore cached test results:
-
-----
- bazel test --cache_test_results=NO //...
-----
-
-To run one or more specific groups of tests:
-
-----
- bazel test --test_tag_filters=api,git //...
-----
-
-To run the tests against a specific index backend (LUCENE, FAKE):
-----
- bazel test --test_env=GERRIT_INDEX_TYPE=LUCENE //...
-----
-
-The following values are currently supported for the group name:
+The following label values are currently supported for the group name:
* annotation
* api
@@ -331,11 +296,90 @@
* server
* ssh
+We can also select tests within a specific BUILD target group. For example
+`javatests/com/google/gerrit/acceptance/rest/account/BUILD` declares a
+rest_account test target group:
+
+----
+ bazelisk test //javatests/com/google/gerrit/acceptance/rest/account:rest_account
+----
+
+[[debugtests]]
+=== Debugging Tests
+
+To debug specific tests you will need to select the test target containing
+that test then use `--test_filter` to select the specific test you want.
+This `--test_filter` is a regex and can be used to select multiple tests
+out of the target:
+
+----
+ bazelisk test --test_output=streamed --test_filter=com.gerrit.TestClass.testMethod testTarget
+----
+
+For example `javatests/com/google/gerrit/acceptance/api/change/BUILD`
+defines a test target group for every `*IT.java` file in the directory.
+We can execute the single `getAmbiguous()` test found in ChangeIT.java using
+this `--test_filter` and target:
+
+----
+ bazelisk test --test_output=streamed \
+ --test_filter=com.google.gerrit.acceptance.api.change.ChangeIT.getAmbiguous \
+ //javatests/com/google/gerrit/acceptance/api/change:ChangeIT
+----
+
+[[additionaltestfiltering]]
+=== Additional Test Filtering
+
+To run only tests that do not use SSH:
+
+----
+ bazelisk test --test_env=GERRIT_USE_SSH=NO //...
+----
+
+To exclude tests that have been marked as flaky:
+
+----
+ bazelisk test --test_tag_filters=-flaky //...
+----
+
+To exclude tests that require very recent git client version:
+
+----
+ bazelisk test --test_tag_filters=-git-protocol-v2 //...
+----
+
+To run the tests against a specific index backend (LUCENE, FAKE):
+----
+ bazelisk test --test_env=GERRIT_INDEX_TYPE=LUCENE //...
+----
+
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]
+link:https://bazel.build/run/build#specifying-build-targets[specify targets,role=external,window=_blank]
for fine-grained test selection that can be combined with many of the examples
above.
+[[testcaching]]
+=== Test Caching
+
+By default Bazel caches test results and will not reexecute tests unless they
+or their dependencies have been modified. To ignore cached test results and
+force the tests to rerun:
+
+----
+ bazelisk test --cache_test_results=NO //...
+----
+
+[[plugintests]]
+=== Running Plugin Tests
+
+Running tests for Gerrit plugins follows the process above. From within the
+Gerrit project root with the desired plugins checked out into `plugins/` we
+execute Bazel with the appropriate target:
+
+----
+ bazelisk test //plugins/replication/...
+----
+
[[debugging-tests]]
== Debugging Unit Tests
In some cases it may be necessary to debug a test while running it in bazel. For example, when we
@@ -345,7 +389,7 @@
Example:
[source,bash]
----
- bazel test --java_debug --test_tag_filters=delete-project //...
+ bazelisk test --java_debug --test_tag_filters=delete-project //...
...
Listening for transport dt_socket at address: 5005
...
@@ -364,9 +408,9 @@
`GERRIT_LOG_LEVEL=debug` environment variable:
----
- bazel test --test_filter=com.google.gerrit.server.notedb.ChangeNotesTest \
- --test_env=GERRIT_LOG_LEVEL=debug \
- javatests/com/google/gerrit/server:server_tests
+ bazelisk test --test_filter=com.google.gerrit.server.notedb.ChangeNotesTest \
+ --test_env=GERRIT_LOG_LEVEL=debug \
+ javatests/com/google/gerrit/server:server_tests
----
The log results can be found in:
@@ -380,7 +424,7 @@
subsequent builds to run without network access:
----
- bazel fetch //...
+ bazelisk fetch //...
----
When downloading from behind a proxy (which is common in some corporate
@@ -485,7 +529,7 @@
The `downloaded-artifacts` cache can be relocated by setting the
`GERRIT_CACHE_HOME` environment variable. The other two can be adjusted with
-`bazel build` options `--repository_cache` and `--disk_cache` respectively.
+`bazelisk build` options `--repository_cache` and `--disk_cache` respectively.
Currently none of these caches have a maximum size limit. See
link:https://github.com/bazelbuild/bazel/issues/5139[this bazel issue,role=external,window=_blank] for
@@ -547,7 +591,7 @@
----
# Add to ui_npm. Other packages.json can be updated in the same way
cd $gerrit_repo/polygerrit-ui/app
-bazel run @nodejs//:yarn add $package
+bazelisk run @nodejs//:yarn add $package
----
Update the `polygerrit-ui/app/node_modules_licenses/licenses.ts` file. You should add licenses
@@ -577,7 +621,7 @@
=== Update NPM Binaries
To update a NPM binary the same actions as for a new one must be done (check licenses,
update `licenses.ts` file, etc...). The only difference is a command to install a package: instead
-of `bazel run @nodejs//:yarn add $package` you should run the `bazel run @nodejs//:yarn upgrade ...`
+of `bazelisk run @nodejs//:yarn add $package` you should run the `bazelisk run @nodejs//:yarn upgrade ...`
command with correct arguments. You can find the list of arguments in the
link:https://classic.yarnpkg.com/en/docs/cli/upgrade/[yarn upgrade doc,role=external,window=_blank].
@@ -644,7 +688,7 @@
To use RBE, execute
```
-bazel test --config=remote \
+bazelisk test --config=remote \
--remote_instance_name=projects/${PROJECT}/instances/default_instance \
javatests/...
```
diff --git a/javatests/com/google/gerrit/acceptance/api/revision/RevisionDiffIT.java b/javatests/com/google/gerrit/acceptance/api/revision/RevisionDiffIT.java
index 5ab9fcb..7eb33b6 100644
--- a/javatests/com/google/gerrit/acceptance/api/revision/RevisionDiffIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/revision/RevisionDiffIT.java
@@ -29,6 +29,7 @@
import static java.util.stream.Collectors.toMap;
import com.google.common.base.Joiner;
+import com.google.common.base.Splitter;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.gerrit.acceptance.AbstractDaemonTest;
@@ -3135,6 +3136,49 @@
assertThat(diffInfo).webLinks().isNull();
}
+ @Test
+ public void diffForBinaryFileThatIsNotTouchedInTheChange() throws Exception {
+ String imageFileName1 = "an_image.png";
+ byte[] imageBytes1 = createRgbImage(255, 0, 0);
+ String imageContent1 = new String(imageBytes1, UTF_8);
+ Change.Id changeId1 =
+ changeOperations.newChange().file(imageFileName1).content(imageContent1).create();
+
+ String imageFileName2 = "another_image.png";
+ byte[] imageBytes2 = createRgbImage(0, 255, 0);
+ Change.Id changeId2 =
+ changeOperations
+ .newChange()
+ .childOf()
+ .change(changeId1)
+ .file(imageFileName2)
+ .content(new String(imageBytes2, UTF_8))
+ .create();
+
+ // Since file imageFileName1 was not touched in the second change, trying to get the diff for it
+ // should probably fail with '404 Not Found'.
+ DiffInfo diffInfo = gApi.changes().id(changeId2.get()).current().file(imageFileName1).diff();
+
+ // This should be detected as a binary file, but it isn't.
+ assertThat(diffInfo).binary().isNull();
+
+ // For binary files linesOfA, linesOfB and commonLines are expected to be null, but the content
+ // of the binary file is returned as common lines.
+ ContentEntrySubject contentEntry = assertThat(diffInfo).content().onlyElement();
+ contentEntry.linesOfA().isNull();
+ contentEntry.linesOfB().isNull();
+ contentEntry
+ .commonLines()
+ .containsExactlyElementsIn(Splitter.on("\n").splitToList(imageContent1));
+
+ // For binary file the header list should contain "Binary files differ", but it doesn't.
+ assertThat(diffInfo).diffHeader().isNull();
+
+ assertThat(diffInfo).metaA().isNotNull();
+ assertThat(diffInfo).metaB().isNotNull();
+ assertThat(diffInfo).webLinks().isNull();
+ }
+
private Registration newEditWebLink() {
EditWebLink webLink =
new EditWebLink() {
diff --git a/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list.ts b/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list.ts
index 1c9ba8c..fdacf8b 100644
--- a/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list.ts
+++ b/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list.ts
@@ -47,8 +47,7 @@
import {DiffPreferencesInfo} from '../../../types/diff';
import {GrDiffHost} from '../../diff/gr-diff-host/gr-diff-host';
import {GrDiffPreferencesDialog} from '../../diff/gr-diff-preferences-dialog/gr-diff-preferences-dialog';
-import {GrDiffCursor as GrDiffCursorNew} from '../../../embed/diff/gr-diff-cursor/gr-diff-cursor';
-import {GrDiffCursor} from '../../../embed/diff-old/gr-diff-cursor/gr-diff-cursor';
+import {GrDiffCursor} from '../../../embed/diff/gr-diff-cursor/gr-diff-cursor';
import {GrCursorManager} from '../../shared/gr-cursor-manager/gr-cursor-manager';
import {ChangeComments} from '../../diff/gr-comment-api/gr-comment-api';
import {ParsedChangeInfo, PatchSetFile} from '../../../types/types';
@@ -87,7 +86,6 @@
import {userModelToken} from '../../../models/user/user-model';
import {pluginLoaderToken} from '../../shared/gr-js-api-interface/gr-plugin-loader';
import {FileMode, fileModeToString} from '../../../utils/file-util';
-import {isNewDiff} from '../../../embed/diff/gr-diff/gr-diff-utils';
export const DEFAULT_NUM_FILES_SHOWN = 200;
@@ -312,8 +310,7 @@
fileCursor = new GrCursorManager();
// private but used in test
- // TODO(newdiff-cleanup): Replace once newdiff migration is completed.
- diffCursor?: GrDiffCursor | GrDiffCursorNew;
+ diffCursor?: GrDiffCursor;
static override get styles() {
return [
@@ -899,8 +896,7 @@
);
}
});
- // TODO(newdiff-cleanup): Remove once newdiff migration is completed.
- this.diffCursor = isNewDiff() ? new GrDiffCursorNew() : new GrDiffCursor();
+ this.diffCursor = new GrDiffCursor();
this.diffCursor.replaceDiffs(this.diffs);
}
@@ -2318,13 +2314,6 @@
* Private but used in tests.
*/
async expandedFilesChanged(oldFiles: Array<PatchSetFile>) {
- // Clear content for any diffs that are not open so if they get re-opened
- // the stale content does not flash before it is cleared and reloaded.
- const collapsedDiffs = this.diffs.filter(
- diff => this.expandedFiles.findIndex(f => f.path === diff.path) === -1
- );
- this.clearCollapsedDiffs(collapsedDiffs);
-
this.filesExpanded = this.computeExpandedFiles();
const newFiles = this.expandedFiles.filter(
@@ -2341,14 +2330,6 @@
this.diffCursor?.reInitAndUpdateStops();
}
- // private but used in test
- clearCollapsedDiffs(collapsedDiffs: GrDiffHost[]) {
- for (const diff of collapsedDiffs) {
- diff.cancel();
- diff.clearDiffContent();
- }
- }
-
/**
* Given an array of paths and a NodeList of diff elements, render the diff
* for each path in order, awaiting the previous render to complete before
@@ -2431,7 +2412,6 @@
if (this.cancelForEachDiff) {
this.cancelForEachDiff();
}
- this.forEachDiff(d => d.cancel());
}
/**
diff --git a/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list_test.ts b/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list_test.ts
index b2cd430..0f9cf6a 100644
--- a/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list_test.ts
+++ b/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list_test.ts
@@ -1373,7 +1373,6 @@
await waitEventLoop();
const renderSpy = sinon.spy(element, 'renderInOrder');
- const collapseStub = sinon.stub(element, 'clearCollapsedDiffs');
assert.equal(
queryAndAssert<GrIcon>(element, 'gr-icon').icon,
@@ -1385,7 +1384,6 @@
// Wait for expandedFilesChanged to finish.
await waitEventLoop();
- assert.equal(collapseStub.lastCall.args[0].length, 0);
assert.equal(
queryAndAssert<GrIcon>(element, 'gr-icon').icon,
'expand_less'
@@ -1404,11 +1402,9 @@
);
assert.equal(renderSpy.callCount, 1);
assert.isFalse(element.expandedFiles.some(f => f.path === path));
- assert.equal(collapseStub.lastCall.args[0].length, 1);
});
test('expandAllDiffs and collapseAllDiffs', async () => {
- const collapseStub = sinon.stub(element, 'clearCollapsedDiffs');
assertIsDefined(element.diffCursor);
const reInitStub = sinon.stub(element.diffCursor, 'reInitAndUpdateStops');
@@ -1423,7 +1419,6 @@
await waitEventLoop();
assert.equal(element.filesExpanded, FilesExpandedState.ALL);
assert.isTrue(reInitStub.calledTwice);
- assert.equal(collapseStub.lastCall.args[0].length, 0);
element.collapseAllDiffs();
await element.updateComplete;
@@ -1431,7 +1426,6 @@
await waitEventLoop();
assert.equal(element.expandedFiles.length, 0);
assert.equal(element.filesExpanded, FilesExpandedState.NONE);
- assert.equal(collapseStub.lastCall.args[0].length, 1);
});
test('expandedFilesChanged', async () => {
@@ -1467,19 +1461,6 @@
await promise;
});
- test('clearCollapsedDiffs', () => {
- // Have to type as any because the type is 'GrDiffHost'
- // which would require stubbing so many different
- // methods / properties that it isn't worth it.
- const diff = {
- cancel: sinon.stub(),
- clearDiffContent: sinon.stub(),
- } as any;
- element.clearCollapsedDiffs([diff]);
- assert.isTrue(diff.cancel.calledOnce);
- assert.isTrue(diff.clearDiffContent.calledOnce);
- });
-
test('filesExpanded value updates to correct enum', async () => {
element.files = [normalize({}, 'foo.bar'), normalize({}, 'baz.bar')];
await element.updateComplete;
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 769b064..7e6e23b 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
@@ -7,7 +7,6 @@
import '../../shared/gr-dialog/gr-dialog';
import '../../shared/gr-icon/gr-icon';
import '../../../embed/diff/gr-diff/gr-diff';
-import '../../../embed/diff-old/gr-diff/gr-diff';
import {navigationToken} from '../../core/gr-navigation/gr-navigation';
import {
NumericChangeId,
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 caa9f60..1f1ac5c 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
@@ -6,7 +6,6 @@
import '../../shared/gr-comment-thread/gr-comment-thread';
import '../../checks/gr-diff-check-result';
import '../../../embed/diff/gr-diff/gr-diff';
-import '../../../embed/diff-old/gr-diff/gr-diff';
import {
anyLineTooLong,
getDiffLength,
@@ -46,8 +45,7 @@
IgnoreWhitespaceType,
WebLinkInfo,
} from '../../../types/diff';
-import {GrDiff as GrDiffNew} from '../../../embed/diff/gr-diff/gr-diff';
-import {GrDiff} from '../../../embed/diff-old/gr-diff/gr-diff';
+import {GrDiff} from '../../../embed/diff/gr-diff/gr-diff';
import {DiffViewMode, Side, CommentSide} from '../../../constants/constants';
import {FilesWebLinks} from '../gr-patch-range-select/gr-patch-range-select';
import {KnownExperimentId} from '../../../services/flags/flags';
@@ -139,9 +137,8 @@
*/
@customElement('gr-diff-host')
export class GrDiffHost extends LitElement {
- // TODO(newdiff-cleanup): Replace once newdiff migration is completed.
@query('#diff')
- diffElement?: GrDiff | GrDiffNew;
+ diffElement?: GrDiff;
@property({type: Number})
changeNum?: NumericChangeId;
@@ -560,8 +557,6 @@
// TODO: Find better names for these 3 clear/cancel methods. Ideally the
// <gr-diff-host> should not re-used at all for another diff rendering pass.
this.clear();
- this.cancel();
- this.clearDiffContent();
assertIsDefined(this.path, 'path');
assertIsDefined(this.changeNum, 'changeNum');
this.diff = undefined;
@@ -815,11 +810,6 @@
};
}
- /** Cancel any remaining diff builder rendering work. */
- cancel() {
- this.diffElement?.cancel();
- }
-
getCursorStops() {
assertIsDefined(this.diffElement);
return this.diffElement.getCursorStops();
@@ -864,10 +854,6 @@
this.blame = null;
}
- clearDiffContent() {
- this.diffElement?.clearDiffContent();
- }
-
toggleAllContext() {
assertIsDefined(this.diffElement);
this.diffElement.toggleAllContext();
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-host/gr-diff-host_test.ts b/polygerrit-ui/app/elements/diff/gr-diff-host/gr-diff-host_test.ts
index d56f63f..9dabe1d 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-host/gr-diff-host_test.ts
+++ b/polygerrit-ui/app/elements/diff/gr-diff-host/gr-diff-host_test.ts
@@ -59,7 +59,6 @@
CommentsModel,
commentsModelToken,
} from '../../../models/comments/comments-model';
-import {isNewDiff} from '../../../embed/diff/gr-diff/gr-diff-utils';
suite('gr-diff-host tests', () => {
let element: GrDiffHost;
@@ -154,25 +153,6 @@
);
});
- test('reload() cancels before network resolves', async () => {
- if (isNewDiff()) return;
- assertIsDefined(element.diffElement);
- const cancelStub = sinon.stub(element.diffElement, 'cancel');
-
- // Stub the network calls into requests that never resolve.
- sinon.stub(element, 'getDiff').callsFake(() => new Promise(() => {}));
- element.patchRange = createPatchRange();
- element.change = createChange();
- element.prefs = undefined;
-
- // Needs to be set to something first for it to cancel.
- element.diff = createDiff();
- await element.updateComplete;
-
- element.reload();
- assert.isTrue(cancelStub.called);
- });
-
test('prefetch getDiff', async () => {
getDiffRestApiStub.returns(Promise.resolve(createDiff()));
element.changeNum = 123 as NumericChangeId;
@@ -558,15 +538,6 @@
assert.isTrue(showAuthRequireSpy.called);
});
- test('delegates cancel()', () => {
- assertIsDefined(element.diffElement);
- const stub = sinon.stub(element.diffElement, 'cancel');
- element.patchRange = createPatchRange();
- element.cancel();
- assert.isTrue(stub.calledOnce);
- assert.equal(stub.lastCall.args.length, 0);
- });
-
test('delegates getCursorStops()', () => {
const returnValue = [document.createElement('b')];
assertIsDefined(element.diffElement);
@@ -666,14 +637,6 @@
});
});
- test('delegates clearDiffContent()', () => {
- assertIsDefined(element.diffElement);
- const stub = sinon.stub(element.diffElement, 'clearDiffContent');
- element.clearDiffContent();
- assert.isTrue(stub.calledOnce);
- assert.equal(stub.lastCall.args.length, 0);
- });
-
test('delegates toggleAllContext()', () => {
assertIsDefined(element.diffElement);
const stub = sinon.stub(element.diffElement, 'toggleAllContext');
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 c76c9d6..246e7ed 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
@@ -56,8 +56,7 @@
FilesWebLinks,
PatchRangeChangeEvent,
} from '../gr-patch-range-select/gr-patch-range-select';
-import {GrDiffCursor as GrDiffCursorNew} from '../../../embed/diff/gr-diff-cursor/gr-diff-cursor';
-import {GrDiffCursor} from '../../../embed/diff-old/gr-diff-cursor/gr-diff-cursor';
+import {GrDiffCursor} from '../../../embed/diff/gr-diff-cursor/gr-diff-cursor';
import {CommentSide, DiffViewMode, Side} from '../../../constants/constants';
import {GrApplyFixDialog} from '../gr-apply-fix-dialog/gr-apply-fix-dialog';
import {OpenFixPreviewEvent, ValueChangedEvent} from '../../../types/events';
@@ -104,7 +103,6 @@
FileNameToNormalizedFileInfoMap,
filesModelToken,
} from '../../../models/change/files-model';
-import {isNewDiff} from '../../../embed/diff/gr-diff/gr-diff-utils';
import {isImageDiff} from '../../../utils/diff-util';
import {formStyles} from '../../../styles/form-styles';
@@ -258,9 +256,8 @@
private throttledToggleFileReviewed?: (e: KeyboardEvent) => void;
- // TODO(newdiff-cleanup): Replace once newdiff migration is completed.
@state()
- cursor?: GrDiffCursor | GrDiffCursorNew;
+ cursor?: GrDiffCursor;
private readonly shortcutsController = new ShortcutController(this);
@@ -697,8 +694,7 @@
this.handleToggleFileReviewed()
);
this.addEventListener('open-fix-preview', e => this.onOpenFixPreview(e));
- // TODO(newdiff-cleanup): Remove once newdiff migration is completed.
- this.cursor = isNewDiff() ? new GrDiffCursorNew() : new GrDiffCursor();
+ this.cursor = new GrDiffCursor();
if (this.diffHost) this.reInitCursor();
window.addEventListener('scroll', this.updateSidebarHeight);
window.addEventListener('resize', this.updateSidebarHeight);
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 f04c233..0b03581 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
@@ -8,7 +8,6 @@
import '../gr-comment/gr-comment';
import '../gr-icon/gr-icon';
import '../../../embed/diff/gr-diff/gr-diff';
-import '../../../embed/diff-old/gr-diff/gr-diff';
import '../gr-copy-clipboard/gr-copy-clipboard';
import {css, html, nothing, LitElement, PropertyValues} from 'lit';
import {
diff --git a/polygerrit-ui/app/elements/shared/gr-suggestion-diff-preview/gr-suggestion-diff-preview_test.ts b/polygerrit-ui/app/elements/shared/gr-suggestion-diff-preview/gr-suggestion-diff-preview_test.ts
index dd6d62d..86be868 100644
--- a/polygerrit-ui/app/elements/shared/gr-suggestion-diff-preview/gr-suggestion-diff-preview_test.ts
+++ b/polygerrit-ui/app/elements/shared/gr-suggestion-diff-preview/gr-suggestion-diff-preview_test.ts
@@ -100,6 +100,15 @@
};
await element.updateComplete;
- assert.shadowDom.equal(element, /* HTML */ '<gr-diff></gr-diff>');
+ assert.shadowDom.equal(
+ element,
+ /* HTML */ `
+ <gr-diff
+ class="disable-context-control-buttons hide-line-length-indicator"
+ >
+ </gr-diff>
+ `,
+ {ignoreAttributes: ['style']}
+ );
});
});
diff --git a/polygerrit-ui/app/embed/diff-old/gr-context-controls/gr-context-controls-section.ts b/polygerrit-ui/app/embed/diff-old/gr-context-controls/gr-context-controls-section.ts
deleted file mode 100644
index e558295..0000000
--- a/polygerrit-ui/app/embed/diff-old/gr-context-controls/gr-context-controls-section.ts
+++ /dev/null
@@ -1,138 +0,0 @@
-/**
- * @license
- * Copyright 2022 Google LLC
- * SPDX-License-Identifier: Apache-2.0
- */
-import '../../../elements/shared/gr-button/gr-button';
-import {html, LitElement} from 'lit';
-import {property, state} from 'lit/decorators.js';
-import {DiffInfo, DiffViewMode, RenderPreferences} from '../../../api/diff';
-import {GrDiffGroup, GrDiffGroupType} from '../gr-diff/gr-diff-group';
-import {diffClasses, isNewDiff} from '../../diff/gr-diff/gr-diff-utils';
-import {getShowConfig} from './gr-context-controls';
-import {ifDefined} from 'lit/directives/if-defined.js';
-import {when} from 'lit/directives/when.js';
-
-export class GrContextControlsSection extends LitElement {
- /** Should context controls be rendered for expanding above the section? */
- @property({type: Boolean}) showAbove = false;
-
- /** Should context controls be rendered for expanding below the section? */
- @property({type: Boolean}) showBelow = false;
-
- /** Must be of type GrDiffGroupType.CONTEXT_CONTROL. */
- @property({type: Object})
- group?: GrDiffGroup;
-
- @property({type: Object})
- diff?: DiffInfo;
-
- @property({type: Object})
- renderPrefs?: RenderPreferences;
-
- /**
- * Semantic DOM diff testing does not work with just table fragments, so when
- * running such tests the render() method has to wrap the DOM in a proper
- * <table> element.
- */
- @state()
- addTableWrapperForTesting = false;
-
- /**
- * The browser API for handling selection does not (yet) work for selection
- * across multiple shadow DOM elements. So we are rendering gr-diff components
- * into the light DOM instead of the shadow DOM by overriding this method,
- * which was the recommended workaround by the lit team.
- * See also https://github.com/WICG/webcomponents/issues/79.
- */
- override createRenderRoot() {
- return this;
- }
-
- private renderPaddingRow(whereClass: 'above' | 'below') {
- if (!this.showAbove && whereClass === 'above') return;
- if (!this.showBelow && whereClass === 'below') return;
- const modeClass = this.isSideBySide() ? 'side-by-side' : 'unified';
- const type = this.isSideBySide()
- ? GrDiffGroupType.CONTEXT_CONTROL
- : undefined;
- return html`
- <tr
- class=${diffClasses('contextBackground', modeClass, whereClass)}
- left-type=${ifDefined(type)}
- right-type=${ifDefined(type)}
- >
- <td class=${diffClasses('blame')} data-line-number="0"></td>
- <td class=${diffClasses('contextLineNum')}></td>
- ${when(
- this.isSideBySide(),
- () => html`
- <td class=${diffClasses('sign')}></td>
- <td class=${diffClasses()}></td>
- `
- )}
- <td class=${diffClasses('contextLineNum')}></td>
- ${when(
- this.isSideBySide(),
- () => html`<td class=${diffClasses('sign')}></td>`
- )}
- <td class=${diffClasses()}></td>
- </tr>
- `;
- }
-
- private isSideBySide() {
- return this.renderPrefs?.view_mode !== DiffViewMode.UNIFIED;
- }
-
- private createContextControlRow() {
- // Note that <td> table cells that have `display: none` don't count!
- const colspan = this.renderPrefs?.show_sign_col ? '5' : '3';
- const showConfig = getShowConfig(this.showAbove, this.showBelow);
- return html`
- <tr class=${diffClasses('dividerRow', `show-${showConfig}`)}>
- <td class=${diffClasses('blame')} data-line-number="0"></td>
- ${when(
- this.isSideBySide(),
- () => html`<td class=${diffClasses()}></td>`
- )}
- <td class=${diffClasses('dividerCell')} colspan=${colspan}>
- <gr-context-controls
- class=${diffClasses()}
- .diff=${this.diff}
- .renderPreferences=${this.renderPrefs}
- .group=${this.group}
- .showConfig=${showConfig}
- >
- </gr-context-controls>
- </td>
- </tr>
- `;
- }
-
- override render() {
- const rows = html`
- ${this.renderPaddingRow('above')} ${this.createContextControlRow()}
- ${this.renderPaddingRow('below')}
- `;
- if (this.addTableWrapperForTesting) {
- return html`<table>
- ${rows}
- </table>`;
- }
- return rows;
- }
-}
-
-if (!isNewDiff()) {
- customElements.define(
- 'gr-context-controls-section',
- GrContextControlsSection
- );
-}
-
-declare global {
- interface HTMLElementTagNameMap {
- 'gr-context-controls-section': LitElement;
- }
-}
diff --git a/polygerrit-ui/app/embed/diff-old/gr-context-controls/gr-context-controls-section_test.ts b/polygerrit-ui/app/embed/diff-old/gr-context-controls/gr-context-controls-section_test.ts
deleted file mode 100644
index 6a557fc..0000000
--- a/polygerrit-ui/app/embed/diff-old/gr-context-controls/gr-context-controls-section_test.ts
+++ /dev/null
@@ -1,70 +0,0 @@
-/**
- * @license
- * Copyright 2022 Google LLC
- * SPDX-License-Identifier: Apache-2.0
- */
-import '../../../test/common-test-setup';
-import './gr-context-controls-section';
-import {GrContextControlsSection} from './gr-context-controls-section';
-import {fixture, html, assert} from '@open-wc/testing';
-
-suite('gr-context-controls-section test', () => {
- let element: GrContextControlsSection;
-
- setup(async () => {
- element = await fixture<GrContextControlsSection>(
- html`<gr-context-controls-section></gr-context-controls-section>`
- );
- element.addTableWrapperForTesting = true;
- await element.updateComplete;
- });
-
- test('render: normal with showAbove and showBelow', async () => {
- element.showAbove = true;
- element.showBelow = true;
- await element.updateComplete;
- assert.lightDom.equal(
- element,
- /* HTML */ `
- <table>
- <tbody>
- <tr
- class="above contextBackground gr-diff side-by-side"
- left-type="contextControl"
- right-type="contextControl"
- >
- <td class="blame gr-diff" data-line-number="0"></td>
- <td class="contextLineNum gr-diff"></td>
- <td class="gr-diff sign"></td>
- <td class="gr-diff"></td>
- <td class="contextLineNum gr-diff"></td>
- <td class="gr-diff sign"></td>
- <td class="gr-diff"></td>
- </tr>
- <tr class="dividerRow gr-diff show-both">
- <td class="blame gr-diff" data-line-number="0"></td>
- <td class="gr-diff"></td>
- <td class="dividerCell gr-diff" colspan="3">
- <gr-context-controls class="gr-diff" showconfig="both">
- </gr-context-controls>
- </td>
- </tr>
- <tr
- class="below contextBackground gr-diff side-by-side"
- left-type="contextControl"
- right-type="contextControl"
- >
- <td class="blame gr-diff" data-line-number="0"></td>
- <td class="contextLineNum gr-diff"></td>
- <td class="gr-diff sign"></td>
- <td class="gr-diff"></td>
- <td class="contextLineNum gr-diff"></td>
- <td class="gr-diff sign"></td>
- <td class="gr-diff"></td>
- </tr>
- </tbody>
- </table>
- `
- );
- });
-});
diff --git a/polygerrit-ui/app/embed/diff-old/gr-context-controls/gr-context-controls.ts b/polygerrit-ui/app/embed/diff-old/gr-context-controls/gr-context-controls.ts
deleted file mode 100644
index e4afd23..0000000
--- a/polygerrit-ui/app/embed/diff-old/gr-context-controls/gr-context-controls.ts
+++ /dev/null
@@ -1,537 +0,0 @@
-/**
- * @license
- * Copyright 2021 Google LLC
- * SPDX-License-Identifier: Apache-2.0
- */
-import '@polymer/paper-button/paper-button';
-import '@polymer/paper-card/paper-card';
-import '@polymer/paper-checkbox/paper-checkbox';
-import '@polymer/paper-dropdown-menu/paper-dropdown-menu';
-import '@polymer/paper-fab/paper-fab';
-import '@polymer/paper-icon-button/paper-icon-button';
-import '@polymer/paper-item/paper-item';
-import '@polymer/paper-listbox/paper-listbox';
-import '@polymer/paper-tooltip/paper-tooltip';
-import {of, EMPTY, Subject} from 'rxjs';
-import {switchMap, delay} from 'rxjs/operators';
-
-import '../../../elements/shared/gr-button/gr-button';
-import {pluralize} from '../../../utils/string-util';
-import {fire} from '../../../utils/event-util';
-import {DiffInfo} from '../../../types/diff';
-import {assertIsDefined} from '../../../utils/common-util';
-import {css, html, LitElement, TemplateResult} from 'lit';
-import {property} from 'lit/decorators.js';
-import {subscribe} from '../../../elements/lit/subscription-controller';
-
-import {
- ContextButtonType,
- DiffContextButtonHoveredDetail,
- RenderPreferences,
- SyntaxBlock,
-} from '../../../api/diff';
-
-import {GrDiffGroup, hideInContextControl} from '../gr-diff/gr-diff-group';
-import {isNewDiff} from '../../diff/gr-diff/gr-diff-utils';
-
-declare global {
- interface HTMLElementEventMap {
- 'diff-context-button-hovered': CustomEvent<DiffContextButtonHoveredDetail>;
- }
-}
-
-const PARTIAL_CONTEXT_AMOUNT = 10;
-
-/**
- * Traverses a hierarchical structure of syntax blocks and
- * finds the most local/nested block that can be associated line.
- * It finds the closest block that contains the whole line and
- * returns the whole path from the syntax layer (blocks) sent as parameter
- * to the most nested block - the complete path from the top to bottom layer of
- * a syntax tree. Example: [myNamespace, MyClass, myMethod1, aLocalFunctionInsideMethod1]
- *
- * @param lineNum line number for the targeted line.
- * @param blocks Blocks for a specific syntax level in the file (to allow recursive calls)
- */
-function findBlockTreePathForLine(
- lineNum: number,
- blocks?: SyntaxBlock[]
-): SyntaxBlock[] {
- const containingBlock = blocks?.find(
- ({range}) => range.start_line < lineNum && range.end_line > lineNum
- );
- if (!containingBlock) return [];
- const innerPathInChild = findBlockTreePathForLine(
- lineNum,
- containingBlock?.children
- );
- return [containingBlock].concat(innerPathInChild);
-}
-
-export type GrContextControlsShowConfig = 'above' | 'below' | 'both';
-
-export function getShowConfig(
- showAbove: boolean,
- showBelow: boolean
-): GrContextControlsShowConfig {
- if (showAbove && !showBelow) return 'above';
- if (!showAbove && showBelow) return 'below';
-
- // Note that !showAbove && !showBelow also intentionally returns 'both'.
- // This means the file is completely collapsed, which is unusual, but at least
- // happens in one test.
- return 'both';
-}
-
-export class GrContextControls extends LitElement {
- @property({type: Object}) renderPreferences?: RenderPreferences;
-
- @property({type: Object}) diff?: DiffInfo;
-
- @property({type: Object}) group?: GrDiffGroup;
-
- @property({type: String, reflect: true})
- showConfig: GrContextControlsShowConfig = 'both';
-
- private expandButtonsHover = new Subject<{
- eventType: 'enter' | 'leave';
- buttonType: ContextButtonType;
- linesToExpand: number;
- }>();
-
- static override get styles() {
- return [
- css`
- :host {
- display: flex;
- justify-content: center;
- flex-direction: column;
- position: relative;
- }
-
- :host([showConfig='above']) {
- justify-content: flex-end;
- margin-top: calc(-1px - var(--line-height-normal) - var(--spacing-s));
- margin-bottom: var(--gr-context-controls-margin-bottom);
- height: calc(var(--line-height-normal) + var(--spacing-s));
- .horizontalFlex {
- align-items: end;
- }
- }
-
- :host([showConfig='below']) {
- justify-content: flex-start;
- margin-top: 1px;
- margin-bottom: calc(
- 0px - var(--line-height-normal) - var(--spacing-s)
- );
- .horizontalFlex {
- align-items: start;
- }
- }
-
- :host([showConfig='both']) {
- margin-top: calc(0px - var(--line-height-normal) - var(--spacing-s));
- margin-bottom: calc(
- 0px - var(--line-height-normal) - var(--spacing-s)
- );
- height: calc(
- 2 * var(--line-height-normal) + 2 * var(--spacing-s) +
- var(--divider-height)
- );
- .horizontalFlex {
- align-items: center;
- }
- }
-
- .contextControlButton {
- background-color: var(--default-button-background-color);
- font: var(--context-control-button-font, inherit);
- }
-
- paper-button {
- text-transform: none;
- align-items: center;
- background-color: var(--background-color);
- font-family: inherit;
- margin: var(--margin, 0);
- min-width: var(--border, 0);
- color: var(--diff-context-control-color);
- border: solid var(--border-color);
- border-width: 1px;
- border-radius: var(--border-radius);
- padding: var(--spacing-s) var(--spacing-l);
- }
-
- paper-button:hover {
- /* same as defined in gr-button */
- background: rgba(0, 0, 0, 0.12);
- }
- paper-button:focus-visible {
- /* paper-button sets this to 0, thus preventing focus-based styling. */
- outline-width: 1px;
- }
-
- .aboveBelowButtons {
- display: flex;
- flex-direction: column;
- justify-content: center;
- margin-left: var(--spacing-m);
- position: relative;
- }
- .aboveBelowButtons:first-child {
- margin-left: 0;
- /* Places a default background layer behind the "all button" that can have opacity */
- background-color: var(--default-button-background-color);
- }
-
- .horizontalFlex {
- display: flex;
- justify-content: center;
- align-items: var(
- --gr-context-controls-horizontal-align-items,
- center
- );
- }
-
- .aboveButton {
- border-bottom-width: 0;
- border-bottom-right-radius: 0;
- border-bottom-left-radius: 0;
- padding: var(--spacing-xxs) var(--spacing-l);
- }
- .belowButton {
- border-top-width: 0;
- border-top-left-radius: 0;
- border-top-right-radius: 0;
- padding: var(--spacing-xxs) var(--spacing-l);
- margin-top: calc(var(--divider-height) + 2 * var(--spacing-xxs));
- }
- .belowButton:first-child {
- margin-top: 0;
- }
- .breadcrumbTooltip {
- white-space: nowrap;
- }
- `,
- ];
- }
-
- constructor() {
- super();
- this.setupButtonHoverHandler();
- }
-
- private showBoth() {
- return this.showConfig === 'both';
- }
-
- private showAbove() {
- return this.showBoth() || this.showConfig === 'above';
- }
-
- private showBelow() {
- return this.showBoth() || this.showConfig === 'below';
- }
-
- private setupButtonHoverHandler() {
- subscribe(
- this,
- () =>
- this.expandButtonsHover.pipe(
- switchMap(e => {
- if (e.eventType === 'leave') {
- // cancel any previous delay
- // for mouse enter
- return EMPTY;
- }
- return of(e).pipe(delay(500));
- })
- ),
- ({buttonType, linesToExpand}) => {
- fire(this, 'diff-context-button-hovered', {
- buttonType,
- linesToExpand,
- });
- }
- );
- }
-
- private numLines() {
- assertIsDefined(this.group);
- // In context groups, there is the same number of lines left and right
- const left = this.group.lineRange.left;
- // Both start and end inclusive, so we need to add 1.
- return left.end_line - left.start_line + 1;
- }
-
- private createExpandAllButtonContainer() {
- return html` <div class="gr-diff aboveBelowButtons fullExpansion">
- ${this.createContextButton(ContextButtonType.ALL, this.numLines())}
- </div>`;
- }
-
- /**
- * Creates a specific expansion button (e.g. +X common lines, +10, +Block).
- */
- private createContextButton(
- type: ContextButtonType,
- linesToExpand: number,
- tooltip?: TemplateResult
- ) {
- if (!this.group) return;
- let text = '';
- let groups: GrDiffGroup[] = []; // The groups that replace this one if tapped.
- let ariaLabel = '';
- let classes = 'contextControlButton showContext ';
-
- if (type === ContextButtonType.ALL) {
- text = `+${pluralize(linesToExpand, 'common line')}`;
- ariaLabel = `Show ${pluralize(linesToExpand, 'common line')}`;
- classes += this.showBoth()
- ? 'centeredButton'
- : this.showAbove()
- ? 'aboveButton'
- : 'belowButton';
- if (this.group?.hasSkipGroup()) {
- // Expanding content would require load of more data
- text += ' (too large)';
- }
- groups.push(...this.group.contextGroups);
- } else if (type === ContextButtonType.ABOVE) {
- groups = hideInContextControl(
- this.group.contextGroups,
- linesToExpand,
- this.numLines()
- );
- text = `+${linesToExpand}`;
- classes += 'aboveButton';
- ariaLabel = `Show ${pluralize(linesToExpand, 'line')} above`;
- } else if (type === ContextButtonType.BELOW) {
- groups = hideInContextControl(
- this.group.contextGroups,
- 0,
- this.numLines() - linesToExpand
- );
- text = `+${linesToExpand}`;
- classes += 'belowButton';
- ariaLabel = `Show ${pluralize(linesToExpand, 'line')} below`;
- } else if (type === ContextButtonType.BLOCK_ABOVE) {
- groups = hideInContextControl(
- this.group.contextGroups,
- linesToExpand,
- this.numLines()
- );
- text = '+Block';
- classes += 'aboveButton';
- ariaLabel = 'Show block above';
- } else if (type === ContextButtonType.BLOCK_BELOW) {
- groups = hideInContextControl(
- this.group.contextGroups,
- 0,
- this.numLines() - linesToExpand
- );
- text = '+Block';
- classes += 'belowButton';
- ariaLabel = 'Show block below';
- }
- const expandHandler = this.createExpansionHandler(
- linesToExpand,
- type,
- groups
- );
-
- const mouseHandler = (eventType: 'enter' | 'leave') => {
- this.expandButtonsHover.next({
- eventType,
- buttonType: type,
- linesToExpand,
- });
- };
-
- const button = html` <paper-button
- class=${classes}
- aria-label=${ariaLabel}
- @click=${expandHandler}
- @mouseenter=${() => mouseHandler('enter')}
- @mouseleave=${() => mouseHandler('leave')}
- >
- <span class="showContext">${text}</span>
- ${tooltip}
- </paper-button>`;
- return button;
- }
-
- private createExpansionHandler(
- linesToExpand: number,
- type: ContextButtonType,
- groups: GrDiffGroup[]
- ) {
- return (e: Event) => {
- assertIsDefined(this.group);
- e.stopPropagation();
- if (type === ContextButtonType.ALL && this.group?.hasSkipGroup()) {
- fire(this, 'content-load-needed', {
- lineRange: this.group.lineRange,
- });
- } else {
- fire(this, 'diff-context-expanded', {
- numLines: this.numLines(),
- buttonType: type,
- expandedLines: linesToExpand,
- });
- fire(this, 'diff-context-expanded-internal', {
- contextGroup: this.group,
- groups,
- numLines: this.numLines(),
- buttonType: type,
- expandedLines: linesToExpand,
- });
- }
- };
- }
-
- private showPartialLinks() {
- return this.numLines() > PARTIAL_CONTEXT_AMOUNT;
- }
-
- /**
- * Creates a container div with partial (+10) expansion buttons (above and/or below).
- */
- private createPartialExpansionButtons() {
- if (!this.showPartialLinks()) {
- return undefined;
- }
- let aboveButton;
- let belowButton;
- if (this.showAbove()) {
- aboveButton = this.createContextButton(
- ContextButtonType.ABOVE,
- PARTIAL_CONTEXT_AMOUNT
- );
- }
- if (this.showBelow()) {
- belowButton = this.createContextButton(
- ContextButtonType.BELOW,
- PARTIAL_CONTEXT_AMOUNT
- );
- }
- return aboveButton || belowButton
- ? html` <div class="aboveBelowButtons partialExpansion">
- ${aboveButton} ${belowButton}
- </div>`
- : undefined;
- }
-
- /**
- * Creates a container div with block expansion buttons (above and/or below).
- */
- private createBlockExpansionButtons() {
- assertIsDefined(this.group, 'group');
- if (
- !this.showPartialLinks() ||
- !this.renderPreferences?.use_block_expansion ||
- this.group?.hasSkipGroup()
- ) {
- return undefined;
- }
- let aboveBlockButton;
- let belowBlockButton;
- if (this.showAbove()) {
- aboveBlockButton = this.createBlockButton(
- ContextButtonType.BLOCK_ABOVE,
- this.numLines(),
- this.group.lineRange.right.start_line - 1
- );
- }
- if (this.showBelow()) {
- belowBlockButton = this.createBlockButton(
- ContextButtonType.BLOCK_BELOW,
- this.numLines(),
- this.group.lineRange.right.end_line + 1
- );
- }
- if (aboveBlockButton || belowBlockButton) {
- return html` <div class="aboveBelowButtons blockExpansion">
- ${aboveBlockButton} ${belowBlockButton}
- </div>`;
- }
- return undefined;
- }
-
- private createBlockButtonTooltip(
- buttonType: ContextButtonType,
- syntaxPath: SyntaxBlock[],
- linesToExpand: number
- ) {
- // Create breadcrumb string:
- // myNamespace > MyClass > myMethod1 > aLocalFunctionInsideMethod1 > (anonymous)
- const tooltipText = syntaxPath.length
- ? syntaxPath.map(b => b.name || '(anonymous)').join(' > ')
- : `${linesToExpand} common lines`;
-
- const position =
- buttonType === ContextButtonType.BLOCK_ABOVE ? 'top' : 'bottom';
- return html`<paper-tooltip offset="10" position=${position}
- ><div class="breadcrumbTooltip">${tooltipText}</div></paper-tooltip
- >`;
- }
-
- private createBlockButton(
- buttonType: ContextButtonType,
- numLines: number,
- referenceLine: number
- ) {
- if (!this.diff?.meta_b) return;
- const syntaxTree = this.diff.meta_b.syntax_tree;
- const outlineSyntaxPath = findBlockTreePathForLine(
- referenceLine,
- syntaxTree
- );
- let linesToExpand = numLines;
- if (outlineSyntaxPath.length) {
- const {range} = outlineSyntaxPath[outlineSyntaxPath.length - 1];
- const targetLine =
- buttonType === ContextButtonType.BLOCK_ABOVE
- ? range.end_line
- : range.start_line;
- const distanceToTargetLine = Math.abs(targetLine - referenceLine);
- if (distanceToTargetLine < numLines) {
- linesToExpand = distanceToTargetLine;
- }
- }
- const tooltip = this.createBlockButtonTooltip(
- buttonType,
- outlineSyntaxPath,
- linesToExpand
- );
- return this.createContextButton(buttonType, linesToExpand, tooltip);
- }
-
- private hasValidProperties() {
- return !!(this.diff && this.group?.contextGroups?.length);
- }
-
- override render() {
- if (!this.hasValidProperties()) {
- console.error('Invalid properties for gr-context-controls!');
- return html`<p>invalid properties</p>`;
- }
- return html`
- <div class="horizontalFlex">
- ${this.createExpandAllButtonContainer()}
- ${this.createPartialExpansionButtons()}
- ${this.createBlockExpansionButtons()}
- </div>
- `;
- }
-}
-if (!isNewDiff()) {
- customElements.define('gr-context-controls', GrContextControls);
-}
-
-declare global {
- interface HTMLElementTagNameMap {
- 'gr-context-controls': LitElement;
- }
-}
diff --git a/polygerrit-ui/app/embed/diff-old/gr-context-controls/gr-context-controls_test.ts b/polygerrit-ui/app/embed/diff-old/gr-context-controls/gr-context-controls_test.ts
deleted file mode 100644
index 196afe5..0000000
--- a/polygerrit-ui/app/embed/diff-old/gr-context-controls/gr-context-controls_test.ts
+++ /dev/null
@@ -1,374 +0,0 @@
-/**
- * @license
- * Copyright 2021 Google LLC
- * SPDX-License-Identifier: Apache-2.0
- */
-import '../../../test/common-test-setup';
-import '../gr-diff/gr-diff-group';
-import './gr-context-controls';
-import {GrContextControls} from './gr-context-controls';
-
-import {GrDiffLine} from '../gr-diff/gr-diff-line';
-import {GrDiffGroup, GrDiffGroupType} from '../gr-diff/gr-diff-group';
-import {
- DiffFileMetaInfo,
- DiffInfo,
- GrDiffLineType,
- SyntaxBlock,
-} from '../../../api/diff';
-import {fixture, html, assert} from '@open-wc/testing';
-import {waitEventLoop} from '../../../test/test-utils';
-
-suite('gr-context-control tests', () => {
- let element: GrContextControls;
-
- setup(async () => {
- element = document.createElement(
- 'gr-context-controls'
- ) as GrContextControls;
- element.diff = {content: []} as any as DiffInfo;
- element.renderPreferences = {};
- const div = await fixture(html`<div></div>`);
- div.appendChild(element);
- await waitEventLoop();
- });
-
- function createContextGroup(options: {offset?: number; count?: number}) {
- const offset = options.offset || 0;
- const numLines = options.count || 10;
- const lines = [];
- for (let i = 0; i < numLines; i++) {
- const line = new GrDiffLine(GrDiffLineType.BOTH);
- line.beforeNumber = offset + i + 1;
- line.afterNumber = offset + i + 1;
- line.text = 'lorem upsum';
- lines.push(line);
- }
- return new GrDiffGroup({
- type: GrDiffGroupType.CONTEXT_CONTROL,
- contextGroups: [new GrDiffGroup({type: GrDiffGroupType.BOTH, lines})],
- });
- }
-
- test('no +10 buttons for 10 or less lines', async () => {
- element.group = createContextGroup({count: 10});
-
- await waitEventLoop();
-
- const buttons = element.shadowRoot!.querySelectorAll(
- 'paper-button.showContext'
- );
- assert.equal(buttons.length, 1);
- assert.equal(buttons[0].textContent!.trim(), '+10 common lines');
- });
-
- test('context control at the top', async () => {
- element.group = createContextGroup({offset: 0, count: 20});
- element.showConfig = 'below';
-
- await waitEventLoop();
-
- const buttons = element.shadowRoot!.querySelectorAll(
- 'paper-button.showContext'
- );
-
- assert.equal(buttons.length, 2);
- assert.equal(buttons[0].textContent!.trim(), '+20 common lines');
- assert.equal(buttons[1].textContent!.trim(), '+10');
-
- assert.include([...buttons[0].classList.values()], 'belowButton');
- assert.include([...buttons[1].classList.values()], 'belowButton');
- });
-
- test('context control in the middle', async () => {
- element.group = createContextGroup({offset: 10, count: 20});
- element.showConfig = 'both';
-
- await waitEventLoop();
-
- const buttons = element.shadowRoot!.querySelectorAll(
- 'paper-button.showContext'
- );
-
- assert.equal(buttons.length, 3);
- assert.equal(buttons[0].textContent!.trim(), '+20 common lines');
- assert.equal(buttons[1].textContent!.trim(), '+10');
- assert.equal(buttons[2].textContent!.trim(), '+10');
-
- assert.include([...buttons[0].classList.values()], 'centeredButton');
- assert.include([...buttons[1].classList.values()], 'aboveButton');
- assert.include([...buttons[2].classList.values()], 'belowButton');
- });
-
- test('context control at the bottom', async () => {
- element.group = createContextGroup({offset: 30, count: 20});
- element.showConfig = 'above';
-
- await waitEventLoop();
-
- const buttons = element.shadowRoot!.querySelectorAll(
- 'paper-button.showContext'
- );
-
- assert.equal(buttons.length, 2);
- assert.equal(buttons[0].textContent!.trim(), '+20 common lines');
- assert.equal(buttons[1].textContent!.trim(), '+10');
-
- assert.include([...buttons[0].classList.values()], 'aboveButton');
- assert.include([...buttons[1].classList.values()], 'aboveButton');
- });
-
- function prepareForBlockExpansion(syntaxTree: SyntaxBlock[]) {
- element.renderPreferences!.use_block_expansion = true;
- element.diff!.meta_b = {
- syntax_tree: syntaxTree,
- } as any as DiffFileMetaInfo;
- }
-
- test('context control with block expansion at the top', async () => {
- prepareForBlockExpansion([]);
- element.group = createContextGroup({offset: 0, count: 20});
- element.showConfig = 'below';
-
- await waitEventLoop();
-
- const fullExpansionButtons = element.shadowRoot!.querySelectorAll(
- '.fullExpansion paper-button'
- );
- const partialExpansionButtons = element.shadowRoot!.querySelectorAll(
- '.partialExpansion paper-button'
- );
- const blockExpansionButtons = element.shadowRoot!.querySelectorAll(
- '.blockExpansion paper-button'
- );
- assert.equal(fullExpansionButtons.length, 1);
- assert.equal(partialExpansionButtons.length, 1);
- assert.equal(blockExpansionButtons.length, 1);
- assert.equal(
- blockExpansionButtons[0].querySelector('span')!.textContent!.trim(),
- '+Block'
- );
- assert.include(
- [...blockExpansionButtons[0].classList.values()],
- 'belowButton'
- );
- });
-
- test('context control with block expansion in the middle', async () => {
- prepareForBlockExpansion([]);
- element.group = createContextGroup({offset: 10, count: 20});
- element.showConfig = 'both';
-
- await waitEventLoop();
-
- const fullExpansionButtons = element.shadowRoot!.querySelectorAll(
- '.fullExpansion paper-button'
- );
- const partialExpansionButtons = element.shadowRoot!.querySelectorAll(
- '.partialExpansion paper-button'
- );
- const blockExpansionButtons = element.shadowRoot!.querySelectorAll(
- '.blockExpansion paper-button'
- );
- assert.equal(fullExpansionButtons.length, 1);
- assert.equal(partialExpansionButtons.length, 2);
- assert.equal(blockExpansionButtons.length, 2);
- assert.equal(
- blockExpansionButtons[0].querySelector('span')!.textContent!.trim(),
- '+Block'
- );
- assert.equal(
- blockExpansionButtons[1].querySelector('span')!.textContent!.trim(),
- '+Block'
- );
- assert.include(
- [...blockExpansionButtons[0].classList.values()],
- 'aboveButton'
- );
- assert.include(
- [...blockExpansionButtons[1].classList.values()],
- 'belowButton'
- );
- });
-
- test('context control with block expansion at the bottom', async () => {
- prepareForBlockExpansion([]);
- element.group = createContextGroup({offset: 30, count: 20});
- element.showConfig = 'above';
-
- await waitEventLoop();
-
- const fullExpansionButtons = element.shadowRoot!.querySelectorAll(
- '.fullExpansion paper-button'
- );
- const partialExpansionButtons = element.shadowRoot!.querySelectorAll(
- '.partialExpansion paper-button'
- );
- const blockExpansionButtons = element.shadowRoot!.querySelectorAll(
- '.blockExpansion paper-button'
- );
- assert.equal(fullExpansionButtons.length, 1);
- assert.equal(partialExpansionButtons.length, 1);
- assert.equal(blockExpansionButtons.length, 1);
- assert.equal(
- blockExpansionButtons[0].querySelector('span')!.textContent!.trim(),
- '+Block'
- );
- assert.include(
- [...blockExpansionButtons[0].classList.values()],
- 'aboveButton'
- );
- });
-
- test('+ Block tooltip tooltip shows syntax block containing the target lines above and below', async () => {
- prepareForBlockExpansion([
- {
- name: 'aSpecificFunction',
- range: {start_line: 1, start_column: 0, end_line: 25, end_column: 0},
- children: [],
- },
- {
- name: 'anotherFunction',
- range: {start_line: 26, start_column: 0, end_line: 50, end_column: 0},
- children: [],
- },
- ]);
- element.group = createContextGroup({offset: 10, count: 20});
- element.showConfig = 'both';
-
- await waitEventLoop();
-
- const blockExpansionButtons = element.shadowRoot!.querySelectorAll(
- '.blockExpansion paper-button'
- );
- assert.equal(
- blockExpansionButtons[0]
- .querySelector('.breadcrumbTooltip')!
- .textContent?.trim(),
- 'aSpecificFunction'
- );
- assert.equal(
- blockExpansionButtons[1]
- .querySelector('.breadcrumbTooltip')!
- .textContent?.trim(),
- 'anotherFunction'
- );
- });
-
- test('+Block tooltip shows nested syntax blocks as breadcrumbs', async () => {
- prepareForBlockExpansion([
- {
- name: 'aSpecificNamespace',
- range: {start_line: 1, start_column: 0, end_line: 200, end_column: 0},
- children: [
- {
- name: 'MyClass',
- range: {
- start_line: 2,
- start_column: 0,
- end_line: 100,
- end_column: 0,
- },
- children: [
- {
- name: 'aMethod',
- range: {
- start_line: 5,
- start_column: 0,
- end_line: 80,
- end_column: 0,
- },
- children: [],
- },
- ],
- },
- ],
- },
- ]);
- element.group = createContextGroup({offset: 10, count: 20});
- element.showConfig = 'both';
-
- await waitEventLoop();
-
- const blockExpansionButtons = element.shadowRoot!.querySelectorAll(
- '.blockExpansion paper-button'
- );
- assert.equal(
- blockExpansionButtons[0]
- .querySelector('.breadcrumbTooltip')!
- .textContent?.trim(),
- 'aSpecificNamespace > MyClass > aMethod'
- );
- });
-
- test('+Block tooltip shows (anonymous) for empty blocks', async () => {
- prepareForBlockExpansion([
- {
- name: 'aSpecificNamespace',
- range: {start_line: 1, start_column: 0, end_line: 200, end_column: 0},
- children: [
- {
- name: '',
- range: {
- start_line: 2,
- start_column: 0,
- end_line: 100,
- end_column: 0,
- },
- children: [
- {
- name: 'aMethod',
- range: {
- start_line: 5,
- start_column: 0,
- end_line: 80,
- end_column: 0,
- },
- children: [],
- },
- ],
- },
- ],
- },
- ]);
- element.group = createContextGroup({offset: 10, count: 20});
- element.showConfig = 'both';
- await waitEventLoop();
-
- const blockExpansionButtons = element.shadowRoot!.querySelectorAll(
- '.blockExpansion paper-button'
- );
- assert.equal(
- blockExpansionButtons[0]
- .querySelector('.breadcrumbTooltip')!
- .textContent?.trim(),
- 'aSpecificNamespace > (anonymous) > aMethod'
- );
- });
-
- test('+Block tooltip shows "all common lines" for empty syntax tree', async () => {
- prepareForBlockExpansion([]);
-
- element.group = createContextGroup({offset: 10, count: 20});
- element.showConfig = 'both';
- await waitEventLoop();
-
- const blockExpansionButtons = element.shadowRoot!.querySelectorAll(
- '.blockExpansion paper-button'
- );
- const tooltipAbove =
- blockExpansionButtons[0].querySelector('paper-tooltip')!;
- const tooltipBelow =
- blockExpansionButtons[1].querySelector('paper-tooltip')!;
- assert.equal(
- tooltipAbove.querySelector('.breadcrumbTooltip')!.textContent?.trim(),
- '20 common lines'
- );
- assert.equal(
- tooltipBelow.querySelector('.breadcrumbTooltip')!.textContent?.trim(),
- '20 common lines'
- );
- assert.equal(tooltipAbove.getAttribute('position'), 'top');
- assert.equal(tooltipBelow.getAttribute('position'), 'bottom');
- });
-});
diff --git a/polygerrit-ui/app/embed/diff-old/gr-diff-builder/gr-diff-builder-binary.ts b/polygerrit-ui/app/embed/diff-old/gr-diff-builder/gr-diff-builder-binary.ts
deleted file mode 100644
index 9467654..0000000
--- a/polygerrit-ui/app/embed/diff-old/gr-diff-builder/gr-diff-builder-binary.ts
+++ /dev/null
@@ -1,43 +0,0 @@
-/**
- * @license
- * Copyright 2017 Google LLC
- * SPDX-License-Identifier: Apache-2.0
- */
-import {GrDiffBuilder} from './gr-diff-builder';
-import {DiffInfo, DiffPreferencesInfo} from '../../../types/diff';
-import {createElementDiff} from '../../diff/gr-diff/gr-diff-utils';
-import {GrDiffGroup} from '../gr-diff/gr-diff-group';
-import {html, render} from 'lit';
-import {FILE} from '../../../api/diff';
-
-export class GrDiffBuilderBinary extends GrDiffBuilder {
- constructor(
- diff: DiffInfo,
- prefs: DiffPreferencesInfo,
- outputEl: HTMLElement
- ) {
- super(diff, prefs, outputEl);
- }
-
- override buildSectionElement(group: GrDiffGroup): HTMLElement {
- const section = createElementDiff('tbody', 'binary-diff');
- // Do not create a diff row for LOST.
- if (group.lines[0].beforeNumber !== FILE) return section;
- return super.buildSectionElement(group);
- }
-
- public renderBinaryDiff() {
- render(
- html`
- <tbody class="gr-diff binary-diff">
- <tr class="gr-diff">
- <td colspan="5" class="gr-diff">
- <span>Difference in binary files</span>
- </td>
- </tr>
- </tbody>
- `,
- this.outputEl
- );
- }
-}
diff --git a/polygerrit-ui/app/embed/diff-old/gr-diff-builder/gr-diff-builder-element.ts b/polygerrit-ui/app/embed/diff-old/gr-diff-builder/gr-diff-builder-element.ts
deleted file mode 100644
index 5a4a26d..0000000
--- a/polygerrit-ui/app/embed/diff-old/gr-diff-builder/gr-diff-builder-element.ts
+++ /dev/null
@@ -1,573 +0,0 @@
-/**
- * @license
- * Copyright 2016 Google LLC
- * SPDX-License-Identifier: Apache-2.0
- */
-import '../gr-diff-processor/gr-diff-processor';
-import '../../../elements/shared/gr-hovercard/gr-hovercard';
-import {GrAnnotation} from '../gr-diff-highlight/gr-annotation';
-import {
- GrDiffBuilder,
- DiffContextExpandedEventDetail,
- isImageDiffBuilder,
- isBinaryDiffBuilder,
-} from './gr-diff-builder';
-import {GrDiffBuilderImage} from './gr-diff-builder-image';
-import {GrDiffBuilderBinary} from './gr-diff-builder-binary';
-import {BlameInfo, ImageInfo} from '../../../types/common';
-import {DiffInfo, DiffPreferencesInfo} from '../../../types/diff';
-import {CoverageRange, DiffLayer} from '../../../types/types';
-import {
- GrDiffProcessor,
- GroupConsumer,
- KeyLocations,
-} from '../gr-diff-processor/gr-diff-processor';
-import {
- CommentRangeLayer,
- GrRangedCommentLayer,
-} from '../../diff/gr-ranged-comment-layer/gr-ranged-comment-layer';
-import {GrCoverageLayer} from '../../diff/gr-coverage-layer/gr-coverage-layer';
-import {DiffViewMode, LineNumber, RenderPreferences} from '../../../api/diff';
-import {createDefaultDiffPrefs, Side} from '../../../constants/constants';
-import {GrDiffLine} from '../gr-diff/gr-diff-line';
-import {
- GrDiffGroup,
- GrDiffGroupType,
- hideInContextControl,
-} from '../gr-diff/gr-diff-group';
-import {getLineNumber, getSideByLineEl} from '../../diff/gr-diff/gr-diff-utils';
-import {fireAlert, fire} from '../../../utils/event-util';
-import {assertIsDefined} from '../../../utils/common-util';
-import {isImageDiff} from '../../../utils/diff-util';
-
-const TRAILING_WHITESPACE_PATTERN = /\s+$/;
-const COMMIT_MSG_PATH = '/COMMIT_MSG';
-const COMMIT_MSG_LINE_LENGTH = 72;
-
-declare global {
- interface HTMLElementEventMap {
- /**
- * Fired when the diff begins rendering - both for full renders and for
- * partial rerenders.
- */
- 'render-start': CustomEvent<{}>;
- /**
- * Fired when the diff finishes rendering text content - both for full
- * renders and for partial rerenders.
- */
- 'render-content': CustomEvent<{}>;
- }
-}
-
-export function getLineNumberCellWidth(prefs: DiffPreferencesInfo) {
- return prefs.font_size * 4;
-}
-
-function annotateSymbols(
- contentEl: HTMLElement,
- line: GrDiffLine,
- separator: string | RegExp,
- className: string
-) {
- const split = line.text.split(separator);
- if (!split || split.length < 2) {
- return;
- }
- for (let i = 0, pos = 0; i < split.length - 1; i++) {
- // Skip forward by the length of the content
- pos += split[i].length;
-
- GrAnnotation.annotateElement(contentEl, pos, 1, `gr-diff ${className}`);
-
- pos++;
- }
-}
-
-// TODO: Rename the class and the file and remove "element". This is not an
-// element anymore.
-export class GrDiffBuilderElement implements GroupConsumer {
- diff?: DiffInfo;
-
- diffElement?: HTMLTableElement;
-
- viewMode?: string;
-
- baseImage: ImageInfo | null = null;
-
- revisionImage: ImageInfo | null = null;
-
- path?: string;
-
- prefs: DiffPreferencesInfo = createDefaultDiffPrefs();
-
- renderPrefs?: RenderPreferences;
-
- useNewImageDiffUi = false;
-
- /**
- * Layers passed in from the outside.
- *
- * See `layersInternal` for where these layers will end up together with the
- * internal layers.
- */
- layers: DiffLayer[] = [];
-
- // visible for testing
- builder?: GrDiffBuilder;
-
- /**
- * All layers, both from the outside and the default ones. See `layers` for
- * the property that can be set from the outside.
- */
- // visible for testing
- layersInternal: DiffLayer[] = [];
-
- // visible for testing
- showTabs?: boolean;
-
- // visible for testing
- showTrailingWhitespace?: boolean;
-
- private coverageLayerLeft = new GrCoverageLayer(Side.LEFT);
-
- private coverageLayerRight = new GrCoverageLayer(Side.RIGHT);
-
- private rangeLayer?: GrRangedCommentLayer;
-
- // visible for testing
- processor?: GrDiffProcessor;
-
- /**
- * Groups are mostly just passed on to the diff builder (this.builder). But
- * we also keep track of them here for being able to fire a `render-content`
- * event when .element of each group has rendered.
- *
- * TODO: Refactor DiffBuilderElement and DiffBuilders with a cleaner
- * separation of responsibilities.
- */
- private groups: GrDiffGroup[] = [];
-
- updateCommentRanges(ranges: CommentRangeLayer[]) {
- this.rangeLayer?.updateRanges(ranges);
- }
-
- updateCoverageRanges(rs: CoverageRange[]) {
- this.coverageLayerLeft.setRanges(rs.filter(r => r?.side === Side.LEFT));
- this.coverageLayerRight.setRanges(rs.filter(r => r?.side === Side.RIGHT));
- }
-
- render(keyLocations: KeyLocations): Promise<void> {
- assertIsDefined(this.diff, 'diff');
- assertIsDefined(this.diffElement, 'diff table');
-
- // Setting up annotation layers must happen after plugins are
- // installed, and |render| satisfies the requirement, however,
- // |attached| doesn't because in the diff view page, the element is
- // attached before plugins are installed.
- this.setupAnnotationLayers();
-
- this.showTabs = this.prefs.show_tabs;
- this.showTrailingWhitespace = this.prefs.show_whitespace_errors;
-
- this.cleanup();
- this.builder = this.getDiffBuilder();
- this.init();
-
- // TODO: Just pass along the diff model here instead of setting many
- // individual properties.
- this.processor = new GrDiffProcessor();
- this.processor.consumer = this;
- this.processor.context = this.prefs.context;
- this.processor.keyLocations = keyLocations;
- if (this.renderPrefs?.num_lines_rendered_at_once) {
- this.processor.asyncThreshold =
- this.renderPrefs.num_lines_rendered_at_once;
- }
-
- this.clearDiffContent();
- this.builder.addColumns(
- this.diffElement,
- getLineNumberCellWidth(this.prefs)
- );
-
- const isBinary = !!(isImageDiff(this.diff) || this.diff.binary);
-
- fire(this.diffElement, 'render-start', {});
- return (
- this.processor
- .process(this.diff.content, isBinary)
- .then(async () => {
- if (isImageDiffBuilder(this.builder)) {
- this.builder.renderImageDiff();
- } else if (isBinaryDiffBuilder(this.builder)) {
- this.builder.renderBinaryDiff();
- }
- await this.untilGroupsRendered();
- fire(this.diffElement, 'render-content', {});
- })
- // Mocha testing does not like uncaught rejections, so we catch
- // the cancels which are expected and should not throw errors in
- // tests.
- .catch(e => {
- if (!e.isCanceled) return Promise.reject(e);
- return;
- })
- );
- }
-
- // visible for testing
- async untilGroupsRendered(groups: readonly GrDiffGroup[] = this.groups) {
- return Promise.all(groups.map(g => g.waitUntilRendered()));
- }
-
- private onDiffContextExpanded = (
- e: CustomEvent<DiffContextExpandedEventDetail>
- ) => {
- // Don't stop propagation. The host may listen for reporting or
- // resizing.
- this.replaceGroup(e.detail.contextGroup, e.detail.groups);
- };
-
- // visible for testing
- setupAnnotationLayers() {
- this.rangeLayer = new GrRangedCommentLayer();
-
- const layers: DiffLayer[] = [
- this.createTrailingWhitespaceLayer(),
- this.createIntralineLayer(),
- this.createTabIndicatorLayer(),
- this.createSpecialCharacterIndicatorLayer(),
- this.rangeLayer,
- this.coverageLayerLeft,
- this.coverageLayerRight,
- ];
-
- if (this.layers) {
- layers.push(...this.layers);
- }
- this.layersInternal = layers;
- }
-
- getContentTdByLine(lineNumber: LineNumber, side?: Side) {
- if (!this.builder) return undefined;
- return this.builder.getContentTdByLine(lineNumber, side);
- }
-
- getContentTdByLineEl(lineEl?: Element): Element | undefined {
- if (!lineEl) return undefined;
- const line = getLineNumber(lineEl);
- if (!line) return undefined;
- const side = getSideByLineEl(lineEl);
- return this.getContentTdByLine(line, side);
- }
-
- getLineElByNumber(lineNumber: LineNumber, side?: Side) {
- if (!this.builder) return undefined;
- return this.builder.getLineElByNumber(lineNumber, side);
- }
-
- getLineNumberRows() {
- if (!this.builder) return [];
- return this.builder.getLineNumberRows();
- }
-
- getLineNumEls(side: Side) {
- if (!this.builder) return [];
- return this.builder.getLineNumEls(side);
- }
-
- /**
- * When the line is hidden behind a context expander, expand it.
- *
- * @param lineNum A line number to expand. Using number here because other
- * special case line numbers are never hidden, so it does not make sense
- * to expand them.
- * @param side The side the line number refer to.
- */
- unhideLine(lineNum: number, side: Side) {
- if (!this.builder) return;
- const group = this.builder.findGroup(side, lineNum);
- // Cannot unhide a line that is not part of the diff.
- if (!group) return;
- // If it's already visible, great!
- if (group.type !== GrDiffGroupType.CONTEXT_CONTROL) return;
- const lineRange = group.lineRange[side];
- const lineOffset = lineNum - lineRange.start_line;
- const newGroups = [];
- const groups = hideInContextControl(
- group.contextGroups,
- 0,
- lineOffset - 1 - this.prefs.context
- );
- // If there is a context group, it will be the first group because we
- // start hiding from 0 offset
- if (groups[0].type === GrDiffGroupType.CONTEXT_CONTROL) {
- newGroups.push(groups.shift()!);
- }
- newGroups.push(
- ...hideInContextControl(
- groups,
- lineOffset + 1 + this.prefs.context,
- // Both ends inclusive, so difference is the offset of the last line.
- // But we need to pass the first line not to hide, which is the element
- // after.
- lineRange.end_line - lineRange.start_line + 1
- )
- );
- this.replaceGroup(group, newGroups);
- }
-
- /**
- * Replace the group of a context control section by rendering the provided
- * groups instead. This happens in response to expanding a context control
- * group.
- *
- * @param contextGroup The context control group to replace
- * @param newGroups The groups that are replacing the context control group
- */
- private replaceGroup(
- contextGroup: GrDiffGroup,
- newGroups: readonly GrDiffGroup[]
- ) {
- if (!this.builder) return;
- fire(this.diffElement, 'render-start', {});
- this.builder.replaceGroup(contextGroup, newGroups);
- this.groups = this.groups.filter(g => g !== contextGroup);
- this.groups.push(...newGroups);
- this.untilGroupsRendered(newGroups).then(() => {
- fire(this.diffElement, 'render-content', {});
- });
- }
-
- /**
- * This is meant to be called when the gr-diff component re-connects, or when
- * the diff is (re-)rendered.
- *
- * Make sure that this method is symmetric with cleanup(), which is called
- * when gr-diff disconnects.
- */
- init() {
- this.cleanup();
- this.diffElement?.addEventListener(
- 'diff-context-expanded-internal',
- this.onDiffContextExpanded
- );
- this.builder?.init();
- }
-
- /**
- * This is meant to be called when the gr-diff component disconnects, or when
- * the diff is (re-)rendered.
- *
- * Make sure that this method is symmetric with init(), which is called when
- * gr-diff re-connects.
- */
- cleanup() {
- this.processor?.cancel();
- this.builder?.cleanup();
- this.diffElement?.removeEventListener(
- 'diff-context-expanded-internal',
- this.onDiffContextExpanded
- );
- }
-
- // visible for testing
- handlePreferenceError(pref: string): never {
- const message =
- `The value of the '${pref}' user preference is ` +
- 'invalid. Fix in diff preferences';
- assertIsDefined(this.diffElement, 'diff table');
- fireAlert(this.diffElement, message);
- throw Error(`Invalid preference value: ${pref}`);
- }
-
- // visible for testing
- getDiffBuilder(): GrDiffBuilder {
- assertIsDefined(this.diff, 'diff');
- assertIsDefined(this.diffElement, 'diff table');
- if (isNaN(this.prefs.tab_size) || this.prefs.tab_size <= 0) {
- this.handlePreferenceError('tab size');
- }
-
- if (isNaN(this.prefs.line_length) || this.prefs.line_length <= 0) {
- this.handlePreferenceError('diff width');
- }
-
- const localPrefs = {...this.prefs};
- if (this.path === COMMIT_MSG_PATH) {
- // override line_length for commit msg the same way as
- // in gr-diff
- localPrefs.line_length = COMMIT_MSG_LINE_LENGTH;
- }
-
- let builder = null;
- if (isImageDiff(this.diff)) {
- builder = new GrDiffBuilderImage(
- this.diff,
- localPrefs,
- this.diffElement,
- this.baseImage,
- this.revisionImage,
- this.renderPrefs,
- this.useNewImageDiffUi
- );
- } else if (this.diff.binary) {
- return new GrDiffBuilderBinary(this.diff, localPrefs, this.diffElement);
- } else if (this.viewMode === DiffViewMode.SIDE_BY_SIDE) {
- this.renderPrefs = {
- ...this.renderPrefs,
- view_mode: DiffViewMode.SIDE_BY_SIDE,
- };
- builder = new GrDiffBuilder(
- this.diff,
- localPrefs,
- this.diffElement,
- this.layersInternal,
- this.renderPrefs
- );
- } else if (this.viewMode === DiffViewMode.UNIFIED) {
- this.renderPrefs = {
- ...this.renderPrefs,
- view_mode: DiffViewMode.UNIFIED,
- };
- builder = new GrDiffBuilder(
- this.diff,
- localPrefs,
- this.diffElement,
- this.layersInternal,
- this.renderPrefs
- );
- }
- if (!builder) {
- throw Error(`Unsupported diff view mode: ${this.viewMode}`);
- }
- return builder;
- }
-
- private clearDiffContent() {
- assertIsDefined(this.diffElement, 'diff table');
- this.diffElement.innerHTML = '';
- }
-
- /**
- * Called when the processor starts converting the diff information from the
- * server into chunks.
- */
- clearGroups() {
- if (!this.builder) return;
- this.groups = [];
- this.builder.clearGroups();
- }
-
- /**
- * Called when the processor is done converting a chunk of the diff.
- */
- addGroup(group: GrDiffGroup) {
- if (!this.builder) return;
- this.builder.addGroups([group]);
- this.groups.push(group);
- }
-
- // visible for testing
- createIntralineLayer(): DiffLayer {
- return {
- // Take a DIV.contentText element and a line object with intraline
- // differences to highlight and apply them to the element as
- // annotations.
- annotate(contentEl: HTMLElement, _: HTMLElement, line: GrDiffLine) {
- const HL_CLASS = 'gr-diff intraline';
- for (const highlight of line.highlights) {
- // The start and end indices could be the same if a highlight is
- // meant to start at the end of a line and continue onto the
- // next one. Ignore it.
- if (highlight.startIndex === highlight.endIndex) {
- continue;
- }
-
- // If endIndex isn't present, continue to the end of the line.
- const endIndex =
- highlight.endIndex === undefined
- ? GrAnnotation.getStringLength(line.text)
- : highlight.endIndex;
-
- GrAnnotation.annotateElement(
- contentEl,
- highlight.startIndex,
- endIndex - highlight.startIndex,
- HL_CLASS
- );
- }
- },
- };
- }
-
- // visible for testing
- createTabIndicatorLayer(): DiffLayer {
- const show = () => this.showTabs;
- return {
- annotate(contentEl: HTMLElement, _: HTMLElement, line: GrDiffLine) {
- // If visible tabs are disabled, do nothing.
- if (!show()) {
- return;
- }
-
- // Find and annotate the locations of tabs.
- annotateSymbols(contentEl, line, '\t', 'tab-indicator');
- },
- };
- }
-
- private createSpecialCharacterIndicatorLayer(): DiffLayer {
- return {
- annotate(contentEl: HTMLElement, _: HTMLElement, line: GrDiffLine) {
- // Find and annotate the locations of soft hyphen (\u00AD)
- annotateSymbols(contentEl, line, '\u00AD', 'special-char-indicator');
- // Find and annotate Stateful Unicode directional controls
- annotateSymbols(
- contentEl,
- line,
- /[\u202A-\u202E\u2066-\u2069]/,
- 'special-char-warning'
- );
- },
- };
- }
-
- // visible for testing
- createTrailingWhitespaceLayer(): DiffLayer {
- const show = () => this.showTrailingWhitespace;
-
- return {
- annotate(contentEl: HTMLElement, _: HTMLElement, line: GrDiffLine) {
- if (!show()) {
- return;
- }
-
- const match = line.text.match(TRAILING_WHITESPACE_PATTERN);
- if (match) {
- // Normalize string positions in case there is unicode before or
- // within the match.
- const index = GrAnnotation.getStringLength(
- line.text.substr(0, match.index)
- );
- const length = GrAnnotation.getStringLength(match[0]);
- GrAnnotation.annotateElement(
- contentEl,
- index,
- length,
- 'gr-diff trailing-whitespace'
- );
- }
- },
- };
- }
-
- setBlame(blame: BlameInfo[] | null) {
- if (!this.builder) return;
- this.builder.setBlame(blame ?? []);
- }
-
- updateRenderPrefs(renderPrefs: RenderPreferences) {
- this.builder?.updateRenderPrefs(renderPrefs);
- }
-}
diff --git a/polygerrit-ui/app/embed/diff-old/gr-diff-builder/gr-diff-builder-element_test.ts b/polygerrit-ui/app/embed/diff-old/gr-diff-builder/gr-diff-builder-element_test.ts
deleted file mode 100644
index 2cee232..0000000
--- a/polygerrit-ui/app/embed/diff-old/gr-diff-builder/gr-diff-builder-element_test.ts
+++ /dev/null
@@ -1,630 +0,0 @@
-/**
- * @license
- * Copyright 2016 Google LLC
- * SPDX-License-Identifier: Apache-2.0
- */
-import '../../../test/common-test-setup';
-import {
- createConfig,
- createEmptyDiff,
-} from '../../../test/test-data-generators';
-import './gr-diff-builder-element';
-import {stubBaseUrl, waitUntil} from '../../../test/test-utils';
-import {GrAnnotation} from '../gr-diff-highlight/gr-annotation';
-import {GrDiffLine} from '../gr-diff/gr-diff-line';
-import {
- DiffContent,
- DiffLayer,
- DiffPreferencesInfo,
- DiffViewMode,
- GrDiffLineType,
- Side,
-} from '../../../api/diff';
-import {stubRestApi} from '../../../test/test-utils';
-import {waitForEventOnce} from '../../../utils/event-util';
-import {GrDiffBuilderElement} from './gr-diff-builder-element';
-import {createDefaultDiffPrefs} from '../../../constants/constants';
-import {KeyLocations} from '../gr-diff-processor/gr-diff-processor';
-import {fixture, html, assert} from '@open-wc/testing';
-import {GrDiffRow} from './gr-diff-row';
-import {GrDiffBuilder} from './gr-diff-builder';
-import {querySelectorAll} from '../../../utils/dom-util';
-
-const DEFAULT_PREFS = createDefaultDiffPrefs();
-
-suite('gr-diff-builder tests', () => {
- let element: GrDiffBuilderElement;
- let builder: GrDiffBuilder;
- let diffTable: HTMLTableElement;
-
- const setBuilderPrefs = (prefs: Partial<DiffPreferencesInfo>) => {
- builder = new GrDiffBuilder(
- createEmptyDiff(),
- {...createDefaultDiffPrefs(), ...prefs},
- diffTable
- );
- };
-
- const line = (text: string) => {
- const line = new GrDiffLine(GrDiffLineType.BOTH);
- line.text = text;
- return line;
- };
-
- setup(async () => {
- diffTable = await fixture(html`<table id="diffTable"></table>`);
- element = new GrDiffBuilderElement();
- element.diffElement = diffTable;
- stubRestApi('getLoggedIn').returns(Promise.resolve(false));
- stubRestApi('getProjectConfig').returns(Promise.resolve(createConfig()));
- stubBaseUrl('/r');
- setBuilderPrefs({});
- });
-
- [DiffViewMode.UNIFIED, DiffViewMode.SIDE_BY_SIDE].forEach(mode => {
- test(`line_length used for regular files under ${mode}`, () => {
- element.path = '/a.txt';
- element.viewMode = mode;
- element.diff = createEmptyDiff();
- element.prefs = {
- ...createDefaultDiffPrefs(),
- tab_size: 4,
- line_length: 50,
- };
- builder = element.getDiffBuilder();
- assert.equal(builder.prefs.line_length, 50);
- });
-
- test(`line_length ignored for commit msg under ${mode}`, () => {
- element.path = '/COMMIT_MSG';
- element.viewMode = mode;
- element.diff = createEmptyDiff();
- element.prefs = {
- ...createDefaultDiffPrefs(),
- tab_size: 4,
- line_length: 50,
- };
- builder = element.getDiffBuilder();
- assert.equal(builder.prefs.line_length, 72);
- });
- });
-
- test('_handlePreferenceError throws with invalid preference', () => {
- element.prefs = {...createDefaultDiffPrefs(), tab_size: 0};
- assert.throws(() => element.getDiffBuilder());
- });
-
- test('_handlePreferenceError triggers alert and javascript error', () => {
- const errorStub = sinon.stub();
- diffTable.addEventListener('show-alert', errorStub);
- assert.throws(() => element.handlePreferenceError('tab size'));
- assert.equal(
- errorStub.lastCall.args[0].detail.message,
- "The value of the 'tab size' user preference is invalid. " +
- 'Fix in diff preferences'
- );
- });
-
- suite('intraline differences', () => {
- let el: HTMLElement;
- let str: string;
- let annotateElementSpy: sinon.SinonSpy;
- let layer: DiffLayer;
- const lineNumberEl = document.createElement('td');
-
- function slice(str: string, start: number, end?: number) {
- return Array.from(str).slice(start, end).join('');
- }
-
- setup(async () => {
- el = await fixture(html`
- <div>Lorem ipsum dolor sit amet, suspendisse inceptos vehicula</div>
- `);
- str = el.textContent ?? '';
- annotateElementSpy = sinon.spy(GrAnnotation, 'annotateElement');
- layer = element.createIntralineLayer();
- });
-
- test('annotate no highlights', () => {
- layer.annotate(el, lineNumberEl, line(str), Side.LEFT);
-
- // The content is unchanged.
- assert.isFalse(annotateElementSpy.called);
- assert.equal(el.childNodes.length, 1);
- assert.instanceOf(el.childNodes[0], Text);
- assert.equal(str, el.childNodes[0].textContent);
- });
-
- test('annotate with highlights', () => {
- const l = line(str);
- l.highlights = [
- {contentIndex: 0, startIndex: 6, endIndex: 12},
- {contentIndex: 0, startIndex: 18, endIndex: 22},
- ];
- const str0 = slice(str, 0, 6);
- const str1 = slice(str, 6, 12);
- const str2 = slice(str, 12, 18);
- const str3 = slice(str, 18, 22);
- const str4 = slice(str, 22);
-
- layer.annotate(el, lineNumberEl, l, Side.LEFT);
-
- assert.isTrue(annotateElementSpy.called);
- assert.equal(el.childNodes.length, 5);
-
- assert.instanceOf(el.childNodes[0], Text);
- assert.equal(el.childNodes[0].textContent, str0);
-
- assert.notInstanceOf(el.childNodes[1], Text);
- assert.equal(el.childNodes[1].textContent, str1);
-
- assert.instanceOf(el.childNodes[2], Text);
- assert.equal(el.childNodes[2].textContent, str2);
-
- assert.notInstanceOf(el.childNodes[3], Text);
- assert.equal(el.childNodes[3].textContent, str3);
-
- assert.instanceOf(el.childNodes[4], Text);
- assert.equal(el.childNodes[4].textContent, str4);
- });
-
- test('annotate without endIndex', () => {
- const l = line(str);
- l.highlights = [{contentIndex: 0, startIndex: 28}];
-
- const str0 = slice(str, 0, 28);
- const str1 = slice(str, 28);
-
- layer.annotate(el, lineNumberEl, l, Side.LEFT);
-
- assert.isTrue(annotateElementSpy.called);
- assert.equal(el.childNodes.length, 2);
-
- assert.instanceOf(el.childNodes[0], Text);
- assert.equal(el.childNodes[0].textContent, str0);
-
- assert.notInstanceOf(el.childNodes[1], Text);
- assert.equal(el.childNodes[1].textContent, str1);
- });
-
- test('annotate ignores empty highlights', () => {
- const l = line(str);
- l.highlights = [{contentIndex: 0, startIndex: 28, endIndex: 28}];
-
- layer.annotate(el, lineNumberEl, l, Side.LEFT);
-
- assert.isFalse(annotateElementSpy.called);
- assert.equal(el.childNodes.length, 1);
- });
-
- test('annotate handles unicode', () => {
- // Put some unicode into the string:
- str = str.replace(/\s/g, '💢');
- el.textContent = str;
- const l = line(str);
- l.highlights = [{contentIndex: 0, startIndex: 6, endIndex: 12}];
-
- const str0 = slice(str, 0, 6);
- const str1 = slice(str, 6, 12);
- const str2 = slice(str, 12);
-
- layer.annotate(el, lineNumberEl, l, Side.LEFT);
-
- assert.isTrue(annotateElementSpy.called);
- assert.equal(el.childNodes.length, 3);
-
- assert.instanceOf(el.childNodes[0], Text);
- assert.equal(el.childNodes[0].textContent, str0);
-
- assert.notInstanceOf(el.childNodes[1], Text);
- assert.equal(el.childNodes[1].textContent, str1);
-
- assert.instanceOf(el.childNodes[2], Text);
- assert.equal(el.childNodes[2].textContent, str2);
- });
-
- test('annotate handles unicode w/o endIndex', () => {
- // Put some unicode into the string:
- str = str.replace(/\s/g, '💢');
- el.textContent = str;
-
- const l = line(str);
- l.highlights = [{contentIndex: 0, startIndex: 6}];
-
- const str0 = slice(str, 0, 6);
- const str1 = slice(str, 6);
- const numHighlightedChars = GrAnnotation.getStringLength(str1);
-
- layer.annotate(el, lineNumberEl, l, Side.LEFT);
-
- assert.isTrue(annotateElementSpy.calledWith(el, 6, numHighlightedChars));
- assert.equal(el.childNodes.length, 2);
-
- assert.instanceOf(el.childNodes[0], Text);
- assert.equal(el.childNodes[0].textContent, str0);
-
- assert.notInstanceOf(el.childNodes[1], Text);
- assert.equal(el.childNodes[1].textContent, str1);
- });
- });
-
- suite('tab indicators', () => {
- let layer: DiffLayer;
- const lineNumberEl = document.createElement('td');
-
- setup(() => {
- element.showTabs = true;
- layer = element.createTabIndicatorLayer();
- });
-
- test('does nothing with empty line', () => {
- const l = line('');
- const el = document.createElement('div');
- const annotateElementStub = sinon.stub(GrAnnotation, 'annotateElement');
-
- layer.annotate(el, lineNumberEl, l, Side.LEFT);
-
- assert.isFalse(annotateElementStub.called);
- });
-
- test('does nothing with no tabs', () => {
- const str = 'lorem ipsum no tabs';
- const l = line(str);
- const el = document.createElement('div');
- el.textContent = str;
- const annotateElementStub = sinon.stub(GrAnnotation, 'annotateElement');
-
- layer.annotate(el, lineNumberEl, l, Side.LEFT);
-
- assert.isFalse(annotateElementStub.called);
- });
-
- test('annotates tab at beginning', () => {
- const str = '\tlorem upsum';
- const l = line(str);
- const el = document.createElement('div');
- el.textContent = str;
- const annotateElementStub = sinon.stub(GrAnnotation, 'annotateElement');
-
- layer.annotate(el, lineNumberEl, l, Side.LEFT);
-
- assert.equal(annotateElementStub.callCount, 1);
- const args = annotateElementStub.getCalls()[0].args;
- assert.equal(args[0], el);
- assert.equal(args[1], 0, 'offset of tab indicator');
- assert.equal(args[2], 1, 'length of tab indicator');
- assert.include(args[3], 'tab-indicator');
- });
-
- test('does not annotate when disabled', () => {
- element.showTabs = false;
-
- const str = '\tlorem upsum';
- const l = line(str);
- const el = document.createElement('div');
- el.textContent = str;
- const annotateElementStub = sinon.stub(GrAnnotation, 'annotateElement');
-
- layer.annotate(el, lineNumberEl, l, Side.LEFT);
-
- assert.isFalse(annotateElementStub.called);
- });
-
- test('annotates multiple in beginning', () => {
- const str = '\t\tlorem upsum';
- const l = line(str);
- const el = document.createElement('div');
- el.textContent = str;
- const annotateElementStub = sinon.stub(GrAnnotation, 'annotateElement');
-
- layer.annotate(el, lineNumberEl, l, Side.LEFT);
-
- assert.equal(annotateElementStub.callCount, 2);
-
- let args = annotateElementStub.getCalls()[0].args;
- assert.equal(args[0], el);
- assert.equal(args[1], 0, 'offset of tab indicator');
- assert.equal(args[2], 1, 'length of tab indicator');
- assert.include(args[3], 'tab-indicator');
-
- args = annotateElementStub.getCalls()[1].args;
- assert.equal(args[0], el);
- assert.equal(args[1], 1, 'offset of tab indicator');
- assert.equal(args[2], 1, 'length of tab indicator');
- assert.include(args[3], 'tab-indicator');
- });
-
- test('annotates intermediate tabs', () => {
- const str = 'lorem\tupsum';
- const l = line(str);
- const el = document.createElement('div');
- el.textContent = str;
- const annotateElementStub = sinon.stub(GrAnnotation, 'annotateElement');
-
- layer.annotate(el, lineNumberEl, l, Side.LEFT);
-
- assert.equal(annotateElementStub.callCount, 1);
- const args = annotateElementStub.getCalls()[0].args;
- assert.equal(args[0], el);
- assert.equal(args[1], 5, 'offset of tab indicator');
- assert.equal(args[2], 1, 'length of tab indicator');
- assert.include(args[3], 'tab-indicator');
- });
- });
-
- suite('layers', () => {
- let initialLayersCount = 0;
- let withLayerCount = 0;
- setup(() => {
- const layers: DiffLayer[] = [];
- element.layers = layers;
- element.showTrailingWhitespace = true;
- element.setupAnnotationLayers();
- initialLayersCount = element.layersInternal.length;
- });
-
- test('no layers', () => {
- element.setupAnnotationLayers();
- assert.equal(element.layersInternal.length, initialLayersCount);
- });
-
- suite('with layers', () => {
- const layers: DiffLayer[] = [{annotate: () => {}}, {annotate: () => {}}];
- setup(() => {
- element.layers = layers;
- element.showTrailingWhitespace = true;
- element.setupAnnotationLayers();
- withLayerCount = element.layersInternal.length;
- });
- test('with layers', () => {
- element.setupAnnotationLayers();
- assert.equal(element.layersInternal.length, withLayerCount);
- assert.equal(initialLayersCount + layers.length, withLayerCount);
- });
- });
- });
-
- suite('trailing whitespace', () => {
- let layer: DiffLayer;
- const lineNumberEl = document.createElement('td');
-
- setup(() => {
- element.showTrailingWhitespace = true;
- layer = element.createTrailingWhitespaceLayer();
- });
-
- test('does nothing with empty line', () => {
- const l = line('');
- const el = document.createElement('div');
- const annotateElementStub = sinon.stub(GrAnnotation, 'annotateElement');
- layer.annotate(el, lineNumberEl, l, Side.LEFT);
- assert.isFalse(annotateElementStub.called);
- });
-
- test('does nothing with no trailing whitespace', () => {
- const str = 'lorem ipsum blah blah';
- const l = line(str);
- const el = document.createElement('div');
- el.textContent = str;
- const annotateElementStub = sinon.stub(GrAnnotation, 'annotateElement');
- layer.annotate(el, lineNumberEl, l, Side.LEFT);
- assert.isFalse(annotateElementStub.called);
- });
-
- test('annotates trailing spaces', () => {
- const str = 'lorem ipsum ';
- const l = line(str);
- const el = document.createElement('div');
- el.textContent = str;
- const annotateElementStub = sinon.stub(GrAnnotation, 'annotateElement');
- layer.annotate(el, lineNumberEl, l, Side.LEFT);
- assert.isTrue(annotateElementStub.called);
- assert.equal(annotateElementStub.lastCall.args[1], 11);
- assert.equal(annotateElementStub.lastCall.args[2], 3);
- });
-
- test('annotates trailing tabs', () => {
- const str = 'lorem ipsum\t\t\t';
- const l = line(str);
- const el = document.createElement('div');
- el.textContent = str;
- const annotateElementStub = sinon.stub(GrAnnotation, 'annotateElement');
- layer.annotate(el, lineNumberEl, l, Side.LEFT);
- assert.isTrue(annotateElementStub.called);
- assert.equal(annotateElementStub.lastCall.args[1], 11);
- assert.equal(annotateElementStub.lastCall.args[2], 3);
- });
-
- test('annotates mixed trailing whitespace', () => {
- const str = 'lorem ipsum\t \t';
- const l = line(str);
- const el = document.createElement('div');
- el.textContent = str;
- const annotateElementStub = sinon.stub(GrAnnotation, 'annotateElement');
- layer.annotate(el, lineNumberEl, l, Side.LEFT);
- assert.isTrue(annotateElementStub.called);
- assert.equal(annotateElementStub.lastCall.args[1], 11);
- assert.equal(annotateElementStub.lastCall.args[2], 3);
- });
-
- test('unicode preceding trailing whitespace', () => {
- const str = '💢\t';
- const l = line(str);
- const el = document.createElement('div');
- el.textContent = str;
- const annotateElementStub = sinon.stub(GrAnnotation, 'annotateElement');
- layer.annotate(el, lineNumberEl, l, Side.LEFT);
- assert.isTrue(annotateElementStub.called);
- assert.equal(annotateElementStub.lastCall.args[1], 1);
- assert.equal(annotateElementStub.lastCall.args[2], 1);
- });
-
- test('does not annotate when disabled', () => {
- element.showTrailingWhitespace = false;
- const str = 'lorem upsum\t \t ';
- const l = line(str);
- const el = document.createElement('div');
- el.textContent = str;
- const annotateElementStub = sinon.stub(GrAnnotation, 'annotateElement');
- layer.annotate(el, lineNumberEl, l, Side.LEFT);
- assert.isFalse(annotateElementStub.called);
- });
- });
-
- suite('rendering text, images and binary files', () => {
- let keyLocations: KeyLocations;
- let content: DiffContent[] = [];
-
- setup(() => {
- element.viewMode = 'SIDE_BY_SIDE';
- keyLocations = {left: {}, right: {}};
- element.prefs = {
- ...DEFAULT_PREFS,
- context: -1,
- syntax_highlighting: true,
- };
- content = [
- {
- a: ['all work and no play make andybons a dull boy'],
- b: ['elgoog elgoog elgoog'],
- },
- {
- ab: [
- 'Non eram nescius, Brute, cum, quae summis ingeniis ',
- 'exquisitaque doctrina philosophi Graeco sermone tractavissent',
- ],
- },
- ];
- });
-
- test('text', async () => {
- element.diff = {...createEmptyDiff(), content};
- element.render(keyLocations);
- await waitForEventOnce(diffTable, 'render-content');
- assert.equal(querySelectorAll(diffTable, 'tbody')?.length, 4);
- });
-
- test('image', async () => {
- const diff = {...createEmptyDiff(), content, binary: true};
- diff.meta_a!.content_type = 'image/png';
- diff.meta_b!.content_type = 'image/png';
- element.diff = diff;
- element.render(keyLocations);
- await waitForEventOnce(diffTable, 'render-content');
- assert.equal(querySelectorAll(diffTable, 'tbody')?.length, 4);
- });
-
- test('binary', async () => {
- element.diff = {...createEmptyDiff(), content, binary: true};
- element.render(keyLocations);
- await waitForEventOnce(diffTable, 'render-content');
- assert.equal(querySelectorAll(diffTable, 'tbody')?.length, 3);
- });
- });
-
- suite('context hiding and expanding', () => {
- let dispatchStub: sinon.SinonStub;
-
- setup(async () => {
- dispatchStub = sinon.stub(diffTable, 'dispatchEvent');
- element.diff = {
- ...createEmptyDiff(),
- content: [
- {ab: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10].map(i => `unchanged ${i}`)},
- {a: ['before'], b: ['after']},
- {ab: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10].map(i => `unchanged ${10 + i}`)},
- ],
- };
- element.viewMode = DiffViewMode.SIDE_BY_SIDE;
-
- const keyLocations: KeyLocations = {left: {}, right: {}};
- element.prefs = {
- ...DEFAULT_PREFS,
- context: 1,
- };
- element.render(keyLocations);
- // Make sure all listeners are installed.
- await element.untilGroupsRendered();
- });
-
- test('hides lines behind two context controls', () => {
- const contextControls = diffTable.querySelectorAll('gr-context-controls');
- assert.equal(contextControls.length, 2);
-
- const diffRows = diffTable.querySelectorAll('.diff-row');
- // The first two are LOST and FILE line
- assert.equal(diffRows.length, 2 + 1 + 1 + 1);
- assert.include(diffRows[2].textContent, 'unchanged 10');
- assert.include(diffRows[3].textContent, 'before');
- assert.include(diffRows[3].textContent, 'after');
- assert.include(diffRows[4].textContent, 'unchanged 11');
- });
-
- test('clicking +x common lines expands those lines', async () => {
- const contextControls = diffTable.querySelectorAll('gr-context-controls');
- const topExpandCommonButton =
- contextControls[0].shadowRoot?.querySelectorAll<HTMLElement>(
- '.showContext'
- )[0];
- assert.isOk(topExpandCommonButton);
- assert.include(topExpandCommonButton!.textContent, '+9 common lines');
- let diffRows = diffTable.querySelectorAll('.diff-row');
- // 5 lines:
- // FILE, LOST, the changed line plus one line of context in each direction
- assert.equal(diffRows.length, 5);
-
- topExpandCommonButton!.click();
-
- await waitUntil(() => {
- diffRows = diffTable.querySelectorAll<GrDiffRow>('.diff-row');
- return diffRows.length === 14;
- });
- // 14 lines: The 5 above plus the 9 unchanged lines that were expanded
- assert.equal(diffRows.length, 14);
- assert.include(diffRows[2].textContent, 'unchanged 1');
- assert.include(diffRows[3].textContent, 'unchanged 2');
- assert.include(diffRows[4].textContent, 'unchanged 3');
- assert.include(diffRows[5].textContent, 'unchanged 4');
- assert.include(diffRows[6].textContent, 'unchanged 5');
- assert.include(diffRows[7].textContent, 'unchanged 6');
- assert.include(diffRows[8].textContent, 'unchanged 7');
- assert.include(diffRows[9].textContent, 'unchanged 8');
- assert.include(diffRows[10].textContent, 'unchanged 9');
- assert.include(diffRows[11].textContent, 'unchanged 10');
- assert.include(diffRows[12].textContent, 'before');
- assert.include(diffRows[12].textContent, 'after');
- assert.include(diffRows[13].textContent, 'unchanged 11');
- });
-
- test('unhideLine shows the line with context', async () => {
- dispatchStub.reset();
- element.unhideLine(4, Side.LEFT);
-
- await waitUntil(() => {
- const rows = diffTable.querySelectorAll<GrDiffRow>('.diff-row');
- return rows.length === 2 + 5 + 1 + 1 + 1;
- });
-
- const diffRows = diffTable.querySelectorAll('.diff-row');
- // The first two are LOST and FILE line
- // Lines 3-5 (Line 4 plus 1 context in each direction) will be expanded
- // Because context expanders do not hide <3 lines, lines 1-2 will also
- // be shown.
- // Lines 6-9 continue to be hidden
- assert.equal(diffRows.length, 2 + 5 + 1 + 1 + 1);
- assert.include(diffRows[2].textContent, 'unchanged 1');
- assert.include(diffRows[3].textContent, 'unchanged 2');
- assert.include(diffRows[4].textContent, 'unchanged 3');
- assert.include(diffRows[5].textContent, 'unchanged 4');
- assert.include(diffRows[6].textContent, 'unchanged 5');
- assert.include(diffRows[7].textContent, 'unchanged 10');
- assert.include(diffRows[8].textContent, 'before');
- assert.include(diffRows[8].textContent, 'after');
- assert.include(diffRows[9].textContent, 'unchanged 11');
-
- await element.untilGroupsRendered();
- const firedEventTypes = dispatchStub.getCalls().map(c => c.args[0].type);
- assert.include(firedEventTypes, 'render-content');
- });
- });
-});
diff --git a/polygerrit-ui/app/embed/diff-old/gr-diff-builder/gr-diff-builder-image.ts b/polygerrit-ui/app/embed/diff-old/gr-diff-builder/gr-diff-builder-image.ts
deleted file mode 100644
index d71d52a..0000000
--- a/polygerrit-ui/app/embed/diff-old/gr-diff-builder/gr-diff-builder-image.ts
+++ /dev/null
@@ -1,279 +0,0 @@
-/**
- * @license
- * Copyright 2016 Google LLC
- * SPDX-License-Identifier: Apache-2.0
- */
-import {ImageInfo} from '../../../types/common';
-import {DiffInfo, DiffPreferencesInfo} from '../../../types/diff';
-import {FILE, RenderPreferences, Side} from '../../../api/diff';
-import '../../diff/gr-diff-image-viewer/gr-image-viewer';
-import {html, LitElement, nothing} from 'lit';
-import {property, query, state} from 'lit/decorators.js';
-import {GrDiffBuilder} from './gr-diff-builder';
-import {createElementDiff, isNewDiff} from '../../diff/gr-diff/gr-diff-utils';
-import {GrDiffGroup} from '../gr-diff/gr-diff-group';
-
-// MIME types for images we allow showing. Do not include SVG, it can contain
-// arbitrary JavaScript.
-const IMAGE_MIME_PATTERN = /^image\/(bmp|gif|x-icon|jpeg|jpg|png|tiff|webp)$/;
-
-export class GrDiffBuilderImage extends GrDiffBuilder {
- constructor(
- diff: DiffInfo,
- prefs: DiffPreferencesInfo,
- outputEl: HTMLElement,
- private readonly baseImage: ImageInfo | null,
- private readonly revisionImage: ImageInfo | null,
- renderPrefs?: RenderPreferences,
- private readonly useNewImageDiffUi: boolean = false
- ) {
- super(diff, prefs, outputEl, [], renderPrefs);
- }
-
- override buildSectionElement(group: GrDiffGroup): HTMLElement {
- const section = createElementDiff('tbody');
- // Do not create a diff row for LOST.
- if (group.lines[0].beforeNumber !== FILE) return section;
- return super.buildSectionElement(group);
- }
-
- public renderImageDiff() {
- const imageDiff = this.useNewImageDiffUi
- ? this.createImageDiffNew()
- : this.createImageDiffOld();
- this.outputEl.appendChild(imageDiff);
- }
-
- private createImageDiffNew() {
- const imageDiff = document.createElement(
- 'gr-diff-image-new'
- ) as GrDiffImageNew;
- imageDiff.automaticBlink = this.autoBlink();
- imageDiff.baseImage = this.baseImage ?? undefined;
- imageDiff.revisionImage = this.revisionImage ?? undefined;
- return imageDiff;
- }
-
- private createImageDiffOld() {
- const imageDiff = document.createElement(
- 'gr-diff-image-old'
- ) as GrDiffImageOld;
- imageDiff.baseImage = this.baseImage ?? undefined;
- imageDiff.revisionImage = this.revisionImage ?? undefined;
- return imageDiff;
- }
-
- private autoBlink(): boolean {
- return !!this.renderPrefs?.image_diff_prefs?.automatic_blink;
- }
-
- override updateRenderPrefs(renderPrefs: RenderPreferences) {
- this.renderPrefs = renderPrefs;
-
- // We have to update `imageDiff.automaticBlink` manually, because `this` is
- // not a LitElement.
- const imageDiff = this.outputEl.querySelector(
- 'gr-diff-image-new'
- ) as GrDiffImageNew;
- if (imageDiff) imageDiff.automaticBlink = this.autoBlink();
- }
-}
-
-class GrDiffImageNew extends LitElement {
- @property() baseImage?: ImageInfo;
-
- @property() revisionImage?: ImageInfo;
-
- @property() automaticBlink = false;
-
- /**
- * The browser API for handling selection does not (yet) work for selection
- * across multiple shadow DOM elements. So we are rendering gr-diff components
- * into the light DOM instead of the shadow DOM by overriding this method,
- * which was the recommended workaround by the lit team.
- * See also https://github.com/WICG/webcomponents/issues/79.
- */
- override createRenderRoot() {
- return this;
- }
-
- override render() {
- return html`
- <tbody class="gr-diff image-diff">
- <tr class="gr-diff">
- <td class="gr-diff" colspan="4">
- <gr-image-viewer
- class="gr-diff"
- .baseUrl=${imageSrc(this.baseImage)}
- .revisionUrl=${imageSrc(this.revisionImage)}
- .automaticBlink=${this.automaticBlink}
- >
- </gr-image-viewer>
- </td>
- </tr>
- </tbody>
- `;
- }
-}
-
-class GrDiffImageOld extends LitElement {
- @property() baseImage?: ImageInfo;
-
- @property() revisionImage?: ImageInfo;
-
- @query('img.left') baseImageEl?: HTMLImageElement;
-
- @query('img.right') revisionImageEl?: HTMLImageElement;
-
- @state() baseError?: string;
-
- @state() revisionError?: string;
-
- /**
- * The browser API for handling selection does not (yet) work for selection
- * across multiple shadow DOM elements. So we are rendering gr-diff components
- * into the light DOM instead of the shadow DOM by overriding this method,
- * which was the recommended workaround by the lit team.
- * See also https://github.com/WICG/webcomponents/issues/79.
- */
- override createRenderRoot() {
- return this;
- }
-
- override render() {
- return html`
- <tbody class="gr-diff image-diff">
- ${this.renderImagePairRow()} ${this.renderImageLabelRow()}
- </tbody>
- ${this.renderEndpoint()}
- `;
- }
-
- private renderEndpoint() {
- return html`
- <tbody class="gr-diff endpoint">
- <tr class="gr-diff">
- <td class="gr-diff" colspan="4">
- <gr-endpoint-decorator class="gr-diff" name="image-diff">
- ${this.renderEndpointParam('baseImage', this.baseImage)}
- ${this.renderEndpointParam('revisionImage', this.revisionImage)}
- </gr-endpoint-decorator>
- </td>
- </tr>
- </tbody>
- `;
- }
-
- private renderEndpointParam(name: string, value: unknown) {
- if (!value) return nothing;
- return html`
- <gr-endpoint-param class="gr-diff" name=${name} .value=${value}>
- </gr-endpoint-param>
- `;
- }
-
- private renderImagePairRow() {
- return html`
- <tr class="gr-diff">
- <td class="gr-diff left lineNum blank"></td>
- <td class="gr-diff left">${this.renderImage(Side.LEFT)}</td>
- <td class="gr-diff right lineNum blank"></td>
- <td class="gr-diff right">${this.renderImage(Side.RIGHT)}</td>
- </tr>
- `;
- }
-
- private renderImage(side: Side) {
- const image = side === Side.LEFT ? this.baseImage : this.revisionImage;
- if (!image) return nothing;
- const error = side === Side.LEFT ? this.baseError : this.revisionError;
- if (error) return error;
- const src = imageSrc(image);
- if (!src) return nothing;
-
- return html`
- <img
- class="gr-diff ${side}"
- src=${src}
- @load=${this.handleLoad}
- @error=${(e: Event) => this.handleError(e, side)}
- >
- </img>
- `;
- }
-
- private handleLoad() {
- this.requestUpdate();
- }
-
- private handleError(e: Event, side: Side) {
- const msg = `[Image failed to load] ${e.type}`;
- if (side === Side.LEFT) this.baseError = msg;
- if (side === Side.RIGHT) this.revisionError = msg;
- }
-
- private renderImageLabelRow() {
- return html`
- <tr class="gr-diff">
- <td class="gr-diff left lineNum blank"></td>
- <td class="gr-diff left">
- <label class="gr-diff">
- ${this.renderName(this.baseImage?._name ?? '')}
- <span class="gr-diff label">${this.imageLabel(Side.LEFT)}</span>
- </label>
- </td>
- <td class="gr-diff right lineNum blank"></td>
- <td class="gr-diff right">
- <label class="gr-diff">
- ${this.renderName(this.revisionImage?._name ?? '')}
- <span class="gr-diff label"> ${this.imageLabel(Side.RIGHT)} </span>
- </label>
- </td>
- </tr>
- `;
- }
-
- private renderName(name?: string) {
- const addNamesInLabel =
- this.baseImage &&
- this.revisionImage &&
- this.baseImage._name !== this.revisionImage._name;
- if (!addNamesInLabel) return nothing;
- return html`
- <span class="gr-diff name">${name}</span><br class="gr-diff" />
- `;
- }
-
- private imageLabel(side: Side) {
- const image = side === Side.LEFT ? this.baseImage : this.revisionImage;
- const imageEl =
- side === Side.LEFT ? this.baseImageEl : this.revisionImageEl;
- if (image) {
- const type = image.type ?? image._expectedType;
- if (imageEl?.naturalWidth && imageEl.naturalHeight) {
- return `${imageEl?.naturalWidth}×${imageEl.naturalHeight} ${type}`;
- } else {
- return type;
- }
- }
- return 'No image';
- }
-}
-
-function imageSrc(image?: ImageInfo): string {
- return image && IMAGE_MIME_PATTERN.test(image.type)
- ? `data:${image.type};base64,${image.body}`
- : '';
-}
-
-if (!isNewDiff()) {
- customElements.define('gr-diff-image-new', GrDiffImageNew);
- customElements.define('gr-diff-image-old', GrDiffImageOld);
-}
-
-declare global {
- interface HTMLElementTagNameMap {
- 'gr-diff-image-new': LitElement;
- 'gr-diff-image-old': LitElement;
- }
-}
diff --git a/polygerrit-ui/app/embed/diff-old/gr-diff-builder/gr-diff-builder.ts b/polygerrit-ui/app/embed/diff-old/gr-diff-builder/gr-diff-builder.ts
deleted file mode 100644
index 951466f..0000000
--- a/polygerrit-ui/app/embed/diff-old/gr-diff-builder/gr-diff-builder.ts
+++ /dev/null
@@ -1,352 +0,0 @@
-/**
- * @license
- * Copyright 2016 Google LLC
- * SPDX-License-Identifier: Apache-2.0
- */
-import './gr-diff-section';
-import '../gr-context-controls/gr-context-controls';
-import {
- ContentLoadNeededEventDetail,
- DiffContextExpandedExternalDetail,
- DiffViewMode,
- LineNumber,
- RenderPreferences,
-} from '../../../api/diff';
-import {GrDiffGroup} from '../gr-diff/gr-diff-group';
-import {BlameInfo} from '../../../types/common';
-import {DiffInfo, DiffPreferencesInfo} from '../../../types/diff';
-import {Side} from '../../../constants/constants';
-import {DiffLayer, isDefined} from '../../../types/types';
-import {GrDiffRow} from './gr-diff-row';
-import {GrDiffSection} from './gr-diff-section';
-import {html, render} from 'lit';
-import {diffClasses} from '../../diff/gr-diff/gr-diff-utils';
-import {when} from 'lit/directives/when.js';
-import {GrDiffBuilderImage} from './gr-diff-builder-image';
-import {GrDiffBuilderBinary} from './gr-diff-builder-binary';
-
-export interface DiffContextExpandedEventDetail
- extends DiffContextExpandedExternalDetail {
- /** The context control group that should be replaced by `groups`. */
- contextGroup: GrDiffGroup;
- groups: GrDiffGroup[];
-}
-
-declare global {
- interface HTMLElementEventMap {
- 'diff-context-expanded-internal': CustomEvent<DiffContextExpandedEventDetail>;
- 'diff-context-expanded': CustomEvent<DiffContextExpandedExternalDetail>;
- 'content-load-needed': CustomEvent<ContentLoadNeededEventDetail>;
- }
-}
-
-export function isImageDiffBuilder<T extends GrDiffBuilder>(
- x: T | GrDiffBuilderImage | undefined
-): x is GrDiffBuilderImage {
- return !!x && !!(x as GrDiffBuilderImage).renderImageDiff;
-}
-
-export function isBinaryDiffBuilder<T extends GrDiffBuilder>(
- x: T | GrDiffBuilderBinary | undefined
-): x is GrDiffBuilderBinary {
- return !!x && !!(x as GrDiffBuilderBinary).renderBinaryDiff;
-}
-
-/**
- * The builder takes GrDiffGroups, and builds the corresponding DOM elements,
- * called sections. Only the builder should add or remove sections from the
- * DOM. Callers can use the ...group() methods to modify groups and thus cause
- * rendering changes.
- */
-export class GrDiffBuilder {
- private readonly diff: DiffInfo;
-
- readonly prefs: DiffPreferencesInfo;
-
- renderPrefs?: RenderPreferences;
-
- readonly outputEl: HTMLElement;
-
- private groups: GrDiffGroup[];
-
- private readonly layerUpdateListener: (
- start: LineNumber,
- end: LineNumber,
- side: Side
- ) => void;
-
- constructor(
- diff: DiffInfo,
- prefs: DiffPreferencesInfo,
- outputEl: HTMLElement,
- readonly layers: DiffLayer[] = [],
- renderPrefs?: RenderPreferences
- ) {
- this.diff = diff;
- this.prefs = prefs;
- this.renderPrefs = renderPrefs;
- this.outputEl = outputEl;
- this.groups = [];
-
- if (isNaN(prefs.tab_size) || prefs.tab_size <= 0) {
- throw Error('Invalid tab size from preferences.');
- }
-
- if (isNaN(prefs.line_length) || prefs.line_length <= 0) {
- throw Error('Invalid line length from preferences.');
- }
-
- this.layerUpdateListener = (
- start: LineNumber,
- end: LineNumber,
- side: Side
- ) => this.renderContentByRange(start, end, side);
- this.init();
- }
-
- getContentTdByLine(
- lineNumber: LineNumber,
- side?: Side
- ): HTMLTableCellElement | undefined {
- if (!side) return undefined;
- const row = this.findRow(lineNumber, side);
- return row?.getContentCell(side);
- }
-
- getLineElByNumber(
- lineNumber: LineNumber,
- side?: Side
- ): HTMLTableCellElement | undefined {
- if (!side) return undefined;
- const row = this.findRow(lineNumber, side);
- return row?.getLineNumberCell(side);
- }
-
- private findRow(lineNumber?: LineNumber, side?: Side): GrDiffRow | undefined {
- if (!side || !lineNumber) return undefined;
- const group = this.findGroup(side, lineNumber);
- if (!group) return undefined;
- const section = this.findSection(group);
- if (!section) return undefined;
- return section.findRow(side, lineNumber);
- }
-
- private getDiffRows() {
- const sections = [
- ...this.outputEl.querySelectorAll<GrDiffSection>('gr-diff-section'),
- ];
- return sections.map(s => s.getDiffRows()).flat();
- }
-
- getLineNumberRows(): HTMLTableRowElement[] {
- const rows = this.getDiffRows();
- return rows.map(r => r.getTableRow()).filter(isDefined);
- }
-
- getLineNumEls(side: Side): HTMLTableCellElement[] {
- const rows = this.getDiffRows();
- return rows.map(r => r.getLineNumberCell(side)).filter(isDefined);
- }
-
- /** This is used when layers initiate an update. */
- renderContentByRange(start: LineNumber, end: LineNumber, side: Side) {
- const groups = this.getGroupsByLineRange(start, end, side);
- for (const group of groups) {
- const section = this.findSection(group);
- for (const row of section?.getDiffRows() ?? []) {
- row.requestUpdate();
- }
- }
- }
-
- private findSection(group: GrDiffGroup): GrDiffSection | undefined {
- const leftClass = `left-${group.startLine(Side.LEFT)}`;
- const rightClass = `right-${group.startLine(Side.RIGHT)}`;
- return (
- this.outputEl.querySelector<GrDiffSection>(
- `gr-diff-section.${leftClass}.${rightClass}`
- ) ?? undefined
- );
- }
-
- buildSectionElement(group: GrDiffGroup): HTMLElement {
- const leftCl = `left-${group.startLine(Side.LEFT)}`;
- const rightCl = `right-${group.startLine(Side.RIGHT)}`;
- const section = html`
- <gr-diff-section
- class="${leftCl} ${rightCl}"
- .group=${group}
- .diff=${this.diff}
- .layers=${this.layers}
- .diffPrefs=${this.prefs}
- .renderPrefs=${this.renderPrefs}
- ></gr-diff-section>
- `;
- // When using Lit's `render()` method it wants to be in full control of the
- // element that it renders into, so we let it render into a temp element.
- // Rendering into the diff table directly would interfere with
- // `clearDiffContent()`for example.
- // TODO: Convert <gr-diff> to be fully lit controlled and incorporate this
- // method into Lit's `render()` cycle.
- const tempEl = document.createElement('div');
- render(section, tempEl);
- const sectionEl = tempEl.firstElementChild as GrDiffSection;
- return sectionEl;
- }
-
- addColumns(outputEl: HTMLElement, lineNumberWidth: number): void {
- const colgroup = html`
- <colgroup>
- <col class=${diffClasses('blame')}></col>
- ${when(
- this.renderPrefs?.view_mode === DiffViewMode.UNIFIED,
- () => html` ${this.renderUnifiedColumns(lineNumberWidth)} `,
- () => html`
- ${this.renderSideBySideColumns(Side.LEFT, lineNumberWidth)}
- ${this.renderSideBySideColumns(Side.RIGHT, lineNumberWidth)}
- `
- )}
- </colgroup>
- `;
- // When using Lit's `render()` method it wants to be in full control of the
- // element that it renders into, so we let it render into a temp element.
- // Rendering into the diff table directly would interfere with
- // `clearDiffContent()`for example.
- // TODO: Convert <gr-diff> to be fully lit controlled and incorporate this
- // method into Lit's `render()` cycle.
- const tempEl = document.createElement('div');
- render(colgroup, tempEl);
- const colgroupEl = tempEl.firstElementChild as HTMLElement;
- outputEl.appendChild(colgroupEl);
- }
-
- private renderUnifiedColumns(lineNumberWidth: number) {
- return html`
- <col class=${diffClasses()} width=${lineNumberWidth}></col>
- <col class=${diffClasses()} width=${lineNumberWidth}></col>
- <col class=${diffClasses()}></col>
- `;
- }
-
- private renderSideBySideColumns(side: Side, lineNumberWidth: number) {
- return html`
- <col class=${diffClasses(side)} width=${lineNumberWidth}></col>
- <col class=${diffClasses(side, 'sign')}></col>
- <col class=${diffClasses(side)}></col>
- `;
- }
-
- /**
- * This is meant to be called when the gr-diff component re-connects, or when
- * the diff is (re-)rendered.
- *
- * Make sure that this method is symmetric with cleanup(), which is called
- * when gr-diff disconnects.
- */
- init() {
- this.cleanup();
- for (const layer of this.layers) {
- if (layer.addListener) {
- layer.addListener(this.layerUpdateListener);
- }
- }
- }
-
- /**
- * This is meant to be called when the gr-diff component disconnects, or when
- * the diff is (re-)rendered.
- *
- * Make sure that this method is symmetric with init(), which is called when
- * gr-diff re-connects.
- */
- cleanup() {
- for (const layer of this.layers) {
- if (layer.removeListener) {
- layer.removeListener(this.layerUpdateListener);
- }
- }
- }
-
- addGroups(groups: readonly GrDiffGroup[]) {
- for (const group of groups) {
- this.groups.push(group);
- this.emitGroup(group);
- }
- }
-
- clearGroups() {
- for (const deletedGroup of this.groups) {
- deletedGroup.element?.remove();
- }
- this.groups = [];
- }
-
- replaceGroup(contextControl: GrDiffGroup, groups: readonly GrDiffGroup[]) {
- const i = this.groups.indexOf(contextControl);
- if (i === -1) throw new Error('cannot find context control group');
-
- const contextControlSection = this.groups[i].element;
- if (!contextControlSection) throw new Error('diff group element not set');
-
- this.groups.splice(i, 1, ...groups);
- for (const group of groups) {
- this.emitGroup(group, contextControlSection);
- }
- if (contextControlSection) contextControlSection.remove();
- }
-
- findGroup(side: Side, line: LineNumber) {
- return this.groups.find(group => group.containsLine(side, line));
- }
-
- private emitGroup(group: GrDiffGroup, beforeSection?: HTMLElement) {
- const element = this.buildSectionElement(group);
- this.outputEl.insertBefore(element, beforeSection ?? null);
- group.element = element;
- }
-
- // visible for testing
- getGroupsByLineRange(
- startLine: LineNumber,
- endLine: LineNumber,
- side: Side
- ): GrDiffGroup[] {
- const startIndex = this.groups.findIndex(group =>
- group.containsLine(side, startLine)
- );
- if (startIndex === -1) return [];
- let endIndex = this.groups.findIndex(group =>
- group.containsLine(side, endLine)
- );
- // Not all groups may have been processed yet (i.e. this.groups is still
- // incomplete). In that case let's just return *all* groups until the end
- // of the array.
- if (endIndex === -1) endIndex = this.groups.length - 1;
- // The filter preserves the legacy behavior to only return non-context
- // groups
- return this.groups
- .slice(startIndex, endIndex + 1)
- .filter(group => group.lines.length > 0);
- }
-
- /**
- * Set the blame information for the diff. For any already-rendered line,
- * re-render its blame cell content.
- */
- setBlame(blame: BlameInfo[]) {
- for (const blameInfo of blame) {
- for (const range of blameInfo.ranges) {
- for (let line = range.start; line <= range.end; line++) {
- const row = this.findRow(line, Side.LEFT);
- if (row) row.blameInfo = blameInfo;
- }
- }
- }
- }
-
- /**
- * Only special builders need to implement this. The default is to
- * just ignore it.
- */
- updateRenderPrefs(_: RenderPreferences) {}
-}
diff --git a/polygerrit-ui/app/embed/diff-old/gr-diff-builder/gr-diff-row.ts b/polygerrit-ui/app/embed/diff-old/gr-diff-builder/gr-diff-row.ts
deleted file mode 100644
index 1486154..0000000
--- a/polygerrit-ui/app/embed/diff-old/gr-diff-builder/gr-diff-row.ts
+++ /dev/null
@@ -1,487 +0,0 @@
-/**
- * @license
- * Copyright 2022 Google LLC
- * SPDX-License-Identifier: Apache-2.0
- */
-import {html, LitElement, nothing, TemplateResult} from 'lit';
-import {property, state} from 'lit/decorators.js';
-import {ifDefined} from 'lit/directives/if-defined.js';
-import {createRef, Ref, ref} from 'lit/directives/ref.js';
-import {
- DiffResponsiveMode,
- Side,
- LineNumber,
- DiffLayer,
- GrDiffLineType,
- LOST,
- FILE,
-} from '../../../api/diff';
-import {BlameInfo} from '../../../types/common';
-import {assertIsDefined} from '../../../utils/common-util';
-import {fire} from '../../../utils/event-util';
-import {getBaseUrl} from '../../../utils/url-util';
-import './gr-diff-text';
-import {GrDiffLine} from '../gr-diff/gr-diff-line';
-import {
- diffClasses,
- isNewDiff,
- isResponsive,
-} from '../../diff/gr-diff/gr-diff-utils';
-
-export class GrDiffRow extends LitElement {
- contentLeftRef: Ref<LitElement> = createRef();
-
- contentRightRef: Ref<LitElement> = createRef();
-
- contentCellLeftRef: Ref<HTMLTableCellElement> = createRef();
-
- contentCellRightRef: Ref<HTMLTableCellElement> = createRef();
-
- lineNumberLeftRef: Ref<HTMLTableCellElement> = createRef();
-
- lineNumberRightRef: Ref<HTMLTableCellElement> = createRef();
-
- blameCellRef: Ref<HTMLTableCellElement> = createRef();
-
- tableRowRef: Ref<HTMLTableRowElement> = createRef();
-
- @property({type: Object})
- left?: GrDiffLine;
-
- @property({type: Object})
- right?: GrDiffLine;
-
- @property({type: Object})
- blameInfo?: BlameInfo;
-
- @property({type: Object})
- responsiveMode?: DiffResponsiveMode;
-
- /**
- * true: side-by-side diff
- * false: unified diff
- */
- @property({type: Boolean})
- unifiedDiff = false;
-
- @property({type: Number})
- tabSize = 2;
-
- @property({type: Number})
- lineLength = 80;
-
- @property({type: Boolean})
- hideFileCommentButton = false;
-
- @property({type: Object})
- layers: DiffLayer[] = [];
-
- /**
- * Semantic DOM diff testing does not work with just table fragments, so when
- * running such tests the render() method has to wrap the DOM in a proper
- * <table> element.
- */
- @state()
- addTableWrapperForTesting = false;
-
- /**
- * Keeps track of whether diff layers have already been applied to the diff
- * row. That happens after the DOM has been created in the `updated()`
- * lifecycle callback.
- *
- * Once layers are applied, the diff row requires two rendering passes for an
- * update: 1. Remove all <gr-diff-text> elements and their layer manipulated
- * DOMs. 2. Add fresh <gr-diff-text> elements and let layers re-apply in
- * `updated()`.
- */
- private layersApplied = false;
-
- /**
- * The browser API for handling selection does not (yet) work for selection
- * across multiple shadow DOM elements. So we are rendering gr-diff components
- * into the light DOM instead of the shadow DOM by overriding this method,
- * which was the recommended workaround by the lit team.
- * See also https://github.com/WICG/webcomponents/issues/79.
- */
- override createRenderRoot() {
- return this;
- }
-
- override updated() {
- if (this.layersApplied) {
- // <gr-diff-text> elements have been removed during rendering. Let's start
- // another rendering cycle with freshly created <gr-diff-text> elements.
- this.updateComplete.then(() => {
- this.layersApplied = false;
- this.requestUpdate();
- });
- } else {
- this.updateLayers(Side.LEFT);
- this.updateLayers(Side.RIGHT);
- }
- }
-
- /**
- * The diff layers API is designed to let layers manipulate the DOM. So we
- * have to apply them after the rendering cycle is done (`updated()`). But
- * when re-rendering a row that already has layers applied, then we have to
- * first wipe away <gr-diff-text>. This is achieved by
- * `this.layersApplied = true`.
- */
- private async updateLayers(side: Side) {
- const line = this.line(side);
- const contentEl = this.contentRef(side).value;
- const lineNumberEl = this.lineNumberRef(side).value;
- if (!line || !contentEl || !lineNumberEl) return;
-
- // We have to wait for the <gr-diff-text> child component to finish
- // rendering before we can apply layers, which will re-write the HTML.
- await contentEl?.updateComplete;
- for (const layer of this.layers) {
- if (typeof layer.annotate === 'function') {
- layer.annotate(contentEl, lineNumberEl, line, side);
- }
- }
- // At this point we consider layers applied. So as soon as <gr-diff-row>
- // enters a new rendering cycle <gr-diff-text> elements will be removed.
- this.layersApplied = true;
- }
-
- override render() {
- if (!this.left || !this.right) return;
- const classes = this.unifiedDiff ? ['unified'] : ['side-by-side'];
- const unifiedType = this.unifiedType();
- if (this.unifiedDiff && unifiedType) classes.push(unifiedType);
- const row = html`
- <tr
- ${ref(this.tableRowRef)}
- class=${diffClasses('diff-row', ...classes)}
- left-type=${ifDefined(this.getType(Side.LEFT))}
- right-type=${ifDefined(this.getType(Side.RIGHT))}
- tabindex="-1"
- aria-labelledby=${this.ariaLabelIds()}
- >
- ${this.renderBlameCell()} ${this.renderLineNumberCell(Side.LEFT)}
- ${this.renderSignCell(Side.LEFT)} ${this.renderContentCell(Side.LEFT)}
- ${this.renderLineNumberCell(Side.RIGHT)}
- ${this.renderSignCell(Side.RIGHT)} ${this.renderContentCell(Side.RIGHT)}
- </tr>
- ${this.renderPostLineSlot(Side.LEFT)}
- ${this.renderPostLineSlot(Side.RIGHT)}
- `;
- if (this.addTableWrapperForTesting) {
- return html`<table>
- ${row}
- </table>`;
- }
- return row;
- }
-
- private ariaLabelIds() {
- const ids: string[] = [];
- ids.push(this.lineNumberId(Side.LEFT));
- if (!this.unifiedDiff) ids.push(this.contentId(Side.LEFT));
- ids.push(this.lineNumberId(Side.RIGHT));
- if (!this.unifiedDiff) ids.push(this.contentId(Side.RIGHT));
- if (this.unifiedDiff) ids.push(this.contentId(this.unifiedSide()));
- return ids.filter(id => !!id).join(' ');
- }
-
- private lineNumberId(side: Side): string {
- const lineNumber = this.lineNumber(side);
- if (!lineNumber) return '';
- return `${side}-button-${lineNumber}`;
- }
-
- private unifiedSide() {
- const isLeft = this.line(Side.RIGHT)?.type === GrDiffLineType.BLANK;
- return isLeft ? Side.LEFT : Side.RIGHT;
- }
-
- private contentId(side: Side): string {
- const lineNumber = this.lineNumber(side);
- if (!lineNumber) return '';
- return `${side}-content-${lineNumber}`;
- }
-
- getTableRow(): HTMLTableRowElement | undefined {
- return this.tableRowRef.value;
- }
-
- getLineNumberCell(side: Side): HTMLTableCellElement | undefined {
- return this.lineNumberRef(side).value;
- }
-
- getContentCell(side: Side) {
- return this.contentCellRef(side)?.value;
- }
-
- getBlameCell() {
- return this.blameCellRef.value;
- }
-
- private renderBlameCell() {
- // td.blame has `white-space: pre`, so prettier must not add spaces.
- // prettier-ignore
- return html`
- <td
- ${ref(this.blameCellRef)}
- class=${diffClasses('blame')}
- data-line-number=${this.left?.beforeNumber ?? 0}
- >${this.renderBlameElement()}</td>
- `;
- }
-
- private renderBlameElement() {
- const lineNum = this.left?.beforeNumber;
- const commit = this.blameInfo;
- if (!lineNum || !commit) return;
-
- const isStartOfRange = commit.ranges.some(r => r.start === lineNum);
- const extras: string[] = [];
- if (isStartOfRange) extras.push('startOfRange');
- const date = new Date(commit.time * 1000).toLocaleDateString();
- const shortName = commit.author.split(' ')[0];
- const url = `${getBaseUrl()}/q/${commit.id}`;
-
- // td.blame has `white-space: pre`, so prettier must not add spaces.
- // prettier-ignore
- return html`<span class=${diffClasses(...extras)}
- ><a href=${url} class=${diffClasses('blameDate')}>${date}</a
- ><span class=${diffClasses('blameAuthor')}> ${shortName}</span
- ><gr-hovercard class=${diffClasses()}>
- <span class=${diffClasses('blameHoverCard')}>
- Commit ${commit.id}<br />
- Author: ${commit.author}<br />
- Date: ${date}<br />
- <br />
- ${commit.commit_msg}
- </span>
- </gr-hovercard
- ></span>`;
- }
-
- private renderLineNumberCell(side: Side): TemplateResult {
- const line = this.line(side);
- const lineNumber = this.lineNumber(side);
- const isBlank = line?.type === GrDiffLineType.BLANK;
- if (!line || !lineNumber || isBlank || this.layersApplied) {
- const blankClass = isBlank && !this.unifiedDiff ? 'blankLineNum' : '';
- return html`<td
- ${ref(this.lineNumberRef(side))}
- class=${diffClasses(side, blankClass)}
- ></td>`;
- }
-
- return html`<td
- ${ref(this.lineNumberRef(side))}
- class=${diffClasses(side, 'lineNum')}
- data-value=${lineNumber}
- >
- ${this.renderLineNumberButton(line, lineNumber, side)}
- </td>`;
- }
-
- private renderLineNumberButton(
- line: GrDiffLine,
- lineNumber: LineNumber,
- side: Side
- ) {
- if (this.hideFileCommentButton && lineNumber === FILE) return;
- if (lineNumber === LOST) return;
- // .lineNumButton has `white-space: pre`, so prettier must not add spaces.
- // prettier-ignore
- return html`
- <button
- id=${this.lineNumberId(side)}
- class=${diffClasses('lineNumButton', side)}
- tabindex="-1"
- data-value=${lineNumber}
- aria-label=${ifDefined(
- this.computeLineNumberAriaLabel(line, lineNumber)
- )}
- @mouseenter=${() =>
- fire(this, 'line-mouse-enter', {lineNum: lineNumber, side})}
- @mouseleave=${() =>
- fire(this, 'line-mouse-leave', {lineNum: lineNumber, side})}
- >${lineNumber === FILE ? 'File' : lineNumber.toString()}</button>
- `;
- }
-
- private computeLineNumberAriaLabel(line: GrDiffLine, lineNumber: LineNumber) {
- if (lineNumber === FILE) return 'Add file comment';
-
- // Add aria-labels for valid line numbers.
- // For unified diff, this method will be called with number set to 0 for
- // the empty line number column for added/removed lines. This should not
- // be announced to the screenreader.
- if (
- lineNumber === LOST ||
- (typeof lineNumber === 'number' && lineNumber <= 0)
- )
- return undefined;
- switch (line.type) {
- case GrDiffLineType.REMOVE:
- return `${lineNumber} removed`;
- case GrDiffLineType.ADD:
- return `${lineNumber} added`;
- case GrDiffLineType.BOTH:
- case GrDiffLineType.BLANK:
- return `${lineNumber} unmodified`;
- }
- }
-
- private renderContentCell(side: Side) {
- let line = this.line(side);
- if (this.unifiedDiff) {
- if (side === Side.LEFT) return nothing;
- if (line?.type === GrDiffLineType.BLANK) {
- side = Side.LEFT;
- line = this.line(Side.LEFT);
- }
- }
- const lineNumber = this.lineNumber(side);
- assertIsDefined(line, 'line');
- const extras: string[] = [line.type, side];
- if (line.type !== GrDiffLineType.BLANK) extras.push('content');
- if (!line.hasIntralineInfo) extras.push('no-intraline-info');
- if (line.beforeNumber === FILE) extras.push('file');
- if (line.beforeNumber === LOST) extras.push('lost');
-
- // .content has `white-space: pre`, so prettier must not add spaces.
- // prettier-ignore
- return html`
- <td
- ${ref(this.contentCellRef(side))}
- class=${diffClasses(...extras)}
- @mouseenter=${() => {
- if (lineNumber)
- fire(this, 'line-mouse-enter', {lineNum: lineNumber, side});
- }}
- @mouseleave=${() => {
- if (lineNumber)
- fire(this, 'line-mouse-leave', {lineNum: lineNumber, side});
- }}
- >${this.renderText(side)}${this.renderThreadGroup(side)}</td>
- `;
- }
-
- private renderSignCell(side: Side) {
- if (this.unifiedDiff) return nothing;
- const line = this.line(side);
- assertIsDefined(line, 'line');
- const isBlank = line.type === GrDiffLineType.BLANK;
- const isAdd = line.type === GrDiffLineType.ADD && side === Side.RIGHT;
- const isRemove = line.type === GrDiffLineType.REMOVE && side === Side.LEFT;
- const extras: string[] = ['sign', side];
- if (isBlank) extras.push('blank');
- if (isAdd) extras.push('add');
- if (isRemove) extras.push('remove');
- if (!line.hasIntralineInfo) extras.push('no-intraline-info');
-
- const sign = isAdd ? '+' : isRemove ? '-' : '';
- return html`<td class=${diffClasses(...extras)}>${sign}</td>`;
- }
-
- private renderThreadGroup(side: Side) {
- const lineNumber = this.lineNumber(side);
- if (!lineNumber) return nothing;
- return html`<div class="thread-group" data-side=${side}>
- <slot name="${side}-${lineNumber}"></slot>
- ${this.renderSecondSlot()}
- </div>`;
- }
-
- private renderSecondSlot() {
- if (!this.unifiedDiff) return nothing;
- if (this.line(Side.LEFT)?.type !== GrDiffLineType.BOTH) return nothing;
- return html`<slot
- name="${Side.LEFT}-${this.lineNumber(Side.LEFT)}"
- ></slot>`;
- }
-
- private contentRef(side: Side) {
- return side === Side.LEFT ? this.contentLeftRef : this.contentRightRef;
- }
-
- private contentCellRef(side: Side) {
- return side === Side.LEFT
- ? this.contentCellLeftRef
- : this.contentCellRightRef;
- }
-
- private lineNumberRef(side: Side) {
- return side === Side.LEFT
- ? this.lineNumberLeftRef
- : this.lineNumberRightRef;
- }
-
- private lineNumber(side: Side) {
- return this.line(side)?.lineNumber(side);
- }
-
- private line(side: Side) {
- return side === Side.LEFT ? this.left : this.right;
- }
-
- private getType(side?: Side): string | undefined {
- if (this.unifiedDiff) return undefined;
- if (side === Side.LEFT) return this.left?.type;
- if (side === Side.RIGHT) return this.right?.type;
- return undefined;
- }
-
- private unifiedType() {
- return this.left?.type === GrDiffLineType.BLANK
- ? this.right?.type
- : this.left?.type;
- }
-
- /**
- * Returns a 'div' element containing the supplied |text| as its innerText,
- * with '\t' characters expanded to a width determined by |tabSize|, and the
- * text wrapped at column |lineLimit|, which may be Infinity if no wrapping is
- * desired.
- */
- private renderText(side: Side) {
- const line = this.line(side);
- const lineNumber = this.lineNumber(side);
- if (typeof lineNumber !== 'number') return;
-
- // Note that `this.layersApplied` will wipe away the <gr-diff-text>, and
- // another rendering cycle will be initiated in `updated()`.
- // prettier-ignore
- const textElement = line?.text && !this.layersApplied
- ? html`<gr-diff-text
- ${ref(this.contentRef(side))}
- .text=${line?.text}
- .tabSize=${this.tabSize}
- .lineLimit=${this.lineLength}
- .isResponsive=${isResponsive(this.responsiveMode)}
- ></gr-diff-text>` : '';
- // .content has `white-space: pre`, so prettier must not add spaces.
- // prettier-ignore
- return html`<div
- class=${diffClasses('contentText')}
- data-side=${ifDefined(side)}
- id=${this.contentId(side)}
- >${textElement}</div>`;
- }
-
- private renderPostLineSlot(side: Side) {
- const lineNumber = this.lineNumber(side);
- return lineNumber && Number.isInteger(lineNumber)
- ? html`<slot name="post-${side}-line-${lineNumber}"></slot>`
- : nothing;
- }
-}
-
-if (!isNewDiff()) {
- customElements.define('gr-diff-row', GrDiffRow);
-}
-
-declare global {
- interface HTMLElementTagNameMap {
- 'gr-diff-row': LitElement;
- }
-}
diff --git a/polygerrit-ui/app/embed/diff-old/gr-diff-builder/gr-diff-row_test.ts b/polygerrit-ui/app/embed/diff-old/gr-diff-builder/gr-diff-row_test.ts
deleted file mode 100644
index 42d30aa..0000000
--- a/polygerrit-ui/app/embed/diff-old/gr-diff-builder/gr-diff-row_test.ts
+++ /dev/null
@@ -1,271 +0,0 @@
-/**
- * @license
- * Copyright 2022 Google LLC
- * SPDX-License-Identifier: Apache-2.0
- */
-import '../../../test/common-test-setup';
-import './gr-diff-row';
-import {GrDiffRow} from './gr-diff-row';
-import {fixture, html, assert} from '@open-wc/testing';
-import {GrDiffLine} from '../gr-diff/gr-diff-line';
-import {GrDiffLineType} from '../../../api/diff';
-
-suite('gr-diff-row test', () => {
- let element: GrDiffRow;
-
- setup(async () => {
- element = await fixture<GrDiffRow>(html`<gr-diff-row></gr-diff-row>`);
- element.addTableWrapperForTesting = true;
- await element.updateComplete;
- });
-
- test('both', async () => {
- const line = new GrDiffLine(GrDiffLineType.BOTH, 1, 1);
- line.text = 'lorem ipsum';
- element.left = line;
- element.right = line;
- await element.updateComplete;
- assert.lightDom.equal(
- element,
- /* HTML */ `
- <table>
- <tbody>
- <tr
- aria-labelledby="left-button-1 left-content-1 right-button-1 right-content-1"
- class="diff-row gr-diff side-by-side"
- left-type="both"
- right-type="both"
- tabindex="-1"
- >
- <td class="blame gr-diff" data-line-number="1"></td>
- <td class="gr-diff left lineNum" data-value="1">
- <button
- aria-label="1 unmodified"
- class="gr-diff left lineNumButton"
- data-value="1"
- id="left-button-1"
- tabindex="-1"
- >
- 1
- </button>
- </td>
- <td class="gr-diff left no-intraline-info sign"></td>
- <td class="both content gr-diff left no-intraline-info">
- <div
- class="contentText gr-diff"
- data-side="left"
- id="left-content-1"
- >
- <gr-diff-text> lorem ipsum </gr-diff-text>
- </div>
- <div class="thread-group" data-side="left">
- <slot name="left-1"> </slot>
- </div>
- </td>
- <td class="gr-diff lineNum right" data-value="1">
- <button
- aria-label="1 unmodified"
- class="gr-diff lineNumButton right"
- data-value="1"
- id="right-button-1"
- tabindex="-1"
- >
- 1
- </button>
- </td>
- <td class="gr-diff no-intraline-info right sign"></td>
- <td class="both content gr-diff no-intraline-info right">
- <div
- class="contentText gr-diff"
- data-side="right"
- id="right-content-1"
- >
- <gr-diff-text> lorem ipsum </gr-diff-text>
- </div>
- <div class="thread-group" data-side="right">
- <slot name="right-1"> </slot>
- </div>
- </td>
- </tr>
- <slot name="post-left-line-1"></slot>
- <slot name="post-right-line-1"></slot>
- </tbody>
- </table>
- `
- );
- });
-
- test('both unified', async () => {
- const line = new GrDiffLine(GrDiffLineType.BOTH, 1, 1);
- line.text = 'lorem ipsum';
- element.left = line;
- element.right = line;
- element.unifiedDiff = true;
- await element.updateComplete;
- assert.lightDom.equal(
- element,
- /* HTML */ `
- <table>
- <tbody>
- <tr
- aria-labelledby="left-button-1 right-button-1 right-content-1"
- class="both diff-row gr-diff unified"
- tabindex="-1"
- >
- <td class="blame gr-diff" data-line-number="1"></td>
- <td class="gr-diff left lineNum" data-value="1">
- <button
- aria-label="1 unmodified"
- class="gr-diff left lineNumButton"
- data-value="1"
- id="left-button-1"
- tabindex="-1"
- >
- 1
- </button>
- </td>
- <td class="gr-diff lineNum right" data-value="1">
- <button
- aria-label="1 unmodified"
- class="gr-diff lineNumButton right"
- data-value="1"
- id="right-button-1"
- tabindex="-1"
- >
- 1
- </button>
- </td>
- <td class="both content gr-diff no-intraline-info right">
- <div
- class="contentText gr-diff"
- data-side="right"
- id="right-content-1"
- >
- <gr-diff-text> lorem ipsum </gr-diff-text>
- </div>
- <div class="thread-group" data-side="right">
- <slot name="right-1"> </slot>
- <slot name="left-1"> </slot>
- </div>
- </td>
- </tr>
- <slot name="post-left-line-1"></slot>
- <slot name="post-right-line-1"></slot>
- </tbody>
- </table>
- `
- );
- });
-
- test('add', async () => {
- const line = new GrDiffLine(GrDiffLineType.ADD, 0, 1);
- line.text = 'lorem ipsum';
- element.left = new GrDiffLine(GrDiffLineType.BLANK);
- element.right = line;
- await element.updateComplete;
- assert.lightDom.equal(
- element,
- /* HTML */ `
- <table>
- <tbody>
- <tr
- aria-labelledby="right-button-1 right-content-1"
- class="diff-row gr-diff side-by-side"
- left-type="blank"
- right-type="add"
- tabindex="-1"
- >
- <td class="blame gr-diff" data-line-number="0"></td>
- <td class="blankLineNum gr-diff left"></td>
- <td class="blank gr-diff left no-intraline-info sign"></td>
- <td class="blank gr-diff left no-intraline-info">
- <div class="contentText gr-diff" data-side="left"></div>
- </td>
- <td class="gr-diff lineNum right" data-value="1">
- <button
- aria-label="1 added"
- class="gr-diff lineNumButton right"
- data-value="1"
- id="right-button-1"
- tabindex="-1"
- >
- 1
- </button>
- </td>
- <td class="add gr-diff no-intraline-info right sign">+</td>
- <td class="add content gr-diff no-intraline-info right">
- <div
- class="contentText gr-diff"
- data-side="right"
- id="right-content-1"
- >
- <gr-diff-text> lorem ipsum </gr-diff-text>
- </div>
- <div class="thread-group" data-side="right">
- <slot name="right-1"> </slot>
- </div>
- </td>
- <slot name="post-right-line-1"></slot>
- </tr>
- </tbody>
- </table>
- `
- );
- });
-
- test('remove', async () => {
- const line = new GrDiffLine(GrDiffLineType.REMOVE, 1, 0);
- line.text = 'lorem ipsum';
- element.left = line;
- element.right = new GrDiffLine(GrDiffLineType.BLANK);
- await element.updateComplete;
- assert.lightDom.equal(
- element,
- /* HTML */ `
- <table>
- <tbody>
- <tr
- aria-labelledby="left-button-1 left-content-1"
- class="diff-row gr-diff side-by-side"
- left-type="remove"
- right-type="blank"
- tabindex="-1"
- >
- <td class="blame gr-diff" data-line-number="1"></td>
- <td class="gr-diff left lineNum" data-value="1">
- <button
- aria-label="1 removed"
- class="gr-diff left lineNumButton"
- data-value="1"
- id="left-button-1"
- tabindex="-1"
- >
- 1
- </button>
- </td>
- <td class="gr-diff left no-intraline-info remove sign">-</td>
- <td class="content gr-diff left no-intraline-info remove">
- <div
- class="contentText gr-diff"
- data-side="left"
- id="left-content-1"
- >
- <gr-diff-text> lorem ipsum </gr-diff-text>
- </div>
- <div class="thread-group" data-side="left">
- <slot name="left-1"> </slot>
- </div>
- </td>
- <td class="blankLineNum gr-diff right"></td>
- <td class="blank gr-diff no-intraline-info right sign"></td>
- <td class="blank gr-diff no-intraline-info right">
- <div class="contentText gr-diff" data-side="right"></div>
- </td>
- </tr>
- <slot name="post-left-line-1"></slot>
- </tbody>
- </table>
- `
- );
- });
-});
diff --git a/polygerrit-ui/app/embed/diff-old/gr-diff-builder/gr-diff-section.ts b/polygerrit-ui/app/embed/diff-old/gr-diff-builder/gr-diff-section.ts
deleted file mode 100644
index 28919e8..0000000
--- a/polygerrit-ui/app/embed/diff-old/gr-diff-builder/gr-diff-section.ts
+++ /dev/null
@@ -1,254 +0,0 @@
-/**
- * @license
- * Copyright 2022 Google LLC
- * SPDX-License-Identifier: Apache-2.0
- */
-import {html, LitElement} from 'lit';
-import {property, state} from 'lit/decorators.js';
-import {
- DiffInfo,
- DiffLayer,
- DiffViewMode,
- RenderPreferences,
- Side,
- LineNumber,
- DiffPreferencesInfo,
-} from '../../../api/diff';
-import {GrDiffGroup, GrDiffGroupType} from '../gr-diff/gr-diff-group';
-import {
- diffClasses,
- getResponsiveMode,
- isNewDiff,
-} from '../../diff/gr-diff/gr-diff-utils';
-import {GrDiffRow} from './gr-diff-row';
-import '../gr-context-controls/gr-context-controls-section';
-import '../gr-context-controls/gr-context-controls';
-import '../../diff/gr-range-header/gr-range-header';
-import './gr-diff-row';
-import {when} from 'lit/directives/when.js';
-import {fire} from '../../../utils/event-util';
-import {countLines} from '../../../utils/diff-util';
-
-export class GrDiffSection extends LitElement {
- @property({type: Object})
- group?: GrDiffGroup;
-
- @property({type: Object})
- diff?: DiffInfo;
-
- @property({type: Object})
- renderPrefs?: RenderPreferences;
-
- @property({type: Object})
- diffPrefs?: DiffPreferencesInfo;
-
- @property({type: Object})
- layers: DiffLayer[] = [];
-
- /**
- * Semantic DOM diff testing does not work with just table fragments, so when
- * running such tests the render() method has to wrap the DOM in a proper
- * <table> element.
- */
- @state()
- addTableWrapperForTesting = false;
-
- /**
- * The browser API for handling selection does not (yet) work for selection
- * across multiple shadow DOM elements. So we are rendering gr-diff components
- * into the light DOM instead of the shadow DOM by overriding this method,
- * which was the recommended workaround by the lit team.
- * See also https://github.com/WICG/webcomponents/issues/79.
- */
- override createRenderRoot() {
- return this;
- }
-
- override render() {
- if (!this.group) return;
- const extras: string[] = [];
- extras.push('section');
- extras.push(this.group.type);
- if (this.group.isTotal()) extras.push('total');
- if (this.group.dueToRebase) extras.push('dueToRebase');
- if (this.group.moveDetails) extras.push('dueToMove');
- if (this.group.moveDetails?.changed) extras.push('changed');
- if (this.group.ignoredWhitespaceOnly) extras.push('ignoredWhitespaceOnly');
-
- const pairs = this.getLinePairs();
- const responsiveMode = getResponsiveMode(this.diffPrefs, this.renderPrefs);
- const hideFileCommentButton =
- this.diffPrefs?.show_file_comment_button === false ||
- this.renderPrefs?.show_file_comment_button === false;
- const body = html`
- <tbody class=${diffClasses(...extras)}>
- ${this.renderContextControls()} ${this.renderMoveControls()}
- ${pairs.map(pair => {
- const leftCl = `left-${pair.left.lineNumber(Side.LEFT)}`;
- const rightCl = `right-${pair.right.lineNumber(Side.RIGHT)}`;
- return html`
- <gr-diff-row
- class="${leftCl} ${rightCl}"
- .left=${pair.left}
- .right=${pair.right}
- .layers=${this.layers}
- .lineLength=${this.diffPrefs?.line_length ?? 80}
- .tabSize=${this.diffPrefs?.tab_size ?? 2}
- .unifiedDiff=${this.isUnifiedDiff()}
- .responsiveMode=${responsiveMode}
- .hideFileCommentButton=${hideFileCommentButton}
- >
- </gr-diff-row>
- `;
- })}
- </tbody>
- `;
- if (this.addTableWrapperForTesting) {
- return html`<table>
- ${body}
- </table>`;
- }
- return body;
- }
-
- private isUnifiedDiff() {
- return this.renderPrefs?.view_mode === DiffViewMode.UNIFIED;
- }
-
- getLinePairs() {
- if (!this.group) return [];
- const isControl = this.group.type === GrDiffGroupType.CONTEXT_CONTROL;
- if (isControl) return [];
- return this.isUnifiedDiff()
- ? this.group.getUnifiedPairs()
- : this.group.getSideBySidePairs();
- }
-
- getDiffRows(): GrDiffRow[] {
- return [...this.querySelectorAll<GrDiffRow>('gr-diff-row')];
- }
-
- private renderContextControls() {
- if (this.group?.type !== GrDiffGroupType.CONTEXT_CONTROL) return;
-
- const leftStart = this.group.lineRange.left.start_line;
- const leftEnd = this.group.lineRange.left.end_line;
- const firstGroupIsSkipped = !!this.group.contextGroups[0].skip;
- const lastGroupIsSkipped =
- !!this.group.contextGroups[this.group.contextGroups.length - 1].skip;
- const lineCountLeft = countLines(this.diff, Side.LEFT);
- const containsWholeFile = lineCountLeft === leftEnd - leftStart + 1;
- const showAbove =
- (leftStart > 1 && !firstGroupIsSkipped) || containsWholeFile;
- const showBelow = leftEnd < lineCountLeft && !lastGroupIsSkipped;
-
- return html`
- <gr-context-controls-section
- .showAbove=${showAbove}
- .showBelow=${showBelow}
- .group=${this.group}
- .diff=${this.diff}
- .renderPrefs=${this.renderPrefs}
- >
- </gr-context-controls-section>
- `;
- }
-
- findRow(side: Side, lineNumber: LineNumber): GrDiffRow | undefined {
- return (
- this.querySelector<GrDiffRow>(`gr-diff-row.${side}-${lineNumber}`) ??
- undefined
- );
- }
-
- private renderMoveControls() {
- if (!this.group?.moveDetails) return;
- const movedIn = this.group.adds.length > 0;
- const plainCell = html`<td class=${diffClasses()}></td>`;
- const signCell = html`<td class=${diffClasses('sign')}></td>`;
- const lineNumberCell = html`
- <td class=${diffClasses('moveControlsLineNumCol')}></td>
- `;
- const moveCell = html`
- <td class=${diffClasses('moveHeader')}>
- <gr-range-header class=${diffClasses()} icon="move_item">
- ${this.renderMoveDescription(movedIn)}
- </gr-range-header>
- </td>
- `;
- return html`
- <tr
- class=${diffClasses('moveControls', movedIn ? 'movedIn' : 'movedOut')}
- >
- ${when(
- this.isUnifiedDiff(),
- () => html`${lineNumberCell} ${lineNumberCell} ${moveCell}`,
- () => html`${lineNumberCell} ${signCell}
- ${movedIn ? plainCell : moveCell} ${lineNumberCell} ${signCell}
- ${movedIn ? moveCell : plainCell}`
- )}
- </tr>
- `;
- }
-
- private renderMoveDescription(movedIn: boolean) {
- if (this.group?.moveDetails?.range) {
- const {changed, range} = this.group.moveDetails;
- const otherSide = movedIn ? Side.LEFT : Side.RIGHT;
- const andChangedLabel = changed ? 'and changed ' : '';
- const direction = movedIn ? 'from' : 'to';
- const textLabel = `Moved ${andChangedLabel}${direction} lines `;
- return html`
- <div class=${diffClasses()}>
- <span class=${diffClasses()}>${textLabel}</span>
- ${this.renderMovedLineAnchor(range.start, otherSide)}
- <span class=${diffClasses()}> - </span>
- ${this.renderMovedLineAnchor(range.end, otherSide)}
- </div>
- `;
- }
-
- return html`
- <div class=${diffClasses()}>
- <span class=${diffClasses()}
- >${movedIn ? 'Moved in' : 'Moved out'}</span
- >
- </div>
- `;
- }
-
- private renderMovedLineAnchor(line: number, side: Side) {
- const listener = (e: MouseEvent) => {
- e.preventDefault();
- this.handleMovedLineAnchorClick(e.target, side, line);
- };
- // `href` is not actually used but important for Screen Readers
- return html`
- <a class=${diffClasses()} href=${`#${line}`} @click=${listener}
- >${line}</a
- >
- `;
- }
-
- private handleMovedLineAnchorClick(
- anchor: EventTarget | null,
- side: Side,
- line: number
- ) {
- if (!anchor) return;
- fire(anchor, 'moved-link-clicked', {
- lineNum: line,
- side,
- });
- }
-}
-
-if (!isNewDiff()) {
- customElements.define('gr-diff-section', GrDiffSection);
-}
-
-declare global {
- interface HTMLElementTagNameMap {
- 'gr-diff-section': LitElement;
- }
-}
diff --git a/polygerrit-ui/app/embed/diff-old/gr-diff-builder/gr-diff-section_test.ts b/polygerrit-ui/app/embed/diff-old/gr-diff-builder/gr-diff-section_test.ts
deleted file mode 100644
index 381f9b2..0000000
--- a/polygerrit-ui/app/embed/diff-old/gr-diff-builder/gr-diff-section_test.ts
+++ /dev/null
@@ -1,315 +0,0 @@
-/**
- * @license
- * Copyright 2022 Google LLC
- * SPDX-License-Identifier: Apache-2.0
- */
-import '../../../test/common-test-setup';
-import './gr-diff-section';
-import {GrDiffSection} from './gr-diff-section';
-import {fixture, html, assert} from '@open-wc/testing';
-import {GrDiffGroup, GrDiffGroupType} from '../gr-diff/gr-diff-group';
-import {GrDiffLine} from '../gr-diff/gr-diff-line';
-import {DiffViewMode, GrDiffLineType} from '../../../api/diff';
-import {waitQueryAndAssert} from '../../../test/test-utils';
-
-suite('gr-diff-section test', () => {
- let element: GrDiffSection;
-
- setup(async () => {
- element = await fixture<GrDiffSection>(
- html`<gr-diff-section></gr-diff-section>`
- );
- element.addTableWrapperForTesting = true;
- await element.updateComplete;
- });
-
- suite('move controls', async () => {
- setup(async () => {
- const lines = [new GrDiffLine(GrDiffLineType.BOTH, 1, 1)];
- lines[0].text = 'asdf';
- const group = new GrDiffGroup({
- type: GrDiffGroupType.BOTH,
- lines,
- moveDetails: {changed: false, range: {start: 1, end: 2}},
- });
- element.group = group;
- await element.updateComplete;
- });
-
- test('side-by-side', async () => {
- const row = await waitQueryAndAssert(element, 'tr.moveControls');
- // Semantic dom diff has a problem with just comparing table rows or
- // cells directly. So as a workaround put the row into an empty test
- // table.
- const testTable = document.createElement('table');
- testTable.appendChild(row);
- assert.dom.equal(
- testTable,
- /* HTML */ `
- <table>
- <tbody>
- <tr class="gr-diff moveControls movedOut">
- <td class="gr-diff moveControlsLineNumCol"></td>
- <td class="gr-diff sign"></td>
- <td class="gr-diff moveHeader">
- <gr-range-header class="gr-diff" icon="move_item">
- <div class="gr-diff">
- <span class="gr-diff"> Moved to lines </span>
- <a class="gr-diff" href="#1"> 1 </a>
- <span class="gr-diff"> - </span>
- <a class="gr-diff" href="#2"> 2 </a>
- </div>
- </gr-range-header>
- </td>
- <td class="gr-diff moveControlsLineNumCol"></td>
- <td class="gr-diff sign"></td>
- <td class="gr-diff"></td>
- </tr>
- </tbody>
- </table>
- `,
- {}
- );
- });
-
- test('unified', async () => {
- element.renderPrefs = {
- ...element.renderPrefs,
- view_mode: DiffViewMode.UNIFIED,
- };
- const row = await waitQueryAndAssert(element, 'tr.moveControls');
- // Semantic dom diff has a problem with just comparing table rows or
- // cells directly. So as a workaround put the row into an empty test
- // table.
- const testTable = document.createElement('table');
- testTable.appendChild(row);
- assert.dom.equal(
- testTable,
- /* HTML */ `
- <table>
- <tbody>
- <tr class="gr-diff moveControls movedOut">
- <td class="gr-diff moveControlsLineNumCol"></td>
- <td class="gr-diff moveControlsLineNumCol"></td>
- <td class="gr-diff moveHeader">
- <gr-range-header class="gr-diff" icon="move_item">
- <div class="gr-diff">
- <span class="gr-diff"> Moved to lines </span>
- <a class="gr-diff" href="#1"> 1 </a>
- <span class="gr-diff"> - </span>
- <a class="gr-diff" href="#2"> 2 </a>
- </div>
- </gr-range-header>
- </td>
- </tr>
- </tbody>
- </table>
- `,
- {}
- );
- });
- });
-
- test('3 normal unchanged rows', async () => {
- const lines = [
- new GrDiffLine(GrDiffLineType.BOTH, 1, 1),
- new GrDiffLine(GrDiffLineType.BOTH, 1, 1),
- new GrDiffLine(GrDiffLineType.BOTH, 1, 1),
- ];
- lines[0].text = 'asdf';
- lines[1].text = 'qwer';
- lines[2].text = 'zxcv';
- const group = new GrDiffGroup({type: GrDiffGroupType.BOTH, lines});
- element.group = group;
- await element.updateComplete;
- assert.lightDom.equal(
- element,
- /* HTML */ `
- <gr-diff-row class="left-1 right-1"> </gr-diff-row>
- <slot name="post-left-line-1"></slot>
- <slot name="post-right-line-1"></slot>
- <gr-diff-row class="left-1 right-1"> </gr-diff-row>
- <slot name="post-left-line-1"></slot>
- <slot name="post-right-line-1"></slot>
- <gr-diff-row class="left-1 right-1"> </gr-diff-row>
- <slot name="post-left-line-1"></slot>
- <slot name="post-right-line-1"></slot>
- <table>
- <tbody class="both gr-diff section">
- <tr
- aria-labelledby="left-button-1 left-content-1 right-button-1 right-content-1"
- class="diff-row gr-diff side-by-side"
- left-type="both"
- right-type="both"
- tabindex="-1"
- >
- <td class="blame gr-diff" data-line-number="1"></td>
- <td class="gr-diff left lineNum" data-value="1">
- <button
- aria-label="1 unmodified"
- class="gr-diff left lineNumButton"
- data-value="1"
- id="left-button-1"
- tabindex="-1"
- >
- 1
- </button>
- </td>
- <td class="gr-diff left no-intraline-info sign"></td>
- <td class="both content gr-diff left no-intraline-info">
- <div
- class="contentText gr-diff"
- data-side="left"
- id="left-content-1"
- >
- <gr-diff-text> </gr-diff-text>
- </div>
- <div class="thread-group" data-side="left">
- <slot name="left-1"> </slot>
- </div>
- </td>
- <td class="gr-diff lineNum right" data-value="1">
- <button
- aria-label="1 unmodified"
- class="gr-diff lineNumButton right"
- data-value="1"
- id="right-button-1"
- tabindex="-1"
- >
- 1
- </button>
- </td>
- <td class="gr-diff no-intraline-info right sign"></td>
- <td class="both content gr-diff no-intraline-info right">
- <div
- class="contentText gr-diff"
- data-side="right"
- id="right-content-1"
- >
- <gr-diff-text> </gr-diff-text>
- </div>
- <div class="thread-group" data-side="right">
- <slot name="right-1"> </slot>
- </div>
- </td>
- </tr>
- <tr
- aria-labelledby="left-button-1 left-content-1 right-button-1 right-content-1"
- class="diff-row gr-diff side-by-side"
- left-type="both"
- right-type="both"
- tabindex="-1"
- >
- <td class="blame gr-diff" data-line-number="1"></td>
- <td class="gr-diff left lineNum" data-value="1">
- <button
- aria-label="1 unmodified"
- class="gr-diff left lineNumButton"
- data-value="1"
- id="left-button-1"
- tabindex="-1"
- >
- 1
- </button>
- </td>
- <td class="gr-diff left no-intraline-info sign"></td>
- <td class="both content gr-diff left no-intraline-info">
- <div
- class="contentText gr-diff"
- data-side="left"
- id="left-content-1"
- >
- <gr-diff-text> </gr-diff-text>
- </div>
- <div class="thread-group" data-side="left">
- <slot name="left-1"> </slot>
- </div>
- </td>
- <td class="gr-diff lineNum right" data-value="1">
- <button
- aria-label="1 unmodified"
- class="gr-diff lineNumButton right"
- data-value="1"
- id="right-button-1"
- tabindex="-1"
- >
- 1
- </button>
- </td>
- <td class="gr-diff no-intraline-info right sign"></td>
- <td class="both content gr-diff no-intraline-info right">
- <div
- class="contentText gr-diff"
- data-side="right"
- id="right-content-1"
- >
- <gr-diff-text> </gr-diff-text>
- </div>
- <div class="thread-group" data-side="right">
- <slot name="right-1"> </slot>
- </div>
- </td>
- </tr>
- <tr
- aria-labelledby="left-button-1 left-content-1 right-button-1 right-content-1"
- class="diff-row gr-diff side-by-side"
- left-type="both"
- right-type="both"
- tabindex="-1"
- >
- <td class="blame gr-diff" data-line-number="1"></td>
- <td class="gr-diff left lineNum" data-value="1">
- <button
- aria-label="1 unmodified"
- class="gr-diff left lineNumButton"
- data-value="1"
- id="left-button-1"
- tabindex="-1"
- >
- 1
- </button>
- </td>
- <td class="gr-diff left no-intraline-info sign"></td>
- <td class="both content gr-diff left no-intraline-info">
- <div
- class="contentText gr-diff"
- data-side="left"
- id="left-content-1"
- >
- <gr-diff-text> </gr-diff-text>
- </div>
- <div class="thread-group" data-side="left">
- <slot name="left-1"> </slot>
- </div>
- </td>
- <td class="gr-diff lineNum right" data-value="1">
- <button
- aria-label="1 unmodified"
- class="gr-diff lineNumButton right"
- data-value="1"
- id="right-button-1"
- tabindex="-1"
- >
- 1
- </button>
- </td>
- <td class="gr-diff no-intraline-info right sign"></td>
- <td class="both content gr-diff no-intraline-info right">
- <div
- class="contentText gr-diff"
- data-side="right"
- id="right-content-1"
- >
- <gr-diff-text> </gr-diff-text>
- </div>
- <div class="thread-group" data-side="right">
- <slot name="right-1"> </slot>
- </div>
- </td>
- </tr>
- </tbody>
- </table>
- `
- );
- });
-});
diff --git a/polygerrit-ui/app/embed/diff-old/gr-diff-builder/gr-diff-text.ts b/polygerrit-ui/app/embed/diff-old/gr-diff-builder/gr-diff-text.ts
deleted file mode 100644
index 3878402..0000000
--- a/polygerrit-ui/app/embed/diff-old/gr-diff-builder/gr-diff-text.ts
+++ /dev/null
@@ -1,155 +0,0 @@
-/**
- * @license
- * Copyright 2022 Google LLC
- * SPDX-License-Identifier: Apache-2.0
- */
-import {LitElement, html, TemplateResult} from 'lit';
-import {property} from 'lit/decorators.js';
-import {styleMap} from 'lit/directives/style-map.js';
-import {diffClasses, isNewDiff} from '../../diff/gr-diff/gr-diff-utils';
-
-const SURROGATE_PAIR = /[\uD800-\uDBFF][\uDC00-\uDFFF]/;
-
-const TAB = '\t';
-
-/**
- * Renders one line of code on one side of the diff. It takes care of:
- * - Tabs, see `tabSize` property.
- * - Line Breaks, see `lineLimit` property.
- * - Surrogate Character Pairs.
- *
- * Note that other modifications to the code in a gr-diff is done via diff
- * layers, which manipulate the DOM directly. So `gr-diff-text` is thrown
- * away and re-rendered every time something changes by its parent
- * `gr-diff-row`. So don't bother to optimize this component for re-rendering
- * performance. And be aware that building longer lived local state is not
- * useful here.
- */
-export class GrDiffText extends LitElement {
- /**
- * The browser API for handling selection does not (yet) work for selection
- * across multiple shadow DOM elements. So we are rendering gr-diff components
- * into the light DOM instead of the shadow DOM by overriding this method,
- * which was the recommended workaround by the lit team.
- * See also https://github.com/WICG/webcomponents/issues/79.
- */
- override createRenderRoot() {
- return this;
- }
-
- @property({type: String})
- text = '';
-
- @property({type: Boolean})
- isResponsive = false;
-
- @property({type: Number})
- tabSize = 2;
-
- @property({type: Number})
- lineLimit = 80;
-
- /** Temporary state while rendering. */
- private textOffset = 0;
-
- /** Temporary state while rendering. */
- private columnPos = 0;
-
- /** Temporary state while rendering. */
- private pieces: (string | TemplateResult)[] = [];
-
- /** Split up the string into tabs, surrogate pairs and regular segments. */
- override render() {
- this.textOffset = 0;
- this.columnPos = 0;
- this.pieces = [];
- const splitByTab = this.text.split('\t');
- for (let i = 0; i < splitByTab.length; i++) {
- const splitBySurrogate = splitByTab[i].split(SURROGATE_PAIR);
- for (let j = 0; j < splitBySurrogate.length; j++) {
- this.renderSegment(splitBySurrogate[j]);
- if (j < splitBySurrogate.length - 1) {
- this.renderSurrogatePair();
- }
- }
- if (i < splitByTab.length - 1) {
- this.renderTab();
- }
- }
- if (this.textOffset !== this.text.length) throw new Error('unfinished');
- return this.pieces;
- }
-
- /** Render regular characters, but insert line breaks appropriately. */
- private renderSegment(segment: string) {
- let segmentOffset = 0;
- while (segmentOffset < segment.length) {
- const newOffset = Math.min(
- segment.length,
- segmentOffset + this.lineLimit - this.columnPos
- );
- this.renderString(segment.substring(segmentOffset, newOffset));
- segmentOffset = newOffset;
- if (segmentOffset < segment.length && this.columnPos === this.lineLimit) {
- this.renderLineBreak();
- }
- }
- }
-
- /** Render regular characters. */
- private renderString(s: string) {
- if (s.length === 0) return;
- this.pieces.push(s);
- this.textOffset += s.length;
- this.columnPos += s.length;
- if (this.columnPos > this.lineLimit) throw new Error('over line limit');
- }
-
- /** Render a tab character. */
- private renderTab() {
- let tabSize = this.tabSize - (this.columnPos % this.tabSize);
- if (this.columnPos + tabSize > this.lineLimit) {
- this.renderLineBreak();
- tabSize = this.tabSize;
- }
- const piece = html`<span
- class=${diffClasses('tab')}
- style=${styleMap({'tab-size': `${tabSize}`})}
- >${TAB}</span
- >`;
- this.pieces.push(piece);
- this.textOffset += 1;
- this.columnPos += tabSize;
- }
-
- /** Render a surrogate pair: string length is 2, but is just 1 char. */
- private renderSurrogatePair() {
- if (this.columnPos === this.lineLimit) {
- this.renderLineBreak();
- }
- this.pieces.push(this.text.substring(this.textOffset, this.textOffset + 2));
- this.textOffset += 2;
- this.columnPos += 1;
- }
-
- /** Render a line break, don't advance text offset, reset col position. */
- private renderLineBreak() {
- if (this.isResponsive) {
- this.pieces.push(html`<wbr class=${diffClasses()}></wbr>`);
- } else {
- this.pieces.push(html`<span class=${diffClasses('br')}></span>`);
- }
- // this.textOffset += 0;
- this.columnPos = 0;
- }
-}
-
-if (!isNewDiff()) {
- customElements.define('gr-diff-text', GrDiffText);
-}
-
-declare global {
- interface HTMLElementTagNameMap {
- 'gr-diff-text': LitElement;
- }
-}
diff --git a/polygerrit-ui/app/embed/diff-old/gr-diff-builder/gr-diff-text_test.ts b/polygerrit-ui/app/embed/diff-old/gr-diff-builder/gr-diff-text_test.ts
deleted file mode 100644
index 3858bed..0000000
--- a/polygerrit-ui/app/embed/diff-old/gr-diff-builder/gr-diff-text_test.ts
+++ /dev/null
@@ -1,166 +0,0 @@
-/**
- * @license
- * Copyright 2022 Google LLC
- * SPDX-License-Identifier: Apache-2.0
- */
-import '../../../test/common-test-setup';
-import './gr-diff-text';
-import {GrDiffText} from './gr-diff-text';
-import {fixture, html, assert} from '@open-wc/testing';
-
-const LINE_BREAK = '<span class="gr-diff br"></span>';
-
-const LINE_BREAK_WBR = '<wbr class="gr-diff"></wbr>';
-
-const TAB = '<span class="" style=""></span>';
-
-const TAB_IGNORE = ['class', 'style'];
-
-suite('gr-diff-text test', () => {
- let element: GrDiffText;
-
- setup(async () => {
- element = await fixture<GrDiffText>(
- html`<gr-diff-text tabsize="4" linelimit="10"></gr-diff-text>`
- );
- });
-
- const check = async (
- text: string,
- html: string,
- ignoreAttributes: string[] = []
- ) => {
- element.text = text;
- await element.updateComplete;
- assert.lightDom.equal(element, html, {ignoreAttributes});
- };
-
- suite('lit rendering', () => {
- test('renderText newlines 1', async () => {
- await check('abcdef', 'abcdef');
- await check('a'.repeat(20), `aaaaaaaaaa${LINE_BREAK}aaaaaaaaaa`);
- });
-
- test('renderText newlines 1 responsive', async () => {
- element.isResponsive = true;
- await check('abcdef', 'abcdef');
- await check('a'.repeat(20), `aaaaaaaaaa${LINE_BREAK_WBR}aaaaaaaaaa`);
- });
-
- test('renderText newlines 2', async () => {
- await check(
- '<span class="thumbsup">👍</span>',
- '<span clas' +
- LINE_BREAK +
- 's="thumbsu' +
- LINE_BREAK +
- 'p">👍</span' +
- LINE_BREAK +
- '>'
- );
- });
-
- test('renderText newlines 3', async () => {
- await check(
- '01234\t56789',
- '01234' + TAB + '56' + LINE_BREAK + '789',
- TAB_IGNORE
- );
- });
-
- test('renderText newlines 4', async () => {
- element.lineLimit = 20;
- await element.updateComplete;
- await check(
- '👍'.repeat(58),
- '👍'.repeat(20) +
- LINE_BREAK +
- '👍'.repeat(20) +
- LINE_BREAK +
- '👍'.repeat(18)
- );
- });
-
- test('tab wrapper style', async () => {
- element.lineLimit = 100;
- element.tabSize = 4;
- await check(
- '\t',
- /* HTML */ '<span class="gr-diff tab" style="tab-size:4;"></span>'
- );
- await check(
- 'abc\t',
- /* HTML */ 'abc<span class="gr-diff tab" style="tab-size:1;"></span>'
- );
-
- element.tabSize = 8;
- await check(
- '\t',
- /* HTML */ '<span class="gr-diff tab" style="tab-size:8;"></span>'
- );
- await check(
- 'abc\t',
- /* HTML */ 'abc<span class="gr-diff tab" style="tab-size:5;"></span>'
- );
- });
-
- test('tab wrapper insertion', async () => {
- await check('abc\tdef', 'abc' + TAB + 'def', TAB_IGNORE);
- });
-
- test('escaping HTML', async () => {
- element.lineLimit = 100;
- await element.updateComplete;
- await check(
- '<script>alert("XSS");<' + '/script>',
- '<script>alert("XSS");</script>'
- );
- await check('& < > " \' / `', '& < > " \' / `');
- });
-
- test('text length with tabs and unicode', async () => {
- async function expectTextLength(
- text: string,
- tabSize: number,
- expected: number
- ) {
- element.text = text;
- element.tabSize = tabSize;
- element.lineLimit = expected;
- await element.updateComplete;
- const result = element.innerHTML;
-
- // Must not contain a line break.
- assert.isNotOk(element.querySelector('span.br'));
-
- // Increasing the line limit by 1 should not change anything.
- element.lineLimit = expected + 1;
- await element.updateComplete;
- const resultPlusOne = element.innerHTML;
- assert.equal(resultPlusOne, result);
-
- // Increasing the line limit to infinity should not change anything.
- element.lineLimit = Infinity;
- await element.updateComplete;
- const resultInf = element.innerHTML;
- assert.equal(resultInf, result);
-
- // Decreasing the line limit by 1 should introduce a line break.
- element.lineLimit = expected + 1;
- await element.updateComplete;
- assert.isNotOk(element.querySelector('span.br'));
- }
- expectTextLength('12345', 4, 5);
- expectTextLength('\t\t12', 4, 10);
- expectTextLength('abc💢123', 4, 7);
- expectTextLength('abc\t', 8, 8);
- expectTextLength('abc\t\t', 10, 20);
- expectTextLength('', 10, 0);
- // 17 Thai combining chars.
- expectTextLength('ก้้้้้้้้้้้้้้้้', 4, 17);
- expectTextLength('abc\tde', 10, 12);
- expectTextLength('abc\tde\t', 10, 20);
- expectTextLength('\t\t\t\t\t', 20, 100);
- });
- });
-});
diff --git a/polygerrit-ui/app/embed/diff-old/gr-diff-cursor/gr-diff-cursor.ts b/polygerrit-ui/app/embed/diff-old/gr-diff-cursor/gr-diff-cursor.ts
deleted file mode 100644
index 3d0e507..0000000
--- a/polygerrit-ui/app/embed/diff-old/gr-diff-cursor/gr-diff-cursor.ts
+++ /dev/null
@@ -1,597 +0,0 @@
-/**
- * @license
- * Copyright 2016 Google LLC
- * SPDX-License-Identifier: Apache-2.0
- */
-import {Subscription} from 'rxjs';
-import {AbortStop, CursorMoveResult, Stop} from '../../../api/core';
-import {
- DiffViewMode,
- GrDiffCursor as GrDiffCursorApi,
- GrDiffLineType,
- LineNumber,
- LineSelectedEventDetail,
-} from '../../../api/diff';
-import {ScrollMode, Side} from '../../../constants/constants';
-import {toggleClass} from '../../../utils/dom-util';
-import {
- GrCursorManager,
- isTargetable,
-} from '../../../elements/shared/gr-cursor-manager/gr-cursor-manager';
-import {GrDiffGroupType} from '../gr-diff/gr-diff-group';
-import {GrDiff} from '../gr-diff/gr-diff';
-import {fire} from '../../../utils/event-util';
-
-type GrDiffRowType = GrDiffLineType | GrDiffGroupType;
-
-const LEFT_SIDE_CLASS = 'target-side-left';
-const RIGHT_SIDE_CLASS = 'target-side-right';
-
-interface Address {
- leftSide: boolean;
- number: number;
-}
-
-/**
- * From <tr> diff row go up to <tbody> diff chunk.
- *
- * In Lit based diff there is a <gr-diff-row> element in between the two.
- */
-export function fromRowToChunk(
- rowEl: HTMLElement
-): HTMLTableSectionElement | undefined {
- const parent = rowEl.parentElement;
- if (!parent) return undefined;
- if (parent.tagName === 'TBODY') {
- return parent as HTMLTableSectionElement;
- }
-
- const grandParent = parent.parentElement;
- if (!grandParent) return undefined;
- if (grandParent.tagName === 'TBODY') {
- return grandParent as HTMLTableSectionElement;
- }
-
- return undefined;
-}
-
-/** A subset of the GrDiff API that the cursor is using. */
-export interface GrDiffCursorable extends HTMLElement {
- isRangeSelected(): boolean;
- createRangeComment(): void;
- getCursorStops(): Stop[];
- path?: string;
-}
-
-export class GrDiffCursor implements GrDiffCursorApi {
- private preventAutoScrollOnManualScroll = false;
-
- set side(side: Side) {
- if (this.sideInternal === side) {
- return;
- }
- if (this.sideInternal && this.diffRow) {
- this.fireCursorMoved(
- 'line-cursor-moved-out',
- this.diffRow,
- this.sideInternal
- );
- }
- this.sideInternal = side;
- this.updateSideClass();
- if (this.diffRow) {
- this.fireCursorMoved('line-cursor-moved-in', this.diffRow, this.side);
- }
- }
-
- get side(): Side {
- return this.sideInternal;
- }
-
- private sideInternal = Side.RIGHT;
-
- set diffRow(diffRow: HTMLElement | undefined) {
- if (this.diffRowInternal) {
- this.diffRowInternal.classList.remove(LEFT_SIDE_CLASS, RIGHT_SIDE_CLASS);
- this.fireCursorMoved(
- 'line-cursor-moved-out',
- this.diffRowInternal,
- this.side
- );
- }
- this.diffRowInternal = diffRow;
-
- this.updateSideClass();
- if (this.diffRow) {
- this.fireCursorMoved('line-cursor-moved-in', this.diffRow, this.side);
- }
- }
-
- get diffRow(): HTMLElement | undefined {
- return this.diffRowInternal;
- }
-
- private diffRowInternal?: HTMLElement;
-
- private diffs: GrDiffCursorable[] = [];
-
- /**
- * If set, the cursor will attempt to move to the line number (instead of
- * the first chunk) the next time the diff renders. It is set back to null
- * when used. It should be only used if you want the line to be focused
- * after initialization of the component and page should scroll
- * to that position. This parameter should be set at most for one gr-diff
- * element in the page.
- */
- initialLineNumber: number | null = null;
-
- // visible for testing
- cursorManager = new GrCursorManager();
-
- private targetSubscription?: Subscription;
-
- constructor() {
- this.cursorManager.cursorTargetClass = 'target-row';
- this.cursorManager.scrollMode = ScrollMode.KEEP_VISIBLE;
- this.cursorManager.focusOnMove = true;
-
- window.addEventListener('scroll', this._boundHandleWindowScroll);
- this.targetSubscription = this.cursorManager.target$.subscribe(target => {
- this.diffRow = target || undefined;
- });
- }
-
- dispose() {
- this.cursorManager.unsetCursor();
- if (this.targetSubscription) this.targetSubscription.unsubscribe();
- window.removeEventListener('scroll', this._boundHandleWindowScroll);
- }
-
- // Don't remove - used by clients embedding gr-diff outside of Gerrit.
- isAtStart() {
- return this.cursorManager.isAtStart();
- }
-
- // Don't remove - used by clients embedding gr-diff outside of Gerrit.
- isAtEnd() {
- return this.cursorManager.isAtEnd();
- }
-
- moveLeft() {
- this.side = Side.LEFT;
- if (this._isTargetBlank()) {
- this.moveUp();
- }
- }
-
- moveRight() {
- this.side = Side.RIGHT;
- if (this._isTargetBlank()) {
- this.moveUp();
- }
- }
-
- moveDown() {
- if (this._getViewMode() === DiffViewMode.SIDE_BY_SIDE) {
- return this.cursorManager.next({
- filter: (row: Element) => this._rowHasSide(row),
- });
- } else {
- return this.cursorManager.next();
- }
- }
-
- moveUp() {
- if (this._getViewMode() === DiffViewMode.SIDE_BY_SIDE) {
- return this.cursorManager.previous({
- filter: (row: Element) => this._rowHasSide(row),
- });
- } else {
- return this.cursorManager.previous();
- }
- }
-
- moveToVisibleArea() {
- if (this._getViewMode() === DiffViewMode.SIDE_BY_SIDE) {
- this.cursorManager.moveToVisibleArea((row: Element) =>
- this._rowHasSide(row)
- );
- } else {
- this.cursorManager.moveToVisibleArea();
- }
- }
-
- moveToNextChunk(clipToTop?: boolean): CursorMoveResult {
- const result = this.cursorManager.next({
- filter: (row: HTMLElement) => this._isFirstRowOfChunk(row),
- getTargetHeight: target => fromRowToChunk(target)?.scrollHeight || 0,
- clipToTop,
- });
- this._fixSide();
- return result;
- }
-
- moveToPreviousChunk(): CursorMoveResult {
- const result = this.cursorManager.previous({
- filter: (row: HTMLElement) => this._isFirstRowOfChunk(row),
- });
- this._fixSide();
- return result;
- }
-
- moveToNextCommentThread(): CursorMoveResult {
- if (this.isAtEnd()) {
- return CursorMoveResult.CLIPPED;
- }
- const result = this.cursorManager.next({
- filter: (row: HTMLElement) => this._rowHasThread(row),
- });
- this._fixSide();
- return result;
- }
-
- moveToPreviousCommentThread(): CursorMoveResult {
- const result = this.cursorManager.previous({
- filter: (row: HTMLElement) => this._rowHasThread(row),
- });
- this._fixSide();
- return result;
- }
-
- moveToLineNumber(
- number: LineNumber,
- side: Side,
- path?: string,
- intentionalMove?: boolean
- ) {
- const row = this._findRowByNumberAndFile(number, side, path);
- if (row) {
- this.side = side;
- this.cursorManager.setCursor(row, undefined, intentionalMove);
- }
- }
-
- /**
- * Get the line number element targeted by the cursor row and side.
- */
- getTargetLineElement(): HTMLElement | null {
- let lineElSelector = '.lineNum';
-
- if (!this.diffRow) {
- return null;
- }
-
- if (this._getViewMode() === DiffViewMode.SIDE_BY_SIDE) {
- lineElSelector += this.side === Side.LEFT ? '.left' : '.right';
- }
-
- return this.diffRow.querySelector(lineElSelector);
- }
-
- getTargetDiffElement(): GrDiff | null {
- if (!this.diffRow) return null;
-
- const hostOwner = this.diffRow.getRootNode() as ShadowRoot;
- if (hostOwner?.host?.tagName === 'GR-DIFF') {
- return hostOwner.host as GrDiff;
- }
- return null;
- }
-
- moveToFirstChunk() {
- this.cursorManager.moveToStart();
- if (this.diffRow && !this._isFirstRowOfChunk(this.diffRow)) {
- this.moveToNextChunk(true);
- } else {
- this._fixSide();
- }
- }
-
- moveToLastChunk() {
- this.cursorManager.moveToEnd();
- if (this.diffRow && !this._isFirstRowOfChunk(this.diffRow)) {
- this.moveToPreviousChunk();
- } else {
- this._fixSide();
- }
- }
-
- /**
- * Move the cursor either to initialLineNumber or the first chunk and
- * reset scroll behavior.
- *
- * This may grab the focus from the app.
- *
- * If you do not want to move the cursor or grab focus, and just want to
- * reset the scroll behavior, use reInitAndUpdateStops() instead.
- */
- reInitCursor() {
- this._updateStops();
- if (!this.diffRow) {
- // does not scroll during init unless requested
- this.cursorManager.scrollMode = this.initialLineNumber
- ? ScrollMode.KEEP_VISIBLE
- : ScrollMode.NEVER;
- if (this.initialLineNumber) {
- this.moveToLineNumber(this.initialLineNumber, this.side);
- this.initialLineNumber = null;
- } else {
- this.moveToFirstChunk();
- }
- }
- this.resetScrollMode();
- }
-
- resetScrollMode() {
- this.cursorManager.scrollMode = ScrollMode.KEEP_VISIBLE;
- }
-
- private _boundHandleWindowScroll = () => {
- if (this.preventAutoScrollOnManualScroll) {
- this.cursorManager.scrollMode = ScrollMode.NEVER;
- this.cursorManager.focusOnMove = false;
- this.preventAutoScrollOnManualScroll = false;
- }
- };
-
- reInitAndUpdateStops() {
- this.resetScrollMode();
- this._updateStops();
- }
-
- private boundHandleDiffLoadingChanged = () => {
- this._updateStops();
- };
-
- private _boundHandleDiffRenderStart = () => {
- this.preventAutoScrollOnManualScroll = true;
- };
-
- private _boundHandleDiffRenderContent = () => {
- this._updateStops();
- // When done rendering, turn focus on move and automatic scrolling back on
- this.cursorManager.focusOnMove = true;
- this.preventAutoScrollOnManualScroll = false;
- };
-
- private _boundHandleDiffLineSelected = (
- e: CustomEvent<LineSelectedEventDetail>
- ) => {
- this.moveToLineNumber(e.detail.number, e.detail.side, e.detail.path);
- };
-
- createCommentInPlace() {
- const diffWithRangeSelected = this.diffs.find(diff =>
- diff.isRangeSelected()
- );
- if (diffWithRangeSelected) {
- diffWithRangeSelected.createRangeComment();
- } else {
- const line = this.getTargetLineElement();
- const diff = this.getTargetDiffElement();
- if (diff && line) {
- diff.addDraftAtLine(line);
- }
- }
- }
-
- getTargetLineNumber(): LineNumber | undefined {
- return this.getAddress()?.number;
- }
-
- /**
- * Get an object describing the location of the cursor. Such as
- * {leftSide: false, number: 123} for line 123 of the revision, or
- * {leftSide: true, number: 321} for line 321 of the base patch.
- * Returns null if an address is not available.
- */
- getAddress(): Address | null {
- if (!this.diffRow) {
- return null;
- }
- // Get the line-number cell targeted by the cursor. If the mode is unified
- // then prefer the revision cell if available.
- return this.getAddressFor(this.diffRow, this.side);
- }
-
- private getAddressFor(diffRow: HTMLElement, side: Side): Address | null {
- let cell;
- if (this._getViewMode() === DiffViewMode.UNIFIED) {
- cell = diffRow.querySelector('.lineNum.right');
- if (!cell) {
- cell = diffRow.querySelector('.lineNum.left');
- }
- } else {
- cell = diffRow.querySelector('.lineNum.' + side);
- }
- if (!cell) {
- return null;
- }
-
- const number = cell.getAttribute('data-value');
- if (!number || number === 'FILE') {
- return null;
- }
-
- return {
- leftSide: cell.matches('.left'),
- number: Number(number),
- };
- }
-
- _getViewMode() {
- if (!this.diffRow) {
- return null;
- }
-
- if (this.diffRow.classList.contains('side-by-side')) {
- return DiffViewMode.SIDE_BY_SIDE;
- } else {
- return DiffViewMode.UNIFIED;
- }
- }
-
- _rowHasSide(row: Element) {
- const selector =
- (this.side === Side.LEFT ? '.left' : '.right') + ' + .content';
- return !!row.querySelector(selector);
- }
-
- _isFirstRowOfChunk(row: HTMLElement) {
- const chunk = fromRowToChunk(row);
- if (!chunk) return false;
-
- const isInDeltaChunk = chunk.classList.contains('delta');
- if (!isInDeltaChunk) return false;
-
- const firstRow = chunk.querySelector('tr:not(.moveControls)');
- return firstRow === row;
- }
-
- _rowHasThread(row: HTMLElement): boolean {
- const slots = [
- ...row.querySelectorAll<HTMLSlotElement>('.thread-group > slot'),
- ];
- return slots.some(slot => slot.assignedElements().length > 0);
- }
-
- /**
- * If we jumped to a row where there is no content on the current side then
- * switch to the alternate side.
- */
- _fixSide() {
- if (
- this._getViewMode() === DiffViewMode.SIDE_BY_SIDE &&
- this._isTargetBlank()
- ) {
- this.side = this.side === Side.LEFT ? Side.RIGHT : Side.LEFT;
- }
- }
-
- _isTargetBlank() {
- if (!this.diffRow) {
- return false;
- }
-
- const actions = this._getActionsForRow();
- return (
- (this.side === Side.LEFT && !actions.left) ||
- (this.side === Side.RIGHT && !actions.right)
- );
- }
-
- private fireCursorMoved(
- event: 'line-cursor-moved-out' | 'line-cursor-moved-in',
- row: HTMLElement,
- side: Side
- ) {
- const address = this.getAddressFor(row, side);
- if (address) {
- const {leftSide, number} = address;
- fire(row, event, {
- lineNum: number,
- side: leftSide ? Side.LEFT : Side.RIGHT,
- });
- }
- }
-
- private updateSideClass() {
- if (!this.diffRow) {
- return;
- }
- toggleClass(this.diffRow, LEFT_SIDE_CLASS, this.side === Side.LEFT);
- toggleClass(this.diffRow, RIGHT_SIDE_CLASS, this.side === Side.RIGHT);
- }
-
- _isActionType(type: GrDiffRowType) {
- return (
- type !== GrDiffLineType.BLANK && type !== GrDiffGroupType.CONTEXT_CONTROL
- );
- }
-
- _getActionsForRow() {
- const actions = {left: false, right: false};
- if (this.diffRow) {
- actions.left = this._isActionType(
- this.diffRow.getAttribute('left-type') as GrDiffRowType
- );
- actions.right = this._isActionType(
- this.diffRow.getAttribute('right-type') as GrDiffRowType
- );
- }
- return actions;
- }
-
- _updateStops() {
- this.cursorManager.stops = this.diffs.reduce(
- (stops: Stop[], diff) => stops.concat(diff.getCursorStops()),
- []
- );
- }
-
- replaceDiffs(diffs: GrDiffCursorable[]) {
- for (const diff of this.diffs) {
- this.removeEventListeners(diff);
- }
- this.diffs = [];
- for (const diff of diffs) {
- this.addEventListeners(diff);
- }
- this.diffs.push(...diffs);
- this._updateStops();
- }
-
- unregisterDiff(diff: GrDiffCursorable) {
- // This can happen during destruction - just don't unregister then.
- if (!this.diffs) return;
- const i = this.diffs.indexOf(diff);
- if (i !== -1) {
- this.diffs.splice(i, 1);
- }
- }
-
- private removeEventListeners(diff: GrDiffCursorable) {
- diff.removeEventListener(
- 'loading-changed',
- this.boundHandleDiffLoadingChanged
- );
- diff.removeEventListener('render-start', this._boundHandleDiffRenderStart);
- diff.removeEventListener(
- 'render-content',
- this._boundHandleDiffRenderContent
- );
- diff.removeEventListener(
- 'line-selected',
- this._boundHandleDiffLineSelected
- );
- }
-
- private addEventListeners(diff: GrDiffCursorable) {
- diff.addEventListener(
- 'loading-changed',
- this.boundHandleDiffLoadingChanged
- );
- diff.addEventListener('render-start', this._boundHandleDiffRenderStart);
- diff.addEventListener('render-content', this._boundHandleDiffRenderContent);
- diff.addEventListener('line-selected', this._boundHandleDiffLineSelected);
- }
-
- _findRowByNumberAndFile(
- targetNumber: LineNumber,
- side: Side,
- path?: string
- ): HTMLElement | undefined {
- let stops: Array<HTMLElement | AbortStop>;
- if (path) {
- const diff = this.diffs.filter(diff => diff.path === path)[0];
- stops = diff.getCursorStops();
- } else {
- stops = this.cursorManager.stops;
- }
- // Sadly needed for type narrowing to understand that the result is always
- // targetable.
- const targetableStops: HTMLElement[] = stops.filter(isTargetable);
- const selector = `.lineNum.${side}[data-value="${targetNumber}"]`;
- return targetableStops.find(stop => stop.querySelector(selector));
- }
-}
diff --git a/polygerrit-ui/app/embed/diff-old/gr-diff-cursor/gr-diff-cursor_test.ts b/polygerrit-ui/app/embed/diff-old/gr-diff-cursor/gr-diff-cursor_test.ts
deleted file mode 100644
index 61f8551..0000000
--- a/polygerrit-ui/app/embed/diff-old/gr-diff-cursor/gr-diff-cursor_test.ts
+++ /dev/null
@@ -1,694 +0,0 @@
-/**
- * @license
- * Copyright 2016 Google LLC
- * SPDX-License-Identifier: Apache-2.0
- */
-import '../../../test/common-test-setup';
-import '../gr-diff/gr-diff';
-import './gr-diff-cursor';
-import {fixture, html, assert} from '@open-wc/testing';
-import {
- mockPromise,
- queryAll,
- queryAndAssert,
- waitUntil,
-} from '../../../test/test-utils';
-import {createDiff} from '../../../test/test-data-generators';
-import {createDefaultDiffPrefs} from '../../../constants/constants';
-import {GrDiffCursor} from './gr-diff-cursor';
-import {waitForEventOnce} from '../../../utils/event-util';
-import {DiffInfo, DiffViewMode, Side} from '../../../api/diff';
-import {GrDiff} from '../gr-diff/gr-diff';
-import {assertIsDefined} from '../../../utils/common-util';
-
-suite('gr-diff-cursor tests', () => {
- let cursor: GrDiffCursor;
- let diffElement: GrDiff;
- let diff: DiffInfo;
-
- setup(async () => {
- diffElement = await fixture(html`<gr-diff></gr-diff>`);
- cursor = new GrDiffCursor();
-
- // Register the diff with the cursor.
- cursor.replaceDiffs([diffElement]);
-
- diffElement.loggedIn = false;
- diffElement.path = 'some/path.ts';
- const promise = mockPromise();
- const setupDone = () => {
- cursor._updateStops();
- cursor.moveToFirstChunk();
- diffElement.removeEventListener('render', setupDone);
- promise.resolve();
- };
- diffElement.addEventListener('render', setupDone);
-
- diff = createDiff();
- diffElement.prefs = createDefaultDiffPrefs();
- diffElement.diff = diff;
- await promise;
- });
-
- test('diff cursor functionality (side-by-side)', () => {
- assert.isOk(cursor.diffRow);
-
- const deltaRows = queryAll<HTMLTableRowElement>(
- diffElement,
- '.section.delta tr.diff-row'
- );
- assert.equal(cursor.diffRow, deltaRows[0]);
-
- cursor.moveDown();
-
- assert.notEqual(cursor.diffRow, deltaRows[0]);
- assert.equal(cursor.diffRow, deltaRows[1]);
-
- cursor.moveUp();
-
- assert.notEqual(cursor.diffRow, deltaRows[1]);
- assert.equal(cursor.diffRow, deltaRows[0]);
- });
-
- test('moveToFirstChunk', async () => {
- const diff: DiffInfo = {
- meta_a: {
- name: 'lorem-ipsum.txt',
- content_type: 'text/plain',
- lines: 3,
- },
- meta_b: {
- name: 'lorem-ipsum.txt',
- content_type: 'text/plain',
- lines: 3,
- },
- intraline_status: 'OK',
- change_type: 'MODIFIED',
- diff_header: [
- 'diff --git a/lorem-ipsum.txt b/lorem-ipsum.txt',
- 'index b2adcf4..554ae49 100644',
- '--- a/lorem-ipsum.txt',
- '+++ b/lorem-ipsum.txt',
- ],
- content: [
- {b: ['new line 1']},
- {ab: ['unchanged line']},
- {a: ['old line 2']},
- {ab: ['more unchanged lines']},
- ],
- };
-
- diffElement.diff = diff;
- // The file comment button, if present, is a cursor stop. Ensure
- // moveToFirstChunk() works correctly even if the button is not shown.
- diffElement.prefs!.show_file_comment_button = false;
- await waitForEventOnce(diffElement, 'render');
-
- cursor._updateStops();
-
- const chunks = [
- ...queryAll(diffElement, '.section.delta'),
- ] as HTMLElement[];
- assert.equal(chunks.length, 2);
-
- const rows = [
- ...queryAll(diffElement, '.section.delta tr.diff-row'),
- ] as HTMLTableRowElement[];
- assert.equal(rows.length, 2);
-
- // Verify it works on fresh diff.
- cursor.moveToFirstChunk();
- assert.ok(cursor.diffRow);
- assert.equal(cursor.diffRow, rows[0]);
- assert.equal(cursor.side, Side.RIGHT);
-
- // Verify it works from other cursor positions.
- cursor.moveToNextChunk();
- assert.ok(cursor.diffRow);
- assert.equal(cursor.diffRow, rows[1]);
- assert.equal(cursor.side, Side.LEFT);
-
- cursor.moveToFirstChunk();
- assert.ok(cursor.diffRow);
- assert.equal(cursor.diffRow, rows[0]);
- assert.equal(cursor.side, Side.RIGHT);
- });
-
- test('moveToLastChunk', async () => {
- const diff: DiffInfo = {
- meta_a: {
- name: 'lorem-ipsum.txt',
- content_type: 'text/plain',
- lines: 3,
- },
- meta_b: {
- name: 'lorem-ipsum.txt',
- content_type: 'text/plain',
- lines: 3,
- },
- intraline_status: 'OK',
- change_type: 'MODIFIED',
- diff_header: [
- 'diff --git a/lorem-ipsum.txt b/lorem-ipsum.txt',
- 'index b2adcf4..554ae49 100644',
- '--- a/lorem-ipsum.txt',
- '+++ b/lorem-ipsum.txt',
- ],
- content: [
- {ab: ['unchanged line']},
- {a: ['old line 2']},
- {ab: ['more unchanged lines']},
- {b: ['new line 3']},
- ],
- };
-
- diffElement.diff = diff;
- await waitForEventOnce(diffElement, 'render');
- cursor._updateStops();
-
- const chunks = [
- ...queryAll(diffElement, '.section.delta'),
- ] as HTMLElement[];
- assert.equal(chunks.length, 2);
-
- const rows = [
- ...queryAll(diffElement, '.section.delta tr.diff-row'),
- ] as HTMLTableRowElement[];
- assert.equal(rows.length, 2);
-
- // Verify it works on fresh diff.
- cursor.moveToLastChunk();
- assert.ok(cursor.diffRow);
- assert.equal(cursor.diffRow, rows[1]);
- assert.equal(cursor.side, Side.RIGHT);
-
- // Verify it works from other cursor positions.
- cursor.moveToPreviousChunk();
- assert.ok(cursor.diffRow);
- assert.equal(cursor.diffRow, rows[0]);
- assert.equal(cursor.side, Side.LEFT);
-
- cursor.moveToLastChunk();
- assert.ok(cursor.diffRow);
- assert.equal(cursor.diffRow, rows[1]);
- assert.equal(cursor.side, Side.RIGHT);
- });
-
- test('cursor scroll behavior', () => {
- assert.equal(cursor.cursorManager.scrollMode, 'keep-visible');
-
- diffElement.dispatchEvent(new Event('render-start'));
- assert.isTrue(cursor.cursorManager.focusOnMove);
-
- window.dispatchEvent(new Event('scroll'));
- assert.equal(cursor.cursorManager.scrollMode, 'never');
- assert.isFalse(cursor.cursorManager.focusOnMove);
-
- diffElement.dispatchEvent(new Event('render-content'));
- assert.isTrue(cursor.cursorManager.focusOnMove);
-
- cursor.reInitCursor();
- assert.equal(cursor.cursorManager.scrollMode, 'keep-visible');
- });
-
- test('moves to selected line', () => {
- const moveToNumStub = sinon.stub(cursor, 'moveToLineNumber');
-
- diffElement.dispatchEvent(
- new CustomEvent('line-selected', {
- detail: {number: '123', side: Side.RIGHT, path: 'some/file'},
- })
- );
-
- assert.isTrue(moveToNumStub.called);
- assert.equal(moveToNumStub.lastCall.args[0], 123);
- assert.equal(moveToNumStub.lastCall.args[1], Side.RIGHT);
- assert.equal(moveToNumStub.lastCall.args[2], 'some/file');
- });
-
- suite('unified diff', () => {
- setup(async () => {
- diffElement.viewMode = DiffViewMode.UNIFIED;
- await waitForEventOnce(diffElement, 'render');
- cursor.reInitCursor();
- });
-
- test('diff cursor functionality (unified)', () => {
- assert.isOk(cursor.diffRow);
-
- const rows = [
- ...queryAll(diffElement, '.section.delta tr.diff-row'),
- ] as HTMLTableRowElement[];
- assert.equal(cursor.diffRow, rows[0]);
-
- cursor.moveDown();
-
- assert.notEqual(cursor.diffRow, rows[0]);
- assert.equal(cursor.diffRow, rows[1]);
-
- cursor.moveUp();
-
- assert.notEqual(cursor.diffRow, rows[1]);
- assert.equal(cursor.diffRow, rows[0]);
- });
- });
-
- test('cursor side functionality', () => {
- // The side only applies to side-by-side mode, which should be the default
- // mode.
- assert.equal(diffElement.viewMode, 'SIDE_BY_SIDE');
-
- const rows = [
- ...queryAll(diffElement, '.section tr.diff-row'),
- ] as HTMLTableRowElement[];
- assert.equal(rows.length, 50);
- const deltaRows = [
- ...queryAll(diffElement, '.section.delta tr.diff-row'),
- ] as HTMLTableRowElement[];
- assert.equal(deltaRows.length, 14);
- const indexFirstDelta = rows.indexOf(deltaRows[0]);
- const rowBeforeFirstDelta = rows[indexFirstDelta - 1];
-
- // Because the first delta in this diff is on the right, it should be set
- // to the right side.
- assert.equal(cursor.side, Side.RIGHT);
- assert.equal(cursor.diffRow, deltaRows[0]);
- const firstIndex = cursor.cursorManager.index;
-
- // Move the side to the left. Because this delta only has a right side, we
- // should be moved up to the previous line where there is content on the
- // right. The previous row is part of the previous section.
- cursor.moveLeft();
-
- assert.equal(cursor.side, Side.LEFT);
- assert.notEqual(cursor.diffRow, rows[0]);
- assert.equal(cursor.diffRow, rowBeforeFirstDelta);
- assert.equal(cursor.cursorManager.index, firstIndex - 1);
-
- // If we move down, we should skip everything in the first delta because
- // we are on the left side and the first delta has no content on the left.
- cursor.moveDown();
-
- assert.equal(cursor.side, Side.LEFT);
- assert.notEqual(cursor.diffRow, rowBeforeFirstDelta);
- assert.notEqual(cursor.diffRow, rows[0]);
- assert.isTrue(cursor.cursorManager.index > firstIndex);
- });
-
- test('chunk skip functionality', () => {
- const deltaChunks = [...queryAll(diffElement, 'tbody.section.delta')];
-
- // We should be initialized to the first chunk. Since this chunk only has
- // content on the right side, our side should be right.
- assert.equal(cursor.diffRow, deltaChunks[0].querySelector('tr'));
- assert.equal(cursor.side, Side.RIGHT);
-
- // Move to the next chunk.
- cursor.moveToNextChunk();
-
- // Since this chunk only has content on the left side. we should have been
- // automatically moved over.
- assert.equal(cursor.diffRow, deltaChunks[1].querySelector('tr'));
- assert.equal(cursor.side, Side.LEFT);
- });
-
- suite('moved chunks without line range)', () => {
- setup(async () => {
- const promise = mockPromise();
- const renderHandler = function () {
- diffElement.removeEventListener('render', renderHandler);
- cursor.reInitCursor();
- promise.resolve();
- };
- diffElement.addEventListener('render', renderHandler);
- diffElement.diff = {
- ...diff,
- content: [
- {
- ab: ['Lorem ipsum dolor sit amet, suspendisse inceptos vehicula, '],
- },
- {
- b: [
- 'Nullam neque, ligula ac, id blandit.',
- 'Sagittis tincidunt torquent, tempor nunc amet.',
- 'At rhoncus id.',
- ],
- move_details: {changed: false},
- },
- {
- ab: ['Sem nascetur, erat ut, non in.'],
- },
- {
- a: [
- 'Nullam neque, ligula ac, id blandit.',
- 'Sagittis tincidunt torquent, tempor nunc amet.',
- 'At rhoncus id.',
- ],
- move_details: {changed: false},
- },
- {
- ab: ['Arcu eget, rhoncus amet cursus, ipsum elementum.'],
- },
- ],
- };
- await promise;
- });
-
- test('renders moveControls with simple descriptions', () => {
- const [movedIn, movedOut] = [
- ...queryAll<HTMLElement>(diffElement, '.dueToMove tr.moveControls'),
- ];
- assert.include(movedIn.innerText, 'Moved in');
- assert.include(movedOut.innerText, 'Moved out');
- });
- });
-
- suite('moved chunks (moveDetails)', () => {
- setup(async () => {
- const promise = mockPromise();
- const renderHandler = function () {
- diffElement.removeEventListener('render', renderHandler);
- cursor.reInitCursor();
- promise.resolve();
- };
- diffElement.addEventListener('render', renderHandler);
- diffElement.diff = {
- ...diff,
- content: [
- {
- ab: ['Lorem ipsum dolor sit amet, suspendisse inceptos vehicula, '],
- },
- {
- b: [
- 'Nullam neque, ligula ac, id blandit.',
- 'Sagittis tincidunt torquent, tempor nunc amet.',
- 'At rhoncus id.',
- ],
- move_details: {changed: false, range: {start: 4, end: 6}},
- },
- {
- ab: ['Sem nascetur, erat ut, non in.'],
- },
- {
- a: [
- 'Nullam neque, ligula ac, id blandit.',
- 'Sagittis tincidunt torquent, tempor nunc amet.',
- 'At rhoncus id.',
- ],
- move_details: {changed: false, range: {start: 2, end: 4}},
- },
- {
- ab: ['Arcu eget, rhoncus amet cursus, ipsum elementum.'],
- },
- ],
- };
- await promise;
- });
-
- test('renders moveControls with simple descriptions', () => {
- const [movedIn, movedOut] = [
- ...queryAll<HTMLElement>(diffElement, '.dueToMove tr.moveControls'),
- ];
- assert.include(movedIn.innerText, 'Moved from lines 4 - 6');
- assert.include(movedOut.innerText, 'Moved to lines 2 - 4');
- });
-
- test('startLineAnchor of movedIn chunk fires events', async () => {
- const [movedIn] = [...queryAll(diffElement, '.dueToMove .moveControls')];
- const [startLineAnchor] = movedIn.querySelectorAll('a');
-
- const promise = mockPromise();
- const onMovedLinkClicked = (e: CustomEvent) => {
- assert.deepEqual(e.detail, {lineNum: 4, side: Side.LEFT});
- promise.resolve();
- };
- assert.equal(startLineAnchor.textContent, '4');
- startLineAnchor.addEventListener(
- 'moved-link-clicked',
- onMovedLinkClicked
- );
- startLineAnchor.click();
- await promise;
- });
-
- test('endLineAnchor of movedOut fires events', async () => {
- const [, movedOut] = [
- ...queryAll(diffElement, '.dueToMove .moveControls'),
- ];
- const [, endLineAnchor] = movedOut.querySelectorAll('a');
-
- const promise = mockPromise();
- const onMovedLinkClicked = (e: CustomEvent) => {
- assert.deepEqual(e.detail, {lineNum: 4, side: Side.RIGHT});
- promise.resolve();
- };
- assert.equal(endLineAnchor.textContent, '4');
- endLineAnchor.addEventListener('moved-link-clicked', onMovedLinkClicked);
- endLineAnchor.click();
- await promise;
- });
- });
-
- test('initialLineNumber not provided', async () => {
- let scrollBehaviorDuringMove;
- const moveToNumStub = sinon.stub(cursor, 'moveToLineNumber');
- const moveToChunkStub = sinon
- .stub(cursor, 'moveToFirstChunk')
- .callsFake(() => {
- scrollBehaviorDuringMove = cursor.cursorManager.scrollMode;
- });
- diffElement.diff = createDiff();
- await diffElement.updateComplete;
- await waitForEventOnce(diffElement, 'render');
- cursor.reInitCursor();
- assert.isFalse(moveToNumStub.called);
- assert.isTrue(moveToChunkStub.called);
- assert.equal(scrollBehaviorDuringMove, 'never');
- assert.equal(cursor.cursorManager.scrollMode, 'keep-visible');
- });
-
- test('initialLineNumber provided', async () => {
- let scrollBehaviorDuringMove;
- const moveToNumStub = sinon
- .stub(cursor, 'moveToLineNumber')
- .callsFake(() => {
- scrollBehaviorDuringMove = cursor.cursorManager.scrollMode;
- });
- const moveToChunkStub = sinon.stub(cursor, 'moveToFirstChunk');
- cursor.initialLineNumber = 10;
- cursor.side = Side.RIGHT;
-
- diffElement.diff = createDiff();
- await diffElement.updateComplete;
- await waitForEventOnce(diffElement, 'render');
- cursor.reInitCursor();
- assert.isFalse(moveToChunkStub.called);
- assert.isTrue(moveToNumStub.called);
- assert.equal(moveToNumStub.lastCall.args[0], 10);
- assert.equal(moveToNumStub.lastCall.args[1], Side.RIGHT);
- assert.equal(scrollBehaviorDuringMove, 'keep-visible');
- assert.equal(cursor.cursorManager.scrollMode, 'keep-visible');
- });
-
- test('getTargetDiffElement', () => {
- cursor.initialLineNumber = 1;
- assert.isTrue(!!cursor.diffRow);
- assert.equal(cursor.getTargetDiffElement(), diffElement);
- });
-
- suite('createCommentInPlace', () => {
- setup(() => {
- diffElement.loggedIn = true;
- });
-
- test('adds new draft for selected line on the left', async () => {
- cursor.moveToLineNumber(2, Side.LEFT);
- const promise = mockPromise();
- diffElement.addEventListener('create-comment', e => {
- const {lineNum, range, side} = e.detail;
- assert.equal(lineNum, 2);
- assert.equal(range, undefined);
- assert.equal(side, Side.LEFT);
- promise.resolve();
- });
- cursor.createCommentInPlace();
- await promise;
- });
-
- test('adds draft for selected line on the right', async () => {
- cursor.moveToLineNumber(4, Side.RIGHT);
- const promise = mockPromise();
- diffElement.addEventListener('create-comment', e => {
- const {lineNum, range, side} = e.detail;
- assert.equal(lineNum, 4);
- assert.equal(range, undefined);
- assert.equal(side, Side.RIGHT);
- promise.resolve();
- });
- cursor.createCommentInPlace();
- await promise;
- });
-
- test('creates comment for range if selected', async () => {
- const someRange = {
- start_line: 2,
- start_character: 3,
- end_line: 6,
- end_character: 1,
- };
- diffElement.highlights.selectedRange = {
- side: Side.RIGHT,
- range: someRange,
- };
- const promise = mockPromise();
- diffElement.addEventListener('create-comment', e => {
- const {lineNum, range, side} = e.detail;
- assert.equal(lineNum, 6);
- assert.equal(range, someRange);
- assert.equal(side, Side.RIGHT);
- promise.resolve();
- });
- cursor.createCommentInPlace();
- await promise;
- });
-
- test('ignores call if nothing is selected', () => {
- const createRangeCommentStub = sinon.stub(
- diffElement,
- 'createRangeComment'
- );
- const addDraftAtLineStub = sinon.stub(diffElement, 'addDraftAtLine');
- cursor.diffRow = undefined;
- cursor.createCommentInPlace();
- assert.isFalse(createRangeCommentStub.called);
- assert.isFalse(addDraftAtLineStub.called);
- });
- });
-
- test('getAddress', () => {
- // It should initialize to the first chunk: line 5 of the revision.
- assert.deepEqual(cursor.getAddress(), {leftSide: false, number: 5});
-
- // Revision line 4 is up.
- cursor.moveUp();
- assert.deepEqual(cursor.getAddress(), {leftSide: false, number: 4});
-
- // Base line 4 is left.
- cursor.moveLeft();
- assert.deepEqual(cursor.getAddress(), {leftSide: true, number: 4});
-
- // Moving to the next chunk takes it back to the start.
- cursor.moveToNextChunk();
- assert.deepEqual(cursor.getAddress(), {leftSide: false, number: 5});
-
- // The following chunk is a removal starting on line 10 of the base.
- cursor.moveToNextChunk();
- assert.deepEqual(cursor.getAddress(), {leftSide: true, number: 10});
-
- // Should be null if there is no selection.
- cursor.cursorManager.unsetCursor();
- assert.isNotOk(cursor.getAddress());
- });
-
- test('_findRowByNumberAndFile', () => {
- // Get the first ab row after the first chunk.
- const rows = [...queryAll<HTMLTableRowElement>(diffElement, 'tr')];
- const row = rows[9];
- assert.ok(row);
-
- // It should be line 8 on the right, but line 5 on the left.
- assert.equal(cursor._findRowByNumberAndFile(8, Side.RIGHT), row);
- assert.equal(cursor._findRowByNumberAndFile(5, Side.LEFT), row);
- });
-
- test('expand context updates stops', async () => {
- const spy = sinon.spy(cursor, '_updateStops');
- const controls = queryAndAssert(diffElement, 'gr-context-controls');
- const showContext = queryAndAssert<HTMLElement>(controls, '.showContext');
- showContext.click();
- await waitForEventOnce(diffElement, 'render');
- await waitUntil(() => spy.called);
- assert.isTrue(spy.called);
- });
-
- test('updates stops when loading changes', () => {
- const spy = sinon.spy(cursor, '_updateStops');
- diffElement.dispatchEvent(new Event('loading-changed'));
- assert.isTrue(spy.called);
- });
-
- suite('multi diff', () => {
- let diffElements: GrDiff[];
-
- setup(async () => {
- diffElements = [
- await fixture(html`<gr-diff></gr-diff>`),
- await fixture(html`<gr-diff></gr-diff>`),
- await fixture(html`<gr-diff></gr-diff>`),
- ];
- cursor = new GrDiffCursor();
-
- // Register the diff with the cursor.
- cursor.replaceDiffs(diffElements);
-
- for (const el of diffElements) {
- el.prefs = createDefaultDiffPrefs();
- }
- });
-
- function getTargetDiffIndex() {
- // Mocha has a bug where when `assert.equals` fails, it will try to
- // JSON.stringify the operands, which fails when they are cyclic structures
- // like GrDiffElement. The failure is difficult to attribute to a specific
- // assertion because of the async nature assertion errors are handled and
- // can cause the test simply timing out, causing a lot of debugging headache.
- // Working with indices circumvents the problem.
- const target = cursor.getTargetDiffElement();
- assertIsDefined(target);
- return diffElements.indexOf(target);
- }
-
- test('do not skip loading diffs', async () => {
- diffElements[0].diff = createDiff();
- diffElements[2].diff = createDiff();
- await waitForEventOnce(diffElements[0], 'render');
- await waitForEventOnce(diffElements[2], 'render');
-
- const lastLine = diffElements[0].diff.meta_b?.lines;
- assertIsDefined(lastLine);
-
- // Goto second last line of the first diff
- cursor.moveToLineNumber(lastLine - 1, Side.RIGHT);
- assert.equal(
- cursor.getTargetLineElement()!.textContent?.trim(),
- `${lastLine - 1}`
- );
-
- // Can move down until we reach the loading file
- cursor.moveDown();
- assert.equal(getTargetDiffIndex(), 0);
- assert.equal(
- cursor.getTargetLineElement()!.textContent?.trim(),
- lastLine.toString()
- );
-
- // Cannot move down while still loading the diff we would switch to
- cursor.moveDown();
- assert.equal(getTargetDiffIndex(), 0);
- assert.equal(
- cursor.getTargetLineElement()!.textContent?.trim(),
- lastLine.toString()
- );
-
- // Diff 1 finishing to load
- diffElements[1].diff = createDiff();
- await waitForEventOnce(diffElements[1], 'render');
-
- // Now we can go down
- cursor.moveDown(); // LOST
- cursor.moveDown(); // FILE
- assert.equal(getTargetDiffIndex(), 1);
- assert.equal(cursor.getTargetLineElement()!.textContent?.trim(), 'File');
- });
- });
-});
diff --git a/polygerrit-ui/app/embed/diff-old/gr-diff-highlight/gr-annotation.ts b/polygerrit-ui/app/embed/diff-old/gr-diff-highlight/gr-annotation.ts
deleted file mode 100644
index 5669bcf..0000000
--- a/polygerrit-ui/app/embed/diff-old/gr-diff-highlight/gr-annotation.ts
+++ /dev/null
@@ -1,285 +0,0 @@
-/**
- * @license
- * Copyright 2016 Google LLC
- * SPDX-License-Identifier: Apache-2.0
- */
-import {getSanitizeDOMValue} from '@polymer/polymer/lib/utils/settings';
-
-// TODO(wyatta): refactor this to be <MARK> rather than <HL>.
-const ANNOTATION_TAG = 'HL';
-
-// Astral code point as per https://mathiasbynens.be/notes/javascript-unicode
-const REGEX_ASTRAL_SYMBOL = /[\uD800-\uDBFF][\uDC00-\uDFFF]/;
-
-export const GrAnnotation = {
- /**
- * The DOM API textContent.length calculation is broken when the text
- * contains Unicode. See https://mathiasbynens.be/notes/javascript-unicode .
- *
- */
- getLength(node: Node) {
- if (node instanceof Comment) return 0;
- return GrAnnotation.getStringLength(node.textContent || '');
- },
-
- /**
- * Returns the number of Unicode code points in the given string
- *
- * This is not necessarily the same as the number of visible symbols.
- * See https://mathiasbynens.be/notes/javascript-unicode for more details.
- */
- getStringLength(str: string) {
- return [...str].length;
- },
-
- /**
- * Annotates the [offset, offset+length) text segment in the parent with the
- * element definition provided as arguments.
- *
- * @param parent the node whose contents will be annotated.
- * If parent is Text then parent.parentNode must not be null
- * @param offset the 0-based offset from which the annotation will
- * start.
- * @param length of the annotated text.
- * @param elementSpec the spec to create the
- * annotating element.
- */
- annotateWithElement(
- parent: Node,
- offset: number,
- length: number,
- elSpec: ElementSpec
- ) {
- const tagName = elSpec.tagName;
- const attributes = elSpec.attributes || {};
- let childNodes: Node[];
-
- if (parent instanceof Element) {
- childNodes = Array.from(parent.childNodes);
- } else if (parent instanceof Text) {
- childNodes = [parent];
- parent = parent.parentNode!;
- } else {
- return;
- }
-
- const nestedNodes: Node[] = [];
- for (let node of childNodes) {
- const initialNodeLength = GrAnnotation.getLength(node);
- // If the current node is completely before the offset.
- if (offset > 0 && initialNodeLength <= offset) {
- offset -= initialNodeLength;
- continue;
- }
-
- if (offset > 0) {
- node = GrAnnotation.splitNode(node, offset);
- offset = 0;
- }
- if (GrAnnotation.getLength(node) > length) {
- GrAnnotation.splitNode(node, length);
- }
- nestedNodes.push(node);
-
- length -= GrAnnotation.getLength(node);
- if (!length) break;
- }
-
- const wrapper = document.createElement(tagName);
- const sanitizer = getSanitizeDOMValue();
- for (let [name, value] of Object.entries(attributes)) {
- if (!value) continue;
- if (sanitizer) {
- value = sanitizer(value, name, 'attribute', wrapper) as string;
- }
- wrapper.setAttribute(name, value);
- }
- for (const inner of nestedNodes) {
- parent.replaceChild(wrapper, inner);
- wrapper.appendChild(inner);
- }
- },
-
- /**
- * Surrounds the element's text at specified range in an ANNOTATION_TAG
- * element. If the element has child elements, the range is split and
- * applied as deeply as possible.
- */
- annotateElement(
- parent: HTMLElement,
- offset: number,
- length: number,
- cssClass: string
- ) {
- const nodes: Array<HTMLElement | Text> = [].slice.apply(parent.childNodes);
- let nodeLength;
- let subLength;
-
- for (const node of nodes) {
- nodeLength = GrAnnotation.getLength(node);
-
- // If the current node is completely before the offset.
- if (nodeLength <= offset) {
- offset -= nodeLength;
- continue;
- }
-
- // Sublength is the annotation length for the current node.
- subLength = Math.min(length, nodeLength - offset);
-
- if (node instanceof Text) {
- GrAnnotation._annotateText(node, offset, subLength, cssClass);
- } else if (node instanceof Element) {
- GrAnnotation.annotateElement(node, offset, subLength, cssClass);
- }
-
- // If there is still more to annotate, then shift the indices, otherwise
- // work is done, so break the loop.
- if (subLength < length) {
- length -= subLength;
- offset = 0;
- } else {
- break;
- }
- }
- },
-
- /**
- * Wraps node in annotation tag with cssClass, replacing the node in DOM.
- */
- wrapInHighlight(node: Element | Text, cssClass: string) {
- let hl;
- if (!(node instanceof Text) && node.tagName === ANNOTATION_TAG) {
- hl = node;
- hl.classList.add(cssClass);
- } else {
- hl = document.createElement(ANNOTATION_TAG);
- hl.className = cssClass;
- if (node.parentElement) node.parentElement.replaceChild(hl, node);
- hl.appendChild(node);
- }
- return hl;
- },
-
- /**
- * Splits Text Node and wraps it in hl with cssClass.
- * Wraps trailing part after split, tailing one if firstPart is true.
- */
- splitAndWrapInHighlight(
- node: Text,
- offset: number,
- cssClass: string,
- firstPart?: boolean
- ) {
- if (
- (GrAnnotation.getLength(node) === offset && firstPart) ||
- (offset === 0 && !firstPart)
- ) {
- return GrAnnotation.wrapInHighlight(node, cssClass);
- }
- if (firstPart) {
- GrAnnotation.splitNode(node, offset);
- // Node points to first part of the Text, second one is sibling.
- } else {
- // if node is Text then splitNode will return a Text
- node = GrAnnotation.splitNode(node, offset) as Text;
- }
- return GrAnnotation.wrapInHighlight(node, cssClass);
- },
-
- /**
- * Splits Node at offset.
- * If Node is Element, it's cloned and the node at offset is split too.
- */
- splitNode(element: Node, offset: number) {
- if (element instanceof Text) {
- return GrAnnotation.splitTextNode(element, offset);
- }
- const tail = element.cloneNode(false);
-
- if (element.parentElement)
- element.parentElement.insertBefore(tail, element.nextSibling);
- // Skip nodes before offset.
- let node = element.firstChild;
- while (
- node &&
- (GrAnnotation.getLength(node) <= offset ||
- GrAnnotation.getLength(node) === 0)
- ) {
- offset -= GrAnnotation.getLength(node);
- node = node.nextSibling;
- }
- if (node && GrAnnotation.getLength(node) > offset) {
- tail.appendChild(GrAnnotation.splitNode(node, offset));
- }
- while (node && node.nextSibling) {
- tail.appendChild(node.nextSibling);
- }
- return tail;
- },
-
- /**
- * Node.prototype.splitText Unicode-valid alternative.
- *
- * DOM Api for splitText() is broken for Unicode:
- * https://mathiasbynens.be/notes/javascript-unicode
- *
- * @return Trailing Text Node.
- */
- splitTextNode(node: Text, offset: number) {
- if (node.textContent?.match(REGEX_ASTRAL_SYMBOL)) {
- const head = Array.from(node.textContent);
- const tail = head.splice(offset);
- const parent = node.parentNode;
-
- // Split the content of the original node.
- node.textContent = head.join('');
-
- const tailNode = document.createTextNode(tail.join(''));
- if (parent) {
- parent.insertBefore(tailNode, node.nextSibling);
- }
- return tailNode;
- } else {
- return node.splitText(offset);
- }
- },
-
- _annotateText(node: Text, offset: number, length: number, cssClass: string) {
- const nodeLength = GrAnnotation.getLength(node);
-
- // There are four cases:
- // 1) Entire node is highlighted.
- // 2) Highlight is at the start.
- // 3) Highlight is at the end.
- // 4) Highlight is in the middle.
-
- if (offset === 0 && nodeLength === length) {
- // Case 1.
- GrAnnotation.wrapInHighlight(node, cssClass);
- } else if (offset === 0) {
- // Case 2.
- GrAnnotation.splitAndWrapInHighlight(node, length, cssClass, true);
- } else if (offset + length === nodeLength) {
- // Case 3
- GrAnnotation.splitAndWrapInHighlight(node, offset, cssClass, false);
- } else {
- // Case 4
- GrAnnotation.splitAndWrapInHighlight(
- GrAnnotation.splitTextNode(node, offset),
- length,
- cssClass,
- true
- );
- }
- },
-};
-
-/**
- * Data used to construct an element.
- *
- */
-export interface ElementSpec {
- tagName: string;
- attributes?: {[attributeName: string]: string | undefined};
-}
diff --git a/polygerrit-ui/app/embed/diff-old/gr-diff-highlight/gr-annotation_test.ts b/polygerrit-ui/app/embed/diff-old/gr-diff-highlight/gr-annotation_test.ts
deleted file mode 100644
index 4543c10..0000000
--- a/polygerrit-ui/app/embed/diff-old/gr-diff-highlight/gr-annotation_test.ts
+++ /dev/null
@@ -1,308 +0,0 @@
-/**
- * @license
- * Copyright 2016 Google LLC
- * SPDX-License-Identifier: Apache-2.0
- */
-
-import '../../../test/common-test-setup';
-import {GrAnnotation} from './gr-annotation';
-import {
- getSanitizeDOMValue,
- setSanitizeDOMValue,
-} from '@polymer/polymer/lib/utils/settings';
-import {assert, fixture, html} from '@open-wc/testing';
-
-suite('annotation', () => {
- let str: string;
- let parent: HTMLDivElement;
- let textNode: Text;
-
- setup(async () => {
- parent = await fixture(
- html`
- <div>Lorem ipsum dolor sit amet, suspendisse inceptos vehicula</div>
- `
- );
- textNode = parent.childNodes[0] as Text;
- str = textNode.textContent!;
- });
-
- test('_annotateText length:0 offset:0', () => {
- GrAnnotation._annotateText(textNode, 0, 0, 'foobar');
-
- assert.equal(parent.textContent, str);
- assert.equal(
- parent.innerHTML,
- '<hl class="foobar"></hl>Lorem ipsum dolor sit amet, suspendisse inceptos vehicula'
- );
- });
-
- test('_annotateText length:0 offset:1', () => {
- GrAnnotation._annotateText(textNode, 1, 0, 'foobar');
-
- assert.equal(parent.textContent, str);
- assert.equal(
- parent.innerHTML,
- 'L<hl class="foobar"></hl>orem ipsum dolor sit amet, suspendisse inceptos vehicula'
- );
- });
-
- test('_annotateText length:0 offset:str.length', () => {
- GrAnnotation._annotateText(textNode, str.length, 0, 'foobar');
-
- assert.equal(parent.textContent, str);
- assert.equal(
- parent.innerHTML,
- 'Lorem ipsum dolor sit amet, suspendisse inceptos vehicula<hl class="foobar"></hl>'
- );
- });
-
- test('_annotateText Case 1', () => {
- GrAnnotation._annotateText(textNode, 0, str.length, 'foobar');
-
- assert.equal(parent.textContent, str);
- assert.equal(
- parent.innerHTML,
- '<hl class="foobar">Lorem ipsum dolor sit amet, suspendisse inceptos vehicula</hl>'
- );
- });
-
- test('_annotateText Case 2', () => {
- GrAnnotation._annotateText(textNode, 0, 12, 'foobar');
-
- assert.equal(parent.textContent, str);
- assert.equal(
- parent.innerHTML,
- '<hl class="foobar">Lorem ipsum </hl>dolor sit amet, suspendisse inceptos vehicula'
- );
- });
-
- test('_annotateText Case 3', () => {
- GrAnnotation._annotateText(textNode, 12, str.length - 12, 'foobar');
-
- assert.equal(parent.textContent, str);
- assert.equal(
- parent.innerHTML,
- 'Lorem ipsum <hl class="foobar">dolor sit amet, suspendisse inceptos vehicula</hl>'
- );
- });
-
- test('_annotateText Case 4', () => {
- const index = str.indexOf('dolor');
- const length = 'dolor '.length;
-
- GrAnnotation._annotateText(textNode, index, length, 'foobar');
-
- assert.equal(parent.textContent, str);
- assert.equal(
- parent.innerHTML,
- 'Lorem ipsum <hl class="foobar">dolor </hl>sit amet, suspendisse inceptos vehicula'
- );
- });
-
- test('_annotateElement design doc example', () => {
- const layers = ['amet, ', 'inceptos ', 'amet, ', 'et, suspendisse ince'];
-
- // Apply the layers successively.
- layers.forEach((layer, i) => {
- GrAnnotation.annotateElement(
- parent,
- str.indexOf(layer),
- layer.length,
- `layer-${i + 1}`
- );
- });
-
- assert.equal(parent.textContent, str);
- assert.equal(
- parent.innerHTML,
- 'Lorem ipsum dolor sit <hl class="layer-1"><hl class="layer-3">am<hl class="layer-4">et, </hl></hl></hl><hl class="layer-4">suspendisse </hl><hl class="layer-2"><hl class="layer-4">ince</hl>ptos </hl>vehicula'
- );
- });
-
- test('splitTextNode', () => {
- const helloString = 'hello';
- const asciiString = 'ASCII';
- const unicodeString = 'Unic💢de';
-
- let node;
- let tail;
-
- // Non-unicode path:
- node = document.createTextNode(helloString + asciiString);
- tail = GrAnnotation.splitTextNode(node, helloString.length);
- assert(node.textContent, helloString);
- assert(tail.textContent, asciiString);
-
- // Unicdoe path:
- node = document.createTextNode(helloString + unicodeString);
- tail = GrAnnotation.splitTextNode(node, helloString.length);
- assert(node.textContent, helloString);
- assert(tail.textContent, unicodeString);
- });
-
- suite('annotateWithElement', () => {
- const fullText = '01234567890123456789';
- let mockSanitize: sinon.SinonSpy;
- let originalSanitizeDOMValue: (
- value: unknown,
- name: string,
- type: 'property' | 'attribute',
- node: Node | null | undefined
- ) => unknown;
-
- setup(() => {
- setSanitizeDOMValue(p0 => p0);
- originalSanitizeDOMValue = getSanitizeDOMValue()!;
- assert.isDefined(originalSanitizeDOMValue);
- mockSanitize = sinon.spy(originalSanitizeDOMValue);
- setSanitizeDOMValue(mockSanitize);
- });
-
- teardown(() => {
- setSanitizeDOMValue(originalSanitizeDOMValue);
- });
-
- test('annotates when fully contained', () => {
- const length = 10;
- const container = document.createElement('div');
- container.textContent = fullText;
- GrAnnotation.annotateWithElement(container, 1, length, {
- tagName: 'test-wrapper',
- });
-
- assert.equal(
- container.innerHTML,
- '0<test-wrapper>1234567890</test-wrapper>123456789'
- );
- });
-
- test('annotates when spanning multiple nodes', () => {
- const length = 10;
- const container = document.createElement('div');
- container.textContent = fullText;
- GrAnnotation.annotateElement(container, 5, length, 'testclass');
- GrAnnotation.annotateWithElement(container, 1, length, {
- tagName: 'test-wrapper',
- });
-
- assert.equal(
- container.innerHTML,
- '0' +
- '<test-wrapper>' +
- '1234' +
- '<hl class="testclass">567890</hl>' +
- '</test-wrapper>' +
- '<hl class="testclass">1234</hl>' +
- '56789'
- );
- });
-
- test('annotates text node', () => {
- const length = 10;
- const container = document.createElement('div');
- container.textContent = fullText;
- GrAnnotation.annotateWithElement(container.childNodes[0], 1, length, {
- tagName: 'test-wrapper',
- });
-
- assert.equal(
- container.innerHTML,
- '0<test-wrapper>1234567890</test-wrapper>123456789'
- );
- });
-
- test('handles zero-length nodes', () => {
- const container = document.createElement('div');
- container.appendChild(document.createTextNode('0123456789'));
- container.appendChild(document.createElement('span'));
- container.appendChild(document.createTextNode('0123456789'));
- GrAnnotation.annotateWithElement(container, 1, 10, {
- tagName: 'test-wrapper',
- });
-
- assert.equal(
- container.innerHTML,
- '0<test-wrapper>123456789<span></span>0</test-wrapper>123456789'
- );
- });
-
- test('handles comment nodes', () => {
- const container = document.createElement('div');
- container.appendChild(document.createComment('comment1'));
- container.appendChild(document.createTextNode('0123456789'));
- container.appendChild(document.createComment('comment2'));
- container.appendChild(document.createElement('span'));
- container.appendChild(document.createTextNode('0123456789'));
- GrAnnotation.annotateWithElement(container, 1, 10, {
- tagName: 'test-wrapper',
- });
-
- assert.equal(
- container.innerHTML,
- '<!--comment1-->' +
- '0<test-wrapper>123456789' +
- '<!--comment2-->' +
- '<span></span>0</test-wrapper>123456789'
- );
- });
-
- test('sets sanitized attributes', () => {
- const container = document.createElement('div');
- container.textContent = fullText;
- const attributes = {
- href: 'foo',
- 'data-foo': 'bar',
- class: 'hello world',
- };
- GrAnnotation.annotateWithElement(container, 1, length, {
- tagName: 'test-wrapper',
- attributes,
- });
- assert(
- mockSanitize.calledWith(
- 'foo',
- 'href',
- 'attribute',
- sinon.match.instanceOf(Element)
- )
- );
- assert(
- mockSanitize.calledWith(
- 'bar',
- 'data-foo',
- 'attribute',
- sinon.match.instanceOf(Element)
- )
- );
- assert(
- mockSanitize.calledWith(
- 'hello world',
- 'class',
- 'attribute',
- sinon.match.instanceOf(Element)
- )
- );
- const el = container.querySelector('test-wrapper')!;
- assert.equal(el.getAttribute('href'), 'foo');
- assert.equal(el.getAttribute('data-foo'), 'bar');
- assert.equal(el.getAttribute('class'), 'hello world');
- });
- });
-
- suite('getStringLength', () => {
- test('ASCII characters are counted correctly', () => {
- assert.equal(GrAnnotation.getStringLength('ASCII'), 5);
- });
-
- test('Unicode surrogate pairs count as one symbol', () => {
- assert.equal(GrAnnotation.getStringLength('Unic💢de'), 7);
- assert.equal(GrAnnotation.getStringLength('💢💢'), 2);
- });
-
- test('Grapheme clusters count as multiple symbols', () => {
- assert.equal(GrAnnotation.getStringLength('man\u0303ana'), 7); // mañana
- assert.equal(GrAnnotation.getStringLength('q\u0307\u0323'), 3); // q̣̇
- });
- });
-});
diff --git a/polygerrit-ui/app/embed/diff-old/gr-diff-highlight/gr-diff-highlight.ts b/polygerrit-ui/app/embed/diff-old/gr-diff-highlight/gr-diff-highlight.ts
deleted file mode 100644
index 2c0663d..0000000
--- a/polygerrit-ui/app/embed/diff-old/gr-diff-highlight/gr-diff-highlight.ts
+++ /dev/null
@@ -1,525 +0,0 @@
-/**
- * @license
- * Copyright 2016 Google LLC
- * SPDX-License-Identifier: Apache-2.0
- */
-import '../../../styles/shared-styles';
-import '../../diff/gr-selection-action-box/gr-selection-action-box';
-import {GrAnnotation} from './gr-annotation';
-import {normalize} from './gr-range-normalizer';
-import {strToClassName} from '../../../utils/dom-util';
-import {Side} from '../../../constants/constants';
-import {CommentRange} from '../../../types/common';
-import {GrSelectionActionBox} from '../../diff/gr-selection-action-box/gr-selection-action-box';
-import {
- getLineElByChild,
- getLineNumberByChild,
- getSideByLineEl,
- GrDiffThreadElement,
-} from '../../diff/gr-diff/gr-diff-utils';
-import {debounce, DelayedTask} from '../../../utils/async-util';
-import {assertIsDefined, queryAndAssert} from '../../../utils/common-util';
-import {fire} from '../../../utils/event-util';
-
-interface SidedRange {
- side: Side;
- range: CommentRange;
-}
-
-interface NormalizedPosition {
- node: Node | null;
- side: Side;
- line: number;
- column: number;
-}
-
-interface NormalizedRange {
- start: NormalizedPosition | null;
- end: NormalizedPosition | null;
-}
-
-/**
- * The methods that we actually want to call on the builder. We don't want a
- * fully blown dependency on GrDiffBuilderElement.
- */
-export interface DiffBuilderInterface {
- getContentTdByLineEl(lineEl?: Element): Element | undefined;
-}
-
-/**
- * Handles showing, positioning and interacting with <gr-selection-action-box>.
- *
- * Toggles a css class for highlighting comment ranges when the mouse leaves or
- * enters a comment thread element.
- */
-export class GrDiffHighlight {
- selectedRange?: SidedRange;
-
- private diffBuilder?: DiffBuilderInterface;
-
- private diffTable?: HTMLElement;
-
- private selectionChangeTask?: DelayedTask;
-
- init(diffTable: HTMLElement, diffBuilder: DiffBuilderInterface) {
- this.cleanup();
-
- this.diffTable = diffTable;
- this.diffBuilder = diffBuilder;
-
- diffTable.addEventListener(
- 'comment-thread-mouseleave',
- this.handleCommentThreadMouseleave
- );
- diffTable.addEventListener(
- 'comment-thread-mouseenter',
- this.handleCommentThreadMouseenter
- );
- diffTable.addEventListener(
- 'create-comment-requested',
- this.handleRangeCommentRequest
- );
- }
-
- cleanup() {
- this.selectionChangeTask?.cancel();
- if (this.diffTable) {
- this.diffTable.removeEventListener(
- 'comment-thread-mouseleave',
- this.handleCommentThreadMouseleave
- );
- this.diffTable.removeEventListener(
- 'comment-thread-mouseenter',
- this.handleCommentThreadMouseenter
- );
- this.diffTable.removeEventListener(
- 'create-comment-requested',
- this.handleRangeCommentRequest
- );
- }
- }
-
- /**
- * Determines side/line/range for a DOM selection and shows a tooltip.
- *
- * With native shadow DOM, gr-diff-highlight cannot access a selection that
- * references the DOM elements making up the diff because they are in the
- * shadow DOM the gr-diff element. For this reason, we listen to the
- * selectionchange event and retrieve the selection in gr-diff, and then
- * call this method to process the Selection.
- *
- * @param selection A DOM Selection living in the shadow DOM of
- * the diff element.
- * @param isMouseUp If true, this is called due to a mouseup
- * event, in which case we might want to immediately create a comment,
- * because isMouseUp === true combined with an existing selection must
- * mean that this is the end of a double-click.
- */
- handleSelectionChange(
- selection: Selection | Range | null,
- isMouseUp: boolean
- ) {
- if (selection === null) return;
- // Debounce is not just nice for waiting until the selection has settled,
- // it is also vital for being able to click on the action box before it is
- // removed.
- // If you wait longer than 50 ms, then you don't properly catch a very
- // quick 'c' press after the selection change. If you wait less than 10
- // ms, then you will have about 50 handleSelection() calls when doing a
- // simple drag for select.
- this.selectionChangeTask = debounce(
- this.selectionChangeTask,
- () => this.handleSelection(selection, isMouseUp),
- 10
- );
- }
-
- private getThreadEl(e: Event): GrDiffThreadElement | null {
- for (const pathEl of e.composedPath()) {
- if (
- pathEl instanceof HTMLElement &&
- pathEl.classList.contains('comment-thread')
- ) {
- return pathEl as GrDiffThreadElement;
- }
- }
- return null;
- }
-
- private toggleRangeElHighlight(
- threadEl: GrDiffThreadElement | null,
- highlightRange = false
- ) {
- const rootId = threadEl?.rootId;
- if (!rootId) return;
- if (!this.diffTable) return;
- if (highlightRange) {
- const selector = `.range.${strToClassName(rootId)}`;
- const rangeNodes = this.diffTable.querySelectorAll(selector);
- rangeNodes.forEach(rangeNode => {
- rangeNode.classList.add('rangeHoverHighlight');
- });
- const hintNode = this.diffTable.querySelector(
- `gr-ranged-comment-hint[threadElRootId="${rootId}"]`
- );
- hintNode?.shadowRoot
- ?.querySelectorAll('.rangeHighlight')
- .forEach(highlightNode =>
- highlightNode.classList.add('rangeHoverHighlight')
- );
- } else {
- const selector = `.rangeHoverHighlight.${strToClassName(rootId)}`;
- const rangeNodes = this.diffTable.querySelectorAll(selector);
- rangeNodes.forEach(rangeNode => {
- rangeNode.classList.remove('rangeHoverHighlight');
- });
- const hintNode = this.diffTable.querySelector(
- `gr-ranged-comment-hint[threadElRootId="${rootId}"]`
- );
- hintNode?.shadowRoot
- ?.querySelectorAll('.rangeHoverHighlight')
- .forEach(highlightNode =>
- highlightNode.classList.remove('rangeHoverHighlight')
- );
- }
- }
-
- private handleCommentThreadMouseenter = (e: Event) => {
- const threadEl = this.getThreadEl(e);
- this.toggleRangeElHighlight(threadEl, /* highlightRange= */ true);
- };
-
- private handleCommentThreadMouseleave = (e: Event) => {
- const threadEl = this.getThreadEl(e);
- this.toggleRangeElHighlight(threadEl, /* highlightRange= */ false);
- };
-
- /**
- * Get current normalized selection.
- * Merges multiple ranges, accounts for triple click, accounts for
- * syntax highligh, convert native DOM Range objects to Gerrit concepts
- * (line, side, etc).
- */
- private getNormalizedRange(selection: Selection | Range) {
- /* On Safari the ShadowRoot.getSelection() isn't there and the only thing
- we can get is a single Range */
- if (selection instanceof Range) {
- return this.normalizeRange(selection);
- }
- const rangeCount = selection.rangeCount;
- if (rangeCount === 0) {
- return null;
- } else if (rangeCount === 1) {
- return this.normalizeRange(selection.getRangeAt(0));
- } else {
- const startRange = this.normalizeRange(selection.getRangeAt(0));
- const endRange = this.normalizeRange(
- selection.getRangeAt(rangeCount - 1)
- );
- return {
- start: startRange.start,
- end: endRange.end,
- };
- }
- }
-
- /**
- * Normalize a specific DOM Range.
- *
- * @return fixed normalized range
- */
- private normalizeRange(domRange: Range): NormalizedRange {
- const range = normalize(domRange);
- return this.fixTripleClickSelection(
- {
- start: this.normalizeSelectionSide(
- range.startContainer,
- range.startOffset
- ),
- end: this.normalizeSelectionSide(range.endContainer, range.endOffset),
- },
- domRange
- );
- }
-
- /**
- * Adjust triple click selection for the whole line.
- * A triple click always results in:
- * - start.column == end.column == 0
- * - end.line == start.line + 1
- *
- * @param range Normalized range, ie column/line numbers
- * @param domRange DOM Range object
- * @return fixed normalized range
- */
- private fixTripleClickSelection(range: NormalizedRange, domRange: Range) {
- if (!range.start) {
- // Selection outside of current diff.
- return range;
- }
- const start = range.start;
- const end = range.end;
- // Happens when triple click in side-by-side mode with other side empty.
- const endsAtOtherEmptySide =
- !end &&
- domRange.endOffset === 0 &&
- domRange.endContainer instanceof HTMLElement &&
- domRange.endContainer.nodeName === 'TD' &&
- (domRange.endContainer.classList.contains('left') ||
- domRange.endContainer.classList.contains('right'));
- const endsAtBeginningOfNextLine =
- end &&
- start.column === 0 &&
- end.column === 0 &&
- end.line === start.line + 1;
- const content = domRange.cloneContents().querySelector('.contentText');
- const lineLength = (content && this.getLength(content)) || 0;
- if (lineLength && (endsAtBeginningOfNextLine || endsAtOtherEmptySide)) {
- // Move the selection to the end of the previous line.
- range.end = {
- node: start.node,
- column: lineLength,
- side: start.side,
- line: start.line,
- };
- }
- return range;
- }
-
- /**
- * Convert DOM Range selection to concrete numbers (line, column, side).
- * Moves range end if it's not inside td.content.
- * Returns null if selection end is not valid (outside of diff).
- *
- * @param node td.content child
- * @param offset offset within node
- */
- private normalizeSelectionSide(
- node: Node | null,
- offset: number
- ): NormalizedPosition | null {
- let column;
- if (!this.diffTable) return null;
- if (!this.diffBuilder) return null;
- if (!node || !this.diffTable.contains(node)) return null;
- const lineEl = getLineElByChild(node);
- if (!lineEl) return null;
- const side = getSideByLineEl(lineEl);
- if (!side) return null;
- const line = getLineNumberByChild(lineEl);
- if (typeof line !== 'number') return null;
- const contentTd = this.diffBuilder.getContentTdByLineEl(lineEl);
- if (!contentTd) return null;
- const contentText = contentTd.querySelector('.contentText');
- if (!contentTd.contains(node)) {
- node = contentText;
- column = 0;
- } else {
- const thread = contentTd.querySelector('.comment-thread');
- if (thread?.contains(node)) {
- column = this.getLength(contentText);
- node = contentText;
- } else {
- column = this.convertOffsetToColumn(node, offset);
- }
- }
-
- return {
- node,
- side,
- line,
- column,
- };
- }
-
- /**
- * The only line in which add a comment tooltip is cut off is the first
- * line. Even if there is a collapsed section, The first visible line is
- * in the position where the second line would have been, if not for the
- * collapsed section, so don't need to worry about this case for
- * positioning the tooltip.
- */
- // visible for testing
- positionActionBox(
- actionBox: GrSelectionActionBox,
- startLine: number,
- range: Text | Element | Range
- ) {
- if (startLine > 1) {
- actionBox.positionBelow = false;
- actionBox.placeAbove(range);
- return;
- }
- actionBox.positionBelow = true;
- actionBox.placeBelow(range);
- }
-
- private isRangeValid(range: NormalizedRange | null) {
- if (!range || !range.start || !range.start.node || !range.end) {
- return false;
- }
- const start = range.start;
- const end = range.end;
- return !(
- start.side !== end.side ||
- end.line < start.line ||
- (start.line === end.line && start.column === end.column)
- );
- }
-
- // visible for testing
- handleSelection(selection: Selection | Range, isMouseUp: boolean) {
- /* On Safari, the selection events may return a null range that should
- be ignored */
- if (!selection) return;
- if (!this.diffTable) return;
-
- const normalizedRange = this.getNormalizedRange(selection);
- if (!this.isRangeValid(normalizedRange)) {
- this.removeActionBox();
- return;
- }
- /* On Safari the ShadowRoot.getSelection() isn't there and the only thing
- we can get is a single Range */
- const domRange =
- selection instanceof Range ? selection : selection.getRangeAt(0);
- const start = normalizedRange!.start!;
- const end = normalizedRange!.end!;
-
- // TODO (viktard): Drop empty first and last lines from selection.
-
- // If the selection is from the end of one line to the start of the next
- // line, then this must have been a double-click, or you have started
- // dragging. Showing the action box is bad in the former case and not very
- // useful in the latter, so never do that.
- // If this was a mouse-up event, we create a comment immediately if
- // the selection is from the end of a line to the start of the next line.
- // In a perfect world we would only do this for double-click, but it is
- // extremely rare that a user would drag from the end of one line to the
- // start of the next and release the mouse, so we don't bother.
- // TODO(brohlfs): This does not work, if the double-click is before a new
- // diff chunk (start will be equal to end), and neither before an "expand
- // the diff context" block (end line will match the first line of the new
- // section and thus be greater than start line + 1).
- if (start.line === end.line - 1 && end.column === 0) {
- // Rather than trying to find the line contents (for comparing
- // start.column with the content length), we just check if the selection
- // is empty to see that it's at the end of a line.
- const content = domRange.cloneContents().querySelector('.contentText');
- if (isMouseUp && this.getLength(content) === 0) {
- this.fireCreateRangeComment(start.side, {
- start_line: start.line,
- start_character: 0,
- end_line: start.line,
- end_character: start.column,
- });
- }
- return;
- }
-
- let actionBox = this.diffTable.querySelector('gr-selection-action-box');
- if (!actionBox) {
- actionBox = document.createElement('gr-selection-action-box');
- this.diffTable.appendChild(actionBox);
- }
- this.selectedRange = {
- range: {
- start_line: start.line,
- start_character: start.column,
- end_line: end.line,
- end_character: end.column,
- },
- side: start.side,
- };
- if (start.line === end.line) {
- this.positionActionBox(actionBox, start.line, domRange);
- } else if (start.node instanceof Text) {
- if (start.column) {
- this.positionActionBox(
- actionBox,
- start.line,
- start.node.splitText(start.column)
- );
- }
- start.node.parentElement!.normalize(); // Undo splitText from above.
- } else if (
- start.node instanceof HTMLElement &&
- start.node.classList.contains('content') &&
- (start.node.firstChild instanceof Element ||
- start.node.firstChild instanceof Text)
- ) {
- this.positionActionBox(actionBox, start.line, start.node.firstChild);
- } else if (start.node instanceof Element || start.node instanceof Text) {
- this.positionActionBox(actionBox, start.line, start.node);
- } else {
- console.warn('Failed to position comment action box.');
- this.removeActionBox();
- }
- }
-
- private fireCreateRangeComment(side: Side, range: CommentRange) {
- if (this.diffTable) {
- fire(this.diffTable, 'create-range-comment', {side, range});
- }
- this.removeActionBox();
- }
-
- private handleRangeCommentRequest = (e: Event) => {
- e.stopPropagation();
- assertIsDefined(this.selectedRange, 'selectedRange');
- const {side, range} = this.selectedRange;
- this.fireCreateRangeComment(side, range);
- };
-
- // visible for testing
- removeActionBox() {
- this.selectedRange = undefined;
- const actionBox = this.diffTable?.querySelector('gr-selection-action-box');
- if (actionBox) actionBox.remove();
- }
-
- private convertOffsetToColumn(el: Node, offset: number) {
- if (el instanceof Element && el.classList.contains('content')) {
- return offset;
- }
- while (
- el.previousSibling ||
- !el.parentElement?.classList.contains('content')
- ) {
- if (el.previousSibling) {
- el = el.previousSibling;
- offset += this.getLength(el);
- } else {
- el = el.parentElement!;
- }
- }
- return offset;
- }
-
- /**
- * Get length of a node. If the node is a content node, then only give the
- * length of its .contentText child.
- *
- * @param node this is sometimes passed as null.
- */
- // visible for testing
- getLength(node: Node | null): number {
- if (node === null) return 0;
- if (node instanceof Element && node.classList.contains('content')) {
- return this.getLength(queryAndAssert(node, '.contentText'));
- } else {
- return GrAnnotation.getLength(node);
- }
- }
-}
-
-export interface CreateRangeCommentEventDetail {
- side: Side;
- range: CommentRange;
-}
-
-declare global {
- interface HTMLElementEventMap {
- 'create-range-comment': CustomEvent<CreateRangeCommentEventDetail>;
- }
-}
diff --git a/polygerrit-ui/app/embed/diff-old/gr-diff-highlight/gr-diff-highlight_test.ts b/polygerrit-ui/app/embed/diff-old/gr-diff-highlight/gr-diff-highlight_test.ts
deleted file mode 100644
index e491e63..0000000
--- a/polygerrit-ui/app/embed/diff-old/gr-diff-highlight/gr-diff-highlight_test.ts
+++ /dev/null
@@ -1,717 +0,0 @@
-/**
- * @license
- * Copyright 2016 Google LLC
- * SPDX-License-Identifier: Apache-2.0
- */
-import '../../../test/common-test-setup';
-import './gr-diff-highlight';
-import {getTextOffset} from './gr-range-normalizer';
-import {fixture, fixtureCleanup, html, assert} from '@open-wc/testing';
-import {
- GrDiffHighlight,
- DiffBuilderInterface,
- CreateRangeCommentEventDetail,
-} from './gr-diff-highlight';
-import {Side} from '../../../api/diff';
-import {SinonStubbedMember} from 'sinon';
-import {queryAndAssert} from '../../../utils/common-util';
-import {GrDiffThreadElement} from '../../diff/gr-diff/gr-diff-utils';
-import {
- stubElement,
- waitQueryAndAssert,
- waitUntil,
-} from '../../../test/test-utils';
-import {GrSelectionActionBox} from '../../diff/gr-selection-action-box/gr-selection-action-box';
-
-// Splitting long lines in html into shorter rows breaks tests:
-// zero-length text nodes and new lines are not expected in some places
-/* eslint-disable max-len, lit/prefer-static-styles */
-/* prettier-ignore */
-const diffTable = html`
- <table id="diffTable">
- <tbody class="section both">
- <tr class="diff-row side-by-side" left-type="both" right-type="both">
- <td class="left lineNum" data-value="1"></td>
- <td class="content both"><div class="contentText">[1] Nam cum ad me in Cumanum salutandi causa uterque venisset,</div></td>
- <td class="right lineNum" data-value="1"></td>
- <td class="content both"><div class="contentText">[1] Nam cum ad me in Cumanum salutandi causa uterque</div></td>
- </tr>
- </tbody>
-
- <tbody class="section delta">
- <tr class="diff-row side-by-side" left-type="remove" right-type="add">
- <td class="left lineNum" data-value="2"></td>
- <!-- Next tag is formatted to eliminate zero-length text nodes. -->
- <td class="content remove"><div class="contentText">na💢ti <hl class="foo range generated_id314">te, inquit</hl>, sumus<hl class="bar">aliquando</hl> otiosum, <hl>certe</hl> a<hl><span class="tab-indicator" style="tab-size:8;"> </span></hl>udiam, <hl>quid</hl> sit,<span class="tab-indicator" style="tab-size:8;"> </span>quod<hl>Epicurum</hl></div></td>
- <td class="right lineNum" data-value="2"></td>
- <!-- Next tag is formatted to eliminate zero-length text nodes. -->
- <td class="content add"><div class="contentText">nacti , <hl>,</hl> sumus<hl><span class="tab-indicator" style="tab-size:8;"> </span></hl>otiosum,<span class="tab-indicator" style="tab-size:8;"> </span> audiam,sit, quod</div></td>
- </tr>
- </tbody>
-
- <tbody class="section both">
- <tr class="diff-row side-by-side" left-type="both" right-type="both">
- <td class="left lineNum" data-value="138"></td>
- <td class="content both"><div class="contentText">[14] Nam cum ad me in Cumanum salutandi causa uterque venisset,</div></td>
- <td class="right lineNum" data-value="119"></td>
- <td class="content both"><div class="contentText">[14] Nam cum ad me in Cumanum salutandi causa uterque venisset,</div></td>
- </tr>
- </tbody>
-
- <tbody class="section delta">
- <tr class="diff-row side-by-side" left-type="remove" right-type="add">
- <td class="left lineNum" data-value="140"></td>
- <!-- Next tag is formatted to eliminate zero-length text nodes. -->
- <td class="content remove"><div class="contentText"><!-- a comment node -->na💢ti <hl class="foo">te, inquit</hl>, sumus <hl class="bar">aliquando</hl> otiosum, <hl>certe</hl> a <hl><span class="tab-indicator" style="tab-size:8;">\u0009</span></hl>udiam, <hl>quid</hl> sit, <span class="tab-indicator" style="tab-size:8;">\u0009</span>quod <hl>Epicurum</hl></div><div class="comment-thread">
- [Yet another random diff thread content here]
- </div></td>
- <td class="right lineNum" data-value="120"></td>
- <!-- Next tag is formatted to eliminate zero-length text nodes. -->
- <td class="content add"><div class="contentText">nacti , <hl>,</hl> sumus <hl><span class="tab-indicator" style="tab-size:8;">\u0009</span></hl> otiosum, <span class="tab-indicator" style="tab-size:8;">\u0009</span> audiam, sit, quod</div></td>
- </tr>
- </tbody>
-
- <tbody class="section both">
- <tr class="diff-row side-by-side" left-type="both" right-type="both">
- <td class="left lineNum" data-value="141"></td>
- <td class="content both"><div class="contentText">nam et<hl><span class="tab-indicator" style="tab-size:8;"> </span></hl>complectitur<span class="tab-indicator" style="tab-size:8;"></span>verbis, quod vult, et dicit plane, quod intellegam;</div></td>
- <td class="right lineNum" data-value="130"></td>
- <td class="content both"><div class="contentText">nam et complectitur verbis, quod vult, et dicit plane, quodintellegam;</div></td>
- </tr>
- </tbody>
-
- <tbody class="section contextControl">
- <tr
- class="diff-row side-by-side"
- left-type="contextControl"
- right-type="contextControl"
- >
- <td class="left contextLineNum"></td>
- <td>
- <gr-button>+10↑</gr-button>
- -
- <gr-button>Show 21 common lines</gr-button>
- -
- <gr-button>+10↓</gr-button>
- </td>
- <td class="right contextLineNum"></td>
- <td>
- <gr-button>+10↑</gr-button>
- -
- <gr-button>Show 21 common lines</gr-button>
- -
- <gr-button>+10↓</gr-button>
- </td>
- </tr>
- </tbody>
-
- <tbody class="section delta total">
- <tr class="diff-row side-by-side" left-type="blank" right-type="add">
- <td class="left"></td>
- <td class="blank"></td>
- <td class="right lineNum" data-value="146"></td>
- <td class="content add"><div class="contentText">[17] Quid igitur est? inquit; audire enim cupio, quid non probes. Principio, inquam,</div></td>
- </tr>
- </tbody>
-
- <tbody class="section both">
- <tr class="diff-row side-by-side" left-type="both" right-type="both">
- <td class="left lineNum" data-value="165"></td>
- <td class="content both"><div class="contentText"></div></td>
- <td class="right lineNum" data-value="147"></td>
- <td class="content both"><div class="contentText">in physicis, <hl><span class="tab-indicator" style="tab-size:8;"> </span></hl>quibus maxime gloriatur, primum totus est alienus. Democritea dicit</div></td>
- </tr>
- </tbody>
- </table>
-`;
-/* eslint-enable max-len */
-
-suite('gr-diff-highlight', () => {
- suite('comment events', () => {
- let threadEl: GrDiffThreadElement;
- let hlRange: HTMLElement;
- let element: GrDiffHighlight;
- let diff: HTMLElement;
- let builder: {
- getContentTdByLineEl: SinonStubbedMember<
- DiffBuilderInterface['getContentTdByLineEl']
- >;
- };
-
- setup(async () => {
- diff = await fixture<HTMLTableElement>(diffTable);
- builder = {
- getContentTdByLineEl: sinon.stub(),
- };
- element = new GrDiffHighlight();
- element.init(diff, builder);
- hlRange = queryAndAssert(diff, 'hl.range.generated_id314');
-
- threadEl = document.createElement(
- 'div'
- ) as unknown as GrDiffThreadElement;
- threadEl.className = 'comment-thread';
- threadEl.rootId = 'id314';
- diff.appendChild(threadEl);
- });
-
- teardown(() => {
- element.cleanup();
- threadEl.remove();
- });
-
- test('comment-thread-mouseenter toggles rangeHoverHighlight class', async () => {
- assert.isFalse(hlRange.classList.contains('rangeHoverHighlight'));
- threadEl.dispatchEvent(
- new CustomEvent('comment-thread-mouseenter', {
- bubbles: true,
- composed: true,
- })
- );
- await waitUntil(() => hlRange.classList.contains('rangeHoverHighlight'));
- assert.isTrue(hlRange.classList.contains('rangeHoverHighlight'));
- });
-
- test('comment-thread-mouseleave toggles rangeHoverHighlight class', async () => {
- hlRange.classList.add('rangeHoverHighlight');
- threadEl.dispatchEvent(
- new CustomEvent('comment-thread-mouseleave', {
- bubbles: true,
- composed: true,
- })
- );
- await waitUntil(() => !hlRange.classList.contains('rangeHoverHighlight'));
- assert.isFalse(hlRange.classList.contains('rangeHoverHighlight'));
- });
-
- test(`create-range-comment for range when create-comment-requested
- is fired`, () => {
- const removeActionBoxStub = sinon.stub(element, 'removeActionBox');
- element.selectedRange = {
- side: Side.LEFT,
- range: {
- start_line: 7,
- start_character: 11,
- end_line: 24,
- end_character: 42,
- },
- };
- const requestEvent = new CustomEvent('create-comment-requested');
- let createRangeEvent: CustomEvent<CreateRangeCommentEventDetail>;
- diff.addEventListener('create-range-comment', e => {
- createRangeEvent = e;
- });
- diff.dispatchEvent(requestEvent);
- if (!createRangeEvent!) assert.fail('event not set');
- assert.deepEqual(element.selectedRange, createRangeEvent.detail);
- assert.isTrue(removeActionBoxStub.called);
- });
- });
-
- suite('selection', () => {
- let element: GrDiffHighlight;
- let diff: HTMLElement;
- let builder: {
- getContentTdByLineEl: SinonStubbedMember<
- DiffBuilderInterface['getContentTdByLineEl']
- >;
- };
- let contentStubs;
-
- setup(async () => {
- diff = await fixture<HTMLTableElement>(diffTable);
- builder = {
- getContentTdByLineEl: sinon.stub(),
- };
- element = new GrDiffHighlight();
- element.init(diff, builder);
- contentStubs = [];
- stubElement('gr-selection-action-box', 'placeAbove');
- stubElement('gr-selection-action-box', 'placeBelow');
- });
-
- teardown(() => {
- fixtureCleanup();
- element.cleanup();
- contentStubs = null;
- document.getSelection()!.removeAllRanges();
- });
-
- const stubContent = (line: number, side: Side) => {
- const contentTd = diff.querySelector(
- `.${side}.lineNum[data-value="${line}"] ~ .content`
- );
- if (!contentTd) assert.fail('content td not found');
- const contentText = contentTd.querySelector('.contentText');
- const lineEl =
- diff.querySelector(`.${side}.lineNum[data-value="${line}"]`) ??
- undefined;
- contentStubs.push({
- lineEl,
- contentTd,
- contentText,
- });
- builder.getContentTdByLineEl.withArgs(lineEl).returns(contentTd);
- return contentText;
- };
-
- const emulateSelection = (
- startNode: Node,
- startOffset: number,
- endNode: Node,
- endOffset: number
- ) => {
- const selection = document.getSelection();
- if (!selection) assert.fail('no selection');
- selection.removeAllRanges();
- const range = document.createRange();
- range.setStart(startNode, startOffset);
- range.setEnd(endNode, endOffset);
- selection.addRange(range);
- element.handleSelection(selection, false);
- };
-
- test('single first line', () => {
- const content = stubContent(1, Side.RIGHT);
- sinon.spy(element, 'positionActionBox');
- if (!content?.firstChild) assert.fail('content first child not found');
- emulateSelection(content.firstChild, 5, content.firstChild, 12);
- const actionBox = diff.querySelector('gr-selection-action-box');
- if (!actionBox) assert.fail('action box not found');
- assert.isTrue(actionBox.positionBelow);
- });
-
- test('multiline starting on first line', () => {
- const startContent = stubContent(1, Side.RIGHT);
- const endContent = stubContent(2, Side.RIGHT);
- sinon.spy(element, 'positionActionBox');
- if (!startContent?.firstChild) {
- assert.fail('first child of start content not found');
- }
- if (!endContent?.lastChild) {
- assert.fail('last child of end content not found');
- }
- emulateSelection(startContent.firstChild, 10, endContent.lastChild, 7);
- const actionBox = diff.querySelector('gr-selection-action-box');
- if (!actionBox) assert.fail('action box not found');
- assert.isTrue(actionBox.positionBelow);
- });
-
- test('single line', async () => {
- const content = stubContent(138, Side.LEFT);
- sinon.spy(element, 'positionActionBox');
- if (!content?.firstChild) assert.fail('content first child not found');
- emulateSelection(content.firstChild, 5, content.firstChild, 12);
- const actionBox = await waitQueryAndAssert<GrSelectionActionBox>(
- diff,
- 'gr-selection-action-box'
- );
- if (!element.selectedRange) assert.fail('no range selected');
- const {range, side} = element.selectedRange;
- assert.deepEqual(range, {
- start_line: 138,
- start_character: 5,
- end_line: 138,
- end_character: 12,
- });
- assert.equal(side, Side.LEFT);
- assert.notOk(actionBox.positionBelow);
- });
-
- test('multiline', () => {
- const startContent = stubContent(119, Side.RIGHT);
- const endContent = stubContent(120, Side.RIGHT);
- sinon.spy(element, 'positionActionBox');
- if (!startContent?.firstChild) {
- assert.fail('first child of start content not found');
- }
- if (!endContent?.lastChild) {
- assert.fail('last child of end content');
- }
- emulateSelection(startContent.firstChild, 10, endContent.lastChild, 7);
- const actionBox = diff.querySelector('gr-selection-action-box');
- if (!actionBox) assert.fail('action box not found');
- if (!element.selectedRange) assert.fail('no range selected');
- const {range, side} = element.selectedRange;
- assert.deepEqual(range, {
- start_line: 119,
- start_character: 10,
- end_line: 120,
- end_character: 36,
- });
- assert.equal(side, Side.RIGHT);
- assert.notOk(actionBox.positionBelow);
- });
-
- test('multiple ranges aka firefox implementation', () => {
- const startContent = stubContent(119, Side.RIGHT);
- const endContent = stubContent(120, Side.RIGHT);
- if (!startContent?.firstChild) {
- assert.fail('first child of start content not found');
- }
- if (!endContent?.lastChild) {
- assert.fail('last child of end content');
- }
-
- const startRange = document.createRange();
- startRange.setStart(startContent.firstChild, 10);
- startRange.setEnd(startContent.firstChild, 11);
-
- const endRange = document.createRange();
- endRange.setStart(endContent.lastChild, 6);
- endRange.setEnd(endContent.lastChild, 7);
-
- const getRangeAtStub = sinon.stub();
- getRangeAtStub
- .onFirstCall()
- .returns(startRange)
- .onSecondCall()
- .returns(endRange);
- const selection = {
- rangeCount: 2,
- getRangeAt: getRangeAtStub,
- removeAllRanges: sinon.stub(),
- } as unknown as Selection;
- element.handleSelection(selection, false);
- if (!element.selectedRange) assert.fail('no range selected');
- const {range} = element.selectedRange;
- assert.deepEqual(range, {
- start_line: 119,
- start_character: 10,
- end_line: 120,
- end_character: 36,
- });
- });
-
- test('multiline grow end highlight over tabs', () => {
- const startContent = stubContent(119, Side.RIGHT);
- const endContent = stubContent(120, Side.RIGHT);
- if (!startContent?.firstChild) {
- assert.fail('first child of start content not found');
- }
- if (!endContent?.firstChild) {
- assert.fail('first child of end content not found');
- }
- emulateSelection(startContent.firstChild, 10, endContent.firstChild, 2);
- if (!element.selectedRange) assert.fail('no range selected');
- const {range, side} = element.selectedRange;
- assert.deepEqual(range, {
- start_line: 119,
- start_character: 10,
- end_line: 120,
- end_character: 2,
- });
- assert.equal(side, Side.RIGHT);
- });
-
- test('collapsed', () => {
- const content = stubContent(138, Side.LEFT);
- if (!content?.firstChild) {
- assert.fail('first child of content not found');
- }
- emulateSelection(content.firstChild, 5, content.firstChild, 5);
- const sel = document.getSelection();
- if (!sel) assert.fail('no selection');
- assert.isOk(sel.getRangeAt(0).startContainer);
- assert.isFalse(!!element.selectedRange);
- });
-
- test('starts inside hl', () => {
- const content = stubContent(140, Side.LEFT);
- if (!content) {
- assert.fail('content not found');
- }
- const hl = content.querySelector('.foo');
- if (!hl?.firstChild) {
- assert.fail('first child of hl element not found');
- }
- if (!hl?.nextSibling) {
- assert.fail('next sibling of hl element not found');
- }
- emulateSelection(hl.firstChild, 2, hl.nextSibling, 7);
- if (!element.selectedRange) assert.fail('no range selected');
- const {range, side} = element.selectedRange;
- assert.deepEqual(range, {
- start_line: 140,
- start_character: 8,
- end_line: 140,
- end_character: 23,
- });
- assert.equal(side, Side.LEFT);
- });
-
- test('ends inside hl', () => {
- const content = stubContent(140, Side.LEFT);
- if (!content) assert.fail('content not found');
- const hl = content.querySelector('.bar');
- if (!hl) assert.fail('hl inside content not found');
- if (!hl.previousSibling) assert.fail('previous sibling not found');
- if (!hl.firstChild) assert.fail('first child not found');
- emulateSelection(hl.previousSibling, 2, hl.firstChild, 3);
- if (!element.selectedRange) assert.fail('no range selected');
- const {range} = element.selectedRange;
- assert.deepEqual(range, {
- start_line: 140,
- start_character: 18,
- end_line: 140,
- end_character: 27,
- });
- });
-
- test('multiple hl', () => {
- const content = stubContent(140, Side.LEFT);
- if (!content) assert.fail('content not found');
- if (!content.firstChild) assert.fail('first child not found');
- const hl = content.querySelectorAll('hl')[4];
- if (!hl) assert.fail('hl not found');
- if (!hl.firstChild) assert.fail('first child of hl not found');
- emulateSelection(content.firstChild, 2, hl.firstChild, 2);
- if (!element.selectedRange) assert.fail('no range selected');
- const {range, side} = element.selectedRange;
- assert.deepEqual(range, {
- start_line: 140,
- start_character: 2,
- end_line: 140,
- end_character: 61,
- });
- assert.equal(side, Side.LEFT);
- });
-
- test('starts outside of diff', () => {
- const contentText = stubContent(140, Side.LEFT);
- if (!contentText) assert.fail('content not found');
- if (!contentText.firstChild) assert.fail('child not found');
- const contentTd = contentText.parentElement;
- if (!contentTd) assert.fail('content td not found');
- if (!contentTd.parentElement) assert.fail('parent of td not found');
-
- emulateSelection(contentTd.parentElement, 0, contentText.firstChild, 2);
- assert.isFalse(!!element.selectedRange);
- });
-
- test('ends outside of diff', () => {
- const content = stubContent(140, Side.LEFT);
- if (!content) assert.fail('content not found');
- if (!content.firstChild) assert.fail('child not found');
- if (!content.nextElementSibling) assert.fail('sibling not found');
- if (!content.nextElementSibling.firstChild) {
- assert.fail('sibling child not found');
- }
- emulateSelection(
- content.nextElementSibling.firstChild,
- 2,
- content.firstChild,
- 2
- );
- assert.isFalse(!!element.selectedRange);
- });
-
- test('starts and ends on different sides', () => {
- const startContent = stubContent(140, Side.LEFT);
- const endContent = stubContent(130, Side.RIGHT);
- if (!startContent?.firstChild) {
- assert.fail('first child of start content not found');
- }
- if (!endContent?.firstChild) {
- assert.fail('first child of end content not found');
- }
- emulateSelection(startContent.firstChild, 2, endContent.firstChild, 2);
- assert.isFalse(!!element.selectedRange);
- });
-
- test('starts in comment thread element', () => {
- const startContent = stubContent(140, Side.LEFT);
- if (!startContent?.parentElement) {
- assert.fail('parent el of start content not found');
- }
- const comment =
- startContent.parentElement.querySelector('.comment-thread');
- if (!comment?.firstChild) {
- assert.fail('first child of comment not found');
- }
- const endContent = stubContent(141, Side.LEFT);
- if (!endContent?.firstChild) {
- assert.fail('first child of end content not found');
- }
- emulateSelection(comment.firstChild, 2, endContent.firstChild, 4);
- if (!element.selectedRange) assert.fail('no range selected');
- const {range, side} = element.selectedRange;
- assert.deepEqual(range, {
- start_line: 140,
- start_character: 83,
- end_line: 141,
- end_character: 4,
- });
- assert.equal(side, Side.LEFT);
- });
-
- test('ends in comment thread element', () => {
- const content = stubContent(140, Side.LEFT);
- if (!content?.firstChild) {
- assert.fail('first child of content not found');
- }
- if (!content?.parentElement) {
- assert.fail('parent element of content not found');
- }
- const comment = content.parentElement.querySelector('.comment-thread');
- if (!comment?.firstChild) {
- assert.fail('first child of comment element not found');
- }
- emulateSelection(content.firstChild, 4, comment.firstChild, 1);
- if (!element.selectedRange) assert.fail('no range selected');
- const {range, side} = element.selectedRange;
- assert.deepEqual(range, {
- start_line: 140,
- start_character: 4,
- end_line: 140,
- end_character: 83,
- });
- assert.equal(side, Side.LEFT);
- });
-
- test('starts in context element', () => {
- const contextControl = diff
- .querySelector('.contextControl')!
- .querySelector('gr-button');
- if (!contextControl) assert.fail('context control not found');
- const content = stubContent(146, Side.RIGHT);
- if (!content) assert.fail('content not found');
- if (!content.firstChild) assert.fail('content child not found');
- emulateSelection(contextControl, 0, content.firstChild, 7);
- // TODO (viktard): Select nearest line.
- assert.isFalse(!!element.selectedRange);
- });
-
- test('ends in context element', () => {
- const contextControl = diff
- .querySelector('.contextControl')!
- .querySelector('gr-button');
- if (!contextControl) {
- assert.fail('context control element not found');
- }
- const content = stubContent(141, Side.LEFT);
- if (!content?.firstChild) {
- assert.fail('first child of content element not found');
- }
- emulateSelection(content.firstChild, 2, contextControl, 1);
- // TODO (viktard): Select nearest line.
- assert.isFalse(!!element.selectedRange);
- });
-
- test('selection containing context element', () => {
- const startContent = stubContent(130, Side.RIGHT);
- const endContent = stubContent(146, Side.RIGHT);
- if (!startContent?.firstChild) {
- assert.fail('first child of start content not found');
- }
- if (!endContent?.firstChild) {
- assert.fail('first child of end content not found');
- }
- emulateSelection(startContent.firstChild, 3, endContent.firstChild, 14);
- if (!element.selectedRange) assert.fail('no range selected');
- const {range, side} = element.selectedRange;
- assert.deepEqual(range, {
- start_line: 130,
- start_character: 3,
- end_line: 146,
- end_character: 14,
- });
- assert.equal(side, Side.RIGHT);
- });
-
- test('ends at a tab', () => {
- const content = stubContent(140, Side.LEFT);
- if (!content?.firstChild) {
- assert.fail('first child of content element not found');
- }
- const span = content.querySelector('span');
- if (!span) assert.fail('span element not found');
- emulateSelection(content.firstChild, 1, span, 0);
- if (!element.selectedRange) assert.fail('no range selected');
- const {range, side} = element.selectedRange;
- assert.deepEqual(range, {
- start_line: 140,
- start_character: 1,
- end_line: 140,
- end_character: 51,
- });
- assert.equal(side, Side.LEFT);
- });
-
- test('starts at a tab', () => {
- const content = stubContent(140, Side.LEFT);
- if (!content) assert.fail('content element not found');
- emulateSelection(
- content.querySelectorAll('hl')[3],
- 0,
- content.querySelectorAll('span')[1].nextSibling!,
- 1
- );
- if (!element.selectedRange) assert.fail('no range selected');
- const {range, side} = element.selectedRange;
- assert.deepEqual(range, {
- start_line: 140,
- start_character: 51,
- end_line: 140,
- end_character: 71,
- });
- assert.equal(side, Side.LEFT);
- });
-
- test('properly accounts for syntax highlighting', () => {
- const content = stubContent(140, Side.LEFT);
- if (!content) assert.fail('content element not found');
- emulateSelection(
- content.querySelectorAll('hl')[3],
- 0,
- content.querySelectorAll('span')[1],
- 0
- );
- if (!element.selectedRange) assert.fail('no range selected');
- const {range, side} = element.selectedRange;
- assert.deepEqual(range, {
- start_line: 140,
- start_character: 51,
- end_line: 140,
- end_character: 69,
- });
- assert.equal(side, Side.LEFT);
- });
-
- test('GrRangeNormalizer.getTextOffset computes text offset', () => {
- let content = stubContent(140, Side.LEFT);
- if (!content) assert.fail('content element not found');
- if (!content.lastChild) assert.fail('last child of content not found');
- let child = content.lastChild.lastChild;
- if (!child) assert.fail('last child of last child of content not found');
- let result = getTextOffset(content, child);
- assert.equal(result, 75);
- content = stubContent(146, Side.RIGHT);
- if (!content) assert.fail('content element not found');
- child = content.lastChild;
- if (!child) assert.fail('child element not found');
- result = getTextOffset(content, child);
- assert.equal(result, 0);
- });
-
- test('fixTripleClickSelection', () => {
- const startContent = stubContent(119, Side.RIGHT);
- const endContent = stubContent(120, Side.RIGHT);
- if (!startContent?.firstChild) {
- assert.fail('first child of start content not found');
- }
- if (!endContent) assert.fail('end content not found');
- if (!endContent.firstChild) assert.fail('first child not found');
- emulateSelection(startContent.firstChild, 0, endContent.firstChild, 0);
- if (!element.selectedRange) assert.fail('no range selected');
- const {range, side} = element.selectedRange;
- assert.deepEqual(range, {
- start_line: 119,
- start_character: 0,
- end_line: 119,
- end_character: element.getLength(startContent),
- });
- assert.equal(side, Side.RIGHT);
- });
- });
-});
diff --git a/polygerrit-ui/app/embed/diff-old/gr-diff-highlight/gr-range-normalizer.ts b/polygerrit-ui/app/embed/diff-old/gr-diff-highlight/gr-range-normalizer.ts
deleted file mode 100644
index b177e14..0000000
--- a/polygerrit-ui/app/embed/diff-old/gr-diff-highlight/gr-range-normalizer.ts
+++ /dev/null
@@ -1,103 +0,0 @@
-/**
- * @license
- * Copyright 2016 Google LLC
- * SPDX-License-Identifier: Apache-2.0
- */
-
-// Astral code point as per https://mathiasbynens.be/notes/javascript-unicode
-const REGEX_ASTRAL_SYMBOL = /[\uD800-\uDBFF][\uDC00-\uDFFF]/;
-
-export interface NormalizedRange {
- endContainer: Node;
- endOffset: number;
- startContainer: Node;
- startOffset: number;
-}
-
-/**
- * Remap DOM range to whole lines of a diff if necessary. If the start or
- * end containers are DOM elements that are singular pieces of syntax
- * highlighting, the containers are remapped to the .contentText divs that
- * contain the entire line of code.
- *
- * @param range - the standard DOM selector range.
- * @return A modified version of the range that correctly accounts
- * for syntax highlighting.
- */
-export function normalize(range: Range): NormalizedRange {
- const startContainer = getContentTextParent(range.startContainer);
- const startOffset =
- range.startOffset + getTextOffset(startContainer, range.startContainer);
- const endContainer = getContentTextParent(range.endContainer);
- const endOffset =
- range.endOffset + getTextOffset(endContainer, range.endContainer);
- return {
- startContainer,
- startOffset,
- endContainer,
- endOffset,
- };
-}
-
-function getContentTextParent(target: Node): Node {
- if (!target.parentElement) return target;
-
- let element: Element | null;
- if (target instanceof Element) {
- element = target;
- } else {
- element = target.parentElement;
- }
-
- while (element && !element.classList.contains('contentText')) {
- if (element.parentElement === null) {
- return target;
- }
- element = element.parentElement;
- }
- return element ? element : target;
-}
-
-/**
- * Gets the character offset of the child within the parent.
- * Performs a synchronous in-order traversal from top to bottom of the node
- * element, counting the length of the syntax until child is found.
- *
- * @param node The root DOM element to be searched through.
- * @param child The child element being searched for.
- */
-// TODO(TS): Only export for test.
-export function getTextOffset(node: Node | null, child: Node): number {
- let count = 0;
- let stack = [node];
- while (stack.length) {
- const n = stack.pop();
- if (n === child) {
- break;
- }
- if (n?.childNodes && n.childNodes.length !== 0) {
- const arr = [];
- for (const childNode of n.childNodes) {
- arr.push(childNode);
- }
- arr.reverse();
- stack = stack.concat(arr);
- } else {
- count += getLength(n);
- }
- }
- return count;
-}
-
-/**
- * The DOM API textContent.length calculation is broken when the text
- * contains Unicode. See https://mathiasbynens.be/notes/javascript-unicode .
- *
- * @param node A text node.
- * @return The length of the text.
- */
-function getLength(node?: Node | null) {
- return node && node.textContent && node.nodeType !== Node.COMMENT_NODE
- ? node.textContent.replace(REGEX_ASTRAL_SYMBOL, '_').length
- : 0;
-}
diff --git a/polygerrit-ui/app/embed/diff-old/gr-diff-model/gr-diff-model.ts b/polygerrit-ui/app/embed/diff-old/gr-diff-model/gr-diff-model.ts
deleted file mode 100644
index 7bd2b3d..0000000
--- a/polygerrit-ui/app/embed/diff-old/gr-diff-model/gr-diff-model.ts
+++ /dev/null
@@ -1,47 +0,0 @@
-/**
- * @license
- * Copyright 2023 Google LLC
- * SPDX-License-Identifier: Apache-2.0
- */
-import {Observable} from 'rxjs';
-import {filter} from 'rxjs/operators';
-import {
- DiffInfo,
- DiffPreferencesInfo,
- RenderPreferences,
-} from '../../../api/diff';
-import {define} from '../../../models/dependency';
-import {Model} from '../../../models/base/model';
-import {isDefined} from '../../../types/types';
-import {select} from '../../../utils/observable-util';
-
-export interface DiffState {
- diff: DiffInfo;
- path?: string;
- renderPrefs: RenderPreferences;
- diffPrefs: DiffPreferencesInfo;
-}
-
-export const diffModelToken = define<DiffModel>('diff-model');
-
-export class DiffModel extends Model<DiffState | undefined> {
- readonly diff$: Observable<DiffInfo> = select(
- this.state$.pipe(filter(isDefined)),
- diffState => diffState.diff
- );
-
- readonly path$: Observable<string | undefined> = select(
- this.state$.pipe(filter(isDefined)),
- diffState => diffState.path
- );
-
- readonly renderPrefs$: Observable<RenderPreferences> = select(
- this.state$.pipe(filter(isDefined)),
- diffState => diffState.renderPrefs
- );
-
- readonly diffPrefs$: Observable<DiffPreferencesInfo> = select(
- this.state$.pipe(filter(isDefined)),
- diffState => diffState.diffPrefs
- );
-}
diff --git a/polygerrit-ui/app/embed/diff-old/gr-diff-processor/gr-diff-processor.ts b/polygerrit-ui/app/embed/diff-old/gr-diff-processor/gr-diff-processor.ts
deleted file mode 100644
index 256dc11..0000000
--- a/polygerrit-ui/app/embed/diff-old/gr-diff-processor/gr-diff-processor.ts
+++ /dev/null
@@ -1,714 +0,0 @@
-/**
- * @license
- * Copyright 2016 Google LLC
- * SPDX-License-Identifier: Apache-2.0
- */
-import {GrDiffLine, Highlights} from '../gr-diff/gr-diff-line';
-import {
- GrDiffGroup,
- GrDiffGroupType,
- hideInContextControl,
-} from '../gr-diff/gr-diff-group';
-import {DiffContent} from '../../../types/diff';
-import {Side} from '../../../constants/constants';
-import {debounce, DelayedTask} from '../../../utils/async-util';
-import {assert, assertIsDefined} from '../../../utils/common-util';
-import {GrAnnotation} from '../gr-diff-highlight/gr-annotation';
-import {FILE, GrDiffLineType, LOST, LineNumber} from '../../../api/diff';
-
-const WHOLE_FILE = -1;
-
-// visible for testing
-export interface State {
- lineNums: {
- left: number;
- right: number;
- };
- chunkIndex: number;
-}
-
-interface ChunkEnd {
- offset: number;
- keyLocation: boolean;
-}
-
-export interface KeyLocations {
- left: {[key: string]: boolean};
- right: {[key: string]: boolean};
-}
-
-/**
- * The maximum size for an addition or removal chunk before it is broken down
- * into a series of chunks that are this size at most.
- *
- * Note: The value of 120 is chosen so that it is larger than the default
- * asyncThreshold of 64, but feel free to tune this constant to your
- * performance needs.
- */
-function calcMaxGroupSize(asyncThreshold?: number): number {
- if (!asyncThreshold) return 120;
- return asyncThreshold * 2;
-}
-
-/** Interface for listening to the output of the processor. */
-export interface GroupConsumer {
- addGroup(group: GrDiffGroup): void;
- clearGroups(): void;
-}
-
-/**
- * Converts the API's `DiffContent`s to `GrDiffGroup`s for rendering.
- *
- * Glossary:
- * - "chunk": A single `DiffContent` as returned by the API.
- * - "group": A single `GrDiffGroup` as used for rendering.
- * - "common" chunk/group: A chunk/group that should be considered unchanged
- * for diffing purposes. This can mean its either actually unchanged, or it
- * has only whitespace changes.
- * - "key location": A line number and side of the diff that should not be
- * collapsed e.g. because a comment is attached to it, or because it was
- * provided in the URL and thus should be visible
- * - "uncollapsible" chunk/group: A chunk/group that is either not "common",
- * or cannot be collapsed because it contains a key location
- *
- * Here a a number of tasks this processor performs:
- * - splitting large chunks to allow more granular async rendering
- * - adding a group for the "File" pseudo line that file-level comments can
- * be attached to
- * - replacing common parts of the diff that are outside the user's
- * context setting and do not have comments with a group representing the
- * "expand context" widget. This may require splitting a chunk/group so
- * that the part that is within the context or has comments is shown, while
- * the rest is not.
- */
-export class GrDiffProcessor {
- context = 3;
-
- consumer?: GroupConsumer;
-
- keyLocations: KeyLocations = {left: {}, right: {}};
-
- asyncThreshold = 64;
-
- // visible for testing
- isScrolling?: boolean;
-
- /** Just for making sure that process() is only called once. */
- private isStarted = false;
-
- /** Indicates that processing should be stopped. */
- private isCancelled = false;
-
- private resetIsScrollingTask?: DelayedTask;
-
- private readonly handleWindowScroll = () => {
- this.isScrolling = true;
- this.resetIsScrollingTask = debounce(
- this.resetIsScrollingTask,
- () => (this.isScrolling = false),
- 50
- );
- };
-
- /**
- * Asynchronously process the diff chunks into groups. As it processes, it
- * will splice groups into the `groups` property of the component.
- *
- * @return A promise that resolves with an
- * array of GrDiffGroups when the diff is completely processed.
- */
- process(chunks: DiffContent[], isBinary: boolean) {
- assert(this.isStarted === false, 'diff processor cannot be started twice');
- this.isStarted = true;
-
- window.addEventListener('scroll', this.handleWindowScroll);
-
- assertIsDefined(this.consumer, 'consumer');
- this.consumer.clearGroups();
- this.consumer.addGroup(this.makeGroup(LOST));
- this.consumer.addGroup(this.makeGroup(FILE));
-
- if (isBinary) return Promise.resolve();
-
- return new Promise<void>(resolve => {
- const state = {
- lineNums: {left: 0, right: 0},
- chunkIndex: 0,
- };
-
- chunks = this.splitLargeChunks(chunks);
- chunks = this.splitCommonChunksWithKeyLocations(chunks);
-
- let currentBatch = 0;
- const nextStep = () => {
- if (this.isCancelled || state.chunkIndex >= chunks.length) {
- resolve();
- return;
- }
- if (this.isScrolling) {
- window.setTimeout(nextStep, 100);
- return;
- }
-
- const stateUpdate = this.processNext(state, chunks);
- for (const group of stateUpdate.groups) {
- this.consumer?.addGroup(group);
- currentBatch += group.lines.length;
- }
- state.lineNums.left += stateUpdate.lineDelta.left;
- state.lineNums.right += stateUpdate.lineDelta.right;
-
- state.chunkIndex = stateUpdate.newChunkIndex;
- if (currentBatch >= this.asyncThreshold) {
- currentBatch = 0;
- window.setTimeout(nextStep, 1);
- } else {
- nextStep.call(this);
- }
- };
-
- nextStep.call(this);
- }).finally(() => {
- this.finish();
- });
- }
-
- finish() {
- this.consumer = undefined;
- window.removeEventListener('scroll', this.handleWindowScroll);
- }
-
- cancel() {
- this.isCancelled = true;
- this.finish();
- }
-
- /**
- * Process the next uncollapsible chunk, or the next collapsible chunks.
- */
- // visible for testing
- processNext(state: State, chunks: DiffContent[]) {
- const firstUncollapsibleChunkIndex = this.firstUncollapsibleChunkIndex(
- chunks,
- state.chunkIndex
- );
- if (firstUncollapsibleChunkIndex === state.chunkIndex) {
- const chunk = chunks[state.chunkIndex];
- return {
- lineDelta: {
- left: this.linesLeft(chunk).length,
- right: this.linesRight(chunk).length,
- },
- groups: [
- this.chunkToGroup(
- chunk,
- state.lineNums.left + 1,
- state.lineNums.right + 1
- ),
- ],
- newChunkIndex: state.chunkIndex + 1,
- };
- }
-
- return this.processCollapsibleChunks(
- state,
- chunks,
- firstUncollapsibleChunkIndex
- );
- }
-
- private linesLeft(chunk: DiffContent) {
- return chunk.ab || chunk.a || [];
- }
-
- private linesRight(chunk: DiffContent) {
- return chunk.ab || chunk.b || [];
- }
-
- private firstUncollapsibleChunkIndex(chunks: DiffContent[], offset: number) {
- let chunkIndex = offset;
- while (
- chunkIndex < chunks.length &&
- this.isCollapsibleChunk(chunks[chunkIndex])
- ) {
- chunkIndex++;
- }
- return chunkIndex;
- }
-
- private isCollapsibleChunk(chunk: DiffContent) {
- return (chunk.ab || chunk.common || chunk.skip) && !chunk.keyLocation;
- }
-
- /**
- * Process a stretch of collapsible chunks.
- *
- * Outputs up to three groups:
- * 1) Visible context before the hidden common code, unless it's the
- * very beginning of the file.
- * 2) Context hidden behind a context bar, unless empty.
- * 3) Visible context after the hidden common code, unless it's the very
- * end of the file.
- */
- private processCollapsibleChunks(
- state: State,
- chunks: DiffContent[],
- firstUncollapsibleChunkIndex: number
- ) {
- const collapsibleChunks = chunks.slice(
- state.chunkIndex,
- firstUncollapsibleChunkIndex
- );
- const lineCount = collapsibleChunks.reduce(
- (sum, chunk) => sum + this.commonChunkLength(chunk),
- 0
- );
-
- let groups = this.chunksToGroups(
- collapsibleChunks,
- state.lineNums.left + 1,
- state.lineNums.right + 1
- );
-
- const hasSkippedGroup = !!groups.find(g => g.skip);
- if (this.context !== WHOLE_FILE || hasSkippedGroup) {
- const contextNumLines = this.context > 0 ? this.context : 0;
- const hiddenStart = state.chunkIndex === 0 ? 0 : contextNumLines;
- const hiddenEnd =
- lineCount -
- (firstUncollapsibleChunkIndex === chunks.length ? 0 : this.context);
- groups = hideInContextControl(groups, hiddenStart, hiddenEnd);
- }
-
- return {
- lineDelta: {
- left: lineCount,
- right: lineCount,
- },
- groups,
- newChunkIndex: firstUncollapsibleChunkIndex,
- };
- }
-
- private commonChunkLength(chunk: DiffContent) {
- if (chunk.skip) {
- return chunk.skip;
- }
- console.assert(!!chunk.ab || !!chunk.common);
-
- console.assert(
- !chunk.a || (!!chunk.b && chunk.a.length === chunk.b.length),
- 'common chunk needs same number of a and b lines: ',
- chunk
- );
- return this.linesLeft(chunk).length;
- }
-
- private chunksToGroups(
- chunks: DiffContent[],
- offsetLeft: number,
- offsetRight: number
- ): GrDiffGroup[] {
- return chunks.map(chunk => {
- const group = this.chunkToGroup(chunk, offsetLeft, offsetRight);
- const chunkLength = this.commonChunkLength(chunk);
- offsetLeft += chunkLength;
- offsetRight += chunkLength;
- return group;
- });
- }
-
- private chunkToGroup(
- chunk: DiffContent,
- offsetLeft: number,
- offsetRight: number
- ): GrDiffGroup {
- const type =
- chunk.ab || chunk.skip ? GrDiffGroupType.BOTH : GrDiffGroupType.DELTA;
- const lines = this.linesFromChunk(chunk, offsetLeft, offsetRight);
- const options = {
- moveDetails: chunk.move_details,
- dueToRebase: !!chunk.due_to_rebase,
- ignoredWhitespaceOnly: !!chunk.common,
- keyLocation: !!chunk.keyLocation,
- };
- if (chunk.skip !== undefined) {
- return new GrDiffGroup({
- type,
- skip: chunk.skip,
- offsetLeft,
- offsetRight,
- ...options,
- });
- } else {
- return new GrDiffGroup({
- type,
- lines,
- ...options,
- });
- }
- }
-
- private linesFromChunk(
- chunk: DiffContent,
- offsetLeft: number,
- offsetRight: number
- ) {
- if (chunk.ab) {
- return chunk.ab.map((row, i) =>
- this.lineFromRow(GrDiffLineType.BOTH, offsetLeft, offsetRight, row, i)
- );
- }
- let lines: GrDiffLine[] = [];
- if (chunk.a) {
- // Avoiding a.push(...b) because that causes callstack overflows for
- // large b, which can occur when large files are added removed.
- lines = lines.concat(
- this.linesFromRows(
- GrDiffLineType.REMOVE,
- chunk.a,
- offsetLeft,
- chunk.edit_a
- )
- );
- }
- if (chunk.b) {
- // Avoiding a.push(...b) because that causes callstack overflows for
- // large b, which can occur when large files are added removed.
- lines = lines.concat(
- this.linesFromRows(
- GrDiffLineType.ADD,
- chunk.b,
- offsetRight,
- chunk.edit_b
- )
- );
- }
- return lines;
- }
-
- // visible for testing
- linesFromRows(
- lineType: GrDiffLineType,
- rows: string[],
- offset: number,
- intralineInfos?: number[][]
- ): GrDiffLine[] {
- const grDiffHighlights = intralineInfos
- ? this.convertIntralineInfos(rows, intralineInfos)
- : undefined;
- return rows.map((row, i) =>
- this.lineFromRow(lineType, offset, offset, row, i, grDiffHighlights)
- );
- }
-
- private lineFromRow(
- type: GrDiffLineType,
- offsetLeft: number,
- offsetRight: number,
- row: string,
- i: number,
- highlights?: Highlights[]
- ): GrDiffLine {
- const line = new GrDiffLine(type);
- line.text = row;
- if (type !== GrDiffLineType.ADD) line.beforeNumber = offsetLeft + i;
- if (type !== GrDiffLineType.REMOVE) line.afterNumber = offsetRight + i;
- if (highlights) {
- line.hasIntralineInfo = true;
- line.highlights = highlights.filter(hl => hl.contentIndex === i);
- } else {
- line.hasIntralineInfo = false;
- }
- return line;
- }
-
- private makeGroup(number: LineNumber) {
- const line = new GrDiffLine(GrDiffLineType.BOTH);
- line.beforeNumber = number;
- line.afterNumber = number;
- return new GrDiffGroup({type: GrDiffGroupType.BOTH, lines: [line]});
- }
-
- /**
- * Split chunks into smaller chunks of the same kind.
- *
- * This is done to prevent doing too much work on the main thread in one
- * uninterrupted rendering step, which would make the browser unresponsive.
- *
- * Note that in the case of unmodified chunks, we only split chunks if the
- * context is set to file (because otherwise they are split up further down
- * the processing into the visible and hidden context), and only split it
- * into 2 chunks, one max sized one and the rest (for reasons that are
- * unclear to me).
- *
- * @param chunks Chunks as returned from the server
- * @return Finer grained chunks.
- */
- // visible for testing
- splitLargeChunks(chunks: DiffContent[]): DiffContent[] {
- const newChunks = [];
-
- for (const chunk of chunks) {
- if (!chunk.ab) {
- for (const subChunk of this.breakdownChunk(chunk)) {
- newChunks.push(subChunk);
- }
- continue;
- }
-
- // If the context is set to "whole file", then break down the shared
- // chunks so they can be rendered incrementally. Note: this is not
- // enabled for any other context preference because manipulating the
- // chunks in this way violates assumptions by the context grouper logic.
- const MAX_GROUP_SIZE = calcMaxGroupSize(this.asyncThreshold);
- if (this.context === -1 && chunk.ab.length > MAX_GROUP_SIZE * 2) {
- // Split large shared chunks in two, where the first is the maximum
- // group size.
- newChunks.push({ab: chunk.ab.slice(0, MAX_GROUP_SIZE)});
- newChunks.push({ab: chunk.ab.slice(MAX_GROUP_SIZE)});
- } else {
- newChunks.push(chunk);
- }
- }
- return newChunks;
- }
-
- /**
- * In order to show key locations, such as comments, out of the bounds of
- * the selected context, treat them as separate chunks within the model so
- * that the content (and context surrounding it) renders correctly.
- *
- * @param chunks DiffContents as returned from server.
- * @return Finer grained DiffContents.
- */
- // visible for testing
- splitCommonChunksWithKeyLocations(chunks: DiffContent[]): DiffContent[] {
- const result = [];
- let leftLineNum = 1;
- let rightLineNum = 1;
-
- for (const chunk of chunks) {
- // If it isn't a common chunk, append it as-is and update line numbers.
- if (!chunk.ab && !chunk.skip && !chunk.common) {
- if (chunk.a) {
- leftLineNum += chunk.a.length;
- }
- if (chunk.b) {
- rightLineNum += chunk.b.length;
- }
- result.push(chunk);
- continue;
- }
-
- if (chunk.common && chunk.a!.length !== chunk.b!.length) {
- throw new Error(
- 'DiffContent with common=true must always have equal length'
- );
- }
- const numLines = this.commonChunkLength(chunk);
- const chunkEnds = this.findChunkEndsAtKeyLocations(
- numLines,
- leftLineNum,
- rightLineNum
- );
- leftLineNum += numLines;
- rightLineNum += numLines;
-
- if (chunk.skip) {
- result.push({
- ...chunk,
- skip: chunk.skip,
- keyLocation: false,
- });
- } else if (chunk.ab) {
- result.push(
- ...this.splitAtChunkEnds(chunk.ab, chunkEnds).map(
- ({lines, keyLocation}) => {
- return {
- ...chunk,
- ab: lines,
- keyLocation,
- };
- }
- )
- );
- } else if (chunk.common) {
- const aChunks = this.splitAtChunkEnds(chunk.a!, chunkEnds);
- const bChunks = this.splitAtChunkEnds(chunk.b!, chunkEnds);
- result.push(
- ...aChunks.map(({lines, keyLocation}, i) => {
- return {
- ...chunk,
- a: lines,
- b: bChunks[i].lines,
- keyLocation,
- };
- })
- );
- }
- }
-
- return result;
- }
-
- /**
- * @return Offsets of the new chunk ends, including whether it's a key
- * location.
- */
- private findChunkEndsAtKeyLocations(
- numLines: number,
- leftOffset: number,
- rightOffset: number
- ): ChunkEnd[] {
- const result = [];
- let lastChunkEnd = 0;
- for (let i = 0; i < numLines; i++) {
- // If this line should not be collapsed.
- if (
- this.keyLocations[Side.LEFT][leftOffset + i] ||
- this.keyLocations[Side.RIGHT][rightOffset + i]
- ) {
- // If any lines have been accumulated into the chunk leading up to
- // this non-collapse line, then add them as a chunk and start a new
- // one.
- if (i > lastChunkEnd) {
- result.push({offset: i, keyLocation: false});
- lastChunkEnd = i;
- }
-
- // Add the non-collapse line as its own chunk.
- result.push({offset: i + 1, keyLocation: true});
- }
- }
-
- if (numLines > lastChunkEnd) {
- result.push({offset: numLines, keyLocation: false});
- }
-
- return result;
- }
-
- private splitAtChunkEnds(lines: string[], chunkEnds: ChunkEnd[]) {
- const result = [];
- let lastChunkEndOffset = 0;
- for (const {offset, keyLocation} of chunkEnds) {
- if (lastChunkEndOffset === offset) continue;
- result.push({
- lines: lines.slice(lastChunkEndOffset, offset),
- keyLocation,
- });
- lastChunkEndOffset = offset;
- }
- return result;
- }
-
- /**
- * Converts `IntralineInfo`s return by the API to `GrLineHighlights` used
- * for rendering.
- */
- // visible for testing
- convertIntralineInfos(
- rows: string[],
- intralineInfos: number[][]
- ): Highlights[] {
- // +1 to account for the \n that is not part of the rows passed here
- const lineLengths = rows.map(r => GrAnnotation.getStringLength(r) + 1);
-
- let rowIndex = 0;
- let idx = 0;
- const normalized = [];
- for (const [skipLength, markLength] of intralineInfos) {
- let lineLength = lineLengths[rowIndex];
- let j = 0;
- while (j < skipLength) {
- if (idx === lineLength) {
- idx = 0;
- lineLength = lineLengths[++rowIndex];
- continue;
- }
- idx++;
- j++;
- }
- let lineHighlight: Highlights = {
- contentIndex: rowIndex,
- startIndex: idx,
- };
-
- j = 0;
- while (lineLength && j < markLength) {
- if (idx === lineLength) {
- idx = 0;
- lineLength = lineLengths[++rowIndex];
- normalized.push(lineHighlight);
- lineHighlight = {
- contentIndex: rowIndex,
- startIndex: idx,
- };
- continue;
- }
- idx++;
- j++;
- }
- lineHighlight.endIndex = idx;
- normalized.push(lineHighlight);
- }
- return normalized;
- }
-
- /**
- * If a group is an addition or a removal, break it down into smaller groups
- * of that type using the MAX_GROUP_SIZE. If the group is a shared chunk
- * or a delta it is returned as the single element of the result array.
- */
- // visible for testing
- breakdownChunk(chunk: DiffContent): DiffContent[] {
- let key: 'a' | 'b' | 'ab' | null = null;
- const {a, b, ab, move_details} = chunk;
- if (a?.length && !b?.length) {
- key = 'a';
- } else if (b?.length && !a?.length) {
- key = 'b';
- } else if (ab?.length) {
- key = 'ab';
- }
-
- // Move chunks should not be divided because of move label
- // positioned in the top of the chunk
- if (!key || move_details) {
- return [chunk];
- }
-
- const MAX_GROUP_SIZE = calcMaxGroupSize(this.asyncThreshold);
- return this.breakdown(chunk[key]!, MAX_GROUP_SIZE).map(subChunkLines => {
- const subChunk: DiffContent = {};
- subChunk[key!] = subChunkLines;
- if (chunk.due_to_rebase) {
- subChunk.due_to_rebase = true;
- }
- if (chunk.move_details) {
- subChunk.move_details = chunk.move_details;
- }
- return subChunk;
- });
- }
-
- /**
- * Given an array and a size, return an array of arrays where no inner array
- * is larger than that size, preserving the original order.
- */
- // visible for testing
- breakdown<T>(array: T[], size: number): T[][] {
- if (!array.length) {
- return [];
- }
- if (array.length < size) {
- return [array];
- }
-
- const head = array.slice(0, array.length - size);
- const tail = array.slice(array.length - size);
-
- return this.breakdown(head, size).concat([tail]);
- }
-}
diff --git a/polygerrit-ui/app/embed/diff-old/gr-diff-processor/gr-diff-processor_test.ts b/polygerrit-ui/app/embed/diff-old/gr-diff-processor/gr-diff-processor_test.ts
deleted file mode 100644
index 335f0d0..0000000
--- a/polygerrit-ui/app/embed/diff-old/gr-diff-processor/gr-diff-processor_test.ts
+++ /dev/null
@@ -1,1136 +0,0 @@
-/**
- * @license
- * Copyright 2016 Google LLC
- * SPDX-License-Identifier: Apache-2.0
- */
-import '../../../test/common-test-setup';
-import './gr-diff-processor';
-import {GrDiffLine} from '../gr-diff/gr-diff-line';
-import {GrDiffGroup, GrDiffGroupType} from '../gr-diff/gr-diff-group';
-import {GrDiffProcessor, State} from './gr-diff-processor';
-import {DiffContent} from '../../../types/diff';
-import {assert} from '@open-wc/testing';
-import {FILE, GrDiffLineType} from '../../../api/diff';
-
-suite('gr-diff-processor tests', () => {
- const WHOLE_FILE = -1;
- const loremIpsum =
- 'Lorem ipsum dolor sit amet, ei nonumes vituperata ius. ' +
- 'Duo animal omnesque fabellas et. Id has phaedrum dignissim ' +
- 'deterruisset, pro ei petentium comprehensam, ut vis solum dicta. ' +
- 'Eos cu aliquam labores qualisque, usu postea inermis te, et solum ' +
- 'fugit assum per.';
-
- let element: GrDiffProcessor;
- let groups: GrDiffGroup[];
-
- setup(() => {});
-
- suite('not logged in', () => {
- setup(() => {
- groups = [];
- element = new GrDiffProcessor();
- element.consumer = {
- addGroup(group: GrDiffGroup) {
- groups.push(group);
- },
- clearGroups() {
- groups = [];
- },
- };
- element.context = 4;
- });
-
- test('process loaded content', () => {
- const content: DiffContent[] = [
- {
- ab: ['<!DOCTYPE html>', '<meta charset="utf-8">'],
- },
- {
- a: [' Welcome ', ' to the wooorld of tomorrow!'],
- b: [' Hello, world!'],
- },
- {
- ab: [
- 'Leela: This is the only place the ship can’t hear us, so ',
- 'everyone pretend to shower.',
- 'Fry: Same as every day. Got it.',
- ],
- },
- ];
-
- return element.process(content, false).then(() => {
- groups.shift(); // remove portedThreadsWithoutRangeGroup
- assert.equal(groups.length, 4);
-
- let group = groups[0];
- assert.equal(group.type, GrDiffGroupType.BOTH);
- assert.equal(group.lines.length, 1);
- assert.equal(group.lines[0].text, '');
- assert.equal(group.lines[0].beforeNumber, FILE);
- assert.equal(group.lines[0].afterNumber, FILE);
-
- group = groups[1];
- assert.equal(group.type, GrDiffGroupType.BOTH);
- assert.equal(group.lines.length, 2);
-
- function beforeNumberFn(l: GrDiffLine) {
- return l.beforeNumber;
- }
- function afterNumberFn(l: GrDiffLine) {
- return l.afterNumber;
- }
- function textFn(l: GrDiffLine) {
- return l.text;
- }
-
- assert.deepEqual(group.lines.map(beforeNumberFn), [1, 2]);
- assert.deepEqual(group.lines.map(afterNumberFn), [1, 2]);
- assert.deepEqual(group.lines.map(textFn), [
- '<!DOCTYPE html>',
- '<meta charset="utf-8">',
- ]);
-
- group = groups[2];
- assert.equal(group.type, GrDiffGroupType.DELTA);
- assert.equal(group.lines.length, 3);
- assert.equal(group.adds.length, 1);
- assert.equal(group.removes.length, 2);
- assert.deepEqual(group.removes.map(beforeNumberFn), [3, 4]);
- assert.deepEqual(group.adds.map(afterNumberFn), [3]);
- assert.deepEqual(group.removes.map(textFn), [
- ' Welcome ',
- ' to the wooorld of tomorrow!',
- ]);
- assert.deepEqual(group.adds.map(textFn), [' Hello, world!']);
-
- group = groups[3];
- assert.equal(group.type, GrDiffGroupType.BOTH);
- assert.equal(group.lines.length, 3);
- assert.deepEqual(group.lines.map(beforeNumberFn), [5, 6, 7]);
- assert.deepEqual(group.lines.map(afterNumberFn), [4, 5, 6]);
- assert.deepEqual(group.lines.map(textFn), [
- 'Leela: This is the only place the ship can’t hear us, so ',
- 'everyone pretend to shower.',
- 'Fry: Same as every day. Got it.',
- ]);
- });
- });
-
- test('first group is for file', () => {
- const content = [{b: ['foo']}];
-
- return element.process(content, false).then(() => {
- groups.shift(); // remove portedThreadsWithoutRangeGroup
-
- assert.equal(groups[0].type, GrDiffGroupType.BOTH);
- assert.equal(groups[0].lines.length, 1);
- assert.equal(groups[0].lines[0].text, '');
- assert.equal(groups[0].lines[0].beforeNumber, FILE);
- assert.equal(groups[0].lines[0].afterNumber, FILE);
- });
- });
-
- suite('context groups', () => {
- test('at the beginning, larger than context', () => {
- element.context = 10;
- const content = [
- {
- ab: Array.from<string>({length: 100}).fill(
- 'all work and no play make jack a dull boy'
- ),
- },
- {a: ['all work and no play make andybons a dull boy']},
- ];
-
- return element.process(content, false).then(() => {
- groups.shift(); // remove portedThreadsWithoutRangeGroup
-
- // group[0] is the file group
-
- assert.equal(groups[1].type, GrDiffGroupType.CONTEXT_CONTROL);
- assert.instanceOf(groups[1].contextGroups[0], GrDiffGroup);
- assert.equal(groups[1].contextGroups[0].lines.length, 90);
- for (const l of groups[1].contextGroups[0].lines) {
- assert.equal(l.text, 'all work and no play make jack a dull boy');
- }
-
- assert.equal(groups[2].type, GrDiffGroupType.BOTH);
- assert.equal(groups[2].lines.length, 10);
- for (const l of groups[2].lines) {
- assert.equal(l.text, 'all work and no play make jack a dull boy');
- }
- });
- });
-
- test('at the beginning with skip chunks', async () => {
- element.context = 10;
- const content = [
- {
- ab: Array.from<string>({length: 20}).fill(
- 'all work and no play make jack a dull boy'
- ),
- },
- {skip: 43900},
- {ab: Array.from<string>({length: 30}).fill('some other content')},
- {a: ['some other content']},
- ];
-
- await element.process(content, false);
-
- groups.shift(); // remove portedThreadsWithoutRangeGroup
-
- // group[0] is the file group
-
- const commonGroup = groups[1];
-
- // Hidden context before
- assert.equal(commonGroup.type, GrDiffGroupType.CONTEXT_CONTROL);
- assert.instanceOf(commonGroup.contextGroups[0], GrDiffGroup);
- assert.equal(commonGroup.contextGroups[0].lines.length, 20);
- for (const l of commonGroup.contextGroups[0].lines) {
- assert.equal(l.text, 'all work and no play make jack a dull boy');
- }
-
- // Skipped group
- const skipGroup = commonGroup.contextGroups[1];
- assert.equal(skipGroup.skip, 43900);
- const expectedRange = {
- left: {start_line: 21, end_line: 43920},
- right: {start_line: 21, end_line: 43920},
- };
- assert.deepEqual(skipGroup.lineRange, expectedRange);
-
- // Hidden context after
- assert.equal(commonGroup.contextGroups[2].lines.length, 20);
- for (const l of commonGroup.contextGroups[2].lines) {
- assert.equal(l.text, 'some other content');
- }
-
- // Displayed lines
- assert.equal(groups[2].type, GrDiffGroupType.BOTH);
- assert.equal(groups[2].lines.length, 10);
- for (const l of groups[2].lines) {
- assert.equal(l.text, 'some other content');
- }
- });
-
- test('at the beginning, smaller than context', () => {
- element.context = 10;
- const content = [
- {
- ab: Array.from<string>({length: 5}).fill(
- 'all work and no play make jack a dull boy'
- ),
- },
- {a: ['all work and no play make andybons a dull boy']},
- ];
-
- return element.process(content, false).then(() => {
- groups.shift(); // remove portedThreadsWithoutRangeGroup
-
- // group[0] is the file group
-
- assert.equal(groups[1].type, GrDiffGroupType.BOTH);
- assert.equal(groups[1].lines.length, 5);
- for (const l of groups[1].lines) {
- assert.equal(l.text, 'all work and no play make jack a dull boy');
- }
- });
- });
-
- test('at the end, larger than context', () => {
- element.context = 10;
- const content = [
- {a: ['all work and no play make andybons a dull boy']},
- {
- ab: Array.from<string>({length: 100}).fill(
- 'all work and no play make jill a dull girl'
- ),
- },
- ];
-
- return element.process(content, false).then(() => {
- groups.shift(); // remove portedThreadsWithoutRangeGroup
-
- // group[0] is the file group
- // group[1] is the "a" group
-
- assert.equal(groups[2].type, GrDiffGroupType.BOTH);
- assert.equal(groups[2].lines.length, 10);
- for (const l of groups[2].lines) {
- assert.equal(l.text, 'all work and no play make jill a dull girl');
- }
-
- assert.equal(groups[3].type, GrDiffGroupType.CONTEXT_CONTROL);
- assert.instanceOf(groups[3].contextGroups[0], GrDiffGroup);
- assert.equal(groups[3].contextGroups[0].lines.length, 90);
- for (const l of groups[3].contextGroups[0].lines) {
- assert.equal(l.text, 'all work and no play make jill a dull girl');
- }
- });
- });
-
- test('at the end, smaller than context', () => {
- element.context = 10;
- const content = [
- {a: ['all work and no play make andybons a dull boy']},
- {
- ab: Array.from<string>({length: 5}).fill(
- 'all work and no play make jill a dull girl'
- ),
- },
- ];
-
- return element.process(content, false).then(() => {
- groups.shift(); // remove portedThreadsWithoutRangeGroup
-
- // group[0] is the file group
- // group[1] is the "a" group
-
- assert.equal(groups[2].type, GrDiffGroupType.BOTH);
- assert.equal(groups[2].lines.length, 5);
- for (const l of groups[2].lines) {
- assert.equal(l.text, 'all work and no play make jill a dull girl');
- }
- });
- });
-
- test('for interleaved ab and common: true chunks', () => {
- element.context = 10;
- const content = [
- {a: ['all work and no play make andybons a dull boy']},
- {
- ab: Array.from<string>({length: 3}).fill(
- 'all work and no play make jill a dull girl'
- ),
- },
- {
- a: Array.from<string>({length: 3}).fill(
- 'all work and no play make jill a dull girl'
- ),
- b: Array.from<string>({length: 3}).fill(
- ' all work and no play make jill a dull girl'
- ),
- common: true,
- },
- {
- ab: Array.from<string>({length: 3}).fill(
- 'all work and no play make jill a dull girl'
- ),
- },
- {
- a: Array.from<string>({length: 3}).fill(
- 'all work and no play make jill a dull girl'
- ),
- b: Array.from<string>({length: 3}).fill(
- ' all work and no play make jill a dull girl'
- ),
- common: true,
- },
- {
- ab: Array.from<string>({length: 3}).fill(
- 'all work and no play make jill a dull girl'
- ),
- },
- ];
-
- return element.process(content, false).then(() => {
- groups.shift(); // remove portedThreadsWithoutRangeGroup
-
- // group[0] is the file group
- // group[1] is the "a" group
-
- // The first three interleaved chunks are completely shown because
- // they are part of the context (3 * 3 <= 10)
-
- assert.equal(groups[2].type, GrDiffGroupType.BOTH);
- assert.equal(groups[2].lines.length, 3);
- for (const l of groups[2].lines) {
- assert.equal(l.text, 'all work and no play make jill a dull girl');
- }
-
- assert.equal(groups[3].type, GrDiffGroupType.DELTA);
- assert.equal(groups[3].lines.length, 6);
- assert.equal(groups[3].adds.length, 3);
- assert.equal(groups[3].removes.length, 3);
- for (const l of groups[3].removes) {
- assert.equal(l.text, 'all work and no play make jill a dull girl');
- }
- for (const l of groups[3].adds) {
- assert.equal(
- l.text,
- ' all work and no play make jill a dull girl'
- );
- }
-
- assert.equal(groups[4].type, GrDiffGroupType.BOTH);
- assert.equal(groups[4].lines.length, 3);
- for (const l of groups[4].lines) {
- assert.equal(l.text, 'all work and no play make jill a dull girl');
- }
-
- // The next chunk is partially shown, so it results in two groups
-
- assert.equal(groups[5].type, GrDiffGroupType.DELTA);
- assert.equal(groups[5].lines.length, 2);
- assert.equal(groups[5].adds.length, 1);
- assert.equal(groups[5].removes.length, 1);
- for (const l of groups[5].removes) {
- assert.equal(l.text, 'all work and no play make jill a dull girl');
- }
- for (const l of groups[5].adds) {
- assert.equal(
- l.text,
- ' all work and no play make jill a dull girl'
- );
- }
-
- assert.equal(groups[6].type, GrDiffGroupType.CONTEXT_CONTROL);
- assert.equal(groups[6].contextGroups.length, 2);
-
- assert.equal(groups[6].contextGroups[0].lines.length, 4);
- assert.equal(groups[6].contextGroups[0].removes.length, 2);
- assert.equal(groups[6].contextGroups[0].adds.length, 2);
- for (const l of groups[6].contextGroups[0].removes) {
- assert.equal(l.text, 'all work and no play make jill a dull girl');
- }
- for (const l of groups[6].contextGroups[0].adds) {
- assert.equal(
- l.text,
- ' all work and no play make jill a dull girl'
- );
- }
-
- // The final chunk is completely hidden
- assert.equal(groups[6].contextGroups[1].type, GrDiffGroupType.BOTH);
- assert.equal(groups[6].contextGroups[1].lines.length, 3);
- for (const l of groups[6].contextGroups[1].lines) {
- assert.equal(l.text, 'all work and no play make jill a dull girl');
- }
- });
- });
-
- test('in the middle, larger than context', () => {
- element.context = 10;
- const content = [
- {a: ['all work and no play make andybons a dull boy']},
- {
- ab: Array.from<string>({length: 100}).fill(
- 'all work and no play make jill a dull girl'
- ),
- },
- {a: ['all work and no play make andybons a dull boy']},
- ];
-
- return element.process(content, false).then(() => {
- groups.shift(); // remove portedThreadsWithoutRangeGroup
-
- // group[0] is the file group
- // group[1] is the "a" group
-
- assert.equal(groups[2].type, GrDiffGroupType.BOTH);
- assert.equal(groups[2].lines.length, 10);
- for (const l of groups[2].lines) {
- assert.equal(l.text, 'all work and no play make jill a dull girl');
- }
-
- assert.equal(groups[3].type, GrDiffGroupType.CONTEXT_CONTROL);
- assert.instanceOf(groups[3].contextGroups[0], GrDiffGroup);
- assert.equal(groups[3].contextGroups[0].lines.length, 80);
- for (const l of groups[3].contextGroups[0].lines) {
- assert.equal(l.text, 'all work and no play make jill a dull girl');
- }
-
- assert.equal(groups[4].type, GrDiffGroupType.BOTH);
- assert.equal(groups[4].lines.length, 10);
- for (const l of groups[4].lines) {
- assert.equal(l.text, 'all work and no play make jill a dull girl');
- }
- });
- });
-
- test('in the middle, smaller than context', () => {
- element.context = 10;
- const content = [
- {a: ['all work and no play make andybons a dull boy']},
- {
- ab: Array.from<string>({length: 5}).fill(
- 'all work and no play make jill a dull girl'
- ),
- },
- {a: ['all work and no play make andybons a dull boy']},
- ];
-
- return element.process(content, false).then(() => {
- groups.shift(); // remove portedThreadsWithoutRangeGroup
-
- // group[0] is the file group
- // group[1] is the "a" group
-
- assert.equal(groups[2].type, GrDiffGroupType.BOTH);
- assert.equal(groups[2].lines.length, 5);
- for (const l of groups[2].lines) {
- assert.equal(l.text, 'all work and no play make jill a dull girl');
- }
- });
- });
- });
-
- test('in the middle with skip chunks', async () => {
- element.context = 10;
- const content = [
- {a: ['all work and no play make andybons a dull boy']},
- {
- ab: Array.from<string>({length: 20}).fill(
- 'all work and no play make jill a dull girl'
- ),
- },
- {skip: 60},
- {
- ab: Array.from<string>({length: 20}).fill(
- 'all work and no play make jill a dull girl'
- ),
- },
- {a: ['all work and no play make andybons a dull boy']},
- ];
-
- await element.process(content, false);
-
- groups.shift(); // remove portedThreadsWithoutRangeGroup
-
- // group[0] is the file group
- // group[1] is the chunk with a
- // group[2] is the displayed part of ab before
-
- const commonGroup = groups[3];
-
- // Hidden context before
- assert.equal(commonGroup.type, GrDiffGroupType.CONTEXT_CONTROL);
- assert.instanceOf(commonGroup.contextGroups[0], GrDiffGroup);
- assert.equal(commonGroup.contextGroups[0].lines.length, 10);
- for (const l of commonGroup.contextGroups[0].lines) {
- assert.equal(l.text, 'all work and no play make jill a dull girl');
- }
-
- // Skipped group
- const skipGroup = commonGroup.contextGroups[1];
- assert.equal(skipGroup.skip, 60);
- const expectedRange = {
- left: {start_line: 22, end_line: 81},
- right: {start_line: 21, end_line: 80},
- };
- assert.deepEqual(skipGroup.lineRange, expectedRange);
-
- // Hidden context after
- assert.equal(commonGroup.contextGroups[2].lines.length, 10);
- for (const l of commonGroup.contextGroups[2].lines) {
- assert.equal(l.text, 'all work and no play make jill a dull girl');
- }
- // group[4] is the displayed part of the second ab
- });
-
- test('works with skip === 0', async () => {
- element.context = 3;
- const content = [
- {
- skip: 0,
- },
- {
- b: [
- '/**',
- ' * @license',
- ' * Copyright 2015 Google LLC',
- ' * SPDX-License-Identifier: Apache-2.0',
- ' */',
- "import '../../../test/common-test-setup';",
- ],
- },
- ];
- await element.process(content, false);
- });
-
- test('break up common diff chunks', () => {
- element.keyLocations = {
- left: {1: true},
- right: {10: true},
- };
-
- const content = [
- {
- ab: [
- 'copy',
- '',
- 'asdf',
- 'qwer',
- 'zxcv',
- '',
- 'http',
- '',
- 'vbnm',
- 'dfgh',
- 'yuio',
- 'sdfg',
- '1234',
- ],
- },
- ];
- const result = element.splitCommonChunksWithKeyLocations(content);
- assert.deepEqual(result, [
- {
- ab: ['copy'],
- keyLocation: true,
- },
- {
- ab: ['', 'asdf', 'qwer', 'zxcv', '', 'http', '', 'vbnm'],
- keyLocation: false,
- },
- {
- ab: ['dfgh'],
- keyLocation: true,
- },
- {
- ab: ['yuio', 'sdfg', '1234'],
- keyLocation: false,
- },
- ]);
- });
-
- test('breaks down shared chunks w/ whole-file', () => {
- const maxGroupSize = 128;
- const size = maxGroupSize * 2 + 5;
- const ab = Array(size)
- .fill(0)
- .map(() => `${Math.random()}`);
- const content = [{ab}];
- element.context = -1;
- const result = element.splitLargeChunks(content);
- assert.equal(result.length, 2);
- assert.deepEqual(result[0].ab, content[0].ab.slice(0, maxGroupSize));
- assert.deepEqual(result[1].ab, content[0].ab.slice(maxGroupSize));
- });
-
- test('breaks down added chunks', () => {
- const maxGroupSize = 128;
- const size = maxGroupSize * 2 + 5;
- const content = Array(size)
- .fill(0)
- .map(() => `${Math.random()}`);
- element.context = 5;
- const splitContent = element
- .splitLargeChunks([{a: [], b: content}])
- .map(r => r.b);
- assert.equal(splitContent.length, 3);
- assert.deepEqual(splitContent[0], content.slice(0, 5));
- assert.deepEqual(splitContent[1], content.slice(5, maxGroupSize + 5));
- assert.deepEqual(splitContent[2], content.slice(maxGroupSize + 5));
- });
-
- test('breaks down removed chunks', () => {
- const maxGroupSize = 128;
- const size = maxGroupSize * 2 + 5;
- const content = Array(size)
- .fill(0)
- .map(() => `${Math.random()}`);
- element.context = 5;
- const splitContent = element
- .splitLargeChunks([{a: content, b: []}])
- .map(r => r.a);
- assert.equal(splitContent.length, 3);
- assert.deepEqual(splitContent[0], content.slice(0, 5));
- assert.deepEqual(splitContent[1], content.slice(5, maxGroupSize + 5));
- assert.deepEqual(splitContent[2], content.slice(maxGroupSize + 5));
- });
-
- test('does not break down moved chunks', () => {
- const size = 120 * 2 + 5;
- const content = Array(size)
- .fill(0)
- .map(() => `${Math.random()}`);
- element.context = 5;
- const splitContent = element
- .splitLargeChunks([
- {
- a: content,
- b: [],
- move_details: {changed: false, range: {start: 1, end: 1}},
- },
- ])
- .map(r => r.a);
- assert.equal(splitContent.length, 1);
- assert.deepEqual(splitContent[0], content);
- });
-
- test('does not break-down common chunks w/ context', () => {
- const ab = Array(75)
- .fill(0)
- .map(() => `${Math.random()}`);
- const content = [{ab}];
- element.context = 4;
- const result = element.splitCommonChunksWithKeyLocations(content);
- assert.equal(result.length, 1);
- assert.deepEqual(result[0].ab, content[0].ab);
- assert.isFalse(result[0].keyLocation);
- });
-
- test('intraline normalization', () => {
- // The content and highlights are in the format returned by the Gerrit
- // REST API.
- let content = [
- ' <section class="summary">',
- ' <gr-formatted-text content="' +
- '[[_computeCurrentRevisionMessage(change)]]"></gr-formatted-text>',
- ' </section>',
- ];
- let highlights = [
- [31, 34],
- [42, 26],
- ];
-
- let results = element.convertIntralineInfos(content, highlights);
- assert.deepEqual(results, [
- {
- contentIndex: 0,
- startIndex: 31,
- },
- {
- contentIndex: 1,
- startIndex: 0,
- endIndex: 33,
- },
- {
- contentIndex: 1,
- endIndex: 101,
- startIndex: 75,
- },
- ]);
- const lines = element.linesFromRows(
- GrDiffLineType.BOTH,
- content,
- 0,
- highlights
- );
- assert.equal(lines.length, 3);
- assert.isTrue(lines[0].hasIntralineInfo);
- assert.equal(lines[0].highlights.length, 1);
- assert.isTrue(lines[1].hasIntralineInfo);
- assert.equal(lines[1].highlights.length, 2);
- assert.isTrue(lines[2].hasIntralineInfo);
- assert.equal(lines[2].highlights.length, 0);
-
- content = [
- ' this._path = value.path;',
- '',
- ' // When navigating away from the page, there is a ' +
- 'possibility that the',
- ' // patch number is no longer a part of the URL ' +
- '(say when navigating to',
- ' // the top-level change info view) and therefore ' +
- 'undefined in `params`.',
- ' if (!this._patchRange.patchNum) {',
- ];
- highlights = [
- [14, 17],
- [11, 70],
- [12, 67],
- [12, 67],
- [14, 29],
- ];
- results = element.convertIntralineInfos(content, highlights);
- assert.deepEqual(results, [
- {
- contentIndex: 0,
- startIndex: 14,
- endIndex: 31,
- },
- {
- contentIndex: 2,
- startIndex: 8,
- endIndex: 78,
- },
- {
- contentIndex: 3,
- startIndex: 11,
- endIndex: 78,
- },
- {
- contentIndex: 4,
- startIndex: 11,
- endIndex: 78,
- },
- {
- contentIndex: 5,
- startIndex: 12,
- endIndex: 41,
- },
- ]);
-
- content = ['🙈 a', '🙉 b', '🙊 c'];
- highlights = [[2, 7]];
- results = element.convertIntralineInfos(content, highlights);
- assert.deepEqual(results, [
- {
- contentIndex: 0,
- startIndex: 2,
- },
- {
- contentIndex: 1,
- startIndex: 0,
- },
- {
- contentIndex: 2,
- startIndex: 0,
- endIndex: 1,
- },
- ]);
- });
-
- test('isScrolling paused', () => {
- const content = Array(200).fill({ab: ['', '']});
- element.isScrolling = true;
- element.process(content, false);
- // Just the FILE and LOST groups.
- assert.equal(groups.length, 2);
- });
-
- test('isScrolling unpaused', () => {
- const content = Array(200).fill({ab: ['', '']});
- element.isScrolling = false;
- element.process(content, false);
- // More groups have been processed. How many does not matter here.
- assert.isAtLeast(groups.length, 3);
- });
-
- test('image diffs', () => {
- const content = Array(200).fill({ab: ['', '']});
- element.process(content, true);
- assert.equal(groups.length, 2);
-
- // Image diffs don't process content, just the 'FILE' line.
- assert.equal(groups[0].lines.length, 1);
- });
-
- suite('processNext', () => {
- let rows: string[];
-
- setup(() => {
- rows = loremIpsum.split(' ');
- });
-
- test('WHOLE_FILE', () => {
- element.context = WHOLE_FILE;
- const state: State = {
- lineNums: {left: 10, right: 100},
- chunkIndex: 1,
- };
- const chunks = [{a: ['foo']}, {ab: rows}, {a: ['bar']}];
- const result = element.processNext(state, chunks);
-
- // Results in one, uncollapsed group with all rows.
- assert.equal(result.groups.length, 1);
- assert.equal(result.groups[0].type, GrDiffGroupType.BOTH);
- assert.equal(result.groups[0].lines.length, rows.length);
-
- // Line numbers are set correctly.
- assert.equal(
- result.groups[0].lines[0].beforeNumber,
- state.lineNums.left + 1
- );
- assert.equal(
- result.groups[0].lines[0].afterNumber,
- state.lineNums.right + 1
- );
-
- assert.equal(
- result.groups[0].lines[rows.length - 1].beforeNumber,
- state.lineNums.left + rows.length
- );
- assert.equal(
- result.groups[0].lines[rows.length - 1].afterNumber,
- state.lineNums.right + rows.length
- );
- });
-
- test('WHOLE_FILE with skip chunks still get collapsed', () => {
- element.context = WHOLE_FILE;
- const lineNums = {left: 10, right: 100};
- const state = {
- lineNums,
- chunkIndex: 1,
- };
- const skip = 10000;
- const chunks = [{a: ['foo']}, {skip}, {ab: rows}, {a: ['bar']}];
- const result = element.processNext(state, chunks);
- // Results in one, uncollapsed group with all rows.
- assert.equal(result.groups.length, 1);
- assert.equal(result.groups[0].type, GrDiffGroupType.CONTEXT_CONTROL);
-
- // Skip and ab group are hidden in the same context control
- assert.equal(result.groups[0].contextGroups.length, 2);
- const [skippedGroup, abGroup] = result.groups[0].contextGroups;
-
- // Line numbers are set correctly.
- assert.deepEqual(skippedGroup.lineRange, {
- left: {
- start_line: lineNums.left + 1,
- end_line: lineNums.left + skip,
- },
- right: {
- start_line: lineNums.right + 1,
- end_line: lineNums.right + skip,
- },
- });
-
- assert.deepEqual(abGroup.lineRange, {
- left: {
- start_line: lineNums.left + skip + 1,
- end_line: lineNums.left + skip + rows.length,
- },
- right: {
- start_line: lineNums.right + skip + 1,
- end_line: lineNums.right + skip + rows.length,
- },
- });
- });
-
- test('with context', () => {
- element.context = 10;
- const state = {
- lineNums: {left: 10, right: 100},
- chunkIndex: 1,
- };
- const chunks = [{a: ['foo']}, {ab: rows}, {a: ['bar']}];
- const result = element.processNext(state, chunks);
- const expectedCollapseSize = rows.length - 2 * element.context;
-
- assert.equal(result.groups.length, 3, 'Results in three groups');
-
- // The first and last are uncollapsed context, whereas the middle has
- // a single context-control line.
- assert.equal(result.groups[0].lines.length, element.context);
- assert.equal(result.groups[2].lines.length, element.context);
-
- // The collapsed group has the hidden lines as its context group.
- assert.equal(
- result.groups[1].contextGroups[0].lines.length,
- expectedCollapseSize
- );
- });
-
- test('first', () => {
- element.context = 10;
- const state = {
- lineNums: {left: 10, right: 100},
- chunkIndex: 0,
- };
- const chunks = [{ab: rows}, {a: ['foo']}, {a: ['bar']}];
- const result = element.processNext(state, chunks);
- const expectedCollapseSize = rows.length - element.context;
-
- assert.equal(result.groups.length, 2, 'Results in two groups');
-
- // Only the first group is collapsed.
- assert.equal(result.groups[1].lines.length, element.context);
-
- // The collapsed group has the hidden lines as its context group.
- assert.equal(
- result.groups[0].contextGroups[0].lines.length,
- expectedCollapseSize
- );
- });
-
- test('few-rows', () => {
- // Only ten rows.
- rows = rows.slice(0, 10);
- element.context = 10;
- const state = {
- lineNums: {left: 10, right: 100},
- chunkIndex: 0,
- };
- const chunks = [{ab: rows}, {a: ['foo']}, {a: ['bar']}];
- const result = element.processNext(state, chunks);
-
- // Results in one uncollapsed group with all rows.
- assert.equal(result.groups.length, 1, 'Results in one group');
- assert.equal(result.groups[0].lines.length, rows.length);
- });
-
- test('no single line collapse', () => {
- rows = rows.slice(0, 7);
- element.context = 3;
- const state = {
- lineNums: {left: 10, right: 100},
- chunkIndex: 1,
- };
- const chunks = [{a: ['foo']}, {ab: rows}, {a: ['bar']}];
- const result = element.processNext(state, chunks);
-
- // Results in one uncollapsed group with all rows.
- assert.equal(result.groups.length, 1, 'Results in one group');
- assert.equal(result.groups[0].lines.length, rows.length);
- });
-
- suite('with key location', () => {
- let state: State;
- let chunks: DiffContent[];
-
- setup(() => {
- state = {
- lineNums: {left: 10, right: 100},
- chunkIndex: 0,
- };
- element.context = 10;
- chunks = [{ab: rows}, {ab: ['foo'], keyLocation: true}, {ab: rows}];
- });
-
- test('context before', () => {
- state.chunkIndex = 0;
- const result = element.processNext(state, chunks);
-
- // The first chunk is split into two groups:
- // 1) A context-control, hiding everything but the context before
- // the key location.
- // 2) The context before the key location.
- // The key location is not processed in this call to processNext
- assert.equal(result.groups.length, 2);
- // The collapsed group has the hidden lines as its context group.
- assert.equal(
- result.groups[0].contextGroups[0].lines.length,
- rows.length - element.context
- );
- assert.equal(result.groups[1].lines.length, element.context);
- });
-
- test('key location itself', () => {
- state.chunkIndex = 1;
- const result = element.processNext(state, chunks);
-
- // The second chunk results in a single group, that is just the
- // line with the key location
- assert.equal(result.groups.length, 1);
- assert.equal(result.groups[0].lines.length, 1);
- assert.equal(result.lineDelta.left, 1);
- assert.equal(result.lineDelta.right, 1);
- });
-
- test('context after', () => {
- state.chunkIndex = 2;
- const result = element.processNext(state, chunks);
-
- // The last chunk is split into two groups:
- // 1) The context after the key location.
- // 1) A context-control, hiding everything but the context after the
- // key location.
- assert.equal(result.groups.length, 2);
- assert.equal(result.groups[0].lines.length, element.context);
- // The collapsed group has the hidden lines as its context group.
- assert.equal(
- result.groups[1].contextGroups[0].lines.length,
- rows.length - element.context
- );
- });
- });
- });
-
- suite('gr-diff-processor helpers', () => {
- let rows: string[];
-
- setup(() => {
- rows = loremIpsum.split(' ');
- });
-
- test('linesFromRows', () => {
- const startLineNum = 10;
- let result = element.linesFromRows(
- GrDiffLineType.ADD,
- rows,
- startLineNum + 1
- );
-
- assert.equal(result.length, rows.length);
- assert.equal(result[0].type, GrDiffLineType.ADD);
- assert.notOk(result[0].hasIntralineInfo);
- assert.equal(result[0].afterNumber, startLineNum + 1);
- assert.notOk(result[0].beforeNumber);
- assert.equal(
- result[result.length - 1].afterNumber,
- startLineNum + rows.length
- );
- assert.notOk(result[result.length - 1].beforeNumber);
-
- result = element.linesFromRows(
- GrDiffLineType.REMOVE,
- rows,
- startLineNum + 1
- );
-
- assert.equal(result.length, rows.length);
- assert.equal(result[0].type, GrDiffLineType.REMOVE);
- assert.notOk(result[0].hasIntralineInfo);
- assert.equal(result[0].beforeNumber, startLineNum + 1);
- assert.notOk(result[0].afterNumber);
- assert.equal(
- result[result.length - 1].beforeNumber,
- startLineNum + rows.length
- );
- assert.notOk(result[result.length - 1].afterNumber);
- });
- });
-
- suite('breakdown*', () => {
- test('breakdownChunk breaks down additions', () => {
- const breakdownSpy = sinon.spy(element, 'breakdown');
- const chunk = {b: ['blah', 'blah', 'blah']};
- const result = element.breakdownChunk(chunk);
- assert.deepEqual(result, [chunk]);
- assert.isTrue(breakdownSpy.called);
- });
-
- test('breakdownChunk keeps due_to_rebase for broken down additions', () => {
- sinon.spy(element, 'breakdown');
- const chunk = {b: ['blah', 'blah', 'blah'], due_to_rebase: true};
- const result = element.breakdownChunk(chunk);
- for (const subResult of result) {
- assert.isTrue(subResult.due_to_rebase);
- }
- });
-
- test('breakdown common case', () => {
- const array = 'Lorem ipsum dolor sit amet, suspendisse inceptos'.split(
- ' '
- );
- const size = 3;
-
- const result = element.breakdown(array, size);
-
- for (const subResult of result) {
- assert.isAtMost(subResult.length, size);
- }
- const flattened = result.reduce((a, b) => a.concat(b), []);
- assert.deepEqual(flattened, array);
- });
-
- test('breakdown smaller than size', () => {
- const array = 'Lorem ipsum dolor sit amet, suspendisse inceptos'.split(
- ' '
- );
- const size = 10;
- const expected = [array];
-
- const result = element.breakdown(array, size);
-
- assert.deepEqual(result, expected);
- });
-
- test('breakdown empty', () => {
- const array: string[] = [];
- const size = 10;
- const expected: string[][] = [];
-
- const result = element.breakdown(array, size);
-
- assert.deepEqual(result, expected);
- });
- });
- });
-});
diff --git a/polygerrit-ui/app/embed/diff-old/gr-diff-selection/gr-diff-selection.ts b/polygerrit-ui/app/embed/diff-old/gr-diff-selection/gr-diff-selection.ts
deleted file mode 100644
index 407b403..0000000
--- a/polygerrit-ui/app/embed/diff-old/gr-diff-selection/gr-diff-selection.ts
+++ /dev/null
@@ -1,189 +0,0 @@
-/**
- * @license
- * Copyright 2016 Google LLC
- * SPDX-License-Identifier: Apache-2.0
- */
-import '../../../styles/shared-styles';
-import {normalize} from '../gr-diff-highlight/gr-range-normalizer';
-import {
- descendedFromClass,
- parentWithClass,
- querySelectorAll,
-} from '../../../utils/dom-util';
-import {DiffInfo} from '../../../types/diff';
-import {Side} from '../../../constants/constants';
-import {
- getLineElByChild,
- getSide,
- getSideByLineEl,
- isThreadEl,
-} from '../../diff/gr-diff/gr-diff-utils';
-import {getContentFromDiff} from '../../../utils/diff-util';
-
-/**
- * Possible CSS classes indicating the state of selection. Dynamically added/
- * removed based on where the user clicks within the diff.
- */
-const SelectionClass = {
- COMMENT: 'selected-comment',
- LEFT: 'selected-left',
- RIGHT: 'selected-right',
- BLAME: 'selected-blame',
-};
-
-function selectionClassForSide(side?: Side) {
- return side === Side.LEFT ? SelectionClass.LEFT : SelectionClass.RIGHT;
-}
-
-export class GrDiffSelection {
- // visible for testing
- diff?: DiffInfo;
-
- // visible for testing
- diffTable?: HTMLElement;
-
- init(diff: DiffInfo, diffTable: HTMLElement) {
- this.cleanup();
- this.diff = diff;
- this.diffTable = diffTable;
- this.diffTable.classList.add(SelectionClass.RIGHT);
- this.diffTable.addEventListener('copy', this.handleCopy);
- this.diffTable.addEventListener('mousedown', this.handleDown);
- }
-
- cleanup() {
- if (!this.diffTable) return;
- this.diffTable.removeEventListener('copy', this.handleCopy);
- this.diffTable.removeEventListener('mousedown', this.handleDown);
- }
-
- handleDown = (e: Event) => {
- const target = e.target;
- if (!(target instanceof Element)) return;
-
- const commentEl = parentWithClass(target, 'comment-thread', this.diffTable);
- if (commentEl && isThreadEl(commentEl)) {
- this.setClasses([
- SelectionClass.COMMENT,
- selectionClassForSide(getSide(commentEl)),
- ]);
- return;
- }
-
- const blameSelected = descendedFromClass(target, 'blame', this.diffTable);
- if (blameSelected) {
- this.setClasses([SelectionClass.BLAME]);
- return;
- }
-
- // This works for both, the content and the line number cells.
- const lineEl = getLineElByChild(target);
- if (lineEl) {
- this.setClasses([selectionClassForSide(getSideByLineEl(lineEl))]);
- return;
- }
- };
-
- /**
- * Set the provided list of classes on the element, to the exclusion of all
- * other SelectionClass values.
- */
- setClasses(targetClasses: string[]) {
- if (!this.diffTable) return;
- // Remove any selection classes that do not belong.
- for (const className of Object.values(SelectionClass)) {
- if (!targetClasses.includes(className)) {
- this.diffTable.classList.remove(className);
- }
- }
- // Add new selection classes iff they are not already present.
- for (const targetClass of targetClasses) {
- if (!this.diffTable.classList.contains(targetClass)) {
- this.diffTable.classList.add(targetClass);
- }
- }
- }
-
- handleCopy = (e: ClipboardEvent) => {
- const target = e.composedPath()[0];
- if (!(target instanceof Element)) return;
- if (target instanceof HTMLTextAreaElement) return;
- if (!descendedFromClass(target, 'diff-row', this.diffTable)) return;
- if (!this.diffTable) return;
- if (this.diffTable.classList.contains(SelectionClass.COMMENT)) return;
-
- const lineEl = getLineElByChild(target);
- if (!lineEl) return;
- const side = getSideByLineEl(lineEl);
- const text = this.getSelectedText(side);
- if (text && e.clipboardData) {
- e.clipboardData.setData('Text', text);
- e.preventDefault();
- }
- };
-
- getSelection() {
- const diffHosts = querySelectorAll(document.body, 'gr-diff');
- if (!diffHosts.length) return document.getSelection();
-
- const curDiffHost = diffHosts.find(diffHost => {
- if (!diffHost?.shadowRoot?.getSelection) return false;
- const selection = diffHost.shadowRoot.getSelection();
- // Pick the one with valid selection:
- // https://developer.mozilla.org/en-US/docs/Web/API/Selection/type
- return selection && selection.type !== 'None';
- });
-
- return curDiffHost?.shadowRoot?.getSelection
- ? curDiffHost.shadowRoot.getSelection()
- : document.getSelection();
- }
-
- /**
- * Get the text of the current selection. If commentSelected is
- * true, it returns only the text of comments within the selection.
- * Otherwise it returns the text of the selected diff region.
- *
- * @param side The side that is selected.
- * @param commentSelected Whether or not a comment is selected.
- * @return The selected text.
- */
- getSelectedText(side: Side) {
- if (!this.diff) return '';
- const sel = this.getSelection();
- if (!sel || sel.rangeCount !== 1) {
- return ''; // No multi-select support yet.
- }
- const range = normalize(sel.getRangeAt(0));
- const startLineEl = getLineElByChild(range.startContainer);
- if (!startLineEl) return;
- const endLineEl = getLineElByChild(range.endContainer);
- // Happens when triple click in side-by-side mode with other side empty.
- const endsAtOtherEmptySide =
- !endLineEl &&
- range.endOffset === 0 &&
- range.endContainer.nodeName === 'TD' &&
- range.endContainer instanceof HTMLTableCellElement &&
- (range.endContainer.classList.contains('left') ||
- range.endContainer.classList.contains('right'));
- const startLineDataValue = startLineEl.getAttribute('data-value');
- if (!startLineDataValue) return;
- const startLineNum = Number(startLineDataValue);
- let endLineNum;
- if (endsAtOtherEmptySide) {
- endLineNum = startLineNum + 1;
- } else if (endLineEl) {
- const endLineDataValue = endLineEl.getAttribute('data-value');
- if (endLineDataValue) endLineNum = Number(endLineDataValue);
- }
-
- return getContentFromDiff(
- this.diff,
- startLineNum,
- range.startOffset,
- endLineNum,
- range.endOffset,
- side
- );
- }
-}
diff --git a/polygerrit-ui/app/embed/diff-old/gr-diff-selection/gr-diff-selection_test.ts b/polygerrit-ui/app/embed/diff-old/gr-diff-selection/gr-diff-selection_test.ts
deleted file mode 100644
index f216e04..0000000
--- a/polygerrit-ui/app/embed/diff-old/gr-diff-selection/gr-diff-selection_test.ts
+++ /dev/null
@@ -1,219 +0,0 @@
-/**
- * @license
- * Copyright 2016 Google LLC
- * SPDX-License-Identifier: Apache-2.0
- */
-import '../../../test/common-test-setup';
-import './gr-diff-selection';
-import '../gr-diff/gr-diff';
-import '../../../elements/shared/gr-comment-thread/gr-comment-thread';
-import {GrDiffSelection} from './gr-diff-selection';
-import {createDiff} from '../../../test/test-data-generators';
-import {DiffInfo, Side} from '../../../api/diff';
-import {fixture, html, assert} from '@open-wc/testing';
-import {mouseDown} from '../../../test/test-utils';
-import {GrDiff} from '../gr-diff/gr-diff';
-import {waitForEventOnce} from '../../../utils/event-util';
-import {createDefaultDiffPrefs} from '../../../constants/constants';
-
-function firstTextNode(el: HTMLElement) {
- return [...el.childNodes].filter(node => node.nodeType === Node.TEXT_NODE)[0];
-}
-
-suite('gr-diff-selection', () => {
- let element: GrDiffSelection;
- let diffTable: HTMLElement;
- let grDiff: GrDiff;
-
- const emulateCopyOn = function (target: HTMLElement | null) {
- const fakeEvent = {
- target,
- preventDefault: sinon.stub(),
- composedPath() {
- return [target];
- },
- clipboardData: {
- setData: sinon.stub(),
- },
- };
- element.handleCopy(fakeEvent as unknown as ClipboardEvent);
- return fakeEvent;
- };
-
- setup(async () => {
- grDiff = await fixture<GrDiff>(html`<gr-diff></gr-diff>`);
- element = grDiff.diffSelection;
-
- const diff: DiffInfo = {
- ...createDiff(),
- content: [
- {
- a: ['ba ba'],
- b: ['some other text'],
- },
- {
- a: ['zin'],
- b: ['more more more'],
- },
- {
- a: ['ga ga'],
- b: ['some other text'],
- },
- ],
- };
- grDiff.prefs = createDefaultDiffPrefs();
- grDiff.diff = diff;
- await waitForEventOnce(grDiff, 'render');
- assert.isOk(element.diffTable);
- diffTable = element.diffTable!;
- });
-
- test('applies selected-left on left side click', () => {
- diffTable.classList.add('selected-right');
- const lineNumberEl = diffTable.querySelector<HTMLElement>('.lineNum.left');
- if (!lineNumberEl) assert.fail('line number element missing');
- mouseDown(lineNumberEl);
- assert.isTrue(
- diffTable.classList.contains('selected-left'),
- 'adds selected-left'
- );
- assert.isFalse(
- diffTable.classList.contains('selected-right'),
- 'removes selected-right'
- );
- });
-
- test('applies selected-right on right side click', () => {
- diffTable.classList.add('selected-left');
- const lineNumberEl = diffTable.querySelector<HTMLElement>('.lineNum.right');
- if (!lineNumberEl) assert.fail('line number element missing');
- mouseDown(lineNumberEl);
- assert.isTrue(
- diffTable.classList.contains('selected-right'),
- 'adds selected-right'
- );
- assert.isFalse(
- diffTable.classList.contains('selected-left'),
- 'removes selected-left'
- );
- });
-
- test('applies selected-blame on blame click', () => {
- diffTable.classList.add('selected-left');
- const blameDiv = document.createElement('div');
- blameDiv.classList.add('blame');
- diffTable.appendChild(blameDiv);
- mouseDown(blameDiv);
- assert.isTrue(
- diffTable.classList.contains('selected-blame'),
- 'adds selected-right'
- );
- assert.isFalse(
- diffTable.classList.contains('selected-left'),
- 'removes selected-left'
- );
- });
-
- test('ignores copy for non-content Element', () => {
- const getSelectedTextStub = sinon.stub(element, 'getSelectedText');
- emulateCopyOn(diffTable.querySelector('.not-diff-row'));
- assert.isFalse(getSelectedTextStub.called);
- });
-
- test('asks for text for left side Elements', () => {
- const getSelectedTextStub = sinon.stub(element, 'getSelectedText');
- emulateCopyOn(diffTable.querySelector('div.contentText'));
- assert.deepEqual([Side.LEFT], getSelectedTextStub.lastCall.args);
- });
-
- test('reacts to copy for content Elements', () => {
- const getSelectedTextStub = sinon.stub(element, 'getSelectedText');
- emulateCopyOn(diffTable.querySelector('div.contentText'));
- assert.isTrue(getSelectedTextStub.called);
- });
-
- test('copy event is prevented for content Elements', () => {
- const getSelectedTextStub = sinon.stub(element, 'getSelectedText');
- getSelectedTextStub.returns('test');
- const event = emulateCopyOn(diffTable.querySelector('div.contentText'));
- assert.isTrue(event.preventDefault.called);
- });
-
- test('inserts text into clipboard on copy', () => {
- sinon.stub(element, 'getSelectedText').returns('the text');
- const event = emulateCopyOn(diffTable.querySelector('div.contentText'));
- assert.deepEqual(
- ['Text', 'the text'],
- event.clipboardData.setData.lastCall.args
- );
- });
-
- test('setClasses adds given SelectionClass values, removes others', () => {
- diffTable.classList.add('selected-right');
- element.setClasses(['selected-comment', 'selected-left']);
- assert.isTrue(diffTable.classList.contains('selected-comment'));
- assert.isTrue(diffTable.classList.contains('selected-left'));
- assert.isFalse(diffTable.classList.contains('selected-right'));
- assert.isFalse(diffTable.classList.contains('selected-blame'));
-
- element.setClasses(['selected-blame']);
- assert.isFalse(diffTable.classList.contains('selected-comment'));
- assert.isFalse(diffTable.classList.contains('selected-left'));
- assert.isFalse(diffTable.classList.contains('selected-right'));
- assert.isTrue(diffTable.classList.contains('selected-blame'));
- });
-
- test('setClasses removes before it ads', () => {
- diffTable.classList.add('selected-right');
- const addStub = sinon.stub(diffTable.classList, 'add');
- const removeStub = sinon
- .stub(diffTable.classList, 'remove')
- .callsFake(() => {
- assert.isFalse(addStub.called);
- });
- element.setClasses(['selected-comment', 'selected-left']);
- assert.isTrue(addStub.called);
- assert.isTrue(removeStub.called);
- });
-
- test('copies content correctly', () => {
- diffTable.classList.add('selected-left');
- diffTable.classList.remove('selected-right');
-
- const selection = document.getSelection();
- if (selection === null) assert.fail('no selection');
- selection.removeAllRanges();
- const range = document.createRange();
- const texts = diffTable.querySelectorAll<HTMLElement>('gr-diff-text');
- range.setStart(firstTextNode(texts[0]), 3);
- range.setEnd(firstTextNode(texts[4]), 2);
- selection.addRange(range);
-
- assert.equal(element.getSelectedText(Side.LEFT), 'ba\nzin\nga');
- });
-
- test('defers to default behavior for textarea', () => {
- diffTable.classList.add('selected-left');
- diffTable.classList.remove('selected-right');
- const selectedTextSpy = sinon.spy(element, 'getSelectedText');
- emulateCopyOn(diffTable.querySelector('textarea'));
-
- assert.isFalse(selectedTextSpy.called);
- });
-
- test('regression test for 4794', () => {
- diffTable.classList.add('selected-right');
- diffTable.classList.remove('selected-left');
-
- const selection = document.getSelection();
- if (!selection) assert.fail('no selection');
- selection.removeAllRanges();
- const range = document.createRange();
- const texts = diffTable.querySelectorAll<HTMLElement>('gr-diff-text');
- range.setStart(firstTextNode(texts[1]), 4);
- range.setEnd(firstTextNode(texts[1]), 10);
- selection.addRange(range);
-
- assert.equal(element.getSelectedText(Side.RIGHT), ' other');
- });
-});
diff --git a/polygerrit-ui/app/embed/diff-old/gr-diff/gr-diff-group.ts b/polygerrit-ui/app/embed/diff-old/gr-diff/gr-diff-group.ts
deleted file mode 100644
index 771e298..0000000
--- a/polygerrit-ui/app/embed/diff-old/gr-diff/gr-diff-group.ts
+++ /dev/null
@@ -1,520 +0,0 @@
-/**
- * @license
- * Copyright 2016 Google LLC
- * SPDX-License-Identifier: Apache-2.0
- */
-import {BLANK_LINE, GrDiffLine} from './gr-diff-line';
-import {GrDiffLineType, LineNumber, LineRange, Side} from '../../../api/diff';
-import {assertIsDefined, assert} from '../../../utils/common-util';
-import {untilRendered} from '../../../utils/dom-util';
-import {isDefined} from '../../../types/types';
-import {LitElement} from 'lit';
-
-export enum GrDiffGroupType {
- /** Unchanged context. */
- BOTH = 'both',
-
- /** A widget used to show more context. */
- CONTEXT_CONTROL = 'contextControl',
-
- /** Added, removed or modified chunk. */
- DELTA = 'delta',
-}
-
-export interface GrDiffLinePair {
- left: GrDiffLine;
- right: GrDiffLine;
-}
-
-/**
- * Hides lines in the given range behind a context control group.
- *
- * Groups that would be partially visible are split into their visible and
- * hidden parts, respectively.
- * The groups need to be "common groups", meaning they have to have either
- * originated from an `ab` chunk, or from an `a`+`b` chunk with
- * `common: true`.
- *
- * If the hidden range is 3 lines or less, nothing is hidden and no context
- * control group is created.
- *
- * @param groups Common groups, ordered by their line ranges.
- * @param hiddenStart The first element to be hidden, as a
- * non-negative line number offset relative to the first group's start
- * line, left and right respectively.
- * @param hiddenEnd The first visible element after the hidden range,
- * as a non-negative line number offset relative to the first group's
- * start line, left and right respectively.
- */
-export function hideInContextControl(
- groups: readonly GrDiffGroup[],
- hiddenStart: number,
- hiddenEnd: number
-): GrDiffGroup[] {
- if (groups.length === 0) return [];
- // Clamp hiddenStart and hiddenEnd - inspired by e.g. substring
- hiddenStart = Math.max(hiddenStart, 0);
- hiddenEnd = Math.max(hiddenEnd, hiddenStart);
-
- let before: GrDiffGroup[] = [];
- let hidden = groups;
- let after: readonly GrDiffGroup[] = [];
-
- const numHidden = hiddenEnd - hiddenStart;
-
- // Showing a context control row for less than 4 lines does not make much,
- // because then that row would consume as much space as the collapsed code.
- if (numHidden > 3) {
- if (hiddenStart) {
- [before, hidden] = splitCommonGroups(hidden, hiddenStart);
- }
- if (hiddenEnd) {
- let beforeLength = 0;
- if (before.length > 0) {
- const beforeStart = before[0].lineRange.left.start_line;
- const beforeEnd = before[before.length - 1].lineRange.left.end_line;
- beforeLength = beforeEnd - beforeStart + 1;
- }
- [hidden, after] = splitCommonGroups(hidden, hiddenEnd - beforeLength);
- }
- } else {
- [hidden, after] = [[], hidden];
- }
-
- const result = [...before];
- if (hidden.length) {
- result.push(
- new GrDiffGroup({
- type: GrDiffGroupType.CONTEXT_CONTROL,
- contextGroups: [...hidden],
- })
- );
- }
- result.push(...after);
- return result;
-}
-
-/**
- * Splits a group in two, defined by leftSplit and rightSplit. Primarily to be
- * used in function splitCommonGroups
- * Groups with some lines before and some lines after the split will be split
- * into two groups, which will be put into the first and second list.
- *
- * @param group The group to be split in two
- * @param leftSplit The line number relative to the split on the left side
- * @param rightSplit The line number relative to the split on the right side
- * @return two new groups, one before the split and another after it
- */
-function splitGroupInTwo(
- group: GrDiffGroup,
- leftSplit: number,
- rightSplit: number
-) {
- let beforeSplit: GrDiffGroup | undefined;
- let afterSplit: GrDiffGroup | undefined;
- // split line is in the middle of a group, we need to break the group
- // in lines before and after the split.
- if (group.skip) {
- // Currently we assume skip chunks "refuse" to be split. Expanding this
- // group will in the future mean load more data - and therefore we want to
- // fire an event when user wants to do it.
- const closerToStartThanEnd =
- leftSplit - group.lineRange.left.start_line <
- group.lineRange.right.end_line - leftSplit;
- if (closerToStartThanEnd) {
- afterSplit = group;
- } else {
- beforeSplit = group;
- }
- } else {
- const before = [];
- const after = [];
- for (const line of group.lines) {
- if (
- (line.beforeNumber &&
- typeof line.beforeNumber === 'number' &&
- line.beforeNumber < leftSplit) ||
- (line.afterNumber &&
- typeof line.afterNumber === 'number' &&
- line.afterNumber < rightSplit)
- ) {
- before.push(line);
- } else {
- after.push(line);
- }
- }
- if (before.length) {
- beforeSplit =
- before.length === group.lines.length
- ? group
- : group.cloneWithLines(before);
- }
- if (after.length) {
- afterSplit =
- after.length === group.lines.length
- ? group
- : group.cloneWithLines(after);
- }
- }
- return {beforeSplit, afterSplit};
-}
-
-/**
- * Splits a list of common groups into two lists of groups.
- *
- * Groups where all lines are before or all lines are after the split will be
- * retained as is and put into the first or second list respectively. Groups
- * with some lines before and some lines after the split will be split into
- * two groups, which will be put into the first and second list.
- *
- * @param split A line number offset relative to the first group's
- * start line at which the groups should be split.
- * @return The outer array has 2 elements, the
- * list of groups before and the list of groups after the split.
- */
-function splitCommonGroups(
- groups: readonly GrDiffGroup[],
- split: number
-): GrDiffGroup[][] {
- if (groups.length === 0) return [[], []];
- const leftSplit = groups[0].lineRange.left.start_line + split;
- const rightSplit = groups[0].lineRange.right.start_line + split;
-
- const beforeGroups = [];
- const afterGroups = [];
- for (const group of groups) {
- const isCompletelyBefore =
- group.lineRange.left.end_line < leftSplit ||
- group.lineRange.right.end_line < rightSplit;
- const isCompletelyAfter =
- leftSplit <= group.lineRange.left.start_line ||
- rightSplit <= group.lineRange.right.start_line;
- if (isCompletelyBefore) {
- beforeGroups.push(group);
- } else if (isCompletelyAfter) {
- afterGroups.push(group);
- } else {
- const {beforeSplit, afterSplit} = splitGroupInTwo(
- group,
- leftSplit,
- rightSplit
- );
- if (beforeSplit) {
- beforeGroups.push(beforeSplit);
- }
- if (afterSplit) {
- afterGroups.push(afterSplit);
- }
- }
- }
- return [beforeGroups, afterGroups];
-}
-
-export interface GrMoveDetails {
- changed: boolean;
- range?: {
- start: number;
- end: number;
- };
-}
-
-/** A chunk of the diff that should be rendered together. */
-export class GrDiffGroup {
- constructor(
- options:
- | {
- type: GrDiffGroupType.BOTH | GrDiffGroupType.DELTA;
- lines?: GrDiffLine[];
- skip?: undefined;
- moveDetails?: GrMoveDetails;
- dueToRebase?: boolean;
- ignoredWhitespaceOnly?: boolean;
- keyLocation?: boolean;
- }
- | {
- type: GrDiffGroupType.BOTH | GrDiffGroupType.DELTA;
- lines?: undefined;
- skip: number;
- offsetLeft: number;
- offsetRight: number;
- moveDetails?: GrMoveDetails;
- dueToRebase?: boolean;
- ignoredWhitespaceOnly?: boolean;
- keyLocation?: boolean;
- }
- | {
- type: GrDiffGroupType.CONTEXT_CONTROL;
- contextGroups: GrDiffGroup[];
- }
- ) {
- this.type = options.type;
- switch (options.type) {
- case GrDiffGroupType.BOTH:
- case GrDiffGroupType.DELTA: {
- this.moveDetails = options.moveDetails;
- this.dueToRebase = options.dueToRebase ?? false;
- this.ignoredWhitespaceOnly = options.ignoredWhitespaceOnly ?? false;
- this.keyLocation = options.keyLocation ?? false;
- if (options.skip && options.lines) {
- throw new Error('Cannot set skip and lines');
- }
- this.skip = options.skip;
- if (options.skip !== undefined) {
- this.lineRange = {
- left: {
- start_line: options.offsetLeft,
- end_line: options.offsetLeft + options.skip - 1,
- },
- right: {
- start_line: options.offsetRight,
- end_line: options.offsetRight + options.skip - 1,
- },
- };
- } else {
- assertIsDefined(options.lines);
- assert(options.lines.length > 0, 'diff group must have lines');
- for (const line of options.lines) {
- this.addLine(line);
- }
- }
- break;
- }
- case GrDiffGroupType.CONTEXT_CONTROL: {
- this.contextGroups = options.contextGroups;
- if (this.contextGroups.length > 0) {
- const firstGroup = this.contextGroups[0];
- const lastGroup = this.contextGroups[this.contextGroups.length - 1];
- this.lineRange = {
- left: {
- start_line: firstGroup.lineRange.left.start_line,
- end_line: lastGroup.lineRange.left.end_line,
- },
- right: {
- start_line: firstGroup.lineRange.right.start_line,
- end_line: lastGroup.lineRange.right.end_line,
- },
- };
- }
- break;
- }
- default:
- throw new Error(`Unknown group type: ${this.type}`);
- }
- }
-
- readonly type: GrDiffGroupType;
-
- readonly dueToRebase: boolean = false;
-
- /**
- * True means all changes in this line are whitespace changes that should
- * not be highlighted as changed as per the user settings.
- */
- readonly ignoredWhitespaceOnly: boolean = false;
-
- /**
- * True means it should not be collapsed (because it was in the URL, or
- * there is a comment on that line)
- */
- readonly keyLocation: boolean = false;
-
- /**
- * Once rendered the diff builder sets this to the diff section element.
- */
- element?: HTMLElement;
-
- readonly lines: GrDiffLine[] = [];
-
- readonly adds: GrDiffLine[] = [];
-
- readonly removes: GrDiffLine[] = [];
-
- readonly contextGroups: GrDiffGroup[] = [];
-
- readonly skip?: number;
-
- /** Both start and end line are inclusive. */
- readonly lineRange: {[side in Side]: LineRange} = {
- [Side.LEFT]: {start_line: 0, end_line: 0},
- [Side.RIGHT]: {start_line: 0, end_line: 0},
- };
-
- readonly moveDetails?: GrMoveDetails;
-
- /**
- * Creates a new group with the same properties but different lines.
- *
- * The element property is not copied, because the original element is still a
- * rendering of the old lines, so that would not make sense.
- */
- cloneWithLines(lines: GrDiffLine[]): GrDiffGroup {
- if (
- this.type !== GrDiffGroupType.BOTH &&
- this.type !== GrDiffGroupType.DELTA
- ) {
- throw new Error('Cannot clone context group with lines');
- }
- const group = new GrDiffGroup({
- type: this.type,
- lines,
- dueToRebase: this.dueToRebase,
- ignoredWhitespaceOnly: this.ignoredWhitespaceOnly,
- });
- return group;
- }
-
- private addLine(line: GrDiffLine) {
- this.lines.push(line);
-
- const notDelta =
- this.type === GrDiffGroupType.BOTH ||
- this.type === GrDiffGroupType.CONTEXT_CONTROL;
- if (
- notDelta &&
- (line.type === GrDiffLineType.ADD || line.type === GrDiffLineType.REMOVE)
- ) {
- throw Error('Cannot add delta line to a non-delta group.');
- }
-
- if (line.type === GrDiffLineType.ADD) {
- this.adds.push(line);
- } else if (line.type === GrDiffLineType.REMOVE) {
- this.removes.push(line);
- }
- this._updateRangeWithNewLine(line);
- }
-
- getSideBySidePairs(): GrDiffLinePair[] {
- if (
- this.type === GrDiffGroupType.BOTH ||
- this.type === GrDiffGroupType.CONTEXT_CONTROL
- ) {
- return this.lines.map(line => {
- return {left: line, right: line};
- });
- }
-
- const pairs: GrDiffLinePair[] = [];
- let i = 0;
- let j = 0;
- while (i < this.removes.length || j < this.adds.length) {
- pairs.push({
- left: this.removes[i] || BLANK_LINE,
- right: this.adds[j] || BLANK_LINE,
- });
- i++;
- j++;
- }
- return pairs;
- }
-
- getUnifiedPairs(): GrDiffLinePair[] {
- return this.lines
- .map(line => {
- if (line.type === GrDiffLineType.ADD) {
- return {left: BLANK_LINE, right: line};
- }
- if (line.type === GrDiffLineType.REMOVE) {
- if (this.ignoredWhitespaceOnly) return undefined;
- return {left: line, right: BLANK_LINE};
- }
- return {left: line, right: line};
- })
- .filter(isDefined);
- }
-
- /** Returns true if it is, or contains, a skip group. */
- hasSkipGroup() {
- return (
- this.skip !== undefined ||
- this.contextGroups?.some(g => g.skip !== undefined)
- );
- }
-
- containsLine(side: Side, line: LineNumber) {
- if (typeof line !== 'number') {
- // For FILE and LOST, beforeNumber and afterNumber are the same
- return this.lines[0]?.beforeNumber === line;
- }
- const lineRange = this.lineRange[side];
- return lineRange.start_line <= line && line <= lineRange.end_line;
- }
-
- startLine(side: Side): LineNumber {
- // For both CONTEXT_CONTROL groups and SKIP groups the `lines` array will
- // be empty. So we have to use `lineRange` instead of looking at the first
- // line.
- if (
- this.type === GrDiffGroupType.CONTEXT_CONTROL ||
- this.skip !== undefined
- ) {
- return side === Side.LEFT
- ? this.lineRange.left.start_line
- : this.lineRange.right.start_line;
- }
- // For "normal" groups we could also use the `lineRange`, but for FILE or
- // LOST lines we want to return FILE or LOST. The `lineRange` contains
- // numbers only.
- return this.lines[0].lineNumber(side);
- }
-
- private _updateRangeWithNewLine(line: GrDiffLine) {
- if (typeof line.beforeNumber !== 'number') return;
- if (typeof line.afterNumber !== 'number') return;
-
- if (line.type === GrDiffLineType.ADD || line.type === GrDiffLineType.BOTH) {
- if (
- this.lineRange.right.start_line === 0 ||
- line.afterNumber < this.lineRange.right.start_line
- ) {
- this.lineRange.right.start_line = line.afterNumber;
- }
- if (line.afterNumber > this.lineRange.right.end_line) {
- this.lineRange.right.end_line = line.afterNumber;
- }
- }
-
- if (
- line.type === GrDiffLineType.REMOVE ||
- line.type === GrDiffLineType.BOTH
- ) {
- if (
- this.lineRange.left.start_line === 0 ||
- line.beforeNumber < this.lineRange.left.start_line
- ) {
- this.lineRange.left.start_line = line.beforeNumber;
- }
- if (line.beforeNumber > this.lineRange.left.end_line) {
- this.lineRange.left.end_line = line.beforeNumber;
- }
- }
- }
-
- async waitUntilRendered() {
- const lineNumber = this.lines[0]?.beforeNumber;
- // The LOST or FILE lines may be hidden and thus never resolve an
- // untilRendered() promise.
- if (
- this.skip !== undefined ||
- typeof lineNumber !== 'number' ||
- this.type === GrDiffGroupType.CONTEXT_CONTROL
- ) {
- return Promise.resolve();
- }
- assertIsDefined(this.element);
- await (this.element as LitElement).updateComplete;
- await untilRendered(this.element.firstElementChild as HTMLElement);
- }
-
- /**
- * Determines whether the group is either totally an addition or totally
- * a removal.
- */
- isTotal(): boolean {
- return (
- this.type === GrDiffGroupType.DELTA &&
- (!this.adds.length || !this.removes.length) &&
- !(!this.adds.length && !this.removes.length)
- );
- }
-}
diff --git a/polygerrit-ui/app/embed/diff-old/gr-diff/gr-diff-group_test.ts b/polygerrit-ui/app/embed/diff-old/gr-diff/gr-diff-group_test.ts
deleted file mode 100644
index bbbb4ad..0000000
--- a/polygerrit-ui/app/embed/diff-old/gr-diff/gr-diff-group_test.ts
+++ /dev/null
@@ -1,314 +0,0 @@
-/**
- * @license
- * Copyright 2016 Google LLC
- * SPDX-License-Identifier: Apache-2.0
- */
-import '../../../test/common-test-setup';
-import {GrDiffLine, BLANK_LINE} from './gr-diff-line';
-import {
- GrDiffGroup,
- GrDiffGroupType,
- hideInContextControl,
-} from './gr-diff-group';
-import {assert} from '@open-wc/testing';
-import {FILE, GrDiffLineType, LOST, Side} from '../../../api/diff';
-
-suite('gr-diff-group tests', () => {
- test('delta line pairs', () => {
- const l1 = new GrDiffLine(GrDiffLineType.ADD, 0, 128);
- const l2 = new GrDiffLine(GrDiffLineType.ADD, 0, 129);
- const l3 = new GrDiffLine(GrDiffLineType.REMOVE, 64, 0);
- let group = new GrDiffGroup({
- type: GrDiffGroupType.DELTA,
- lines: [l1, l2, l3],
- });
- assert.deepEqual(group.lines, [l1, l2, l3]);
- assert.deepEqual(group.adds, [l1, l2]);
- assert.deepEqual(group.removes, [l3]);
- assert.deepEqual(group.lineRange, {
- left: {start_line: 64, end_line: 64},
- right: {start_line: 128, end_line: 129},
- });
-
- let pairs = group.getSideBySidePairs();
- assert.deepEqual(pairs, [
- {left: l3, right: l1},
- {left: BLANK_LINE, right: l2},
- ]);
-
- group = new GrDiffGroup({type: GrDiffGroupType.DELTA, lines: [l1, l2, l3]});
- assert.deepEqual(group.lines, [l1, l2, l3]);
- assert.deepEqual(group.adds, [l1, l2]);
- assert.deepEqual(group.removes, [l3]);
-
- pairs = group.getSideBySidePairs();
- assert.deepEqual(pairs, [
- {left: l3, right: l1},
- {left: BLANK_LINE, right: l2},
- ]);
- });
-
- test('group must have lines', () => {
- try {
- new GrDiffGroup({type: GrDiffGroupType.BOTH});
- } catch (e) {
- // expected
- return;
- }
- assert.fail('a standard diff group cannot be empty');
- });
-
- test('group/header line pairs', () => {
- const l1 = new GrDiffLine(GrDiffLineType.BOTH, 64, 128);
- const l2 = new GrDiffLine(GrDiffLineType.BOTH, 65, 129);
- const l3 = new GrDiffLine(GrDiffLineType.BOTH, 66, 130);
-
- const group = new GrDiffGroup({
- type: GrDiffGroupType.BOTH,
- lines: [l1, l2, l3],
- });
-
- assert.deepEqual(group.lines, [l1, l2, l3]);
- assert.deepEqual(group.adds, []);
- assert.deepEqual(group.removes, []);
-
- assert.deepEqual(group.lineRange, {
- left: {start_line: 64, end_line: 66},
- right: {start_line: 128, end_line: 130},
- });
-
- const pairs = group.getSideBySidePairs();
- assert.deepEqual(pairs, [
- {left: l1, right: l1},
- {left: l2, right: l2},
- {left: l3, right: l3},
- ]);
- });
-
- test('adding delta lines to non-delta group', () => {
- const l1 = new GrDiffLine(GrDiffLineType.ADD);
- const l2 = new GrDiffLine(GrDiffLineType.REMOVE);
- const l3 = new GrDiffLine(GrDiffLineType.BOTH);
-
- assert.throws(
- () => new GrDiffGroup({type: GrDiffGroupType.BOTH, lines: [l1, l2, l3]})
- );
- });
-
- suite('hideInContextControl', () => {
- let groups: GrDiffGroup[];
- setup(() => {
- groups = [
- new GrDiffGroup({
- type: GrDiffGroupType.BOTH,
- lines: [
- new GrDiffLine(GrDiffLineType.BOTH, 5, 7),
- new GrDiffLine(GrDiffLineType.BOTH, 6, 8),
- new GrDiffLine(GrDiffLineType.BOTH, 7, 9),
- ],
- }),
- new GrDiffGroup({
- type: GrDiffGroupType.DELTA,
- lines: [
- new GrDiffLine(GrDiffLineType.REMOVE, 8),
- new GrDiffLine(GrDiffLineType.ADD, 0, 10),
- new GrDiffLine(GrDiffLineType.REMOVE, 9),
- new GrDiffLine(GrDiffLineType.ADD, 0, 11),
- new GrDiffLine(GrDiffLineType.REMOVE, 10),
- new GrDiffLine(GrDiffLineType.ADD, 0, 12),
- new GrDiffLine(GrDiffLineType.REMOVE, 11),
- new GrDiffLine(GrDiffLineType.ADD, 0, 13),
- ],
- }),
- new GrDiffGroup({
- type: GrDiffGroupType.BOTH,
- lines: [
- new GrDiffLine(GrDiffLineType.BOTH, 12, 14),
- new GrDiffLine(GrDiffLineType.BOTH, 13, 15),
- new GrDiffLine(GrDiffLineType.BOTH, 14, 16),
- ],
- }),
- ];
- });
-
- test('hides hidden groups in context control', () => {
- const collapsedGroups = hideInContextControl(groups, 3, 7);
- assert.equal(collapsedGroups.length, 3);
-
- assert.equal(collapsedGroups[0], groups[0]);
-
- assert.equal(collapsedGroups[1].type, GrDiffGroupType.CONTEXT_CONTROL);
- assert.equal(collapsedGroups[1].contextGroups.length, 1);
- assert.equal(collapsedGroups[1].contextGroups[0], groups[1]);
-
- assert.equal(collapsedGroups[2], groups[2]);
- });
-
- test('splits partially hidden groups', () => {
- const collapsedGroups = hideInContextControl(groups, 4, 8);
- assert.equal(collapsedGroups.length, 4);
- assert.equal(collapsedGroups[0], groups[0]);
-
- assert.equal(collapsedGroups[1].type, GrDiffGroupType.DELTA);
- assert.deepEqual(collapsedGroups[1].adds, [groups[1].adds[0]]);
- assert.deepEqual(collapsedGroups[1].removes, [groups[1].removes[0]]);
-
- assert.equal(collapsedGroups[2].type, GrDiffGroupType.CONTEXT_CONTROL);
- assert.equal(collapsedGroups[2].contextGroups.length, 2);
-
- assert.equal(
- collapsedGroups[2].contextGroups[0].type,
- GrDiffGroupType.DELTA
- );
- assert.deepEqual(
- collapsedGroups[2].contextGroups[0].adds,
- groups[1].adds.slice(1)
- );
- assert.deepEqual(
- collapsedGroups[2].contextGroups[0].removes,
- groups[1].removes.slice(1)
- );
-
- assert.equal(
- collapsedGroups[2].contextGroups[1].type,
- GrDiffGroupType.BOTH
- );
- assert.deepEqual(collapsedGroups[2].contextGroups[1].lines, [
- groups[2].lines[0],
- ]);
-
- assert.equal(collapsedGroups[3].type, GrDiffGroupType.BOTH);
- assert.deepEqual(collapsedGroups[3].lines, groups[2].lines.slice(1));
- });
-
- suite('with skip chunks', () => {
- setup(() => {
- const skipGroup = new GrDiffGroup({
- type: GrDiffGroupType.BOTH,
- skip: 60,
- offsetLeft: 8,
- offsetRight: 10,
- });
- groups = [
- new GrDiffGroup({
- type: GrDiffGroupType.BOTH,
- lines: [
- new GrDiffLine(GrDiffLineType.BOTH, 5, 7),
- new GrDiffLine(GrDiffLineType.BOTH, 6, 8),
- new GrDiffLine(GrDiffLineType.BOTH, 7, 9),
- ],
- }),
- skipGroup,
- new GrDiffGroup({
- type: GrDiffGroupType.BOTH,
- lines: [
- new GrDiffLine(GrDiffLineType.BOTH, 68, 70),
- new GrDiffLine(GrDiffLineType.BOTH, 69, 71),
- new GrDiffLine(GrDiffLineType.BOTH, 70, 72),
- ],
- }),
- ];
- });
-
- test('refuses to split skip group when closer to before', () => {
- const collapsedGroups = hideInContextControl(groups, 4, 10);
- assert.deepEqual(groups, collapsedGroups);
- });
- });
-
- test('groups unchanged if the hidden range is empty', () => {
- assert.deepEqual(hideInContextControl(groups, 0, 0), groups);
- });
-
- test('groups unchanged if there is only 1 line to hide', () => {
- assert.deepEqual(hideInContextControl(groups, 3, 4), groups);
- });
- });
-
- suite('isTotal', () => {
- test('is total for add', () => {
- const lines = [];
- for (let idx = 0; idx < 10; idx++) {
- lines.push(new GrDiffLine(GrDiffLineType.ADD));
- }
- const group = new GrDiffGroup({type: GrDiffGroupType.DELTA, lines});
- assert.isTrue(group.isTotal());
- });
-
- test('is total for remove', () => {
- const lines = [];
- for (let idx = 0; idx < 10; idx++) {
- lines.push(new GrDiffLine(GrDiffLineType.REMOVE));
- }
- const group = new GrDiffGroup({type: GrDiffGroupType.DELTA, lines});
- assert.isTrue(group.isTotal());
- });
-
- test('not total for non-delta', () => {
- const lines = [];
- for (let idx = 0; idx < 10; idx++) {
- lines.push(new GrDiffLine(GrDiffLineType.BOTH));
- }
- const group = new GrDiffGroup({type: GrDiffGroupType.DELTA, lines});
- assert.isFalse(group.isTotal());
- });
- });
-
- suite('startLine', () => {
- test('DELTA', () => {
- const lines: GrDiffLine[] = [];
- lines.push(new GrDiffLine(GrDiffLineType.BOTH, 3, 4));
- const group = new GrDiffGroup({type: GrDiffGroupType.DELTA, lines});
- assert.equal(group.startLine(Side.LEFT), 3);
- assert.equal(group.startLine(Side.RIGHT), 4);
- });
-
- test('CONTEXT CONTROL', () => {
- const lines: GrDiffLine[] = [];
- lines.push(new GrDiffLine(GrDiffLineType.BOTH, 3, 4));
- const delta = new GrDiffGroup({type: GrDiffGroupType.DELTA, lines});
- const group = new GrDiffGroup({
- type: GrDiffGroupType.CONTEXT_CONTROL,
- contextGroups: [delta],
- });
- assert.equal(group.startLine(Side.LEFT), 3);
- assert.equal(group.startLine(Side.RIGHT), 4);
- });
-
- test('SKIP', () => {
- const group = new GrDiffGroup({
- type: GrDiffGroupType.BOTH,
- skip: 10,
- offsetLeft: 3,
- offsetRight: 6,
- });
- assert.equal(group.startLine(Side.LEFT), 3);
- assert.equal(group.startLine(Side.RIGHT), 6);
-
- const group2 = new GrDiffGroup({
- type: GrDiffGroupType.BOTH,
- skip: 0,
- offsetLeft: 3,
- offsetRight: 6,
- });
- assert.equal(group2.startLine(Side.LEFT), 3);
- assert.equal(group2.startLine(Side.RIGHT), 6);
- });
-
- test('FILE', () => {
- const lines: GrDiffLine[] = [];
- lines.push(new GrDiffLine(GrDiffLineType.BOTH, FILE, FILE));
- const group = new GrDiffGroup({type: GrDiffGroupType.DELTA, lines});
- assert.equal(group.startLine(Side.LEFT), FILE);
- assert.equal(group.startLine(Side.RIGHT), FILE);
- });
-
- test('LOST', () => {
- const lines: GrDiffLine[] = [];
- lines.push(new GrDiffLine(GrDiffLineType.BOTH, LOST, LOST));
- const group = new GrDiffGroup({type: GrDiffGroupType.DELTA, lines});
- assert.equal(group.startLine(Side.LEFT), LOST);
- assert.equal(group.startLine(Side.RIGHT), LOST);
- });
- });
-});
diff --git a/polygerrit-ui/app/embed/diff-old/gr-diff/gr-diff-line.ts b/polygerrit-ui/app/embed/diff-old/gr-diff/gr-diff-line.ts
deleted file mode 100644
index 1a89207..0000000
--- a/polygerrit-ui/app/embed/diff-old/gr-diff/gr-diff-line.ts
+++ /dev/null
@@ -1,52 +0,0 @@
-/**
- * @license
- * Copyright 2016 Google LLC
- * SPDX-License-Identifier: Apache-2.0
- */
-import {
- FILE,
- GrDiffLine as GrDiffLineApi,
- GrDiffLineType,
- LineNumber,
- Side,
-} from '../../../api/diff';
-
-export class GrDiffLine implements GrDiffLineApi {
- constructor(
- readonly type: GrDiffLineType,
- public beforeNumber: LineNumber = 0,
- public afterNumber: LineNumber = 0
- ) {}
-
- hasIntralineInfo = false;
-
- highlights: Highlights[] = [];
-
- text = '';
-
- lineNumber(side: Side) {
- return side === Side.LEFT ? this.beforeNumber : this.afterNumber;
- }
-
- // TODO(TS): remove this properties
- static readonly Type = GrDiffLineType;
-
- static readonly File = FILE;
-}
-
-/**
- * A line highlight object consists of three fields:
- * - contentIndex: The index of the chunk `content` field (the line
- * being referred to).
- * - startIndex: Index of the character where the highlight should begin.
- * - endIndex: (optional) Index of the character where the highlight should
- * end. If omitted, the highlight is meant to be a continuation onto the
- * next line.
- */
-export interface Highlights {
- contentIndex: number;
- startIndex: number;
- endIndex?: number;
-}
-
-export const BLANK_LINE = new GrDiffLine(GrDiffLineType.BLANK);
diff --git a/polygerrit-ui/app/embed/diff-old/gr-diff/gr-diff-styles.ts b/polygerrit-ui/app/embed/diff-old/gr-diff/gr-diff-styles.ts
deleted file mode 100644
index a6dddd64..0000000
--- a/polygerrit-ui/app/embed/diff-old/gr-diff/gr-diff-styles.ts
+++ /dev/null
@@ -1,671 +0,0 @@
-/**
- * @license
- * Copyright 2023 Google LLC
- * SPDX-License-Identifier: Apache-2.0
- */
-import {css} from 'lit';
-
-export const grDiffStyles = css`
- /* This is used to hide all left side of the diff (e.g. diffs besides
- comments in the change log). Since we want to remove the first 4
- cells consistently in all rows except context buttons (.dividerRow). */
- :host(.no-left) .sideBySide colgroup col:nth-child(-n + 4),
- :host(.no-left) .sideBySide tr:not(.dividerRow) td:nth-child(-n + 4) {
- display: none;
- }
- :host(.disable-context-control-buttons) {
- --context-control-display: none;
- }
- :host(.disable-context-control-buttons) .section {
- border-right: none;
- }
- :host(.hide-line-length-indicator) .full-width td.content .contentText {
- background-image: none;
- }
-
- :host {
- font-family: var(--monospace-font-family, ''), 'Roboto Mono';
- font-size: var(--font-size, var(--font-size-code, 12px));
- /* usually 16px = 12px + 4px */
- line-height: calc(
- var(--font-size, var(--font-size-code, 12px)) + var(--spacing-s, 4px)
- );
- }
-
- .thread-group {
- display: block;
- max-width: var(--content-width, 80ch);
- white-space: normal;
- background-color: var(--diff-blank-background-color);
- }
- .diffContainer {
- max-width: var(--diff-max-width, none);
- font-family: var(--monospace-font-family);
- }
- table {
- border-collapse: collapse;
- table-layout: fixed;
- }
- td.lineNum {
- /* Enforces background whenever lines wrap */
- background-color: var(--diff-blank-background-color);
- }
-
- /* Provides the option to add side borders (left and right) to the line
- number column. */
- td.lineNum,
- td.blankLineNum,
- td.moveControlsLineNumCol,
- td.contextLineNum {
- box-shadow: var(--line-number-box-shadow, unset);
- }
-
- /* Context controls break up the table visually, so we set the right
- border on individual sections to leave a gap for the divider.
-
- Also taken into account for max-width calculations in SHRINK_ONLY mode
- (check GrDiff.updatePreferenceStyles). */
- .section {
- border-right: 1px solid var(--border-color);
- }
- .section.contextControl {
- /* Divider inside this section must not have border; we set borders on
- the padding rows below. */
- border-right-width: 0;
- }
- /* Padding rows behind context controls. The diff is styled to be cut
- into two halves by the negative space of the divider on which the
- context control buttons are anchored. */
- .contextBackground {
- border-right: 1px solid var(--border-color);
- }
- .contextBackground.above {
- border-bottom: 1px solid var(--border-color);
- }
- .contextBackground.below {
- border-top: 1px solid var(--border-color);
- }
-
- .lineNumButton {
- display: block;
- width: 100%;
- height: 100%;
- background-color: var(--diff-blank-background-color);
- box-shadow: var(--line-number-box-shadow, unset);
- }
- td.lineNum {
- vertical-align: top;
- }
-
- /* The only way to focus this (clicking) will apply our own focus
- styling, so this default styling is not needed and distracting. */
- .lineNumButton:focus {
- outline: none;
- }
- gr-image-viewer {
- width: 100%;
- height: 100%;
- max-width: var(--image-viewer-max-width, 95vw);
- max-height: var(--image-viewer-max-height, 90vh);
- /* Defined by paper-styles default-theme and used in various
- components. background-color-secondary is a compromise between
- fairly light in light theme (where we ideally would want
- background-color-primary) yet slightly offset against the app
- background in dark mode, where drop shadows e.g. around paper-card
- are almost invisible. */
- --primary-background-color: var(--background-color-secondary);
- }
- .image-diff .gr-diff {
- text-align: center;
- }
- .image-diff img {
- box-shadow: var(--elevation-level-1);
- max-width: 50em;
- }
- .image-diff .right.lineNumButton {
- border-left: 1px solid var(--border-color);
- }
- .image-diff label {
- font-family: var(--font-family);
- font-style: italic;
- }
- tbody.binary-diff td {
- font-family: var(--font-family);
- font-style: italic;
- text-align: center;
- padding: var(--spacing-s) 0;
- }
- .diff-row {
- outline: none;
- user-select: none;
- }
- .diff-row.target-row.target-side-left .lineNumButton.left,
- .diff-row.target-row.target-side-right .lineNumButton.right,
- .diff-row.target-row.unified .lineNumButton {
- color: var(--primary-text-color);
- }
-
- /* Preparing selected line cells with position relative so it allows a
- positioned overlay with 'position: absolute'. */
- .target-row td {
- position: relative;
- }
-
- /* Defines an overlay to the selected line for drawing an outline without
- blocking user interaction (e.g. text selection). */
- .target-row td::before {
- border-width: 0;
- border-style: solid;
- border-color: var(--focused-line-outline-color);
- position: absolute;
- top: 0;
- left: 0;
- width: 100%;
- height: 100%;
- pointer-events: none;
- user-select: none;
- content: ' ';
- }
-
- /* The outline for the selected content cell should be the same in all
- cases. */
- .target-row.target-side-left td.left.content::before,
- .target-row.target-side-right td.right.content::before,
- .unified.target-row td.content::before {
- border-width: 1px 1px 1px 0;
- }
-
- /* The outline for the sign cell should be always be contiguous
- top/bottom. */
- .target-row.target-side-left td.left.sign::before,
- .target-row.target-side-right td.right.sign::before {
- border-width: 1px 0;
- }
-
- /* For side-by-side we need to select the correct line number to
- "visually close" the outline. */
- .side-by-side.target-row.target-side-left td.left.lineNum::before,
- .side-by-side.target-row.target-side-right td.right.lineNum::before {
- border-width: 1px 0 1px 1px;
- }
-
- /* For unified diff we always start the overlay from the left cell. */
- .unified.target-row td.left:not(.content)::before {
- border-width: 1px 0 1px 1px;
- }
-
- /* For unified diff we should continue the top/bottom border in right
- line number column. */
- .unified.target-row td.right:not(.content)::before {
- border-width: 1px 0;
- }
-
- .content {
- background-color: var(--diff-blank-background-color);
- }
-
- /* Describes two states of semantic tokens: whenever a token has a
- definition that can be navigated to (navigable) and whenever
- the token is actually clickable to perform this navigation. */
- .semantic-token.navigable {
- text-decoration-style: dotted;
- text-decoration-line: underline;
- }
- .semantic-token.navigable.clickable {
- text-decoration-style: solid;
- cursor: pointer;
- }
-
- /* The file line, which has no contentText, add some margin before the
- first comment. We cannot add padding the container because we only
- want it if there is at least one comment thread, and the slotting
- makes :empty not work as expected. */
- .content.file slot:first-child::slotted(.comment-thread) {
- display: block;
- margin-top: var(--spacing-xs);
- }
- .contentText {
- background-color: var(--view-background-color);
- }
- .blank {
- background-color: var(--diff-blank-background-color);
- }
- .image-diff .content {
- background-color: var(--diff-blank-background-color);
- }
- .responsive {
- width: 100%;
- }
- .responsive .contentText {
- white-space: break-spaces;
- word-break: break-all;
- }
- .lineNumButton,
- .content {
- vertical-align: top;
- white-space: pre;
- }
- .contextLineNum,
- .lineNumButton {
- -webkit-user-select: none;
- -moz-user-select: none;
- -ms-user-select: none;
- user-select: none;
-
- color: var(--deemphasized-text-color);
- padding: 0 var(--spacing-m);
- text-align: right;
- }
- .canComment .lineNumButton {
- cursor: pointer;
- }
- .sign {
- min-width: 1ch;
- width: 1ch;
- background-color: var(--view-background-color);
- }
- .sign.blank {
- background-color: var(--diff-blank-background-color);
- }
- .content {
- /* Set min width since setting width on table cells still allows them
- to shrink. Do not set max width because CJK
- (Chinese-Japanese-Korean) glyphs have variable width. */
- min-width: var(--content-width, 80ch);
- width: var(--content-width, 80ch);
- }
- /* If there are no intraline info, consider everything changed */
- .content.add .contentText .intraline,
- .content.add.no-intraline-info .contentText,
- .sign.add.no-intraline-info,
- .delta.total .content.add .contentText {
- background-color: var(--dark-add-highlight-color);
- }
- .content.add .contentText,
- .sign.add {
- background-color: var(--light-add-highlight-color);
- }
- /* If there are no intraline info, consider everything changed */
- .content.remove .contentText .intraline,
- .content.remove.no-intraline-info .contentText,
- .delta.total .content.remove .contentText,
- .sign.remove.no-intraline-info {
- background-color: var(--dark-remove-highlight-color);
- }
- .content.remove .contentText,
- .sign.remove {
- background-color: var(--light-remove-highlight-color);
- }
-
- .ignoredWhitespaceOnly .sign.no-intraline-info {
- background-color: var(--view-background-color);
- }
-
- /* dueToRebase */
- .dueToRebase .content.add .contentText .intraline,
- .delta.total.dueToRebase .content.add .contentText {
- background-color: var(--dark-rebased-add-highlight-color);
- }
- .dueToRebase .content.add .contentText {
- background-color: var(--light-rebased-add-highlight-color);
- }
- .dueToRebase .content.remove .contentText .intraline,
- .delta.total.dueToRebase .content.remove .contentText {
- background-color: var(--dark-rebased-remove-highlight-color);
- }
- .dueToRebase .content.remove .contentText {
- background-color: var(--light-rebased-remove-highlight-color);
- }
-
- /* dueToMove */
- .dueToMove .sign.add,
- .dueToMove .content.add .contentText,
- .dueToMove .moveControls.movedIn .sign.right,
- .dueToMove .moveControls.movedIn .moveHeader,
- .delta.total.dueToMove .content.add .contentText {
- background-color: var(--diff-moved-in-background);
- }
-
- .dueToMove.changed .sign.add,
- .dueToMove.changed .content.add .contentText,
- .dueToMove.changed .moveControls.movedIn .sign.right,
- .dueToMove.changed .moveControls.movedIn .moveHeader,
- .delta.total.dueToMove.changed .content.add .contentText {
- background-color: var(--diff-moved-in-changed-background);
- }
-
- .dueToMove .sign.remove,
- .dueToMove .content.remove .contentText,
- .dueToMove .moveControls.movedOut .moveHeader,
- .dueToMove .moveControls.movedOut .sign.left,
- .delta.total.dueToMove .content.remove .contentText {
- background-color: var(--diff-moved-out-background);
- }
-
- .delta.dueToMove .movedIn .moveHeader {
- --gr-range-header-color: var(--diff-moved-in-label-color);
- }
- .delta.dueToMove.changed .movedIn .moveHeader {
- --gr-range-header-color: var(--diff-moved-in-changed-label-color);
- }
- .delta.dueToMove .movedOut .moveHeader {
- --gr-range-header-color: var(--diff-moved-out-label-color);
- }
-
- .moveHeader a {
- color: inherit;
- }
-
- /* ignoredWhitespaceOnly */
- .ignoredWhitespaceOnly .content.add .contentText .intraline,
- .delta.total.ignoredWhitespaceOnly .content.add .contentText,
- .ignoredWhitespaceOnly .content.add .contentText,
- .ignoredWhitespaceOnly .content.remove .contentText .intraline,
- .delta.total.ignoredWhitespaceOnly .content.remove .contentText,
- .ignoredWhitespaceOnly .content.remove .contentText {
- background-color: var(--view-background-color);
- }
-
- .content .contentText gr-diff-text:empty:after,
- .content .contentText gr-legacy-text:empty:after,
- .content .contentText:empty:after {
- /* Newline, to ensure empty lines are one line-height tall. */
- content: '\\A';
- }
-
- /* Context controls */
- .contextControl {
- display: var(--context-control-display, table-row-group);
- background-color: transparent;
- border: none;
- --divider-height: var(--spacing-s);
- --divider-border: 1px;
- }
- /* TODO: Is this still used? */
- .contextControl gr-button gr-icon {
- /* should match line-height of gr-button */
- font-size: var(--line-height-mono, 18px);
- }
- .contextControl td:not(.lineNumButton) {
- text-align: center;
- }
-
- /* Padding rows behind context controls. Styled as a continuation of the
- line gutters and code area. */
- .contextBackground > .contextLineNum {
- background-color: var(--diff-blank-background-color);
- }
- .contextBackground > td:not(.contextLineNum) {
- background-color: var(--view-background-color);
- }
- .contextBackground {
- /* One line of background behind the context expanders which they can
- render on top of, plus some padding. */
- height: calc(var(--line-height-normal) + var(--spacing-s));
- }
-
- .dividerCell {
- vertical-align: top;
- }
- .dividerRow.show-both .dividerCell {
- height: var(--divider-height);
- }
- .dividerRow.show-above .dividerCell,
- .dividerRow.show-above .dividerCell {
- height: 0;
- }
-
- .br:after {
- /* Line feed */
- content: '\\A';
- }
- .tab {
- display: inline-block;
- }
- .tab-indicator:before {
- color: var(--diff-tab-indicator-color);
- /* >> character */
- content: '\\00BB';
- position: absolute;
- }
- .special-char-indicator {
- /* spacing so elements don't collide */
- padding-right: var(--spacing-m);
- }
- .special-char-indicator:before {
- color: var(--diff-tab-indicator-color);
- content: '•';
- position: absolute;
- }
- .special-char-warning {
- /* spacing so elements don't collide */
- padding-right: var(--spacing-m);
- }
- .special-char-warning:before {
- color: var(--warning-foreground);
- content: '!';
- position: absolute;
- }
- /* Is defined after other background-colors, such that this
- rule wins in case of same specificity. */
- .trailing-whitespace,
- .content .contentText .trailing-whitespace,
- .trailing-whitespace .intraline,
- .content .contentText .trailing-whitespace .intraline {
- border-radius: var(--border-radius, 4px);
- background-color: var(--diff-trailing-whitespace-indicator);
- }
- #diffHeader {
- background-color: var(--table-header-background-color);
- border-bottom: 1px solid var(--border-color);
- color: var(--link-color);
- padding: var(--spacing-m) 0 var(--spacing-m) 48px;
- }
- #diffTable {
- /* for gr-selection-action-box positioning */
- position: relative;
- }
- #diffTable:focus {
- outline: none;
- }
- #loadingError,
- #sizeWarning {
- display: block;
- margin: var(--spacing-l) auto;
- max-width: 60em;
- text-align: center;
- }
- #loadingError {
- color: var(--error-text-color);
- }
- #sizeWarning gr-button {
- margin: var(--spacing-l);
- }
- .target-row td.blame {
- background: var(--diff-selection-background-color);
- }
- td.lost div {
- background-color: var(--info-background);
- }
- td.lost div.lost-message {
- font-family: var(--font-family, 'Roboto');
- font-size: var(--font-size-normal, 14px);
- line-height: var(--line-height-normal);
- padding: var(--spacing-s) 0;
- }
- td.lost div.lost-message gr-icon {
- padding: 0 var(--spacing-s) 0 var(--spacing-m);
- color: var(--blue-700);
- }
-
- col.sign,
- td.sign {
- display: none;
- }
-
- /* Sign column should only be shown in high-contrast mode. */
- :host(.with-sign-col) col.sign {
- display: table-column;
- }
- :host(.with-sign-col) td.sign {
- display: table-cell;
- }
- col.blame {
- display: none;
- }
- td.blame {
- display: none;
- padding: 0 var(--spacing-m);
- white-space: pre;
- }
- :host(.showBlame) col.blame {
- display: table-column;
- }
- :host(.showBlame) td.blame {
- display: table-cell;
- }
- td.blame > span {
- opacity: 0.6;
- }
- td.blame > span.startOfRange {
- opacity: 1;
- }
- td.blame .blameDate {
- font-family: var(--monospace-font-family);
- color: var(--link-color);
- text-decoration: none;
- }
- .responsive td.blame {
- overflow: hidden;
- width: 200px;
- }
- /** Support the line length indicator **/
- .responsive td.content .contentText {
- /* Same strategy as in
- https://stackoverflow.com/questions/1179928/how-can-i-put-a-vertical-line-down-the-center-of-a-div
- */
- background-image: linear-gradient(
- var(--line-length-indicator-color),
- var(--line-length-indicator-color)
- );
- background-size: 1px 100%;
- background-position: var(--line-limit-marker) 0;
- background-repeat: no-repeat;
- }
- .newlineWarning {
- color: var(--deemphasized-text-color);
- text-align: center;
- }
- .newlineWarning.hidden {
- display: none;
- }
- .lineNum.COVERED .lineNumButton {
- color: var(
- --coverage-covered-line-num-color,
- var(--deemphasized-text-color)
- );
- background-color: var(--coverage-covered, #e0f2f1);
- }
- .lineNum.NOT_COVERED .lineNumButton {
- color: var(
- --coverage-covered-line-num-color,
- var(--deemphasized-text-color)
- );
- background-color: var(--coverage-not-covered, #ffd1a4);
- }
- .lineNum.PARTIALLY_COVERED .lineNumButton {
- color: var(
- --coverage-covered-line-num-color,
- var(--deemphasized-text-color)
- );
- background: linear-gradient(
- to right bottom,
- var(--coverage-not-covered, #ffd1a4) 0%,
- var(--coverage-not-covered, #ffd1a4) 50%,
- var(--coverage-covered, #e0f2f1) 50%,
- var(--coverage-covered, #e0f2f1) 100%
- );
- }
-
- // TODO: Investigate whether this CSS is still necessary.
- /* BEGIN: Select and copy for Polymer 2 */
- /* Below was copied and modified from the original css in gr-diff-selection.html. */
- .content,
- .contextControl,
- .blame {
- -webkit-user-select: none;
- -moz-user-select: none;
- -ms-user-select: none;
- user-select: none;
- }
-
- .selected-left:not(.selected-comment)
- .side-by-side
- .left
- + .content
- .contentText,
- .selected-right:not(.selected-comment)
- .side-by-side
- .right
- + .content
- .contentText,
- .selected-left:not(.selected-comment)
- .unified
- .left.lineNum
- ~ .content:not(.both)
- .contentText,
- .selected-right:not(.selected-comment)
- .unified
- .right.lineNum
- ~ .content
- .contentText,
- .selected-left.selected-comment .side-by-side .left + .content .message,
- .selected-right.selected-comment
- .side-by-side
- .right
- + .content
- .message
- :not(.collapsedContent),
- .selected-comment .unified .message :not(.collapsedContent),
- .selected-blame .blame {
- -webkit-user-select: text;
- -moz-user-select: text;
- -ms-user-select: text;
- user-select: text;
- }
-
- /* Make comments and check results selectable when selected */
- .selected-left.selected-comment ::slotted(.comment-thread[diff-side='left']),
- .selected-right.selected-comment
- ::slotted(.comment-thread[diff-side='right']) {
- -webkit-user-select: text;
- -moz-user-select: text;
- -ms-user-select: text;
- user-select: text;
- }
- /* END: Select and copy for Polymer 2 */
-
- .whitespace-change-only-message {
- background-color: var(--diff-context-control-background-color);
- border: 1px solid var(--diff-context-control-border-color);
- text-align: center;
- }
-
- .token-highlight {
- background-color: var(--token-highlighting-color, #fffd54);
- }
-
- gr-selection-action-box {
- /* Needs z-index to appear above wrapped content, since it's inserted
- into DOM before it. */
- z-index: 120;
- }
-
- gr-diff-image-new,
- gr-diff-image-old,
- gr-diff-section,
- gr-context-controls-section,
- gr-diff-row {
- display: contents;
- }
-`;
diff --git a/polygerrit-ui/app/embed/diff-old/gr-diff/gr-diff.ts b/polygerrit-ui/app/embed/diff-old/gr-diff/gr-diff.ts
deleted file mode 100644
index 47ed7cf..0000000
--- a/polygerrit-ui/app/embed/diff-old/gr-diff/gr-diff.ts
+++ /dev/null
@@ -1,1127 +0,0 @@
-/**
- * @license
- * Copyright 2015 Google LLC
- * SPDX-License-Identifier: Apache-2.0
- */
-import '../../../styles/shared-styles';
-import '../../../elements/shared/gr-button/gr-button';
-import '../../../elements/shared/gr-icon/gr-icon';
-import '../gr-diff-builder/gr-diff-builder-element';
-import '../gr-diff-highlight/gr-diff-highlight';
-import '../gr-diff-selection/gr-diff-selection';
-import '../../diff/gr-syntax-themes/gr-syntax-theme';
-import '../../diff/gr-ranged-comment-themes/gr-ranged-comment-theme';
-import '../../diff/gr-ranged-comment-hint/gr-ranged-comment-hint';
-import {
- getLine,
- getLineElByChild,
- getLineNumber,
- getRange,
- getSide,
- GrDiffThreadElement,
- isLongCommentRange,
- isThreadEl,
- rangesEqual,
- getResponsiveMode,
- isResponsive,
- isNewDiff,
- getDataFromCommentThreadEl,
- GrDiffCommentThread,
-} from '../../diff/gr-diff/gr-diff-utils';
-import {BlameInfo, CommentRange, ImageInfo} from '../../../types/common';
-import {DiffInfo, DiffPreferencesInfo} from '../../../types/diff';
-import {
- CreateRangeCommentEventDetail,
- GrDiffHighlight,
-} from '../gr-diff-highlight/gr-diff-highlight';
-import {
- GrDiffBuilderElement,
- getLineNumberCellWidth,
-} from '../gr-diff-builder/gr-diff-builder-element';
-import {CoverageRange, DiffLayer} from '../../../types/types';
-import {CommentRangeLayer} from '../../diff/gr-ranged-comment-layer/gr-ranged-comment-layer';
-import {
- createDefaultDiffPrefs,
- DiffViewMode,
- Side,
-} from '../../../constants/constants';
-import {KeyLocations} from '../gr-diff-processor/gr-diff-processor';
-import {fire, fireAlert} from '../../../utils/event-util';
-import {MovedLinkClickedEvent, ValueChangedEvent} from '../../../types/events';
-import {getContentEditableRange} from '../../../utils/safari-selection-util';
-import {AbortStop} from '../../../api/core';
-import {
- RenderPreferences,
- GrDiff as GrDiffApi,
- DisplayLine,
- LineNumber,
- LOST,
-} from '../../../api/diff';
-import {isSafari, toggleClass} from '../../../utils/dom-util';
-import {assertIsDefined} from '../../../utils/common-util';
-import {
- debounceP,
- DelayedPromise,
- DELAYED_CANCELLATION,
-} from '../../../utils/async-util';
-import {GrDiffSelection} from '../gr-diff-selection/gr-diff-selection';
-import {property, query, state} from 'lit/decorators.js';
-import {sharedStyles} from '../../../styles/shared-styles';
-import {html, LitElement, nothing, PropertyValues} from 'lit';
-import {when} from 'lit/directives/when.js';
-import {grSyntaxTheme} from '../../diff/gr-syntax-themes/gr-syntax-theme';
-import {grRangedCommentTheme} from '../../diff/gr-ranged-comment-themes/gr-ranged-comment-theme';
-import {classMap} from 'lit/directives/class-map.js';
-import {iconStyles} from '../../../styles/gr-icon-styles';
-import {expandFileMode} from '../../../utils/file-util';
-import {DiffModel, diffModelToken} from '../gr-diff-model/gr-diff-model';
-import {provide} from '../../../models/dependency';
-import {grDiffStyles} from './gr-diff-styles';
-import {getDiffLength} from '../../../utils/diff-util';
-
-const NO_NEWLINE_LEFT = 'No newline at end of left file.';
-const NO_NEWLINE_RIGHT = 'No newline at end of right file.';
-
-const LARGE_DIFF_THRESHOLD_LINES = 10000;
-const FULL_CONTEXT = -1;
-
-const COMMIT_MSG_PATH = '/COMMIT_MSG';
-/**
- * 72 is the unofficial length standard for git commit messages.
- * Derived from the fact that git log/show appends 4 ws in the beginning of
- * each line when displaying commit messages. To center the commit message
- * in an 80 char terminal a 4 ws border is added to the rightmost side:
- * 4 + 72 + 4
- */
-const COMMIT_MSG_LINE_LENGTH = 72;
-
-export class GrDiff extends LitElement implements GrDiffApi {
- /**
- * Fired when the user selects a line.
- *
- * @event line-selected
- */
-
- /**
- * Fired if being logged in is required.
- *
- * @event show-auth-required
- */
-
- /**
- * Fired when a comment is created
- *
- * @event create-comment
- */
-
- /**
- * Fired when rendering, including syntax highlighting, is done. Also fired
- * when no rendering can be done because required preferences are not set.
- *
- * @event render
- */
-
- /**
- * Fired for interaction reporting when a diff context is expanded.
- * Contains an event.detail with numLines about the number of lines that
- * were expanded.
- *
- * @event diff-context-expanded
- */
-
- @query('#diffTable')
- diffTable?: HTMLTableElement;
-
- @property({type: Boolean})
- noAutoRender = false;
-
- @property({type: String})
- path?: string;
-
- @property({type: Object})
- prefs?: DiffPreferencesInfo;
-
- @property({type: Object})
- renderPrefs: RenderPreferences = {};
-
- @property({type: Boolean, reflect: true})
- override hidden = false;
-
- @property({type: Boolean})
- noRenderOnPrefsChange?: boolean;
-
- // Private but used in tests.
- @state()
- commentRanges: CommentRangeLayer[] = [];
-
- // explicitly highlight a range if it is not associated with any comment
- @property({type: Object})
- highlightRange?: CommentRange;
-
- @property({type: Array})
- coverageRanges: CoverageRange[] = [];
-
- @property({type: Boolean})
- lineWrapping = false;
-
- @property({type: String})
- viewMode = DiffViewMode.SIDE_BY_SIDE;
-
- @property({type: Object})
- lineOfInterest?: DisplayLine;
-
- /**
- * True when diff is changed, until the content is done rendering.
- * Use getter/setter loading instead of this.
- */
- private _loading = true;
-
- get loading() {
- return this._loading;
- }
-
- set loading(loading: boolean) {
- if (this._loading === loading) return;
- const oldLoading = this._loading;
- this._loading = loading;
- fire(this, 'loading-changed', {value: this._loading});
- this.requestUpdate('loading', oldLoading);
- }
-
- @property({type: Boolean})
- loggedIn = false;
-
- @property({type: Object})
- diff?: DiffInfo;
-
- @state()
- private diffTableClass = '';
-
- @property({type: Object})
- baseImage?: ImageInfo;
-
- @property({type: Object})
- revisionImage?: ImageInfo;
-
- /**
- * In order to allow multi-select in Safari browsers, a workaround is required
- * to trigger 'beforeinput' events to get a list of static ranges. This is
- * obtained by making the content of the diff table "contentEditable".
- */
- @property({type: Boolean})
- override isContentEditable = isSafari();
-
- /**
- * Whether the safety check for large diffs when whole-file is set has
- * been bypassed. If the value is null, then the safety has not been
- * bypassed. If the value is a number, then that number represents the
- * context preference to use when rendering the bypassed diff.
- *
- * Private but used in tests.
- */
- @state()
- safetyBypass: number | null = null;
-
- // Private but used in tests.
- @state()
- showWarning?: boolean;
-
- @property({type: String})
- errorMessage: string | null = null;
-
- @property({type: Array})
- blame: BlameInfo[] | null = null;
-
- @property({type: Boolean})
- showNewlineWarningLeft = false;
-
- @property({type: Boolean})
- showNewlineWarningRight = false;
-
- @property({type: Boolean})
- useNewImageDiffUi = false;
-
- // Private but used in tests.
- @state()
- diffLength?: number;
-
- /**
- * Observes comment nodes added or removed at any point.
- * Can be used to unregister upon detachment.
- */
- private nodeObserver?: MutationObserver;
-
- @property({type: Array})
- layers?: DiffLayer[];
-
- // Private but used in tests.
- renderDiffTableTask?: DelayedPromise<void>;
-
- // Private but used in tests.
- diffSelection = new GrDiffSelection();
-
- // Private but used in tests.
- highlights = new GrDiffHighlight();
-
- // Private but used in tests.
- diffBuilder = new GrDiffBuilderElement();
-
- private diffModel = new DiffModel(undefined);
-
- static override get styles() {
- return [
- iconStyles,
- sharedStyles,
- grSyntaxTheme,
- grRangedCommentTheme,
- grDiffStyles,
- ];
- }
-
- constructor() {
- super();
- provide(this, diffModelToken, () => this.diffModel);
- this.addEventListener(
- 'create-range-comment',
- (e: CustomEvent<CreateRangeCommentEventDetail>) =>
- this.handleCreateRangeComment(e)
- );
- this.addEventListener('render-content', () => this.handleRenderContent());
- this.addEventListener('moved-link-clicked', (e: MovedLinkClickedEvent) => {
- this.dispatchSelectedLine(e.detail.lineNum, e.detail.side);
- });
- }
-
- override connectedCallback() {
- super.connectedCallback();
- if (this.loggedIn) {
- this.addSelectionListeners();
- }
- if (this.diff && this.diffTable) {
- this.diffSelection.init(this.diff, this.diffTable);
- }
- if (this.diffTable && this.diffBuilder) {
- this.highlights.init(this.diffTable, this.diffBuilder);
- }
- this.diffBuilder.init();
- }
-
- override disconnectedCallback() {
- this.removeSelectionListeners();
- this.renderDiffTableTask?.cancel();
- this.diffSelection.cleanup();
- this.highlights.cleanup();
- this.diffBuilder.cleanup();
- super.disconnectedCallback();
- }
-
- protected override willUpdate(changedProperties: PropertyValues<this>): void {
- if (
- changedProperties.has('path') ||
- changedProperties.has('lineWrapping') ||
- changedProperties.has('viewMode') ||
- changedProperties.has('useNewImageDiffUi') ||
- changedProperties.has('prefs')
- ) {
- this.prefsChanged();
- }
- if (changedProperties.has('blame')) {
- this.blameChanged();
- }
- if (changedProperties.has('renderPrefs')) {
- this.renderPrefsChanged();
- }
- if (changedProperties.has('loggedIn')) {
- if (this.loggedIn && this.isConnected) {
- this.addSelectionListeners();
- } else {
- this.removeSelectionListeners();
- }
- }
- if (changedProperties.has('coverageRanges')) {
- this.diffBuilder.updateCoverageRanges(this.coverageRanges);
- }
- if (changedProperties.has('lineOfInterest')) {
- this.lineOfInterestChanged();
- }
- }
-
- protected override updated(changedProperties: PropertyValues<this>): void {
- if (changedProperties.has('diff')) {
- // diffChanged relies on diffTable ahving been rendered.
- this.diffChanged();
- }
- }
-
- override render() {
- return html`
- ${this.renderHeader()} ${this.renderContainer()}
- ${this.renderNewlineWarning()} ${this.renderLoadingError()}
- ${this.renderSizeWarning()}
- `;
- }
-
- private renderHeader() {
- const diffheaderItems = this.computeDiffHeaderItems();
- if (diffheaderItems.length === 0) return nothing;
- return html`
- <div id="diffHeader">
- ${diffheaderItems.map(item => html`<div>${item}</div>`)}
- </div>
- `;
- }
-
- private renderContainer() {
- const cssClasses = {
- oldDiff: true,
- diffContainer: true,
- unified: this.viewMode === DiffViewMode.UNIFIED,
- sideBySide: this.viewMode === DiffViewMode.SIDE_BY_SIDE,
- canComment: this.loggedIn,
- };
- return html`
- <div class=${classMap(cssClasses)} @click=${this.handleTap}>
- <table
- id="diffTable"
- class=${this.diffTableClass}
- ?contenteditable=${this.isContentEditable}
- ></table>
- ${when(
- this.showNoChangeMessage(),
- () => html`
- <div class="whitespace-change-only-message">
- This file only contains whitespace changes. Modify the whitespace
- setting to see the changes.
- </div>
- `
- )}
- </div>
- `;
- }
-
- private renderNewlineWarning() {
- const newlineWarning = this.computeNewlineWarning();
- if (!newlineWarning) return nothing;
- return html`<div class="newlineWarning">${newlineWarning}</div>`;
- }
-
- private renderLoadingError() {
- if (!this.errorMessage) return nothing;
- return html`<div id="loadingError">${this.errorMessage}</div>`;
- }
-
- private renderSizeWarning() {
- if (!this.showWarning) return nothing;
- // TODO: Update comment about 'Whole file' as it's not in settings.
- return html`
- <div id="sizeWarning">
- <p>
- Prevented render because "Whole file" is enabled and this diff is very
- large (about ${this.diffLength} lines).
- </p>
- <gr-button @click=${this.collapseContext}>
- Render with limited context
- </gr-button>
- <gr-button @click=${this.handleFullBypass}>
- Render anyway (may be slow)
- </gr-button>
- </div>
- `;
- }
-
- private addSelectionListeners() {
- document.addEventListener('selectionchange', this.handleSelectionChange);
- document.addEventListener('mouseup', this.handleMouseUp);
- }
-
- private removeSelectionListeners() {
- document.removeEventListener('selectionchange', this.handleSelectionChange);
- document.removeEventListener('mouseup', this.handleMouseUp);
- }
-
- getLineNumEls(side: Side): HTMLElement[] {
- return this.diffBuilder.getLineNumEls(side);
- }
-
- // Private but used in tests.
- showNoChangeMessage() {
- return (
- !this.loading &&
- this.diff &&
- !this.diff.binary &&
- this.prefs &&
- this.prefs.ignore_whitespace !== 'IGNORE_NONE' &&
- this.diffLength === 0
- );
- }
-
- private readonly handleSelectionChange = () => {
- // Because of shadow DOM selections, we handle the selectionchange here,
- // and pass the shadow DOM selection into gr-diff-highlight, where the
- // corresponding range is determined and normalized.
- const selection = this.getShadowOrDocumentSelection();
- this.highlights.handleSelectionChange(selection, false);
- };
-
- private readonly handleMouseUp = () => {
- // To handle double-click outside of text creating comments, we check on
- // mouse-up if there's a selection that just covers a line change. We
- // can't do that on selection change since the user may still be dragging.
- const selection = this.getShadowOrDocumentSelection();
- this.highlights.handleSelectionChange(selection, true);
- };
-
- /** Gets the current selection, preferring the shadow DOM selection. */
- private getShadowOrDocumentSelection() {
- // When using native shadow DOM, the selection returned by
- // document.getSelection() cannot reference the actual DOM elements making
- // up the diff in Safari because they are in the shadow DOM of the gr-diff
- // element. This takes the shadow DOM selection if one exists.
- return this.shadowRoot?.getSelection
- ? this.shadowRoot.getSelection()
- : isSafari()
- ? getContentEditableRange()
- : document.getSelection();
- }
-
- private updateRanges(
- addedThreadEls: GrDiffThreadElement[],
- removedThreadEls: GrDiffThreadElement[]
- ) {
- function commentRangeFromThreadEl(
- threadEl: GrDiffThreadElement
- ): CommentRangeLayer | undefined {
- const side = getSide(threadEl);
- if (!side) return undefined;
- const range = getRange(threadEl);
- if (!range) return undefined;
-
- return {side, range, id: threadEl.rootId};
- }
-
- // TODO(brohlfs): Rewrite `.map().filter() as ...` with `.reduce()` instead.
- const addedCommentRanges = addedThreadEls
- .map(commentRangeFromThreadEl)
- .filter(range => !!range) as CommentRangeLayer[];
- const removedCommentRanges = removedThreadEls
- .map(commentRangeFromThreadEl)
- .filter(range => !!range) as CommentRangeLayer[];
- for (const removedCommentRange of removedCommentRanges) {
- const i = this.commentRanges.findIndex(
- cr =>
- cr.side === removedCommentRange.side &&
- rangesEqual(cr.range, removedCommentRange.range)
- );
- this.commentRanges.splice(i, 1);
- }
-
- if (addedCommentRanges?.length) {
- this.commentRanges.push(...addedCommentRanges);
- }
- if (this.highlightRange) {
- this.commentRanges.push({
- side: Side.RIGHT,
- range: this.highlightRange,
- id: 'highlightRange',
- });
- }
-
- this.diffBuilder.updateCommentRanges(this.commentRanges);
- }
-
- /**
- * The key locations based on the comments and line of interests,
- * where lines should not be collapsed.
- *
- */
- private computeKeyLocations() {
- const keyLocations: KeyLocations = {left: {}, right: {}};
- if (this.lineOfInterest) {
- const side = this.lineOfInterest.side;
- keyLocations[side][this.lineOfInterest.lineNum] = true;
- }
- const threadEls = [...this.childNodes].filter(isThreadEl);
-
- for (const threadEl of threadEls) {
- const side = getSide(threadEl);
- if (!side) continue;
- const lineNum = getLine(threadEl);
- const commentRange = getRange(threadEl);
- keyLocations[side][lineNum] = true;
- // Add start_line as well if exists,
- // the being and end of the range should not be collapsed.
- if (commentRange?.start_line) {
- keyLocations[side][commentRange.start_line] = true;
- }
- }
- return keyLocations;
- }
-
- // Dispatch events that are handled by the gr-diff-highlight.
- private redispatchHoverEvents(
- hoverEl: HTMLElement,
- threadEl: GrDiffThreadElement
- ) {
- hoverEl.addEventListener('mouseenter', () => {
- const data = getDataFromCommentThreadEl(threadEl);
- if (data) fire(threadEl, 'comment-thread-mouseenter', data);
- });
- hoverEl.addEventListener('mouseleave', () => {
- const data = getDataFromCommentThreadEl(threadEl);
- if (data) fire(threadEl, 'comment-thread-mouseleave', data);
- });
- }
-
- /** Cancel any remaining diff builder rendering work. */
- cancel() {
- this.diffBuilder.cleanup();
- this.renderDiffTableTask?.cancel();
- }
-
- getCursorStops(): Array<HTMLElement | AbortStop> {
- if (this.hidden && this.noAutoRender) return [];
-
- // Get rendered stops.
- const stops: Array<HTMLElement | AbortStop> =
- this.diffBuilder.getLineNumberRows();
-
- // If we are still loading this diff, abort after the rendered stops to
- // avoid skipping over to e.g. the next file.
- if (this.loading) {
- stops.push(new AbortStop());
- }
- return stops;
- }
-
- isRangeSelected() {
- return !!this.highlights.selectedRange;
- }
-
- toggleLeftDiff() {
- toggleClass(this, 'no-left');
- }
-
- private blameChanged() {
- this.diffBuilder.setBlame(this.blame);
- if (this.blame) {
- this.classList.add('showBlame');
- } else {
- this.classList.remove('showBlame');
- }
- }
-
- // Private but used in tests.
- handleTap(e: Event) {
- const el = e.target as Element;
-
- if (
- el.getAttribute('data-value') !== LOST &&
- (el.classList.contains('lineNum') ||
- el.classList.contains('lineNumButton'))
- ) {
- this.addDraftAtLine(el);
- } else if (
- el.tagName === 'HL' ||
- el.classList.contains('content') ||
- el.classList.contains('contentText')
- ) {
- const target = getLineElByChild(el);
- if (target) {
- this.selectLine(target);
- }
- }
- }
-
- // Private but used in tests.
- selectLine(el: Element) {
- const lineNumber = Number(el.getAttribute('data-value'));
- const side = el.classList.contains('left') ? Side.LEFT : Side.RIGHT;
- this.dispatchSelectedLine(lineNumber, side);
- }
-
- private dispatchSelectedLine(number: LineNumber, side: Side) {
- fire(this, 'line-selected', {
- number,
- side,
- path: this.path,
- });
- }
-
- addDraftAtLine(el: Element) {
- this.selectLine(el);
-
- const lineNum = getLineNumber(el);
- if (lineNum === null) {
- fireAlert(this, 'Invalid line number');
- return;
- }
-
- this.createComment(el, lineNum);
- }
-
- createRangeComment() {
- if (!this.isRangeSelected()) {
- throw Error('Selection is needed for new range comment');
- }
- const selectedRange = this.highlights.selectedRange;
- if (!selectedRange) throw Error('selected range not set');
- const {side, range} = selectedRange;
- this.createCommentForSelection(side, range);
- }
-
- createCommentForSelection(side: Side, range: CommentRange) {
- const lineNum = range.end_line;
- const lineEl = this.diffBuilder.getLineElByNumber(lineNum, side);
- if (lineEl) {
- this.createComment(lineEl, lineNum, side, range);
- }
- }
-
- private handleCreateRangeComment(
- e: CustomEvent<CreateRangeCommentEventDetail>
- ) {
- const range = e.detail.range;
- const side = e.detail.side;
- this.createCommentForSelection(side, range);
- }
-
- // Private but used in tests.
- createComment(
- lineEl: Element,
- lineNum: LineNumber,
- side?: Side,
- range?: CommentRange
- ) {
- const contentEl = this.diffBuilder.getContentTdByLineEl(lineEl);
- if (!contentEl) throw new Error('content el not found for line el');
- side = side ?? this.getCommentSideByLineAndContent(lineEl, contentEl);
- fire(this, 'create-comment', {
- side,
- lineNum,
- range,
- });
- }
-
- private getCommentSideByLineAndContent(
- lineEl: Element,
- contentEl: Element
- ): Side {
- return lineEl.classList.contains(Side.LEFT) ||
- contentEl.classList.contains('remove')
- ? Side.LEFT
- : Side.RIGHT;
- }
-
- private lineOfInterestChanged() {
- if (this.loading) return;
- if (!this.lineOfInterest) return;
- const lineNum = this.lineOfInterest.lineNum;
- if (typeof lineNum !== 'number') return;
- this.diffBuilder.unhideLine(lineNum, this.lineOfInterest.side);
- }
-
- private cleanup() {
- this.cancel();
- this.blame = null;
- this.safetyBypass = null;
- this.showWarning = false;
- this.clearDiffContent();
- }
-
- private prefsChanged() {
- if (!this.prefs) return;
- this.diffModel.updateState({diffPrefs: this.prefs});
-
- this.blame = null;
- this.updatePreferenceStyles();
-
- if (this.diff && !this.noRenderOnPrefsChange) {
- this.debounceRenderDiffTable();
- }
- }
-
- private updatePreferenceStyles() {
- assertIsDefined(this.prefs, 'prefs');
- const lineLength =
- this.path === COMMIT_MSG_PATH
- ? COMMIT_MSG_LINE_LENGTH
- : this.prefs.line_length;
- const sideBySide = this.viewMode === 'SIDE_BY_SIDE';
-
- const responsiveMode = getResponsiveMode(this.prefs, this.renderPrefs);
- const responsive = isResponsive(responsiveMode);
- this.diffTableClass = responsive ? 'responsive' : '';
- const lineLimit = `${lineLength}ch`;
- this.style.setProperty(
- '--line-limit-marker',
- responsiveMode === 'FULL_RESPONSIVE' ? lineLimit : '-1px'
- );
- this.style.setProperty('--content-width', responsive ? 'none' : lineLimit);
- if (responsiveMode === 'SHRINK_ONLY') {
- // Calculating ideal (initial) width for the whole table including
- // width of each table column (content and line number columns) and
- // border. We also add a 1px correction as some values are calculated
- // in 'ch'.
-
- // We might have 1 to 2 columns for content depending if side-by-side
- // or unified mode
- const contentWidth = `${sideBySide ? 2 : 1} * ${lineLimit}`;
-
- // We always have 2 columns for line number
- const lineNumberWidth = `2 * ${getLineNumberCellWidth(this.prefs)}px`;
-
- // border-right in ".section" css definition (in gr-diff_html.ts)
- const sectionRightBorder = '1px';
-
- // each sign col has 1ch width.
- const signColsWidth =
- sideBySide && this.renderPrefs?.show_sign_col ? '2ch' : '0ch';
-
- // As some of these calculations are done using 'ch' we end up having <1px
- // difference between ideal and calculated size for each side leading to
- // lines using the max columns (e.g. 80) to wrap (decided exclusively by
- // the browser).This happens even in monospace fonts. Empirically adding
- // 2px as correction to be sure wrapping won't happen in these cases so it
- // doesn't block further experimentation with the SHRINK_MODE. This was
- // previously set to 1px but due to to a more aggressive text wrapping
- // (via word-break: break-all; - check .contextText) we need to be even
- // more lenient in some cases. If we find another way to avoid this
- // correction we will change it.
- const dontWrapCorrection = '2px';
- this.style.setProperty(
- '--diff-max-width',
- `calc(${contentWidth} + ${lineNumberWidth} + ${signColsWidth} + ${sectionRightBorder} + ${dontWrapCorrection})`
- );
- } else {
- this.style.setProperty('--diff-max-width', 'none');
- }
- if (this.prefs.font_size) {
- this.style.setProperty('--font-size', `${this.prefs.font_size}px`);
- }
- }
-
- private renderPrefsChanged() {
- this.diffModel.updateState({renderPrefs: this.renderPrefs});
- if (this.renderPrefs.hide_left_side) {
- this.classList.add('no-left');
- }
- if (this.renderPrefs.disable_context_control_buttons) {
- this.classList.add('disable-context-control-buttons');
- }
- if (this.renderPrefs.hide_line_length_indicator) {
- this.classList.add('hide-line-length-indicator');
- }
- if (this.renderPrefs.show_sign_col) {
- this.classList.add('with-sign-col');
- }
- if (this.prefs) {
- this.updatePreferenceStyles();
- }
- this.diffBuilder.updateRenderPrefs(this.renderPrefs);
- }
-
- private diffChanged() {
- this.loading = true;
- this.cleanup();
- if (this.diff) {
- this.diffLength = this.getDiffLength(this.diff);
- this.debounceRenderDiffTable();
- assertIsDefined(this.diffTable, 'diffTable');
- this.diffSelection.init(this.diff, this.diffTable);
- this.highlights.init(this.diffTable, this.diffBuilder);
- }
- }
-
- // Implemented so the test can stub it.
- getDiffLength(diff?: DiffInfo) {
- return getDiffLength(diff);
- }
-
- /**
- * When called multiple times from the same task, will call
- * _renderDiffTable only once, in the next task (scheduled via `setTimeout`).
- *
- * This should be used instead of calling _renderDiffTable directly to
- * render the diff in response to an input change, because there may be
- * multiple inputs changing in the same microtask, but we only want to
- * render once.
- */
- private debounceRenderDiffTable() {
- // at this point gr-diff might be considered as rendered from the outside
- // (client), although it was not actually rendered. Clients need to know
- // when it is safe to perform operations like cursor moves, for example,
- // and if changing an input actually requires a reload of the diff table.
- // Since `fire` is synchronous it allows clients to be aware when an
- // async render is needed and that they can wait for a further `render`
- // event to actually take further action.
- fire(this, 'render-required', {});
- this.renderDiffTableTask = debounceP(
- this.renderDiffTableTask,
- async () => await this.renderDiffTable()
- );
- this.renderDiffTableTask.catch((e: unknown) => {
- if (e === DELAYED_CANCELLATION) return;
- throw e;
- });
- }
-
- // Private but used in tests.
- async renderDiffTable() {
- this.unobserveNodes();
- if (!this.diff || !this.prefs) {
- fire(this, 'render', {});
- return;
- }
- if (
- this.prefs.context === -1 &&
- this.diffLength &&
- this.diffLength >= LARGE_DIFF_THRESHOLD_LINES &&
- this.safetyBypass === null
- ) {
- this.showWarning = true;
- fire(this, 'render', {});
- return;
- }
-
- this.showWarning = false;
-
- const keyLocations = this.computeKeyLocations();
-
- this.diffModel.setState({
- diff: this.diff,
- path: this.path,
- renderPrefs: this.renderPrefs,
- diffPrefs: this.prefs,
- });
-
- // TODO: Setting tons of public properties like this is obviously a code
- // smell. We are introducing a diff model for managing all this
- // data. Then diff builder will only need access to that model.
- this.diffBuilder.prefs = this.getBypassPrefs();
- this.diffBuilder.renderPrefs = this.renderPrefs;
- this.diffBuilder.diff = this.diff;
- this.diffBuilder.path = this.path;
- this.diffBuilder.viewMode = this.viewMode;
- this.diffBuilder.layers = this.layers ?? [];
- this.diffBuilder.baseImage = this.baseImage ?? null;
- this.diffBuilder.revisionImage = this.revisionImage ?? null;
- this.diffBuilder.useNewImageDiffUi = this.useNewImageDiffUi;
- this.diffBuilder.diffElement = this.diffTable;
- // `this.commentRanges` are probably empty here, because they will only be
- // populated by the node observer, which starts observing *after* rendering.
- this.diffBuilder.updateCommentRanges(this.commentRanges);
- this.diffBuilder.updateCoverageRanges(this.coverageRanges);
- await this.diffBuilder.render(keyLocations);
- }
-
- private handleRenderContent() {
- this.querySelectorAll('gr-ranged-comment-hint').forEach(element =>
- element.remove()
- );
- this.loading = false;
- this.observeNodes();
- // We are just converting 'render-content' into 'render' here. Maybe we
- // should retire the 'render' event in favor of 'render-content'?
- fire(this, 'render', {});
- }
-
- private observeNodes() {
- // First stop observing old nodes.
- this.unobserveNodes();
- // Then introduce a Mutation observer that watches for children being added
- // to gr-diff. If those children are `isThreadEl`, namely then they are
- // processed.
- this.nodeObserver = new MutationObserver(mutations => {
- const addedThreadEls = extractAddedNodes(mutations).filter(isThreadEl);
- const removedThreadEls =
- extractRemovedNodes(mutations).filter(isThreadEl);
- this.processNodes(addedThreadEls, removedThreadEls);
- });
- this.nodeObserver.observe(this, {childList: true});
- // Make sure to process existing gr-comment-threads that already exist.
- this.processNodes([...this.childNodes].filter(isThreadEl), []);
- }
-
- private processNodes(
- addedThreadEls: GrDiffThreadElement[],
- removedThreadEls: GrDiffThreadElement[]
- ) {
- this.updateRanges(addedThreadEls, removedThreadEls);
- addedThreadEls.forEach(threadEl =>
- this.redispatchHoverEvents(threadEl, threadEl)
- );
- // Removed nodes do not need to be handled because all this code does is
- // adding a slot for the added thread elements, and the extra slots do
- // not hurt. It's probably a bigger performance cost to remove them than
- // to keep them around. Medium term we can even consider to add one slot
- // for each line from the start.
- for (const threadEl of addedThreadEls) {
- const lineNum = getLine(threadEl);
- const commentSide = getSide(threadEl);
- const range = getRange(threadEl);
- if (!commentSide) continue;
- const lineEl = this.diffBuilder.getLineElByNumber(lineNum, commentSide);
- // When the line the comment refers to does not exist, log an error
- // but don't crash. This can happen e.g. if the API does not fully
- // validate e.g. (robot) comments
- if (!lineEl) {
- console.error(
- 'thread attached to line ',
- commentSide,
- lineNum,
- ' which does not exist.'
- );
- continue;
- }
- const contentEl = this.diffBuilder.getContentTdByLineEl(lineEl);
- if (!contentEl) continue;
- if (lineNum === LOST) {
- this.insertPortedCommentsWithoutRangeMessage(contentEl);
- }
-
- const slotAtt = threadEl.getAttribute('slot');
- if (range && isLongCommentRange(range) && slotAtt) {
- const longRangeCommentHint = document.createElement(
- 'gr-ranged-comment-hint'
- );
- longRangeCommentHint.range = range;
- longRangeCommentHint.setAttribute('threadElRootId', threadEl.rootId);
- longRangeCommentHint.setAttribute('slot', slotAtt);
- this.insertBefore(longRangeCommentHint, threadEl);
- this.redispatchHoverEvents(longRangeCommentHint, threadEl);
- }
- }
-
- for (const threadEl of removedThreadEls) {
- this.querySelector(
- `gr-ranged-comment-hint[threadElRootId="${threadEl.rootId}"]`
- )?.remove();
- }
- }
-
- private unobserveNodes() {
- if (this.nodeObserver) {
- this.nodeObserver.disconnect();
- this.nodeObserver = undefined;
- }
- // You only stop observing for comment thread elements when the diff is
- // completely rendered from scratch. And then comment thread elements
- // will be (re-)added *after* rendering is done. That is also when we
- // re-start observing. So it is appropriate to thoroughly clean up
- // everything that the observer is managing.
- this.commentRanges = [];
- }
-
- private insertPortedCommentsWithoutRangeMessage(lostCell: Element) {
- const existingMessage = lostCell.querySelector('div.lost-message');
- if (existingMessage) return;
-
- const div = document.createElement('div');
- div.className = 'lost-message';
- const icon = document.createElement('gr-icon');
- icon.setAttribute('icon', 'info');
- div.appendChild(icon);
- const span = document.createElement('span');
- span.innerText = 'Original comment position not found in this patchset';
- div.appendChild(span);
- lostCell.insertBefore(div, lostCell.firstChild);
- }
-
- /**
- * Get the preferences object including the safety bypass context (if any).
- */
- private getBypassPrefs() {
- assertIsDefined(this.prefs, 'prefs');
- if (this.safetyBypass !== null) {
- return {...this.prefs, context: this.safetyBypass};
- }
- return this.prefs;
- }
-
- clearDiffContent() {
- this.unobserveNodes();
- if (!this.diffTable) return;
- while (this.diffTable.hasChildNodes()) {
- this.diffTable.removeChild(this.diffTable.lastChild!);
- }
- }
-
- // Private but used in tests.
- computeDiffHeaderItems() {
- return (this.diff?.diff_header ?? [])
- .filter(
- item =>
- !(
- item.startsWith('diff --git ') ||
- item.startsWith('index ') ||
- item.startsWith('+++ ') ||
- item.startsWith('--- ') ||
- item === 'Binary files differ'
- )
- )
- .map(expandFileMode);
- }
-
- private handleFullBypass() {
- this.safetyBypass = FULL_CONTEXT;
- this.debounceRenderDiffTable();
- }
-
- private 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();
- }
-
- toggleAllContext() {
- if (!this.prefs) {
- return;
- }
- if (this.getBypassPrefs().context < 0) {
- this.collapseContext();
- } else {
- this.handleFullBypass();
- }
- }
-
- private computeNewlineWarning(): string | undefined {
- const messages = [];
- if (this.showNewlineWarningLeft) {
- messages.push(NO_NEWLINE_LEFT);
- }
- if (this.showNewlineWarningRight) {
- messages.push(NO_NEWLINE_RIGHT);
- }
- if (!messages.length) {
- return undefined;
- }
- return messages.join(' \u2014 '); // \u2014 - '—'
- }
-}
-
-function extractAddedNodes(mutations: MutationRecord[]) {
- return mutations.flatMap(mutation => [...mutation.addedNodes]);
-}
-
-function extractRemovedNodes(mutations: MutationRecord[]) {
- return mutations.flatMap(mutation => [...mutation.removedNodes]);
-}
-
-if (!isNewDiff()) {
- customElements.define('gr-diff', GrDiff);
-}
-
-declare global {
- interface HTMLElementTagNameMap {
- 'gr-diff': LitElement;
- }
- interface HTMLElementEventMap {
- 'comment-thread-mouseenter': CustomEvent<GrDiffCommentThread>;
- 'comment-thread-mouseleave': CustomEvent<GrDiffCommentThread>;
- 'loading-changed': ValueChangedEvent<boolean>;
- 'render-required': CustomEvent<{}>;
- }
-}
diff --git a/polygerrit-ui/app/embed/diff-old/gr-diff/gr-diff_test.ts b/polygerrit-ui/app/embed/diff-old/gr-diff/gr-diff_test.ts
deleted file mode 100644
index ac444b7..0000000
--- a/polygerrit-ui/app/embed/diff-old/gr-diff/gr-diff_test.ts
+++ /dev/null
@@ -1,4183 +0,0 @@
-/**
- * @license
- * Copyright 2015 Google LLC
- * SPDX-License-Identifier: Apache-2.0
- */
-import '../../../test/common-test-setup';
-import {createDiff} from '../../../test/test-data-generators';
-import './gr-diff';
-import {getComputedStyleValue} from '../../../utils/dom-util';
-import '@polymer/paper-button/paper-button';
-import {
- DiffContent,
- DiffInfo,
- DiffPreferencesInfo,
- DiffViewMode,
- IgnoreWhitespaceType,
- Side,
-} from '../../../api/diff';
-import {
- mockPromise,
- mouseDown,
- query,
- queryAll,
- queryAndAssert,
- waitEventLoop,
- waitQueryAndAssert,
- waitUntil,
-} from '../../../test/test-utils';
-import {AbortStop} from '../../../api/core';
-import {waitForEventOnce} from '../../../utils/event-util';
-import {GrDiff} from './gr-diff';
-import {ImageInfo} from '../../../types/common';
-import {GrRangedCommentHint} from '../../diff/gr-ranged-comment-hint/gr-ranged-comment-hint';
-import {assertIsDefined} from '../../../utils/common-util';
-import {fixture, html, assert} from '@open-wc/testing';
-
-suite('gr-diff a11y test', () => {
- test('audit', async () => {
- assert.isAccessible(await fixture(html`<gr-diff></gr-diff>`));
- });
-});
-
-suite('gr-diff tests', () => {
- let element: GrDiff;
-
- const MINIMAL_PREFS: DiffPreferencesInfo = {
- tab_size: 2,
- line_length: 80,
- font_size: 12,
- context: 3,
- ignore_whitespace: 'IGNORE_NONE',
- };
-
- setup(async () => {
- element = await fixture<GrDiff>(html`<gr-diff></gr-diff>`);
- });
-
- suite('rendering', () => {
- test('empty diff', async () => {
- await element.updateComplete;
- assert.shadowDom.equal(
- element,
- /* HTML */ `
- <div class="diffContainer oldDiff sideBySide">
- <table id="diffTable"></table>
- </div>
- `
- );
- });
-
- test('a unified diff lit', async () => {
- element.viewMode = DiffViewMode.UNIFIED;
- element.prefs = {...MINIMAL_PREFS};
- element.diff = createDiff();
- await element.updateComplete;
- await waitForEventOnce(element, 'render');
- assert.shadowDom.equal(
- element,
- /* HTML */ `
- <div class="diffContainer oldDiff unified">
- <table class="selected-right" id="diffTable">
- <colgroup>
- <col class="blame gr-diff" />
- <col class="gr-diff" width="48" />
- <col class="gr-diff" width="48" />
- <col class="gr-diff" />
- </colgroup>
- <tbody class="both gr-diff section">
- <tr
- aria-labelledby="left-button-LOST right-button-LOST right-content-LOST"
- class="both diff-row gr-diff unified"
- tabindex="-1"
- >
- <td class="blame gr-diff" data-line-number="LOST"></td>
- <td class="gr-diff left lineNum" data-value="LOST"></td>
- <td class="gr-diff lineNum right" data-value="LOST"></td>
- <td class="both content gr-diff lost no-intraline-info right">
- <div class="thread-group" data-side="right"></div>
- </td>
- </tr>
- </tbody>
- <tbody class="both gr-diff section">
- <tr
- aria-labelledby="left-button-FILE right-button-FILE right-content-FILE"
- class="both diff-row gr-diff unified"
- tabindex="-1"
- >
- <td class="blame gr-diff" data-line-number="FILE"></td>
- <td class="gr-diff left lineNum" data-value="FILE">
- <button
- aria-label="Add file comment"
- class="gr-diff left lineNumButton"
- data-value="FILE"
- id="left-button-FILE"
- tabindex="-1"
- >
- File
- </button>
- </td>
- <td class="gr-diff lineNum right" data-value="FILE">
- <button
- aria-label="Add file comment"
- class="gr-diff lineNumButton right"
- data-value="FILE"
- id="right-button-FILE"
- tabindex="-1"
- >
- File
- </button>
- </td>
- <td class="both content file gr-diff no-intraline-info right">
- <div class="thread-group" data-side="right"></div>
- </td>
- </tr>
- </tbody>
- <tbody class="both gr-diff section">
- <tr
- aria-labelledby="left-button-1 right-button-1 right-content-1"
- class="both diff-row gr-diff unified"
- tabindex="-1"
- >
- <td class="blame gr-diff" data-line-number="1"></td>
- <td class="gr-diff left lineNum" data-value="1">
- <button
- aria-label="1 unmodified"
- class="gr-diff left lineNumButton"
- data-value="1"
- id="left-button-1"
- tabindex="-1"
- >
- 1
- </button>
- </td>
- <td class="gr-diff lineNum right" data-value="1">
- <button
- aria-label="1 unmodified"
- class="gr-diff lineNumButton right"
- data-value="1"
- id="right-button-1"
- tabindex="-1"
- >
- 1
- </button>
- </td>
- <td class="both content gr-diff no-intraline-info right">
- <div
- class="contentText gr-diff"
- data-side="right"
- id="right-content-1"
- ></div>
- <div class="thread-group" data-side="right"></div>
- </td>
- </tr>
- <tr
- aria-labelledby="left-button-2 right-button-2 right-content-2"
- class="both diff-row gr-diff unified"
- tabindex="-1"
- >
- <td class="blame gr-diff" data-line-number="2"></td>
- <td class="gr-diff left lineNum" data-value="2">
- <button
- aria-label="2 unmodified"
- class="gr-diff left lineNumButton"
- data-value="2"
- id="left-button-2"
- tabindex="-1"
- >
- 2
- </button>
- </td>
- <td class="gr-diff lineNum right" data-value="2">
- <button
- aria-label="2 unmodified"
- class="gr-diff lineNumButton right"
- data-value="2"
- id="right-button-2"
- tabindex="-1"
- >
- 2
- </button>
- </td>
- <td class="both content gr-diff no-intraline-info right">
- <div
- class="contentText gr-diff"
- data-side="right"
- id="right-content-2"
- ></div>
- <div class="thread-group" data-side="right"></div>
- </td>
- </tr>
- <tr
- aria-labelledby="left-button-3 right-button-3 right-content-3"
- class="both diff-row gr-diff unified"
- tabindex="-1"
- >
- <td class="blame gr-diff" data-line-number="3"></td>
- <td class="gr-diff left lineNum" data-value="3">
- <button
- aria-label="3 unmodified"
- class="gr-diff left lineNumButton"
- data-value="3"
- id="left-button-3"
- tabindex="-1"
- >
- 3
- </button>
- </td>
- <td class="gr-diff lineNum right" data-value="3">
- <button
- aria-label="3 unmodified"
- class="gr-diff lineNumButton right"
- data-value="3"
- id="right-button-3"
- tabindex="-1"
- >
- 3
- </button>
- </td>
- <td class="both content gr-diff no-intraline-info right">
- <div
- class="contentText gr-diff"
- data-side="right"
- id="right-content-3"
- ></div>
- <div class="thread-group" data-side="right"></div>
- </td>
- </tr>
- <tr
- aria-labelledby="left-button-4 right-button-4 right-content-4"
- class="both diff-row gr-diff unified"
- tabindex="-1"
- >
- <td class="blame gr-diff" data-line-number="4"></td>
- <td class="gr-diff left lineNum" data-value="4">
- <button
- aria-label="4 unmodified"
- class="gr-diff left lineNumButton"
- data-value="4"
- id="left-button-4"
- tabindex="-1"
- >
- 4
- </button>
- </td>
- <td class="gr-diff lineNum right" data-value="4">
- <button
- aria-label="4 unmodified"
- class="gr-diff lineNumButton right"
- data-value="4"
- id="right-button-4"
- tabindex="-1"
- >
- 4
- </button>
- </td>
- <td class="both content gr-diff no-intraline-info right">
- <div
- class="contentText gr-diff"
- data-side="right"
- id="right-content-4"
- ></div>
- <div class="thread-group" data-side="right"></div>
- </td>
- </tr>
- </tbody>
- <tbody class="delta gr-diff section total">
- <tr
- aria-labelledby="right-button-5 right-content-5"
- class="add diff-row gr-diff unified"
- tabindex="-1"
- >
- <td class="blame gr-diff" data-line-number="0"></td>
- <td class="gr-diff left"></td>
- <td class="gr-diff lineNum right" data-value="5">
- <button
- aria-label="5 added"
- class="gr-diff lineNumButton right"
- data-value="5"
- id="right-button-5"
- tabindex="-1"
- >
- 5
- </button>
- </td>
- <td class="add content gr-diff no-intraline-info right">
- <div
- class="contentText gr-diff"
- data-side="right"
- id="right-content-5"
- ></div>
- <div class="thread-group" data-side="right"></div>
- </td>
- </tr>
- <tr
- aria-labelledby="right-button-6 right-content-6"
- class="add diff-row gr-diff unified"
- tabindex="-1"
- >
- <td class="blame gr-diff" data-line-number="0"></td>
- <td class="gr-diff left"></td>
- <td class="gr-diff lineNum right" data-value="6">
- <button
- aria-label="6 added"
- class="gr-diff lineNumButton right"
- data-value="6"
- id="right-button-6"
- tabindex="-1"
- >
- 6
- </button>
- </td>
- <td class="add content gr-diff no-intraline-info right">
- <div
- class="contentText gr-diff"
- data-side="right"
- id="right-content-6"
- ></div>
- <div class="thread-group" data-side="right"></div>
- </td>
- </tr>
- <tr
- aria-labelledby="right-button-7 right-content-7"
- class="add diff-row gr-diff unified"
- tabindex="-1"
- >
- <td class="blame gr-diff" data-line-number="0"></td>
- <td class="gr-diff left"></td>
- <td class="gr-diff lineNum right" data-value="7">
- <button
- aria-label="7 added"
- class="gr-diff lineNumButton right"
- data-value="7"
- id="right-button-7"
- tabindex="-1"
- >
- 7
- </button>
- </td>
- <td class="add content gr-diff no-intraline-info right">
- <div
- class="contentText gr-diff"
- data-side="right"
- id="right-content-7"
- ></div>
- <div class="thread-group" data-side="right"></div>
- </td>
- </tr>
- </tbody>
- <tbody class="both gr-diff section">
- <tr
- aria-labelledby="left-button-5 right-button-8 right-content-8"
- class="both diff-row gr-diff unified"
- tabindex="-1"
- >
- <td class="blame gr-diff" data-line-number="5"></td>
- <td class="gr-diff left lineNum" data-value="5">
- <button
- aria-label="5 unmodified"
- class="gr-diff left lineNumButton"
- data-value="5"
- id="left-button-5"
- tabindex="-1"
- >
- 5
- </button>
- </td>
- <td class="gr-diff lineNum right" data-value="8">
- <button
- aria-label="8 unmodified"
- class="gr-diff lineNumButton right"
- data-value="8"
- id="right-button-8"
- tabindex="-1"
- >
- 8
- </button>
- </td>
- <td class="both content gr-diff no-intraline-info right">
- <div
- class="contentText gr-diff"
- data-side="right"
- id="right-content-8"
- ></div>
- <div class="thread-group" data-side="right"></div>
- </td>
- </tr>
- <tr
- aria-labelledby="left-button-6 right-button-9 right-content-9"
- class="both diff-row gr-diff unified"
- tabindex="-1"
- >
- <td class="blame gr-diff" data-line-number="6"></td>
- <td class="gr-diff left lineNum" data-value="6">
- <button
- aria-label="6 unmodified"
- class="gr-diff left lineNumButton"
- data-value="6"
- id="left-button-6"
- tabindex="-1"
- >
- 6
- </button>
- </td>
- <td class="gr-diff lineNum right" data-value="9">
- <button
- aria-label="9 unmodified"
- class="gr-diff lineNumButton right"
- data-value="9"
- id="right-button-9"
- tabindex="-1"
- >
- 9
- </button>
- </td>
- <td class="both content gr-diff no-intraline-info right">
- <div
- class="contentText gr-diff"
- data-side="right"
- id="right-content-9"
- ></div>
- <div class="thread-group" data-side="right"></div>
- </td>
- </tr>
- <tr
- aria-labelledby="left-button-7 right-button-10 right-content-10"
- class="both diff-row gr-diff unified"
- tabindex="-1"
- >
- <td class="blame gr-diff" data-line-number="7"></td>
- <td class="gr-diff left lineNum" data-value="7">
- <button
- aria-label="7 unmodified"
- class="gr-diff left lineNumButton"
- data-value="7"
- id="left-button-7"
- tabindex="-1"
- >
- 7
- </button>
- </td>
- <td class="gr-diff lineNum right" data-value="10">
- <button
- aria-label="10 unmodified"
- class="gr-diff lineNumButton right"
- data-value="10"
- id="right-button-10"
- tabindex="-1"
- >
- 10
- </button>
- </td>
- <td class="both content gr-diff no-intraline-info right">
- <div
- class="contentText gr-diff"
- data-side="right"
- id="right-content-10"
- ></div>
- <div class="thread-group" data-side="right"></div>
- </td>
- </tr>
- <tr
- aria-labelledby="left-button-8 right-button-11 right-content-11"
- class="both diff-row gr-diff unified"
- tabindex="-1"
- >
- <td class="blame gr-diff" data-line-number="8"></td>
- <td class="gr-diff left lineNum" data-value="8">
- <button
- aria-label="8 unmodified"
- class="gr-diff left lineNumButton"
- data-value="8"
- id="left-button-8"
- tabindex="-1"
- >
- 8
- </button>
- </td>
- <td class="gr-diff lineNum right" data-value="11">
- <button
- aria-label="11 unmodified"
- class="gr-diff lineNumButton right"
- data-value="11"
- id="right-button-11"
- tabindex="-1"
- >
- 11
- </button>
- </td>
- <td class="both content gr-diff no-intraline-info right">
- <div
- class="contentText gr-diff"
- data-side="right"
- id="right-content-11"
- ></div>
- <div class="thread-group" data-side="right"></div>
- </td>
- </tr>
- <tr
- aria-labelledby="left-button-9 right-button-12 right-content-12"
- class="both diff-row gr-diff unified"
- tabindex="-1"
- >
- <td class="blame gr-diff" data-line-number="9"></td>
- <td class="gr-diff left lineNum" data-value="9">
- <button
- aria-label="9 unmodified"
- class="gr-diff left lineNumButton"
- data-value="9"
- id="left-button-9"
- tabindex="-1"
- >
- 9
- </button>
- </td>
- <td class="gr-diff lineNum right" data-value="12">
- <button
- aria-label="12 unmodified"
- class="gr-diff lineNumButton right"
- data-value="12"
- id="right-button-12"
- tabindex="-1"
- >
- 12
- </button>
- </td>
- <td class="both content gr-diff no-intraline-info right">
- <div
- class="contentText gr-diff"
- data-side="right"
- id="right-content-12"
- ></div>
- <div class="thread-group" data-side="right"></div>
- </td>
- </tr>
- </tbody>
- <tbody class="delta gr-diff section total">
- <tr
- aria-labelledby="left-button-10 left-content-10"
- class="diff-row gr-diff remove unified"
- tabindex="-1"
- >
- <td class="blame gr-diff" data-line-number="10"></td>
- <td class="gr-diff left lineNum" data-value="10">
- <button
- aria-label="10 removed"
- class="gr-diff left lineNumButton"
- data-value="10"
- id="left-button-10"
- tabindex="-1"
- >
- 10
- </button>
- </td>
- <td class="gr-diff right"></td>
- <td class="content gr-diff left no-intraline-info remove">
- <div
- class="contentText gr-diff"
- data-side="left"
- id="left-content-10"
- ></div>
- <div class="thread-group" data-side="left"></div>
- </td>
- </tr>
- <tr
- aria-labelledby="left-button-11 left-content-11"
- class="diff-row gr-diff remove unified"
- tabindex="-1"
- >
- <td class="blame gr-diff" data-line-number="11"></td>
- <td class="gr-diff left lineNum" data-value="11">
- <button
- aria-label="11 removed"
- class="gr-diff left lineNumButton"
- data-value="11"
- id="left-button-11"
- tabindex="-1"
- >
- 11
- </button>
- </td>
- <td class="gr-diff right"></td>
- <td class="content gr-diff left no-intraline-info remove">
- <div
- class="contentText gr-diff"
- data-side="left"
- id="left-content-11"
- ></div>
- <div class="thread-group" data-side="left"></div>
- </td>
- </tr>
- <tr
- aria-labelledby="left-button-12 left-content-12"
- class="diff-row gr-diff remove unified"
- tabindex="-1"
- >
- <td class="blame gr-diff" data-line-number="12"></td>
- <td class="gr-diff left lineNum" data-value="12">
- <button
- aria-label="12 removed"
- class="gr-diff left lineNumButton"
- data-value="12"
- id="left-button-12"
- tabindex="-1"
- >
- 12
- </button>
- </td>
- <td class="gr-diff right"></td>
- <td class="content gr-diff left no-intraline-info remove">
- <div
- class="contentText gr-diff"
- data-side="left"
- id="left-content-12"
- ></div>
- <div class="thread-group" data-side="left"></div>
- </td>
- </tr>
- <tr
- aria-labelledby="left-button-13 left-content-13"
- class="diff-row gr-diff remove unified"
- tabindex="-1"
- >
- <td class="blame gr-diff" data-line-number="13"></td>
- <td class="gr-diff left lineNum" data-value="13">
- <button
- aria-label="13 removed"
- class="gr-diff left lineNumButton"
- data-value="13"
- id="left-button-13"
- tabindex="-1"
- >
- 13
- </button>
- </td>
- <td class="gr-diff right"></td>
- <td class="content gr-diff left no-intraline-info remove">
- <div
- class="contentText gr-diff"
- data-side="left"
- id="left-content-13"
- ></div>
- <div class="thread-group" data-side="left"></div>
- </td>
- </tr>
- </tbody>
- <tbody class="delta gr-diff ignoredWhitespaceOnly section">
- <tr
- aria-labelledby="right-button-13 right-content-13"
- class="add diff-row gr-diff unified"
- tabindex="-1"
- >
- <td class="blame gr-diff" data-line-number="0"></td>
- <td class="gr-diff left"></td>
- <td class="gr-diff lineNum right" data-value="13">
- <button
- aria-label="13 added"
- class="gr-diff lineNumButton right"
- data-value="13"
- id="right-button-13"
- tabindex="-1"
- >
- 13
- </button>
- </td>
- <td class="add content gr-diff no-intraline-info right">
- <div
- class="contentText gr-diff"
- data-side="right"
- id="right-content-13"
- ></div>
- <div class="thread-group" data-side="right"></div>
- </td>
- </tr>
- <tr
- aria-labelledby="right-button-14 right-content-14"
- class="add diff-row gr-diff unified"
- tabindex="-1"
- >
- <td class="blame gr-diff" data-line-number="0"></td>
- <td class="gr-diff left"></td>
- <td class="gr-diff lineNum right" data-value="14">
- <button
- aria-label="14 added"
- class="gr-diff lineNumButton right"
- data-value="14"
- id="right-button-14"
- tabindex="-1"
- >
- 14
- </button>
- </td>
- <td class="add content gr-diff no-intraline-info right">
- <div
- class="contentText gr-diff"
- data-side="right"
- id="right-content-14"
- ></div>
- <div class="thread-group" data-side="right"></div>
- </td>
- </tr>
- </tbody>
- <tbody class="delta gr-diff section">
- <tr
- aria-labelledby="left-button-16 left-content-16"
- class="diff-row gr-diff remove unified"
- tabindex="-1"
- >
- <td class="blame gr-diff" data-line-number="16"></td>
- <td class="gr-diff left lineNum" data-value="16">
- <button
- aria-label="16 removed"
- class="gr-diff left lineNumButton"
- data-value="16"
- id="left-button-16"
- tabindex="-1"
- >
- 16
- </button>
- </td>
- <td class="gr-diff right"></td>
- <td class="content gr-diff left remove">
- <div
- class="contentText gr-diff"
- data-side="left"
- id="left-content-16"
- ></div>
- <div class="thread-group" data-side="left"></div>
- </td>
- </tr>
- <tr
- aria-labelledby="right-button-15 right-content-15"
- class="add diff-row gr-diff unified"
- tabindex="-1"
- >
- <td class="blame gr-diff" data-line-number="0"></td>
- <td class="gr-diff left"></td>
- <td class="gr-diff lineNum right" data-value="15">
- <button
- aria-label="15 added"
- class="gr-diff lineNumButton right"
- data-value="15"
- id="right-button-15"
- tabindex="-1"
- >
- 15
- </button>
- </td>
- <td class="add content gr-diff right">
- <div
- class="contentText gr-diff"
- data-side="right"
- id="right-content-15"
- ></div>
- <div class="thread-group" data-side="right"></div>
- </td>
- </tr>
- </tbody>
- <tbody class="both gr-diff section">
- <tr
- aria-labelledby="left-button-17 right-button-16 right-content-16"
- class="both diff-row gr-diff unified"
- tabindex="-1"
- >
- <td class="blame gr-diff" data-line-number="17"></td>
- <td class="gr-diff left lineNum" data-value="17">
- <button
- aria-label="17 unmodified"
- class="gr-diff left lineNumButton"
- data-value="17"
- id="left-button-17"
- tabindex="-1"
- >
- 17
- </button>
- </td>
- <td class="gr-diff lineNum right" data-value="16">
- <button
- aria-label="16 unmodified"
- class="gr-diff lineNumButton right"
- data-value="16"
- id="right-button-16"
- tabindex="-1"
- >
- 16
- </button>
- </td>
- <td class="both content gr-diff no-intraline-info right">
- <div
- class="contentText gr-diff"
- data-side="right"
- id="right-content-16"
- ></div>
- <div class="thread-group" data-side="right"></div>
- </td>
- </tr>
- <tr
- aria-labelledby="left-button-18 right-button-17 right-content-17"
- class="both diff-row gr-diff unified"
- tabindex="-1"
- >
- <td class="blame gr-diff" data-line-number="18"></td>
- <td class="gr-diff left lineNum" data-value="18">
- <button
- aria-label="18 unmodified"
- class="gr-diff left lineNumButton"
- data-value="18"
- id="left-button-18"
- tabindex="-1"
- >
- 18
- </button>
- </td>
- <td class="gr-diff lineNum right" data-value="17">
- <button
- aria-label="17 unmodified"
- class="gr-diff lineNumButton right"
- data-value="17"
- id="right-button-17"
- tabindex="-1"
- >
- 17
- </button>
- </td>
- <td class="both content gr-diff no-intraline-info right">
- <div
- class="contentText gr-diff"
- data-side="right"
- id="right-content-17"
- ></div>
- <div class="thread-group" data-side="right"></div>
- </td>
- </tr>
- <tr
- aria-labelledby="left-button-19 right-button-18 right-content-18"
- class="both diff-row gr-diff unified"
- tabindex="-1"
- >
- <td class="blame gr-diff" data-line-number="19"></td>
- <td class="gr-diff left lineNum" data-value="19">
- <button
- aria-label="19 unmodified"
- class="gr-diff left lineNumButton"
- data-value="19"
- id="left-button-19"
- tabindex="-1"
- >
- 19
- </button>
- </td>
- <td class="gr-diff lineNum right" data-value="18">
- <button
- aria-label="18 unmodified"
- class="gr-diff lineNumButton right"
- data-value="18"
- id="right-button-18"
- tabindex="-1"
- >
- 18
- </button>
- </td>
- <td class="both content gr-diff no-intraline-info right">
- <div
- class="contentText gr-diff"
- data-side="right"
- id="right-content-18"
- ></div>
- <div class="thread-group" data-side="right"></div>
- </td>
- </tr>
- </tbody>
- <tbody class="contextControl gr-diff section">
- <tr class="above contextBackground gr-diff unified">
- <td class="blame gr-diff" data-line-number="0"></td>
- <td class="contextLineNum gr-diff"></td>
- <td class="contextLineNum gr-diff"></td>
- <td class="gr-diff"></td>
- </tr>
- <tr class="dividerRow gr-diff show-both">
- <td class="blame gr-diff" data-line-number="0"></td>
- <td class="dividerCell gr-diff" colspan="3">
- <gr-context-controls class="gr-diff" showconfig="both">
- </gr-context-controls>
- </td>
- </tr>
- <tr class="below contextBackground gr-diff unified">
- <td class="blame gr-diff" data-line-number="0"></td>
- <td class="contextLineNum gr-diff"></td>
- <td class="contextLineNum gr-diff"></td>
- <td class="gr-diff"></td>
- </tr>
- </tbody>
- <tbody class="both gr-diff section">
- <tr
- aria-labelledby="left-button-38 right-button-37 right-content-37"
- class="both diff-row gr-diff unified"
- tabindex="-1"
- >
- <td class="blame gr-diff" data-line-number="38"></td>
- <td class="gr-diff left lineNum" data-value="38">
- <button
- aria-label="38 unmodified"
- class="gr-diff left lineNumButton"
- data-value="38"
- id="left-button-38"
- tabindex="-1"
- >
- 38
- </button>
- </td>
- <td class="gr-diff lineNum right" data-value="37">
- <button
- aria-label="37 unmodified"
- class="gr-diff lineNumButton right"
- data-value="37"
- id="right-button-37"
- tabindex="-1"
- >
- 37
- </button>
- </td>
- <td class="both content gr-diff no-intraline-info right">
- <div
- class="contentText gr-diff"
- data-side="right"
- id="right-content-37"
- ></div>
- <div class="thread-group" data-side="right"></div>
- </td>
- </tr>
- <tr
- aria-labelledby="left-button-39 right-button-38 right-content-38"
- class="both diff-row gr-diff unified"
- tabindex="-1"
- >
- <td class="blame gr-diff" data-line-number="39"></td>
- <td class="gr-diff left lineNum" data-value="39">
- <button
- aria-label="39 unmodified"
- class="gr-diff left lineNumButton"
- data-value="39"
- id="left-button-39"
- tabindex="-1"
- >
- 39
- </button>
- </td>
- <td class="gr-diff lineNum right" data-value="38">
- <button
- aria-label="38 unmodified"
- class="gr-diff lineNumButton right"
- data-value="38"
- id="right-button-38"
- tabindex="-1"
- >
- 38
- </button>
- </td>
- <td class="both content gr-diff no-intraline-info right">
- <div
- class="contentText gr-diff"
- data-side="right"
- id="right-content-38"
- ></div>
- <div class="thread-group" data-side="right"></div>
- </td>
- </tr>
- <tr
- aria-labelledby="left-button-40 right-button-39 right-content-39"
- class="both diff-row gr-diff unified"
- tabindex="-1"
- >
- <td class="blame gr-diff" data-line-number="40"></td>
- <td class="gr-diff left lineNum" data-value="40">
- <button
- aria-label="40 unmodified"
- class="gr-diff left lineNumButton"
- data-value="40"
- id="left-button-40"
- tabindex="-1"
- >
- 40
- </button>
- </td>
- <td class="gr-diff lineNum right" data-value="39">
- <button
- aria-label="39 unmodified"
- class="gr-diff lineNumButton right"
- data-value="39"
- id="right-button-39"
- tabindex="-1"
- >
- 39
- </button>
- </td>
- <td class="both content gr-diff no-intraline-info right">
- <div
- class="contentText gr-diff"
- data-side="right"
- id="right-content-39"
- ></div>
- <div class="thread-group" data-side="right"></div>
- </td>
- </tr>
- </tbody>
- <tbody class="delta gr-diff section total">
- <tr
- aria-labelledby="right-button-40 right-content-40"
- class="add diff-row gr-diff unified"
- tabindex="-1"
- >
- <td class="blame gr-diff" data-line-number="0"></td>
- <td class="gr-diff left"></td>
- <td class="gr-diff lineNum right" data-value="40">
- <button
- aria-label="40 added"
- class="gr-diff lineNumButton right"
- data-value="40"
- id="right-button-40"
- tabindex="-1"
- >
- 40
- </button>
- </td>
- <td class="add content gr-diff no-intraline-info right">
- <div
- class="contentText gr-diff"
- data-side="right"
- id="right-content-40"
- ></div>
- <div class="thread-group" data-side="right"></div>
- </td>
- </tr>
- <tr
- aria-labelledby="right-button-41 right-content-41"
- class="add diff-row gr-diff unified"
- tabindex="-1"
- >
- <td class="blame gr-diff" data-line-number="0"></td>
- <td class="gr-diff left"></td>
- <td class="gr-diff lineNum right" data-value="41">
- <button
- aria-label="41 added"
- class="gr-diff lineNumButton right"
- data-value="41"
- id="right-button-41"
- tabindex="-1"
- >
- 41
- </button>
- </td>
- <td class="add content gr-diff no-intraline-info right">
- <div
- class="contentText gr-diff"
- data-side="right"
- id="right-content-41"
- ></div>
- <div class="thread-group" data-side="right"></div>
- </td>
- </tr>
- <tr
- aria-labelledby="right-button-42 right-content-42"
- class="add diff-row gr-diff unified"
- tabindex="-1"
- >
- <td class="blame gr-diff" data-line-number="0"></td>
- <td class="gr-diff left"></td>
- <td class="gr-diff lineNum right" data-value="42">
- <button
- aria-label="42 added"
- class="gr-diff lineNumButton right"
- data-value="42"
- id="right-button-42"
- tabindex="-1"
- >
- 42
- </button>
- </td>
- <td class="add content gr-diff no-intraline-info right">
- <div
- class="contentText gr-diff"
- data-side="right"
- id="right-content-42"
- ></div>
- <div class="thread-group" data-side="right"></div>
- </td>
- </tr>
- <tr
- aria-labelledby="right-button-43 right-content-43"
- class="add diff-row gr-diff unified"
- tabindex="-1"
- >
- <td class="blame gr-diff" data-line-number="0"></td>
- <td class="gr-diff left"></td>
- <td class="gr-diff lineNum right" data-value="43">
- <button
- aria-label="43 added"
- class="gr-diff lineNumButton right"
- data-value="43"
- id="right-button-43"
- tabindex="-1"
- >
- 43
- </button>
- </td>
- <td class="add content gr-diff no-intraline-info right">
- <div
- class="contentText gr-diff"
- data-side="right"
- id="right-content-43"
- ></div>
- <div class="thread-group" data-side="right"></div>
- </td>
- </tr>
- </tbody>
- <tbody class="both gr-diff section">
- <tr
- aria-labelledby="left-button-41 right-button-44 right-content-44"
- class="both diff-row gr-diff unified"
- tabindex="-1"
- >
- <td class="blame gr-diff" data-line-number="41"></td>
- <td class="gr-diff left lineNum" data-value="41">
- <button
- aria-label="41 unmodified"
- class="gr-diff left lineNumButton"
- data-value="41"
- id="left-button-41"
- tabindex="-1"
- >
- 41
- </button>
- </td>
- <td class="gr-diff lineNum right" data-value="44">
- <button
- aria-label="44 unmodified"
- class="gr-diff lineNumButton right"
- data-value="44"
- id="right-button-44"
- tabindex="-1"
- >
- 44
- </button>
- </td>
- <td class="both content gr-diff no-intraline-info right">
- <div
- class="contentText gr-diff"
- data-side="right"
- id="right-content-44"
- ></div>
- <div class="thread-group" data-side="right"></div>
- </td>
- </tr>
- <tr
- aria-labelledby="left-button-42 right-button-45 right-content-45"
- class="both diff-row gr-diff unified"
- tabindex="-1"
- >
- <td class="blame gr-diff" data-line-number="42"></td>
- <td class="gr-diff left lineNum" data-value="42">
- <button
- aria-label="42 unmodified"
- class="gr-diff left lineNumButton"
- data-value="42"
- id="left-button-42"
- tabindex="-1"
- >
- 42
- </button>
- </td>
- <td class="gr-diff lineNum right" data-value="45">
- <button
- aria-label="45 unmodified"
- class="gr-diff lineNumButton right"
- data-value="45"
- id="right-button-45"
- tabindex="-1"
- >
- 45
- </button>
- </td>
- <td class="both content gr-diff no-intraline-info right">
- <div
- class="contentText gr-diff"
- data-side="right"
- id="right-content-45"
- ></div>
- <div class="thread-group" data-side="right"></div>
- </td>
- </tr>
- <tr
- aria-labelledby="left-button-43 right-button-46 right-content-46"
- class="both diff-row gr-diff unified"
- tabindex="-1"
- >
- <td class="blame gr-diff" data-line-number="43"></td>
- <td class="gr-diff left lineNum" data-value="43">
- <button
- aria-label="43 unmodified"
- class="gr-diff left lineNumButton"
- data-value="43"
- id="left-button-43"
- tabindex="-1"
- >
- 43
- </button>
- </td>
- <td class="gr-diff lineNum right" data-value="46">
- <button
- aria-label="46 unmodified"
- class="gr-diff lineNumButton right"
- data-value="46"
- id="right-button-46"
- tabindex="-1"
- >
- 46
- </button>
- </td>
- <td class="both content gr-diff no-intraline-info right">
- <div
- class="contentText gr-diff"
- data-side="right"
- id="right-content-46"
- ></div>
- <div class="thread-group" data-side="right"></div>
- </td>
- </tr>
- <tr
- aria-labelledby="left-button-44 right-button-47 right-content-47"
- class="both diff-row gr-diff unified"
- tabindex="-1"
- >
- <td class="blame gr-diff" data-line-number="44"></td>
- <td class="gr-diff left lineNum" data-value="44">
- <button
- aria-label="44 unmodified"
- class="gr-diff left lineNumButton"
- data-value="44"
- id="left-button-44"
- tabindex="-1"
- >
- 44
- </button>
- </td>
- <td class="gr-diff lineNum right" data-value="47">
- <button
- aria-label="47 unmodified"
- class="gr-diff lineNumButton right"
- data-value="47"
- id="right-button-47"
- tabindex="-1"
- >
- 47
- </button>
- </td>
- <td class="both content gr-diff no-intraline-info right">
- <div
- class="contentText gr-diff"
- data-side="right"
- id="right-content-47"
- ></div>
- <div class="thread-group" data-side="right"></div>
- </td>
- </tr>
- <tr
- aria-labelledby="left-button-45 right-button-48 right-content-48"
- class="both diff-row gr-diff unified"
- tabindex="-1"
- >
- <td class="blame gr-diff" data-line-number="45"></td>
- <td class="gr-diff left lineNum" data-value="45">
- <button
- aria-label="45 unmodified"
- class="gr-diff left lineNumButton"
- data-value="45"
- id="left-button-45"
- tabindex="-1"
- >
- 45
- </button>
- </td>
- <td class="gr-diff lineNum right" data-value="48">
- <button
- aria-label="48 unmodified"
- class="gr-diff lineNumButton right"
- data-value="48"
- id="right-button-48"
- tabindex="-1"
- >
- 48
- </button>
- </td>
- <td class="both content gr-diff no-intraline-info right">
- <div
- class="contentText gr-diff"
- data-side="right"
- id="right-content-48"
- ></div>
- <div class="thread-group" data-side="right"></div>
- </td>
- </tr>
- </tbody>
- </table>
- </div>
- `,
- {
- ignoreTags: [
- 'gr-context-controls-section',
- 'gr-diff-section',
- 'gr-diff-row',
- 'gr-diff-text',
- 'gr-legacy-text',
- 'slot',
- ],
- }
- );
- });
-
- test('a normal diff lit', async () => {
- element.prefs = {...MINIMAL_PREFS};
- element.diff = createDiff();
- await element.updateComplete;
- await waitForEventOnce(element, 'render');
- assert.shadowDom.equal(
- element,
- /* HTML */ `
- <div class="diffContainer oldDiff sideBySide">
- <table class="selected-right" id="diffTable">
- <colgroup>
- <col class="blame gr-diff" />
- <col class="gr-diff left" width="48" />
- <col class="gr-diff left sign" />
- <col class="gr-diff left" />
- <col class="gr-diff right" width="48" />
- <col class="gr-diff right sign" />
- <col class="gr-diff right" />
- </colgroup>
- <tbody class="both gr-diff section">
- <tr
- aria-labelledby="left-button-LOST left-content-LOST right-button-LOST right-content-LOST"
- class="diff-row gr-diff side-by-side"
- left-type="both"
- right-type="both"
- tabindex="-1"
- >
- <td class="blame gr-diff" data-line-number="LOST"></td>
- <td class="gr-diff left lineNum" data-value="LOST"></td>
- <td class="gr-diff left no-intraline-info sign"></td>
- <td class="both content gr-diff left lost no-intraline-info">
- <div class="thread-group" data-side="left"></div>
- </td>
- <td class="gr-diff lineNum right" data-value="LOST"></td>
- <td class="gr-diff no-intraline-info right sign"></td>
- <td class="both content gr-diff lost no-intraline-info right">
- <div class="thread-group" data-side="right"></div>
- </td>
- </tr>
- </tbody>
- <tbody class="both gr-diff section">
- <tr
- aria-labelledby="left-button-FILE left-content-FILE right-button-FILE right-content-FILE"
- class="diff-row gr-diff side-by-side"
- left-type="both"
- right-type="both"
- tabindex="-1"
- >
- <td class="blame gr-diff" data-line-number="FILE"></td>
- <td class="gr-diff left lineNum" data-value="FILE">
- <button
- aria-label="Add file comment"
- class="gr-diff left lineNumButton"
- data-value="FILE"
- id="left-button-FILE"
- tabindex="-1"
- >
- File
- </button>
- </td>
- <td class="gr-diff left no-intraline-info sign"></td>
- <td class="both content file gr-diff left no-intraline-info">
- <div class="thread-group" data-side="left"></div>
- </td>
- <td class="gr-diff lineNum right" data-value="FILE">
- <button
- aria-label="Add file comment"
- class="gr-diff lineNumButton right"
- data-value="FILE"
- id="right-button-FILE"
- tabindex="-1"
- >
- File
- </button>
- </td>
- <td class="gr-diff no-intraline-info right sign"></td>
- <td class="both content file gr-diff no-intraline-info right">
- <div class="thread-group" data-side="right"></div>
- </td>
- </tr>
- </tbody>
- <tbody class="both gr-diff section">
- <tr
- aria-labelledby="left-button-1 left-content-1 right-button-1 right-content-1"
- class="diff-row gr-diff side-by-side"
- left-type="both"
- right-type="both"
- tabindex="-1"
- >
- <td class="blame gr-diff" data-line-number="1"></td>
- <td class="gr-diff left lineNum" data-value="1">
- <button
- aria-label="1 unmodified"
- class="gr-diff left lineNumButton"
- data-value="1"
- id="left-button-1"
- tabindex="-1"
- >
- 1
- </button>
- </td>
- <td class="gr-diff left no-intraline-info sign"></td>
- <td class="both content gr-diff left no-intraline-info">
- <div
- class="contentText gr-diff"
- data-side="left"
- id="left-content-1"
- ></div>
- <div class="thread-group" data-side="left"></div>
- </td>
- <td class="gr-diff lineNum right" data-value="1">
- <button
- aria-label="1 unmodified"
- class="gr-diff lineNumButton right"
- data-value="1"
- id="right-button-1"
- tabindex="-1"
- >
- 1
- </button>
- </td>
- <td class="gr-diff no-intraline-info right sign"></td>
- <td class="both content gr-diff no-intraline-info right">
- <div
- class="contentText gr-diff"
- data-side="right"
- id="right-content-1"
- ></div>
- <div class="thread-group" data-side="right"></div>
- </td>
- </tr>
- <tr
- aria-labelledby="left-button-2 left-content-2 right-button-2 right-content-2"
- class="diff-row gr-diff side-by-side"
- left-type="both"
- right-type="both"
- tabindex="-1"
- >
- <td class="blame gr-diff" data-line-number="2"></td>
- <td class="gr-diff left lineNum" data-value="2">
- <button
- aria-label="2 unmodified"
- class="gr-diff left lineNumButton"
- data-value="2"
- id="left-button-2"
- tabindex="-1"
- >
- 2
- </button>
- </td>
- <td class="gr-diff left no-intraline-info sign"></td>
- <td class="both content gr-diff left no-intraline-info">
- <div
- class="contentText gr-diff"
- data-side="left"
- id="left-content-2"
- ></div>
- <div class="thread-group" data-side="left"></div>
- </td>
- <td class="gr-diff lineNum right" data-value="2">
- <button
- aria-label="2 unmodified"
- class="gr-diff lineNumButton right"
- data-value="2"
- id="right-button-2"
- tabindex="-1"
- >
- 2
- </button>
- </td>
- <td class="gr-diff no-intraline-info right sign"></td>
- <td class="both content gr-diff no-intraline-info right">
- <div
- class="contentText gr-diff"
- data-side="right"
- id="right-content-2"
- ></div>
- <div class="thread-group" data-side="right"></div>
- </td>
- </tr>
- <tr
- aria-labelledby="left-button-3 left-content-3 right-button-3 right-content-3"
- class="diff-row gr-diff side-by-side"
- left-type="both"
- right-type="both"
- tabindex="-1"
- >
- <td class="blame gr-diff" data-line-number="3"></td>
- <td class="gr-diff left lineNum" data-value="3">
- <button
- aria-label="3 unmodified"
- class="gr-diff left lineNumButton"
- data-value="3"
- id="left-button-3"
- tabindex="-1"
- >
- 3
- </button>
- </td>
- <td class="gr-diff left no-intraline-info sign"></td>
- <td class="both content gr-diff left no-intraline-info">
- <div
- class="contentText gr-diff"
- data-side="left"
- id="left-content-3"
- ></div>
- <div class="thread-group" data-side="left"></div>
- </td>
- <td class="gr-diff lineNum right" data-value="3">
- <button
- aria-label="3 unmodified"
- class="gr-diff lineNumButton right"
- data-value="3"
- id="right-button-3"
- tabindex="-1"
- >
- 3
- </button>
- </td>
- <td class="gr-diff no-intraline-info right sign"></td>
- <td class="both content gr-diff no-intraline-info right">
- <div
- class="contentText gr-diff"
- data-side="right"
- id="right-content-3"
- ></div>
- <div class="thread-group" data-side="right"></div>
- </td>
- </tr>
- <tr
- aria-labelledby="left-button-4 left-content-4 right-button-4 right-content-4"
- class="diff-row gr-diff side-by-side"
- left-type="both"
- right-type="both"
- tabindex="-1"
- >
- <td class="blame gr-diff" data-line-number="4"></td>
- <td class="gr-diff left lineNum" data-value="4">
- <button
- aria-label="4 unmodified"
- class="gr-diff left lineNumButton"
- data-value="4"
- id="left-button-4"
- tabindex="-1"
- >
- 4
- </button>
- </td>
- <td class="gr-diff left no-intraline-info sign"></td>
- <td class="both content gr-diff left no-intraline-info">
- <div
- class="contentText gr-diff"
- data-side="left"
- id="left-content-4"
- ></div>
- <div class="thread-group" data-side="left"></div>
- </td>
- <td class="gr-diff lineNum right" data-value="4">
- <button
- aria-label="4 unmodified"
- class="gr-diff lineNumButton right"
- data-value="4"
- id="right-button-4"
- tabindex="-1"
- >
- 4
- </button>
- </td>
- <td class="gr-diff no-intraline-info right sign"></td>
- <td class="both content gr-diff no-intraline-info right">
- <div
- class="contentText gr-diff"
- data-side="right"
- id="right-content-4"
- ></div>
- <div class="thread-group" data-side="right"></div>
- </td>
- </tr>
- </tbody>
- <tbody class="delta gr-diff section total">
- <tr
- aria-labelledby="right-button-5 right-content-5"
- class="diff-row gr-diff side-by-side"
- left-type="blank"
- right-type="add"
- tabindex="-1"
- >
- <td class="blame gr-diff" data-line-number="0"></td>
- <td class="blankLineNum gr-diff left"></td>
- <td class="blank gr-diff left no-intraline-info sign"></td>
- <td class="blank gr-diff left no-intraline-info">
- <div class="contentText gr-diff" data-side="left"></div>
- </td>
- <td class="gr-diff lineNum right" data-value="5">
- <button
- aria-label="5 added"
- class="gr-diff lineNumButton right"
- data-value="5"
- id="right-button-5"
- tabindex="-1"
- >
- 5
- </button>
- </td>
- <td class="add gr-diff no-intraline-info right sign">+</td>
- <td class="add content gr-diff no-intraline-info right">
- <div
- class="contentText gr-diff"
- data-side="right"
- id="right-content-5"
- ></div>
- <div class="thread-group" data-side="right"></div>
- </td>
- </tr>
- <tr
- aria-labelledby="right-button-6 right-content-6"
- class="diff-row gr-diff side-by-side"
- left-type="blank"
- right-type="add"
- tabindex="-1"
- >
- <td class="blame gr-diff" data-line-number="0"></td>
- <td class="blankLineNum gr-diff left"></td>
- <td class="blank gr-diff left no-intraline-info sign"></td>
- <td class="blank gr-diff left no-intraline-info">
- <div class="contentText gr-diff" data-side="left"></div>
- </td>
- <td class="gr-diff lineNum right" data-value="6">
- <button
- aria-label="6 added"
- class="gr-diff lineNumButton right"
- data-value="6"
- id="right-button-6"
- tabindex="-1"
- >
- 6
- </button>
- </td>
- <td class="add gr-diff no-intraline-info right sign">+</td>
- <td class="add content gr-diff no-intraline-info right">
- <div
- class="contentText gr-diff"
- data-side="right"
- id="right-content-6"
- ></div>
- <div class="thread-group" data-side="right"></div>
- </td>
- </tr>
- <tr
- aria-labelledby="right-button-7 right-content-7"
- class="diff-row gr-diff side-by-side"
- left-type="blank"
- right-type="add"
- tabindex="-1"
- >
- <td class="blame gr-diff" data-line-number="0"></td>
- <td class="blankLineNum gr-diff left"></td>
- <td class="blank gr-diff left no-intraline-info sign"></td>
- <td class="blank gr-diff left no-intraline-info">
- <div class="contentText gr-diff" data-side="left"></div>
- </td>
- <td class="gr-diff lineNum right" data-value="7">
- <button
- aria-label="7 added"
- class="gr-diff lineNumButton right"
- data-value="7"
- id="right-button-7"
- tabindex="-1"
- >
- 7
- </button>
- </td>
- <td class="add gr-diff no-intraline-info right sign">+</td>
- <td class="add content gr-diff no-intraline-info right">
- <div
- class="contentText gr-diff"
- data-side="right"
- id="right-content-7"
- ></div>
- <div class="thread-group" data-side="right"></div>
- </td>
- </tr>
- </tbody>
- <tbody class="both gr-diff section">
- <tr
- aria-labelledby="left-button-5 left-content-5 right-button-8 right-content-8"
- class="diff-row gr-diff side-by-side"
- left-type="both"
- right-type="both"
- tabindex="-1"
- >
- <td class="blame gr-diff" data-line-number="5"></td>
- <td class="gr-diff left lineNum" data-value="5">
- <button
- aria-label="5 unmodified"
- class="gr-diff left lineNumButton"
- data-value="5"
- id="left-button-5"
- tabindex="-1"
- >
- 5
- </button>
- </td>
- <td class="gr-diff left no-intraline-info sign"></td>
- <td class="both content gr-diff left no-intraline-info">
- <div
- class="contentText gr-diff"
- data-side="left"
- id="left-content-5"
- ></div>
- <div class="thread-group" data-side="left"></div>
- </td>
- <td class="gr-diff lineNum right" data-value="8">
- <button
- aria-label="8 unmodified"
- class="gr-diff lineNumButton right"
- data-value="8"
- id="right-button-8"
- tabindex="-1"
- >
- 8
- </button>
- </td>
- <td class="gr-diff no-intraline-info right sign"></td>
- <td class="both content gr-diff no-intraline-info right">
- <div
- class="contentText gr-diff"
- data-side="right"
- id="right-content-8"
- ></div>
- <div class="thread-group" data-side="right"></div>
- </td>
- </tr>
- <tr
- aria-labelledby="left-button-6 left-content-6 right-button-9 right-content-9"
- class="diff-row gr-diff side-by-side"
- left-type="both"
- right-type="both"
- tabindex="-1"
- >
- <td class="blame gr-diff" data-line-number="6"></td>
- <td class="gr-diff left lineNum" data-value="6">
- <button
- aria-label="6 unmodified"
- class="gr-diff left lineNumButton"
- data-value="6"
- id="left-button-6"
- tabindex="-1"
- >
- 6
- </button>
- </td>
- <td class="gr-diff left no-intraline-info sign"></td>
- <td class="both content gr-diff left no-intraline-info">
- <div
- class="contentText gr-diff"
- data-side="left"
- id="left-content-6"
- ></div>
- <div class="thread-group" data-side="left"></div>
- </td>
- <td class="gr-diff lineNum right" data-value="9">
- <button
- aria-label="9 unmodified"
- class="gr-diff lineNumButton right"
- data-value="9"
- id="right-button-9"
- tabindex="-1"
- >
- 9
- </button>
- </td>
- <td class="gr-diff no-intraline-info right sign"></td>
- <td class="both content gr-diff no-intraline-info right">
- <div
- class="contentText gr-diff"
- data-side="right"
- id="right-content-9"
- ></div>
- <div class="thread-group" data-side="right"></div>
- </td>
- </tr>
- <tr
- aria-labelledby="left-button-7 left-content-7 right-button-10 right-content-10"
- class="diff-row gr-diff side-by-side"
- left-type="both"
- right-type="both"
- tabindex="-1"
- >
- <td class="blame gr-diff" data-line-number="7"></td>
- <td class="gr-diff left lineNum" data-value="7">
- <button
- aria-label="7 unmodified"
- class="gr-diff left lineNumButton"
- data-value="7"
- id="left-button-7"
- tabindex="-1"
- >
- 7
- </button>
- </td>
- <td class="gr-diff left no-intraline-info sign"></td>
- <td class="both content gr-diff left no-intraline-info">
- <div
- class="contentText gr-diff"
- data-side="left"
- id="left-content-7"
- ></div>
- <div class="thread-group" data-side="left"></div>
- </td>
- <td class="gr-diff lineNum right" data-value="10">
- <button
- aria-label="10 unmodified"
- class="gr-diff lineNumButton right"
- data-value="10"
- id="right-button-10"
- tabindex="-1"
- >
- 10
- </button>
- </td>
- <td class="gr-diff no-intraline-info right sign"></td>
- <td class="both content gr-diff no-intraline-info right">
- <div
- class="contentText gr-diff"
- data-side="right"
- id="right-content-10"
- ></div>
- <div class="thread-group" data-side="right"></div>
- </td>
- </tr>
- <tr
- aria-labelledby="left-button-8 left-content-8 right-button-11 right-content-11"
- class="diff-row gr-diff side-by-side"
- left-type="both"
- right-type="both"
- tabindex="-1"
- >
- <td class="blame gr-diff" data-line-number="8"></td>
- <td class="gr-diff left lineNum" data-value="8">
- <button
- aria-label="8 unmodified"
- class="gr-diff left lineNumButton"
- data-value="8"
- id="left-button-8"
- tabindex="-1"
- >
- 8
- </button>
- </td>
- <td class="gr-diff left no-intraline-info sign"></td>
- <td class="both content gr-diff left no-intraline-info">
- <div
- class="contentText gr-diff"
- data-side="left"
- id="left-content-8"
- ></div>
- <div class="thread-group" data-side="left"></div>
- </td>
- <td class="gr-diff lineNum right" data-value="11">
- <button
- aria-label="11 unmodified"
- class="gr-diff lineNumButton right"
- data-value="11"
- id="right-button-11"
- tabindex="-1"
- >
- 11
- </button>
- </td>
- <td class="gr-diff no-intraline-info right sign"></td>
- <td class="both content gr-diff no-intraline-info right">
- <div
- class="contentText gr-diff"
- data-side="right"
- id="right-content-11"
- ></div>
- <div class="thread-group" data-side="right"></div>
- </td>
- </tr>
- <tr
- aria-labelledby="left-button-9 left-content-9 right-button-12 right-content-12"
- class="diff-row gr-diff side-by-side"
- left-type="both"
- right-type="both"
- tabindex="-1"
- >
- <td class="blame gr-diff" data-line-number="9"></td>
- <td class="gr-diff left lineNum" data-value="9">
- <button
- aria-label="9 unmodified"
- class="gr-diff left lineNumButton"
- data-value="9"
- id="left-button-9"
- tabindex="-1"
- >
- 9
- </button>
- </td>
- <td class="gr-diff left no-intraline-info sign"></td>
- <td class="both content gr-diff left no-intraline-info">
- <div
- class="contentText gr-diff"
- data-side="left"
- id="left-content-9"
- ></div>
- <div class="thread-group" data-side="left"></div>
- </td>
- <td class="gr-diff lineNum right" data-value="12">
- <button
- aria-label="12 unmodified"
- class="gr-diff lineNumButton right"
- data-value="12"
- id="right-button-12"
- tabindex="-1"
- >
- 12
- </button>
- </td>
- <td class="gr-diff no-intraline-info right sign"></td>
- <td class="both content gr-diff no-intraline-info right">
- <div
- class="contentText gr-diff"
- data-side="right"
- id="right-content-12"
- ></div>
- <div class="thread-group" data-side="right"></div>
- </td>
- </tr>
- </tbody>
- <tbody class="delta gr-diff section total">
- <tr
- aria-labelledby="left-button-10 left-content-10"
- class="diff-row gr-diff side-by-side"
- left-type="remove"
- right-type="blank"
- tabindex="-1"
- >
- <td class="blame gr-diff" data-line-number="10"></td>
- <td class="gr-diff left lineNum" data-value="10">
- <button
- aria-label="10 removed"
- class="gr-diff left lineNumButton"
- data-value="10"
- id="left-button-10"
- tabindex="-1"
- >
- 10
- </button>
- </td>
- <td class="gr-diff left no-intraline-info remove sign">-</td>
- <td class="content gr-diff left no-intraline-info remove">
- <div
- class="contentText gr-diff"
- data-side="left"
- id="left-content-10"
- ></div>
- <div class="thread-group" data-side="left"></div>
- </td>
- <td class="blankLineNum gr-diff right"></td>
- <td class="blank gr-diff no-intraline-info right sign"></td>
- <td class="blank gr-diff no-intraline-info right">
- <div class="contentText gr-diff" data-side="right"></div>
- </td>
- </tr>
- <tr
- aria-labelledby="left-button-11 left-content-11"
- class="diff-row gr-diff side-by-side"
- left-type="remove"
- right-type="blank"
- tabindex="-1"
- >
- <td class="blame gr-diff" data-line-number="11"></td>
- <td class="gr-diff left lineNum" data-value="11">
- <button
- aria-label="11 removed"
- class="gr-diff left lineNumButton"
- data-value="11"
- id="left-button-11"
- tabindex="-1"
- >
- 11
- </button>
- </td>
- <td class="gr-diff left no-intraline-info remove sign">-</td>
- <td class="content gr-diff left no-intraline-info remove">
- <div
- class="contentText gr-diff"
- data-side="left"
- id="left-content-11"
- ></div>
- <div class="thread-group" data-side="left"></div>
- </td>
- <td class="blankLineNum gr-diff right"></td>
- <td class="blank gr-diff no-intraline-info right sign"></td>
- <td class="blank gr-diff no-intraline-info right">
- <div class="contentText gr-diff" data-side="right"></div>
- </td>
- </tr>
- <tr
- aria-labelledby="left-button-12 left-content-12"
- class="diff-row gr-diff side-by-side"
- left-type="remove"
- right-type="blank"
- tabindex="-1"
- >
- <td class="blame gr-diff" data-line-number="12"></td>
- <td class="gr-diff left lineNum" data-value="12">
- <button
- aria-label="12 removed"
- class="gr-diff left lineNumButton"
- data-value="12"
- id="left-button-12"
- tabindex="-1"
- >
- 12
- </button>
- </td>
- <td class="gr-diff left no-intraline-info remove sign">-</td>
- <td class="content gr-diff left no-intraline-info remove">
- <div
- class="contentText gr-diff"
- data-side="left"
- id="left-content-12"
- ></div>
- <div class="thread-group" data-side="left"></div>
- </td>
- <td class="blankLineNum gr-diff right"></td>
- <td class="blank gr-diff no-intraline-info right sign"></td>
- <td class="blank gr-diff no-intraline-info right">
- <div class="contentText gr-diff" data-side="right"></div>
- </td>
- </tr>
- <tr
- aria-labelledby="left-button-13 left-content-13"
- class="diff-row gr-diff side-by-side"
- left-type="remove"
- right-type="blank"
- tabindex="-1"
- >
- <td class="blame gr-diff" data-line-number="13"></td>
- <td class="gr-diff left lineNum" data-value="13">
- <button
- aria-label="13 removed"
- class="gr-diff left lineNumButton"
- data-value="13"
- id="left-button-13"
- tabindex="-1"
- >
- 13
- </button>
- </td>
- <td class="gr-diff left no-intraline-info remove sign">-</td>
- <td class="content gr-diff left no-intraline-info remove">
- <div
- class="contentText gr-diff"
- data-side="left"
- id="left-content-13"
- ></div>
- <div class="thread-group" data-side="left"></div>
- </td>
- <td class="blankLineNum gr-diff right"></td>
- <td class="blank gr-diff no-intraline-info right sign"></td>
- <td class="blank gr-diff no-intraline-info right">
- <div class="contentText gr-diff" data-side="right"></div>
- </td>
- </tr>
- </tbody>
- <tbody class="delta gr-diff ignoredWhitespaceOnly section">
- <tr
- aria-labelledby="left-button-14 left-content-14 right-button-13 right-content-13"
- class="diff-row gr-diff side-by-side"
- left-type="remove"
- right-type="add"
- tabindex="-1"
- >
- <td class="blame gr-diff" data-line-number="14"></td>
- <td class="gr-diff left lineNum" data-value="14">
- <button
- aria-label="14 removed"
- class="gr-diff left lineNumButton"
- data-value="14"
- id="left-button-14"
- tabindex="-1"
- >
- 14
- </button>
- </td>
- <td class="gr-diff left no-intraline-info remove sign">-</td>
- <td class="content gr-diff left no-intraline-info remove">
- <div
- class="contentText gr-diff"
- data-side="left"
- id="left-content-14"
- ></div>
- <div class="thread-group" data-side="left"></div>
- </td>
- <td class="gr-diff lineNum right" data-value="13">
- <button
- aria-label="13 added"
- class="gr-diff lineNumButton right"
- data-value="13"
- id="right-button-13"
- tabindex="-1"
- >
- 13
- </button>
- </td>
- <td class="add gr-diff no-intraline-info right sign">+</td>
- <td class="add content gr-diff no-intraline-info right">
- <div
- class="contentText gr-diff"
- data-side="right"
- id="right-content-13"
- ></div>
- <div class="thread-group" data-side="right"></div>
- </td>
- </tr>
- <tr
- aria-labelledby="left-button-15 left-content-15 right-button-14 right-content-14"
- class="diff-row gr-diff side-by-side"
- left-type="remove"
- right-type="add"
- tabindex="-1"
- >
- <td class="blame gr-diff" data-line-number="15"></td>
- <td class="gr-diff left lineNum" data-value="15">
- <button
- aria-label="15 removed"
- class="gr-diff left lineNumButton"
- data-value="15"
- id="left-button-15"
- tabindex="-1"
- >
- 15
- </button>
- </td>
- <td class="gr-diff left no-intraline-info remove sign">-</td>
- <td class="content gr-diff left no-intraline-info remove">
- <div
- class="contentText gr-diff"
- data-side="left"
- id="left-content-15"
- ></div>
- <div class="thread-group" data-side="left"></div>
- </td>
- <td class="gr-diff lineNum right" data-value="14">
- <button
- aria-label="14 added"
- class="gr-diff lineNumButton right"
- data-value="14"
- id="right-button-14"
- tabindex="-1"
- >
- 14
- </button>
- </td>
- <td class="add gr-diff no-intraline-info right sign">+</td>
- <td class="add content gr-diff no-intraline-info right">
- <div
- class="contentText gr-diff"
- data-side="right"
- id="right-content-14"
- ></div>
- <div class="thread-group" data-side="right"></div>
- </td>
- </tr>
- </tbody>
- <tbody class="delta gr-diff section">
- <tr
- aria-labelledby="left-button-16 left-content-16 right-button-15 right-content-15"
- class="diff-row gr-diff side-by-side"
- left-type="remove"
- right-type="add"
- tabindex="-1"
- >
- <td class="blame gr-diff" data-line-number="16"></td>
- <td class="gr-diff left lineNum" data-value="16">
- <button
- aria-label="16 removed"
- class="gr-diff left lineNumButton"
- data-value="16"
- id="left-button-16"
- tabindex="-1"
- >
- 16
- </button>
- </td>
- <td class="gr-diff left remove sign">-</td>
- <td class="content gr-diff left remove">
- <div
- class="contentText gr-diff"
- data-side="left"
- id="left-content-16"
- ></div>
- <div class="thread-group" data-side="left"></div>
- </td>
- <td class="gr-diff lineNum right" data-value="15">
- <button
- aria-label="15 added"
- class="gr-diff lineNumButton right"
- data-value="15"
- id="right-button-15"
- tabindex="-1"
- >
- 15
- </button>
- </td>
- <td class="add gr-diff right sign">+</td>
- <td class="add content gr-diff right">
- <div
- class="contentText gr-diff"
- data-side="right"
- id="right-content-15"
- ></div>
- <div class="thread-group" data-side="right"></div>
- </td>
- </tr>
- </tbody>
- <tbody class="both gr-diff section">
- <tr
- aria-labelledby="left-button-17 left-content-17 right-button-16 right-content-16"
- class="diff-row gr-diff side-by-side"
- left-type="both"
- right-type="both"
- tabindex="-1"
- >
- <td class="blame gr-diff" data-line-number="17"></td>
- <td class="gr-diff left lineNum" data-value="17">
- <button
- aria-label="17 unmodified"
- class="gr-diff left lineNumButton"
- data-value="17"
- id="left-button-17"
- tabindex="-1"
- >
- 17
- </button>
- </td>
- <td class="gr-diff left no-intraline-info sign"></td>
- <td class="both content gr-diff left no-intraline-info">
- <div
- class="contentText gr-diff"
- data-side="left"
- id="left-content-17"
- ></div>
- <div class="thread-group" data-side="left"></div>
- </td>
- <td class="gr-diff lineNum right" data-value="16">
- <button
- aria-label="16 unmodified"
- class="gr-diff lineNumButton right"
- data-value="16"
- id="right-button-16"
- tabindex="-1"
- >
- 16
- </button>
- </td>
- <td class="gr-diff no-intraline-info right sign"></td>
- <td class="both content gr-diff no-intraline-info right">
- <div
- class="contentText gr-diff"
- data-side="right"
- id="right-content-16"
- ></div>
- <div class="thread-group" data-side="right"></div>
- </td>
- </tr>
- <tr
- aria-labelledby="left-button-18 left-content-18 right-button-17 right-content-17"
- class="diff-row gr-diff side-by-side"
- left-type="both"
- right-type="both"
- tabindex="-1"
- >
- <td class="blame gr-diff" data-line-number="18"></td>
- <td class="gr-diff left lineNum" data-value="18">
- <button
- aria-label="18 unmodified"
- class="gr-diff left lineNumButton"
- data-value="18"
- id="left-button-18"
- tabindex="-1"
- >
- 18
- </button>
- </td>
- <td class="gr-diff left no-intraline-info sign"></td>
- <td class="both content gr-diff left no-intraline-info">
- <div
- class="contentText gr-diff"
- data-side="left"
- id="left-content-18"
- ></div>
- <div class="thread-group" data-side="left"></div>
- </td>
- <td class="gr-diff lineNum right" data-value="17">
- <button
- aria-label="17 unmodified"
- class="gr-diff lineNumButton right"
- data-value="17"
- id="right-button-17"
- tabindex="-1"
- >
- 17
- </button>
- </td>
- <td class="gr-diff no-intraline-info right sign"></td>
- <td class="both content gr-diff no-intraline-info right">
- <div
- class="contentText gr-diff"
- data-side="right"
- id="right-content-17"
- ></div>
- <div class="thread-group" data-side="right"></div>
- </td>
- </tr>
- <tr
- aria-labelledby="left-button-19 left-content-19 right-button-18 right-content-18"
- class="diff-row gr-diff side-by-side"
- left-type="both"
- right-type="both"
- tabindex="-1"
- >
- <td class="blame gr-diff" data-line-number="19"></td>
- <td class="gr-diff left lineNum" data-value="19">
- <button
- aria-label="19 unmodified"
- class="gr-diff left lineNumButton"
- data-value="19"
- id="left-button-19"
- tabindex="-1"
- >
- 19
- </button>
- </td>
- <td class="gr-diff left no-intraline-info sign"></td>
- <td class="both content gr-diff left no-intraline-info">
- <div
- class="contentText gr-diff"
- data-side="left"
- id="left-content-19"
- ></div>
- <div class="thread-group" data-side="left"></div>
- </td>
- <td class="gr-diff lineNum right" data-value="18">
- <button
- aria-label="18 unmodified"
- class="gr-diff lineNumButton right"
- data-value="18"
- id="right-button-18"
- tabindex="-1"
- >
- 18
- </button>
- </td>
- <td class="gr-diff no-intraline-info right sign"></td>
- <td class="both content gr-diff no-intraline-info right">
- <div
- class="contentText gr-diff"
- data-side="right"
- id="right-content-18"
- ></div>
- <div class="thread-group" data-side="right"></div>
- </td>
- </tr>
- </tbody>
- <tbody class="contextControl gr-diff section">
- <tr
- class="above contextBackground gr-diff side-by-side"
- left-type="contextControl"
- right-type="contextControl"
- >
- <td class="blame gr-diff" data-line-number="0"></td>
- <td class="contextLineNum gr-diff"></td>
- <td class="gr-diff sign"></td>
- <td class="gr-diff"></td>
- <td class="contextLineNum gr-diff"></td>
- <td class="gr-diff sign"></td>
- <td class="gr-diff"></td>
- </tr>
- <tr class="dividerRow gr-diff show-both">
- <td class="blame gr-diff" data-line-number="0"></td>
- <td class="gr-diff"></td>
- <td class="dividerCell gr-diff" colspan="3">
- <gr-context-controls
- class="gr-diff"
- showconfig="both"
- ></gr-context-controls>
- </td>
- </tr>
- <tr
- class="below contextBackground gr-diff side-by-side"
- left-type="contextControl"
- right-type="contextControl"
- >
- <td class="blame gr-diff" data-line-number="0"></td>
- <td class="contextLineNum gr-diff"></td>
- <td class="gr-diff sign"></td>
- <td class="gr-diff"></td>
- <td class="contextLineNum gr-diff"></td>
- <td class="gr-diff sign"></td>
- <td class="gr-diff"></td>
- </tr>
- </tbody>
- <tbody class="both gr-diff section">
- <tr
- aria-labelledby="left-button-38 left-content-38 right-button-37 right-content-37"
- class="diff-row gr-diff side-by-side"
- left-type="both"
- right-type="both"
- tabindex="-1"
- >
- <td class="blame gr-diff" data-line-number="38"></td>
- <td class="gr-diff left lineNum" data-value="38">
- <button
- aria-label="38 unmodified"
- class="gr-diff left lineNumButton"
- data-value="38"
- id="left-button-38"
- tabindex="-1"
- >
- 38
- </button>
- </td>
- <td class="gr-diff left no-intraline-info sign"></td>
- <td class="both content gr-diff left no-intraline-info">
- <div
- class="contentText gr-diff"
- data-side="left"
- id="left-content-38"
- ></div>
- <div class="thread-group" data-side="left"></div>
- </td>
- <td class="gr-diff lineNum right" data-value="37">
- <button
- aria-label="37 unmodified"
- class="gr-diff lineNumButton right"
- data-value="37"
- id="right-button-37"
- tabindex="-1"
- >
- 37
- </button>
- </td>
- <td class="gr-diff no-intraline-info right sign"></td>
- <td class="both content gr-diff no-intraline-info right">
- <div
- class="contentText gr-diff"
- data-side="right"
- id="right-content-37"
- ></div>
- <div class="thread-group" data-side="right"></div>
- </td>
- </tr>
- <tr
- aria-labelledby="left-button-39 left-content-39 right-button-38 right-content-38"
- class="diff-row gr-diff side-by-side"
- left-type="both"
- right-type="both"
- tabindex="-1"
- >
- <td class="blame gr-diff" data-line-number="39"></td>
- <td class="gr-diff left lineNum" data-value="39">
- <button
- aria-label="39 unmodified"
- class="gr-diff left lineNumButton"
- data-value="39"
- id="left-button-39"
- tabindex="-1"
- >
- 39
- </button>
- </td>
- <td class="gr-diff left no-intraline-info sign"></td>
- <td class="both content gr-diff left no-intraline-info">
- <div
- class="contentText gr-diff"
- data-side="left"
- id="left-content-39"
- ></div>
- <div class="thread-group" data-side="left"></div>
- </td>
- <td class="gr-diff lineNum right" data-value="38">
- <button
- aria-label="38 unmodified"
- class="gr-diff lineNumButton right"
- data-value="38"
- id="right-button-38"
- tabindex="-1"
- >
- 38
- </button>
- </td>
- <td class="gr-diff no-intraline-info right sign"></td>
- <td class="both content gr-diff no-intraline-info right">
- <div
- class="contentText gr-diff"
- data-side="right"
- id="right-content-38"
- ></div>
- <div class="thread-group" data-side="right"></div>
- </td>
- </tr>
- <tr
- aria-labelledby="left-button-40 left-content-40 right-button-39 right-content-39"
- class="diff-row gr-diff side-by-side"
- left-type="both"
- right-type="both"
- tabindex="-1"
- >
- <td class="blame gr-diff" data-line-number="40"></td>
- <td class="gr-diff left lineNum" data-value="40">
- <button
- aria-label="40 unmodified"
- class="gr-diff left lineNumButton"
- data-value="40"
- id="left-button-40"
- tabindex="-1"
- >
- 40
- </button>
- </td>
- <td class="gr-diff left no-intraline-info sign"></td>
- <td class="both content gr-diff left no-intraline-info">
- <div
- class="contentText gr-diff"
- data-side="left"
- id="left-content-40"
- ></div>
- <div class="thread-group" data-side="left"></div>
- </td>
- <td class="gr-diff lineNum right" data-value="39">
- <button
- aria-label="39 unmodified"
- class="gr-diff lineNumButton right"
- data-value="39"
- id="right-button-39"
- tabindex="-1"
- >
- 39
- </button>
- </td>
- <td class="gr-diff no-intraline-info right sign"></td>
- <td class="both content gr-diff no-intraline-info right">
- <div
- class="contentText gr-diff"
- data-side="right"
- id="right-content-39"
- ></div>
- <div class="thread-group" data-side="right"></div>
- </td>
- </tr>
- </tbody>
- <tbody class="delta gr-diff section total">
- <tr
- aria-labelledby="right-button-40 right-content-40"
- class="diff-row gr-diff side-by-side"
- left-type="blank"
- right-type="add"
- tabindex="-1"
- >
- <td class="blame gr-diff" data-line-number="0"></td>
- <td class="blankLineNum gr-diff left"></td>
- <td class="blank gr-diff left no-intraline-info sign"></td>
- <td class="blank gr-diff left no-intraline-info">
- <div class="contentText gr-diff" data-side="left"></div>
- </td>
- <td class="gr-diff lineNum right" data-value="40">
- <button
- aria-label="40 added"
- class="gr-diff lineNumButton right"
- data-value="40"
- id="right-button-40"
- tabindex="-1"
- >
- 40
- </button>
- </td>
- <td class="add gr-diff no-intraline-info right sign">+</td>
- <td class="add content gr-diff no-intraline-info right">
- <div
- class="contentText gr-diff"
- data-side="right"
- id="right-content-40"
- ></div>
- <div class="thread-group" data-side="right"></div>
- </td>
- </tr>
- <tr
- aria-labelledby="right-button-41 right-content-41"
- class="diff-row gr-diff side-by-side"
- left-type="blank"
- right-type="add"
- tabindex="-1"
- >
- <td class="blame gr-diff" data-line-number="0"></td>
- <td class="blankLineNum gr-diff left"></td>
- <td class="blank gr-diff left no-intraline-info sign"></td>
- <td class="blank gr-diff left no-intraline-info">
- <div class="contentText gr-diff" data-side="left"></div>
- </td>
- <td class="gr-diff lineNum right" data-value="41">
- <button
- aria-label="41 added"
- class="gr-diff lineNumButton right"
- data-value="41"
- id="right-button-41"
- tabindex="-1"
- >
- 41
- </button>
- </td>
- <td class="add gr-diff no-intraline-info right sign">+</td>
- <td class="add content gr-diff no-intraline-info right">
- <div
- class="contentText gr-diff"
- data-side="right"
- id="right-content-41"
- ></div>
- <div class="thread-group" data-side="right"></div>
- </td>
- </tr>
- <tr
- aria-labelledby="right-button-42 right-content-42"
- class="diff-row gr-diff side-by-side"
- left-type="blank"
- right-type="add"
- tabindex="-1"
- >
- <td class="blame gr-diff" data-line-number="0"></td>
- <td class="blankLineNum gr-diff left"></td>
- <td class="blank gr-diff left no-intraline-info sign"></td>
- <td class="blank gr-diff left no-intraline-info">
- <div class="contentText gr-diff" data-side="left"></div>
- </td>
- <td class="gr-diff lineNum right" data-value="42">
- <button
- aria-label="42 added"
- class="gr-diff lineNumButton right"
- data-value="42"
- id="right-button-42"
- tabindex="-1"
- >
- 42
- </button>
- </td>
- <td class="add gr-diff no-intraline-info right sign">+</td>
- <td class="add content gr-diff no-intraline-info right">
- <div
- class="contentText gr-diff"
- data-side="right"
- id="right-content-42"
- ></div>
- <div class="thread-group" data-side="right"></div>
- </td>
- </tr>
- <tr
- aria-labelledby="right-button-43 right-content-43"
- class="diff-row gr-diff side-by-side"
- left-type="blank"
- right-type="add"
- tabindex="-1"
- >
- <td class="blame gr-diff" data-line-number="0"></td>
- <td class="blankLineNum gr-diff left"></td>
- <td class="blank gr-diff left no-intraline-info sign"></td>
- <td class="blank gr-diff left no-intraline-info">
- <div class="contentText gr-diff" data-side="left"></div>
- </td>
- <td class="gr-diff lineNum right" data-value="43">
- <button
- aria-label="43 added"
- class="gr-diff lineNumButton right"
- data-value="43"
- id="right-button-43"
- tabindex="-1"
- >
- 43
- </button>
- </td>
- <td class="add gr-diff no-intraline-info right sign">+</td>
- <td class="add content gr-diff no-intraline-info right">
- <div
- class="contentText gr-diff"
- data-side="right"
- id="right-content-43"
- ></div>
- <div class="thread-group" data-side="right"></div>
- </td>
- </tr>
- </tbody>
- <tbody class="both gr-diff section">
- <tr
- aria-labelledby="left-button-41 left-content-41 right-button-44 right-content-44"
- class="diff-row gr-diff side-by-side"
- left-type="both"
- right-type="both"
- tabindex="-1"
- >
- <td class="blame gr-diff" data-line-number="41"></td>
- <td class="gr-diff left lineNum" data-value="41">
- <button
- aria-label="41 unmodified"
- class="gr-diff left lineNumButton"
- data-value="41"
- id="left-button-41"
- tabindex="-1"
- >
- 41
- </button>
- </td>
- <td class="gr-diff left no-intraline-info sign"></td>
- <td class="both content gr-diff left no-intraline-info">
- <div
- class="contentText gr-diff"
- data-side="left"
- id="left-content-41"
- ></div>
- <div class="thread-group" data-side="left"></div>
- </td>
- <td class="gr-diff lineNum right" data-value="44">
- <button
- aria-label="44 unmodified"
- class="gr-diff lineNumButton right"
- data-value="44"
- id="right-button-44"
- tabindex="-1"
- >
- 44
- </button>
- </td>
- <td class="gr-diff no-intraline-info right sign"></td>
- <td class="both content gr-diff no-intraline-info right">
- <div
- class="contentText gr-diff"
- data-side="right"
- id="right-content-44"
- ></div>
- <div class="thread-group" data-side="right"></div>
- </td>
- </tr>
- <tr
- aria-labelledby="left-button-42 left-content-42 right-button-45 right-content-45"
- class="diff-row gr-diff side-by-side"
- left-type="both"
- right-type="both"
- tabindex="-1"
- >
- <td class="blame gr-diff" data-line-number="42"></td>
- <td class="gr-diff left lineNum" data-value="42">
- <button
- aria-label="42 unmodified"
- class="gr-diff left lineNumButton"
- data-value="42"
- id="left-button-42"
- tabindex="-1"
- >
- 42
- </button>
- </td>
- <td class="gr-diff left no-intraline-info sign"></td>
- <td class="both content gr-diff left no-intraline-info">
- <div
- class="contentText gr-diff"
- data-side="left"
- id="left-content-42"
- ></div>
- <div class="thread-group" data-side="left"></div>
- </td>
- <td class="gr-diff lineNum right" data-value="45">
- <button
- aria-label="45 unmodified"
- class="gr-diff lineNumButton right"
- data-value="45"
- id="right-button-45"
- tabindex="-1"
- >
- 45
- </button>
- </td>
- <td class="gr-diff no-intraline-info right sign"></td>
- <td class="both content gr-diff no-intraline-info right">
- <div
- class="contentText gr-diff"
- data-side="right"
- id="right-content-45"
- ></div>
- <div class="thread-group" data-side="right"></div>
- </td>
- </tr>
- <tr
- aria-labelledby="left-button-43 left-content-43 right-button-46 right-content-46"
- class="diff-row gr-diff side-by-side"
- left-type="both"
- right-type="both"
- tabindex="-1"
- >
- <td class="blame gr-diff" data-line-number="43"></td>
- <td class="gr-diff left lineNum" data-value="43">
- <button
- aria-label="43 unmodified"
- class="gr-diff left lineNumButton"
- data-value="43"
- id="left-button-43"
- tabindex="-1"
- >
- 43
- </button>
- </td>
- <td class="gr-diff left no-intraline-info sign"></td>
- <td class="both content gr-diff left no-intraline-info">
- <div
- class="contentText gr-diff"
- data-side="left"
- id="left-content-43"
- ></div>
- <div class="thread-group" data-side="left"></div>
- </td>
- <td class="gr-diff lineNum right" data-value="46">
- <button
- aria-label="46 unmodified"
- class="gr-diff lineNumButton right"
- data-value="46"
- id="right-button-46"
- tabindex="-1"
- >
- 46
- </button>
- </td>
- <td class="gr-diff no-intraline-info right sign"></td>
- <td class="both content gr-diff no-intraline-info right">
- <div
- class="contentText gr-diff"
- data-side="right"
- id="right-content-46"
- ></div>
- <div class="thread-group" data-side="right"></div>
- </td>
- </tr>
- <tr
- aria-labelledby="left-button-44 left-content-44 right-button-47 right-content-47"
- class="diff-row gr-diff side-by-side"
- left-type="both"
- right-type="both"
- tabindex="-1"
- >
- <td class="blame gr-diff" data-line-number="44"></td>
- <td class="gr-diff left lineNum" data-value="44">
- <button
- aria-label="44 unmodified"
- class="gr-diff left lineNumButton"
- data-value="44"
- id="left-button-44"
- tabindex="-1"
- >
- 44
- </button>
- </td>
- <td class="gr-diff left no-intraline-info sign"></td>
- <td class="both content gr-diff left no-intraline-info">
- <div
- class="contentText gr-diff"
- data-side="left"
- id="left-content-44"
- ></div>
- <div class="thread-group" data-side="left"></div>
- </td>
- <td class="gr-diff lineNum right" data-value="47">
- <button
- aria-label="47 unmodified"
- class="gr-diff lineNumButton right"
- data-value="47"
- id="right-button-47"
- tabindex="-1"
- >
- 47
- </button>
- </td>
- <td class="gr-diff no-intraline-info right sign"></td>
- <td class="both content gr-diff no-intraline-info right">
- <div
- class="contentText gr-diff"
- data-side="right"
- id="right-content-47"
- ></div>
- <div class="thread-group" data-side="right"></div>
- </td>
- </tr>
- <tr
- aria-labelledby="left-button-45 left-content-45 right-button-48 right-content-48"
- class="diff-row gr-diff side-by-side"
- left-type="both"
- right-type="both"
- tabindex="-1"
- >
- <td class="blame gr-diff" data-line-number="45"></td>
- <td class="gr-diff left lineNum" data-value="45">
- <button
- aria-label="45 unmodified"
- class="gr-diff left lineNumButton"
- data-value="45"
- id="left-button-45"
- tabindex="-1"
- >
- 45
- </button>
- </td>
- <td class="gr-diff left no-intraline-info sign"></td>
- <td class="both content gr-diff left no-intraline-info">
- <div
- class="contentText gr-diff"
- data-side="left"
- id="left-content-45"
- ></div>
- <div class="thread-group" data-side="left"></div>
- </td>
- <td class="gr-diff lineNum right" data-value="48">
- <button
- aria-label="48 unmodified"
- class="gr-diff lineNumButton right"
- data-value="48"
- id="right-button-48"
- tabindex="-1"
- >
- 48
- </button>
- </td>
- <td class="gr-diff no-intraline-info right sign"></td>
- <td class="both content gr-diff no-intraline-info right">
- <div
- class="contentText gr-diff"
- data-side="right"
- id="right-content-48"
- ></div>
- <div class="thread-group" data-side="right"></div>
- </td>
- </tr>
- </tbody>
- </table>
- </div>
- `,
- {
- ignoreTags: [
- 'gr-context-controls-section',
- 'gr-diff-section',
- 'gr-diff-row',
- 'gr-diff-text',
- 'gr-legacy-text',
- 'slot',
- ],
- }
- );
- });
- });
-
- suite('selectionchange event handling', () => {
- let handleSelectionChangeStub: sinon.SinonSpy;
-
- const emulateSelection = function () {
- document.dispatchEvent(new CustomEvent('selectionchange'));
- };
-
- setup(async () => {
- handleSelectionChangeStub = sinon.spy(
- element.highlights,
- 'handleSelectionChange'
- );
- });
-
- test('enabled if logged in', async () => {
- element.loggedIn = true;
- await element.updateComplete;
- emulateSelection();
- assert.isTrue(handleSelectionChangeStub.called);
- });
-
- test('ignored if logged out', async () => {
- element.loggedIn = false;
- await element.updateComplete;
- emulateSelection();
- assert.isFalse(handleSelectionChangeStub.called);
- });
- });
-
- test('cancel', () => {
- const cleanupStub = sinon.stub(element.diffBuilder, 'cleanup');
- element.cancel();
- assert.isTrue(cleanupStub.calledOnce);
- });
-
- test('line limit with line_wrapping', async () => {
- element.prefs = {...MINIMAL_PREFS, line_wrapping: true};
- await element.updateComplete;
- assert.equal(getComputedStyleValue('--line-limit-marker', element), '80ch');
- });
-
- test('line limit without line_wrapping', async () => {
- element.prefs = {...MINIMAL_PREFS, line_wrapping: false};
- await element.updateComplete;
- assert.equal(getComputedStyleValue('--line-limit-marker', element), '-1px');
- });
-
- suite('FULL_RESPONSIVE mode', () => {
- setup(async () => {
- element.prefs = {...MINIMAL_PREFS};
- element.renderPrefs = {responsive_mode: 'FULL_RESPONSIVE'};
- await element.updateComplete;
- });
-
- test('line limit is based on line_length', async () => {
- element.prefs = {...element.prefs!, line_length: 100};
- await element.updateComplete;
- assert.equal(
- getComputedStyleValue('--line-limit-marker', element),
- '100ch'
- );
- });
-
- test('content-width should not be defined', () => {
- assert.equal(getComputedStyleValue('--content-width', element), 'none');
- });
- });
-
- suite('SHRINK_ONLY mode', () => {
- setup(async () => {
- element.prefs = {...MINIMAL_PREFS};
- element.renderPrefs = {responsive_mode: 'SHRINK_ONLY'};
- await element.updateComplete;
- });
-
- test('content-width should not be defined', () => {
- assert.equal(getComputedStyleValue('--content-width', element), 'none');
- });
-
- test('max-width considers two content columns in side-by-side', async () => {
- element.viewMode = DiffViewMode.SIDE_BY_SIDE;
- await element.updateComplete;
- assert.equal(
- getComputedStyleValue('--diff-max-width', element),
- 'calc(2 * 80ch + 2 * 48px + 0ch + 1px + 2px)'
- );
- });
-
- test('max-width considers one content column in unified', async () => {
- element.viewMode = DiffViewMode.UNIFIED;
- await element.updateComplete;
- assert.equal(
- getComputedStyleValue('--diff-max-width', element),
- 'calc(1 * 80ch + 2 * 48px + 0ch + 1px + 2px)'
- );
- });
-
- test('max-width considers font-size', async () => {
- element.prefs = {...element.prefs!, font_size: 13};
- await element.updateComplete;
- // Each line number column: 4 * 13 = 52px
- assert.equal(
- getComputedStyleValue('--diff-max-width', element),
- 'calc(2 * 80ch + 2 * 52px + 0ch + 1px + 2px)'
- );
- });
-
- test('sign cols are considered if show_sign_col is true', async () => {
- element.renderPrefs = {...element.renderPrefs, show_sign_col: true};
- await element.updateComplete;
- assert.equal(
- getComputedStyleValue('--diff-max-width', element),
- 'calc(2 * 80ch + 2 * 48px + 2ch + 1px + 2px)'
- );
- });
- });
-
- suite('not logged in', () => {
- setup(async () => {
- element.loggedIn = false;
- await element.updateComplete;
- });
-
- test('toggleLeftDiff', () => {
- element.toggleLeftDiff();
- assert.isTrue(element.classList.contains('no-left'));
- element.toggleLeftDiff();
- assert.isFalse(element.classList.contains('no-left'));
- });
-
- suite('binary diffs', () => {
- test('render binary diff', async () => {
- element.prefs = {
- ...MINIMAL_PREFS,
- };
- element.diff = {
- meta_a: {name: 'carrot.exe', content_type: 'binary', lines: 0},
- meta_b: {name: 'carrot.exe', content_type: 'binary', lines: 0},
- change_type: 'MODIFIED',
- intraline_status: 'OK',
- diff_header: [],
- content: [],
- binary: true,
- };
- await waitForEventOnce(element, 'render');
-
- assert.shadowDom.equal(
- element,
- /* HTML */ `
- <div class="diffContainer oldDiff sideBySide">
- <gr-diff-section class="left-FILE right-FILE"> </gr-diff-section>
- <gr-diff-row class="left-FILE right-FILE"> </gr-diff-row>
- <table class="selected-right" id="diffTable">
- <colgroup>
- <col class="blame gr-diff" />
- <col class="gr-diff left" width="48" />
- <col class="gr-diff left sign" />
- <col class="gr-diff left" />
- <col class="gr-diff right" width="48" />
- <col class="gr-diff right sign" />
- <col class="gr-diff right" />
- </colgroup>
- <tbody class="binary-diff gr-diff"></tbody>
- <tbody class="both gr-diff section">
- <tr
- aria-labelledby="left-button-FILE left-content-FILE right-button-FILE right-content-FILE"
- class="diff-row gr-diff side-by-side"
- left-type="both"
- right-type="both"
- tabindex="-1"
- >
- <td class="blame gr-diff" data-line-number="FILE"></td>
- <td class="gr-diff left lineNum" data-value="FILE">
- <button
- aria-label="Add file comment"
- class="gr-diff left lineNumButton"
- data-value="FILE"
- id="left-button-FILE"
- tabindex="-1"
- >
- File
- </button>
- </td>
- <td class="gr-diff left no-intraline-info sign"></td>
- <td
- class="both content file gr-diff left no-intraline-info"
- >
- <div class="thread-group" data-side="left">
- <slot name="left-FILE"> </slot>
- </div>
- </td>
- <td class="gr-diff lineNum right" data-value="FILE">
- <button
- aria-label="Add file comment"
- class="gr-diff lineNumButton right"
- data-value="FILE"
- id="right-button-FILE"
- tabindex="-1"
- >
- File
- </button>
- </td>
- <td class="gr-diff no-intraline-info right sign"></td>
- <td
- class="both content file gr-diff no-intraline-info right"
- >
- <div class="thread-group" data-side="right">
- <slot name="right-FILE"> </slot>
- </div>
- </td>
- </tr>
- </tbody>
- <tbody class="binary-diff gr-diff">
- <tr class="gr-diff">
- <td class="gr-diff" colspan="5">
- <span> Difference in binary files </span>
- </td>
- </tr>
- </tbody>
- </table>
- </div>
- `
- );
- });
- });
-
- suite('image diffs', () => {
- let mockFile1: ImageInfo;
- let mockFile2: ImageInfo;
- setup(() => {
- mockFile1 = {
- body:
- 'Qk06AAAAAAAAADYAAAAoAAAAAQAAAP////8BACAAAAAAAAAAAAATCwAAE' +
- 'wsAAAAAAAAAAAAAAAAA/w==',
- type: 'image/bmp',
- };
- mockFile2 = {
- body:
- 'Qk06AAAAAAAAADYAAAAoAAAAAQAAAP////8BACAAAAAAAAAAAAATCwAAE' +
- 'wsAAAAAAAAAAAAA/////w==',
- type: 'image/bmp',
- };
-
- element.prefs = {
- context: 10,
- cursor_blink_rate: 0,
- font_size: 12,
- ignore_whitespace: 'IGNORE_NONE',
- line_length: 100,
- line_wrapping: false,
- show_line_endings: true,
- show_tabs: true,
- show_whitespace_errors: true,
- syntax_highlighting: true,
- tab_size: 8,
- };
- });
-
- test('render image diff', async () => {
- element.baseImage = mockFile1;
- element.revisionImage = mockFile2;
- element.diff = {
- meta_a: {name: 'carrot.jpg', content_type: 'image/jpeg', lines: 66},
- meta_b: {name: 'carrot.jpg', content_type: 'image/jpeg', lines: 560},
- intraline_status: 'OK',
- change_type: 'MODIFIED',
- diff_header: [
- 'diff --git a/carrot.jpg b/carrot.jpg',
- 'index 2adc47d..f9c2f2c 100644',
- '--- a/carrot.jpg',
- '+++ b/carrot.jpg',
- 'Binary files differ',
- ],
- content: [{skip: 66}],
- binary: true,
- };
-
- await waitForEventOnce(element, 'render');
- const imageDiffSection = queryAndAssert(element, 'tbody.image-diff');
- assert.lightDom.equal(
- imageDiffSection,
- /* HTML */ `
- <tbody class="gr-diff image-diff">
- <tr class="gr-diff">
- <td class="blank gr-diff left lineNum"></td>
- <td class="gr-diff left">
- <img
- class="gr-diff left"
- src="data:image/bmp;base64,${mockFile1.body}"
- />
- </td>
- <td class="blank gr-diff lineNum right"></td>
- <td class="gr-diff right">
- <img
- class="gr-diff right"
- src="data:image/bmp;base64,${mockFile2.body}"
- />
- </td>
- </tr>
- <tr class="gr-diff">
- <td class="blank gr-diff left lineNum"></td>
- <td class="gr-diff left">
- <label class="gr-diff">
- <span class="gr-diff label"> image/bmp </span>
- </label>
- </td>
- <td class="blank gr-diff lineNum right"></td>
- <td class="gr-diff right">
- <label class="gr-diff">
- <span class="gr-diff label"> image/bmp </span>
- </label>
- </td>
- </tr>
- </tbody>
- `
- );
- const endpoint = queryAndAssert(element, 'tbody.endpoint');
- assert.dom.equal(
- endpoint,
- /* HTML */ `
- <tbody class="gr-diff endpoint">
- <tr class="gr-diff">
- <gr-endpoint-decorator class="gr-diff" name="image-diff">
- <gr-endpoint-param class="gr-diff" name="baseImage">
- </gr-endpoint-param>
- <gr-endpoint-param class="gr-diff" name="revisionImage">
- </gr-endpoint-param>
- </gr-endpoint-decorator>
- </tr>
- </tbody>
- `
- );
- });
-
- test('renders image diffs with a different file name', async () => {
- const mockDiff: DiffInfo = {
- meta_a: {name: 'carrot.jpg', content_type: 'image/jpeg', lines: 66},
- meta_b: {name: 'carrot2.jpg', content_type: 'image/jpeg', lines: 560},
- intraline_status: 'OK',
- change_type: 'MODIFIED',
- diff_header: [
- 'diff --git a/carrot.jpg b/carrot2.jpg',
- 'index 2adc47d..f9c2f2c 100644',
- '--- a/carrot.jpg',
- '+++ b/carrot2.jpg',
- 'Binary files differ',
- ],
- content: [{skip: 66}],
- binary: true,
- };
-
- element.baseImage = mockFile1;
- element.baseImage._name = mockDiff.meta_a!.name;
- element.revisionImage = mockFile2;
- element.revisionImage._name = mockDiff.meta_b!.name;
- element.diff = mockDiff;
-
- await waitForEventOnce(element, 'render');
- const imageDiffSection = queryAndAssert(element, 'tbody.image-diff');
- const leftLabel = queryAndAssert(imageDiffSection, 'td.left label');
- const rightLabel = queryAndAssert(imageDiffSection, 'td.right label');
- assert.dom.equal(
- leftLabel,
- /* HTML */ `
- <label class="gr-diff">
- <span class="gr-diff name"> carrot.jpg </span>
- <br class="gr-diff" />
- <span class="gr-diff label"> image/bmp </span>
- </label>
- `
- );
- assert.dom.equal(
- rightLabel,
- /* HTML */ `
- <label class="gr-diff">
- <span class="gr-diff name"> carrot2.jpg </span>
- <br class="gr-diff" />
- <span class="gr-diff label"> image/bmp </span>
- </label>
- `
- );
- });
-
- test('renders added image', async () => {
- const mockDiff: DiffInfo = {
- meta_b: {name: 'carrot.jpg', content_type: 'image/jpeg', lines: 560},
- intraline_status: 'OK',
- change_type: 'ADDED',
- diff_header: [
- 'diff --git a/carrot.jpg b/carrot.jpg',
- 'index 0000000..f9c2f2c 100644',
- '--- /dev/null',
- '+++ b/carrot.jpg',
- 'Binary files differ',
- ],
- content: [{skip: 66}],
- binary: true,
- };
- element.revisionImage = mockFile2;
- element.diff = mockDiff;
-
- await waitForEventOnce(element, 'render');
- const imageDiffSection = queryAndAssert(element, 'tbody.image-diff');
- const leftImage = query(imageDiffSection, 'td.left img');
- const rightImage = queryAndAssert(imageDiffSection, 'td.right img');
- assert.isNotOk(leftImage);
- assert.dom.equal(
- rightImage,
- /* HTML */ `
- <img
- class="gr-diff right"
- src="data:image/bmp;base64,${mockFile2.body}"
- />
- `
- );
- });
-
- test('renders removed image', async () => {
- const mockDiff: DiffInfo = {
- meta_a: {name: 'carrot.jpg', content_type: 'image/jpeg', lines: 560},
- intraline_status: 'OK',
- change_type: 'DELETED',
- diff_header: [
- 'diff --git a/carrot.jpg b/carrot.jpg',
- 'index f9c2f2c..0000000 100644',
- '--- a/carrot.jpg',
- '+++ /dev/null',
- 'Binary files differ',
- ],
- content: [{skip: 66}],
- binary: true,
- };
- element.baseImage = mockFile1;
- element.diff = mockDiff;
-
- await waitForEventOnce(element, 'render');
- const imageDiffSection = queryAndAssert(element, 'tbody.image-diff');
- const leftImage = queryAndAssert(imageDiffSection, 'td.left img');
- const rightImage = query(imageDiffSection, 'td.right img');
- assert.isNotOk(rightImage);
- assert.dom.equal(
- leftImage,
- /* HTML */ `
- <img
- class="gr-diff left"
- src="data:image/bmp;base64,${mockFile1.body}"
- />
- `
- );
- });
-
- test('does not render disallowed image type', async () => {
- const mockDiff: DiffInfo = {
- meta_a: {
- name: 'carrot.jpg',
- content_type: 'image/jpeg-evil',
- lines: 560,
- },
- intraline_status: 'OK',
- change_type: 'DELETED',
- diff_header: [
- 'diff --git a/carrot.jpg b/carrot.jpg',
- 'index f9c2f2c..0000000 100644',
- '--- a/carrot.jpg',
- '+++ /dev/null',
- 'Binary files differ',
- ],
- content: [{skip: 66}],
- binary: true,
- };
- mockFile1.type = 'image/jpeg-evil';
- element.baseImage = mockFile1;
- element.diff = mockDiff;
-
- await waitForEventOnce(element, 'render');
- const imageDiffSection = queryAndAssert(element, 'tbody.image-diff');
- const leftImage = query(imageDiffSection, 'td.left img');
- assert.isNotOk(leftImage);
- });
- });
-
- test('handleTap lineNum', async () => {
- const addDraftStub = sinon.stub(element, 'addDraftAtLine');
- const el = document.createElement('div');
- el.className = 'lineNum';
- const promise = mockPromise();
- el.addEventListener('click', e => {
- element.handleTap(e);
- assert.isTrue(addDraftStub.called);
- assert.equal(addDraftStub.lastCall.args[0], el);
- promise.resolve();
- });
- el.click();
- await promise;
- });
-
- test('handleTap content', async () => {
- const content = document.createElement('div');
- const lineEl = document.createElement('div');
- lineEl.className = 'lineNum';
- const row = document.createElement('div');
- row.appendChild(lineEl);
- row.appendChild(content);
-
- const selectStub = sinon.stub(element, 'selectLine');
-
- content.className = 'content';
- const promise = mockPromise();
- content.addEventListener('click', e => {
- element.handleTap(e);
- assert.isTrue(selectStub.called);
- assert.equal(selectStub.lastCall.args[0], lineEl);
- promise.resolve();
- });
- content.click();
- await promise;
- });
-
- suite('getCursorStops', () => {
- async function setupDiff() {
- element.diff = createDiff();
- element.prefs = {
- context: 10,
- tab_size: 8,
- font_size: 12,
- line_length: 100,
- cursor_blink_rate: 0,
- line_wrapping: false,
-
- show_line_endings: true,
- show_tabs: true,
- show_whitespace_errors: true,
- syntax_highlighting: true,
- ignore_whitespace: 'IGNORE_NONE',
- };
- await element.updateComplete;
- element.renderDiffTable();
- }
-
- test('returns [] when hidden and noAutoRender', async () => {
- element.noAutoRender = true;
- await setupDiff();
- element.loading = false;
- await element.updateComplete;
- element.hidden = true;
- await element.updateComplete;
- assert.equal(element.getCursorStops().length, 0);
- });
-
- test('returns one stop per line and one for the file row', async () => {
- await setupDiff();
- element.loading = false;
- await element.updateComplete;
- const ROWS = 48;
- const FILE_ROW = 1;
- const LOST_ROW = 1;
- assert.equal(
- element.getCursorStops().length,
- ROWS + FILE_ROW + LOST_ROW
- );
- });
-
- test('returns an additional AbortStop when still loading', async () => {
- await setupDiff();
- element.loading = true;
- await element.updateComplete;
- const ROWS = 48;
- const FILE_ROW = 1;
- const LOST_ROW = 1;
- const actual = element.getCursorStops();
- assert.equal(actual.length, ROWS + FILE_ROW + LOST_ROW + 1);
- assert.isTrue(actual[actual.length - 1] instanceof AbortStop);
- });
- });
- });
-
- suite('logged in', async () => {
- let fakeLineEl: HTMLElement;
- setup(async () => {
- element.loggedIn = true;
-
- fakeLineEl = {
- getAttribute: sinon.stub().returns(42),
- classList: {
- contains: sinon.stub().returns(true),
- },
- } as unknown as HTMLElement;
- await element.updateComplete;
- });
-
- test('addDraftAtLine', () => {
- sinon.stub(element, 'selectLine');
- const createCommentStub = sinon.stub(element, 'createComment');
- element.addDraftAtLine(fakeLineEl);
- assert.isTrue(createCommentStub.calledWithExactly(fakeLineEl, 42));
- });
-
- test('adds long range comment hint', async () => {
- const range = {
- start_line: 1,
- end_line: 12,
- start_character: 0,
- end_character: 0,
- };
- const threadEl = document.createElement('div');
- threadEl.className = 'comment-thread';
- threadEl.setAttribute('diff-side', 'right');
- threadEl.setAttribute('line-num', '1');
- threadEl.setAttribute('range', JSON.stringify(range));
- threadEl.setAttribute('slot', 'right-1');
- const content = [
- {
- a: ['asdf'],
- },
- {
- ab: Array(13).fill('text'),
- },
- ];
- await setupSampleDiff({content});
-
- element.appendChild(threadEl);
-
- const hint = await waitQueryAndAssert<GrRangedCommentHint>(
- element,
- 'gr-ranged-comment-hint'
- );
- assert.deepEqual(hint.range, range);
- });
-
- test('no duplicate range hint for same thread', async () => {
- const range = {
- start_line: 1,
- end_line: 12,
- start_character: 0,
- end_character: 0,
- };
- const threadEl = document.createElement('div');
- threadEl.className = 'comment-thread';
- threadEl.setAttribute('diff-side', 'right');
- threadEl.setAttribute('line-num', '1');
- threadEl.setAttribute('range', JSON.stringify(range));
- threadEl.setAttribute('slot', 'right-1');
- const firstHint = document.createElement('gr-ranged-comment-hint');
- firstHint.range = range;
- firstHint.setAttribute('slot', 'right-1');
- const content = [
- {
- a: ['asdf'],
- },
- {
- ab: Array(13).fill('text'),
- },
- ];
- await setupSampleDiff({content});
-
- element.appendChild(firstHint);
- element.appendChild(threadEl);
-
- assert.equal(
- element.querySelectorAll('gr-ranged-comment-hint').length,
- 1
- );
- });
-
- test('removes long range comment hint when comment is discarded', async () => {
- const range = {
- start_line: 1,
- end_line: 7,
- start_character: 0,
- end_character: 0,
- };
- const threadEl = document.createElement('div');
- threadEl.className = 'comment-thread';
- threadEl.setAttribute('diff-side', 'right');
- threadEl.setAttribute('line-num', '1');
- threadEl.setAttribute('range', JSON.stringify(range));
- threadEl.setAttribute('slot', 'right-1');
- const content = [
- {
- ab: Array(8).fill('text'),
- },
- ];
- await setupSampleDiff({content});
-
- element.appendChild(threadEl);
- await waitUntil(() => element.commentRanges.length === 1);
-
- threadEl.remove();
- await waitUntil(() => element.commentRanges.length === 0);
-
- assert.isEmpty(element.querySelectorAll('gr-ranged-comment-hint'));
- });
-
- suite('change in preferences', () => {
- setup(async () => {
- element.diff = {
- meta_a: {name: 'carrot.jpg', content_type: 'image/jpeg', lines: 66},
- meta_b: {name: 'carrot.jpg', content_type: 'image/jpeg', lines: 560},
- diff_header: [],
- intraline_status: 'OK',
- change_type: 'MODIFIED',
- content: [{skip: 66}],
- };
- await element.updateComplete;
- await element.renderDiffTableTask?.flush();
- });
-
- test('change in preferences re-renders diff', async () => {
- const stub = sinon.stub(element, 'renderDiffTable');
- element.prefs = {
- ...MINIMAL_PREFS,
- };
- await element.updateComplete;
- await element.renderDiffTableTask?.flush();
- assert.isTrue(stub.called);
- });
-
- test('adding/removing property in preferences re-renders diff', async () => {
- const stub = sinon.stub(element, 'renderDiffTable');
- const newPrefs1: DiffPreferencesInfo = {
- ...MINIMAL_PREFS,
- line_wrapping: true,
- };
- element.prefs = newPrefs1;
- await element.updateComplete;
- await element.renderDiffTableTask?.flush();
- assert.isTrue(stub.called);
- stub.reset();
-
- const newPrefs2 = {...newPrefs1};
- delete newPrefs2.line_wrapping;
- element.prefs = newPrefs2;
- await element.updateComplete;
- await element.renderDiffTableTask?.flush();
- assert.isTrue(stub.called);
- });
-
- test(
- 'change in preferences does not re-renders diff with ' +
- 'noRenderOnPrefsChange',
- async () => {
- const stub = sinon.stub(element, 'renderDiffTable');
- element.noRenderOnPrefsChange = true;
- element.prefs = {
- ...MINIMAL_PREFS,
- context: 12,
- };
- await element.updateComplete;
- await element.renderDiffTableTask?.flush();
- assert.isFalse(stub.called);
- }
- );
- });
- });
-
- suite('diff header', () => {
- setup(async () => {
- element.diff = {
- meta_a: {name: 'carrot.jpg', content_type: 'image/jpeg', lines: 66},
- meta_b: {name: 'carrot.jpg', content_type: 'image/jpeg', lines: 560},
- diff_header: [],
- intraline_status: 'OK',
- change_type: 'MODIFIED',
- content: [{skip: 66}],
- };
- await element.updateComplete;
- });
-
- test('hidden', async () => {
- assert.equal(element.computeDiffHeaderItems().length, 0);
- element.diff?.diff_header?.push('diff --git a/test.jpg b/test.jpg');
- assert.equal(element.computeDiffHeaderItems().length, 0);
- element.diff?.diff_header?.push('index 2adc47d..f9c2f2c 100644');
- assert.equal(element.computeDiffHeaderItems().length, 0);
- element.diff?.diff_header?.push('--- a/test.jpg');
- assert.equal(element.computeDiffHeaderItems().length, 0);
- element.diff?.diff_header?.push('+++ b/test.jpg');
- assert.equal(element.computeDiffHeaderItems().length, 0);
- element.diff?.diff_header?.push('test');
- assert.equal(element.computeDiffHeaderItems().length, 1);
- element.requestUpdate('diff');
- await element.updateComplete;
-
- const header = queryAndAssert(element, '#diffHeader');
- assert.equal(header.textContent?.trim(), 'test');
- });
-
- test('binary files', () => {
- element.diff!.binary = true;
- assert.equal(element.computeDiffHeaderItems().length, 0);
- element.diff?.diff_header?.push('diff --git a/test.jpg b/test.jpg');
- assert.equal(element.computeDiffHeaderItems().length, 0);
- element.diff?.diff_header?.push('test');
- assert.equal(element.computeDiffHeaderItems().length, 1);
- element.diff?.diff_header?.push('Binary files differ');
- assert.equal(element.computeDiffHeaderItems().length, 1);
- });
- });
-
- suite('safety and bypass', () => {
- let renderStub: sinon.SinonStub;
-
- setup(async () => {
- renderStub = sinon.stub(element.diffBuilder, 'render').callsFake(() => {
- assertIsDefined(element.diffTable);
- const diffTable = element.diffTable;
- diffTable.dispatchEvent(
- new CustomEvent('render', {bubbles: true, composed: true})
- );
- return Promise.resolve();
- });
- sinon.stub(element, 'getDiffLength').returns(10000);
- element.diff = createDiff();
- element.noRenderOnPrefsChange = true;
- await element.updateComplete;
- });
-
- test('large render w/ context = 10', async () => {
- element.prefs = {...MINIMAL_PREFS, context: 10};
- element.renderDiffTable();
- await waitForEventOnce(element, 'render');
-
- assert.isTrue(renderStub.called);
- assert.isFalse(element.showWarning);
- });
-
- test('large render w/ whole file and bypass', async () => {
- element.prefs = {...MINIMAL_PREFS, context: -1};
- element.safetyBypass = 10;
- element.renderDiffTable();
- await waitForEventOnce(element, 'render');
-
- assert.isTrue(renderStub.called);
- assert.isFalse(element.showWarning);
- });
-
- test('large render w/ whole file and no bypass', async () => {
- element.prefs = {...MINIMAL_PREFS, context: -1};
- element.renderDiffTable();
- await waitForEventOnce(element, 'render');
-
- assert.isFalse(renderStub.called);
- assert.isTrue(element.showWarning);
- });
-
- test('toggles expand context using bypass', async () => {
- element.prefs = {...MINIMAL_PREFS, context: 3};
-
- element.toggleAllContext();
- element.renderDiffTable();
- await element.updateComplete;
-
- assert.equal(element.prefs.context, 3);
- assert.equal(element.safetyBypass, -1);
- assert.equal(element.diffBuilder.prefs.context, -1);
- });
-
- test('toggles collapse context from bypass', async () => {
- element.prefs = {...MINIMAL_PREFS, context: 3};
- element.safetyBypass = -1;
-
- element.toggleAllContext();
- element.renderDiffTable();
- await element.updateComplete;
-
- assert.equal(element.prefs.context, 3);
- assert.isNull(element.safetyBypass);
- assert.equal(element.diffBuilder.prefs.context, 3);
- });
-
- test('toggles collapse context from pref using default', async () => {
- element.prefs = {...MINIMAL_PREFS, context: -1};
-
- element.toggleAllContext();
- element.renderDiffTable();
- await element.updateComplete;
-
- assert.equal(element.prefs.context, -1);
- assert.equal(element.safetyBypass, 10);
- assert.equal(element.diffBuilder.prefs.context, 10);
- });
- });
-
- suite('blame', () => {
- test('unsetting', async () => {
- element.blame = [];
- const setBlameSpy = sinon.spy(element.diffBuilder, 'setBlame');
- element.classList.add('showBlame');
- element.blame = null;
- await element.updateComplete;
- assert.isTrue(setBlameSpy.calledWithExactly(null));
- assert.isFalse(element.classList.contains('showBlame'));
- });
-
- test('setting', async () => {
- element.blame = [
- {
- author: 'test-author',
- time: 12345,
- commit_msg: '',
- id: 'commit id',
- ranges: [{start: 1, end: 2}],
- },
- ];
- await element.updateComplete;
- assert.isTrue(element.classList.contains('showBlame'));
- });
- });
-
- suite('trailing newline warnings', () => {
- const NO_NEWLINE_LEFT = 'No newline at end of left file.';
- const NO_NEWLINE_RIGHT = 'No newline at end of right file.';
-
- const getWarning = (element: GrDiff) => {
- const warningElement = query(element, '.newlineWarning');
- return warningElement?.textContent ?? '';
- };
-
- setup(async () => {
- element.showNewlineWarningLeft = false;
- element.showNewlineWarningRight = false;
- await element.updateComplete;
- });
-
- test('shows combined warning if both sides set to warn', async () => {
- element.showNewlineWarningLeft = true;
- element.showNewlineWarningRight = true;
- await element.updateComplete;
- assert.include(
- getWarning(element),
- NO_NEWLINE_LEFT + ' \u2014 ' + NO_NEWLINE_RIGHT
- ); // \u2014 - '—'
- });
-
- suite('showNewlineWarningLeft', () => {
- test('show warning if true', async () => {
- element.showNewlineWarningLeft = true;
- await element.updateComplete;
- assert.include(getWarning(element), NO_NEWLINE_LEFT);
- });
-
- test('hide warning if false', async () => {
- element.showNewlineWarningLeft = false;
- await element.updateComplete;
- assert.notInclude(getWarning(element), NO_NEWLINE_LEFT);
- });
- });
-
- suite('showNewlineWarningRight', () => {
- test('show warning if true', async () => {
- element.showNewlineWarningRight = true;
- await element.updateComplete;
- assert.include(getWarning(element), NO_NEWLINE_RIGHT);
- });
-
- test('hide warning if false', async () => {
- element.showNewlineWarningRight = false;
- await element.updateComplete;
- assert.notInclude(getWarning(element), NO_NEWLINE_RIGHT);
- });
- });
- });
-
- suite('key locations', () => {
- let renderStub: sinon.SinonStub;
-
- setup(async () => {
- element.prefs = {...MINIMAL_PREFS};
- element.diff = createDiff();
- renderStub = sinon.stub(element.diffBuilder, 'render');
- await element.updateComplete;
- });
-
- test('lineOfInterest is a key location', () => {
- element.lineOfInterest = {lineNum: 789, side: Side.LEFT};
- element.renderDiffTable();
- assert.isTrue(renderStub.called);
- assert.deepEqual(renderStub.lastCall.args[0], {
- left: {789: true},
- right: {},
- });
- });
-
- test('line comments are key locations', async () => {
- const threadEl = document.createElement('div');
- threadEl.className = 'comment-thread';
- threadEl.setAttribute('diff-side', 'right');
- threadEl.setAttribute('line-num', '3');
- element.appendChild(threadEl);
- await element.updateComplete;
-
- element.renderDiffTable();
- assert.isTrue(renderStub.called);
- assert.deepEqual(renderStub.lastCall.args[0], {
- left: {},
- right: {3: true},
- });
- });
-
- test('file comments are key locations', async () => {
- const threadEl = document.createElement('div');
- threadEl.className = 'comment-thread';
- threadEl.setAttribute('diff-side', 'left');
- element.appendChild(threadEl);
- await element.updateComplete;
-
- element.renderDiffTable();
- assert.isTrue(renderStub.called);
- assert.deepEqual(renderStub.lastCall.args[0], {
- left: {FILE: true},
- right: {},
- });
- });
- });
- const setupSampleDiff = async function (params: {
- content: DiffContent[];
- ignore_whitespace?: IgnoreWhitespaceType;
- binary?: boolean;
- }) {
- const {ignore_whitespace, content} = params;
- // binary can't be undefined, use false if not set
- const binary = params.binary || false;
- element.prefs = {
- ignore_whitespace: ignore_whitespace || 'IGNORE_ALL',
- context: 10,
- cursor_blink_rate: 0,
- font_size: 12,
-
- line_length: 100,
- line_wrapping: false,
- show_line_endings: true,
- show_tabs: true,
- show_whitespace_errors: true,
- syntax_highlighting: true,
- tab_size: 8,
- };
- element.diff = {
- intraline_status: 'OK',
- change_type: 'MODIFIED',
- diff_header: [
- 'diff --git a/carrot.js b/carrot.js',
- 'index 2adc47d..f9c2f2c 100644',
- '--- a/carrot.js',
- '+++ b/carrot.jjs',
- 'file differ',
- ],
- content,
- binary,
- };
- await element.updateComplete;
- await element.renderDiffTableTask;
- };
-
- test('clear diff table content as soon as diff changes', async () => {
- const content = [
- {
- a: ['all work and no play make andybons a dull boy'],
- },
- {
- b: ['Non eram nescius, Brute, cum, quae summis ingeniis '],
- },
- ];
- function diffTableHasContent() {
- assertIsDefined(element.diffTable);
- const diffTable = element.diffTable;
- return diffTable.innerText.includes(content[0].a?.[0] ?? '');
- }
- await setupSampleDiff({content});
- await waitUntil(diffTableHasContent);
- element.diff = {...element.diff!};
- await element.updateComplete;
- // immediately cleaned up
- assertIsDefined(element.diffTable);
- const diffTable = element.diffTable;
- assert.equal(diffTable.innerHTML, '');
- element.renderDiffTable();
- await element.updateComplete;
- // rendered again
- await waitUntil(diffTableHasContent);
- });
-
- suite('selection test', () => {
- test('user-select set correctly on side-by-side view', async () => {
- const content = [
- {
- a: ['all work and no play make andybons a dull boy'],
- b: ['elgoog elgoog elgoog'],
- },
- {
- ab: [
- 'Non eram nescius, Brute, cum, quae summis ingeniis ',
- 'exquisitaque doctrina philosophi Graeco sermone tractavissent',
- ],
- },
- ];
- await setupSampleDiff({content});
- await waitEventLoop();
-
- const diffLine = queryAll<HTMLElement>(element, '.contentText')[2];
- assert.equal(getComputedStyle(diffLine).userSelect, 'none');
- mouseDown(diffLine);
- assert.equal(getComputedStyle(diffLine).userSelect, 'text');
- });
-
- test('user-select set correctly on unified view', async () => {
- const content = [
- {
- a: ['all work and no play make andybons a dull boy'],
- b: ['elgoog elgoog elgoog'],
- },
- {
- ab: [
- 'Non eram nescius, Brute, cum, quae summis ingeniis ',
- 'exquisitaque doctrina philosophi Graeco sermone tractavissent',
- ],
- },
- ];
- await setupSampleDiff({content});
- element.viewMode = DiffViewMode.UNIFIED;
- await element.updateComplete;
- const diffLine = queryAll<HTMLElement>(element, '.contentText')[2];
- assert.equal(getComputedStyle(diffLine).userSelect, 'none');
- mouseDown(diffLine);
- assert.equal(getComputedStyle(diffLine).userSelect, 'text');
- });
- });
-
- suite('whitespace changes only message', () => {
- test('show the message if ignore_whitespace is criteria matches', async () => {
- await setupSampleDiff({content: [{skip: 100}]});
- element.loading = false;
- assert.isTrue(element.showNoChangeMessage());
- });
-
- test('do not show the message for binary files', async () => {
- await setupSampleDiff({content: [{skip: 100}], binary: true});
- element.loading = false;
- assert.isFalse(element.showNoChangeMessage());
- });
-
- test('do not show the message if still loading', async () => {
- await setupSampleDiff({content: [{skip: 100}]});
- element.loading = true;
- assert.isFalse(element.showNoChangeMessage());
- });
-
- test('do not show the message if contains valid changes', async () => {
- const content = [
- {
- a: ['all work and no play make andybons a dull boy'],
- b: ['elgoog elgoog elgoog'],
- },
- {
- ab: [
- 'Non eram nescius, Brute, cum, quae summis ingeniis ',
- 'exquisitaque doctrina philosophi Graeco sermone tractavissent',
- ],
- },
- ];
- await setupSampleDiff({content});
- element.loading = false;
- assert.equal(element.diffLength, 3);
- assert.isFalse(element.showNoChangeMessage());
- });
-
- test('do not show message if ignore whitespace is disabled', async () => {
- const content = [
- {
- a: ['all work and no play make andybons a dull boy'],
- b: ['elgoog elgoog elgoog'],
- },
- {
- ab: [
- 'Non eram nescius, Brute, cum, quae summis ingeniis ',
- 'exquisitaque doctrina philosophi Graeco sermone tractavissent',
- ],
- },
- ];
- await setupSampleDiff({ignore_whitespace: 'IGNORE_NONE', content});
- element.loading = false;
- assert.isFalse(element.showNoChangeMessage());
- });
- });
-
- test('getDiffLength', () => {
- const diff = createDiff();
- assert.equal(element.getDiffLength(diff), 52);
- });
-});
diff --git a/polygerrit-ui/app/embed/diff/gr-context-controls/gr-context-controls-section.ts b/polygerrit-ui/app/embed/diff/gr-context-controls/gr-context-controls-section.ts
index e7ae8a4..394d09b 100644
--- a/polygerrit-ui/app/embed/diff/gr-context-controls/gr-context-controls-section.ts
+++ b/polygerrit-ui/app/embed/diff/gr-context-controls/gr-context-controls-section.ts
@@ -8,7 +8,7 @@
import {property, state} from 'lit/decorators.js';
import {DiffInfo, DiffViewMode, RenderPreferences} from '../../../api/diff';
import {GrDiffGroup, GrDiffGroupType} from '../gr-diff/gr-diff-group';
-import {diffClasses, isNewDiff} from '../gr-diff/gr-diff-utils';
+import {diffClasses} from '../gr-diff/gr-diff-utils';
import {getShowConfig} from './gr-context-controls';
import {ifDefined} from 'lit/directives/if-defined.js';
import {when} from 'lit/directives/when.js';
@@ -140,17 +140,10 @@
}
}
-// TODO(newdiff-cleanup): Remove once newdiff migration is completed.
-if (isNewDiff()) {
- customElements.define(
- 'gr-context-controls-section',
- GrContextControlsSection
- );
-}
+customElements.define('gr-context-controls-section', GrContextControlsSection);
declare global {
interface HTMLElementTagNameMap {
- // TODO(newdiff-cleanup): Replace once newdiff migration is completed.
- 'gr-context-controls-section': LitElement;
+ 'gr-context-controls-section': GrContextControlsSection;
}
}
diff --git a/polygerrit-ui/app/embed/diff/gr-context-controls/gr-context-controls.ts b/polygerrit-ui/app/embed/diff/gr-context-controls/gr-context-controls.ts
index b2c0fcb..e889b90 100644
--- a/polygerrit-ui/app/embed/diff/gr-context-controls/gr-context-controls.ts
+++ b/polygerrit-ui/app/embed/diff/gr-context-controls/gr-context-controls.ts
@@ -32,7 +32,6 @@
} from '../../../api/diff';
import {GrDiffGroup, hideInContextControl} from '../gr-diff/gr-diff-group';
-import {isNewDiff} from '../gr-diff/gr-diff-utils';
declare global {
interface HTMLElementEventMap {
@@ -527,14 +526,10 @@
}
}
-// TODO(newdiff-cleanup): Remove once newdiff migration is completed.
-if (isNewDiff()) {
- customElements.define('gr-context-controls', GrContextControls);
-}
+customElements.define('gr-context-controls', GrContextControls);
declare global {
interface HTMLElementTagNameMap {
- // TODO(newdiff-cleanup): Replace once newdiff migration is completed.
- 'gr-context-controls': LitElement;
+ 'gr-context-controls': GrContextControls;
}
}
diff --git a/polygerrit-ui/app/embed/diff/gr-context-controls/gr-context-controls_test.ts b/polygerrit-ui/app/embed/diff/gr-context-controls/gr-context-controls_test.ts
index 7f5827c..215dc88 100644
--- a/polygerrit-ui/app/embed/diff/gr-context-controls/gr-context-controls_test.ts
+++ b/polygerrit-ui/app/embed/diff/gr-context-controls/gr-context-controls_test.ts
@@ -23,10 +23,7 @@
let element: GrContextControls;
setup(async () => {
- // TODO(newdiff-cleanup): Remove cast when newdiff migration is complete.
- element = document.createElement(
- 'gr-context-controls'
- ) as GrContextControls;
+ element = document.createElement('gr-context-controls');
element.diff = {content: []} as any as DiffInfo;
element.renderPreferences = {};
const div = await fixture(html`<div></div>`);
diff --git a/polygerrit-ui/app/embed/diff/gr-diff-builder/gr-diff-builder-image.ts b/polygerrit-ui/app/embed/diff/gr-diff-builder/gr-diff-builder-image.ts
index fd3b975..9eb1e14 100644
--- a/polygerrit-ui/app/embed/diff/gr-diff-builder/gr-diff-builder-image.ts
+++ b/polygerrit-ui/app/embed/diff/gr-diff-builder/gr-diff-builder-image.ts
@@ -8,7 +8,6 @@
import '../gr-diff-image-viewer/gr-image-viewer';
import {html, LitElement, nothing} from 'lit';
import {property, query, state} from 'lit/decorators.js';
-import {isNewDiff} from '../gr-diff/gr-diff-utils';
// MIME types for images we allow showing. Do not include SVG, it can contain
// arbitrary JavaScript.
@@ -201,16 +200,12 @@
: '';
}
-// TODO(newdiff-cleanup): Remove once newdiff migration is completed.
-if (isNewDiff()) {
- customElements.define('gr-diff-image-new', GrDiffImageNew);
- customElements.define('gr-diff-image-old', GrDiffImageOld);
-}
+customElements.define('gr-diff-image-new', GrDiffImageNew);
+customElements.define('gr-diff-image-old', GrDiffImageOld);
declare global {
interface HTMLElementTagNameMap {
- // TODO(newdiff-cleanup): Replace once newdiff migration is completed.
- 'gr-diff-image-new': LitElement;
- 'gr-diff-image-old': LitElement;
+ 'gr-diff-image-new': GrDiffImageNew;
+ 'gr-diff-image-old': GrDiffImageOld;
}
}
diff --git a/polygerrit-ui/app/embed/diff/gr-diff-builder/gr-diff-row.ts b/polygerrit-ui/app/embed/diff/gr-diff-builder/gr-diff-row.ts
index da84b34..8f71936 100644
--- a/polygerrit-ui/app/embed/diff/gr-diff-builder/gr-diff-row.ts
+++ b/polygerrit-ui/app/embed/diff/gr-diff-builder/gr-diff-row.ts
@@ -26,7 +26,6 @@
diffClasses,
GrDiffCommentThread,
isLongCommentRange,
- isNewDiff,
isResponsive,
} from '../gr-diff/gr-diff-utils';
import {resolve} from '../../../models/dependency';
@@ -579,14 +578,10 @@
}
}
-// TODO(newdiff-cleanup): Remove once newdiff migration is completed.
-if (isNewDiff()) {
- customElements.define('gr-diff-row', GrDiffRow);
-}
+customElements.define('gr-diff-row', GrDiffRow);
declare global {
interface HTMLElementTagNameMap {
- // TODO(newdiff-cleanup): Replace once newdiff migration is completed.
- 'gr-diff-row': LitElement;
+ 'gr-diff-row': GrDiffRow;
}
}
diff --git a/polygerrit-ui/app/embed/diff/gr-diff-builder/gr-diff-section.ts b/polygerrit-ui/app/embed/diff/gr-diff-builder/gr-diff-section.ts
index 325f902..4dd580f 100644
--- a/polygerrit-ui/app/embed/diff/gr-diff-builder/gr-diff-section.ts
+++ b/polygerrit-ui/app/embed/diff/gr-diff-builder/gr-diff-section.ts
@@ -15,11 +15,7 @@
DiffPreferencesInfo,
} from '../../../api/diff';
import {GrDiffGroup, GrDiffGroupType} from '../gr-diff/gr-diff-group';
-import {
- isNewDiff,
- diffClasses,
- getResponsiveMode,
-} from '../gr-diff/gr-diff-utils';
+import {diffClasses, getResponsiveMode} from '../gr-diff/gr-diff-utils';
import {GrDiffRow} from './gr-diff-row';
import '../gr-context-controls/gr-context-controls-section';
import '../gr-context-controls/gr-context-controls';
@@ -297,14 +293,10 @@
}
}
-// TODO(newdiff-cleanup): Remove once newdiff migration is completed.
-if (isNewDiff()) {
- customElements.define('gr-diff-section', GrDiffSection);
-}
+customElements.define('gr-diff-section', GrDiffSection);
declare global {
interface HTMLElementTagNameMap {
- // TODO(newdiff-cleanup): Replace once newdiff migration is completed.
- 'gr-diff-section': LitElement;
+ 'gr-diff-section': GrDiffSection;
}
}
diff --git a/polygerrit-ui/app/embed/diff/gr-diff-builder/gr-diff-text.ts b/polygerrit-ui/app/embed/diff/gr-diff-builder/gr-diff-text.ts
index 2acedc8..5161b18 100644
--- a/polygerrit-ui/app/embed/diff/gr-diff-builder/gr-diff-text.ts
+++ b/polygerrit-ui/app/embed/diff/gr-diff-builder/gr-diff-text.ts
@@ -6,7 +6,7 @@
import {LitElement, html, TemplateResult} from 'lit';
import {property} from 'lit/decorators.js';
import {styleMap} from 'lit/directives/style-map.js';
-import {isNewDiff, diffClasses} from '../gr-diff/gr-diff-utils';
+import {diffClasses} from '../gr-diff/gr-diff-utils';
const SURROGATE_PAIR = /[\uD800-\uDBFF][\uDC00-\uDFFF]/;
@@ -144,14 +144,10 @@
}
}
-// TODO(newdiff-cleanup): Remove once newdiff migration is completed.
-if (isNewDiff()) {
- customElements.define('gr-diff-text', GrDiffText);
-}
+customElements.define('gr-diff-text', GrDiffText);
declare global {
interface HTMLElementTagNameMap {
- // TODO(newdiff-cleanup): Replace once newdiff migration is completed.
- 'gr-diff-text': LitElement;
+ 'gr-diff-text': GrDiffText;
}
}
diff --git a/polygerrit-ui/app/embed/diff/gr-diff/gr-diff-element.ts b/polygerrit-ui/app/embed/diff/gr-diff/gr-diff-element.ts
index 352d5db..3ea3ada 100644
--- a/polygerrit-ui/app/embed/diff/gr-diff/gr-diff-element.ts
+++ b/polygerrit-ui/app/embed/diff/gr-diff/gr-diff-element.ts
@@ -16,7 +16,6 @@
import '../gr-diff-builder/gr-diff-row';
import {
isResponsive,
- isNewDiff,
FullContext,
diffClasses,
FULL_CONTEXT,
@@ -186,7 +185,6 @@
private renderContainer() {
const cssClasses = {
- newDiff: true,
diffContainer: true,
unified: this.viewMode === DiffViewMode.UNIFIED,
sideBySide: this.viewMode === DiffViewMode.SIDE_BY_SIDE,
@@ -411,10 +409,7 @@
return prefs.font_size * 4;
}
-// TODO(newdiff-cleanup): Remove once newdiff migration is completed.
-if (isNewDiff()) {
- customElements.define('gr-diff-element', GrDiffElement);
-}
+customElements.define('gr-diff-element', GrDiffElement);
declare global {
interface HTMLElementTagNameMap {
diff --git a/polygerrit-ui/app/embed/diff/gr-diff/gr-diff-element_test.ts b/polygerrit-ui/app/embed/diff/gr-diff/gr-diff-element_test.ts
index 8cc5324..c6194c5 100644
--- a/polygerrit-ui/app/embed/diff/gr-diff/gr-diff-element_test.ts
+++ b/polygerrit-ui/app/embed/diff/gr-diff/gr-diff-element_test.ts
@@ -57,7 +57,7 @@
assert.lightDom.equal(
element,
/* HTML */ `
- <div class="diffContainer newDiff sideBySide">
+ <div class="diffContainer sideBySide">
<table id="diffTable">
<colgroup>
<col class="blame gr-diff" />
@@ -85,7 +85,7 @@
assert.lightDom.equal(
element,
/* HTML */ `
- <div class="diffContainer newDiff unified">
+ <div class="diffContainer unified">
<table id="diffTable">
<colgroup>
<col class="blame gr-diff" />
@@ -1319,7 +1319,7 @@
assert.lightDom.equal(
element,
/* HTML */ `
- <div class="diffContainer newDiff sideBySide">
+ <div class="diffContainer sideBySide">
<table id="diffTable">
<colgroup>
<col class="blame gr-diff" />
@@ -2935,7 +2935,7 @@
assert.lightDom.equal(
element,
/* HTML */ `
- <div class="diffContainer newDiff sideBySide">
+ <div class="diffContainer sideBySide">
<gr-diff-section class="left-FILE right-FILE"> </gr-diff-section>
<gr-diff-row class="left-FILE right-FILE"> </gr-diff-row>
<table id="diffTable">
diff --git a/polygerrit-ui/app/embed/diff/gr-diff/gr-diff-styles.ts b/polygerrit-ui/app/embed/diff/gr-diff/gr-diff-styles.ts
index e5a5e65..f2f85af 100644
--- a/polygerrit-ui/app/embed/diff/gr-diff/gr-diff-styles.ts
+++ b/polygerrit-ui/app/embed/diff/gr-diff/gr-diff-styles.ts
@@ -462,10 +462,7 @@
color: var(--link-color);
padding: var(--spacing-m) 0 var(--spacing-m) 48px;
}
- /* for new diff */
- gr-diff-element,
- /* for old diff, TODO: remove */
- #diffTable {
+ gr-diff-element {
/* for gr-selection-action-box positioning */
position: relative;
}
diff --git a/polygerrit-ui/app/embed/diff/gr-diff/gr-diff-utils.ts b/polygerrit-ui/app/embed/diff/gr-diff/gr-diff-utils.ts
index 7e30581..0a4d5b9 100644
--- a/polygerrit-ui/app/embed/diff/gr-diff/gr-diff-utils.ts
+++ b/polygerrit-ui/app/embed/diff/gr-diff/gr-diff-utils.ts
@@ -40,12 +40,6 @@
*/
export const REGEX_TAB_OR_SURROGATE_PAIR = /\t|[\uD800-\uDBFF][\uDC00-\uDFFF]/;
-// TODO(newdiff-cleanup): Remove once newdiff migration is completed.
-export function isNewDiff() {
- const flags = new Set(window.ENABLED_EXPERIMENTS ?? []);
- return flags.has('UiFeature__new_diff');
-}
-
export function getResponsiveMode(
prefs?: DiffPreferencesInfo,
renderPrefs?: RenderPreferences
diff --git a/polygerrit-ui/app/embed/diff/gr-diff/gr-diff.ts b/polygerrit-ui/app/embed/diff/gr-diff/gr-diff.ts
index 58cad19..5738cb7 100644
--- a/polygerrit-ui/app/embed/diff/gr-diff/gr-diff.ts
+++ b/polygerrit-ui/app/embed/diff/gr-diff/gr-diff.ts
@@ -20,7 +20,6 @@
isThreadEl,
getResponsiveMode,
isResponsive,
- isNewDiff,
getSideByLineEl,
compareComments,
getDataFromCommentThreadEl,
@@ -47,6 +46,7 @@
DisplayLine,
LineNumber,
ContentLoadNeededEventDetail,
+ DiffContextExpandedExternalDetail,
} from '../../../api/diff';
import {isSafari, toggleClass} from '../../../utils/dom-util';
import {assertIsDefined} from '../../../utils/common-util';
@@ -490,9 +490,6 @@
this.commentThreadRedispatcher(e.target, 'comment-thread-mouseleave');
};
- /** TODO: Can be removed when diff-old is gone. */
- cancel() {}
-
getCursorStops(): Array<HTMLElement | AbortStop> {
if (this.hidden && this.noAutoRender) return [];
@@ -697,9 +694,6 @@
this.rangeLayer.updateRanges(ranges);
}
- /** TODO: Can be removed when diff-old is gone. */
- clearDiffContent() {}
-
// TODO: Migrate callers to just update prefs.context.
toggleAllContext() {
const current = this.diffModel.getState().showFullContext;
@@ -1017,15 +1011,11 @@
}
}
-// TODO(newdiff-cleanup): Remove once newdiff migration is completed.
-if (isNewDiff()) {
- customElements.define('gr-diff', GrDiff);
-}
+customElements.define('gr-diff', GrDiff);
declare global {
interface HTMLElementTagNameMap {
- // TODO(newdiff-cleanup): Replace once newdiff migration is completed.
- 'gr-diff': LitElement;
+ 'gr-diff': GrDiff;
}
interface HTMLElementEventMap {
'comment-thread-mouseenter': CustomEvent<GrDiffCommentThread>;
@@ -1042,6 +1032,7 @@
* renders and for partial rerenders.
*/
'render-content': CustomEvent<{}>;
+ 'diff-context-expanded': CustomEvent<DiffContextExpandedExternalDetail>;
'diff-context-expanded-internal-new': CustomEvent<DiffContextExpandedEventDetail>;
'content-load-needed': CustomEvent<ContentLoadNeededEventDetail>;
}
diff --git a/polygerrit-ui/app/embed/gr-diff.ts b/polygerrit-ui/app/embed/gr-diff.ts
index 02a153d..6de43ed 100644
--- a/polygerrit-ui/app/embed/gr-diff.ts
+++ b/polygerrit-ui/app/embed/gr-diff.ts
@@ -12,25 +12,20 @@
// exposed by shared gr-diff component.
import '../api/embed';
import '../scripts/bundled-polymer';
-import './diff-old/gr-diff/gr-diff';
-import './diff-old/gr-diff-cursor/gr-diff-cursor';
import './diff/gr-diff/gr-diff';
import './diff/gr-diff-cursor/gr-diff-cursor';
import {TokenHighlightLayer} from './diff/gr-diff-builder/token-highlight-layer';
-import {GrDiffCursor as GrDiffCursorOld} from './diff-old/gr-diff-cursor/gr-diff-cursor';
-import {GrDiffCursor as GrDiffCursorNew} from './diff/gr-diff-cursor/gr-diff-cursor';
-import {GrAnnotation as GrAnnotationOld} from './diff-old/gr-diff-highlight/gr-annotation';
-import {GrAnnotationImpl as GrAnnotationNew} from './diff/gr-diff-highlight/gr-annotation';
+import {GrDiffCursor} from './diff/gr-diff-cursor/gr-diff-cursor';
+import {GrAnnotationImpl as GrAnnotation} from './diff/gr-diff-highlight/gr-annotation';
import {createDiffAppContext} from './gr-diff-app-context-init';
import {injectAppContext} from '../services/app-context';
-import {isNewDiff} from './diff/gr-diff/gr-diff-utils';
// Setup appContext for diff.
// TODO (dmfilippov): find a better solution
injectAppContext(createDiffAppContext());
// Setup global variables for existing usages of this component
window.grdiff = {
- GrAnnotation: isNewDiff() ? GrAnnotationNew : GrAnnotationOld,
- GrDiffCursor: isNewDiff() ? GrDiffCursorNew : GrDiffCursorOld,
+ GrAnnotation,
+ GrDiffCursor,
TokenHighlightLayer,
};
diff --git a/polygerrit-ui/web-test-runner.config.mjs b/polygerrit-ui/web-test-runner.config.mjs
index bd8d9ac..415571c 100644
--- a/polygerrit-ui/web-test-runner.config.mjs
+++ b/polygerrit-ui/web-test-runner.config.mjs
@@ -2,8 +2,7 @@
import { defaultReporter, summaryReporter } from "@web/test-runner";
import { visualRegressionPlugin } from "@web/test-runner-visual-regression/plugin";
-function testRunnerHtmlFactory(options) {
- const setNewDiffExp = `<script type="text/javascript">window.ENABLED_EXPERIMENTS = ['UiFeature__new_diff'];</script>`;
+function testRunnerHtmlFactory() {
return (testFramework) => `
<!DOCTYPE html>
<html>
@@ -15,7 +14,6 @@
href="polygerrit-ui/app/styles/material-icons.css">
</head>
<body>
- ${options.newDiff ? setNewDiffExp : ''}
<script type="module" src="${testFramework}"></script>
</body>
</html>
@@ -26,34 +24,11 @@
const config = {
files: [
"app/**/*_test.{ts,js}",
- "!app/embed/diff/gr-context-controls/**/*_test.{ts,js}",
- "!app/embed/diff/gr-diff/**/*_test.{ts,js}",
- "!app/embed/diff/gr-diff-builder/**/*_test.{ts,js}",
- "!app/embed/diff/gr-diff-cursor/**/*_test.{ts,js}",
- "!app/embed/diff/gr-diff-highlight/**/*_test.{ts,js}",
- "!app/embed/diff/gr-diff-model/**/*_test.{ts,js}",
- "!app/embed/diff/gr-diff-processor/**/*_test.{ts,js}",
- "!app/embed/diff/gr-diff-selection/**/*_test.{ts,js}",
"!**/node_modules/**/*",
...(process.argv.includes("--run-screenshots")
? []
: ["!app/**/*_screenshot_test.{ts,js}"]),
],
- // TODO(newdiff-cleanup): Remove once newdiff migration is completed.
- groups: [
- {
- name: "new-diff",
- files: [
- "app/embed/diff/**/*_test.{ts,js}",
- "app/elements/change/gr-file-list/gr-file-list_test.{ts,js}",
- "app/elements/diff/gr-apply-fix-dialog/gr-apply-fix-dialog_test.{ts,js}",
- "app/elements/diff/gr-diff-host/gr-diff-host_test.{ts,js}",
- "app/elements/diff/gr-diff-view/gr-diff-view_test.{ts,js}",
- "app/elements/shared/gr-comment-thread/gr-comment-thread_test.{ts,js}",
- ],
- testRunnerHtml: testRunnerHtmlFactory({newDiff: true}),
- },
- ],
port: 9876,
nodeResolve: true,
testFramework: { config: { ui: "tdd", timeout: 5000 } },
@@ -85,6 +60,6 @@
await next();
},
],
- testRunnerHtml: testRunnerHtmlFactory({newDiff: false}),
+ testRunnerHtml: testRunnerHtmlFactory(),
};
export default config;