Skip to content

Instantly share code, notes, and snippets.

@davestewart
Created September 21, 2011 15:06
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save davestewart/1232292 to your computer and use it in GitHub Desktop.
Save davestewart/1232292 to your computer and use it in GitHub Desktop.
Auto-Commenting macro for Komodo Edit
/**
* @fileoverview Enter trigger for PHPdoc (code based on TAB trigger for Abbreviations by Stan Angeloff)
* @author Nathan Rijksen (http://naatan.com/)
* @contributor Todd Whiteman
* @contributor Michal Kocarek (http://brainbox.cz/)
* @contributor Alexander Kavoun (http://takkmoil.com/)
* @contributor Dave Stewart (http://davestewart.co.uk/)
* @version 0.3
*/
xjsfl.autocomment =
{
// ----------------------------------------------------------------------------------------------------
// Events
// ----------------------------------------------------------------------------------------------------
events:
{
add:function()
{
this.remove();
ko.views.manager.topView.addEventListener('keypress', this.onKeyPress, true);
},
remove:function()
{
if (xjsfl.autocomment && xjsfl.autocomment.onKeyPress)
{
ko.views.manager.topView.removeEventListener('keypress', this.onKeyPress, true);
}
},
onKeyPress:function(event)
{
// Only trap when ENTER pressed with no modifiers
if (event.keyCode === 13 && ( ! event.ctrlKey && ! event.altKey && ! event.shiftKey ) )
{
if(xjsfl.autocomment.processInput())
{
event.preventDefault();
event.stopPropagation();
}
}
}
},
// ----------------------------------------------------------------------------------------------------
// Prefs
// ----------------------------------------------------------------------------------------------------
prefs:
{
tabWidth: 4,
fixedWidths: {tag:7, type:15, name:15},
useFixedWidths: false
},
styles:
{
exts:
{
php: [/^php\d?$/i],
js: [/^(js\w*|as)$/i]
},
pref: 'php',
associate:function(rx, style)
{
if(/^(php|js)$/i.test(style))
{
this.exts[style].push(rx);
}
}
},
// ----------------------------------------------------------------------------------------------------
// Input
// ----------------------------------------------------------------------------------------------------
processInput:function()
{
// variables
var view = ko.views.manager.currentView;
/**
* @type {Components.interfaces.ISciMoz}
*/
var editor = view.scimoz;
// Don't do anything if there is a selection within the document
if (editor.anchor != editor.currentPos)
{
return false;
}
// grab last 3 characters so we can test for /**
var currentPos = editor.currentPos;
var text = editor.getTextRange(currentPos - 3, currentPos);
// test for opening block comment
if (text !== null && text == '/**')
{
// get the contents of the next line
var lineIndex = editor.lineFromPosition(currentPos);
var nextLineStart = editor.positionFromLine(lineIndex + 1);
var nextLineEnd = editor.getLineEndPosition(lineIndex + 1);
var nextLine = editor.getTextRange(nextLineStart, nextLineEnd);
// return early if the next line already starts with whitespace + a star character
if (/^[\t ]*\*/.test(nextLine))
{
return false;
}
// get doc style-type
var ext = view.item.url.split('.').pop();
for(var style in this.styles.exts)
{
for each(var rx in this.styles.exts[style])
{
if (rx.test(ext))
{
return this.processOutput(nextLine, style);
}
}
}
// fall back to default style
return this.processOutput(nextLine, this.styles.pref);
}
// return false
return false;
},
// ----------------------------------------------------------------------------------------------------
// Output
// ----------------------------------------------------------------------------------------------------
processOutput:function(line, style)
{
// --------------------------------------------------------------------------------
// params object
/**
* The Param class is an object-oriented wrapper to create rows and columns of param types
* @param {String} kind The kind of param, i.e. @param, @returm
* @param {String} type The datatype of the param (if relevant)
* @param {String} name The name of the param
* @returns {String} A description of the param or return type
*/
function Param(kind, type, name)
{
// param values
this.kind = kind;
this.type = type || '';
this.name = name || '';
// update type & name column widths (declared in createOutput())
widths.type = Math.max(this.type.length, widths.type);
widths.name = Math.max(this.name.length, widths.name);
// to string
/**
* Converts the Param class to a String
* @param {Number} padding The number of extra tabs to add
* @returns {String} A @param or @returns row
*/
this.toString = function(padding)
{
var output = ' * @';
output += pad(this.kind, widths.tag, tabWidth, 0);
output += pad(this.type, widths.type, tabWidth, 1);
output += pad(this.name, widths.name, tabWidth, 1);
output += tabstopDesc + '\n';
return output;
}
// utility function to pad columns to the correct widths
function pad(str, width, tabWidth, padding)
{
// set virtual width to the initial string width
var vwidth = str.length;
var output = '';
// pad initial word to the next column
var mod = str.length % tabWidth;
if(mod != 0)
{
output += '\t';
vwidth += (tabWidth - mod);
}
// while the column is smaller than the max width, pad to fit
while(vwidth < width)
{
vwidth += tabWidth;
output += '\t';
}
// ensure that any tabs that butt exactly up to the next tab are given space
if(width % tabWidth == 0)
{
output += '\t'
}
// add any extra gutters between columns
output += new Array((padding || 0) + 1).join('\t')
// return
return str + output;
}
}
// --------------------------------------------------------------------------------
// processing functions
function processFunction(matches)
{
// --------------------------------------------------------------------------------
// function components
function processParams(strParams)
{
// variables
var params = [];
var matches = strParams.match(rxParams);
// loop over params
for each(var match in matches)
{
// match parts
var parts = match.match(rxParam);
// create Param object
if(style == 'js')
{
params.push(new Param('param', '{' + createTabstop(parts[2] || 'Type') + '}', parts[1]));
}
else
{
params.push(new Param('param', createTabstop(parts[1]|| 'Object'), parts[2]));
}
}
// return
return params;
}
/**
* Processes the return type of a match and returns a Param object
* @param {String} type The String type of an object
* @returns {Param} A Param instance
*/
function processReturn(type)
{
// create param
if(style == 'js')
{
var param = new Param('returns', '{' + createTabstop(type || 'Type') + '}', '');
}
else
{
var param = new Param('returns', createTabstop(type || 'Object'), '');
}
// return
return param
}
// --------------------------------------------------------------------------------
// process
// create components
var params = processParams(matches[1]);
var returns = processReturn(matches[2]);
// optionally hard-code columns
if(this.prefs.useFixedWidths)
{
widths = this.prefs.fixedWidths;
}
// output
var output = '\n';
output += ' * [[%tabstop:Summary]]\n';
if(params && params.length)
{
for each(var param in params)
{
output += param.toString();
}
}
output += returns.toString();
output += ' */';
// trace
return output;
}
function processClass()
{
return '\n * ' + tabstopDesc + ' */';
}
function processVariable()
{
if (style == 'php')
{
return '\n * @var ' + tabstopType + ' ' + tabstopDesc + '\n */';
}
else if (style == 'js')
{
return ' @type {' + tabstopType + '} ' + tabstopDesc + ' */';
}
return '';
}
function createTabstop(text)
{
return '[[%tabstop:' +text+ ']]';
}
// --------------------------------------------------------------------------------
// variables
// matching parameters
var rxClass = /^\s*?class/i;
var rxVariable = /^\s*?(?:var|private|public|protected)/;
var rxFunction = /\bfunction\b\s*(?:\w*)\s*\((.*)\):?([\w\*]+)?/
var rxParam = /([$\w]+)[\s:]?([$\w\*]+)?/;
var rxParams = new RegExp(rxParam.source, 'g');
// variables
var tabWidth = this.prefs.tabWidth;
var widths = {tag:0, type:0, name:0};
var matches = null;
var snippet = '';
// tabstops
var tabstopType = '[[%tabstop:Type]]';
var tabstopDesc = '[[%tabstop:Description]]';
// --------------------------------------------------------------------------------
// process
// process the next line
if(rxClass.test(line))
{
snippet = processClass();
}
else if(rxFunction.test(line))
{
snippet = processFunction.call(this, line.match(rxFunction));
}
else if(rxVariable.test(line))
{
snippet = processVariable();
}
else
{
return false;
}
// create the snippet
var snippetObj =
{
value: snippet,
name: 'autodoc snippet',
indent_relative: 'true',
hasAttribute: function (name)
{
return name in this;
},
getStringAttribute: function (name)
{
return this[name];
}
};
// insert snippet
ko.projects.snippetInsert(snippetObj);
// return
return true;
}
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment