Mercurial and Git clients can push and pull from this alias URL to interact with this repository. You can change to which repository an alias points by going to the Aliases link on the project page.
# fileview.py - File diff, content, and annotation display widget## Copyright 2010 Steve Borho <steve@borho.org>## This software may be used and distributed according to the terms of the# GNU General Public License version 2, incorporated herein by reference.importosimportdifflibfrommercurialimporterror,utilfromtortoisehg.utilimporthglib,patchctx,colormap,thread2fromtortoisehg.hgqt.i18nimport_fromtortoisehg.hgqtimportqscilib,qtlib,blockmatcher,lexersfromtortoisehg.hgqtimportvisdiff,filedatafromPyQt4.QtCoreimport*fromPyQt4.QtGuiimport*fromPyQt4importQsciqsci=Qsci.QsciScintillaDiffMode=1FileMode=2AnnMode=3classHgFileView(QFrame):"file diff, content, and annotation viewer"linkActivated=pyqtSignal(QString)fileDisplayed=pyqtSignal(QString,QString)showMessage=pyqtSignal(QString)revisionSelected=pyqtSignal(int)shelveToolExited=pyqtSignal()searchRequested=pyqtSignal(unicode)"""Emitted (pattern) when user request to search content"""grepRequested=pyqtSignal(unicode,dict)"""Emitted (pattern, opts) when user request to search changelog"""def__init__(self,repo,parent):QFrame.__init__(self,parent)framelayout=QVBoxLayout(self)framelayout.setContentsMargins(0,0,0,0)framelayout.setSpacing(0)l=QHBoxLayout()l.setContentsMargins(0,0,0,0)l.setSpacing(0)self.repo=repoself.topLayout=QVBoxLayout()self.labelhbox=hbox=QHBoxLayout()hbox.setContentsMargins(0,0,0,0)hbox.setSpacing(2)self.topLayout.addLayout(hbox)self.diffToolbar=QToolBar(_('Diff Toolbar'))self.diffToolbar.setIconSize(QSize(16,16))hbox.addWidget(self.diffToolbar)self.filenamelabel=w=QLabel()w.setWordWrap(True)f=w.textInteractionFlags()w.setTextInteractionFlags(f|Qt.TextSelectableByMouse)w.linkActivated.connect(self.linkActivated)hbox.addWidget(w,1)self.extralabel=w=QLabel()w.setWordWrap(True)w.linkActivated.connect(self.linkActivated)self.topLayout.addWidget(w)w.hide()framelayout.addLayout(self.topLayout)framelayout.addLayout(l,1)hbox=QHBoxLayout()hbox.setContentsMargins(0,0,0,0)hbox.setSpacing(0)l.addLayout(hbox)self.blk=blockmatcher.BlockList(self)self.sci=AnnotateView(repo,self) hbox.addWidget(self.blk)
hbox.addWidget(self.sci, 1)
- for name in ('searchRequested', 'editSelected', 'grepRequested'):- getattr(self.sci, name).connect(getattr(self, name))- self.sci.revisionHint.connect(self.showMessage)
- self.sci.sourceChanged.connect(self.sourceChanged)+ self.sci.showMessage.connect(self.showMessage)
self.sci.setAnnotationEnabled(False)
+ self.sci.setContextMenuPolicy(Qt.CustomContextMenu)+ self.sci.customContextMenuRequested.connect(self.menuRequest) self.blk.linkScrollBar(self.sci.verticalScrollBar())
self.blk.setVisible(False)
self.sci.setFrameStyle(0)self.sci.setReadOnly(True)self.sci.setUtf8(True)self.sci.installEventFilter(qscilib.KeyPressInterceptor(self))self.sci.setCaretLineVisible(False)# define markers for colorize zones of diffself.markerplus=self.sci.markerDefine(qsci.Background)self.markerminus=self.sci.markerDefine(qsci.Background)self.markertriangle=self.sci.markerDefine(qsci.Background)self.sci.setMarkerBackgroundColor(QColor('#B0FFA0'),self.markerplus)self.sci.setMarkerBackgroundColor(QColor('#A0A0FF'),self.markerminus)self.sci.setMarkerBackgroundColor(QColor('#FFA0A0'),self.markertriangle)# hide margin 0 (markers)self.sci.setMarginType(0,qsci.SymbolMargin)self.sci.setMarginWidth(0,0)self.searchbar=qscilib.SearchToolBar(hidable=True)self.searchbar.hide()self.searchbar.searchRequested.connect(self.find)self.searchbar.conditionChanged.connect(self.highlightText)self.layout().addWidget(self.searchbar)self._ctx=Noneself._filename=Noneself._status=Noneself._mode=Noneself._parent=0self._lostMode=Noneself._lastSearch=u'',Falseself.actionDiffMode=QAction(qtlib.geticon('view-diff'),_('View change as unified diff output'),self)self.actionDiffMode.setCheckable(True)self.actionDiffMode._mode=DiffModeself.actionFileMode=QAction(qtlib.geticon('view-file'),_('View change in context of file'),self)self.actionFileMode.setCheckable(True)self.actionFileMode._mode=FileModeself.actionAnnMode=QAction(qtlib.geticon('view-annotate'),_('annotate with revision numbers'),self)self.actionAnnMode.setCheckable(True)self.actionAnnMode._mode=AnnModeself.modeToggleGroup=QActionGroup(self)self.modeToggleGroup.addAction(self.actionDiffMode)self.modeToggleGroup.addAction(self.actionFileMode)self.modeToggleGroup.addAction(self.actionAnnMode)self.modeToggleGroup.triggered.connect(self.setMode)# Next/Prev diff (in full file mode)self.actionNextDiff=QAction(qtlib.geticon('go-down'),'Next diff (alt+down)',self)self.actionNextDiff.setShortcut('Alt+Down')self.actionNextDiff.triggered.connect(self.nextDiff)self.actionPrevDiff=QAction(qtlib.geticon('go-up'),'Previous diff (alt+up)',self)self.actionPrevDiff.setShortcut('Alt+Up')self.actionPrevDiff.triggered.connect(self.prevDiff)self.setMode(self.actionDiffMode)self.actionFirstParent=QAction('1',self)self.actionFirstParent.setCheckable(True)self.actionFirstParent.setChecked(True)self.actionFirstParent.setShortcut('CTRL+1')self.actionFirstParent.setToolTip(_('Show changes from first parent'))self.actionSecondParent=QAction('2',self)self.actionSecondParent.setCheckable(True)self.actionSecondParent.setShortcut('CTRL+2')self.actionSecondParent.setToolTip(_('Show changes from second parent'))self.parentToggleGroup=QActionGroup(self)self.parentToggleGroup.addAction(self.actionFirstParent)self.parentToggleGroup.addAction(self.actionSecondParent)self.parentToggleGroup.triggered.connect(self.setParent)self.actionFind=self.searchbar.toggleViewAction()self.actionFind.setIcon(qtlib.geticon('edit-find'))self.actionFind.setToolTip(_('Toggle display of text search bar'))self.actionFind.setShortcut(QKeySequence.Find)self.actionShelf=QAction('Shelve',self)self.actionShelf.setIcon(qtlib.geticon('shelve'))self.actionShelf.setToolTip(_('Open shelve tool'))self.actionShelf.triggered.connect(self.launchShelve)tb=self.diffToolbartb.addAction(self.actionFirstParent)tb.addAction(self.actionSecondParent)tb.addSeparator()tb.addAction(self.actionDiffMode)tb.addAction(self.actionFileMode)tb.addAction(self.actionAnnMode)tb.addSeparator()tb.addAction(self.actionNextDiff)tb.addAction(self.actionPrevDiff)tb.addSeparator()tb.addAction(self.actionFind)tb.addAction(self.actionShelf)self.timer=QTimer()self.timer.setSingleShot(False)self.timer.timeout.connect(self.timerBuildDiffMarkers)deflaunchShelve(self):fromtortoisehg.hgqtimportshelve# TODO: pass self._filenamedlg=shelve.ShelveDialog(self.repo,self)dlg.finished.connect(dlg.deleteLater)dlg.exec_()self.shelveToolExited.emit()defsetFont(self,font):self.sci.setFont(font)defloadSettings(self,qs,prefix):self.sci.loadSettings(qs,prefix)defsaveSettings(self,qs,prefix):self.sci.saveSettings(qs,prefix)defsetRepo(self,repo):self.repo=repoself.sci.repo=repo@pyqtSlot(QAction)defsetMode(self,action):'One of the mode toolbar buttons has been toggled'mode=action._modeifmode!=self._mode:self._mode=modeself.actionNextDiff.setEnabled(False)self.actionPrevDiff.setEnabled(False)self.blk.setVisible(mode!=DiffMode)self.sci.setAnnotationEnabled(mode==AnnMode)self.displayFile(self._filename,self._status)@pyqtSlot(QAction)defsetParent(self,action):ifaction.text()=='1':parent=0else:parent=1ifself._parent!=parent:self._parent=parentself.displayFile(self._filename,self._status)defrestrictModes(self,candiff,canfile,canann):'Disable modes based on content constraints'self.actionDiffMode.setEnabled(candiff)self.actionFileMode.setEnabled(canfile)self.actionAnnMode.setEnabled(canann)# Switch mode if necessarymode=self._modeifnotcandiffandmode==DiffModeandcanfile:mode=FileModeifnotcanfileandmode!=DiffMode:mode=DiffModeifself._lostModeisNone:self._lostMode=self._modeifself._mode!=mode:self.actionNextDiff.setEnabled(False)self.actionPrevDiff.setEnabled(False)self.blk.setVisible(mode!=DiffMode)self.sci.setAnnotationEnabled(mode==AnnMode)self._mode=modeifself._mode==DiffMode:self.actionDiffMode.setChecked(True)elifself._mode==FileMode:self.actionFileMode.setChecked(True)else:self.actionAnnMode.setChecked(True)defsetContext(self,ctx,ctx2=None):self._ctx=ctxself._ctx2=ctx2self.sci.setTabWidth(ctx._repo.tabwidth)self.actionAnnMode.setVisible(ctx.rev()!=None)self.actionShelf.setVisible(ctx.rev()==None)self.actionFirstParent.setVisible(len(ctx.parents())==2)self.actionSecondParent.setVisible(len(ctx.parents())==2)self.actionFirstParent.setEnabled(len(ctx.parents())==2)self.actionSecondParent.setEnabled(len(ctx.parents())==2)defshowLine(self,line):ifline<self.sci.lines():self.sci.setCursorPosition(line,0)@pyqtSlot()defclearDisplay(self):self._filename=Noneself.restrictModes(False,False,False)self.sci.setMarginWidth(1,0)self.clearMarkup()defclearMarkup(self):self.sci.clear()self.blk.clear()# Setting the label to ' ' rather than clear() keeps the label# from disappearing during refresh, and tool layouts bouncingself.filenamelabel.setText(' ')self.extralabel.hide()defdisplayFile(self,filename=None,status=None):ifisinstance(filename,(unicode,QString)):filename=hglib.fromunicode(filename)status=hglib.fromunicode(status)ifself._filename==filename:# Get the last visible line to restore it after reloading the editorlastScrollPosition=self.sci.firstVisibleLine()else:# Reset the scroll positions when the file is changedlastScrollPosition=0self._filename,self._status=filename,statusself.clearMarkup()iffilenameisNone:self.restrictModes(False,False,False)returnifself._ctx2:ctx2=self._ctx2elifself._parent==0orlen(self._ctx.parents())==1:ctx2=self._ctx.p1()else:ctx2=self._ctx.p2()fd=filedata.FileData(self._ctx,ctx2,filename,status)iffd.elabel:self.extralabel.setText(fd.elabel)self.extralabel.show()else:self.extralabel.hide()self.filenamelabel.setText(fd.flabel)ifnotfd.isValid():self.sci.setText(fd.error)self.restrictModes(False,False,False)returncandiff=bool(fd.diff)canfile=bool(fd.contents)canann=canfileandtype(self._ctx.rev())isintifnotcandiffornotcanfile:self.restrictModes(candiff,canfile,canann)else:self.actionDiffMode.setEnabled(True)self.actionFileMode.setEnabled(True)self.actionAnnMode.setEnabled(True)ifself._lostMode:self._mode=self._lostModeifself._lostMode==DiffMode:self.actionDiffMode.trigger()elifself._lostMode==FileMode:self.actionFileMode.trigger()elifself._lostMode==AnnMode:self.actionAnnMode.trigger()self._lostMode=Noneself.blk.setVisible(self._mode!=DiffMode)self.sci.setAnnotationEnabled(self._mode==AnnMode)ifself._mode==DiffMode:self.sci.setMarginWidth(1,0)lexer=lexers.get_diff_lexer(self)self.sci.setLexer(lexer)iflexerisNone:self.setFont(qtlib.getfont('fontlog').font())# trim first three lines, for example:# diff -r f6bfc41af6d7 -r c1b18806486d tortoisehg/hgqt/thgrepo.py# --- a/tortoisehg/hgqt/thgrepo.py# +++ b/tortoisehg/hgqt/thgrepo.pyiffd.diff:out=fd.diff.split('\n',3)iflen(out)==4:self.sci.setText(hglib.tounicode(out[3]))else:# there was an error or rename without diffsself.sci.setText(hglib.tounicode(fd.diff))eliffd.contentsisNone:returnelse:lexer=lexers.get_lexer(filename,fd.contents,self)self.sci.setLexer(lexer)iflexerisNone:self.setFont(qtlib.getfont('fontlog').font()) self.sci.setText(fd.contents)
self.sci._updatemarginwidth()
if self._mode == AnnMode:
- self.sci.annfile = filename- self.sci._rev = self._ctx.rev()- self.sci._updateannotation()
+ self.sci._updateannotation(self._ctx, filename)
# Recover the last scroll position
# Make sure that lastScrollPosition never exceeds the amount of
# lines on the editorlastScrollPosition=min(lastScrollPosition,self.sci.lines()-1)self.sci.verticalScrollBar().setValue(lastScrollPosition)self.highlightText(*self._lastSearch)uf=hglib.tounicode(filename)self.fileDisplayed.emit(uf,fd.contentsorQString())ifself._mode!=DiffModeandfd.contentsandfd.olddata:# Update blk marginifself.timer.isActive():self.timer.stop()self._fd=fdself.actionNextDiff.setEnabled(False)self.actionPrevDiff.setEnabled(False)self.blk.syncPageStep()self.timer.start()## These four functions are used by Shift+Cursor actions in revdetails#defnextLine(self):x,y=self.sci.getCursorPosition()self.sci.setCursorPosition(x+1,y)defprevLine(self):x,y=self.sci.getCursorPosition()self.sci.setCursorPosition(x-1,y)defnextCol(self):x,y=self.sci.getCursorPosition()self.sci.setCursorPosition(x,y+1)defprevCol(self): x, y = self.sci.getCursorPosition()
self.sci.setCursorPosition(x, y-1)
- @pyqtSlot(unicode, object)- @pyqtSlot(unicode, object, int)- def sourceChanged(self, path, rev, line=None):- if rev != self._ctx.rev() and type(rev) is int:- self.revisionSelected.emit(rev)- @pyqtSlot(unicode, object, int)
def editSelected(self, path, rev, line):
"""Open editor to show the specified file"""
path=hglib.fromunicode(path)base=visdiff.snapshot(self.repo,[path],self.repo[rev])[0]files=[os.path.join(base,path)]pattern=hglib.fromunicode(self._lastSearch[0])qtlib.editfiles(self.repo,files,line,pattern,self)@pyqtSlot(unicode,bool,bool,bool)deffind(self,exp,icase=True,wrap=False,forward=True):self.sci.find(exp,icase,wrap,forward)@pyqtSlot(unicode,bool)defhighlightText(self,match,icase=False):self._lastSearch=match,icaseself.sci.highlightText(match,icase)defverticalScrollBar(self):returnself.sci.verticalScrollBar()## file mode diff markers#deftimerBuildDiffMarkers(self):'show modified and added lines in the self.blk margin'self.sci.setUpdatesEnabled(False)self.blk.setUpdatesEnabled(False)ifself._fd:olddata=self._fd.olddata.splitlines()newdata=self._fd.contents.splitlines()diff=difflib.SequenceMatcher(None,olddata,newdata)self._opcodes=diff.get_opcodes()self._fd=Noneself._diffs=[]fortag,alo,ahi,blo,bhiinself._opcodes[:30]:iftag=='replace':self._diffs.append([blo,bhi])self.blk.addBlock('x',blo,bhi)foriinrange(blo,bhi):self.sci.markerAdd(i,self.markertriangle)eliftag=='insert':self._diffs.append([blo,bhi])self.blk.addBlock('+',blo,bhi)foriinrange(blo,bhi):self.sci.markerAdd(i,self.markerplus)eliftagin('equal','delete'):passelse:raiseValueError,'unknown tag %r'%(tag,)self._opcodes=self._opcodes[30:]ifnotself._opcodes:self.actionNextDiff.setEnabled(bool(self._diffs))self.actionPrevDiff.setEnabled(False)self.timer.stop()self.sci.setUpdatesEnabled(True)self.blk.setUpdatesEnabled(True)defnextDiff(self):ifself._mode==DiffModeornotself._diffs:self.actionNextDiff.setEnabled(False)self.actionPrevDiff.setEnabled(False)returnrow,column=self.sci.getCursorPosition()fori,(lo,hi)inenumerate(self._diffs):iflo>row:last=(i==(len(self._diffs)-1))self.sci.setCursorPosition(lo,0)self.sci.verticalScrollBar().setValue(lo)breakelse:last=Trueself.actionNextDiff.setEnabled(notlast)self.actionPrevDiff.setEnabled(True)defprevDiff(self):ifself._mode==DiffModeornotself._diffs:self.actionNextDiff.setEnabled(False)self.actionPrevDiff.setEnabled(False)returnrow,column=self.sci.getCursorPosition()fori,(lo,hi)inenumerate(reversed(self._diffs)):ifhi<row:first=(i==(len(self._diffs)-1))self.sci.setCursorPosition(lo,0)self.sci.verticalScrollBar().setValue(lo)breakelse:first=Trueself.actionNextDiff.setEnabled(True)self.actionPrevDiff.setEnabled(notfirst) def nDiffs(self):
return len(self._diffs)
+ @pyqtSlot(QPoint)+ def menuRequest(self, point):+ menu = self.sci.createStandardContextMenu()+ line = self.sci.lineAt(point)+ point = self.sci.mapToGlobal(point)+ if line < 0 or self._mode != AnnMode:+ return menu.exec_(point)++ def setSource(path, rev, line):+ self.revisionSelected.emit(rev)+ self.setContext(self.repo[rev])+ self.displayFile(path, None)+ self.showLine(line)++ fctx, line = self.sci._links[line]+ data = [hglib.tounicode(fctx.path()), fctx.rev(), line]++ if self.sci.hasSelectedText():+ selection = self.sci.selectedText()+ def sreq(**opts):+ return lambda: self.grepRequested.emit(selection, opts)+ def sann():+ self.searchRequested.emit(selection)+ menu.addSeparator()+ for name, func in [(_('Search in original revision'),+ sreq(rev=fctx.rev())),+ (_('Search in working revision'),+ sreq(rev='.')),+ (_('Search in current annotation'), sann),+ (_('Search in history'), sreq(all=True))]:+ def add(name, func):+ action = menu.addAction(name)+ action.triggered.connect(func)+ add(name, func)++ def annorig():+ setSource(*data)+ def editorig():+ self.editSelected(*data)+ menu.addSeparator()+ for name, func in [(_('Annotate originating revision'), annorig),+ (_('View originating revision'), editorig)]:+ def add(name, func):+ action = menu.addAction(name)+ action.triggered.connect(func)+ add(name, func)+ for pfctx in fctx.parents():+ pdata = [hglib.tounicode(pfctx.path()), pfctx.changectx().rev(),+ line]+ def annparent(data):+ setSource(*data)+ def editparent(data):+ self.editSelected(*data)+ for name, func in [(_('Annotate parent revision %d') % pdata[1],+ annparent),+ (_('View parent revision %d') % pdata[1],+ editparent)]:+ def add(name, func):+ action = menu.addAction(name)+ action.data = pdata+ action.run = lambda: func(action.data)+ action.triggered.connect(action.run)+ add(name, func)+ menu.exec_(point)+class AnnotateView(qscilib.Scintilla):
'QScintilla widget capable of displaying annotations'
-revisionHint = pyqtSignal(QString)
+showMessage = pyqtSignal(QString)
- searchRequested = pyqtSignal(QString)- """Emitted (pattern) when user request to search content"""-- editSelected = pyqtSignal(unicode, object, int)- """Emitted (path, rev, line) when user requests to open editor"""-- grepRequested = pyqtSignal(QString, dict)- """Emitted (pattern, **opts) when user request to search changelog"""-- sourceChanged = pyqtSignal(unicode, object)- """Emitted (path, rev) when the content source changed"""-- def __init__(self, repo, parent=None, **opts):
+ def __init__(self, repo, parent=None):
super(AnnotateView, self).__init__(parent)
self.setReadOnly(True)
self.setMarginLineNumbers(1, True)
self.setMarginType(2, qsci.TextMarginRightJustified)
- self.setMouseTracking(True)
- self.setContextMenuPolicy(Qt.CustomContextMenu)- self.customContextMenuRequested.connect(self.menuRequest)+ self.setMouseTracking(False)
- self.repo = repo- self.repo.configChanged.connect(self.configChanged)- self.configChanged()- self._rev = None- self.annfile = None- self._annotation_enabled = bool(opts.get('annotationEnabled', False))-+ self._annotation_enabled = False
self._links = [] # by line
self._revmarkers = {} # by rev
self._lastrev = None
self._thread = AnnotateThread(self)
self._thread.finished.connect(self.fillModel)
+ self.repo = repo+ self.repo.configChanged.connect(self.configChanged)+ self.configChanged()+ def configChanged(self):
self.setIndentationWidth(self.repo.tabwidth)
self.setTabWidth(self.repo.tabwidth)
defkeyPressEvent(self,event):ifevent.key()==Qt.Key_Escape:self._thread.abort()returnreturnsuper(AnnotateView,self).keyPressEvent(event)defmouseMoveEvent(self,event):self._emitRevisionHintAtLine(self.lineAt(event.pos()))super(AnnotateView,self).mouseMoveEvent(event)def_emitRevisionHintAtLine(self,line):ifline<0:returntry:fctx=self._links[line][0] if fctx.rev() != self._lastrev:
s = hglib.get_revision_desc(fctx,
hglib.fromunicode(self.annfile))
- self.revisionHint.emit(s)
+ self.showMessage.emit(s)
self._lastrev = fctx.rev()
except IndexError:
pass
- @pyqtSlot(QPoint)- def menuRequest(self, point):
-menu= self.createStandardContextMenu()
-line=self.lineAt(point)- point = self.mapToGlobal(point)- if line < 0 or not self.isAnnotationEnabled():- return menu.exec_(point)-- fctx, line = self._links[line]- data = [hglib.tounicode(fctx.path()), fctx.rev(), line]-- if self.hasSelectedText():- selection = self.selectedText()- def sreq(**opts):- return lambda: self.grepRequested.emit(selection, opts)- def sann():- self.searchRequested.emit(selection)- menu.addSeparator()- for name, func in [(_('Search in original revision'),- sreq(rev=fctx.rev())),- (_('Search in working revision'),- sreq(rev='.')),- (_('Search in current annotation'), sann),- (_('Search in history'), sreq(all=True))]:- def add(name, func):- action = menu.addAction(name)- action.triggered.connect(func)- add(name, func)-- def annorig():- self.setSource(*data)- def editorig():- self.editSelected.emit(*data)- menu.addSeparator()- for name, func in [(_('Annotate originating revision'), annorig),- (_('View originating revision'), editorig)]:- def add(name, func):- action = menu.addAction(name)- action.triggered.connect(func)- add(name, func)- for pfctx in fctx.parents():- pdata = [hglib.tounicode(pfctx.path()), pfctx.changectx().rev(),- line]- def annparent(data):- self.setSource(*data)- def editparent(data):- self.editSelected.emit(*data)- for name, func in [(_('Annotate parent revision %d') % pdata[1],- annparent),- (_('View parent revision %d') % pdata[1],- editparent)]:- def add(name, func):- action = menu.addAction(name)- action.data = pdata- action.run = lambda: func(action.data)- action.triggered.connect(action.run)- add(name, func)- menu.exec_(point)-- @property- def rev(self):- """Returns the current revision number"""- return self._rev-- @pyqtSlot(unicode, object, int)- def setSource(self, wfile, rev, line=None):- """Change the content to the specified file at rev [unicode]-- line is counted from 1.- """- if self.annfile == wfile and self.rev == rev:- if line:- self.setCursorPosition(int(line) - 1, 0)- return-- try:-ctx = self.repo[rev]- fctx = ctx[hglib.fromunicode(wfile)]- except error.LookupError:- qtlib.ErrorMsgBox(_('Unable to annotate'),- _('%s is not found in revision %d') % (wfile, ctx.rev()))- return-- try:- if rev is None:- size = fctx.size()- else:- size = fctx._filelog.rawsize(fctx.filerev())- except (EnvironmentError, error.LookupError), e:- self.setText(_('File or diffs not displayed: ') + \- hglib.tounicode(str(e)))- self.error = p + hglib.tounicode(str(e))- return-- if size > ctx._repo.maxdiff:- self.setText(_('File or diffs not displayed: ') + \- _('File is larger than the specified max size.\n'))- else:- self._rev = ctx.rev()- self.clear()- self.annfile = wfile- if util.binary(fctx.data()):- self.setText(_('File is binary.\n'))- else:- self.setText(hglib.tounicode(fctx.data()))- if line:- self.setCursorPosition(int(line) - 1, 0)- self._updatelexer(fctx)- self._updatemarginwidth()- self.sourceChanged.emit(wfile, self._rev)- self._updateannotation()-- def _updateannotation(self):- if not self.isAnnotationEnabled() or not self.annfile:- return- ctx = self.repo[self._rev]- fctx = ctx[hglib.fromunicode(self.annfile)]- if util.binary(fctx.data()):- return+ def _updateannotation(self, ctx, filename):
+assertctx.rev() is not None+assertfilenamein ctx+ self.ctx = ctx+ self.annfile = filename self._thread.abort()
- self._thread.start(fctx)
+ self._thread.start(ctx[filename])
@pyqtSlot()
def fillModel(self):
self._thread.wait()ifself._thread.dataisNone:returnself._links=list(self._thread.data)self._updaterevmargin()self._updatemarkers()self._updatemarginwidth()defclear(self): super(AnnotateView, self).clear()
self.clearMarginText()
self.markerDeleteAll()
- self.annfile = None- @pyqtSlot(bool) def setAnnotationEnabled(self, enabled):
"""Enable / disable annotation"""
- enabled = bool(enabled)- if enabled == self.isAnnotationEnabled():- return self._annotation_enabled = enabled
- self._updateannotation() self._updatemarginwidth()
self.setMouseTracking(enabled)
- if not self.isAnnotationEnabled():
- self.annfile = None+ if not enabled:
self.markerDeleteAll()
def isAnnotationEnabled(self):
"""True if annotation enabled and available"""
- if self.rev is None:- return False # annotate working copy is not supported return self._annotation_enabled
- def _updatelexer(self, fctx):- """Update the lexer according to the given file"""- lex = lexers.get_lexer(fctx.path(), hglib.tounicode(fctx.data()), self)- self.setLexer(lex)- if lex is None:- self.setFont(qtlib.getfont('fontlog').font())- def _updaterevmargin(self):
"""Update the content of margin area showing revisions"""
s = self._margin_style
# Workaround to set style of the current sci widget.# QsciStyle sends style data only to the first sci widget.# See qscintilla2/Qt4/qscistyle.cppself.SendScintilla(qsci.SCI_STYLESETBACK,s.style(),s.paper())self.SendScintilla(qsci.SCI_STYLESETFONT,s.style(),s.font().family().toAscii().data())self.SendScintilla(qsci.SCI_STYLESETSIZE,s.style(),s.font().pointSize())fori,(fctx,_origline)inenumerate(self._links):self.setMarginText(i,str(fctx.rev()),s)def_updatemarkers(self):"""Update markers which colorizes each line"""self._redefinemarkers()fori,(fctx,_origline)inenumerate(self._links):m=self._revmarkers.get(fctx.rev())ifmisnotNone:self.markerAdd(i,m) def _redefinemarkers(self):
"""Redefine line markers according to the current revs"""
- curdate = self.repo[self._rev].date()[0]
+ curdate = self.ctx.date()[0]
# make sure to colorize at least 1 year
mindate = curdate - 365 * 24 * 60 * 60
self._revmarkers.clear()filectxs=iter(fctxforfctx,_origlineinself._links)palette=colormap.makeannotatepalette(filectxs,curdate,maxcolors=32,maxhues=8,maxsaturations=16,mindate=mindate)fori,(color,fctxs)inenumerate(palette.iteritems()):self.markerDefine(qsci.Background,i)self.setMarkerBackgroundColor(QColor(color),i)forfctxinfctxs:self._revmarkers[fctx.rev()]=i@util.propertycachedef_margin_style(self):"""Style for margin area"""s=Qsci.QsciStyle()s.setPaper(QApplication.palette().color(QPalette.Window))s.setFont(self.font())returns@pyqtSlot()def_updatemarginwidth(self):self.setMarginsFont(self.font())deflentext(s):return'M'*(len(str(s))+2)# 2 for marginself.setMarginWidth(1,lentext(self.lines()))ifself.isAnnotationEnabled()andself._links:maxrev=max(fctx.rev()forfctx,_origlineinself._links)self.setMarginWidth(2,lentext(maxrev))else:self.setMarginWidth(2,0)classAnnotateThread(QThread):'Background thread for annotating a file at a revision'def__init__(self,parent=None):super(AnnotateThread,self).__init__(parent)self._threadid=None@pyqtSlot(object)defstart(self,fctx):self._fctx=fctxsuper(AnnotateThread,self).start()self.data=None@pyqtSlot()defabort(self):ifself._threadidisNone:returntry:thread2._async_raise(self._threadid,KeyboardInterrupt)self.wait()exceptValueError:passdefrun(self):assertself.currentThread()!=qApp.thread()self._threadid=self.currentThreadId()try:data=[]for(fctx,line),_textinself._fctx.annotate(True,True):data.append((fctx,line))self.data=dataexceptKeyboardInterrupt:passfinally:self._threadid=Nonedelself._fctx
Attach a Trello Card
Add a tag
Your session has expired
You are no longer logged in. Please log in and try your request again.
Filter RSS Feed
This RSS feed URL allows you to see the contents of your current filter using any feed reader.
This link includes a special authentication token. If you share the URL with anyone else, they can see this RSS feed's activity. You can disable these tokens when needed.
Your current filter is unsaved; changing it won't affect this RSS feed.