$engine JScript
$uname textEditorExt
$dname Расширение редактора текстов
$addin global
$addin stdlib
/* ======================================================================
AUTHOR: Василий Фролов aka Палыч, palytsh@mail.ru
DATE: 02.09.2011
COMMENT:
1. Макросы НайтиВыделенныйТекстВниз и НайтиВыделенныйТекстВверх.
Горячие клавиши Ctrl + Down и Ctrl + Up.
2. Макрос КлонироватьТекст. Позволяет скопировать выделенный фрагмент
текста без использования буфера обмена. Горячие клавиши Ctrl + D.
3. Макросы OnPressEnterInComment, OnPressBackspaceInComment,
OnPressDeleteInComment предназначены для более удобного редактирования
многострочных комментариев. Вызываются неявно при нажатии соответствующих
клавиш.
========================================================================= */
stdlib.require('TextWindow.js', SelfScript);
function getPredefinedHotkeys(predef){
predef.setVersion(5);
predef.add("НайтиВыделенныйТекстВниз", "Ctrl + Down");
predef.add("НайтиВыделенныйТекстВверх", "Ctrl + Up");
predef.add("КлонироватьТекст", "Ctrl + D");
predef.add("OnPressEnterInComment", "Enter");
predef.add("OnPressDeleteInComment", "Del");
predef.add("OnPressBackspaceInComment", "BkSpace");
predef.add("Преобразовать регистр: ПРОПИСНЫЕ", "Ctrl + Shift + U");
predef.add("Преобразовать регистр: строчные", "Ctrl + U");
}
function macrosНайтиВыделенныйТекстВниз(){
selectNextPattern(1);
}
function macrosНайтиВыделенныйТекстВверх(){
selectNextPattern(-1);
}
function macrosПоменятьОперандыПрисваиванияМестами() {
var w = GetTextWindow();
if (!w) return false;
var selText = w.GetSelectedText();
if (selText == '') return;
var sel = w.GetSelection();
var lines = StringUtils.toLines(selText);
for(var lineNo = 0; lineNo < lines.length; lineNo++)
lines[lineNo] = lines[lineNo].replace(/(\s*)(\S*.+?)\s*=\s*(\S*.+)\s*;/, "$1$3=$2;");
w.SetSelectedText(StringUtils.fromLines(lines));
// Восстановим исходное выделение.
w.SetSelection(sel.beginRow, sel.beginCol, sel.endRow, sel.endCol);
if (lines.length > 1)
{
// Если было выделено несколько строк, выровняем в них по знакам '='.
var formatScript = addins.byUniqueName('format_script');
if (formatScript)
{
formatScript.invokeMacros('ВыровнятьЗнакиРавно');
// И снова восстановим исходное выделение.
w.SetSelection(sel.beginRow, sel.beginCol, sel.endRow, sel.endCol);
}
}
}
SelfScript.Self['macrosПреобразовать регистр: ПРОПИСНЫЕ'] = function() {
processSelectedText(function(selText){ return selText.toUpperCase(); });
}
SelfScript.Self['macrosПреобразовать регистр: строчные'] = function() {
processSelectedText(function(selText){ return selText.toLowerCase(); });
}
function selectNextPattern(dir){
//debugger;
var w = GetTextWindow(); //snegopat.activeTextWindow();
if (!w) return false;
//debugger;
var sel = w.getSelection();
var selText = w.selectedText();
if (selText == '') return false;
var row = dir < 0 ?
searchPatternUp(w, selText, sel.beginRow) :
searchPatternDown(w, selText, sel.beginRow);
if (!row) return false;
var str = w.line(row);
var col = 1 + str.indexOf(selText);
w.setCaretPos(1, 1);
w.setSelection(row, col, row, col + selText.length);
w.setCaretPos(row, col);
w.setSelection(row, col, row, col + selText.length);
return true;
}
function searchPatternDown(doc, pattern, startRow){
var q = 0;
for (var i = startRow + 1; i <= doc.linesCount(); i++){
if (++q > 10000) return null;
var str = doc.line(i);
var j = str.indexOf(pattern);
if (j > -1) return i;
}
return null;
}
function searchPatternUp(doc, pattern, startRow){
//debugger;
var q = 0;
for (var i = startRow - 1; i >= 1; i--){
if (++q > 10000) return null;
var str = doc.line(i);
var j = str.indexOf(pattern);
if (j > -1) return i;
}
return null;
}
function macrosКлонироватьТекст(){
var w = GetTextWindow();//snegopat.activeTextWindow();
if (!w) return false;
var sel = w.getSelection();
var selText = w.selectedText();
if (selText != ''){
w.SetSelectedText(selText + "\n" + selText);
}
else{
// Если ничего не выделено, склонируем текущую строку
var pos = w.getCaretPos();
w.setSelection(pos.beginRow, 1, pos.beginRow, 1 + w.line(pos.beginRow).length);
w.SetSelectedText(w.selectedText() + "\n" + w.selectedText());
}
}
function macrosOnPressEnterInComment(){
var w = GetTextWindow();//snegopat.activeTextWindow();
if (!w) return false;
var pos = w.getCaretPos();
var str = w.line(pos.beginRow);
if (str.match(/^\s*\/\/\s*[^\s]+/ig)) {
// Если курсор установлен левее символов комментария, то не обрабатываем нажатие,
// чтобы добавлялась пустая строка, как и ожидается.
if (pos.beginCol <= str.search('//') + 1)
return false;
// Это непустой комментарий - добавляем комментарий ниже
// с отступом как в предыдущей строке.
var newStr = getCommentStringHeader(str);
// Если справа от курсора есть текст, перенесем его
// на новую строку комментария.
if (str.length >= pos.endCol) {
var tail = str.substr(pos.beginCol - 1, str.length - pos.beginCol + 1);
w.ReplaceLine(pos.beginRow, str.substr(0, pos.beginCol - 1)); // отрезаем хвост от текущей строки
newStr = newStr + tail.replace(/^\s*/ig, "");
}
var newCaretRow = 1 + pos.beginRow;
w.InsertLine(newCaretRow, newStr);
w.setCaretPos(newCaretRow, 1 + newStr.length);
return true;
}
return false;
}
function macrosOnPressDeleteInComment(){
var w = GetTextWindow();//snegopat.activeTextWindow();
if (!w) return false;
if (w.selectedText() != ""){
return false; /// TODO: обработать этот вариант.
}
var pos = w.getCaretPos();
if (pos.beginRow == w.linesCount) return false;
var str = w.line(pos.beginRow);
if (!isCommentString(str)) return false;
if (pos.endCol > str.length){
// Курсор стоит в самом конце строки и при нажатии
// Delete следующая строка должна приклеиться в конец текущей.
var newTail = w.line(1 + pos.beginRow);
if (!isCommentString(newTail)) return false;
newTail = newTail.replace(/^\s*\/\/*\/\s*/ig, "");
if (str.match(/[^\s]$/ig)) newTail = " " + newTail;
w.ReplaceLine(pos.beginRow, str + newTail);
w.DeleteLine(1 + pos.beginRow);
w.setCaretPos(pos.beginRow, pos.beginCol);
return true;
}
return false;
}
function macrosOnPressBackspaceInComment(){
var w = GetTextWindow();//snegopat.activeTextWindow();
if (!w) return false;
if (w.selectedText() != ""){
return false; /// TODO: обработать этот вариант.
}
var pos = w.getCaretPos();
if (pos.beginRow == 1) return false; // И так уже стоим в первой строке.
// Обрабатываем только если стоим в самом начале строки и
// если эта и предыдущая строки являются комментариями.
if (pos.beginCol > 1) return false;
var str = w.line(pos.beginRow);
if (!isCommentString(str)) return false;
var prevStr = w.line(pos.beginRow - 1);
if (!isCommentString(prevStr)) return false;
// Курсор стоит в самом начале строки и при нажатии
// Backspace текущая строка должна приклеиться в конец предыдущей.
var newTail = str.replace(/^\s*\/\/*\/\s*/ig, "");
if (prevStr.match(/[^\s]$/ig)) newTail = " " + newTail;
w.setSelection(pos.beginRow - 1, 1, pos.beginRow,
w.line(pos.beginRow).length + 1);
w.SetSelectedText(prevStr + newTail);
w.setCaretPos(pos.beginRow - 1, 1 + prevStr.length);
return true;
}
function isCommentString(str){
return null != str.match(/^\s*\/\//ig);
}
function getTextBlockOffset(str){
var match = str.match(/^([\s]+)/ig);
var res = !match ? "" : match[0];
return res;
}
function getCommentStringHeader(str){
var match = str.match(/^([\s]*\/\/*\/[\s]?)*/ig);
var res = !match ? "" : match[0];
return res;
}
function processSelectedText(selTextHandler, doNotRestoreSelection) {
var w = GetTextWindow();
if (!w) return false;
var sel = w.GetSelection();
var selText = w.GetSelectedText();
try
{
selText = selTextHandler.call(null, selText);
}
catch (e)
{
Message(e.description);
return;
}
w.SetSelectedText(selText);
// Восстановим исходное выделение.
if (!doNotRestoreSelection)
w.SetSelection(sel.beginRow, sel.beginCol, sel.endRow, sel.endCol);
}