Code: Select all
// http://akelpad.sourceforge.net/forum/viewtopic.php?p=1607#p1607
// Description(1033): Works with text. Script implemented as the library for using in other scripts.
// Description(1049): Библиотека функций для работы с текстом
// Version: 4.11 (2015.01.16)
// Author: VladSh
//GLOBAL VARIABLES
var pBreak = "\r"; //by default
var pTab = "\t";
var sShift; //shift definition (\t for Tab or Spaces)
var sbOpen = "{";
var sbClose = "}";
var nEndLineOfFile = -1; //индекс доступа к последней строке файла; (на -2 с наскока не удалось переделать, т.к. в correctRangebyBorders сложные условия)
var pContent = ""; //содержимое всего изменяемого файла; определяется и используется внутри данного скрипта, извне вручную не менять!
//"Класс"-объект модификации текста
var oCh =
{
rBegin: [], //Границы выделения при запуске скрипта - позиции символов в файле для начала и окончания выделения
Text: "", //Обработанная строка - результат работы скрипта
rResult: [], //Границы выделения изменяемого текста
rCaret: [], //Границы для установки выделения после работы скрипта
saveSelRange: function() //Запоминает границы выделения при запуске скрипта (вспомогательный Private-метод)
{
if (typeof(rBegin) == "undefined")
{
this.rBegin[0] = AkelPad.GetSelStart();
this.rBegin[1] = AkelPad.GetSelEnd();
this.rCaret = [this.rBegin[0], this.rBegin[0]]; //по умолчанию по окончании работы ставим каретку в начало выделения, которое в начале сделал пользователь
}
},
getSelTextEmpty: function() //Если текст не выделен, возвращает пустую строку
{
this.rResult = this.rBegin;
if (this.rBegin[0] == this.rBegin[1])
return "";
else
return getTextbyRange(this.rBegin);
},
getSelTextAll: function() //Если текст не выделен, возвращает всё содержимое файла
{
if (this.rBegin[0] == this.rBegin[1])
{
this.rResult = getTextAll();
return pContent;
}
else
{
this.rResult = this.rBegin;
return getTextbyRange(this.rResult);
}
},
getTextAll: function() //Возвращает всё содержимое файла вне зависимости, выделено что-то или нет
{
this.rResult = getTextAll();
return pContent;
},
setCompleteLineText: function() //Работает автоматически по выделению
{
this.rResult = getRangeCompleteLine(this.rBegin[0], this.rBegin[1]);
this.Text = getTextbyRange(this.rResult);
},
setCompleteLineRange: function(nPosStart, nPosEnd) //Используется в сложних случаях, когда необходимо самому просчитать и передать границы
{
this.rResult = getRangeCompleteLine(nPosStart, nPosEnd);
this.Text = getTextbyRange(this.rResult);
},
run: function() //Основной метод запуска (без отключения прорисовки)
{
this.start();
this.modify();
},
runWithRedraw: function() //Основной метод запуска (с отключением прорисовки)
{
this.start();
var hWndEdit = AkelPad.GetEditWnd();
AutoRedrawOff(hWndEdit);
this.modify();
AutoRedrawOn(hWndEdit);
},
start: function() //Вспомогательный "Private"-метод
{
this.saveSelRange();
process(); //Определяется внутри вызывающего скрипта!
},
setSelCaret: function(nCaretStart, nCaretEnd) //Установка параметров выделения; 2-й параметр необязателен
{
if (nCaretStart != null) //Если в nCaretStart передан null - оставляем выделение в ReplaceSel (см. метод modify)
{
this.rCaret[0] = nCaretStart;
this.rCaret[1] = nCaretEnd || nCaretStart; //Если nCaretEnd не задан - устанавливаем курсор в позицию nCaretStart
}
else
this.rCaret = null;
},
modify: function() //Окончание работы скрипта: установка и замена выделения + установка курсора (вспомогательный Private-метод)
{
if (this.rResult != null)
{
AkelPad.SetSel(this.rResult[0], this.rResult[1]);
var nSelect = (this.rCaret == null) ? -1 : 0;
AkelPad.ReplaceSel(this.Text, nSelect);
if (!nSelect)
AkelPad.SetSel(this.rCaret[0], this.rCaret[1]);
}
},
testSetSelResult: function() //Тестирование установки выделения заменяемого текста
{
if (this.rResult != null)
AkelPad.SetSel(this.rResult[0], this.rResult[1]);
else
AkelPad.MessageBox(AkelPad.GetEditWnd(), " this.rResult = null", "oCh.testSetSelResult() function", 0 /*MB_OK*/);
AutoRedrawOn(AkelPad.GetEditWnd()); //если вдруг работу начинали с вызова runWithRedraw
WScript.Quit();
}
};
//FUNCTIONS
//Берёт текст чисто по указанным границам; вынесена, чтобы сделать "класс" более абстрактным
function getTextbyRange(Range)
{
return AkelPad.GetTextRange(Range[0], Range[1]);
}
//Нужно для простых случаев, когда надо сразу выделить захваченые строки; иначе пользуемся алгоритмами с GetTextRange
function selCompleteLine(nCaretPosStart, nCaretPosEnd)
{
var Range = getRangeCompleteLine(nCaretPosStart, nCaretPosEnd);
AkelPad.SetSel(Range[0], Range[1]);
}
//Возвращает границы выделенных полных строк с учётом ограничивающих переводов строк; также поддерживаются значения -1 и -2.
function getRangeCompleteLine(nCaretPosStart, nCaretPosEnd)
{
var hWndEdit = AkelPad.GetEditWnd();
var Range = [];
Range[0] = getOffset(hWndEdit, 18 /*AEGI_WRAPLINEBEGIN*/, nCaretPosStart);
Range[1] = getOffset(hWndEdit, 19 /*AEGI_WRAPLINEEND*/, nCaretPosEnd);
return Range;
//return getRangebyBorders(nCaretPosStart, nCaretPosEnd, pBreak, pBreak, false); //старая рабочая; закоментил на всякий случай, вдруг придётся открывать...
}
//Возвращает смещение объекта, определяемого nType (см. ScriptConsts.js в папке документации).
//by Instructor function: http://akelpad.sourceforge.net/forum/viewtopic.php?p=11382#p11382
function getOffset(hWndEdit, nType /*AEGI_*/, nOffset)
{
var lpIndex;
if (lpIndex = AkelPad.MemAlloc(_X64 ? 24 : 12 /*sizeof(AECHARINDEX)*/))
{
if (nOffset != -1)
AkelPad.SendMessage(hWndEdit, 3137 /*AEM_RICHOFFSETTOINDEX*/, nOffset, lpIndex);
AkelPad.SendMessage(hWndEdit, 3130 /*AEM_GETINDEX*/, nType, lpIndex);
nOffset = AkelPad.SendMessage(hWndEdit, 3136 /*AEM_INDEXTORICHOFFSET*/, 0, lpIndex);
AkelPad.MemFree(lpIndex);
}
return nOffset;
}
//Получает весь текст; при работе с "классом" перед её вызовом желателен вызов saveSelRange, чтобы запомнить границы выделения
function getTextAll()
{
var rResult = [0, nEndLineOfFile];
pContent = pContent || getTextbyRange(rResult); //берём весь текст в бэкграунде в глобальную переменную
if (pContent)
return rResult;
}
//Простое определение границ по открывающему и закрывающему тэгу
function getRangebyBorders(nMinSel, nMaxSel, pBOpen, pBClose, bIncludeBorders)
{
var Range = [];
if (getTextAll())
{
Range[1] = pContent.indexOf(pBClose, nMaxSel); //Ищем вниз
Range[0] = pContent.lastIndexOf(pBOpen, nMinSel); //Ищем вверх
Range = correctRangebyBorders(nMinSel, pBOpen, pBClose, bIncludeBorders, Range);
}
return Range;
}
//Определение границ по открывающему и закрывающему тэгу с учётом возможности их вложенности
function getRangebyBordersEx(iCursor, pBOpen, pBClose, bIncludeBorders)
{
var Range = [];
var iStart;
var cBOpen;
var cBClose;
if (!getTextAll())
return null;
var iBOpen = pContent.lastIndexOf(pBOpen, iCursor); //ищем вверх индекс открывающего тэга
var iBClose = pContent.lastIndexOf(pBClose, iCursor); //ищем вверх индекс закрывающего тэга
if (iBOpen < iBClose) //если закрывающий тэг ниже открывающего, значит продолжаем искать открывающий тэг выше
{
do
{
iStart = iBOpen - 1;
iBOpen = pContent.lastIndexOf(pBOpen, iStart); //индекс открывающего тэга
if (iBOpen == -1)
return null; //защита от зацикливания при передаче неправильных или несуществующих в файле тэгов
cBOpen = substringCount(pContent, iBOpen, iCursor, pBOpen); //количество открывающих тэгов
cBClose = substringCount(pContent, iBOpen, iCursor, pBClose); //количество закрывающих тэгов
}
while (cBOpen - 1 != cBClose);
iBClose = iBOpen - 1; //чтобы зайти в следующее условие (поиск последнего закрывающего тэга)
}
if (iBOpen > iBClose) //если закрывающий тэг выше открывающего, значит ищем закрывающий тэг ниже курсора
{
iStart = iCursor;
do
{
iBClose = pContent.indexOf(pBClose, iStart); //индекс закрывающего тэга
if (iBClose == -1)
return null; //защита от зацикливания при передаче неправильных или несуществующих в файле тэгов
cBOpen = substringCount(pContent, iBOpen, iBClose, pBOpen);
cBClose = substringCount(pContent, iBOpen, iBClose, pBClose);
iStart = iBClose + 1;
}
while (cBOpen - 1 != cBClose);
}
Range[0] = iBOpen;
Range[1] = iBClose;
Range = correctRangebyBorders(iCursor, pBOpen, pBClose, bIncludeBorders, Range);
return Range;
}
function correctRangebyBorders(nMinSel, pBOpen, pBClose, bIncludeBorders, Range)
{
if ((Range[0] == -1 && pBOpen != pBreak) || (Range[1] == -1 && pBClose != pBreak))
return null;
else if (Range[0] == Range[1]) //Конец строки
{
if (Range[0] != 0)
Range[0] = pContent.lastIndexOf(pBOpen, nMinSel - 1); //Повторно ищем вверх
else
bIncludeBorders = null;
}
if (bIncludeBorders != null)
{
if (bIncludeBorders)
Range[1] += (pBClose.length);
else
Range[0] += pBOpen.length;
}
return Range;
}
//Возвращает границы блока текста ограниченный разделителями
//поиск производится снизу вверх
//nMaxSel - позиция в файле, с которой будет начинаться поиск;
// для внешнего цикла будет быстрее; по умолчанию передавать длину строки
function getRangebyBordersBack(pContent, pBOpen, pBClose, nMaxSel)
{
var Range = [];
var nMaxSelTmp = pContent.lastIndexOf(pBClose, nMaxSel);
if ( !(nMaxSelTmp == -1 && pBClose == pBreak) )
nMaxSel = nMaxSelTmp;
if (nMaxSel != -1)
{
Range[1] = nMaxSel + pBClose.length;
Range[0] = pContent.lastIndexOf(pBOpen, nMaxSel);
var nMaxSel = Range[0];
if (nMaxSel != -1)
return Range;
}
return null;
}
//Возвращает границы строки, в которой находится искомое вхождение
//3-й элемент результирующего массива - nMaxSel для следующей итерации
function getTextLineRange(pContent, pText, nMaxSel)
{
var Range = [];
// находим вхождение строки
nMaxSel = pContent.lastIndexOf(pText, nMaxSel);
if (nMaxSel != -1)
{
// ищем её окончание
Range[1] = pContent.indexOf(pBreak, nMaxSel);
if (Range[1] == -1)
Range[1] = pContent.length;
// ищем её начало
Range[0] = pContent.lastIndexOf(pBreak, nMaxSel);
if (Range[0] == -1)
Range[0] = 0;
var nMaxSel = Range[0];
return Range;
}
return null;
}
//Удаляет строку, в которой находится искомое вхождение;
//Возвращает массив:
// - 1-й элемент - содержимое строки;
// - 2-й - nMaxSel, т.е. точка отсчёта, откуда начинать поиск в следующей итерации
function removeTextLine(pContent, pText, nMaxSel)
{
var iRange = getTextLineRange(pContent, pText, nMaxSel)
if (iRange != null)
{
var sRange = splitbyBorders(pContent, iRange[0], iRange[1]);
sRange[0] = sRange[0] + sRange[1];
sRange[1] = iRange[0];
return sRange;
}
return null;
}
//Удаляет все строки, в которых находится искомое вхождение
function removeTextLineAll(pContent, pTextRemove)
{
var nMaxSel = pContent.length;
do
{
var Range = removeTextLine(pContent, pTextRemove, nMaxSel);
if (Range == null)
return pContent;
pContent = Range[0];
nMaxSel = Range[1];
}
while (true);
}
//Разбивка строки: слева "пустые" символы начала строки, справа все остальные значащие
//by Infocatcher code
function separateRow(line)
{
var sNull = line.match(/^\s*/)[0];
return {
left: sNull, //начальные "пустые" символы текущей строки
right: line.substr(sNull.length) //все символы текущей строки, идущие после "пустых"
};
}
//Количество вхождений в определённом диапазоне
function substringCount(pContent, nStart, nEnd, pTextSearch)
{
var pTextSource = pContent.substring(nStart, nEnd);
return pTextSource.split(pTextSearch).length - 1;
}
//Разбивка строки в массив исключая участок, ограниченный номерами позиций;
//массив будет содержать 2 элемента
function splitbyBorders(pText, nStart, nEnd)
{
var arrTmp = [];
arrTmp[0] = pText.substring(0, nStart);
arrTmp[1] = pText.substr(nEnd);
return arrTmp;
}
//Замена участка, ограниченного номерами позиций, на подстроку;
//при nStart = nEnd будет, естественно, производиться вставка
function replacebyBorders(pText, nStart, nEnd, pTextIns)
{
return splitbyBorders(pText, nStart, nEnd).join(pTextIns);
}
//STOP redraw window (by code of the Instructor)
function AutoRedrawOff(hWndEdit)
{
AkelPad.SendMessage(hWndEdit, 11 /*WM_SETREDRAW*/, false, 0);
}
//START redraw window (by code of the Instructor)
function AutoRedrawOn(hWndEdit)
{
AkelPad.SendMessage(hWndEdit, 11 /*WM_SETREDRAW*/, true, 0);
var oFunction = AkelPad.SystemFunction();
oFunction.Call("user32::InvalidateRect", hWndEdit, 0, true);
}
//Определение символа(-ов) сдвига из настроек
function getShift()
{
if (sShift == undefined)
{
var bTabStopAsSpaces = AkelPad.SendMessage(AkelPad.GetMainWnd(), 1223 /*AKD_GETFRAMEINFO*/, 53 /*FI_TABSTOPASSPACES*/, 0);
if (bTabStopAsSpaces)
{
var nTabStop = AkelPad.SendMessage(AkelPad.GetEditWnd(), 3239 /*AEM_GETTABSTOP*/, 0, 0); //Number of Spaces in Tabs (take from the program settings)
sShift = oStr.repeat(" ", nTabStop);
}
else
sShift = pTab;
}
return sShift;
}
function shiftRightText(pText)
{
getShift();
return sShift + pText.replace(/\r/g, pBreak + sShift);
}
//"Класс"-объект расширенного функционала по работе со строками
var oStr =
{
flags: "g", //by default
ltrim: function(pText, chars)
{
chars = chars || "\\s";
return pText.replace(new RegExp("^[" + chars + "]+", this.flags), "");
},
rtrim: function(pText, chars)
{
chars = chars || "\\s";
return pText.replace(new RegExp("[" + chars + "]+$", this.flags), "");
},
trim: function(pText, chars)
{
return this.ltrim(this.rtrim(pText, chars), chars);
},
left: function(pText, pSep)
{
var poz = pText.indexOf(pSep);
if (poz > 0)
return pText.slice(0, poz);
else
return "";
},
right: function(pText, pSep)
{
var poz = pText.indexOf(pSep);
if (poz > 0)
return pText.slice(poz + 1, pText.length);
else
return "";
},
leftback: function(pText, pSep)
{
var poz = pText.lastIndexOf(pSep);
if (poz > 0)
return pText.slice(0, poz);
else
return "";
},
rightback: function(pText, pSep)
{
var poz = pText.lastIndexOf(pSep);
if (poz > 0)
return pText.slice(poz + 1);
else
return "";
},
cleanbyBorders: function(pText, pBStart, pBEnd)
{
return replacebyBorders(pText, pBStart, pBEnd, "");
},
replacebyBorders: function(pText, pBStart, pBEnd, pNewText)
{
//При создании объекта RegExp в паттерне все слэши д.б. экранированы! Передавать тоже экранированные!
return pText.replace(new RegExp(pBStart + "*?[\\s\\S]*?" + pBEnd, this.flags), pNewText);
},
repeat: function(pText, nCount)
{
return (new Array(nCount + 1)).join(pText);
}
};
//Обработка Esc-последовательностей
function escSequencesProcessing(pText)
{
if (pText)
{
pText = pText.replace(/\\\\/g, "\0");
// if (pText.search(/\\[^rnt]/g) != -1)
// {
pText = pText.replace(/\\r\\n|\\r|\\n/g, "\r");
pText = pText.replace(/\\t/g, pTab);
pText = pText.replace(/\0/g, "\\");
// }
}
return pText;
}
// ------------------------------------------------------
// Functions for working with each row of selected text.
// Функции для организации обработки каждой строки текста
// (бывший ProcessRowText.js)
function processRowText(pSelText, pBreakLine, pJoinLine) {
var arrOutput = []; // output array of strings
if (pSelText.length > 0) {
var arrInput = pSelText.split(pBreakLine); // input array of strings; универсальный "символ разрыва" для внутренних и внешних файлов: /\r\n|\n|\r/, но в виде "\r\n|\n|\r" в split это не работает
var vResult; // the result of processing row
var index = -1;
for (index = 0; index < arrInput.length; index++) {
vResult = processString(arrInput[index]); // process each string
addToResult(arrOutput, vResult);
}
}
if (!pJoinLine)
pJoinLine = pBreakLine;
pSelText = arrOutput.join(pJoinLine);
return pSelText;
}
// User-defined function; copy into your script, decomment and modify it
//function processString(s) {
// var pResult;
// //code modify the string s...
// return pResult;
//}
//function addToResult(arrOutput, vResult) {
// //condition and code added value in the resulting array
// arrOutput[arrOutput.length] = vResult; //by default return all values (with empty)
//}