Created
February 3, 2020 18:22
-
-
Save furnox/3e374f3a3e71979ed7fa7885158c0a6a to your computer and use it in GitHub Desktop.
Tool to show git and Subversion annotations (blame) in the file
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
komodo tool: SCC Annotations | |
============================ | |
async: 0 | |
is_clean: true | |
keyboard_shortcut: Ctrl+K, Ctrl+A | |
language: JavaScript | |
rank: 100 | |
trigger: trigger_postopen | |
trigger_enabled: 0 | |
type: macro | |
version: 1.1.6 | |
============================*/ | |
/** | |
* Macro to show source code control annotate information in the left scintilla | |
* margin. | |
*/ | |
// Scintilla margin parameters. | |
const MARGIN_WIDTH = 268; // pixels wide | |
const MARGIN = 4; // The unused scintilla margin. | |
const koLogger = require("ko/logging"); | |
const log = koLogger.getLogger("macro.scc_annotate"); | |
//log.setLevel(koLogger.LOG_DEBUG); | |
/** | |
* Parse git annotate line, return array of annotate entries [date, author, rev] | |
*/ | |
var GitAnnotateParser = function(stdout) { | |
var lines = stdout.split("\n"); | |
var line_split; | |
var revision, date, author; | |
var result = []; | |
for (var lineno=0; lineno < lines.length; lineno++) { | |
line_split = lines[lineno].match(/\s*(\w+).*?\((.*?)([-\d]+).*?\).*/); | |
if (!line_split) { | |
result.push(""); | |
continue; | |
} | |
revision = line_split[1]; | |
author = line_split[2]; | |
date = line_split[3]; | |
if (date && author) { | |
result.push([date, author, revision]); | |
} else { | |
result.push(""); | |
} | |
} | |
return result; | |
}; | |
/** | |
* Parse svn annotate line, return array of annotate entries [date, author, rev] | |
*/ | |
var SvnAnnotateParser = function(stdout) { | |
var lines = stdout.split("\n"); | |
var line_split; | |
var revision, author, date; | |
var result = []; | |
for (var lineno=0; lineno < lines.length; lineno++) { | |
line_split = lines[lineno].split(/\s+/, 4); | |
if (!line_split[0]) { | |
line_split = line_split.slice(1); | |
} | |
revision = line_split[0]; | |
author = line_split[1]; | |
date = line_split[2]; | |
if (revision && author && date) { | |
result.push([date, author, revision]); | |
} | |
} | |
return result; | |
}; | |
/** | |
* Fetch annotate lines for the given file. | |
*/ | |
var FetchAnnotationLines = function(koFileEx) { | |
log.debug("FetchAnnotationLines for file " + koFileEx.path); | |
if (!koFileEx.isLocal || !koFileEx.isFile) { | |
throw new Exception("Annotate only works on local files"); | |
} | |
var sccType = koFileEx.sccType; | |
if (!sccType) { | |
throw new Exception("Annotate only works on files under scc control"); | |
} | |
if (sccType != "svn" && sccType != "git") { | |
throw new Exception("Annotate only works for Git or Subversion repositories"); | |
} | |
var scc_annotate_parser = GitAnnotateParser; | |
if (sccType == "svn") { | |
scc_annotate_parser = SvnAnnotateParser; | |
} | |
var sccSvc = Components.classes["@activestate.com/koSCC?type=" + sccType + ";1"]. | |
getService(Components.interfaces.koISCC); | |
var scc_executable = sccSvc.executable || sccSvc.name; | |
var filename = koFileEx.leafName; | |
// Double quote the paths on Windows. | |
if (navigator.platform.startsWith("Win")) { | |
scc_executable = '"' + scc_executable + '"'; | |
filename = '"' + filename + '"'; | |
} | |
var cmd = scc_executable + ' annotate ' + filename; | |
var runSvc = Components.classes["@activestate.com/koRunService;1"]. | |
getService(Components.interfaces.koIRunService); | |
var process = runSvc.RunAndNotify(cmd, koFileEx.dirName, '', ''); | |
var retval = process.wait(-1); /* wait till the process is done */ | |
if (retval == 0) { | |
return scc_annotate_parser(process.getStdout()); | |
} else { | |
throw Error(process.getStderr()); | |
} | |
return []; | |
}; | |
/** | |
* Generate a unique color style for this entry. | |
* | |
* TODO: Be smarter - check both previous and next entries to ensure the color | |
* is different between changes. | |
*/ | |
var GetUniqueStyle = function(sm, entry_styles, entry) { | |
// Default color for this author. | |
var entryS = entry.toString(); | |
var style_num = entry_styles[entryS]; | |
if (style_num !== undefined) { | |
return style_num; | |
} | |
// Create a color for this entry. | |
style_num = 0; | |
for (var item in entry_styles) { | |
style_num += 1; | |
} | |
style_num = (style_num % 8); | |
entry_styles[entryS] = style_num; | |
return style_num; | |
}; | |
/** | |
* Show the annotations in the margin. | |
*/ | |
var DisplayAnnotations = function(view, lines) { | |
log.debug("DisplayAnnotations for " + lines.length + " lines"); | |
const MARGIN_STYLE_OFFSET = 255; | |
/** @type Components.interfaces.ISciMoz */ | |
var sm = view.scimoz; | |
sm.setMarginTypeN(MARGIN, sm.SC_MARGIN_TEXT); // left-justified text | |
sm.setMarginWidthN(MARGIN, MARGIN_WIDTH); | |
sm.setMarginSensitiveN(MARGIN, true); // Allow mouse clicks. | |
sm.marginStyleOffset = MARGIN_STYLE_OFFSET; | |
var isDarkScheme = view.scheme.isDarkBackground; | |
// Scintilla colors are in BGR format. | |
sm.styleSetBack(MARGIN_STYLE_OFFSET+1, isDarkScheme ? 0x442200 : 0xBBDDFF); | |
sm.styleSetBack(MARGIN_STYLE_OFFSET+2, isDarkScheme ? 0x220044 : 0xDDFFBB); | |
sm.styleSetBack(MARGIN_STYLE_OFFSET+3, isDarkScheme ? 0x004422 : 0xFFBBDD); | |
sm.styleSetBack(MARGIN_STYLE_OFFSET+4, isDarkScheme ? 0x000022 : 0xFFFFDD); | |
sm.styleSetBack(MARGIN_STYLE_OFFSET+5, isDarkScheme ? 0x002200 : 0xFFDDFF); | |
sm.styleSetBack(MARGIN_STYLE_OFFSET+6, isDarkScheme ? 0x220000 : 0xDDFFFF); | |
sm.styleSetBack(MARGIN_STYLE_OFFSET+7, isDarkScheme ? 0x440044 : 0xBBBBBB); | |
sm.styleSetBack(MARGIN_STYLE_OFFSET+8, isDarkScheme ? 0x220022 : 0xDDDDDD); | |
var entry, style, entry_styles = {}; | |
for (var lineno=0; lineno < lines.length; lineno++) { | |
entry = lines[lineno]; | |
if (!entry) { | |
continue; | |
} | |
style = GetUniqueStyle(sm, entry_styles, entry); | |
sm.marginSetText(lineno, entry[2] + ': ' + entry[0] + ': ' + entry[1].slice(0,10)); | |
sm.marginSetStyle(lineno, style); | |
} | |
}; | |
/** | |
* Handle annotate margin click and launch scc history dialog when clicked. | |
*/ | |
var OnMarginClick = function(modifiers, position, margin) { | |
log.debug("OnMarginClick:: margin " + margin); | |
var view = ko.views.manager.currentView; | |
if (margin != MARGIN) { | |
view._annotate_orig_onMarginClick(modifiers, position, margin); | |
} | |
var lineno = view.scimoz.lineFromPosition(position); | |
var revision = view._annotate_lines[lineno][2]; | |
ko.commands.doCommand("cmd_SCChistory_File", revision); | |
}; | |
/** | |
* Generate and show the annotations. | |
*/ | |
var ShowAnnotations = function(view) { | |
log.debug("ShowAnnotations"); | |
var lines = FetchAnnotationLines(view.koDoc.file); | |
DisplayAnnotations(view, lines); | |
view._annotate_lines = lines; | |
view._annotate_orig_onMarginClick = view.onMarginClick; | |
view.onMarginClick = OnMarginClick; | |
}; | |
/** | |
* Remove (hide) existing annotations. | |
*/ | |
var RemoveAnnotations = function(view) { | |
log.debug("RemoveAnnotations"); | |
view.scimoz.setMarginWidthN(MARGIN, 0); | |
view.onMarginClick = view._annotate_orig_onMarginClick; | |
delete view._annotate_orig_onMarginClick; | |
delete view._annotate_lines; | |
}; | |
try { | |
var view = ko.views.manager.currentView; | |
if (view) { | |
if (view.scimoz.getMarginWidthN(MARGIN) > 0) { | |
// Already visible - hide it. | |
RemoveAnnotations(view); | |
} else { | |
ShowAnnotations(view); | |
} | |
} | |
} catch (ex) { | |
log.exception(ex); | |
ko.statusBar.AddMessage("annotate error" + ex.toString(), "macros", 5000, true); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment