716ac27f9987edd8f9a71d097e8499077c94be45b243696e7938e32985bf9cc0679762b4c8740cf6d91f3baab9dffbf66003ed6feeeda3c6831f7efcf8be47 35 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731
  1. /*---------------------------------------------------------------------------------------------
  2. * Copyright (c) Microsoft Corporation. All rights reserved.
  3. * Licensed under the MIT License. See License.txt in the project root for license information.
  4. *--------------------------------------------------------------------------------------------*/
  5. var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
  6. var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
  7. if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
  8. else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
  9. return c > 3 && r && Object.defineProperty(target, key, r), r;
  10. };
  11. var __param = (this && this.__param) || function (paramIndex, decorator) {
  12. return function (target, key) { decorator(target, key, paramIndex); }
  13. };
  14. var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
  15. function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
  16. return new (P || (P = Promise))(function (resolve, reject) {
  17. function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
  18. function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
  19. function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
  20. step((generator = generator.apply(thisArg, _arguments || [])).next());
  21. });
  22. };
  23. var _a;
  24. import './media/diffReview.css';
  25. import * as nls from '../../../nls.js';
  26. import * as dom from '../../../base/browser/dom.js';
  27. import { createFastDomNode } from '../../../base/browser/fastDomNode.js';
  28. import { ActionBar } from '../../../base/browser/ui/actionbar/actionbar.js';
  29. import { DomScrollableElement } from '../../../base/browser/ui/scrollbar/scrollableElement.js';
  30. import { Action } from '../../../base/common/actions.js';
  31. import { Disposable } from '../../../base/common/lifecycle.js';
  32. import { applyFontInfo } from '../config/domFontInfo.js';
  33. import { EditorAction, registerEditorAction } from '../editorExtensions.js';
  34. import { ICodeEditorService } from '../services/codeEditorService.js';
  35. import { EditorFontLigatures } from '../../common/config/editorOptions.js';
  36. import { LineTokens } from '../../common/tokens/lineTokens.js';
  37. import { Position } from '../../common/core/position.js';
  38. import { editorLineNumbers } from '../../common/core/editorColorRegistry.js';
  39. import { RenderLineInput, renderViewLine2 as renderViewLine } from '../../common/viewLayout/viewLineRenderer.js';
  40. import { ViewLineRenderingData } from '../../common/viewModel.js';
  41. import { ContextKeyExpr } from '../../../platform/contextkey/common/contextkey.js';
  42. import { scrollbarShadow } from '../../../platform/theme/common/colorRegistry.js';
  43. import { registerThemingParticipant, ThemeIcon } from '../../../platform/theme/common/themeService.js';
  44. import { Codicon } from '../../../base/common/codicons.js';
  45. import { registerIcon } from '../../../platform/theme/common/iconRegistry.js';
  46. import { ILanguageService } from '../../common/languages/language.js';
  47. const DIFF_LINES_PADDING = 3;
  48. class DiffEntry {
  49. constructor(originalLineStart, originalLineEnd, modifiedLineStart, modifiedLineEnd) {
  50. this.originalLineStart = originalLineStart;
  51. this.originalLineEnd = originalLineEnd;
  52. this.modifiedLineStart = modifiedLineStart;
  53. this.modifiedLineEnd = modifiedLineEnd;
  54. }
  55. getType() {
  56. if (this.originalLineStart === 0) {
  57. return 1 /* DiffEntryType.Insert */;
  58. }
  59. if (this.modifiedLineStart === 0) {
  60. return 2 /* DiffEntryType.Delete */;
  61. }
  62. return 0 /* DiffEntryType.Equal */;
  63. }
  64. }
  65. class Diff {
  66. constructor(entries) {
  67. this.entries = entries;
  68. }
  69. }
  70. const diffReviewInsertIcon = registerIcon('diff-review-insert', Codicon.add, nls.localize('diffReviewInsertIcon', 'Icon for \'Insert\' in diff review.'));
  71. const diffReviewRemoveIcon = registerIcon('diff-review-remove', Codicon.remove, nls.localize('diffReviewRemoveIcon', 'Icon for \'Remove\' in diff review.'));
  72. const diffReviewCloseIcon = registerIcon('diff-review-close', Codicon.close, nls.localize('diffReviewCloseIcon', 'Icon for \'Close\' in diff review.'));
  73. let DiffReview = class DiffReview extends Disposable {
  74. constructor(diffEditor, _languageService) {
  75. super();
  76. this._languageService = _languageService;
  77. this._width = 0;
  78. this._diffEditor = diffEditor;
  79. this._isVisible = false;
  80. this.shadow = createFastDomNode(document.createElement('div'));
  81. this.shadow.setClassName('diff-review-shadow');
  82. this.actionBarContainer = createFastDomNode(document.createElement('div'));
  83. this.actionBarContainer.setClassName('diff-review-actions');
  84. this._actionBar = this._register(new ActionBar(this.actionBarContainer.domNode));
  85. this._actionBar.push(new Action('diffreview.close', nls.localize('label.close', "Close"), 'close-diff-review ' + ThemeIcon.asClassName(diffReviewCloseIcon), true, () => __awaiter(this, void 0, void 0, function* () { return this.hide(); })), { label: false, icon: true });
  86. this.domNode = createFastDomNode(document.createElement('div'));
  87. this.domNode.setClassName('diff-review monaco-editor-background');
  88. this._content = createFastDomNode(document.createElement('div'));
  89. this._content.setClassName('diff-review-content');
  90. this._content.setAttribute('role', 'code');
  91. this.scrollbar = this._register(new DomScrollableElement(this._content.domNode, {}));
  92. this.domNode.domNode.appendChild(this.scrollbar.getDomNode());
  93. this._register(diffEditor.onDidUpdateDiff(() => {
  94. if (!this._isVisible) {
  95. return;
  96. }
  97. this._diffs = this._compute();
  98. this._render();
  99. }));
  100. this._register(diffEditor.getModifiedEditor().onDidChangeCursorPosition(() => {
  101. if (!this._isVisible) {
  102. return;
  103. }
  104. this._render();
  105. }));
  106. this._register(dom.addStandardDisposableListener(this.domNode.domNode, 'click', (e) => {
  107. e.preventDefault();
  108. const row = dom.findParentWithClass(e.target, 'diff-review-row');
  109. if (row) {
  110. this._goToRow(row);
  111. }
  112. }));
  113. this._register(dom.addStandardDisposableListener(this.domNode.domNode, 'keydown', (e) => {
  114. if (e.equals(18 /* KeyCode.DownArrow */)
  115. || e.equals(2048 /* KeyMod.CtrlCmd */ | 18 /* KeyCode.DownArrow */)
  116. || e.equals(512 /* KeyMod.Alt */ | 18 /* KeyCode.DownArrow */)) {
  117. e.preventDefault();
  118. this._goToRow(this._getNextRow());
  119. }
  120. if (e.equals(16 /* KeyCode.UpArrow */)
  121. || e.equals(2048 /* KeyMod.CtrlCmd */ | 16 /* KeyCode.UpArrow */)
  122. || e.equals(512 /* KeyMod.Alt */ | 16 /* KeyCode.UpArrow */)) {
  123. e.preventDefault();
  124. this._goToRow(this._getPrevRow());
  125. }
  126. if (e.equals(9 /* KeyCode.Escape */)
  127. || e.equals(2048 /* KeyMod.CtrlCmd */ | 9 /* KeyCode.Escape */)
  128. || e.equals(512 /* KeyMod.Alt */ | 9 /* KeyCode.Escape */)
  129. || e.equals(1024 /* KeyMod.Shift */ | 9 /* KeyCode.Escape */)) {
  130. e.preventDefault();
  131. this.hide();
  132. }
  133. if (e.equals(10 /* KeyCode.Space */)
  134. || e.equals(3 /* KeyCode.Enter */)) {
  135. e.preventDefault();
  136. this.accept();
  137. }
  138. }));
  139. this._diffs = [];
  140. this._currentDiff = null;
  141. }
  142. prev() {
  143. let index = 0;
  144. if (!this._isVisible) {
  145. this._diffs = this._compute();
  146. }
  147. if (this._isVisible) {
  148. let currentIndex = -1;
  149. for (let i = 0, len = this._diffs.length; i < len; i++) {
  150. if (this._diffs[i] === this._currentDiff) {
  151. currentIndex = i;
  152. break;
  153. }
  154. }
  155. index = (this._diffs.length + currentIndex - 1);
  156. }
  157. else {
  158. index = this._findDiffIndex(this._diffEditor.getPosition());
  159. }
  160. if (this._diffs.length === 0) {
  161. // Nothing to do
  162. return;
  163. }
  164. index = index % this._diffs.length;
  165. const entries = this._diffs[index].entries;
  166. this._diffEditor.setPosition(new Position(entries[0].modifiedLineStart, 1));
  167. this._diffEditor.setSelection({ startColumn: 1, startLineNumber: entries[0].modifiedLineStart, endColumn: 1073741824 /* Constants.MAX_SAFE_SMALL_INTEGER */, endLineNumber: entries[entries.length - 1].modifiedLineEnd });
  168. this._isVisible = true;
  169. this._diffEditor.doLayout();
  170. this._render();
  171. this._goToRow(this._getNextRow());
  172. }
  173. next() {
  174. let index = 0;
  175. if (!this._isVisible) {
  176. this._diffs = this._compute();
  177. }
  178. if (this._isVisible) {
  179. let currentIndex = -1;
  180. for (let i = 0, len = this._diffs.length; i < len; i++) {
  181. if (this._diffs[i] === this._currentDiff) {
  182. currentIndex = i;
  183. break;
  184. }
  185. }
  186. index = (currentIndex + 1);
  187. }
  188. else {
  189. index = this._findDiffIndex(this._diffEditor.getPosition());
  190. }
  191. if (this._diffs.length === 0) {
  192. // Nothing to do
  193. return;
  194. }
  195. index = index % this._diffs.length;
  196. const entries = this._diffs[index].entries;
  197. this._diffEditor.setPosition(new Position(entries[0].modifiedLineStart, 1));
  198. this._diffEditor.setSelection({ startColumn: 1, startLineNumber: entries[0].modifiedLineStart, endColumn: 1073741824 /* Constants.MAX_SAFE_SMALL_INTEGER */, endLineNumber: entries[entries.length - 1].modifiedLineEnd });
  199. this._isVisible = true;
  200. this._diffEditor.doLayout();
  201. this._render();
  202. this._goToRow(this._getNextRow());
  203. }
  204. accept() {
  205. let jumpToLineNumber = -1;
  206. const current = this._getCurrentFocusedRow();
  207. if (current) {
  208. const lineNumber = parseInt(current.getAttribute('data-line'), 10);
  209. if (!isNaN(lineNumber)) {
  210. jumpToLineNumber = lineNumber;
  211. }
  212. }
  213. this.hide();
  214. if (jumpToLineNumber !== -1) {
  215. this._diffEditor.setPosition(new Position(jumpToLineNumber, 1));
  216. this._diffEditor.revealPosition(new Position(jumpToLineNumber, 1), 1 /* ScrollType.Immediate */);
  217. }
  218. }
  219. hide() {
  220. this._isVisible = false;
  221. this._diffEditor.updateOptions({ readOnly: false });
  222. this._diffEditor.focus();
  223. this._diffEditor.doLayout();
  224. this._render();
  225. }
  226. _getPrevRow() {
  227. const current = this._getCurrentFocusedRow();
  228. if (!current) {
  229. return this._getFirstRow();
  230. }
  231. if (current.previousElementSibling) {
  232. return current.previousElementSibling;
  233. }
  234. return current;
  235. }
  236. _getNextRow() {
  237. const current = this._getCurrentFocusedRow();
  238. if (!current) {
  239. return this._getFirstRow();
  240. }
  241. if (current.nextElementSibling) {
  242. return current.nextElementSibling;
  243. }
  244. return current;
  245. }
  246. _getFirstRow() {
  247. return this.domNode.domNode.querySelector('.diff-review-row');
  248. }
  249. _getCurrentFocusedRow() {
  250. const result = document.activeElement;
  251. if (result && /diff-review-row/.test(result.className)) {
  252. return result;
  253. }
  254. return null;
  255. }
  256. _goToRow(row) {
  257. const prev = this._getCurrentFocusedRow();
  258. row.tabIndex = 0;
  259. row.focus();
  260. if (prev && prev !== row) {
  261. prev.tabIndex = -1;
  262. }
  263. this.scrollbar.scanDomNode();
  264. }
  265. isVisible() {
  266. return this._isVisible;
  267. }
  268. layout(top, width, height) {
  269. this._width = width;
  270. this.shadow.setTop(top - 6);
  271. this.shadow.setWidth(width);
  272. this.shadow.setHeight(this._isVisible ? 6 : 0);
  273. this.domNode.setTop(top);
  274. this.domNode.setWidth(width);
  275. this.domNode.setHeight(height);
  276. this._content.setHeight(height);
  277. this._content.setWidth(width);
  278. if (this._isVisible) {
  279. this.actionBarContainer.setAttribute('aria-hidden', 'false');
  280. this.actionBarContainer.setDisplay('block');
  281. }
  282. else {
  283. this.actionBarContainer.setAttribute('aria-hidden', 'true');
  284. this.actionBarContainer.setDisplay('none');
  285. }
  286. }
  287. _compute() {
  288. const lineChanges = this._diffEditor.getLineChanges();
  289. if (!lineChanges || lineChanges.length === 0) {
  290. return [];
  291. }
  292. const originalModel = this._diffEditor.getOriginalEditor().getModel();
  293. const modifiedModel = this._diffEditor.getModifiedEditor().getModel();
  294. if (!originalModel || !modifiedModel) {
  295. return [];
  296. }
  297. return DiffReview._mergeAdjacent(lineChanges, originalModel.getLineCount(), modifiedModel.getLineCount());
  298. }
  299. static _mergeAdjacent(lineChanges, originalLineCount, modifiedLineCount) {
  300. if (!lineChanges || lineChanges.length === 0) {
  301. return [];
  302. }
  303. const diffs = [];
  304. let diffsLength = 0;
  305. for (let i = 0, len = lineChanges.length; i < len; i++) {
  306. const lineChange = lineChanges[i];
  307. const originalStart = lineChange.originalStartLineNumber;
  308. const originalEnd = lineChange.originalEndLineNumber;
  309. const modifiedStart = lineChange.modifiedStartLineNumber;
  310. const modifiedEnd = lineChange.modifiedEndLineNumber;
  311. const r = [];
  312. let rLength = 0;
  313. // Emit before anchors
  314. {
  315. const originalEqualAbove = (originalEnd === 0 ? originalStart : originalStart - 1);
  316. const modifiedEqualAbove = (modifiedEnd === 0 ? modifiedStart : modifiedStart - 1);
  317. // Make sure we don't step into the previous diff
  318. let minOriginal = 1;
  319. let minModified = 1;
  320. if (i > 0) {
  321. const prevLineChange = lineChanges[i - 1];
  322. if (prevLineChange.originalEndLineNumber === 0) {
  323. minOriginal = prevLineChange.originalStartLineNumber + 1;
  324. }
  325. else {
  326. minOriginal = prevLineChange.originalEndLineNumber + 1;
  327. }
  328. if (prevLineChange.modifiedEndLineNumber === 0) {
  329. minModified = prevLineChange.modifiedStartLineNumber + 1;
  330. }
  331. else {
  332. minModified = prevLineChange.modifiedEndLineNumber + 1;
  333. }
  334. }
  335. let fromOriginal = originalEqualAbove - DIFF_LINES_PADDING + 1;
  336. let fromModified = modifiedEqualAbove - DIFF_LINES_PADDING + 1;
  337. if (fromOriginal < minOriginal) {
  338. const delta = minOriginal - fromOriginal;
  339. fromOriginal = fromOriginal + delta;
  340. fromModified = fromModified + delta;
  341. }
  342. if (fromModified < minModified) {
  343. const delta = minModified - fromModified;
  344. fromOriginal = fromOriginal + delta;
  345. fromModified = fromModified + delta;
  346. }
  347. r[rLength++] = new DiffEntry(fromOriginal, originalEqualAbove, fromModified, modifiedEqualAbove);
  348. }
  349. // Emit deleted lines
  350. {
  351. if (originalEnd !== 0) {
  352. r[rLength++] = new DiffEntry(originalStart, originalEnd, 0, 0);
  353. }
  354. }
  355. // Emit inserted lines
  356. {
  357. if (modifiedEnd !== 0) {
  358. r[rLength++] = new DiffEntry(0, 0, modifiedStart, modifiedEnd);
  359. }
  360. }
  361. // Emit after anchors
  362. {
  363. const originalEqualBelow = (originalEnd === 0 ? originalStart + 1 : originalEnd + 1);
  364. const modifiedEqualBelow = (modifiedEnd === 0 ? modifiedStart + 1 : modifiedEnd + 1);
  365. // Make sure we don't step into the next diff
  366. let maxOriginal = originalLineCount;
  367. let maxModified = modifiedLineCount;
  368. if (i + 1 < len) {
  369. const nextLineChange = lineChanges[i + 1];
  370. if (nextLineChange.originalEndLineNumber === 0) {
  371. maxOriginal = nextLineChange.originalStartLineNumber;
  372. }
  373. else {
  374. maxOriginal = nextLineChange.originalStartLineNumber - 1;
  375. }
  376. if (nextLineChange.modifiedEndLineNumber === 0) {
  377. maxModified = nextLineChange.modifiedStartLineNumber;
  378. }
  379. else {
  380. maxModified = nextLineChange.modifiedStartLineNumber - 1;
  381. }
  382. }
  383. let toOriginal = originalEqualBelow + DIFF_LINES_PADDING - 1;
  384. let toModified = modifiedEqualBelow + DIFF_LINES_PADDING - 1;
  385. if (toOriginal > maxOriginal) {
  386. const delta = maxOriginal - toOriginal;
  387. toOriginal = toOriginal + delta;
  388. toModified = toModified + delta;
  389. }
  390. if (toModified > maxModified) {
  391. const delta = maxModified - toModified;
  392. toOriginal = toOriginal + delta;
  393. toModified = toModified + delta;
  394. }
  395. r[rLength++] = new DiffEntry(originalEqualBelow, toOriginal, modifiedEqualBelow, toModified);
  396. }
  397. diffs[diffsLength++] = new Diff(r);
  398. }
  399. // Merge adjacent diffs
  400. let curr = diffs[0].entries;
  401. const r = [];
  402. let rLength = 0;
  403. for (let i = 1, len = diffs.length; i < len; i++) {
  404. const thisDiff = diffs[i].entries;
  405. const currLast = curr[curr.length - 1];
  406. const thisFirst = thisDiff[0];
  407. if (currLast.getType() === 0 /* DiffEntryType.Equal */
  408. && thisFirst.getType() === 0 /* DiffEntryType.Equal */
  409. && thisFirst.originalLineStart <= currLast.originalLineEnd) {
  410. // We are dealing with equal lines that overlap
  411. curr[curr.length - 1] = new DiffEntry(currLast.originalLineStart, thisFirst.originalLineEnd, currLast.modifiedLineStart, thisFirst.modifiedLineEnd);
  412. curr = curr.concat(thisDiff.slice(1));
  413. continue;
  414. }
  415. r[rLength++] = new Diff(curr);
  416. curr = thisDiff;
  417. }
  418. r[rLength++] = new Diff(curr);
  419. return r;
  420. }
  421. _findDiffIndex(pos) {
  422. const lineNumber = pos.lineNumber;
  423. for (let i = 0, len = this._diffs.length; i < len; i++) {
  424. const diff = this._diffs[i].entries;
  425. const lastModifiedLine = diff[diff.length - 1].modifiedLineEnd;
  426. if (lineNumber <= lastModifiedLine) {
  427. return i;
  428. }
  429. }
  430. return 0;
  431. }
  432. _render() {
  433. const originalOptions = this._diffEditor.getOriginalEditor().getOptions();
  434. const modifiedOptions = this._diffEditor.getModifiedEditor().getOptions();
  435. const originalModel = this._diffEditor.getOriginalEditor().getModel();
  436. const modifiedModel = this._diffEditor.getModifiedEditor().getModel();
  437. const originalModelOpts = originalModel.getOptions();
  438. const modifiedModelOpts = modifiedModel.getOptions();
  439. if (!this._isVisible || !originalModel || !modifiedModel) {
  440. dom.clearNode(this._content.domNode);
  441. this._currentDiff = null;
  442. this.scrollbar.scanDomNode();
  443. return;
  444. }
  445. this._diffEditor.updateOptions({ readOnly: true });
  446. const diffIndex = this._findDiffIndex(this._diffEditor.getPosition());
  447. if (this._diffs[diffIndex] === this._currentDiff) {
  448. return;
  449. }
  450. this._currentDiff = this._diffs[diffIndex];
  451. const diffs = this._diffs[diffIndex].entries;
  452. const container = document.createElement('div');
  453. container.className = 'diff-review-table';
  454. container.setAttribute('role', 'list');
  455. container.setAttribute('aria-label', 'Difference review. Use "Stage | Unstage | Revert Selected Ranges" commands');
  456. applyFontInfo(container, modifiedOptions.get(46 /* EditorOption.fontInfo */));
  457. let minOriginalLine = 0;
  458. let maxOriginalLine = 0;
  459. let minModifiedLine = 0;
  460. let maxModifiedLine = 0;
  461. for (let i = 0, len = diffs.length; i < len; i++) {
  462. const diffEntry = diffs[i];
  463. const originalLineStart = diffEntry.originalLineStart;
  464. const originalLineEnd = diffEntry.originalLineEnd;
  465. const modifiedLineStart = diffEntry.modifiedLineStart;
  466. const modifiedLineEnd = diffEntry.modifiedLineEnd;
  467. if (originalLineStart !== 0 && ((minOriginalLine === 0 || originalLineStart < minOriginalLine))) {
  468. minOriginalLine = originalLineStart;
  469. }
  470. if (originalLineEnd !== 0 && ((maxOriginalLine === 0 || originalLineEnd > maxOriginalLine))) {
  471. maxOriginalLine = originalLineEnd;
  472. }
  473. if (modifiedLineStart !== 0 && ((minModifiedLine === 0 || modifiedLineStart < minModifiedLine))) {
  474. minModifiedLine = modifiedLineStart;
  475. }
  476. if (modifiedLineEnd !== 0 && ((maxModifiedLine === 0 || modifiedLineEnd > maxModifiedLine))) {
  477. maxModifiedLine = modifiedLineEnd;
  478. }
  479. }
  480. const header = document.createElement('div');
  481. header.className = 'diff-review-row';
  482. const cell = document.createElement('div');
  483. cell.className = 'diff-review-cell diff-review-summary';
  484. const originalChangedLinesCnt = maxOriginalLine - minOriginalLine + 1;
  485. const modifiedChangedLinesCnt = maxModifiedLine - minModifiedLine + 1;
  486. cell.appendChild(document.createTextNode(`${diffIndex + 1}/${this._diffs.length}: @@ -${minOriginalLine},${originalChangedLinesCnt} +${minModifiedLine},${modifiedChangedLinesCnt} @@`));
  487. header.setAttribute('data-line', String(minModifiedLine));
  488. const getAriaLines = (lines) => {
  489. if (lines === 0) {
  490. return nls.localize('no_lines_changed', "no lines changed");
  491. }
  492. else if (lines === 1) {
  493. return nls.localize('one_line_changed', "1 line changed");
  494. }
  495. else {
  496. return nls.localize('more_lines_changed', "{0} lines changed", lines);
  497. }
  498. };
  499. const originalChangedLinesCntAria = getAriaLines(originalChangedLinesCnt);
  500. const modifiedChangedLinesCntAria = getAriaLines(modifiedChangedLinesCnt);
  501. header.setAttribute('aria-label', nls.localize({
  502. key: 'header',
  503. comment: [
  504. 'This is the ARIA label for a git diff header.',
  505. 'A git diff header looks like this: @@ -154,12 +159,39 @@.',
  506. 'That encodes that at original line 154 (which is now line 159), 12 lines were removed/changed with 39 lines.',
  507. 'Variables 0 and 1 refer to the diff index out of total number of diffs.',
  508. 'Variables 2 and 4 will be numbers (a line number).',
  509. 'Variables 3 and 5 will be "no lines changed", "1 line changed" or "X lines changed", localized separately.'
  510. ]
  511. }, "Difference {0} of {1}: original line {2}, {3}, modified line {4}, {5}", (diffIndex + 1), this._diffs.length, minOriginalLine, originalChangedLinesCntAria, minModifiedLine, modifiedChangedLinesCntAria));
  512. header.appendChild(cell);
  513. // @@ -504,7 +517,7 @@
  514. header.setAttribute('role', 'listitem');
  515. container.appendChild(header);
  516. const lineHeight = modifiedOptions.get(61 /* EditorOption.lineHeight */);
  517. let modLine = minModifiedLine;
  518. for (let i = 0, len = diffs.length; i < len; i++) {
  519. const diffEntry = diffs[i];
  520. DiffReview._renderSection(container, diffEntry, modLine, lineHeight, this._width, originalOptions, originalModel, originalModelOpts, modifiedOptions, modifiedModel, modifiedModelOpts, this._languageService.languageIdCodec);
  521. if (diffEntry.modifiedLineStart !== 0) {
  522. modLine = diffEntry.modifiedLineEnd;
  523. }
  524. }
  525. dom.clearNode(this._content.domNode);
  526. this._content.domNode.appendChild(container);
  527. this.scrollbar.scanDomNode();
  528. }
  529. static _renderSection(dest, diffEntry, modLine, lineHeight, width, originalOptions, originalModel, originalModelOpts, modifiedOptions, modifiedModel, modifiedModelOpts, languageIdCodec) {
  530. const type = diffEntry.getType();
  531. let rowClassName = 'diff-review-row';
  532. let lineNumbersExtraClassName = '';
  533. const spacerClassName = 'diff-review-spacer';
  534. let spacerIcon = null;
  535. switch (type) {
  536. case 1 /* DiffEntryType.Insert */:
  537. rowClassName = 'diff-review-row line-insert';
  538. lineNumbersExtraClassName = ' char-insert';
  539. spacerIcon = diffReviewInsertIcon;
  540. break;
  541. case 2 /* DiffEntryType.Delete */:
  542. rowClassName = 'diff-review-row line-delete';
  543. lineNumbersExtraClassName = ' char-delete';
  544. spacerIcon = diffReviewRemoveIcon;
  545. break;
  546. }
  547. const originalLineStart = diffEntry.originalLineStart;
  548. const originalLineEnd = diffEntry.originalLineEnd;
  549. const modifiedLineStart = diffEntry.modifiedLineStart;
  550. const modifiedLineEnd = diffEntry.modifiedLineEnd;
  551. const cnt = Math.max(modifiedLineEnd - modifiedLineStart, originalLineEnd - originalLineStart);
  552. const originalLayoutInfo = originalOptions.get(133 /* EditorOption.layoutInfo */);
  553. const originalLineNumbersWidth = originalLayoutInfo.glyphMarginWidth + originalLayoutInfo.lineNumbersWidth;
  554. const modifiedLayoutInfo = modifiedOptions.get(133 /* EditorOption.layoutInfo */);
  555. const modifiedLineNumbersWidth = 10 + modifiedLayoutInfo.glyphMarginWidth + modifiedLayoutInfo.lineNumbersWidth;
  556. for (let i = 0; i <= cnt; i++) {
  557. const originalLine = (originalLineStart === 0 ? 0 : originalLineStart + i);
  558. const modifiedLine = (modifiedLineStart === 0 ? 0 : modifiedLineStart + i);
  559. const row = document.createElement('div');
  560. row.style.minWidth = width + 'px';
  561. row.className = rowClassName;
  562. row.setAttribute('role', 'listitem');
  563. if (modifiedLine !== 0) {
  564. modLine = modifiedLine;
  565. }
  566. row.setAttribute('data-line', String(modLine));
  567. const cell = document.createElement('div');
  568. cell.className = 'diff-review-cell';
  569. cell.style.height = `${lineHeight}px`;
  570. row.appendChild(cell);
  571. const originalLineNumber = document.createElement('span');
  572. originalLineNumber.style.width = (originalLineNumbersWidth + 'px');
  573. originalLineNumber.style.minWidth = (originalLineNumbersWidth + 'px');
  574. originalLineNumber.className = 'diff-review-line-number' + lineNumbersExtraClassName;
  575. if (originalLine !== 0) {
  576. originalLineNumber.appendChild(document.createTextNode(String(originalLine)));
  577. }
  578. else {
  579. originalLineNumber.innerText = '\u00a0';
  580. }
  581. cell.appendChild(originalLineNumber);
  582. const modifiedLineNumber = document.createElement('span');
  583. modifiedLineNumber.style.width = (modifiedLineNumbersWidth + 'px');
  584. modifiedLineNumber.style.minWidth = (modifiedLineNumbersWidth + 'px');
  585. modifiedLineNumber.style.paddingRight = '10px';
  586. modifiedLineNumber.className = 'diff-review-line-number' + lineNumbersExtraClassName;
  587. if (modifiedLine !== 0) {
  588. modifiedLineNumber.appendChild(document.createTextNode(String(modifiedLine)));
  589. }
  590. else {
  591. modifiedLineNumber.innerText = '\u00a0';
  592. }
  593. cell.appendChild(modifiedLineNumber);
  594. const spacer = document.createElement('span');
  595. spacer.className = spacerClassName;
  596. if (spacerIcon) {
  597. const spacerCodicon = document.createElement('span');
  598. spacerCodicon.className = ThemeIcon.asClassName(spacerIcon);
  599. spacerCodicon.innerText = '\u00a0\u00a0';
  600. spacer.appendChild(spacerCodicon);
  601. }
  602. else {
  603. spacer.innerText = '\u00a0\u00a0';
  604. }
  605. cell.appendChild(spacer);
  606. let lineContent;
  607. if (modifiedLine !== 0) {
  608. let html = this._renderLine(modifiedModel, modifiedOptions, modifiedModelOpts.tabSize, modifiedLine, languageIdCodec);
  609. if (DiffReview._ttPolicy) {
  610. html = DiffReview._ttPolicy.createHTML(html);
  611. }
  612. cell.insertAdjacentHTML('beforeend', html);
  613. lineContent = modifiedModel.getLineContent(modifiedLine);
  614. }
  615. else {
  616. let html = this._renderLine(originalModel, originalOptions, originalModelOpts.tabSize, originalLine, languageIdCodec);
  617. if (DiffReview._ttPolicy) {
  618. html = DiffReview._ttPolicy.createHTML(html);
  619. }
  620. cell.insertAdjacentHTML('beforeend', html);
  621. lineContent = originalModel.getLineContent(originalLine);
  622. }
  623. if (lineContent.length === 0) {
  624. lineContent = nls.localize('blankLine', "blank");
  625. }
  626. let ariaLabel = '';
  627. switch (type) {
  628. case 0 /* DiffEntryType.Equal */:
  629. if (originalLine === modifiedLine) {
  630. ariaLabel = nls.localize({ key: 'unchangedLine', comment: ['The placeholders are contents of the line and should not be translated.'] }, "{0} unchanged line {1}", lineContent, originalLine);
  631. }
  632. else {
  633. ariaLabel = nls.localize('equalLine', "{0} original line {1} modified line {2}", lineContent, originalLine, modifiedLine);
  634. }
  635. break;
  636. case 1 /* DiffEntryType.Insert */:
  637. ariaLabel = nls.localize('insertLine', "+ {0} modified line {1}", lineContent, modifiedLine);
  638. break;
  639. case 2 /* DiffEntryType.Delete */:
  640. ariaLabel = nls.localize('deleteLine', "- {0} original line {1}", lineContent, originalLine);
  641. break;
  642. }
  643. row.setAttribute('aria-label', ariaLabel);
  644. dest.appendChild(row);
  645. }
  646. }
  647. static _renderLine(model, options, tabSize, lineNumber, languageIdCodec) {
  648. const lineContent = model.getLineContent(lineNumber);
  649. const fontInfo = options.get(46 /* EditorOption.fontInfo */);
  650. const lineTokens = LineTokens.createEmpty(lineContent, languageIdCodec);
  651. const isBasicASCII = ViewLineRenderingData.isBasicASCII(lineContent, model.mightContainNonBasicASCII());
  652. const containsRTL = ViewLineRenderingData.containsRTL(lineContent, isBasicASCII, model.mightContainRTL());
  653. const r = renderViewLine(new RenderLineInput((fontInfo.isMonospace && !options.get(29 /* EditorOption.disableMonospaceOptimizations */)), fontInfo.canUseHalfwidthRightwardsArrow, lineContent, false, isBasicASCII, containsRTL, 0, lineTokens, [], tabSize, 0, fontInfo.spaceWidth, fontInfo.middotWidth, fontInfo.wsmiddotWidth, options.get(107 /* EditorOption.stopRenderingLineAfter */), options.get(90 /* EditorOption.renderWhitespace */), options.get(85 /* EditorOption.renderControlCharacters */), options.get(47 /* EditorOption.fontLigatures */) !== EditorFontLigatures.OFF, null));
  654. return r.html;
  655. }
  656. };
  657. DiffReview._ttPolicy = (_a = window.trustedTypes) === null || _a === void 0 ? void 0 : _a.createPolicy('diffReview', { createHTML: value => value });
  658. DiffReview = __decorate([
  659. __param(1, ILanguageService)
  660. ], DiffReview);
  661. export { DiffReview };
  662. // theming
  663. registerThemingParticipant((theme, collector) => {
  664. const lineNumbers = theme.getColor(editorLineNumbers);
  665. if (lineNumbers) {
  666. collector.addRule(`.monaco-diff-editor .diff-review-line-number { color: ${lineNumbers}; }`);
  667. }
  668. const shadow = theme.getColor(scrollbarShadow);
  669. if (shadow) {
  670. collector.addRule(`.monaco-diff-editor .diff-review-shadow { box-shadow: ${shadow} 0 -6px 6px -6px inset; }`);
  671. }
  672. });
  673. class DiffReviewNext extends EditorAction {
  674. constructor() {
  675. super({
  676. id: 'editor.action.diffReview.next',
  677. label: nls.localize('editor.action.diffReview.next', "Go to Next Difference"),
  678. alias: 'Go to Next Difference',
  679. precondition: ContextKeyExpr.has('isInDiffEditor'),
  680. kbOpts: {
  681. kbExpr: null,
  682. primary: 65 /* KeyCode.F7 */,
  683. weight: 100 /* KeybindingWeight.EditorContrib */
  684. }
  685. });
  686. }
  687. run(accessor, editor) {
  688. const diffEditor = findFocusedDiffEditor(accessor);
  689. if (diffEditor) {
  690. diffEditor.diffReviewNext();
  691. }
  692. }
  693. }
  694. class DiffReviewPrev extends EditorAction {
  695. constructor() {
  696. super({
  697. id: 'editor.action.diffReview.prev',
  698. label: nls.localize('editor.action.diffReview.prev', "Go to Previous Difference"),
  699. alias: 'Go to Previous Difference',
  700. precondition: ContextKeyExpr.has('isInDiffEditor'),
  701. kbOpts: {
  702. kbExpr: null,
  703. primary: 1024 /* KeyMod.Shift */ | 65 /* KeyCode.F7 */,
  704. weight: 100 /* KeybindingWeight.EditorContrib */
  705. }
  706. });
  707. }
  708. run(accessor, editor) {
  709. const diffEditor = findFocusedDiffEditor(accessor);
  710. if (diffEditor) {
  711. diffEditor.diffReviewPrev();
  712. }
  713. }
  714. }
  715. function findFocusedDiffEditor(accessor) {
  716. const codeEditorService = accessor.get(ICodeEditorService);
  717. const diffEditors = codeEditorService.listDiffEditors();
  718. const activeCodeEditor = codeEditorService.getActiveCodeEditor();
  719. if (!activeCodeEditor) {
  720. return null;
  721. }
  722. for (let i = 0, len = diffEditors.length; i < len; i++) {
  723. const diffEditor = diffEditors[i];
  724. if (diffEditor.getModifiedEditor().getId() === activeCodeEditor.getId() || diffEditor.getOriginalEditor().getId() === activeCodeEditor.getId()) {
  725. return diffEditor;
  726. }
  727. }
  728. return null;
  729. }
  730. registerEditorAction(DiffReviewNext);
  731. registerEditorAction(DiffReviewPrev);