ChangesView.js 8.0 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344
  1. export default class ChangesView extends UI.VBox{constructor(){super(true);this.registerRequiredCSS('changes/changesView.css');const splitWidget=new UI.SplitWidget(true,false);const mainWidget=new UI.Widget();splitWidget.setMainWidget(mainWidget);splitWidget.show(this.contentElement);this._emptyWidget=new UI.EmptyWidget('');this._emptyWidget.show(mainWidget.element);this._workspaceDiff=WorkspaceDiff.workspaceDiff();this._changesSidebar=new Changes.ChangesSidebar(this._workspaceDiff);this._changesSidebar.addEventListener(Changes.ChangesSidebar.Events.SelectedUISourceCodeChanged,this._selectedUISourceCodeChanged,this);splitWidget.setSidebarWidget(this._changesSidebar);this._selectedUISourceCode=null;this._diffRows=[];this._maxLineDigits=1;this._editor=new TextEditor.CodeMirrorTextEditor({lineNumbers:true,lineWrapping:false,maxHighlightLength:Infinity});this._editor.setReadOnly(true);this._editor.show(mainWidget.element.createChild('div','editor-container'));this._editor.hideWidget();this._editor.element.addEventListener('click',this._click.bind(this),false);this._toolbar=new UI.Toolbar('changes-toolbar',mainWidget.element);const revertButton=new UI.ToolbarButton(Common.UIString('Revert all changes'),'largeicon-undo');revertButton.addEventListener(UI.ToolbarButton.Events.Click,this._revert.bind(this));this._toolbar.appendToolbarItem(revertButton);this._diffStats=new UI.ToolbarText('');this._toolbar.appendToolbarItem(this._diffStats);this._toolbar.setEnabled(false);this._hideDiff(ls`No changes`);this._selectedUISourceCodeChanged();}
  2. _selectedUISourceCodeChanged(){this._revealUISourceCode(this._changesSidebar.selectedUISourceCode());}
  3. _revert(){const uiSourceCode=this._selectedUISourceCode;if(!uiSourceCode){return;}
  4. this._workspaceDiff.revertToOriginal(uiSourceCode);}
  5. _click(event){const selection=this._editor.selection();if(!selection.isEmpty()){return;}
  6. const row=this._diffRows[selection.startLine];Common.Revealer.reveal(this._selectedUISourceCode.uiLocation(row.currentLineNumber-1,selection.startColumn),false);event.consume(true);}
  7. _revealUISourceCode(uiSourceCode){if(this._selectedUISourceCode===uiSourceCode){return;}
  8. if(this._selectedUISourceCode){this._workspaceDiff.unsubscribeFromDiffChange(this._selectedUISourceCode,this._refreshDiff,this);}
  9. if(uiSourceCode&&this.isShowing()){this._workspaceDiff.subscribeToDiffChange(uiSourceCode,this._refreshDiff,this);}
  10. this._selectedUISourceCode=uiSourceCode;this._refreshDiff();}
  11. wasShown(){this._refreshDiff();}
  12. _refreshDiff(){if(!this.isShowing()){return;}
  13. if(!this._selectedUISourceCode){this._renderDiffRows(null);return;}
  14. const uiSourceCode=this._selectedUISourceCode;if(!uiSourceCode.contentType().isTextType()){this._hideDiff(ls`Binary data`);return;}
  15. this._workspaceDiff.requestDiff(uiSourceCode).then(diff=>{if(this._selectedUISourceCode!==uiSourceCode){return;}
  16. this._renderDiffRows(diff);});}
  17. _hideDiff(message){this._diffStats.setText('');this._toolbar.setEnabled(false);this._editor.hideWidget();this._emptyWidget.text=message;this._emptyWidget.showWidget();}
  18. _renderDiffRows(diff){this._diffRows=[];if(!diff||(diff.length===1&&diff[0][0]===Diff.Diff.Operation.Equal)){this._hideDiff(ls`No changes`);return;}
  19. let insertions=0;let deletions=0;let currentLineNumber=0;let baselineLineNumber=0;const paddingLines=3;const originalLines=[];const currentLines=[];for(let i=0;i<diff.length;++i){const token=diff[i];switch(token[0]){case Diff.Diff.Operation.Equal:this._diffRows.pushAll(createEqualRows(token[1],i===0,i===diff.length-1));originalLines.pushAll(token[1]);currentLines.pushAll(token[1]);break;case Diff.Diff.Operation.Insert:for(const line of token[1]){this._diffRows.push(createRow(line,RowType.Addition));}
  20. insertions+=token[1].length;currentLines.pushAll(token[1]);break;case Diff.Diff.Operation.Delete:deletions+=token[1].length;originalLines.pushAll(token[1]);if(diff[i+1]&&diff[i+1][0]===Diff.Diff.Operation.Insert){i++;this._diffRows.pushAll(createModifyRows(token[1].join('\n'),diff[i][1].join('\n')));insertions+=diff[i][1].length;currentLines.pushAll(diff[i][1]);}else{for(const line of token[1]){this._diffRows.push(createRow(line,RowType.Deletion));}}
  21. break;}}
  22. this._maxLineDigits=Math.ceil(Math.log10(Math.max(currentLineNumber,baselineLineNumber)));let insertionText='';if(insertions===1){insertionText=ls`${insertions} insertion (+),`;}else{insertionText=ls`${insertions} insertions (+),`;}
  23. let deletionText='';if(deletions===1){deletionText=ls`${deletions} deletion (-)`;}else{deletionText=ls`${deletions} deletions (-)`;}
  24. this._diffStats.setText(`${insertionText} ${deletionText}`);this._toolbar.setEnabled(true);this._emptyWidget.hideWidget();this._editor.operation(()=>{this._editor.showWidget();this._editor.setHighlightMode({name:'devtools-diff',diffRows:this._diffRows,mimeType:(this._selectedUISourceCode).mimeType(),baselineLines:originalLines,currentLines:currentLines});this._editor.setText(this._diffRows.map(row=>row.tokens.map(t=>t.text).join('')).join('\n'));this._editor.setLineNumberFormatter(this._lineFormatter.bind(this));});function createEqualRows(lines,atStart,atEnd){const equalRows=[];if(!atStart){for(let i=0;i<paddingLines&&i<lines.length;i++){equalRows.push(createRow(lines[i],RowType.Equal));}
  25. if(lines.length>paddingLines*2+1&&!atEnd){equalRows.push(createRow(Common.UIString('( \u2026 Skipping %d matching lines \u2026 )',lines.length-paddingLines*2),RowType.Spacer));}}
  26. if(!atEnd){const start=Math.max(lines.length-paddingLines-1,atStart?0:paddingLines);let skip=lines.length-paddingLines-1;if(!atStart){skip-=paddingLines;}
  27. if(skip>0){baselineLineNumber+=skip;currentLineNumber+=skip;}
  28. for(let i=start;i<lines.length;i++){equalRows.push(createRow(lines[i],RowType.Equal));}}
  29. return equalRows;}
  30. function createModifyRows(before,after){const internalDiff=Diff.Diff.charDiff(before,after,true);const deletionRows=[createRow('',RowType.Deletion)];const insertionRows=[createRow('',RowType.Addition)];for(const token of internalDiff){const text=token[1];const type=token[0];const className=type===Diff.Diff.Operation.Equal?'':'inner-diff';const lines=text.split('\n');for(let i=0;i<lines.length;i++){if(i>0&&type!==Diff.Diff.Operation.Insert){deletionRows.push(createRow('',RowType.Deletion));}
  31. if(i>0&&type!==Diff.Diff.Operation.Delete){insertionRows.push(createRow('',RowType.Addition));}
  32. if(!lines[i]){continue;}
  33. if(type!==Diff.Diff.Operation.Insert){deletionRows[deletionRows.length-1].tokens.push({text:lines[i],className});}
  34. if(type!==Diff.Diff.Operation.Delete){insertionRows[insertionRows.length-1].tokens.push({text:lines[i],className});}}}
  35. return deletionRows.concat(insertionRows);}
  36. function createRow(text,type){if(type===RowType.Addition){currentLineNumber++;}
  37. if(type===RowType.Deletion){baselineLineNumber++;}
  38. if(type===RowType.Equal){baselineLineNumber++;currentLineNumber++;}
  39. return{baselineLineNumber,currentLineNumber,tokens:text?[{text,className:'inner-diff'}]:[],type};}}
  40. _lineFormatter(lineNumber){const row=this._diffRows[lineNumber-1];let showBaseNumber=row.type===RowType.Deletion;let showCurrentNumber=row.type===RowType.Addition;if(row.type===RowType.Equal){showBaseNumber=true;showCurrentNumber=true;}
  41. const base=showBaseNumber?numberToStringWithSpacesPadding(row.baselineLineNumber,this._maxLineDigits):spacesPadding(this._maxLineDigits);const current=showCurrentNumber?numberToStringWithSpacesPadding(row.currentLineNumber,this._maxLineDigits):spacesPadding(this._maxLineDigits);return base+spacesPadding(1)+current;}}
  42. export const RowType={Deletion:'deletion',Addition:'addition',Equal:'equal',Spacer:'spacer'};export class DiffUILocationRevealer{async reveal(diffUILocation,omitFocus){if(!(diffUILocation instanceof WorkspaceDiff.DiffUILocation)){throw new Error('Internal error: not a diff ui location');}
  43. const changesView=self.runtime.sharedInstance(Changes.ChangesView);await UI.viewManager.showView('changes.changes');changesView._changesSidebar.selectUISourceCode(diffUILocation.uiSourceCode,omitFocus);}}
  44. self.Changes=self.Changes||{};Changes=Changes||{};Changes.ChangesView=ChangesView;Changes.ChangesView.RowType=RowType;Changes.ChangesView.Row;Changes.ChangesView.DiffUILocationRevealer=DiffUILocationRevealer;