JMichaelTX
1/14/2015 - 6:50 PM

Toggle selected comments in OS X 10.10 Script Editor

Toggle selected comments in OS X 10.10 Script Editor

// ROBIN TREW 2015 MIT License

// OSX 10.10 SCRIPT EDITOR – TOGGLE COMMENTS

// Add remove '// ' from before printing characters

// Depends on a library .scpt being saved at ~/Library/Script Libraries/DraftsSE.scpt
// See: See: http://support.foldingtext.com/t/writing-scripts-which-run-on-both-foldingtext-and-ios-drafts-4/652/7

// INCLUDE LIBRARY OF IOS DRAFTS-COMPATIBLE EDITOR FUNCTIONS:
function run() {


	// PART 1: CODE SPECIFIC TO YOSEMITE SCRIPT EDITOR:

	// SE implmentations of the basic iOS Drafts functions
	// (Implementations of these functions for FoldingText and other editors with .js scripting at:
	// 
	// getText()                               getSelectedLineRange()
	// setText(string)                         getSelectedRange()
	// getSelectedText()                       setSelectedRange(start, length)
	// setSelectedText(string)                 getClipboard()
	// getTextInRange(start, length)           setClipboard(string)
	// setTextInRange(start, length, string)


	var app = Application("Script Editor"),
		lstDoc = app.documents(),
		oDoc = lstDoc.length ? lstDoc[0] : null;

	if (!oDoc) return false;

	//either var drafts = Library('DraftsSE'); // Assuming file saved at: ~/Library/Script Libraries/DraftsSE.scpt
	// or locally:
	drafts = {
		getTextInRange: function (iStart, iLength) {
			if (oDoc) return oDoc.text().substring(iStart, iStart + iLength);
		},
		setSelectedRange: function (iStart, iLength) {
			if (oDoc) {
				oDoc.selection = oDoc.text.characters.slice(iStart, iStart + iLength);
			}
		},
		getSelectedLineRange: function () {
			// works, but let me know if you see a shorter route :-)
			var rgxLFCR=/[\r\n]/, dct, strFull, iFrom, iTo, lngChars;
			if (oDoc) {
				dct = oDoc.selection.characterRange();
				strFull = oDoc.text();
				lngChars = strFull.length;
				iFrom = dct.x;
				while (iFrom--)
					if (strFull[iFrom].match(rgxLFCR)) break;
				iFrom += 1;
				iTo = dct.y - 1;
				while (iTo++ < lngChars)
					if (strFull[iTo].match(rgxLFCR)) break;
				return [iFrom, (iTo - iFrom)];
			}
		},
		setTextInRange: function (iStart, iLength, strText) {
			if (oDoc) { // record the existing selection coordinates
				var lngDelta = (strText.length - iLength) - 2,
					oSeln = oDoc.selection,
					dct = oSeln.characterRange(),
					iFrom = dct.x,
					iTo = dct.y;

				try { // use the selection to edit elsewhere, then restore with any adjustment
					oDoc.selection = oDoc.text.characters.slice(iStart, iStart + iLength);
					oSeln.contents = strText;
					oDoc.selection = oDoc.text.characters.slice(
						(iStart < iFrom) ? iFrom + lngDelta : iFrom, ((iStart + iLength) < iTo) ? iTo + lngDelta : iTo
					);
				} catch (e) {}
			}
		}
	};

	app.includeStandardAdditions = true;
	// Javascript '//' or Applescript '--' ?
	strComment = (oDoc.language.id()[0] === 'j') ? '\/\/' : '--';

	// PART 2: CODE COMPATIBLE WITH FOLDINGTEXT, DRAFTS, 1WRITER, TEXTWELL

	function toggleComments(strCommentChars) {
		// Javascript '//' or Applescript '--' ( this draft doesn't cover /* */ )
		var rgxComment = new RegExp('^(\\s*)' + strCommentChars + '(\\s?)(\\s*)(?=\\S)', 'gm'),
			rgxLeftSpace = /^(\s*)(?=\S)/,
			rgxAddComment,
			strAddComment,
			lstLinesFromTo = drafts.getSelectedLineRange(),
			iFrom = lstLinesFromTo[0],
			lngSelnChars = lstLinesFromTo[1],
			strLines = drafts.getTextInRange(iFrom, lngSelnChars),
			blnCommented = rgxComment.test(strLines),
			lstLines,
			i, lngChars, oMatch, lngMatch,
			lngMin = Infinity,
			strLine,
			strNew,lngLen;

		if (blnCommented)
			strNew = strLines.replace(rgxComment, '$1$3');
		else {
			// shortest whitespace to left ?
			lstLines = strLines.split(/^/m);
			i = lstLines.length;
			while (i--) {
				strLine = lstLines[i];
				if (strLine) {
					oMatch = rgxLeftSpace.exec(strLine);
					if (oMatch) {
						lngChars = oMatch[1].length;
						if (lngChars < lngMin) lngMin = lngChars;
					} else {
						lngMin = 0;
						break;
					}
				}
			}
			if (lngMin === Infinity) lngMin = 0;

			// MAKE A REGEX TO INSERT THE COMMENT PATTERN N SPACE CHARS INTO THE LINE
			// strAddComment = '^(' + Array(lngMin + 1).join('\\s') + ')';
			// rgxAddComment = new RegExp(strAddComment, 'gm');
			// strNew = strLines.replace(rgxAddComment, '$1' + strCommentChars + ' ');
			strNew = strLines.replace(
				new RegExp(
					'^(' + Array(lngMin + 1).join('\\s') + ')', 'gm'
				), '$1' + strCommentChars + ' '
			);

		}

		drafts.setTextInRange(iFrom, lngSelnChars-1, strNew);
		lngLen = strNew.length-1;
		drafts.setSelectedRange(iFrom, lngLen);
		return [iFrom, lngLen];
	}

	// MAIN
	lstSeln = toggleComments(strComment);
	drafts.setSelectedRange(lstSeln[0], lstSeln[1])
	
}