Skip to content

Commit 5f850ce

Browse files
authored
Merge pull request #41 from rars/add-selected-line-change-event
2 parents b1066a5 + f5a081f commit 5f850ce

14 files changed

+170
-595
lines changed
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
1+
import { LineDiffType } from './line-diff-type';
2+
13
/* Holds the state of the calculation of the diff result we intend to display.
24
* > lines contains the data that will be displayed on screen.
35
* > lineInOldText keeps track of the document line number in the [oldText] input.
46
* > lineInNewText keeps track of the document line number in the [newText] input.
57
*/
68
export interface IDiffCalculation {
7-
lines: Array<[string, string, string, string]>;
9+
lines: Array<[LineDiffType, number | null, number | null, string]>;
810
lineInOldText: number;
911
lineInNewText: number;
1012
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
export const enum LineDiffType {
2+
Equal = 1,
3+
Insert = 2,
4+
Delete = 3,
5+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import { LineDiffType } from './line-diff-type';
2+
3+
export type LineSelectEvent = {
4+
index: number;
5+
type: LineDiffType;
6+
lineNumberInOldText: number | null;
7+
lineNumberInNewText: number | null;
8+
line: string;
9+
};
Lines changed: 24 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,32 @@
11
<div class="inline-diff-no-changes-text" *ngIf="isContentEqual">
2-
There are no changes to display.
2+
There are no changes to display.
33
</div>
44
<div class="inline-diff" *ngIf="!isContentEqual">
5-
<div class="inline-diff-margin">
6-
<div [ngClass]="lineDiff[0]" *ngFor="let lineDiff of calculatedDiff">
7-
<div class="inline-diff-old">{{ lineDiff[1] }}</div>
8-
<!-- No space
5+
<div class="inline-diff-margin">
6+
<div
7+
class="line-selector"
8+
[ngClass]="lineDiff === selectedLine ? [lineDiff[4], 'selected'] : [lineDiff[4]]"
9+
*ngFor="let lineDiff of calculatedDiff; index as idx"
10+
(click)="selectLine(idx, lineDiff)"
11+
>
12+
<div class="inline-diff-old">{{ lineDiff[1] | lineNumber }}</div>
13+
<!-- No space
914
-->
10-
<div class="inline-diff-new">{{ lineDiff[2] }}</div>
11-
</div>
12-
<div class="dmp-margin-bottom-spacer"></div>
15+
<div class="inline-diff-new">{{ lineDiff[2] | lineNumber }}</div>
1316
</div>
14-
<!-- No space
17+
<div class="dmp-margin-bottom-spacer"></div>
18+
</div>
19+
<!-- No space
1520
-->
16-
<div class="inline-diff-content">
17-
<div class="inline-diff-content-wrapper">
18-
<div [ngClass]="lineDiff[0]" *ngFor="let lineDiff of calculatedDiff">
19-
<div class="inline-diff-text">{{ lineDiff[3] }}</div>
20-
</div>
21-
</div>
21+
<div class="inline-diff-content">
22+
<div class="inline-diff-content-wrapper">
23+
<div
24+
class="line-content"
25+
[ngClass]="lineDiff === selectedLine ? [lineDiff[4], 'selected'] : [lineDiff[4]]"
26+
*ngFor="let lineDiff of calculatedDiff"
27+
>
28+
<div class="inline-diff-text">{{ lineDiff[3] }}</div>
29+
</div>
2230
</div>
31+
</div>
2332
</div>

projects/ngx-diff/src/lib/components/inline-diff/inline-diff.component.scss

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@ div.inline-diff-content-wrapper {
2626
div.inline-diff-old {
2727
width: 50px;
2828
text-align: center;
29-
color: #484848;
3029
}
3130

3231
div.inline-diff-equal>div.inline-diff-old,
@@ -47,7 +46,6 @@ div.inline-diff-delete>div.inline-diff-new {
4746
div.inline-diff-new {
4847
width: 50px;
4948
text-align: center;
50-
color: #484848;
5149
border-right: 1px solid #888888;
5250
}
5351

@@ -81,3 +79,24 @@ div.inline-diff-text {
8179
background-color: #dedede;
8280
border-right: 1px solid #888888;
8381
}
82+
83+
.line-selector {
84+
color: #484848;
85+
86+
&:hover {
87+
cursor: pointer;
88+
color: mix(white, #484848, 30%);
89+
}
90+
91+
&.selected {
92+
border-top: 1px solid red;
93+
border-left: 1px solid red;
94+
border-bottom: 1px solid red;
95+
}
96+
}
97+
98+
.line-content.selected {
99+
border-top: 1px solid red;
100+
border-right: 1px solid red;
101+
border-bottom: 1px solid red;
102+
}

projects/ngx-diff/src/lib/components/inline-diff/inline-diff.component.spec.ts

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ import { Diff, DiffOp } from 'diff-match-patch-ts';
22

33
import { ComponentFixture, TestBed } from '@angular/core/testing';
44

5+
import { LineDiffType } from '../../common/line-diff-type';
6+
import { LineNumberPipe } from '../../pipes/line-number/line-number.pipe';
57
import { DiffMatchPatchService } from '../../services/diff-match-patch/diff-match-patch.service';
68
import { InlineDiffComponent } from './inline-diff.component';
79

@@ -23,7 +25,7 @@ describe('InlineDiffComponent', () => {
2325

2426
beforeEach(async () => {
2527
await TestBed.configureTestingModule({
26-
declarations: [InlineDiffComponent],
28+
declarations: [InlineDiffComponent, LineNumberPipe],
2729
providers: [{ provide: DiffMatchPatchService, useClass: DiffMatchPatchServiceMock }],
2830
}).compileComponents();
2931

@@ -42,23 +44,23 @@ describe('InlineDiffComponent', () => {
4244

4345
it('should have correct line numbers', () => {
4446
const leftLineNumbers = component.calculatedDiff.map((x) => x[1]);
45-
expect(leftLineNumbers).toEqual(['1', '2', '-', '-', '3', '4', '5', '6']);
47+
expect(leftLineNumbers).toEqual([1, 2, null, null, 3, 4, 5, 6]);
4648

4749
const rightLineNumbers = component.calculatedDiff.map((x) => x[2]);
48-
expect(rightLineNumbers).toEqual(['1', '2', '3', '4', '-', '-', '5', '6']);
50+
expect(rightLineNumbers).toEqual([1, 2, 3, 4, null, null, 5, 6]);
4951
});
5052

5153
it('should have correct class annotations', () => {
5254
const classes = component.calculatedDiff.map((x) => x[0]);
5355
expect(classes).toEqual([
54-
'inline-diff-equal',
55-
'inline-diff-equal',
56-
'inline-diff-insert',
57-
'inline-diff-insert',
58-
'inline-diff-delete',
59-
'inline-diff-delete',
60-
'inline-diff-equal',
61-
'inline-diff-equal',
56+
LineDiffType.Equal,
57+
LineDiffType.Equal,
58+
LineDiffType.Insert,
59+
LineDiffType.Insert,
60+
LineDiffType.Delete,
61+
LineDiffType.Delete,
62+
LineDiffType.Equal,
63+
LineDiffType.Equal,
6264
]);
6365
});
6466

projects/ngx-diff/src/lib/components/inline-diff/inline-diff.component.ts

Lines changed: 47 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,14 @@
11
import { Diff, DiffOp } from 'diff-match-patch-ts';
22

3-
import { Component, Input, OnChanges, OnInit } from '@angular/core';
3+
import { Component, EventEmitter, Input, OnChanges, OnInit, Output } from '@angular/core';
44

5-
import { IDiffCalculation } from '../../../inline-diff/diff-calculation.interface';
5+
import { IDiffCalculation } from '../../common/diff-calculation.interface';
6+
import { LineDiffType } from '../../common/line-diff-type';
7+
import { LineSelectEvent } from '../../common/line-select-event';
68
import { DiffMatchPatchService } from '../../services/diff-match-patch/diff-match-patch.service';
79

10+
type LineDiff = [LineDiffType, number | null, number | null, string, string];
11+
812
@Component({
913
selector: 'inline-diff',
1014
templateUrl: './inline-diff.component.html',
@@ -20,7 +24,11 @@ export class InlineDiffComponent implements OnInit, OnChanges {
2024
@Input()
2125
public lineContextSize: number | undefined;
2226

23-
public calculatedDiff: Array<[string, string, string, string]>;
27+
@Output()
28+
public selectedLineChange = new EventEmitter<LineSelectEvent>();
29+
30+
public calculatedDiff: LineDiff[];
31+
public selectedLine?: LineDiff;
2432
public isContentEqual: boolean;
2533

2634
public constructor(private readonly dmp: DiffMatchPatchService) {
@@ -36,6 +44,18 @@ export class InlineDiffComponent implements OnInit, OnChanges {
3644
this.updateHtml();
3745
}
3846

47+
public selectLine(index: number, lineDiff: LineDiff): void {
48+
this.selectedLine = lineDiff;
49+
const [type, lineNumberInOldText, lineNumberInNewText, line] = lineDiff;
50+
this.selectedLineChange.emit({
51+
index,
52+
type,
53+
lineNumberInOldText,
54+
lineNumberInNewText,
55+
line,
56+
});
57+
}
58+
3959
private updateHtml(): void {
4060
if (typeof this.oldText === 'number' || typeof this.oldText === 'boolean') {
4161
this.oldText = this.oldText.toString();
@@ -87,7 +107,11 @@ export class InlineDiffComponent implements OnInit, OnChanges {
87107
}
88108
}
89109

90-
this.calculatedDiff = diffCalculation.lines;
110+
this.calculatedDiff = diffCalculation.lines.map(
111+
([type, lineNumberInOldText, lineNumberInNewText, line]) => {
112+
return [type, lineNumberInOldText, lineNumberInNewText, line, this.getCssClass(type)];
113+
},
114+
);
91115
}
92116

93117
/* If the number of diffLines is greater than lineContextSize then we may need to adjust the diff
@@ -125,7 +149,7 @@ export class InlineDiffComponent implements OnInit, OnChanges {
125149
this.outputEqualDiffLines(diffLines.slice(0, this.lineContextSize), diffCalculation);
126150

127151
// Output a special line indicating that some content is equal and has been skipped
128-
diffCalculation.lines.push(['inline-diff-equal', '...', '...', '...']);
152+
diffCalculation.lines.push([LineDiffType.Equal, null, null, '...']);
129153
const numberOfSkippedLines = diffLines.length - 2 * this.lineContextSize;
130154
diffCalculation.lineInOldText += numberOfSkippedLines;
131155
diffCalculation.lineInNewText += numberOfSkippedLines;
@@ -146,9 +170,9 @@ export class InlineDiffComponent implements OnInit, OnChanges {
146170
private outputEqualDiffLines(diffLines: string[], diffCalculation: IDiffCalculation): void {
147171
for (const line of diffLines) {
148172
diffCalculation.lines.push([
149-
'inline-diff-equal',
150-
`${diffCalculation.lineInOldText}`,
151-
`${diffCalculation.lineInNewText}`,
173+
LineDiffType.Equal,
174+
diffCalculation.lineInOldText,
175+
diffCalculation.lineInNewText,
152176
line,
153177
]);
154178
diffCalculation.lineInOldText++;
@@ -158,25 +182,28 @@ export class InlineDiffComponent implements OnInit, OnChanges {
158182

159183
private outputDeleteDiff(diffLines: string[], diffCalculation: IDiffCalculation): void {
160184
for (const line of diffLines) {
161-
diffCalculation.lines.push([
162-
'inline-diff-delete',
163-
`${diffCalculation.lineInOldText}`,
164-
'-',
165-
line,
166-
]);
185+
diffCalculation.lines.push([LineDiffType.Delete, diffCalculation.lineInOldText, null, line]);
167186
diffCalculation.lineInOldText++;
168187
}
169188
}
170189

171190
private outputInsertDiff(diffLines: string[], diffCalculation: IDiffCalculation): void {
172191
for (const line of diffLines) {
173-
diffCalculation.lines.push([
174-
'inline-diff-insert',
175-
'-',
176-
`${diffCalculation.lineInNewText}`,
177-
line,
178-
]);
192+
diffCalculation.lines.push([LineDiffType.Insert, null, diffCalculation.lineInNewText, line]);
179193
diffCalculation.lineInNewText++;
180194
}
181195
}
196+
197+
private getCssClass(type: LineDiffType): string {
198+
switch (type) {
199+
case LineDiffType.Equal:
200+
return 'inline-diff-equal';
201+
case LineDiffType.Insert:
202+
return 'inline-diff-insert';
203+
case LineDiffType.Delete:
204+
return 'inline-diff-delete';
205+
default:
206+
return 'unknown';
207+
}
208+
}
182209
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import { LineNumberPipe } from './line-number.pipe';
2+
3+
describe('LineNumberPipe', () => {
4+
it('create an instance', () => {
5+
const pipe = new LineNumberPipe();
6+
expect(pipe).toBeTruthy();
7+
});
8+
});
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import { Pipe, PipeTransform } from '@angular/core';
2+
3+
@Pipe({
4+
name: 'lineNumber',
5+
})
6+
export class LineNumberPipe implements PipeTransform {
7+
public transform(value: number | null): string {
8+
return value === null ? '-' : `${value}`;
9+
}
10+
}

projects/ngx-diff/src/ngx-diff.module.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,10 @@ import { CommonModule } from '@angular/common';
22
import { NgModule } from '@angular/core';
33

44
import { InlineDiffComponent } from './lib/components/inline-diff/inline-diff.component';
5+
import { LineNumberPipe } from './lib/pipes/line-number/line-number.pipe';
56

67
@NgModule({
7-
declarations: [InlineDiffComponent],
8+
declarations: [InlineDiffComponent, LineNumberPipe],
89
exports: [InlineDiffComponent],
910
imports: [CommonModule],
1011
})

projects/ngx-diff/src/public-api.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
* Public API Surface of ngx-diff
33
*/
44

5-
export { DiffMatchPatchService } from './lib/services/diff-match-patch/diff-match-patch.service';
6-
export { InlineDiffComponent } from './lib/components/inline-diff/inline-diff.component';
7-
export { NgxDiffModule } from './ngx-diff.module';
5+
export * from './lib/services/diff-match-patch/diff-match-patch.service';
6+
export * from './lib/components/inline-diff/inline-diff.component';
7+
export * from './lib/common/line-select-event';
8+
export * from './lib/common/line-diff-type';
9+
export * from './ngx-diff.module';

0 commit comments

Comments
 (0)